@skitterbyte/skitterspec 17.0.0 → 19.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/MIGRATION.md +260 -10
  2. package/README.md +53 -4
  3. package/assets/claude-md-section.md +48 -2
  4. package/assets/commands/spec-connect.md +2 -2
  5. package/assets/commands/spec-live.md +2 -2
  6. package/assets/core/env.config.json.example +9 -3
  7. package/assets/core/env.config.md +102 -30
  8. package/assets/core/gating.config.json.example +4 -0
  9. package/assets/core/gating.config.md +81 -0
  10. package/assets/review/page.html +1501 -0
  11. package/assets/rules/spec-planning.md +224 -15
  12. package/assets/rules/spec-reports.md +269 -0
  13. package/assets/skills/spec/SKILL.md +63 -12
  14. package/assets/skills/spec-bug/SKILL.md +134 -26
  15. package/assets/skills/spec-cancel/SKILL.md +85 -6
  16. package/assets/skills/spec-complete/SKILL.md +109 -20
  17. package/assets/skills/spec-diff/SKILL.md +564 -0
  18. package/assets/skills/spec-hotfix/SKILL.md +143 -21
  19. package/assets/skills/spec-init/SKILL.md +49 -9
  20. package/assets/skills/spec-next/SKILL.md +289 -7
  21. package/assets/skills/spec-review/SKILL.md +45 -9
  22. package/assets/skills/spec-reviewed/SKILL.md +241 -0
  23. package/assets/skills/spec-start/SKILL.md +323 -66
  24. package/assets/skills/spec-to-main/SKILL.md +42 -20
  25. package/package.json +11 -7
  26. package/src/cli.js +1710 -80
  27. package/src/env/building.js +143 -0
  28. package/src/env/classify.js +91 -0
  29. package/src/env/config.js +57 -9
  30. package/src/env/provision.js +192 -19
  31. package/src/env/proxy.js +34 -1
  32. package/src/env/render.js +3 -12
  33. package/src/env/resolve.js +296 -9
  34. package/src/env/review.js +1329 -0
  35. package/src/env/serve.js +549 -0
  36. package/src/env/teardown.js +13 -6
  37. package/src/gating.js +155 -0
  38. package/src/init.js +124 -2
  39. package/src/prompts.js +10 -1
  40. package/LICENSE +0 -21
@@ -1,18 +1,19 @@
1
1
  # `env.config.json` — per-spec isolation config
2
2
 
3
3
  Opt-in config for per-spec isolation (git worktree + optional namespaced Docker
4
- stack + host dev servers + a front-door proxy + an optional opener per
5
- in-progress spec). Provisioning is folded into `/spec-start`, teardown into
6
- `/spec-complete` · `/spec-cancel`, and traffic diversion is `/spec-connect`; the
7
- `skitterspec spec-env <up|down|prune|dev|connect|integrate>` CLI is the engine
8
- beneath them.
4
+ stack + host dev servers + a front-door proxy per in-progress spec).
5
+ Provisioning is folded into `/spec-start`, teardown into `/spec-complete` ·
6
+ `/spec-cancel`, traffic diversion is `/spec-connect`, and reading a spec's diff
7
+ is `/spec-diff`; the `skitterspec spec-env
8
+ <up|down|prune|dev|connect|integrate|hotfix|live|review|stage|status|resolve>` CLI is
9
+ the engine beneath them.
9
10
 
10
11
  **Once this file is present, isolation is the default policy:** `/spec-start` gives
11
- **every** in-progress spec its own git worktree automatically. Docker is a **per-
12
- spec escalation** — a spec brings up a stack only when its `> **Stack:**` header
13
- is `worktree + docker` (set at `/spec` when it touches the DB / stateful
14
- services). A `worktree`-only spec takes no registry slot, no port block, and no
15
- `.env`.
12
+ **every** in-progress spec its own git worktree automatically. Docker is a
13
+ **per- spec escalation** — a spec brings up a stack only when its
14
+ `> **Stack:**` header is `worktree + docker` (set at `/spec` when it touches
15
+ the DB / stateful services). A `worktree`-only spec takes no registry slot, no
16
+ port block, and no `.env`.
16
17
 
