@benjaminthomas/spec-driven-dev 1.8.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +150 -63
  3. package/bin/cli.mjs +76 -13
  4. package/package.json +3 -3
  5. package/{skills → plugin/skills}/spec-checkpoint/SKILL.md +23 -0
  6. package/{skills → plugin/skills}/spec-create/SKILL.md +18 -1
  7. package/plugin/skills/spec-create/references/log-template.md +27 -0
  8. package/{skills → plugin/skills}/spec-create/references/readme-template.md +12 -0
  9. package/{skills → plugin/skills}/spec-create/references/requirements-template.md +12 -4
  10. package/{skills → plugin/skills}/spec-create/references/task-template.md +12 -0
  11. package/{skills → plugin/skills}/spec-implement/SKILL.md +25 -6
  12. package/{skills → plugin/skills}/spec-investigate/SKILL.md +29 -5
  13. package/{skills → plugin/skills}/spec-investigate/references/report-template.md +7 -0
  14. package/plugin/skills/spec-review/SKILL.md +131 -0
  15. package/plugin/skills/spec-review/references/report-template.md +44 -0
  16. package/{skills → plugin/skills}/spec-setup/SKILL.md +34 -9
  17. package/plugin/skills/spec-setup/references/audit.md +71 -0
  18. package/{skills → plugin/skills}/spec-setup/references/config-template.yml +7 -2
  19. package/plugin/skills/spec-setup/references/context-template.md +83 -0
  20. package/{skills → plugin/skills}/spec-ship/SKILL.md +10 -0
  21. package/{skills → plugin/skills}/spec-verify/SKILL.md +10 -2
  22. /package/{skills → plugin/skills}/generative-seo/SKILL.md +0 -0
  23. /package/{skills → plugin/skills}/generative-seo/assets/citation-log-template.md +0 -0
  24. /package/{skills → plugin/skills}/generative-seo/assets/competitor-analysis-template.md +0 -0
  25. /package/{skills → plugin/skills}/generative-seo/assets/content-plan-template.md +0 -0
  26. /package/{skills → plugin/skills}/generative-seo/assets/geo-config-template.md +0 -0
  27. /package/{skills → plugin/skills}/generative-seo/assets/geo-playbook-template.md +0 -0
  28. /package/{skills → plugin/skills}/generative-seo/assets/social-queue-template.md +0 -0
  29. /package/{skills → plugin/skills}/generative-seo/references/geo-methodology.md +0 -0
  30. /package/{skills → plugin/skills}/spec-create/references/action-required-template.md +0 -0
  31. /package/{skills → plugin/skills}/spec-create/references/complexity-rubric.md +0 -0
  32. /package/{skills → plugin/skills}/spec-implement/references/coder-prompt-template.md +0 -0
  33. /package/{skills → plugin/skills}/spec-implement/references/fix-prompt-template.md +0 -0
  34. /package/{skills → plugin/skills}/spec-implement/references/review-prompt-template.md +0 -0
  35. /package/{skills → plugin/skills}/spec-verify/references/report-template.md +0 -0
package/bin/cli.mjs CHANGED
@@ -5,10 +5,11 @@
5
5
  // spec-driven-dev install + project setup (interactive)
6
6
  // spec-driven-dev setup project setup only
7
7
  // spec-driven-dev doctor check .spec-driven.yml against the real repos (exit 1 on problems)
8
- // spec-driven-dev status list specs with unfinished tasks (used by the session-start hook)
8
+ // spec-driven-dev status list specs with unfinished work and each one's "Resume here" next step
9
+ // spec-driven-dev context print specs/CONTEXT.md; --areas=billing,api prints only those area files
9
10
  // flags: --yes --global --tools=claude,cursor --skills=spec-create,... --no-setup --dir=<path>
10
11
  // --layout=single|monorepo|multi-repo --repos=~/Projects/frontend,/srv/backend --workspace=<config dir>
11
- // --packages=apps/web,packages/ui --after-approval=stop|run-through --version
12
+ // --packages=apps/web,packages/ui --after-approval=stop|run-through --project=new|existing --version
12
13
  import fs from "node:fs";
13
14
  import os from "node:os";
14
15
  import path from "node:path";
@@ -17,7 +18,7 @@ import prompts from "prompts"; // arrow-key menus that work in PowerShell, cmd,
17
18
  import { fileURLToPath } from "node:url";
18
19
 
19
20
  const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
20
- const SKILLS_DIR = path.join(ROOT, "skills");
21
+ const SKILLS_DIR = path.join(ROOT, "plugin", "skills");
21
22
  const HOME = process.env.SPEC_DRIVEN_HOME || os.homedir(); // overridable for tests
22
23
  const ALL_SKILLS = fs.readdirSync(SKILLS_DIR).filter((d) => fs.existsSync(path.join(SKILLS_DIR, d, "SKILL.md")));
23
24
 
@@ -33,7 +34,7 @@ const args = process.argv.slice(2);
33
34
  const flag = (n) => args.includes(`--${n}`);
34
35
  const opt = (n) => args.find((a) => a.startsWith(`--${n}=`))?.split("=")[1];
35
36
  const command = args.find((a) => !a.startsWith("-")) || "install";
36
- const YES = flag("yes") || flag("y") || !process.stdin.isTTY || ["doctor", "status"].includes(command);
37
+ const YES = flag("yes") || flag("y") || !process.stdin.isTTY || ["doctor", "status", "context"].includes(command);
37
38
  const DIR = path.resolve(opt("dir") || process.cwd());
38
39
 
