@google/gemini-cli 0.1.12-nightly.250714.b018e2d3 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/dist/google-gemini-cli-0.1.12.tgz +0 -0
- package/dist/package.json +2 -3
- package/dist/src/acp/acp.d.ts +208 -0
- package/dist/src/acp/acp.js +193 -0
- package/dist/src/acp/acp.js.map +1 -0
- package/dist/src/acp/acpPeer.d.ts +8 -0
- package/dist/src/acp/acpPeer.js +537 -0
- package/dist/src/acp/acpPeer.js.map +1 -0
- package/dist/src/config/config.d.ts +3 -0
- package/dist/src/config/config.js +84 -10
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/extension.d.ts +2 -2
- package/dist/src/config/extension.js +21 -16
- package/dist/src/config/extension.js.map +1 -1
- package/dist/src/config/settings.d.ts +8 -0
- package/dist/src/config/settings.js.map +1 -1
- package/dist/src/gemini.js +6 -2
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +1 -1
- package/dist/src/generated/git-commit.js +1 -1
- package/dist/src/services/CommandService.d.ts +3 -1
- package/dist/src/services/CommandService.js +45 -8
- package/dist/src/services/CommandService.js.map +1 -1
- package/dist/src/ui/App.js +54 -50
- package/dist/src/ui/App.js.map +1 -1
- package/dist/src/ui/commands/aboutCommand.d.ts +7 -0
- package/dist/src/ui/commands/aboutCommand.js +37 -0
- package/dist/src/ui/commands/aboutCommand.js.map +1 -0
- package/dist/src/ui/commands/authCommand.d.ts +7 -0
- package/dist/src/ui/commands/authCommand.js +14 -0
- package/dist/src/ui/commands/authCommand.js.map +1 -0
- package/dist/src/ui/commands/bugCommand.d.ts +7 -0
- package/dist/src/ui/commands/bugCommand.js +61 -0
- package/dist/src/ui/commands/bugCommand.js.map +1 -0
- package/dist/src/ui/commands/chatCommand.d.ts +7 -0
- package/dist/src/ui/commands/chatCommand.js +169 -0
- package/dist/src/ui/commands/chatCommand.js.map +1 -0
- package/dist/src/ui/commands/clearCommand.js +12 -2
- package/dist/src/ui/commands/clearCommand.js.map +1 -1
- package/dist/src/ui/commands/compressCommand.d.ts +7 -0
- package/dist/src/ui/commands/compressCommand.js +62 -0
- package/dist/src/ui/commands/compressCommand.js.map +1 -0
- package/dist/src/ui/commands/corgiCommand.d.ts +7 -0
- package/dist/src/ui/commands/corgiCommand.js +13 -0
- package/dist/src/ui/commands/corgiCommand.js.map +1 -0
- package/dist/src/ui/commands/docsCommand.d.ts +7 -0
- package/dist/src/ui/commands/docsCommand.js +29 -0
- package/dist/src/ui/commands/docsCommand.js.map +1 -0
- package/dist/src/ui/commands/editorCommand.d.ts +7 -0
- package/dist/src/ui/commands/editorCommand.js +14 -0
- package/dist/src/ui/commands/editorCommand.js.map +1 -0
- package/dist/src/ui/commands/extensionsCommand.d.ts +7 -0
- package/dist/src/ui/commands/extensionsCommand.js +29 -0
- package/dist/src/ui/commands/extensionsCommand.js.map +1 -0
- package/dist/src/ui/commands/ideCommand.d.ts +8 -0
- package/dist/src/ui/commands/ideCommand.js +123 -0
- package/dist/src/ui/commands/ideCommand.js.map +1 -0
- package/dist/src/ui/commands/mcpCommand.d.ts +7 -0
- package/dist/src/ui/commands/mcpCommand.js +204 -0
- package/dist/src/ui/commands/mcpCommand.js.map +1 -0
- package/dist/src/ui/commands/privacyCommand.d.ts +7 -0
- package/dist/src/ui/commands/privacyCommand.js +14 -0
- package/dist/src/ui/commands/privacyCommand.js.map +1 -0
- package/dist/src/ui/commands/quitCommand.d.ts +7 -0
- package/dist/src/ui/commands/quitCommand.js +32 -0
- package/dist/src/ui/commands/quitCommand.js.map +1 -0
- package/dist/src/ui/commands/restoreCommand.d.ts +8 -0
- package/dist/src/ui/commands/restoreCommand.js +126 -0
- package/dist/src/ui/commands/restoreCommand.js.map +1 -0
- package/dist/src/ui/commands/statsCommand.d.ts +7 -0
- package/dist/src/ui/commands/statsCommand.js +50 -0
- package/dist/src/ui/commands/statsCommand.js.map +1 -0
- package/dist/src/ui/commands/toolsCommand.d.ts +7 -0
- package/dist/src/ui/commands/toolsCommand.js +54 -0
- package/dist/src/ui/commands/toolsCommand.js.map +1 -0
- package/dist/src/ui/commands/types.d.ts +36 -2
- package/dist/src/ui/components/ContextSummaryDisplay.d.ts +6 -1
- package/dist/src/ui/components/ContextSummaryDisplay.js +48 -21
- package/dist/src/ui/components/ContextSummaryDisplay.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.js +109 -51
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/ThemeDialog.js +25 -24
- package/dist/src/ui/components/ThemeDialog.js.map +1 -1
- package/dist/src/ui/components/messages/ToolGroupMessage.js +3 -1
- package/dist/src/ui/components/messages/ToolGroupMessage.js.map +1 -1
- package/dist/src/ui/components/shared/MaxSizedBox.js +69 -2
- package/dist/src/ui/components/shared/MaxSizedBox.js.map +1 -1
- package/dist/src/ui/components/shared/RadioButtonSelect.d.ts +3 -1
- package/dist/src/ui/components/shared/RadioButtonSelect.js +60 -10
- package/dist/src/ui/components/shared/RadioButtonSelect.js.map +1 -1
- package/dist/src/ui/components/shared/text-buffer.js +1 -1
- package/dist/src/ui/components/shared/text-buffer.js.map +1 -1
- package/dist/src/ui/constants.d.ts +1 -0
- package/dist/src/ui/constants.js +1 -0
- package/dist/src/ui/constants.js.map +1 -1
- package/dist/src/ui/hooks/shellCommandProcessor.d.ts +1 -1
- package/dist/src/ui/hooks/shellCommandProcessor.js +35 -11
- package/dist/src/ui/hooks/shellCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.d.ts +2 -9
- package/dist/src/ui/hooks/slashCommandProcessor.js +42 -843
- package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/useAuthCommand.js +3 -2
- package/dist/src/ui/hooks/useAuthCommand.js.map +1 -1
- package/dist/src/ui/hooks/useCompletion.d.ts +1 -0
- package/dist/src/ui/hooks/useCompletion.js +25 -3
- package/dist/src/ui/hooks/useCompletion.js.map +1 -1
- package/dist/src/ui/hooks/useFocus.d.ts +6 -0
- package/dist/src/ui/hooks/useFocus.js +41 -0
- package/dist/src/ui/hooks/useFocus.js.map +1 -0
- package/dist/src/ui/hooks/useGeminiStream.js +17 -0
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/usePrivacySettings.js +5 -5
- package/dist/src/ui/hooks/usePrivacySettings.js.map +1 -1
- package/dist/src/ui/themes/ansi-light.js +1 -1
- package/dist/src/ui/themes/ansi-light.js.map +1 -1
- package/dist/src/ui/themes/googlecode.js +1 -1
- package/dist/src/ui/themes/googlecode.js.map +1 -1
- package/dist/src/ui/themes/xcode.js +1 -1
- package/dist/src/ui/themes/xcode.js.map +1 -1
- package/dist/src/ui/utils/updateCheck.js +4 -0
- package/dist/src/ui/utils/updateCheck.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -4
|
@@ -4,22 +4,16 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
import { useCallback, useMemo, useEffect, useState } from 'react';
|
|
7
|
-
import open from 'open';
|
|
8
7
|
import process from 'node:process';
|
|
9
8
|
import { useStateAndRef } from './useStateAndRef.js';
|
|
10
|
-
import { GitService, Logger
|
|
9
|
+
import { GitService, Logger } from '@google/gemini-cli-core';
|
|
11
10
|
import { useSessionStats } from '../contexts/SessionContext.js';
|
|
12
11
|
import { MessageType, } from '../types.js';
|
|
13
|
-
import { promises as fs } from 'fs';
|
|
14
|
-
import path from 'path';
|
|
15
|
-
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
|
|
16
|
-
import { formatDuration, formatMemoryUsage } from '../utils/formatters.js';
|
|
17
|
-
import { getCliVersion } from '../../utils/version.js';
|
|
18
12
|
import { CommandService } from '../../services/CommandService.js';
|
|
19
13
|
/**
|
|
20
14
|
* Hook to define and process slash commands (e.g., /help, /clear).
|
|
21
15
|
*/
|
|
22
|
-
export const useSlashCommandProcessor = (config, settings,
|
|
16
|
+
export const useSlashCommandProcessor = (config, settings, addItem, clearItems, loadHistory, refreshStatic, setShowHelp, onDebugMessage, openThemeDialog, openAuthDialog, openEditorDialog, toggleCorgiMode, setQuittingMessages, openPrivacyNotice) => {
|
|
23
17
|
const session = useSessionStats();
|
|
24
18
|
const [commands, setCommands] = useState([]);
|
|
25
19
|
const gitService = useMemo(() => {
|
|
@@ -106,7 +100,11 @@ export const useSlashCommandProcessor = (config, settings, history, addItem, cle
|
|
|
106
100
|
console.clear();
|
|
107
101
|
refreshStatic();
|
|
108
102
|
},
|
|
103
|
+
loadHistory,
|
|
109
104
|
setDebugMessage: onDebugMessage,
|
|
105
|
+
pendingItem: pendingCompressionItemRef.current,
|
|
106
|
+
setPendingItem: setPendingCompressionItem,
|
|
107
|
+
toggleCorgiMode,
|
|
110
108
|
},
|
|
111
109
|
session: {
|
|
112
110
|
stats: session.stats,
|
|
@@ -116,13 +114,17 @@ export const useSlashCommandProcessor = (config, settings, history, addItem, cle
|
|
|
116
114
|
settings,
|
|
117
115
|
gitService,
|
|
118
116
|
logger,
|
|
117
|
+
loadHistory,
|
|
119
118
|
addItem,
|
|
120
119
|
clearItems,
|
|
121
120
|
refreshStatic,
|
|
122
121
|
session.stats,
|
|
123
122
|
onDebugMessage,
|
|
123
|
+
pendingCompressionItemRef,
|
|
124
|
+
setPendingCompressionItem,
|
|
125
|
+
toggleCorgiMode,
|
|
124
126
|
]);
|
|
125
|
-
const commandService = useMemo(() => new CommandService(), []);
|
|
127
|
+
const commandService = useMemo(() => new CommandService(config), [config]);
|
|
126
128
|
useEffect(() => {
|
|
127
129
|
const load = async () => {
|
|
128
130
|
await commandService.loadCommands();
|
|
@@ -130,791 +132,6 @@ export const useSlashCommandProcessor = (config, settings, history, addItem, cle
|
|
|
130
132
|
};
|
|
131
133
|
load();
|
|
132
134
|
}, [commandService]);
|
|
133
|
-
const savedChatTags = useCallback(async () => {
|
|
134
|
-
const geminiDir = config?.getProjectTempDir();
|
|
135
|
-
if (!geminiDir) {
|
|
136
|
-
return [];
|
|
137
|
-
}
|
|
138
|
-
try {
|
|
139
|
-
const files = await fs.readdir(geminiDir);
|
|
140
|
-
return files
|
|
141
|
-
.filter((file) => file.startsWith('checkpoint-') && file.endsWith('.json'))
|
|
142
|
-
.map((file) => file.replace('checkpoint-', '').replace('.json', ''));
|
|
143
|
-
}
|
|
144
|
-
catch (_err) {
|
|
145
|
-
return [];
|
|
146
|
-
}
|
|
147
|
-
}, [config]);
|
|
148
|
-
// Define legacy commands
|
|
149
|
-
// This list contains all commands that have NOT YET been migrated to the
|
|
150
|
-
// new system. As commands are migrated, they are removed from this list.
|
|
151
|
-
const legacyCommands = useMemo(() => {
|
|
152
|
-
const commands = [
|
|
153
|
-
// `/help` and `/clear` have been migrated and REMOVED from this list.
|
|
154
|
-
{
|
|
155
|
-
name: 'docs',
|
|
156
|
-
description: 'open full Gemini CLI documentation in your browser',
|
|
157
|
-
action: async (_mainCommand, _subCommand, _args) => {
|
|
158
|
-
const docsUrl = 'https://goo.gle/gemini-cli-docs';
|
|
159
|
-
if (process.env.SANDBOX && process.env.SANDBOX !== 'sandbox-exec') {
|
|
160
|
-
addMessage({
|
|
161
|
-
type: MessageType.INFO,
|
|
162
|
-
content: `Please open the following URL in your browser to view the documentation:\n${docsUrl}`,
|
|
163
|
-
timestamp: new Date(),
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
addMessage({
|
|
168
|
-
type: MessageType.INFO,
|
|
169
|
-
content: `Opening documentation in your browser: ${docsUrl}`,
|
|
170
|
-
timestamp: new Date(),
|
|
171
|
-
});
|
|
172
|
-
await open(docsUrl);
|
|
173
|
-
}
|
|
174
|
-
},
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
name: 'auth',
|
|
178
|
-
description: 'change the auth method',
|
|
179
|
-
action: (_mainCommand, _subCommand, _args) => openAuthDialog(),
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
name: 'editor',
|
|
183
|
-
description: 'set external editor preference',
|
|
184
|
-
action: (_mainCommand, _subCommand, _args) => openEditorDialog(),
|
|
185
|
-
},
|
|
186
|
-
{
|
|
187
|
-
name: 'privacy',
|
|
188
|
-
description: 'display the privacy notice',
|
|
189
|
-
action: (_mainCommand, _subCommand, _args) => openPrivacyNotice(),
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
name: 'stats',
|
|
193
|
-
altName: 'usage',
|
|
194
|
-
description: 'check session stats. Usage: /stats [model|tools]',
|
|
195
|
-
action: (_mainCommand, subCommand, _args) => {
|
|
196
|
-
if (subCommand === 'model') {
|
|
197
|
-
addMessage({
|
|
198
|
-
type: MessageType.MODEL_STATS,
|
|
199
|
-
timestamp: new Date(),
|
|
200
|
-
});
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
else if (subCommand === 'tools') {
|
|
204
|
-
addMessage({
|
|
205
|
-
type: MessageType.TOOL_STATS,
|
|
206
|
-
timestamp: new Date(),
|
|
207
|
-
});
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
const now = new Date();
|
|
211
|
-
const { sessionStartTime } = session.stats;
|
|
212
|
-
const wallDuration = now.getTime() - sessionStartTime.getTime();
|
|
213
|
-
addMessage({
|
|
214
|
-
type: MessageType.STATS,
|
|
215
|
-
duration: formatDuration(wallDuration),
|
|
216
|
-
timestamp: new Date(),
|
|
217
|
-
});
|
|
218
|
-
},
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
name: 'mcp',
|
|
222
|
-
description: 'list configured MCP servers and tools',
|
|
223
|
-
action: async (_mainCommand, _subCommand, _args) => {
|
|
224
|
-
// Check if the _subCommand includes a specific flag to control description visibility
|
|
225
|
-
let useShowDescriptions = showToolDescriptions;
|
|
226
|
-
if (_subCommand === 'desc' || _subCommand === 'descriptions') {
|
|
227
|
-
useShowDescriptions = true;
|
|
228
|
-
}
|
|
229
|
-
else if (_subCommand === 'nodesc' ||
|
|
230
|
-
_subCommand === 'nodescriptions') {
|
|
231
|
-
useShowDescriptions = false;
|
|
232
|
-
}
|
|
233
|
-
else if (_args === 'desc' || _args === 'descriptions') {
|
|
234
|
-
useShowDescriptions = true;
|
|
235
|
-
}
|
|
236
|
-
else if (_args === 'nodesc' || _args === 'nodescriptions') {
|
|
237
|
-
useShowDescriptions = false;
|
|
238
|
-
}
|
|
239
|
-
// Check if the _subCommand includes a specific flag to show detailed tool schema
|
|
240
|
-
let useShowSchema = false;
|
|
241
|
-
if (_subCommand === 'schema' || _args === 'schema') {
|
|
242
|
-
useShowSchema = true;
|
|
243
|
-
}
|
|
244
|
-
const toolRegistry = await config?.getToolRegistry();
|
|
245
|
-
if (!toolRegistry) {
|
|
246
|
-
addMessage({
|
|
247
|
-
type: MessageType.ERROR,
|
|
248
|
-
content: 'Could not retrieve tool registry.',
|
|
249
|
-
timestamp: new Date(),
|
|
250
|
-
});
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
const mcpServers = config?.getMcpServers() || {};
|
|
254
|
-
const serverNames = Object.keys(mcpServers);
|
|
255
|
-
if (serverNames.length === 0) {
|
|
256
|
-
const docsUrl = 'https://goo.gle/gemini-cli-docs-mcp';
|
|
257
|
-
if (process.env.SANDBOX && process.env.SANDBOX !== 'sandbox-exec') {
|
|
258
|
-
addMessage({
|
|
259
|
-
type: MessageType.INFO,
|
|
260
|
-
content: `No MCP servers configured. Please open the following URL in your browser to view documentation:\n${docsUrl}`,
|
|
261
|
-
timestamp: new Date(),
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
else {
|
|
265
|
-
addMessage({
|
|
266
|
-
type: MessageType.INFO,
|
|
267
|
-
content: `No MCP servers configured. Opening documentation in your browser: ${docsUrl}`,
|
|
268
|
-
timestamp: new Date(),
|
|
269
|
-
});
|
|
270
|
-
await open(docsUrl);
|
|
271
|
-
}
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
// Check if any servers are still connecting
|
|
275
|
-
const connectingServers = serverNames.filter((name) => getMCPServerStatus(name) === MCPServerStatus.CONNECTING);
|
|
276
|
-
const discoveryState = getMCPDiscoveryState();
|
|
277
|
-
let message = '';
|
|
278
|
-
// Add overall discovery status message if needed
|
|
279
|
-
if (discoveryState === MCPDiscoveryState.IN_PROGRESS ||
|
|
280
|
-
connectingServers.length > 0) {
|
|
281
|
-
message += `\u001b[33m⏳ MCP servers are starting up (${connectingServers.length} initializing)...\u001b[0m\n`;
|
|
282
|
-
message += `\u001b[90mNote: First startup may take longer. Tool availability will update automatically.\u001b[0m\n\n`;
|
|
283
|
-
}
|
|
284
|
-
message += 'Configured MCP servers:\n\n';
|
|
285
|
-
for (const serverName of serverNames) {
|
|
286
|
-
const serverTools = toolRegistry.getToolsByServer(serverName);
|
|
287
|
-
const status = getMCPServerStatus(serverName);
|
|
288
|
-
// Add status indicator with descriptive text
|
|
289
|
-
let statusIndicator = '';
|
|
290
|
-
let statusText = '';
|
|
291
|
-
switch (status) {
|
|
292
|
-
case MCPServerStatus.CONNECTED:
|
|
293
|
-
statusIndicator = '🟢';
|
|
294
|
-
statusText = 'Ready';
|
|
295
|
-
break;
|
|
296
|
-
case MCPServerStatus.CONNECTING:
|
|
297
|
-
statusIndicator = '🔄';
|
|
298
|
-
statusText = 'Starting... (first startup may take longer)';
|
|
299
|
-
break;
|
|
300
|
-
case MCPServerStatus.DISCONNECTED:
|
|
301
|
-
default:
|
|
302
|
-
statusIndicator = '🔴';
|
|
303
|
-
statusText = 'Disconnected';
|
|
304
|
-
break;
|
|
305
|
-
}
|
|
306
|
-
// Get server description if available
|
|
307
|
-
const server = mcpServers[serverName];
|
|
308
|
-
// Format server header with bold formatting and status
|
|
309
|
-
message += `${statusIndicator} \u001b[1m${serverName}\u001b[0m - ${statusText}`;
|
|
310
|
-
// Add tool count with conditional messaging
|
|
311
|
-
if (status === MCPServerStatus.CONNECTED) {
|
|
312
|
-
message += ` (${serverTools.length} tools)`;
|
|
313
|
-
}
|
|
314
|
-
else if (status === MCPServerStatus.CONNECTING) {
|
|
315
|
-
message += ` (tools will appear when ready)`;
|
|
316
|
-
}
|
|
317
|
-
else {
|
|
318
|
-
message += ` (${serverTools.length} tools cached)`;
|
|
319
|
-
}
|
|
320
|
-
// Add server description with proper handling of multi-line descriptions
|
|
321
|
-
if ((useShowDescriptions || useShowSchema) && server?.description) {
|
|
322
|
-
const greenColor = '\u001b[32m';
|
|
323
|
-
const resetColor = '\u001b[0m';
|
|
324
|
-
const descLines = server.description.trim().split('\n');
|
|
325
|
-
if (descLines) {
|
|
326
|
-
message += ':\n';
|
|
327
|
-
for (const descLine of descLines) {
|
|
328
|
-
message += ` ${greenColor}${descLine}${resetColor}\n`;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
else {
|
|
332
|
-
message += '\n';
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
else {
|
|
336
|
-
message += '\n';
|
|
337
|
-
}
|
|
338
|
-
// Reset formatting after server entry
|
|
339
|
-
message += '\u001b[0m';
|
|
340
|
-
if (serverTools.length > 0) {
|
|
341
|
-
serverTools.forEach((tool) => {
|
|
342
|
-
if ((useShowDescriptions || useShowSchema) &&
|
|
343
|
-
tool.description) {
|
|
344
|
-
// Format tool name in cyan using simple ANSI cyan color
|
|
345
|
-
message += ` - \u001b[36m${tool.name}\u001b[0m`;
|
|
346
|
-
// Apply green color to the description text
|
|
347
|
-
const greenColor = '\u001b[32m';
|
|
348
|
-
const resetColor = '\u001b[0m';
|
|
349
|
-
// Handle multi-line descriptions by properly indenting and preserving formatting
|
|
350
|
-
const descLines = tool.description.trim().split('\n');
|
|
351
|
-
if (descLines) {
|
|
352
|
-
message += ':\n';
|
|
353
|
-
for (const descLine of descLines) {
|
|
354
|
-
message += ` ${greenColor}${descLine}${resetColor}\n`;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
else {
|
|
358
|
-
message += '\n';
|
|
359
|
-
}
|
|
360
|
-
// Reset is handled inline with each line now
|
|
361
|
-
}
|
|
362
|
-
else {
|
|
363
|
-
// Use cyan color for the tool name even when not showing descriptions
|
|
364
|
-
message += ` - \u001b[36m${tool.name}\u001b[0m\n`;
|
|
365
|
-
}
|
|
366
|
-
if (useShowSchema) {
|
|
367
|
-
// Prefix the parameters in cyan
|
|
368
|
-
message += ` \u001b[36mParameters:\u001b[0m\n`;
|
|
369
|
-
// Apply green color to the parameter text
|
|
370
|
-
const greenColor = '\u001b[32m';
|
|
371
|
-
const resetColor = '\u001b[0m';
|
|
372
|
-
const paramsLines = JSON.stringify(tool.schema.parameters, null, 2)
|
|
373
|
-
.trim()
|
|
374
|
-
.split('\n');
|
|
375
|
-
if (paramsLines) {
|
|
376
|
-
for (const paramsLine of paramsLines) {
|
|
377
|
-
message += ` ${greenColor}${paramsLine}${resetColor}\n`;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
else {
|
|
384
|
-
message += ' No tools available\n';
|
|
385
|
-
}
|
|
386
|
-
message += '\n';
|
|
387
|
-
}
|
|
388
|
-
// Make sure to reset any ANSI formatting at the end to prevent it from affecting the terminal
|
|
389
|
-
message += '\u001b[0m';
|
|
390
|
-
addMessage({
|
|
391
|
-
type: MessageType.INFO,
|
|
392
|
-
content: message,
|
|
393
|
-
timestamp: new Date(),
|
|
394
|
-
});
|
|
395
|
-
},
|
|
396
|
-
},
|
|
397
|
-
{
|
|
398
|
-
name: 'extensions',
|
|
399
|
-
description: 'list active extensions',
|
|
400
|
-
action: async () => {
|
|
401
|
-
const activeExtensions = config?.getActiveExtensions();
|
|
402
|
-
if (!activeExtensions || activeExtensions.length === 0) {
|
|
403
|
-
addMessage({
|
|
404
|
-
type: MessageType.INFO,
|
|
405
|
-
content: 'No active extensions.',
|
|
406
|
-
timestamp: new Date(),
|
|
407
|
-
});
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
let message = 'Active extensions:\n\n';
|
|
411
|
-
for (const ext of activeExtensions) {
|
|
412
|
-
message += ` - \u001b[36m${ext.name} (v${ext.version})\u001b[0m\n`;
|
|
413
|
-
}
|
|
414
|
-
// Make sure to reset any ANSI formatting at the end to prevent it from affecting the terminal
|
|
415
|
-
message += '\u001b[0m';
|
|
416
|
-
addMessage({
|
|
417
|
-
type: MessageType.INFO,
|
|
418
|
-
content: message,
|
|
419
|
-
timestamp: new Date(),
|
|
420
|
-
});
|
|
421
|
-
},
|
|
422
|
-
},
|
|
423
|
-
{
|
|
424
|
-
name: 'tools',
|
|
425
|
-
description: 'list available Gemini CLI tools',
|
|
426
|
-
action: async (_mainCommand, _subCommand, _args) => {
|
|
427
|
-
// Check if the _subCommand includes a specific flag to control description visibility
|
|
428
|
-
let useShowDescriptions = showToolDescriptions;
|
|
429
|
-
if (_subCommand === 'desc' || _subCommand === 'descriptions') {
|
|
430
|
-
useShowDescriptions = true;
|
|
431
|
-
}
|
|
432
|
-
else if (_subCommand === 'nodesc' ||
|
|
433
|
-
_subCommand === 'nodescriptions') {
|
|
434
|
-
useShowDescriptions = false;
|
|
435
|
-
}
|
|
436
|
-
else if (_args === 'desc' || _args === 'descriptions') {
|
|
437
|
-
useShowDescriptions = true;
|
|
438
|
-
}
|
|
439
|
-
else if (_args === 'nodesc' || _args === 'nodescriptions') {
|
|
440
|
-
useShowDescriptions = false;
|
|
441
|
-
}
|
|
442
|
-
const toolRegistry = await config?.getToolRegistry();
|
|
443
|
-
const tools = toolRegistry?.getAllTools();
|
|
444
|
-
if (!tools) {
|
|
445
|
-
addMessage({
|
|
446
|
-
type: MessageType.ERROR,
|
|
447
|
-
content: 'Could not retrieve tools.',
|
|
448
|
-
timestamp: new Date(),
|
|
449
|
-
});
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
// Filter out MCP tools by checking if they have a serverName property
|
|
453
|
-
const geminiTools = tools.filter((tool) => !('serverName' in tool));
|
|
454
|
-
let message = 'Available Gemini CLI tools:\n\n';
|
|
455
|
-
if (geminiTools.length > 0) {
|
|
456
|
-
geminiTools.forEach((tool) => {
|
|
457
|
-
if (useShowDescriptions && tool.description) {
|
|
458
|
-
// Format tool name in cyan using simple ANSI cyan color
|
|
459
|
-
message += ` - \u001b[36m${tool.displayName} (${tool.name})\u001b[0m:\n`;
|
|
460
|
-
// Apply green color to the description text
|
|
461
|
-
const greenColor = '\u001b[32m';
|
|
462
|
-
const resetColor = '\u001b[0m';
|
|
463
|
-
// Handle multi-line descriptions by properly indenting and preserving formatting
|
|
464
|
-
const descLines = tool.description.trim().split('\n');
|
|
465
|
-
// If there are multiple lines, add proper indentation for each line
|
|
466
|
-
if (descLines) {
|
|
467
|
-
for (const descLine of descLines) {
|
|
468
|
-
message += ` ${greenColor}${descLine}${resetColor}\n`;
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
else {
|
|
473
|
-
// Use cyan color for the tool name even when not showing descriptions
|
|
474
|
-
message += ` - \u001b[36m${tool.displayName}\u001b[0m\n`;
|
|
475
|
-
}
|
|
476
|
-
});
|
|
477
|
-
}
|
|
478
|
-
else {
|
|
479
|
-
message += ' No tools available\n';
|
|
480
|
-
}
|
|
481
|
-
message += '\n';
|
|
482
|
-
// Make sure to reset any ANSI formatting at the end to prevent it from affecting the terminal
|
|
483
|
-
message += '\u001b[0m';
|
|
484
|
-
addMessage({
|
|
485
|
-
type: MessageType.INFO,
|
|
486
|
-
content: message,
|
|
487
|
-
timestamp: new Date(),
|
|
488
|
-
});
|
|
489
|
-
},
|
|
490
|
-
},
|
|
491
|
-
{
|
|
492
|
-
name: 'corgi',
|
|
493
|
-
action: (_mainCommand, _subCommand, _args) => {
|
|
494
|
-
toggleCorgiMode();
|
|
495
|
-
},
|
|
496
|
-
},
|
|
497
|
-
{
|
|
498
|
-
name: 'about',
|
|
499
|
-
description: 'show version info',
|
|
500
|
-
action: async (_mainCommand, _subCommand, _args) => {
|
|
501
|
-
const osVersion = process.platform;
|
|
502
|
-
let sandboxEnv = 'no sandbox';
|
|
503
|
-
if (process.env.SANDBOX && process.env.SANDBOX !== 'sandbox-exec') {
|
|
504
|
-
sandboxEnv = process.env.SANDBOX;
|
|
505
|
-
}
|
|
506
|
-
else if (process.env.SANDBOX === 'sandbox-exec') {
|
|
507
|
-
sandboxEnv = `sandbox-exec (${process.env.SEATBELT_PROFILE || 'unknown'})`;
|
|
508
|
-
}
|
|
509
|
-
const modelVersion = config?.getModel() || 'Unknown';
|
|
510
|
-
const cliVersion = await getCliVersion();
|
|
511
|
-
const selectedAuthType = settings.merged.selectedAuthType || '';
|
|
512
|
-
const gcpProject = process.env.GOOGLE_CLOUD_PROJECT || '';
|
|
513
|
-
addMessage({
|
|
514
|
-
type: MessageType.ABOUT,
|
|
515
|
-
timestamp: new Date(),
|
|
516
|
-
cliVersion,
|
|
517
|
-
osVersion,
|
|
518
|
-
sandboxEnv,
|
|
519
|
-
modelVersion,
|
|
520
|
-
selectedAuthType,
|
|
521
|
-
gcpProject,
|
|
522
|
-
});
|
|
523
|
-
},
|
|
524
|
-
},
|
|
525
|
-
{
|
|
526
|
-
name: 'bug',
|
|
527
|
-
description: 'submit a bug report',
|
|
528
|
-
action: async (_mainCommand, _subCommand, args) => {
|
|
529
|
-
let bugDescription = _subCommand || '';
|
|
530
|
-
if (args) {
|
|
531
|
-
bugDescription += ` ${args}`;
|
|
532
|
-
}
|
|
533
|
-
bugDescription = bugDescription.trim();
|
|
534
|
-
const osVersion = `${process.platform} ${process.version}`;
|
|
535
|
-
let sandboxEnv = 'no sandbox';
|
|
536
|
-
if (process.env.SANDBOX && process.env.SANDBOX !== 'sandbox-exec') {
|
|
537
|
-
sandboxEnv = process.env.SANDBOX.replace(/^gemini-(?:code-)?/, '');
|
|
538
|
-
}
|
|
539
|
-
else if (process.env.SANDBOX === 'sandbox-exec') {
|
|
540
|
-
sandboxEnv = `sandbox-exec (${process.env.SEATBELT_PROFILE || 'unknown'})`;
|
|
541
|
-
}
|
|
542
|
-
const modelVersion = config?.getModel() || 'Unknown';
|
|
543
|
-
const cliVersion = await getCliVersion();
|
|
544
|
-
const memoryUsage = formatMemoryUsage(process.memoryUsage().rss);
|
|
545
|
-
const info = `
|
|
546
|
-
* **CLI Version:** ${cliVersion}
|
|
547
|
-
* **Git Commit:** ${GIT_COMMIT_INFO}
|
|
548
|
-
* **Operating System:** ${osVersion}
|
|
549
|
-
* **Sandbox Environment:** ${sandboxEnv}
|
|
550
|
-
* **Model Version:** ${modelVersion}
|
|
551
|
-
* **Memory Usage:** ${memoryUsage}
|
|
552
|
-
`;
|
|
553
|
-
let bugReportUrl = 'https://github.com/google-gemini/gemini-cli/issues/new?template=bug_report.yml&title={title}&info={info}';
|
|
554
|
-
const bugCommand = config?.getBugCommand();
|
|
555
|
-
if (bugCommand?.urlTemplate) {
|
|
556
|
-
bugReportUrl = bugCommand.urlTemplate;
|
|
557
|
-
}
|
|
558
|
-
bugReportUrl = bugReportUrl
|
|
559
|
-
.replace('{title}', encodeURIComponent(bugDescription))
|
|
560
|
-
.replace('{info}', encodeURIComponent(info));
|
|
561
|
-
addMessage({
|
|
562
|
-
type: MessageType.INFO,
|
|
563
|
-
content: `To submit your bug report, please open the following URL in your browser:\n${bugReportUrl}`,
|
|
564
|
-
timestamp: new Date(),
|
|
565
|
-
});
|
|
566
|
-
(async () => {
|
|
567
|
-
try {
|
|
568
|
-
await open(bugReportUrl);
|
|
569
|
-
}
|
|
570
|
-
catch (error) {
|
|
571
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
572
|
-
addMessage({
|
|
573
|
-
type: MessageType.ERROR,
|
|
574
|
-
content: `Could not open URL in browser: ${errorMessage}`,
|
|
575
|
-
timestamp: new Date(),
|
|
576
|
-
});
|
|
577
|
-
}
|
|
578
|
-
})();
|
|
579
|
-
},
|
|
580
|
-
},
|
|
581
|
-
{
|
|
582
|
-
name: 'chat',
|
|
583
|
-
description: 'Manage conversation history. Usage: /chat <list|save|resume> <tag>',
|
|
584
|
-
action: async (_mainCommand, subCommand, args) => {
|
|
585
|
-
const tag = (args || '').trim();
|
|
586
|
-
const logger = new Logger(config?.getSessionId() || '');
|
|
587
|
-
await logger.initialize();
|
|
588
|
-
const chat = await config?.getGeminiClient()?.getChat();
|
|
589
|
-
if (!chat) {
|
|
590
|
-
addMessage({
|
|
591
|
-
type: MessageType.ERROR,
|
|
592
|
-
content: 'No chat client available for conversation status.',
|
|
593
|
-
timestamp: new Date(),
|
|
594
|
-
});
|
|
595
|
-
return;
|
|
596
|
-
}
|
|
597
|
-
if (!subCommand) {
|
|
598
|
-
addMessage({
|
|
599
|
-
type: MessageType.ERROR,
|
|
600
|
-
content: 'Missing command\nUsage: /chat <list|save|resume> <tag>',
|
|
601
|
-
timestamp: new Date(),
|
|
602
|
-
});
|
|
603
|
-
return;
|
|
604
|
-
}
|
|
605
|
-
switch (subCommand) {
|
|
606
|
-
case 'save': {
|
|
607
|
-
if (!tag) {
|
|
608
|
-
addMessage({
|
|
609
|
-
type: MessageType.ERROR,
|
|
610
|
-
content: 'Missing tag. Usage: /chat save <tag>',
|
|
611
|
-
timestamp: new Date(),
|
|
612
|
-
});
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
const history = chat.getHistory();
|
|
616
|
-
if (history.length > 0) {
|
|
617
|
-
await logger.saveCheckpoint(chat?.getHistory() || [], tag);
|
|
618
|
-
addMessage({
|
|
619
|
-
type: MessageType.INFO,
|
|
620
|
-
content: `Conversation checkpoint saved with tag: ${tag}.`,
|
|
621
|
-
timestamp: new Date(),
|
|
622
|
-
});
|
|
623
|
-
}
|
|
624
|
-
else {
|
|
625
|
-
addMessage({
|
|
626
|
-
type: MessageType.INFO,
|
|
627
|
-
content: 'No conversation found to save.',
|
|
628
|
-
timestamp: new Date(),
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
return;
|
|
632
|
-
}
|
|
633
|
-
case 'resume':
|
|
634
|
-
case 'restore':
|
|
635
|
-
case 'load': {
|
|
636
|
-
if (!tag) {
|
|
637
|
-
addMessage({
|
|
638
|
-
type: MessageType.ERROR,
|
|
639
|
-
content: 'Missing tag. Usage: /chat resume <tag>',
|
|
640
|
-
timestamp: new Date(),
|
|
641
|
-
});
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
644
|
-
const conversation = await logger.loadCheckpoint(tag);
|
|
645
|
-
if (conversation.length === 0) {
|
|
646
|
-
addMessage({
|
|
647
|
-
type: MessageType.INFO,
|
|
648
|
-
content: `No saved checkpoint found with tag: ${tag}.`,
|
|
649
|
-
timestamp: new Date(),
|
|
650
|
-
});
|
|
651
|
-
return;
|
|
652
|
-
}
|
|
653
|
-
clearItems();
|
|
654
|
-
chat.clearHistory();
|
|
655
|
-
const rolemap = {
|
|
656
|
-
user: MessageType.USER,
|
|
657
|
-
model: MessageType.GEMINI,
|
|
658
|
-
};
|
|
659
|
-
let hasSystemPrompt = false;
|
|
660
|
-
let i = 0;
|
|
661
|
-
for (const item of conversation) {
|
|
662
|
-
i += 1;
|
|
663
|
-
// Add each item to history regardless of whether we display
|
|
664
|
-
// it.
|
|
665
|
-
chat.addHistory(item);
|
|
666
|
-
const text = item.parts
|
|
667
|
-
?.filter((m) => !!m.text)
|
|
668
|
-
.map((m) => m.text)
|
|
669
|
-
.join('') || '';
|
|
670
|
-
if (!text) {
|
|
671
|
-
// Parsing Part[] back to various non-text output not yet implemented.
|
|
672
|
-
continue;
|
|
673
|
-
}
|
|
674
|
-
if (i === 1 && text.match(/context for our chat/)) {
|
|
675
|
-
hasSystemPrompt = true;
|
|
676
|
-
}
|
|
677
|
-
if (i > 2 || !hasSystemPrompt) {
|
|
678
|
-
addItem({
|
|
679
|
-
type: (item.role && rolemap[item.role]) || MessageType.GEMINI,
|
|
680
|
-
text,
|
|
681
|
-
}, i);
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
console.clear();
|
|
685
|
-
refreshStatic();
|
|
686
|
-
return;
|
|
687
|
-
}
|
|
688
|
-
case 'list':
|
|
689
|
-
addMessage({
|
|
690
|
-
type: MessageType.INFO,
|
|
691
|
-
content: 'list of saved conversations: ' +
|
|
692
|
-
(await savedChatTags()).join(', '),
|
|
693
|
-
timestamp: new Date(),
|
|
694
|
-
});
|
|
695
|
-
return;
|
|
696
|
-
default:
|
|
697
|
-
addMessage({
|
|
698
|
-
type: MessageType.ERROR,
|
|
699
|
-
content: `Unknown /chat command: ${subCommand}. Available: list, save, resume`,
|
|
700
|
-
timestamp: new Date(),
|
|
701
|
-
});
|
|
702
|
-
return;
|
|
703
|
-
}
|
|
704
|
-
},
|
|
705
|
-
completion: async () => (await savedChatTags()).map((tag) => 'resume ' + tag),
|
|
706
|
-
},
|
|
707
|
-
{
|
|
708
|
-
name: 'quit',
|
|
709
|
-
altName: 'exit',
|
|
710
|
-
description: 'exit the cli',
|
|
711
|
-
action: async (mainCommand, _subCommand, _args) => {
|
|
712
|
-
const now = new Date();
|
|
713
|
-
const { sessionStartTime } = session.stats;
|
|
714
|
-
const wallDuration = now.getTime() - sessionStartTime.getTime();
|
|
715
|
-
setQuittingMessages([
|
|
716
|
-
{
|
|
717
|
-
type: 'user',
|
|
718
|
-
text: `/${mainCommand}`,
|
|
719
|
-
id: now.getTime() - 1,
|
|
720
|
-
},
|
|
721
|
-
{
|
|
722
|
-
type: 'quit',
|
|
723
|
-
duration: formatDuration(wallDuration),
|
|
724
|
-
id: now.getTime(),
|
|
725
|
-
},
|
|
726
|
-
]);
|
|
727
|
-
setTimeout(() => {
|
|
728
|
-
process.exit(0);
|
|
729
|
-
}, 100);
|
|
730
|
-
},
|
|
731
|
-
},
|
|
732
|
-
{
|
|
733
|
-
name: 'compress',
|
|
734
|
-
altName: 'summarize',
|
|
735
|
-
description: 'Compresses the context by replacing it with a summary.',
|
|
736
|
-
action: async (_mainCommand, _subCommand, _args) => {
|
|
737
|
-
if (pendingCompressionItemRef.current !== null) {
|
|
738
|
-
addMessage({
|
|
739
|
-
type: MessageType.ERROR,
|
|
740
|
-
content: 'Already compressing, wait for previous request to complete',
|
|
741
|
-
timestamp: new Date(),
|
|
742
|
-
});
|
|
743
|
-
return;
|
|
744
|
-
}
|
|
745
|
-
setPendingCompressionItem({
|
|
746
|
-
type: MessageType.COMPRESSION,
|
|
747
|
-
compression: {
|
|
748
|
-
isPending: true,
|
|
749
|
-
originalTokenCount: null,
|
|
750
|
-
newTokenCount: null,
|
|
751
|
-
},
|
|
752
|
-
});
|
|
753
|
-
try {
|
|
754
|
-
const compressed = await config
|
|
755
|
-
.getGeminiClient()
|
|
756
|
-
// TODO: Set Prompt id for CompressChat from SlashCommandProcessor.
|
|
757
|
-
.tryCompressChat('Prompt Id not set', true);
|
|
758
|
-
if (compressed) {
|
|
759
|
-
addMessage({
|
|
760
|
-
type: MessageType.COMPRESSION,
|
|
761
|
-
compression: {
|
|
762
|
-
isPending: false,
|
|
763
|
-
originalTokenCount: compressed.originalTokenCount,
|
|
764
|
-
newTokenCount: compressed.newTokenCount,
|
|
765
|
-
},
|
|
766
|
-
timestamp: new Date(),
|
|
767
|
-
});
|
|
768
|
-
}
|
|
769
|
-
else {
|
|
770
|
-
addMessage({
|
|
771
|
-
type: MessageType.ERROR,
|
|
772
|
-
content: 'Failed to compress chat history.',
|
|
773
|
-
timestamp: new Date(),
|
|
774
|
-
});
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
catch (e) {
|
|
778
|
-
addMessage({
|
|
779
|
-
type: MessageType.ERROR,
|
|
780
|
-
content: `Failed to compress chat history: ${e instanceof Error ? e.message : String(e)}`,
|
|
781
|
-
timestamp: new Date(),
|
|
782
|
-
});
|
|
783
|
-
}
|
|
784
|
-
setPendingCompressionItem(null);
|
|
785
|
-
},
|
|
786
|
-
},
|
|
787
|
-
];
|
|
788
|
-
if (config?.getCheckpointingEnabled()) {
|
|
789
|
-
commands.push({
|
|
790
|
-
name: 'restore',
|
|
791
|
-
description: 'restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested',
|
|
792
|
-
completion: async () => {
|
|
793
|
-
const checkpointDir = config?.getProjectTempDir()
|
|
794
|
-
? path.join(config.getProjectTempDir(), 'checkpoints')
|
|
795
|
-
: undefined;
|
|
796
|
-
if (!checkpointDir) {
|
|
797
|
-
return [];
|
|
798
|
-
}
|
|
799
|
-
try {
|
|
800
|
-
const files = await fs.readdir(checkpointDir);
|
|
801
|
-
return files
|
|
802
|
-
.filter((file) => file.endsWith('.json'))
|
|
803
|
-
.map((file) => file.replace('.json', ''));
|
|
804
|
-
}
|
|
805
|
-
catch (_err) {
|
|
806
|
-
return [];
|
|
807
|
-
}
|
|
808
|
-
},
|
|
809
|
-
action: async (_mainCommand, subCommand, _args) => {
|
|
810
|
-
const checkpointDir = config?.getProjectTempDir()
|
|
811
|
-
? path.join(config.getProjectTempDir(), 'checkpoints')
|
|
812
|
-
: undefined;
|
|
813
|
-
if (!checkpointDir) {
|
|
814
|
-
addMessage({
|
|
815
|
-
type: MessageType.ERROR,
|
|
816
|
-
content: 'Could not determine the .gemini directory path.',
|
|
817
|
-
timestamp: new Date(),
|
|
818
|
-
});
|
|
819
|
-
return;
|
|
820
|
-
}
|
|
821
|
-
try {
|
|
822
|
-
// Ensure the directory exists before trying to read it.
|
|
823
|
-
await fs.mkdir(checkpointDir, { recursive: true });
|
|
824
|
-
const files = await fs.readdir(checkpointDir);
|
|
825
|
-
const jsonFiles = files.filter((file) => file.endsWith('.json'));
|
|
826
|
-
if (!subCommand) {
|
|
827
|
-
if (jsonFiles.length === 0) {
|
|
828
|
-
addMessage({
|
|
829
|
-
type: MessageType.INFO,
|
|
830
|
-
content: 'No restorable tool calls found.',
|
|
831
|
-
timestamp: new Date(),
|
|
832
|
-
});
|
|
833
|
-
return;
|
|
834
|
-
}
|
|
835
|
-
const truncatedFiles = jsonFiles.map((file) => {
|
|
836
|
-
const components = file.split('.');
|
|
837
|
-
if (components.length <= 1) {
|
|
838
|
-
return file;
|
|
839
|
-
}
|
|
840
|
-
components.pop();
|
|
841
|
-
return components.join('.');
|
|
842
|
-
});
|
|
843
|
-
const fileList = truncatedFiles.join('\n');
|
|
844
|
-
addMessage({
|
|
845
|
-
type: MessageType.INFO,
|
|
846
|
-
content: `Available tool calls to restore:\n\n${fileList}`,
|
|
847
|
-
timestamp: new Date(),
|
|
848
|
-
});
|
|
849
|
-
return;
|
|
850
|
-
}
|
|
851
|
-
const selectedFile = subCommand.endsWith('.json')
|
|
852
|
-
? subCommand
|
|
853
|
-
: `${subCommand}.json`;
|
|
854
|
-
if (!jsonFiles.includes(selectedFile)) {
|
|
855
|
-
addMessage({
|
|
856
|
-
type: MessageType.ERROR,
|
|
857
|
-
content: `File not found: ${selectedFile}`,
|
|
858
|
-
timestamp: new Date(),
|
|
859
|
-
});
|
|
860
|
-
return;
|
|
861
|
-
}
|
|
862
|
-
const filePath = path.join(checkpointDir, selectedFile);
|
|
863
|
-
const data = await fs.readFile(filePath, 'utf-8');
|
|
864
|
-
const toolCallData = JSON.parse(data);
|
|
865
|
-
if (toolCallData.history) {
|
|
866
|
-
loadHistory(toolCallData.history);
|
|
867
|
-
}
|
|
868
|
-
if (toolCallData.clientHistory) {
|
|
869
|
-
await config
|
|
870
|
-
?.getGeminiClient()
|
|
871
|
-
?.setHistory(toolCallData.clientHistory);
|
|
872
|
-
}
|
|
873
|
-
if (toolCallData.commitHash) {
|
|
874
|
-
await gitService?.restoreProjectFromSnapshot(toolCallData.commitHash);
|
|
875
|
-
addMessage({
|
|
876
|
-
type: MessageType.INFO,
|
|
877
|
-
content: `Restored project to the state before the tool call.`,
|
|
878
|
-
timestamp: new Date(),
|
|
879
|
-
});
|
|
880
|
-
}
|
|
881
|
-
return {
|
|
882
|
-
type: 'tool',
|
|
883
|
-
toolName: toolCallData.toolCall.name,
|
|
884
|
-
toolArgs: toolCallData.toolCall.args,
|
|
885
|
-
};
|
|
886
|
-
}
|
|
887
|
-
catch (error) {
|
|
888
|
-
addMessage({
|
|
889
|
-
type: MessageType.ERROR,
|
|
890
|
-
content: `Could not read restorable tool calls. This is the error: ${error}`,
|
|
891
|
-
timestamp: new Date(),
|
|
892
|
-
});
|
|
893
|
-
}
|
|
894
|
-
},
|
|
895
|
-
});
|
|
896
|
-
}
|
|
897
|
-
return commands;
|
|
898
|
-
}, [
|
|
899
|
-
addMessage,
|
|
900
|
-
openAuthDialog,
|
|
901
|
-
openEditorDialog,
|
|
902
|
-
openPrivacyNotice,
|
|
903
|
-
toggleCorgiMode,
|
|
904
|
-
savedChatTags,
|
|
905
|
-
config,
|
|
906
|
-
settings,
|
|
907
|
-
showToolDescriptions,
|
|
908
|
-
session,
|
|
909
|
-
gitService,
|
|
910
|
-
loadHistory,
|
|
911
|
-
addItem,
|
|
912
|
-
setQuittingMessages,
|
|
913
|
-
pendingCompressionItemRef,
|
|
914
|
-
setPendingCompressionItem,
|
|
915
|
-
clearItems,
|
|
916
|
-
refreshStatic,
|
|
917
|
-
]);
|
|
918
135
|
const handleSlashCommand = useCallback(async (rawQuery) => {
|
|
919
136
|
if (typeof rawQuery !== 'string') {
|
|
920
137
|
return false;
|
|
@@ -929,7 +146,6 @@ export const useSlashCommandProcessor = (config, settings, history, addItem, cle
|
|
|
929
146
|
}
|
|
930
147
|
const parts = trimmed.substring(1).trim().split(/\s+/);
|
|
931
148
|
const commandPath = parts.filter((p) => p); // The parts of the command, e.g., ['memory', 'add']
|
|
932
|
-
// --- Start of New Tree Traversal Logic ---
|
|
933
149
|
let currentCommands = commands;
|
|
934
150
|
let commandToExecute;
|
|
935
151
|
let pathIndex = 0;
|
|
@@ -974,14 +190,39 @@ export const useSlashCommandProcessor = (config, settings, history, addItem, cle
|
|
|
974
190
|
case 'help':
|
|
975
191
|
setShowHelp(true);
|
|
976
192
|
return { type: 'handled' };
|
|
193
|
+
case 'auth':
|
|
194
|
+
openAuthDialog();
|
|
195
|
+
return { type: 'handled' };
|
|
977
196
|
case 'theme':
|
|
978
197
|
openThemeDialog();
|
|
979
198
|
return { type: 'handled' };
|
|
199
|
+
case 'editor':
|
|
200
|
+
openEditorDialog();
|
|
201
|
+
return { type: 'handled' };
|
|
202
|
+
case 'privacy':
|
|
203
|
+
openPrivacyNotice();
|
|
204
|
+
return { type: 'handled' };
|
|
980
205
|
default: {
|
|
981
206
|
const unhandled = result.dialog;
|
|
982
207
|
throw new Error(`Unhandled slash command result: ${unhandled}`);
|
|
983
208
|
}
|
|
984
209
|
}
|
|
210
|
+
case 'load_history': {
|
|
211
|
+
await config
|
|
212
|
+
?.getGeminiClient()
|
|
213
|
+
?.setHistory(result.clientHistory);
|
|
214
|
+
commandContext.ui.clear();
|
|
215
|
+
result.history.forEach((item, index) => {
|
|
216
|
+
commandContext.ui.addItem(item, index);
|
|
217
|
+
});
|
|
218
|
+
return { type: 'handled' };
|
|
219
|
+
}
|
|
220
|
+
case 'quit':
|
|
221
|
+
setQuittingMessages(result.messages);
|
|
222
|
+
setTimeout(() => {
|
|
223
|
+
process.exit(0);
|
|
224
|
+
}, 100);
|
|
225
|
+
return { type: 'handled' };
|
|
985
226
|
default: {
|
|
986
227
|
const unhandled = result;
|
|
987
228
|
throw new Error(`Unhandled slash command result: ${unhandled}`);
|
|
@@ -1002,32 +243,6 @@ export const useSlashCommandProcessor = (config, settings, history, addItem, cle
|
|
|
1002
243
|
return { type: 'handled' };
|
|
1003
244
|
}
|
|
1004
245
|
}
|
|
1005
|
-
// --- End of New Tree Traversal Logic ---
|
|
1006
|
-
// --- Legacy Fallback Logic (for commands not yet migrated) ---
|
|
1007
|
-
const mainCommand = parts[0];
|
|
1008
|
-
const subCommand = parts[1];
|
|
1009
|
-
const legacyArgs = parts.slice(2).join(' ');
|
|
1010
|
-
for (const cmd of legacyCommands) {
|
|
1011
|
-
if (mainCommand === cmd.name || mainCommand === cmd.altName) {
|
|
1012
|
-
const actionResult = await cmd.action(mainCommand, subCommand, legacyArgs);
|
|
1013
|
-
if (actionResult?.type === 'tool') {
|
|
1014
|
-
return {
|
|
1015
|
-
type: 'schedule_tool',
|
|
1016
|
-
toolName: actionResult.toolName,
|
|
1017
|
-
toolArgs: actionResult.toolArgs,
|
|
1018
|
-
};
|
|
1019
|
-
}
|
|
1020
|
-
if (actionResult?.type === 'message') {
|
|
1021
|
-
addItem({
|
|
1022
|
-
type: actionResult.messageType === 'error'
|
|
1023
|
-
? MessageType.ERROR
|
|
1024
|
-
: MessageType.INFO,
|
|
1025
|
-
text: actionResult.content,
|
|
1026
|
-
}, Date.now());
|
|
1027
|
-
}
|
|
1028
|
-
return { type: 'handled' };
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
246
|
addMessage({
|
|
1032
247
|
type: MessageType.ERROR,
|
|
1033
248
|
content: `Unknown command: ${trimmed}`,
|
|
@@ -1035,37 +250,21 @@ export const useSlashCommandProcessor = (config, settings, history, addItem, cle
|
|
|
1035
250
|
});
|
|
1036
251
|
return { type: 'handled' };
|
|
1037
252
|
}, [
|
|
253
|
+
config,
|
|
1038
254
|
addItem,
|
|
1039
255
|
setShowHelp,
|
|
256
|
+
openAuthDialog,
|
|
1040
257
|
commands,
|
|
1041
|
-
legacyCommands,
|
|
1042
258
|
commandContext,
|
|
1043
259
|
addMessage,
|
|
1044
260
|
openThemeDialog,
|
|
261
|
+
openPrivacyNotice,
|
|
262
|
+
openEditorDialog,
|
|
263
|
+
setQuittingMessages,
|
|
1045
264
|
]);
|
|
1046
|
-
const allCommands = useMemo(() => {
|
|
1047
|
-
// Adapt legacy commands to the new SlashCommand interface
|
|
1048
|
-
const adaptedLegacyCommands = legacyCommands.map((legacyCmd) => ({
|
|
1049
|
-
name: legacyCmd.name,
|
|
1050
|
-
altName: legacyCmd.altName,
|
|
1051
|
-
description: legacyCmd.description,
|
|
1052
|
-
action: async (_context, args) => {
|
|
1053
|
-
const parts = args.split(/\s+/);
|
|
1054
|
-
const subCommand = parts[0] || undefined;
|
|
1055
|
-
const restOfArgs = parts.slice(1).join(' ') || undefined;
|
|
1056
|
-
return legacyCmd.action(legacyCmd.name, subCommand, restOfArgs);
|
|
1057
|
-
},
|
|
1058
|
-
completion: legacyCmd.completion
|
|
1059
|
-
? async (_context, _partialArg) => legacyCmd.completion()
|
|
1060
|
-
: undefined,
|
|
1061
|
-
}));
|
|
1062
|
-
const newCommandNames = new Set(commands.map((c) => c.name));
|
|
1063
|
-
const filteredAdaptedLegacy = adaptedLegacyCommands.filter((c) => !newCommandNames.has(c.name));
|
|
1064
|
-
return [...commands, ...filteredAdaptedLegacy];
|
|
1065
|
-
}, [commands, legacyCommands]);
|
|
1066
265
|
return {
|
|
1067
266
|
handleSlashCommand,
|
|
1068
|
-
slashCommands:
|
|
267
|
+
slashCommands: commands,
|
|
1069
268
|
pendingHistoryItems,
|
|
1070
269
|
commandContext,
|
|
1071
270
|
};
|