mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-21 10:34:35 -07:00
feat: Update permissions command to support modifying trust for other… (#11642)
This commit is contained in:
@@ -5,7 +5,8 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { directoryCommand, expandHomeDir } from './directoryCommand.js';
|
||||
import { directoryCommand } from './directoryCommand.js';
|
||||
import { expandHomeDir } from '../utils/directoryUtils.js';
|
||||
import type { Config, WorkspaceContext } from '@google/gemini-cli-core';
|
||||
import type { CommandContext } from './types.js';
|
||||
import { MessageType } from '../types.js';
|
||||
|
||||
@@ -7,22 +7,8 @@
|
||||
import type { SlashCommand, CommandContext } from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { MessageType } from '../types.js';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
import { refreshServerHierarchicalMemory } from '@google/gemini-cli-core';
|
||||
|
||||
export function expandHomeDir(p: string): string {
|
||||
if (!p) {
|
||||
return '';
|
||||
}
|
||||
let expandedPath = p;
|
||||
if (p.toLowerCase().startsWith('%userprofile%')) {
|
||||
expandedPath = os.homedir() + p.substring('%userprofile%'.length);
|
||||
} else if (p === '~' || p.startsWith('~/')) {
|
||||
expandedPath = os.homedir() + p.substring(1);
|
||||
}
|
||||
return path.normalize(expandedPath);
|
||||
}
|
||||
import { expandHomeDir } from '../utils/directoryUtils.js';
|
||||
|
||||
export const directoryCommand: SlashCommand = {
|
||||
name: 'directory',
|
||||
|
||||
@@ -4,32 +4,113 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
||||
import * as process from 'node:process';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { permissionsCommand } from './permissionsCommand.js';
|
||||
import { type CommandContext, CommandKind } from './types.js';
|
||||
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
|
||||
|
||||
vi.mock('node:fs');
|
||||
|
||||
describe('permissionsCommand', () => {
|
||||
let mockContext: CommandContext;
|
||||
|
||||
beforeEach(() => {
|
||||
mockContext = createMockCommandContext();
|
||||
vi.mocked(fs).statSync.mockReturnValue({
|
||||
isDirectory: vi.fn(() => true),
|
||||
} as unknown as fs.Stats);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should have the correct name and description', () => {
|
||||
expect(permissionsCommand.name).toBe('permissions');
|
||||
expect(permissionsCommand.description).toBe('Manage folder trust settings');
|
||||
expect(permissionsCommand.description).toBe(
|
||||
'Manage folder trust settings and other permissions',
|
||||
);
|
||||
});
|
||||
|
||||
it('should be a built-in command', () => {
|
||||
expect(permissionsCommand.kind).toBe(CommandKind.BUILT_IN);
|
||||
});
|
||||
|
||||
it('should return an action to open the permissions dialog', () => {
|
||||
const actionResult = permissionsCommand.action?.(mockContext, '');
|
||||
it('should have a trust subcommand', () => {
|
||||
const trustCommand = permissionsCommand.subCommands?.find(
|
||||
(cmd) => cmd.name === 'trust',
|
||||
);
|
||||
expect(trustCommand).toBeDefined();
|
||||
expect(trustCommand?.name).toBe('trust');
|
||||
expect(trustCommand?.description).toBe(
|
||||
'Manage folder trust settings. Usage: /permissions trust [<directory-path>]',
|
||||
);
|
||||
expect(trustCommand?.kind).toBe(CommandKind.BUILT_IN);
|
||||
});
|
||||
|
||||
it('should return an action to open the permissions dialog with a specified directory', () => {
|
||||
const trustCommand = permissionsCommand.subCommands?.find(
|
||||
(cmd) => cmd.name === 'trust',
|
||||
);
|
||||
const actionResult = trustCommand?.action?.(mockContext, '/test/dir');
|
||||
expect(actionResult).toEqual({
|
||||
type: 'dialog',
|
||||
dialog: 'permissions',
|
||||
props: {
|
||||
targetDirectory: path.resolve('/test/dir'),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should return an action to open the permissions dialog with the current directory if no path is provided', () => {
|
||||
const trustCommand = permissionsCommand.subCommands?.find(
|
||||
(cmd) => cmd.name === 'trust',
|
||||
);
|
||||
const actionResult = trustCommand?.action?.(mockContext, '');
|
||||
expect(actionResult).toEqual({
|
||||
type: 'dialog',
|
||||
dialog: 'permissions',
|
||||
props: {
|
||||
targetDirectory: process.cwd(),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should return an error message if the provided path does not exist', () => {
|
||||
const trustCommand = permissionsCommand.subCommands?.find(
|
||||
(cmd) => cmd.name === 'trust',
|
||||
);
|
||||
vi.mocked(fs).statSync.mockImplementation(() => {
|
||||
throw new Error('ENOENT: no such file or directory');
|
||||
});
|
||||
const actionResult = trustCommand?.action?.(
|
||||
mockContext,
|
||||
'/nonexistent/dir',
|
||||
);
|
||||
expect(actionResult).toEqual({
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: `Error accessing path: ${path.resolve(
|
||||
'/nonexistent/dir',
|
||||
)}. ENOENT: no such file or directory`,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return an error message if the provided path is not a directory', () => {
|
||||
const trustCommand = permissionsCommand.subCommands?.find(
|
||||
(cmd) => cmd.name === 'trust',
|
||||
);
|
||||
vi.mocked(fs).statSync.mockReturnValue({
|
||||
isDirectory: vi.fn(() => false),
|
||||
} as unknown as fs.Stats);
|
||||
const actionResult = trustCommand?.action?.(mockContext, '/file/not/dir');
|
||||
expect(actionResult).toEqual({
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: `Path is not a directory: ${path.resolve('/file/not/dir')}`,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,15 +4,80 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type { OpenDialogActionReturn, SlashCommand } from './types.js';
|
||||
import type {
|
||||
OpenDialogActionReturn,
|
||||
SlashCommand,
|
||||
SlashCommandActionReturn,
|
||||
} from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import * as process from 'node:process';
|
||||
import * as path from 'node:path';
|
||||
import * as fs from 'node:fs';
|
||||
import { expandHomeDir } from '../utils/directoryUtils.js';
|
||||
|
||||
export const permissionsCommand: SlashCommand = {
|
||||
name: 'permissions',
|
||||
description: 'Manage folder trust settings',
|
||||
description: 'Manage folder trust settings and other permissions',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (): OpenDialogActionReturn => ({
|
||||
type: 'dialog',
|
||||
dialog: 'permissions',
|
||||
}),
|
||||
subCommands: [
|
||||
{
|
||||
name: 'trust',
|
||||
description:
|
||||
'Manage folder trust settings. Usage: /permissions trust [<directory-path>]',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context, input): SlashCommandActionReturn => {
|
||||
const dirPath = input.trim();
|
||||
let targetDirectory: string;
|
||||
|
||||
if (!dirPath) {
|
||||
targetDirectory = process.cwd();
|
||||
} else {
|
||||
targetDirectory = path.resolve(expandHomeDir(dirPath));
|
||||
}
|
||||
|
||||
try {
|
||||
if (!fs.statSync(targetDirectory).isDirectory()) {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: `Path is not a directory: ${targetDirectory}`,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
const message = e instanceof Error ? e.message : String(e);
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: `Error accessing path: ${targetDirectory}. ${message}`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'dialog',
|
||||
dialog: 'permissions',
|
||||
props: {
|
||||
targetDirectory,
|
||||
},
|
||||
} as OpenDialogActionReturn;
|
||||
},
|
||||
},
|
||||
],
|
||||
action: (context, input): SlashCommandActionReturn => {
|
||||
const parts = input.trim().split(' ');
|
||||
const subcommand = parts[0];
|
||||
|
||||
if (!subcommand) {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: `Please provide a subcommand for /permissions. Usage: /permissions trust [<directory-path>]`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: `Invalid subcommand for /permissions: ${subcommand}. Usage: /permissions trust [<directory-path>]`,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -113,6 +113,7 @@ export interface MessageActionReturn {
|
||||
*/
|
||||
export interface OpenDialogActionReturn {
|
||||
type: 'dialog';
|
||||
props?: Record<string, unknown>;
|
||||
|
||||
dialog:
|
||||
| 'help'
|
||||
|
||||
Reference in New Issue
Block a user