Previously, calling `mark-complete` on already-completed requirements
reported them as `not_found`, since the regex only matched unchecked
`[ ]` checkboxes and `Pending` table cells.
Now detects `[x]` checkboxes and `Complete` table cells and returns
them in a new `already_complete` array instead of `not_found`.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Three bugs preventing /gsd:profile-user from generating complete profiles:
1. Template path resolves to bin/templates/ (doesn't exist) instead of
templates/ — __dirname is bin/lib/, needs two levels up not one
2. write-profile reads analysis.projects_list and analysis.message_count
but the profiler agent outputs projects_analyzed and messages_analyzed
3. Evidence block checks dim.evidence but profiler outputs evidence_quotes
Fixes all three with fallback patterns (accepts both old and new field
names) so existing and future analysis formats both work.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two fixes to verify.cjs:
1. Add CWD guard to cmdValidateHealth — detects when CWD is the home
directory (likely accidental) and returns error E010 before running
checks that would read the wrong .planning/ directory.
2. Import and apply stripShippedMilestones to both cmdValidateConsistency
and cmdValidateHealth (Check 8) — prevents false warnings when
archived milestones reuse phase numbers.
This PR subsumes #1071 (strip archived milestones) to avoid merge
conflicts on the same import line.
Addresses patch 2 from #899, fixes#1060
resolveModelInternal() was converting 'opus' to 'inherit', assuming
the parent process runs on Opus. When the orchestrator runs on Sonnet
(the default), 'inherit' resolves to Sonnet — silently downgrading
quality profile subagents.
Remove the opus→inherit conversion so the resolved model name is
passed through directly. Claude Code's Task tool now supports model
aliases like 'opus', 'sonnet', 'haiku'.
Fixes#695
* perf: make `/gsd:set-profile`'s implementation more programmatic
* perf: run the set-profile script without Claude needing to invoke it
- inject the script's output as dynamic context into the set-profile skill, so that Claude doesn't
need to invoke it and can simply read + print the output to the user
- reference: https://code.claude.com/docs/en/skills#inject-dynamic-context
* feat: improve output message for case where model profile wasn't changed
* feat: specify haiku model for set-profile command since it's so simple
* fix: remove ' (default)' from previousProfile to avoid false negative for didChange
* chore: add docstring to MODEL_PROFILES with note about the analogous markdown reference table
* chore: delete set-profile workflow file that's no longer needed
Phase numbers reset per milestone (v1.0 Phase 1-26, v1.1 Phase 1-6,
v1.2 Phase 1-6). Functions that searched ROADMAP.md for phase headings
or checkboxes would match archived milestone entries inside <details>
blocks before reaching the current milestone's entries.
This caused:
- roadmap_complete: true for phases that aren't complete (checkbox
regex matched the archived [x] Phase N from a previous milestone)
- phase-complete marking the wrong checkbox (archived instead of current)
- phase-add computing wrong maxPhase from archived headings
- progress not showing phases defined in ROADMAP but not yet on disk
Two bugs caused workflow.research to silently reset to false:
1. new-milestone.md unconditionally overwrote workflow.research via
config-set after asking about research — coupling a per-milestone
decision to a persistent user preference. Now the research question
is per-invocation only; persistent config changes via /gsd:settings.
2. verify.cjs health repair (createConfig/resetConfig) used flat keys
(research, plan_checker, verifier) instead of the canonical nested
workflow object from config.cjs, also missing branch templates,
nyquist_validation, and brave_search fields.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add a new `/gsd:stats` command that displays comprehensive project
statistics including phase progress, plan execution metrics, requirements
completion, git history, and timeline information.
- New command definition: commands/gsd/stats.md
- New workflow: get-shit-done/workflows/stats.md
- CLI handler: `gsd-tools stats [json|table]`
- Stats function in commands.cjs with JSON and table output formats
- 5 new tests covering empty project, phase/plan counting, requirements
counting, last activity, and table format rendering
Co-authored-by: ashanuoc <ashanuoc@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add pre-planning design contract generation and post-execution visual audit
for frontend phases. Closes the gap where execute-phase runs without a visual
contract, producing inconsistent spacing, color, and typography across components.
New agents: gsd-ui-researcher (UI-SPEC.md), gsd-ui-checker (6-dimension
validation), gsd-ui-auditor (6-pillar scored audit + registry re-vetting).
Third-party shadcn registry blocks are machine-vetted at contract time,
verification time, and audit time — three enforcement points, not a checkbox.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Scope phase section regex to prevent cross-phase boundary matching
- Handle 'In Progress' status in traceability table (not just 'Pending')
- Add requirements_updated field to phase complete result object
- Add 3 new tests: result field, In Progress status, cross-boundary safety
Co-authored-by: ashanuoc <ashanuoc@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
When ROADMAP.md marks a phase as [x] complete, trust that over the
disk file structure. Phases completed before GSD tracking started
(or via external tools) may lack PLAN/SUMMARY pairs but are still
done — the roadmap checkbox is the higher-authority signal.
Without this fix, completed phases show as "discussed" or "planned"
in /gsd:progress, causing incorrect routing and progress percentages.
Fixes#977
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: plan-phase Nyquist validation when research is disabled (#980)
plan-phase step 5.5 required Nyquist artifacts even when research was
disabled, creating an impossible state: no RESEARCH.md to extract
Validation Architecture from. Step 7.5 then told Claude to "disable
Nyquist in config" without specifying the exact key, causing Claude to
guess wrong keys that config-set silently accepted.
Three fixes:
- plan-phase step 5.5: skip when research_enabled is false
- plan-phase step 7.5: specify exact config-set command for disabling
- config-set: reject unknown keys with whitelist validation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: update config-set tests for key whitelist validation
Tests that used arbitrary keys (some_number, some_string, a.b.c) now
use valid config keys to test the same coercion and nesting behavior.
Adds new test asserting unknown keys are rejected with error.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Quick task IDs were sequential integers (001, 002...) computed by reading
the local .planning/quick/ directory max value. Two users running /gsd:quick
simultaneously would get the same number, causing directory collisions on git.
Replace with a collision-resistant format: YYMMDD-xxx where xxx is the
number of 2-second blocks elapsed since midnight, encoded as 3 lowercase
Base36 characters (000–xbz). Practical collision window is ~2 seconds per
user — effectively zero for any realistic team workflow.
- init.cjs: remove nextNum scan logic, generate quickId from wall clock
- quick.md: rename all next_num refs to quick_id, update directory patterns
- init.test.cjs: rewrite cmdInitQuick tests for new ID format
Co-authored-by: yanbing <yanbing@corp.netease.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: change nyquist_validation default to true and harden absent-key skip conditions
new-project.md never wrote the key, so agents reading config directly
treated absent as falsy. Changed all agent skip conditions from "is false"
to "explicitly set to false; absent = enabled". Default changed from false
to true in core.cjs, config.cjs, and templates/config.json.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: enforce VALIDATION.md creation with verification gate and Check 8e
Step 5.5 was narrative markdown that Claude skipped under context pressure.
Now MANDATORY with Write tool requirement and file-existence verification.
Step 7.5 gates planner spawn on VALIDATION.md presence. Check 8e blocks
Dimension 8 if file missing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add W008/W009 health checks and addNyquistKey repair for Nyquist drift detection
W008 warns when workflow.nyquist_validation key is absent from config.json
(agents may skip validation). W009 warns when RESEARCH.md has Validation
Architecture section but no VALIDATION.md file exists. addNyquistKey repair
adds the missing key with default true value.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add /gsd:validate-phase command and gsd-nyquist-auditor agent
Retroactively applies Nyquist validation to already-executed phases.
Works mid-milestone and post-milestone. Detects existing test coverage,
maps gaps to phase requirements, writes missing tests, debugs failing
ones, and produces {phase}-VALIDATION.md from existing artifacts.
Handles three states: VALIDATION.md exists (audit + update), no
VALIDATION.md (reconstruct from PLAN.md + SUMMARY.md), phase not yet
executed (exit cleanly with guidance).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: audit-milestone reports Nyquist compliance gaps across phases
Adds Nyquist coverage table to audit-milestone output when
workflow.nyquist_validation is true. Identifies phases missing
VALIDATION.md or with nyquist_compliant: false/partial.
Routes to /gsd:validate-phase for resolution. Updates USER-GUIDE
with retroactive validation documentation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: compress Nyquist prompts to match GSD meta-prompt density conventions
Auditor agent: deleted philosophy section (35 lines), compressed execution
flow 60%, removed redundant constraints. Workflow: cut purpose bloat,
collapsed state narrative, compressed auditor spawn template. Command:
removed redundant process section. Plan-phase Steps 5.5/7.5: replaced
hedging language with directives. Audit-milestone Step 5.5: collapsed
sub-steps into inline instructions. Net: -376 lines.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The "depth" setting with values quick/standard/comprehensive implied
investigation thoroughness, but it only controls phase count. Renamed to
"granularity" with values coarse/standard/fine to accurately reflect what
it controls: how finely scope is sliced into phases.
Includes backward-compatible migration in loadConfig and config-ensure
that auto-renames depth→granularity with value mapping in both
.planning/config.json and ~/.gsd/defaults.json.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two defensive hardening changes:
1. state.cjs: Replace two identical inline extractField closures
(cmdStateSnapshot and buildStateFrontmatter) with a shared
stateExtractField(content, fieldName) helper at module scope.
The helper uses escapeRegex on fieldName before interpolation
into RegExp constructors, preventing breakage if a field name
ever contains regex metacharacters. Net removal of duplicated
logic.
2. gsd-plan-checker.md: Bound the "relevant" definition in the
exhaustive cross-check instruction. A requirement is "relevant"
only if ROADMAP.md explicitly maps it to this phase or the phase
goal directly implies it — prevents false blocker flags for
requirements belonging to other phases.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two hardening changes to cmdMilestoneComplete:
1. Replace 27 lines of inline isDirInMilestone logic (roadmap parsing,
normalization, and matching) with a single call to the shared
getMilestonePhaseFilter(cwd) from core.cjs. The inline copy was
identical to the core version — deduplicating prevents future drift.
2. Handle empty MILESTONES.md files. Previously, an existing but empty
file would fall into the headerMatch branch and produce malformed
output. Now an empty file is treated the same as a missing one,
writing the standard "# Milestones" header before the entry.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three files emit path.join or path.relative results directly into
JSON output without normalizing backslashes on Windows. Wrap these
with toPosixPath (already used in core.cjs and init.cjs) so agents
receive consistent forward-slash paths on all platforms.
Files changed:
- phase.cjs: wrap directory path in cmdPhasesFind
- commands.cjs: wrap todo path in cmdListTodos, relPath in cmdScaffold
- template.cjs: wrap relPath in cmdTemplateFill (both exists and
created branches)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cmdRequirementsMarkComplete interpolates user-supplied reqId strings
directly into RegExp constructors. If a reqId contains regex
metacharacters (e.g. parentheses, brackets, dots), the patterns break
or match unintended content.
Import escapeRegex from core.cjs (already used in state.cjs and
phase.cjs) and apply it to reqId before interpolation into all four
regex patterns in the function.
Same class of fix as gsd-build/get-shit-done#741 (state.cjs).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phases listed in ROADMAP.md but not yet planned (no directory on disk)
were excluded from total_phases, causing premature milestone completion.
Now uses Math.max(diskDirs, roadmapCount) via filter.phaseCount.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract getMilestonePhaseFilter() from milestone.cjs closure into core.cjs
as a shared helper. Apply it in buildStateFrontmatter and cmdPhaseComplete
so multi-milestone projects count only current milestone's phases instead
of all directories on disk.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Expand dual-format parsing to cover all 8 field extraction and
replacement locations in state.cjs. The previous fix only covered
cmdStateSnapshot and buildStateFrontmatter (read path), leaving 6
write-path functions with bold-only regex that silently failed on
plain-format STATE.md files.
Functions fixed:
- stateExtractField: shared read helper (cascades to cmdStateAdvancePlan,
cmdStateRecordSession)
- stateReplaceField: shared write helper (cascades to all state mutations)
- cmdStateGet: individual field lookup
- cmdStatePatch: batch field updates
- cmdStateUpdate: single field updates
- cmdStateUpdateProgress: progress bar writes
- cmdStateSnapshot session section: Last Date, Stopped At, Resume File
Each function now tries **Field:** bold format first (preserving
existing behavior), then falls back to plain Field: format. This
eliminates the read/write asymmetry where state-snapshot could read
plain-format fields but state-update/state-patch could not modify them.
Closes#730
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The commit case in the CLI router reads only args[1] for the message,
which captures just the first word when the shell strips quotes before
passing arguments to Node.js. This silently truncates every multi-word
commit message (e.g. "docs(40): create phase plan" becomes "docs(40):").
Collect all positional args between the command name and the first
flag (--files, --amend), then join them. Works correctly whether the
shell preserves quotes (single arg) or strips them (multiple args).
Closes#733
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
extractField in state-snapshot and buildStateFrontmatter only matches
the **Field:** bold markdown format, but STATE.md may use plain
Field: format depending on how it was generated. When all fields
return null, progress routing freezes with no matching condition.
Add dual-format parsing: try **Field:** first, fall back to plain
Field: with line-start anchor. Both instances in cmdStateSnapshot
and buildStateFrontmatter are updated consistently.
Closes#730
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
getMilestoneInfo matches the first version string in ROADMAP.md, which
is typically the oldest shipped milestone. For list-format roadmaps
that use emoji markers (e.g. "🚧 **v2.1 Belgium**"), the function now
checks for the in-progress marker first before falling back to the
heading-based and bare version matching.
Closes#700
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cmdPhaseComplete determines is_last_phase and next_phase by scanning
.planning/phases/ directories on disk. Phases defined in ROADMAP.md
but not yet planned (no directory created) are invisible to this scan,
causing premature is_last_phase:true when only the first phase has
been scaffolded.
Add a fallback that parses ROADMAP.md phase headings when the
filesystem scan finds no next phase. Uses the existing comparePhaseNum
utility for consistent ordering with letter suffixes and decimals.
Closes#709
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Without --no-index, git check-ignore only reports files as ignored if
they are untracked. Once .planning/ files enter git's index (e.g., from
an initial commit before .gitignore was set up), check-ignore returns
"not ignored" even when .gitignore explicitly lists .planning/.
This means the documented safety net — "if .planning/ is gitignored,
commit_docs is automatically false" — silently fails for any repo where
.planning/ was ever committed. The --no-index flag checks .gitignore
rules regardless of tracking state, matching user expectations.
Closes#703
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
stateReplaceField properly escaped fieldName before building the regex,
but stateExtractField did not. Field names containing regex metacharacters
could cause incorrect matches or regex errors.
Co-authored-by: Ethan Hurst <ethan.hurst@outlook.com.au>
loadConfig() didn't include nyquist_validation in its return object, so
cmdInitPlanPhase always set nyquist_validation_enabled to undefined. The
plan-phase workflow could never detect whether Nyquist validation was
enabled or disabled via config.
Co-authored-by: Ethan Hurst <ethan.hurst@outlook.com.au>
cmdPhasePlanIndex had 3 mismatches with the canonical XML plan format
defined in templates/phase-prompt.md:
- files_modified: looked up fm['files-modified'] (hyphen) but plans use
files_modified (underscore). Now checks underscore first, hyphen fallback.
- objective: read from YAML frontmatter but plans put it in <objective>
XML tag. Now extracts first line from the tag, falls back to frontmatter.
- task_count: matched ## Task N markdown headings but plans use <task>
XML tags. Now counts XML tags first, markdown fallback.
All three fixes preserve backward compat with legacy markdown-style plans.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Add toPosixPath() helper to normalize output paths to forward slashes
- Use string concatenation for relative base paths instead of path.join()
- Apply toPosixPath() to all user-facing file paths in init.cjs output
- Use array-based execFileSync in test helpers to bypass shell quoting
issues with JSON args and dollar signs on Windows cmd.exe
Fixes 7 test failures on Windows: frontmatter set/merge (3), init
path assertions (2), and state dollar-amount corruption (2).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cmdMilestoneComplete previously appended new milestone entries at the
end of MILESTONES.md. Over successive milestones this produced
chronological order (oldest first), which is counterintuitive — users
expect the most recent milestone at the top.
This fix detects the file header (h1-h3) and inserts the new entry
immediately after it, pushing older entries down. Files without a
recognizable header get the entry prepended. New files still get a
default '# Milestones' header.
Adds 2 tests: single insertion ordering assertion and three-sequential-
completions verification (v1.2 < v1.1 < v1.0 in file order).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cmdMilestoneComplete previously iterated ALL phase directories in
.planning/phases/, including phases from prior milestones that remain on
disk. This produced inflated stats, stale accomplishments from old
summaries, and --archive-phases moving directories that belong to
earlier milestones.
This fix parses ROADMAP.md to extract phase numbers for the current
milestone, builds a normalized Set for O(1) lookup, and filters all
phase directory operations through an isDirInMilestone() helper.
The helper handles edge cases: leading zeros (01 -> 1), letter suffixes
(3A), decimal phases (3.1), large numbers (456), and excludes
non-numeric directories (notes/, misc/).
Adds 5 tests covering scoped stats, scoped archive, prefix collision
guard, non-numeric directory exclusion, and large phase numbers.
Complements upstream PRs #756 and #783 which fix getMilestoneInfo()
milestone detection — this fix addresses milestone completion scoping.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
loadConfig() never returned model_overrides, so resolveModelInternal()
could never find per-agent overrides — they were silently ignored.
Additionally, cmdResolveModel duplicated model resolution logic but
skipped the override check entirely. Now delegates to resolveModelInternal
so both code paths behave consistently.
Phase number parsing only matched single-decimal (e.g. 03.2) but crashed
on multi-level decimals (e.g. 03.2.1). Requirement IDs with regex
metacharacters (parentheses, dots) were interpolated raw into RegExp
constructors, causing SyntaxError crashes.
- Add escapeRegex() utility for safe regex interpolation
- Update normalizePhaseName/comparePhaseNum for multi-level decimals
- Replace all .replace('.', '\\.') with escapeRegex() across modules
- Escape reqId before regex interpolation in cmdPhaseComplete
- Update all phase dir matching regexes from (?:\.\d+)? to (?:\.\d+)*
- Add regression test for phase complete 03.2.1
Closes#621
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>