Compare commits

...

19 Commits

Author SHA1 Message Date
Tom Boucher
b1a670e662 fix(#2697): replace retired /gsd: prefix with /gsd- in all user-facing text (#2699)
All workflow, command, reference, template, and tool-output files that
surfaced /gsd:<cmd> as a user-typed slash command have been updated to
use /gsd-<cmd>, matching the Claude Code skill directory name.

Closes #2697

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 10:59:33 -04:00
Tom Boucher
7c6f8005f3 test: destroy 9 config-schema.cjs/core.cjs source-grep tests, replace with behavioral config-set (#2696)
* test: destroy 9 config-schema.cjs/core.cjs source-grep tests, add behavioral config-set tests (#2691, #2693)

Replace source-grep theater with config-set behavioral tests:
- execute-phase-wave: config-set workflow.use_worktrees replaces VALID_CONFIG_KEYS grep
- inline-plan-threshold: delete redundant source-grep (behavioral test at L36 already covered it)
- plan-bounce: config-set for plan_bounce / plan_bounce_script / plan_bounce_passes replaces 3 key-presence greps
- code-review: config-set for code_review / code_review_depth replaces 2 greps; removes CONFIG_PATH constant
- thinking-partner: config-set features.thinking_partner replaces two greps (config-schema.cjs AND core.cjs)

Behavioral tests survive refactors (no path constants, no file reads). The config-schema.cjs →
core.cjs migration commit 990c3e64 happened because these tests groped source paths.

Add allow-test-rule: source-text-is-the-product annotations to legitimate product-content tests:
autonomous-allowed-tools, agent-frontmatter, agent-skills-awareness, bug-2334, bug-2346,
execute-phase-wave (MD reads), plan-bounce (workflow reads). Annotations explain WHY text
inspection is the right level of testing for AI instruction files.

Closes #2691
Closes #2693

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: address CodeRabbit findings on #2696

- agent-frontmatter.test.cjs: move allow-test-rule annotation from block comment
  to standalone // line comment so rule scanners can detect it
- thinking-partner.test.cjs: strengthen config-set test with config-get read-back
  assertion to verify the value was persisted, not just accepted (exit 0)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: tighten thinking_partner config assertion per CodeRabbit (#2696)

Replace config-get output substring check (includes('true') false-positive
risk) with a direct JSON read of .planning/config.json, asserting the
exact persisted value via strictEqual. This also validates the config file
was created, catching silent key-acceptance without persistence.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 10:50:54 -04:00
Tom Boucher
cd05725576 fix(#2661): unconditional plan-checkbox sync in execute-plan (#2682)
* fix(#2661): unconditional plan-checkbox sync in execute-plan

Checkpoint A in execute-plan.md was wrapped in a "Skip in parallel mode"
guard that also short-circuited the parallelization-without-worktrees
case. With `parallelization: true, use_worktrees: false`, only
Checkpoint C (phase.complete) then remained, and any interruption
between the final SUMMARY write and phase complete left ROADMAP.md
plan checkboxes stale.

Remove the guard: `roadmap update-plan-progress` is idempotent and
atomically serialized via readModifyWriteRoadmapMd's lockfile, so
concurrent invocations from parallel plans converge safely.

Checkpoint B (worktree-merge post-step) and Checkpoint C
(phase.complete) become redundant after A is unconditional; their
removal is deferred to a follow-up per the RCA.

Closes #2661

* fix(#2661): gate ROADMAP sync on use_worktrees=false to preserve single-writer contract

Adversarial review of PR #2682 found that unconditionally removing the
IS_WORKTREE guard violates the single-writer contract for shared
ROADMAP.md established by commit dcb50396 (PR #1486). The lockfile only
serializes within a single working tree; separate worktrees have
separate ROADMAP.md files that diverge.

Restore the worktree guard but document its intent explicitly: the
in-handler sync runs only when use_worktrees=false (the actual #2661
reproducer). Worktree mode relies on the orchestrator's post-merge
update at execute-phase.md lines 815-834, which is the documented
single-writer for shared tracking files.

Update tests to assert both branches of the gate:
- use_worktrees: false mode runs the sync (the #2661 case)
- use_worktrees: true mode does NOT run the in-handler sync
- handler-level idempotence and lockfile contention tests retained,
  scope clarified to within-tree concurrency only
2026-04-24 20:27:59 -04:00
Tom Boucher
c811792967 fix(#2660): capture prose after labeled bold in extractOneLinerFromBody (#2679)
* fix(#2660): capture prose after label in extractOneLinerFromBody

The regex `\*\*([^*]+)\*\*` matched the first bold span, so for the new
SUMMARY template `**One-liner:** Real prose here.` it captured the label
`One-liner:` instead of the prose. MILESTONES.md then wrote bullets like
`- One-liner:` with no content.

Handle both template forms:
- Labeled:  `**One-liner:** prose`  → prose
- Bare:     `**prose**`             → prose (legacy)

Empty prose after a label returns null so no bogus bullets are emitted.

Note: existing MILESTONES.md entries generated under the bug are not
regenerated here — that is a follow-up.

Closes #2660

* fix(#2660): normalize CRLF before one-liner extraction

Windows-authored SUMMARY files use CRLF line endings; the LF-only regex
in extractOneLinerFromBody would fail to match. Normalize \r\n and \r
to \n before stripping frontmatter and matching the one-liner pattern.

Adds test case (h) covering CRLF input.
2026-04-24 20:22:29 -04:00
Tom Boucher
34b39f0a37 test(#2659): regression guard against bare output() in audit-open handler (#2680)
* fix(#2659): qualify bare output() calls in audit-open handler

The audit-open dispatch case in bin/gsd-tools.cjs previously called bare
output() on both --json and text branches, which crashed with
ReferenceError: output is not defined. The core module is imported as
`const core`, so every other case uses core.output(). HEAD already
qualifies the calls correctly; this commit adds a regression test that
invokes `audit-open` and `audit-open --json` through runGsdTools and
asserts a clean exit plus non-empty stdout (and an explicit check that
the failure mode is not ReferenceError). The test fails on any revision
where either call reverts to bare output().

Closes #2659

* test(#2659): assert valid JSON output in --json mode

CodeRabbit nit: tighten --json regression coverage by parsing stdout
and asserting the result is a JSON object/array, not just non-empty.
2026-04-24 20:22:17 -04:00
Tom Boucher
b1278f6fc3 fix(#2674): align initProgress with initManager ROADMAP [x] precedence (#2681)
initProgress computed phase status purely from disk (PLAN/SUMMARY counts),
consulting the ROADMAP `- [x] Phase N` checkbox only for phases with no
directory. initManager, by contrast, applied an explicit override: a
ROADMAP `[x]` forces status to `complete` regardless of disk state.

Result: a phase with a stub directory (no SUMMARY.md) and a ticked
ROADMAP checkbox reported `complete` from /gsd-manager and `pending`
from /gsd-progress — same data, different answer.

Apply ROADMAP-[x]-wins as the unified policy inside initProgress, mirroring
initManager's override. A user who typed `- [x] Phase 3` has made an
explicit assertion; a leftover stub dir is the weaker signal.

Adds sdk/src/query/init-progress-precedence.test.ts covering six cases
(stub dir + [x], full dir + [x], full dir + [ ], stub dir + [ ],
ROADMAP-only + [x], and completed_count parity). Pre-fix: cases 1 and 6
failed. Post-fix: all six pass. No existing tests were modified.

Closes #2674
2026-04-24 20:20:11 -04:00
Tom Boucher
303fd26b45 fix(#2662): add state.add-roadmap-evolution SDK handler; insert-phase uses it (#2683)
/gsd-insert-phase step 4 instructed the agent to directly Edit/Write
.planning/STATE.md to append a Roadmap Evolution entry. Projects that
ship a protect-files.sh PreToolUse hook (a recommended hardening
pattern) blocked the raw write, silently leaving STATE.md out of sync
with ROADMAP.md.

Adds a dedicated SDK handler state.add-roadmap-evolution (plus space
alias) that:

  - Reads STATE.md through the shared readModifyWriteStateMd lockfile
    path (matches sibling mutation handlers — atomic against
    concurrent writers).
  - Locates ### Roadmap Evolution under ## Accumulated Context, or
    creates both sections as needed.
  - Dedupes on exact-line match so idempotent retries are no-ops
    ({ added: false, reason: "duplicate" }).
  - Validates --phase / --action presence and action membership,
    throwing GSDError(Validation) for bad input (no silent
    { ok: false } swallow).

Workflow change (insert-phase.md step 4):

  - Replaces the raw Edit/Write instructions for STATE.md with
    gsd-sdk query state.patch (for the next-phase pointer) and
    gsd-sdk query state.add-roadmap-evolution (for the evolution
    log).
  - Updates success criteria to check handler responses.
  - Drops "Write" from commands/gsd/insert-phase.md allowed-tools
    (no step in the workflow needs it any more).

Tests (vitest, sdk/src/query/state-mutation.test.ts): subsection
creation when missing; append-preserving-order when present;
duplicate -> reason=duplicate; idempotence over two calls; three
validation cases covering missing --phase, missing --action, and
invalid action.

This is the first SDK handler dedicated to STATE.md Roadmap
Evolution mutations. Other workflows with similar raw STATE.md
edits (/gsd-pause-work, /gsd-resume-work, /gsd-new-project,
/gsd-complete-milestone, /gsd-add-phase) remain on raw Edit/Write
and will need follow-up issues to migrate — out of scope for this
fix.

Closes #2662
2026-04-24 20:20:02 -04:00
Tom Boucher
7b470f2625 fix(#2633): ROADMAP.md is the authority for current-milestone phase counts (#2665)
* fix(#2633): use ROADMAP.md as authority for current-milestone phase counts

initMilestoneOp (SDK + CJS) derives phase_count and completed_phases from
the current milestone section of ROADMAP.md instead of counting on-disk
`.planning/phases/` directories. After `phases clear` at the start of a new
milestone the on-disk set is a subset of the roadmap, causing premature
`all_phases_complete: true`.

validateHealth W002 now unions ROADMAP.md phase declarations (all milestones
— current, shipped, backlog) with on-disk dirs when checking STATE.md phase
refs. Eliminates false positives for future-phase refs in the current
milestone and history-phase refs from shipped milestones.

Falls back to legacy on-disk counting when ROADMAP.md is missing or
unparseable so no-roadmap fixtures still work.

Adds vitest regressions for both handlers; all 66 SDK + 118 CJS tests pass.

* fix(#2633): preserve full phase tokens in W002 + completion lookup

CodeRabbit flagged that the parseInt-based normalization collapses distinct
phase IDs (3, 3A, 3.1) into the same integer bucket, masking real
STATE/ROADMAP mismatches and miscounting completions in milestones with
inserted/sub-phases.

Index disk dirs and validate STATE.md refs by canonical full phase token —
strip leading zeros from the integer head only, preserve [A-Z] suffix and
dotted segments, and accept just the leading-zero variant of the integer
prefix as a tolerated alias. 3A and 3 never share a bucket.

Also widens the disk and STATE.md regexes to accept [A-Z]? suffix tokens.
2026-04-24 18:11:12 -04:00
Tom Boucher
c8ae6b3b4f fix(#2636): surface gsd-sdk query failures and add workflow↔handler parity check (#2656)
* fix(#2636): surface gsd-sdk query failures and add workflow↔handler parity check

Root cause: workflows invoked `gsd-sdk query agent-skills <slug>` with a
trailing `2>/dev/null`, swallowing stderr and exit code. When the installed
`@gsd-build/sdk` npm was stale (pre-query), the call resolved to an empty
string and `agent_skills.<slug>` config was never injected into spawn
prompts — silently. The handler exists on main (sdk/src/query/skills.ts),
so this is a publish-drift + silent-fallback bug, not a missing handler.

Fix:
- Remove bare `2>/dev/null` from every `gsd-sdk query agent-skills …`
  invocation in workflows so SDK failures surface to stderr.
- Apply the same rule to other no-fallback calls (audit-open, write-profile,
  generate-* profile handlers, frontmatter.get in commands). Best-effort
  cleanup calls (config-set workflow._auto_chain_active false) keep
  exit-code forgiveness via `|| true` but no longer suppress stderr.

Parity tests:
- New: tests/bug-2636-gsd-sdk-query-silent-swallow.test.cjs — fails if any
  `gsd-sdk query agent-skills … 2>/dev/null` is reintroduced.
- Existing: tests/gsd-sdk-query-registry-integration.test.cjs already
  asserts every workflow noun resolves to a registered handler; confirmed
  passing post-change.

Note: npm republish of @gsd-build/sdk is a separate release concern and is
not included in this PR.

* fix(#2636): address review — restore broken markdown fences and shell syntax

The previous commit's mass removal of '2>/dev/null' suffixes also
collapsed adjacent closing code fences and 'fi' tokens onto the
command line, producing malformed markdown blocks and 'truefi' /
'true   fi' shell syntax errors in the workflows.

Repaired sites:
- commands/gsd/quick.md, thread.md (frontmatter.get fences)
- workflows/complete-milestone.md (audit-open fence)
- workflows/profile-user.md (write-profile + generate-* fences)
- workflows/verify-work.md (audit-open --json fence)
- workflows/execute-phase.md (truefi -> true / fi)
- workflows/plan-phase.md, discuss-phase-assumptions.md,
  discuss-phase/modes/chain.md (true   fi -> true / fi)

All 5450 tests pass.
2026-04-24 18:10:45 -04:00
Tom Boucher
7ed05c8811 fix(#2645): emit [[agents]] array-of-tables in Codex config.toml (#2664)
* fix(#2645): emit [[agents]] array-of-tables in Codex config.toml

Codex ≥0.116 rejects `[agents.<name>]` map tables with `invalid type:
map, expected a sequence`. Switch generateCodexConfigBlock to emit
`[[agents]]` array-of-tables with an explicit `name` field per entry.

Strip + merge paths now self-heal on reinstall — both the legacy
`[agents.gsd-*]` map shape (pre-#2645 configs) and the new
`[[agents]]` with `name = "gsd-*"` shape are recognized and replaced,
while user-authored `[[agents]]` entries are preserved.

Fixes #2645

* fix(#2645): use TOML-aware parser to strip managed [[agents]] sections

CodeRabbit flagged that the prior regex-based stripper for [[agents]]
array-of-tables only matched headers at column 0 and stopped at any line
beginning with `[`. An indented [[agents]] header would not terminate the
preceding match, so a managed `gsd-*` block could absorb a following
user-authored agent and silently delete it.

Replace the ad-hoc regex with the existing TOML-aware section parser
(getTomlTableSections + removeContentRanges) so section boundaries are
authoritative regardless of indentation. Same logic applies to legacy
[agents.gsd-*] map sections.

Add a comprehensive mixed-shape test covering multiple GSD entries (both
legacy map and new array-of-tables, double- and single-quoted names)
interleaved with multiple user-authored agents in both shapes — verifies
all GSD entries are stripped and every user entry is preserved.
2026-04-24 18:09:01 -04:00
Tom Boucher
0f8f7537da fix(#2652): layer ~/.gsd/defaults.json over built-ins in SDK loadConfig (#2663)
* fix(#2652): layer ~/.gsd/defaults.json over built-ins in SDK loadConfig

SDK loadConfig only merged built-in CONFIG_DEFAULTS, so pre-project init
queries (e.g. resolveModel in Codex installs) ignored user-level knobs like
resolve_model_ids: "omit" and emitted Claude model aliases from MODEL_PROFILES.

Port the user-defaults layer from get-shit-done/bin/lib/config.cjs:65 to the
TS loader. CJS parity: user defaults only apply when no .planning/config.json
exists (buildNewProjectConfig already bakes them in at /gsd:new-project time).

Fixes #2652

* fix(#2652): isolate GSD_HOME in test, refresh loadConfig JSDoc (CodeRabbit)
2026-04-24 18:08:07 -04:00
Tom Boucher
709f0382bf fix(#2639): route Codex TOML emit through full Claude→Codex neutralization pipeline (#2657)
installCodexConfig() applied a narrow path-only regex pass before
generateCodexAgentToml(), skipping the convertClaudeToCodexMarkdown() +
neutralizeAgentReferences(..., 'AGENTS.md') pipeline used on the .md emit
path. Result: emitted Codex agent TOMLs carried stale Claude-specific
references (CLAUDE.md, .claude/skills/, .claude/commands/, .claude/agents/,
.claudeignore, bare "Claude" agent-name mentions).

Route the TOML path through convertClaudeToCodexMarkdown and extend that
pipeline to cover bare .claude/<subdir>/ references and .claudeignore
(both previously unhandled on the .md path too). The $HOME/.claude/
get-shit-done prefix substitution still runs first so the absolute Codex
install path is preserved before the generic .claude → .codex rewrite.

Regression test: tests/issue-2639-codex-toml-neutralization.test.cjs —
drives installCodexConfig against a fixture containing every flagged
marker and asserts the emitted TOML contains zero CLAUDE.md / .claude/
/ .claudeignore occurrences and that Claude Code / Claude Opus product
names survive.

Fixes #2639
2026-04-24 18:06:13 -04:00
Tom Boucher
a6e692f789 fix(#2646): honor ROADMAP [x] checkboxes when no phases/ directory exists (#2669)
initProgress (and its CJS twin) hardcoded `not_started` for ROADMAP-only
phases, so `completed_count` stayed at 0 even when the ROADMAP showed
`- [x] Phase N`. Extract ROADMAP checkbox states into a shared helper
and use `- [x]` as the completion signal when no phase directory is
present. Disk status continues to win when both exist.

Adds a regression test that reproduces the bug with no phases/ dir and
one `[x]` / one `[ ]` phase, asserting completed_count===1.

Fixes #2646
2026-04-24 18:05:41 -04:00
Tom Boucher
b67ab38098 fix(#2643): align skill frontmatter name with workflow gsd: emission (#2672)
Flat-skills installs write SKILL.md files under gsd-<cmd>/ dirs, but
Claude Code resolves skills by their frontmatter `name:`, not directory
name. PR #2595 normalized every `/gsd-<cmd>` to `/gsd:<cmd>` across
workflows — including inside `Skill(skill="...")` args — but the
installer still emitted `name: gsd-<cmd>`, so every Skill() call on a
flat-skills install resolved to nothing.

Fix: emit `name: gsd:<cmd>` (colon form) in
`convertClaudeCommandToClaudeSkill`. Keep the hyphen-form directory
name for Windows path safety.

Codex stays on hyphen form: its adapter invokes skills as `$gsd-<cmd>`
(shell-var syntax) and a colon would terminate the variable name.
`convertClaudeCommandToCodexSkill` uses `yamlQuote(skillName)` directly
and is untouched.

- Extract `skillFrontmatterName(dirName)` helper (exported for tests).
- Update claude-skills-migration and qwen-skills-migration assertions
  that encoded the old hyphen emission.
- Add `tests/bug-2643-skill-frontmatter-name.test.cjs` asserting every
  `Skill(skill="gsd:<cmd>")` reference in workflows resolves to an
  emitted frontmatter name.

Full suite: 5452/5452 passing.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 18:05:40 -04:00
Tom Boucher
06463860e4 fix(#2638): write sub_repos to canonical planning.sub_repos (#2668)
loadConfig's multiRepo migration and filesystem-sync writers targeted the
top-level parsed.sub_repos, but KNOWN_TOP_LEVEL (the unknown-key validator's
allowlist) only recognizes planning.sub_repos (canonical per #2561). Each
migration/sync therefore persisted a key the next loadConfig call warned was
unknown.

Redirect both writers to parsed.planning.sub_repos, ensuring parsed.planning
is initialized first. Also self-heal legacy/buggy installs by stripping any
stale top-level sub_repos on load, preserving its value as the
planning.sub_repos seed if that slot is empty.

Tests cover: (a) canonical planning.sub_repos emits no warning, (b) multiRepo
migration writes to planning.sub_repos with no top-level residue,
(c) filesystem sync relocates to planning.sub_repos, (d) stale top-level
sub_repos from older buggy installs is stripped on load.

Closes #2638
2026-04-24 18:05:33 -04:00
Tom Boucher
259c1d07d3 fix(#2647): guard tarball ships sdk/dist so gsd-sdk query works (#2671)
v1.38.3 shipped without sdk/dist/ because the outer `files` whitelist
and `prepublishOnly` chain had drifted. The `gsd-sdk` bin shim then
fell through to a stale @gsd-build/sdk@0.1.0 (pre-`query`), breaking
every workflow that called `gsd-sdk query <noun>` on fresh installs.

Current package.json already restores `sdk/dist` + `build:sdk`
prepublish; this PR locks the fix in with:

- tests/bug-2647-outer-tarball-sdk-dist.test.cjs — asserts `files`
  includes `sdk/dist`, `prepublishOnly` invokes `build:sdk`, the
  shim resolves sdk/dist/cli.js, `npm pack --dry-run` lists
  sdk/dist/cli.js, and the built CLI exposes a `query` subcommand.
- scripts/verify-tarball-sdk-dist.sh — packs, extracts, installs
  prod deps, and runs `node sdk/dist/cli.js query --help` against
  the real tarball output.
- .github/workflows/release.yml — runs the verify script in both
  next and stable release jobs before `npm publish`.

Partial fix for #2649 (same root cause on the sibling sdk package).

Fixes #2647
2026-04-24 18:05:18 -04:00
Tom Boucher
387c8a1f9c fix(#2653): eliminate SDK↔CJS config-schema drift (#2670)
The SDK's config-set kept its own hand-maintained allowlist (28-key
drift vs. get-shit-done/bin/lib/config-schema.cjs), so documented
keys accepted by the CJS config-set — planning.sub_repos,
workflow.code_review_command, workflow.security_*, review.models.*,
model_profile_overrides.*, etc. — were rejected with
"Unknown config key" when routed through the SDK.

Changes:
- New sdk/src/query/config-schema.ts mirrors the CJS schema exactly
  (exact-match keys + dynamic regex sources).
- config-mutation.ts imports VALID_CONFIG_KEYS / DYNAMIC_KEY_PATTERNS
  from the shared module instead of rolling its own set and regex
  branches.
- Drop hand-coded agent_skills.* / features.* regex branches —
  now schema-driven so claude_md_assembly.blocks.*, review.models.*,
  and model_profile_overrides.<runtime>.<tier> are also accepted.
- Add tests/config-schema-sdk-parity.test.cjs (node:test) as the
  CI drift guard: asserts CJS VALID_CONFIG_KEYS set-equals the
  literal set parsed from config-schema.ts, and that every CJS
  dynamic pattern source has an identical counterpart in the SDK.
  Parallel to the CJS↔docs parity added in #2479.
- Vitest #2653 specs iterate every CJS key through the SDK
  validator, spot-check each dynamic pattern, and lock in
  planning.sub_repos.
- While here: add workflow.context_coverage_gate to the CJS schema
  (already in docs and SDK; CJS previously rejected it) and sync
  the missing curated typo-suggestions (review.model, sub_repos,
  plan_checker, workflow.review_command) into the SDK.

Fixes #2653.
2026-04-24 18:05:16 -04:00
Tom Boucher
e973ff4cb6 fix(#2630): reset STATE.md frontmatter atomically on milestone switch (#2666)
The /gsd:new-milestone workflow Step 5 rewrote STATE.md's Current Position
body but never touched the YAML frontmatter, so every downstream reader
(state.json, getMilestoneInfo, progress bars) kept reporting the stale
milestone until the first phase advance forced a resync. Asymmetric with
milestone.complete, which uses readModifyWriteStateMdFull.

Add a new `state milestone-switch` handler (both SDK and CJS) that atomically:
- Stomps frontmatter milestone/milestone_name with caller-supplied values
- Resets status to 'planning' and progress counters to zero
- Rewrites the ## Current Position section to the new-milestone template
- Preserves Accumulated Context (decisions, blockers, todos)

Wire the workflow Step 5 to invoke `state.milestone-switch` instead of the
manual body rewrite. Note the flag is `--milestone` not `--version`:
gsd-tools reserves `--version` as a globally-invalid help flag.

Red vitest in sdk/src/query/state-mutation.test.ts asserts the frontmatter
reset. Regression guard via node:test in tests/bug-2630-*.test.cjs runs
through gsd-tools end-to-end.

Fixes #2630

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 18:05:10 -04:00
Tom Boucher
8caa7d4c3a fix(#2649): installer fail-fast when sdk/dist missing in npx cache (#2667)
Root cause shared with #2647: a broken 1.38.3 tarball shipped without
sdk/dist/. The pre-#2441-decouple installer reacted by running
spawnSync('npm.cmd', ['install'], { cwd: sdkDir }) inside the npx cache
on Windows, where the cache is read-only, producing the misleading
"Failed to npm install in sdk/" error.

Defensive changes here (user-facing behavior only; packaging fix lives
in the sibling PR for #2647):

- Classify the install context (classifySdkInstall): detect npx cache
  paths, node_modules-based installs, and dev clones via path heuristics
  plus a side-effect-free write probe. Exported for test.
- Rewrite the dist-missing error to branch on context:
    tarball + npxCache -> "don't touch npx cache; npm i -g ...@latest"
    tarball (other)    -> upgrade path + clone-build escape hatch
    dev-clone          -> keep existing cd sdk && npm install && npm run build
- Preserve the invariant that the installer never shells out to
  npm install itself — users always drive that.
- Add tests/bug-2649-sdk-fail-fast.test.cjs covering the classifier and
  both failure messages, with spawnSync/execSync interceptors that
  assert no nested npm install is attempted.

Cross-ref: #2647 (packaging).

Fixes #2649

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 18:05:04 -04:00
204 changed files with 4302 additions and 1146 deletions

View File

@@ -192,6 +192,9 @@ jobs:
- name: Build SDK dist for tarball - name: Build SDK dist for tarball
run: npm run build:sdk run: npm run build:sdk
- name: Verify tarball ships sdk/dist/cli.js (bug #2647)
run: bash scripts/verify-tarball-sdk-dist.sh
- name: Dry-run publish validation - name: Dry-run publish validation
run: | run: |
npm publish --dry-run --tag next npm publish --dry-run --tag next
@@ -333,6 +336,9 @@ jobs:
- name: Build SDK dist for tarball - name: Build SDK dist for tarball
run: npm run build:sdk run: npm run build:sdk
- name: Verify tarball ships sdk/dist/cli.js (bug #2647)
run: bash scripts/verify-tarball-sdk-dist.sh
- name: Dry-run publish validation - name: Dry-run publish validation
run: | run: |
npm publish --dry-run npm publish --dry-run

View File

@@ -28,6 +28,8 @@ If you use GSD **as a workflow**—milestones, phases, `.planning/` artifacts, b
### Fixed ### Fixed
- **End-of-phase routing suggestions now use `/gsd-<cmd>` (not the retired `/gsd:<cmd>`)** — All user-visible command suggestions in workflows (`execute-phase.md`, `transition.md`), tool output (`profile-output.cjs`, `init.cjs`), references, and templates have been updated from `/gsd:<cmd>` to `/gsd-<cmd>`, matching the Claude Code skill directory name and the user-typed slash-command format. Internal `Skill(skill="gsd:<cmd>")` calls (no leading slash) are preserved unchanged — those resolve by frontmatter `name:` not directory name. The namespace test (`bug-2543-gsd-slash-namespace.test.cjs`) has been updated to enforce the current invariant. Closes #2697.
- **`gsd-sdk query` now resolves parent `.planning/` root in multi-repo (`sub_repos`) workspaces** — when invoked from inside a `sub_repos`-listed child repo (e.g. `workspace/app/`), the SDK now walks up to the parent workspace that owns `.planning/`, matching the legacy `gsd-tools.cjs` `findProjectRoot` behavior. Previously `gsd-sdk query init.new-milestone` reported `project_exists: false` from the sub-repo, while `gsd-tools.cjs` resolved the parent root correctly. Resolution happens once in `cli.ts` before dispatch; if `projectDir` already owns `.planning/` (including explicit `--project-dir`), the walk is a no-op. Ported as `findProjectRoot` in `sdk/src/query/helpers.ts` with the same detection order (own `.planning/` wins, then parent `sub_repos` match, then legacy `multiRepo: true`, then `.git` heuristic), capped at 10 parent levels and never crossing `$HOME`. Closes #2623. - **`gsd-sdk query` now resolves parent `.planning/` root in multi-repo (`sub_repos`) workspaces** — when invoked from inside a `sub_repos`-listed child repo (e.g. `workspace/app/`), the SDK now walks up to the parent workspace that owns `.planning/`, matching the legacy `gsd-tools.cjs` `findProjectRoot` behavior. Previously `gsd-sdk query init.new-milestone` reported `project_exists: false` from the sub-repo, while `gsd-tools.cjs` resolved the parent root correctly. Resolution happens once in `cli.ts` before dispatch; if `projectDir` already owns `.planning/` (including explicit `--project-dir`), the walk is a no-op. Ported as `findProjectRoot` in `sdk/src/query/helpers.ts` with the same detection order (own `.planning/` wins, then parent `sub_repos` match, then legacy `multiRepo: true`, then `.git` heuristic), capped at 10 parent levels and never crossing `$HOME`. Closes #2623.
- **Shell hooks falsely flagged as stale on every session** — `gsd-phase-boundary.sh`, `gsd-session-state.sh`, and `gsd-validate-commit.sh` now ship with a `# gsd-hook-version: {{GSD_VERSION}}` header; the installer substitutes `{{GSD_VERSION}}` in `.sh` hooks the same way it does for `.js` hooks; and the stale-hook detector in `gsd-check-update.js` now matches bash `#` comment syntax in addition to JS `//` syntax. All three changes are required together — neither the regex fix alone nor the install fix alone is sufficient to resolve the false positive (#2136, #2206, #2209, #2210, #2212) - **Shell hooks falsely flagged as stale on every session** — `gsd-phase-boundary.sh`, `gsd-session-state.sh`, and `gsd-validate-commit.sh` now ship with a `# gsd-hook-version: {{GSD_VERSION}}` header; the installer substitutes `{{GSD_VERSION}}` in `.sh` hooks the same way it does for `.js` hooks; and the stale-hook detector in `gsd-check-update.js` now matches bash `#` comment syntax in addition to JS `//` syntax. All three changes are required together — neither the regex fix alone nor the install fix alone is sufficient to resolve the false positive (#2136, #2206, #2209, #2210, #2212)

View File

@@ -1113,11 +1113,31 @@ function convertClaudeCommandToCopilotSkill(content, skillName, isGlobal = false
return `${fm}\n${body}`; return `${fm}\n${body}`;
} }
/**
* Map a skill directory name (gsd-<cmd>) to the frontmatter `name:` used
* by Claude Code as the skill identity. Workflows emit `Skill(skill="gsd:<cmd>")`
* (colon form) and Claude Code resolves skills by frontmatter `name:`, not
* directory name — so emit colon form here. Directory stays hyphenated for
* Windows path safety. See #2643.
*
* Codex must NOT use this helper: its adapter invokes skills as `$gsd-<cmd>`
* (shell-var syntax) and a colon would terminate the variable name. Codex
* keeps the hyphen form via `yamlQuote(skillName)` directly.
*/
function skillFrontmatterName(skillDirName) {
if (typeof skillDirName !== 'string') return skillDirName;
// Idempotent on already-colon form.
if (skillDirName.includes(':')) return skillDirName;
// Only rewrite the first hyphen after the `gsd` prefix.
return skillDirName.replace(/^gsd-/, 'gsd:');
}
/** /**
* Convert a Claude command (.md) to a Claude skill (SKILL.md). * Convert a Claude command (.md) to a Claude skill (SKILL.md).
* Claude Code is the native format, so minimal conversion needed — * Claude Code is the native format, so minimal conversion needed —
* preserve allowed-tools as YAML multiline list, preserve argument-hint, * preserve allowed-tools as YAML multiline list, preserve argument-hint.
* convert name from gsd:xxx to gsd-xxx format. * Emits `name: gsd:<cmd>` (colon) so Skill(skill="gsd:<cmd>") calls in
* workflows resolve on flat-skills installs — see #2643.
*/ */
function convertClaudeCommandToClaudeSkill(content, skillName) { function convertClaudeCommandToClaudeSkill(content, skillName) {
const { frontmatter, body } = extractFrontmatterAndBody(content); const { frontmatter, body } = extractFrontmatterAndBody(content);
@@ -1137,7 +1157,8 @@ function convertClaudeCommandToClaudeSkill(content, skillName) {
} }
// Reconstruct frontmatter in Claude skill format // Reconstruct frontmatter in Claude skill format
let fm = `---\nname: ${skillName}\ndescription: ${yamlQuote(description)}\n`; const frontmatterName = skillFrontmatterName(skillName);
let fm = `---\nname: ${frontmatterName}\ndescription: ${yamlQuote(description)}\n`;
if (argumentHint) fm += `argument-hint: ${yamlQuote(argumentHint)}\n`; if (argumentHint) fm += `argument-hint: ${yamlQuote(argumentHint)}\n`;
if (agent) fm += `agent: ${agent}\n`; if (agent) fm += `agent: ${agent}\n`;
if (toolsBlock) fm += toolsBlock; if (toolsBlock) fm += toolsBlock;
@@ -1873,6 +1894,14 @@ function convertClaudeToCodexMarkdown(content) {
converted = converted.replace(/\$HOME\/\.claude\//g, '$HOME/.codex/'); converted = converted.replace(/\$HOME\/\.claude\//g, '$HOME/.codex/');
converted = converted.replace(/~\/\.claude\//g, '~/.codex/'); converted = converted.replace(/~\/\.claude\//g, '~/.codex/');
converted = converted.replace(/\.\/\.claude\//g, './.codex/'); converted = converted.replace(/\.\/\.claude\//g, './.codex/');
// Bare/project-relative .claude/... references (#2639). Covers strings like
// "check `.claude/skills/`" where there is no ~/, $HOME/, or ./ anchor.
// Negative lookbehind prevents double-replacing already-anchored forms and
// avoids matching inside URLs or other slash-prefixed paths.
converted = converted.replace(/(?<![A-Za-z0-9_\-./~$])\.claude\//g, '.codex/');
// `.claudeignore` → `.codexignore` (#2639). Codex honors its own ignore
// file; leaving the Claude-specific name is misleading in agent prompts.
converted = converted.replace(/\.claudeignore\b/g, '.codexignore');
// Runtime-neutral agent name replacement (#766) // Runtime-neutral agent name replacement (#766)
converted = neutralizeAgentReferences(converted, 'AGENTS.md'); converted = neutralizeAgentReferences(converted, 'AGENTS.md');
return converted; return converted;
@@ -2037,7 +2066,10 @@ function generateCodexConfigBlock(agents, targetDir) {
]; ];
for (const { name, description } of agents) { for (const { name, description } of agents) {
lines.push(`[agents.${name}]`); // #2645 — Codex schema requires [[agents]] array-of-tables, not [agents.<name>] maps.
// Emitting [agents.<name>] produces `invalid type: map, expected a sequence` on load.
lines.push(`[[agents]]`);
lines.push(`name = ${JSON.stringify(name)}`);
lines.push(`description = ${JSON.stringify(description)}`); lines.push(`description = ${JSON.stringify(description)}`);
lines.push(`config_file = "${agentsPrefix}/${name}.toml"`); lines.push(`config_file = "${agentsPrefix}/${name}.toml"`);
lines.push(''); lines.push('');
@@ -2046,8 +2078,39 @@ function generateCodexConfigBlock(agents, targetDir) {
return lines.join('\n'); return lines.join('\n');
} }
/**
* Strip any managed GSD agent sections from a TOML string.
*
* Handles BOTH shapes so reinstall self-heals broken legacy configs:
* - Legacy: `[agents.gsd-*]` single-keyed map tables (pre-#2645).
* - Current: `[[agents]]` array-of-tables whose `name = "gsd-*"`.
*
* A section runs from its header to the next `[` header or EOF.
*/
function stripCodexGsdAgentSections(content) { function stripCodexGsdAgentSections(content) {
return content.replace(/^\[agents\.gsd-[^\]]+\]\n(?:(?!\[)[^\n]*\n?)*/gm, ''); // Use the TOML-aware section parser so we never absorb adjacent user-authored
// tables — even if their headers are indented or otherwise oddly placed.
const sections = getTomlTableSections(content).filter((section) => {
// Legacy `[agents.gsd-<name>]` map tables (pre-#2645).
if (!section.array && /^agents\.gsd-/.test(section.path)) {
return true;
}
// Current `[[agents]]` array-of-tables — only strip blocks whose
// `name = "gsd-..."`, preserving user-authored [[agents]] entries.
if (section.array && section.path === 'agents') {
const body = content.slice(section.headerEnd, section.end);
const nameMatch = body.match(/^[ \t]*name[ \t]*=[ \t]*["']([^"']+)["']/m);
return Boolean(nameMatch && /^gsd-/.test(nameMatch[1]));
}
return false;
});
return removeContentRanges(
content,
sections.map(({ start, end }) => ({ start, end })),
);
} }
/** /**
@@ -2745,13 +2808,27 @@ function isLegacyGsdAgentsSection(body) {
function stripLeakedGsdCodexSections(content) { function stripLeakedGsdCodexSections(content) {
const leakedSections = getTomlTableSections(content) const leakedSections = getTomlTableSections(content)
.filter((section) => .filter((section) => {
section.path.startsWith('agents.gsd-') || // Legacy [agents.gsd-<name>] map tables (pre-#2645).
( if (!section.array && section.path.startsWith('agents.gsd-')) return true;
// Legacy bare [agents] table with only the old max_threads/max_depth keys.
if (
!section.array &&
section.path === 'agents' && section.path === 'agents' &&
isLegacyGsdAgentsSection(content.slice(section.headerEnd, section.end)) isLegacyGsdAgentsSection(content.slice(section.headerEnd, section.end))
) ) return true;
);
// Current [[agents]] array-of-tables whose name is gsd-*. Preserve
// user-authored [[agents]] entries (other names) untouched.
if (section.array && section.path === 'agents') {
const body = content.slice(section.headerEnd, section.end);
const nameMatch = body.match(/^[ \t]*name[ \t]*=[ \t]*["']([^"']+)["']/m);
if (nameMatch && /^gsd-/.test(nameMatch[1])) return true;
}
return false;
});
if (leakedSections.length === 0) { if (leakedSections.length === 0) {
return content; return content;
@@ -3232,15 +3309,16 @@ function installCodexConfig(targetDir, agentsSrc) {
for (const file of agentEntries) { for (const file of agentEntries) {
let content = fs.readFileSync(path.join(agentsSrc, file), 'utf8'); let content = fs.readFileSync(path.join(agentsSrc, file), 'utf8');
// Replace full .claude/get-shit-done prefix so path resolves to codex GSD install // Replace full .claude/get-shit-done prefix so path resolves to the Codex
// GSD install before generic .claude → .codex conversion rewrites it.
content = content.replace(/~\/\.claude\/get-shit-done\//g, codexGsdPath); content = content.replace(/~\/\.claude\/get-shit-done\//g, codexGsdPath);
content = content.replace(/\$HOME\/\.claude\/get-shit-done\//g, codexGsdPath); content = content.replace(/\$HOME\/\.claude\/get-shit-done\//g, codexGsdPath);
// Replace remaining .claude paths with .codex equivalents (#2320). // Route TOML emit through the same full Claude→Codex conversion pipeline
// Capture group handles both trailing-slash form (~/.claude/) and // used on the `.md` emit path (#2639). Covers: slash-command rewrites,
// bare end-of-string form (~/.claude) in a single pass. // $ARGUMENTS → {{GSD_ARGS}}, /clear removal, anchored and bare .claude/
content = content.replace(/\$HOME\/\.claude(\/|$)/g, '$HOME/.codex$1'); // paths, .claudeignore → .codexignore, and standalone "Claude" /
content = content.replace(/~\/\.claude(\/|$)/g, '~/.codex$1'); // CLAUDE.md neutralization via neutralizeAgentReferences(..., 'AGENTS.md').
content = content.replace(/\.\/\.claude(\/|$)/g, './.codex$1'); content = convertClaudeToCodexMarkdown(content);
const { frontmatter } = extractFrontmatterAndBody(content); const { frontmatter } = extractFrontmatterAndBody(content);
const name = extractFrontmatterField(frontmatter, 'name') || file.replace('.md', ''); const name = extractFrontmatterField(frontmatter, 'name') || file.replace('.md', '');
const description = extractFrontmatterField(frontmatter, 'description') || ''; const description = extractFrontmatterField(frontmatter, 'description') || '';
@@ -7023,8 +7101,72 @@ function maybeSuggestPathExport(globalBin, homeDir) {
* --no-sdk skips the check entirely (back-compat). * --no-sdk skips the check entirely (back-compat).
* --sdk forces the check even if it would otherwise be skipped. * --sdk forces the check even if it would otherwise be skipped.
*/ */
function installSdkIfNeeded() { /**
if (hasNoSdk) { * Classify the install context for the SDK directory.
*
* Distinguishes three shapes the installer must handle differently when
* `sdk/dist/` is missing:
*
* - `tarball` + `npxCache: true`
* User ran `npx get-shit-done-cc@latest`. sdk/ lives under
* `<npm-cache>/_npx/<hash>/node_modules/get-shit-done-cc/sdk` which
* is treated as read-only by npm/npx on Windows (#2649). We MUST
* NOT attempt a nested `npm install` there — it will fail with
* EACCES/EPERM and produce the misleading "Failed to npm install
* in sdk/" error the user reported. Point at the global upgrade.
*
* - `tarball` + `npxCache: false`
* User ran a global install (`npm i -g get-shit-done-cc`). sdk/dist
* ships in the published tarball; if it's missing, the published
* artifact itself is broken (see #2647). Same user-facing fix:
* upgrade to latest.
*
* - `dev-clone`
* Developer running from a git clone. Keep the existing "cd sdk &&
* npm install && npm run build" hint — the user is expected to run
* that themselves. The installer itself never shells out to npm.
*
* Detection heuristics are path-based and side-effect-free: we look for
* `_npx` and `node_modules` segments that indicate a packaged install,
* and for a `.git` directory nearby that indicates a clone. A best-effort
* write probe detects read-only filesystems (tmpfile create + unlink);
* probe failures are treated as read-only.
*/
function classifySdkInstall(sdkDir) {
const path = require('path');
const fs = require('fs');
const segments = sdkDir.split(/[\\/]+/);
const npxCache = segments.includes('_npx');
const inNodeModules = segments.includes('node_modules');
const parent = path.dirname(sdkDir);
const hasGitNearby = fs.existsSync(path.join(parent, '.git'));
let mode;
if (hasGitNearby && !npxCache && !inNodeModules) {
mode = 'dev-clone';
} else if (npxCache || inNodeModules) {
mode = 'tarball';
} else {
mode = 'dev-clone';
}
let readOnly = npxCache; // assume true for npx cache
if (!readOnly) {
try {
const probe = path.join(sdkDir, `.gsd-write-probe-${process.pid}`);
fs.writeFileSync(probe, '');
fs.unlinkSync(probe);
} catch {
readOnly = true;
}
}
return { mode, npxCache, readOnly };
}
function installSdkIfNeeded(opts) {
opts = opts || {};
if (hasNoSdk && !opts.sdkDir) {
console.log(`\n ${dim}Skipping GSD SDK check (--no-sdk)${reset}`); console.log(`\n ${dim}Skipping GSD SDK check (--no-sdk)${reset}`);
return; return;
} }
@@ -7032,9 +7174,11 @@ function installSdkIfNeeded() {
const path = require('path'); const path = require('path');
const fs = require('fs'); const fs = require('fs');
const sdkCliPath = path.resolve(__dirname, '..', 'sdk', 'dist', 'cli.js'); const sdkDir = opts.sdkDir || path.resolve(__dirname, '..', 'sdk');
const sdkCliPath = path.join(sdkDir, 'dist', 'cli.js');
if (!fs.existsSync(sdkCliPath)) { if (!fs.existsSync(sdkCliPath)) {
const ctx = classifySdkInstall(sdkDir);
const bar = '━'.repeat(72); const bar = '━'.repeat(72);
const redBold = `${red}${bold}`; const redBold = `${red}${bold}`;
console.error(''); console.error('');
@@ -7043,9 +7187,33 @@ function installSdkIfNeeded() {
console.error(`${redBold}${bar}${reset}`); console.error(`${redBold}${bar}${reset}`);
console.error(` ${red}Reason:${reset} sdk/dist/cli.js not found at ${sdkCliPath}`); console.error(` ${red}Reason:${reset} sdk/dist/cli.js not found at ${sdkCliPath}`);
console.error(''); console.error('');
console.error(` This should not happen with a published tarball install.`);
console.error(` If you are running from a git clone, build the SDK first:`); if (ctx.mode === 'tarball') {
// User install (including `npx get-shit-done-cc@latest`, which stages
// a read-only tarball under the npx cache). The sdk/dist/ artifact
// should ship in the published tarball. If it's missing, the only
// sane fix from the user's side is a fresh global install of a
// version that includes dist/. Do NOT attempt a nested `npm install`
// inside the (read-only) npx cache — that's the #2649 failure mode.
if (ctx.npxCache) {
console.error(` Detected read-only npx cache install (${dim}${sdkDir}${reset}).`);
console.error(` The installer will ${bold}not${reset} attempt \`npm install\` inside the npx cache.`);
console.error('');
} else {
console.error(` The published tarball appears to be missing sdk/dist/ (see #2647).`);
console.error('');
}
console.error(` Fix: install a version that ships sdk/dist/ globally:`);
console.error(` ${cyan}npm install -g get-shit-done-cc@latest${reset}`);
console.error(` Or, if you prefer a one-shot run, clear the npx cache first:`);
console.error(` ${cyan}npx --yes get-shit-done-cc@latest${reset}`);
console.error(` Or build from source (git clone):`);
console.error(` ${cyan}git clone https://github.com/gsd-build/get-shit-done && cd get-shit-done/sdk && npm install && npm run build${reset}`);
} else {
// Dev clone: keep the existing build-from-source hint.
console.error(` Running from a git clone — build the SDK first:`);
console.error(` ${cyan}cd sdk && npm install && npm run build${reset}`); console.error(` ${cyan}cd sdk && npm install && npm run build${reset}`);
}
console.error(`${redBold}${bar}${reset}`); console.error(`${redBold}${bar}${reset}`);
console.error(''); console.error('');
process.exit(1); process.exit(1);
@@ -7146,6 +7314,8 @@ if (process.env.GSD_TEST_MODE) {
readGsdEffectiveModelOverrides, readGsdEffectiveModelOverrides,
install, install,
uninstall, uninstall,
installSdkIfNeeded,
classifySdkInstall,
convertClaudeCommandToCodexSkill, convertClaudeCommandToCodexSkill,
convertClaudeToOpencodeFrontmatter, convertClaudeToOpencodeFrontmatter,
convertClaudeToKiloFrontmatter, convertClaudeToKiloFrontmatter,
@@ -7173,6 +7343,7 @@ if (process.env.GSD_TEST_MODE) {
convertClaudeAgentToAntigravityAgent, convertClaudeAgentToAntigravityAgent,
copyCommandsAsAntigravitySkills, copyCommandsAsAntigravitySkills,
convertClaudeCommandToClaudeSkill, convertClaudeCommandToClaudeSkill,
skillFrontmatterName,
copyCommandsAsClaudeSkills, copyCommandsAsClaudeSkills,
convertClaudeToWindsurfMarkdown, convertClaudeToWindsurfMarkdown,
convertClaudeCommandToWindsurfSkill, convertClaudeCommandToWindsurfSkill,

View File

@@ -42,7 +42,7 @@ the normal phase sequence and accumulate context over time.
**Plans:** 0 plans **Plans:** 0 plans
Plans: Plans:
- [ ] TBD (promote with /gsd:review-backlog when ready) - [ ] TBD (promote with /gsd-review-backlog when ready)
``` ```
4. **Create the phase directory:** 4. **Create the phase directory:**
@@ -65,15 +65,15 @@ the normal phase sequence and accumulate context over time.
Directory: .planning/phases/{NEXT}-{slug}/ Directory: .planning/phases/{NEXT}-{slug}/
This item lives in the backlog parking lot. This item lives in the backlog parking lot.
Use /gsd:discuss-phase {NEXT} to explore it further. Use /gsd-discuss-phase {NEXT} to explore it further.
Use /gsd:review-backlog to promote items to active milestone. Use /gsd-review-backlog to promote items to active milestone.
``` ```
</process> </process>
<notes> <notes>
- 999.x numbering keeps backlog items out of the active phase sequence - 999.x numbering keeps backlog items out of the active phase sequence
- Phase directories are created immediately, so /gsd:discuss-phase and /gsd:plan-phase work on them - Phase directories are created immediately, so /gsd-discuss-phase and /gsd-plan-phase work on them
- No `Depends on:` field — backlog items are unsequenced by definition - No `Depends on:` field — backlog items are unsequenced by definition
- Sparse numbering is fine (999.1, 999.3) — always uses next-decimal - Sparse numbering is fine (999.1, 999.3) — always uses next-decimal
</notes> </notes>

View File

@@ -13,8 +13,8 @@ allowed-tools:
- AskUserQuestion - AskUserQuestion
argument-instructions: | argument-instructions: |
Parse the argument as a phase number (integer, decimal, or letter-suffix), plus optional free-text instructions. Parse the argument as a phase number (integer, decimal, or letter-suffix), plus optional free-text instructions.
Example: /gsd:add-tests 12 Example: /gsd-add-tests 12
Example: /gsd:add-tests 12 focus on edge cases in the pricing module Example: /gsd-add-tests 12 focus on edge cases in the pricing module
--- ---
<objective> <objective>
Generate unit and E2E tests for a completed phase, using its SUMMARY.md, CONTEXT.md, and VERIFICATION.md as specifications. Generate unit and E2E tests for a completed phase, using its SUMMARY.md, CONTEXT.md, and VERIFICATION.md as specifications.

View File

@@ -25,7 +25,7 @@ Then suggest `Depends on` updates to ROADMAP.md.
<context> <context>
No arguments required. Requires an active milestone with ROADMAP.md. No arguments required. Requires an active milestone with ROADMAP.md.
Run this command BEFORE `/gsd:manager` to fill in missing `Depends on` fields and prevent merge conflicts from unordered parallel execution. Run this command BEFORE `/gsd-manager` to fill in missing `Depends on` fields and prevent merge conflicts from unordered parallel execution.
</context> </context>
<process> <process>

View File

@@ -42,19 +42,19 @@ Output: Milestone archived (roadmap + requirements), PROJECT.md evolved, git tag
0. **Check for audit:** 0. **Check for audit:**
- Look for `.planning/v{{version}}-MILESTONE-AUDIT.md` - Look for `.planning/v{{version}}-MILESTONE-AUDIT.md`
- If missing or stale: recommend `/gsd:audit-milestone` first - If missing or stale: recommend `/gsd-audit-milestone` first
- If audit status is `gaps_found`: recommend `/gsd:plan-milestone-gaps` first - If audit status is `gaps_found`: recommend `/gsd-plan-milestone-gaps` first
- If audit status is `passed`: proceed to step 1 - If audit status is `passed`: proceed to step 1
```markdown ```markdown
## Pre-flight Check ## Pre-flight Check
{If no v{{version}}-MILESTONE-AUDIT.md:} {If no v{{version}}-MILESTONE-AUDIT.md:}
⚠ No milestone audit found. Run `/gsd:audit-milestone` first to verify ⚠ No milestone audit found. Run `/gsd-audit-milestone` first to verify
requirements coverage, cross-phase integration, and E2E flows. requirements coverage, cross-phase integration, and E2E flows.
{If audit has gaps:} {If audit has gaps:}
⚠ Milestone audit found gaps. Run `/gsd:plan-milestone-gaps` to create ⚠ Milestone audit found gaps. Run `/gsd-plan-milestone-gaps` to create
phases that close the gaps, or proceed anyway to accept as tech debt. phases that close the gaps, or proceed anyway to accept as tech debt.
{If audit passed:} {If audit passed:}
@@ -108,7 +108,7 @@ Output: Milestone archived (roadmap + requirements), PROJECT.md evolved, git tag
- Ask about pushing tag - Ask about pushing tag
8. **Offer next steps:** 8. **Offer next steps:**
- `/gsd:new-milestone` — start next milestone (questioning → research → requirements → roadmap) - `/gsd-new-milestone` — start next milestone (questioning → research → requirements → roadmap)
</process> </process>
@@ -132,5 +132,5 @@ Output: Milestone archived (roadmap + requirements), PROJECT.md evolved, git tag
- **Archive before deleting:** Always create archive files before updating/deleting originals - **Archive before deleting:** Always create archive files before updating/deleting originals
- **One-line summary:** Collapsed milestone in ROADMAP.md should be single line with link - **One-line summary:** Collapsed milestone in ROADMAP.md should be single line with link
- **Context efficiency:** Archive keeps ROADMAP.md and REQUIREMENTS.md constant size per milestone - **Context efficiency:** Archive keeps ROADMAP.md and REQUIREMENTS.md constant size per milestone
- **Fresh requirements:** Next milestone starts with `/gsd:new-milestone` which includes requirements definition - **Fresh requirements:** Next milestone starts with `/gsd-new-milestone` which includes requirements definition
</critical_rules> </critical_rules>

View File

@@ -88,11 +88,11 @@ Active Debug Sessions
hypothesis: Missing null check on req.body.user hypothesis: Missing null check on req.body.user
next: Verify fix passes regression test next: Verify fix passes regression test
───────────────────────────────────────────── ─────────────────────────────────────────────
Run `/gsd:debug continue <slug>` to resume a session. Run `/gsd-debug continue <slug>` to resume a session.
No sessions? `/gsd:debug <description>` to start. No sessions? `/gsd-debug <description>` to start.
``` ```
If no files exist or the glob returns nothing: print "No active debug sessions. Run `/gsd:debug <issue description>` to start one." If no files exist or the glob returns nothing: print "No active debug sessions. Run `/gsd-debug <issue description>` to start one."
STOP after displaying list. Do NOT proceed to further steps. STOP after displaying list. Do NOT proceed to further steps.
@@ -117,7 +117,7 @@ No agent spawn. Just information display. STOP after printing.
When SUBCMD=continue and SLUG is set: When SUBCMD=continue and SLUG is set:
Check `.planning/debug/{SLUG}.md` exists. If not, print "No active debug session found with slug: {SLUG}. Check `/gsd:debug list` for active sessions." and stop. Check `.planning/debug/{SLUG}.md` exists. If not, print "No active debug session found with slug: {SLUG}. Check `/gsd-debug list` for active sessions." and stop.
Read file and print Current Focus block to console: Read file and print Current Focus block to console:
@@ -247,7 +247,7 @@ specialist_dispatch_enabled: true
Display the compact summary returned by the session manager. Display the compact summary returned by the session manager.
If summary shows `DEBUG SESSION COMPLETE`: done. If summary shows `DEBUG SESSION COMPLETE`: done.
If summary shows `ABANDONED`: note session saved at `.planning/debug/{slug}.md` for later `/gsd:debug continue {slug}`. If summary shows `ABANDONED`: note session saved at `.planning/debug/{slug}.md` for later `/gsd-debug continue {slug}`.
</process> </process>

View File

@@ -15,7 +15,7 @@ Open-ended Socratic ideation session. Guides the developer through exploring an
probing questions, optionally spawns research, then routes outputs to the appropriate GSD probing questions, optionally spawns research, then routes outputs to the appropriate GSD
artifacts (notes, todos, seeds, research questions, requirements, or new phases). artifacts (notes, todos, seeds, research questions, requirements, or new phases).
Accepts an optional topic argument: `/gsd:explore authentication strategy` Accepts an optional topic argument: `/gsd-explore authentication strategy`
</objective> </objective>
<execution_context> <execution_context>

View File

@@ -16,8 +16,8 @@ Execute a trivial task directly in the current context without spawning subagent
or generating PLAN.md files. For tasks too small to justify planning overhead: or generating PLAN.md files. For tasks too small to justify planning overhead:
typo fixes, config changes, small refactors, forgotten commits, simple additions. typo fixes, config changes, small refactors, forgotten commits, simple additions.
This is NOT a replacement for /gsd:quick — use /gsd:quick for anything that This is NOT a replacement for /gsd-quick — use /gsd-quick for anything that
needs research, multi-step planning, or verification. /gsd:fast is for tasks needs research, multi-step planning, or verification. /gsd-fast is for tasks
you could describe in one sentence and execute in under 2 minutes. you could describe in one sentence and execute in under 2 minutes.
</objective> </objective>

View File

@@ -43,7 +43,7 @@ Knowledge graph is disabled. To activate:
node $HOME/.claude/get-shit-done/bin/gsd-tools.cjs config-set graphify.enabled true node $HOME/.claude/get-shit-done/bin/gsd-tools.cjs config-set graphify.enabled true
Then run /gsd:graphify build to create the initial graph. Then run /gsd-graphify build to create the initial graph.
``` ```
--- ---
@@ -65,7 +65,7 @@ Parse `$ARGUMENTS` to determine the operation mode:
``` ```
GSD > GRAPHIFY GSD > GRAPHIFY
Usage: /gsd:graphify <mode> Usage: /gsd-graphify <mode>
Modes: Modes:
build Build or rebuild the knowledge graph build Build or rebuild the knowledge graph
@@ -85,7 +85,7 @@ node $HOME/.claude/get-shit-done/bin/gsd-tools.cjs graphify query <term>
Parse the JSON output and display results: Parse the JSON output and display results:
- If the output contains `"disabled": true`, display the disabled message from Step 1 and **STOP** - If the output contains `"disabled": true`, display the disabled message from Step 1 and **STOP**
- If the output contains `"error"` field, display the error message and **STOP** - If the output contains `"error"` field, display the error message and **STOP**
- If no nodes found, display: `No graph matches for '<term>'. Try /gsd:graphify build to create or rebuild the graph.` - If no nodes found, display: `No graph matches for '<term>'. Try /gsd-graphify build to create or rebuild the graph.`
- Otherwise, display matched nodes grouped by type, with edge relationships and confidence tiers (EXTRACTED/INFERRED/AMBIGUOUS) - Otherwise, display matched nodes grouped by type, with edge relationships and confidence tiers (EXTRACTED/INFERRED/AMBIGUOUS)
**STOP** after displaying results. Do not spawn an agent. **STOP** after displaying results. Do not spawn an agent.

View File

@@ -4,7 +4,6 @@ description: Insert urgent work as decimal phase (e.g., 72.1) between existing p
argument-hint: <after> <description> argument-hint: <after> <description>
allowed-tools: allowed-tools:
- Read - Read
- Write
- Bash - Bash
--- ---

View File

@@ -41,7 +41,7 @@ Intel system is disabled. To activate:
gsd-sdk query config-set intel.enabled true gsd-sdk query config-set intel.enabled true
Then run /gsd:intel refresh to build the initial index. Then run /gsd-intel refresh to build the initial index.
``` ```
--- ---
@@ -63,7 +63,7 @@ Parse `$ARGUMENTS` to determine the operation mode:
``` ```
GSD > INTEL GSD > INTEL
Usage: /gsd:intel <mode> Usage: /gsd-intel <mode>
Modes: Modes:
query <term> Search intel files for a term query <term> Search intel files for a term
@@ -82,7 +82,7 @@ gsd-sdk query intel.query <term>
Parse the JSON output and display results: Parse the JSON output and display results:
- If the output contains `"disabled": true`, display the disabled message from Step 1 and **STOP** - If the output contains `"disabled": true`, display the disabled message from Step 1 and **STOP**
- If no matches found, display: `No intel matches for '<term>'. Try /gsd:intel refresh to build the index.` - If no matches found, display: `No intel matches for '<term>'. Try /gsd-intel refresh to build the index.`
- Otherwise, display matching entries grouped by intel file - Otherwise, display matching entries grouped by intel file
**STOP** after displaying results. Do not spawn an agent. **STOP** after displaying results. Do not spawn an agent.

View File

@@ -30,8 +30,8 @@ Focus area: $ARGUMENTS (optional - if provided, tells agents to focus on specifi
Check for .planning/STATE.md - loads context if project already initialized Check for .planning/STATE.md - loads context if project already initialized
**This command can run:** **This command can run:**
- Before /gsd:new-project (brownfield codebases) - creates codebase map first - Before /gsd-new-project (brownfield codebases) - creates codebase map first
- After /gsd:new-project (greenfield codebases) - updates codebase map as code evolves - After /gsd-new-project (greenfield codebases) - updates codebase map as code evolves
- Anytime to refresh codebase understanding - Anytime to refresh codebase understanding
</context> </context>
@@ -59,7 +59,7 @@ Check for .planning/STATE.md - loads context if project already initialized
4. Wait for agents to complete, collect confirmations (NOT document contents) 4. Wait for agents to complete, collect confirmations (NOT document contents)
5. Verify all 7 documents exist with line counts 5. Verify all 7 documents exist with line counts
6. Commit codebase map 6. Commit codebase map
7. Offer next steps (typically: /gsd:new-project or /gsd:plan-phase) 7. Offer next steps (typically: /gsd-new-project or /gsd-plan-phase)
</process> </process>
<success_criteria> <success_criteria>

View File

@@ -21,7 +21,7 @@ Brownfield equivalent of new-project. Project exists, PROJECT.md has history. Ga
- `.planning/ROADMAP.md` — phase structure (continues numbering) - `.planning/ROADMAP.md` — phase structure (continues numbering)
- `.planning/STATE.md` — reset for new milestone - `.planning/STATE.md` — reset for new milestone
**After:** `/gsd:plan-phase [N]` to start execution. **After:** `/gsd-plan-phase [N]` to start execution.
</objective> </objective>
<execution_context> <execution_context>

View File

@@ -29,7 +29,7 @@ Initialize a new project through unified flow: questioning → research (optiona
- `.planning/ROADMAP.md` — phase structure - `.planning/ROADMAP.md` — phase structure
- `.planning/STATE.md` — project memory - `.planning/STATE.md` — project memory
**After this command:** Run `/gsd:plan-phase 1` to start execution. **After this command:** Run `/gsd-plan-phase 1` to start execution.
</objective> </objective>
<execution_context> <execution_context>

View File

@@ -30,7 +30,7 @@ Create a physical workspace directory containing copies of specified git repos (
- `<path>/.planning/` — independent planning directory - `<path>/.planning/` — independent planning directory
- `<path>/<repo>/` — git worktree or clone for each specified repo - `<path>/<repo>/` — git worktree or clone for each specified repo
**After this command:** `cd` into the workspace and run `/gsd:new-project` to initialize GSD. **After this command:** `cd` into the workspace and run `/gsd-new-project` to initialize GSD.
</objective> </objective>
<execution_context> <execution_context>

View File

@@ -10,11 +10,11 @@ allowed-tools:
- AskUserQuestion - AskUserQuestion
--- ---
<objective> <objective>
Create all phases necessary to close gaps identified by `/gsd:audit-milestone`. Create all phases necessary to close gaps identified by `/gsd-audit-milestone`.
Reads MILESTONE-AUDIT.md, groups gaps into logical phases, creates phase entries in ROADMAP.md, and offers to plan each phase. Reads MILESTONE-AUDIT.md, groups gaps into logical phases, creates phase entries in ROADMAP.md, and offers to plan each phase.
One command creates all fix phases — no manual `/gsd:add-phase` per gap. One command creates all fix phases — no manual `/gsd-add-phase` per gap.
</objective> </objective>
<execution_context> <execution_context>

View File

@@ -40,7 +40,7 @@ Phase number: $ARGUMENTS (optional — auto-detects next unplanned phase if omit
- `--gaps` — Gap closure mode (reads VERIFICATION.md, skips research) - `--gaps` — Gap closure mode (reads VERIFICATION.md, skips research)
- `--skip-verify` — Skip verification loop - `--skip-verify` — Skip verification loop
- `--prd <file>` — Use a PRD/acceptance criteria file instead of discuss-phase. Parses requirements into CONTEXT.md automatically. Skips discuss-phase entirely. - `--prd <file>` — Use a PRD/acceptance criteria file instead of discuss-phase. Parses requirements into CONTEXT.md automatically. Skips discuss-phase entirely.
- `--reviews` — Replan incorporating cross-AI review feedback from REVIEWS.md (produced by `/gsd:review`) - `--reviews` — Replan incorporating cross-AI review feedback from REVIEWS.md (produced by `/gsd-review`)
- `--text` — Use plain-text numbered lists instead of TUI menus (required for `/rc` remote sessions) - `--text` — Use plain-text numbered lists instead of TUI menus (required for `/rc` remote sessions)
Normalize phase input in step 2 before any directory lookups. Normalize phase input in step 2 before any directory lookups.

View File

@@ -16,7 +16,7 @@ milestone arrives. Seeds solve context rot: instead of a one-liner in Deferred t
reads, a seed preserves the full WHY, WHEN to surface, and breadcrumbs to details. reads, a seed preserves the full WHY, WHEN to surface, and breadcrumbs to details.
Creates: .planning/seeds/SEED-NNN-slug.md Creates: .planning/seeds/SEED-NNN-slug.md
Consumed by: /gsd:new-milestone (scans seeds and presents matches) Consumed by: /gsd-new-milestone (scans seeds and presents matches)
</objective> </objective>
<execution_context> <execution_context>

View File

@@ -71,7 +71,7 @@ For each directory found:
- Check if PLAN.md exists - Check if PLAN.md exists
- Check if SUMMARY.md exists; if so, read `status` from its frontmatter via: - Check if SUMMARY.md exists; if so, read `status` from its frontmatter via:
```bash ```bash
gsd-sdk query frontmatter.get .planning/quick/{dir}/SUMMARY.md status 2>/dev/null gsd-sdk query frontmatter.get .planning/quick/{dir}/SUMMARY.md status
``` ```
- Determine directory creation date: `stat -f "%SB" -t "%Y-%m-%d"` (macOS) or `stat -c "%w"` (Linux); fall back to the date prefix in the directory name (format: `YYYYMMDD-` prefix) - Determine directory creation date: `stat -f "%SB" -t "%Y-%m-%d"` (macOS) or `stat -c "%w"` (Linux); fall back to the date prefix in the directory name (format: `YYYYMMDD-` prefix)
- Derive display status: - Derive display status:
@@ -118,7 +118,7 @@ Status: {status from SUMMARY.md frontmatter, or "no summary yet"}
Description: {first non-empty line from PLAN.md after frontmatter} Description: {first non-empty line from PLAN.md after frontmatter}
Last action: {last meaningful line of SUMMARY.md, or "none"} Last action: {last meaningful line of SUMMARY.md, or "none"}
───────────────────────────────────── ─────────────────────────────────────
Resume with: /gsd:quick resume {slug} Resume with: /gsd-quick resume {slug}
``` ```
No agent spawn. STOP after printing. No agent spawn. STOP after printing.

View File

@@ -115,7 +115,7 @@ Read `backup-meta.json` from the patches directory.
``` ```
No local patches found. Nothing to reapply. No local patches found. Nothing to reapply.
Local patches are automatically saved when you run /gsd:update Local patches are automatically saved when you run /gsd-update
after modifying any GSD workflow, command, or agent files. after modifying any GSD workflow, command, or agent files.
``` ```
Exit. Exit.
@@ -278,7 +278,7 @@ Before proceeding to cleanup, evaluate the Hunk Verification Table produced in S
**If the Hunk Verification Table is absent** (Step 4 did not produce it), STOP immediately and report to the user: **If the Hunk Verification Table is absent** (Step 4 did not produce it), STOP immediately and report to the user:
``` ```
ERROR: Hunk Verification Table is missing. Post-merge verification was not completed. ERROR: Hunk Verification Table is missing. Post-merge verification was not completed.
Rerun /gsd:reapply-patches to retry with full verification. Rerun /gsd-reapply-patches to retry with full verification.
``` ```
**If any row in the Hunk Verification Table shows `verified: no`**, STOP and report to the user: **If any row in the Hunk Verification Table shows `verified: no`**, STOP and report to the user:

View File

@@ -1,6 +1,6 @@
--- ---
name: gsd:research-phase name: gsd:research-phase
description: Research how to implement a phase (standalone - usually use /gsd:plan-phase instead) description: Research how to implement a phase (standalone - usually use /gsd-plan-phase instead)
argument-hint: "[phase]" argument-hint: "[phase]"
allowed-tools: allowed-tools:
- Read - Read
@@ -11,7 +11,7 @@ allowed-tools:
<objective> <objective>
Research how to implement a phase. Spawns gsd-phase-researcher agent with phase context. Research how to implement a phase. Spawns gsd-phase-researcher agent with phase context.
**Note:** This is a standalone research command. For most workflows, use `/gsd:plan-phase` which integrates research automatically. **Note:** This is a standalone research command. For most workflows, use `/gsd-plan-phase` which integrates research automatically.
**Use this command when:** **Use this command when:**
- You want to research without planning yet - You want to research without planning yet
@@ -115,7 +115,7 @@ Mode: ecosystem
</additional_context> </additional_context>
<downstream_consumer> <downstream_consumer>
Your RESEARCH.md will be loaded by `/gsd:plan-phase` which uses specific sections: Your RESEARCH.md will be loaded by `/gsd-plan-phase` which uses specific sections:
- `## Standard Stack` → Plans use these libraries - `## Standard Stack` → Plans use these libraries
- `## Architecture Patterns` → Task structure follows these - `## Architecture Patterns` → Task structure follows these
- `## Don't Hand-Roll` → Tasks NEVER build custom solutions for listed problems - `## Don't Hand-Roll` → Tasks NEVER build custom solutions for listed problems

View File

@@ -13,7 +13,7 @@ allowed-tools:
<objective> <objective>
Invoke external AI CLIs (Gemini, Claude, Codex, OpenCode, Qwen Code, Cursor) to independently review phase plans. Invoke external AI CLIs (Gemini, Claude, Codex, OpenCode, Qwen Code, Cursor) to independently review phase plans.
Produces a structured REVIEWS.md with per-reviewer feedback that can be fed back into Produces a structured REVIEWS.md with per-reviewer feedback that can be fed back into
planning via /gsd:plan-phase --reviews. planning via /gsd-plan-phase --reviews.
**Flow:** Detect CLIs → Build review prompt → Invoke each CLI → Collect responses → Write REVIEWS.md **Flow:** Detect CLIs → Build review prompt → Invoke each CLI → Collect responses → Write REVIEWS.md
</objective> </objective>

View File

@@ -1,6 +1,6 @@
--- ---
name: gsd:scan name: gsd:scan
description: Rapid codebase assessment — lightweight alternative to /gsd:map-codebase description: Rapid codebase assessment — lightweight alternative to /gsd-map-codebase
allowed-tools: allowed-tools:
- Read - Read
- Write - Write
@@ -14,7 +14,7 @@ allowed-tools:
Run a focused codebase scan for a single area, producing targeted documents in `.planning/codebase/`. Run a focused codebase scan for a single area, producing targeted documents in `.planning/codebase/`.
Accepts an optional `--focus` flag: `tech`, `arch`, `quality`, `concerns`, or `tech+arch` (default). Accepts an optional `--focus` flag: `tech`, `arch`, `quality`, `concerns`, or `tech+arch` (default).
Lightweight alternative to `/gsd:map-codebase` — spawns one mapper agent instead of four parallel ones. Lightweight alternative to `/gsd-map-codebase` — spawns one mapper agent instead of four parallel ones.
</objective> </objective>
<execution_context> <execution_context>

View File

@@ -9,4 +9,4 @@ allowed-tools:
Show the following output to the user verbatim, with no extra commentary: Show the following output to the user verbatim, with no extra commentary:
!`if ! command -v gsd-sdk >/dev/null 2>&1; then printf '⚠ gsd-sdk not found in PATH — /gsd:set-profile requires it.\n\nInstall the GSD SDK:\n npm install -g @gsd-build/sdk\n\nOr update GSD to get the latest packages:\n /gsd:update\n'; exit 1; fi; gsd-sdk query config-set-model-profile $ARGUMENTS --raw` !`if ! command -v gsd-sdk >/dev/null 2>&1; then printf '⚠ gsd-sdk not found in PATH — /gsd-set-profile requires it.\n\nInstall the GSD SDK:\n npm install -g @gsd-build/sdk\n\nOr update GSD to get the latest packages:\n /gsd-update\n'; exit 1; fi; gsd-sdk query config-set-model-profile $ARGUMENTS --raw`

View File

@@ -9,7 +9,7 @@ allowed-tools:
--- ---
<objective> <objective>
Interactive configuration of GSD power-user knobs that don't belong in the common-case `/gsd:settings` prompt. Interactive configuration of GSD power-user knobs that don't belong in the common-case `/gsd-settings` prompt.
Routes to the settings-advanced workflow which handles: Routes to the settings-advanced workflow which handles:
- Config existence ensuring (workstream-aware path resolution) - Config existence ensuring (workstream-aware path resolution)
@@ -18,7 +18,7 @@ Routes to the settings-advanced workflow which handles:
- Config merging that preserves every unrelated key - Config merging that preserves every unrelated key
- Confirmation table display - Confirmation table display
Use `/gsd:settings` for the common-case toggles (model profile, research/plan_check/verifier, branching strategy, context warnings). Use `/gsd:settings-advanced` once those are set and you want to tune the internals. Use `/gsd-settings` for the common-case toggles (model profile, research/plan_check/verifier, branching strategy, context warnings). Use `/gsd-settings-advanced` once those are set and you want to tune the internals.
</objective> </objective>
<execution_context> <execution_context>

View File

@@ -19,8 +19,8 @@ API keys are stored plaintext in `.planning/config.json` but are masked
(`****<last-4>`) in every piece of interactive output. The workflow never (`****<last-4>`) in every piece of interactive output. The workflow never
echoes plaintext to stdout, stderr, or any log. echoes plaintext to stdout, stderr, or any log.
This command is deliberately distinct from `/gsd:settings` (workflow toggles) This command is deliberately distinct from `/gsd-settings` (workflow toggles)
and any `/gsd:settings-advanced` tuning surface. It handles *connectivity*, and any `/gsd-settings-advanced` tuning surface. It handles *connectivity*,
not pipeline shape. not pipeline shape.
</objective> </objective>

View File

@@ -11,7 +11,7 @@ allowed-tools:
- AskUserQuestion - AskUserQuestion
--- ---
<objective> <objective>
Bridge local completion → merged PR. After /gsd:verify-work passes, ship the work: push branch, create PR with auto-generated body, optionally trigger review, and track the merge. Bridge local completion → merged PR. After /gsd-verify-work passes, ship the work: push branch, create PR with auto-generated body, optionally trigger review, and track the merge.
Closes the plan → execute → verify → ship loop. Closes the plan → execute → verify → ship loop.
</objective> </objective>

View File

@@ -25,7 +25,7 @@ Two modes:
- **Idea mode** (default) — describe a design idea to sketch - **Idea mode** (default) — describe a design idea to sketch
- **Frontier mode** (no argument or "frontier") — analyzes existing sketch landscape and proposes consistency and frontier sketches - **Frontier mode** (no argument or "frontier") — analyzes existing sketch landscape and proposes consistency and frontier sketches
Does not require `/gsd:new-project` — auto-creates `.planning/sketches/` if needed. Does not require `/gsd-new-project` — auto-creates `.planning/sketches/` if needed.
</objective> </objective>
<execution_context> <execution_context>

View File

@@ -58,5 +58,5 @@ Execute the spec-phase workflow from @~/.claude/get-shit-done/workflows/spec-pha
- Gate passed: ambiguity ≤ 0.20 AND all dimension minimums met - Gate passed: ambiguity ≤ 0.20 AND all dimension minimums met
- SPEC.md written with falsifiable requirements, explicit boundaries, and acceptance criteria - SPEC.md written with falsifiable requirements, explicit boundaries, and acceptance criteria
- SPEC.md committed atomically - SPEC.md committed atomically
- User knows they can now run /gsd:discuss-phase which will load SPEC.md automatically - User knows they can now run /gsd-discuss-phase which will load SPEC.md automatically
</success_criteria> </success_criteria>

View File

@@ -25,7 +25,7 @@ Two modes:
- **Idea mode** (default) — describe an idea to spike - **Idea mode** (default) — describe an idea to spike
- **Frontier mode** (no argument or "frontier") — analyzes existing spike landscape and proposes integration and frontier spikes - **Frontier mode** (no argument or "frontier") — analyzes existing spike landscape and proposes integration and frontier spikes
Does not require `/gsd:new-project` — auto-creates `.planning/spikes/` if needed. Does not require `/gsd-new-project` — auto-creates `.planning/spikes/` if needed.
</objective> </objective>
<execution_context> <execution_context>

View File

@@ -38,7 +38,7 @@ ls .planning/threads/*.md 2>/dev/null
For each thread file found: For each thread file found:
- Read frontmatter `status` field via: - Read frontmatter `status` field via:
```bash ```bash
gsd-sdk query frontmatter.get .planning/threads/{file} status 2>/dev/null gsd-sdk query frontmatter.get .planning/threads/{file} status
``` ```
- If frontmatter `status` field is missing, fall back to reading markdown heading `## Status: OPEN` (or IN PROGRESS / RESOLVED) from the file body - If frontmatter `status` field is missing, fall back to reading markdown heading `## Status: OPEN` (or IN PROGRESS / RESOLVED) from the file body
- Read frontmatter `updated` field for the last-updated date - Read frontmatter `updated` field for the last-updated date
@@ -62,7 +62,7 @@ frontend-build-tools resolved 2026-04-01 Vite vs webpack
If no threads exist (or none match the filter): If no threads exist (or none match the filter):
``` ```
No threads found. Create one with: /gsd:thread <description> No threads found. Create one with: /gsd-thread <description>
``` ```
STOP after displaying. Do NOT proceed to further steps. STOP after displaying. Do NOT proceed to further steps.
@@ -117,8 +117,8 @@ When SUBCMD=status and SLUG is set (already sanitized):
Next Steps: Next Steps:
{content of ## Next Steps section} {content of ## Next Steps section}
───────────────────────────────────── ─────────────────────────────────────
Resume with: /gsd:thread {SLUG} Resume with: /gsd-thread {SLUG}
Close with: /gsd:thread close {SLUG} Close with: /gsd-thread close {SLUG}
``` ```
No agent spawn. STOP after printing. No agent spawn. STOP after printing.
@@ -201,8 +201,8 @@ updated: {today ISO date}
Thread: {slug} Thread: {slug}
File: .planning/threads/{slug}.md File: .planning/threads/{slug}.md
Resume anytime with: /gsd:thread {slug} Resume anytime with: /gsd-thread {slug}
Close when done with: /gsd:thread close {slug} Close when done with: /gsd-thread close {slug}
``` ```
</mode_create> </mode_create>
@@ -210,10 +210,10 @@ updated: {today ISO date}
<notes> <notes>
- Threads are NOT phase-scoped — they exist independently of the roadmap - Threads are NOT phase-scoped — they exist independently of the roadmap
- Lighter weight than /gsd:pause-work — no phase state, no plan context - Lighter weight than /gsd-pause-work — no phase state, no plan context
- The value is in Context and Next Steps — a cold-start session can pick up immediately - The value is in Context and Next Steps — a cold-start session can pick up immediately
- Threads can be promoted to phases or backlog items when they mature: - Threads can be promoted to phases or backlog items when they mature:
/gsd:add-phase or /gsd:add-backlog with context from the thread /gsd-add-phase or /gsd-add-backlog with context from the thread
- Thread files live in .planning/threads/ — no collision with phases or other GSD structures - Thread files live in .planning/threads/ — no collision with phases or other GSD structures
- Thread status values: `open`, `in_progress`, `resolved` - Thread status values: `open`, `in_progress`, `resolved`
</notes> </notes>

View File

@@ -1,6 +1,6 @@
--- ---
name: gsd:ultraplan-phase name: gsd:ultraplan-phase
description: "[BETA] Offload plan phase to Claude Code's ultraplan cloud — drafts remotely while terminal stays free, review in browser with inline comments, import back via /gsd:import. Claude Code only." description: "[BETA] Offload plan phase to Claude Code's ultraplan cloud — drafts remotely while terminal stays free, review in browser with inline comments, import back via /gsd-import. Claude Code only."
argument-hint: "[phase-number]" argument-hint: "[phase-number]"
allowed-tools: allowed-tools:
- Read - Read
@@ -13,9 +13,9 @@ allowed-tools:
Offload GSD's plan phase to Claude Code's ultraplan cloud infrastructure. Offload GSD's plan phase to Claude Code's ultraplan cloud infrastructure.
Ultraplan drafts the plan in a remote cloud session while your terminal stays free. Ultraplan drafts the plan in a remote cloud session while your terminal stays free.
Review and comment on the plan in your browser, then import it back via /gsd:import --from. Review and comment on the plan in your browser, then import it back via /gsd-import --from.
⚠ BETA: ultraplan is in research preview. Use /gsd:plan-phase for stable local planning. ⚠ BETA: ultraplan is in research preview. Use /gsd-plan-phase for stable local planning.
Requirements: Claude Code v2.1.91+, claude.ai account, GitHub repository. Requirements: Claude Code v2.1.91+, claude.ai account, GitHub repository.
</objective> </objective>

View File

@@ -16,7 +16,7 @@ Validate built features through conversational testing with persistent state.
Purpose: Confirm what Claude built actually works from user's perspective. One test at a time, plain text responses, no interrogation. When issues are found, automatically diagnose, plan fixes, and prepare for execution. Purpose: Confirm what Claude built actually works from user's perspective. One test at a time, plain text responses, no interrogation. When issues are found, automatically diagnose, plan fixes, and prepare for execution.
Output: {phase_num}-UAT.md tracking all test results. If issues found: diagnosed gaps, verified fix plans ready for /gsd:execute-phase Output: {phase_num}-UAT.md tracking all test results. If issues found: diagnosed gaps, verified fix plans ready for /gsd-execute-phase
</objective> </objective>
<execution_context> <execution_context>

View File

@@ -6,13 +6,13 @@ allowed-tools:
- Bash - Bash
--- ---
# /gsd:workstreams # /gsd-workstreams
Manage parallel workstreams for concurrent milestone work. Manage parallel workstreams for concurrent milestone work.
## Usage ## Usage
`/gsd:workstreams [subcommand] [args]` `/gsd-workstreams [subcommand] [args]`
### Subcommands ### Subcommands
@@ -40,7 +40,7 @@ Display the workstreams in a table format showing name, status, current phase, a
### create ### create
Run: `gsd-sdk query workstream.create <name> --raw --cwd "$CWD"` Run: `gsd-sdk query workstream.create <name> --raw --cwd "$CWD"`
After creation, display the new workstream path and suggest next steps: After creation, display the new workstream path and suggest next steps:
- `/gsd:new-milestone --ws <name>` to set up the milestone - `/gsd-new-milestone --ws <name>` to set up the milestone
### status ### status
Run: `gsd-sdk query workstream.status <name> --raw --cwd "$CWD"` Run: `gsd-sdk query workstream.status <name> --raw --cwd "$CWD"`
@@ -61,7 +61,7 @@ Run: `gsd-sdk query workstream.complete <name> --raw --cwd "$CWD"`
Archive the workstream to milestones/. Archive the workstream to milestones/.
### resume ### resume
Set the workstream as active and suggest `/gsd:resume-work --ws <name>`. Set the workstream as active and suggest `/gsd-resume-work --ws <name>`.
## Step 3: Display Results ## Step 3: Display Results

View File

@@ -483,6 +483,12 @@ async function runCommand(command, args, cwd, raw, defaultValue) {
} else if (subcommand === 'prune') { } else if (subcommand === 'prune') {
const { 'keep-recent': keepRecent, 'dry-run': dryRun } = parseNamedArgs(args, ['keep-recent'], ['dry-run']); const { 'keep-recent': keepRecent, 'dry-run': dryRun } = parseNamedArgs(args, ['keep-recent'], ['dry-run']);
state.cmdStatePrune(cwd, { keepRecent: keepRecent || '3', dryRun: !!dryRun }, raw); state.cmdStatePrune(cwd, { keepRecent: keepRecent || '3', dryRun: !!dryRun }, raw);
} else if (subcommand === 'milestone-switch') {
// Bug #2630: reset STATE.md frontmatter + Current Position for new milestone.
// NB: the flag is `--milestone`, not `--version` — gsd-tools reserves
// `--version` as a globally-invalid help flag (see NEVER_VALID_FLAGS above).
const { milestone, name } = parseNamedArgs(args, ['milestone', 'name']);
state.cmdStateMilestoneSwitch(cwd, milestone, name, raw);
} else { } else {
state.cmdStateLoad(cwd, raw); state.cmdStateLoad(cwd, raw);
} }

View File

@@ -4,7 +4,7 @@
* Scans all .planning/ artifact categories for items with open/unresolved state. * Scans all .planning/ artifact categories for items with open/unresolved state.
* Returns structured JSON for workflow consumption. * Returns structured JSON for workflow consumption.
* Called by: gsd-tools.cjs audit-open * Called by: gsd-tools.cjs audit-open
* Used by: /gsd:complete-milestone pre-close gate * Used by: /gsd-complete-milestone pre-close gate
*/ */
'use strict'; 'use strict';

View File

@@ -773,7 +773,7 @@ function cmdScaffold(cwd, type, options, raw) {
switch (type) { switch (type) {
case 'context': { case 'context': {
filePath = path.join(phaseDir, `${padded}-CONTEXT.md`); filePath = path.join(phaseDir, `${padded}-CONTEXT.md`);
content = `---\nphase: "${padded}"\nname: "${name || phaseInfo?.phase_name || 'Unnamed'}"\ncreated: ${today}\n---\n\n# Phase ${phase}: ${name || phaseInfo?.phase_name || 'Unnamed'} — Context\n\n## Decisions\n\n_Decisions will be captured during /gsd:discuss-phase ${phase}_\n\n## Discretion Areas\n\n_Areas where the executor can use judgment_\n\n## Deferred Ideas\n\n_Ideas to consider later_\n`; content = `---\nphase: "${padded}"\nname: "${name || phaseInfo?.phase_name || 'Unnamed'}"\ncreated: ${today}\n---\n\n# Phase ${phase}: ${name || phaseInfo?.phase_name || 'Unnamed'} — Context\n\n## Decisions\n\n_Decisions will be captured during /gsd-discuss-phase ${phase}_\n\n## Discretion Areas\n\n_Areas where the executor can use judgment_\n\n## Deferred Ideas\n\n_Ideas to consider later_\n`;
break; break;
} }
case 'uat': { case 'uat': {

View File

@@ -47,6 +47,7 @@ const VALID_CONFIG_KEYS = new Set([
'workflow.inline_plan_threshold', 'workflow.inline_plan_threshold',
'hooks.context_warnings', 'hooks.context_warnings',
'hooks.workflow_guard', 'hooks.workflow_guard',
'workflow.context_coverage_gate',
'statusline.show_last_command', 'statusline.show_last_command',
'workflow.ui_review', 'workflow.ui_review',
'workflow.max_discuss_passes', 'workflow.max_discuss_passes',

View File

@@ -42,7 +42,7 @@ function validateKnownConfigKeyPath(keyPath) {
* Merges (increasing priority): * Merges (increasing priority):
* 1. Hardcoded defaults — every key that loadConfig() resolves, plus mode/granularity * 1. Hardcoded defaults — every key that loadConfig() resolves, plus mode/granularity
* 2. User-level defaults from ~/.gsd/defaults.json (if present) * 2. User-level defaults from ~/.gsd/defaults.json (if present)
* 3. userChoices — the settings the user explicitly selected during /gsd:new-project * 3. userChoices — the settings the user explicitly selected during /gsd-new-project
* *
* Uses the canonical `git` namespace for branching keys (consistent with VALID_CONFIG_KEYS * Uses the canonical `git` namespace for branching keys (consistent with VALID_CONFIG_KEYS
* and the settings workflow). loadConfig() handles both flat and nested formats, so this * and the settings workflow). loadConfig() handles both flat and nested formats, so this
@@ -166,7 +166,7 @@ function buildNewProjectConfig(userChoices) {
* Command: create a fully-materialized .planning/config.json for a new project. * Command: create a fully-materialized .planning/config.json for a new project.
* *
* Accepts user-chosen settings as a JSON string (the keys the user explicitly * Accepts user-chosen settings as a JSON string (the keys the user explicitly
* configured during /gsd:new-project). All remaining keys are filled from * configured during /gsd-new-project). All remaining keys are filled from
* hardcoded defaults and optional ~/.gsd/defaults.json. * hardcoded defaults and optional ~/.gsd/defaults.json.
* *
* Idempotent: if config.json already exists, returns { created: false }. * Idempotent: if config.json already exists, returns { created: false }.

View File

@@ -263,7 +263,7 @@ const CONFIG_DEFAULTS = {
phase_naming: 'sequential', // 'sequential' (default, auto-increment) or 'custom' (arbitrary string IDs) phase_naming: 'sequential', // 'sequential' (default, auto-increment) or 'custom' (arbitrary string IDs)
project_code: null, // optional short prefix for phase dirs (e.g., 'CK' → 'CK-01-foundation') project_code: null, // optional short prefix for phase dirs (e.g., 'CK' → 'CK-01-foundation')
subagent_timeout: 300000, // 5 min default; increase for large codebases or slower models (ms) subagent_timeout: 300000, // 5 min default; increase for large codebases or slower models (ms)
security_enforcement: true, // workflow.security_enforcement — threat-model-anchored security verification via /gsd:secure-phase security_enforcement: true, // workflow.security_enforcement — threat-model-anchored security verification via /gsd-secure-phase
security_asvs_level: 1, // workflow.security_asvs_level — OWASP ASVS verification level (1=opportunistic, 2=standard, 3=comprehensive) security_asvs_level: 1, // workflow.security_asvs_level — OWASP ASVS verification level (1=opportunistic, 2=standard, 3=comprehensive)
security_block_on: 'high', // workflow.security_block_on — minimum severity that blocks phase advancement ('high' | 'medium' | 'low') security_block_on: 'high', // workflow.security_block_on — minimum severity that blocks phase advancement ('high' | 'medium' | 'low')
post_planning_gaps: true, // workflow.post_planning_gaps — unified post-planning gap report (#2493): scan REQUIREMENTS.md + CONTEXT.md decisions vs all PLAN.md files post_planning_gaps: true, // workflow.post_planning_gaps — unified post-planning gap report (#2493): scan REQUIREMENTS.md + CONTEXT.md decisions vs all PLAN.md files
@@ -288,26 +288,40 @@ function loadConfig(cwd) {
// Auto-detect and sync sub_repos: scan for child directories with .git // Auto-detect and sync sub_repos: scan for child directories with .git
let configDirty = false; let configDirty = false;
// Migrate legacy "multiRepo: true" boolean → sub_repos array // Migrate legacy "multiRepo: true" boolean → planning.sub_repos array.
// Canonical location is planning.sub_repos (#2561); writing to top-level
// would be flagged as unknown by the validator below (#2638).
if (parsed.multiRepo === true && !parsed.sub_repos && !parsed.planning?.sub_repos) { if (parsed.multiRepo === true && !parsed.sub_repos && !parsed.planning?.sub_repos) {
const detected = detectSubRepos(cwd); const detected = detectSubRepos(cwd);
if (detected.length > 0) { if (detected.length > 0) {
parsed.sub_repos = detected;
if (!parsed.planning) parsed.planning = {}; if (!parsed.planning) parsed.planning = {};
parsed.planning.sub_repos = detected;
parsed.planning.commit_docs = false; parsed.planning.commit_docs = false;
delete parsed.multiRepo; delete parsed.multiRepo;
configDirty = true; configDirty = true;
} }
} }
// Keep sub_repos in sync with actual filesystem // Self-heal legacy/buggy installs: strip any stale top-level sub_repos,
const currentSubRepos = parsed.sub_repos || parsed.planning?.sub_repos || []; // preserving its value as the planning.sub_repos seed if that slot is empty.
if (Object.prototype.hasOwnProperty.call(parsed, 'sub_repos')) {
if (!parsed.planning) parsed.planning = {};
if (!parsed.planning.sub_repos) {
parsed.planning.sub_repos = parsed.sub_repos;
}
delete parsed.sub_repos;
configDirty = true;
}
// Keep planning.sub_repos in sync with actual filesystem
const currentSubRepos = parsed.planning?.sub_repos || [];
if (Array.isArray(currentSubRepos) && currentSubRepos.length > 0) { if (Array.isArray(currentSubRepos) && currentSubRepos.length > 0) {
const detected = detectSubRepos(cwd); const detected = detectSubRepos(cwd);
if (detected.length > 0) { if (detected.length > 0) {
const sorted = [...currentSubRepos].sort(); const sorted = [...currentSubRepos].sort();
if (JSON.stringify(sorted) !== JSON.stringify(detected)) { if (JSON.stringify(sorted) !== JSON.stringify(detected)) {
parsed.sub_repos = detected; if (!parsed.planning) parsed.planning = {};
parsed.planning.sub_repos = detected;
configDirty = true; configDirty = true;
} }
} }
@@ -1742,11 +1756,28 @@ function resolveReasoningEffortInternal(cwd, agentType) {
*/ */
function extractOneLinerFromBody(content) { function extractOneLinerFromBody(content) {
if (!content) return null; if (!content) return null;
// Normalize EOLs so matching works for LF and CRLF files.
const normalized = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
// Strip frontmatter first // Strip frontmatter first
const body = content.replace(/^---\n[\s\S]*?\n---\n*/, ''); const body = normalized.replace(/^---\n[\s\S]*?\n---\n*/, '');
// Find the first **...** line after a # heading // Find the first **...** span on a line after a # heading.
const match = body.match(/^#[^\n]*\n+\*\*([^*]+)\*\*/m); // Two supported template forms:
return match ? match[1].trim() : null; // 1) Labeled: **One-liner:** Real prose here. (bug #2660 — new template)
// 2) Bare: **Real prose here.** (legacy template)
// For (1), the first bold span ends in a colon and the prose that follows
// on the same line is the one-liner. For (2), the bold span itself is the
// one-liner.
const match = body.match(/^#[^\n]*\n+\*\*([^*\n]+)\*\*([^\n]*)/m);
if (!match) return null;
const boldInner = match[1].trim();
const afterBold = match[2];
// Labeled form: bold span is a "Label:" prefix — capture prose after it.
if (/:\s*$/.test(boldInner)) {
const prose = afterBold.trim();
return prose.length > 0 ? prose : null;
}
// Bare form: the bold content itself is the one-liner.
return boldInner.length > 0 ? boldInner : null;
} }
// ─── Misc utilities ─────────────────────────────────────────────────────────── // ─── Misc utilities ───────────────────────────────────────────────────────────

View File

@@ -253,7 +253,7 @@ function buildMessage(elements, affectedPaths, action) {
lines.push(`Auto-remap scheduled for paths: ${affectedPaths.join(', ')}`); lines.push(`Auto-remap scheduled for paths: ${affectedPaths.join(', ')}`);
} else { } else {
lines.push( lines.push(
`Run /gsd:map-codebase --paths ${affectedPaths.join(',')} to refresh planning context.`, `Run /gsd-map-codebase --paths ${affectedPaths.join(',')} to refresh planning context.`,
); );
} }
return lines.join('\n'); return lines.join('\n');

View File

@@ -420,7 +420,7 @@ function buildPreview(gsd2Data, artifacts) {
lines.push(''); lines.push('');
lines.push('Cannot migrate automatically:'); lines.push('Cannot migrate automatically:');
lines.push(' - GSD-2 cost/token ledger (no v1 equivalent)'); lines.push(' - GSD-2 cost/token ledger (no v1 equivalent)');
lines.push(' - GSD-2 database state (rebuilt from files on first /gsd:health)'); lines.push(' - GSD-2 database state (rebuilt from files on first /gsd-health)');
lines.push(' - VS Code extension state'); lines.push(' - VS Code extension state');
return lines.join('\n'); return lines.join('\n');

View File

@@ -827,12 +827,61 @@ function cmdInitMilestoneOp(cwd, raw) {
let phaseCount = 0; let phaseCount = 0;
let completedPhases = 0; let completedPhases = 0;
const phasesDir = path.join(planningDir(cwd), 'phases'); const phasesDir = path.join(planningDir(cwd), 'phases');
// Bug #2633 — ROADMAP.md (current milestone section) is the authority for
// phase counts, NOT the on-disk `.planning/phases/` directory. After
// `phases clear` between milestones, on-disk dirs will be a subset of the
// roadmap until each phase is materialized; reading from disk causes
// `all_phases_complete: true` to fire prematurely.
let roadmapPhaseNumbers = [];
try {
const roadmapPath = path.join(planningDir(cwd), 'ROADMAP.md');
const roadmapRaw = fs.readFileSync(roadmapPath, 'utf-8');
const currentSection = extractCurrentMilestone(roadmapRaw, cwd);
const phasePattern = /#{2,4}\s*Phase\s+(\d+[A-Z]?(?:\.\d+)*)\s*:/gi;
let m;
while ((m = phasePattern.exec(currentSection)) !== null) {
roadmapPhaseNumbers.push(m[1]);
}
} catch { /* intentionally empty */ }
// Canonicalize a phase token by stripping leading zeros from the integer
// head while preserving any [A-Z]? suffix and dotted segments. So "03" →
// "3", "03A" → "3A", "03.1" → "3.1", "3A" → "3A". Disk dirs that pad
// ("03-alpha") then match roadmap tokens ("Phase 3") without ever
// collapsing distinct tokens like "3" / "3A" / "3.1" into the same bucket.
const canonicalizePhase = (tok) => {
const m = tok.match(/^(\d+)([A-Z]?(?:\.\d+)*)$/);
return m ? String(parseInt(m[1], 10)) + m[2] : tok;
};
const diskPhaseDirs = new Map();
try {
const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
for (const e of entries) {
if (!e.isDirectory()) continue;
const m = e.name.match(/^(\d+[A-Z]?(?:\.\d+)*)/);
if (!m) continue;
diskPhaseDirs.set(canonicalizePhase(m[1]), e.name);
}
} catch { /* intentionally empty */ }
if (roadmapPhaseNumbers.length > 0) {
phaseCount = roadmapPhaseNumbers.length;
for (const num of roadmapPhaseNumbers) {
const dirName = diskPhaseDirs.get(canonicalizePhase(num));
if (!dirName) continue;
try {
const phaseFiles = fs.readdirSync(path.join(phasesDir, dirName));
const hasSummary = phaseFiles.some(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md');
if (hasSummary) completedPhases++;
} catch { /* intentionally empty */ }
}
} else {
// Fallback: no parseable ROADMAP — preserve legacy on-disk behavior.
try { try {
const entries = fs.readdirSync(phasesDir, { withFileTypes: true }); const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
const dirs = entries.filter(e => e.isDirectory()).map(e => e.name); const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
phaseCount = dirs.length; phaseCount = dirs.length;
// Count phases with summaries (completed)
for (const dir of dirs) { for (const dir of dirs) {
try { try {
const phaseFiles = fs.readdirSync(path.join(phasesDir, dir)); const phaseFiles = fs.readdirSync(path.join(phasesDir, dir));
@@ -841,6 +890,7 @@ function cmdInitMilestoneOp(cwd, raw) {
} catch { /* intentionally empty */ } } catch { /* intentionally empty */ }
} }
} catch { /* intentionally empty */ } } catch { /* intentionally empty */ }
}
// Check archive // Check archive
const archiveDir = path.join(planningRoot(cwd), 'archive'); const archiveDir = path.join(planningRoot(cwd), 'archive');
@@ -929,10 +979,10 @@ function cmdInitManager(cwd, raw) {
// Validate prerequisites // Validate prerequisites
if (!fs.existsSync(paths.roadmap)) { if (!fs.existsSync(paths.roadmap)) {
error('No ROADMAP.md found. Run /gsd:new-milestone first.'); error('No ROADMAP.md found. Run /gsd-new-milestone first.');
} }
if (!fs.existsSync(paths.state)) { if (!fs.existsSync(paths.state)) {
error('No STATE.md found. Run /gsd:new-milestone first.'); error('No STATE.md found. Run /gsd-new-milestone first.');
} }
const rawContent = fs.readFileSync(paths.roadmap, 'utf-8'); const rawContent = fs.readFileSync(paths.roadmap, 'utf-8');
const content = extractCurrentMilestone(rawContent, cwd); const content = extractCurrentMilestone(rawContent, cwd);
@@ -1111,7 +1161,7 @@ function cmdInitManager(cwd, raw) {
phase_name: phase.name, phase_name: phase.name,
action: 'execute', action: 'execute',
reason: `${phase.plan_count} plans ready, dependencies met`, reason: `${phase.plan_count} plans ready, dependencies met`,
command: `/gsd:execute-phase ${phase.number}`, command: `/gsd-execute-phase ${phase.number}`,
}); });
} else if (phase.disk_status === 'discussed' || phase.disk_status === 'researched') { } else if (phase.disk_status === 'discussed' || phase.disk_status === 'researched') {
recommendedActions.push({ recommendedActions.push({
@@ -1119,7 +1169,7 @@ function cmdInitManager(cwd, raw) {
phase_name: phase.name, phase_name: phase.name,
action: 'plan', action: 'plan',
reason: 'Context gathered, ready for planning', reason: 'Context gathered, ready for planning',
command: `/gsd:plan-phase ${phase.number}`, command: `/gsd-plan-phase ${phase.number}`,
}); });
} else if ((phase.disk_status === 'empty' || phase.disk_status === 'no_directory') && phase.is_next_to_discuss) { } else if ((phase.disk_status === 'empty' || phase.disk_status === 'no_directory') && phase.is_next_to_discuss) {
recommendedActions.push({ recommendedActions.push({
@@ -1127,7 +1177,7 @@ function cmdInitManager(cwd, raw) {
phase_name: phase.name, phase_name: phase.name,
action: 'discuss', action: 'discuss',
reason: 'Unblocked, ready to gather context', reason: 'Unblocked, ready to gather context',
command: `/gsd:discuss-phase ${phase.number}`, command: `/gsd-discuss-phase ${phase.number}`,
}); });
} }
} }
@@ -1230,6 +1280,7 @@ function cmdInitProgress(cwd, raw) {
// Build set of phases defined in ROADMAP for the current milestone // Build set of phases defined in ROADMAP for the current milestone
const roadmapPhaseNums = new Set(); const roadmapPhaseNums = new Set();
const roadmapPhaseNames = new Map(); const roadmapPhaseNames = new Map();
const roadmapCheckboxStates = new Map();
try { try {
const roadmapContent = extractCurrentMilestone( const roadmapContent = extractCurrentMilestone(
fs.readFileSync(path.join(planningDir(cwd), 'ROADMAP.md'), 'utf-8'), cwd fs.readFileSync(path.join(planningDir(cwd), 'ROADMAP.md'), 'utf-8'), cwd
@@ -1240,6 +1291,13 @@ function cmdInitProgress(cwd, raw) {
roadmapPhaseNums.add(hm[1]); roadmapPhaseNums.add(hm[1]);
roadmapPhaseNames.set(hm[1], hm[2].replace(/\(INSERTED\)/i, '').trim()); roadmapPhaseNames.set(hm[1], hm[2].replace(/\(INSERTED\)/i, '').trim());
} }
// #2646: parse `- [x] Phase N` checkbox states so ROADMAP-only phases
// inherit completion from the ROADMAP when no phase directory exists.
const cbPattern = /-\s*\[(x| )\]\s*.*Phase\s+(\d+[A-Z]?(?:\.\d+)*)[:\s]/gi;
let cbm;
while ((cbm = cbPattern.exec(roadmapContent)) !== null) {
roadmapCheckboxStates.set(cbm[2], cbm[1].toLowerCase() === 'x');
}
} catch { /* intentionally empty */ } } catch { /* intentionally empty */ }
const isDirInMilestone = getMilestonePhaseFilter(cwd); const isDirInMilestone = getMilestonePhaseFilter(cwd);
@@ -1295,21 +1353,27 @@ function cmdInitProgress(cwd, raw) {
} }
} catch { /* intentionally empty */ } } catch { /* intentionally empty */ }
// Add phases defined in ROADMAP but not yet scaffolded to disk // Add phases defined in ROADMAP but not yet scaffolded to disk. When the
// ROADMAP has a `- [x] Phase N` checkbox, honor it as 'complete' so
// completed_count and status reflect the ROADMAP source of truth (#2646).
for (const [num, name] of roadmapPhaseNames) { for (const [num, name] of roadmapPhaseNames) {
const stripped = num.replace(/^0+/, '') || '0'; const stripped = num.replace(/^0+/, '') || '0';
if (!seenPhaseNums.has(stripped)) { if (!seenPhaseNums.has(stripped)) {
const checkboxComplete =
roadmapCheckboxStates.get(num) === true ||
roadmapCheckboxStates.get(stripped) === true;
const status = checkboxComplete ? 'complete' : 'not_started';
const phaseInfo = { const phaseInfo = {
number: num, number: num,
name: name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, ''), name: name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, ''),
directory: null, directory: null,
status: 'not_started', status,
plan_count: 0, plan_count: 0,
summary_count: 0, summary_count: 0,
has_research: false, has_research: false,
}; };
phases.push(phaseInfo); phases.push(phaseInfo);
if (!nextPhase && !currentPhase) { if (!nextPhase && !currentPhase && status !== 'complete') {
nextPhase = phaseInfo; nextPhase = phaseInfo;
} }
} }

View File

@@ -381,7 +381,7 @@ function cmdPhaseAdd(cwd, description, raw, customId) {
// Build phase entry // Build phase entry
const dependsOn = config.phase_naming === 'custom' ? '' : `\n**Depends on:** Phase ${typeof _newPhaseId === 'number' ? _newPhaseId - 1 : 'TBD'}`; const dependsOn = config.phase_naming === 'custom' ? '' : `\n**Depends on:** Phase ${typeof _newPhaseId === 'number' ? _newPhaseId - 1 : 'TBD'}`;
const phaseEntry = `\n### Phase ${_newPhaseId}: ${description}\n\n**Goal:** [To be planned]\n**Requirements**: TBD${dependsOn}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /gsd:plan-phase ${_newPhaseId} to break down)\n`; const phaseEntry = `\n### Phase ${_newPhaseId}: ${description}\n\n**Goal:** [To be planned]\n**Requirements**: TBD${dependsOn}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /gsd-plan-phase ${_newPhaseId} to break down)\n`;
// Find insertion point: before last "---" or at end // Find insertion point: before last "---" or at end
let updatedContent; let updatedContent;
@@ -458,7 +458,7 @@ function cmdPhaseAddBatch(cwd, descriptions, raw) {
fs.mkdirSync(dirPath, { recursive: true }); fs.mkdirSync(dirPath, { recursive: true });
fs.writeFileSync(path.join(dirPath, '.gitkeep'), ''); fs.writeFileSync(path.join(dirPath, '.gitkeep'), '');
const dependsOn = config.phase_naming === 'custom' ? '' : `\n**Depends on:** Phase ${typeof newPhaseId === 'number' ? newPhaseId - 1 : 'TBD'}`; const dependsOn = config.phase_naming === 'custom' ? '' : `\n**Depends on:** Phase ${typeof newPhaseId === 'number' ? newPhaseId - 1 : 'TBD'}`;
const phaseEntry = `\n### Phase ${newPhaseId}: ${description}\n\n**Goal:** [To be planned]\n**Requirements**: TBD${dependsOn}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /gsd:plan-phase ${newPhaseId} to break down)\n`; const phaseEntry = `\n### Phase ${newPhaseId}: ${description}\n\n**Goal:** [To be planned]\n**Requirements**: TBD${dependsOn}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /gsd-plan-phase ${newPhaseId} to break down)\n`;
const lastSeparator = rawContent.lastIndexOf('\n---'); const lastSeparator = rawContent.lastIndexOf('\n---');
rawContent = lastSeparator > 0 rawContent = lastSeparator > 0
? rawContent.slice(0, lastSeparator) + phaseEntry + rawContent.slice(lastSeparator) ? rawContent.slice(0, lastSeparator) + phaseEntry + rawContent.slice(lastSeparator)
@@ -542,7 +542,7 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw) {
fs.writeFileSync(path.join(dirPath, '.gitkeep'), ''); fs.writeFileSync(path.join(dirPath, '.gitkeep'), '');
// Build phase entry // Build phase entry
const phaseEntry = `\n### Phase ${_decimalPhase}: ${description} (INSERTED)\n\n**Goal:** [Urgent work - to be planned]\n**Requirements**: TBD\n**Depends on:** Phase ${afterPhase}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /gsd:plan-phase ${_decimalPhase} to break down)\n`; const phaseEntry = `\n### Phase ${_decimalPhase}: ${description} (INSERTED)\n\n**Goal:** [Urgent work - to be planned]\n**Requirements**: TBD\n**Depends on:** Phase ${afterPhase}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /gsd-plan-phase ${_decimalPhase} to break down)\n`;
// Insert after the target phase section // Insert after the target phase section
const headerPattern = new RegExp(`(#{2,4}\\s*Phase\\s+0*${afterPhaseEscaped}:[^\\n]*\\n)`, 'i'); const headerPattern = new RegExp(`(#{2,4}\\s*Phase\\s+0*${afterPhaseEscaped}:[^\\n]*\\n)`, 'i');
@@ -828,7 +828,7 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
// Update plan count in phase section. // Update plan count in phase section.
// Use direct .replace() rather than replaceInCurrentMilestone() so this // Use direct .replace() rather than replaceInCurrentMilestone() so this
// works when the current milestone section is itself inside a <details> // works when the current milestone section is itself inside a <details>
// block (the standard /gsd:new-project layout). replaceInCurrentMilestone // block (the standard /gsd-new-project layout). replaceInCurrentMilestone
// scopes to content after the last </details>, which misses content inside // scopes to content after the last </details>, which misses content inside
// the current milestone's own <details> wrapper (#2005). // the current milestone's own <details> wrapper (#2005).
// The phase-scoped heading pattern is specific enough to avoid matching // The phase-scoped heading pattern is specific enough to avoid matching

View File

@@ -173,7 +173,7 @@ const CLAUDE_INSTRUCTIONS = {
}; };
const CLAUDE_MD_FALLBACKS = { const CLAUDE_MD_FALLBACKS = {
project: 'Project not yet initialized. Run /gsd:new-project to set up.', project: 'Project not yet initialized. Run /gsd-new-project to set up.',
stack: 'Technology stack not yet documented. Will populate after codebase mapping or first phase.', stack: 'Technology stack not yet documented. Will populate after codebase mapping or first phase.',
conventions: 'Conventions not yet established. Will populate as patterns emerge during development.', conventions: 'Conventions not yet established. Will populate as patterns emerge during development.',
architecture: 'Architecture not yet mapped. Follow existing patterns found in the codebase.', architecture: 'Architecture not yet mapped. Follow existing patterns found in the codebase.',
@@ -187,9 +187,9 @@ const CLAUDE_MD_WORKFLOW_ENFORCEMENT = [
'Before using Edit, Write, or other file-changing tools, start work through a GSD command so planning artifacts and execution context stay in sync.', 'Before using Edit, Write, or other file-changing tools, start work through a GSD command so planning artifacts and execution context stay in sync.',
'', '',
'Use these entry points:', 'Use these entry points:',
'- `/gsd:quick` for small fixes, doc updates, and ad-hoc tasks', '- `/gsd-quick` for small fixes, doc updates, and ad-hoc tasks',
'- `/gsd:debug` for investigation and bug fixing', '- `/gsd-debug` for investigation and bug fixing',
'- `/gsd:execute-phase` for planned phase work', '- `/gsd-execute-phase` for planned phase work',
'', '',
'Do not make direct repo edits outside a GSD workflow unless the user explicitly asks to bypass it.', 'Do not make direct repo edits outside a GSD workflow unless the user explicitly asks to bypass it.',
].join('\n'); ].join('\n');
@@ -198,7 +198,7 @@ const CLAUDE_MD_PROFILE_PLACEHOLDER = [
'<!-- GSD:profile-start -->', '<!-- GSD:profile-start -->',
'## Developer Profile', '## Developer Profile',
'', '',
'> Profile not yet configured. Run `/gsd:profile-user` to generate your developer profile.', '> Profile not yet configured. Run `/gsd-profile-user` to generate your developer profile.',
'> This section is managed by `generate-claude-profile` -- do not edit manually.', '> This section is managed by `generate-claude-profile` -- do not edit manually.',
'<!-- GSD:profile-end -->', '<!-- GSD:profile-end -->',
].join('\n'); ].join('\n');
@@ -768,7 +768,7 @@ function cmdGenerateDevPreferences(cwd, options, raw) {
let stackBlock; let stackBlock;
if (analysis.data_source === 'questionnaire') { if (analysis.data_source === 'questionnaire') {
stackBlock = 'Stack preferences not available (questionnaire-only profile). Run `/gsd:profile-user --refresh` with session data to populate.'; stackBlock = 'Stack preferences not available (questionnaire-only profile). Run `/gsd-profile-user --refresh` with session data to populate.';
} else if (options.stack) { } else if (options.stack) {
stackBlock = options.stack; stackBlock = options.stack;
} else { } else {
@@ -854,7 +854,7 @@ function cmdGenerateClaudeProfile(cwd, options, raw) {
'<!-- GSD:profile-start -->', '<!-- GSD:profile-start -->',
'## Developer Profile', '## Developer Profile',
'', '',
`> Generated by GSD from ${dataSource}. Run \`/gsd:profile-user --refresh\` to update.`, `> Generated by GSD from ${dataSource}. Run \`/gsd-profile-user --refresh\` to update.`,
'', '',
'| Dimension | Rating | Confidence |', '| Dimension | Rating | Confidence |',
'|-----------|--------|------------|', '|-----------|--------|------------|',
@@ -1053,7 +1053,7 @@ function cmdGenerateClaudeMd(cwd, options, raw) {
let message = `Generated ${genCount}/${totalManaged} sections.`; let message = `Generated ${genCount}/${totalManaged} sections.`;
if (sectionsFallback.length > 0) message += ` Fallback: ${sectionsFallback.join(', ')}.`; if (sectionsFallback.length > 0) message += ` Fallback: ${sectionsFallback.join(', ')}.`;
if (sectionsSkipped.length > 0) message += ` Skipped (manually edited): ${sectionsSkipped.join(', ')}.`; if (sectionsSkipped.length > 0) message += ` Skipped (manually edited): ${sectionsSkipped.join(', ')}.`;
if (profileStatus === 'placeholder_added') message += ' Run /gsd:profile-user to unlock Developer Profile.'; if (profileStatus === 'placeholder_added') message += ' Run /gsd-profile-user to unlock Developer Profile.';
const result = { const result = {
claude_md_path: outputPath, claude_md_path: outputPath,

View File

@@ -2,7 +2,7 @@
/** /**
* Secrets handling — masking convention for API keys and other * Secrets handling — masking convention for API keys and other
* credentials managed via /gsd:settings-integrations. * credentials managed via /gsd-settings-integrations.
* *
* Convention: strings 8+ chars long render as `****<last-4>`; shorter * Convention: strings 8+ chars long render as `****<last-4>`; shorter
* strings render as `****` with no tail (to avoid leaking a meaningful * strings render as `****` with no tail (to avoid leaking a meaningful

View File

@@ -1253,6 +1253,70 @@ function cmdStatePlannedPhase(cwd, phaseNumber, planCount, raw) {
output({ updated, phase: phaseNumber, plan_count: planCount }, raw, updated.length > 0 ? 'true' : 'false'); output({ updated, phase: phaseNumber, plan_count: planCount }, raw, updated.length > 0 ? 'true' : 'false');
} }
/**
* Bug #2630: reset STATE.md for a new milestone cycle.
* Stomps frontmatter milestone/milestone_name/status/progress AND rewrites
* the Current Position body. Preserves Accumulated Context.
* Symmetric with the SDK `stateMilestoneSwitch` handler.
*/
function cmdStateMilestoneSwitch(cwd, version, name, raw) {
if (!version || !String(version).trim()) {
output({ error: 'milestone required (--milestone <vX.Y>)' }, raw);
return;
}
const resolvedName = (name && String(name).trim()) || 'milestone';
const statePath = planningPaths(cwd).state;
const today = new Date().toISOString().split('T')[0];
const lockPath = acquireStateLock(statePath);
try {
const content = fs.existsSync(statePath) ? fs.readFileSync(statePath, 'utf-8') : '';
const existingFm = extractFrontmatter(content);
const body = stripFrontmatter(content);
const positionPattern = /(##\s*Current Position\s*\n)([\s\S]*?)(?=\n##|$)/i;
const resetPositionBody =
`\nPhase: Not started (defining requirements)\n` +
`Plan: —\n` +
`Status: Defining requirements\n` +
`Last activity: ${today} — Milestone ${version} started\n\n`;
let newBody;
if (positionPattern.test(body)) {
newBody = body.replace(positionPattern, (_m, header) => `${header}${resetPositionBody}`);
} else {
const preface = body.trim().length > 0 ? body : '# Project State\n';
newBody = `${preface.trimEnd()}\n\n## Current Position\n${resetPositionBody}`;
}
const fm = {
gsd_state_version: existingFm.gsd_state_version || '1.0',
milestone: version,
milestone_name: resolvedName,
status: 'planning',
last_updated: new Date().toISOString(),
last_activity: today,
progress: {
total_phases: 0,
completed_phases: 0,
total_plans: 0,
completed_plans: 0,
percent: 0,
},
};
const yamlStr = reconstructFrontmatter(fm);
const assembled = `---\n${yamlStr}\n---\n\n${newBody.replace(/^\n+/, '')}`;
atomicWriteFileSync(statePath, normalizeMd(assembled), 'utf-8');
output(
{ switched: true, version, name: resolvedName, status: 'planning' },
raw,
'true',
);
} finally {
releaseStateLock(lockPath);
}
}
/** /**
* Gate 1: Validate STATE.md against filesystem. * Gate 1: Validate STATE.md against filesystem.
* Returns { valid, warnings, drift } JSON. * Returns { valid, warnings, drift } JSON.
@@ -1644,6 +1708,7 @@ module.exports = {
cmdStateValidate, cmdStateValidate,
cmdStateSync, cmdStateSync,
cmdStatePrune, cmdStatePrune,
cmdStateMilestoneSwitch,
cmdSignalWaiting, cmdSignalWaiting,
cmdSignalResume, cmdSignalResume,
}; };

View File

@@ -555,7 +555,7 @@ function cmdValidateHealth(cwd, options, raw) {
// ─── Check 1: .planning/ exists ─────────────────────────────────────────── // ─── Check 1: .planning/ exists ───────────────────────────────────────────
if (!fs.existsSync(planBase)) { if (!fs.existsSync(planBase)) {
addIssue('error', 'E001', '.planning/ directory not found', 'Run /gsd:new-project to initialize'); addIssue('error', 'E001', '.planning/ directory not found', 'Run /gsd-new-project to initialize');
output({ output({
status: 'broken', status: 'broken',
errors, errors,
@@ -568,7 +568,7 @@ function cmdValidateHealth(cwd, options, raw) {
// ─── Check 2: PROJECT.md exists and has required sections ───────────────── // ─── Check 2: PROJECT.md exists and has required sections ─────────────────
if (!fs.existsSync(projectPath)) { if (!fs.existsSync(projectPath)) {
addIssue('error', 'E002', 'PROJECT.md not found', 'Run /gsd:new-project to create'); addIssue('error', 'E002', 'PROJECT.md not found', 'Run /gsd-new-project to create');
} else { } else {
const content = fs.readFileSync(projectPath, 'utf-8'); const content = fs.readFileSync(projectPath, 'utf-8');
const requiredSections = ['## What This Is', '## Core Value', '## Requirements']; const requiredSections = ['## What This Is', '## Core Value', '## Requirements'];
@@ -581,39 +581,68 @@ function cmdValidateHealth(cwd, options, raw) {
// ─── Check 3: ROADMAP.md exists ─────────────────────────────────────────── // ─── Check 3: ROADMAP.md exists ───────────────────────────────────────────
if (!fs.existsSync(roadmapPath)) { if (!fs.existsSync(roadmapPath)) {
addIssue('error', 'E003', 'ROADMAP.md not found', 'Run /gsd:new-milestone to create roadmap'); addIssue('error', 'E003', 'ROADMAP.md not found', 'Run /gsd-new-milestone to create roadmap');
} }
// ─── Check 4: STATE.md exists and references valid phases ───────────────── // ─── Check 4: STATE.md exists and references valid phases ─────────────────
if (!fs.existsSync(statePath)) { if (!fs.existsSync(statePath)) {
addIssue('error', 'E004', 'STATE.md not found', 'Run /gsd:health --repair to regenerate', true); addIssue('error', 'E004', 'STATE.md not found', 'Run /gsd-health --repair to regenerate', true);
repairs.push('regenerateState'); repairs.push('regenerateState');
} else { } else {
const stateContent = fs.readFileSync(statePath, 'utf-8'); const stateContent = fs.readFileSync(statePath, 'utf-8');
// Extract phase references from STATE.md // Extract phase references from STATE.md
const phaseRefs = [...stateContent.matchAll(/[Pp]hase\s+(\d+(?:\.\d+)*)/g)].map(m => m[1]); const phaseRefs = [...stateContent.matchAll(/[Pp]hase\s+(\d+[A-Z]?(?:\.\d+)*)/g)].map(m => m[1]);
// Get disk phases // Bug #2633 — ROADMAP.md is the authority for which phases are valid.
const diskPhases = new Set(); // STATE.md may legitimately reference current-milestone future phases
// (not yet materialized on disk) and shipped-milestone history phases
// (archived / cleared off disk). Matching only against on-disk dirs
// produces false W002 warnings in both cases.
const validPhases = new Set();
try { try {
const entries = fs.readdirSync(phasesDir, { withFileTypes: true }); const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
for (const e of entries) { for (const e of entries) {
if (e.isDirectory()) { if (e.isDirectory()) {
const m = e.name.match(/^(\d+(?:\.\d+)*)/); const m = e.name.match(/^(\d+[A-Z]?(?:\.\d+)*)/);
if (m) diskPhases.add(m[1]); if (m) validPhases.add(m[1]);
} }
} }
} catch { /* intentionally empty */ } } catch { /* intentionally empty */ }
// Union in every phase declared anywhere in ROADMAP.md (current + shipped + backlog).
try {
if (fs.existsSync(roadmapPath)) {
const roadmapRaw = fs.readFileSync(roadmapPath, 'utf-8');
const all = [...roadmapRaw.matchAll(/#{2,4}\s*Phase\s+(\d+[A-Z]?(?:\.\d+)*)/gi)];
for (const m of all) validPhases.add(m[1]);
}
} catch { /* intentionally empty */ }
// Compare canonical full phase tokens. Also accept a leading-zero variant
// on the integer prefix only (e.g. "03" matching "3", "03.1" matching
// "3.1") so historic STATE.md formatting still validates. Suffix tokens
// like "3A" must match exactly — never collapsed to "3".
const normalizedValid = new Set();
for (const p of validPhases) {
normalizedValid.add(p);
const dotIdx = p.indexOf('.');
const head = dotIdx === -1 ? p : p.slice(0, dotIdx);
const tail = dotIdx === -1 ? '' : p.slice(dotIdx);
if (/^\d+$/.test(head)) {
normalizedValid.add(head.padStart(2, '0') + tail);
}
}
// Check for invalid references // Check for invalid references
for (const ref of phaseRefs) { for (const ref of phaseRefs) {
const normalizedRef = String(parseInt(ref, 10)).padStart(2, '0'); const dotIdx = ref.indexOf('.');
if (!diskPhases.has(ref) && !diskPhases.has(normalizedRef) && !diskPhases.has(String(parseInt(ref, 10)))) { const head = dotIdx === -1 ? ref : ref.slice(0, dotIdx);
// Only warn if phases dir has any content (not just an empty project) const tail = dotIdx === -1 ? '' : ref.slice(dotIdx);
if (diskPhases.size > 0) { const padded = /^\d+$/.test(head) ? head.padStart(2, '0') + tail : ref;
if (!normalizedValid.has(ref) && !normalizedValid.has(padded)) {
// Only warn if we know any valid phases (not just an empty project)
if (normalizedValid.size > 0) {
addIssue( addIssue(
'warning', 'warning',
'W002', 'W002',
`STATE.md references phase ${ref}, but only phases ${[...diskPhases].sort().join(', ')} exist`, `STATE.md references phase ${ref}, but only phases ${[...validPhases].sort().join(', ')} are declared`,
'Review STATE.md manually before changing it; /gsd:health --repair will not overwrite an existing STATE.md for phase mismatches' 'Review STATE.md manually before changing it; /gsd-health --repair will not overwrite an existing STATE.md for phase mismatches'
); );
} }
} }
@@ -622,7 +651,7 @@ function cmdValidateHealth(cwd, options, raw) {
// ─── Check 5: config.json valid JSON + valid schema ─────────────────────── // ─── Check 5: config.json valid JSON + valid schema ───────────────────────
if (!fs.existsSync(configPath)) { if (!fs.existsSync(configPath)) {
addIssue('warning', 'W003', 'config.json not found', 'Run /gsd:health --repair to create with defaults', true); addIssue('warning', 'W003', 'config.json not found', 'Run /gsd-health --repair to create with defaults', true);
repairs.push('createConfig'); repairs.push('createConfig');
} else { } else {
try { try {
@@ -634,7 +663,7 @@ function cmdValidateHealth(cwd, options, raw) {
addIssue('warning', 'W004', `config.json: invalid model_profile "${parsed.model_profile}"`, `Valid values: ${validProfiles.join(', ')}`); addIssue('warning', 'W004', `config.json: invalid model_profile "${parsed.model_profile}"`, `Valid values: ${validProfiles.join(', ')}`);
} }
} catch (err) { } catch (err) {
addIssue('error', 'E005', `config.json: JSON parse error - ${err.message}`, 'Run /gsd:health --repair to reset to defaults', true); addIssue('error', 'E005', `config.json: JSON parse error - ${err.message}`, 'Run /gsd-health --repair to reset to defaults', true);
repairs.push('resetConfig'); repairs.push('resetConfig');
} }
} }
@@ -645,11 +674,11 @@ function cmdValidateHealth(cwd, options, raw) {
const configRaw = fs.readFileSync(configPath, 'utf-8'); const configRaw = fs.readFileSync(configPath, 'utf-8');
const configParsed = JSON.parse(configRaw); const configParsed = JSON.parse(configRaw);
if (configParsed.workflow && configParsed.workflow.nyquist_validation === undefined) { if (configParsed.workflow && configParsed.workflow.nyquist_validation === undefined) {
addIssue('warning', 'W008', 'config.json: workflow.nyquist_validation absent (defaults to enabled but agents may skip)', 'Run /gsd:health --repair to add key', true); addIssue('warning', 'W008', 'config.json: workflow.nyquist_validation absent (defaults to enabled but agents may skip)', 'Run /gsd-health --repair to add key', true);
if (!repairs.includes('addNyquistKey')) repairs.push('addNyquistKey'); if (!repairs.includes('addNyquistKey')) repairs.push('addNyquistKey');
} }
if (configParsed.workflow && configParsed.workflow.ai_integration_phase === undefined) { if (configParsed.workflow && configParsed.workflow.ai_integration_phase === undefined) {
addIssue('warning', 'W016', 'config.json: workflow.ai_integration_phase absent (defaults to enabled — run /gsd:ai-integration-phase before planning AI system phases)', 'Run /gsd:health --repair to add key', true); addIssue('warning', 'W016', 'config.json: workflow.ai_integration_phase absent (defaults to enabled — run /gsd-ai-integration-phase before planning AI system phases)', 'Run /gsd-health --repair to add key', true);
if (!repairs.includes('addAiIntegrationPhaseKey')) repairs.push('addAiIntegrationPhaseKey'); if (!repairs.includes('addAiIntegrationPhaseKey')) repairs.push('addAiIntegrationPhaseKey');
} }
} catch { /* intentionally empty */ } } catch { /* intentionally empty */ }
@@ -699,7 +728,7 @@ function cmdValidateHealth(cwd, options, raw) {
try { try {
const researchContent = fs.readFileSync(path.join(phasesDir, e.name, researchFile), 'utf-8'); const researchContent = fs.readFileSync(path.join(phasesDir, e.name, researchFile), 'utf-8');
if (researchContent.includes('## Validation Architecture')) { if (researchContent.includes('## Validation Architecture')) {
addIssue('warning', 'W009', `Phase ${e.name}: has Validation Architecture in RESEARCH.md but no VALIDATION.md`, 'Re-run /gsd:plan-phase with --research to regenerate'); addIssue('warning', 'W009', `Phase ${e.name}: has Validation Architecture in RESEARCH.md but no VALIDATION.md`, 'Re-run /gsd-plan-phase with --research to regenerate');
} }
} catch { /* intentionally empty */ } } catch { /* intentionally empty */ }
} }
@@ -792,7 +821,7 @@ function cmdValidateHealth(cwd, options, raw) {
if (statusVal !== 'complete' && statusVal !== 'done') { if (statusVal !== 'complete' && statusVal !== 'done') {
addIssue('warning', 'W011', addIssue('warning', 'W011',
`STATE.md says current phase is ${statePhase} (status: ${statusVal || 'unknown'}) but ROADMAP.md shows it as [x] complete — state files may be out of sync`, `STATE.md says current phase is ${statePhase} (status: ${statusVal || 'unknown'}) but ROADMAP.md shows it as [x] complete — state files may be out of sync`,
'Run /gsd:progress to re-derive current position, or manually update STATE.md'); 'Run /gsd-progress to re-derive current position, or manually update STATE.md');
} }
} }
} }
@@ -895,7 +924,7 @@ function cmdValidateHealth(cwd, options, raw) {
if (missingFromRegistry.length > 0) { if (missingFromRegistry.length > 0) {
addIssue('warning', 'W018', addIssue('warning', 'W018',
`MILESTONES.md missing ${missingFromRegistry.length} archived milestone(s): ${missingFromRegistry.join(', ')}`, `MILESTONES.md missing ${missingFromRegistry.length} archived milestone(s): ${missingFromRegistry.join(', ')}`,
'Run /gsd:health --backfill to synthesize missing entries from archive snapshots', 'Run /gsd-health --backfill to synthesize missing entries from archive snapshots',
true); true);
repairs.push('backfillMilestones'); repairs.push('backfillMilestones');
} }
@@ -969,7 +998,7 @@ function cmdValidateHealth(cwd, options, raw) {
stateContent += `**Current phase:** (determining...)\n`; stateContent += `**Current phase:** (determining...)\n`;
stateContent += `**Status:** Resuming\n\n`; stateContent += `**Status:** Resuming\n\n`;
stateContent += `## Session Log\n\n`; stateContent += `## Session Log\n\n`;
stateContent += `- ${new Date().toISOString().split('T')[0]}: STATE.md regenerated by /gsd:health --repair\n`; stateContent += `- ${new Date().toISOString().split('T')[0]}: STATE.md regenerated by /gsd-health --repair\n`;
writeStateMd(statePath, stateContent, cwd); writeStateMd(statePath, stateContent, cwd);
repairActions.push({ action: repair, success: true, path: 'STATE.md' }); repairActions.push({ action: repair, success: true, path: 'STATE.md' });
break; break;
@@ -1019,7 +1048,7 @@ function cmdValidateHealth(cwd, options, raw) {
// Build minimal entry from snapshot title or version // Build minimal entry from snapshot title or version
const titleMatch = snapshot && snapshot.match(/^#\s+(.+)$/m); const titleMatch = snapshot && snapshot.match(/^#\s+(.+)$/m);
const milestoneName = titleMatch ? titleMatch[1].replace(/^Milestone\s+/i, '').replace(/^v[\d.]+\s*/, '').trim() : ver; const milestoneName = titleMatch ? titleMatch[1].replace(/^Milestone\s+/i, '').replace(/^v[\d.]+\s*/, '').trim() : ver;
const entry = `## ${ver}${milestoneName && milestoneName !== ver ? ` ${milestoneName}` : ''} (Backfilled: ${today})\n\n**Note:** Synthesized from archive snapshot by \`/gsd:health --backfill\`. Original completion date unknown.\n\n---\n\n`; const entry = `## ${ver}${milestoneName && milestoneName !== ver ? ` ${milestoneName}` : ''} (Backfilled: ${today})\n\n**Note:** Synthesized from archive snapshot by \`/gsd-health --backfill\`. Original completion date unknown.\n\n---\n\n`;
const milestonesContent = fs.existsSync(milestonesPath) const milestonesContent = fs.existsSync(milestonesPath)
? fs.readFileSync(milestonesPath, 'utf-8') ? fs.readFileSync(milestonesPath, 'utf-8')
: ''; : '';

View File

@@ -78,7 +78,7 @@ function cmdWorkstreamCreate(cwd, name, options, raw) {
const baseDir = planningRoot(cwd); const baseDir = planningRoot(cwd);
if (!fs.existsSync(baseDir)) { if (!fs.existsSync(baseDir)) {
error('.planning/ directory not found — run /gsd:new-project first'); error('.planning/ directory not found — run /gsd-new-project first');
} }
const wsRoot = path.join(baseDir, 'workstreams'); const wsRoot = path.join(baseDir, 'workstreams');

View File

@@ -72,21 +72,21 @@ reads is inert — the consumption mechanism is what gives an artifact meaning.
- **Location**: `.planning/spikes/SPIKE-NNN/` - **Location**: `.planning/spikes/SPIKE-NNN/`
- **Consumed by**: Planner when spike is referenced; `pause-work` for spike context handoff - **Consumed by**: Planner when spike is referenced; `pause-work` for spike context handoff
### Spike README.md / MANIFEST.md (per-spike, via /gsd:spike) ### Spike README.md / MANIFEST.md (per-spike, via /gsd-spike)
- **Shape**: YAML frontmatter (spike, name, validates, verdict, related, tags) + run instructions + results - **Shape**: YAML frontmatter (spike, name, validates, verdict, related, tags) + run instructions + results
- **Lifecycle**: Created by `/gsd:spike` → Verified → Wrapped up by `/gsd:spike-wrap-up` - **Lifecycle**: Created by `/gsd-spike` → Verified → Wrapped up by `/gsd-spike-wrap-up`
- **Location**: `.planning/spikes/NNN-name/README.md`, `.planning/spikes/MANIFEST.md` - **Location**: `.planning/spikes/NNN-name/README.md`, `.planning/spikes/MANIFEST.md`
- **Consumed by**: `/gsd:spike-wrap-up` for curation; `pause-work` for spike context handoff - **Consumed by**: `/gsd-spike-wrap-up` for curation; `pause-work` for spike context handoff
### Sketch README.md / MANIFEST.md / index.html (per-sketch) ### Sketch README.md / MANIFEST.md / index.html (per-sketch)
- **Shape**: YAML frontmatter (sketch, name, question, winner, tags) + variants as tabbed HTML - **Shape**: YAML frontmatter (sketch, name, question, winner, tags) + variants as tabbed HTML
- **Lifecycle**: Created by `/gsd:sketch` → Evaluated → Wrapped up by `/gsd:sketch-wrap-up` - **Lifecycle**: Created by `/gsd-sketch` → Evaluated → Wrapped up by `/gsd-sketch-wrap-up`
- **Location**: `.planning/sketches/NNN-name/README.md`, `.planning/sketches/NNN-name/index.html`, `.planning/sketches/MANIFEST.md` - **Location**: `.planning/sketches/NNN-name/README.md`, `.planning/sketches/NNN-name/index.html`, `.planning/sketches/MANIFEST.md`
- **Consumed by**: `/gsd:sketch-wrap-up` for curation; `pause-work` for sketch context handoff - **Consumed by**: `/gsd-sketch-wrap-up` for curation; `pause-work` for sketch context handoff
### WRAP-UP-SUMMARY.md (per wrap-up session) ### WRAP-UP-SUMMARY.md (per wrap-up session)
- **Shape**: Curation results, included/excluded items, feature/design area groupings - **Shape**: Curation results, included/excluded items, feature/design area groupings
- **Lifecycle**: Created by `/gsd:spike-wrap-up` or `/gsd:sketch-wrap-up` - **Lifecycle**: Created by `/gsd-spike-wrap-up` or `/gsd-sketch-wrap-up`
- **Location**: `.planning/spikes/WRAP-UP-SUMMARY.md` or `.planning/sketches/WRAP-UP-SUMMARY.md` - **Location**: `.planning/spikes/WRAP-UP-SUMMARY.md` or `.planning/sketches/WRAP-UP-SUMMARY.md`
- **Consumed by**: Project history; not read by automated workflows - **Consumed by**: Project history; not read by automated workflows

View File

@@ -50,13 +50,13 @@ Standard format for presenting next steps after completing a command or workflow
`/clear` then: `/clear` then:
`/gsd:execute-phase 2` `/gsd-execute-phase 2`
--- ---
**Also available:** **Also available:**
- Review plan before executing - Review plan before executing
- `/gsd:list-phase-assumptions 2` — check assumptions - `/gsd-list-phase-assumptions 2` — check assumptions
--- ---
``` ```
@@ -75,7 +75,7 @@ Add note that this is the last plan and what comes after:
`/clear` then: `/clear` then:
`/gsd:execute-phase 2` `/gsd-execute-phase 2`
--- ---
@@ -97,13 +97,13 @@ Add note that this is the last plan and what comes after:
`/clear` then: `/clear` then:
`/gsd:plan-phase 2` `/gsd-plan-phase 2`
--- ---
**Also available:** **Also available:**
- `/gsd:discuss-phase 2` — gather context first - `/gsd-discuss-phase 2` — gather context first
- `/gsd:research-phase 2` — investigate unknowns - `/gsd-research-phase 2` — investigate unknowns
- Review roadmap - Review roadmap
--- ---
@@ -126,13 +126,13 @@ Show completion status before next action:
`/clear` then: `/clear` then:
`/gsd:plan-phase 3` `/gsd-plan-phase 3`
--- ---
**Also available:** **Also available:**
- `/gsd:discuss-phase 3` — gather context first - `/gsd-discuss-phase 3` — gather context first
- `/gsd:research-phase 3` — investigate unknowns - `/gsd-research-phase 3` — investigate unknowns
- Review what Phase 2 built - Review what Phase 2 built
--- ---
@@ -151,11 +151,11 @@ When there's no clear primary action:
`/clear` then one of: `/clear` then one of:
**To plan directly:** `/gsd:plan-phase 3` **To plan directly:** `/gsd-plan-phase 3`
**To discuss context first:** `/gsd:discuss-phase 3` **To discuss context first:** `/gsd-discuss-phase 3`
**To research unknowns:** `/gsd:research-phase 3` **To research unknowns:** `/gsd-research-phase 3`
--- ---
``` ```
@@ -175,7 +175,7 @@ All 4 phases shipped
`/clear` then: `/clear` then:
`/gsd:new-milestone` `/gsd-new-milestone`
--- ---
``` ```
@@ -218,7 +218,7 @@ Extract: `**02-03: Refresh Token Rotation** — Add /api/auth/refresh with slidi
## To Continue ## To Continue
Run `/clear`, then paste: Run `/clear`, then paste:
/gsd:execute-phase 2 /gsd-execute-phase 2
``` ```
User has no idea what 02-03 is about. User has no idea what 02-03 is about.
@@ -226,7 +226,7 @@ User has no idea what 02-03 is about.
### Don't: Missing /clear explanation ### Don't: Missing /clear explanation
``` ```
`/gsd:plan-phase 3` `/gsd-plan-phase 3`
Run /clear first. Run /clear first.
``` ```
@@ -246,7 +246,7 @@ Sounds like an afterthought. Use "Also available:" instead.
``` ```
``` ```
/gsd:plan-phase 3 /gsd-plan-phase 3
``` ```
``` ```

View File

@@ -1,6 +1,6 @@
# Doc Conflict Engine # Doc Conflict Engine
Shared conflict-detection contract for workflows that ingest external content into `.planning/` (e.g., `/gsd:import`, `/gsd:ingest-docs`). Defines the report format, severity semantics, and safety-gate behavior. The specific checks that populate each severity bucket are workflow-specific and defined by the calling workflow. Shared conflict-detection contract for workflows that ingest external content into `.planning/` (e.g., `/gsd-import`, `/gsd-ingest-docs`). Defines the report format, severity semantics, and safety-gate behavior. The specific checks that populate each severity bucket are workflow-specific and defined by the calling workflow.
--- ---

View File

@@ -1,6 +1,6 @@
# Domain-Aware Probing Patterns # Domain-Aware Probing Patterns
Shared reference for `/gsd-begin`, `/gsd:discuss-phase`, and domain exploration workflows. Shared reference for `/gsd-begin`, `/gsd-discuss-phase`, and domain exploration workflows.
When the user mentions a technology area, use these probes to ask insightful follow-up questions. Don't run through them as a checklist -- pick the 2-3 most relevant based on context. The goal is to surface hidden assumptions and trade-offs the user may not have considered yet. When the user mentions a technology area, use these probes to ask insightful follow-up questions. Don't run through them as a checklist -- pick the 2-3 most relevant based on context. The goal is to surface hidden assumptions and trade-offs the user may not have considered yet.

View File

@@ -47,7 +47,7 @@ Simple 2-option confirmation for re-planning, rebuild, replace plans, commit.
4-option escalation for review escalation (max retries exceeded). 4-option escalation for review escalation (max retries exceeded).
- question: "Phase {N} has failed verification {attempt} times. How should we proceed?" - question: "Phase {N} has failed verification {attempt} times. How should we proceed?"
- header: "Escalate" - header: "Escalate"
- options: Accept gaps | Re-plan (via /gsd:plan-phase) | Debug (via /gsd:debug) | Retry - options: Accept gaps | Re-plan (via /gsd-plan-phase) | Debug (via /gsd-debug) | Retry
## Pattern: multi-option-gaps ## Pattern: multi-option-gaps
4-option gap handler for review gaps-found. 4-option gap handler for review gaps-found.
@@ -78,7 +78,7 @@ Up to 4 suggested next actions with selection (status, resume workflows).
3-option confirmation for quick task scope validation. 3-option confirmation for quick task scope validation.
- question: "This task looks complex. Proceed as quick task or use full planning?" - question: "This task looks complex. Proceed as quick task or use full planning?"
- header: "Scope" - header: "Scope"
- options: Quick task | Full plan (via /gsd:plan-phase) | Revise - options: Quick task | Full plan (via /gsd-plan-phase) | Revise
## Pattern: depth-select ## Pattern: depth-select
3-option depth selection for planning workflow preferences. 3-option depth selection for planning workflow preferences.

View File

@@ -38,7 +38,7 @@ Canonical gate types used across GSD workflows. Every validation checkpoint maps
**Recovery:** Developer investigates root cause, fixes, restarts from checkpoint. **Recovery:** Developer investigates root cause, fixes, restarts from checkpoint.
**Examples:** **Examples:**
- Context window critically low during execution - Context window critically low during execution
- STATE.md in error state blocking /gsd:next - STATE.md in error state blocking /gsd-next
- Verification finds critical missing deliverables - Verification finds critical missing deliverables
--- ---

View File

@@ -274,7 +274,7 @@ Set `commit_docs: false` so planning docs stay local and are not committed to an
### How It Works ### How It Works
1. **Auto-detection:** During `/gsd:new-project`, directories with their own `.git` folder are detected and offered for selection as sub-repos. On subsequent runs, `loadConfig` auto-syncs the `sub_repos` list with the filesystem — adding newly created repos and removing deleted ones. This means `config.json` may be rewritten automatically when repos change on disk. 1. **Auto-detection:** During `/gsd-new-project`, directories with their own `.git` folder are detected and offered for selection as sub-repos. On subsequent runs, `loadConfig` auto-syncs the `sub_repos` list with the filesystem — adding newly created repos and removing deleted ones. This means `config.json` may be rewritten automatically when repos change on disk.
2. **File grouping:** Code files are grouped by their sub-repo prefix (e.g., `backend/src/api/users.ts` belongs to the `backend/` repo). 2. **File grouping:** Code files are grouped by their sub-repo prefix (e.g., `backend/src/api/users.ts` belongs to the `backend/` repo).
3. **Independent commits:** Each sub-repo receives its own atomic commit via `gsd-tools.cjs commit-to-subrepo`. File paths are made relative to the sub-repo root before staging. 3. **Independent commits:** Each sub-repo receives its own atomic commit via `gsd-tools.cjs commit-to-subrepo`. File paths are made relative to the sub-repo root before staging.
4. **Planning stays local:** The `.planning/` directory is not committed; it acts as cross-repo coordination. 4. **Planning stays local:** The `.planning/` directory is not committed; it acts as cross-repo coordination.

View File

@@ -75,7 +75,7 @@ If you're using Claude Code with OpenRouter, a local model, or any non-Anthropic
```bash ```bash
# Via settings command # Via settings command
/gsd:settings /gsd-settings
# → Select "Inherit" for model profile # → Select "Inherit" for model profile
# Or manually in .planning/config.json # Or manually in .planning/config.json
@@ -115,7 +115,7 @@ Overrides take precedence over the profile. Valid values: `opus`, `sonnet`, `hai
## Switching Profiles ## Switching Profiles
Runtime: `/gsd:set-profile <profile>` Runtime: `/gsd-set-profile <profile>`
Per-project default: Set in `.planning/config.json`: Per-project default: Set in `.planning/config.json`:
```json ```json

View File

@@ -36,7 +36,7 @@ Configuration options for `.planning/` directory behavior.
| `workflow.use_worktrees` | `true` | Whether executor agents run in isolated git worktrees. Set to `false` to disable worktrees — agents execute sequentially on the main working tree instead. Recommended for solo developers or when worktree merges cause issues. | | `workflow.use_worktrees` | `true` | Whether executor agents run in isolated git worktrees. Set to `false` to disable worktrees — agents execute sequentially on the main working tree instead. Recommended for solo developers or when worktree merges cause issues. |
| `workflow.subagent_timeout` | `300000` | Timeout in milliseconds for parallel subagent tasks (e.g. codebase mapping). Increase for large codebases or slower models. Default: 300000 (5 minutes). | | `workflow.subagent_timeout` | `300000` | Timeout in milliseconds for parallel subagent tasks (e.g. codebase mapping). Increase for large codebases or slower models. Default: 300000 (5 minutes). |
| `workflow.inline_plan_threshold` | `2` | Plans with this many tasks or fewer execute inline (Pattern C) instead of spawning a subagent. Avoids ~14K token spawn overhead for small plans. Set to `0` to always spawn subagents. | | `workflow.inline_plan_threshold` | `2` | Plans with this many tasks or fewer execute inline (Pattern C) instead of spawning a subagent. Avoids ~14K token spawn overhead for small plans. Set to `0` to always spawn subagents. |
| `manager.flags.discuss` | `""` | Flags passed to `/gsd:discuss-phase` when dispatched from manager (e.g. `"--auto --analyze"`) | | `manager.flags.discuss` | `""` | Flags passed to `/gsd-discuss-phase` when dispatched from manager (e.g. `"--auto --analyze"`) |
| `manager.flags.plan` | `""` | Flags passed to plan workflow when dispatched from manager | | `manager.flags.plan` | `""` | Flags passed to plan workflow when dispatched from manager |
| `manager.flags.execute` | `""` | Flags passed to execute workflow when dispatched from manager | | `manager.flags.execute` | `""` | Flags passed to execute workflow when dispatched from manager |
| `response_language` | `null` | Language for user-facing questions and prompts across all phases/subagents (e.g. `"Portuguese"`, `"Japanese"`, `"Spanish"`). When set, all spawned agents include a directive to respond in this language. | | `response_language` | `null` | Language for user-facing questions and prompts across all phases/subagents (e.g. `"Portuguese"`, `"Japanese"`, `"Spanish"`). When set, all spawned agents include a directive to respond in this language. |
@@ -236,7 +236,7 @@ Generated from `CONFIG_DEFAULTS` (core.cjs) and `VALID_CONFIG_KEYS` (config.cjs)
| `context_window` | number | `200000` | `200000`, `1000000` | Context window size; set `1000000` for 1M-context models | | `context_window` | number | `200000` | `200000`, `1000000` | Context window size; set `1000000` for 1M-context models |
| `resolve_model_ids` | boolean\|string | `false` | `false`, `true`, `"omit"` | Map model aliases to full Claude IDs; `"omit"` returns empty string | | `resolve_model_ids` | boolean\|string | `false` | `false`, `true`, `"omit"` | Map model aliases to full Claude IDs; `"omit"` returns empty string |
| `context` | string\|null | `null` | `"dev"`, `"research"`, `"review"` | Execution context profile that adjusts agent behavior: `"dev"` for development tasks, `"research"` for investigation/exploration, `"review"` for code review workflows | | `context` | string\|null | `null` | `"dev"`, `"research"`, `"review"` | Execution context profile that adjusts agent behavior: `"dev"` for development tasks, `"research"` for investigation/exploration, `"review"` for code review workflows |
| `review.models.<cli>` | string\|null | `null` | Any model ID string | Per-CLI model override for /gsd:review (e.g., `review.models.gemini`). Falls back to CLI default when null. | | `review.models.<cli>` | string\|null | `null` | Any model ID string | Per-CLI model override for /gsd-review (e.g., `review.models.gemini`). Falls back to CLI default when null. |
### Workflow Fields ### Workflow Fields
@@ -252,7 +252,7 @@ Set via `workflow.*` namespace in config.json (e.g., `"workflow": { "research":
| `workflow.auto_advance` | boolean | `false` | `true`, `false` | Auto-advance to next phase after completion | | `workflow.auto_advance` | boolean | `false` | `true`, `false` | Auto-advance to next phase after completion |
| `workflow.node_repair` | boolean | `true` | `true`, `false` | Attempt automatic repair of failed plan nodes | | `workflow.node_repair` | boolean | `true` | `true`, `false` | Attempt automatic repair of failed plan nodes |
| `workflow.node_repair_budget` | number | `2` | Any positive integer | Max repair retries per failed node | | `workflow.node_repair_budget` | number | `2` | Any positive integer | Max repair retries per failed node |
| `workflow.ai_integration_phase` | boolean | `true` | `true`, `false` | Run /gsd:ai-integration-phase before planning AI system phases | | `workflow.ai_integration_phase` | boolean | `true` | `true`, `false` | Run /gsd-ai-integration-phase before planning AI system phases |
| `workflow.ui_phase` | boolean | `true` | `true`, `false` | Generate UI-SPEC.md for frontend phases | | `workflow.ui_phase` | boolean | `true` | `true`, `false` | Generate UI-SPEC.md for frontend phases |
| `workflow.ui_safety_gate` | boolean | `true` | `true`, `false` | Require safety gate approval for UI changes | | `workflow.ui_safety_gate` | boolean | `true` | `true`, `false` | Require safety gate approval for UI changes |
| `workflow.text_mode` | boolean | `false` | `true`, `false` | Use plain-text numbered lists instead of AskUserQuestion menus | | `workflow.text_mode` | boolean | `false` | `true`, `false` | Use plain-text numbered lists instead of AskUserQuestion menus |
@@ -265,7 +265,7 @@ Set via `workflow.*` namespace in config.json (e.g., `"workflow": { "research":
| `workflow.code_review` | boolean | `true` | `true`, `false` | Enable built-in code review step in the ship workflow | | `workflow.code_review` | boolean | `true` | `true`, `false` | Enable built-in code review step in the ship workflow |
| `workflow.code_review_depth` | string | `"standard"` | `"light"`, `"standard"`, `"deep"` | Depth level for code review analysis in the ship workflow | | `workflow.code_review_depth` | string | `"standard"` | `"light"`, `"standard"`, `"deep"` | Depth level for code review analysis in the ship workflow |
| `workflow._auto_chain_active` | boolean | `false` | `true`, `false` | Internal: tracks whether autonomous chaining is active | | `workflow._auto_chain_active` | boolean | `false` | `true`, `false` | Internal: tracks whether autonomous chaining is active |
| `workflow.security_enforcement` | boolean | `true` | `true`, `false` | Enable threat-model-anchored security verification via `/gsd:secure-phase`. When `false`, security checks are skipped entirely | | `workflow.security_enforcement` | boolean | `true` | `true`, `false` | Enable threat-model-anchored security verification via `/gsd-secure-phase`. When `false`, security checks are skipped entirely |
| `workflow.security_asvs_level` | number | `1` | `1`, `2`, `3` | OWASP ASVS verification level. Level 1 = opportunistic, Level 2 = standard, Level 3 = comprehensive | | `workflow.security_asvs_level` | number | `1` | `1`, `2`, `3` | OWASP ASVS verification level. Level 1 = opportunistic, Level 2 = standard, Level 3 = comprehensive |
| `workflow.security_block_on` | string | `"high"` | `"high"`, `"medium"`, `"low"` | Minimum severity that blocks phase advancement | | `workflow.security_block_on` | string | `"high"` | `"high"`, `"medium"`, `"low"` | Minimum severity that blocks phase advancement |
| `workflow.post_planning_gaps` | boolean | `true` | `true`, `false` | Post-planning gap report (#2493). After plans are generated, scans REQUIREMENTS.md and CONTEXT.md `<decisions>` against all PLAN.md files and emits a unified `Source \| Item \| Status` table. Non-blocking. Set to `false` to skip Step 13e of plan-phase. _Alias:_ `post_planning_gaps` is the flat-key form used in `CONFIG_DEFAULTS`; `workflow.post_planning_gaps` is the canonical namespaced form. | | `workflow.post_planning_gaps` | boolean | `true` | `true`, `false` | Post-planning gap report (#2493). After plans are generated, scans REQUIREMENTS.md and CONTEXT.md `<decisions>` against all PLAN.md files and emits a unified `Source \| Item \| Status` table. Non-blocking. Set to `false` to skip Step 13e of plan-phase. _Alias:_ `post_planning_gaps` is the flat-key form used in `CONFIG_DEFAULTS`; `workflow.post_planning_gaps` is the canonical namespaced form. |
@@ -319,11 +319,11 @@ Set via `learnings.*` namespace (e.g., `"learnings": { "max_inject": 5 }`). Used
### Intel Fields ### Intel Fields
Set via `intel.*` namespace (e.g., `"intel": { "enabled": true }`). Controls the queryable codebase intelligence system consumed by `/gsd:intel`. Set via `intel.*` namespace (e.g., `"intel": { "enabled": true }`). Controls the queryable codebase intelligence system consumed by `/gsd-intel`.
| Key | Type | Default | Allowed Values | Description | | Key | Type | Default | Allowed Values | Description |
|-----|------|---------|----------------|-------------| |-----|------|---------|----------------|-------------|
| `intel.enabled` | boolean | `false` | `true`, `false` | Enable queryable codebase intelligence system. When `true`, `/gsd:intel` commands build and query a JSON index in `.planning/intel/`. | | `intel.enabled` | boolean | `false` | `true`, `false` | Enable queryable codebase intelligence system. When `true`, `/gsd-intel` commands build and query a JSON index in `.planning/intel/`. |
### Manager Fields ### Manager Fields
@@ -331,7 +331,7 @@ Set via `manager.*` namespace (e.g., `"manager": { "flags": { "discuss": "--auto
| Key | Type | Default | Allowed Values | Description | | Key | Type | Default | Allowed Values | Description |
|-----|------|---------|----------------|-------------| |-----|------|---------|----------------|-------------|
| `manager.flags.discuss` | string | `""` | Any CLI flags string | Flags passed to `/gsd:discuss-phase` from manager (e.g., `"--auto --analyze"`) | | `manager.flags.discuss` | string | `""` | Any CLI flags string | Flags passed to `/gsd-discuss-phase` from manager (e.g., `"--auto --analyze"`) |
| `manager.flags.plan` | string | `""` | Any CLI flags string | Flags passed to plan workflow from manager | | `manager.flags.plan` | string | `""` | Any CLI flags string | Flags passed to plan workflow from manager |
| `manager.flags.execute` | string | `""` | Any CLI flags string | Flags passed to execute workflow from manager | | `manager.flags.execute` | string | `""` | Any CLI flags string | Flags passed to execute workflow from manager |

View File

@@ -69,7 +69,7 @@ Apply this recommendation to the revision? [Yes] / [No, let me decide]
### 3. Explore — Approach Comparison (requires #1729) ### 3. Explore — Approach Comparison (requires #1729)
**When:** During Socratic conversation, when multiple viable approaches emerge. **When:** During Socratic conversation, when multiple viable approaches emerge.
**Note:** This integration point will be added when /gsd:explore (#1729) lands. **Note:** This integration point will be added when /gsd-explore (#1729) lands.
--- ---

View File

@@ -163,10 +163,10 @@ Then re-run verification to apply.
Overrides can also be managed through the verification workflow: Overrides can also be managed through the verification workflow:
1. Run `/gsd:verify-work` — verification finds gaps 1. Run `/gsd-verify-work` — verification finds gaps
2. Review gaps — determine which are intentional deviations 2. Review gaps — determine which are intentional deviations
3. Add override entries to VERIFICATION.md frontmatter 3. Add override entries to VERIFICATION.md frontmatter
4. Re-run `/gsd:verify-work` — overrides are applied, remaining gaps shown 4. Re-run `/gsd-verify-work` — overrides are applied, remaining gaps shown
</creating_overrides> </creating_overrides>
@@ -183,7 +183,7 @@ When a phase is re-verified (e.g., after gap closure):
### At Milestone Completion ### At Milestone Completion
During `/gsd:audit-milestone`, overrides are surfaced in the audit report: During `/gsd-audit-milestone`, overrides are surfaced in the audit report:
``` ```
### Verification Overrides ({count} across {phase_count} phases) ### Verification Overrides ({count} across {phase_count} phases)

View File

@@ -1,6 +1,6 @@
# AI-SPEC — Phase {N}: {phase_name} # AI-SPEC — Phase {N}: {phase_name}
> AI design contract generated by `/gsd:ai-integration-phase`. Consumed by `gsd-planner` and `gsd-eval-auditor`. > AI design contract generated by `/gsd-ai-integration-phase`. Consumed by `gsd-planner` and `gsd-eval-auditor`.
> Locks framework selection, implementation guidance, and evaluation strategy before planning begins. > Locks framework selection, implementation guidance, and evaluation strategy before planning begins.
--- ---

View File

@@ -104,7 +104,7 @@ files_changed: []
<lifecycle> <lifecycle>
**Creation:** Immediately when /gsd:debug is called **Creation:** Immediately when /gsd-debug is called
- Create file with trigger from user input - Create file with trigger from user input
- Set status to "gathering" - Set status to "gathering"
- Current Focus: next_action = "gather symptoms" - Current Focus: next_action = "gather symptoms"

View File

@@ -12,24 +12,24 @@ These files live directly at `.planning/` — not inside phase subdirectories.
| File | Template | Produced by | Purpose | | File | Template | Produced by | Purpose |
|------|----------|-------------|---------| |------|----------|-------------|---------|
| `PROJECT.md` | `project.md` | `/gsd:new-project` | Project identity, goals, requirements summary | | `PROJECT.md` | `project.md` | `/gsd-new-project` | Project identity, goals, requirements summary |
| `ROADMAP.md` | `roadmap.md` | `/gsd:new-milestone`, `/gsd:new-project` | Phase plan with milestones and progress tracking | | `ROADMAP.md` | `roadmap.md` | `/gsd-new-milestone`, `/gsd-new-project` | Phase plan with milestones and progress tracking |
| `STATE.md` | `state.md` | `/gsd:new-project`, `/gsd:health --repair` | Current session state, active phase, last activity | | `STATE.md` | `state.md` | `/gsd-new-project`, `/gsd-health --repair` | Current session state, active phase, last activity |
| `REQUIREMENTS.md` | `requirements.md` | `/gsd:new-milestone` | Functional requirements with traceability | | `REQUIREMENTS.md` | `requirements.md` | `/gsd-new-milestone` | Functional requirements with traceability |
| `MILESTONES.md` | `milestone.md` | `/gsd:complete-milestone` | Log of completed milestones with accomplishments | | `MILESTONES.md` | `milestone.md` | `/gsd-complete-milestone` | Log of completed milestones with accomplishments |
| `BACKLOG.md` | *(inline)* | `/gsd:add-backlog` | Pending ideas and deferred work | | `BACKLOG.md` | *(inline)* | `/gsd-add-backlog` | Pending ideas and deferred work |
| `LEARNINGS.md` | *(inline)* | `/gsd-extract-learnings`, `/gsd:execute-phase` | Phase retrospective learnings for future plans | | `LEARNINGS.md` | *(inline)* | `/gsd-extract-learnings`, `/gsd-execute-phase` | Phase retrospective learnings for future plans |
| `THREADS.md` | *(inline)* | `/gsd:thread` | Persistent discussion threads | | `THREADS.md` | *(inline)* | `/gsd-thread` | Persistent discussion threads |
| `config.json` | `config.json` | `/gsd:new-project`, `/gsd:health --repair` | Project-specific GSD configuration | | `config.json` | `config.json` | `/gsd-new-project`, `/gsd-health --repair` | Project-specific GSD configuration |
| `CLAUDE.md` | `claude-md.md` | `/gsd-profile` | Auto-assembled Claude Code context file | | `CLAUDE.md` | `claude-md.md` | `/gsd-profile` | Auto-assembled Claude Code context file |
### Version-stamped artifacts (pattern: `vX.Y-*.md`) ### Version-stamped artifacts (pattern: `vX.Y-*.md`)
| Pattern | Produced by | Purpose | | Pattern | Produced by | Purpose |
|---------|-------------|---------| |---------|-------------|---------|
| `vX.Y-MILESTONE-AUDIT.md` | `/gsd:audit-milestone` | Milestone audit report before archiving | | `vX.Y-MILESTONE-AUDIT.md` | `/gsd-audit-milestone` | Milestone audit report before archiving |
These files are archived to `.planning/milestones/` by `/gsd:complete-milestone`. Finding them at the `.planning/` root after completion indicates the archive step was skipped. These files are archived to `.planning/milestones/` by `/gsd-complete-milestone`. Finding them at the `.planning/` root after completion indicates the archive step was skipped.
--- ---
@@ -39,24 +39,24 @@ These files live inside a phase directory. They are NOT checked by W019 (which o
| File Pattern | Template | Produced by | Purpose | | File Pattern | Template | Produced by | Purpose |
|-------------|----------|-------------|---------| |-------------|----------|-------------|---------|
| `NN-MM-PLAN.md` | `phase-prompt.md` | `/gsd:plan-phase` | Executable implementation plan | | `NN-MM-PLAN.md` | `phase-prompt.md` | `/gsd-plan-phase` | Executable implementation plan |
| `NN-MM-SUMMARY.md` | `summary.md` | `/gsd:execute-phase` | Post-execution summary with learnings | | `NN-MM-SUMMARY.md` | `summary.md` | `/gsd-execute-phase` | Post-execution summary with learnings |
| `NN-CONTEXT.md` | `context.md` | `/gsd:discuss-phase` | Scoped discussion decisions for the phase | | `NN-CONTEXT.md` | `context.md` | `/gsd-discuss-phase` | Scoped discussion decisions for the phase |
| `NN-RESEARCH.md` | `research.md` | `/gsd:research-phase`, `/gsd:plan-phase` | Technical research for the phase | | `NN-RESEARCH.md` | `research.md` | `/gsd-research-phase`, `/gsd-plan-phase` | Technical research for the phase |
| `NN-VALIDATION.md` | `VALIDATION.md` | `/gsd:research-phase` (Nyquist) | Validation architecture (Nyquist method) | | `NN-VALIDATION.md` | `VALIDATION.md` | `/gsd-research-phase` (Nyquist) | Validation architecture (Nyquist method) |
| `NN-UAT.md` | `UAT.md` | `/gsd:validate-phase` | User acceptance test results | | `NN-UAT.md` | `UAT.md` | `/gsd-validate-phase` | User acceptance test results |
| `NN-PATTERNS.md` | *(inline)* | `/gsd:plan-phase` (pattern mapper) | Analog file mapping for the phase | | `NN-PATTERNS.md` | *(inline)* | `/gsd-plan-phase` (pattern mapper) | Analog file mapping for the phase |
| `NN-UI-SPEC.md` | `UI-SPEC.md` | `/gsd:ui-phase` | UI design contract | | `NN-UI-SPEC.md` | `UI-SPEC.md` | `/gsd-ui-phase` | UI design contract |
| `NN-SECURITY.md` | `SECURITY.md` | `/gsd:secure-phase` | Security threat model | | `NN-SECURITY.md` | `SECURITY.md` | `/gsd-secure-phase` | Security threat model |
| `NN-AI-SPEC.md` | `AI-SPEC.md` | `/gsd:ai-integration-phase` | AI integration spec with eval strategy | | `NN-AI-SPEC.md` | `AI-SPEC.md` | `/gsd-ai-integration-phase` | AI integration spec with eval strategy |
| `NN-DEBUG.md` | `DEBUG.md` | `/gsd:debug` | Debug session log | | `NN-DEBUG.md` | `DEBUG.md` | `/gsd-debug` | Debug session log |
| `NN-REVIEWS.md` | *(inline)* | `/gsd:review` | Cross-AI review feedback | | `NN-REVIEWS.md` | *(inline)* | `/gsd-review` | Cross-AI review feedback |
--- ---
## Milestone Archive (`.planning/milestones/`) ## Milestone Archive (`.planning/milestones/`)
Files archived by `/gsd:complete-milestone`. These are never checked by W019. Files archived by `/gsd-complete-milestone`. These are never checked by W019.
| File Pattern | Source | | File Pattern | Source |
|-------------|--------| |-------------|--------|

View File

@@ -106,7 +106,7 @@ blocked: [N]
**Gaps:** **Gaps:**
- APPEND only when issue found (YAML format) - APPEND only when issue found (YAML format)
- After diagnosis: fill `root_cause`, `artifacts`, `missing`, `debug_session` - After diagnosis: fill `root_cause`, `artifacts`, `missing`, `debug_session`
- This section feeds directly into /gsd:plan-phase --gaps - This section feeds directly into /gsd-plan-phase --gaps
</section_rules> </section_rules>
@@ -120,7 +120,7 @@ blocked: [N]
4. UAT.md Gaps section updated with diagnosis: 4. UAT.md Gaps section updated with diagnosis:
- Each gap gets `root_cause`, `artifacts`, `missing`, `debug_session` filled - Each gap gets `root_cause`, `artifacts`, `missing`, `debug_session` filled
5. status → "diagnosed" 5. status → "diagnosed"
6. Ready for /gsd:plan-phase --gaps with root causes 6. Ready for /gsd-plan-phase --gaps with root causes
**After diagnosis:** **After diagnosis:**
```yaml ```yaml
@@ -144,7 +144,7 @@ blocked: [N]
<lifecycle> <lifecycle>
**Creation:** When /gsd:verify-work starts new session **Creation:** When /gsd-verify-work starts new session
- Extract tests from SUMMARY.md files - Extract tests from SUMMARY.md files
- Set status to "testing" - Set status to "testing"
- Current Test points to test 1 - Current Test points to test 1
@@ -171,7 +171,7 @@ blocked: [N]
- Present summary with outstanding items highlighted - Present summary with outstanding items highlighted
**Resuming partial session:** **Resuming partial session:**
- `/gsd:verify-work {phase}` picks up from first pending/blocked test - `/gsd-verify-work {phase}` picks up from first pending/blocked test
- When all items resolved, status advances to "complete" - When all items resolved, status advances to "complete"
**Resume after /clear:** **Resume after /clear:**

View File

@@ -29,7 +29,7 @@ created: {date}
- **After every task commit:** Run `{quick run command}` - **After every task commit:** Run `{quick run command}`
- **After every plan wave:** Run `{full suite command}` - **After every plan wave:** Run `{full suite command}`
- **Before `/gsd:verify-work`:** Full suite must be green - **Before `/gsd-verify-work`:** Full suite must be green
- **Max feedback latency:** {N} seconds - **Max feedback latency:** {N} seconds
--- ---

View File

@@ -21,7 +21,7 @@ The profile section is managed exclusively by `generate-claude-profile`.
**Fallback text:** **Fallback text:**
``` ```
Project not yet initialized. Run /gsd:new-project to set up. Project not yet initialized. Run /gsd-new-project to set up.
``` ```
### Stack Section ### Stack Section
@@ -96,9 +96,9 @@ No project skills found. Add skills to any of: `.claude/skills/`, `.agents/skill
Before using Edit, Write, or other file-changing tools, start work through a GSD command so planning artifacts and execution context stay in sync. Before using Edit, Write, or other file-changing tools, start work through a GSD command so planning artifacts and execution context stay in sync.
Use these entry points: Use these entry points:
- `/gsd:quick` for small fixes, doc updates, and ad-hoc tasks - `/gsd-quick` for small fixes, doc updates, and ad-hoc tasks
- `/gsd:debug` for investigation and bug fixing - `/gsd-debug` for investigation and bug fixing
- `/gsd:execute-phase` for planned phase work - `/gsd-execute-phase` for planned phase work
Do not make direct repo edits outside a GSD workflow unless the user explicitly asks to bypass it. Do not make direct repo edits outside a GSD workflow unless the user explicitly asks to bypass it.
<!-- GSD:workflow-end --> <!-- GSD:workflow-end -->
@@ -109,7 +109,7 @@ Do not make direct repo edits outside a GSD workflow unless the user explicitly
<!-- GSD:profile-start --> <!-- GSD:profile-start -->
## Developer Profile ## Developer Profile
> Profile not yet configured. Run `/gsd:profile-user` to generate your developer profile. > Profile not yet configured. Run `/gsd-profile-user` to generate your developer profile.
> This section is managed by `generate-claude-profile` — do not edit manually. > This section is managed by `generate-claude-profile` — do not edit manually.
<!-- GSD:profile-end --> <!-- GSD:profile-end -->
``` ```

View File

@@ -51,7 +51,7 @@ Create: .planning/debug/{slug}.md
## Usage ## Usage
**From /gsd:debug:** **From /gsd-debug:**
```python ```python
Task( Task(
prompt=filled_template, prompt=filled_template,

View File

@@ -5,7 +5,7 @@ description: Load developer preferences into this session
# Developer Preferences # Developer Preferences
> Generated by GSD on {{generated_at}} from {{data_source}}. > Generated by GSD on {{generated_at}} from {{data_source}}.
> Run `/gsd:profile-user --refresh` to regenerate. > Run `/gsd-profile-user --refresh` to regenerate.
## Behavioral Directives ## Behavioral Directives

View File

@@ -4,7 +4,7 @@ Template for `.planning/phases/XX-name/DISCOVERY.md` - shallow research for libr
**Purpose:** Answer "which library/option should we use" questions during mandatory discovery in plan-phase. **Purpose:** Answer "which library/option should we use" questions during mandatory discovery in plan-phase.
For deep ecosystem research ("how do experts build this"), use `/gsd:research-phase` which produces RESEARCH.md. For deep ecosystem research ("how do experts build this"), use `/gsd-research-phase` which produces RESEARCH.md.
--- ---
@@ -142,5 +142,5 @@ Create `.planning/phases/XX-name/DISCOVERY.md`:
- Niche/complex domains (3D, games, audio, shaders) - Niche/complex domains (3D, games, audio, shaders)
- Need ecosystem knowledge, not just library choice - Need ecosystem knowledge, not just library choice
- "How do experts build this" questions - "How do experts build this" questions
- Use `/gsd:research-phase` for these - Use `/gsd-research-phase` for these
</guidelines> </guidelines>

View File

@@ -142,7 +142,7 @@ After completion, create `.planning/phases/XX-name/{phase}-{plan}-SUMMARY.md`
| `user_setup` | No | Array of human-required setup items (external services) | | `user_setup` | No | Array of human-required setup items (external services) |
| `must_haves` | Yes | Goal-backward verification criteria (see below) | | `must_haves` | Yes | Goal-backward verification criteria (see below) |
**Wave is pre-computed:** Wave numbers are assigned during `/gsd:plan-phase`. Execute-phase reads `wave` directly from frontmatter and groups plans by wave number. No runtime dependency analysis needed. **Wave is pre-computed:** Wave numbers are assigned during `/gsd-plan-phase`. Execute-phase reads `wave` directly from frontmatter and groups plans by wave number. No runtime dependency analysis needed.
**Must-haves enable verification:** The `must_haves` field carries goal-backward requirements from planning to execution. After all plans complete, execute-phase spawns a verification subagent that checks these criteria against the actual codebase. **Must-haves enable verification:** The `must_haves` field carries goal-backward requirements from planning to execution. After all plans complete, execute-phase spawns a verification subagent that checks these criteria against the actual codebase.

View File

@@ -34,7 +34,7 @@ Template for spawning gsd-planner agent. The agent contains all planning experti
</planning_context> </planning_context>
<downstream_consumer> <downstream_consumer>
Output consumed by /gsd:execute-phase Output consumed by /gsd-execute-phase
Plans must be executable prompts with: Plans must be executable prompts with:
- Frontmatter (wave, depends_on, files_modified, autonomous) - Frontmatter (wave, depends_on, files_modified, autonomous)
- Tasks in XML format - Tasks in XML format
@@ -68,7 +68,7 @@ Before returning PLANNING COMPLETE:
## Usage ## Usage
**From /gsd:plan-phase (standard mode):** **From /gsd-plan-phase (standard mode):**
```python ```python
Task( Task(
prompt=filled_template, prompt=filled_template,
@@ -77,7 +77,7 @@ Task(
) )
``` ```
**From /gsd:plan-phase --gaps (gap closure mode):** **From /gsd-plan-phase --gaps (gap closure mode):**
```python ```python
Task( Task(
prompt=filled_template, # with mode: gap_closure prompt=filled_template, # with mode: gap_closure

View File

@@ -149,7 +149,7 @@ and implemented by workflows/transition.md and workflows/complete-milestone.md.
For existing codebases: For existing codebases:
1. **Map codebase first** via `/gsd:map-codebase` 1. **Map codebase first** via `/gsd-map-codebase`
2. **Infer Validated requirements** from existing code: 2. **Infer Validated requirements** from existing code:
- What does the codebase actually do? - What does the codebase actually do?

View File

@@ -18,7 +18,7 @@ Template for `.planning/phases/XX-name/{phase_num}-RESEARCH.md` - comprehensive
<user_constraints> <user_constraints>
## User Constraints (from CONTEXT.md) ## User Constraints (from CONTEXT.md)
**CRITICAL:** If CONTEXT.md exists from /gsd:discuss-phase, copy locked decisions here verbatim. These MUST be honored by the planner. **CRITICAL:** If CONTEXT.md exists from /gsd-discuss-phase, copy locked decisions here verbatim. These MUST be honored by the planner.
### Locked Decisions ### Locked Decisions
[Copy from CONTEXT.md `## Decisions` section - these are NON-NEGOTIABLE] [Copy from CONTEXT.md `## Decisions` section - these are NON-NEGOTIABLE]

View File

@@ -96,7 +96,7 @@ Status: ✓ = met minimum, ⚠ = below minimum (planner treats as assumption)
*Phase: [XX-name]* *Phase: [XX-name]*
*Spec created: [date]* *Spec created: [date]*
*Next step: /gsd:discuss-phase [X] — implementation decisions (how to build what's specified above)* *Next step: /gsd-discuss-phase [X] — implementation decisions (how to build what's specified above)*
``` ```
<good_examples> <good_examples>
@@ -192,7 +192,7 @@ The database has a `posts` table and `follows` table. No feed query or feed UI e
*Phase: 03-post-feed* *Phase: 03-post-feed*
*Spec created: 2025-01-20* *Spec created: 2025-01-20*
*Next step: /gsd:discuss-phase 3 — implementation decisions (card layout, loading skeleton, etc.)* *Next step: /gsd-discuss-phase 3 — implementation decisions (card layout, loading skeleton, etc.)*
``` ```
**Example 2: CLI tool (Database backup)** **Example 2: CLI tool (Database backup)**
@@ -280,7 +280,7 @@ No backup tooling exists. The project uses PostgreSQL. Developers currently use
*Phase: 02-backup-command* *Phase: 02-backup-command*
*Spec created: 2025-01-20* *Spec created: 2025-01-20*
*Next step: /gsd:discuss-phase 2 — implementation decisions (progress reporting, flag design, etc.)* *Next step: /gsd-discuss-phase 2 — implementation decisions (progress reporting, flag design, etc.)*
``` ```
</good_examples> </good_examples>

View File

@@ -153,10 +153,10 @@ Updated after each plan completion.
**Decisions:** Reference to PROJECT.md Key Decisions table, plus recent decisions summary for quick access. Full decision log lives in PROJECT.md. **Decisions:** Reference to PROJECT.md Key Decisions table, plus recent decisions summary for quick access. Full decision log lives in PROJECT.md.
**Pending Todos:** Ideas captured via /gsd:add-todo **Pending Todos:** Ideas captured via /gsd-add-todo
- Count of pending todos - Count of pending todos
- Reference to .planning/todos/pending/ - Reference to .planning/todos/pending/
- Brief list if few, count if many (e.g., "5 pending todos — see /gsd:check-todos") - Brief list if few, count if many (e.g., "5 pending todos — see /gsd-check-todos")
**Blockers/Concerns:** From "Next Phase Readiness" sections **Blockers/Concerns:** From "Next Phase Readiness" sections
- Issues that affect future work - Issues that affect future work

View File

@@ -11,15 +11,15 @@ Read all files referenced by the invoking prompt's execution_context before star
<step name="parse_arguments"> <step name="parse_arguments">
Parse the command arguments: Parse the command arguments:
- All arguments become the phase description - All arguments become the phase description
- Example: `/gsd:add-phase Add authentication` → description = "Add authentication" - Example: `/gsd-add-phase Add authentication` → description = "Add authentication"
- Example: `/gsd:add-phase Fix critical performance issues` → description = "Fix critical performance issues" - Example: `/gsd-add-phase Fix critical performance issues` → description = "Fix critical performance issues"
If no arguments provided: If no arguments provided:
``` ```
ERROR: Phase description required ERROR: Phase description required
Usage: /gsd:add-phase <description> Usage: /gsd-add-phase <description>
Example: /gsd:add-phase Add authentication system Example: /gsd-add-phase Add authentication system
``` ```
Exit. Exit.
@@ -36,7 +36,7 @@ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
Check `roadmap_exists` from init JSON. If false: Check `roadmap_exists` from init JSON. If false:
``` ```
ERROR: No roadmap found (.planning/ROADMAP.md) ERROR: No roadmap found (.planning/ROADMAP.md)
Run /gsd:new-project to initialize. Run /gsd-new-project to initialize.
``` ```
Exit. Exit.
</step> </step>
@@ -89,12 +89,12 @@ Roadmap updated: .planning/ROADMAP.md
`/clear` then: `/clear` then:
`/gsd:plan-phase {N}` `/gsd-plan-phase {N}`
--- ---
**Also available:** **Also available:**
- `/gsd:add-phase <description>` — add another phase - `/gsd-add-phase <description>` — add another phase
- Review roadmap - Review roadmap
--- ---

View File

@@ -1,7 +1,7 @@
<purpose> <purpose>
Generate unit and E2E tests for a completed phase based on its SUMMARY.md, CONTEXT.md, and implementation. Classifies each changed file into TDD (unit), E2E (browser), or Skip categories, presents a test plan for user approval, then generates tests following RED-GREEN conventions. Generate unit and E2E tests for a completed phase based on its SUMMARY.md, CONTEXT.md, and implementation. Classifies each changed file into TDD (unit), E2E (browser), or Skip categories, presents a test plan for user approval, then generates tests following RED-GREEN conventions.
Users currently hand-craft `/gsd:quick` prompts for test generation after each phase. This workflow standardizes the process with proper classification, quality gates, and gap reporting. Users currently hand-craft `/gsd-quick` prompts for test generation after each phase. This workflow standardizes the process with proper classification, quality gates, and gap reporting.
</purpose> </purpose>
<required_reading> <required_reading>
@@ -15,15 +15,15 @@ Parse `$ARGUMENTS` for:
- Phase number (integer, decimal, or letter-suffix) → store as `$PHASE_ARG` - Phase number (integer, decimal, or letter-suffix) → store as `$PHASE_ARG`
- Remaining text after phase number → store as `$EXTRA_INSTRUCTIONS` (optional) - Remaining text after phase number → store as `$EXTRA_INSTRUCTIONS` (optional)
Example: `/gsd:add-tests 12 focus on edge cases``$PHASE_ARG=12`, `$EXTRA_INSTRUCTIONS="focus on edge cases"` Example: `/gsd-add-tests 12 focus on edge cases``$PHASE_ARG=12`, `$EXTRA_INSTRUCTIONS="focus on edge cases"`
If no phase argument provided: If no phase argument provided:
``` ```
ERROR: Phase number required ERROR: Phase number required
Usage: /gsd:add-tests <phase> [additional instructions] Usage: /gsd-add-tests <phase> [additional instructions]
Example: /gsd:add-tests 12 Example: /gsd-add-tests 12
Example: /gsd:add-tests 12 focus on edge cases in the pricing module Example: /gsd-add-tests 12 focus on edge cases in the pricing module
``` ```
Exit. Exit.
@@ -54,7 +54,7 @@ Read the phase artifacts (in order of priority):
If no SUMMARY.md exists: If no SUMMARY.md exists:
``` ```
ERROR: No SUMMARY.md found for phase ${PHASE_ARG} ERROR: No SUMMARY.md found for phase ${PHASE_ARG}
This command works on completed phases. Run /gsd:execute-phase first. This command works on completed phases. Run /gsd-execute-phase first.
``` ```
Exit. Exit.
@@ -318,7 +318,7 @@ Present next steps:
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE} ## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
{if bugs discovered:} {if bugs discovered:}
**Fix discovered bugs:** `/gsd:quick fix the {N} test failures discovered in phase ${phase_number}` **Fix discovered bugs:** `/gsd-quick fix the {N} test failures discovered in phase ${phase_number}`
{if blocked tests:} {if blocked tests:}
**Resolve test blockers:** {description of what's needed} **Resolve test blockers:** {description of what's needed}
@@ -329,8 +329,8 @@ Present next steps:
--- ---
**Also available:** **Also available:**
- `/gsd:add-tests {next_phase}` — test another phase - `/gsd-add-tests {next_phase}` — test another phase
- `/gsd:verify-work {phase_number}` — run UAT verification - `/gsd-verify-work {phase_number}` — run UAT verification
--- ---
``` ```

View File

@@ -28,7 +28,7 @@ Note existing areas from the todos array for consistency in infer_area step.
<step name="extract_content"> <step name="extract_content">
**With arguments:** Use as the title/focus. **With arguments:** Use as the title/focus.
- `/gsd:add-todo Add auth token refresh` → title = "Add auth token refresh" - `/gsd-add-todo Add auth token refresh` → title = "Add auth token refresh"
**Without arguments:** Analyze recent conversation to extract: **Without arguments:** Analyze recent conversation to extract:
- The specific problem, idea, or task discussed - The specific problem, idea, or task discussed
@@ -143,7 +143,7 @@ Would you like to:
1. Continue with current work 1. Continue with current work
2. Add another todo 2. Add another todo
3. View all todos (/gsd:check-todos) 3. View all todos (/gsd-check-todos)
``` ```
</step> </step>

View File

@@ -43,11 +43,11 @@ AI_PHASE_ENABLED=$(gsd-sdk query config-get workflow.ai_integration_phase 2>/dev
**If `AI_PHASE_ENABLED` is `false`:** **If `AI_PHASE_ENABLED` is `false`:**
``` ```
AI phase is disabled in config. Enable via /gsd:settings. AI phase is disabled in config. Enable via /gsd-settings.
``` ```
Exit workflow. Exit workflow.
**If `planning_exists` is false:** Error — run `/gsd:new-project` first. **If `planning_exists` is false:** Error — run `/gsd-new-project` first.
## 2. Parse and Validate Phase ## 2. Parse and Validate Phase
@@ -64,7 +64,7 @@ PHASE_INFO=$(gsd-sdk query roadmap.get-phase "${PHASE}")
**If `has_context` is false:** **If `has_context` is false:**
``` ```
No CONTEXT.md found for Phase {N}. No CONTEXT.md found for Phase {N}.
Recommended: run /gsd:discuss-phase {N} first to capture framework preferences. Recommended: run /gsd-discuss-phase {N} first to capture framework preferences.
Continuing without user decisions — framework selector will ask all questions. Continuing without user decisions — framework selector will ask all questions.
``` ```
Continue (non-blocking). Continue (non-blocking).
@@ -122,7 +122,7 @@ Goal: {phase_goal}
Parse selector output for: `primary_framework`, `system_type`, `model_provider`, `eval_concerns`, `alternative_framework`. Parse selector output for: `primary_framework`, `system_type`, `model_provider`, `eval_concerns`, `alternative_framework`.
**If selector fails or returns empty:** Exit with error — "Framework selection failed. Re-run /gsd:ai-integration-phase {N} or answer the framework question in /gsd:discuss-phase {N} first." **If selector fails or returns empty:** Exit with error — "Framework selection failed. Re-run /gsd-ai-integration-phase {N} or answer the framework question in /gsd-discuss-phase {N} first."
## 6. Initialize AI-SPEC.md ## 6. Initialize AI-SPEC.md
@@ -266,7 +266,7 @@ git commit -m "docs({phase_slug}): generate AI-SPEC.md — {primary_framework} +
◆ Output: {ai_spec_path} ◆ Output: {ai_spec_path}
Next step: Next step:
/gsd:plan-phase {N} — planner will consume AI-SPEC.md /gsd-plan-phase {N} — planner will consume AI-SPEC.md
``` ```
</process> </process>

View File

@@ -1,12 +1,12 @@
<purpose> <purpose>
Analyze ROADMAP.md phases for dependency relationships before execution. Detect file overlap between phases, semantic API/data-flow dependencies, and suggest `Depends on` entries to prevent merge conflicts during parallel execution by `/gsd:manager`. Analyze ROADMAP.md phases for dependency relationships before execution. Detect file overlap between phases, semantic API/data-flow dependencies, and suggest `Depends on` entries to prevent merge conflicts during parallel execution by `/gsd-manager`.
</purpose> </purpose>
<process> <process>
## 1. Load ROADMAP.md ## 1. Load ROADMAP.md
Read `.planning/ROADMAP.md`. If it does not exist, error: "No ROADMAP.md found — run `/gsd:new-project` first." Read `.planning/ROADMAP.md`. If it does not exist, error: "No ROADMAP.md found — run `/gsd-new-project` first."
Extract all phases. For each phase capture: Extract all phases. For each phase capture:
- Phase number and name - Phase number and name
@@ -91,6 +91,6 @@ When writing to ROADMAP.md:
- Preserve all other phase content unchanged - Preserve all other phase content unchanged
- Do not reorder phases - Do not reorder phases
After applying: "ROADMAP.md updated. Run `/gsd:manager` to execute phases in the correct order." After applying: "ROADMAP.md updated. Run `/gsd-manager` to execute phases in the correct order."
</process> </process>

View File

@@ -18,7 +18,7 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo
```bash ```bash
INIT=$(gsd-sdk query init.milestone-op) INIT=$(gsd-sdk query init.milestone-op)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_CHECKER=$(gsd-sdk query agent-skills gsd-integration-checker 2>/dev/null) AGENT_SKILLS_CHECKER=$(gsd-sdk query agent-skills gsd-integration-checker)
``` ```
Extract from init JSON: `milestone_version`, `milestone_name`, `phase_count`, `completed_phases`, `commit_docs`. Extract from init JSON: `milestone_version`, `milestone_name`, `phase_count`, `completed_phases`, `commit_docs`.
@@ -158,7 +158,7 @@ Classify per phase:
Add to audit YAML: `nyquist: { compliant_phases, partial_phases, missing_phases, overall }` Add to audit YAML: `nyquist: { compliant_phases, partial_phases, missing_phases, overall }`
Discovery only — never auto-calls `/gsd:validate-phase`. Discovery only — never auto-calls `/gsd-validate-phase`.
## 6. Aggregate into v{version}-MILESTONE-AUDIT.md ## 6. Aggregate into v{version}-MILESTONE-AUDIT.md
@@ -231,7 +231,7 @@ All requirements covered. Cross-phase integration verified. E2E flows complete.
/clear then: /clear then:
/gsd:complete-milestone {version} /gsd-complete-milestone {version}
─────────────────────────────────────────────────────────────── ───────────────────────────────────────────────────────────────
@@ -264,9 +264,9 @@ All requirements covered. Cross-phase integration verified. E2E flows complete.
| Phase | VALIDATION.md | Compliant | Action | | Phase | VALIDATION.md | Compliant | Action |
|-------|---------------|-----------|--------| |-------|---------------|-----------|--------|
| {phase} | exists/missing | true/false/partial | `/gsd:validate-phase {N}` | | {phase} | exists/missing | true/false/partial | `/gsd-validate-phase {N}` |
Phases needing validation: run `/gsd:validate-phase {N}` for each flagged phase. Phases needing validation: run `/gsd-validate-phase {N}` for each flagged phase.
─────────────────────────────────────────────────────────────── ───────────────────────────────────────────────────────────────
@@ -276,13 +276,13 @@ Phases needing validation: run `/gsd:validate-phase {N}` for each flagged phase.
/clear then: /clear then:
/gsd:plan-milestone-gaps /gsd-plan-milestone-gaps
─────────────────────────────────────────────────────────────── ───────────────────────────────────────────────────────────────
**Also available:** **Also available:**
- cat .planning/v{version}-MILESTONE-AUDIT.md — see full report - cat .planning/v{version}-MILESTONE-AUDIT.md — see full report
- /gsd:complete-milestone {version} — proceed anyway (accept tech debt) - /gsd-complete-milestone {version} — proceed anyway (accept tech debt)
─────────────────────────────────────────────────────────────── ───────────────────────────────────────────────────────────────
@@ -312,13 +312,13 @@ All requirements met. No critical blockers. Accumulated tech debt needs review.
**A. Complete milestone** — accept debt, track in backlog **A. Complete milestone** — accept debt, track in backlog
/gsd:complete-milestone {version} /gsd-complete-milestone {version}
**B. Plan cleanup phase** — address debt before completing **B. Plan cleanup phase** — address debt before completing
/clear then: /clear then:
/gsd:plan-milestone-gaps /gsd-plan-milestone-gaps
─────────────────────────────────────────────────────────────── ───────────────────────────────────────────────────────────────
</offer_next> </offer_next>

View File

@@ -76,9 +76,9 @@ Present the audit report:
## Recommended Actions ## Recommended Actions
1. **Close stale items:** `/gsd:verify-work {phase}` — mark stale tests as resolved 1. **Close stale items:** `/gsd-verify-work {phase}` — mark stale tests as resolved
2. **Run active tests:** Human UAT test plan below 2. **Run active tests:** Human UAT test plan below
3. **When prerequisites met:** Retest blocked items with `/gsd:verify-work {phase}` 3. **When prerequisites met:** Retest blocked items with `/gsd-verify-work {phase}`
``` ```
</step> </step>

View File

@@ -54,8 +54,8 @@ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
Parse JSON for: `milestone_version`, `milestone_name`, `phase_count`, `completed_phases`, `roadmap_exists`, `state_exists`, `commit_docs`. Parse JSON for: `milestone_version`, `milestone_name`, `phase_count`, `completed_phases`, `roadmap_exists`, `state_exists`, `commit_docs`.
**If `roadmap_exists` is false:** Error — "No ROADMAP.md found. Run `/gsd:new-milestone` first." **If `roadmap_exists` is false:** Error — "No ROADMAP.md found. Run `/gsd-new-milestone` first."
**If `state_exists` is false:** Error — "No STATE.md found. Run `/gsd:new-milestone` first." **If `state_exists` is false:** Error — "No STATE.md found. Run `/gsd-new-milestone` first."
Display startup banner: Display startup banner:
@@ -537,7 +537,7 @@ Read and execute: `$HOME/.claude/get-shit-done/references/autonomous-smart-discu
Completed through phase ${TO_PHASE} as requested. Completed through phase ${TO_PHASE} as requested.
Remaining phases were not executed. Remaining phases were not executed.
Resume with: /gsd:autonomous --from ${next_incomplete_phase} Resume with: /gsd-autonomous --from ${next_incomplete_phase}
``` ```
Proceed directly to lifecycle step (which handles partial completion — skips audit/complete/cleanup since not all phases are done). Exit cleanly. Proceed directly to lifecycle step (which handles partial completion — skips audit/complete/cleanup since not all phases are done). Exit cleanly.
@@ -589,7 +589,7 @@ If all phases complete, proceed to lifecycle step.
Phase ${ONLY_PHASE}: ${PHASE_NAME} — Done Phase ${ONLY_PHASE}: ${PHASE_NAME} — Done
Mode: Single phase (--only) Mode: Single phase (--only)
Lifecycle skipped — run /gsd:autonomous without --only Lifecycle skipped — run /gsd-autonomous without --only
after all phases complete to trigger audit/complete/cleanup. after all phases complete to trigger audit/complete/cleanup.
``` ```
@@ -647,7 +647,7 @@ Ask user via AskUserQuestion:
On **"Continue anyway"**: Display `Audit ⏭ Gaps accepted — proceeding to complete milestone` and proceed to 5b. On **"Continue anyway"**: Display `Audit ⏭ Gaps accepted — proceeding to complete milestone` and proceed to 5b.
On **"Stop"**: Go to handle_blocker with "User stopped — audit gaps remain. Run /gsd:audit-milestone to review, then /gsd:complete-milestone when ready." On **"Stop"**: Go to handle_blocker with "User stopped — audit gaps remain. Run /gsd-audit-milestone to review, then /gsd-complete-milestone when ready."
**If `tech_debt`:** **If `tech_debt`:**
@@ -662,7 +662,7 @@ Show the summary, then ask user via AskUserQuestion:
On **"Continue with tech debt"**: Display `Audit ⏭ Tech debt acknowledged — proceeding to complete milestone` and proceed to 5b. On **"Continue with tech debt"**: Display `Audit ⏭ Tech debt acknowledged — proceeding to complete milestone` and proceed to 5b.
On **"Stop"**: Go to handle_blocker with "User stopped — tech debt to address. Run /gsd:audit-milestone to review details." On **"Stop"**: Go to handle_blocker with "User stopped — tech debt to address. Run /gsd-audit-milestone to review details."
**5b. Complete Milestone** **5b. Complete Milestone**
@@ -732,7 +732,7 @@ When any phase operation fails or a blocker is detected, present 3 options via A
Skipped: {list of skipped phases} Skipped: {list of skipped phases}
Remaining: {list of remaining phases} Remaining: {list of remaining phases}
Resume with: /gsd:autonomous ${ONLY_PHASE ? "--only " + ONLY_PHASE : "--from " + next_phase}${TO_PHASE ? " --to " + TO_PHASE : ""} Resume with: /gsd-autonomous ${ONLY_PHASE ? "--only " + ONLY_PHASE : "--from " + next_phase}${TO_PHASE ? " --to " + TO_PHASE : ""}
``` ```
</step> </step>

View File

@@ -22,14 +22,14 @@ If `todo_count` is 0:
``` ```
No pending todos. No pending todos.
Todos are captured during work sessions with /gsd:add-todo. Todos are captured during work sessions with /gsd-add-todo.
--- ---
Would you like to: Would you like to:
1. Continue with current phase (/gsd:progress) 1. Continue with current phase (/gsd-progress)
2. Add a todo now (/gsd:add-todo) 2. Add a todo now (/gsd-add-todo)
``` ```
Exit. Exit.
@@ -37,8 +37,8 @@ Exit.
<step name="parse_filter"> <step name="parse_filter">
Check for area filter in arguments: Check for area filter in arguments:
- `/gsd:check-todos` → show all - `/gsd-check-todos` → show all
- `/gsd:check-todos api` → filter to area:api only - `/gsd-check-todos api` → filter to area:api only
</step> </step>
<step name="list_todos"> <step name="list_todos">
@@ -56,7 +56,7 @@ Pending Todos:
--- ---
Reply with a number to view details, or: Reply with a number to view details, or:
- `/gsd:check-todos [area]` to filter by area - `/gsd-check-todos [area]` to filter by area
- `q` to exit - `q` to exit
``` ```
@@ -120,7 +120,7 @@ Use AskUserQuestion:
- question: "What would you like to do with this todo?" - question: "What would you like to do with this todo?"
- options: - options:
- "Work on it now" — move to done, start working - "Work on it now" — move to done, start working
- "Create a phase" — /gsd:add-phase with this scope - "Create a phase" — /gsd-add-phase with this scope
- "Brainstorm approach" — think through before deciding - "Brainstorm approach" — think through before deciding
- "Put it back" — return to list - "Put it back" — return to list
</step> </step>
@@ -136,7 +136,7 @@ Update STATE.md todo count. Present problem/solution context. Begin work or ask
Note todo reference in phase planning notes. Keep in pending. Return to list or exit. Note todo reference in phase planning notes. Keep in pending. Return to list or exit.
**Create a phase:** **Create a phase:**
Display: `/gsd:add-phase [description from todo]` Display: `/gsd-add-phase [description from todo]`
Keep in pending. User runs command in fresh context. Keep in pending. User runs command in fresh context.
**Brainstorm approach:** **Brainstorm approach:**

View File

@@ -93,7 +93,7 @@ Verify that REVIEW.md exists:
```bash ```bash
if [ ! -f "${REVIEW_PATH}" ]; then if [ ! -f "${REVIEW_PATH}" ]; then
echo "Error: No REVIEW.md found for Phase ${PHASE_ARG}. Run /gsd:code-review ${PHASE_ARG} first." echo "Error: No REVIEW.md found for Phase ${PHASE_ARG}. Run /gsd-code-review ${PHASE_ARG} first."
exit 1 exit 1
fi fi
``` ```
@@ -221,7 +221,7 @@ Check if FIX_REPORT_PATH exists:
Either way: Either way:
``` ```
Some fix commits may already exist in git history — check git log for fix(${PADDED_PHASE}) commits. Some fix commits may already exist in git history — check git log for fix(${PADDED_PHASE}) commits.
You can retry with /gsd:code-review-fix ${PHASE_ARG}. You can retry with /gsd-code-review-fix ${PHASE_ARG}.
``` ```
Exit workflow (skip auto loop). Exit workflow (skip auto loop).
@@ -394,7 +394,7 @@ if [ ! -f "${FIX_REPORT_PATH}" ]; then
echo "The fixer agent may have failed before completing." echo "The fixer agent may have failed before completing."
echo "Check git log for any fix(${PADDED_PHASE}) commits." echo "Check git log for any fix(${PADDED_PHASE}) commits."
echo "" echo ""
echo "Retry: /gsd:code-review-fix ${PHASE_ARG}" echo "Retry: /gsd-code-review-fix ${PHASE_ARG}"
echo "" echo ""
echo "═══════════════════════════════════════════════════════════════" echo "═══════════════════════════════════════════════════════════════"
exit 1 exit 1
@@ -451,7 +451,7 @@ if [ "$FIX_STATUS" = "all_fixed" ]; then
echo "Full report: ${FIX_REPORT_PATH}" echo "Full report: ${FIX_REPORT_PATH}"
echo "" echo ""
echo "Next step:" echo "Next step:"
echo " /gsd:verify-work — Verify phase completion" echo " /gsd-verify-work — Verify phase completion"
echo "" echo ""
fi fi
``` ```
@@ -465,8 +465,8 @@ if [ "$FIX_STATUS" = "partial" ] || [ "$FIX_STATUS" = "none_fixed" ]; then
echo "" echo ""
echo "Next steps:" echo "Next steps:"
echo " cat ${FIX_REPORT_PATH} — View fix report" echo " cat ${FIX_REPORT_PATH} — View fix report"
echo " /gsd:code-review ${PHASE_NUMBER} — Re-review code" echo " /gsd-code-review ${PHASE_NUMBER} — Re-review code"
echo " /gsd:verify-work — Verify phase completion" echo " /gsd-verify-work — Verify phase completion"
echo "" echo ""
fi fi
``` ```

View File

@@ -227,7 +227,7 @@ if [ ${#REVIEW_FILES[@]} -eq 0 ]; then
else else
# Fail closed — no reliable diff base found. Do not use arbitrary HEAD~N. # Fail closed — no reliable diff base found. Do not use arbitrary HEAD~N.
echo "Warning: No phase commits found for '${PADDED_PHASE}'. Cannot determine reliable diff scope." echo "Warning: No phase commits found for '${PADDED_PHASE}'. Cannot determine reliable diff scope."
echo "Use --files flag to specify files explicitly: /gsd:code-review ${PHASE_ARG} --files=file1,file2,..." echo "Use --files flag to specify files explicitly: /gsd-code-review ${PHASE_ARG} --files=file1,file2,..."
fi fi
fi fi
``` ```
@@ -372,7 +372,7 @@ If the Task() call fails (agent error, timeout, or exception):
``` ```
Error: Code review agent failed: ${error_message} Error: Code review agent failed: ${error_message}
No REVIEW.md created. You can retry with /gsd:code-review ${PHASE_ARG} or check agent logs. No REVIEW.md created. You can retry with /gsd-code-review ${PHASE_ARG} or check agent logs.
``` ```
Do NOT proceed to commit_review step. Do NOT create a partial or empty REVIEW.md. Exit workflow. Do NOT proceed to commit_review step. Do NOT create a partial or empty REVIEW.md. Exit workflow.
@@ -405,7 +405,7 @@ if [ -f "${REVIEW_PATH}" ]; then
fi fi
else else
echo "Warning: Agent completed but REVIEW.md not found at ${REVIEW_PATH}. This may indicate an agent issue." echo "Warning: Agent completed but REVIEW.md not found at ${REVIEW_PATH}. This may indicate an agent issue."
echo "No REVIEW.md to commit. Please retry with /gsd:code-review ${PHASE_ARG}" echo "No REVIEW.md to commit. Please retry with /gsd-code-review ${PHASE_ARG}"
fi fi
``` ```
</step> </step>
@@ -469,7 +469,7 @@ If total findings > 0:
Full report: ${REVIEW_PATH} Full report: ${REVIEW_PATH}
Next steps: Next steps:
/gsd:code-review-fix ${PHASE_NUMBER} — Auto-fix issues /gsd-code-review-fix ${PHASE_NUMBER} — Auto-fix issues
cat ${REVIEW_PATH} — View full report cat ${REVIEW_PATH} — View full report
``` ```

View File

@@ -41,7 +41,7 @@ When a milestone completes:
Before proceeding with milestone close, run the comprehensive open artifact audit. Before proceeding with milestone close, run the comprehensive open artifact audit.
```bash ```bash
gsd-sdk query audit-open 2>/dev/null gsd-sdk query audit-open
``` ```
If the output contains open items (any section with count > 0): If the output contains open items (any section with count > 0):
@@ -51,7 +51,7 @@ Display the full audit report to the user.
Then ask: Then ask:
``` ```
These items are open. Choose an action: These items are open. Choose an action:
[R] Resolve — stop and fix items, then re-run /gsd:complete-milestone [R] Resolve — stop and fix items, then re-run /gsd-complete-milestone
[A] Acknowledge all — document as deferred and proceed with close [A] Acknowledge all — document as deferred and proceed with close
[C] Cancel — exit without closing [C] Cancel — exit without closing
``` ```
@@ -124,7 +124,7 @@ Requirements: {N}/{M} v1 requirements checked off
MUST present 3 options: MUST present 3 options:
1. **Proceed anyway** — mark milestone complete with known gaps 1. **Proceed anyway** — mark milestone complete with known gaps
2. **Run audit first** — `/gsd:audit-milestone` to assess gap severity 2. **Run audit first** — `/gsd-audit-milestone` to assess gap severity
3. **Abort** — return to development 3. **Abort** — return to development
If user selects "Proceed anyway": note incomplete requirements in MILESTONES.md under `### Known Gaps` with REQ-IDs and descriptions. If user selects "Proceed anyway": note incomplete requirements in MILESTONES.md under `### Known Gaps` with REQ-IDs and descriptions.
@@ -441,7 +441,7 @@ mv .planning/phases/{phase-dir} .planning/milestones/v[X.Y]-phases/
``` ```
Verify: `✅ Phase directories archived to .planning/milestones/v[X.Y]-phases/` Verify: `✅ Phase directories archived to .planning/milestones/v[X.Y]-phases/`
If "Skip": Phase directories remain in `.planning/phases/` as raw execution history. Use `/gsd:cleanup` later to archive retroactively. If "Skip": Phase directories remain in `.planning/phases/` as raw execution history. Use `/gsd-cleanup` later to archive retroactively.
After archival, the AI still handles: After archival, the AI still handles:
- Reorganizing ROADMAP.md with milestone grouping (requires judgment) — overwrite in place after extracting Backlog section - Reorganizing ROADMAP.md with milestone grouping (requires judgment) — overwrite in place after extracting Backlog section
@@ -786,7 +786,7 @@ Tag: v[X.Y]
`/clear` then: `/clear` then:
`/gsd:new-milestone` `/gsd-new-milestone`
--- ---
``` ```
@@ -842,6 +842,6 @@ Milestone completion is successful when:
- [ ] Known gaps recorded in MILESTONES.md if user proceeded with incomplete requirements - [ ] Known gaps recorded in MILESTONES.md if user proceeded with incomplete requirements
- [ ] RETROSPECTIVE.md updated with milestone section - [ ] RETROSPECTIVE.md updated with milestone section
- [ ] Cross-milestone trends updated - [ ] Cross-milestone trends updated
- [ ] User knows next step (/gsd:new-milestone) - [ ] User knows next step (/gsd-new-milestone)
</success_criteria> </success_criteria>

View File

@@ -87,7 +87,7 @@ This runs in parallel - all gaps investigated simultaneously.
**Load agent skills:** **Load agent skills:**
```bash ```bash
AGENT_SKILLS_DEBUGGER=$(gsd-sdk query agent-skills gsd-debugger 2>/dev/null) AGENT_SKILLS_DEBUGGER=$(gsd-sdk query agent-skills gsd-debugger)
EXPECTED_BASE=$(git rev-parse HEAD) EXPECTED_BASE=$(git rev-parse HEAD)
``` ```
@@ -220,7 +220,7 @@ Agents only diagnose—plan-phase --gaps handles fixes (no fix application).
**Agent times out:** **Agent times out:**
- Check DEBUG-{slug}.md for partial progress - Check DEBUG-{slug}.md for partial progress
- Can resume with /gsd:debug - Can resume with /gsd-debug
**All agents fail:** **All agents fail:**
- Something systemic (permissions, git, etc.) - Something systemic (permissions, git, etc.)

View File

@@ -4,7 +4,7 @@ Produces DISCOVERY.md (for Level 2-3) that informs PLAN.md creation.
Called from plan-phase.md's mandatory_discovery step with a depth parameter. Called from plan-phase.md's mandatory_discovery step with a depth parameter.
NOTE: For comprehensive ecosystem research ("how do experts build this"), use /gsd:research-phase instead, which produces RESEARCH.md. NOTE: For comprehensive ecosystem research ("how do experts build this"), use /gsd-research-phase instead, which produces RESEARCH.md.
</purpose> </purpose>
<depth_levels> <depth_levels>
@@ -254,8 +254,8 @@ Confidence: [level]
What's next? What's next?
1. Discuss phase context (/gsd:discuss-phase [current-phase]) 1. Discuss phase context (/gsd-discuss-phase [current-phase])
2. Create phase plan (/gsd:plan-phase [current-phase]) 2. Create phase plan (/gsd-plan-phase [current-phase])
3. Refine discovery (dig deeper) 3. Refine discovery (dig deeper)
4. Review discovery 4. Review discovery

View File

@@ -66,7 +66,7 @@ Phase number from argument (required).
```bash ```bash
INIT=$(gsd-sdk query init.phase-op "${PHASE}") INIT=$(gsd-sdk query init.phase-op "${PHASE}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_ANALYZER=$(gsd-sdk query agent-skills gsd-assumptions-analyzer 2>/dev/null) AGENT_SKILLS_ANALYZER=$(gsd-sdk query agent-skills gsd-assumptions-analyzer)
``` ```
Parse JSON for: `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, Parse JSON for: `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`,
@@ -77,7 +77,7 @@ Parse JSON for: `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phas
``` ```
Phase [X] not found in roadmap. Phase [X] not found in roadmap.
Use /gsd:progress to see available phases. Use /gsd-progress to see available phases.
``` ```
Exit workflow. Exit workflow.
@@ -599,13 +599,13 @@ Created: .planning/phases/${PADDED_PHASE}-${SLUG}/${PADDED_PHASE}-CONTEXT.md
`/clear` then: `/clear` then:
`/gsd:plan-phase ${PHASE}` `/gsd-plan-phase ${PHASE}`
--- ---
**Also available:** **Also available:**
- `/gsd:plan-phase ${PHASE} --skip-research` — plan without research - `/gsd-plan-phase ${PHASE} --skip-research` — plan without research
- `/gsd:ui-phase ${PHASE}` — generate UI design contract (if frontend work) - `/gsd-ui-phase ${PHASE}` — generate UI design contract (if frontend work)
- Review/edit CONTEXT.md before continuing - Review/edit CONTEXT.md before continuing
--- ---
@@ -619,7 +619,7 @@ Check for auto-advance trigger:
2. Sync chain flag: 2. Sync chain flag:
```bash ```bash
if [[ ! "$ARGUMENTS" =~ --auto ]]; then if [[ ! "$ARGUMENTS" =~ --auto ]]; then
gsd-sdk query config-set workflow._auto_chain_active false 2>/dev/null gsd-sdk query config-set workflow._auto_chain_active false || true
fi fi
``` ```
3. Read consolidated auto-mode (`active` = chain flag OR user preference): 3. Read consolidated auto-mode (`active` = chain flag OR user preference):

View File

@@ -5,7 +5,7 @@ Power user mode for discuss-phase. Generates ALL questions upfront into a JSON s
</purpose> </purpose>
<trigger> <trigger>
This workflow executes when `--power` flag is present in ARGUMENTS to `/gsd:discuss-phase`. This workflow executes when `--power` flag is present in ARGUMENTS to `/gsd-discuss-phase`.
The caller (discuss-phase.md) has already: The caller (discuss-phase.md) has already:
- Validated the phase exists - Validated the phase exists
@@ -265,7 +265,7 @@ Process all answered questions from the JSON file and generate CONTEXT.md.
``` ```
Warning: Only {answered}/{total} questions answered ({pct}%). Warning: Only {answered}/{total} questions answered ({pct}%).
CONTEXT.md generated with available decisions. Unanswered questions listed as deferred. CONTEXT.md generated with available decisions. Unanswered questions listed as deferred.
Consider running /gsd:discuss-phase {N} again to refine before planning. Consider running /gsd-discuss-phase {N} again to refine before planning.
``` ```
7. Print completion message: 7. Print completion message:
@@ -275,7 +275,7 @@ CONTEXT.md written: {phase_dir}/{padded_phase}-CONTEXT.md
Decisions captured: {answered} Decisions captured: {answered}
Deferred: {remaining} Deferred: {remaining}
Next step: /gsd:plan-phase {N} Next step: /gsd-plan-phase {N}
``` ```
</step> </step>

View File

@@ -103,7 +103,7 @@ Phase: "API documentation" → Structure/navigation, Code examples depth,
<process> <process>
**Express path available:** If you already have a PRD or acceptance criteria document, use `/gsd:plan-phase {phase} --prd path/to/prd.md` to skip this discussion and go straight to planning. **Express path available:** If you already have a PRD or acceptance criteria document, use `/gsd-plan-phase {phase} --prd path/to/prd.md` to skip this discussion and go straight to planning.
<step name="initialize" priority="first"> <step name="initialize" priority="first">
Phase number from argument (required). Phase number from argument (required).
@@ -111,7 +111,7 @@ Phase number from argument (required).
```bash ```bash
INIT=$(gsd-sdk query init.phase-op "${PHASE}") INIT=$(gsd-sdk query init.phase-op "${PHASE}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_ADVISOR=$(gsd-sdk query agent-skills gsd-advisor-researcher 2>/dev/null) AGENT_SKILLS_ADVISOR=$(gsd-sdk query agent-skills gsd-advisor-researcher)
``` ```
Parse JSON for: `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_research`, `has_context`, `has_plans`, `has_verification`, `plan_count`, `roadmap_exists`, `planning_exists`, `response_language`. Parse JSON for: `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_research`, `has_context`, `has_plans`, `has_verification`, `plan_count`, `roadmap_exists`, `planning_exists`, `response_language`.
@@ -121,7 +121,7 @@ Parse JSON for: `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phas
**If `phase_found` is false:** **If `phase_found` is false:**
``` ```
Phase [X] not found in roadmap. Phase [X] not found in roadmap.
Use /gsd:progress ${GSD_WS} to see available phases. Use /gsd-progress ${GSD_WS} to see available phases.
``` ```
Exit workflow. Exit workflow.
@@ -172,7 +172,7 @@ Write these answers inline before continuing. If a blocking anti-pattern cannot
</step> </step>
<step name="check_spec"> <step name="check_spec">
Check if a SPEC.md (from `/gsd:spec-phase`) exists for this phase. SPEC.md locks requirements before implementation decisions. Check if a SPEC.md (from `/gsd-spec-phase`) exists for this phase. SPEC.md locks requirements before implementation decisions.
```bash ```bash
ls ${phase_dir}/*-SPEC.md 2>/dev/null | grep -v AI-SPEC | head -1 || true ls ${phase_dir}/*-SPEC.md 2>/dev/null | grep -v AI-SPEC | head -1 || true
@@ -187,7 +187,7 @@ ls ${phase_dir}/*-SPEC.md 2>/dev/null | grep -v AI-SPEC | head -1 || true
**If no SPEC.md is found:** Continue with `spec_loaded = false`. **If no SPEC.md is found:** Continue with `spec_loaded = false`.
**Note:** SPEC.md files named `AI-SPEC.md` (from `/gsd:ai-integration-phase`) are excluded — different purpose. **Note:** SPEC.md files named `AI-SPEC.md` (from `/gsd-ai-integration-phase`) are excluded — different purpose.
</step> </step>
<step name="check_existing"> <step name="check_existing">
@@ -252,7 +252,7 @@ RAW_SKETCHES=$(ls .planning/sketches/MANIFEST.md 2>/dev/null)
If findings skills exist, read SKILL.md and reference files; extract validated patterns, landmines, constraints, design decisions. Add them to `<prior_decisions>`. If findings skills exist, read SKILL.md and reference files; extract validated patterns, landmines, constraints, design decisions. Add them to `<prior_decisions>`.
If raw spikes/sketches exist but no findings skill, note: `⚠ Unpackaged spikes/sketches detected — run /gsd:spike-wrap-up or /gsd:sketch-wrap-up to make findings available.` If raw spikes/sketches exist but no findings skill, note: `⚠ Unpackaged spikes/sketches detected — run /gsd-spike-wrap-up or /gsd-sketch-wrap-up to make findings available.`
Build internal `<prior_decisions>` with sections for Project-Level (from PROJECT.md / REQUIREMENTS.md), From Prior Phases (per-phase decisions), and From Spike/Sketch Findings (validated patterns, landmines, design decisions). Build internal `<prior_decisions>` with sections for Project-Level (from PROJECT.md / REQUIREMENTS.md), From Prior Phases (per-phase decisions), and From Spike/Sketch Findings (validated patterns, landmines, design decisions).
@@ -418,11 +418,11 @@ Created: .planning/phases/${PADDED_PHASE}-${SLUG}/${PADDED_PHASE}-CONTEXT.md
`/clear` then: `/clear` then:
`/gsd:plan-phase ${PHASE} ${GSD_WS}` `/gsd-plan-phase ${PHASE} ${GSD_WS}`
--- ---
**Also available:** `--chain` for auto plan+execute after; `/gsd:plan-phase ${PHASE} --skip-research ${GSD_WS}` to plan without research; `/gsd:ui-phase ${PHASE} ${GSD_WS}` for UI design contracts; review/edit CONTEXT.md before continuing. **Also available:** `--chain` for auto plan+execute after; `/gsd-plan-phase ${PHASE} --skip-research ${GSD_WS}` to plan without research; `/gsd-ui-phase ${PHASE} ${GSD_WS}` for UI design contracts; review/edit CONTEXT.md before continuing.
``` ```
</step> </step>

View File

@@ -26,7 +26,7 @@
(the user's persistent settings preference): (the user's persistent settings preference):
```bash ```bash
if [[ ! "$ARGUMENTS" =~ --auto ]] && [[ ! "$ARGUMENTS" =~ --chain ]]; then if [[ ! "$ARGUMENTS" =~ --auto ]] && [[ ! "$ARGUMENTS" =~ --chain ]]; then
gsd-sdk query config-set workflow._auto_chain_active false 2>/dev/null gsd-sdk query config-set workflow._auto_chain_active false || true
fi fi
``` ```
@@ -75,22 +75,22 @@
/clear then: /clear then:
Next: /gsd:discuss-phase ${NEXT_PHASE} ${WAS_CHAIN ? "--chain" : "--auto"} ${GSD_WS} Next: /gsd-discuss-phase ${NEXT_PHASE} ${WAS_CHAIN ? "--chain" : "--auto"} ${GSD_WS}
``` ```
- **PLANNING COMPLETE** → Planning done, execution didn't complete: - **PLANNING COMPLETE** → Planning done, execution didn't complete:
``` ```
Auto-advance partial: Planning complete, execution did not finish. Auto-advance partial: Planning complete, execution did not finish.
Continue: /gsd:execute-phase ${PHASE} ${GSD_WS} Continue: /gsd-execute-phase ${PHASE} ${GSD_WS}
``` ```
- **PLANNING INCONCLUSIVE / CHECKPOINT** → Stop chain: - **PLANNING INCONCLUSIVE / CHECKPOINT** → Stop chain:
``` ```
Auto-advance stopped: Planning needs input. Auto-advance stopped: Planning needs input.
Continue: /gsd:plan-phase ${PHASE} ${GSD_WS} Continue: /gsd-plan-phase ${PHASE} ${GSD_WS}
``` ```
- **GAPS FOUND** → Stop chain: - **GAPS FOUND** → Stop chain:
``` ```
Auto-advance stopped: Gaps found during execution. Auto-advance stopped: Gaps found during execution.
Continue: /gsd:plan-phase ${PHASE} --gaps ${GSD_WS} Continue: /gsd-plan-phase ${PHASE} --gaps ${GSD_WS}
``` ```
7. **If none of `--auto`, `--chain`, nor config enabled:** route to 7. **If none of `--auto`, `--chain`, nor config enabled:** route to

View File

@@ -17,7 +17,7 @@ Claude App cannot forward TUI menu selections back to the host.
## Activation ## Activation
- Per-session: pass `--text` flag to any command (e.g., - Per-session: pass `--text` flag to any command (e.g.,
`/gsd:discuss-phase --text`) `/gsd-discuss-phase --text`)
- Per-project: `gsd-sdk query config-set workflow.text_mode true` - Per-project: `gsd-sdk query config-set workflow.text_mode true`
Text mode applies to ALL workflows in the session, not just discuss-phase. Text mode applies to ALL workflows in the session, not just discuss-phase.

View File

@@ -39,35 +39,35 @@ Evaluate `$ARGUMENTS` against these routing rules. Apply the **first matching**
| If the text describes... | Route to | Why | | If the text describes... | Route to | Why |
|--------------------------|----------|-----| |--------------------------|----------|-----|
| Starting a new project, "set up", "initialize" | `/gsd:new-project` | Needs full project initialization | | Starting a new project, "set up", "initialize" | `/gsd-new-project` | Needs full project initialization |
| Mapping or analyzing an existing codebase | `/gsd:map-codebase` | Codebase discovery | | Mapping or analyzing an existing codebase | `/gsd-map-codebase` | Codebase discovery |
| A bug, error, crash, failure, or something broken | `/gsd:debug` | Needs systematic investigation | | A bug, error, crash, failure, or something broken | `/gsd-debug` | Needs systematic investigation |
| Spiking, "test if", "will this work", "experiment", "prove this out", validate feasibility | `/gsd:spike` | Throwaway experiment to validate feasibility | | Spiking, "test if", "will this work", "experiment", "prove this out", validate feasibility | `/gsd-spike` | Throwaway experiment to validate feasibility |
| Sketching, "mockup", "what would this look like", "prototype the UI", "design this", explore visual direction | `/gsd:sketch` | Throwaway HTML mockups to explore design | | 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 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 | | 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-research-phase` | Domain research before planning |
| Discussing vision, "how should X look", brainstorming | `/gsd:discuss-phase` | Needs context gathering | | Discussing vision, "how should X look", brainstorming | `/gsd-discuss-phase` | Needs context gathering |
| A complex task: refactoring, migration, multi-file architecture, system redesign | `/gsd:add-phase` | Needs a full phase with plan/build cycle | | A complex task: refactoring, migration, multi-file architecture, system redesign | `/gsd-add-phase` | Needs a full phase with plan/build cycle |
| Planning a specific phase or "plan phase N" | `/gsd:plan-phase` | Direct planning request | | Planning a specific phase or "plan phase N" | `/gsd-plan-phase` | Direct planning request |
| Executing a phase or "build phase N", "run phase N" | `/gsd:execute-phase` | Direct execution request | | Executing a phase or "build phase N", "run phase N" | `/gsd-execute-phase` | Direct execution request |
| Running all remaining phases automatically | `/gsd:autonomous` | Full autonomous execution | | Running all remaining phases automatically | `/gsd-autonomous` | Full autonomous execution |
| A review or quality concern about existing work | `/gsd:verify-work` | Needs verification | | A review or quality concern about existing work | `/gsd-verify-work` | Needs verification |
| Checking progress, status, "where am I" | `/gsd:progress` | Status check | | Checking progress, status, "where am I" | `/gsd-progress` | Status check |
| Resuming work, "pick up where I left off" | `/gsd:resume-work` | Session restoration | | Resuming work, "pick up where I left off" | `/gsd-resume-work` | Session restoration |
| A note, idea, or "remember to..." | `/gsd:add-todo` | Capture for later | | A note, idea, or "remember to..." | `/gsd-add-todo` | Capture for later |
| Adding tests, "write tests", "test coverage" | `/gsd:add-tests` | Test generation | | Adding tests, "write tests", "test coverage" | `/gsd-add-tests` | Test generation |
| Completing a milestone, shipping, releasing | `/gsd:complete-milestone` | Milestone lifecycle | | 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 | | 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`, `/gsd-help`, and `/gsd-join-discord`. 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: **Ambiguity handling:** If the text could reasonably match multiple routes, ask the user via AskUserQuestion with the top 2-3 options. For example:
``` ```
"Refactor the authentication system" could be: "Refactor the authentication system" could be:
1. /gsd:add-phase — Full planning cycle (recommended for multi-file refactors) 1. /gsd-add-phase — Full planning cycle (recommended for multi-file refactors)
2. /gsd:quick — Quick execution (if scope is small and clear) 2. /gsd-quick — Quick execution (if scope is small and clear)
Which approach fits better? Which approach fits better?
``` ```

View File

@@ -16,7 +16,7 @@ Load docs-update context:
```bash ```bash
INIT=$(gsd-sdk query docs-init) INIT=$(gsd-sdk query docs-init)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS=$(gsd-sdk query agent-skills gsd-doc-writer 2>/dev/null) AGENT_SKILLS=$(gsd-sdk query agent-skills gsd-doc-writer)
``` ```
Extract from init JSON: Extract from init JSON:
@@ -988,8 +988,8 @@ Failed claims:
Display note: Display note:
``` ```
To fix failures automatically: /gsd:docs-update (runs generation + fix loop) To fix failures automatically: /gsd-docs-update (runs generation + fix loop)
To regenerate all docs from scratch: /gsd:docs-update --force To regenerate all docs from scratch: /gsd-docs-update --force
``` ```
Clean up temp files: remove `.planning/tmp/verify-*.json` files. Clean up temp files: remove `.planning/tmp/verify-*.json` files.
@@ -1024,7 +1024,7 @@ This would expose credentials if committed.
Action required: Action required:
1. Review the flagged lines above 1. Review the flagged lines above
2. Remove any real secrets from the doc files 2. Remove any real secrets from the doc files
3. Re-run /gsd:docs-update to regenerate clean docs 3. Re-run /gsd-docs-update to regenerate clean docs
``` ```
Then confirm with AskUserQuestion: Then confirm with AskUserQuestion:
@@ -1126,7 +1126,7 @@ All generated files committed.
Remind the user they can fact-check generated docs: Remind the user they can fact-check generated docs:
``` ```
Run `/gsd:docs-update --verify-only` to fact-check generated docs against the codebase. Run `/gsd-docs-update --verify-only` to fact-check generated docs against the codebase.
``` ```
End workflow. End workflow.

View File

@@ -1,7 +1,7 @@
<purpose> <purpose>
Retroactive audit of an implemented AI phase's evaluation coverage. Standalone command that works on any GSD-managed AI phase. Produces a scored EVAL-REVIEW.md with gap analysis and remediation plan. Retroactive audit of an implemented AI phase's evaluation coverage. Standalone command that works on any GSD-managed AI phase. Produces a scored EVAL-REVIEW.md with gap analysis and remediation plan.
Use after /gsd:execute-phase to verify that the evaluation strategy from AI-SPEC.md was actually implemented. Mirrors the pattern of /gsd:ui-review and /gsd:validate-phase. Use after /gsd-execute-phase to verify that the evaluation strategy from AI-SPEC.md was actually implemented. Mirrors the pattern of /gsd-ui-review and /gsd-validate-phase.
</purpose> </purpose>
<required_reading> <required_reading>
@@ -40,7 +40,7 @@ EVAL_REVIEW_FILE=$(ls "${PHASE_DIR}"/*-EVAL-REVIEW.md 2>/dev/null | head -1)
**State A** — AI-SPEC.md + SUMMARY.md exist: Full audit against spec **State A** — AI-SPEC.md + SUMMARY.md exist: Full audit against spec
**State B** — SUMMARY.md exists, no AI-SPEC.md: Audit against general best practices **State B** — SUMMARY.md exists, no AI-SPEC.md: Audit against general best practices
**State C** — No SUMMARY.md: Exit — "Phase {N} not executed. Run /gsd:execute-phase {N} first." **State C** — No SUMMARY.md: Exit — "Phase {N} not executed. Run /gsd-execute-phase {N} first."
**Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. **Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
@@ -58,7 +58,7 @@ If "Re-audit": continue.
``` ```
No AI-SPEC.md found for Phase {N}. No AI-SPEC.md found for Phase {N}.
Audit will evaluate against general AI eval best practices rather than a phase-specific plan. Audit will evaluate against general AI eval best practices rather than a phase-specific plan.
Consider running /gsd:ai-integration-phase {N} before implementation next time. Consider running /gsd-ai-integration-phase {N} before implementation next time.
``` ```
Continue (non-blocking). Continue (non-blocking).
@@ -124,10 +124,10 @@ Read the written EVAL-REVIEW.md. Extract:
◆ Output: {eval_review_path} ◆ Output: {eval_review_path}
{If PRODUCTION READY:} {If PRODUCTION READY:}
Next step: /gsd:plan-phase (next phase) or deploy Next step: /gsd-plan-phase (next phase) or deploy
{If NEEDS WORK:} {If NEEDS WORK:}
Address critical gaps in EVAL-REVIEW.md, then re-run /gsd:eval-review {N} Address critical gaps in EVAL-REVIEW.md, then re-run /gsd-eval-review {N}
{If SIGNIFICANT GAPS or NOT IMPLEMENTED:} {If SIGNIFICANT GAPS or NOT IMPLEMENTED:}
Review AI-SPEC.md evaluation plan. Critical eval dimensions are not implemented. Review AI-SPEC.md evaluation plan. Critical eval dimensions are not implemented.

View File

@@ -69,7 +69,7 @@ Load all context in one call:
```bash ```bash
INIT=$(gsd-sdk query init.execute-phase "${PHASE_ARG}") INIT=$(gsd-sdk query init.execute-phase "${PHASE_ARG}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS=$(gsd-sdk query agent-skills gsd-executor 2>/dev/null) AGENT_SKILLS=$(gsd-sdk query agent-skills gsd-executor)
``` ```
Parse JSON for: `executor_model`, `verifier_model`, `commit_docs`, `parallelization`, `branching_strategy`, `branch_name`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `plans`, `incomplete_plans`, `plan_count`, `incomplete_count`, `state_exists`, `roadmap_exists`, `phase_req_ids`, `response_language`. Parse JSON for: `executor_model`, `verifier_model`, `commit_docs`, `parallelization`, `branching_strategy`, `branch_name`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `plans`, `incomplete_plans`, `plan_count`, `incomplete_count`, `state_exists`, `roadmap_exists`, `phase_req_ids`, `response_language`.
@@ -130,7 +130,7 @@ inline path for each plan.
```bash ```bash
# REQUIRED: prevents stale auto-chain from previous --auto runs # REQUIRED: prevents stale auto-chain from previous --auto runs
if [[ ! "$ARGUMENTS" =~ --auto ]]; then if [[ ! "$ARGUMENTS" =~ --auto ]]; then
gsd-sdk query config-set workflow._auto_chain_active false 2>/dev/null gsd-sdk query config-set workflow._auto_chain_active false || true
fi fi
``` ```
</step> </step>
@@ -344,7 +344,7 @@ between a large tool_result and the next assistant turn (seen on Claude Code
+ Opus 4.7 at ~200K+ cache_read). To keep the stream warm, emit short + Opus 4.7 at ~200K+ cache_read). To keep the stream warm, emit short
assistant-text heartbeats — **no tool call, just a literal line** — at every assistant-text heartbeats — **no tool call, just a literal line** — at every
wave and plan boundary. Each heartbeat MUST start with `[checkpoint]` so wave and plan boundary. Each heartbeat MUST start with `[checkpoint]` so
tooling and `/gsd:manager`'s background-completion handler can grep partial tooling and `/gsd-manager`'s background-completion handler can grep partial
transcripts. `{P}/{Q}` is the phase-wide completed/total plans counter and transcripts. `{P}/{Q}` is the phase-wide completed/total plans counter and
increases monotonically across waves. `{status}` is `complete` (success), increases monotonically across waves. `{status}` is `complete` (success),
`failed` (executor error), or `checkpoint` (human-gate returned). `failed` (executor error), or `checkpoint` (human-gate returned).
@@ -1017,13 +1017,13 @@ If `SECURITY_CFG` is `true` AND `SECURITY_FILE` is empty (no SECURITY.md yet):
Include in the next-steps routing output: Include in the next-steps routing output:
``` ```
⚠ Security enforcement enabled — run before advancing: ⚠ Security enforcement enabled — run before advancing:
/gsd:secure-phase {PHASE} ${GSD_WS} /gsd-secure-phase {PHASE} ${GSD_WS}
``` ```
If `SECURITY_CFG` is `true` AND SECURITY.md exists: check frontmatter `threats_open`. If > 0: If `SECURITY_CFG` is `true` AND SECURITY.md exists: check frontmatter `threats_open`. If > 0:
``` ```
⚠ Security gate: {threats_open} threats open ⚠ Security gate: {threats_open} threats open
/gsd:secure-phase {PHASE} — resolve before advancing /gsd-secure-phase {PHASE} — resolve before advancing
``` ```
</step> </step>
@@ -1093,8 +1093,8 @@ Apply the same "incomplete" filtering rules as earlier:
Selected wave finished successfully. This phase still has incomplete plans, so phase-level verification and completion were intentionally skipped. Selected wave finished successfully. This phase still has incomplete plans, so phase-level verification and completion were intentionally skipped.
/gsd:execute-phase {phase} ${GSD_WS} # Continue remaining waves /gsd-execute-phase {phase} ${GSD_WS} # Continue remaining waves
/gsd:execute-phase {phase} --wave {next} ${GSD_WS} # Run the next wave explicitly /gsd-execute-phase {phase} --wave {next} ${GSD_WS} # Run the next wave explicitly
``` ```
**If no incomplete plans remain after the selected wave finishes:** **If no incomplete plans remain after the selected wave finishes:**
@@ -1127,7 +1127,7 @@ REVIEW_STATUS=$(sed -n '/^---$/,/^---$/p' "$REVIEW_FILE" | grep "^status:" | hea
If REVIEW_STATUS is not "clean" and not "skipped" and not empty, display: If REVIEW_STATUS is not "clean" and not "skipped" and not empty, display:
``` ```
Code review found issues. Consider running: Code review found issues. Consider running:
/gsd:code-review-fix ${PHASE_NUMBER} /gsd-code-review-fix ${PHASE_NUMBER}
``` ```
**Error handling:** If the Skill invocation fails or throws, catch the error, display "Code review encountered an error (non-blocking): {error}" and proceed to next step. Review failures must never block execution. **Error handling:** If the Skill invocation fails or throws, catch the error, display "Code review encountered an error (non-blocking): {error}" and proceed to next step. Review failures must never block execution.
@@ -1339,7 +1339,7 @@ spawn template, and the two `workflow.drift_*` config keys.
Verify phase achieved its GOAL, not just completed tasks. Verify phase achieved its GOAL, not just completed tasks.
```bash ```bash
VERIFIER_SKILLS=$(gsd-sdk query agent-skills gsd-verifier 2>/dev/null) VERIFIER_SKILLS=$(gsd-sdk query agent-skills gsd-verifier)
``` ```
``` ```
@@ -1379,7 +1379,7 @@ grep "^status:" "$PHASE_DIR"/*-VERIFICATION.md | cut -d: -f2 | tr -d ' '
|--------|--------| |--------|--------|
| `passed` | → update_roadmap | | `passed` | → update_roadmap |
| `human_needed` | Present items for human testing, get approval or feedback | | `human_needed` | Present items for human testing, get approval or feedback |
| `gaps_found` | Present gap summary, offer `/gsd:plan-phase {phase} --gaps ${GSD_WS}` | | `gaps_found` | Present gap summary, offer `/gsd-plan-phase {phase} --gaps ${GSD_WS}` |
**If human_needed:** **If human_needed:**
@@ -1434,12 +1434,12 @@ All automated checks passed. {N} items need human testing:
{From VERIFICATION.md human_verification section} {From VERIFICATION.md human_verification section}
Items saved to `{phase_num}-HUMAN-UAT.md` — they will appear in `/gsd:progress` and `/gsd:audit-uat`. Items saved to `{phase_num}-HUMAN-UAT.md` — they will appear in `/gsd-progress` and `/gsd-audit-uat`.
"approved" → continue | Report issues → gap closure "approved" → continue | Report issues → gap closure
``` ```
**If user says "approved":** Proceed to `update_roadmap`. The HUMAN-UAT.md file persists with `status: partial` and will surface in future progress checks until the user runs `/gsd:verify-work` on it. **If user says "approved":** Proceed to `update_roadmap`. The HUMAN-UAT.md file persists with `status: partial` and will surface in future progress checks until the user runs `/gsd-verify-work` on it.
**If user reports issues:** Proceed to gap closure as currently implemented. **If user reports issues:** Proceed to gap closure as currently implemented.
@@ -1458,13 +1458,13 @@ Items saved to `{phase_num}-HUMAN-UAT.md` — they will appear in `/gsd:progress
`/clear` then: `/clear` then:
`/gsd:plan-phase {X} --gaps ${GSD_WS}` `/gsd-plan-phase {X} --gaps ${GSD_WS}`
Also: `cat {phase_dir}/{phase_num}-VERIFICATION.md` — full report Also: `cat {phase_dir}/{phase_num}-VERIFICATION.md` — full report
Also: `/gsd:verify-work {X} ${GSD_WS}` — manual testing first Also: `/gsd-verify-work {X} ${GSD_WS}` — manual testing first
``` ```
Gap closure cycle: `/gsd:plan-phase {X} --gaps ${GSD_WS}` reads VERIFICATION.md → creates gap plans with `gap_closure: true` → user runs `/gsd:execute-phase {X} --gaps-only ${GSD_WS}` → verifier re-runs. Gap closure cycle: `/gsd-plan-phase {X} --gaps ${GSD_WS}` reads VERIFICATION.md → creates gap plans with `gap_closure: true` → user runs `/gsd-execute-phase {X} --gaps-only ${GSD_WS}` → verifier re-runs.
</step> </step>
<step name="update_roadmap"> <step name="update_roadmap">
@@ -1490,7 +1490,7 @@ Extract from result: `next_phase`, `next_phase_name`, `is_last_phase`, `warnings
{list each warning} {list each warning}
These items are tracked and will appear in `/gsd:progress` and `/gsd:audit-uat`. These items are tracked and will appear in `/gsd-progress` and `/gsd-audit-uat`.
``` ```
```bash ```bash
@@ -1577,7 +1577,7 @@ gsd-sdk query commit "docs(phase-{X}): evolve PROJECT.md after phase completion"
<step name="offer_next"> <step name="offer_next">
**Exception:** If `gaps_found`, the `verify_phase_goal` step already presents the gap-closure path (`/gsd:plan-phase {X} --gaps`). No additional routing needed — skip auto-advance. **Exception:** If `gaps_found`, the `verify_phase_goal` step already presents the gap-closure path (`/gsd-plan-phase {X} --gaps`). No additional routing needed — skip auto-advance.
**No-transition check (spawned by auto-advance chain):** **No-transition check (spawned by auto-advance chain):**
@@ -1640,10 +1640,10 @@ If CONTEXT.md does **not** exist for the next phase, present:
``` ```
## ✓ Phase {X}: {Name} Complete ## ✓ Phase {X}: {Name} Complete
/gsd:progress ${GSD_WS} — see updated roadmap /gsd-progress ${GSD_WS} — see updated roadmap
/gsd:discuss-phase {next} ${GSD_WS} — start here: discuss next phase before planning ← recommended /gsd-discuss-phase {next} ${GSD_WS} — start here: discuss next phase before planning ← recommended
/gsd:plan-phase {next} ${GSD_WS} — plan next phase (skip discuss) /gsd-plan-phase {next} ${GSD_WS} — plan next phase (skip discuss)
/gsd:execute-phase {next} ${GSD_WS} — execute next phase (skip discuss and plan) /gsd-execute-phase {next} ${GSD_WS} — execute next phase (skip discuss and plan)
``` ```
If CONTEXT.md **exists** for the next phase, present: If CONTEXT.md **exists** for the next phase, present:
@@ -1651,10 +1651,10 @@ If CONTEXT.md **exists** for the next phase, present:
``` ```
## ✓ Phase {X}: {Name} Complete ## ✓ Phase {X}: {Name} Complete
/gsd:progress ${GSD_WS} — see updated roadmap /gsd-progress ${GSD_WS} — see updated roadmap
/gsd:plan-phase {next} ${GSD_WS} — start here: plan next phase (CONTEXT.md already present) ← recommended /gsd-plan-phase {next} ${GSD_WS} — start here: plan next phase (CONTEXT.md already present) ← recommended
/gsd:discuss-phase {next} ${GSD_WS} — re-discuss next phase /gsd-discuss-phase {next} ${GSD_WS} — re-discuss next phase
/gsd:execute-phase {next} ${GSD_WS} — execute next phase (skip planning) /gsd-execute-phase {next} ${GSD_WS} — execute next phase (skip planning)
``` ```
Only suggest the commands listed above. Do not invent or hallucinate command names. Only suggest the commands listed above. Do not invent or hallucinate command names.
@@ -1681,7 +1681,7 @@ For 1M+ context models, consider:
</failure_handling> </failure_handling>
<resumption> <resumption>
Re-run `/gsd:execute-phase {phase}` → discover_plans finds completed SUMMARYs → skips them → resumes from first incomplete plan → continues wave execution. Re-run `/gsd-execute-phase {phase}` → discover_plans finds completed SUMMARYs → skips them → resumes from first incomplete plan → continues wave execution.
STATE.md tracks: last completed plan, current wave, pending checkpoints. STATE.md tracks: last completed plan, current wave, pending checkpoints.
</resumption> </resumption>

Some files were not shown because too many files have changed in this diff Show More