Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
cbff63a
fix: rename reserved $input variable in PowerShell hooks (#17)
developzoneio Jul 2, 2026
5408c7c
fix: stop $Matches clobber in subagent-retro.ps1 spec-ID extraction (…
developzoneio Jul 2, 2026
c69fc95
fix: same-line in-progress detection parity in spec-gate.sh
developzoneio Jul 2, 2026
23fa100
fix: align feature.md and refactor.md subagent field names with contr…
developzoneio Jul 2, 2026
4a29d98
fix: make /sd:spec validate plan/tasks check type-aware
developzoneio Jul 2, 2026
b2f750d
fix: parse debounce timestamp as UTC in subagent-retro.ps1
developzoneio Jul 2, 2026
f38e8ef
fix: apply per-workflow keyword defaults in prompt-router.ps1
developzoneio Jul 2, 2026
4ac2647
fix: replace stale MCP tool names in agents, skills, and explore command
developzoneio Jul 2, 2026
e5b8520
fix: remove hardcoded MSSQL/C#/TS stack references from agents and co…
developzoneio Jul 2, 2026
e426237
fix: code-explorer impact-map returns analysis instead of self-appending
developzoneio Jul 2, 2026
a668962
fix: walk full lifecycle state machine in bug/rca/perf workflows
developzoneio Jul 2, 2026
9abea65
fix: guard workflow Phase 0 against missing or malformed Layer-2 context
developzoneio Jul 2, 2026
fc0552f
refactor: dedup rules copy-pasted from skills into agent/command bodies
developzoneio Jul 2, 2026
3996575
docs: fix README/ROADMAP/usage drift after v1.3.0+ shipped
developzoneio Jul 2, 2026
1bf4313
refactor: derive validate.sh/ps1 install counts from source tree
developzoneio Jul 2, 2026
fb9f020
feat: add macOS to CI matrix and hook smoke tests
developzoneio Jul 2, 2026
a679016
fix: harden install.sh - strict mode, prefix guard, quoting
developzoneio Jul 2, 2026
37dd195
fix: strip CRLF from jq keyword output in prompt-router.sh
developzoneio Jul 2, 2026
e3ce460
fix: make prompt-router.sh bash 3.2 compatible
developzoneio Jul 3, 2026
51eb8c7
docs: add review findings TODO before merge
developzoneio Jul 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: Checkout
uses: actions/checkout@v4

# --- Run the full invariant validator (platform-native) ---------------
- name: Validate (bash)
if: runner.os == 'Linux'
if: runner.os == 'Linux' || runner.os == 'macOS'
shell: bash
run: bash scripts/validate.sh

Expand All @@ -28,9 +28,21 @@ jobs:
shell: pwsh
run: ./scripts/validate.ps1

# --- Hook smoke tests: pipe fixture JSON into every hook, assert exit
# codes and key output substrings (not just "did not crash") -----------
- name: Hook smoke tests (bash)
if: runner.os == 'Linux' || runner.os == 'macOS'
shell: bash
run: bash scripts/smoke-hooks.sh

- name: Hook smoke tests (PowerShell)
if: runner.os == 'Windows'
shell: pwsh
run: ./scripts/smoke-hooks.ps1

# --- Install -> uninstall round-trip (CLAUDE.md sandbox recipe) --------
- name: Install -> uninstall round-trip (bash)
if: runner.os == 'Linux'
if: runner.os == 'Linux' || runner.os == 'macOS'
shell: bash
run: |
set -euo pipefail
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ desktop.ini
.claude/settings.local.json
.claude/.cache/

# ---- Local AI work docs (not shipped) ----
.ai-docs/

# ---- Logs ----
*.log
logs/
Expand Down
167 changes: 167 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,19 @@ A subagent is a markdown file with YAML frontmatter consumed by the `Task` tool.
```yaml
---
name: sd-<role>
color: <color> # e.g. cyan, orange, purple, green, blue - used for display only
description: One-line summary used by routing.
model: sonnet # MUST be an alias: sonnet | haiku | opus | inherit
tools: Read, Grep, Glob, ... # MINIMAL allowlist
skills:
- sd-<shared-rule-pack> # any skill this agent's body references; see Skills below
---
```

**Critical:**
- `model:` MUST be an alias. Full IDs like `claude-sonnet-4-7` are not portable and may not even exist. The alias `sonnet` auto-resolves to the latest Sonnet.
- `tools:` should be the minimum set the agent needs. Read-only agents do not get `Write`. Implementer does not get `WebSearch`.
- `skills:` must list every skill the agent body references (`**skill-name**` in prose). A rule used by multiple agents lives in one `SKILL.md`, never copy-pasted into agent bodies.
- Agent must read `CLAUDE.md` and `constitution.md` at runtime. No hardcoded stack assumptions (no `cs`, `csproj`, `dotnet`, etc. literal references unless they come from project config).
- Every finding cites `file:line`. No prose without citations.

Expand Down
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# specwright

> **Spec-driven development workflows for Claude Code.**
> Ten slash commands, five specialized subagents, three guard-rail hooks, nine templates, six reusable skills - all under the `sd:` namespace, stack-agnostic, cross-platform, and ready to drop into any project.
> Eleven slash commands, six specialized subagents, three guard-rail hooks, nine templates, six reusable skills - all under the `sd:` namespace, stack-agnostic, cross-platform, and ready to drop into any project.

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Claude Code](https://img.shields.io/badge/Claude%20Code-compatible-blue)](https://docs.claude.com/en/docs/claude-code)
Expand Down Expand Up @@ -88,7 +88,7 @@ Per-project artifacts (`.specs/`, `.claude/`, project `CLAUDE.md`) remain untouc

| Command | Type | Hard gates | Purpose |
|---|---|---|---|
| `/sd:feature <ID-or-slug>` | Workflow | 4 | Spec-driven feature: spec -> impact -> plan -> execute -> review -> close |
| `/sd:feature <ID-or-slug>` | Workflow | 3 | Spec-driven feature: spec -> impact -> plan -> execute -> batch review -> close |
| `/sd:bug <ID-or-slug>` | Workflow | 5 | Root-cause-first fix: capture -> reproduce -> investigate -> failing test -> minimal fix -> regression |
| `/sd:rca <slug>` | Workflow | 3 | Incident analysis. **Output is the spec - no code change.** |
| `/sd:refactor <slug>` | Workflow | 6 | Coverage-gated restructure: requires >=80% coverage before touching code |
Expand All @@ -98,6 +98,7 @@ Per-project artifacts (`.specs/`, `.claude/`, project `CLAUDE.md`) remain untouc
| `/sd:review [path / "recent" / "spec ID"]` | Utility | - | Standalone constitution-compliance review with severity tags |
| `/sd:setup` | Utility | - | Idempotent project scaffold (interactive) |
| `/sd:release [version]` | Utility | 1 | Release notes from `done` specs -> Keep-a-Changelog sections, then archive them |
| `/sd:adr <spec-ID \| "decision title">` | Utility | 1 | Author an ADR from a spec's decisions under `.specs/_adr/` |

---

Expand All @@ -107,9 +108,10 @@ Per-project artifacts (`.specs/`, `.claude/`, project `CLAUDE.md`) remain untouc
|---|---|---|---|
| `sd-spec-architect` | sonnet | Read, Write, Edit, Grep, Glob, Atlassian MCP, Context7 MCP | Create / refine specs, plans, and tasks. Constitution-aware. |
| `sd-code-explorer` | haiku | Read, Grep, Glob, GitNexus MCP | Read-only navigation. Every finding cites `file:line`. |
| `sd-debugger` | sonnet | Read, Grep, Glob, Bash, sequential-thinking, GitNexus, MSSQL (SELECT only), Tavily, Context7 | Hypothesis-tree investigation. Distinguishes proximate vs root cause. |
| `sd-debugger` | sonnet | Read, Grep, Glob, Bash, sequential-thinking, GitNexus, Tavily, Context7 | Hypothesis-tree investigation. Distinguishes proximate vs root cause. |
| `sd-implementer` | haiku | Read, Write, Edit, MultiEdit, Grep, Glob, Bash, Context7 | Executes ONE atomic task. Scope-disciplined, no opportunism. |
| `sd-reviewer` | sonnet | Read, Grep, Glob, sequential-thinking, GitNexus | Severity-tagged review: BLOCK / WARN / SUGGEST / PASS. |
| `sd-docs-writer` | sonnet | Read, Write, Glob, Grep | Authors one MADR-style ADR from a spec's decisions. Writes only the ADR file. |

All models use **portable aliases** (`sonnet`, `haiku`) so they auto-update.

Expand Down Expand Up @@ -154,6 +156,9 @@ Every project that adopts `specwright` ends up with:
.specs/
constitution.md # Architectural rules + conventions + quality bars
index.md # Registry of all specs with lifecycle states
_explorations/ # Scratchpad for /sd:explore saves
_reviews/ # Scratchpad for /sd:review saves
_adr/ # Architecture decision records from /sd:adr
FEAT-INV-2501/ # One folder per spec
00-spec.md # Why / What / Success criteria / Constitution check
01-plan.md # Implementation plan
Expand Down Expand Up @@ -257,8 +262,7 @@ Configure per project in `.claude/project-config.json` under the `mcp` section.
Forward-looking work lives in [`ROADMAP.md`](ROADMAP.md). Highlights:

- **Near-term** - GitHub Issue auto-fetch (`gh issue view`) to match the existing JIRA snapshot path.
- **Planned** - `/sd:setup` codebase scan (detected defaults instead of `<<placeholder>>`s) and an
optional `sd-docs-writer` agent for ADRs.
- **Planned** - nothing queued right now.
- **Exploratory** - local-only, opt-in usage analytics.

Shipped work is in [`CHANGELOG.md`](CHANGELOG.md).
Expand Down
45 changes: 45 additions & 0 deletions REVIEW-TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Review findings to fix (from 2026-07-03 deep review) - delete this file when done

Branch: hotfix/ps-hooks-audit. CI is green; these are defects found in the branch's own new code.

## Must fix before PR

1. `install/install.sh:93` - guard validates PREFIX but not BASE_PATH.
- `--base-path ""` accepted -> install plan targets filesystem root (`/commands/sd` ...).
- Also `install.sh:74`: `--base-path` with NO value -> `shift 2` fails under `set -e`, dies exit 1 with zero output.
- Fix: reject empty/whitespace BASE_PATH; print usage on missing flag value. Mirror in install.ps1.

2. `install/install.sh:204` - partial-install ERR trap never fires.
- `trap on_error ERR` without `set -E` does not fire inside functions; all copy work is in copy_one().
- Fix: change line 16 to `set -Eeuo pipefail` (or trap EXIT + exit-code check).
- Also: install.ps1 has NO equivalent partial-install guard at all - add one (pairs rule).

3. `hooks/bash/subagent-retro.sh:~166` - UTC debounce fix landed only in the PS twin.
- macOS fallback `date -j -f '%Y-%m-%dT%H:%M:%S'` parses saved UTC timestamp as LOCAL time.
- On UTC+7 Mac: debounce always elapsed -> retro-reminder spam; smoke test fails locally (CI green only because runners are UTC).
- Fix: parse as UTC (e.g. append `TZ=UTC0` / use `-u`), mirror the ps1 fix (hooks ship in pairs).

4. `commands/rca.md:78` - Phase 2 step 3 still passive: "Hypothesis tree written to 00-spec.md".
- Debugger has no Write tool -> tree never persisted, Gate 2 empty.
- Fix: reword to "Main thread appends the returned hypothesis tree ..." (match bug.md:111 / perf.md / rca.md Phase 3).

## Judgment calls (fix or file follow-up issues)

5. `agents/debugger.md:6` + body line ~46 - body prescribes "project-provided database MCP tool"
but frontmatter allowlist has no DB tool -> path unreachable. Also docs/architecture.md:92,309
still documents removed mcp__mssql__execute_sql.

6. `scripts/validate.sh:26` + `scripts/validate.ps1:39` - counts derived from source tree are
self-referential: a deleted/renamed asset moves expected+actual in lockstep, CI stays green.
Consider minimum-count floor or manifest.

## Cleanups (fast-follow OK)

7. `scripts/smoke-hooks.sh:1` - add `set -euo pipefail` (repo bash rule); run_hook line 96 needs
`CODE=0; ... || CODE=$?` to stay set-e-safe.
8. `scripts/smoke-hooks.sh:106` - add jq preflight: `command -v jq || { echo 'jq required'; exit 1; }`
(hooks exit 0 silently without jq -> assertions blame the hooks).
9. `commands/bug.md:42` (+ feature.md:32, perf.md:41, rca.md:30, refactor.md:44) - Phase 0 bootstrap
guard copy-pasted 5x, already drifted in feature.md -> dedupe into a shared skill/rule pack.
10. `install/install.sh:94` - prefix emptiness check `${PREFIX// /}` strips spaces only; use
`${PREFIX//[[:space:]]/}` to match install.ps1's IsNullOrWhiteSpace.
9 changes: 2 additions & 7 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,8 @@ Small, well-scoped items targeted at the next minor release.

Larger items that each warrant a full `/sd:feature` spec before building.

- **`/sd:setup` codebase scan** - pre-fill `constitution.md` (architectural rules, conventions) and
`CLAUDE.md` from sampled source files instead of leaving `<<placeholder>>`s, with an optional
`paths.layers` map in `project-config.json`. Turns first-run setup from blank-template into
detected-defaults.
- **`sd-docs-writer` agent** - an optional subagent that authors Architecture Decision Records (ADRs)
and architecture docs from spec artifacts, promoting durable `.specs/` decisions into human-facing
documentation.
_Nothing queued right now - the last two Planned items (`/sd:setup` codebase scan, `sd-docs-writer`
agent) shipped; see [`CHANGELOG.md`](CHANGELOG.md)._

## Exploratory

Expand Down
30 changes: 17 additions & 13 deletions agents/code-explorer.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: sd-code-explorer
color: cyan
description: Read-only code navigation. Six task types covering definition, callers, traces, impact mapping, pattern search, and structural overview. Every finding cites file:line. Use this agent for any read-only exploration; do NOT invoke for fixes or refactors.
model: haiku
tools: Read, Grep, Glob, mcp__gitnexus__search, mcp__gitnexus__get_file, mcp__gitnexus__find_references, mcp__gitnexus__get_call_graph, mcp__gitnexus__list_symbols
tools: Read, Grep, Glob, mcp__gitnexus__query, mcp__gitnexus__context, mcp__gitnexus__impact, mcp__gitnexus__list_repos
skills:
- sd-evidence-citation
---
Expand All @@ -17,7 +17,7 @@ You are the code explorer for specwright. You navigate codebases and report find
1. **Read `CLAUDE.md`** for stack hints (file extensions, layer names, conventions).
2. Read the `TASK` field. It selects which workflow you run.
3. Check `GITNEXUS_AVAILABLE` (passed by the caller from `project-config.mcp.gitnexus.enabled`):
- `true` -> GitNexus-first. Test with a cheap call (e.g. `mcp__gitnexus__list_symbols` on a known small file). If it fails, fall back to grep with a noted caveat.
- `true` -> GitNexus-first. Test with a cheap call (e.g. `mcp__gitnexus__list_repos`). If it fails, fall back to grep with a noted caveat.
- `false` -> grep / Glob only. Add to your output: "GitNexus disabled - transitive callers and call graphs may be incomplete."

---
Expand All @@ -32,14 +32,17 @@ Route internally based on `DETECTED_INTENT`. Use the matching sub-routine below.

### `TASK = impact-map`

Inputs: `SPEC` (path to `00-spec.md`), `OUTPUT_APPEND_TO` (typically `03-decisions.md`).
Inputs: `SPEC` (path to `00-spec.md`), `OUTPUT_TARGET` (informational - typically `03-decisions.md`;
identifies which file the caller will append your output to).

Behavior:
1. Read the spec. Identify the target: feature scope, bug-affected components, refactor primary file(s), or perf hotspot endpoint.
2. Produce structured analysis (sections below). APPEND to `OUTPUT_APPEND_TO`. Do not overwrite.
3. For "Precedents & conventions": derive conventions by sampling, never by stack assumption - `Glob` the target directory, then `Read` the top ~30 lines (or `mcp__gitnexus__list_symbols`) of at most 3 sibling files, and state the observed pattern with evidence.
2. Produce the structured analysis (sections below) as your final output. Do not attempt to write
files - your tool allowlist has no `Write`/`Edit` by design. The calling command appends your
returned analysis to `OUTPUT_TARGET`.
3. For "Precedents & conventions": derive conventions by sampling, never by stack assumption - `Glob` the target directory, then `Read` the top ~30 lines (or `mcp__gitnexus__query` with a goal naming the directory) of at most 3 sibling files, and state the observed pattern with evidence.

Structure of appended content:
Structure of the returned analysis (the caller appends this verbatim):

```markdown
## Impact analysis (sd-code-explorer)
Expand Down Expand Up @@ -91,23 +94,23 @@ Structure of appended content:

Inputs: `SYMBOL` or `QUERY`.

GitNexus-first: `mcp__gitnexus__find_references` with the symbol. Fall back: `Grep` for invocation patterns (`SymbolName(`, `\.SymbolName\(`).
GitNexus-first: `mcp__gitnexus__impact` with `target: SYMBOL`, `direction: upstream`. Fall back: `Grep` for invocation patterns (`SymbolName(`, `\.SymbolName\(`).

Output: list of `file:line` with the calling context (one line of code).

### `TASK = definition`

Inputs: `SYMBOL` or `QUERY`.

GitNexus-first: `mcp__gitnexus__list_symbols` filtered by name, then `mcp__gitnexus__get_file` for context. Fall back: `Grep` for definition markers (e.g. `class SymbolName`, `def SymbolName`, `function SymbolName`, `interface SymbolName`).
GitNexus-first: `mcp__gitnexus__context` with the symbol name (pass `file_path` to disambiguate if multiple candidates are returned). Fall back: `Grep` for definition markers (e.g. `class SymbolName`, `def SymbolName`, `function SymbolName`, `interface SymbolName`).

Output: `file:line` + 5-line snippet showing the definition.

### `TASK = trace`

Inputs: `ENTRY_POINT` (symbol or `file:line`), optional `DEPTH` (default 2).

GitNexus-first: `mcp__gitnexus__get_call_graph` with the entry point and depth. Fall back: recursive `Grep` for callers up to `DEPTH` hops (note: imprecise for dynamic dispatch).
GitNexus-first: `mcp__gitnexus__impact` with `target: ENTRY_POINT`, `direction: downstream`, `maxDepth: DEPTH`. Fall back: recursive `Grep` for callers up to `DEPTH` hops (note: imprecise for dynamic dispatch).

Output: indented tree with `file:line` at each node.

Expand All @@ -123,7 +126,7 @@ Output: grouped by file when >5 hits in one file. Limit total to 50 results; tel

Inputs: `PATH` (directory) or none (project root).

Use `Glob` to list files, `mcp__gitnexus__list_symbols` per file (or top-of-file `Read` for the first 30 lines).
Use `Glob` to list files, `mcp__gitnexus__query` (goal naming the directory) for a symbol overview (or top-of-file `Read` for the first 30 lines).

Output: tree of directories + files + top-level symbols per file.

Expand All @@ -141,10 +144,11 @@ Additional rules:

## Anti-patterns (do NOT do these)

Apply the **sd-evidence-citation** skill's Anti-patterns section in full (no citation = invalid,
trusting memory over a live grep, vendored/generated directories without explicit request).

Explorer-specific, not covered by the skill:
- **Suggesting fixes.** You report. The reviewer or implementer decides what to do.
- **Opining on code quality.** "This is poorly structured" is not a finding. "Class `Foo` has 12 callers across 3 layers" is a finding.
- **Modifying files.** Your tool allowlist excludes `Write` / `Edit` / `MultiEdit` precisely for this reason.
- **Producing prose without citations.** "I noticed that..." with no `file:line` is invalid output. Re-prompt yourself.
- **Trusting your memory over the grep.** If you "recall" that a class lives in `src/Foo.cs`, that recall is stale by default - verify with `Glob` or `Read`.
- **Burning tool calls when a single grep suffices.** Haiku model = cost-aware. Plan the cheapest sequence that answers the question. 1 GitNexus call > 4 greps when GitNexus is enabled; 1 grep > 4 file reads when the pattern is known.
- **Following call graphs into vendored / generated code** (e.g. `node_modules/`, `bin/`, `obj/`) unless the caller explicitly asks. Filter those out.
Loading
Loading