@benjaminthomas/spec-driven-dev 1.7.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.
- package/LICENSE +1 -1
- package/README.md +169 -70
- package/bin/cli.mjs +97 -22
- package/package.json +3 -3
- package/{skills → plugin/skills}/spec-checkpoint/SKILL.md +23 -0
- package/{skills → plugin/skills}/spec-create/SKILL.md +18 -1
- package/plugin/skills/spec-create/references/log-template.md +27 -0
- package/{skills → plugin/skills}/spec-create/references/readme-template.md +12 -0
- package/{skills → plugin/skills}/spec-create/references/requirements-template.md +12 -4
- package/{skills → plugin/skills}/spec-create/references/task-template.md +12 -0
- package/{skills → plugin/skills}/spec-implement/SKILL.md +40 -16
- package/{skills → plugin/skills}/spec-investigate/SKILL.md +29 -5
- package/{skills → plugin/skills}/spec-investigate/references/report-template.md +8 -1
- package/plugin/skills/spec-review/SKILL.md +131 -0
- package/plugin/skills/spec-review/references/report-template.md +44 -0
- package/{skills → plugin/skills}/spec-setup/SKILL.md +34 -9
- package/plugin/skills/spec-setup/references/audit.md +71 -0
- package/{skills → plugin/skills}/spec-setup/references/config-template.yml +25 -9
- package/plugin/skills/spec-setup/references/context-template.md +83 -0
- package/{skills → plugin/skills}/spec-ship/SKILL.md +10 -0
- package/{skills → plugin/skills}/spec-verify/SKILL.md +10 -2
- /package/{skills → plugin/skills}/generative-seo/SKILL.md +0 -0
- /package/{skills → plugin/skills}/generative-seo/assets/citation-log-template.md +0 -0
- /package/{skills → plugin/skills}/generative-seo/assets/competitor-analysis-template.md +0 -0
- /package/{skills → plugin/skills}/generative-seo/assets/content-plan-template.md +0 -0
- /package/{skills → plugin/skills}/generative-seo/assets/geo-config-template.md +0 -0
- /package/{skills → plugin/skills}/generative-seo/assets/geo-playbook-template.md +0 -0
- /package/{skills → plugin/skills}/generative-seo/assets/social-queue-template.md +0 -0
- /package/{skills → plugin/skills}/generative-seo/references/geo-methodology.md +0 -0
- /package/{skills → plugin/skills}/spec-create/references/action-required-template.md +0 -0
- /package/{skills → plugin/skills}/spec-create/references/complexity-rubric.md +0 -0
- /package/{skills → plugin/skills}/spec-implement/references/coder-prompt-template.md +0 -0
- /package/{skills → plugin/skills}/spec-implement/references/fix-prompt-template.md +0 -0
- /package/{skills → plugin/skills}/spec-implement/references/review-prompt-template.md +0 -0
- /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
|
|
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")
|
|
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
|
}
|
|
@@ -235,22 +236,31 @@ function toYaml(c) {
|
|
|
235
236
|
out += `tracker:\n type: ${c.tracker.type}\n key: ${y(c.tracker.key)}\n url: ${y(c.tracker.url)}\n`;
|
|
236
237
|
out += `git:\n${cmds(c.git, " ")}\n`;
|
|
237
238
|
out += `commands:\n${cmds(c.commands, " ")}\n dev_url: ${y(c.devUrl)}\n`;
|
|
238
|
-
out += "models: # model per task tier
|
|
239
|
-
.map(([tool, m]) => ` ${tool}:\n` +
|
|
240
|
-
out +=
|
|
239
|
+
out += "models: # model per task tier; \"default\" = the session's model; escalate = last resort for repeat failures\n" + Object.entries(MODEL_DEFAULTS)
|
|
240
|
+
.map(([tool, m]) => ` ${tool}:\n` + MODEL_KEYS.map((t) => ` ${t}: ${m[t]}\n`).join("")).join("");
|
|
241
|
+
out += "effort: # effort / reasoning level per tier (Claude Code, Codex): low | medium | high | xhigh | max | default\n"
|
|
242
|
+
+ MODEL_KEYS.map((t) => ` ${t}: ${EFFORT_DEFAULTS[t]}\n`).join("");
|
|
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`;
|
|
241
245
|
return out + `specs_dir: ${y(c.specsDir)}\n`;
|
|
242
246
|
}
|
|
243
247
|
|
|
244
248
|
const LAYOUTS = ["single", "monorepo", "multi-repo"];
|
|
245
249
|
const TIERS = ["light", "standard", "deep"];
|
|
246
|
-
|
|
247
|
-
|
|
250
|
+
const MODEL_KEYS = [...TIERS, "escalate"]; // escalate: optional last resort when a deep task keeps failing
|
|
251
|
+
const EFFORTS = ["low", "medium", "high", "xhigh", "max", "default"];
|
|
252
|
+
// Checked against the vendors' docs on 2026-11-06 (Claude Code model-config, OpenAI Codex config,
|
|
253
|
+
// Cursor models page). Claude Code aliases track the newest model per family on your provider.
|
|
254
|
+
// Cursor's models depend on your plan, so it defaults to the session's model.
|
|
255
|
+
// shortcut: model names go stale; refresh these defaults (and the README table) when vendors ship new lines.
|
|
248
256
|
const MODEL_DEFAULTS = {
|
|
249
|
-
claude: { light: "haiku", standard: "sonnet", deep: "opus" },
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
antigravity: { light: "default", standard: "default", deep: "default" },
|
|
257
|
+
claude: { light: "haiku", standard: "sonnet", deep: "opus", escalate: "fable" },
|
|
258
|
+
codex: { light: "gpt-6-luna", standard: "gpt-6.1-sol", deep: "gpt-6.1-sol", escalate: "default" },
|
|
259
|
+
cursor: { light: "default", standard: "default", deep: "default", escalate: "default" },
|
|
260
|
+
antigravity: { light: "default", standard: "default", deep: "default", escalate: "default" },
|
|
253
261
|
};
|
|
262
|
+
// Effort / reasoning level per tier, for tools that support it (Claude Code, Codex).
|
|
263
|
+
const EFFORT_DEFAULTS = { light: "low", standard: "medium", deep: "high", escalate: "xhigh" };
|
|
254
264
|
const splitList = (v) => (v || "").split(",").map((x) => x.trim()).filter(Boolean);
|
|
255
265
|
// Paths are stored relative to the config's folder when inside it, absolute otherwise.
|
|
256
266
|
const real = (p) => { try { return fs.realpathSync(p); } catch { return p; } }; // /var vs /private/var etc.
|
|
@@ -373,7 +383,16 @@ async function choosePackages(d) {
|
|
|
373
383
|
return { root, repos: pkgs };
|
|
374
384
|
}
|
|
375
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
|
+
|
|
376
394
|
async function setup() {
|
|
395
|
+
if (opt("project") && !["new", "existing"].includes(opt("project"))) throw new Error("--project must be new or existing");
|
|
377
396
|
if (opt("after-approval") && !["stop", "run-through"].includes(opt("after-approval"))) throw new Error("--after-approval must be stop or run-through");
|
|
378
397
|
console.log("\nProject setup — detecting how this project works…");
|
|
379
398
|
const d = detectLayout(DIR);
|
|
@@ -431,11 +450,17 @@ async function setup() {
|
|
|
431
450
|
const specsDir = await askText("Folder for specs (one sub-folder per feature)", "specs");
|
|
432
451
|
const afterApproval = opt("after-approval") || await askSelect("After you approve a spec…", [
|
|
433
452
|
{ title: "Stop after each stage", value: "stop", description: "you start implement, verify and checkpoint yourself" },
|
|
434
|
-
{ 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" },
|
|
435
454
|
], "stop");
|
|
436
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
|
+
|
|
437
462
|
const config = {
|
|
438
|
-
layout, tracker: { type: trackerType, key, url }, specsDir, devUrl: "", afterApproval,
|
|
463
|
+
project, layout, tracker: { type: trackerType, key, url }, specsDir, devUrl: "", afterApproval,
|
|
439
464
|
git: { remote: "origin", base_branch: base, pr_target: pr, branch_name: branchName, commit_style: commitStyle },
|
|
440
465
|
commands: layout === "single" ? commandsFor(repoDirs[0]) : commandsFor(root),
|
|
441
466
|
repos: repos.map((r) => {
|
|
@@ -448,7 +473,9 @@ async function setup() {
|
|
|
448
473
|
if (layout === "multi-repo") register(real(file), repos.map((r) => real(path.resolve(root, r))));
|
|
449
474
|
console.log(`✓ Wrote ${file}`);
|
|
450
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.");
|
|
451
|
-
console.log(
|
|
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.`);
|
|
452
479
|
}
|
|
453
480
|
|
|
454
481
|
// ---------- reading the config ----------
|
|
@@ -514,7 +541,11 @@ function doctor() {
|
|
|
514
541
|
for (const [tool, m] of Object.entries(c.models || {})) {
|
|
515
542
|
if (typeof m !== "object") { problems.push(`models.${tool} must list light / standard / deep`); continue; }
|
|
516
543
|
for (const t of TIERS) if (!m[t]) problems.push(`models.${tool}.${t} is missing (use "default" for the session's model)`);
|
|
517
|
-
for (const k of Object.keys(m)) if (!
|
|
544
|
+
for (const k of Object.keys(m)) if (!MODEL_KEYS.includes(k)) problems.push(`models.${tool}.${k}: unknown tier (use light, standard, deep, escalate)`);
|
|
545
|
+
}
|
|
546
|
+
for (const [k, v] of Object.entries(c.effort || {})) {
|
|
547
|
+
if (!MODEL_KEYS.includes(k)) problems.push(`effort.${k}: unknown tier (use light, standard, deep, escalate)`);
|
|
548
|
+
else if (!EFFORTS.includes(v)) problems.push(`effort.${k} "${v}" must be one of ${EFFORTS.join(", ")}`);
|
|
518
549
|
}
|
|
519
550
|
if (c.pipeline?.after_approval && !["stop", "run-through"].includes(c.pipeline.after_approval)) problems.push(`pipeline.after_approval "${c.pipeline.after_approval}" must be stop or run-through`);
|
|
520
551
|
if (c.git?.branch_name && !/\{(ticket|slug)\}/.test(c.git.branch_name)) problems.push(`git.branch_name "${c.git.branch_name}" needs {ticket} or {slug}`);
|
|
@@ -542,6 +573,10 @@ function doctor() {
|
|
|
542
573
|
}
|
|
543
574
|
const specs = path.join(root, c.specs_dir || "specs");
|
|
544
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);
|
|
545
580
|
console.log(`Checked ${file}`);
|
|
546
581
|
for (const n of notes) console.log(` • ${n}`);
|
|
547
582
|
for (const p of problems) console.log(` ✗ ${p}`);
|
|
@@ -549,7 +584,43 @@ function doctor() {
|
|
|
549
584
|
else console.log("✓ Config matches the repos.");
|
|
550
585
|
}
|
|
551
586
|
|
|
552
|
-
|
|
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.
|
|
553
624
|
function status() {
|
|
554
625
|
const file = findConfig(DIR);
|
|
555
626
|
const root = file ? path.dirname(file) : DIR;
|
|
@@ -563,9 +634,12 @@ function status() {
|
|
|
563
634
|
continue;
|
|
564
635
|
}
|
|
565
636
|
const t = fs.readFileSync(readme, "utf8");
|
|
637
|
+
const next = t.match(/^## Resume here[\s\S]*?^- \*\*Next:\*\*\s*(.+)$/m)?.[1]?.trim();
|
|
566
638
|
const done = (t.match(/^\s*- \[x\]/gim) || []).length, todo = (t.match(/^\s*- \[ \]/gm) || []).length;
|
|
567
|
-
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"}`);
|
|
568
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)`);
|
|
569
643
|
}
|
|
570
644
|
if (lines.length) console.log(`spec-driven-dev: unfinished work in ${root}\n${lines.join("\n")}`);
|
|
571
645
|
}
|
|
@@ -573,11 +647,12 @@ function status() {
|
|
|
573
647
|
try {
|
|
574
648
|
if (command === "doctor") doctor();
|
|
575
649
|
else if (command === "status") status();
|
|
650
|
+
else if (command === "context") context();
|
|
576
651
|
else if (command === "install") {
|
|
577
652
|
await install();
|
|
578
653
|
if (!flag("no-setup") && (await askConfirm("Set up this project now?", true))) await setup();
|
|
579
654
|
} else if (command === "setup") await setup();
|
|
580
|
-
else throw new Error(`Unknown command "${command}" — use install (default), setup, doctor, or
|
|
655
|
+
else throw new Error(`Unknown command "${command}" — use install (default), setup, doctor, status, or context`);
|
|
581
656
|
if (["install", "setup"].includes(command)) console.log('\nNext: in your AI tool, say "investigate <ticket>" or "create a spec".');
|
|
582
657
|
} catch (e) {
|
|
583
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.
|
|
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 —
|
|
@@ -108,14 +122,17 @@ Read `references/coder-prompt-template.md` and construct each agent's prompt by
|
|
|
108
122
|
|
|
109
123
|
The coder agents should NOT commit their changes — the orchestrator handles commits after review.
|
|
110
124
|
|
|
111
|
-
**Pick each coder's model from the task's tier.** Read the task's `## Complexity` tier
|
|
112
|
-
missing tier as `standard`)
|
|
113
|
-
where `<tool>` is the host you're running in (`claude`, `codex`, `cursor`,
|
|
114
|
-
`
|
|
115
|
-
`deep → opus
|
|
116
|
-
|
|
117
|
-
- **Host can set a model per agent** (e.g. Claude Code's Agent tool `model`
|
|
118
|
-
each coder with its tier's model.
|
|
125
|
+
**Pick each coder's model and effort from the task's tier.** Read the task's `## Complexity` tier
|
|
126
|
+
(treat a missing tier as `standard`), then look up `.spec-driven.yml` → `models.<tool>.<tier>` and
|
|
127
|
+
`effort.<tier>`, where `<tool>` is the host you're running in (`claude`, `codex`, `cursor`,
|
|
128
|
+
`antigravity`). Without those sections, use these defaults: Claude Code `light → haiku/low`,
|
|
129
|
+
`standard → sonnet/medium`, `deep → opus/high`; other hosts use the session's model.
|
|
130
|
+
|
|
131
|
+
- **Host can set a model per agent** (e.g. Claude Code's Agent tool `model` and `effort`
|
|
132
|
+
parameters): dispatch each coder with its tier's model and effort.
|
|
133
|
+
- **Model not available** (not on your plan or provider, or rejected by the host): fall back to the
|
|
134
|
+
session's model, say which task fell back and why, and suggest fixing `models:`. Don't stop the
|
|
135
|
+
batch for this.
|
|
119
136
|
- **Host can't** (one model per session, as in Cursor or Codex): don't silently run deep tasks on a
|
|
120
137
|
light model. Before dispatching a batch, compare its highest tier with the model you're running
|
|
121
138
|
on. If the batch needs a stronger one, stop and say:
|
|
@@ -183,8 +200,10 @@ If the review returns **FAIL**:
|
|
|
183
200
|
- **{task_content}**: the original task file for context
|
|
184
201
|
4. After fix agents complete, re-run the review (Step 6)
|
|
185
202
|
5. **Escalate on repeat failure.** If a task's issues survive a fix cycle, run its next fix one tier
|
|
186
|
-
higher (`light → standard → deep`).
|
|
187
|
-
|
|
203
|
+
higher (`light → standard → deep`). A `deep` task that still fails goes to `models.<tool>.escalate`
|
|
204
|
+
(e.g. `fable`) at `effort.escalate`, if it's set and not `default`. Ask first if that model can
|
|
205
|
+
bill to usage credits, as Fable can on some plans. Note every escalation in the batch report:
|
|
206
|
+
a task that needed escalating was probably under-rated, which is useful feedback for future specs.
|
|
188
207
|
|
|
189
208
|
**Cap at 3 review cycles per batch.** If the third review still fails, stop and report to the user:
|
|
190
209
|
|
|
@@ -204,12 +223,14 @@ After the batch passes review (or the user chooses to proceed):
|
|
|
204
223
|
|
|
205
224
|
1. **Update task files**: for each completed task, change the Status field from `pending` to `complete`
|
|
206
225
|
2. **Update README.md**: change `- [ ]` to `- [x]` for each completed task
|
|
207
|
-
3. **
|
|
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**:
|
|
208
229
|
```
|
|
209
230
|
git add -A
|
|
210
231
|
git commit -m "feat({feature}): complete batch {N} — {brief summary}" # or the config's git.commit_style
|
|
211
232
|
```
|
|
212
|
-
|
|
233
|
+
5. **Report batch completion**:
|
|
213
234
|
```
|
|
214
235
|
Batch {N} of {total} complete.
|
|
215
236
|
|
|
@@ -249,7 +270,7 @@ Next steps:
|
|
|
249
270
|
|
|
250
271
|
## Run-through mode (after approval)
|
|
251
272
|
|
|
252
|
-
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
|
|
253
274
|
commit it") or when `.spec-driven.yml` has `pipeline.after_approval: run-through` and the user has
|
|
254
275
|
approved the spec. In that case, don't stop after Step 9. Continue:
|
|
255
276
|
|
|
@@ -257,16 +278,18 @@ approved the spec. In that case, don't stop after Step 9. Continue:
|
|
|
257
278
|
Run-through progress:
|
|
258
279
|
- [ ] Implement all batches (Steps 1–9) — every review gate passed
|
|
259
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)
|
|
260
282
|
- [ ] Checkpoint — follow the spec-checkpoint skill; every check passes, commit made
|
|
261
283
|
- [ ] Stop and hand off to spec-ship
|
|
262
284
|
```
|
|
263
285
|
|
|
264
286
|
1. **Gate on each stage.** Move on only if the previous stage fully passed. On any failure (a review
|
|
265
|
-
gate exhausted its 3 cycles, a criterion FAILed or was SKIPPED, a
|
|
287
|
+
gate exhausted its 3 cycles, a criterion FAILed or was SKIPPED, spec-review found a blocker, a
|
|
288
|
+
check failed), stop the
|
|
266
289
|
run-through and report where it stopped, why, and the command to resume (e.g. "fix X, then say
|
|
267
290
|
'continue the run-through'").
|
|
268
|
-
2. **Verify, then checkpoint.** Follow those skills' own instructions in full, including
|
|
269
|
-
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.
|
|
270
293
|
3. **Never push.** The run-through ends at a local commit. Finish with the summary below and ask
|
|
271
294
|
whether to run `spec-ship`. `spec-ship` still shows exactly what will be pushed and waits for a
|
|
272
295
|
yes.
|
|
@@ -277,6 +300,7 @@ Run-through progress:
|
|
|
277
300
|
Run-through complete for "{feature}":
|
|
278
301
|
- Implement: {N} batches, all review gates PASS
|
|
279
302
|
- Verify: {P}/{P} criteria PASS (specs/{feature}/verification-report.md)
|
|
303
|
+
- Review: 0 blockers · {S} should-fix · {N} nits (specs/{feature}/review.md)
|
|
280
304
|
- Checkpoint: {commit hash} — {subject}
|
|
281
305
|
|
|
282
306
|
Ready to push and open a PR. Run spec-ship? (it will show exactly what gets pushed first)
|