Compare commits

...

18 Commits

Author SHA1 Message Date
Lex Christopherson
4cbe0b6d56 1.37.1 2026-04-17 10:38:47 -06:00
Lex Christopherson
d32e5bd461 docs: update changelog for v1.37.1
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 10:38:47 -06:00
Lex Christopherson
b13eb88ae2 fix: load sketch findings into ui-phase researcher
The UI researcher creates UI-SPEC.md but wasn't checking for
sketch-findings skills. Validated design decisions from /gsd-sketch
were being ignored, causing the researcher to re-ask questions
already answered during sketching.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 10:38:47 -06:00
Tom Boucher
8798e68721 fix(ci): update ARCHITECTURE.md counts and add TEXT_MODE fallback to sketch workflow (#2377)
* fix(tests): clear CLAUDECODE env var in read-guard test runner

The hook skips its advisory on two env vars: CLAUDE_SESSION_ID and
CLAUDECODE. runHook() cleared CLAUDE_SESSION_ID but inherited CLAUDECODE
from process.env, so tests run inside a Claude Code session silently
no-oped and produced no stdout, causing JSON.parse to throw.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): update ARCHITECTURE.md counts and add TEXT_MODE fallback to sketch workflow

Four new spike/sketch files were added in 1.37.0 but two housekeeping
items were missed: ARCHITECTURE.md component counts (75→79 commands,
72→76 workflows) and the required TEXT_MODE fallback in sketch.md for
non-Claude runtimes (#2012).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): update directory-tree slash command count in ARCHITECTURE.md

Missed the second count in the directory tree (# 75 slash commands → 79).
The prose "Total commands" was updated but the tree annotation was not,
causing command-count-sync.test.cjs to fail.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 12:31:14 -04:00
Tom Boucher
71af170a08 fix(tests): clear CLAUDECODE env var in read-guard test runner (#2375)
The hook skips its advisory on two env vars: CLAUDE_SESSION_ID and
CLAUDECODE. runHook() cleared CLAUDE_SESSION_ID but inherited CLAUDECODE
from process.env, so tests run inside a Claude Code session silently
no-oped and produced no stdout, causing JSON.parse to throw.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 12:22:09 -04:00
Lex Christopherson
9e8257a3b1 1.37.0 2026-04-17 09:53:04 -06:00
Lex Christopherson
bbcec632b6 docs: update changelog for v1.37.0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 09:52:59 -06:00
Lex Christopherson
9ef8f9ba2a docs: add spike/sketch commands to README command tables
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 09:50:32 -06:00
Lex Christopherson
f983925eca feat: add /gsd-spike, /gsd-sketch, /gsd-spike-wrap-up, /gsd-sketch-wrap-up commands
First-class GSD commands for rapid feasibility spiking and UI design sketching,
ported from personal skills into the framework with full GSD integration:

- Spikes save to .planning/spikes/, sketches to .planning/sketches/
- GSD banners, checkpoint boxes, Next Up blocks, gsd-sdk query commits
- --quick flag skips intake/decomposition for both commands
- Wrap-up commands package findings into project-local .claude/skills/
  and write WRAP-UP-SUMMARY.md to .planning/ for project history
- Neither requires /gsd-new-project — auto-creates .planning/ subdirs

Pipeline integration:
- new-project.md detects prior spike/sketch work on init
- discuss-phase.md loads spike/sketch findings into prior context
- plan-phase.md includes findings in planner <files_to_read>
- do.md routes spike/sketch intent to new commands
- explore.md offers spike/sketch as output routes
- next.md surfaces pending spike/sketch work as notices
- pause-work.md detects active sketch context for handoff
- help.md documents all 4 commands with usage examples
- artifact-types.md registers spike/sketch artifact taxonomy

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 09:47:15 -06:00
Tom Boucher
c5e77c8809 feat(agents): enforce size budget + extract duplicated boilerplate (#2361) (#2362)
Adds tiered agent-size-budget test to prevent unbounded growth in agent
definitions, which are loaded verbatim into context on every subagent
dispatch. Extracts two duplicated blocks (mandatory-initial-read,
project-skills-discovery) to shared references under
get-shit-done/references/ and migrates the 5 top agents (planner,
executor, debugger, verifier, phase-researcher) to @file includes.

Also fixes two broken relative @planner-source-audit.md references in
gsd-planner.md that silently disabled the planner's source audit
discipline.

Closes #2361

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 10:47:08 -04:00
Tom Boucher
4a912e2e45 feat(debugger): extract philosophy block to shared reference (#2363) (#2364)
The gsd-debugger philosophy block contains 76 lines of evergreen
debugging disciplines (user-as-reporter, meta-debugging, cognitive
biases, restart protocol) that are not debugger-specific workflow
and are paid in context on every debugger dispatch.

Extracts to get-shit-done/references/debugger-philosophy.md, replaces
the inline block with a single @file include. Behavior-preserving.

Closes #2363

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 10:23:18 -04:00
Tom Boucher
c2158b9690 docs(contributing): clarify agents/ source of truth vs install-sync targets (#2365) (#2366)
Documents that only agents/ at the repo root is tracked by git.
.claude/agents/, .cursor/agents/, and .github/agents/ are gitignored
install-sync outputs and must not be edited — they will be overwritten.

Closes #2365

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 10:15:47 -04:00
Tom Boucher
3589f7b256 fix(worktrees): prune orphaned worktrees in code, not prose (#2367)
* feat: add /gsd-spec-phase — Socratic spec refinement with ambiguity scoring (#2213)

Introduces `/gsd-spec-phase <phase>` as an optional pre-step before discuss-phase.
Clarifies WHAT a phase delivers (requirements, boundaries, acceptance criteria) with
quantitative ambiguity scoring before discuss-phase handles HOW to implement.

- `commands/gsd/spec-phase.md` — slash command routing to workflow
- `get-shit-done/workflows/spec-phase.md` — full Socratic interview loop (up to 6
  rounds, 5 rotating perspectives: Researcher, Simplifier, Boundary Keeper, Failure
  Analyst, Seed Closer) with weighted 4-dimension ambiguity gate (≤ 0.20 to write SPEC.md)
- `get-shit-done/templates/spec.md` — SPEC.md template with falsifiable requirements
  (Current/Target/Acceptance per requirement), Boundaries, Acceptance Criteria,
  Ambiguity Report, and Interview Log; includes two full worked examples
- `get-shit-done/workflows/discuss-phase.md` — new `check_spec` step detects
  `{padded_phase}-SPEC.md` at startup; displays "Found SPEC.md — N requirements
  locked. Focusing on implementation decisions."; `analyze_phase` respects `spec_loaded`
  flag to skip "what/why" gray areas; `write_context` emits `<spec_lock>` section
  with boundary summary and canonical ref to SPEC.md
- `docs/ARCHITECTURE.md` — update command/workflow counts (74→75, 71→72)

Closes #2213

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(worktrees): auto-prune merged worktrees in code, not prose

Adds pruneOrphanedWorktrees(repoRoot) to core.cjs. It runs on every
cmdInitProgress call (the entry point for most GSD commands) and removes
linked worktrees whose branch is fully merged into main, then runs
git worktree prune to clear stale references. Guards prevent removal of
the main worktree, the current process.cwd(), or any unmerged branch.

Covered by 4 new real-git integration tests in
tests/prune-orphaned-worktrees.test.cjs (TDD red→green).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:11:08 -04:00
Tom Boucher
d7b613d147 fix(hooks): check CLAUDECODE env var in read-guard skip (#2344) (#2352)
Closes #2344

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 09:30:20 -04:00
Tom Boucher
f8448a337b fix(quick): add gsd-sdk pre-flight check with install hint (#2334) (#2354)
Closes #2334

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 09:29:59 -04:00
Tom Boucher
d8b851346e fix(agents): add no-re-read critical rules to ui-checker and planner (#2346) (#2355)
* fix(agents): add no-re-read critical rules to ui-checker and planner (#2346)

Closes #2346

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(agents): correct contradictory heredoc rule in read-only ui-checker

The critical_rules block instructed the agent to "use the Write tool"
for any output, but gsd-ui-checker has no Write tool and is explicitly
read-only. Replaced with a simple no-file-creation rule.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(planner): trim verbose prose to satisfy 46KB size constraint

Condenses documentation_lookup, philosophy, project_context, and
context_fidelity sections — removing redundant examples while
preserving all semantic content. Fixes CI failure on planner
decomposition size test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 09:26:49 -04:00
Tom Boucher
fb7856f9d2 fix(intel): detect .kilo runtime layout for canonical scope resolution (#2351) (#2356)
Under a .kilo install the runtime root is .kilo/ and the command
directory is command/ (not commands/gsd/). Hardcoded paths produced
semantically empty intel files. Add runtime layout detection and a
mapping table so paths are resolved against the correct root.

Closes #2351

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 09:20:42 -04:00
Tom Boucher
6deef7e7ed feat(init): allow parallel discuss across independent phases (#2268) (#2357)
The sliding-window pattern serialized discuss to one phase at a time
even when phases had no dependency relationship. Replaced it with a
simple predicate: every undiscussed phase whose dependencies are
satisfied is marked is_next_to_discuss, letting the user pick any of
them from the manager's recommended_actions list.

Closes #2268

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 09:20:26 -04:00
51 changed files with 2813 additions and 200 deletions

View File

@@ -6,8 +6,54 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased]
## [1.37.1] - 2026-04-17
### Fixed
- **Shell hooks falsely flagged as stale on every session** — `gsd-phase-boundary.sh`, `gsd-session-state.sh`, and `gsd-validate-commit.sh` now ship with a `# gsd-hook-version: {{GSD_VERSION}}` header; the installer substitutes `{{GSD_VERSION}}` in `.sh` hooks the same way it does for `.js` hooks; and the stale-hook detector in `gsd-check-update.js` now matches bash `#` comment syntax in addition to JS `//` syntax. All three changes are required together — neither the regex fix alone nor the install fix alone is sufficient to resolve the false positive (#2136, #2206, #2209, #2210, #2212)
- UI-phase researcher now loads sketch findings skills, preventing re-asking questions already answered during `/gsd-sketch`
## [1.37.0] - 2026-04-17
### Added
- **`/gsd-spike` and `/gsd-sketch` commands** — First-class GSD commands for rapid feasibility spiking and UI design sketching. Each produces throwaway experiments (spikes) or HTML mockups with multi-variant exploration (sketches), saved to `.planning/spikes/` and `.planning/sketches/` with full GSD integration: banners, checkpoint boxes, `gsd-sdk query` commits, and `--quick` flag to skip intake. Neither requires `/gsd-new-project` — auto-creates `.planning/` subdirs on demand
- **`/gsd-spike-wrap-up` and `/gsd-sketch-wrap-up` commands** — Package spike/sketch findings into project-local skills at `./.claude/skills/` with a planning summary at `.planning/`. Curates each spike/sketch one-at-a-time, groups by feature/design area, and adds auto-load routing to project CLAUDE.md
- **Spike/sketch pipeline integration** — `new-project` detects prior spike/sketch work on init, `discuss-phase` loads findings into prior context, `plan-phase` includes findings in planner `<files_to_read>`, `explore` offers spike/sketch as output routes, `next` surfaces pending spike/sketch work as notices, `pause-work` detects active sketch context for handoff, `do` routes spike/sketch intent to new commands
- **`/gsd-spec-phase` command** — Socratic spec refinement with ambiguity scoring to clarify WHAT a phase delivers before discuss-phase. Produces a SPEC.md with falsifiable requirements locked before implementation decisions begin (#2213)
- **`/gsd-progress --forensic` flag** — Appends a 6-check integrity audit after the standard progress report (#2231)
- **`/gsd-discuss-phase --all` flag** — Skip area selection and discuss all gray areas interactively (#2230)
- **Parallel discuss across independent phases** — Multiple phases without dependencies can be discussed concurrently (#2268)
- **`gsd-read-injection-scanner` hook** — PostToolUse hook that scans for prompt injection attempts in read file contents (#2201)
- **SDK Phase 2 caller migration** — Workflows, agents, and commands now use `gsd-sdk query` instead of raw `gsd-tools.cjs` calls (#2179)
- **Project identity in Next Up blocks** — All Next Up blocks include workspace context for multi-project clarity (#1948)
- **Agent size-budget enforcement** — New `tests/agent-size-budget.test.cjs` enforces tiered line-count limits on every `gsd-*.md` agent (XL=1600, LARGE=1000, DEFAULT=500). Unbounded agent growth is paid in context on every subagent dispatch; the test prevents regressions and requires a deliberate PR rationale to raise a budget (#2361)
- **Shared `references/mandatory-initial-read.md`** — Extracts the `<required_reading>` enforcement block that was duplicated across 5 top agents. Agents now include it via a single `@~/.claude/get-shit-done/references/mandatory-initial-read.md` line, using Claude Code's progressive-disclosure `@file` reference mechanism (#2361)
- **Shared `references/project-skills-discovery.md`** — Extracts the 5-step project skills discovery checklist that was copy-pasted across 5 top agents with slight divergence. Single source of truth with a per-agent "Application" paragraph documenting how planners, executors, researchers, verifiers, and debuggers each apply the rules (#2361)
### Changed
- **`gsd-debugger` philosophy extracted to shared reference** — The 76-line `<philosophy>` block containing evergreen debugging disciplines (user-as-reporter framing, meta-debugging, foundation principles, cognitive-bias table, systematic investigation, when-to-restart protocol) is now in `get-shit-done/references/debugger-philosophy.md` and pulled into the agent via a single `@file` include. Same content, lighter per-dispatch context footprint (#2363)
- **`gsd-planner`, `gsd-executor`, `gsd-debugger`, `gsd-verifier`, `gsd-phase-researcher`** — Migrated to `@file` includes for the mandatory-initial-read and project-skills-discovery boilerplate. Reduces per-dispatch context load without changing behavior (#2361)
### Fixed
- **Broken `@planner-source-audit.md` relative references in `gsd-planner.md`** — Two locations referenced `@planner-source-audit.md` (resolves relative to working directory, almost always missing) instead of the correct absolute `@~/.claude/get-shit-done/references/planner-source-audit.md`. The planner's source audit discipline was silently unenforced (#2361)
- **Shell hooks falsely flagged as stale** — `.sh` hooks now ship with version headers; installer stamps them; stale-hook detector matches bash comment syntax (#2136)
- **Worktree cleanup** — Orphaned worktrees pruned in code, not prose; pre-merge deletion guard in quick.md (#2367, #2275)
- **`/gsd-quick` crashes** — gsd-sdk pre-flight check with install hint (#2334); rescue uncommitted SUMMARY.md before worktree removal (#2296)
- **Pattern mapper redundant reads** — Early-stop rule prevents re-reading files (#2312)
- **Context meter scaling** — Respects `CLAUDE_CODE_AUTO_COMPACT_WINDOW` for accurate context bar (#2219)
- **Codex install paths** — Replace all `~/.claude/` paths in Codex `.toml` files (#2320)
- **Graphify edge fallback** — Falls back to `graph.links` when `graph.edges` is absent (#2323)
- **New-project saved defaults** — Display saved defaults before prompting to use them (#2333)
- **UAT parser** — Accept bracketed result values and fix decimal phase renumber padding (#2283)
- **Stats duplicate rows** — Normalize phase numbers in Map to prevent duplicates (#2220)
- **Review prompt shell expansion** — Pipe prompts via stdin (#2222)
- **Intel scope resolution** — Detect .kilo runtime layout (#2351)
- **Read-guard CLAUDECODE env** — Check env var in skip condition (#2344)
- **Add-backlog directory ordering** — Write ROADMAP entry before directory creation (#2286)
- **Settings workstream routing** — Route reads/writes through workstream-aware config path (#2285)
- **Quick normalize flags** — `--discuss --research --validate` combo normalizes to FULL_MODE (#2274)
- **Windows path normalization** — Normalize in update scope detection (#2278)
- **Codex/OpenCode model overrides** — Embed model_overrides in agent files (#2279)
- **Installer custom files** — Restore detect-custom-files and backup_custom_files (#1997)
- **Agent re-read loops** — Add no-re-read critical rules to ui-checker and planner (#2346)
## [1.36.0] - 2026-04-14
@@ -1978,7 +2024,9 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- YOLO mode for autonomous execution
- Interactive mode with checkpoints
[Unreleased]: https://github.com/gsd-build/get-shit-done/compare/v1.36.0...HEAD
[Unreleased]: https://github.com/gsd-build/get-shit-done/compare/v1.37.1...HEAD
[1.37.1]: https://github.com/gsd-build/get-shit-done/compare/v1.37.0...v1.37.1
[1.37.0]: https://github.com/gsd-build/get-shit-done/compare/v1.36.0...v1.37.0
[1.36.0]: https://github.com/gsd-build/get-shit-done/releases/tag/v1.36.0
[1.35.0]: https://github.com/gsd-build/get-shit-done/releases/tag/v1.35.0
[1.34.2]: https://github.com/gsd-build/get-shit-done/releases/tag/v1.34.2

View File

@@ -316,13 +316,25 @@ get-shit-done/
workflows/ — Workflow definitions (.md)
references/ — Reference documentation (.md)
templates/ — File templates
agents/ — Agent definitions (.md)
agents/ — Agent definitions (.md) — CANONICAL SOURCE
commands/gsd/ — Slash command definitions (.md)
tests/ — Test files (.test.cjs)
helpers.cjs — Shared test utilities
docs/ — User-facing documentation
```
### Source of truth for agents
Only `agents/` at the repo root is tracked by git. The following directories may exist on a developer machine with GSD installed and **must not be edited** — they are install-sync outputs and will be overwritten:
| Path | Gitignored | What it is |
|------|-----------|------------|
| `.claude/agents/` | Yes (`.gitignore:9`) | Local Claude Code runtime sync |
| `.cursor/agents/` | Yes (`.gitignore:12`) | Local Cursor IDE bundle |
| `.github/agents/gsd-*` | Yes (`.gitignore:37`) | Local CI-surface bundle |
If you find that `.claude/agents/` has drifted from `agents/` (e.g., after a branch change), re-run `bin/install.js` to re-sync from the canonical source. Always edit `agents/` — never the derivative directories.
## Security
- **Path validation** — use `validatePath()` from `security.cjs` for any user-provided paths

View File

@@ -595,6 +595,15 @@ You're never locked in. The system adapts.
| `/gsd-list-workspaces` | Show all GSD workspaces and their status |
| `/gsd-remove-workspace` | Remove workspace and clean up worktrees |
### Spiking & Sketching
| Command | What it does |
|---------|--------------|
| `/gsd-spike [idea] [--quick]` | Throwaway experiments to validate feasibility before planning — no project init required |
| `/gsd-sketch [idea] [--quick]` | Throwaway HTML mockups with multi-variant exploration — no project init required |
| `/gsd-spike-wrap-up` | Package spike findings into a project-local skill for future build conversations |
| `/gsd-sketch-wrap-up` | Package sketch design findings into a project-local skill for future builds |
### UI Design
| Command | What it does |

View File

@@ -21,8 +21,7 @@ You are spawned by:
Your job: Find the root cause through hypothesis testing, maintain debug file state, optionally fix and verify (depending on mode).
**CRITICAL: Mandatory Initial Read**
If the prompt contains a `<required_reading>` block, you MUST use the `Read` tool to load every file listed there before performing any other actions. This is your primary context.
@~/.claude/get-shit-done/references/mandatory-initial-read.md
**Core responsibilities:**
- Investigate autonomously (user reports symptoms, you find cause)
@@ -37,89 +36,13 @@ If the prompt contains a `<required_reading>` block, you MUST use the `Read` too
@~/.claude/get-shit-done/references/common-bug-patterns.md
</required_reading>
**Project skills:** Check `.claude/skills/` or `.agents/skills/` directory if either exists:
1. List available skills (subdirectories)
2. Read `SKILL.md` for each skill (lightweight index ~130 lines)
3. Load specific `rules/*.md` files as needed during implementation
4. Do NOT load full `AGENTS.md` files (100KB+ context cost)
5. Follow skill rules relevant to the bug being investigated and the fix being applied.
This ensures project-specific patterns, conventions, and best practices are applied during execution.
**Project skills:** @~/.claude/get-shit-done/references/project-skills-discovery.md
- Load `rules/*.md` as needed during **investigation and fix**.
- Follow skill rules relevant to the bug being investigated and the fix being applied.
<philosophy>
## User = Reporter, Claude = Investigator
The user knows:
- What they expected to happen
- What actually happened
- Error messages they saw
- When it started / if it ever worked
The user does NOT know (don't ask):
- What's causing the bug
- Which file has the problem
- What the fix should be
Ask about experience. Investigate the cause yourself.
## Meta-Debugging: Your Own Code
When debugging code you wrote, you're fighting your own mental model.
**Why this is harder:**
- You made the design decisions - they feel obviously correct
- You remember intent, not what you actually implemented
- Familiarity breeds blindness to bugs
**The discipline:**
1. **Treat your code as foreign** - Read it as if someone else wrote it
2. **Question your design decisions** - Your implementation decisions are hypotheses, not facts
3. **Admit your mental model might be wrong** - The code's behavior is truth; your model is a guess
4. **Prioritize code you touched** - If you modified 100 lines and something breaks, those are prime suspects
**The hardest admission:** "I implemented this wrong." Not "requirements were unclear" - YOU made an error.
## Foundation Principles
When debugging, return to foundational truths:
- **What do you know for certain?** Observable facts, not assumptions
- **What are you assuming?** "This library should work this way" - have you verified?
- **Strip away everything you think you know.** Build understanding from observable facts.
## Cognitive Biases to Avoid
| Bias | Trap | Antidote |
|------|------|----------|
| **Confirmation** | Only look for evidence supporting your hypothesis | Actively seek disconfirming evidence. "What would prove me wrong?" |
| **Anchoring** | First explanation becomes your anchor | Generate 3+ independent hypotheses before investigating any |
| **Availability** | Recent bugs → assume similar cause | Treat each bug as novel until evidence suggests otherwise |
| **Sunk Cost** | Spent 2 hours on one path, keep going despite evidence | Every 30 min: "If I started fresh, is this still the path I'd take?" |
## Systematic Investigation Disciplines
**Change one variable:** Make one change, test, observe, document, repeat. Multiple changes = no idea what mattered.
**Complete reading:** Read entire functions, not just "relevant" lines. Read imports, config, tests. Skimming misses crucial details.
**Embrace not knowing:** "I don't know why this fails" = good (now you can investigate). "It must be X" = dangerous (you've stopped thinking).
## When to Restart
Consider starting over when:
1. **2+ hours with no progress** - You're likely tunnel-visioned
2. **3+ "fixes" that didn't work** - Your mental model is wrong
3. **You can't explain the current behavior** - Don't add changes on top of confusion
4. **You're debugging the debugger** - Something fundamental is wrong
5. **The fix works but you don't know why** - This isn't fixed, this is luck
**Restart protocol:**
1. Close all files and terminals
2. Write down what you know for certain
3. Write down what you've ruled out
4. List new hypotheses (different from before)
5. Begin again from Phase 1: Evidence Gathering
@~/.claude/get-shit-done/references/debugger-philosophy.md
</philosophy>

View File

@@ -18,8 +18,7 @@ Spawned by `/gsd-execute-phase` orchestrator.
Your job: Execute the plan completely, commit each task, create SUMMARY.md, update STATE.md.
**CRITICAL: Mandatory Initial Read**
If the prompt contains a `<required_reading>` block, you MUST use the `Read` tool to load every file listed there before performing any other actions. This is your primary context.
@~/.claude/get-shit-done/references/mandatory-initial-read.md
</role>
<documentation_lookup>
@@ -54,14 +53,9 @@ Before executing, discover project context:
**Project instructions:** Read `./CLAUDE.md` if it exists in the working directory. Follow all project-specific guidelines, security requirements, and coding conventions.
**Project skills:** Check `.claude/skills/` or `.agents/skills/` directory if either exists:
1. List available skills (subdirectories)
2. Read `SKILL.md` for each skill (lightweight index ~130 lines)
3. Load specific `rules/*.md` files as needed during implementation
4. Do NOT load full `AGENTS.md` files (100KB+ context cost)
5. Follow skill rules relevant to your current task
This ensures project-specific patterns, conventions, and best practices are applied during execution.
**Project skills:** @~/.claude/get-shit-done/references/project-skills-discovery.md
- Load `rules/*.md` as needed during **implementation**.
- Follow skill rules relevant to the task you are about to commit.
**CLAUDE.md enforcement:** If `./CLAUDE.md` exists, treat its directives as hard constraints during execution. Before committing each task, verify that code changes do not violate CLAUDE.md rules (forbidden patterns, required conventions, mandated tools). If a task action would contradict a CLAUDE.md directive, apply the CLAUDE.md rule — it takes precedence over plan instructions. Document any CLAUDE.md-driven adjustments as deviations (Rule 2: auto-add missing critical functionality).
</project_context>

View File

@@ -57,14 +57,23 @@ The /gsd-intel command has already confirmed that intel.enabled is true before s
## Project Scope
When analyzing this project, use ONLY canonical source locations:
**Runtime layout detection (do this first):** Check which runtime root exists by running:
```bash
ls -d .kilo 2>/dev/null && echo "kilo" || (ls -d .claude/get-shit-done 2>/dev/null && echo "claude") || echo "unknown"
```
- `agents/*.md` -- Agent instruction files
- `commands/gsd/*.md` -- Command files
- `get-shit-done/bin/` -- CLI tooling
- `get-shit-done/workflows/` -- Workflow files
- `get-shit-done/references/` -- Reference docs
- `hooks/*.js` -- Git hooks
Use the detected root to resolve all canonical paths below:
| Source type | Standard `.claude` layout | `.kilo` layout |
|-------------|--------------------------|----------------|
| Agent files | `agents/*.md` | `.kilo/agents/*.md` |
| Command files | `commands/gsd/*.md` | `.kilo/command/*.md` |
| CLI tooling | `get-shit-done/bin/` | `.kilo/get-shit-done/bin/` |
| Workflow files | `get-shit-done/workflows/` | `.kilo/get-shit-done/workflows/` |
| Reference docs | `get-shit-done/references/` | `.kilo/get-shit-done/references/` |
| Hook files | `hooks/*.js` | `.kilo/hooks/*.js` |
When analyzing this project, use ONLY the canonical source locations matching the detected layout. Do not fall back to the standard layout paths if the `.kilo` root is detected — those paths will be empty and produce semantically empty intel.
EXCLUDE from counts and analysis:
@@ -72,8 +81,8 @@ EXCLUDE from counts and analysis:
- `node_modules/`, `dist/`, `build/`, `.git/`
**Count accuracy:** When reporting component counts in stack.json or arch.md, always derive
counts by running Glob on canonical locations above, not from memory or CLAUDE.md.
Example: `Glob("agents/*.md")` for agent count.
counts by running Glob on the layout-resolved canonical locations above, not from memory or CLAUDE.md.
Example (standard layout): `Glob("agents/*.md")`. Example (kilo): `Glob(".kilo/agents/*.md")`.
## Forbidden Files

View File

@@ -16,8 +16,7 @@ You are a GSD phase researcher. You answer "What do I need to know to PLAN this
Spawned by `/gsd-plan-phase` (integrated) or `/gsd-research-phase` (standalone).
**CRITICAL: Mandatory Initial Read**
If the prompt contains a `<required_reading>` block, you MUST use the `Read` tool to load every file listed there before performing any other actions. This is your primary context.
@~/.claude/get-shit-done/references/mandatory-initial-read.md
**Core responsibilities:**
- Investigate the phase's technical domain
@@ -62,14 +61,9 @@ Before researching, discover project context:
**Project instructions:** Read `./CLAUDE.md` if it exists in the working directory. Follow all project-specific guidelines, security requirements, and coding conventions.
**Project skills:** Check `.claude/skills/` or `.agents/skills/` directory if either exists:
1. List available skills (subdirectories)
2. Read `SKILL.md` for each skill (lightweight index ~130 lines)
3. Load specific `rules/*.md` files as needed during research
4. Do NOT load full `AGENTS.md` files (100KB+ context cost)
5. Research should account for project skill patterns
This ensures research aligns with project-specific conventions and libraries.
**Project skills:** @~/.claude/get-shit-done/references/project-skills-discovery.md
- Load `rules/*.md` as needed during **research**.
- Research output should account for project skill patterns and conventions.
**CLAUDE.md enforcement:** If `./CLAUDE.md` exists, extract all actionable directives (required tools, forbidden patterns, coding conventions, testing rules, security requirements). Include a `## Project Constraints (from CLAUDE.md)` section in RESEARCH.md listing these directives so the planner can verify compliance. Treat CLAUDE.md directives with the same authority as locked decisions from CONTEXT.md — research should not recommend approaches that contradict them.
</project_context>

View File

@@ -22,8 +22,7 @@ Spawned by:
Your job: Produce PLAN.md files that Claude executors can implement without interpretation. Plans are prompts, not documents that become prompts.
**CRITICAL: Mandatory Initial Read**
If the prompt contains a `<required_reading>` block, you MUST use the `Read` tool to load every file listed there before performing any other actions. This is your primary context.
@~/.claude/get-shit-done/references/mandatory-initial-read.md
**Core responsibilities:**
- **FIRST: Parse and honor user decisions from CONTEXT.md** (locked decisions are NON-NEGOTIABLE)
@@ -36,13 +35,7 @@ If the prompt contains a `<required_reading>` block, you MUST use the `Read` too
</role>
<documentation_lookup>
For library docs: use Context7 MCP (`mcp__context7__*`) if available. If not (upstream
bug #13898 strips MCP from `tools:`-restricted agents), use the Bash CLI fallback:
```bash
npx --yes ctx7@latest library <name> "<query>" # resolve library ID
npx --yes ctx7@latest docs <libraryId> "<query>" # fetch docs
```
Do not skip — the CLI fallback works via Bash and produces equivalent output.
For library docs: use Context7 MCP (`mcp__context7__*`) if available; otherwise use the Bash CLI fallback (`npx --yes ctx7@latest library <name> "<query>"` then `npx --yes ctx7@latest docs <libraryId> "<query>"`). The CLI fallback works via Bash when MCP is unavailable.
</documentation_lookup>
<project_context>
@@ -50,14 +43,9 @@ Before planning, discover project context:
**Project instructions:** Read `./CLAUDE.md` if it exists in the working directory. Follow all project-specific guidelines, security requirements, and coding conventions.
**Project skills:** Check `.claude/skills/` or `.agents/skills/` directory if either exists:
1. List available skills (subdirectories)
2. Read `SKILL.md` for each skill (lightweight index ~130 lines)
3. Load specific `rules/*.md` files as needed during planning
4. Do NOT load full `AGENTS.md` files (100KB+ context cost)
5. Ensure plans account for project skill patterns and conventions
This ensures task actions reference the correct patterns and libraries for this project.
**Project skills:** @~/.claude/get-shit-done/references/project-skills-discovery.md
- Load `rules/*.md` as needed during **planning**.
- Ensure plans account for project skill patterns and conventions.
</project_context>
<context_fidelity>
@@ -67,18 +55,11 @@ The orchestrator provides user decisions in `<user_decisions>` tags from `/gsd-d
**Before creating ANY task, verify:**
1. **Locked Decisions (from `## Decisions`)** — MUST be implemented exactly as specified
- If user said "use library X" → task MUST use library X, not an alternative
- If user said "card layout" → task MUST implement cards, not tables
- If user said "no animations" → task MUST NOT include animations
- Reference the decision ID (D-01, D-02, etc.) in task actions for traceability
1. **Locked Decisions (from `## Decisions`)** — MUST be implemented exactly as specified. Reference the decision ID (D-01, D-02, etc.) in task actions for traceability.
2. **Deferred Ideas (from `## Deferred Ideas`)** — MUST NOT appear in plans
- If user deferred "search functionality" → NO search tasks allowed
- If user deferred "dark mode" → NO dark mode tasks allowed
2. **Deferred Ideas (from `## Deferred Ideas`)** — MUST NOT appear in plans.
3. **Claude's Discretion (from `## Claude's Discretion`)** — Use your judgment
- Make reasonable choices and document in task actions
3. **Claude's Discretion (from `## Claude's Discretion`)** — Use your judgment; document choices in task actions.
**Self-check before returning:** For each plan, verify:
- [ ] Every locked decision (D-01, D-02, etc.) has a task implementing it
@@ -115,7 +96,7 @@ Do NOT silently omit features. Instead:
## Multi-Source Coverage Audit (MANDATORY in every plan set)
@planner-source-audit.md for full format, examples, and gap-handling rules.
@~/.claude/get-shit-done/references/planner-source-audit.md for full format, examples, and gap-handling rules.
Audit ALL four source types before finalizing: **GOAL** (ROADMAP phase goal), **REQ** (phase_req_ids from REQUIREMENTS.md), **RESEARCH** (RESEARCH.md features/constraints), **CONTEXT** (D-XX decisions from CONTEXT.md).
@@ -127,7 +108,7 @@ Exclusions (not gaps): Deferred Ideas in CONTEXT.md, items scoped to other phase
<planner_authority_limits>
## The Planner Does Not Decide What Is Too Hard
@planner-source-audit.md for constraint examples.
@~/.claude/get-shit-done/references/planner-source-audit.md for constraint examples.
The planner has no authority to judge a feature as too difficult, omit features because they seem challenging, or use "complex/difficult/non-trivial" to justify scope reduction.
@@ -171,12 +152,7 @@ PLAN.md IS the prompt (not a document that becomes one). Contains:
Plan -> Execute -> Ship -> Learn -> Repeat
**Anti-enterprise patterns (delete if seen):**
- Team structures, RACI matrices, stakeholder management
- Sprint ceremonies, change management processes
- Time estimates in human units (see `<planner_authority_limits>`)
- Complexity/difficulty as scope justification (see `<planner_authority_limits>`)
- Documentation for documentation's sake
**Anti-enterprise patterns (delete if seen):** team structures, RACI matrices, sprint ceremonies, time estimates in human units, complexity/difficulty as scope justification, documentation for documentation's sake.
</philosophy>
@@ -1224,6 +1200,15 @@ Follow templates in checkpoints and revision_mode sections respectively.
</structured_returns>
<critical_rules>
- **No re-reads:** Never re-read a range already in context. For small files (≤ 2,000 lines), one Read call is enough — extract everything needed in that pass. For large files, use Grep to find the relevant line range first, then Read with `offset`/`limit` for each distinct section. Duplicate range reads are forbidden.
- **Codebase pattern reads (Level 1+):** Read each source file once. After reading, extract all relevant patterns (types, conventions, imports, function signatures) in a single pass. Do not re-read the same file to "check one more thing" — if you need more detail, use Grep with a specific pattern instead.
- **Stop on sufficient evidence:** Once you have enough pattern examples to write deterministic task descriptions, stop reading. There is no benefit to reading more analogs of the same pattern.
- **No heredoc writes:** Always use the Write or Edit tool, never `Bash(cat << 'EOF')`.
</critical_rules>
<success_criteria>
## Standard Mode

View File

@@ -277,6 +277,15 @@ Fix blocking issues in UI-SPEC.md and re-run `/gsd-ui-phase`.
</structured_returns>
<critical_rules>
- **No re-reads:** Once a file is loaded via `<required_reading>` or a manual Read call, it is in context — do not read it again. The UI-SPEC.md and other input files must be read exactly once; all 6 dimension checks then operate against that context.
- **Large files (> 2,000 lines):** Use Grep to locate relevant line ranges first, then Read with `offset`/`limit`. Never reload the whole file for a second dimension.
- **No source edits:** This agent is read-only. The only output is the structured return to the orchestrator.
- **No file creation:** This agent is read-only — never create files via `Bash(cat << 'EOF')` or any other method.
</critical_rules>
<success_criteria>
Verification is complete when:

View File

@@ -16,8 +16,7 @@ You are a GSD phase verifier. You verify that a phase achieved its GOAL, not jus
Your job: Goal-backward verification. Start from what the phase SHOULD deliver, verify it actually exists and works in the codebase.
**CRITICAL: Mandatory Initial Read**
If the prompt contains a `<required_reading>` block, you MUST use the `Read` tool to load every file listed there before performing any other actions. This is your primary context.
@~/.claude/get-shit-done/references/mandatory-initial-read.md
**Critical mindset:** Do NOT trust SUMMARY.md claims. SUMMARYs document what Claude SAID it did. You verify what ACTUALLY exists in the code. These often differ.
@@ -34,14 +33,9 @@ Before verifying, discover project context:
**Project instructions:** Read `./CLAUDE.md` if it exists in the working directory. Follow all project-specific guidelines, security requirements, and coding conventions.
**Project skills:** Check `.claude/skills/` or `.agents/skills/` directory if either exists:
1. List available skills (subdirectories)
2. Read `SKILL.md` for each skill (lightweight index ~130 lines)
3. Load specific `rules/*.md` files as needed during verification
4. Do NOT load full `AGENTS.md` files (100KB+ context cost)
5. Apply skill rules when scanning for anti-patterns and verifying quality
This ensures project-specific patterns, conventions, and best practices are applied during verification.
**Project skills:** @~/.claude/get-shit-done/references/project-skills-discovery.md
- Load `rules/*.md` as needed during **verification**.
- Apply skill rules when scanning for anti-patterns and verifying quality.
</project_context>
<core_principle>

View File

@@ -0,0 +1,31 @@
---
name: gsd:sketch-wrap-up
description: Package sketch design findings into a persistent project skill for future build conversations
allowed-tools:
- Read
- Write
- Edit
- Bash
- Grep
- Glob
- AskUserQuestion
---
<objective>
Curate sketch design findings and package them into a persistent project skill that Claude
auto-loads when building the real UI. Also writes a summary to `.planning/sketches/` for
project history. Output skill goes to `./.claude/skills/sketch-findings-[project]/` (project-local).
</objective>
<execution_context>
@~/.claude/get-shit-done/workflows/sketch-wrap-up.md
@~/.claude/get-shit-done/references/ui-brand.md
</execution_context>
<runtime_note>
**Copilot (VS Code):** Use `vscode_askquestions` wherever this workflow calls `AskUserQuestion`.
</runtime_note>
<process>
Execute the sketch-wrap-up workflow from @~/.claude/get-shit-done/workflows/sketch-wrap-up.md end-to-end.
Preserve all curation gates (per-sketch review, grouping approval, CLAUDE.md routing line).
</process>

45
commands/gsd/sketch.md Normal file
View File

@@ -0,0 +1,45 @@
---
name: gsd:sketch
description: Rapidly sketch UI/design ideas using throwaway HTML mockups with multi-variant exploration
argument-hint: "<design idea to explore> [--quick]"
allowed-tools:
- Read
- Write
- Edit
- Bash
- Grep
- Glob
- AskUserQuestion
---
<objective>
Explore design directions through throwaway HTML mockups before committing to implementation.
Each sketch produces 2-3 variants for comparison. Sketches live in `.planning/sketches/` and
integrate with GSD commit patterns, state tracking, and handoff workflows.
Does not require `/gsd-new-project` — auto-creates `.planning/sketches/` if needed.
</objective>
<execution_context>
@~/.claude/get-shit-done/workflows/sketch.md
@~/.claude/get-shit-done/references/ui-brand.md
@~/.claude/get-shit-done/references/sketch-theme-system.md
@~/.claude/get-shit-done/references/sketch-interactivity.md
@~/.claude/get-shit-done/references/sketch-tooling.md
@~/.claude/get-shit-done/references/sketch-variant-patterns.md
</execution_context>
<runtime_note>
**Copilot (VS Code):** Use `vscode_askquestions` wherever this workflow calls `AskUserQuestion`.
</runtime_note>
<context>
Design idea: $ARGUMENTS
**Available flags:**
- `--quick` — Skip mood/direction intake, jump straight to decomposition and building. Use when the design direction is already clear.
</context>
<process>
Execute the sketch workflow from @~/.claude/get-shit-done/workflows/sketch.md end-to-end.
Preserve all workflow gates (intake, decomposition, variant evaluation, MANIFEST updates, commit patterns).
</process>

View File

@@ -0,0 +1,31 @@
---
name: gsd:spike-wrap-up
description: Package spike findings into a persistent project skill for future build conversations
allowed-tools:
- Read
- Write
- Edit
- Bash
- Grep
- Glob
- AskUserQuestion
---
<objective>
Curate spike experiment findings and package them into a persistent project skill that Claude
auto-loads in future build conversations. Also writes a summary to `.planning/spikes/` for
project history. Output skill goes to `./.claude/skills/spike-findings-[project]/` (project-local).
</objective>
<execution_context>
@~/.claude/get-shit-done/workflows/spike-wrap-up.md
@~/.claude/get-shit-done/references/ui-brand.md
</execution_context>
<runtime_note>
**Copilot (VS Code):** Use `vscode_askquestions` wherever this workflow calls `AskUserQuestion`.
</runtime_note>
<process>
Execute the spike-wrap-up workflow from @~/.claude/get-shit-done/workflows/spike-wrap-up.md end-to-end.
Preserve all curation gates (per-spike review, grouping approval, CLAUDE.md routing line).
</process>

41
commands/gsd/spike.md Normal file
View File

@@ -0,0 +1,41 @@
---
name: gsd:spike
description: Rapidly spike an idea with throwaway experiments to validate feasibility before planning
argument-hint: "<idea to validate> [--quick]"
allowed-tools:
- Read
- Write
- Edit
- Bash
- Grep
- Glob
- AskUserQuestion
---
<objective>
Rapid feasibility validation through focused, throwaway experiments. Each spike answers one
specific question with observable evidence. Spikes live in `.planning/spikes/` and integrate
with GSD commit patterns, state tracking, and handoff workflows.
Does not require `/gsd-new-project` — auto-creates `.planning/spikes/` if needed.
</objective>
<execution_context>
@~/.claude/get-shit-done/workflows/spike.md
@~/.claude/get-shit-done/references/ui-brand.md
</execution_context>
<runtime_note>
**Copilot (VS Code):** Use `vscode_askquestions` wherever this workflow calls `AskUserQuestion`.
</runtime_note>
<context>
Idea: $ARGUMENTS
**Available flags:**
- `--quick` — Skip decomposition/alignment, jump straight to building. Use when you already know what to spike.
</context>
<process>
Execute the spike workflow from @~/.claude/get-shit-done/workflows/spike.md end-to-end.
Preserve all workflow gates (decomposition, risk ordering, verification, MANIFEST updates, commit patterns).
</process>

View File

@@ -113,7 +113,7 @@ User-facing entry points. Each file contains YAML frontmatter (name, description
- **Copilot:** Slash commands (`/gsd-command-name`)
- **Antigravity:** Skills
**Total commands:** 75
**Total commands:** 79
### Workflows (`get-shit-done/workflows/*.md`)
@@ -124,7 +124,7 @@ Orchestration logic that commands reference. Contains the step-by-step process i
- State update patterns
- Error handling and recovery
**Total workflows:** 72
**Total workflows:** 76
### Agents (`agents/*.md`)
@@ -409,7 +409,7 @@ UI-SPEC.md (per phase) ───────────────────
```
~/.claude/ # Claude Code (global install)
├── commands/gsd/*.md # 75 slash commands
├── commands/gsd/*.md # 79 slash commands
├── get-shit-done/
│ ├── bin/gsd-tools.cjs # CLI utility
│ ├── bin/lib/*.cjs # 19 domain modules

View File

@@ -609,6 +609,98 @@ function resolveWorktreeRoot(cwd) {
return cwd;
}
/**
* Parse `git worktree list --porcelain` output into an array of
* { path, branch } objects. Entries with a detached HEAD (no branch line)
* are skipped because we cannot safely reason about their merge status.
*
* @param {string} porcelain - raw output from git worktree list --porcelain
* @returns {{ path: string, branch: string }[]}
*/
function parseWorktreePorcelain(porcelain) {
const entries = [];
let current = null;
for (const line of porcelain.split('\n')) {
if (line.startsWith('worktree ')) {
current = { path: line.slice('worktree '.length).trim(), branch: null };
} else if (line.startsWith('branch refs/heads/') && current) {
current.branch = line.slice('branch refs/heads/'.length).trim();
} else if (line === '' && current) {
if (current.branch) entries.push(current);
current = null;
}
}
// flush last entry if file doesn't end with blank line
if (current && current.branch) entries.push(current);
return entries;
}
/**
* Remove linked git worktrees whose branch has already been merged into the
* current HEAD of the main worktree. Also runs `git worktree prune` to clear
* any stale references left by manually-deleted worktree directories.
*
* Safe guards:
* - Never removes the main worktree (first entry in --porcelain output).
* - Never removes the worktree at process.cwd().
* - Never removes a worktree whose branch has unmerged commits.
* - Skips detached-HEAD worktrees (no branch name).
*
* @param {string} repoRoot - absolute path to the main (or any) worktree of
* the repository; used as `cwd` for git commands.
* @returns {string[]} list of worktree paths that were removed
*/
function pruneOrphanedWorktrees(repoRoot) {
const pruned = [];
const cwd = process.cwd();
try {
// 1. Get all worktrees in porcelain format
const listResult = execGit(repoRoot, ['worktree', 'list', '--porcelain']);
if (listResult.exitCode !== 0) return pruned;
const worktrees = parseWorktreePorcelain(listResult.stdout);
if (worktrees.length === 0) {
execGit(repoRoot, ['worktree', 'prune']);
return pruned;
}
// 2. First entry is the main worktree — never touch it
const mainWorktreePath = worktrees[0].path;
// 3. Check each non-main worktree
for (let i = 1; i < worktrees.length; i++) {
const { path: wtPath, branch } = worktrees[i];
// Never remove the worktree for the current process directory
if (wtPath === cwd || cwd.startsWith(wtPath + path.sep)) continue;
// Check if the branch is fully merged into HEAD (main)
// git merge-base --is-ancestor <branch> HEAD exits 0 when merged
const ancestorCheck = execGit(repoRoot, [
'merge-base', '--is-ancestor', branch, 'HEAD',
]);
if (ancestorCheck.exitCode !== 0) {
// Not yet merged — leave it alone
continue;
}
// Remove the worktree and delete the branch
const removeResult = execGit(repoRoot, ['worktree', 'remove', '--force', wtPath]);
if (removeResult.exitCode === 0) {
execGit(repoRoot, ['branch', '-D', branch]);
pruned.push(wtPath);
}
}
} catch { /* never crash the caller */ }
// 4. Always run prune to clear stale references (e.g. manually-deleted dirs)
execGit(repoRoot, ['worktree', 'prune']);
return pruned;
}
/**
* Acquire a file-based lock for .planning/ writes.
* Prevents concurrent worktrees from corrupting shared planning files.
@@ -1637,4 +1729,5 @@ module.exports = {
checkAgentsInstalled,
atomicWriteFileSync,
timeAgo,
pruneOrphanedWorktrees,
};

View File

@@ -1080,15 +1080,10 @@ function cmdInitManager(cwd, raw) {
: '—';
}
// Sliding window: discuss is sequential — only the first undiscussed phase is available
let foundNextToDiscuss = false;
for (const phase of phases) {
if (!foundNextToDiscuss && (phase.disk_status === 'empty' || phase.disk_status === 'no_directory')) {
phase.is_next_to_discuss = true;
foundNextToDiscuss = true;
} else {
phase.is_next_to_discuss = false;
}
phase.is_next_to_discuss =
(phase.disk_status === 'empty' || phase.disk_status === 'no_directory') &&
phase.deps_satisfied;
}
// Check for WAITING.json signal
@@ -1216,6 +1211,10 @@ function cmdInitManager(cwd, raw) {
}
function cmdInitProgress(cwd, raw) {
try {
const { pruneOrphanedWorktrees } = require('./core.cjs');
pruneOrphanedWorktrees(cwd);
} catch (_) {}
const config = loadConfig(cwd);
const milestone = getMilestoneInfo(cwd);

View File

@@ -72,6 +72,24 @@ reads is inert — the consumption mechanism is what gives an artifact meaning.
- **Location**: `.planning/spikes/SPIKE-NNN/`
- **Consumed by**: Planner when spike is referenced; `pause-work` for spike context handoff
### Spike README.md / MANIFEST.md (per-spike, via /gsd-spike)
- **Shape**: YAML frontmatter (spike, name, validates, verdict, related, tags) + run instructions + results
- **Lifecycle**: Created by `/gsd-spike` → Verified → Wrapped up by `/gsd-spike-wrap-up`
- **Location**: `.planning/spikes/NNN-name/README.md`, `.planning/spikes/MANIFEST.md`
- **Consumed by**: `/gsd-spike-wrap-up` for curation; `pause-work` for spike context handoff
### Sketch README.md / MANIFEST.md / index.html (per-sketch)
- **Shape**: YAML frontmatter (sketch, name, question, winner, tags) + variants as tabbed HTML
- **Lifecycle**: Created by `/gsd-sketch` → Evaluated → Wrapped up by `/gsd-sketch-wrap-up`
- **Location**: `.planning/sketches/NNN-name/README.md`, `.planning/sketches/NNN-name/index.html`, `.planning/sketches/MANIFEST.md`
- **Consumed by**: `/gsd-sketch-wrap-up` for curation; `pause-work` for sketch context handoff
### WRAP-UP-SUMMARY.md (per wrap-up session)
- **Shape**: Curation results, included/excluded items, feature/design area groupings
- **Lifecycle**: Created by `/gsd-spike-wrap-up` or `/gsd-sketch-wrap-up`
- **Location**: `.planning/spikes/WRAP-UP-SUMMARY.md` or `.planning/sketches/WRAP-UP-SUMMARY.md`
- **Consumed by**: Project history; not read by automated workflows
---
## Standing Reference Artifacts

View File

@@ -0,0 +1,76 @@
# Debugger Philosophy
Evergreen debugging disciplines — applies across every bug, every language, every system. Loaded by `gsd-debugger` via `@file` include.
## User = Reporter, Claude = Investigator
The user knows:
- What they expected to happen
- What actually happened
- Error messages they saw
- When it started / if it ever worked
The user does NOT know (don't ask):
- What's causing the bug
- Which file has the problem
- What the fix should be
Ask about experience. Investigate the cause yourself.
## Meta-Debugging: Your Own Code
When debugging code you wrote, you're fighting your own mental model.
**Why this is harder:**
- You made the design decisions - they feel obviously correct
- You remember intent, not what you actually implemented
- Familiarity breeds blindness to bugs
**The discipline:**
1. **Treat your code as foreign** - Read it as if someone else wrote it
2. **Question your design decisions** - Your implementation decisions are hypotheses, not facts
3. **Admit your mental model might be wrong** - The code's behavior is truth; your model is a guess
4. **Prioritize code you touched** - If you modified 100 lines and something breaks, those are prime suspects
**The hardest admission:** "I implemented this wrong." Not "requirements were unclear" - YOU made an error.
## Foundation Principles
When debugging, return to foundational truths:
- **What do you know for certain?** Observable facts, not assumptions
- **What are you assuming?** "This library should work this way" - have you verified?
- **Strip away everything you think you know.** Build understanding from observable facts.
## Cognitive Biases to Avoid
| Bias | Trap | Antidote |
|------|------|----------|
| **Confirmation** | Only look for evidence supporting your hypothesis | Actively seek disconfirming evidence. "What would prove me wrong?" |
| **Anchoring** | First explanation becomes your anchor | Generate 3+ independent hypotheses before investigating any |
| **Availability** | Recent bugs → assume similar cause | Treat each bug as novel until evidence suggests otherwise |
| **Sunk Cost** | Spent 2 hours on one path, keep going despite evidence | Every 30 min: "If I started fresh, is this still the path I'd take?" |
## Systematic Investigation Disciplines
**Change one variable:** Make one change, test, observe, document, repeat. Multiple changes = no idea what mattered.
**Complete reading:** Read entire functions, not just "relevant" lines. Read imports, config, tests. Skimming misses crucial details.
**Embrace not knowing:** "I don't know why this fails" = good (now you can investigate). "It must be X" = dangerous (you've stopped thinking).
## When to Restart
Consider starting over when:
1. **2+ hours with no progress** - You're likely tunnel-visioned
2. **3+ "fixes" that didn't work** - Your mental model is wrong
3. **You can't explain the current behavior** - Don't add changes on top of confusion
4. **You're debugging the debugger** - Something fundamental is wrong
5. **The fix works but you don't know why** - This isn't fixed, this is luck
**Restart protocol:**
1. Close all files and terminals
2. Write down what you know for certain
3. Write down what you've ruled out
4. List new hypotheses (different from before)
5. Begin again from Phase 1: Evidence Gathering

View File

@@ -0,0 +1,2 @@
**CRITICAL: Mandatory Initial Read**
If the prompt contains a `<required_reading>` block, you MUST use the `Read` tool to load every file listed there before performing any other actions. This is your primary context.

View File

@@ -0,0 +1,19 @@
# Project Skills Discovery
Before execution, check for project-defined skills and apply their rules.
**Discovery steps (shared across all GSD agents):**
1. Check `.claude/skills/` or `.agents/skills/` directory — if neither exists, skip.
2. List available skills (subdirectories).
3. Read `SKILL.md` for each skill (lightweight index, typically ~130 lines).
4. Load specific `rules/*.md` files only as needed during the current task.
5. Do NOT load full `AGENTS.md` files — they are large (100KB+) and cost significant context.
**Application** — how to apply the loaded rules depends on the calling agent:
- Planners account for project skill patterns and conventions in the plan.
- Executors follow skill rules relevant to the task being implemented.
- Researchers ensure research output accounts for project skill patterns.
- Verifiers apply skill rules when scanning for anti-patterns and verifying quality.
- Debuggers follow skill rules relevant to the bug being investigated and the fix being applied.
The caller's agent file should specify which application applies.

View File

@@ -0,0 +1,41 @@
# Making Sketches Feel Alive
Static mockups are barely better than screenshots. Every interactive element in a sketch must respond to interaction.
## Required Interactivity
| Element | Must Have |
|---------|-----------|
| Buttons | Click handler with visible feedback (state change, animation, toast) |
| Forms | Input validation on blur, submit handler that shows success state |
| Lists | Add/remove items, empty state, populated state |
| Toggles/switches | Working toggle with visible state change |
| Tabs/nav | Click to switch content |
| Modals/drawers | Open/close with transition |
| Hover states | Every clickable element needs a hover effect |
| Dropdowns | Open/close, item selection |
## Transitions
Add `transition: all 0.15s ease` as a baseline to interactive elements. Subtle motion makes the sketch feel real and helps judge whether the interaction pattern works.
## Fake the Backend
If the sketch shows a "Save" button, clicking it should show a brief loading state then a success message. If it shows a search bar, typing should filter hardcoded results. The goal is to feel the full interaction loop, not just see the resting state.
## State Cycling
If the sketch has multiple states (empty, loading, populated, error), include buttons to cycle through them. Label each state clearly. This lets the user experience how the design handles different data conditions.
## Implementation
Use vanilla JS in inline `<script>` tags. No frameworks, no build step. Keep it simple:
```html
<script>
// Toggle a panel
document.querySelector('.panel-toggle').addEventListener('click', (e) => {
e.target.closest('.panel').classList.toggle('collapsed');
});
</script>
```

View File

@@ -0,0 +1,94 @@
# Shared Theme System
All sketches share a CSS variable theme so design decisions compound across sketches.
## Setup
On the first sketch, create `.planning/sketches/themes/` with a default theme:
```
.planning/sketches/
themes/
default.css <- all sketches link to this
001-dashboard-layout/
index.html <- links to ../themes/default.css
```
## Theme File Structure
Each theme defines CSS custom properties only — no component styles, no layout rules. Just the visual vocabulary:
```css
:root {
/* Colors */
--color-bg: #fafafa;
--color-surface: #ffffff;
--color-border: #e5e5e5;
--color-text: #1a1a1a;
--color-text-muted: #6b6b6b;
--color-primary: #2563eb;
--color-primary-hover: #1d4ed8;
--color-accent: #f59e0b;
--color-danger: #ef4444;
--color-success: #22c55e;
/* Typography */
--font-sans: 'Inter', system-ui, sans-serif;
--font-mono: 'JetBrains Mono', monospace;
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.25rem;
--text-2xl: 1.5rem;
--text-3xl: 1.875rem;
/* Spacing */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-6: 24px;
--space-8: 32px;
--space-12: 48px;
/* Shapes */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-full: 9999px;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
--shadow-md: 0 4px 6px rgba(0,0,0,0.07);
--shadow-lg: 0 10px 15px rgba(0,0,0,0.1);
}
```
Adapt the default theme to match the mood/direction established during intake. The values above are a starting point — change colors, fonts, spacing, and shapes to match the agreed aesthetic.
## Linking
Every sketch links to the theme:
```html
<link rel="stylesheet" href="../themes/default.css">
```
## Creating New Themes
When a sketch reveals an aesthetic fork ("should this feel clinical or warm?"), create both as theme files rather than arguing about it. The user can switch and feel the difference.
Name themes descriptively: `midnight.css`, `warm-minimal.css`, `brutalist.css`.
## Theme Switcher
Include in every sketch (part of the sketch toolbar):
```html
<select id="theme-switcher" onchange="document.querySelector('link[href*=themes]').href='../themes/'+this.value+'.css'">
<option value="default">Default</option>
</select>
```
Dynamically populate options by listing available theme files, or hardcode the known themes.

View File

@@ -0,0 +1,45 @@
# Sketch Toolbar
Include a small floating toolbar in every sketch. It provides utilities without competing with the actual design.
## Implementation
A small `<div>` fixed to the bottom-right, semi-transparent, expands on hover:
```html
<div id="sketch-tools" style="position:fixed;bottom:12px;right:12px;z-index:9999;font-family:system-ui;font-size:12px;background:rgba(0,0,0,0.7);color:white;padding:8px 12px;border-radius:8px;opacity:0.4;transition:opacity 0.2s;" onmouseenter="this.style.opacity='1'" onmouseleave="this.style.opacity='0.4'">
<!-- Theme switcher -->
<!-- Viewport buttons -->
<!-- Annotation toggle -->
</div>
```
## Components
### Theme Switcher
A dropdown that swaps the theme CSS file at runtime:
```html
<select onchange="document.querySelector('link[href*=themes]').href='../themes/'+this.value+'.css'">
<option value="default">Default</option>
</select>
```
### Viewport Preview
Three buttons that constrain the sketch content area to standard widths:
- Phone: 375px
- Tablet: 768px
- Desktop: 1280px (or full width)
Implemented by wrapping sketch content in a container and adjusting its `max-width`.
### Annotation Mode
A toggle that overlays spacing values, color hex codes, and font sizes on hover. Implemented as a JS snippet that reads computed styles and shows them in a tooltip. Helps understand visual decisions without opening dev tools.
## Styling
The toolbar should be unobtrusive — small, dark, semi-transparent. It should never compete with the sketch visually. Style it independently of the theme (hardcoded dark background, white text).

View File

@@ -0,0 +1,81 @@
# Multi-Variant HTML Patterns
Every sketch produces 2-3 variants in the same HTML file. The user switches between them to compare.
## Tab-Based Variants
The standard approach: a tab bar at the top of the page, each tab shows a different variant.
```html
<div id="variant-nav" style="position:fixed;top:0;left:0;right:0;z-index:9998;background:var(--color-surface, #fff);border-bottom:1px solid var(--color-border, #e5e5e5);padding:8px 16px;display:flex;gap:8px;font-family:system-ui;">
<button class="variant-tab active" onclick="showVariant('a')">A: Sidebar Layout</button>
<button class="variant-tab" onclick="showVariant('b')">B: Top Nav</button>
<button class="variant-tab" onclick="showVariant('c')">C: Floating Panels</button>
</div>
<div id="variant-a" class="variant active">
<!-- Variant A content -->
</div>
<div id="variant-b" class="variant" style="display:none">
<!-- Variant B content -->
</div>
<div id="variant-c" class="variant" style="display:none">
<!-- Variant C content -->
</div>
<script>
function showVariant(id) {
document.querySelectorAll('.variant').forEach(v => v.style.display = 'none');
document.querySelectorAll('.variant-tab').forEach(t => t.classList.remove('active'));
document.getElementById('variant-' + id).style.display = 'block';
event.target.classList.add('active');
}
</script>
```
Add `padding-top` to the body to account for the fixed tab bar.
## Marking the Winner
After the user picks a direction, add a visual indicator to the winning tab:
```html
<button class="variant-tab active">A: Sidebar Layout ★ Selected</button>
```
Keep all variants visible and navigable — the winner is highlighted, not the only option.
## Side-by-Side (for small variants)
When comparing small elements (button styles, card layouts, icon treatments), render them next to each other with labels rather than using tabs:
```html
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:24px;padding:24px;">
<div>
<h3>A: Rounded</h3>
<!-- variant content -->
</div>
<div>
<h3>B: Sharp</h3>
<!-- variant content -->
</div>
<div>
<h3>C: Pill</h3>
<!-- variant content -->
</div>
</div>
```
## Variant Count
- **First round (dramatic):** 2-3 meaningfully different approaches
- **Refinement rounds:** 2-3 subtle variations within the chosen direction
- **Never more than 4** — more than that overwhelms. If there are 5+ options, narrow before showing.
## Synthesis Variants
When the user cherry-picks elements across variants, create a new variant tab labeled descriptively:
```html
<button class="variant-tab" onclick="showVariant('synth1')">Synthesis: A's layout + C's palette</button>
```

View File

@@ -348,9 +348,40 @@ Structure the extracted information:
</prior_decisions>
```
**Step 4: Load spike/sketch findings (if they exist)**
```bash
# Check for spike/sketch findings skills (project-local)
SPIKE_FINDINGS=$(ls ./.claude/skills/spike-findings-*/SKILL.md 2>/dev/null | head -1)
SKETCH_FINDINGS=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/null | head -1)
# Also check for raw spikes/sketches not yet wrapped up
RAW_SPIKES=$(ls .planning/spikes/MANIFEST.md 2>/dev/null)
RAW_SKETCHES=$(ls .planning/sketches/MANIFEST.md 2>/dev/null)
```
If spike/sketch findings skills exist, read their SKILL.md and reference files. Extract:
- **Validated patterns** — what was proven to work (use these, don't re-explore)
- **Landmines** — what was proven NOT to work (avoid these)
- **Constraints** — hard limits discovered (rate limits, API gaps, library limitations)
- **Design decisions** — winning visual directions, CSS patterns, layout choices
Add to `<prior_decisions>`:
```
## From Spike Experiments
- [Validated pattern or constraint from spike findings]
## From Design Sketches
- [Design decision or visual direction from sketch findings]
```
If raw spikes/sketches exist but no findings skill, note in output:
```
⚠ Unpackaged spikes/sketches detected — run `/gsd-spike-wrap-up` or `/gsd-sketch-wrap-up` to make findings available to planning agents.
```
**Usage in subsequent steps:**
- `analyze_phase`: Skip gray areas already decided in prior phases
- `present_gray_areas`: Annotate options with prior decisions ("You chose X in Phase 5")
- `analyze_phase`: Skip gray areas already decided in prior phases or validated by spikes/sketches
- `present_gray_areas`: Annotate options with prior decisions ("You chose X in Phase 5") and spike/sketch findings ("Spike 002 validated this approach")
- `discuss_areas`: Pre-fill answers or flag conflicts ("This contradicts Phase 3 — same here or different?")
**If no prior context exists:** Continue without — this is expected for early phases.

View File

@@ -42,6 +42,10 @@ Evaluate `$ARGUMENTS` against these routing rules. Apply the **first matching**
| Starting a new project, "set up", "initialize" | `/gsd-new-project` | Needs full project initialization |
| Mapping or analyzing an existing codebase | `/gsd-map-codebase` | Codebase discovery |
| A bug, error, crash, failure, or something broken | `/gsd-debug` | Needs systematic investigation |
| Spiking, "test if", "will this work", "experiment", "prove this out", validate feasibility | `/gsd-spike` | Throwaway experiment to validate feasibility |
| Sketching, "mockup", "what would this look like", "prototype the UI", "design this", explore visual direction | `/gsd-sketch` | Throwaway HTML mockups to explore design |
| Wrapping up spikes, "package the spikes", "consolidate spike findings" | `/gsd-spike-wrap-up` | Package spike findings into reusable skill |
| Wrapping up sketches, "package the designs", "consolidate sketch findings" | `/gsd-sketch-wrap-up` | Package sketch findings into reusable skill |
| Exploring, researching, comparing, or "how does X work" | `/gsd-research-phase` | Domain research before planning |
| Discussing vision, "how should X look", brainstorming | `/gsd-discuss-phase` | Needs context gathering |
| A complex task: refactoring, migration, multi-file architecture, system redesign | `/gsd-add-phase` | Needs a full phase with plan/build cycle |
@@ -56,7 +60,7 @@ Evaluate `$ARGUMENTS` against these routing rules. Apply the **first matching**
| Completing a milestone, shipping, releasing | `/gsd-complete-milestone` | Milestone lifecycle |
| A specific, actionable, small task (add feature, fix typo, update config) | `/gsd-quick` | Self-contained, single executor |
**Requires `.planning/` directory:** All routes except `/gsd-new-project`, `/gsd-map-codebase`, `/gsd-help`, and `/gsd-join-discord`. If the project doesn't exist and the route requires it, suggest `/gsd-new-project` first.
**Requires `.planning/` directory:** All routes except `/gsd-new-project`, `/gsd-map-codebase`, `/gsd-spike`, `/gsd-sketch`, `/gsd-help`, and `/gsd-join-discord`. If the project doesn't exist and the route requires it, suggest `/gsd-new-project` first.
**Ambiguity handling:** If the text could reasonably match multiple routes, ask the user via AskUserQuestion with the top 2-3 options. For example:

View File

@@ -82,6 +82,8 @@ When the conversation reaches natural conclusions or the developer signals readi
| Research question | `.planning/research/questions.md` (append) | Open questions that need deeper investigation |
| Requirement | `REQUIREMENTS.md` (append) | Clear requirements that emerged from discussion |
| New phase | `ROADMAP.md` (append) | Scope large enough to warrant its own phase |
| Spike | `/gsd-spike` (invoke) | Feasibility uncertainty surfaced — "will this API work?", "can we do X?" |
| Sketch | `/gsd-sketch` (invoke) | Design direction unclear — "what should this look like?", "how should this feel?" |
Present suggestions:
```

View File

@@ -276,6 +276,57 @@ Systematic debugging with persistent state across context resets.
Usage: `/gsd-debug "login button doesn't work"`
Usage: `/gsd-debug` (resume active session)
### Spiking & Sketching
**`/gsd-spike [idea] [--quick]`**
Rapidly spike an idea with throwaway experiments to validate feasibility.
- Decomposes idea into 2-5 focused experiments (risk-ordered)
- Each spike answers one specific Given/When/Then question
- Builds minimum code, runs it, captures verdict (VALIDATED/INVALIDATED/PARTIAL)
- Saves to `.planning/spikes/` with MANIFEST.md tracking
- Does not require `/gsd-new-project` — works in any repo
- `--quick` skips decomposition, builds immediately
Usage: `/gsd-spike "can we stream LLM output over WebSockets?"`
Usage: `/gsd-spike --quick "test if pdfjs extracts tables"`
**`/gsd-sketch [idea] [--quick]`**
Rapidly sketch UI/design ideas using throwaway HTML mockups with multi-variant exploration.
- Conversational mood/direction intake before building
- Each sketch produces 2-3 variants as tabbed HTML pages
- User compares variants, cherry-picks elements, iterates
- Shared CSS theme system compounds across sketches
- Saves to `.planning/sketches/` with MANIFEST.md tracking
- Does not require `/gsd-new-project` — works in any repo
- `--quick` skips mood intake, jumps to building
Usage: `/gsd-sketch "dashboard layout for the admin panel"`
Usage: `/gsd-sketch --quick "form card grouping"`
**`/gsd-spike-wrap-up`**
Package spike findings into a persistent project skill.
- Curates each spike one-at-a-time (include/exclude/partial/UAT)
- Groups findings by feature area
- Generates `./.claude/skills/spike-findings-[project]/` with references and sources
- Writes summary to `.planning/spikes/WRAP-UP-SUMMARY.md`
- Adds auto-load routing line to project CLAUDE.md
Usage: `/gsd-spike-wrap-up`
**`/gsd-sketch-wrap-up`**
Package sketch design findings into a persistent project skill.
- Curates each sketch one-at-a-time (include/exclude/partial/revisit)
- Groups findings by design area
- Generates `./.claude/skills/sketch-findings-[project]/` with design decisions, CSS patterns, HTML structures
- Writes summary to `.planning/sketches/WRAP-UP-SUMMARY.md`
- Adds auto-load routing line to project CLAUDE.md
Usage: `/gsd-sketch-wrap-up`
### Quick Notes
**`/gsd-note <text>`**
@@ -478,6 +529,13 @@ Usage: `/gsd-join-discord`
├── todos/ # Captured ideas and tasks
│ ├── pending/ # Todos waiting to be worked on
│ └── done/ # Completed todos
├── spikes/ # Spike experiments (/gsd-spike)
│ ├── MANIFEST.md # Spike inventory and verdicts
│ └── NNN-name/ # Individual spike directories
├── sketches/ # Design sketches (/gsd-sketch)
│ ├── MANIFEST.md # Sketch inventory and winners
│ ├── themes/ # Shared CSS theme files
│ └── NNN-name/ # Individual sketch directories (HTML + README)
├── debug/ # Active debug sessions
│ └── resolved/ # Archived resolved issues
├── milestones/

View File

@@ -233,6 +233,36 @@ gsd-sdk query config-set workflow._auto_chain_active true
Proceed to Step 4 (skip Steps 3 and 5).
## 2b. Prior Spike/Sketch Detection
Check for existing spike and sketch work that should inform project setup:
```bash
# Check for spike findings skill (project-local)
SPIKE_SKILL=$(ls ./.claude/skills/spike-findings-*/SKILL.md 2>/dev/null | head -1)
# Check for sketch findings skill (project-local)
SKETCH_SKILL=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/null | head -1)
# Check for raw spikes/sketches in .planning/
HAS_SPIKES=$(ls .planning/spikes/MANIFEST.md 2>/dev/null)
HAS_SKETCHES=$(ls .planning/sketches/MANIFEST.md 2>/dev/null)
```
If any of these exist, surface them before questioning:
```
⚡ Prior exploration detected:
{if SPIKE_SKILL} ✓ Spike findings skill: {path} — validated patterns from experiments
{if SKETCH_SKILL} ✓ Sketch findings skill: {path} — validated design decisions
{if HAS_SPIKES && !SPIKE_SKILL} ◆ Raw spikes in .planning/spikes/ — consider `/gsd-spike-wrap-up` to package findings
{if HAS_SKETCHES && !SKETCH_SKILL} ◆ Raw sketches in .planning/sketches/ — consider `/gsd-sketch-wrap-up` to package findings
These findings will be incorporated into project context and available to planning agents.
```
If spike/sketch findings skills exist, read their SKILL.md files to inform the questioning phase — they contain validated patterns, constraints, and design decisions that should shape the project definition.
## 3. Deep Questioning
**If auto mode:** Skip (already handled in Step 2a). Extract project context from provided document instead and proceed to Step 4.

View File

@@ -134,6 +134,29 @@ gsd-sdk query commit "docs: defer incomplete Phase {src} items to backlog"
**If the user chooses "Force" (F):** Continue to `determine_next_action` without recording deferral.
</step>
<step name="spike_sketch_notice">
Check for pending spike/sketch work and surface a notice (does not change routing):
```bash
# Check for pending spikes (verdict: PENDING in any README)
PENDING_SPIKES=$(grep -rl 'verdict: PENDING' .planning/spikes/*/README.md 2>/dev/null | wc -l | tr -d ' ')
# Check for pending sketches (winner: null in any README)
PENDING_SKETCHES=$(grep -rl 'winner: null' .planning/sketches/*/README.md 2>/dev/null | wc -l | tr -d ' ')
```
If either count is > 0, display before routing:
```
⚠ Pending exploratory work:
{PENDING_SPIKES} spike(s) with unresolved verdicts in .planning/spikes/
{PENDING_SKETCHES} sketch(es) without a winning variant in .planning/sketches/
Resume with `/gsd-spike` or `/gsd-sketch`, or continue with phase work below.
```
Only show lines for non-zero counts. If both are 0, skip this notice entirely.
</step>
<step name="determine_next_action">
Apply routing rules based on state:

View File

@@ -18,7 +18,10 @@ Determine what kind of work is being paused and set the handoff destination acco
phase=$(( ls -lt .planning/phases/*/PLAN.md 2>/dev/null || true ) | head -1 | grep -oP 'phases/\K[^/]+' || true)
# Check for active spike
spike=$(( ls -lt .planning/spikes/*/SPIKE.md .planning/spikes/*/DESIGN.md 2>/dev/null || true ) | head -1 | grep -oP 'spikes/\K[^/]+' || true)
spike=$(( ls -lt .planning/spikes/*/SPIKE.md .planning/spikes/*/DESIGN.md .planning/spikes/*/README.md 2>/dev/null || true ) | head -1 | grep -oP 'spikes/\K[^/]+' || true)
# Check for active sketch
sketch=$(( ls -lt .planning/sketches/*/README.md .planning/sketches/*/index.html 2>/dev/null || true ) | head -1 | grep -oP 'sketches/\K[^/]+' || true)
# Check for active deliberation
deliberation=$(ls .planning/deliberations/*.md 2>/dev/null | head -1 || true)
@@ -26,8 +29,9 @@ deliberation=$(ls .planning/deliberations/*.md 2>/dev/null | head -1 || true)
- **Phase work**: active phase directory → handoff to `.planning/phases/XX-name/.continue-here.md`
- **Spike work**: active spike directory or spike-related files (no active phase) → handoff to `.planning/spikes/SPIKE-NNN/.continue-here.md` (create directory if needed)
- **Deliberation work**: active deliberation file (no phase/spike) → handoff to `.planning/deliberations/.continue-here.md`
- **Research work**: research notes exist but no phase/spike/deliberation → handoff to `.planning/.continue-here.md`
- **Sketch work**: active sketch directory (no active phase/spike) → handoff to `.planning/sketches/.continue-here.md`
- **Deliberation work**: active deliberation file (no phase/spike/sketch) → handoff to `.planning/deliberations/.continue-here.md`
- **Research work**: research notes exist but no phase/spike/sketch/deliberation → handoff to `.planning/.continue-here.md`
- **Default**: no detectable context → handoff to `.planning/.continue-here.md`, note the ambiguity in `<current_state>`
If phase is detected, proceed with phase handoff path. Otherwise use the first matching non-phase path above.
@@ -106,7 +110,7 @@ timestamp=$(gsd-sdk query current-timestamp full --raw)
```markdown
---
context: [phase|spike|deliberation|research|default]
context: [phase|spike|sketch|deliberation|research|default]
phase: XX-name
task: 3
total_tasks: 7

View File

@@ -593,6 +593,10 @@ UAT_PATH=$(_gsd_field "$INIT" uat_path)
CONTEXT_PATH=$(_gsd_field "$INIT" context_path)
REVIEWS_PATH=$(_gsd_field "$INIT" reviews_path)
PATTERNS_PATH=$(_gsd_field "$INIT" patterns_path)
# Detect spike/sketch findings skills (project-local)
SPIKE_FINDINGS_PATH=$(ls ./.claude/skills/spike-findings-*/SKILL.md 2>/dev/null | head -1)
SKETCH_FINDINGS_PATH=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/null | head -1)
```
## 7.5. Verify Nyquist Artifacts
@@ -706,6 +710,8 @@ Planner prompt:
- {uat_path} (UAT Gaps - if --gaps)
- {reviews_path} (Cross-AI Review Feedback - if --reviews)
- {UI_SPEC_PATH} (UI Design Contract — visual/interaction specs, if exists)
- {SPIKE_FINDINGS_PATH} (Spike Findings — validated patterns, constraints, landmines from experiments, if exists)
- {SKETCH_FINDINGS_PATH} (Sketch Findings — validated design decisions, CSS patterns, visual direction, if exists)
${CONTEXT_WINDOW >= 500000 ? `
**Cross-phase context (1M model enrichment):**
- CONTEXT.md files from the 3 most recent completed phases (locked decisions — maintain consistency)

View File

@@ -124,6 +124,19 @@ If `$VALIDATE_MODE` only:
**Step 2: Initialize**
```bash
if ! command -v gsd-sdk &>/dev/null; then
echo "⚠ gsd-sdk not found in PATH — /gsd-quick requires it."
echo ""
echo "Install the GSD SDK:"
echo " npm install -g @gsd-build/sdk"
echo ""
echo "Or update GSD to get the latest packages:"
echo " /gsd-update"
exit 1
fi
```
```bash
INIT=$(gsd-sdk query init.quick "$DESCRIPTION")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi

View File

@@ -0,0 +1,283 @@
<purpose>
Curate sketch design findings and package them into a persistent project skill for future
UI implementation. Reads from `.planning/sketches/`, writes skill to `./.claude/skills/sketch-findings-[project]/`
(project-local) and summary to `.planning/sketches/WRAP-UP-SUMMARY.md`.
Companion to `/gsd-sketch`.
</purpose>
<required_reading>
Read all files referenced by the invoking prompt's execution_context before starting.
</required_reading>
<process>
<step name="banner">
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GSD ► SKETCH WRAP-UP
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
</step>
<step name="gather">
## Gather Sketch Inventory
1. Read `.planning/sketches/MANIFEST.md` for the design direction and reference points
2. Glob `.planning/sketches/*/README.md` and parse YAML frontmatter from each
3. Check if `./.claude/skills/sketch-findings-*/SKILL.md` exists for this project
- If yes: read its `processed_sketches` list and filter those out
- If no: all sketches are candidates
If no unprocessed sketches exist:
```
No unprocessed sketches found in `.planning/sketches/`.
Run `/gsd-sketch` first to create design explorations.
```
Exit.
Check `commit_docs` config:
```bash
COMMIT_DOCS=$(gsd-sdk query config-get commit_docs 2>/dev/null || echo "true")
```
</step>
<step name="curate">
## Curate Sketches One-at-a-Time
Present each unprocessed sketch in ascending order. For each sketch, show:
- **Sketch number and name**
- **Design question:** from frontmatter
- **Winner:** which variant was selected (if any)
- **Tags:** from frontmatter
- **Key decisions:** summarize what was decided visually
Then ask the user:
╔══════════════════════════════════════════════════════════════╗
║ CHECKPOINT: Decision Required ║
╚══════════════════════════════════════════════════════════════╝
Sketch {NNN}: {name} — Winner: Variant {X}
{key design decisions summary}
──────────────────────────────────────────────────────────────
→ Include / Exclude / Partial / Let me look at it
──────────────────────────────────────────────────────────────
**If "Let me look at it":**
1. Provide: `open .planning/sketches/NNN-name/index.html`
2. Remind them which variant won and what to look for
3. After they've looked, return to the include/exclude/partial decision
**If "Partial":**
Ask what specifically to include or exclude from this sketch's decisions.
</step>
<step name="group">
## Auto-Group by Design Area
After all sketches are curated:
1. Read all included sketches' tags, names, and content
2. Propose design-area groupings, e.g.:
- "**Layout & Navigation** — sketches 001, 004"
- "**Form Controls** — sketches 002, 005"
- "**Color & Typography** — sketches 003"
3. Present the grouping for approval — user may merge, split, rename, or rearrange
Each group becomes one reference file in the generated skill.
</step>
<step name="skill_name">
## Determine Output Skill Name
Derive from the project directory name: `./.claude/skills/sketch-findings-[project-dir-name]/`
If a skill already exists at that path (append mode), update in place.
</step>
<step name="copy_sources">
## Copy Source Files
For each included sketch:
1. Copy the winning variant's HTML file (or the full index.html with all variants) into `sources/NNN-sketch-name/`
2. Copy the winning theme.css into `sources/themes/`
3. Exclude node_modules, build artifacts, .DS_Store
</step>
<step name="synthesize">
## Synthesize Reference Files
For each design-area group, write a reference file at `references/[design-area-name].md`:
```markdown
# [Design Area Name]
## Design Decisions
[For each validated decision: what was chosen, why it won over alternatives, the key visual properties (colors, spacing, border radius, typography)]
## CSS Patterns
[Key CSS snippets from winning variants — layout structures, component patterns, animation patterns. Extracted and cleaned up for reference.]
## HTML Structures
[Key HTML patterns from winning variants — page layout, component markup, navigation structures.]
## What to Avoid
[Design directions that were tried and rejected. Why they didn't work.]
## Origin
Synthesized from sketches: NNN, NNN
Source files available in: sources/NNN-sketch-name/
```
</step>
<step name="write_skill">
## Write SKILL.md
Create (or update) the generated skill's SKILL.md:
```markdown
---
name: sketch-findings-[project-dir-name]
description: Validated design decisions, CSS patterns, and visual direction from sketch experiments. Auto-loaded during UI implementation on [project-dir-name].
---
<context>
## Project: [project-dir-name]
[Design direction paragraph from MANIFEST.md]
[Reference points mentioned during intake]
Sketch sessions wrapped: [date(s)]
</context>
<design_direction>
## Overall Direction
[Summary of the validated visual direction: palette, typography, spacing system, layout approach, interaction patterns]
</design_direction>
<findings_index>
## Design Areas
| Area | Reference | Key Decision |
|------|-----------|--------------|
| [Name] | references/[name].md | [One-line summary] |
## Theme
The winning theme file is at `sources/themes/default.css`.
## Source Files
Original sketch HTML files are preserved in `sources/` for complete reference.
</findings_index>
<metadata>
## Processed Sketches
[List of sketch numbers wrapped up]
- 001-sketch-name
- 002-sketch-name
</metadata>
```
</step>
<step name="write_summary">
## Write Planning Summary
Write `.planning/sketches/WRAP-UP-SUMMARY.md` for project history:
```markdown
# Sketch Wrap-Up Summary
**Date:** [date]
**Sketches processed:** [count]
**Design areas:** [list]
**Skill output:** `./.claude/skills/sketch-findings-[project]/`
## Included Sketches
| # | Name | Winner | Design Area |
|---|------|--------|-------------|
## Excluded Sketches
| # | Name | Reason |
|---|------|--------|
## Design Direction
[consolidated design direction summary]
## Key Decisions
[layout, palette, typography, spacing, interaction patterns]
```
</step>
<step name="update_claude_md">
## Update Project CLAUDE.md
Add an auto-load routing line:
```
- **Sketch findings for [project]** (design decisions, CSS patterns, visual direction) → `Skill("sketch-findings-[project-dir-name]")`
```
If this routing line already exists (append mode), leave it as-is.
</step>
<step name="commit">
Commit all artifacts (if `COMMIT_DOCS` is true):
```bash
gsd-sdk query commit "docs(sketch-wrap-up): package [N] sketch findings into project skill" .planning/sketches/WRAP-UP-SUMMARY.md
```
</step>
<step name="report">
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GSD ► SKETCH WRAP-UP COMPLETE ✓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
**Curated:** {N} sketches ({included} included, {excluded} excluded)
**Design areas:** {list}
**Skill:** `./.claude/skills/sketch-findings-[project]/`
**Summary:** `.planning/sketches/WRAP-UP-SUMMARY.md`
**CLAUDE.md:** routing line added
The sketch-findings skill will auto-load when building the UI.
```
───────────────────────────────────────────────────────────────
## ▶ Next Up
**Start building** — implement the validated design
`/gsd-plan-phase`
───────────────────────────────────────────────────────────────
**Also available:**
- `/gsd-ui-phase` — generate a UI design contract for a frontend phase
- `/gsd-sketch` — sketch additional design areas
- `/gsd-explore` — continue exploring
───────────────────────────────────────────────────────────────
</step>
</process>
<success_criteria>
- [ ] Every unprocessed sketch presented for individual curation
- [ ] Design-area grouping proposed and approved
- [ ] Sketch-findings skill exists at `./.claude/skills/` with SKILL.md, references/, sources/
- [ ] Winning theme.css copied into skill sources
- [ ] Reference files contain design decisions, CSS patterns, HTML structures, anti-patterns
- [ ] `.planning/sketches/WRAP-UP-SUMMARY.md` written for project history
- [ ] Project CLAUDE.md has auto-load routing line
- [ ] Summary presented with next-step routing
</success_criteria>

View File

@@ -0,0 +1,263 @@
<purpose>
Explore design directions through throwaway HTML mockups before committing to implementation.
Each sketch produces 2-3 variants for comparison. Saves artifacts to `.planning/sketches/`.
Companion to `/gsd-sketch-wrap-up`.
</purpose>
<required_reading>
Read all files referenced by the invoking prompt's execution_context before starting.
@~/.claude/get-shit-done/references/sketch-theme-system.md
@~/.claude/get-shit-done/references/sketch-variant-patterns.md
@~/.claude/get-shit-done/references/sketch-interactivity.md
@~/.claude/get-shit-done/references/sketch-tooling.md
</required_reading>
<process>
<step name="banner">
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GSD ► SKETCHING
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
Parse `$ARGUMENTS` for:
- `--quick` flag → set `QUICK_MODE=true`
- `--text` flag → set `TEXT_MODE=true`
- Remaining text → the design idea to sketch
**Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
</step>
<step name="setup_directory">
Create `.planning/sketches/` and themes directory if they don't exist:
```bash
mkdir -p .planning/sketches/themes
```
Check for existing sketches to determine numbering:
```bash
ls -d .planning/sketches/[0-9][0-9][0-9]-* 2>/dev/null | sort | tail -1
```
Check `commit_docs` config:
```bash
COMMIT_DOCS=$(gsd-sdk query config-get commit_docs 2>/dev/null || echo "true")
```
</step>
<step name="mood_intake">
**If `QUICK_MODE` is true:** Skip mood intake. Use whatever the user provided in `$ARGUMENTS` as the design direction. Jump to `decompose`.
**Otherwise:**
Before sketching anything, explore the design intent through conversation. Ask one question at a time using AskUserQuestion, with a paragraph of context and reasoning for each.
**Questions to cover (adapt to what the user has already shared):**
1. **Feel:** "What should this feel like? Give me adjectives, emotions, or a vibe." (e.g., "clean and clinical", "warm and playful", "dense and powerful")
2. **References:** "What apps, sites, or products have a similar feel to what you're imagining?" (gives concrete visual anchors)
3. **Core action:** "What's the single most important thing a user does here?" (focuses the sketch on what matters)
You may need more or fewer questions depending on how much the user shares upfront. After each answer, briefly reflect what you heard and how it shapes your thinking.
When you have enough signal, ask: **"I think I have a good sense of the direction. Ready for me to sketch, or want to keep discussing?"**
Only proceed when the user says go.
</step>
<step name="decompose">
Break the idea into 2-5 design questions. Present as a table:
| Sketch | Design question | Approach | Risk |
|--------|----------------|----------|------|
| 001 | Does a two-panel layout feel right? | Sidebar + main, variants: fixed/collapsible/floating | **High** — sets page structure |
| 002 | How should the form controls look? | Grouped cards, variants: stacked/inline/floating labels | Medium |
Each sketch answers one specific visual question. Good sketches:
- "Does this layout feel right?" — build with real-ish content
- "How should these controls be grouped?" — build with actual labels and inputs
- "What does this interaction feel like?" — build the hover/click/transition
- "Does this color palette work?" — apply to actual UI, not a swatch grid
Bad sketches:
- "Design the whole app" — too broad
- "Set up the component library" — that's implementation
- "Pick a color palette" — apply it to UI instead
Present the table and get alignment before building.
</step>
<step name="create_manifest">
Create or update `.planning/sketches/MANIFEST.md`:
```markdown
# Sketch Manifest
## Design Direction
[One paragraph capturing the mood/feel/direction from the intake conversation]
## Reference Points
[Apps/sites the user referenced]
## Sketches
| # | Name | Design Question | Winner | Tags |
|---|------|----------------|--------|------|
```
If MANIFEST.md already exists, append new sketches to the existing table.
</step>
<step name="create_theme">
If no theme exists yet at `.planning/sketches/themes/default.css`, create one based on the mood/direction from the intake step. See `sketch-theme-system.md` for the full template.
Adapt colors, fonts, spacing, and shapes to match the agreed aesthetic — don't use the defaults verbatim unless they match the mood.
</step>
<step name="build_sketches">
Build each sketch in order.
### For Each Sketch:
**a.** Find next available number by checking existing `.planning/sketches/NNN-*/` directories.
Format: three-digit zero-padded + hyphenated descriptive name.
**b.** Create the sketch directory: `.planning/sketches/NNN-descriptive-name/`
**c.** Build `index.html` with 2-3 variants:
**First round — dramatic differences:** Build 2-3 meaningfully different approaches to the design question. Different layouts, different visual structures, different interaction models.
**Subsequent rounds — refinements:** Once the user has picked a direction or cherry-picked elements, build subtler variations within that direction.
Each variant is a page/tab in the same HTML file. Include:
- Tab navigation to switch between variants (see `sketch-variant-patterns.md`)
- Clear labels: "Variant A: Sidebar Layout", "Variant B: Top Nav", etc.
- The sketch toolbar (see `sketch-tooling.md`)
- All interactive elements functional (see `sketch-interactivity.md`)
- Real-ish content, not lorem ipsum
- Link to `../themes/default.css` for shared theme variables
**All sketches are plain HTML with inline CSS and JS.** No build step, no npm, no framework. Opens instantly in a browser.
**d.** Write `README.md`:
```markdown
---
sketch: NNN
name: descriptive-name
question: "What layout structure feels right for the dashboard?"
winner: null
tags: [layout, dashboard]
---
# Sketch NNN: Descriptive Name
## Design Question
[The specific visual question this sketch answers]
## How to View
open .planning/sketches/NNN-descriptive-name/index.html
## Variants
- **A: [name]** — [one-line description of this approach]
- **B: [name]** — [one-line description]
- **C: [name]** — [one-line description]
## What to Look For
[Specific things to pay attention to when comparing variants]
```
**e.** Present to the user with a checkpoint:
╔══════════════════════════════════════════════════════════════╗
║ CHECKPOINT: Verification Required ║
╚══════════════════════════════════════════════════════════════╝
**Sketch {NNN}: {name}**
Open: `open .planning/sketches/NNN-name/index.html`
Compare: {what to look for between variants}
──────────────────────────────────────────────────────────────
→ Which variant feels right? Or cherry-pick elements across variants.
──────────────────────────────────────────────────────────────
**f.** Handle feedback:
- **Pick a direction:** "I like variant B" → mark winner in README, move to next sketch
- **Cherry-pick elements:** "Rounded edges from A, color treatment from C" → build a synthesis as a new variant, show again
- **Want more exploration:** "None of these feel right, try X instead" → build new variants
Iterate until the user is satisfied with a direction for this sketch.
**g.** Finalize:
1. Mark the winning variant in the README frontmatter (`winner: "B"`)
2. Add ★ indicator to the winning tab in the HTML
3. Update `.planning/sketches/MANIFEST.md` with the sketch row
**h.** Commit (if `COMMIT_DOCS` is true):
```bash
gsd-sdk query commit "docs(sketch-NNN): [winning direction] — [key visual insight]" .planning/sketches/NNN-descriptive-name/ .planning/sketches/MANIFEST.md
```
**i.** Report:
```
◆ Sketch NNN: {name}
Winner: Variant {X} — {description}
Insight: {key visual decision made}
```
</step>
<step name="report">
After all sketches complete, present the summary:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GSD ► SKETCH COMPLETE ✓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
## Design Direction
{what we landed on overall}
## Key Decisions
{layout, palette, typography, spacing, interaction patterns}
## Open Questions
{anything unresolved or worth revisiting}
```
───────────────────────────────────────────────────────────────
## ▶ Next Up
**Package findings** — wrap design decisions into a reusable skill
`/gsd-sketch-wrap-up`
───────────────────────────────────────────────────────────────
**Also available:**
- `/gsd-plan-phase` — start building the real UI
- `/gsd-explore` — continue exploring the concept
- `/gsd-spike` — spike technical feasibility of a design pattern
───────────────────────────────────────────────────────────────
</step>
</process>
<success_criteria>
- [ ] `.planning/sketches/` created (auto-creates if needed, no project init required)
- [ ] Design direction explored conversationally before any code (unless --quick)
- [ ] Each sketch has 2-3 variants for comparison
- [ ] User can open and interact with sketches in a browser
- [ ] Winning variant selected and marked for each sketch
- [ ] All variants preserved (winner marked, not others deleted)
- [ ] MANIFEST.md is current
- [ ] Commits use `docs(sketch-NNN): [winner]` format
- [ ] Summary presented with next-step routing
</success_criteria>

View File

@@ -0,0 +1,273 @@
<purpose>
Curate spike experiment findings and package them into a persistent project skill for future
build conversations. Reads from `.planning/spikes/`, writes skill to `./.claude/skills/spike-findings-[project]/`
(project-local) and summary to `.planning/spikes/WRAP-UP-SUMMARY.md`.
Companion to `/gsd-spike`.
</purpose>
<required_reading>
Read all files referenced by the invoking prompt's execution_context before starting.
</required_reading>
<process>
<step name="banner">
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GSD ► SPIKE WRAP-UP
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
</step>
<step name="gather">
## Gather Spike Inventory
1. Read `.planning/spikes/MANIFEST.md` for the overall idea context
2. Glob `.planning/spikes/*/README.md` and parse YAML frontmatter from each
3. Check if `./.claude/skills/spike-findings-*/SKILL.md` exists for this project
- If yes: read its `processed_spikes` list from the metadata section and filter those out
- If no: all spikes are candidates
If no unprocessed spikes exist:
```
No unprocessed spikes found in `.planning/spikes/`.
Run `/gsd-spike` first to create experiments.
```
Exit.
Check `commit_docs` config:
```bash
COMMIT_DOCS=$(gsd-sdk query config-get commit_docs 2>/dev/null || echo "true")
```
</step>
<step name="curate">
## Curate Spikes One-at-a-Time
Present each unprocessed spike in ascending order. For each spike, show:
- **Spike number and name**
- **Validates:** the Given/When/Then from frontmatter
- **Verdict:** VALIDATED / INVALIDATED / PARTIAL
- **Tags:** from frontmatter
- **Key findings:** summarize the Results section from the README
- **Grey areas:** anything uncertain or partially proven
Then ask the user:
╔══════════════════════════════════════════════════════════════╗
║ CHECKPOINT: Decision Required ║
╚══════════════════════════════════════════════════════════════╝
Spike {NNN}: {name} — {verdict}
{key findings summary}
──────────────────────────────────────────────────────────────
→ Include / Exclude / Partial / Help me UAT this
──────────────────────────────────────────────────────────────
**If "Help me UAT this":**
1. Read the spike's README "How to Run" and "What to Expect" sections
2. Present step-by-step instructions
3. Ask: "Does this match what you expected?"
4. After UAT, return to the include/exclude/partial decision
**If "Partial":**
Ask what specifically to include or exclude. Record their notes alongside the spike.
</step>
<step name="group">
## Auto-Group by Feature Area
After all spikes are curated:
1. Read all included spikes' tags, names, `related` fields, and content
2. Propose feature-area groupings, e.g.:
- "**WebSocket Streaming** — spikes 001, 004, 007"
- "**Foo API Integration** — spikes 002, 003"
- "**PDF Parsing** — spike 005"
3. Present the grouping for approval — user may merge, split, rename, or rearrange
Each group becomes one reference file in the generated skill.
</step>
<step name="skill_name">
## Determine Output Skill Name
Derive the skill name from the project directory:
1. Get the project root directory name (e.g., `solana-tracker`)
2. The skill will be created at `./.claude/skills/spike-findings-[project-dir-name]/`
If a skill already exists at that path (append mode), update in place.
</step>
<step name="copy_sources">
## Copy Source Files
For each included spike:
1. Identify the core source files — the actual scripts, main files, and config that make the spike work. Exclude:
- `node_modules/`, `__pycache__/`, `.venv/`, build artifacts
- Lock files (`package-lock.json`, `yarn.lock`, etc.)
- `.git/`, `.DS_Store`
2. Copy the README.md and core source files into `sources/NNN-spike-name/` inside the generated skill directory
</step>
<step name="synthesize">
## Synthesize Reference Files
For each feature-area group, write a reference file at `references/[feature-area-name].md`:
```markdown
# [Feature Area Name]
## Validated Patterns
[For each validated finding: describe the approach that works, include key code snippets extracted from the spike source, explain why it works]
## Landmines
[Things that look right but aren't. Gotchas. Anti-patterns discovered during spiking.]
## Constraints
[Hard facts: rate limits, library limitations, version requirements, incompatibilities]
## Origin
Synthesized from spikes: NNN, NNN, NNN
Source files available in: sources/NNN-spike-name/, sources/NNN-spike-name/
```
</step>
<step name="write_skill">
## Write SKILL.md
Create (or update) the generated skill's SKILL.md:
```markdown
---
name: spike-findings-[project-dir-name]
description: Validated patterns, constraints, and implementation knowledge from spike experiments. Auto-loaded during implementation work on [project-dir-name].
---
<context>
## Project: [project-dir-name]
[One paragraph from MANIFEST.md describing the overall idea]
Spike sessions wrapped: [date(s)]
</context>
<findings_index>
## Feature Areas
| Area | Reference | Key Finding |
|------|-----------|-------------|
| [Name] | references/[name].md | [One-line summary] |
## Source Files
Original spike source files are preserved in `sources/` for complete reference.
</findings_index>
<metadata>
## Processed Spikes
[List of spike numbers wrapped up]
- 001-spike-name
- 002-spike-name
</metadata>
```
</step>
<step name="write_summary">
## Write Planning Summary
Write `.planning/spikes/WRAP-UP-SUMMARY.md` for project history:
```markdown
# Spike Wrap-Up Summary
**Date:** [date]
**Spikes processed:** [count]
**Feature areas:** [list]
**Skill output:** `./.claude/skills/spike-findings-[project]/`
## Included Spikes
| # | Name | Verdict | Feature Area |
|---|------|---------|--------------|
## Excluded Spikes
| # | Name | Reason |
|---|------|--------|
## Key Findings
[consolidated findings summary]
```
</step>
<step name="update_claude_md">
## Update Project CLAUDE.md
Add an auto-load routing line to the project's CLAUDE.md (create the file if it doesn't exist):
```
- **Spike findings for [project]** (implementation patterns, constraints, gotchas) → `Skill("spike-findings-[project-dir-name]")`
```
If this routing line already exists (append mode), leave it as-is.
</step>
<step name="commit">
Commit all artifacts (if `COMMIT_DOCS` is true):
```bash
gsd-sdk query commit "docs(spike-wrap-up): package [N] spike findings into project skill" .planning/spikes/WRAP-UP-SUMMARY.md
```
</step>
<step name="report">
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GSD ► SPIKE WRAP-UP COMPLETE ✓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
**Curated:** {N} spikes ({included} included, {excluded} excluded)
**Feature areas:** {list}
**Skill:** `./.claude/skills/spike-findings-[project]/`
**Summary:** `.planning/spikes/WRAP-UP-SUMMARY.md`
**CLAUDE.md:** routing line added
The spike-findings skill will auto-load in future build conversations.
```
───────────────────────────────────────────────────────────────
## ▶ Next Up
**Start building** — plan the real implementation
`/gsd-plan-phase`
───────────────────────────────────────────────────────────────
**Also available:**
- `/gsd-add-phase` — add a phase based on spike findings
- `/gsd-spike` — spike additional ideas
- `/gsd-explore` — continue exploring
───────────────────────────────────────────────────────────────
</step>
</process>
<success_criteria>
- [ ] Every unprocessed spike presented for individual curation
- [ ] Feature-area grouping proposed and approved
- [ ] Spike-findings skill exists at `./.claude/skills/` with SKILL.md, references/, sources/
- [ ] Core source files from included spikes copied into sources/
- [ ] Reference files contain validated patterns, code snippets, landmines, constraints
- [ ] `.planning/spikes/WRAP-UP-SUMMARY.md` written for project history
- [ ] Project CLAUDE.md has auto-load routing line
- [ ] Summary presented with next-step routing
</success_criteria>

View File

@@ -0,0 +1,270 @@
<purpose>
Rapid feasibility validation through focused, throwaway experiments. Each spike answers one
specific question with observable evidence. Saves artifacts to `.planning/spikes/`.
Companion to `/gsd-spike-wrap-up`.
</purpose>
<required_reading>
Read all files referenced by the invoking prompt's execution_context before starting.
</required_reading>
<process>
<step name="banner">
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GSD ► SPIKING
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
Parse `$ARGUMENTS` for:
- `--quick` flag → set `QUICK_MODE=true`
- Remaining text → the idea to spike
</step>
<step name="setup_directory">
Create `.planning/spikes/` if it doesn't exist:
```bash
mkdir -p .planning/spikes
```
Check for existing spikes to determine numbering:
```bash
ls -d .planning/spikes/[0-9][0-9][0-9]-* 2>/dev/null | sort | tail -1
```
Check `commit_docs` config:
```bash
COMMIT_DOCS=$(gsd-sdk query config-get commit_docs 2>/dev/null || echo "true")
```
</step>
<step name="detect_stack">
Check for the project's tech stack to inform spike technology choices:
```bash
ls package.json pyproject.toml Cargo.toml go.mod 2>/dev/null
```
Use the project's language/framework by default. For greenfield projects with no existing stack, pick whatever gets to a runnable result fastest (Python, Node, Bash, single HTML file).
Avoid unless the spike specifically requires it:
- Complex package management beyond `npm install` or `pip install`
- Build tools, bundlers, or transpilers
- Docker, containers, or infrastructure
- Env files or config systems — hardcode everything
</step>
<step name="decompose">
**If `QUICK_MODE` is true:** Skip decomposition and alignment. Take the user's idea as a single spike question. Assign it spike number `001` (or next available). Jump to `build_spikes`.
**Otherwise:**
Break the idea into 2-5 independent questions that each prove something specific. Frame each as an informal Given/When/Then. Present as a table:
```
| # | Spike | Validates (Given/When/Then) | Risk |
|---|-------|-----------------------------|------|
| 001 | websocket-streaming | Given a WS connection, when LLM streams tokens, then client receives chunks < 100ms | **High** |
| 002 | pdf-extraction | Given a multi-page PDF, when parsed with pdfjs, then structured text is extractable | Medium |
```
Good spikes answer one specific feasibility question:
- "Can we parse X format and extract Y?" — script that does it on a sample file
- "How fast is X approach?" — benchmark with real-ish data
- "Can we get X and Y to talk to each other?" — thinnest integration
- "What does X feel like as a UI?" — minimal interactive prototype
- "Does X API actually support Y?" — script that calls it and shows the response
Bad spikes are too broad or don't produce observable output:
- "Set up the project" — not a question, just busywork
- "Design the architecture" — planning, not spiking
- "Build the backend" — too broad, no specific question
Order by risk — the spike most likely to kill the idea runs first.
</step>
<step name="align">
**If `QUICK_MODE` is true:** Skip.
Present the ordered spike list and ask which to build:
╔══════════════════════════════════════════════════════════════╗
║ CHECKPOINT: Decision Required ║
╚══════════════════════════════════════════════════════════════╝
{spike table from decompose step}
──────────────────────────────────────────────────────────────
→ Build all in this order, or adjust the list?
──────────────────────────────────────────────────────────────
The user may reorder, merge, split, or skip spikes. Wait for alignment.
</step>
<step name="create_manifest">
Create or update `.planning/spikes/MANIFEST.md`:
```markdown
# Spike Manifest
## Idea
[One paragraph describing the overall idea being explored]
## Spikes
| # | Name | Validates | Verdict | Tags |
|---|------|-----------|---------|------|
```
If MANIFEST.md already exists, append new spikes to the existing table.
</step>
<step name="build_spikes">
Build each spike sequentially, highest-risk first.
### For Each Spike:
**a.** Find next available number by checking existing `.planning/spikes/NNN-*/` directories.
Format: three-digit zero-padded + hyphenated descriptive name.
**b.** Create the spike directory: `.planning/spikes/NNN-descriptive-name/`
**c.** Build the minimum code that answers the spike's question. Every line must serve the question — nothing incidental. If auth isn't the question, hardcode a token. If the database isn't the question, use a JSON file. Strip everything that doesn't directly answer "does X work?"
**d.** Write `README.md` with YAML frontmatter:
```markdown
---
spike: NNN
name: descriptive-name
validates: "Given [precondition], when [action], then [expected outcome]"
verdict: PENDING
related: []
tags: [tag1, tag2]
---
# Spike NNN: Descriptive Name
## What This Validates
[The specific feasibility question, framed as Given/When/Then]
## How to Run
[Single command or short sequence to run the spike]
## What to Expect
[Concrete observable outcomes: "When you click X, you should see Y within Z seconds"]
## Results
[Filled in after running — verdict, evidence, surprises]
```
**e.** Auto-link related spikes: read existing spike READMEs and infer relationships from tags, names, and descriptions. Write the `related` field silently.
**f.** Run and verify:
- If self-verifiable: run it, check output, update README verdict and Results section
- If needs human judgment: run it, present instructions using a checkpoint box:
╔══════════════════════════════════════════════════════════════╗
║ CHECKPOINT: Verification Required ║
╚══════════════════════════════════════════════════════════════╝
**Spike {NNN}: {name}**
**How to run:** {command}
**What to expect:** {concrete outcomes}
──────────────────────────────────────────────────────────────
→ Does this match what you expected? Describe what you see.
──────────────────────────────────────────────────────────────
**g.** Update verdict to VALIDATED / INVALIDATED / PARTIAL. Update Results section with evidence.
**h.** Update `.planning/spikes/MANIFEST.md` with the spike's row.
**i.** Commit (if `COMMIT_DOCS` is true):
```bash
gsd-sdk query commit "docs(spike-NNN): [VERDICT] — [key finding in one sentence]" .planning/spikes/NNN-descriptive-name/ .planning/spikes/MANIFEST.md
```
**j.** Report before moving to next spike:
```
◆ Spike NNN: {name}
Verdict: {VALIDATED ✓ / INVALIDATED ✗ / PARTIAL ⚠}
Finding: {one sentence}
Impact: {effect on remaining spikes, if any}
```
**k.** If a spike invalidates a core assumption: stop and present:
╔══════════════════════════════════════════════════════════════╗
║ CHECKPOINT: Decision Required ║
╚══════════════════════════════════════════════════════════════╝
Core assumption invalidated by Spike {NNN}.
{what was invalidated and why}
──────────────────────────────────────────────────────────────
→ Continue with remaining spikes / Pivot approach / Abandon
──────────────────────────────────────────────────────────────
Only proceed if the user says to.
</step>
<step name="report">
After all spikes complete, present the consolidated report:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GSD ► SPIKE COMPLETE ✓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
## Verdicts
| # | Name | Verdict |
|---|------|---------|
| 001 | {name} | ✓ VALIDATED |
| 002 | {name} | ✗ INVALIDATED |
## Key Discoveries
{surprises, gotchas, things that weren't expected}
## Feasibility Assessment
{overall, is the idea viable?}
## Signal for the Build
{what the real implementation should use, avoid, or watch out for}
```
───────────────────────────────────────────────────────────────
## ▶ Next Up
**Package findings** — wrap spike knowledge into a reusable skill
`/gsd-spike-wrap-up`
───────────────────────────────────────────────────────────────
**Also available:**
- `/gsd-plan-phase` — start planning the real implementation
- `/gsd-explore` — continue exploring the idea
- `/gsd-add-phase` — add a phase to the roadmap based on findings
───────────────────────────────────────────────────────────────
</step>
</process>
<success_criteria>
- [ ] `.planning/spikes/` created (auto-creates if needed, no project init required)
- [ ] Each spike answers one specific question with observable evidence
- [ ] Each spike README has complete frontmatter, run instructions, and results
- [ ] User verified each spike (self-verified or human checkpoint)
- [ ] MANIFEST.md is current
- [ ] Commits use `docs(spike-NNN): [VERDICT]` format
- [ ] Consolidated report presented with next-step routing
- [ ] If core assumption invalidated, execution stopped and user consulted
</success_criteria>

View File

@@ -29,6 +29,11 @@ Parse JSON for: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded
**File paths:** `state_path`, `roadmap_path`, `requirements_path`, `context_path`, `research_path`.
Detect sketch findings:
```bash
SKETCH_FINDINGS_PATH=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/null | head -1)
```
Resolve UI agent models:
```bash
@@ -77,6 +82,13 @@ Note: stack decisions (component library, styling approach) will be asked during
```
Continue (non-blocking).
**If `SKETCH_FINDINGS_PATH` is not empty:**
```
⚡ Sketch findings detected: {SKETCH_FINDINGS_PATH}
Validated design decisions from /gsd-sketch will be loaded into the UI researcher.
Pre-validated decisions (layout, palette, typography, spacing) should be treated as locked — not re-asked.
```
## 4. Check Existing UI-SPEC
```bash
@@ -124,6 +136,7 @@ Answer: "What visual and interaction contracts does this phase need?"
- {requirements_path} (Requirements)
- {context_path} (USER DECISIONS from /gsd-discuss-phase)
- {research_path} (Technical Research — stack decisions)
- {SKETCH_FINDINGS_PATH} (Sketch Findings — validated design decisions, CSS patterns, visual direction from /gsd-sketch, if exists)
</files_to_read>
${AGENT_SKILLS_UI}

View File

@@ -36,8 +36,8 @@ process.stdin.on('end', () => {
process.exit(0);
}
// Claude Code natively enforces read-before-edit — skip the advisory (#1984)
if (process.env.CLAUDE_SESSION_ID) {
// Claude Code natively enforces read-before-edit — skip the advisory (#1984, #2344)
if (process.env.CLAUDE_SESSION_ID || process.env.CLAUDECODE) {
process.exit(0);
}

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "get-shit-done-cc",
"version": "1.36.0",
"version": "1.37.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "get-shit-done-cc",
"version": "1.36.0",
"version": "1.37.1",
"license": "MIT",
"bin": {
"get-shit-done-cc": "bin/install.js"

View File

@@ -1,6 +1,6 @@
{
"name": "get-shit-done-cc",
"version": "1.36.0",
"version": "1.37.1",
"description": "A meta-prompting, context engineering and spec-driven development system for Claude Code, OpenCode, Gemini and Codex by TÂCHES.",
"bin": {
"get-shit-done-cc": "bin/install.js"

View File

@@ -0,0 +1,112 @@
/**
* Agent size budget.
*
* Agent definitions in `agents/gsd-*.md` are loaded verbatim into Claude's
* context on every subagent dispatch. Unbounded growth is paid on every call
* across every workflow.
*
* Budgets are tiered to reflect the intent of each agent class:
* - XL : top-level orchestrators that own end-to-end rubrics
* - LARGE : multi-phase operators with branching workflows
* - DEFAULT : focused single-purpose agents
*
* Raising a budget is a deliberate choice — adjust the constant, write a
* rationale in the PR, and make sure the bloat is not duplicated content
* that belongs in `get-shit-done/references/`.
*
* See: https://github.com/gsd-build/get-shit-done/issues/2361
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const AGENTS_DIR = path.join(__dirname, '..', 'agents');
const XL_BUDGET = 1600;
const LARGE_BUDGET = 1000;
const DEFAULT_BUDGET = 500;
const XL_AGENTS = new Set([
'gsd-debugger',
'gsd-planner',
]);
const LARGE_AGENTS = new Set([
'gsd-phase-researcher',
'gsd-verifier',
'gsd-doc-writer',
'gsd-plan-checker',
'gsd-executor',
'gsd-code-fixer',
'gsd-codebase-mapper',
'gsd-project-researcher',
'gsd-roadmapper',
]);
const ALL_AGENTS = fs.readdirSync(AGENTS_DIR)
.filter(f => f.startsWith('gsd-') && f.endsWith('.md'))
.map(f => f.replace('.md', ''));
function budgetFor(agent) {
if (XL_AGENTS.has(agent)) return { tier: 'XL', limit: XL_BUDGET };
if (LARGE_AGENTS.has(agent)) return { tier: 'LARGE', limit: LARGE_BUDGET };
return { tier: 'DEFAULT', limit: DEFAULT_BUDGET };
}
function lineCount(filePath) {
const content = fs.readFileSync(filePath, 'utf-8');
if (content.length === 0) return 0;
const trailingNewline = content.endsWith('\n') ? 1 : 0;
return content.split('\n').length - trailingNewline;
}
describe('SIZE: agent line-count budget', () => {
for (const agent of ALL_AGENTS) {
const { tier, limit } = budgetFor(agent);
test(`${agent} (${tier}) stays under ${limit} lines`, () => {
const filePath = path.join(AGENTS_DIR, agent + '.md');
const lines = lineCount(filePath);
assert.ok(
lines <= limit,
`${agent}.md has ${lines} lines — exceeds ${tier} budget of ${limit}. ` +
`Extract shared boilerplate to get-shit-done/references/ or raise the budget ` +
`in tests/agent-size-budget.test.cjs with a rationale.`
);
});
}
});
describe('SIZE: every agent is classified', () => {
test('every agent falls in exactly one tier', () => {
for (const agent of ALL_AGENTS) {
const inXL = XL_AGENTS.has(agent);
const inLarge = LARGE_AGENTS.has(agent);
assert.ok(
!(inXL && inLarge),
`${agent} is in both XL_AGENTS and LARGE_AGENTS — pick one`
);
}
});
test('every named XL agent exists', () => {
for (const agent of XL_AGENTS) {
const filePath = path.join(AGENTS_DIR, agent + '.md');
assert.ok(
fs.existsSync(filePath),
`XL_AGENTS references ${agent}.md which does not exist — clean up the set`
);
}
});
test('every named LARGE agent exists', () => {
for (const agent of LARGE_AGENTS) {
const filePath = path.join(AGENTS_DIR, agent + '.md');
assert.ok(
fs.existsSync(filePath),
`LARGE_AGENTS references ${agent}.md which does not exist — clean up the set`
);
}
});
});

View File

@@ -0,0 +1,112 @@
/**
* Regression test for bug #2268
*
* cmdInitProgress used a sliding-window pattern that set is_next_to_discuss
* only on the FIRST undiscussed phase. Multiple independent undiscussed phases
* could not be discussed in parallel — the manager only ever recommended one
* discuss action at a time.
*
* Fix: mark ALL undiscussed phases as is_next_to_discuss = true so the user
* can pick any of them.
*/
'use strict';
const { test, describe, beforeEach, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
function writeRoadmap(tmpDir, phases) {
const sections = phases.map(p => {
let section = `### Phase ${p.number}: ${p.name}\n\n**Goal:** Do the thing\n`;
return section;
}).join('\n');
const checklist = phases.map(p => {
const mark = p.complete ? 'x' : ' ';
return `- [${mark}] **Phase ${p.number}: ${p.name}**`;
}).join('\n');
fs.writeFileSync(
path.join(tmpDir, '.planning', 'ROADMAP.md'),
`# Roadmap\n\n## Progress\n\n${checklist}\n\n${sections}`
);
}
function writeState(tmpDir) {
fs.writeFileSync(path.join(tmpDir, '.planning', 'STATE.md'), '---\nstatus: active\n---\n# State\n');
}
let tmpDir;
describe('bug #2268: parallel discuss — all undiscussed phases marked is_next_to_discuss', () => {
beforeEach(() => { tmpDir = createTempProject(); });
afterEach(() => { cleanup(tmpDir); });
test('two undiscussed phases: both marked is_next_to_discuss', () => {
writeState(tmpDir);
writeRoadmap(tmpDir, [
{ number: '1', name: 'Foundation' },
{ number: '2', name: 'Cloud Deployment' },
]);
const result = runGsdTools('init manager', tmpDir);
const output = JSON.parse(result.output);
assert.strictEqual(output.phases[0].is_next_to_discuss, true, 'phase 1 should be discussable');
assert.strictEqual(output.phases[1].is_next_to_discuss, true, 'phase 2 should also be discussable');
});
test('two undiscussed phases: both get discuss recommendations', () => {
writeState(tmpDir);
writeRoadmap(tmpDir, [
{ number: '1', name: 'Foundation' },
{ number: '2', name: 'Cloud Deployment' },
]);
const result = runGsdTools('init manager', tmpDir);
const output = JSON.parse(result.output);
const discussActions = output.recommended_actions.filter(a => a.action === 'discuss');
assert.strictEqual(discussActions.length, 2, 'should recommend discuss for both undiscussed phases');
const phases = discussActions.map(a => a.phase).sort();
assert.deepStrictEqual(phases, ['1', '2']);
});
test('five undiscussed phases: all five marked is_next_to_discuss', () => {
writeState(tmpDir);
writeRoadmap(tmpDir, [
{ number: '1', name: 'Alpha' },
{ number: '2', name: 'Beta' },
{ number: '3', name: 'Gamma' },
{ number: '4', name: 'Delta' },
{ number: '5', name: 'Epsilon' },
]);
const result = runGsdTools('init manager', tmpDir);
const output = JSON.parse(result.output);
for (const phase of output.phases) {
assert.strictEqual(phase.is_next_to_discuss, true, `phase ${phase.number} should be discussable`);
}
});
test('discussed phase stays false; undiscussed sibling is true', () => {
writeState(tmpDir);
writeRoadmap(tmpDir, [
{ number: '1', name: 'Foundation' },
{ number: '2', name: 'API Layer' },
]);
// scaffold CONTEXT.md to mark phase 1 as discussed
const dir = path.join(tmpDir, '.planning', 'phases', '01-foundation');
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(path.join(dir, '01-CONTEXT.md'), '# Context');
const result = runGsdTools('init manager', tmpDir);
const output = JSON.parse(result.output);
assert.strictEqual(output.phases[0].is_next_to_discuss, false, 'discussed phase must not be is_next_to_discuss');
assert.strictEqual(output.phases[1].is_next_to_discuss, true, 'undiscussed sibling must be is_next_to_discuss');
});
});

View File

@@ -0,0 +1,62 @@
/**
* Regression test for bug #2334
*
* /gsd-quick crashed with `command not found: gsd-sdk` (exit code 127) when
* the gsd-sdk binary was not installed or not in PATH. The workflow's Step 2
* called `gsd-sdk query init.quick` directly with no pre-flight check and no
* fallback, so missing gsd-sdk caused an immediate abort with no helpful message.
*
* Fix: Step 2 must check for gsd-sdk in PATH before invoking it. If absent,
* emit a human-readable error pointing users to the install command.
*/
'use strict';
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const WORKFLOW_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'quick.md');
describe('bug #2334: quick workflow gsd-sdk pre-flight check', () => {
let content;
test('workflow file exists', () => {
assert.ok(fs.existsSync(WORKFLOW_PATH), 'workflows/quick.md should exist');
content = fs.readFileSync(WORKFLOW_PATH, 'utf-8');
});
test('Step 2 checks for gsd-sdk before invoking it', () => {
content = content || fs.readFileSync(WORKFLOW_PATH, 'utf-8');
// The check must appear before the first gsd-sdk invocation in Step 2
const step2Start = content.indexOf('**Step 2:');
assert.ok(step2Start !== -1, 'Step 2 must exist in quick workflow');
const firstSdkCall = content.indexOf('gsd-sdk query init.quick', step2Start);
assert.ok(firstSdkCall !== -1, 'gsd-sdk query init.quick must be present in Step 2');
// Find any gsd-sdk availability check between the Step 2 heading and the first call
const step2Section = content.slice(step2Start, firstSdkCall);
const hasCommandCheck = step2Section.includes('command -v gsd-sdk') || step2Section.includes('which gsd-sdk');
assert.ok(
hasCommandCheck,
'Step 2 must check for gsd-sdk in PATH (via `command -v gsd-sdk` or `which gsd-sdk`) ' +
'before calling `gsd-sdk query init.quick`. Without this guard, the workflow crashes ' +
'with exit code 127 when gsd-sdk is not installed (root cause of #2334).'
);
});
test('pre-flight error message references the install command', () => {
content = content || fs.readFileSync(WORKFLOW_PATH, 'utf-8');
const step2Start = content.indexOf('**Step 2:');
const firstSdkCall = content.indexOf('gsd-sdk query init.quick', step2Start);
const step2Section = content.slice(step2Start, firstSdkCall);
const hasInstallHint = step2Section.includes('@gsd-build/sdk') || step2Section.includes('gsd-update') || step2Section.includes('/gsd-update');
assert.ok(
hasInstallHint,
'Pre-flight error must include a hint on how to install gsd-sdk (npm install -g @gsd-build/sdk or /gsd-update)'
);
});
});

View File

@@ -0,0 +1,98 @@
/**
* Regression test for bug #2344
*
* gsd-read-guard.js checked process.env.CLAUDE_SESSION_ID to detect the
* Claude Code runtime and skip its advisory. However, Claude Code CLI exports
* CLAUDECODE=1, not CLAUDE_SESSION_ID. The skip never fired, so the
* READ-BEFORE-EDIT advisory injected on every Edit/Write call inside Claude
* Code — producing noise in long-running sessions.
*
* Fix: check CLAUDECODE (and CLAUDE_SESSION_ID for back-compat) before
* emitting the advisory.
*/
process.env.GSD_TEST_MODE = '1';
const { test, describe, beforeEach, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { execFileSync } = require('node:child_process');
const { createTempDir, cleanup } = require('./helpers.cjs');
const HOOK_PATH = path.join(__dirname, '..', 'hooks', 'gsd-read-guard.js');
function runHook(payload, envOverrides = {}) {
const input = JSON.stringify(payload);
const env = {
...process.env,
CLAUDE_SESSION_ID: '',
CLAUDECODE: '',
...envOverrides,
};
try {
const stdout = execFileSync(process.execPath, [HOOK_PATH], {
input,
encoding: 'utf-8',
timeout: 5000,
stdio: ['pipe', 'pipe', 'pipe'],
env,
});
return { exitCode: 0, stdout: stdout.trim(), stderr: '' };
} catch (err) {
return {
exitCode: err.status ?? 1,
stdout: (err.stdout || '').toString().trim(),
stderr: (err.stderr || '').toString().trim(),
};
}
}
describe('bug #2344: read guard skips on CLAUDECODE env var', () => {
let tmpDir;
beforeEach(() => { tmpDir = createTempDir('gsd-read-guard-2344-'); });
afterEach(() => { cleanup(tmpDir); });
test('skips advisory when CLAUDECODE=1 is set (Claude Code CLI env)', () => {
const filePath = path.join(tmpDir, 'existing.js');
fs.writeFileSync(filePath, 'const x = 1;\n');
const result = runHook(
{ tool_name: 'Edit', tool_input: { file_path: filePath, old_string: 'const x = 1;', new_string: 'const x = 2;' } },
{ CLAUDECODE: '1' }
);
assert.equal(result.exitCode, 0);
assert.equal(result.stdout, '', 'advisory must not fire when CLAUDECODE=1');
});
test('skips advisory when CLAUDE_SESSION_ID is set (back-compat)', () => {
const filePath = path.join(tmpDir, 'existing.js');
fs.writeFileSync(filePath, 'const x = 1;\n');
const result = runHook(
{ tool_name: 'Edit', tool_input: { file_path: filePath, old_string: 'const x = 1;', new_string: 'const x = 2;' } },
{ CLAUDE_SESSION_ID: 'test-session-123' }
);
assert.equal(result.exitCode, 0);
assert.equal(result.stdout, '', 'advisory must not fire when CLAUDE_SESSION_ID is set');
});
test('still injects advisory when neither CLAUDECODE nor CLAUDE_SESSION_ID is set', () => {
const filePath = path.join(tmpDir, 'existing.js');
fs.writeFileSync(filePath, 'const x = 1;\n');
const result = runHook(
{ tool_name: 'Edit', tool_input: { file_path: filePath, old_string: 'const x = 1;', new_string: 'const x = 2;' } },
{ CLAUDECODE: '', CLAUDE_SESSION_ID: '' }
);
assert.equal(result.exitCode, 0);
assert.ok(result.stdout.length > 0, 'advisory should fire on non-Claude-Code runtimes');
const output = JSON.parse(result.stdout);
assert.ok(output.hookSpecificOutput?.additionalContext?.includes('Read'));
});
});

View File

@@ -0,0 +1,108 @@
/**
* Regression tests for bug #2346
*
* Multiple GSD agents (gsd-ui-checker, gsd-planner) entered unbounded Read
* loops — re-reading the same file hundreds of times in a single run. Root
* cause: no explicit no-re-read rule or tool-budget cap in the agent prompts.
* gsd-pattern-mapper was fixed in #2312; this covers the remaining agents.
*
* Fix: add <critical_rules> block to each affected agent with:
* 1. No-re-read constraint
* 2. Large-file strategy (Grep first, then targeted offset/limit Read)
* 3. Stop-on-sufficient-evidence rule (where applicable)
*/
'use strict';
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const AGENTS_DIR = path.join(__dirname, '..', 'agents');
describe('bug #2346: agent read loop guards', () => {
describe('gsd-ui-checker', () => {
const agentPath = path.join(AGENTS_DIR, 'gsd-ui-checker.md');
let content;
test('agent file exists', () => {
assert.ok(fs.existsSync(agentPath), 'agents/gsd-ui-checker.md must exist');
content = fs.readFileSync(agentPath, 'utf-8');
});
test('has <critical_rules> block', () => {
content = content || fs.readFileSync(agentPath, 'utf-8');
assert.ok(
content.includes('<critical_rules>'),
'gsd-ui-checker.md must have a <critical_rules> block to prevent unbounded read loops (#2346)'
);
});
test('critical_rules contains no-re-read constraint', () => {
content = content || fs.readFileSync(agentPath, 'utf-8');
const rulesStart = content.indexOf('<critical_rules>');
const rulesEnd = content.indexOf('</critical_rules>', rulesStart);
assert.ok(rulesStart !== -1 && rulesEnd !== -1, '<critical_rules> block must be complete');
const rulesBlock = content.slice(rulesStart, rulesEnd);
assert.ok(
rulesBlock.includes('re-read') || rulesBlock.includes('re read'),
'critical_rules must include a no-re-read rule'
);
});
test('critical_rules appears before success_criteria', () => {
content = content || fs.readFileSync(agentPath, 'utf-8');
const rulesIdx = content.indexOf('<critical_rules>');
const successIdx = content.indexOf('<success_criteria>');
assert.ok(rulesIdx !== -1 && successIdx !== -1, 'both sections must exist');
assert.ok(
rulesIdx < successIdx,
'<critical_rules> must appear before <success_criteria>'
);
});
});
describe('gsd-planner', () => {
const agentPath = path.join(AGENTS_DIR, 'gsd-planner.md');
let content;
test('agent file exists', () => {
assert.ok(fs.existsSync(agentPath), 'agents/gsd-planner.md must exist');
content = fs.readFileSync(agentPath, 'utf-8');
});
test('has <critical_rules> block', () => {
content = content || fs.readFileSync(agentPath, 'utf-8');
assert.ok(
content.includes('<critical_rules>'),
'gsd-planner.md must have a <critical_rules> block to prevent unbounded read loops (#2346)'
);
});
test('critical_rules contains no-re-read constraint', () => {
content = content || fs.readFileSync(agentPath, 'utf-8');
const rulesStart = content.indexOf('<critical_rules>');
const rulesEnd = content.indexOf('</critical_rules>', rulesStart);
assert.ok(rulesStart !== -1 && rulesEnd !== -1, '<critical_rules> block must be complete');
const rulesBlock = content.slice(rulesStart, rulesEnd);
assert.ok(
rulesBlock.includes('re-read') || rulesBlock.includes('re read'),
'critical_rules must include a no-re-read rule'
);
});
test('critical_rules appears before success_criteria', () => {
content = content || fs.readFileSync(agentPath, 'utf-8');
const rulesIdx = content.indexOf('<critical_rules>');
const successIdx = content.lastIndexOf('<success_criteria>');
assert.ok(rulesIdx !== -1 && successIdx !== -1, 'both sections must exist');
assert.ok(
rulesIdx < successIdx,
'<critical_rules> must appear before <success_criteria>'
);
});
});
});

View File

@@ -0,0 +1,77 @@
/**
* Regression test for bug #2351
*
* gsd-intel-updater used hardcoded canonical paths (`agents/*.md`,
* `commands/gsd/*.md`, `hooks/*.js`, etc.) that assumed the standard
* `.claude/` runtime layout. Under a `.kilo` install, the runtime root is
* `.kilo/`, and the command directory is `command/` (not `commands/gsd/`).
* Globs against the old paths returned no results, producing semantically
* empty intel files (`"entries": {}`).
*
* Fix: add runtime layout detection and a mapping table so the agent
* resolves paths against the correct root.
*/
'use strict';
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const AGENT_PATH = path.join(__dirname, '..', 'agents', 'gsd-intel-updater.md');
describe('bug #2351: intel updater kilo layout support', () => {
let content;
test('agent file exists', () => {
assert.ok(fs.existsSync(AGENT_PATH), 'agents/gsd-intel-updater.md must exist');
content = fs.readFileSync(AGENT_PATH, 'utf-8');
});
test('scope section includes layout detection step', () => {
content = content || fs.readFileSync(AGENT_PATH, 'utf-8');
const hasDetection =
content.includes('ls -d .kilo') ||
content.includes('Runtime layout detection') ||
content.includes('detected layout') ||
content.includes('layout detection');
assert.ok(
hasDetection,
'gsd-intel-updater.md must instruct the agent to detect the runtime layout ' +
'(.kilo vs .claude) before resolving canonical paths (#2351)'
);
});
test('scope section maps .kilo/agents path', () => {
content = content || fs.readFileSync(AGENT_PATH, 'utf-8');
assert.ok(
content.includes('.kilo/agents'),
'scope section must include the .kilo/agents/*.md path so agent count is correct under kilo layout'
);
});
test('scope section maps .kilo/command path (not commands/gsd)', () => {
content = content || fs.readFileSync(AGENT_PATH, 'utf-8');
assert.ok(
content.includes('.kilo/command'),
'scope section must include .kilo/command path — kilo uses "command/" not "commands/gsd/"'
);
});
test('scope section maps .kilo/hooks path', () => {
content = content || fs.readFileSync(AGENT_PATH, 'utf-8');
assert.ok(
content.includes('.kilo/hooks'),
'scope section must include .kilo/hooks path for hook file counts'
);
});
test('scope section retains standard layout paths for .claude installs', () => {
content = content || fs.readFileSync(AGENT_PATH, 'utf-8');
assert.ok(
content.includes('agents/*.md') || content.includes('Standard `.claude` layout'),
'scope section must still document the standard .claude layout paths for non-kilo installs'
);
});
});

View File

@@ -160,7 +160,7 @@ describe('init manager', () => {
assert.strictEqual(output.phases[1].deps_satisfied, false); // phase 1 not complete
});
test('sliding window: only first undiscussed phase is next to discuss', () => {
test('parallel discuss: all undiscussed phases are marked is_next_to_discuss', () => {
writeState(tmpDir);
writeRoadmap(tmpDir, [
{ number: '1', name: 'Foundation' },
@@ -171,18 +171,17 @@ describe('init manager', () => {
const result = runGsdTools('init manager', tmpDir);
const output = JSON.parse(result.output);
// Only phase 1 should be discussable
// All three phases are undiscussed — all should be discussable
assert.strictEqual(output.phases[0].is_next_to_discuss, true);
assert.strictEqual(output.phases[1].is_next_to_discuss, false);
assert.strictEqual(output.phases[2].is_next_to_discuss, false);
assert.strictEqual(output.phases[1].is_next_to_discuss, true);
assert.strictEqual(output.phases[2].is_next_to_discuss, true);
// Only recommendation should be discuss phase 1
assert.strictEqual(output.recommended_actions.length, 1);
assert.strictEqual(output.recommended_actions[0].action, 'discuss');
assert.strictEqual(output.recommended_actions[0].phase, '1');
// All three should have discuss recommendations
const discussActions = output.recommended_actions.filter(a => a.action === 'discuss');
assert.strictEqual(discussActions.length, 3);
});
test('sliding window: after discussing N, plan N + discuss N+1', () => {
test('discussed phase is not discussable; undiscussed siblings are', () => {
writeState(tmpDir);
writeRoadmap(tmpDir, [
{ number: '1', name: 'Foundation' },
@@ -196,16 +195,18 @@ describe('init manager', () => {
const result = runGsdTools('init manager', tmpDir);
const output = JSON.parse(result.output);
// Phase 1 is discussed, phase 2 is next to discuss
// Phase 1 is discussed; phases 2 and 3 are both undiscussed and discussable
assert.strictEqual(output.phases[0].is_next_to_discuss, false);
assert.strictEqual(output.phases[1].is_next_to_discuss, true);
assert.strictEqual(output.phases[2].is_next_to_discuss, false);
assert.strictEqual(output.phases[2].is_next_to_discuss, true);
// Should recommend plan phase 1 AND discuss phase 2
// Should recommend plan phase 1 AND discuss phases 2 and 3
const phase1Rec = output.recommended_actions.find(r => r.phase === '1');
const phase2Rec = output.recommended_actions.find(r => r.phase === '2');
const phase3Rec = output.recommended_actions.find(r => r.phase === '3');
assert.strictEqual(phase1Rec.action, 'plan');
assert.strictEqual(phase2Rec.action, 'discuss');
assert.strictEqual(phase3Rec.action, 'discuss');
});
test('sliding window: full pipeline — execute N, plan N+1, discuss N+2', () => {
@@ -225,17 +226,17 @@ describe('init manager', () => {
const result = runGsdTools('init manager', tmpDir);
const output = JSON.parse(result.output);
// Phase 4 is first undiscussed
// Phases 4 and 5 are both undiscussed — both discussable
assert.strictEqual(output.phases[3].is_next_to_discuss, true);
assert.strictEqual(output.phases[4].is_next_to_discuss, false);
assert.strictEqual(output.phases[4].is_next_to_discuss, true);
// Recommendations: execute 2, plan 3, discuss 4
// Recommendations: execute 2, plan 3, discuss 4, discuss 5
assert.strictEqual(output.recommended_actions[0].action, 'execute');
assert.strictEqual(output.recommended_actions[0].phase, '2');
assert.strictEqual(output.recommended_actions[1].action, 'plan');
assert.strictEqual(output.recommended_actions[1].phase, '3');
assert.strictEqual(output.recommended_actions[2].action, 'discuss');
assert.strictEqual(output.recommended_actions[2].phase, '4');
const discussRecs = output.recommended_actions.filter(a => a.action === 'discuss').map(a => a.phase).sort();
assert.deepStrictEqual(discussRecs, ['4', '5']);
});
test('recommendation ordering: execute > plan > discuss', () => {

View File

@@ -0,0 +1,177 @@
/**
* Tests for pruneOrphanedWorktrees()
*
* Uses real temporary git repos (no mocks).
* All 4 tests must fail (RED) before implementation is added.
*/
'use strict';
const { describe, test, beforeEach, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const { createTempDir, cleanup } = require('./helpers.cjs');
// Lazy-loaded so tests can fail clearly when the export doesn't exist yet.
function getPruneOrphanedWorktrees() {
const { pruneOrphanedWorktrees } = require('../get-shit-done/bin/lib/core.cjs');
return pruneOrphanedWorktrees;
}
// Create a minimal git repo with an initial commit on main.
function createGitRepo(dir) {
fs.mkdirSync(dir, { recursive: true });
execSync('git init', { cwd: dir, stdio: 'pipe' });
execSync('git config user.email "test@test.com"', { cwd: dir, stdio: 'pipe' });
execSync('git config user.name "Test"', { cwd: dir, stdio: 'pipe' });
execSync('git config commit.gpgsign false', { cwd: dir, stdio: 'pipe' });
fs.writeFileSync(path.join(dir, 'README.md'), '# Test\n');
execSync('git add -A', { cwd: dir, stdio: 'pipe' });
execSync('git commit -m "initial commit"', { cwd: dir, stdio: 'pipe' });
// Rename to main if it isn't already (handles older git defaults)
try {
execSync('git branch -m master main', { cwd: dir, stdio: 'pipe' });
} catch { /* already named main */ }
}
// --- Test suite ---------------------------------------------------------------
describe('pruneOrphanedWorktrees', () => {
let tmpBase;
beforeEach(() => {
tmpBase = createTempDir('prune-wt-test-');
});
afterEach(() => {
cleanup(tmpBase);
});
// Test 1: removes a worktree whose branch is merged into main
test('removes a worktree whose branch is merged into main', () => {
const repoDir = path.join(tmpBase, 'repo');
const worktreeDir = path.join(tmpBase, 'wt-merged');
createGitRepo(repoDir);
// Create worktree on a new branch (main is checked out in repoDir)
execSync('git worktree add "' + worktreeDir + '" -b fix/old-work', { cwd: repoDir, stdio: 'pipe' });
assert.ok(fs.existsSync(worktreeDir), 'worktree dir should exist before prune');
// Add a commit in the worktree
fs.writeFileSync(path.join(worktreeDir, 'feature.txt'), 'work\n');
execSync('git add -A', { cwd: worktreeDir, stdio: 'pipe' });
execSync('git commit -m "old work"', { cwd: worktreeDir, stdio: 'pipe' });
// Merge the branch into main from repoDir
execSync('git merge fix/old-work --no-ff -m "merge old-work"', { cwd: repoDir, stdio: 'pipe' });
// Act
const pruneOrphanedWorktrees = getPruneOrphanedWorktrees();
pruneOrphanedWorktrees(repoDir);
// Assert: worktree directory no longer exists
assert.ok(
!fs.existsSync(worktreeDir),
'worktree directory should have been removed but still exists: ' + worktreeDir
);
// Assert: git worktree list no longer shows it
const listOut = execSync('git worktree list', { cwd: repoDir, encoding: 'utf8' });
assert.ok(
!listOut.includes(worktreeDir),
'git worktree list still references removed worktree:\n' + listOut
);
});
// Test 2: keeps a worktree whose branch has unmerged commits
test('keeps a worktree whose branch has unmerged commits', () => {
const repoDir = path.join(tmpBase, 'repo2');
const worktreeDir = path.join(tmpBase, 'wt-active');
createGitRepo(repoDir);
// Create the worktree on a new branch (main is checked out in repoDir)
execSync('git worktree add "' + worktreeDir + '" -b fix/active-work', { cwd: repoDir, stdio: 'pipe' });
// Add a commit in the worktree (NOT merged into main)
fs.writeFileSync(path.join(worktreeDir, 'active.txt'), 'active\n');
execSync('git add -A', { cwd: worktreeDir, stdio: 'pipe' });
execSync('git commit -m "active work"', { cwd: worktreeDir, stdio: 'pipe' });
// main stays at its original commit — no merge
// Act
const pruneOrphanedWorktrees = getPruneOrphanedWorktrees();
pruneOrphanedWorktrees(repoDir);
// Assert: worktree directory still exists
assert.ok(
fs.existsSync(worktreeDir),
'worktree directory should NOT have been removed: ' + worktreeDir
);
});
// Test 3: never removes the worktree at process.cwd()
test('never removes the worktree at process.cwd()', () => {
const repoDir = path.join(tmpBase, 'repo3');
const wtDir = path.join(tmpBase, 'wt-cwd-test');
createGitRepo(repoDir);
// Create a worktree, add a commit, merge it into main
execSync('git worktree add "' + wtDir + '" -b fix/another-merged', { cwd: repoDir, stdio: 'pipe' });
fs.writeFileSync(path.join(wtDir, 'more.txt'), 'more\n');
execSync('git add -A', { cwd: wtDir, stdio: 'pipe' });
execSync('git commit -m "another merged"', { cwd: wtDir, stdio: 'pipe' });
execSync('git checkout main', { cwd: repoDir, stdio: 'pipe' });
execSync('git merge fix/another-merged --no-ff -m "merge another"', { cwd: repoDir, stdio: 'pipe' });
// Run pruning
const pruneOrphanedWorktrees = getPruneOrphanedWorktrees();
const pruned = pruneOrphanedWorktrees(repoDir);
// process.cwd() must not appear in pruned paths
assert.ok(
!pruned.includes(process.cwd()),
'process.cwd() should never be pruned, but found in: ' + JSON.stringify(pruned)
);
// The main worktree (repoDir) itself must still exist
assert.ok(
fs.existsSync(repoDir),
'main repo dir should still exist: ' + repoDir
);
});
// Test 4: runs git worktree prune to clear stale references
test('runs git worktree prune to clear stale references', () => {
const repoDir = path.join(tmpBase, 'repo4');
const worktreeDir = path.join(tmpBase, 'wt-stale');
createGitRepo(repoDir);
// Create a worktree
execSync('git worktree add "' + worktreeDir + '" -b fix/stale-ref', { cwd: repoDir, stdio: 'pipe' });
assert.ok(fs.existsSync(worktreeDir), 'worktree dir should exist before manual deletion');
// Verify it appears in git worktree list
const beforeList = execSync('git worktree list --porcelain', { cwd: repoDir, encoding: 'utf8' });
assert.ok(beforeList.includes(worktreeDir), 'worktree should appear in list before deletion');
// Manually delete the worktree directory (simulate orphan)
fs.rmSync(worktreeDir, { recursive: true, force: true });
// Act
const pruneOrphanedWorktrees = getPruneOrphanedWorktrees();
pruneOrphanedWorktrees(repoDir);
// Assert: git worktree list no longer shows the stale entry
const afterList = execSync('git worktree list --porcelain', { cwd: repoDir, encoding: 'utf8' });
assert.ok(
!afterList.includes(worktreeDir),
'git worktree list still shows stale entry after prune:\n' + afterList
);
});
});

View File

@@ -29,7 +29,7 @@ const HOOK_PATH = path.join(__dirname, '..', 'hooks', 'gsd-read-guard.js');
function runHook(payload, envOverrides = {}) {
const input = JSON.stringify(payload);
// Sanitize CLAUDE_SESSION_ID so positive-path tests work inside Claude Code sessions
const env = { ...process.env, CLAUDE_SESSION_ID: '', ...envOverrides };
const env = { ...process.env, CLAUDE_SESSION_ID: '', CLAUDECODE: '', ...envOverrides };
try {
const stdout = execFileSync(process.execPath, [HOOK_PATH], {
input,