From 137a80b9bf2ca977f5cd436ebf5502e36f83fd0b Mon Sep 17 00:00:00 2001 From: Jeremy McSpadden Date: Sat, 21 Mar 2026 09:36:08 -0500 Subject: [PATCH] fix(autocomplete): repair /gsd skip, add widget/next completions, add discuss to hint (#1675) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(autocomplete): repair /gsd skip, add widget/next --debug completions, add discuss to description - fix: bare `/gsd skip` (no args) fell through all handlers and hit the "Unknown command" warning — add a usage message handler matching `trimmed === "skip"` consistent with steer/knowledge/run-hook - fix: `next` handler supports `--debug` (enables debug logging) but it was absent from NESTED_COMPLETIONS; add alongside --verbose/--dry-run - fix: `widget` accepts full|small|min|off args but had no autocomplete entries; add widget to NESTED_COMPLETIONS with all four modes - fix: `discuss` was in TOP_LEVEL_SUBCOMMANDS and fully implemented but omitted from GSD_COMMAND_DESCRIPTION hint string; add it * test(gsd): add autocomplete regressions for skip/widget/next/discuss --- .../extensions/gsd/commands/catalog.ts | 9 +- .../extensions/gsd/commands/handlers/ops.ts | 4 + .../autocomplete-regressions-1675.test.ts | 83 +++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 src/resources/extensions/gsd/tests/autocomplete-regressions-1675.test.ts diff --git a/src/resources/extensions/gsd/commands/catalog.ts b/src/resources/extensions/gsd/commands/catalog.ts index 2a311b4d8..4709a2769 100644 --- a/src/resources/extensions/gsd/commands/catalog.ts +++ b/src/resources/extensions/gsd/commands/catalog.ts @@ -14,7 +14,7 @@ export interface GsdCommandDefinition { type CompletionMap = Record; export const GSD_COMMAND_DESCRIPTION = - "GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|queue|quick|capture|triage|dispatch|history|undo|rate|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|park|unpark|init|setup|inspect|extensions|update"; + "GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|queue|quick|discuss|capture|triage|dispatch|history|undo|rate|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|park|unpark|init|setup|inspect|extensions|update"; export const TOP_LEVEL_SUBCOMMANDS: readonly GsdCommandDefinition[] = [ { cmd: "help", desc: "Categorized command reference with descriptions" }, @@ -74,6 +74,13 @@ const NESTED_COMPLETIONS: CompletionMap = { next: [ { cmd: "--verbose", desc: "Show detailed step output" }, { cmd: "--dry-run", desc: "Preview next step without executing" }, + { cmd: "--debug", desc: "Enable debug logging" }, + ], + widget: [ + { cmd: "full", desc: "Full widget display" }, + { cmd: "small", desc: "Compact widget display" }, + { cmd: "min", desc: "Minimal widget display" }, + { cmd: "off", desc: "Hide widget" }, ], mode: [ { cmd: "global", desc: "Edit global workflow mode" }, diff --git a/src/resources/extensions/gsd/commands/handlers/ops.ts b/src/resources/extensions/gsd/commands/handlers/ops.ts index 0d6823fce..612fce50d 100644 --- a/src/resources/extensions/gsd/commands/handlers/ops.ts +++ b/src/resources/extensions/gsd/commands/handlers/ops.ts @@ -57,6 +57,10 @@ export async function handleOpsCommand(trimmed: string, ctx: ExtensionCommandCon await handleUndo(trimmed.replace(/^undo\s*/, "").trim(), ctx, pi, projectRoot()); return true; } + if (trimmed === "skip") { + ctx.ui.notify("Usage: /gsd skip Example: /gsd skip M001/S01/T03", "warning"); + return true; + } if (trimmed.startsWith("skip ")) { await handleSkip(trimmed.replace(/^skip\s*/, "").trim(), ctx, projectRoot()); return true; diff --git a/src/resources/extensions/gsd/tests/autocomplete-regressions-1675.test.ts b/src/resources/extensions/gsd/tests/autocomplete-regressions-1675.test.ts new file mode 100644 index 000000000..22e1528db --- /dev/null +++ b/src/resources/extensions/gsd/tests/autocomplete-regressions-1675.test.ts @@ -0,0 +1,83 @@ +import test from "node:test"; +import assert from "node:assert/strict"; + +import { registerGSDCommand } from "../commands.ts"; +import { handleGSDCommand } from "../commands/dispatcher.ts"; + +function createMockPi() { + const commands = new Map(); + return { + registerCommand(name: string, options: any) { + commands.set(name, options); + }, + registerTool() {}, + registerShortcut() {}, + on() {}, + sendMessage() {}, + commands, + }; +} + +function createMockCtx() { + const notifications: { message: string; level: string }[] = []; + return { + notifications, + ui: { + notify(message: string, level: string) { + notifications.push({ message, level }); + }, + custom: async () => {}, + }, + shutdown: async () => {}, + }; +} + +test("/gsd description includes discuss", () => { + const pi = createMockPi(); + registerGSDCommand(pi as any); + + const gsd = pi.commands.get("gsd"); + assert.ok(gsd, "registerGSDCommand should register /gsd"); + assert.ok( + gsd.description.includes("discuss"), + "description should include discuss", + ); +}); + +test("/gsd next completions include --debug", () => { + const pi = createMockPi(); + registerGSDCommand(pi as any); + + const gsd = pi.commands.get("gsd"); + const completions = gsd.getArgumentCompletions("next "); + const debug = completions.find((c: any) => c.value === "next --debug"); + assert.ok(debug, "next --debug should appear in completions"); +}); + +test("/gsd widget completions include full|small|min|off", () => { + const pi = createMockPi(); + registerGSDCommand(pi as any); + + const gsd = pi.commands.get("gsd"); + const completions = gsd.getArgumentCompletions("widget "); + const values = completions.map((c: any) => c.value); + for (const expected of ["widget full", "widget small", "widget min", "widget off"]) { + assert.ok(values.includes(expected), `missing completion: ${expected}`); + } +}); + +test("bare /gsd skip shows usage and does not fall through to unknown-command warning", async () => { + const ctx = createMockCtx(); + + await handleGSDCommand("skip", ctx as any, {} as any); + + assert.ok( + ctx.notifications.some((n) => n.message.includes("Usage: /gsd skip ")), + "should show skip usage guidance", + ); + assert.ok( + !ctx.notifications.some((n) => n.message.startsWith("Unknown: /gsd skip")), + "should not emit unknown-command warning for bare skip", + ); +}); +