@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.
- package/MIGRATION.md +260 -10
- package/README.md +53 -4
- package/assets/claude-md-section.md +48 -2
- package/assets/commands/spec-connect.md +2 -2
- package/assets/commands/spec-live.md +2 -2
- package/assets/core/env.config.json.example +9 -3
- package/assets/core/env.config.md +102 -30
- package/assets/core/gating.config.json.example +4 -0
- package/assets/core/gating.config.md +81 -0
- package/assets/review/page.html +1501 -0
- package/assets/rules/spec-planning.md +224 -15
- package/assets/rules/spec-reports.md +269 -0
- package/assets/skills/spec/SKILL.md +63 -12
- package/assets/skills/spec-bug/SKILL.md +134 -26
- package/assets/skills/spec-cancel/SKILL.md +85 -6
- package/assets/skills/spec-complete/SKILL.md +109 -20
- package/assets/skills/spec-diff/SKILL.md +564 -0
- package/assets/skills/spec-hotfix/SKILL.md +143 -21
- package/assets/skills/spec-init/SKILL.md +49 -9
- package/assets/skills/spec-next/SKILL.md +289 -7
- package/assets/skills/spec-review/SKILL.md +45 -9
- package/assets/skills/spec-reviewed/SKILL.md +241 -0
- package/assets/skills/spec-start/SKILL.md +323 -66
- package/assets/skills/spec-to-main/SKILL.md +42 -20
- package/package.json +11 -7
- package/src/cli.js +1710 -80
- package/src/env/building.js +143 -0
- package/src/env/classify.js +91 -0
- package/src/env/config.js +57 -9
- package/src/env/provision.js +192 -19
- package/src/env/proxy.js +34 -1
- package/src/env/render.js +3 -12
- package/src/env/resolve.js +296 -9
- package/src/env/review.js +1329 -0
- package/src/env/serve.js +549 -0
- package/src/env/teardown.js +13 -6
- package/src/gating.js +155 -0
- package/src/init.js +124 -2
- package/src/prompts.js +10 -1
- 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
|
|
5
|
-
|
|
6
|
-
`/spec-
|
|
7
|
-
`skitterspec spec-env
|
|
8
|
-
|
|
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
|
|
12
|
-
spec escalation** — a spec brings up a stack only when its
|
|
13
|
-
is `worktree + docker` (set at `/spec` when it touches
|
|
14
|
-
services). A `worktree`-only spec takes no registry slot, 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
|
|
35
|
-
//
|
|
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,
|
|
38
|
-
// terminal
|
|
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.
|
|
172
|
-
// branch
|
|
173
|
-
// branch on the remote
|
|
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,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
|
+
```
|