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>
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>
Add requirements_completed field to summary-extract output, mapping
from SUMMARY frontmatter requirements-completed key. Enables
/gsd:audit-milestone cross-check to receive data from SUMMARY source.
Re-applied from #631 against refactored codebase (commands.cjs + split tests).
Co-authored-by: Colin Johnson <Solvely-Colin@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
When orphaned SUMMARY.md files cause totalSummaries > totalPlans, the
progress percentage exceeds 100%, making String.repeat() throw RangeError
on negative arguments. Clamp percent to Math.min(100, ...) at all three
computation sites (state, commands, roadmap).
Closes#633
Co-authored-by: vinicius-tersi <vinicius-tersi@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Break the 5324-line monolith into focused modules:
- core.cjs: shared utilities, constants, internal helpers
- frontmatter.cjs: YAML frontmatter parsing/serialization/CRUD
- state.cjs: STATE.md operations + progression engine
- phase.cjs: phase CRUD, query, and lifecycle
- roadmap.cjs: roadmap parsing and updates
- verify.cjs: verification suite + consistency/health validation
- config.cjs: config ensure/set/get
- template.cjs: template selection and fill
- milestone.cjs: milestone + requirements lifecycle
- commands.cjs: standalone utility commands
- init.cjs: compound init commands for workflow bootstrapping
gsd-tools.cjs is now a thin CLI router (~550 lines including
JSDoc) that imports from lib/ modules. All 81 tests pass.
Also updates package.json test script to point to tests/.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>