17
18
  **Adopt it** with `skitterspec init --isolation` (or copy
18
19
  `env.config.json.example` → `env.config.json` here) and edit the values. While
@@ -31,11 +32,12 @@ no live `env.config.json` was found.
31
32
  // Where a spec's branch gets built.
32
33
  //
33
34
  // "worktree" (default) — every spec gets its own git worktree. Several
34
- // specs run side by side and `main` stays free, at the cost of
35
- // one terminal session per spec (`/spec-start` sets it up for you).
35
+ // specs run side by side and `main` stays free. `/spec-start`
36
+ // moves your session into the spec's worktree with a `cd`, so
37
+ // the terminal you are already in follows the work.
36
38
  // "checkout" — the branch is built in the primary checkout instead. One
37
- // spec at a time, but no second session and no hand-off: the
38
- // terminal you are already in follows the work.
39
+ // spec at a time, and nowhere else to stand: the work comes to
40
+ // your terminal rather than your terminal going to it.
39
41
  //
40
42
  // Pick it for how you work, not for what this repo contains — a project with
41
43
  // no dev servers may still want several specs in flight. An unrecognised
@@ -124,19 +126,6 @@ no live `env.config.json` was found.
124
126
  "host": "127.0.0.1" // bind host for the canonical ports
125
127
  },
126
128
 
127
- // Optional, editor/terminal-agnostic opener. `/spec-start` RUNS it when it hands
128
- // you into a new worktree — after provisioning and bootstrap, so the session
129
- // opens onto a tree that is ready to work in. The template is expanded with
130
- // {worktreePath}, {slug}, {branch}, {projectName}, {portOffset}.
131
- // Empty = nothing is opened (the path is just printed), which is how you turn
132
- // the auto-open off. A non-interactive run skips it either way — an opened
133
- // window nobody is sitting at helps no one.
134
- // Examples: "code {worktreePath}", "tmux new-window -c {worktreePath}",
135
- // or a "warp://..." deeplink for Warp users.
136
- "open": {
137
- "command": ""
138
- },
139
-
140
129
  // Machine-local slot registry (spec → slot index). Resolved against the
141
130
  // primary checkout root, shared by all worktrees, gitignored.
142
131
  "registry": ".spec-env/registry.json",
@@ -153,6 +142,26 @@ no live `env.config.json` was found.
153
142
  "identifierField": ""
154
143
  },
155
144
 
145
+ // Paths that belong to a spec ALONGSIDE its own `specs/<bucket>/<name>/`
146
+ // folder. `/spec-start` uses this to tell "the spec you just wrote, not yet
147
+ // committed" apart from someone else's uncommitted work: if every dirty path
148
+ // belongs to the spec being started it is committed for you, and if a single
149
+ // path does not, the start is refused as before.
150
+ //
151
+ // Provider-neutral by design — the base engine must not know that any
152
+ // particular tracker exists — so you declare the shape here. `{slug}` and
153
+ // `{identifier}` expand exactly as in `branch.pattern` above, `{identifier}`
154
+ // via `branch.identifierField`.
155
+ //
156
+ // A pattern using {identifier} matches NOTHING when no identifier resolves
157
+ // (no `identifierField` set, or a spec never pushed to a tracker). That is
158
+ // deliberate: the file it names then belongs to some other spec, and the safe
159
+ // failure is a refusal you clear with /commit, not a stranger's file swept
160
+ // into your commit. Default: none — a spec owns only its own folder.
161
+ "spec": {
162
+ "companionPaths": []
163
+ },
164
+
156
165
  // Integration base branch — the branch specs fork from and land back onto
157
166
  // (used by the teardown "merged?" guard and, later, the integrate step).
158
167
  // Empty = auto-detect: origin/HEAD → main → master. Set it when your default
@@ -168,9 +177,10 @@ no live `env.config.json` was found.
168
177
  "refuseTeardownIfUnpushed": true
169
178
  },
170
179
 
