From 0d6abb87ac2588fd182c39f63ec18c385519ce87 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Fri, 1 May 2026 13:36:44 -0400 Subject: [PATCH] fix(#2954): align help.md with post-#2824 skill consolidation (#2959) --- CHANGELOG.md | 2 +- get-shit-done/workflows/do.md | 4 +- get-shit-done/workflows/help.md | 228 ++++++++++++------ ...-2954-help-md-slash-command-stubs.test.cjs | 165 +++++++++++++ 4 files changed, 322 insertions(+), 77 deletions(-) create mode 100644 tests/bug-2954-help-md-slash-command-stubs.test.cjs diff --git a/CHANGELOG.md b/CHANGELOG.md index b2c1b422c..65e0924ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - **`gsd-sdk query agent-skills` emits raw `` block instead of JSON-wrapped string** — workflows that embed via `$(gsd-sdk query agent-skills )` were receiving a JSON-quoted string literal mid-prompt (e.g. `"\n…"`), silently breaking all `` injection into spawned subagents. The CLI dispatcher now honors an opt-in `format: 'text'` field on `QueryResult` and writes such results raw via `process.stdout.write`; `--pick` always returns JSON regardless. (#2917) - **`sketch --wrap-up` now dispatches correctly** — `/gsd-sketch --wrap-up` was silently no-oping because the flag dispatch wiring was omitted when the micro-skill entry point was absorbed in #2790. (#2949) -- **Post-install message for `--claude --global` now reflects the skills-only layout** — `npx get-shit-done-cc --claude --global` ships skills to `~/.claude/skills/gsd-*/SKILL.md` (CC 2.1.88+ format) and removes the legacy `commands/gsd/`, but the post-install message still instructed users to type `/gsd-new-project` without mentioning the required CC restart or the skill-name fallback. Users on configurations where CC does not auto-surface skills in the slash menu hit a dead-end "no commands appear". The Claude-global branch now reads: *"Restart Claude Code, then in any directory either type /gsd-new-project or ask Claude to run the gsd-new-project skill."* Other runtimes and the `--claude --local` path are unchanged. (#2957) +- **`help.md` no longer advertises eight slash commands removed by the #2824 consolidation** — `/gsd-do`, `/gsd-note`, `/gsd-check-todos`, `/gsd-plant-seed`, `/gsd-research-phase`, `/gsd-list-phase-assumptions`, `/gsd-plan-milestone-gaps`, and `/gsd-join-discord` were removed when 86 skills were folded into 59. `help.md` was not updated alongside, so users typing the documented commands hit *Unknown command*. Each entry is now either rewritten to the surviving flag-based dispatcher (e.g., `/gsd-do …` → `/gsd-progress --do "…"`, `/gsd-note` → `/gsd-capture --note`, `/gsd-plant-seed` → `/gsd-capture --seed`, `/gsd-check-todos` → `/gsd-capture --list`) or removed for skills with no replacement. A regression test now asserts every `/gsd-*` reference in `help.md` has a matching `commands/gsd/*.md` stub. (#2954) ### Added — 1.40.0-rc.1 - **Six namespace meta-skills with keyword-tag descriptions** — replace the flat 86-skill diff --git a/get-shit-done/workflows/do.md b/get-shit-done/workflows/do.md index 6bd3c848f..8b6c780e5 100644 --- a/get-shit-done/workflows/do.md +++ b/get-shit-done/workflows/do.md @@ -46,7 +46,7 @@ Evaluate `$ARGUMENTS` against these routing rules. Apply the **first matching** | Sketching, "mockup", "what would this look like", "prototype the UI", "design this", explore visual direction | `/gsd-sketch` | Throwaway HTML mockups to explore design | | Wrapping up spikes, "package the spikes", "consolidate spike findings" | `/gsd-spike --wrap-up` | Package spike findings into reusable skill | | Wrapping up sketches, "package the designs", "consolidate sketch findings" | `/gsd-sketch --wrap-up` | Package sketch findings into reusable skill | -| Exploring, researching, comparing, or "how does X work" | `/gsd-research-phase` | Domain research before planning | +| Exploring, researching, comparing, or "how does X work" | `/gsd-explore` | Socratic ideation and idea routing | | Discussing vision, "how should X look", brainstorming | `/gsd-discuss-phase` | Needs context gathering | | A complex task: refactoring, migration, multi-file architecture, system redesign | `/gsd-phase` | Needs a full phase with plan/build cycle | | Planning a specific phase or "plan phase N" | `/gsd-plan-phase` | Direct planning request | @@ -60,7 +60,7 @@ Evaluate `$ARGUMENTS` against these routing rules. Apply the **first matching** | Completing a milestone, shipping, releasing | `/gsd-complete-milestone` | Milestone lifecycle | | A specific, actionable, small task (add feature, fix typo, update config) | `/gsd-quick` | Self-contained, single executor | -**Requires `.planning/` directory:** All routes except `/gsd-new-project`, `/gsd-map-codebase`, `/gsd-spike`, `/gsd-sketch`, `/gsd-help`, and `/gsd-join-discord`. If the project doesn't exist and the route requires it, suggest `/gsd-new-project` first. +**Requires `.planning/` directory:** All routes except `/gsd-new-project`, `/gsd-map-codebase`, `/gsd-spike`, `/gsd-sketch`, and `/gsd-help`. If the project doesn't exist and the route requires it, suggest `/gsd-new-project` first. **Ambiguity handling:** If the text could reasonably match multiple routes, ask the user via AskUserQuestion with the top 2-3 options. For example: diff --git a/get-shit-done/workflows/help.md b/get-shit-done/workflows/help.md index 2a4b4e6ed..4fe27944f 100644 --- a/get-shit-done/workflows/help.md +++ b/get-shit-done/workflows/help.md @@ -48,9 +48,13 @@ Creates all `.planning/` artifacts: Usage: `/gsd-new-project` -**`/gsd-map-codebase`** +**`/gsd-map-codebase [--fast] [--focus ] [--query ]`** Map an existing codebase for brownfield projects. +- `--fast` — rapid lightweight assessment (replaces the former `gsd-scan`) +- `--focus ` — scope the map to a specific area +- `--query ` — query the codebase intelligence index in `.planning/intel/` (replaces the former `gsd-intel`) + - Analyzes codebase with parallel Explore agents - Creates `.planning/codebase/` with 7 focused documents - Covers stack, architecture, structure, conventions, testing, integrations, concerns @@ -60,9 +64,13 @@ Usage: `/gsd-map-codebase` ### Phase Planning -**`/gsd-discuss-phase `** +**`/gsd-discuss-phase [--chain | --analyze | --power] [--batch[=N]]`** Help articulate your vision for a phase before planning. +- `--chain` — chained-prompt discuss flow +- `--analyze` — deep assumption analysis pass +- `--power` — power-user mode with extended question set + - Captures how you imagine this phase working - Creates CONTEXT.md with your vision, essentials, and boundaries - Use when you have ideas about how something should look/feel @@ -72,28 +80,15 @@ Usage: `/gsd-discuss-phase 2` Usage: `/gsd-discuss-phase 2 --batch` Usage: `/gsd-discuss-phase 2 --batch=3` -**`/gsd-research-phase `** -Comprehensive ecosystem research for niche/complex domains. - -- Discovers standard stack, architecture patterns, pitfalls -- Creates RESEARCH.md with "how experts build this" knowledge -- Use for 3D, games, audio, shaders, ML, and other specialized domains -- Goes beyond "which library" to ecosystem knowledge - -Usage: `/gsd-research-phase 3` - -**`/gsd-list-phase-assumptions `** -See what Claude is planning to do before it starts. - -- Shows Claude's intended approach for a phase -- Lets you course-correct if Claude misunderstood your vision -- No files created - conversational output only - -Usage: `/gsd-list-phase-assumptions 3` - -**`/gsd-plan-phase `** +**`/gsd-plan-phase [--skip-research] [--gaps] [--skip-verify] [--tdd] [--mvp]`** Create detailed execution plan for a specific phase. +- `--skip-research` — bypass the research subagent +- `--gaps` — focus only on closing gaps from a prior plan-check +- `--skip-verify` — skip the post-plan verifier loop +- `--tdd` — plan in test-driven order (tests before code) +- `--mvp` — vertical-slice MVP planning mode + - Generates `.planning/phases/XX-phase-name/XX-YY-PLAN.md` - Breaks phase into concrete, actionable tasks - Includes verification criteria and success measures @@ -106,9 +101,13 @@ Result: Creates `.planning/phases/01-foundation/01-01-PLAN.md` ### Execution -**`/gsd-execute-phase `** +**`/gsd-execute-phase [--wave N] [--gaps-only] [--tdd]`** Execute all plans in a phase, or run a specific wave. +- `--wave N` — execute only wave N (see *Plans within each wave* below) +- `--gaps-only` — re-run only plans flagged as gaps by a prior verifier +- `--tdd` — enforce test-driven order during execution + - Groups plans by wave (from frontmatter), executes waves sequentially - Plans within each wave run in parallel via Task tool - Optional `--wave N` flag executes only Wave `N` and stops unless the phase is now fully complete @@ -120,7 +119,7 @@ Usage: `/gsd-execute-phase 5 --wave 2` ### Smart Router -**`/gsd-do `** +**`/gsd-progress --do ""`** Route freeform text to the right GSD command automatically. - Analyzes natural language input to find the best matching GSD command @@ -128,9 +127,9 @@ Route freeform text to the right GSD command automatically. - Resolves ambiguity by asking you to pick between top matches - Use when you know what you want but don't know which `/gsd-*` command to run -Usage: `/gsd-do fix the login button` -Usage: `/gsd-do refactor the auth system` -Usage: `/gsd-do I want to start a new milestone` +Usage: `/gsd-progress --do "fix the login button"` +Usage: `/gsd-progress --do "refactor the auth system"` +Usage: `/gsd-progress --do "I want to start a new milestone"` ### Quick Mode @@ -202,6 +201,12 @@ Remove a future phase and renumber subsequent phases. Usage: `/gsd-phase --remove 17` Result: Phase 17 deleted, phases 18-20 become 17-19 +**`/gsd-phase --edit [--force]`** +Edit any field of an existing roadmap phase in place, preserving number and position. + +- Updates title, description, requirements, dependencies in `ROADMAP.md` +- `--force` allows editing already-started phases (use with caution) + ### Milestone Management **`/gsd-new-milestone `** @@ -230,7 +235,7 @@ Usage: `/gsd-complete-milestone 1.0.0` ### Progress Tracking -**`/gsd-progress`** +**`/gsd-progress [--next | --forensic | --do ""]`** Check project status and intelligently route to next action. - Shows visual progress bar and completion percentage @@ -240,7 +245,15 @@ Check project status and intelligently route to next action. - Offers to execute next plan or create it if missing - Detects 100% milestone completion +Modes: +- **default** — progress report + intelligent routing +- **`--next`** — auto-advance to the next logical step (use `--next --force` to bypass safety gates) +- **`--forensic`** — append a 6-check integrity audit after the progress report +- **`--do ""`** — smart router: dispatch freeform intent to the matching `/gsd-*` command (see *Smart Router* above) + Usage: `/gsd-progress` +Usage: `/gsd-progress --next` +Usage: `/gsd-progress --forensic` ### Session Management @@ -264,9 +277,11 @@ Usage: `/gsd-pause-work` ### Debugging -**`/gsd-debug [issue description]`** +**`/gsd-debug [issue description] [--diagnose]`** Systematic debugging with persistent state across context resets. +- `--diagnose` — run a one-shot diagnostic pass without opening a persistent debug session + - Gathers symptoms through adaptive questioning - Creates `.planning/debug/[slug].md` to track investigation - Investigates using scientific method (evidence → hypothesis → test) @@ -327,25 +342,10 @@ Package sketch design findings into a persistent project skill. Usage: `/gsd-sketch --wrap-up` -### Quick Notes - -**`/gsd-note `** -Zero-friction idea capture — one command, instant save, no questions. - -- Saves timestamped note to `.planning/notes/` (or `~/.claude/notes/` globally) -- Three subcommands: append (default), list, promote -- Promote converts a note into a structured todo -- Works without a project (falls back to global scope) - -Usage: `/gsd-note refactor the hook system` -Usage: `/gsd-note list` -Usage: `/gsd-note promote 3` -Usage: `/gsd-note --global cross-project idea` - -### Todo Management +### Capturing Ideas, Notes, and Todos **`/gsd-capture [description]`** -Capture idea or task as todo from current conversation. +Capture an idea or task as a structured todo from current conversation. - Extracts context from conversation (or uses provided description) - Creates structured todo file in `.planning/todos/pending/` @@ -356,17 +356,30 @@ Capture idea or task as todo from current conversation. Usage: `/gsd-capture` (infers from conversation) Usage: `/gsd-capture Add auth token refresh` -**`/gsd-check-todos [area]`** +**`/gsd-capture --note `** +Zero-friction note capture — one command, instant save, no questions. + +- Saves timestamped note to `.planning/notes/` (or `~/.claude/notes/` globally) +- Three subcommands: append (default), list, promote +- Promote converts a note into a structured todo +- Works without a project (falls back to global scope) + +Usage: `/gsd-capture --note refactor the hook system` +Usage: `/gsd-capture --note list` +Usage: `/gsd-capture --note promote 3` +Usage: `/gsd-capture --note --global cross-project idea` + +**`/gsd-capture --list [area]`** List pending todos and select one to work on. - Lists all pending todos with title, area, age -- Optional area filter (e.g., `/gsd-check-todos api`) +- Optional area filter (e.g., `/gsd-capture --list api`) - Loads full context for selected todo - Routes to appropriate action (work now, add to phase, brainstorm) - Moves todo to done/ when work begins -Usage: `/gsd-check-todos` -Usage: `/gsd-check-todos api` +Usage: `/gsd-capture --list` +Usage: `/gsd-capture --list api` ### User Acceptance Testing @@ -420,14 +433,23 @@ Usage: `/gsd-pr-branch` or `/gsd-pr-branch main` --- -**`/gsd-plant-seed [idea]`** +**`/gsd-capture --seed [idea]`** Capture a forward-looking idea with trigger conditions for automatic surfacing. - Seeds preserve WHY, WHEN to surface, and breadcrumbs to related code - Auto-surfaces during `/gsd-new-milestone` when trigger conditions match - Better than deferred items — triggers are checked, not forgotten -Usage: `/gsd-plant-seed "add real-time notifications when we build the events system"` +Usage: `/gsd-capture --seed "add real-time notifications when we build the events system"` + +**`/gsd-capture --backlog [description]`** +Add an idea to the backlog parking lot for future milestones. + +- Creates a backlog item under 999.x numbering in ROADMAP.md +- Reserves ideas without committing to the current milestone +- Surface and promote later via `/gsd-review-backlog` + +Usage: `/gsd-capture --backlog "real-time notifications when events ship"` --- @@ -452,16 +474,6 @@ Audit milestone completion against original intent. Usage: `/gsd-audit-milestone` -**`/gsd-plan-milestone-gaps`** -Create phases to close gaps identified by audit. - -- Reads MILESTONE-AUDIT.md and groups gaps into phases -- Prioritizes by requirement priority (must/should/nice) -- Adds gap closure phases to ROADMAP.md -- Ready for `/gsd-plan-phase` on new phases - -Usage: `/gsd-plan-milestone-gaps` - ### Configuration **`/gsd-settings`** @@ -473,8 +485,12 @@ Configure workflow toggles and model profile interactively. Usage: `/gsd-settings` -**`/gsd-config --profile `** -Quick switch model profile for GSD agents. +**`/gsd-config [--profile | --advanced | --integrations]`** +Configure GSD beyond the basic settings: model profile, advanced tuning, and third-party integrations. + +- `--profile ` — quick switch model profile (`quality | balanced | budget | inherit`) +- `--advanced` — power-user tuning: plan bounce, timeouts, branch templates, cross-AI execution (replaces the former `gsd-settings-advanced`) +- `--integrations` — third-party API keys, code-review CLI routing, agent-skill injection (replaces the former `gsd-settings-integrations`) - `quality` — Opus everywhere except verification - `balanced` — Opus for planning, Sonnet for execution (default) @@ -498,9 +514,12 @@ Usage: `/gsd-cleanup` **`/gsd-help`** Show this command reference. -**`/gsd-update`** +**`/gsd-update [--sync] [--reapply]`** Update GSD to latest version with changelog preview. +- `--sync` — sync managed GSD skills across runtime roots (replaces the former `gsd-sync-skills`) +- `--reapply` — reapply local modifications after an update (replaces the former `gsd-reapply-patches`) + - Shows installed vs latest version comparison - Displays changelog entries for versions you've missed - Highlights breaking changes @@ -509,13 +528,72 @@ Update GSD to latest version with changelog preview. Usage: `/gsd-update` -**`/gsd-join-discord`** -Join the GSD Discord community. +## Additional Commands -- Get help, share what you're building, stay updated -- Connect with other GSD users +The commands above cover the most common day-to-day flows. Every command listed here is also a live `/gsd-*` slash command and is grouped by purpose. -Usage: `/gsd-join-discord` +### Discovery & Specification + +- **`/gsd-explore`** — Socratic ideation and idea routing. Think through ideas before committing to plans. +- **`/gsd-spec-phase [--auto] [--text]`** — Clarify WHAT a phase delivers with ambiguity scoring; produces a SPEC.md before discuss-phase. +- **`/gsd-ai-integration-phase [phase]`** — Generate an AI-SPEC.md design contract for phases that involve building AI systems. +- **`/gsd-ui-phase [phase]`** — Generate UI design contract (UI-SPEC.md) for frontend phases. +- **`/gsd-import --from `** — Ingest external plans with conflict detection against project decisions before writing anything. +- **`/gsd-ingest-docs [path] [--mode new|merge] [--manifest ] [--resolve auto|interactive]`** — Bootstrap or merge a `.planning/` setup from existing ADRs, PRDs, SPECs, and docs in a repo. + +### Planning & Execution + +- **`/gsd-ultraplan-phase [phase]`** — [BETA] Offload plan phase to Claude Code's ultraplan cloud; review in browser and import back. +- **`/gsd-plan-review-convergence [--codex] [--gemini] [--claude] [--opencode] [--ollama] [--lm-studio] [--llama-cpp] [--all] [--text] [--ws ] [--max-cycles N]`** — Cross-AI plan convergence loop — replan with review feedback until no HIGH concerns remain. Supports both cloud reviewers (Codex/Gemini/Claude/OpenCode) and local model runtimes (Ollama, LM Studio, llama.cpp). +- **`/gsd-autonomous [--from N] [--to N] [--only N] [--interactive]`** — Run all remaining phases autonomously: discuss → plan → execute per phase. + +### Quality, Review & Verification + +- **`/gsd-code-review [--depth=quick|standard|deep] [--files file1,file2,...] [--fix [--all] [--auto]]`** — Review source files changed during a phase for bugs, security issues, and code quality problems. +- **`/gsd-secure-phase [phase]`** — Retroactively verify threat mitigations for a completed phase. +- **`/gsd-validate-phase [phase]`** — Retroactively audit and fill Nyquist validation gaps for a completed phase. +- **`/gsd-ui-review [phase]`** — Retroactive 6-pillar visual audit of implemented frontend code. +- **`/gsd-eval-review [phase]`** — Audit an executed AI phase's evaluation coverage and produce an EVAL-REVIEW.md remediation plan. +- **`/gsd-audit-fix --source [--severity medium|high|all] [--max N] [--dry-run]`** — Autonomous audit-to-fix pipeline: find issues, classify, fix, test, commit. +- **`/gsd-add-tests [additional instructions]`** — Generate tests for a completed phase based on UAT criteria and implementation. + +### Diagnostics & Maintenance + +- **`/gsd-health [--repair] [--context]`** — Diagnose planning directory health and optionally repair issues. +- **`/gsd-forensics [problem description]`** — Post-mortem investigation for failed GSD workflows; diagnoses what went wrong. +- **`/gsd-undo --last N | --phase NN | --plan NN-MM`** — Safe git revert. Roll back phase or plan commits using the phase manifest with dependency checks. +- **`/gsd-docs-update [--force] [--verify-only]`** — Generate or update project documentation verified against the codebase. +- **`/gsd-extract-learnings `** — Extract decisions, lessons, patterns, and surprises from completed phase artifacts. + +### Knowledge & Context + +- **`/gsd-graphify [build|query |status|diff]`** — Build, query, and inspect the project knowledge graph in `.planning/graphs/`. +- **`/gsd-thread [list [--open|--resolved] | close | status | name | description]`** — Manage persistent context threads for cross-session work. +- **`/gsd-profile-user [--questionnaire] [--refresh]`** — Generate developer behavioral profile and create Claude-discoverable artifacts. +- **`/gsd-stats`** — Display project statistics: phases, plans, requirements, git metrics, and timeline. + +### Workflow & Orchestration + +- **`/gsd-manager`** — Interactive command center for managing multiple phases from one terminal. +- **`/gsd-workspace [--new | --list | --remove] [name]`** — Manage GSD workspaces: create, list, or remove isolated workspace environments. +- **`/gsd-workstreams`** — Manage parallel workstreams: list, create, switch, status, progress, complete, and resume. +- **`/gsd-review-backlog`** — Review and promote backlog items to active milestone. +- **`/gsd-milestone-summary [version]`** — Generate a comprehensive project summary from milestone artifacts for team onboarding and review. + +### Repository Integration + +- **`/gsd-inbox [--issues] [--prs] [--label] [--close-incomplete] [--repo owner/repo]`** — Triage and review open GitHub issues and PRs against project templates and contribution guidelines. + +### Namespace Routers (model-facing meta-skills) + +These six skills exist primarily for the model to perform two-stage hierarchical routing across 60+ skills. You can invoke them directly when you want to browse a category interactively. + +- **`/gsd-context`** — Codebase intelligence routing (map, graphify, docs, learnings). +- **`/gsd-ideate`** — Exploration / capture routing (explore, sketch, spike, spec, capture). +- **`/gsd-manage`** — Configuration and workspace routing (workstreams, thread, update, ship, inbox). +- **`/gsd-project`** — Project-lifecycle routing (milestones, audits, summary). +- **`/gsd-review`** — Quality-gate routing (code review, debug, audit, security, eval, ui). +- **`/gsd-workflow`** — Phase-pipeline routing (discuss, plan, execute, verify, phase, progress). ## Files & Structure @@ -643,10 +721,12 @@ Example config: **Capturing ideas during work:** ``` -/gsd-capture # Capture from conversation context -/gsd-capture Fix modal z-index # Capture with explicit description -/gsd-check-todos # Review and work on todos -/gsd-check-todos api # Filter by area +/gsd-capture # Capture from conversation context +/gsd-capture Fix modal z-index # Capture with explicit description +/gsd-capture --note refactor auth system # Quick friction-free note +/gsd-capture --seed "real-time notifications" # Forward-looking idea with triggers +/gsd-capture --list # Review and work on todos +/gsd-capture --list api # Filter by area ``` **Debugging an issue:** diff --git a/tests/bug-2954-help-md-slash-command-stubs.test.cjs b/tests/bug-2954-help-md-slash-command-stubs.test.cjs new file mode 100644 index 000000000..0b5dceb9e --- /dev/null +++ b/tests/bug-2954-help-md-slash-command-stubs.test.cjs @@ -0,0 +1,165 @@ +'use strict'; + +process.env.GSD_TEST_MODE = '1'; + +/** + * Bug #2954: keep `help.md` and the live `commands/gsd/*` slash surface + * in lockstep. Two regression tests: + * + * 1. help.md must not advertise any /gsd- that has no shipped + * slash command. (Caught the original #2954 regression: #2824 deleted + * 31 stubs without updating help.md.) + * + * 2. Every shipped /gsd- command must appear in help.md. (Caught + * the inverse: a command lands without docs, so users never discover it.) + * + * The shipped slash name is parsed from frontmatter `name:` (which can be + * either `gsd:foo` or `gsd-foo` — Claude Code surfaces both as `/gsd-foo`), + * NOT from the filename, because some files (e.g. `ns-context.md`) ship a + * different slash name (`gsd-context`) than their filename suggests. + * + * Also covers `do.md`, the dispatcher invoked at runtime by + * `/gsd-progress --do`: any `/gsd-` token in its routing table must + * resolve to a live command, otherwise the dispatcher emits "Unknown command". + */ + +const { test, describe } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); + +const ROOT = path.join(__dirname, '..'); +const COMMANDS_DIR = path.join(ROOT, 'commands', 'gsd'); +const HELP_MD = path.join(ROOT, 'get-shit-done', 'workflows', 'help.md'); +const DO_MD = path.join(ROOT, 'get-shit-done', 'workflows', 'do.md'); + +function parseFrontmatter(content) { + const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/); + if (!match) return null; + const fields = {}; + for (const line of match[1].split(/\r?\n/)) { + const fieldMatch = line.match(/^([a-zA-Z0-9_-]+):\s*(.*)$/); + if (!fieldMatch) continue; + const value = fieldMatch[2].trim().replace(/^["']|["']$/g, ''); + fields[fieldMatch[1]] = value; + } + return fields; +} + +/** + * Returns the set of slash-base-names actually shipped under commands/gsd/. + * A "slash-base-name" is the part after `/gsd-` — e.g. for frontmatter + * `name: gsd:foo` or `name: gsd-foo`, the slash-base-name is `foo`. + */ +function listShippedSlashBaseNames() { + const names = new Set(); + for (const entry of fs.readdirSync(COMMANDS_DIR, { withFileTypes: true })) { + if (!entry.isFile() || !entry.name.endsWith('.md')) continue; + const content = fs.readFileSync(path.join(COMMANDS_DIR, entry.name), 'utf8'); + const fm = parseFrontmatter(content); + if (!fm || !fm.name) continue; + const fmName = fm.name; + let base = null; + if (fmName.startsWith('gsd:')) base = fmName.slice(4); + else if (fmName.startsWith('gsd-')) base = fmName.slice(4); + if (base && /^[a-z][a-z0-9-]*$/.test(base)) names.add(base); + } + return names; +} + +function extractSlashReferences(contents) { + const names = new Set(); + const tokenRe = /\/gsd-([a-z][a-z0-9-]*)/g; + let match; + while ((match = tokenRe.exec(contents)) !== null) { + names.add(match[1]); + } + return names; +} + +/** + * For every shipped command with an `argument-hint:` frontmatter entry, + * collect the `--flag` tokens it advertises. Returns a Map>. Flags are recorded without their leading `--`. + */ +function listShippedFlagsByCommand() { + const out = new Map(); + for (const entry of fs.readdirSync(COMMANDS_DIR, { withFileTypes: true })) { + if (!entry.isFile() || !entry.name.endsWith('.md')) continue; + const content = fs.readFileSync(path.join(COMMANDS_DIR, entry.name), 'utf8'); + const fm = parseFrontmatter(content); + if (!fm || !fm.name || !fm['argument-hint']) continue; + const fmName = fm.name; + let base = null; + if (fmName.startsWith('gsd:')) base = fmName.slice(4); + else if (fmName.startsWith('gsd-')) base = fmName.slice(4); + if (!base || !/^[a-z][a-z0-9-]*$/.test(base)) continue; + const flags = new Set(); + for (const m of fm['argument-hint'].matchAll(/--([a-z][a-z0-9-]*)/g)) { + flags.add(m[1]); + } + if (flags.size) out.set(base, flags); + } + return out; +} + +describe('Bug #2954: help.md ↔ commands/gsd/ bidirectional parity', () => { + test('every /gsd- referenced in help.md is a shipped command', () => { + const helpContents = fs.readFileSync(HELP_MD, 'utf8'); + const referenced = extractSlashReferences(helpContents); + const shipped = listShippedSlashBaseNames(); + const dangling = [...referenced].filter((n) => !shipped.has(n)).sort(); + assert.deepEqual( + dangling, + [], + `help.md advertises /gsd- commands that are not shipped: ${dangling.join(', ')}`, + ); + }); + + test('every shipped /gsd- command is documented in help.md', () => { + const helpContents = fs.readFileSync(HELP_MD, 'utf8'); + const referenced = extractSlashReferences(helpContents); + const shipped = listShippedSlashBaseNames(); + const undocumented = [...shipped].filter((n) => !referenced.has(n)).sort(); + assert.deepEqual( + undocumented, + [], + `commands shipped under commands/gsd/ with no /gsd- reference in help.md: ${undocumented.join(', ')}`, + ); + }); + + test('every /gsd- in do.md (live dispatcher) is a shipped command', () => { + const doContents = fs.readFileSync(DO_MD, 'utf8'); + const referenced = extractSlashReferences(doContents); + const shipped = listShippedSlashBaseNames(); + const dangling = [...referenced].filter((n) => !shipped.has(n)).sort(); + assert.deepEqual( + dangling, + [], + `do.md routing table references /gsd- that is not shipped: ${dangling.join(', ')}`, + ); + }); + + test('every --flag in a command\'s argument-hint appears in help.md', () => { + const helpContents = fs.readFileSync(HELP_MD, 'utf8'); + const flagsByCommand = listShippedFlagsByCommand(); + const gaps = []; + for (const [command, flags] of flagsByCommand) { + for (const flag of flags) { + // Accept `/gsd- --` (precise) OR a bare `--` token + // anywhere in help.md (good enough for shared flags like `--force` that + // appear under multiple commands' descriptions). + const preciseToken = `/gsd-${command} --${flag}`; + const flagToken = `--${flag}`; + if (!helpContents.includes(preciseToken) && !helpContents.includes(flagToken)) { + gaps.push(`/gsd-${command} --${flag}`); + } + } + } + assert.deepEqual( + gaps.sort(), + [], + `commands ship --flag(s) in argument-hint that are absent from help.md: ${gaps.join(', ')}`, + ); + }); +});