summaryrefslogtreecommitdiff
path: root/packages/cli/src
diff options
context:
space:
mode:
authorBilly Biggs <[email protected]>2025-06-15 11:40:39 -0700
committerGitHub <[email protected]>2025-06-15 11:40:39 -0700
commitb67806ae9a99e5a3c449c60457933b47d14ba66c (patch)
treef1334ae7d120c3ba39e5725d5e1f56db8ad443ff /packages/cli/src
parent6d772a30c0540ad83c99a794c820af635961694a (diff)
Support completion of checkpoint names in /resume (#1063)
Diffstat (limited to 'packages/cli/src')
-rw-r--r--packages/cli/src/ui/components/InputPrompt.tsx23
-rw-r--r--packages/cli/src/ui/hooks/slashCommandProcessor.ts20
-rw-r--r--packages/cli/src/ui/hooks/useCompletion.ts28
3 files changed, 67 insertions, 4 deletions
diff --git a/packages/cli/src/ui/components/InputPrompt.tsx b/packages/cli/src/ui/components/InputPrompt.tsx
index c4177c00..d31b7106 100644
--- a/packages/cli/src/ui/components/InputPrompt.tsx
+++ b/packages/cli/src/ui/components/InputPrompt.tsx
@@ -109,11 +109,20 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
const selectedSuggestion = completionSuggestions[indexToUse];
if (query.trimStart().startsWith('/')) {
+ const parts = query.trimStart().substring(1).split(' ');
+ const commandName = parts[0];
const slashIndex = query.indexOf('/');
const base = query.substring(0, slashIndex + 1);
- const newValue = base + selectedSuggestion.value;
- buffer.setText(newValue);
- handleSubmitAndClear(newValue);
+
+ const command = slashCommands.find((cmd) => cmd.name === commandName);
+ if (command && command.completion) {
+ const newValue = `${base}${commandName} ${selectedSuggestion.value}`;
+ buffer.setText(newValue);
+ } else {
+ const newValue = base + selectedSuggestion.value;
+ buffer.setText(newValue);
+ handleSubmitAndClear(newValue);
+ }
} else {
const atIndex = query.lastIndexOf('@');
if (atIndex === -1) return;
@@ -131,7 +140,13 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
}
resetCompletionState();
},
- [resetCompletionState, handleSubmitAndClear, buffer, completionSuggestions],
+ [
+ resetCompletionState,
+ handleSubmitAndClear,
+ buffer,
+ completionSuggestions,
+ slashCommands,
+ ],
);
useInput(
diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.ts
index a4b13d0a..dfefc562 100644
--- a/packages/cli/src/ui/hooks/slashCommandProcessor.ts
+++ b/packages/cli/src/ui/hooks/slashCommandProcessor.ts
@@ -44,6 +44,7 @@ export interface SlashCommand {
name: string;
altName?: string;
description?: string;
+ completion?: () => Promise<string[]>;
action: (
mainCommand: string,
subCommand?: string,
@@ -643,6 +644,25 @@ Add any other context about the problem here.
name: 'resume',
description:
'resume from conversation checkpoint. Usage: /resume [tag]',
+ completion: async () => {
+ const geminiDir = config?.getGeminiDir();
+ if (!geminiDir) {
+ return [];
+ }
+ try {
+ const files = await fs.readdir(geminiDir);
+ return files
+ .filter(
+ (file) =>
+ file.startsWith('checkpoint-') && file.endsWith('.json'),
+ )
+ .map((file) =>
+ file.replace('checkpoint-', '').replace('.json', ''),
+ );
+ } catch (_err) {
+ return [];
+ }
+ },
action: async (_mainCommand, subCommand, _args) => {
const tag = (subCommand || '').trim();
const logger = new Logger(config?.getSessionId() || '');
diff --git a/packages/cli/src/ui/hooks/useCompletion.ts b/packages/cli/src/ui/hooks/useCompletion.ts
index b7603720..5217e60c 100644
--- a/packages/cli/src/ui/hooks/useCompletion.ts
+++ b/packages/cli/src/ui/hooks/useCompletion.ts
@@ -127,6 +127,34 @@ export function useCompletion(
// --- Handle Slash Command Completion ---
if (trimmedQuery.startsWith('/')) {
+ const parts = trimmedQuery.substring(1).split(' ');
+ const commandName = parts[0];
+ const subCommand = parts.slice(1).join(' ');
+
+ const command = slashCommands.find(
+ (cmd) => cmd.name === commandName || cmd.altName === commandName,
+ );
+
+ if (command && command.completion) {
+ const fetchAndSetSuggestions = async () => {
+ setIsLoadingSuggestions(true);
+ if (command.completion) {
+ const results = await command.completion();
+ const filtered = results.filter((r) => r.startsWith(subCommand));
+ const newSuggestions = filtered.map((s) => ({
+ label: s,
+ value: s,
+ }));
+ setSuggestions(newSuggestions);
+ setShowSuggestions(newSuggestions.length > 0);
+ setActiveSuggestionIndex(newSuggestions.length > 0 ? 0 : -1);
+ }
+ setIsLoadingSuggestions(false);
+ };
+ fetchAndSetSuggestions();
+ return;
+ }
+
const partialCommand = trimmedQuery.substring(1);
const filteredSuggestions = slashCommands
.filter(