mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
110 lines
3.5 KiB
TypeScript
110 lines
3.5 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import type { PartListUnion } from '@google/genai';
|
|
import { parseSlashCommand } from './utils/commands.js';
|
|
import {
|
|
FatalInputError,
|
|
Logger,
|
|
uiTelemetryService,
|
|
type Config,
|
|
} from '@qwen-code/qwen-code-core';
|
|
import { CommandService } from './services/CommandService.js';
|
|
import { FileCommandLoader } from './services/FileCommandLoader.js';
|
|
import type { CommandContext } from './ui/commands/types.js';
|
|
import { createNonInteractiveUI } from './ui/noninteractive/nonInteractiveUi.js';
|
|
import type { LoadedSettings } from './config/settings.js';
|
|
import type { SessionStatsState } from './ui/contexts/SessionContext.js';
|
|
|
|
/**
|
|
* Processes a slash command in a non-interactive environment.
|
|
*
|
|
* @returns A Promise that resolves to `PartListUnion` if a valid command is
|
|
* found and results in a prompt, or `undefined` otherwise.
|
|
* @throws {FatalInputError} if the command result is not supported in
|
|
* non-interactive mode.
|
|
*/
|
|
export const handleSlashCommand = async (
|
|
rawQuery: string,
|
|
abortController: AbortController,
|
|
config: Config,
|
|
settings: LoadedSettings,
|
|
): Promise<PartListUnion | undefined> => {
|
|
const trimmed = rawQuery.trim();
|
|
if (!trimmed.startsWith('/')) {
|
|
return;
|
|
}
|
|
|
|
// Only custom commands are supported for now.
|
|
const loaders = [new FileCommandLoader(config)];
|
|
const commandService = await CommandService.create(
|
|
loaders,
|
|
abortController.signal,
|
|
);
|
|
const commands = commandService.getCommands();
|
|
|
|
const { commandToExecute, args } = parseSlashCommand(rawQuery, commands);
|
|
|
|
if (commandToExecute) {
|
|
if (commandToExecute.action) {
|
|
// Not used by custom commands but may be in the future.
|
|
const sessionStats: SessionStatsState = {
|
|
sessionId: config?.getSessionId(),
|
|
sessionStartTime: new Date(),
|
|
metrics: uiTelemetryService.getMetrics(),
|
|
lastPromptTokenCount: 0,
|
|
promptCount: 1,
|
|
};
|
|
|
|
const logger = new Logger(config?.getSessionId() || '', config?.storage);
|
|
|
|
const context: CommandContext = {
|
|
services: {
|
|
config,
|
|
settings,
|
|
git: undefined,
|
|
logger,
|
|
},
|
|
ui: createNonInteractiveUI(),
|
|
session: {
|
|
stats: sessionStats,
|
|
sessionShellAllowlist: new Set(),
|
|
},
|
|
invocation: {
|
|
raw: trimmed,
|
|
name: commandToExecute.name,
|
|
args,
|
|
},
|
|
};
|
|
|
|
const result = await commandToExecute.action(context, args);
|
|
|
|
if (result) {
|
|
switch (result.type) {
|
|
case 'submit_prompt':
|
|
return result.content;
|
|
case 'confirm_shell_commands':
|
|
// This result indicates a command attempted to confirm shell commands.
|
|
// However note that currently, ShellTool is excluded in non-interactive
|
|
// mode unless 'YOLO mode' is active, so confirmation actually won't
|
|
// occur because of YOLO mode.
|
|
// This ensures that if a command *does* request confirmation (e.g.
|
|
// in the future with more granular permissions), it's handled appropriately.
|
|
throw new FatalInputError(
|
|
'Exiting due to a confirmation prompt requested by the command.',
|
|
);
|
|
default:
|
|
throw new FatalInputError(
|
|
'Exiting due to command result that is not supported in non-interactive mode.',
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
};
|