@benjaminthomas/spec-driven-dev 1.14.1 → 1.14.2

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/README.md CHANGED
@@ -760,7 +760,7 @@ skips that step rather than failing.
760
760
 
761
761
  | Skill | Stage | SKILL.md lines | Words | Supporting files |
762
762
  |---|---|---:|---:|---:|
763
- | `spec-setup` | 0 · configure | 222 | 2,329 | 4 |
763
+ | `spec-setup` | 0 · configure | 273 | 2,829 | 4 |
764
764
  | `spec-investigate` | 1 · understand | 387 | 3,367 | 1 |
765
765
  | `spec-create` | 2 · plan | 198 | 1,782 | 6 |
766
766
  | `spec-implement` | 3 · build | 327 | 2,753 | 3 |
@@ -769,7 +769,7 @@ skips that step rather than failing.
769
769
  | `spec-checkpoint` | 6 · commit | 140 | 1,144 | 0 |
770
770
  | `spec-ship` | 7 · PR | 230 | 1,388 | 0 |
771
771
  | `generative-seo` | standalone | 255 | 2,209 | 7 |
772
- | **Total** | | **2,059** | **17,823** | **23** |
772
+ | **Total** | | **2,110** | **18,323** | **23** |
773
773
 
774
774
  - **8** skills: **1** setup + **6** workflow stages + **1** standalone
775
775
  - **4** tools supported natively (Claude Code, Codex CLI, Cursor, Antigravity), plus an MCP fallback
@@ -822,7 +822,7 @@ plugin/evals/ `claude plugin eval` cases
822
822
  1. Increase `version` in both `package.json` and `plugin/.claude-plugin/plugin.json` (the check fails if
823
823
  they differ).
824
824
  2. Add a `CHANGELOG.md` entry, then commit and push.
825
- 3. Tag the release and push the tag: `git tag v1.14.1 && git push origin v1.14.1`.
825
+ 3. Tag the release and push the tag: `git tag v1.14.2 && git push origin v1.14.2`.
826
826
  4. Watch the **publish** run in the repo's Actions tab. When it's green, the new version is on
827
827
  npm. Claude Code plugin users get it from the marketplace once the commit is on `main`.
828
828
 
package/bin/cli.mjs CHANGED
@@ -20,7 +20,7 @@ import path from "node:path";
20
20
  import { execFileSync } from "node:child_process";
21
21
  import prompts from "prompts"; // arrow-key menus that work in PowerShell, cmd, macOS and Linux terminals
22
22
  import { fileURLToPath } from "node:url";
23
- import { scoreRepo, riskMarkdown, normWeights, weightProblems, setConfigValue } from "./risk.mjs";
23
+ import { scoreRepo, riskMarkdown, normWeights, weightProblems, setConfigValue, riskSummaryBlock, withRiskSummary } from "./risk.mjs";
24
24
  import { SHARE_MODES, shareState, planShare, shareTitle } from "./share.mjs";
25
25
 
26
26
  const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