171
- // What teardown cleans up beyond this machine. `/spec-start` pushes the spec
172
- // branch when it provisions, so without this a completed spec leaves a merged
173
- // branch on the remote forever. `deleteRemoteBranch`:
180
+ // What teardown cleans up beyond this machine. Nothing publishes a spec
181
+ // branch for you, so a remote copy exists only because you pushed it by hand —
182
+ // and without this a completed spec leaves that merged branch on the remote
183
+ // forever. `deleteRemoteBranch`:
174
184
  // "prompt" (default) — plan `git push <remote> --delete <branch>` in its own
175
185
  // "confirm with the user first" section; /spec-complete and
176
186
  // /spec-cancel ask before running it.
@@ -207,6 +217,68 @@ no live `env.config.json` was found.
207
217
  "bump": "patch",
208
218
  "cherryPickMain": true,
209
219
  "targets": []
220
+ },
221
+
222
+ // Reading a spec's diff (`spec-env review`, `/spec-diff`).
223
+ //
224
+ // `reader` decides how the page's LOCATION IS WORDED, and — through
225
+ // `serveOnRemote` below — whether the engine stands its local server up so a
226
+ // remote reader gets a link that opens. It never decides to PUBLISH. Three
227
+ // values:
228
+ // "local" — you are at the machine holding the page; a file:// URL opens.
229
+ // "remote" — you are not; it does not, so the page is served instead.
230
+ // "detect" — work it out (the default).
231
+ // An explicit "local"/"remote" is BELIEVED WITHOUT SNIFFING: you know where
232
+ // you are reading, and no signal outranks being told. Detection is only the
233
+ // default, and it has three outcomes rather than two — local, remote, and
234
+ // unknown. Unknown behaves exactly as the tool did before any of this existed
235
+ // (the file:// URL, no warning), because a wrong "local" prints a dead link
236
+ // and a wrong "remote" warns at someone whose link works fine. An
237
+ // unrecognised value falls through to "detect", so a typo cannot become a
238
+ // confident answer. Default: detect.
239
+ //
240
+ // `servePort` is the default port for `spec-env review serve`, which renders
241
+ // every spec's diff per request on one local server. `--port` overrides it per
242
+ // run. The server binds 127.0.0.1 unless `--host 0.0.0.0` is passed, which
243
+ // mints an unguessable path token and prints the LAN URL including it —
244
+ // anyone holding that URL can read every spec's diff while it runs.
245
+ // Default: 7777.
246
+ //
247
+ // `serveOnRemote` is whether a "remote" reader may have that server started
248
+ // FOR them. On (the default) the engine brings it up, binds 0.0.0.0, and puts
249
+ // the served URL on the `open:` line — best-guess network address first, the
250
+ // rest listed under it, because the guess reads interface names and a VPN or
251
+ // an unusual adapter will fool it. Off, you get the file:// URL with its
252
+ // "will not open where you are reading" marker and the command to type.
253
+ // Either way NOTHING IS PUBLISHED on a detection: a server is one process
254
+ // ended by one flag, while a published page is one this tooling cannot
255
+ // remove, so that half stays an explicit ask. Teardown names a server that
256
+ // served the last spec, and `spec-env prune` reaps a pidfile whose process is
257
+ // gone. Default: true.
258
+ //
259
+ // `commitWith` names the skill a COMMITTING verdict hands off to. A review
260
+ // page ends in a verdict — commit, commit & continue, request changes,
261
+ // discuss — and the point of the first two is that the commit follows from
262
+ // the reading rather than costing a separate decision. Skitterspec never
263
+ // commits through a skill it vendored: `/commit` ships with skittership, a
264
+ // different package, and a copy living here would fork it. Two shapes:
265
+ // "/commit" — the default; hand off to skittership's commit skill.
266
+ // "<name>" — any other skill your project installs.
267
+ // With the named skill unavailable, the committing branch commits directly —
268
+ // stage, typecheck, test, conventional message — and SAYS it took that path,
269
+ // because a commit made under rules nobody configured must not read as one
270
+ // made under /commit.
271
+ //
272
+ // THERE IS NO OFF SWITCH. `"none"` existed and was removed: it produced a
273
+ // verdict that records itself and does nothing, which is the one thing a
274
+ // review page must not offer — a review is the guard in front of an action.
275
+ // Recording an approval for SOMEONE ELSE to act on is a separate mechanism,
276
+ // not a value of this key. Default: "/commit".
277
+ "review": {
278
+ "reader": "detect",
279
+ "servePort": 7777,
280
+ "serveOnRemote": true,
281
+ "commitWith": "/commit"
210
282
  }
