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>