Commit Graph

60 Commits

Author SHA1 Message Date
Tibsfox
d4859220e2 fix(core): add concurrency safety and atomic state writes
Wire withPlanningLock into all ROADMAP.md write paths (phase add/insert/complete/remove, roadmap update-plan-progress) to prevent concurrent corruption when parallel agents modify planning files.

Extract acquireStateLock/releaseStateLock from writeStateMd and add readModifyWriteStateMd helper that holds the lock across the entire read-modify-write cycle, preventing lost updates.

Replace O(n^2) normalizeMd fence detection with single-pass O(n) pre-computed fence state array.

Warn on must_haves parse failure and stateReplaceFieldWithFallback field miss.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 04:28:06 -07:00
Tom Boucher
d70ca3ee95 Merge pull request #1397 from bshiggins/feat/project-code-prefix
feat: add project_code config for phase directory prefixing
2026-04-01 12:14:48 -04:00
Brandon Higgins
1f34965717 feat: add project_code config for phase directory prefixing
When project_code is set (e.g., "CK"), phase directories are prefixed
with the code: CK-01-foundation, CK-02-api, etc. This disambiguates
phases across multiple GSD projects in the same session.

Changes:
- Add project_code to VALID_CONFIG_KEYS and buildNewProjectConfig defaults
- Add project_code to loadConfig in core.cjs
- Prepend prefix in cmdPhaseAdd and cmdPhaseInsert
- Update searchPhaseInDir, cmdFindPhase, comparePhaseNum, and
  normalizePhaseName to strip prefix before matching/sorting
- Support {project} placeholder in git.phase_branch_template
- Add 4 tests covering prefixed add, null code, find, and sort

Closes #1019

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 08:43:05 -06:00
Tibsfox
9647c719c4 fix(slug): add --raw flag to generate-slug callers and cap length
add-backlog and thread commands called generate-slug without --raw,
capturing JSON output (with newlines) as the directory name. Also
cap slugs at 60 chars to prevent absurdly long directory names.

Fixes gsd-build/get-shit-done#1391

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 01:16:01 -07:00
Tom Boucher
ef290664cf Merge pull request #1372 from gsd-build/fix/agent-install-validation-1371
fix: detect missing GSD agents to prevent silent subagent_type fallback
2026-03-24 21:19:59 -04:00
Tom Boucher
bc352a66c0 fix: detect missing GSD agents and warn when subagent_type falls back to general-purpose
When GSD agents are not installed in .claude/agents/, Task(subagent_type="gsd-*")
silently falls back to general-purpose, losing specialized instructions, structured
outputs, and verification protocols.

What changed:
- Added checkAgentsInstalled() to core.cjs that validates all expected agents exist on disk
- All init commands now include agents_installed and missing_agents in their output
- Health check (validate health) reports W010 when agents are missing or incomplete
- New validate agents subcommand for standalone agent installation diagnostics

