1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { useCallback, useMemo } from 'react';
import { type PartListUnion } from '@google/genai';
import { getCommandFromQuery } from '../utils/commandUtils.js';
import { UseHistoryManagerReturn } from './useHistoryManager.js';
import { Config } from '@gemini-code/server'; // Import Config
import { Message, MessageType, HistoryItemWithoutId } from '../types.js'; // Import Message types
import {
createShowMemoryAction,
SHOW_MEMORY_COMMAND_NAME,
} from './useShowMemoryCommand.js';
import { REFRESH_MEMORY_COMMAND_NAME } from './useRefreshMemoryCommand.js'; // Only import name now
import process from 'node:process'; // For process.exit
export interface SlashCommand {
name: string;
altName?: string;
description: string;
action: (value: PartListUnion | string) => void; // Allow string for simpler actions
}
/**
* Hook to define and process slash commands (e.g., /help, /clear).
*/
export const useSlashCommandProcessor = (
config: Config | null, // Add config here
addItem: UseHistoryManagerReturn['addItem'],
clearItems: UseHistoryManagerReturn['clearItems'],
refreshStatic: () => void,
setShowHelp: React.Dispatch<React.SetStateAction<boolean>>,
onDebugMessage: (message: string) => void,
openThemeDialog: () => void,
performMemoryRefresh: () => Promise<void>, // Add performMemoryRefresh prop
) => {
const addMessage = useCallback(
(message: Message) => {
// Convert Message to HistoryItemWithoutId
const historyItemContent: HistoryItemWithoutId = {
type: message.type, // MessageType enum should be compatible with HistoryItemWithoutId string literal types
text: message.content,
};
addItem(historyItemContent, message.timestamp.getTime());
},
[addItem],
);
const showMemoryAction = useCallback(async () => {
const actionFn = createShowMemoryAction(config, addMessage);
await actionFn();
}, [config, addMessage]);
const slashCommands: SlashCommand[] = useMemo(
() => [
{
name: 'help',
altName: '?',
description: 'for help on gemini-code',
action: (_value: PartListUnion | string) => {
onDebugMessage('Opening help.');
setShowHelp(true);
},
},
{
name: 'clear',
description: 'clear the screen',
action: (_value: PartListUnion | string) => {
onDebugMessage('Clearing terminal.');
clearItems();
console.clear();
refreshStatic();
},
},
{
name: 'theme',
description: 'change the theme',
action: (_value) => {
openThemeDialog();
},
},
{
name: REFRESH_MEMORY_COMMAND_NAME.substring(1), // Remove leading '/'
description: 'Reloads instructions from all GEMINI.md files.',
action: performMemoryRefresh, // Use the passed in function
},
{
name: SHOW_MEMORY_COMMAND_NAME.substring(1), // Remove leading '/'
description: 'Displays the current hierarchical memory content.',
action: showMemoryAction,
},
{
name: 'quit',
altName: 'exit',
description: 'exit the cli',
action: (_value: PartListUnion | string) => {
onDebugMessage('Quitting. Good-bye.');
process.exit(0);
},
},
],
[
onDebugMessage,
setShowHelp,
refreshStatic,
openThemeDialog,
clearItems,
performMemoryRefresh, // Add to dependencies
showMemoryAction,
],
);
const handleSlashCommand = useCallback(
(rawQuery: PartListUnion): boolean => {
if (typeof rawQuery !== 'string') {
return false;
}
const trimmed = rawQuery.trim();
const [symbol, test] = getCommandFromQuery(trimmed);
if (symbol !== '/' && symbol !== '?') {
return false;
}
const userMessageTimestamp = Date.now();
// Add user message to history only if it's not a silent command or handled internally
// For now, adding all slash commands to history for transparency.
addItem({ type: MessageType.USER, text: trimmed }, userMessageTimestamp);
for (const cmd of slashCommands) {
if (
test === cmd.name ||
test === cmd.altName ||
(symbol === '?' && cmd.altName === '?') // Special handling for ? as help
) {
cmd.action(trimmed); // Pass the full trimmed command for context if needed
return true;
}
}
addItem(
{ type: MessageType.ERROR, text: `Unknown command: ${trimmed}` },
userMessageTimestamp,
);
return true;
},
[addItem, slashCommands],
);
return { handleSlashCommand, slashCommands };
};
|