From ce63d2a10636490c1bdb1dedf36db093735a58e0 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Wed, 25 Mar 2026 21:38:58 +0200 Subject: loop-scheduler: fix autocomplete dead-end for cancel and preset subcommands Previously typing "/loop preset" returned an exact-match stub which caused the UI to fall back to filesystem completions. Now cancel/preset expand directly to their full third-level items ("cancel all"/"cancel ", "preset ") as soon as the prefix matches the verb. Co-Authored-By: Claude Sonnet 4.6 --- pi/agent/extensions/loop-scheduler/index.ts | 38 ++++++++++++++++------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/pi/agent/extensions/loop-scheduler/index.ts b/pi/agent/extensions/loop-scheduler/index.ts index a5bad6e..356bccf 100644 --- a/pi/agent/extensions/loop-scheduler/index.ts +++ b/pi/agent/extensions/loop-scheduler/index.ts @@ -419,45 +419,49 @@ export default function loopSchedulerExtension(pi: ExtensionAPI): void { description: "Schedule a recurring prompt: /loop 10m , /loop list, /loop cancel , /loop ", // Provide autocomplete for subcommands and preset names loaded from loop-presets.md. - // Third-level completions (after a subcommand word) are handled before the top-level list. + // "cancel" and "preset" subcommands expand directly into their third-level completions + // so the user never gets stuck with just the verb and no further suggestions. getArgumentCompletions: (prefix: string) => { - // Third-level: /loop cancel|rm|delete - // Suggest "all" and any active job IDs matching the partial input. - const cancelMatch = prefix.match(/^(cancel|rm|delete)\s+(\S*)$/i); - if (cancelMatch) { - const verb = cancelMatch[1]!; - const partial = (cancelMatch[2] ?? "").toLowerCase(); + // cancel/rm/delete : expand to full "cancel all" / "cancel " items + // as soon as the prefix matches the verb (with or without trailing space/partial id). + if (/^(cancel|rm|delete)(\s+\S*)?$/i.test(prefix)) { + const verb = prefix.split(/\s+/)[0]!; + const partial = (prefix.match(/^(?:cancel|rm|delete)\s+(\S*)$/i)?.[1] ?? "").toLowerCase(); const results = []; if ("all".startsWith(partial)) { - results.push({ value: `${verb} all`, label: "all", description: "Cancel all active jobs" }); + results.push({ value: `${verb} all`, label: `${verb} all`, description: "Cancel all active jobs" }); } for (const job of jobs.values()) { if (job.id.startsWith(partial)) { - results.push({ value: `${verb} ${job.id}`, label: job.id, description: shortenPrompt(job.prompt, 50) }); + results.push({ + value: `${verb} ${job.id}`, + label: `${verb} ${job.id}`, + description: shortenPrompt(job.prompt, 50), + }); } } return results.length > 0 ? results : null; } - // Third-level: /loop preset - // Suggest preset names from loop-presets.md. - const presetMatch = prefix.match(/^preset\s+(\S*)$/i); - if (presetMatch) { - const partial = (presetMatch[1] ?? "").toLowerCase(); + // preset : expand directly to "preset " completions as soon as + // the prefix matches "preset" (with or without trailing space/partial name), + // so the user never hits a dead end at the bare verb. + if (/^preset(\s+\S*)?$/i.test(prefix)) { + const partial = (prefix.match(/^preset\s+(\S*)$/i)?.[1] ?? "").toLowerCase(); const results = loadPresets() .filter((p) => p.name.startsWith(partial)) .map((p) => ({ value: `preset ${p.name}`, - label: p.name, + label: `preset ${p.name}`, description: `every ${p.intervalLabel} — ${shortenPrompt(p.prompt, 50)}`, })); return results.length > 0 ? results : null; } - // Second-level: subcommand names and direct preset names. + // Top-level: subcommand stubs and direct preset name shortcuts. const fixed = [ { value: "list", label: "list", description: "Show active loop jobs" }, - { value: "cancel", label: "cancel", description: "Cancel: cancel " }, + { value: "cancel", label: "cancel", description: "Cancel a job: cancel " }, { value: "preset", label: "preset", description: "Activate a named preset: preset " }, { value: "edit", label: "edit", description: "Edit presets file in $EDITOR" }, { value: "presets", label: "presets", description: "List available presets" }, -- cgit v1.2.3