Fixes #1371

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 21:10:25 -04:00
Tom Boucher
db3eeb8fe4 feat: agent skill injection via config (#1355)
Add agent_skills config section that maps agent types to skill directory
paths. At spawn time, workflows load configured skills and inject them
as <agent_skills> blocks in Task() prompts, giving subagents access to
project-specific skill files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:47:40 -04:00
Tom Boucher
c16b874aaa fix: findProjectRoot returns startDir when it already has .planning/
When cwd is a git repo inside a GSD workspace, findProjectRoot() walked
up and returned the workspace parent (which also has .planning/) instead
of the cwd itself. This caused all init commands to resolve project_root
to the workspace root, making phase/roadmap lookups fail with "Phase not
found" errors.

The fix adds an early return: if startDir already contains a .planning/
directory, it is the project root — no need to walk up to a parent.

Fixes #1362

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 15:59:42 -04:00
Tom Boucher
58c2b1f502 fix: Windows shell robustness, project_root detection, and hook stdin safety (#1343)
Address 4 root causes of Windows + Claude Code reliability issues:

1. Workflow shell robustness: add || true guards to informational commands
   (ls, grep, find, cat) that return non-zero on "no results", preventing
   workflow step failures under strict execution models. Guard glob loops
   with [ -e "$var" ] || continue to handle empty glob expansion.

2. Hook stdin handling: replace readFileSync('/dev/stdin') with async
   process.stdin + timeout in agent templates (gsd-verifier.md). Existing
   JS hooks already have timeout guards.

3. project_root detection: fix isInsideGitRepo() to check .git at the
   candidate parent level (not just below it), enabling correct detection
   when .git and .planning/ are siblings at the same directory level —
   the common single-repo case from a subdirectory.

4. @file: handoff: add missing @file: handlers to autonomous.md and
   manager.md workflows that call gsd-tools init but lacked the handler
   for large output payloads.

Fixes #1343

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 20:20:15 -04:00
Tom Boucher
5eb3c04bce fix: respect .planning/ in linked worktrees before resolving to main repo
resolveWorktreeRoot() unconditionally resolved linked worktrees to the main
repo root. When a linked worktree has its own independent .planning/ directory
(e.g., Conductor workspaces), all GSD commands read/wrote the wrong planning
state. Add an early return that checks for a local .planning/ before falling
through to main repo resolution.

The caller in gsd-tools.cjs already had this guard (added in #1283), but the
function itself should be correct regardless of call site. This is defense-in-
depth for any future callers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 10:58:24 -04:00
Tom Boucher
7c762058e1 fix: address post-merge review concerns from PR #1268
Three non-blocking findings from the adversarial re-review of the
workstream namespacing PR, addressed as a follow-up:

1. setActiveWorkstream now validates names with the same regex used
   at CLI entry and cmdWorkstreamSet — defense-in-depth so future
   callers can't poison the active-workstream file

2. Replaced tautological test assertion (result.success || !result.success
   was always true) with actual validation that cmdWorkstreamSet returns
   invalid_name error for path traversal attempts. Added 8 new tests
   for setActiveWorkstream's own validation.

3. Updated stale comment in copilot-install.test.cjs (said 31, actual 56)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 10:47:55 -04:00
SalesTeamToolbox
1ad5ab8097 fix: use planningRoot in setActiveWorkstream and add path traversal tests
Address review feedback on PR #1268:
- setActiveWorkstream now uses planningRoot(cwd) instead of hardcoded
  path, matching getActiveWorkstream for consistency
- Add 33 path traversal rejection tests covering all entry points:
  CLI --ws flag, GSD_WORKSTREAM env var, cmdWorkstreamSet, and
  poisoned active-workstream file

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:21:13 -07:00
SalesTeamToolbox
5f95fea4d7 refactor: extract shared utilities to reduce duplication across codebase
- core.cjs: add filterPlanFiles, filterSummaryFiles, getPhaseFileStats,
  readSubdirectories helpers; use them in searchPhaseInDir and getArchivedPhaseDirs
- gsd-tools.cjs: add parseNamedArgs() helper; replace ~50 repetitive indexOf/ternary
  patterns in state record-metric, add-decision, add-blocker, record-session,
  begin-phase, signal-waiting, template fill, and frontmatter subcommands
- phase.cjs: decompose 250-line cmdPhaseRemove into renameDecimalPhases(),
  renameIntegerPhases(), and updateRoadmapAfterPhaseRemoval(); import readSubdirectories
- workstream.cjs: import stateExtractField from state.cjs and shared helpers from
  core.cjs; replace all inline regex state parsing and readdirSync+filter+map patterns

All 1062 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 15:21:13 -07:00
SalesTeamToolbox
e3a427252d fix: prevent path traversal via workstream name sanitization
Validates workstream name at all entry points — CLI --ws flag,
GSD_WORKSTREAM env var, active-workstream file, and cmdWorkstreamSet —
blocking names that don't match [a-zA-Z0-9_-]+. Also fixes
getActiveWorkstream to use planningRoot() consistently and validates
names read from the active-workstream file before using them in path
joins.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 15:21:13 -07:00
SalesTeamToolbox
c83b69bbb5 feat: workstream namespacing for parallel milestone work
Enable multiple Claude Code instances to work on the same codebase
simultaneously by scoping .planning/ state into workstreams.

Core changes:
- planningDir(cwd, ws?) and planningPaths(cwd, ws?) are now workstream-aware
  via GSD_WORKSTREAM env var (auto-detected from --ws flag or active-workstream file)
- All bin/lib modules use planningDir(cwd) for scoped paths (STATE.md, ROADMAP.md,
  phases/, REQUIREMENTS.md) and planningRoot(cwd) for shared paths (milestones/,
  PROJECT.md, config.json, codebase/)
- New workstream.cjs module: create, list, status, complete, set, get, progress
- gsd-tools.cjs: --ws flag parsing with priority chain
  (--ws > GSD_WORKSTREAM env > active-workstream file > flat mode)
- Collision detection: transition.md checks for other active workstreams before
  suggesting next-milestone continuation (prevents WS A from trampling WS B)
- ${GSD_WS} routing propagation across all 9 workflow files ensures workstream
  scope chains automatically through the workflow lifecycle

New files:
- get-shit-done/bin/lib/workstream.cjs (CRUD + collision detection)
- get-shit-done/commands/gsd/workstreams.md (slash command)
- get-shit-done/references/workstream-flag.md (documentation)
- tests/workstream.test.cjs (20 tests covering CRUD, env var routing, --ws flag)

All 1062 tests passing (1042 existing + 20 new workstream tests).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:21:13 -07:00
Tom Boucher
02254db611 fix: resolve ProviderModelNotFoundError on non-Claude runtimes (#1156)
Extend resolve_model_ids to accept "omit" value: returns empty string
so non-Claude runtimes (OpenCode, Codex, Gemini, etc.) use their
configured default model instead of unresolvable Claude aliases.

- resolve_model_ids: "omit" short-circuits before alias resolution
- model_overrides still respected (checked first) for explicit IDs
- Installer sets resolve_model_ids: "omit" in ~/.gsd/defaults.json
  for non-Claude runtimes during install
- 4 new tests covering omit behavior and override passthrough
- Fix websearch test mocks for fs.writeSync output change (#1276)

Closes #1156

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 00:57:20 -04:00
David Kay
045eabbbf9 fix: use fs.writeSync for stdout to prevent pipe truncation
process.stdout.write() is async when stdout is a pipe. The
immediate process.exit(0) tears down the process before the
downstream reader (jq, python3, etc) consumes the buffer,
producing truncated JSON.

Replace with fs.writeSync(1, data) which blocks until the
kernel pipe buffer accepts the bytes, and drop process.exit(0)
on the success path so the event loop drains naturally.

Fixes #1275
Related: #493 (addressed >50KB case, this fixes <50KB)
2026-03-20 23:05:15 -05:00
Chris Esposito
4addcea4cf feat: implement --reviews flag for gsd:plan-phase
Wire the --reviews flag through the full stack so plan-phase can
replan incorporating cross-AI review feedback from REVIEWS.md:

- core.cjs: add has_reviews detection in searchPhaseInDir
- init.cjs: wire has_reviews and reviews_path through all init functions
- plan-phase.md command: add --reviews to argument-hint and flags
- plan-phase.md workflow: add step 2.5 validation, skip research,
  skip existing plans prompt, pass reviews_path to planner
- gsd-planner.md: add reviews_mode section for consuming review feedback
- COMMANDS.md: add --reviews and missing flags to docs

Closes the gap where --reviews was referenced in 6 places (review
workflow, review command, help workflow, COMMANDS.md, FEATURES.md)
but never implemented.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 16:08:17 -04:00
Tom Boucher
1a5259ce16 fix: add temp file reaper to prevent unbounded /tmp accumulation
core.cjs output() created gsd-*.json files in /tmp for large payloads
but never cleaned them up, causing unbounded disk usage (800+ GB
reported in #1251). Similarly, profile-pipeline.cjs created
gsd-pipeline-* and gsd-profile-* temp directories without cleanup.

Adds reapStaleTempFiles() that removes gsd-prefixed temp files/dirs
older than 5 minutes. Called opportunistically before each new temp
file/dir creation. Non-critical — cleanup failures never break output.

Closes #1251

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 15:46:06 -04:00
Tom Boucher
28166e4839 fix(core): auto-detect commit_docs from gitignore in loadConfig (#1250)
loadConfig() defaulted commit_docs to true regardless of whether
.planning/ was gitignored. The documented auto-detection only existed
inside cmdCommit, so init commands returned commit_docs: true even
when .planning/ was in .gitignore. This caused LLM executors to
bypass the cmdCommit gate and re-commit planning files with raw git.

Now loadConfig() checks isGitIgnored(cwd, '.planning/') when no
explicit commit_docs value is set in config.json. If .planning/ is
gitignored, commit_docs defaults to false. An explicit commit_docs
value in config.json is always respected.

Added 5 regression tests covering auto-detection, explicit overrides,
and the no-config-file edge case.
2026-03-20 10:41:42 -04:00
benzntech
b621d556f0 feat: add Exa and Firecrawl MCP support for research agents
Integrate Exa (semantic search) and Firecrawl (deep web scraping) as
MCP-based research tools, following the existing Brave Search pattern.

- Add tool declarations to all 3 researcher agents
- Add tool strategy sections with usage guidance and priority
- Add config detection for FIRECRAWL_API_KEY and EXA_API_KEY env vars
- Add firecrawl/exa_search config keys to core defaults and init output
- Update source priority hierarchy across all researchers
2026-03-20 14:53:46 +05:30
Srinivas Koduri
d0aae7b63c fix: address PR review — nested path resolution, sub_repos in init, depth-1 detection
- findProjectRoot: use isInsideGitRepo() to walk up and find .git in
  ancestor dirs, fixing nested paths like backend/src/modules/
- Add sub_repos to cmdInitExecutePhase output so execute-plan.md and
  gsd-executor.md can route commits correctly
- Align new-project.md sub-repo detection to maxdepth 1 matching
  detectSubRepos() behavior
- Add 3 nested path tests for .git heuristic, sub_repos, and multiRepo

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 12:18:20 -07:00
Srinivas Koduri
21081dc821 feat: multi-repo workspace support with auto-detection and project root resolution
Add support for workspaces with multiple independent git repositories.
When configured, GSD routes commits to the correct sub-repo and ensures
.planning/ stays at the project root.

Core features:
- detectSubRepos(): scans child directories for .git to discover repos
- findProjectRoot(): walks up from CWD to find the project root that
  owns .planning/, preventing orphaned .planning/ in sub-repos
- loadConfig auto-syncs sub_repos when repos are added or removed
- Migrates legacy "multiRepo: true" to sub_repos array automatically
- All init commands include project_root in output
- cmdCommitToSubrepo: groups files by sub-repo prefix, commits independently

Zero impact on single-repo workflows — sub_repos defaults to empty array.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 12:18:20 -07:00
Tom Boucher
3e2c85e6fd refactor: consolidate STATE.md field helpers, fix command injection in isGitIgnored
Refactoring:
- Extract stateReplaceFieldWithFallback() to state.cjs as single source of truth
  for the try-primary-then-fallback pattern that was duplicated inline across
  phase.cjs, milestone.cjs, and state.cjs
- Replace all inline bold-only regex patterns in cmdPhaseComplete with shared
  stateReplaceField/stateExtractField helpers — now supports both **Bold:**
  and plain Field: STATE.md formats (fixes the same bug as #924)
- Replace inline bold-only regex patterns in cmdMilestoneComplete with shared helpers
- Replace inline bold-only regex for Completed Phases/Total Phases/Progress
  counters in cmdPhaseComplete with stateExtractField/stateReplaceField
- Replace inline bold-only Total Phases regex in cmdPhaseRemove with shared helpers

Security:
- Fix command injection surface in isGitIgnored (core.cjs): replace execSync with
  string concatenation with execFileSync using array arguments — prevents shell
  interpretation of special characters in file paths

Tests (7 new):
- 5 tests for stateReplaceFieldWithFallback: primary field, fallback, neither,
  preference, and plain format
- 1 regression test: phase complete with plain-format STATE.md fields
- 1 regression test: milestone complete with plain-format STATE.md fields

854 tests pass (was 847). No behavioral regressions.
2026-03-19 15:05:20 -04:00
Tom Boucher
0afffb15f5 feat(core): worktree-aware .planning/ resolution and file locking (#1215)
- Add resolveWorktreeRoot() that detects linked worktrees via
  git rev-parse --git-common-dir and resolves to the main worktree
  where .planning/ lives
- Add withPlanningLock() file-based locking mechanism to prevent
  concurrent worktrees from corrupting shared planning files
- Wire worktree root resolution into gsd-tools.cjs main entry point
- Add regression tests for resolveWorktreeRoot (non-git, normal repo)
  and withPlanningLock (normal execution, error cleanup, stale lock
  recovery)
2026-03-19 13:41:11 -04:00
j2h4u
104a39e573 fix(lifecycle): extract one-liner from summary body when not in frontmatter
The summary template puts the one-liner as a `**bold**` line after the
`# Phase N` heading, but `cmdSummaryExtract` and `cmdMilestoneComplete`
only checked frontmatter `one-liner` field — which is often empty.

Adds `extractOneLinerFromBody()` to core.cjs as a fallback that parses
the first `**...**` line after the heading.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 21:29:25 +05:00
Tom Boucher
efc398c554 Merge pull request #1223 from trek-e/fix/remote-session-menu-fallback-1214
fix(workflows): text_mode config for Claude Code remote session compatibility
2026-03-19 12:13:10 -04:00
Tom Boucher
841da5a80d Merge pull request #1155 from gsd-build/Solvely/quick-task-branching
feat(quick): add quick-task branch support
2026-03-19 12:03:58 -04:00
Tom Boucher
9506b895c1 Merge pull request #1151 from 0Shard/fix/semver-and-frontmatter-parsing
fix: semver 3+ segment parsing and CRLF frontmatter corruption recovery
2026-03-19 12:02:43 -04:00
Tom Boucher
0487151142 fix(workflows): add text_mode config for Claude Code remote session compatibility (#1214)
- Add workflow.text_mode config option (default: false) that replaces
  AskUserQuestion TUI menus with plain-text numbered lists
- Document --text flag and config-set workflow.text_mode true as the
  fix for /rc remote sessions where the Claude App cannot forward TUI
  menu selections
- Update discuss-phase.md with text mode parsing and answer_validation
  fallback documentation
- Add text_mode to loadConfig defaults and VALID_CONFIG_KEYS
- Add regression tests for config-set and loadConfig
2026-03-19 09:19:01 -04:00
Tom Boucher
a4da216523 feat: support ticket-based phase identifiers for team workflows (#1019)
Teams sharing a GSD repo collide on sequential phase numbers when multiple
people create phases concurrently. This adds support for arbitrary string
IDs (e.g. PROJ-42, AUTH-101) as phase identifiers.

New config option:
  { "phase_naming": "custom" }

Changes:
- core.cjs: normalizePhaseName() handles non-numeric IDs gracefully
- core.cjs: comparePhaseNum() falls back to string comparison for custom IDs
- core.cjs: searchPhaseInDir() matches custom ID prefixes case-insensitively
- core.cjs: getMilestonePhaseFilter() recognizes custom ID directory names
- core.cjs: getRoadmapPhaseInternal() matches Phase headers with any word chars
- core.cjs: loadConfig() adds phase_naming option (default: 'sequential')
- phase.cjs: cmdPhaseAdd() accepts --id flag for custom phase IDs
- verify.cjs: health check skips sequential numbering validation in custom mode
- gsd-tools.cjs: phase add --id PROJ-42 "description" wired up

Usage:
  # Sequential mode (default, backward compatible)
  gsd-tools phase add "User Authentication"  # → Phase 3

  # Custom mode (config: phase_naming: custom)
  gsd-tools phase add --id PROJ-42 "User Authentication"
  # Creates: .planning/phases/PROJ-42-user-authentication/
  # ROADMAP: ### Phase PROJ-42: User Authentication

All 755 existing tests pass.

Fixes #1019
2026-03-18 20:27:12 -04:00
Tom Boucher
73d6ec06cc Merge pull request #1132 from trek-e/feat/1m-context-awareness-1086 2026-03-18 18:08:19 -04:00
Tom Boucher
c7954d1ad7 refactor: deduplicate code, add planningPaths helper, annotate empty catches
Code quality refactoring across 9 lib modules:

1. state.cjs: Remove duplicate stateExtractField() definition (lines 12
   vs 184). Replace 3 inline regex escape patterns with escapeRegex()
   from core.cjs. Add getStatePath() local helper for the 15 repeated
   statePath declarations. Import planningPaths from core.

2. core.cjs: Add planningPaths(cwd) helper returning an object with all
   common .planning paths (state, roadmap, project, config, phases,
   requirements). Add planningDir(cwd) shorthand. Export both.

3. init.cjs: Replace Unix-only execSync('find . | grep | head') with
   cross-platform fs.readdirSync recursive walk for code file detection.
   Remove unused child_process import. Fixes Windows compatibility.

4. All lib modules: Annotate 48 empty catch blocks with
   /* intentionally empty */ to distinguish deliberate error swallowing
   from accidental missing handlers. Affected: commands.cjs (8),
   config.cjs (1), core.cjs (4), init.cjs (13), milestone.cjs (3),
   phase.cjs (6), roadmap.cjs (1), state.cjs (3), verify.cjs (9).

All 755 tests pass.
2026-03-18 12:31:20 -04:00
Tom Boucher
0377951a04 feat: context window size awareness for 1M+ models (#1086)
GSD was designed around 200k context windows. With Opus 4.6 / Sonnet 4.6
shipping 1M context at standard pricing, GSD should adapt its strategies.

Changes:
- Add context_window config option (default: 200000, configurable to 1000000)
- Expose context_window in init output so workflows can adapt behavior
- Update execute-phase.md to note context-size-dependent strategies:
  - 200k: paths-only executor context, 10-15% orchestrator budget
  - 1M+: richer context passing, inline small phases, relaxed /clear
- Context monitor already uses percentage-based thresholds (adaptive)

Users can opt in via config.json:
  { "context_window": 1000000 }

Addresses #1086
2026-03-18 12:27:55 -04:00
Tom Boucher
c2c4301a98 feat: model alias-to-full-ID resolution for Task API compatibility (#991) (#1141)
Claude Code's Task tool sometimes doesn't resolve short aliases (opus,
sonnet, haiku) and passes them directly to the API, causing 404s. Tasks
then inherit the parent session's model instead of the configured one.

Added:
- MODEL_ALIAS_MAP in core.cjs mapping aliases to full model IDs
- resolve_model_ids config option (default: false for backward compat)
- resolveModelInternal() maps aliases when resolve_model_ids is true

Usage:
  { "resolve_model_ids": true }

This causes gsd-tools resolve-model to return 'claude-sonnet-4-5' instead
of 'sonnet', which the Task tool passes to the API without needing alias
resolution on Claude Code's side.

The alias map is maintained per release. Users can also use model_overrides
for full control.

All 755 tests pass.

Fixes #991
2026-03-18 09:57:42 -06:00
Tom Boucher
26d742c548 fix(core): replace negative-heuristic stripShippedMilestones with positive milestone lookup (#1145) (#1146)
stripShippedMilestones() uses a negative heuristic: strip all <details>
blocks, assume what remains is the current milestone. This breaks when
agents accidentally wrap the current milestone in <details> for
collapsibility — all downstream consumers then see an empty milestone.

Observed failure: cmdPhaseComplete() returns is_last_phase: true and
next_phase: null for non-final phases because the current milestone's
phases were stripped along with shipped ones.

Added extractCurrentMilestone(content, cwd) — a positive lookup that:
1. Reads the current milestone version from STATE.md frontmatter
2. Falls back to 🚧 in-progress marker in ROADMAP.md
3. Finds the section heading matching that version
4. Returns only that section's content
5. Falls back to stripShippedMilestones() if version can't be determined

Updated 12 call sites across 6 files to use extractCurrentMilestone:
- core.cjs: getRoadmapPhaseInternal(), getMilestonePhaseFilter()
- phase.cjs: cmdPhaseAdd(), cmdPhaseInsert(), cmdPhaseComplete() (2 sites)
- roadmap.cjs: cmdRoadmapGetPhase(), cmdRoadmapAnalyze()
- commands.cjs: stats/progress display
- verify.cjs: phase verification (2 sites)
- init.cjs: project initialization

Kept stripShippedMilestones() for:
- getMilestoneInfo() — determines the version itself, can't use positive lookup
- replaceInCurrentMilestone() — write operations, conservative boundary
- extractCurrentMilestone() fallback — when no version available

All 755 tests pass.

Fixes #1145
2026-03-18 09:57:31 -06:00
Colin
3a0c81133b feat(quick): add quick-task branch support 2026-03-17 12:04:02 -04:00
0Shard
7156f02ed5 fix: semver 3+ segment parsing and CRLF frontmatter corruption recovery
- fix(core): getMilestoneInfo() version regex `\d+\.\d+` only matched
  2-segment versions (v1.2). Changed to `\d+(?:\.\d+)+` to support
  3+ segments (v1.2.1, v2.0.1). Same fix in roadmap.cjs milestone
  extraction pattern.

- fix(state): stripFrontmatter() used `^---\n` (LF-only) which failed
  to strip CRLF frontmatter blocks. When STATE.md had dual frontmatter
  blocks from prior CRLF corruption, each writeStateMd() call preserved
  the stale block and prepended a new wrong one. Now handles CRLF and
  strips all stacked frontmatter blocks.

- fix(frontmatter): extractFrontmatter() always used the first ---
  block. When dual blocks exist from corruption, the first is stale.
  Now uses the last block (most recent sync).
2026-03-17 17:18:48 +02:00
Tom Boucher
460f92e727 feat: normalize generated markdown for markdownlint compliance (#1112)
Add normalizeMd() utility that fixes common markdownlint violations
in GSD-generated .planning/ files, improving IDE readability:

  MD022 — Blank lines around headings
  MD031 — Blank lines around fenced code blocks
  MD032 — Blank lines around lists
  MD012 — No multiple consecutive blank lines (collapsed to max 2)
  MD047 — Files end with a single newline

Applied at key write points:
  - state.cjs: writeStateMd() normalizes STATE.md on every write
  - frontmatter.cjs: frontmatter set/merge commands normalize output
  - template.cjs: template fill normalizes generated plan/summary files
  - milestone.cjs: MILESTONES.md archive writes normalized

Includes 14 new tests covering all rules, edge cases (CRLF, frontmatter,
ordered lists, code blocks, real-world STATE.md patterns).

Closes #1040
2026-03-16 13:39:34 -06:00
Tom Boucher
2b0f595a17 fix(core): return 'opus' directly instead of mapping to 'inherit' (#1079)
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
2026-03-16 08:49:55 -06:00
Colin Johnson
0fde04f561 fix(stats): correct git and roadmap reporting (#1066) 2026-03-15 19:31:28 -06:00
Aly Thobani
44de7c210c feat: Programmatic implementation of /gsd:set-profile command (#1007)
* 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
2026-03-15 11:45:14 -06:00
jorge g
698985feb1 feat: add inherit model profile for OpenCode /model (#951) 2026-03-15 11:40:08 -06:00
Shard
ca4ae7b3b8 fix: scope ROADMAP.md searches to current milestone (#1059)
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
2026-03-15 11:39:59 -06:00
Fana
9481fdf802 feat: add /gsd:ui-phase + /gsd:ui-review design contract and visual audit layer (closes #986)
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>
2026-03-14 21:57:59 -06:00
Fana
ef032bc8f1 feat: harden Nyquist defaults, add retroactive validation, compress prompts (#855)
* 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>
2026-03-03 11:19:44 -06:00
Lex Christopherson
c298a1a4dc refactor: rename depth setting to granularity (closes #879)
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>
2026-03-03 10:53:34 -06:00
TÂCHES
43c0bde6d0 Merge pull request #822 from Tibsfox/fix/core-lifecycle-state
fix(core): correct phase lifecycle, milestone detection, state parsing, and CLI commit messages
2026-03-02 16:20:03 -06:00
TÂCHES
023d3bce1f Merge pull request #821 from Tibsfox/fix/config-and-path-corrections
Clean surgical fix - adds --no-index to git check-ignore for correct .gitignore handling of tracked files
2026-03-02 16:14:38 -06:00
j2h4u
e1b3277869 fix(state): derive total_phases from ROADMAP when phases lack directories
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>
2026-03-02 16:07:53 -06:00