@benjaminthomas/spec-driven-dev 1.12.0 → 1.13.1
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 +63 -91
- package/bin/cli.mjs +84 -30
- package/bin/share.mjs +5 -2
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/skills/spec-investigate/SKILL.md +15 -1
- package/plugin/skills/spec-setup/SKILL.md +35 -4
- package/plugin/skills/spec-setup/references/config-template.yml +12 -1
package/README.md
CHANGED
|
@@ -63,26 +63,21 @@ start a new session and continue where you left off.
|
|
|
63
63
|
npx @benjaminthomas/spec-driven-dev@latest
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
2. **Sets up your project** by detecting its layout, issue tracker, branch conventions, and
|
|
82
|
-
commands, then asking you to confirm. The answers go into `.spec-driven.yml`
|
|
83
|
-
([details below](#project-setup-your-teams-rules)).
|
|
84
|
-
|
|
85
|
-
**3. In your AI tool, run each stage in order.** You can type the slash command or just say the
|
|
66
|
+
It installs the skills into the AI tools you pick (Claude Code, Cursor, Codex CLI, Antigravity),
|
|
67
|
+
for this project or all your projects. Each step has its own colour so it's easy to follow. It also
|
|
68
|
+
works with `!` inside Claude Code: it then installs for the tools it finds.
|
|
69
|
+
|
|
70
|
+
> **Why not `npm install -g`?** You can: `npm install -g @benjaminthomas/spec-driven-dev`, then run
|
|
71
|
+
> `spec-driven-dev` in place of the `npx …` command. Handy if you use `doctor`/`status` often.
|
|
72
|
+
|
|
73
|
+
**2. Say "run spec-setup" in your AI tool.** Setup is a conversation, so the agent can read the code
|
|
74
|
+
and you can change any answer as you go. It asks how the project is organised (one repo, a monorepo,
|
|
75
|
+
or several repos that change together), your tracker, branches and commit style, where specs live
|
|
76
|
+
and whether your team shares them, then looks through the code and saves the project memory
|
|
77
|
+
([details below](#project-setup-your-teams-rules)). If Claude Code or Codex is installed, the
|
|
78
|
+
installer offers to open it with "run spec-setup" already typed in.
|
|
79
|
+
|
|
80
|
+
**3. Then run each stage in order.** You can type the slash command or just say the
|
|
86
81
|
words in quotes.
|
|
87
82
|
|
|
88
83
|
```
|
|
@@ -145,36 +140,24 @@ others `fix/STP-123-short-name`. Some have one repo, others have six. None of th
|
|
|
145
140
|
skills. Each project describes its own rules in **`.spec-driven.yml`**, and every skill reads that
|
|
146
141
|
file before doing anything.
|
|
147
142
|
|
|
148
|
-
**How the file gets created:**
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
**What `spec-driven-dev` asks, in order.** Each question is a menu: move with **↑/↓**, tick with
|
|
155
|
-
**Space**, and confirm with **Enter**. What it detected is pre-selected and marked *(detected)*, so you
|
|
156
|
-
can usually just press Enter. Repo paths are added one at a time: paste a path (quotes, `~`, and
|
|
157
|
-
Windows paths like `C:\work\api` all work). A path that doesn't exist or isn't a git repo is flagged
|
|
158
|
-
straight away, so you can fix it before moving on. Press Enter on an empty line when you're done.
|
|
159
|
-
**Ctrl+C** cancels without changing anything.
|
|
143
|
+
**How the file gets created:** in your AI tool, say **"run spec-setup"**. It detects what it can
|
|
144
|
+
(layout, tracker, branches, commit style per repo, commands, written rules in `CLAUDE.md`/`AGENTS.md`,
|
|
145
|
+
CI config, recent PR targets), shows you a draft marked *detected* or *guess*, and asks only about
|
|
146
|
+
the guesses, in one round. You can correct anything in plain words ("actually it's Jira, key STP")
|
|
147
|
+
before it writes the file. If any skill finds no config, it runs this setup first.
|
|
160
148
|
|
|
161
|
-
|
|
|
149
|
+
| What it asks | Detected from | Example answer |
|
|
162
150
|
|---|---|---|
|
|
163
|
-
|
|
|
164
|
-
|
|
|
165
|
-
|
|
|
166
|
-
| *multi-repo:* which repos nearby belong | git repos up to two folders down, grouped by GitHub owner | `frontend` |
|
|
167
|
-
| *multi-repo:* repos in other places | (you type paths: absolute, `~/…`, or relative) | `~/Work/backend, /srv/database` |
|
|
168
|
-
| *multi-repo:* where the config lives | the repos' shared parent folder, or `~/.spec-driven/<name>/` | `~/Projects` |
|
|
169
|
-
| *monorepo:* package folders | the workspaces your `package.json`/`pnpm-workspace.yaml` declares, or `apps/*`, `packages/*` | `apps/web, packages/ui` |
|
|
151
|
+
| How the project is organised | git repos and workspace files (`pnpm-workspace.yaml`, `turbo.json`, `nx.json`, `go.work`, …). A Shopify app's `extensions/` count as one app | `single`, `monorepo`, or `multi-repo` |
|
|
152
|
+
| *multi-repo:* which repos you work on | git repos nearby, grouped by GitHub owner, plus the team's `team-repos.yml` | `soreto-shopify, reverb-backend` |
|
|
153
|
+
| *multi-repo:* where specs live | (you choose) | a dedicated specs repo, a code repo, or this machine only |
|
|
170
154
|
| Issue tracker | ticket keys in branch names and commits | Jira, key `STP` |
|
|
171
|
-
| Base branch / PR target | default branch, `staging`/`develop
|
|
155
|
+
| Base branch / PR target | default branch, `staging`/`develop`, recent PR targets | `staging` |
|
|
172
156
|
| Branch name pattern | your recent branch names | `{ticket}` → `STP-123` |
|
|
173
|
-
| Commit style |
|
|
174
|
-
|
|
|
175
|
-
|
|
|
157
|
+
| Commit style | recent commit messages, judged per repo | `ticket` (`STP-123: …`), with one repo on `conventional` |
|
|
158
|
+
| Commands | `package.json` scripts, lockfile, Makefile, CI | `npm run lint` |
|
|
159
|
+
| Share specs with the team | your `.gitignore` today | everything / project memory only / local |
|
|
176
160
|
| After you approve a spec | (you choose) | `stop` (wait at each stage) or `run-through` |
|
|
177
|
-
| Model and effort per task tier | written with the defaults above | edit `models:` / `effort:` later |
|
|
178
161
|
|
|
179
162
|
**Where the file lives, by layout:**
|
|
180
163
|
|
|
@@ -184,34 +167,23 @@ straight away, so you can fix it before moving on. Press Enter on an empty line
|
|
|
184
167
|
| **monorepo** | one repo, many apps/packages | the repo root (commit it) | tasks name their package, and checks run per package |
|
|
185
168
|
| **multi-repo** | several repos that change together, in any folders | their shared parent folder, or `~/.spec-driven/<name>/` (personal to your machine) | investigation searches every listed repo, tasks stay inside one repo each, and `spec-ship` opens one PR per repo, each linking the others |
|
|
186
169
|
|
|
187
|
-
**Repos in different places.** A multi-repo workspace doesn't need its repos in one folder
|
|
188
|
-
|
|
170
|
+
**Repos in different places.** A multi-repo workspace doesn't need its repos in one folder
|
|
171
|
+
(`/Projects/frontend`, `~/Work/backend`, `/srv/database`). The config goes in their shared parent
|
|
172
|
+
folder, or `~/.spec-driven/<name>/` when that would be your home folder, and is recorded in
|
|
173
|
+
`~/.spec-driven/workspaces.json` so every repo finds it.
|
|
189
174
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
✓ added /Users/you/Work/backend
|
|
197
|
-
? Add a repo from another folder — paste its path, or press Enter to finish (2 selected) › /srv/database
|
|
198
|
-
✓ added /srv/database
|
|
199
|
-
? Add a repo from another folder — paste its path, or press Enter to finish (3 selected) ›
|
|
200
|
-
✔ Folder for this workspace's settings file (.spec-driven.yml) › ~/.spec-driven/frontend-workspace
|
|
201
|
-
```
|
|
175
|
+
**Teams where people work on different repos.** One developer has the Shopify app and the backend,
|
|
176
|
+
another the dashboard and the backend. Each person's workspace lists only the repos they have, and
|
|
177
|
+
specs go in a place everyone has: a small dedicated specs repo is best. Its `team-repos.yml` lists
|
|
178
|
+
every team repo with its git URL, so setup offers them, `doctor` shows which aren't cloned yet (with
|
|
179
|
+
the clone command), and an investigation stops and says so when a ticket needs a repo that isn't on
|
|
180
|
+
this machine.
|
|
202
181
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
- **Paths are checked:** ones that don't exist or aren't git repos are skipped with a message, and
|
|
209
|
-
setup stops if a workspace would end up with no repos.
|
|
210
|
-
|
|
211
|
-
Every question also has a flag, for scripts and CI:
|
|
212
|
-
`--layout=multi-repo --repos=~/Projects/frontend,~/Work/backend --workspace=~/Projects`,
|
|
213
|
-
`--layout=monorepo --packages=apps/web,packages/ui`, `--after-approval=run-through`,
|
|
214
|
-
`--project=new|existing`, `--share=all|memory|none`, `--specs-dir=<path>`, `--tools=cursor,codex`, `--global`, `--yes` (accept everything detected), and `--version`.
|
|
182
|
+
**For scripts and CI** there's `spec-driven-dev setup` with flags (it asks nothing):
|
|
183
|
+
`--yes`, `--layout=multi-repo --repos=~/Projects/frontend,~/Work/backend --workspace=~/Projects`,
|
|
184
|
+
`--layout=monorepo --packages=apps/web,packages/ui`, `--specs-dir=<path>`, `--share=all|memory|none`,
|
|
185
|
+
`--after-approval=run-through`, `--project=new|existing`. Installer flags: `--tools=cursor,codex`,
|
|
186
|
+
`--global`, `--no-setup`, `--version`.
|
|
215
187
|
|
|
216
188
|
To change the rules later, edit the file or run setup again, then run `spec-driven-dev doctor`
|
|
217
189
|
to confirm the file still matches your repos. Setup never overwrites the file without
|
|
@@ -601,19 +573,19 @@ run one after another, stopping at the first failure or review blocker. Two step
|
|
|
601
573
|
spec, and pushing or opening the PR. You don't need slash commands either. Saying "investigate
|
|
602
574
|
STP-123" or "ship it" picks the right skill.
|
|
603
575
|
|
|
604
|
-
**
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
576
|
+
**The installer didn't ask me about my project.**
|
|
577
|
+
That's expected from 1.13.0: the installer only installs the skills. Project setup happens in your
|
|
578
|
+
AI tool: say "run spec-setup". It runs on Node 14+, and works from `!` inside Claude Code too.
|
|
579
|
+
|
|
580
|
+
**npm prints "Unknown project config" warnings.**
|
|
581
|
+
They come from your project's `.npmrc`, not from this package: settings such as `auto-install-peers`
|
|
582
|
+
or `shamefully-hoist` are for pnpm, and npm warns about them on every `npm`/`npx` command in that
|
|
583
|
+
folder. If the project uses npm (it has a `package-lock.json`), delete those lines. If it uses pnpm,
|
|
584
|
+
run `pnpm dlx @benjaminthomas/spec-driven-dev` instead.
|
|
611
585
|
|
|
612
|
-
**Setup
|
|
613
|
-
|
|
614
|
-
`
|
|
615
|
-
there was no terminal to type answers into (it prints a note saying so), so run it directly in a
|
|
616
|
-
terminal. Check which version ran with `npx @benjaminthomas/spec-driven-dev@latest --version`.
|
|
586
|
+
**Setup thought my repo was a monorepo (or a single repo).**
|
|
587
|
+
Tell it in the chat: "this is one repo that works with reverb-backend". A Shopify app's
|
|
588
|
+
`extensions/` folder is treated as part of one app from 1.13.0.
|
|
617
589
|
|
|
618
590
|
**Can I skip stages?**
|
|
619
591
|
Yes. Every skill works on its own. `spec-verify` even works without a spec: it asks what to check.
|
|
@@ -663,7 +635,7 @@ Then, in a project:
|
|
|
663
635
|
|
|
664
636
|
```bash
|
|
665
637
|
spec-driven-dev # detect tools, install skills, set up the project
|
|
666
|
-
spec-driven-dev setup
|
|
638
|
+
spec-driven-dev setup --yes # only (re)run project setup
|
|
667
639
|
spec-driven-dev --global # install skills for all projects, not just this one
|
|
668
640
|
spec-driven-dev --tools=cursor,codex # pick tools without being asked
|
|
669
641
|
spec-driven-dev --skills=spec-create,spec-ship # install only some skills
|
|
@@ -679,7 +651,7 @@ spec-driven-dev --help # show all of the above
|
|
|
679
651
|
```
|
|
680
652
|
|
|
681
653
|
**Without installing:** replace `spec-driven-dev` with `npx @benjaminthomas/spec-driven-dev`, for
|
|
682
|
-
example `npx @benjaminthomas/spec-driven-dev
|
|
654
|
+
example `npx @benjaminthomas/spec-driven-dev doctor`. That downloads and runs the latest version once,
|
|
683
655
|
which is handy in CI or on a machine you won't use again.
|
|
684
656
|
|
|
685
657
|
`doctor` checks that every branch, folder, and `npm run` script named in the config exists, and
|
|
@@ -713,8 +685,8 @@ install `ticket-to-pr@spec-driven-dev`; the old name no longer gets updates.
|
|
|
713
685
|
|
|
714
686
|
To see unfinished specs and their next step at any time, run `spec-driven-dev status`
|
|
715
687
|
(for example, `specs/stp-123: 3/5 tasks done → next: spec-implement`). If
|
|
716
|
-
you use the plugin, `spec-driven-dev` skips Claude Code so you don't get every skill twice.
|
|
717
|
-
|
|
688
|
+
you use the plugin, `spec-driven-dev` skips Claude Code so you don't get every skill twice. Then say
|
|
689
|
+
"run spec-setup" in Claude Code for the project config.
|
|
718
690
|
|
|
719
691
|
### By hand
|
|
720
692
|
|
|
@@ -768,8 +740,8 @@ skips that step rather than failing.
|
|
|
768
740
|
|
|
769
741
|
| Skill | Stage | SKILL.md lines | Words | Supporting files |
|
|
770
742
|
|---|---|---:|---:|---:|
|
|
771
|
-
| `spec-setup` | 0 · configure |
|
|
772
|
-
| `spec-investigate` | 1 · understand |
|
|
743
|
+
| `spec-setup` | 0 · configure | 210 | 2,201 | 3 |
|
|
744
|
+
| `spec-investigate` | 1 · understand | 387 | 3,367 | 1 |
|
|
773
745
|
| `spec-create` | 2 · plan | 198 | 1,782 | 6 |
|
|
774
746
|
| `spec-implement` | 3 · build | 327 | 2,753 | 3 |
|
|
775
747
|
| `spec-verify` | 4 · prove | 163 | 1,557 | 1 |
|
|
@@ -777,7 +749,7 @@ skips that step rather than failing.
|
|
|
777
749
|
| `spec-checkpoint` | 6 · commit | 140 | 1,144 | 0 |
|
|
778
750
|
| `spec-ship` | 7 · PR | 230 | 1,388 | 0 |
|
|
779
751
|
| `generative-seo` | standalone | 255 | 2,209 | 7 |
|
|
780
|
-
| **Total** | | **2,
|
|
752
|
+
| **Total** | | **2,047** | **17,695** | **22** |
|
|
781
753
|
|
|
782
754
|
- **8** skills: **1** setup + **6** workflow stages + **1** standalone
|
|
783
755
|
- **4** tools supported natively (Claude Code, Codex CLI, Cursor, Antigravity), plus an MCP fallback
|
|
@@ -830,7 +802,7 @@ plugin/evals/ `claude plugin eval` cases
|
|
|
830
802
|
1. Increase `version` in both `package.json` and `plugin/.claude-plugin/plugin.json` (the check fails if
|
|
831
803
|
they differ).
|
|
832
804
|
2. Add a `CHANGELOG.md` entry, then commit and push.
|
|
833
|
-
3. Tag the release and push the tag: `git tag v1.
|
|
805
|
+
3. Tag the release and push the tag: `git tag v1.13.1 && git push origin v1.13.1`.
|
|
834
806
|
4. Watch the **publish** run in the repo's Actions tab. When it's green, the new version is on
|
|
835
807
|
npm. Claude Code plugin users get it from the marketplace once the commit is on `main`.
|
|
836
808
|
|
package/bin/cli.mjs
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// spec-driven-dev installer (npm i -g @benjaminthomas/spec-driven-dev): detects your AI coding tools, copies the skills into each one, and
|
|
3
3
|
// runs project discovery (layout, tracker, branches, commands) to write .spec-driven.yml.
|
|
4
4
|
//
|
|
5
|
-
// spec-driven-dev install
|
|
6
|
-
// spec-driven-dev setup
|
|
5
|
+
// spec-driven-dev install the skills, then hand over to your AI tool ("run spec-setup")
|
|
6
|
+
// spec-driven-dev setup scripted setup for CI: needs --yes or answer flags (people: say "run spec-setup")
|
|
7
7
|
// spec-driven-dev doctor check .spec-driven.yml against the real repos (exit 1 on problems)
|
|
8
8
|
// spec-driven-dev status list specs with unfinished work and each one's "Resume here" next step
|
|
9
9
|
// spec-driven-dev context print specs/CONTEXT.md; --areas=billing,api prints only those area files
|
|
@@ -39,9 +39,24 @@ const args = process.argv.slice(2);
|
|
|
39
39
|
const flag = (n) => args.includes(`--${n}`);
|
|
40
40
|
const opt = (n) => args.find((a) => a.startsWith(`--${n}=`))?.split("=")[1];
|
|
41
41
|
const command = args.find((a) => !a.startsWith("-")) || "install";
|
|
42
|
-
const YES = flag("yes") || flag("y") || !process.stdin.isTTY || ["doctor", "status", "context", "risk", "risk-eval"].includes(command);
|
|
42
|
+
const YES = flag("yes") || flag("y") || !process.stdin.isTTY || ["doctor", "status", "context", "risk", "risk-eval", "setup"].includes(command);
|
|
43
43
|
const DIR = path.resolve(opt("dir") || process.cwd());
|
|
44
44
|
|
|
45
|
+
// Colour only for a person at a terminal; pipes, CI and NO_COLOR get plain text (and the tests stay plain).
|
|
46
|
+
const COLOR = (process.stdout.isTTY || !!process.env.FORCE_COLOR) && !process.env.NO_COLOR && process.env.TERM !== "dumb";
|
|
47
|
+
const paint = (code) => (t) => (COLOR ? `\x1b[${code}m${t}\x1b[0m` : t);
|
|
48
|
+
const ink = { green: paint("32"), yellow: paint("33"), red: paint("31"), cyan: paint("36"), magenta: paint("35"), blue: paint("34"), bold: paint("1"), dim: paint("2") };
|
|
49
|
+
// Each stage gets its own colour so the steps are easy to tell apart while scrolling.
|
|
50
|
+
const step = (n, title, color) => console.log(`\n${color(ink.bold(`━━ ${n} · ${title} `))}${color("━".repeat(Math.max(4, 44 - title.length)))}`);
|
|
51
|
+
if (COLOR) {
|
|
52
|
+
const plain = console.log.bind(console);
|
|
53
|
+
console.log = (...a) => {
|
|
54
|
+
const t = a.join(" ");
|
|
55
|
+
plain(t.replace(/^(\s*)(✓)/gm, (_, s, g) => s + ink.green(g)).replace(/^(\s*)(✗.*)$/gm, (_, s, g) => s + ink.red(g))
|
|
56
|
+
.replace(/^(\s*)(•)/gm, (_, s, g) => s + ink.yellow(g)).replace(/^(Next:.*)$/gm, (_, g) => ink.cyan(ink.bold(g))));
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
45
60
|
if (flag("version") || flag("v")) {
|
|
46
61
|
console.log(JSON.parse(fs.readFileSync(path.join(ROOT, "package.json"), "utf8")).version);
|
|
47
62
|
process.exit(0);
|
|
@@ -53,18 +68,17 @@ const NO_TTY = !process.stdin.isTTY && !args.some((a) => ANSWER_FLAGS.some((f) =
|
|
|
53
68
|
// Installing the skills needs no answers, so the default command still does that without a terminal
|
|
54
69
|
// (e.g. `!` in Claude Code) and hands the questions to the spec-setup skill, which asks them in the chat.
|
|
55
70
|
const HANDOFF = command === "install" && NO_TTY;
|
|
56
|
-
|
|
71
|
+
// Setup is a conversation in the AI tool now; the setup command stays for scripts and CI (flags only).
|
|
72
|
+
const SETUP_IN_AGENT = command === "setup" && !args.some((a) => ANSWER_FLAGS.some((f) => a === `--${f}` || a.startsWith(`--${f}=`)));
|
|
73
|
+
if (command === "settings" && NO_TTY) {
|
|
57
74
|
console.log(`✗ No interactive terminal, so ${command === "settings" ? "the settings menu can't open" : "setup can't ask its questions"}. Nothing was changed.
|
|
58
75
|
|
|
59
76
|
This happens when it runs through another program: a "!" command in Claude Code, an editor task,
|
|
60
77
|
or piped input. Either:
|
|
61
78
|
• run it yourself in a terminal window (Terminal, iTerm, or your editor's terminal panel):
|
|
62
|
-
npx @benjaminthomas/spec-driven-dev@latest
|
|
63
|
-
• or
|
|
64
|
-
|
|
65
|
-
• or give the answers as flags, for example:
|
|
66
|
-
npx @benjaminthomas/spec-driven-dev@latest ${command === "settings" ? "settings --risk=off" : "setup --layout=multi-repo --repos=~/Projects/frontend,~/Projects/backend"}
|
|
67
|
-
(add --yes to accept everything it detects)`);
|
|
79
|
+
npx @benjaminthomas/spec-driven-dev@latest settings
|
|
80
|
+
• or give the answer as a flag, for example:
|
|
81
|
+
npx @benjaminthomas/spec-driven-dev@latest settings --risk=off`);
|
|
68
82
|
process.exit(1);
|
|
69
83
|
}
|
|
70
84
|
|
|
@@ -113,6 +127,7 @@ function copyDir(from, to) {
|
|
|
113
127
|
|
|
114
128
|
// ---------- install ----------
|
|
115
129
|
async function install() {
|
|
130
|
+
step(1, "Install the skills", ink.blue);
|
|
116
131
|
const detected = Object.keys(TOOLS).filter((t) => fs.existsSync(path.join(HOME, TOOLS[t].home)) || onPath(TOOLS[t].bin));
|
|
117
132
|
let tools = opt("tools")?.split(",");
|
|
118
133
|
if (!tools) tools = await askMulti("Which AI coding tools should get the skills?",
|
|
@@ -145,7 +160,33 @@ async function install() {
|
|
|
145
160
|
console.log(`✓ ${skills.length} skills → ${dest}`);
|
|
146
161
|
}
|
|
147
162
|
if (!dests.length) console.log(HANDOFF ? "• No AI coding tools found (Claude Code, Cursor, Codex or Antigravity). Install one, or pass --tools=claude,cursor." : "• No tools selected — nothing installed.");
|
|
148
|
-
return dests.length;
|
|
163
|
+
return dests.length ? tools : [];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// The setup questions (layout, repos, tracker, branches, sharing) and the audit need a model, so they
|
|
167
|
+
// happen in the AI tool. Claude Code and Codex can be opened with the request already typed in.
|
|
168
|
+
const LAUNCHERS = [["claude", "Claude Code"], ["codex", "Codex"]];
|
|
169
|
+
async function handOver(tools, onlySetup) {
|
|
170
|
+
// The folder says where the AI tool opens; "project" says what gets set up (it can span several repos).
|
|
171
|
+
const name = path.basename(gitTop(DIR));
|
|
172
|
+
const what = "It first asks how your project is organised: one repo, a monorepo, or several repos that change together.";
|
|
173
|
+
const next = `Next: in ${name}, open your AI tool and say "run spec-setup".\n${what}
|
|
174
|
+
Then it asks about your tracker, branches and sharing, and looks through the code.
|
|
175
|
+
Start a new session if your tool doesn't recognise it yet.`;
|
|
176
|
+
step(onlySetup ? 1 : 2, "Set up your project in your AI tool", ink.magenta);
|
|
177
|
+
if (onlySetup) {
|
|
178
|
+
console.log(`Setup happens in your AI tool now, so it can read the code and you can change any answer as you go.\n${next}\nFor scripts and CI: spec-driven-dev setup --yes [--layout=… --repos=… --share=…]`);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const launcher = LAUNCHERS.find(([bin]) => tools.includes(bin) && onPath(bin));
|
|
182
|
+
if (!launcher || !process.stdin.isTTY || YES) { console.log(`Skills installed.\n${next}`); return; }
|
|
183
|
+
// The question carries the explanation, so there's no paragraph to read twice.
|
|
184
|
+
if (!(await askConfirm(`Open ${launcher[1]} in ${name} to set up your project? ${what} (A few minutes, on your ${launcher[1]} plan.)`, true))) {
|
|
185
|
+
console.log(next);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const { spawnSync } = await import("node:child_process");
|
|
189
|
+
spawnSync(launcher[0], ["run spec-setup"], { cwd: DIR, stdio: "inherit" });
|
|
149
190
|
}
|
|
150
191
|
|
|
151
192
|
// ---------- discovery ----------
|
|
@@ -186,7 +227,9 @@ function detectLayout(dir) {
|
|
|
186
227
|
const base = g.replace(/\/\*\*?$/, "");
|
|
187
228
|
return base === g ? [g.replace(/\/$/, "")] : subdirs(path.join(top, base)).map((n) => `${base}/${n}`);
|
|
188
229
|
}))].filter(hasManifest);
|
|
189
|
-
|
|
230
|
+
// A Shopify app keeps its app extensions in npm workspaces under extensions/: one app, not a monorepo.
|
|
231
|
+
const shopifyApp = fs.readdirSync(top).some((f) => /^shopify\.app.*\.toml$/.test(f)) && declared.length > 0 && declared.every((g) => g.startsWith("extensions/"));
|
|
232
|
+
const mono = !shopifyApp && (MARKERS.some((m) => fs.existsSync(path.join(top, m))) || pkg?.workspaces || cargo || packages.length > 1);
|
|
190
233
|
return { layout: mono ? "monorepo" : "single", root: top, repos: mono ? packages : [] };
|
|
191
234
|
}
|
|
192
235
|
const repos = findRepos(dir);
|
|
@@ -237,11 +280,12 @@ function infer(facts) {
|
|
|
237
280
|
const y = (v) => JSON.stringify(v ?? ""); // JSON strings are valid YAML scalars
|
|
238
281
|
function toYaml(c) {
|
|
239
282
|
const cmds = (o, pad) => Object.entries(o).map(([k, v]) => `${pad}${k}: ${y(v)}`).join("\n");
|
|
240
|
-
let out = `# .spec-driven.yml — written by spec-driven-dev
|
|
283
|
+
let out = `# .spec-driven.yml — written by spec-driven-dev. Edit freely, or say \"run spec-setup\" in your AI tool to review it.\n# Every spec-* skill reads this first. Field reference: skills/spec-setup/references/config-template.yml\nversion: 1\nlayout: ${c.layout}\n`;
|
|
241
284
|
if (c.repos.length) {
|
|
242
285
|
out += "repos:\n" + c.repos.map((r) => {
|
|
243
286
|
let e = ` - path: ${y(r.path)}\n role: ${y(r.role)}\n`;
|
|
244
|
-
|
|
287
|
+
const g = [r.base && r.base !== c.git.base_branch ? ` base_branch: ${y(r.base)}\n pr_target: ${y(r.base)}\n` : "", r.commitStyle ? ` commit_style: ${y(r.commitStyle)}\n` : ""].join("");
|
|
288
|
+
if (g) e += ` git:\n${g}`;
|
|
245
289
|
if (r.commands && Object.values(r.commands).some(Boolean)) e += ` commands:\n${cmds(r.commands, " ")}\n`;
|
|
246
290
|
return e;
|
|
247
291
|
}).join("");
|
|
@@ -481,7 +525,9 @@ async function setup() {
|
|
|
481
525
|
repos: repos.map((r) => {
|
|
482
526
|
const f = layout === "multi-repo" ? gitFacts(path.resolve(root, r)) : null;
|
|
483
527
|
// a repo without an integration branch must say so, or it inherits the top-level base (e.g. staging) it doesn't have
|
|
484
|
-
|
|
528
|
+
// each repo keeps its own commit style when it differs from the workspace's (one repo uses STP-123:, another feat:)
|
|
529
|
+
const own = f && f.subjects.length >= 5 ? infer([f]).commitStyle : "";
|
|
530
|
+
return { path: r, role: "", base: f ? f.integration || "default" : "", commitStyle: own && own !== commitStyle ? own : "", commands: commandsFor(path.resolve(root, r)) };
|
|
485
531
|
}),
|
|
486
532
|
};
|
|
487
533
|
fs.writeFileSync(file, toYaml(config));
|
|
@@ -555,8 +601,9 @@ function findConfig(dir) {
|
|
|
555
601
|
}
|
|
556
602
|
|
|
557
603
|
function doctor() {
|
|
604
|
+
if (COLOR) step("✓", "Check the settings", ink.cyan);
|
|
558
605
|
const file = findConfig(DIR);
|
|
559
|
-
if (!file) throw new Error(`No .spec-driven.yml in ${DIR} or above.
|
|
606
|
+
if (!file) throw new Error(`No .spec-driven.yml in ${DIR} or above. In your AI tool, say "run spec-setup".`);
|
|
560
607
|
const root = path.dirname(file);
|
|
561
608
|
let c;
|
|
562
609
|
try { c = parseYaml(fs.readFileSync(file, "utf8")); } catch (e) { throw new Error(`${file} could not be read: ${e.message}`); }
|
|
@@ -604,6 +651,17 @@ function doctor() {
|
|
|
604
651
|
// Multi-repo context names paths from the workspace folder (soreto-shopify/app/) or from inside a repo (app/).
|
|
605
652
|
const repoRoots = c.layout === "multi-repo" ? [root, ...(c.repos || []).map((r) => path.resolve(root, expandHome(r.path)))] : [root];
|
|
606
653
|
checkContext(specs, c.specs_dir || "specs", repoRoots, problems, notes);
|
|
654
|
+
// The team's repo list (in the specs location) vs the repos on this machine.
|
|
655
|
+
const team = parseYaml(fs.existsSync(path.join(specs, "team-repos.yml")) ? fs.readFileSync(path.join(specs, "team-repos.yml"), "utf8") : "").repos;
|
|
656
|
+
if (Array.isArray(team)) {
|
|
657
|
+
const mine = new Set((c.repos || []).map((r) => path.basename(String(r.path || "").replace(/\/+$/, ""))));
|
|
658
|
+
for (const t of team) {
|
|
659
|
+
if (!t.name || mine.has(t.name)) continue;
|
|
660
|
+
const here = path.join(root, t.name);
|
|
661
|
+
notes.push(isRepo(here) ? `${t.name} is cloned at ${here} but isn't in this workspace — say "add ${t.name} to the workspace" if you work on it`
|
|
662
|
+
: `team repo ${t.name} isn't on this machine (fine if you don't work on it)${t.url ? `: git clone ${t.url} ${here}` : ""}`);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
607
665
|
if (c.context?.share) {
|
|
608
666
|
if (!SHARE_MODES.includes(c.context.share)) problems.push(`context.share "${c.context.share}" must be all, memory or none`);
|
|
609
667
|
const g = specsInGit(root, c.specs_dir || "specs");
|
|
@@ -687,6 +745,7 @@ async function chooseShare(root, specsDir) {
|
|
|
687
745
|
const g = specsInGit(root, specsDir);
|
|
688
746
|
if (opt("share") && !SHARE_MODES.includes(opt("share"))) throw new Error("--share must be all, memory or none");
|
|
689
747
|
if (!g) { if (opt("share") && opt("share") !== "none") console.log(` • ${specsDir} isn't inside a git repo, so it can't be shared. Choose a folder inside a repo (--specs-dir) to share it.`); return null; }
|
|
748
|
+
if (!g.rel) { console.log(` • ${specsDir} is a repo of its own, so everything in it is shared.`); return "all"; }
|
|
690
749
|
const now = shareState(g.top, g.rel);
|
|
691
750
|
if (now !== "all") console.log(` • Your .gitignore currently makes ${g.rel}: ${shareTitle[now].toLowerCase()}.`);
|
|
692
751
|
return opt("share") || askSelect("Share specs and project memory with your team?", [
|
|
@@ -699,6 +758,7 @@ async function chooseShare(root, specsDir) {
|
|
|
699
758
|
async function applyShare(root, specsDir, mode, { quietIfSame } = {}) {
|
|
700
759
|
const g = specsInGit(root, specsDir);
|
|
701
760
|
if (!g) { console.log(` • ${specsDir} isn't inside a git repo, so there's nothing to share or ignore.`); return; }
|
|
761
|
+
if (!g.rel) mode = "all"; // a dedicated specs repo is shared by definition
|
|
702
762
|
if (quietIfSame && shareState(g.top, g.rel) === mode && mode !== "all") return;
|
|
703
763
|
const gi = path.join(g.top, ".gitignore");
|
|
704
764
|
const before = fs.existsSync(gi) ? fs.readFileSync(gi, "utf8") : "";
|
|
@@ -804,7 +864,7 @@ const SETTINGS = [
|
|
|
804
864
|
|
|
805
865
|
async function settings() {
|
|
806
866
|
const file = findConfig(DIR);
|
|
807
|
-
if (!file) throw new Error(`No .spec-driven.yml in ${DIR} or above.
|
|
867
|
+
if (!file) throw new Error(`No .spec-driven.yml in ${DIR} or above. In your AI tool, say "run spec-setup" first.`);
|
|
808
868
|
let text = fs.readFileSync(file, "utf8");
|
|
809
869
|
const label = (s, v) => s.options.find((o) => o.value === v)?.title || v;
|
|
810
870
|
const save = async (s, v) => {
|
|
@@ -864,20 +924,14 @@ try {
|
|
|
864
924
|
else if (command === "risk") risk();
|
|
865
925
|
else if (command === "settings") await settings();
|
|
866
926
|
else if (command === "risk-eval") await riskEvalCmd();
|
|
867
|
-
else if (command === "install"
|
|
868
|
-
console.log("No interactive terminal (for example a \"!\" command in Claude Code), so
|
|
869
|
-
const
|
|
870
|
-
if (
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
If your AI tool doesn't recognise it yet, start a new session so it loads the new skills.
|
|
874
|
-
To answer in a terminal instead, open Terminal or iTerm and run: npx @benjaminthomas/spec-driven-dev setup`);
|
|
875
|
-
} else if (command === "install") {
|
|
876
|
-
await install();
|
|
877
|
-
if (!flag("no-setup") && (await askConfirm("Set up this project now?", true))) await setup();
|
|
878
|
-
} else if (command === "setup") await setup();
|
|
927
|
+
else if (command === "install") {
|
|
928
|
+
if (HANDOFF) console.log("No interactive terminal (for example a \"!\" command in Claude Code), so the skills go to the AI tools it finds, for this project.\n");
|
|
929
|
+
const tools = await install();
|
|
930
|
+
if (tools.length && !flag("no-setup")) await handOver(tools);
|
|
931
|
+
} else if (SETUP_IN_AGENT) handOver([], true);
|
|
932
|
+
else if (command === "setup") await setup();
|
|
879
933
|
else throw new Error(`Unknown command "${command}" — use install (default), setup, settings, doctor, status, context, risk, or risk-eval`);
|
|
880
|
-
if (
|
|
934
|
+
if (command === "setup" && !SETUP_IN_AGENT) console.log('\nNext: in your AI tool, say "investigate <ticket>" or "create a spec".');
|
|
881
935
|
} catch (e) {
|
|
882
936
|
console.error(`✗ ${e.message}`);
|
|
883
937
|
process.exitCode = 1;
|
package/bin/share.mjs
CHANGED
|
@@ -12,13 +12,16 @@ const git = (cwd, args) => {
|
|
|
12
12
|
const ignored = (top, rel) => !!git(top, ["check-ignore", "--no-index", rel]);
|
|
13
13
|
|
|
14
14
|
// What git does today: is the project memory ignored, and is a feature's working folder ignored?
|
|
15
|
+
const under = (specs, p) => (specs ? `${specs}/${p}` : p); // specs can be a whole repo (a dedicated specs repo)
|
|
16
|
+
|
|
15
17
|
export function shareState(top, specs) {
|
|
16
|
-
const memory = !ignored(top,
|
|
17
|
-
const features = !ignored(top,
|
|
18
|
+
const memory = !ignored(top, under(specs, "CONTEXT.md")) && !ignored(top, under(specs, "context/notes.md"));
|
|
19
|
+
const features = !ignored(top, under(specs, "some-feature/README.md"));
|
|
18
20
|
return features && memory ? "all" : memory ? "memory" : features ? "custom" : "none";
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
export function blockFor(specs, mode) {
|
|
24
|
+
if (!specs) return [BEGIN, "/context/risk.md", END]; // a specs repo exists to be shared: only the generated file is ignored
|
|
22
25
|
const lines = mode === "none" ? [`${specs}/`]
|
|
23
26
|
: mode === "memory" ? [`${specs}/*`, `!${specs}/CONTEXT.md`, `!${specs}/context/`, `${specs}/context/risk.md`]
|
|
24
27
|
: [`${specs}/context/risk.md`];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@benjaminthomas/spec-driven-dev",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.1",
|
|
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.
|
|
3
|
+
"version": "1.13.1",
|
|
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",
|
|
@@ -65,6 +65,18 @@ Cheap sources first, code second:
|
|
|
65
65
|
|
|
66
66
|
No CONTEXT.md? Investigate as usual, and suggest running `spec-setup` afterwards to create one.
|
|
67
67
|
|
|
68
|
+
## Repos this machine doesn't have (multi-repo)
|
|
69
|
+
|
|
70
|
+
The workspace config lists only the repos cloned here. If the ticket or the evidence points at a
|
|
71
|
+
repo that isn't listed (its name in the ticket, an API or package it owns, a `team-repos.yml` role
|
|
72
|
+
that matches), **stop before concluding** and say so plainly:
|
|
73
|
+
|
|
74
|
+
> This ticket also touches **reverb-react** (the merchant dashboard), which isn't on this machine.
|
|
75
|
+
> Clone it: `git clone <url from team-repos.yml> <workspace>/reverb-react`, then say "add
|
|
76
|
+
> reverb-react to the workspace" and investigate again. Or I can carry on and mark that part UNKNOWN.
|
|
77
|
+
|
|
78
|
+
Never present a cross-repo change as complete when part of it lives in a repo you couldn't read.
|
|
79
|
+
|
|
68
80
|
## Risk signal (GitRisk)
|
|
69
81
|
|
|
70
82
|
Use it only when `.spec-driven.yml` has `risk.enabled: on`, or has no `risk:` block, **and**
|
|
@@ -72,12 +84,14 @@ Use it only when `.spec-driven.yml` has `risk.enabled: on`, or has no `risk:` bl
|
|
|
72
84
|
file, compute scores, or mention risk. That keeps on/off comparisons (`spec-driven-dev risk-eval`)
|
|
73
85
|
clean.
|
|
74
86
|
|
|
75
|
-
1.
|
|
87
|
+
1. **Required, not optional:** once you know the change surface, and before writing the report, look up its files: `spec-driven-dev risk --files=a,b` (or
|
|
76
88
|
`npx -y @benjaminthomas/spec-driven-dev risk --files=…`), or find them in `risk.md`. Without the
|
|
77
89
|
CLI, approximate a file's history with `git log --since="2 years ago" --format=%s -- <file>`.
|
|
78
90
|
2. For each **high** or **critical** file in the surface, look harder: read its last ~10 commit
|
|
79
91
|
subjects and the diffs of its last 1–2 fix commits, and check its dependents. Write what keeps
|
|
80
92
|
breaking there in the report's **Risk Hotspots** section.
|
|
93
|
+
The report's **Risk Hotspots** section must exist when risk is on: list the high/critical files,
|
|
94
|
+
or write "none of the touched files are high or critical (checked)" so a reader knows it was done.
|
|
81
95
|
3. A score is a reason to look harder, never evidence. Hotspot findings still need their
|
|
82
96
|
VERIFIED / INFERRED labels.
|
|
83
97
|
4. **New files** (added in the last 90 days with fewer than 3 commits, or not committed yet) show as tier `new`: too little
|
|
@@ -17,7 +17,9 @@ license: MIT
|
|
|
17
17
|
# spec-setup
|
|
18
18
|
|
|
19
19
|
Learn this project's rules and context once, save them, and let every other `spec-*` skill reuse
|
|
20
|
-
them.
|
|
20
|
+
them. This is **the** setup: the npm installer only copies the skills and then sends people here, so
|
|
21
|
+
treat it as their first conversation with the workflow. Explain each question in a line, and let
|
|
22
|
+
them change any earlier answer at any point ("actually it's Jira") before you write anything. Team rules live in a config file the team can review and change — never in the skills
|
|
21
23
|
themselves. What the project *knows* (areas, conventions, decisions) lives in
|
|
22
24
|
`{specs_dir}/CONTEXT.md`, kept small so loading it is cheap.
|
|
23
25
|
|
|
@@ -44,6 +46,11 @@ Read, don't guess. Collect a draft of every field in `references/config-template
|
|
|
44
46
|
`package.json`, `turbo.json`, `nx.json`, `lerna.json`, `go.work`, `[workspace]` in
|
|
45
47
|
`Cargo.toml`, several `apps/*`/`packages/*`/`services/*` with their own manifests) →
|
|
46
48
|
`monorepo`; list the packages.
|
|
49
|
+
- **Not a monorepo:** a Shopify app (`shopify.app*.toml` at the root) whose workspaces only cover
|
|
50
|
+
`extensions/`. Those are parts of one app deployed together, so it's `single` (or one repo of a
|
|
51
|
+
multi-repo workspace). The same goes for any repo whose "packages" are plug-ins of one app.
|
|
52
|
+
- **Ask when one repo works with others.** Even inside a single repo, ask once: "Does this repo
|
|
53
|
+
change together with other repos (for example a backend)?" A yes means `multi-repo`.
|
|
47
54
|
- Not inside a git repo, but sibling folders are git repos → candidate `multi-repo`. Group them
|
|
48
55
|
by remote owner (`git remote get-url origin`) and show the groups — the user picks which repos
|
|
49
56
|
belong together. Never assume every folder in the parent belongs. Always also ask whether
|
|
@@ -68,7 +75,9 @@ Read, don't guess. Collect a draft of every field in `references/config-template
|
|
|
68
75
|
`gh repo view --json defaultBranchRef`); integration branches (`staging`, `develop`,
|
|
69
76
|
`pre-prod`); which branch recent PRs targeted (`gh pr list --state merged --limit 20 --json
|
|
70
77
|
baseRefName`); the pattern of the last ~20 remote branch names; the commit style from
|
|
71
|
-
`git log -30 --format=%s`.
|
|
78
|
+
`git log -30 --format=%s`. Judge commit style **per repo**: when one repo writes `STP-123: …` and
|
|
79
|
+
the others write `feat: …`, keep the majority at the top level and give that repo its own
|
|
80
|
+
`git.commit_style`.
|
|
72
81
|
5. **Commands, per repo/package.** `package.json` scripts plus the lockfile's package manager,
|
|
73
82
|
`Makefile`, `pyproject.toml`/`tox.ini`, `Cargo.toml`, `go.mod`, `composer.json`, CI workflow
|
|
74
83
|
steps (CI is often the most truthful source). Dev URL from the dev script, `.env.example`, or
|
|
@@ -82,7 +91,22 @@ Read, don't guess. Collect a draft of every field in `references/config-template
|
|
|
82
91
|
pushing. Store it as `pipeline.after_approval`.
|
|
83
92
|
8. **New or existing project.** Detect it (commit and file counts) and confirm in one line. Store
|
|
84
93
|
it as `context.project`. This decides what Step 4 does.
|
|
85
|
-
9. **
|
|
94
|
+
9. **Where specs live (multi-repo).** One specs folder serves the whole workspace: each ticket gets
|
|
95
|
+
one folder there even when it changes several repos; code, branches and PRs stay in each repo.
|
|
96
|
+
Offer, in this order, with the reason for each:
|
|
97
|
+
- **A dedicated specs repo (recommended for teams where developers work on different subsets of
|
|
98
|
+
repos):** everyone clones it, whatever code repos they have. Use an existing one (path or git
|
|
99
|
+
URL; offer the `git clone` command) or create one: only after a yes, `mkdir` it next to the
|
|
100
|
+
repos and `git init`. Never create a remote repo; say how to push it (`gh repo create`).
|
|
101
|
+
Then set `specs_dir: <specs-repo>` (its root holds `CONTEXT.md`, `context/` and feature folders).
|
|
102
|
+
- **Inside one code repo** (`<repo>/specs`): fine when every developer has that repo.
|
|
103
|
+
- **The workspace folder:** on this machine only, since it isn't a git repo.
|
|
104
|
+
10. **The team's repo list.** Keep `{specs_dir}/team-repos.yml` (see `references/config-template.yml`)
|
|
105
|
+
listing every repo the team works across with its git URL and a one-line role. Each developer's
|
|
106
|
+
own `.spec-driven.yml` lists only the repos they have. When the file exists, offer its repos
|
|
107
|
+
instead of making people remember them, and for any not cloned here show
|
|
108
|
+
`git clone <url> <workspace>/<name>`. When someone adds a repo, add it to the file too.
|
|
109
|
+
11. **Sharing with the team.** Find out what git does with the specs folder today:
|
|
86
110
|
`git check-ignore -v {specs_dir}/CONTEXT.md` and `git check-ignore -v {specs_dir}/x/README.md`.
|
|
87
111
|
Then ask once, saying what's true now (e.g. "your .gitignore ignores `specs/`, so nothing is
|
|
88
112
|
shared today"):
|
|
@@ -93,7 +117,8 @@ Read, don't guess. Collect a draft of every field in `references/config-template
|
|
|
93
117
|
|
|
94
118
|
**Multi-repo:** if the config folder isn't a git repo, specs kept there can't be shared. Say so
|
|
95
119
|
and offer `specs_dir: <repo>/specs` inside the repo the team treats as home.
|
|
96
|
-
Store the answer as `context.share`.
|
|
120
|
+
Store the answer as `context.share`. A dedicated specs repo is shared by nature: set
|
|
121
|
+
`context.share: all`.
|
|
97
122
|
|
|
98
123
|
## Step 2 — Confirm in one round
|
|
99
124
|
|
|
@@ -145,6 +170,11 @@ Skip if `{specs_dir}/CONTEXT.md` already exists (offer a refresh instead, see `r
|
|
|
145
170
|
6. Re-run `npx -y @benjaminthomas/spec-driven-dev doctor` if available. It checks the size limits
|
|
146
171
|
and flags context lines that point to files which no longer exist.
|
|
147
172
|
|
|
173
|
+
## After setup
|
|
174
|
+
|
|
175
|
+
Finish with **one** next step, not a list: "Setup's done. Say *investigate STP-123* to start your
|
|
176
|
+
first ticket." If anything was left out (a tracker URL, a repo not cloned), name it in one line.
|
|
177
|
+
|
|
148
178
|
## Quick switches (plain words, no YAML)
|
|
149
179
|
|
|
150
180
|
When the user asks to switch a feature on or off, change only that one line of `.spec-driven.yml`,
|
|
@@ -156,6 +186,7 @@ keep every other line and comment, and confirm in one sentence. Don't re-run dis
|
|
|
156
186
|
| "turn GitRisk on", "use the risk scores" | `risk.enabled: on`, then run `spec-driven-dev risk` if `{specs_dir}/context/risk.md` doesn't exist yet |
|
|
157
187
|
| "run straight through after I approve", "fewer stops" | `pipeline.after_approval: run-through` |
|
|
158
188
|
| "stop after each stage" | `pipeline.after_approval: stop` |
|
|
189
|
+
| "add reverb-react to the workspace", "I also work on X" | add it under `repos:` (clone first if needed, from `team-repos.yml`), and to `team-repos.yml` if it's new to the team |
|
|
159
190
|
| "share the specs with the team", "keep specs local", "share only the project memory" | `context.share: all / none / memory`: show the `.gitignore` change, then run `spec-driven-dev settings --share=…` on a yes |
|
|
160
191
|
|
|
161
192
|
Reply with what changed and what it means, for example: "GitRisk is now off. Investigations, plans
|
|
@@ -101,4 +101,15 @@ context:
|
|
|
101
101
|
# CONTEXT.md + context/ | none = ignore the specs folder. risk.md is always ignored.
|
|
102
102
|
# Change with: spec-driven-dev settings --share=…
|
|
103
103
|
|
|
104
|
-
specs_dir: specs # where specs/<feature>/ folders go (relative to this file)
|
|
104
|
+
specs_dir: specs # where specs/<feature>/ folders go (relative to this file).
|
|
105
|
+
# Multi-repo teams: a dedicated specs repo (e.g. soreto-specs) everyone clones.
|
|
106
|
+
|
|
107
|
+
# {specs_dir}/team-repos.yml — every repo the team works across, shared with the specs. Each developer's
|
|
108
|
+
# .spec-driven.yml lists only the repos they have; setup offers the rest with their clone commands.
|
|
109
|
+
# repos:
|
|
110
|
+
# - name: reverb-backend
|
|
111
|
+
# url: git@github.com:acme/reverb-backend.git
|
|
112
|
+
# role: REST API and workers
|
|
113
|
+
# - name: reverb-react
|
|
114
|
+
# url: git@github.com:acme/reverb-react.git
|
|
115
|
+
# role: merchant dashboard
|