summaryrefslogtreecommitdiff
path: root/packages/cli/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/cli/src')
-rw-r--r--packages/cli/src/services/CommandService.test.ts32
-rw-r--r--packages/cli/src/services/CommandService.ts50
-rw-r--r--packages/cli/src/ui/App.test.tsx1
-rw-r--r--packages/cli/src/ui/commands/ideCommand.test.ts256
-rw-r--r--packages/cli/src/ui/commands/ideCommand.ts165
-rw-r--r--packages/cli/src/ui/hooks/slashCommandProcessor.test.ts36
-rw-r--r--packages/cli/src/ui/hooks/slashCommandProcessor.ts2
7 files changed, 514 insertions, 28 deletions
diff --git a/packages/cli/src/services/CommandService.test.ts b/packages/cli/src/services/CommandService.test.ts
index 5c28228e..d9799146 100644
--- a/packages/cli/src/services/CommandService.test.ts
+++ b/packages/cli/src/services/CommandService.test.ts
@@ -6,6 +6,7 @@
import { vi, describe, it, expect, beforeEach } from 'vitest';
import { CommandService } from './CommandService.js';
+import { type Config } from '@google/gemini-cli-core';
import { type SlashCommand } from '../ui/commands/types.js';
import { memoryCommand } from '../ui/commands/memoryCommand.js';
import { helpCommand } from '../ui/commands/helpCommand.js';
@@ -17,6 +18,7 @@ import { themeCommand } from '../ui/commands/themeCommand.js';
import { statsCommand } from '../ui/commands/statsCommand.js';
import { privacyCommand } from '../ui/commands/privacyCommand.js';
import { aboutCommand } from '../ui/commands/aboutCommand.js';
+import { ideCommand } from '../ui/commands/ideCommand.js';
import { extensionsCommand } from '../ui/commands/extensionsCommand.js';
import { toolsCommand } from '../ui/commands/toolsCommand.js';
import { compressCommand } from '../ui/commands/compressCommand.js';
@@ -50,6 +52,9 @@ vi.mock('../ui/commands/statsCommand.js', () => ({
vi.mock('../ui/commands/aboutCommand.js', () => ({
aboutCommand: { name: 'about', description: 'Mock About' },
}));
+vi.mock('../ui/commands/ideCommand.js', () => ({
+ ideCommand: vi.fn(),
+}));
vi.mock('../ui/commands/extensionsCommand.js', () => ({
extensionsCommand: { name: 'extensions', description: 'Mock Extensions' },
}));
@@ -65,12 +70,20 @@ vi.mock('../ui/commands/mcpCommand.js', () => ({
describe('CommandService', () => {
const subCommandLen = 14;
+ let mockConfig: vi.Mocked<Config>;
+
+ beforeEach(() => {
+ mockConfig = {
+ getIdeMode: vi.fn(),
+ } as unknown as vi.Mocked<Config>;
+ vi.mocked(ideCommand).mockReturnValue(null);
+ });
describe('when using default production loader', () => {
let commandService: CommandService;
beforeEach(() => {
- commandService = new CommandService();
+ commandService = new CommandService(mockConfig);
});
it('should initialize with an empty command tree', () => {
@@ -106,6 +119,21 @@ describe('CommandService', () => {
expect(commandNames).toContain('tools');
expect(commandNames).toContain('compress');
expect(commandNames).toContain('mcp');
+ expect(commandNames).not.toContain('ide');
+ });
+
+ it('should include ide command when ideMode is on', async () => {
+ mockConfig.getIdeMode.mockReturnValue(true);
+ vi.mocked(ideCommand).mockReturnValue({
+ name: 'ide',
+ description: 'Mock IDE',
+ });
+ await commandService.loadCommands();
+ const tree = commandService.getCommands();
+
+ expect(tree.length).toBe(subCommandLen + 1);
+ const commandNames = tree.map((cmd) => cmd.name);
+ expect(commandNames).toContain('ide');
});
it('should overwrite any existing commands when called again', async () => {
@@ -163,7 +191,7 @@ describe('CommandService', () => {
const mockLoader = vi.fn().mockResolvedValue(mockCommands);
// Act: Instantiate the service WITH the injected loader function.
- const commandService = new CommandService(mockLoader);
+ const commandService = new CommandService(mockConfig, mockLoader);
await commandService.loadCommands();
const tree = commandService.getCommands();
diff --git a/packages/cli/src/services/CommandService.ts b/packages/cli/src/services/CommandService.ts
index 51fe2ad8..d8604276 100644
--- a/packages/cli/src/services/CommandService.ts
+++ b/packages/cli/src/services/CommandService.ts
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { Config } from '@google/gemini-cli-core';
import { SlashCommand } from '../ui/commands/types.js';
import { memoryCommand } from '../ui/commands/memoryCommand.js';
import { helpCommand } from '../ui/commands/helpCommand.js';
@@ -19,29 +20,42 @@ import { aboutCommand } from '../ui/commands/aboutCommand.js';
import { extensionsCommand } from '../ui/commands/extensionsCommand.js';
import { toolsCommand } from '../ui/commands/toolsCommand.js';
import { compressCommand } from '../ui/commands/compressCommand.js';
+import { ideCommand } from '../ui/commands/ideCommand.js';
-const loadBuiltInCommands = async (): Promise<SlashCommand[]> => [
- aboutCommand,
- authCommand,
- chatCommand,
- clearCommand,
- compressCommand,
- docsCommand,
- extensionsCommand,
- helpCommand,
- mcpCommand,
- memoryCommand,
- privacyCommand,
- statsCommand,
- themeCommand,
- toolsCommand,
-];
+const loadBuiltInCommands = async (
+ config: Config | null,
+): Promise<SlashCommand[]> => {
+ const allCommands = [
+ aboutCommand,
+ authCommand,
+ chatCommand,
+ clearCommand,
+ compressCommand,
+ docsCommand,
+ extensionsCommand,
+ helpCommand,
+ ideCommand(config),
+ mcpCommand,
+ memoryCommand,
+ privacyCommand,
+ statsCommand,
+ themeCommand,
+ toolsCommand,
+ ];
+
+ return allCommands.filter(
+ (command): command is SlashCommand => command !== null,
+ );
+};
export class CommandService {
private commands: SlashCommand[] = [];
constructor(
- private commandLoader: () => Promise<SlashCommand[]> = loadBuiltInCommands,
+ private config: Config | null,
+ private commandLoader: (
+ config: Config | null,
+ ) => Promise<SlashCommand[]> = loadBuiltInCommands,
) {
// The constructor can be used for dependency injection in the future.
}
@@ -49,7 +63,7 @@ export class CommandService {
async loadCommands(): Promise<void> {
// For now, we only load the built-in commands.
// File-based and remote commands will be added later.
- this.commands = await this.commandLoader();
+ this.commands = await this.commandLoader(this.config);
}
getCommands(): SlashCommand[] {
diff --git a/packages/cli/src/ui/App.test.tsx b/packages/cli/src/ui/App.test.tsx
index 4b65603b..0c18b042 100644
--- a/packages/cli/src/ui/App.test.tsx
+++ b/packages/cli/src/ui/App.test.tsx
@@ -135,6 +135,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
setFlashFallbackHandler: vi.fn(),
getSessionId: vi.fn(() => 'test-session-id'),
getUserTier: vi.fn().mockResolvedValue(undefined),
+ getIdeMode: vi.fn(() => false),
};
});
return {
diff --git a/packages/cli/src/ui/commands/ideCommand.test.ts b/packages/cli/src/ui/commands/ideCommand.test.ts
new file mode 100644
index 00000000..9e5f798d
--- /dev/null
+++ b/packages/cli/src/ui/commands/ideCommand.test.ts
@@ -0,0 +1,256 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
+import { ideCommand } from './ideCommand.js';
+import { type CommandContext } from './types.js';
+import { type Config } from '@google/gemini-cli-core';
+import * as child_process from 'child_process';
+import { glob } from 'glob';
+
+import {
+ getMCPDiscoveryState,
+ getMCPServerStatus,
+ IDE_SERVER_NAME,
+ MCPDiscoveryState,
+ MCPServerStatus,
+} from '@google/gemini-cli-core';
+
+vi.mock('child_process');
+vi.mock('glob');
+vi.mock('@google/gemini-cli-core', async (importOriginal) => {
+ const original =
+ await importOriginal<typeof import('@google/gemini-cli-core')>();
+ return {
+ ...original,
+ getMCPServerStatus: vi.fn(),
+ getMCPDiscoveryState: vi.fn(),
+ };
+});
+
+describe('ideCommand', () => {
+ let mockContext: CommandContext;
+ let mockConfig: Config;
+ let execSyncSpy: vi.SpyInstance;
+ let globSyncSpy: vi.SpyInstance;
+ let platformSpy: vi.SpyInstance;
+ let getMCPServerStatusSpy: vi.SpyInstance;
+ let getMCPDiscoveryStateSpy: vi.SpyInstance;
+
+ beforeEach(() => {
+ mockContext = {
+ ui: {
+ addItem: vi.fn(),
+ },
+ } as unknown as CommandContext;
+
+ mockConfig = {
+ getIdeMode: vi.fn(),
+ } as unknown as Config;
+
+ execSyncSpy = vi.spyOn(child_process, 'execSync');
+ globSyncSpy = vi.spyOn(glob, 'sync');
+ platformSpy = vi.spyOn(process, 'platform', 'get');
+ getMCPServerStatusSpy = vi.mocked(getMCPServerStatus);
+ getMCPDiscoveryStateSpy = vi.mocked(getMCPDiscoveryState);
+ });
+
+ afterEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ it('should return null if ideMode is not enabled', () => {
+ (mockConfig.getIdeMode as vi.Mock).mockReturnValue(false);
+ const command = ideCommand(mockConfig);
+ expect(command).toBeNull();
+ });
+
+ it('should return the ide command if ideMode is enabled', () => {
+ (mockConfig.getIdeMode as vi.Mock).mockReturnValue(true);
+ const command = ideCommand(mockConfig);
+ expect(command).not.toBeNull();
+ expect(command?.name).toBe('ide');
+ expect(command?.subCommands).toHaveLength(2);
+ expect(command?.subCommands?.[0].name).toBe('status');
+ expect(command?.subCommands?.[1].name).toBe('install');
+ });
+
+ describe('status subcommand', () => {
+ beforeEach(() => {
+ (mockConfig.getIdeMode as vi.Mock).mockReturnValue(true);
+ });
+
+ it('should show connected status', () => {
+ getMCPServerStatusSpy.mockReturnValue(MCPServerStatus.CONNECTED);
+ const command = ideCommand(mockConfig);
+ const result = command?.subCommands?.[0].action(mockContext, '');
+ expect(getMCPServerStatusSpy).toHaveBeenCalledWith(IDE_SERVER_NAME);
+ expect(result).toEqual({
+ type: 'message',
+ messageType: 'info',
+ content: '🟢 Connected',
+ });
+ });
+
+ it('should show connecting status', () => {
+ getMCPServerStatusSpy.mockReturnValue(MCPServerStatus.CONNECTING);
+ const command = ideCommand(mockConfig);
+ const result = command?.subCommands?.[0].action(mockContext, '');
+ expect(result).toEqual({
+ type: 'message',
+ messageType: 'info',
+ content: '🔄 Initializing...',
+ });
+ });
+
+ it('should show discovery in progress status', () => {
+ getMCPServerStatusSpy.mockReturnValue(MCPServerStatus.DISCONNECTED);
+ getMCPDiscoveryStateSpy.mockReturnValue(MCPDiscoveryState.IN_PROGRESS);
+ const command = ideCommand(mockConfig);
+ const result = command?.subCommands?.[0].action(mockContext, '');
+ expect(result).toEqual({
+ type: 'message',
+ messageType: 'info',
+ content: '🔄 Initializing...',
+ });
+ });
+
+ it('should show disconnected status', () => {
+ getMCPServerStatusSpy.mockReturnValue(MCPServerStatus.DISCONNECTED);
+ getMCPDiscoveryStateSpy.mockReturnValue(MCPDiscoveryState.NOT_FOUND);
+ const command = ideCommand(mockConfig);
+ const result = command?.subCommands?.[0].action(mockContext, '');
+ expect(result).toEqual({
+ type: 'message',
+ messageType: 'error',
+ content: '🔴 Disconnected',
+ });
+ });
+ });
+
+ describe('install subcommand', () => {
+ beforeEach(() => {
+ (mockConfig.getIdeMode as vi.Mock).mockReturnValue(true);
+ platformSpy.mockReturnValue('linux');
+ });
+
+ it('should show an error if VSCode is not installed', async () => {
+ execSyncSpy.mockImplementation(() => {
+ throw new Error('Command not found');
+ });
+
+ const command = ideCommand(mockConfig);
+ await command?.subCommands?.[1].action(mockContext, '');
+
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
+ expect.objectContaining({
+ type: 'error',
+ text: expect.stringContaining(
+ 'VS Code command-line tool "code" not found',
+ ),
+ }),
+ expect.any(Number),
+ );
+ });
+
+ it('should show an error if the VSIX file is not found', async () => {
+ execSyncSpy.mockReturnValue(''); // VSCode is installed
+ globSyncSpy.mockReturnValue([]); // No .vsix file found
+
+ const command = ideCommand(mockConfig);
+ await command?.subCommands?.[1].action(mockContext, '');
+
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
+ expect.objectContaining({
+ type: 'error',
+ text: 'Could not find the required VS Code companion extension. Please file a bug via /bug.',
+ }),
+ expect.any(Number),
+ );
+ });
+
+ it('should install the extension if found in the bundle directory', async () => {
+ const vsixPath = '/path/to/bundle/gemini.vsix';
+ execSyncSpy.mockReturnValue(''); // VSCode is installed
+ globSyncSpy.mockReturnValue([vsixPath]); // Found .vsix file
+
+ const command = ideCommand(mockConfig);
+ await command?.subCommands?.[1].action(mockContext, '');
+
+ expect(globSyncSpy).toHaveBeenCalledWith(
+ expect.stringContaining('.vsix'),
+ );
+ expect(execSyncSpy).toHaveBeenCalledWith(
+ `code --install-extension ${vsixPath} --force`,
+ { stdio: 'pipe' },
+ );
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
+ expect.objectContaining({
+ type: 'info',
+ text: `Installing VS Code companion extension...`,
+ }),
+ expect.any(Number),
+ );
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
+ expect.objectContaining({
+ type: 'info',
+ text: 'VS Code companion extension installed successfully. Restart gemini-cli in a fresh terminal window.',
+ }),
+ expect.any(Number),
+ );
+ });
+
+ it('should install the extension if found in the dev directory', async () => {
+ const vsixPath = '/path/to/dev/gemini.vsix';
+ execSyncSpy.mockReturnValue(''); // VSCode is installed
+ // First glob call for bundle returns nothing, second for dev returns path.
+ globSyncSpy.mockReturnValueOnce([]).mockReturnValueOnce([vsixPath]);
+
+ const command = ideCommand(mockConfig);
+ await command?.subCommands?.[1].action(mockContext, '');
+
+ expect(globSyncSpy).toHaveBeenCalledTimes(2);
+ expect(execSyncSpy).toHaveBeenCalledWith(
+ `code --install-extension ${vsixPath} --force`,
+ { stdio: 'pipe' },
+ );
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
+ expect.objectContaining({
+ type: 'info',
+ text: 'VS Code companion extension installed successfully. Restart gemini-cli in a fresh terminal window.',
+ }),
+ expect.any(Number),
+ );
+ });
+
+ it('should show an error if installation fails', async () => {
+ const vsixPath = '/path/to/bundle/gemini.vsix';
+ const errorMessage = 'Installation failed';
+ execSyncSpy
+ .mockReturnValueOnce('') // VSCode is installed check
+ .mockImplementation(() => {
+ // Installation command
+ const error: Error & { stderr?: Buffer } = new Error(
+ 'Command failed',
+ );
+ error.stderr = Buffer.from(errorMessage);
+ throw error;
+ });
+ globSyncSpy.mockReturnValue([vsixPath]);
+
+ const command = ideCommand(mockConfig);
+ await command?.subCommands?.[1].action(mockContext, '');
+
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
+ expect.objectContaining({
+ type: 'error',
+ text: `Failed to install VS Code companion extension.`,
+ }),
+ expect.any(Number),
+ );
+ });
+ });
+});
diff --git a/packages/cli/src/ui/commands/ideCommand.ts b/packages/cli/src/ui/commands/ideCommand.ts
new file mode 100644
index 00000000..0251e619
--- /dev/null
+++ b/packages/cli/src/ui/commands/ideCommand.ts
@@ -0,0 +1,165 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { fileURLToPath } from 'url';
+import {
+ Config,
+ getMCPDiscoveryState,
+ getMCPServerStatus,
+ IDE_SERVER_NAME,
+ MCPDiscoveryState,
+ MCPServerStatus,
+} from '@google/gemini-cli-core';
+import {
+ CommandContext,
+ SlashCommand,
+ SlashCommandActionReturn,
+} from './types.js';
+import * as child_process from 'child_process';
+import * as process from 'process';
+import { glob } from 'glob';
+import * as path from 'path';
+
+const VSCODE_COMMAND = process.platform === 'win32' ? 'code.cmd' : 'code';
+const VSCODE_COMPANION_EXTENSION_FOLDER = 'vscode-ide-companion';
+
+function isVSCodeInstalled(): boolean {
+ try {
+ child_process.execSync(
+ process.platform === 'win32'
+ ? `where.exe ${VSCODE_COMMAND}`
+ : `command -v ${VSCODE_COMMAND}`,
+ { stdio: 'ignore' },
+ );
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+export const ideCommand = (config: Config | null): SlashCommand | null => {
+ if (!config?.getIdeMode()) {
+ return null;
+ }
+
+ return {
+ name: 'ide',
+ description: 'manage IDE integration',
+ subCommands: [
+ {
+ name: 'status',
+ description: 'check status of IDE integration',
+ action: (_context: CommandContext): SlashCommandActionReturn => {
+ const status = getMCPServerStatus(IDE_SERVER_NAME);
+ const discoveryState = getMCPDiscoveryState();
+ switch (status) {
+ case MCPServerStatus.CONNECTED:
+ return {
+ type: 'message',
+ messageType: 'info',
+ content: `🟢 Connected`,
+ };
+ case MCPServerStatus.CONNECTING:
+ return {
+ type: 'message',
+ messageType: 'info',
+ content: `🔄 Initializing...`,
+ };
+ case MCPServerStatus.DISCONNECTED:
+ default:
+ if (discoveryState === MCPDiscoveryState.IN_PROGRESS) {
+ return {
+ type: 'message',
+ messageType: 'info',
+ content: `🔄 Initializing...`,
+ };
+ } else {
+ return {
+ type: 'message',
+ messageType: 'error',
+ content: `🔴 Disconnected`,
+ };
+ }
+ }
+ },
+ },
+ {
+ name: 'install',
+ description: 'install required VS Code companion extension',
+ action: async (context) => {
+ if (!isVSCodeInstalled()) {
+ context.ui.addItem(
+ {
+ type: 'error',
+ text: `VS Code command-line tool "${VSCODE_COMMAND}" not found in your PATH.`,
+ },
+ Date.now(),
+ );
+ return;
+ }
+
+ const bundleDir = path.dirname(fileURLToPath(import.meta.url));
+ // The VSIX file is copied to the bundle directory as part of the build.
+ let vsixFiles = glob.sync(path.join(bundleDir, '*.vsix'));
+ if (vsixFiles.length === 0) {
+ // If the VSIX file is not in the bundle, it might be a dev
+ // environment running with `npm start`. Look for it in the original
+ // package location, relative to the bundle dir.
+ const devPath = path.join(
+ bundleDir,
+ '..',
+ '..',
+ '..',
+ '..',
+ '..',
+ VSCODE_COMPANION_EXTENSION_FOLDER,
+ '*.vsix',
+ );
+ vsixFiles = glob.sync(devPath);
+ }
+ if (vsixFiles.length === 0) {
+ context.ui.addItem(
+ {
+ type: 'error',
+ text: 'Could not find the required VS Code companion extension. Please file a bug via /bug.',
+ },
+ Date.now(),
+ );
+ return;
+ }
+
+ const vsixPath = vsixFiles[0];
+ const command = `${VSCODE_COMMAND} --install-extension ${vsixPath} --force`;
+ context.ui.addItem(
+ {
+ type: 'info',
+ text: `Installing VS Code companion extension...`,
+ },
+ Date.now(),
+ );
+ try {
+ child_process.execSync(command, { stdio: 'pipe' });
+ context.ui.addItem(
+ {
+ type: 'info',
+ text: 'VS Code companion extension installed successfully. Restart gemini-cli in a fresh terminal window.',
+ },
+ Date.now(),
+ );
+ } catch (_error) {
+ context.ui.addItem(
+ {
+ type: 'error',
+ text: `Failed to install VS Code companion extension.`,
+ },
+ Date.now(),
+ );
+ }
+ },
+ },
+ ],
+ };
+};
diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts
index 2d7a8ffd..399a923b 100644
--- a/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts
+++ b/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts
@@ -153,6 +153,7 @@ describe('useSlashCommandProcessor', () => {
getCheckpointingEnabled: vi.fn(() => true),
getBugCommand: vi.fn(() => undefined),
getSessionId: vi.fn(() => 'test-session-id'),
+ getIdeMode: vi.fn(() => false),
} as unknown as Config;
mockCorgiMode = vi.fn();
mockUseSessionStats.mockReturnValue({
@@ -237,7 +238,10 @@ describe('useSlashCommandProcessor', () => {
const mockLoader = async () => [newCommand];
// We create the instance outside the mock implementation.
- const commandServiceInstance = new ActualCommandService(mockLoader);
+ const commandServiceInstance = new ActualCommandService(
+ mockConfig,
+ mockLoader,
+ );
// This mock ensures the hook uses our pre-configured instance.
vi.mocked(CommandService).mockImplementation(
@@ -271,7 +275,10 @@ describe('useSlashCommandProcessor', () => {
});
const newCommand: SlashCommand = { name: 'test', action: mockAction };
const mockLoader = async () => [newCommand];
- const commandServiceInstance = new ActualCommandService(mockLoader);
+ const commandServiceInstance = new ActualCommandService(
+ mockConfig,
+ mockLoader,
+ );
vi.mocked(CommandService).mockImplementation(
() => commandServiceInstance,
);
@@ -301,7 +308,10 @@ describe('useSlashCommandProcessor', () => {
});
const newCommand: SlashCommand = { name: 'test', action: mockAction };
const mockLoader = async () => [newCommand];
- const commandServiceInstance = new ActualCommandService(mockLoader);
+ const commandServiceInstance = new ActualCommandService(
+ mockConfig,
+ mockLoader,
+ );
vi.mocked(CommandService).mockImplementation(
() => commandServiceInstance,
);
@@ -333,7 +343,10 @@ describe('useSlashCommandProcessor', () => {
});
const newCommand: SlashCommand = { name: 'test', action: mockAction };
const mockLoader = async () => [newCommand];
- const commandServiceInstance = new ActualCommandService(mockLoader);
+ const commandServiceInstance = new ActualCommandService(
+ mockConfig,
+ mockLoader,
+ );
vi.mocked(CommandService).mockImplementation(
() => commandServiceInstance,
);
@@ -360,7 +373,10 @@ describe('useSlashCommandProcessor', () => {
const newAuthCommand: SlashCommand = { name: 'auth', action: mockAction };
const mockLoader = async () => [newAuthCommand];
- const commandServiceInstance = new ActualCommandService(mockLoader);
+ const commandServiceInstance = new ActualCommandService(
+ mockConfig,
+ mockLoader,
+ );
vi.mocked(CommandService).mockImplementation(
() => commandServiceInstance,
);
@@ -386,7 +402,10 @@ describe('useSlashCommandProcessor', () => {
});
const newCommand: SlashCommand = { name: 'test', action: mockAction };
const mockLoader = async () => [newCommand];
- const commandServiceInstance = new ActualCommandService(mockLoader);
+ const commandServiceInstance = new ActualCommandService(
+ mockConfig,
+ mockLoader,
+ );
vi.mocked(CommandService).mockImplementation(
() => commandServiceInstance,
);
@@ -414,7 +433,10 @@ describe('useSlashCommandProcessor', () => {
};
const mockLoader = async () => [parentCommand];
- const commandServiceInstance = new ActualCommandService(mockLoader);
+ const commandServiceInstance = new ActualCommandService(
+ mockConfig,
+ mockLoader,
+ );
vi.mocked(CommandService).mockImplementation(
() => commandServiceInstance,
);
diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.ts
index 24758842..c1c65080 100644
--- a/packages/cli/src/ui/hooks/slashCommandProcessor.ts
+++ b/packages/cli/src/ui/hooks/slashCommandProcessor.ts
@@ -182,7 +182,7 @@ export const useSlashCommandProcessor = (
],
);
- const commandService = useMemo(() => new CommandService(), []);
+ const commandService = useMemo(() => new CommandService(config), [config]);
useEffect(() => {
const load = async () => {