@@ -818,6 +818,11 @@ function risk() {
818
818
  const out = path.join(specs, "context", "risk.md");
819
819
  fs.writeFileSync(out, riskMarkdown({ rows, repos: scored, weights, since, top: Number(opt("top")) || 40 }));
820
820
  ignoreGenerated(out);
821
+ const ctx = path.join(specs, "CONTEXT.md");
822
+ if (fs.existsSync(ctx)) {
823
+ fs.writeFileSync(ctx, withRiskSummary(fs.readFileSync(ctx, "utf8"), riskSummaryBlock(rows, new Date().toISOString().slice(0, 10))));
824
+ console.log(` Updated the "Risk hotspots" summary in ${path.relative(DIR, ctx) || ctx}, which is shared with the team.`);
825
+ }
821
826
  const count = (t) => rows.filter((r) => r.tier === t).length;
822
827
  console.log(`✓ Scored ${rows.length} files from ${scored.reduce((t, s) => t + s.commits, 0)} commits → ${path.relative(DIR, out) || out}`);
823
828
  console.log(` ${count("critical")} critical · ${count("high")} high · ${count("medium")} medium · ${count("new")} new. Top 5:`);
package/bin/risk.mjs CHANGED
@@ -167,3 +167,18 @@ export function setConfigValue(text, blockName, key, value) {
167
167
  const body = line.test(block[0]) ? block[0].replace(line, `$1${value}`) : block[0].replace(new RegExp(`^${blockName}:.*\\n`), `$& ${key}: ${value}\n`);
168
168
  return text.replace(block[0], body);
169
169
  }
170
+
171
+ // A short, committed summary inside CONTEXT.md, so the hotspots are part of the shared project memory
172
+ // (risk.md itself is gitignored: it's regenerated on every run). Replaced in place between markers.
173
+ const RB = "<!-- risk:start (written by spec-driven-dev risk; don't edit) -->", RE = "<!-- risk:end -->";
174
+ export function riskSummaryBlock(rows, date) {
175
+ const top = rows.filter((r) => r.tier === "critical" || r.tier === "high").slice(0, 5);
176
+ const counts = ["critical", "high", "medium"].map((t) => `${rows.filter((r) => r.tier === t).length} ${t}`).join(" · ");
177
+ return [RB, "## Risk hotspots", `GitRisk on ${date}: ${counts}. Full table: context/risk.md (regenerate: spec-driven-dev risk).`,
178
+ ...(top.length ? top.map((r) => `- \`${r.file}\` ${r.score} ${r.tier}`) : ["- No high or critical files."]), RE].join("\n");
179
+ }
180
+ export function withRiskSummary(text, block) {
181
+ const i = text.indexOf(RB), j = text.indexOf(RE);
182
+ if (i >= 0 && j > i) return text.slice(0, i) + block + text.slice(j + RE.length);
183
+ return `${text.replace(/\n*$/, "")}\n\n${block}\n`;
184
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@benjaminthomas/spec-driven-dev",
3
- "version": "1.14.1",
3
+ "version": "1.14.2",
4
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": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ticket-to-pr",
3
- "version": "1.14.1",
3
+ "version": "1.14.2",
4
4
  "description": "Parallel-batch spec-driven feature workflow (spec-setup, spec-investigate, spec-create, spec-implement, spec-verify, spec-review, spec-checkpoint, spec-ship) plus a portable SEO/GEO operations skill (generative-seo) for any codebase.",
5
5
  "author": {
6
6
  "name": "Benjamin Thomas",
@@ -125,23 +125,74 @@ Read, don't guess. Collect a draft of every field in `references/config-template
125
125
 
126
126
  ## How to ask
127
127
 
128
- - **Real choices** (layout, keep these repos, sharing, after-approval mode) can be a menu, but only
129
- with **two or more** options. A menu with one option is rejected by the tool and the user never
130
- sees the question. "Other" doesn't count as an option.
131
- - **Open questions** (what the product does, fragile spots, direction, unwritten rules, anything
132
- else to add) are plain text in **one chat message**. The user answers in their own words or says
133
- "skip". Never turn an open question into a menu.
128
+ Two kinds of question, asked two different ways:
129
+
130
+ 1. **Choices** (layout and repos, sharing, specs location, after-approval mode, a guessed command):
131
+ use the host's **interactive question tool** when it has one (Claude Code: `AskUserQuestion`).
132
+ - Up to 4 questions per call; each needs **2 to 4 options** (a one-option question is rejected
133
+ and the user never sees it; "Other" is added automatically and doesn't count).
134
+ - Put the recommended option first and end its label with "(Recommended)". Put the evidence in
135
+ each option's description (e.g. "called via BACKBONE_URL, app/services/x.ts:105").
136
+ - **Layout and repos are two separate calls, always** (Step 2). Never ask layout together with
137
+ other questions.
138
+ - **Repos are never offered as preset bundles** ("+backend +melissa", "all four"); bundles don't
139
+ scale and stop people mixing their own. Ask in two calls:
140
+ 1. **Layout first**, on its own: "Single repo" vs "Several repos that change together"
141
+ (vs "Monorepo" when it fits), recommendation first with the evidence.
142
+ 2. **Only if several:** a `multiSelect` question with **one option per repo**: label = repo
143
+ name (the recommended ones first, marked "(Recommended)"), description = what it is plus the
144
+ link evidence. At most 4 options. The menu can't show the rest, so the **question text** must
145
+ end by naming the other repos found ("Also found: a, b, c (no code link).") and saying how to
146
+ add them: "To add any of these, or a repo from another folder, choose *Type something* and
147
+ enter names or paths, e.g. `a, ~/Work/billing`." Put the remaining choices (specs location,
148
+ sharing, pipeline) in this same call.
149
+ - If the tool isn't available (or a call fails), ask the same choices as a short numbered list
150
+ in text instead. Never drop a question.
151
+ 2. **Open questions** (what the product does, fragile spots, direction, unwritten rules): plain text
152
+ in **one chat message**, asked **after** the choices are answered. Never turn these into a menu.
134
153
 
135
154
  ## Step 2 — Confirm in one round
136
155
 
137
156
  Show the draft as a short table grouped by layout, tracker, git, commands, and specs location.
138
- Mark each value **detected** (with where it came from) or **guess**. Then ask only about guesses
139
- and conflicts — in one message, not a drip of questions. Typical ones:
140
-
141
- - "Your repos default to `master`, but recent PRs targeted `staging`. Cut work from `staging`?"
142
- - "Recent branches are bare ticket IDs (`STP-282`). Keep that, or use `fix/STP-282-short-slug`?"
143
- - "These 6 repos share the `acme` GitHub org. Do they form one workspace for the pipeline?"
144
- - "Where should specs live — this folder's `specs/`, or inside one repo so they go through PRs?"
157
+ Mark each value **detected** (with where it came from) or **guess**. Then ask in exactly this order
158
+ (interactive question tool when there is one; see How to ask):
159
+
160
+ **Call 1: layout, alone. Nothing else in this call.** Use these option labels exactly; no repo names
161
+ in them:
162
+ - `Single repo`: "Only <this repo>." (+ the evidence for or against)
163
+ - `Several repos`: "<this repo> changes together with other repos. You'll pick which next."
164
+ - `Monorepo`: only when workspace markers were found.
165
+
166
+ Recommend one (first, with "(Recommended)") and put the evidence in the question text.
167
+
168
+ **Call 2: repos (only if the answer was `Several repos`).** The repo tick-box list (`multiSelect`, one
169
+ option per repo, see `references/repo-picker.md`) and where specs live (a dedicated specs repo, inside
170
+ one code repo, or this machine only).
171
+
172
+ **Call 3: git conventions, always, even when detected.** These decide how every branch and commit is
173
+ made, and detection can be wrong, so confirm them. Four questions, the detected value first with its
174
+ evidence in the description, then the common alternatives:
175
+ - **Base branch** (work starts from): e.g. `staging (Recommended)`: "16 of the last 20 merged PRs";
176
+ `main`; `develop`. In multi-repo, say which repos differ ("staging; soreto-cms has none, uses main").
177
+ - **PR target**: usually the same as the base branch.
178
+ - **Branch names**: e.g. `STP-123 (Recommended)` (bare ticket id, seen in recent branches);
179
+ `feat/STP-123-short-name`; `feat/short-name`.
180
+ - **Commit style**: e.g. `STP-123: message (Recommended)`; `feat: message`; free text. In multi-repo,
181
+ name any repo that uses a different style.
182
+
183
+ **Call 4: tracker, team and pipeline.** The tracker, confirmed like the git conventions (e.g.
184
+ `Jira, key STP (Recommended)`: "STP-123 in recent branches", `GitHub Issues`, `Linear`, `None`), then
185
+ sharing (if specs are inside a repo), after approval (stop / run straight through), and any command
186
+ that's a guess (e.g. typecheck). At most 4; a guessed command that doesn't fit goes in the summary as
187
+ "I'll use X, say if that's wrong".
188
+
189
+ Then the open questions, as plain text (see How to ask).
190
+
191
+ Typical things to flag in the descriptions:
192
+
193
+ - "Your repos default to `master`, but recent PRs targeted `staging`."
194
+ - "Recent branches are bare ticket IDs (`STP-282`)."
195
+ - "These 6 repos share the `acme` GitHub org."
145
196
 
146
197
  If the user says "just use the defaults", accept the detected values and the template defaults
147
198
  for the rest.
@@ -41,7 +41,8 @@ never ask again.
41
41
  ## 3. Discovery questions (one round)
42
42
 
43
43
  Show the draft map first, with each line marked **detected** (with its source) or **guess**. Then
44
- ask only what the code can't answer, as **plain text in one message**. These are open questions, so
44
+ ask only what the code can't answer, as **plain text in one message**, after the setup choices have
45
+ been answered. These are open questions, so
45
46
  never put them in a menu or multiple-choice tool (a menu needs two or more real options, and these
46
47
  have none). The user replies in their own words, or "skip":
47
48
 
@@ -54,8 +54,18 @@ Project: existing · audited at a1b2c3d (2026-11-10) · graph: none
54
54
 
55
55
  ## Specs
56
56
  - {stp-123: retry failed webhooks, touched billing (2026-11-10)}
57
+
58
+ <!-- risk:start (written by spec-driven-dev risk; don't edit) -->
59
+ ## Risk hotspots
60
+ GitRisk on 2026-11-10: 0 critical · 8 high · 30 medium. Full table: context/risk.md (regenerate: spec-driven-dev risk).
61
+ - `app/services/billing.ts` 61.7 high
62
+ <!-- risk:end -->
57
63
  ```
58
64
 
65
+ The **Risk hotspots** block is written by `spec-driven-dev risk` (setup, every batch, checkpoint) and
66
+ replaced in place, so don't edit it. It's the shared part of GitRisk: `context/risk.md` itself is
67
+ gitignored because it's regenerated on every run.
68
+
59
69
  ## context/<area>.md
60
70
 
61
71
  ```markdown
@@ -52,6 +52,11 @@ Group the candidates:
52
52
  with this one. Some systems connect only through a shared database or a message queue the code
53
53
  doesn't name.
54
54
 
55
+ Ask it with the host's interactive question tool when there is one: after the layout question, a
56
+ `multiSelect` question with **one option per repo** (never bundles of repos), the recommended ones
57
+ first, evidence in each option's description, 2–4 options. End the question text with the other
58
+ repos found by name and how to add them ("choose *Type something* and enter names or paths, e.g.
59
+ `old-admin, ~/Work/billing`"), since the menu only shows 4. Otherwise as text.
55
60
  Shape of the question, with placeholders rather than real names:
56
61
 
57
62
  > I found 6 repos under ~/code/github.com/acme. Based on the code, I'd include: