mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-01 21:20:44 +00:00
Merge pull request #2822 from qqqys/fix/cli_command
fix(cli): prevent ideCommand failure from breaking all slash commands…
This commit is contained in:
commit
fe4f2567c6
3 changed files with 62 additions and 12 deletions
|
|
@ -207,6 +207,33 @@ describe('BuiltinCommandLoader', () => {
|
|||
expect(modelCmd?.name).toBe('model');
|
||||
});
|
||||
|
||||
it('should still load all other commands when ideCommand() throws', async () => {
|
||||
// Simulate ideCommand() failure (e.g., platform-specific process detection fails)
|
||||
const { ideCommand: ideCommandMock } = await import(
|
||||
'../ui/commands/ideCommand.js'
|
||||
);
|
||||
(ideCommandMock as Mock).mockRejectedValueOnce(
|
||||
new Error('PowerShell not available'),
|
||||
);
|
||||
|
||||
const loader = new BuiltinCommandLoader(mockConfig);
|
||||
const commands = await loader.loadCommands(new AbortController().signal);
|
||||
|
||||
// IDE command should NOT be present
|
||||
const ideCmd = commands.find((c) => c.name === 'ide');
|
||||
expect(ideCmd).toBeUndefined();
|
||||
|
||||
// But all other built-in commands should still be loaded
|
||||
const modelCmd = commands.find((c) => c.name === 'model');
|
||||
expect(modelCmd).toBeDefined();
|
||||
|
||||
const statusCmd = commands.find((c) => c.name === 'status');
|
||||
expect(statusCmd).toBeDefined();
|
||||
|
||||
const mcpCmd = commands.find((c) => c.name === 'mcp');
|
||||
expect(mcpCmd).toBeDefined();
|
||||
});
|
||||
|
||||
it('should always include hooks command regardless of disableAllHooks', async () => {
|
||||
// When disableAllHooks is false
|
||||
const loader1 = new BuiltinCommandLoader(mockConfig);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import { extensionsCommand } from '../ui/commands/extensionsCommand.js';
|
|||
import { helpCommand } from '../ui/commands/helpCommand.js';
|
||||
import { hooksCommand } from '../ui/commands/hooksCommand.js';
|
||||
import { ideCommand } from '../ui/commands/ideCommand.js';
|
||||
import { createDebugLogger } from '@qwen-code/qwen-code-core';
|
||||
import { initCommand } from '../ui/commands/initCommand.js';
|
||||
import { languageCommand } from '../ui/commands/languageCommand.js';
|
||||
import { mcpCommand } from '../ui/commands/mcpCommand.js';
|
||||
|
|
@ -47,6 +48,8 @@ import { vimCommand } from '../ui/commands/vimCommand.js';
|
|||
import { setupGithubCommand } from '../ui/commands/setupGithubCommand.js';
|
||||
import { insightCommand } from '../ui/commands/insightCommand.js';
|
||||
|
||||
const builtinDebugLogger = createDebugLogger('BUILTIN_COMMAND_LOADER');
|
||||
|
||||
/**
|
||||
* Loads the core, hard-coded slash commands that are an integral part
|
||||
* of the Qwen Code application.
|
||||
|
|
@ -62,6 +65,19 @@ export class BuiltinCommandLoader implements ICommandLoader {
|
|||
* @returns A promise that resolves to an array of `SlashCommand` objects.
|
||||
*/
|
||||
async loadCommands(_signal: AbortSignal): Promise<SlashCommand[]> {
|
||||
// Load ideCommand separately with error handling so that a failure
|
||||
// (e.g., platform-specific process detection on Windows) does not
|
||||
// prevent ALL built-in commands from loading.
|
||||
let resolvedIdeCommand: SlashCommand | null = null;
|
||||
try {
|
||||
resolvedIdeCommand = await ideCommand();
|
||||
} catch (error) {
|
||||
builtinDebugLogger.warn(
|
||||
'Failed to load IDE command:',
|
||||
error instanceof Error ? error.message : String(error),
|
||||
);
|
||||
}
|
||||
|
||||
const allDefinitions: Array<SlashCommand | null> = [
|
||||
aboutCommand,
|
||||
agentsCommand,
|
||||
|
|
@ -81,7 +97,7 @@ export class BuiltinCommandLoader implements ICommandLoader {
|
|||
extensionsCommand,
|
||||
helpCommand,
|
||||
hooksCommand,
|
||||
await ideCommand(),
|
||||
resolvedIdeCommand,
|
||||
initCommand,
|
||||
languageCommand,
|
||||
mcpCommand,
|
||||
|
|
|
|||
|
|
@ -333,17 +333,24 @@ export const useSlashCommandProcessor = (
|
|||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
const load = async () => {
|
||||
const loaders = [
|
||||
new McpPromptLoader(config),
|
||||
new BuiltinCommandLoader(config),
|
||||
new BundledSkillLoader(config),
|
||||
new FileCommandLoader(config),
|
||||
];
|
||||
const commandService = await CommandService.create(
|
||||
loaders,
|
||||
controller.signal,
|
||||
);
|
||||
setCommands(commandService.getCommands());
|
||||
try {
|
||||
const loaders = [
|
||||
new McpPromptLoader(config),
|
||||
new BuiltinCommandLoader(config),
|
||||
new BundledSkillLoader(config),
|
||||
new FileCommandLoader(config),
|
||||
];
|
||||
const commandService = await CommandService.create(
|
||||
loaders,
|
||||
controller.signal,
|
||||
);
|
||||
// Avoid overwriting newer results from a subsequent effect run
|
||||
if (!controller.signal.aborted) {
|
||||
setCommands(commandService.getCommands());
|
||||
}
|
||||
} catch (error) {
|
||||
debugLogger.error('Failed to load slash commands:', error);
|
||||
}
|
||||
};
|
||||
|
||||
load();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue