summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/hooks/slashCommandProcessor.ts
diff options
context:
space:
mode:
authorRichie Foreman <[email protected]>2025-08-13 16:37:08 -0400
committerGitHub <[email protected]>2025-08-13 20:37:08 +0000
commit2dbd5ecdc80b55cc13c81a0f836ad65ef874e8f8 (patch)
tree38dff03aac43d27f205ae5968a53b3db97023084 /packages/cli/src/ui/hooks/slashCommandProcessor.ts
parent61047173a8f5bc279f480c5ab150d74337c0265a (diff)
chore(cli/slashcommands): Add status enum to SlashCommandEvent telemetry (#6166)
Diffstat (limited to 'packages/cli/src/ui/hooks/slashCommandProcessor.ts')
-rw-r--r--packages/cli/src/ui/hooks/slashCommandProcessor.ts126
1 files changed, 69 insertions, 57 deletions
diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.ts
index 32f55de2..b8799ec3 100644
--- a/packages/cli/src/ui/hooks/slashCommandProcessor.ts
+++ b/packages/cli/src/ui/hooks/slashCommandProcessor.ts
@@ -14,7 +14,8 @@ import {
GitService,
Logger,
logSlashCommand,
- SlashCommandEvent,
+ makeSlashCommandEvent,
+ SlashCommandStatus,
ToolConfirmationOutcome,
} from '@google/gemini-cli-core';
import { useSessionStats } from '../contexts/SessionContext.js';
@@ -235,77 +236,71 @@ export const useSlashCommandProcessor = (
oneTimeShellAllowlist?: Set<string>,
overwriteConfirmed?: boolean,
): Promise<SlashCommandProcessorResult | false> => {
- setIsProcessing(true);
- try {
- if (typeof rawQuery !== 'string') {
- return false;
- }
+ if (typeof rawQuery !== 'string') {
+ return false;
+ }
- const trimmed = rawQuery.trim();
- if (!trimmed.startsWith('/') && !trimmed.startsWith('?')) {
- return false;
- }
+ const trimmed = rawQuery.trim();
+ if (!trimmed.startsWith('/') && !trimmed.startsWith('?')) {
+ return false;
+ }
- const userMessageTimestamp = Date.now();
- addItem(
- { type: MessageType.USER, text: trimmed },
- userMessageTimestamp,
- );
+ setIsProcessing(true);
- const parts = trimmed.substring(1).trim().split(/\s+/);
- const commandPath = parts.filter((p) => p); // The parts of the command, e.g., ['memory', 'add']
+ const userMessageTimestamp = Date.now();
+ addItem({ type: MessageType.USER, text: trimmed }, userMessageTimestamp);
- let currentCommands = commands;
- let commandToExecute: SlashCommand | undefined;
- let pathIndex = 0;
- const canonicalPath: string[] = [];
+ const parts = trimmed.substring(1).trim().split(/\s+/);
+ const commandPath = parts.filter((p) => p); // The parts of the command, e.g., ['memory', 'add']
- for (const part of commandPath) {
- // TODO: For better performance and architectural clarity, this two-pass
- // search could be replaced. A more optimal approach would be to
- // pre-compute a single lookup map in `CommandService.ts` that resolves
- // all name and alias conflicts during the initial loading phase. The
- // processor would then perform a single, fast lookup on that map.
+ let currentCommands = commands;
+ let commandToExecute: SlashCommand | undefined;
+ let pathIndex = 0;
+ let hasError = false;
+ const canonicalPath: string[] = [];
- // First pass: check for an exact match on the primary command name.
- let foundCommand = currentCommands.find((cmd) => cmd.name === part);
+ for (const part of commandPath) {
+ // TODO: For better performance and architectural clarity, this two-pass
+ // search could be replaced. A more optimal approach would be to
+ // pre-compute a single lookup map in `CommandService.ts` that resolves
+ // all name and alias conflicts during the initial loading phase. The
+ // processor would then perform a single, fast lookup on that map.
- // Second pass: if no primary name matches, check for an alias.
- if (!foundCommand) {
- foundCommand = currentCommands.find((cmd) =>
- cmd.altNames?.includes(part),
- );
- }
+ // First pass: check for an exact match on the primary command name.
+ let foundCommand = currentCommands.find((cmd) => cmd.name === part);
- if (foundCommand) {
- commandToExecute = foundCommand;
- canonicalPath.push(foundCommand.name);
- pathIndex++;
- if (foundCommand.subCommands) {
- currentCommands = foundCommand.subCommands;
- } else {
- break;
- }
+ // Second pass: if no primary name matches, check for an alias.
+ if (!foundCommand) {
+ foundCommand = currentCommands.find((cmd) =>
+ cmd.altNames?.includes(part),
+ );
+ }
+
+ if (foundCommand) {
+ commandToExecute = foundCommand;
+ canonicalPath.push(foundCommand.name);
+ pathIndex++;
+ if (foundCommand.subCommands) {
+ currentCommands = foundCommand.subCommands;
} else {
break;
}
+ } else {
+ break;
}
+ }
+ const resolvedCommandPath = canonicalPath;
+ const subcommand =
+ resolvedCommandPath.length > 1
+ ? resolvedCommandPath.slice(1).join(' ')
+ : undefined;
+
+ try {
if (commandToExecute) {
const args = parts.slice(pathIndex).join(' ');
if (commandToExecute.action) {
- if (config) {
- const resolvedCommandPath = canonicalPath;
- const event = new SlashCommandEvent(
- resolvedCommandPath[0],
- resolvedCommandPath.length > 1
- ? resolvedCommandPath.slice(1).join(' ')
- : undefined,
- );
- logSlashCommand(config, event);
- }
-
const fullCommandContext: CommandContext = {
...commandContext,
invocation: {
@@ -327,7 +322,6 @@ export const useSlashCommandProcessor = (
]),
};
}
-
const result = await commandToExecute.action(
fullCommandContext,
args,
@@ -500,8 +494,18 @@ export const useSlashCommandProcessor = (
content: `Unknown command: ${trimmed}`,
timestamp: new Date(),
});
+
return { type: 'handled' };
- } catch (e) {
+ } catch (e: unknown) {
+ hasError = true;
+ if (config) {
+ const event = makeSlashCommandEvent({
+ command: resolvedCommandPath[0],
+ subcommand,
+ status: SlashCommandStatus.ERROR,
+ });
+ logSlashCommand(config, event);
+ }
addItem(
{
type: MessageType.ERROR,
@@ -511,6 +515,14 @@ export const useSlashCommandProcessor = (
);
return { type: 'handled' };
} finally {
+ if (config && resolvedCommandPath[0] && !hasError) {
+ const event = makeSlashCommandEvent({
+ command: resolvedCommandPath[0],
+ subcommand,
+ status: SlashCommandStatus.SUCCESS,
+ });
+ logSlashCommand(config, event);
+ }
setIsProcessing(false);
}
},