New test suite covering:
- HDOC: anti-heredoc instruction present in all 9 file-writing agents
- SKILL: skills: frontmatter present in all 11 agents
- HOOK: commented hooks pattern in file-writing agents
- SPAWN: no stale workaround patterns, valid agent type references
- AGENT: required frontmatter fields (name, description, tools, color)
509 total tests (462 existing + 47 new), 0 failures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace general-purpose workaround pattern with named subagent types:
- plan-phase: researcher and planner now spawn as gsd-phase-researcher/gsd-planner
- new-project: 4 researcher spawns now use gsd-project-researcher
- research-phase: researcher spawns now use gsd-phase-researcher
- quick: planner revision now uses gsd-planner
- diagnose-issues: debug agents now use gsd-debugger (matches template spec)
Removes 'First, read agent .md file' prompt prefix — named agent types
auto-load their .md file as system prompt, making the workaround redundant.
Preserves intentional general-purpose orchestrator spawns in discuss-phase
and plan-phase (auto-advance) where the agent runs an entire workflow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add skills: field to all 11 agent frontmatter files with forward-compatible
GSD workflow skill references (silently ignored until skill files are created).
Add commented hooks: examples to 9 file-writing agents showing PostToolUse
hook syntax for project-specific linting/formatting. Read-only agents
(plan-checker, integration-checker) skip hooks as they cannot modify files.
Per Claude Code docs: subagents don't inherit skills or hooks from the
parent conversation — they must be explicitly listed in frontmatter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 'never use heredoc' instruction to 6 agents that were missing it:
gsd-codebase-mapper, gsd-debugger, gsd-phase-researcher,
gsd-project-researcher, gsd-research-synthesizer, gsd-roadmapper.
All 9 file-writing agents now consistently prevent settings.local.json
corruption from heredoc permission entries (GSD #526).
Read-only agents (plan-checker, integration-checker) excluded as they
cannot write files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When users select "Other" to provide freeform input, Claude re-prompts
with another AskUserQuestion instead of dropping to plain text. This
loops 2-3 times before finally accepting freeform input.
Add explicit freeform escape rule to questioning.md reference, and
update new-milestone.md and discuss-phase.md to switch to plain text
when users signal freeform intent.
Closes#778
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>
Prevents re-asking questions already decided in earlier phases by reading
PROJECT.md, REQUIREMENTS.md, STATE.md, and all prior CONTEXT.md files
before generating gray areas. Prior decisions annotate options and skip
already-decided areas.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
zsh's built-in echo interprets \n escape sequences in JSON strings,
converting properly-escaped \\n back into literal newlines. This breaks
jq parsing with "control characters U+0000 through U+001F must be escaped".
printf '%s\n' preserves the JSON verbatim.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The auto-advance chain (discuss → plan → execute) was spawning each
subsequent phase as a Task(subagent_type="general-purpose"), creating
3-4 levels of nested agent sessions. At that nesting depth, the
Claude Code runtime hits resource limits or stdio contention, causing
the execute-phase to freeze or attempt to shell out to `claude` as a
subprocess (which is explicitly blocked).
The fix replaces Task spawns with Skill invocations for phase
transitions:
- discuss-phase auto-advance: Skill("gsd:plan-phase") instead of
Task(general-purpose)
- plan-phase auto-advance: Skill("gsd:execute-phase") instead of
Task(general-purpose)
The Skill tool runs in the same process context as the caller,
keeping the entire auto-advance chain flat at a single nesting level.
Each phase still spawns its own worker agents (gsd-executor,
gsd-planner, etc.) as Tasks, but the orchestration chain itself
no longer creates unnecessary depth.
Closes#686
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three files had hardcoded `.claude` paths that break OpenCode and
Gemini installations where the config directory is `.config/opencode`,
`.opencode`, or `.gemini` respectively.
Changes:
- hooks/gsd-check-update.js: add detectConfigDir() helper that checks
all runtime directories for get-shit-done/VERSION, falling back to
.claude. Used for cache dir, project VERSION, and global VERSION.
- commands/gsd/reapply-patches.md: detect runtime directory for both
global and local patch directories instead of hardcoding ~/.claude/
and ./.claude/
- workflows/update.md: detect runtime directory for local and global
VERSION/marker files, and clear cache across all runtime directories
Closes#682
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Gemini CLI uses AfterTool as the post-tool hook event name, not
PostToolUse (which is Claude Code's event name). The installer was
registering the context monitor under PostToolUse for all runtimes,
causing Gemini to print "Invalid hook event name" warnings on every
run and silently disabling the context monitor.
Changes:
- install.js: use runtime-aware event name (AfterTool for Gemini,
PostToolUse for others) when registering context monitor hook
- install.js: uninstall cleans up both PostToolUse and AfterTool
entries for backward compatibility with existing installs
- gsd-context-monitor.js: runtime-aware hookEventName in output
- docs/context-monitor.md: document both event names with Gemini
example
Closes#750
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>
Expand Codex adapter with AskUserQuestion → request_user_input parameter
mapping (including multiSelect workaround and Execute mode fallback) and
Task() → spawn_agent mapping (parallel fan-out, result parsing).
Add convertClaudeAgentToCodexAgent() that generates <codex_agent_role>
headers with role/tools/purpose and cleans agent frontmatter.
Generate config.toml with [features] (multi_agent, request_user_input)
and [agents.gsd-*] role sections pointing to per-agent .toml configs
with sandbox_mode (workspace-write/read-only) and developer_instructions.
Config merge handles 3 cases: new file, existing with GSD marker
(truncate + re-append), existing without marker (inject features +
append agents). Uninstall strips all GSD content including injected
feature keys while preserving user settings.
Closes#779
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The SessionStart hook always writes to ~/.claude/cache/ via os.homedir()
regardless of install type. The update workflow previously only cleared
the install-type-specific path, leaving stale cache at the global path
for local installs.
Clear both ./.claude/cache/ and ~/.claude/cache/ unconditionally.
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
The #330 migration that renames `statusline.js` → `gsd-statusline.js`
uses `.includes('statusline.js')` which matches ANY file containing
that substring. For example, a user's custom `ted-statusline.js` gets
silently rewritten to `ted-gsd-statusline.js` (which doesn't exist).
This happens inside `cleanupOrphanedHooks()` which runs before the
interactive "Keep existing / Replace" prompt, so even choosing "Keep
existing" doesn't prevent the damage.
Fix: narrow the regex to only match the specific old GSD path pattern
`hooks/statusline.js` (or `hooks\statusline.js` on Windows).
Co-authored-by: ddungan <sckim@mococo.co.kr>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Claude Code subagents sometimes rewrite ~/. paths to relative paths,
causing MODULE_NOT_FOUND when CWD is the project directory. $HOME is a
shell variable resolved at runtime, immune to model path rewriting.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- gsd-executor: Add <analysis_paralysis_guard> block after deviation_rules.
If executor makes 5+ consecutive Read/Grep/Glob calls without any
Edit/Write/Bash action, it must stop and either write or report blocked.
Prevents infinite analysis loops that stall execution.
- gsd-plan-checker: Add exhaustive cross-check in Step 4 requirement coverage.
Checker now also reads PROJECT.md requirements (not just phase goal) to
verify no relevant requirement is silently dropped. Unmapped requirements
become automatic blockers listed explicitly in issues.
- gsd-planner: Add task-level TDD guidance alongside existing TDD Detection.
For code-producing tasks in standard plans, tdd="true" + <behavior> block
makes test expectations explicit before implementation. Complements the
existing dedicated TDD plan approach — both can coexist.
Co-authored-by: CyPack <GITHUB_EMAIL_ADRESIN>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Add lightweight codebase scanning before gray area identification:
- New scout_codebase step checks for existing maps or does targeted grep
- Gray areas annotated with code context (existing components, patterns)
- Discussion options informed by what already exists in the codebase
- Context7 integration for library-specific questions
- CONTEXT.md template includes code_context section
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use `.claude/skills/` instead of `.agents/skills/` in agent and workflow skill references
Claude Code resolves project skills from `.claude/skills/` (project-level)
and `~/.claude/skills/` (user-level). The `.agents/skills/` path is the
universal/IDE-agnostic convention that Claude Code does not resolve, causing
project skills to be silently ignored by all affected agents and workflows.
Fixes#758
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: support both `.claude/skills/` and `.agents/skills/` for cross-IDE compatibility
Instead of replacing `.agents/skills/` with `.claude/skills/`, reference both
paths so GSD works with Claude Code (`.claude/skills/`) and other IDE agents
like OpenCode (`.agents/skills/`).
Addresses review feedback from begna112 on #758.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Stephen Miller <Stephen@betterbox.pw>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
DEBUGGER_MODEL and CHECKER_MODEL used uppercase bash convention but the
Task() calls referenced {debugger_model} and {integration_checker_model}
(lowercase). The mismatch caused Claude to skip substitution and fall back
to the parent session model, ignoring the configured GSD profile.
Co-authored-by: Ethan Hurst <ethan.hurst@outlook.com.au>
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() 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.
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>
The source now outputs posix paths; update the test to match instead
of using path.join (which produces backslashes on Windows).
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>
The run-tests.cjs child process now inherits NODE_V8_COVERAGE from the
parent so c8 collects coverage data. Also restores npm scripts to use
the cross-platform runner for both test and test:coverage commands.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
npm scripts pass `tests/*.test.cjs` to node/c8 as a literal string on
Windows (PowerShell/cmd don't expand globs). Adding `shell: bash` to CI
steps doesn't help because c8 spawns node as a child process using the
system shell. Use a Node script to enumerate test files cross-platform.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Windows PowerShell doesn't expand `tests/*.test.cjs` globs, causing
the test runner to fail with "Could not find" on Windows Node 20.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>