39
40
  if (flag("version") || flag("v")) {
@@ -42,7 +43,7 @@ if (flag("version") || flag("v")) {
42
43
  }
43
44
  // No terminal to answer in (e.g. `!` in Claude Code, an IDE task, a pipe) and nothing passed explicitly:
44
45
  // stop rather than write a config from guesses.
45
- const ANSWER_FLAGS = ["yes", "y", "layout", "repos", "packages", "tools", "workspace", "after-approval", "skills", "global", "no-setup"];
46
+ const ANSWER_FLAGS = ["yes", "y", "layout", "repos", "packages", "tools", "workspace", "after-approval", "skills", "global", "no-setup", "project"];
46
47
  if (["install", "setup"].includes(command) && !process.stdin.isTTY && !args.some((a) => ANSWER_FLAGS.some((f) => a === `--${f}` || a.startsWith(`--${f}=`)))) {
47
48
  console.log(`✗ No interactive terminal, so setup can't ask its questions. Nothing was changed.
48
49
 
@@ -109,7 +110,7 @@ async function install() {
109
110
 
110
111
  // Claude Code users who already have the plugin would get every skill twice.
111
112
  const plugins = path.join(HOME, ".claude/plugins/installed_plugins.json");
112
- if (tools.includes("claude") && !opt("tools") && fs.existsSync(plugins) && fs.readFileSync(plugins, "utf8").includes('"spec-driven-dev@')) {
113
+ if (tools.includes("claude") && !opt("tools") && fs.existsSync(plugins) && /"(spec-driven-dev|ticket-to-pr)@/.test(fs.readFileSync(plugins, "utf8"))) {
113
114
  console.log("• Claude Code already has the spec-driven-dev plugin, so it's skipped here (update it with /plugin).");
114
115
  tools = tools.filter((t) => t !== "claude");
115
116
  }
@@ -239,7 +240,8 @@ function toYaml(c) {
239
240
  .map(([tool, m]) => ` ${tool}:\n` + MODEL_KEYS.map((t) => ` ${t}: ${m[t]}\n`).join("")).join("");
240
241
  out += "effort: # effort / reasoning level per tier (Claude Code, Codex): low | medium | high | xhigh | max | default\n"
241
242
  + MODEL_KEYS.map((t) => ` ${t}: ${EFFORT_DEFAULTS[t]}\n`).join("");
242
- out += `pipeline:\n after_approval: ${c.afterApproval || "stop"} # stop | run-through (implement → verify → checkpoint; ship always asks)\n`;
243
+ out += `context: # project memory: {specs_dir}/CONTEXT.md (index) + context/<area>.md\n project: ${c.project || "existing"} # existing | new\n graph: auto # auto = use graphify if installed | off\n`;
244
+ out += `pipeline:\n after_approval: ${c.afterApproval || "stop"} # stop | run-through (implement → verify → review → checkpoint; ship always asks)\n`;
243
245
  return out + `specs_dir: ${y(c.specsDir)}\n`;
244
246
  }
245
247
 
@@ -381,7 +383,16 @@ async function choosePackages(d) {
381
383
  return { root, repos: pkgs };
382
384
  }
383
385
 
386
+ // shortcut: counts only (no model here), so a big template repo with 1 commit reads as existing; the spec-setup skill confirms.
387
+ function detectProject(dirs) {
388
+ const n = (d, a) => Number(run("git", a, d).split("\n").filter(Boolean).length);
389
+ const commits = dirs.reduce((t, d) => t + (Number(run("git", ["rev-list", "--count", "HEAD"], d)) || 0), 0);
390
+ const files = dirs.reduce((t, d) => t + n(d, ["ls-files"]), 0);
391
+ return { commits, files, kind: commits >= 20 || files >= 50 ? "existing" : "new" };
392
+ }
393
+
384
394
  async function setup() {
395
+ if (opt("project") && !["new", "existing"].includes(opt("project"))) throw new Error("--project must be new or existing");
385
396
  if (opt("after-approval") && !["stop", "run-through"].includes(opt("after-approval"))) throw new Error("--after-approval must be stop or run-through");
386
397
  console.log("\nProject setup — detecting how this project works…");
387
398
  const d = detectLayout(DIR);
@@ -439,11 +450,17 @@ async function setup() {
439
450
  const specsDir = await askText("Folder for specs (one sub-folder per feature)", "specs");
440
451
  const afterApproval = opt("after-approval") || await askSelect("After you approve a spec…", [
441
452
  { title: "Stop after each stage", value: "stop", description: "you start implement, verify and checkpoint yourself" },
442
- { title: "Run straight through", value: "run-through", description: "implement → verify → commit in one go; stops at any failure, never pushes without asking" },
453
+ { title: "Run straight through", value: "run-through", description: "implement → verify → review → commit in one go; stops at any failure or review blocker, never pushes without asking" },
443
454
  ], "stop");
444
455
 
456
+ const pd = detectProject(repoDirs.filter(isRepo));
457
+ const project = opt("project") || await askSelect("Is this a new project or an existing one?", [
458
+ { title: `Existing project${pd.kind === "existing" ? " (detected)" : ""}`, value: "existing", description: `${pd.commits} commits, ${pd.files} files — your AI tool audits it once and saves what it learns` },
459
+ { title: `New project${pd.kind === "new" ? " (detected)" : ""}`, value: "new", description: "nothing to audit — your AI tool asks about the stack and conventions you want" },
460
+ ], pd.kind);
461
+
445
462
  const config = {
446
- layout, tracker: { type: trackerType, key, url }, specsDir, devUrl: "", afterApproval,
463
+ project, layout, tracker: { type: trackerType, key, url }, specsDir, devUrl: "", afterApproval,
447
464
  git: { remote: "origin", base_branch: base, pr_target: pr, branch_name: branchName, commit_style: commitStyle },
448
465
  commands: layout === "single" ? commandsFor(repoDirs[0]) : commandsFor(root),
449
466
  repos: repos.map((r) => {
@@ -456,7 +473,9 @@ async function setup() {
456
473
  if (layout === "multi-repo") register(real(file), repos.map((r) => real(path.resolve(root, r))));
457
474
  console.log(`✓ Wrote ${file}`);
458
475
  console.log(isRepo(root) ? " Commit it so your team shares the same rules." : " This folder isn't a git repo, so the config is personal to this machine.");
459
- console.log(' Fill in repo roles, dev_url, and non-Node commands by asking your AI tool to "run spec-setup".');
476
+ console.log(project === "existing"
477
+ ? ` Next: ask your AI tool to "run spec-setup". It audits the ${layout === "multi-repo" ? "repos" : "repo"}, asks a few questions, and saves ${specsDir}/CONTEXT.md.`
478
+ : ` Next: ask your AI tool to "run spec-setup". It asks about your stack and conventions and saves ${specsDir}/CONTEXT.md.`);
460
479
  }
461
480
 
462
481
  // ---------- reading the config ----------
@@ -554,6 +573,10 @@ function doctor() {
554
573
  }
555
574
  const specs = path.join(root, c.specs_dir || "specs");
556
575
  if (!fs.existsSync(specs)) notes.push(`${c.specs_dir || "specs"}/ doesn't exist yet — created on first spec`);
576
+ if (c.context?.project && !["new", "existing"].includes(c.context.project)) problems.push(`context.project "${c.context.project}" must be new or existing`);
577
+ if (c.context?.graph && !["auto", "off"].includes(c.context.graph)) problems.push(`context.graph "${c.context.graph}" must be auto or off`);
578
+ const repoRoots = c.layout === "multi-repo" ? (c.repos || []).map((r) => path.resolve(root, expandHome(r.path))) : [root];
579
+ checkContext(specs, c.specs_dir || "specs", repoRoots, problems, notes);
557
580
  console.log(`Checked ${file}`);
558
581
  for (const n of notes) console.log(` • ${n}`);
559
582
  for (const p of problems) console.log(` ✗ ${p}`);
@@ -561,7 +584,43 @@ function doctor() {
561
584
  else console.log("✓ Config matches the repos.");
562
585
  }
563
586
 
564
- // Silent unless something is unfinished, so it costs no context in a session start hook.
587
+ const LIMITS = { "CONTEXT.md": 60, area: 150 };
588
+ // Context lines point at code as `path` or `path:line`; flag the ones that no longer exist anywhere in the repos.
589
+ function checkContext(specs, specsName, repoRoots, problems, notes) {
590
+ const index = path.join(specs, "CONTEXT.md");
591
+ if (!fs.existsSync(index)) { notes.push(`no ${specsName}/CONTEXT.md yet — ask your AI tool to "run spec-setup" to audit the project`); return; }
592
+ const areaDir = path.join(specs, "context");
593
+ const files = [index, ...(fs.existsSync(areaDir) ? fs.readdirSync(areaDir).filter((f) => f.endsWith(".md") && f !== "archive.md").map((f) => path.join(areaDir, f)) : [])];
594
+ for (const f of files) {
595
+ const text = fs.readFileSync(f, "utf8");
596
+ const rel = path.relative(specs, f), max = f === index ? LIMITS["CONTEXT.md"] : LIMITS.area, lines = text.split("\n").length;
597
+ if (lines > max) problems.push(`${specsName}/${rel} is ${lines} lines (limit ${max}) — merge duplicates or move old entries to context/archive.md`);
598
+ for (const [, p] of text.matchAll(/`([\w.@-]+(?:\/[\w.@-]+)+\/?)(?::\d+)?`/g)) {
599
+ if (p.startsWith("context/") || /^(?:https?|www|@)/.test(p)) continue; // @scope/pkg is a package name, not a path
600
+ if (!repoRoots.some((r) => fs.existsSync(path.join(r, p)))) notes.push(`${specsName}/${rel} mentions \`${p}\`, which no longer exists — stale context?`);
601
+ }
602
+ }
603
+ }
604
+
605
+ function context() {
606
+ const file = findConfig(DIR);
607
+ const root = file ? path.dirname(file) : DIR;
608
+ const specsName = (file && parseYaml(fs.readFileSync(file, "utf8")).specs_dir) || "specs";
609
+ const index = path.join(root, specsName, "CONTEXT.md");
610
+ const areas = splitList(opt("areas"));
611
+ if (!areas.length) {
612
+ if (!fs.existsSync(index)) { console.log(`No ${specsName}/CONTEXT.md yet. Ask your AI tool to "run spec-setup" to create it.`); return; }
613
+ process.stdout.write(fs.readFileSync(index, "utf8"));
614
+ return;
615
+ }
616
+ for (const a of areas) {
617
+ const f = path.join(root, specsName, "context", `${a.replace(/[^\w-]/g, "")}.md`);
618
+ if (fs.existsSync(f)) process.stdout.write(fs.readFileSync(f, "utf8").replace(/\n*$/, "\n\n"));
619
+ else console.log(`(no ${specsName}/context/${a}.md — nothing recorded for this area yet)\n`);
620
+ }
621
+ }
622
+
623
+ // Silent unless something is unfinished, so it's cheap to run at the start of a session.
565
624
  function status() {
566
625
  const file = findConfig(DIR);
567
626
  const root = file ? path.dirname(file) : DIR;
@@ -575,9 +634,12 @@ function status() {
575
634
  continue;
576
635
  }
577
636
  const t = fs.readFileSync(readme, "utf8");
637
+ const next = t.match(/^## Resume here[\s\S]*?^- \*\*Next:\*\*\s*(.+)$/m)?.[1]?.trim();
578
638
  const done = (t.match(/^\s*- \[x\]/gim) || []).length, todo = (t.match(/^\s*- \[ \]/gm) || []).length;
579
- if (todo) lines.push(`- ${specsName}/${name}: ${done}/${done + todo} tasks done → next: spec-implement`);
639
+ if (todo) lines.push(`- ${specsName}/${name}: ${done}/${done + todo} tasks done → next: ${next || "spec-implement"}`);
580
640
  else if (done && !fs.existsSync(path.join(specs, name, "verification-report.md"))) lines.push(`- ${specsName}/${name}: all tasks done, not verified → next: spec-verify`);
641
+ else if (done && !fs.existsSync(path.join(specs, name, "review.md"))) lines.push(`- ${specsName}/${name}: verified, not reviewed → next: spec-review`);
642
+ else if (done && /\[blocker\][^\n]*\n(?:(?!###)[^\n]*\n)*?- \*\*Status:\*\* open/.test(fs.readFileSync(path.join(specs, name, "review.md"), "utf8"))) lines.push(`- ${specsName}/${name}: review has open blockers → next: fix them (spec-review)`);
581
643
  }
582
644
  if (lines.length) console.log(`spec-driven-dev: unfinished work in ${root}\n${lines.join("\n")}`);
583
645
  }
@@ -585,11 +647,12 @@ function status() {
585
647
  try {
586
648
  if (command === "doctor") doctor();
587
649
  else if (command === "status") status();
650
+ else if (command === "context") context();
588
651
  else if (command === "install") {
589
652
  await install();
590
653
  if (!flag("no-setup") && (await askConfirm("Set up this project now?", true))) await setup();
591
654
  } else if (command === "setup") await setup();
592
- else throw new Error(`Unknown command "${command}" — use install (default), setup, doctor, or status`);
655
+ else throw new Error(`Unknown command "${command}" — use install (default), setup, doctor, status, or context`);
593
656
  if (["install", "setup"].includes(command)) console.log('\nNext: in your AI tool, say "investigate <ticket>" or "create a spec".');
594
657
  } catch (e) {
595
658
  console.error(`✗ ${e.message}`);
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@benjaminthomas/spec-driven-dev",
3
- "version": "1.8.0",
4
- "description": "Spec-driven AI development workflow (investigate → spec → implement → verify → ship) as Agent Skills for Claude Code, Cursor, Codex CLI and Antigravity.",
3
+ "version": "1.10.0",
4
+ "description": "Spec-driven AI development workflow (investigate → spec → implement → verify → review → ship) as Agent Skills for Claude Code, Cursor, Codex CLI and Antigravity.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "spec-driven-dev": "bin/spec-driven-dev.cjs"
8
8
  },
9
9
  "files": [
10
10
  "bin",
11
- "skills"
11
+ "plugin/skills"
12
12
  ],
13
13
  "scripts": {
14
14
  "test": "python3 scripts/check.py && node scripts/test-cli.mjs"
@@ -49,6 +49,26 @@ issues before proceeding — do not skip or ignore failures.
49
49
 
50
50
  Iterate until every check passes cleanly. Only then move to the next step.
51
51
 
52
+ ### Step 2b: Update project context (spec work only)
53
+
54
+ Skip this step unless the changes belong to a spec (`{specs_dir}/{feature}/` exists for this work).
55
+
56
+ 1. Overwrite the README's **Resume here** (`Stage: committed`, `Next: spec-ship`). Write
57
+ `Last commit: this checkpoint (git log -1)`, because the commit's hash doesn't exist yet and
58
+ amending afterwards would change it again.
59
+ 2. **When the feature is done** (every task checked) and `{specs_dir}/CONTEXT.md` exists, distil
60
+ the lasting lessons. Use the light model tier for this where the host lets you pick a subagent
61
+ model.
62
+ - Read the feature's `log.md`. This is the one time it's read in full.
63
+ - Keep only what a future feature in the same area needs: decisions and why, conventions found,
64
+ pitfalls. Drop routine progress.
65
+ - Add them to the end of the right `context/<area>.md` (create it if the area has none), and add
66
+ one line to CONTEXT.md's **Specs** list. Follow the rules in
67
+ references/context-template.md inside the installed `spec-setup` skill folder (not this repo): dated, with a source, added at the end, within the size
68
+ limits, superseded lines moved to `context/archive.md`.
69
+ - Show the context changes in the report so the user can correct them.
70
+ 3. No CONTEXT.md? Skip the distilling and suggest running `spec-setup` to create one.
71
+
52
72
  ### Step 3: Stage Everything
53
73
 
54
74
  Stage all changes — tracked modifications, deletions, and new untracked files:
@@ -65,6 +85,9 @@ anything you don't recognize; skim its diff if you're unsure.
65
85
 
66
86
  ### Step 4: Craft the Commit Message
67
87
 
88
+ Check `{specs_dir}/CONTEXT.md`'s **Conventions** first, if it exists. A recorded rule about commits
89
+ (authorship, trailers, format) beats the defaults below.
90
+
68
91
  Write a commit message in the config's `git.commit_style` (`conventional` → `feat: …`, `ticket` → `STP-123: …` using the ticket ID from the branch name, `freeform`). Without config, follow the project's existing conventions (observed from `git log`). Structure:
69
92
 
70
93
  - **First line**: clear, concise summary in imperative mood (50-72 chars)
@@ -32,6 +32,19 @@ isn't, detect what this skill needs from the repo, ask about anything ambiguous,
32
32
  the answers to `.spec-driven.yml` (keys: `layout`, `repos`, `tracker`, `git`, `commands`,
33
33
  `specs_dir`).
34
34
 
35
+ ## Project context
36
+
37
+ - **Take context from the investigation.** Use `investigation.md`'s **Relevant Context**. Without an
38
+ investigation, read `{specs_dir}/CONTEXT.md` (or `spec-driven-dev context`) and only the
39
+ `context/<area>.md` files this feature touches. Never load the archive.
40
+ - **Copy, don't link.** Put only the 3–5 lines each task needs into that task's **Project Context**.
41
+ Coders never read the context files, which keeps parallel coders from each loading them.
42
+ - **Trace requirements.** Number them R1, R2… (keep the investigation's numbers), fill **Covers** in
43
+ every task and the **Coverage** table in requirements.md. Don't finish while a requirement has no
44
+ task.
45
+ - **Start the memory.** Create or extend `log.md` (`references/log-template.md`) and fill the
46
+ README's **Resume here** block.
47
+
35
48
  ## When to Use
36
49
 
37
50
  - After a planning conversation where requirements and technical details have been discussed
@@ -92,6 +105,7 @@ specs/{feature-name}/
92
105
  ├── README.md
93
106
  ├── requirements.md
94
107
  ├── action-required.md
108
+ ├── log.md
95
109
  └── tasks/
96
110
  ├── task-01-{name}.md
97
111
  ├── task-02-{name}.md
@@ -103,6 +117,7 @@ Read the templates in `references/` before writing each file:
103
117
  - `references/task-template.md` — for each task file (self-contained with all context)
104
118
  - `references/requirements-template.md` — for the requirements document
105
119
  - `references/action-required-template.md` — for manual human steps
120
+ - `references/log-template.md` — for the append-only log
106
121
 
107
122
  Task files are numbered with zero-padded two-digit prefixes in topological order: Batch 1 tasks first, then Batch 2, etc. Within a batch, order is arbitrary but stable.
108
123
 
@@ -135,6 +150,7 @@ Files created:
135
150
  - action-required.md
136
151
  - tasks/ ({T} task files)
137
152
 
153
+ Coverage: R1–R{n} all mapped to tasks
138
154
  Model plan: {n} light · {n} standard · {n} deep
139
155
  (models from .spec-driven.yml → light: {model}, standard: {model}, deep: {model})
140
156
  Deep tasks: {task-nn — main reason}
@@ -149,7 +165,7 @@ Next steps:
149
165
  2. Review the requirements and task files
150
166
  3. When you're happy with it, either:
151
167
  - say "implement" to build it and stop for review, or
152
- - say "approved, run it through" to build, verify and commit in one go (stops at any failure,
168
+ - say "approved, run it through" to build, verify, review and commit in one go (stops at any failure,
153
169
  never pushes without asking)
154
170
  ```
155
171
 
@@ -163,4 +179,5 @@ by default after approval. Never start implementing from this skill.
163
179
  - Tasks within the same batch must not modify overlapping files. Parallel agents on the same branch cannot safely touch the same files.
164
180
  - Keep tasks atomic — one concern per task. If a task has more than 5-7 files to modify, consider splitting it.
165
181
  - Put tests in each task's Tests section, not in separate testing tasks (unless the user asks, or it's a cross-cutting suite).
182
+ - Every requirement (R1…) maps to at least one task, and every task lists what it **Covers**.
166
183
  - Number task files in topological order (batch 1 first, then batch 2, etc.) for easy scanning.
@@ -0,0 +1,27 @@
1
+ # Log Template
2
+
3
+ `specs/{feature}/log.md` keeps what would otherwise live only in the chat: decisions, deviations,
4
+ answers from the user, review findings, and dead ends. Without it, those are lost when the session
5
+ ends or the conversation is compacted.
6
+
7
+ **Rules**
8
+ - **Append only.** Add entries at the bottom. Never rewrite or reorder.
9
+ - **Never read it in full.** A new session reads the README's **Resume here** block. Read the log's
10
+ last ~20 lines only when Resume here isn't enough.
11
+ - **One to three lines per entry.** Point to files as `path:line`. Don't paste code.
12
+ - **Log what's worth keeping:** a decision and its reason, a change from the spec, the user's answer
13
+ to a question, a review finding and its fix, an approach that failed and why. Skip routine
14
+ progress, which the checkboxes already show.
15
+
16
+ ## Template
17
+
18
+ ```markdown
19
+ # Log: {Feature Name}
20
+
21
+ - 2026-11-10 spec-create · Created spec: {N} tasks in {B} batches. Covers R1–R{n}.
22
+ - 2026-11-10 spec-implement · Decision: reuse `retryWithBackoff` (`src/lib/retry.ts:12`) instead of a new helper. The user asked for it.
23
+ - 2026-11-10 spec-implement · Deviation: task-03 also touches `src/api/routes.ts`, because the route table lives there.
24
+ - 2026-11-11 spec-verify · R3 failed: webhook retried 5 times, not 3. Fixed in task-04 and re-verified.
25
+ ```
26
+
27
+ Each entry has the date, the skill that wrote it, then what happened and why.
@@ -7,6 +7,15 @@ Use this structure for the README.md at the root of each spec folder. The README
7
7
  ```markdown
8
8
  # {Feature Name}
9
9
 
10
+ ## Resume here
11
+
12
+ <!-- Overwritten by each spec-* skill when it finishes. A new session reads this first. Keep it ~10 lines. -->
13
+ - **Stage:** spec created, awaiting approval
14
+ - **Next:** review the spec, then say "implement" (or "approved, run it through")
15
+ - **Open questions:** {none, or one line each}
16
+ - **Last commit:** {short sha, or "none yet"}
17
+ - **Areas:** {context areas this feature touches, e.g. billing, api}
18
+
10
19
  ## Overview
11
20
 
12
21
  {One paragraph summary of what the feature does and why it exists.}
@@ -15,6 +24,7 @@ Use this structure for the README.md at the root of each spec folder. The README
15
24
 
16
25
  - [Requirements](./requirements.md) — full requirements and acceptance criteria
17
26
  - [Action Required](./action-required.md) — manual steps needing human action
27
+ - [Log](./log.md) — decisions, deviations and review findings (append-only)
18
28
 
19
29
  ## Dependency Graph
20
30
 
@@ -48,6 +58,8 @@ graph TD
48
58
 
49
59
  ## Key Points
50
60
 
61
+ - **Resume here** is the cheap way back in. Every `spec-*` skill overwrites it when it finishes (stage, next action, open questions, last commit), so a new or compacted session reads ~10 lines instead of the whole history. `spec-driven-dev status` prints each spec's **Next** line.
62
+
51
63
  - The **Dependency Graph** uses Mermaid syntax (widely rendered in GitHub, VS Code, etc.). Each node shows the task number and title for quick reference.
52
64
  - The **Batches** table provides a scannable overview of what runs in parallel.
53
65
  - The **Task Status** section uses markdown checkboxes that the `spec-implement` skill updates as tasks complete. The orchestrator parses these to determine which batch to resume from.
@@ -26,11 +26,18 @@ Use this structure for the requirements.md at the root of each spec folder.
26
26
 
27
27
  ## Acceptance Criteria
28
28
 
29
- {High-level criteria for the entire feature. Individual task files have their own granular criteria.}
29
+ {High-level criteria for the entire feature, numbered R1, R2… so tasks and verification can point to them. Keep the numbers from investigation.md if it has them. Individual task files have their own granular criteria.}
30
30
 
31
- - [ ] {Criterion 1}
32
- - [ ] {Criterion 2}
33
- - [ ] {Criterion 3}
31
+ - [ ] R1: {Criterion 1}
32
+ - [ ] R2: {Criterion 2}
33
+ - [ ] R3: {Criterion 3}
34
+
35
+ ## Coverage
36
+
37
+ | Requirement | Tasks |
38
+ |---|---|
39
+ | R1 | task-01, task-03 |
40
+ | R2 | task-02 |
34
41
 
35
42
  ## Assumptions
36
43
 
@@ -51,4 +58,5 @@ Use this structure for the requirements.md at the root of each spec folder.
51
58
 
52
59
  - The requirements doc provides **overall feature context** that each coder agent receives alongside its task file. Keep it focused on the "what" and "why" — individual task files handle the "how."
53
60
  - Non-goals are important: they prevent agents from over-building. If search functionality is out of scope, say so — otherwise a well-meaning agent might add it.
61
+ - **Every requirement must map to at least one task.** `spec-create` doesn't finish while the Coverage table has a requirement with no task, and `spec-verify` reports pass/fail per requirement number.
54
62
  - Acceptance criteria here are feature-level (the whole thing works end-to-end). Task-level criteria live in each task file.
@@ -15,6 +15,10 @@ pending
15
15
 
16
16
  {N}
17
17
 
18
+ ## Covers
19
+
20
+ {Requirement numbers from requirements.md this task delivers, e.g. R1, R3.}
21
+
18
22
  ## Complexity
19
23
 
20
24
  **Tier:** {light | standard | deep}
@@ -31,6 +35,12 @@ pending
31
35
 
32
36
  **Context from dependencies:** {Prose summary of what the dependency tasks produce that this task needs. For example: "task-01-setup-database.md creates the PostgreSQL schema with users and sessions tables. This task builds the API routes that query those tables." This is what makes each file self-contained — the agent does not need to read other task files to understand its inputs.}
33
37
 
38
+ ## Project Context
39
+
40
+ {Only the 3–5 lines from the project context (CONTEXT.md / context/<area>.md, via the investigation's Relevant Context) that apply to THIS task: conventions, pitfalls, off-limits areas. The coder never reads the context files itself. Write "None" if nothing applies.}
41
+
42
+ - {e.g. Errors go through `AppError` (`src/lib/errors.ts:8`); never throw raw strings.}
43
+
34
44
  ## Files to Create
35
45
 
36
46
  - `path/to/new-file.ts` — {Brief purpose}
@@ -87,6 +97,8 @@ pending
87
97
  - The **Dependencies** section includes prose context (not just filenames) so the agent understands its inputs without reading other files. This is the most important part of making task files self-contained.
88
98
  - **Files to Create** and **Files to Modify** make the task's scope explicit. Tasks in the same batch should not overlap on these lists.
89
99
  - **Technical Details** captures everything from the planning conversation — CLI commands, schemas, code snippets, file paths, env vars, API endpoints. If it was discussed during planning and is relevant to this task, it belongs here.
100
+ - **Covers** links the task to requirement numbers, so nothing in requirements.md is left without a task.
101
+ - **Project Context** carries just the conventions this task needs. Copy lines; don't link to CONTEXT.md, because parallel coders reading the whole context would multiply the token cost.
90
102
  - **Complexity** comes from `complexity-rubric.md`. `spec-implement` picks the coder's model from this tier, so rate honestly: under-rating a risky task is the expensive mistake.
91
103
  - **Tests** names the tests the coder writes alongside the code. A behavioural acceptance criterion without a matching test is a gap — add the test or explain why it can only be checked by `spec-verify`.
92
104
  - **Acceptance Criteria** should be specific enough that completion can be verified objectively (by a code review agent or a human).
@@ -38,6 +38,20 @@ isn't, detect what this skill needs from the repo, ask about anything ambiguous,
38
38
  the answers to `.spec-driven.yml` (keys: `layout`, `repos`, `tracker`, `git`, `commands`,
39
39
  `specs_dir`).
40
40
 
41
+ ## Project context and memory
42
+
43
+ - **Start cheap.** Read the README's **Resume here** block first. Read the last ~20 lines of
44
+ `log.md` only if that isn't enough. You, the orchestrator, read `{specs_dir}/CONTEXT.md` once,
45
+ because its **Conventions** and **Off-limits** apply to your own work too: commit messages and
46
+ trailers, branches, files nobody may touch. Coders don't read it. Each task file's **Project
47
+ Context** already carries what its coder needs. Open one `context/<area>.md` only when a review
48
+ finding depends on a convention the task file doesn't state.
49
+ - **Log as you go.** Append 1–3 lines to `log.md` for each decision, deviation from the spec, user
50
+ answer, review finding and fix, or failed approach. Skip routine progress.
51
+ - **Before stopping for any reason** (batch done, failure, user stop, run-through gate), overwrite
52
+ **Resume here** with the stage, next action, open questions, and last commit. A new or compacted
53
+ session must be able to continue from that block alone.
54
+
41
55
  ## Orchestration Model
42
56
 
43
57
  This skill's parallelism assumes your host can run multiple independent agents at once —
@@ -209,12 +223,14 @@ After the batch passes review (or the user chooses to proceed):
209
223
 
210
224
  1. **Update task files**: for each completed task, change the Status field from `pending` to `complete`
211
225
  2. **Update README.md**: change `- [ ]` to `- [x]` for each completed task
212
- 3. **Commit the batch**:
226
+ 3. **Update memory**: append a log entry (what this batch decided or changed from the spec) and
227
+ overwrite the README's **Resume here** (`Stage: batch {N}/{total} done`, `Next: …`).
228
+ 4. **Commit the batch**:
213
229
  ```
214
230
  git add -A
215
231
  git commit -m "feat({feature}): complete batch {N} — {brief summary}" # or the config's git.commit_style
216
232
  ```
217
- 4. **Report batch completion**:
233
+ 5. **Report batch completion**:
218
234
  ```
219
235
  Batch {N} of {total} complete.
220
236
 
@@ -254,7 +270,7 @@ Next steps:
254
270
 
255
271
  ## Run-through mode (after approval)
256
272
 
257
- Run-through applies when the user asks for it ("approved, run it through", "build, verify and
273
+ Run-through applies when the user asks for it ("approved, run it through", "build, verify, review and
258
274
  commit it") or when `.spec-driven.yml` has `pipeline.after_approval: run-through` and the user has
259
275
  approved the spec. In that case, don't stop after Step 9. Continue:
260
276
 
@@ -262,16 +278,18 @@ approved the spec. In that case, don't stop after Step 9. Continue:
262
278
  Run-through progress:
263
279
  - [ ] Implement all batches (Steps 1–9) — every review gate passed
264
280
  - [ ] Verify — follow the spec-verify skill; every acceptance criterion PASS
281
+ - [ ] Review — follow the spec-review skill; no blockers (should-fix items are listed, not blocking)
265
282
  - [ ] Checkpoint — follow the spec-checkpoint skill; every check passes, commit made
266
283
  - [ ] Stop and hand off to spec-ship
267
284
  ```
268
285
 
269
286
  1. **Gate on each stage.** Move on only if the previous stage fully passed. On any failure (a review
270
- gate exhausted its 3 cycles, a criterion FAILed or was SKIPPED, a check failed), stop the
287
+ gate exhausted its 3 cycles, a criterion FAILed or was SKIPPED, spec-review found a blocker, a
288
+ check failed), stop the
271
289
  run-through and report where it stopped, why, and the command to resume (e.g. "fix X, then say
272
290
  'continue the run-through'").
273
- 2. **Verify, then checkpoint.** Follow those skills' own instructions in full, including the
274
- verification report. Don't shortcut them because you're running them in sequence.
291
+ 2. **Verify, review, then checkpoint.** Follow those skills' own instructions in full, including
292
+ the verification report and review.md. Review fixes still need the user's yes. Don't shortcut them because you're running them in sequence.
275
293
  3. **Never push.** The run-through ends at a local commit. Finish with the summary below and ask
276
294
  whether to run `spec-ship`. `spec-ship` still shows exactly what will be pushed and waits for a
277
295
  yes.
@@ -282,6 +300,7 @@ Run-through progress:
282
300
  Run-through complete for "{feature}":
283
301
  - Implement: {N} batches, all review gates PASS
284
302
  - Verify: {P}/{P} criteria PASS (specs/{feature}/verification-report.md)
303
+ - Review: 0 blockers · {S} should-fix · {N} nits (specs/{feature}/review.md)
285
304
  - Checkpoint: {commit hash} — {subject}
286
305
 
287
306
  Ready to push and open a PR. Run spec-ship? (it will show exactly what gets pushed first)
@@ -8,9 +8,9 @@ description: >
8
8
  plan it", or hands over any of those inputs and wants the
9
9
  system understood (current behaviour, root cause, affected repos/files, risks, open
10
10
  questions) rather than fixed or built yet. This is the first stage of the
11
- investigate → create → (human approval) → implement → verify → ship pipeline — it hands off
11
+ investigate → create → (human approval) → implement → verify → review → ship pipeline — it hands off
12
12
  to the existing spec-create skill and does not replace, duplicate, or invoke spec-create,
13
- spec-implement, spec-verify, or spec-ship itself. Ticket-agnostic: works for bugs, features,
13
+ spec-implement, spec-verify, spec-review, or spec-ship itself. Ticket-agnostic: works for bugs, features,
14
14
  refactors, security, performance, infra, or cross-repository tickets in any repo.
15
15
  license: MIT
16
16
  ---
@@ -22,11 +22,11 @@ Discover and record the facts. Do not design the fix and do not write any code.
22
22
  This skill is stage 1 of:
23
23
 
24
24
  ```
25
- spec-investigate → spec-create → human approval → spec-implement → spec-verify → spec-ship
25
+ spec-investigate → spec-create → human approval → spec-implement → spec-verify → spec-review → spec-checkpoint → spec-ship
26
26
  ```
27
27
 
28
- `spec-create`, `spec-implement`, `spec-verify`, and `spec-ship` already exist elsewhere (e.g. the
29
- `spec-driven-dev` plugin, or whatever equivalent this host provides). **Never create, redesign,
28
+ `spec-create`, `spec-implement`, `spec-verify`, `spec-review`, and `spec-ship` already exist elsewhere (e.g. the
29
+ `ticket-to-pr` plugin, or whatever equivalent this host provides). **Never create, redesign,
30
30
  duplicate, or invoke those skills from here.** Your only output is an investigation report plus a
31
31
  ready-to-use work branch; a human decides when to run `spec-create` next.
32
32
 
@@ -45,6 +45,26 @@ isn't, detect what this skill needs from the repo, ask about anything ambiguous,
45
45
  the answers to `.spec-driven.yml` (keys: `layout`, `repos`, `tracker`, `git`, `commands`,
46
46
  `specs_dir`).
47
47
 
48
+ ## Project context (read before investigating)
49
+
50
+ Cheap sources first, code second:
51
+
52
+ 1. Read `{specs_dir}/CONTEXT.md`, the index of 60 lines or fewer, or run
53
+ `spec-driven-dev context`. Pick the areas this work touches and read only those
54
+ `context/<area>.md` files (`spec-driven-dev context --areas=billing,api`). Never load
55
+ `context/archive.md`.
56
+ 2. **Optional graph.** If `context.graph` isn't `off` and graphify is available
57
+ (`graphify-out/graph.json` exists or `graphify` is on the PATH), map the system with
58
+ `graphify query "<topic>" --budget 1500` and `graphify path "<A>" "<B>"` before grepping. Treat
59
+ INFERRED or AMBIGUOUS edges as hints. Never install it.
60
+ 3. **Context is a lead, not evidence.** Verify it against the code like any other source. Where the
61
+ code contradicts it, record that under **Relevant Context → Context corrections**. Don't edit
62
+ CONTEXT.md from here.
63
+ 4. Copy the 10–20 lines that apply into the report's **Relevant Context** section. That digest is
64
+ what later stages use, so they don't reload the context files.
65
+
66
+ No CONTEXT.md? Investigate as usual, and suggest running `spec-setup` afterwards to create one.
67
+
48
68
  ## Invocation
49
69
 
50
70
  ```
@@ -285,6 +305,10 @@ needed — this mirrors where `spec-create` already keeps its own output, so the
285
305
  naturally from the same place; don't commit this folder if the repo's convention already
286
306
  gitignores `specs/`, matching whatever this repo already does there). Use the structure in `references/report-template.md`.
287
307
 
308
+ Number the **Potential Requirements** R1, R2… so `spec-create` and `spec-verify` can trace
309
+ each one. Start `{specs_dir}/{feature-slug}/log.md` (append-only; template: references/log-template.md inside the installed
310
+ `spec-create` skill folder, next to its SKILL.md, not in this repo) with one entry: the date, `spec-investigate`, and the headline finding.
311
+
288
312
  Every section should be specific and evidence-backed, or explicitly say "None found" /
289
313
  "Not applicable to this ticket" rather than being left looking like an oversight.
290
314
 
@@ -16,6 +16,12 @@ Copy this structure into `specs/{feature-slug}/investigation.md`.
16
16
  - Base Branch:
17
17
  - Base Commit:
18
18
 
19
+ ## Relevant Context
20
+ <!-- 10–20 lines max: only what applies to this work, copied from specs/CONTEXT.md and context/<area>.md. spec-create passes these on to task files. -->
21
+ - Areas: (e.g. billing, api)
22
+ - Conventions / decisions / pitfalls that apply: (one line each, with the source)
23
+ - Context corrections: (where the code contradicts CONTEXT.md, with evidence; or "none")
24
+
19
25
  ## Business Context
20
26
  ## Problem Statement
21
27
  ## Expected Behaviour
@@ -45,6 +51,7 @@ Copy this structure into `specs/{feature-slug}/investigation.md`.
45
51
  ### Business Decisions
46
52
  ### Technical Decisions
47
53
  ### Potential Requirements
54
+ (Number them R1, R2… so spec-create and spec-verify can trace each one.)
48
55
  ### Proposed Solutions Discussed
49
56
  ### Open Questions
50
57
  ### Conflicts / Discrepancies