fix(#2954): align help.md with post-#2824 skill consolidation (#2959)

This commit is contained in:
Tom Boucher
2026-05-01 13:36:44 -04:00
committed by GitHub
parent c5dfdbe42e
commit 0d6abb87ac
4 changed files with 322 additions and 77 deletions

View File

@@ -10,7 +10,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- **`gsd-sdk query agent-skills` emits raw `<agent_skills>` block instead of JSON-wrapped string** — workflows that embed via `$(gsd-sdk query agent-skills <agent>)` were receiving a JSON-quoted string literal mid-prompt (e.g. `"<agent_skills>\n…"`), silently breaking all `<agent_skills>` 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

View File

@@ -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:

View File

@@ -48,9 +48,13 @@ Creates all `.planning/` artifacts:
Usage: `/gsd-new-project`
**`/gsd-map-codebase`**
**`/gsd-map-codebase [--fast] [--focus <area>] [--query <term>]`**
Map an existing codebase for brownfield projects.
- `--fast` — rapid lightweight assessment (replaces the former `gsd-scan`)
- `--focus <area>` — scope the map to a specific area
- `--query <term>` — 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 <number>`**
**`/gsd-discuss-phase <number> [--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 <number>`**
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 <number>`**
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 <number>`**
**`/gsd-plan-phase <number> [--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 <phase-number>`**
**`/gsd-execute-phase <phase-number> [--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 <description>`**
**`/gsd-progress --do "<description>"`**
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 <number> [--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 <name>`**
@@ -230,7 +235,7 @@ Usage: `/gsd-complete-milestone 1.0.0`
### Progress Tracking
**`/gsd-progress`**
**`/gsd-progress [--next | --forensic | --do "<description>"]`**
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 "<text>"`** — 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 <text>`**
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 <text>`**
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 <profile>`**
Quick switch model profile for GSD agents.
**`/gsd-config [--profile <profile> | --advanced | --integrations]`**
Configure GSD beyond the basic settings: model profile, advanced tuning, and third-party integrations.
- `--profile <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 <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 <filepath>`** — Ingest external plans with conflict detection against project decisions before writing anything.
- **`/gsd-ingest-docs [path] [--mode new|merge] [--manifest <file>] [--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 <phase> [--codex] [--gemini] [--claude] [--opencode] [--ollama] [--lm-studio] [--llama-cpp] [--all] [--text] [--ws <name>] [--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 <phase> [--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 <audit-uat> [--severity medium|high|all] [--max N] [--dry-run]`** — Autonomous audit-to-fix pipeline: find issues, classify, fix, test, commit.
- **`/gsd-add-tests <phase> [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 <phase>`** — Extract decisions, lessons, patterns, and surprises from completed phase artifacts.
### Knowledge & Context
- **`/gsd-graphify [build|query <term>|status|diff]`** — Build, query, and inspect the project knowledge graph in `.planning/graphs/`.
- **`/gsd-thread [list [--open|--resolved] | close <slug> | status <slug> | 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:**

View File

@@ -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-<name> that has no shipped
* slash command. (Caught the original #2954 regression: #2824 deleted
* 31 stubs without updating help.md.)
*
* 2. Every shipped /gsd-<name> 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-<name>` 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<slashBaseName,
* Set<flagName>>. 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-<name> 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-<name> commands that are not shipped: ${dangling.join(', ')}`,
);
});
test('every shipped /gsd-<name> 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-<name> reference in help.md: ${undocumented.join(', ')}`,
);
});
test('every /gsd-<name> 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-<name> 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-<command> --<flag>` (precise) OR a bare `--<flag>` 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(', ')}`,
);
});
});