211
283
  }
212
284
  ```
@@ -0,0 +1,4 @@
1
+ {
2
+ "guidance": "",
3
+ "default": "none: <reason>"
4
+ }
@@ -0,0 +1,81 @@
1
+ # `gating.config.json` — release-gating config
2
+
3
+ Opt-in config for **release gating**: whether each spec records a decision about
4
+ shipping behind a feature flag.
5
+
6
+ The problem it solves is not "we forgot to use a flag" — it is that
7
+ **nobody can tell whether the question was asked**. A spec can go from `/spec`
8
+ through
9
+ implementation to `/spec-complete` with no flag and no mention of one, and that
10
+ is indistinguishable from "we considered it and decided against". Prose guidance
11
+ in a project rule has already been shown not to close that gap; a required header
12
+ does, because its absence is visible.
13
+
14
+ **Skitterspec bakes in the offer, never the mechanism.** How your project does
15
+ flags — a resolver, an admin toggle, env precedence, a vendor SDK — is none of
16
+ its business, and it never reads your flag code. It asks the question, cites your
17
+ own documentation, and records the answer.
18
+
19
+ **Adopt it** by copying `gating.config.json.example` → `gating.config.json` here
20
+ (or `skitterspec init --gating`). While this file is absent the feature is
21
+ entirely unused: no grill question, no header, nothing to check — which is read
22
+ as "this project does not use feature flags".
23
+
24
+ ## Fields
25
+
26
+ ```jsonc
27
+ {
28
+ // Where THIS project documents how its flags work — a repo-relative path,
29
+ // cited when the question is asked so the answer is an informed one.
30
+ // Skitterspec never reads the file; it only names it. Empty = say nothing.
31
+ "guidance": ".claude/rules/feature-flags.md",
32
+
33
+ // The value written when the user declines a flag, so a project can
34
+ // standardise its wording. Must keep the `none: <reason>` shape — the reason
35
+ // half is the whole point (see below). Empty falls back to `none: <reason>`.
36
+ "default": "none: <reason>"
37
+ }
38
+ ```
39
+
40
+ ## The header it drives
41
+
42
+ With this file present, every spec `/spec`, `/spec-bug` and `/spec-hotfix` write
43
+ carries a `Gating:` field beside `Stack:` in `00-overview.md`:
44
+
45
+ ```
46
+ > **Gating:** search-ranking-v2
47
+ > **Gating:** none: additive, nothing to revert
48
+ ```
49
+
50
+ Two valid shapes, and one that is not:
51
+
52
+ | Value | Meaning |
53
+ |-------|---------|
54
+ | a flag name | ships behind that flag |
55
+ | `none: <reason>` | deliberately not flagged, and why |
56
+ | *missing, empty, or a bare `none`* | **not a decision** — nobody answered |
57
+
58
+ The reason half is load-bearing. `none: additive, nothing to revert` is a
59
+ decision a reviewer can disagree with; a bare `none` is a shrug, and a missing
60
+ line is an oversight. Distinguishing those three is the entire feature.
61
+
62
+ ## What it never does
63
+
64
+ - **It never blocks.** `skitterspec gating check` reports and exits 0;
65
+ `/spec-start` and `/spec-complete` mention a missing header and carry on.
66
+ Nothing here can stop a spec being started, completed or landed.
67
+ - **It never accuses an old spec.** Only `specs/backlog/` and
68
+ `specs/in-progress/` are read. Specs finished or abandoned before you adopted
69
+ gating are out of range by construction, not by a filter someone has to
70
+ remember.
71
+ - **It never learns your flag system.** `guidance` is a path it prints, nothing
72
+ more.
73
+
74
+ ## Checking
75
+
76
+ ```
77
+ skitterspec gating check # the spec in flight here
78
+ skitterspec gating check <spec> # one named spec
79
+ skitterspec gating check --all # every active spec
80
+ skitterspec gating check --json # for tooling
81
+ ```