@skitterbyte/skitterspec 1.0.0 → 2.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/README.md +27 -244
- package/assets/claude-md-section.md +0 -6
- package/assets/core/env.config.json.example +5 -1
- package/assets/core/env.config.md +21 -5
- package/assets/rules/spec-planning.md +14 -10
- package/assets/skills/spec/SKILL.md +11 -38
- package/assets/skills/spec-complete/SKILL.md +31 -4
- package/assets/skills/spec-env/SKILL.md +6 -0
- package/assets/skills/spec-env-down/SKILL.md +16 -8
- package/assets/skills/spec-go/SKILL.md +15 -17
- package/package.json +6 -11
- package/src/cli.js +174 -318
- package/src/deprecate.js +138 -0
- package/src/env/config.js +17 -4
- package/src/env/integrate.js +46 -0
- package/src/env/resolve.js +54 -45
- package/src/env/teardown.js +19 -4
- package/src/env/trust.js +87 -0
- package/src/init.js +78 -170
- package/src/prompts.js +26 -63
- package/LICENSE +0 -21
- package/assets/core/linear.config.json.example +0 -39
- package/assets/core/linear.config.md +0 -121
- package/assets/rules/commit-messages.md +0 -85
- package/assets/scripts/generate-changelog.js +0 -274
- package/assets/scripts/generate-releases.js +0 -360
- package/assets/scripts/lib/config.js +0 -127
- package/assets/scripts/lib/git-commits.js +0 -265
- package/assets/skills/commit/SKILL.md +0 -28
- package/assets/skills/spec-pull/SKILL.md +0 -46
- package/assets/skills/spec-push/SKILL.md +0 -53
- package/assets/skills/spec-status/SKILL.md +0 -46
- package/src/config.js +0 -13
- package/src/sync/apply.js +0 -66
- package/src/sync/base.js +0 -83
- package/src/sync/compare.js +0 -99
- package/src/sync/config.js +0 -198
- package/src/sync/mcp.js +0 -112
- package/src/sync/normalize.js +0 -249
- package/src/sync/pull.js +0 -84
- package/src/sync/push.js +0 -106
- package/src/sync/write.js +0 -86
package/README.md
CHANGED
|
@@ -1,259 +1,42 @@
|
|
|
1
1
|
# @skitterbyte/skitterspec
|
|
2
2
|
|
|
3
|
-
Spec-driven
|
|
4
|
-
|
|
3
|
+
Spec-driven development for [Claude Code](https://claude.com/claude-code) — a
|
|
4
|
+
**tracker-free** filesystem workflow. Ships the spec-lifecycle skills (`/spec`,
|
|
5
|
+
`/spec-ready`, `/spec-go`, `/spec-complete`, `/spec-cancel`, `/spec-bug`,
|
|
6
|
+
`/spec-init`) and per-spec **isolation** (a git worktree per in-progress spec,
|
|
7
|
+
Docker on demand).
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
skill, two governing rules, and the `specs/` folder structure. The lifecycle is
|
|
8
|
-
`backlog → in-progress → complete / cancelled`, with `.core` holding always-apply
|
|
9
|
-
project rules.
|
|
10
|
-
|
|
11
|
-
| Skill | Action | Status | Folder |
|
|
12
|
-
|-------|--------|--------|--------|
|
|
13
|
-
| `/spec` | (Feature) Grill to a shared understanding, then write a concise spec | `Draft` | `specs/backlog/` |
|
|
14
|
-
| `/spec-bug` | (Bug) Reproduce with a failing test, capture spec, drive red→green | `In Progress` | `specs/in-progress/` |
|
|
15
|
-
| `/spec-ready` | Confirm the spec is groomed | `Ready` | `specs/backlog/` |
|
|
16
|
-
| `/spec-review` | Re-validate a spec against the codebase; refresh stale parts | `—` | (unchanged) |
|
|
17
|
-
| `/spec-go` | Implement the next phase (with tests) | `In Progress` | `specs/in-progress/` |
|
|
18
|
-
| `/spec-complete` | Verify all phases done + tests green | `Complete` | `specs/complete/` |
|
|
19
|
-
| `/spec-cancel` | Record progress, stamp a reason | `Cancelled` | `specs/cancelled/` |
|
|
20
|
-
| `/spec-init` | Bootstrap/repair the workflow (manual path) | — | — |
|
|
21
|
-
| `/commit` | Stage the task's files, run typecheck + tests, write a conventional commit (+ release-note footer) | — | (unchanged) |
|
|
22
|
-
|
|
23
|
-
## Install into a project
|
|
24
|
-
|
|
25
|
-
From the root of the target project:
|
|
26
|
-
|
|
27
|
-
```bash
|
|
9
|
+
```sh
|
|
28
10
|
npx @skitterbyte/skitterspec init
|
|
29
11
|
```
|
|
30
12
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
```
|
|
36
|
-
.claude/skills/spec*/SKILL.md # the 8 spec-lifecycle skills
|
|
37
|
-
.claude/skills/commit/SKILL.md # the /commit skill
|
|
38
|
-
.claude/rules/spec-planning.md # governing rule (the single source of truth)
|
|
39
|
-
.claude/rules/commit-messages.md # commit message + release-note grammar
|
|
40
|
-
specs/{.core,backlog,in-progress,complete,cancelled}/
|
|
41
|
-
CLAUDE.md # adds a "## Spec workflow" section (created if absent)
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
If you enable the **release tooling** (see below) it also writes:
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
skitterspec.config.json # which artifacts to generate, filenames, scope→area map
|
|
48
|
-
scripts/generate-changelog.js # dev-facing CHANGELOG generator (if changelog enabled)
|
|
49
|
-
scripts/generate-releases.js # user-facing RELEASES generator (if releases enabled)
|
|
50
|
-
scripts/lib/ # shared git + config helpers
|
|
51
|
-
package.json # adds a "version" hook + changelog/releases npm scripts
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Options
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
npx @skitterbyte/skitterspec init ./path/to/project # target a dir (default: cwd)
|
|
58
|
-
npx @skitterbyte/skitterspec init --yes # accept defaults, skip the prompts
|
|
59
|
-
npx @skitterbyte/skitterspec init --force # overwrite existing skill/rule/script files
|
|
60
|
-
npx @skitterbyte/skitterspec init --no-claude-md # don't touch CLAUDE.md
|
|
61
|
-
npx @skitterbyte/skitterspec init --isolation # adopt per-spec isolation (worktree per spec)
|
|
62
|
-
npx @skitterbyte/skitterspec update # re-copy skills + rule + scripts, leave specs/ + config alone
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Release-tooling flags (drive setup without the prompts):
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
--changelog / --no-changelog # enable/disable CHANGELOG generation
|
|
69
|
-
--releases / --no-releases # enable/disable user-facing release notes
|
|
70
|
-
--changelog-file=NAME # changelog filename (default CHANGELOG.md)
|
|
71
|
-
--releases-file=NAME # release-notes filename (default RELEASES.md)
|
|
72
|
-
--product-name=NAME # product name shown in the release-notes header
|
|
73
|
-
--version-hook / --no-version-hook # wire (or skip) the npm "version" hook
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
`update` pulls newer skill/rule/script versions after upgrading the package,
|
|
77
|
-
without disturbing your specs or `skitterspec.config.json`. The CLAUDE.md section
|
|
78
|
-
is wrapped in `<!-- skitterspec:start -->`…`<!-- skitterspec:end -->` markers so
|
|
79
|
-
`update` can refresh it in place.
|
|
80
|
-
|
|
81
|
-
## Changelog & release-note tooling (opt-in)
|
|
82
|
-
|
|
83
|
-
Conventional commits already say what changed; skitterspec can turn them into two
|
|
84
|
-
generated artifacts at `npm version`:
|
|
85
|
-
|
|
86
|
-
- **`CHANGELOG.md`** — dev-facing, built from commit **subjects** (Keep a Changelog
|
|
87
|
-
format: feat→Added, fix→Fixed, perf/refactor→Changed, breaking→Changed).
|
|
88
|
-
- **`RELEASES.md`** — user-facing, built **only** from `Release-Note:` commit
|
|
89
|
-
**footers**, grouped by area and bucket (New / Improved / Fixed / Action
|
|
90
|
-
required). The `/commit` skill writes these footers; the grammar lives in
|
|
91
|
-
`.claude/rules/commit-messages.md`.
|
|
92
|
-
|
|
93
|
-
Both walk *commits since the last version tag*. Generation is opt-in per artifact
|
|
94
|
-
and recorded in **`skitterspec.config.json`** at the repo root:
|
|
95
|
-
|
|
96
|
-
```json
|
|
97
|
-
{
|
|
98
|
-
"version": 1,
|
|
99
|
-
"changelog": { "enabled": true, "file": "CHANGELOG.md" },
|
|
100
|
-
"releases": { "enabled": true, "file": "RELEASES.md",
|
|
101
|
-
"productName": "My App", "scopeAreas": {} },
|
|
102
|
-
"versionHook": true
|
|
103
|
-
}
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
`scopeAreas` maps a commit scope to a user-facing area (e.g. `{"reqs":
|
|
107
|
-
"Requisitions"}`); unmapped scopes fall back to Title-Case, and a `Release-Area:`
|
|
108
|
-
footer overrides per-commit. When `versionHook` is on, `init` wires npm scripts:
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
npm run changelog # regenerate CHANGELOG.md from commits since last tag
|
|
112
|
-
npm run releases # regenerate RELEASES.md
|
|
113
|
-
npm run changelog:retro -- 5 # backfill the last 5 tagged releases
|
|
114
|
-
npm version <patch|minor|major> # bumps, regenerates both, and stages them
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
The generators are plain Node (no `tsx`/`ts-node`); the only runtime dependency
|
|
118
|
-
the package itself adds is [`prompts`](https://www.npmjs.com/package/prompts) for
|
|
119
|
-
the interactive `init`.
|
|
120
|
-
|
|
121
|
-
## Spec structure
|
|
122
|
-
|
|
123
|
-
Every spec is a **folder**, never a bare file:
|
|
124
|
-
|
|
125
|
-
```
|
|
126
|
-
specs/backlog/feat-<name>/
|
|
127
|
-
00-overview.md # dashboard: problem, decisions, solution, phase index, logs
|
|
128
|
-
01-<phase-slug>.md # phase 1 — goal + task checkboxes (tests included)
|
|
129
|
-
02-<phase-slug>.md # phase 2 …
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
`00-overview.md` is the index — it carries a **phase table** linking to each
|
|
133
|
-
phase file with its status (`⬜`/`🔄`/`✅`). **Each phase is its own file** so it's
|
|
134
|
-
easy to dive into one phase without wading through the whole spec. The lifecycle
|
|
135
|
-
skills keep the index and phase files in sync.
|
|
136
|
-
|
|
137
|
-
## Per-spec isolation — worktree by default, Docker on demand
|
|
138
|
-
|
|
139
|
-
Work several specs in parallel without them stepping on each other. **Adopt it
|
|
140
|
-
once** with `npx @skitterbyte/skitterspec init --isolation` (or copy
|
|
141
|
-
`specs/.core/env.config.json.example` → `specs/.core/env.config.json`; every field
|
|
142
|
-
is documented in `specs/.core/env.config.md`). While the config is absent the
|
|
143
|
-
feature is simply unused.
|
|
144
|
-
|
|
145
|
-
Once adopted it's the **default policy**, not a per-spec chore:
|
|
146
|
-
|
|
147
|
-
- **Worktree — automatic for every in-progress spec.** `/spec-go` gives each spec
|
|
148
|
-
its own sibling git worktree on its own branch, so you never stash or rebuild to
|
|
149
|
-
switch specs and `main` stays free for hotfixes. All housekeeping (the
|
|
150
|
-
backlog→in-progress move, header edits, the code) happens on that branch and
|
|
151
|
-
lands in one PR; `main` changes only when it merges.
|
|
152
|
-
- **Docker — a per-spec escalation.** `/spec` records `> **Stack:** worktree`
|
|
153
|
-
(default) or `worktree + docker` when the spec touches the DB / stateful
|
|
154
|
-
services. Only an escalated spec gets a **namespaced stack** — a per-spec
|
|
155
|
-
`COMPOSE_PROJECT_NAME` isolates containers, networks, and **named volumes**, and
|
|
156
|
-
a `PORT_OFFSET` reserves a distinct port block, so N stacks run at once with no
|
|
157
|
-
clashes. A worktree-only spec takes **no** slot, port block, or `.env`.
|
|
158
|
-
- an optional **opener** — a single, editor/terminal-agnostic `open.command`
|
|
159
|
-
(e.g. `code {worktreePath}`, a `tmux` command, or a `warp://` deeplink).
|
|
160
|
-
|
|
161
|
-
The machine-local slot registry and volume backups live under `/.spec-env/`
|
|
162
|
-
(gitignored). `docker.enabled` in the config is the project **master switch**
|
|
163
|
-
("is Docker escalation available?"), not "always run Docker".
|
|
164
|
-
|
|
165
|
-
`/spec-env` · `/spec-env-down` remain the **manual engine** behind the automation
|
|
166
|
-
— use them to escalate Docker onto an existing worktree, re-attach, or tear down:
|
|
167
|
-
|
|
168
|
-
```
|
|
169
|
-
/spec-env <spec> # worktree (+ stack iff Stack: worktree + docker) + opener
|
|
170
|
-
# (idempotent; re-run attaches)
|
|
171
|
-
/spec-env-down <spec> # stop stack, drop volumes (backed up first), remove worktree,
|
|
172
|
-
# free the slot. Guards refuse a dirty/unpushed worktree
|
|
173
|
-
# unless --force; --keep-volumes preserves data.
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
Your `docker-compose.yml` must reference `${PORT_OFFSET}` on each published port
|
|
177
|
-
so services land in the spec's reserved block. Two adoption modes:
|
|
178
|
-
|
|
179
|
-
- **Standalone** (`linkLinear: false`) — plain `{type}/{slug}` branch names; pure
|
|
180
|
-
worktree + Docker + opener. No Linear needed.
|
|
181
|
-
- **Linear-linked** (`linkLinear: true` + `specs/.core/linear.config.json`) —
|
|
182
|
-
branch names follow Linear's pattern so pushing fires Linear's GitHub
|
|
183
|
-
automation.
|
|
184
|
-
|
|
185
|
-
`/spec-complete` and `/spec-cancel` will *offer* to tear down when the config is
|
|
186
|
-
present — never forced.
|
|
187
|
-
|
|
188
|
-
## Linear hybrid sync — git-like `/spec-status` · `/spec-pull` · `/spec-push`
|
|
189
|
-
|
|
190
|
-
Let **Linear own status and discussion** while the repo stays the **co-authoring
|
|
191
|
-
surface for spec content**. The sync is bidirectional but git-like: explicit
|
|
192
|
-
commands, a committed **base sidecar** for three-way merge, and no blind
|
|
193
|
-
overwrites. It's **opt-in** — everything below is inert until
|
|
194
|
-
`specs/.core/linear.config.json` exists (copy `linear.config.json.example` and
|
|
195
|
-
fill in your team / initiative IDs; every field is documented in
|
|
196
|
-
`specs/.core/linear.config.md`). Without it, `/spec`, `/spec-go`, and the CLI
|
|
197
|
-
behave exactly as before.
|
|
198
|
-
|
|
199
|
-
**Mapping** (config-driven): a spec folder → Linear **Project**; each phase
|
|
200
|
-
(`01-…`, `02-…`) → a **Milestone**; tasks → **Issues**; an optional **Initiative**
|
|
201
|
-
groups specs. When linked, `/spec` creates the project + a milestone per phase and
|
|
202
|
-
writes the linking frontmatter into `00-overview.md`.
|
|
203
|
-
|
|
204
|
-
**The git-like lifecycle:**
|
|
205
|
-
|
|
206
|
-
```
|
|
207
|
-
/spec-status # read-only — per-field divergence (local-only / remote-only
|
|
208
|
-
# / conflict / in-sync). Changes nothing.
|
|
209
|
-
/spec-pull [--force] # Linear → repo. Applies remote-only fields; refuses to
|
|
210
|
-
# clobber a conflicting local edit unless --force.
|
|
211
|
-
…refine the spec locally (the repo is the co-authoring surface)…
|
|
212
|
-
/spec-push [--force] # repo → Linear. Ownership-respecting, concurrency-checked;
|
|
213
|
-
# refuses if Linear moved since base unless --force.
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
**Field ownership** collapses conflicts: each field is `both` (co-authored,
|
|
217
|
-
can conflict), `pull` (Linear owns it — e.g. status/priority/labels), or `push`
|
|
218
|
-
(the repo owns it). Only a `both` field that moved on **both** sides is a real
|
|
219
|
-
conflict. `--force` never destroys blindly — it backs up the losing side into
|
|
220
|
-
`sync.backupDir` (a local reflog) first, then wins. After any successful
|
|
221
|
-
pull/push the engine **rewrites the base** so the next compare starts clean.
|
|
13
|
+
This installs the skills + rules into `.claude/`, scaffolds `specs/`, and patches
|
|
14
|
+
`CLAUDE.md`. See `.claude/rules/spec-planning.md` after install to set your
|
|
15
|
+
project's typecheck/test/lint commands.
|
|
222
16
|
|
|
223
|
-
|
|
224
|
-
the current shared state. **Base sidecars** (`sync.baseDir`, default
|
|
225
|
-
`specs/.core/linear-base/`) are **committed** — each worktree carries its own
|
|
226
|
-
base. **Backups** (`sync.backupDir`, default `specs/.core/linear-backups/`) are
|
|
227
|
-
local recovery and **gitignored**.
|
|
17
|
+
## Pick one distribution
|
|
228
18
|
|
|
229
|
-
|
|
230
|
-
because both sides author, the sync had to be bidirectional and three-way, not a
|
|
231
|
-
blind import.
|
|
19
|
+
Ticketing sync is a **separate superset you install instead of this one**:
|
|
232
20
|
|
|
233
|
-
|
|
21
|
+
| Install | You get |
|
|
22
|
+
|---------|---------|
|
|
23
|
+
| `@skitterbyte/skitterspec` | The base filesystem workflow. No tracker. |
|
|
24
|
+
| `@skitterbyte/skitterspec-linear` | Everything here **plus** Linear hybrid-sync (`/spec-status` · `/spec-pull` · `/spec-push`, the `spec-sync` CLI). |
|
|
234
25
|
|
|
235
|
-
|
|
236
|
-
project's typecheck and test commands" and "honour the project's conventions".
|
|
237
|
-
Make those concrete once, in **`.claude/rules/spec-planning.md`** (the
|
|
238
|
-
*Project conventions* section): set your real typecheck/test/lint commands and
|
|
239
|
-
link your other `.claude/rules/*.md`. The skills point at that file, so you don't
|
|
240
|
-
edit seven files per project.
|
|
26
|
+
Install exactly one — the superset is a strict superset of this package.
|
|
241
27
|
|
|
242
|
-
##
|
|
28
|
+
## v2 — Linear removed from the base
|
|
243
29
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
one runtime dependency, [`prompts`](https://www.npmjs.com/package/prompts), used
|
|
249
|
-
only for the interactive `init`. The copied generator scripts are dependency-free
|
|
250
|
-
and read their config from `skitterspec.config.json` — they never call back into
|
|
251
|
-
this package.
|
|
30
|
+
`@skitterbyte/skitterspec` **2.0** is tracker-free: the Linear sync skills, the
|
|
31
|
+
`spec-sync` CLI, and the `linear.config.*` templates moved to
|
|
32
|
+
`@skitterbyte/skitterspec-linear`. If you used Linear sync on v1, see
|
|
33
|
+
[MIGRATION.md](../../MIGRATION.md) — switching is one install + re-`init`.
|
|
252
34
|
|
|
253
|
-
|
|
254
|
-
pins its own version and can diverge. Re-run `update` to re-sync from a newer
|
|
255
|
-
package release.
|
|
35
|
+
## How it's built
|
|
256
36
|
|
|
257
|
-
|
|
37
|
+
This distribution is composed from the private workspace packages by
|
|
38
|
+
`scripts/build-dist.js`: the base fills the shared skills' provider seams with
|
|
39
|
+
nothing, so it's self-contained and depends only on
|
|
40
|
+
[`prompts`](https://www.npmjs.com/package/prompts) (for interactive `init`).
|
|
258
41
|
|
|
259
|
-
MIT
|
|
42
|
+
MIT © Reuben Greaves
|
|
@@ -27,12 +27,6 @@ State log.
|
|
|
27
27
|
> Tailor the per-phase test commands and project conventions referenced by the
|
|
28
28
|
> spec skills to this project's stack (see `.claude/rules/spec-planning.md`).
|
|
29
29
|
|
|
30
|
-
Also installed: **`/commit`** — stage only the task's files, run typecheck +
|
|
31
|
-
tests, then write a conventional-commit message with a `Release-Note:` footer
|
|
32
|
-
for user-visible changes (grammar in `.claude/rules/commit-messages.md`). If the
|
|
33
|
-
release tooling is enabled (`skitterspec.config.json`), those footers feed the
|
|
34
|
-
generated `CHANGELOG.md`/`RELEASES.md` at `npm version`.
|
|
35
|
-
|
|
36
30
|
**Per-spec isolation (adopt once, then default):** with isolation adopted
|
|
37
31
|
(`skitterspec init --isolation`, or `specs/.core/env.config.json` present),
|
|
38
32
|
**`/spec-go`** gives every in-progress spec its own git worktree automatically —
|
|
@@ -16,7 +16,11 @@
|
|
|
16
16
|
"command": ""
|
|
17
17
|
},
|
|
18
18
|
"registry": ".spec-env/registry.json",
|
|
19
|
-
"
|
|
19
|
+
"branch": {
|
|
20
|
+
"pattern": "{type}/{slug}",
|
|
21
|
+
"identifierField": ""
|
|
22
|
+
},
|
|
23
|
+
"baseBranch": "",
|
|
20
24
|
"guards": {
|
|
21
25
|
"refuseTeardownIfDirty": true,
|
|
22
26
|
"refuseTeardownIfUnpushed": true
|
|
@@ -62,12 +62,28 @@ no live `env.config.json` was found.
|
|
|
62
62
|
// primary checkout root, shared by all worktrees, gitignored.
|
|
63
63
|
"registry": ".spec-env/registry.json",
|
|
64
64
|
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
|
|
65
|
+
// Git branch naming, provider-neutral. `pattern` expands {type} and {slug}
|
|
66
|
+
// (e.g. "feat/add-widget"). When a ticketing provider is linked and you want
|
|
67
|
+
// tracker ids in branch names, use {identifier} in the pattern and point
|
|
68
|
+
// `identifierField` at the 00-overview.md frontmatter field the provider
|
|
69
|
+
// writes the id into — pushing that branch can then fire the tracker's
|
|
70
|
+
// automation. Empty `identifierField` (or a spec missing that field) makes a
|
|
71
|
+
// pattern with {identifier} fall back to {type}/{slug}.
|
|
72
|
+
"branch": {
|
|
73
|
+
"pattern": "{type}/{slug}",
|
|
74
|
+
"identifierField": ""
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
// Integration base branch — the branch specs fork from and land back onto
|
|
78
|
+
// (used by the teardown "merged?" guard and, later, the integrate step).
|
|
79
|
+
// Empty = auto-detect: origin/HEAD → main → master. Set it when your default
|
|
80
|
+
// branch isn't discoverable (e.g. no remote) or differs (trunk, develop).
|
|
81
|
+
"baseBranch": "",
|
|
69
82
|
|
|
70
|
-
// Teardown safety. --force overrides both.
|
|
83
|
+
// Teardown safety. --force overrides both. refuseTeardownIfUnpushed only
|
|
84
|
+
// blocks when the commits are ALSO unmerged into the base branch — a branch
|
|
85
|
+
// already landed on base tears down (and its branch is deleted) without
|
|
86
|
+
// --force, even with no remote.
|
|
71
87
|
"guards": {
|
|
72
88
|
"refuseTeardownIfDirty": true,
|
|
73
89
|
"refuseTeardownIfUnpushed": true
|
|
@@ -34,14 +34,17 @@ re-attach, tear down). Isolation is **orthogonal to lifecycle status** and
|
|
|
34
34
|
inactive when `env.config.json` is absent — every skill then behaves as it does
|
|
35
35
|
today.
|
|
36
36
|
|
|
37
|
-
**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
(
|
|
41
|
-
|
|
42
|
-
`/spec`
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
**Ticketing-provider sync (opt-in, a separate package).** The base is
|
|
38
|
+
tracker-free: it knows nothing about any specific ticketing system. A
|
|
39
|
+
ticketing provider is installed as its own distribution that plugs into two named
|
|
40
|
+
**seams** in the shared skills (`/spec` Phase E, `/spec-go` step 3b) and fulfils a
|
|
41
|
+
skill-name + CLI contract — it ships `/spec-status` (read-only per-field
|
|
42
|
+
divergence), `/spec-pull` (tracker→repo), and `/spec-push` (repo→tracker), backed
|
|
43
|
+
by a `spec-sync` CLI, three-way merged against a committed base sidecar. When a
|
|
44
|
+
provider is present, `/spec` also links the spec to the tracker and `/spec-go`
|
|
45
|
+
pulls first. With no provider installed the seams are empty and every skill
|
|
46
|
+
behaves as a plain filesystem workflow. See the provider package's own docs for
|
|
47
|
+
its config and field reference.
|
|
45
48
|
|
|
46
49
|
## Project conventions (fill this in)
|
|
47
50
|
|
|
@@ -135,8 +138,9 @@ those, but new specs always use the folder + phase-file form.
|
|
|
135
138
|
The **folder buckets are the source of truth** — a spec's bucket is its status.
|
|
136
139
|
To see the backlog, list `specs/backlog/`; for the latest completed specs, use
|
|
137
140
|
`git log`/mtime on `specs/complete/` or each spec's dated **State log**. Live
|
|
138
|
-
status also lives in
|
|
139
|
-
summary files — the folder tree, headers, and State logs are
|
|
141
|
+
status also lives in the tracker when a ticketing provider is linked. (There are
|
|
142
|
+
no `00-index.md` summary files — the folder tree, headers, and State logs are
|
|
143
|
+
queried directly.)
|
|
140
144
|
|
|
141
145
|
## Rules
|
|
142
146
|
|
|
@@ -184,41 +184,14 @@ run `/spec-env <name>` to add Docker to an existing worktree). If
|
|
|
184
184
|
`env.config.json` is absent, isolation is off — leave the default `worktree` and
|
|
185
185
|
finish as above.
|
|
186
186
|
|
|
187
|
-
## Phase E — link to
|
|
188
|
-
|
|
189
|
-
**Only when
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
`00-overview.md` plan. Attach the `initiativeId` from `linear.config.json` when
|
|
199
|
-
one is set.
|
|
200
|
-
- **Create a Milestone per phase** (the `mapping.phases` target — milestones by
|
|
201
|
-
default), named from each phase file, in execution order.
|
|
202
|
-
- **Add the frontmatter block** to `00-overview.md` (above the `#` title) so the
|
|
203
|
-
spec is linkable:
|
|
204
|
-
|
|
205
|
-
```yaml
|
|
206
|
-
---
|
|
207
|
-
linear_project_id: "<uuid>"
|
|
208
|
-
linear_identifier: "<TEAM-123>"
|
|
209
|
-
linear_url: "https://linear.app/..."
|
|
210
|
-
spec_status: "backlog"
|
|
211
|
-
last_synced_at: "<ISO-8601 now>"
|
|
212
|
-
---
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
- **Write the initial base sidecar** so the spec starts clean and non-diverged —
|
|
216
|
-
run `skitterspec spec-sync normalize <spec>` to capture the local snapshot as the
|
|
217
|
-
committed base (`sync.baseDir`). `/spec-status` should report in-sync right after.
|
|
218
|
-
- **Echo the branch name** from `branch.pattern` so the user knows what `/spec-go`
|
|
219
|
-
will fork.
|
|
220
|
-
|
|
221
|
-
Leave committing to the existing convention (the user commits the spec as usual)
|
|
222
|
-
and **never auto-push git** — Linear's own automation reacts to real branch/PR
|
|
223
|
-
events later. Report the Linear project URL and the base as part of Phase C's
|
|
224
|
-
finish-up message.
|
|
187
|
+
## Phase E — link to a ticketing provider (only if one is installed)
|
|
188
|
+
|
|
189
|
+
**Only when a ticketing provider is installed and configured** (it ships the
|
|
190
|
+
`/spec-push` · `/spec-pull` · `/spec-status` skills and a provider config under
|
|
191
|
+
`specs/.core/`). If none is present, skip this phase entirely — the spec stays
|
|
192
|
+
local-only and `/spec` behaves exactly as above. When a provider is present, link
|
|
193
|
+
the spec to the tracker after writing it, so status and discussion live there
|
|
194
|
+
while the repo stays the co-authoring surface — follow the provider's link steps
|
|
195
|
+
below (nothing to do here without one).
|
|
196
|
+
|
|
197
|
+
|
|
@@ -51,10 +51,37 @@ specs — `git log`/the per-spec State log give the completion order.
|
|
|
51
51
|
Confirm the move, the final test result, and list anything deferred. Do **not**
|
|
52
52
|
`git commit` unless the user asks.
|
|
53
53
|
|
|
54
|
-
## 6.
|
|
54
|
+
## 6. Integrate onto the base branch (opt-in, only if isolated)
|
|
55
|
+
|
|
56
|
+
**Only when `specs/.core/env.config.json` exists and the spec is on a worktree**
|
|
57
|
+
(it was provisioned by `/spec-go`). Otherwise skip this entirely — a non-isolated
|
|
58
|
+
spec has nothing to land, and `/spec-complete` behaves exactly as before. When it
|
|
59
|
+
applies, offer to land the finished branch on the base branch so the work reaches
|
|
60
|
+
`main` (or your configured `baseBranch`) in one flow:
|
|
61
|
+
|
|
62
|
+
1. **Require a clean worktree.** The completion edits (status flip, the
|
|
63
|
+
`git mv` to `complete/`) must be committed first — integrate refuses a dirty
|
|
64
|
+
tree. If it's dirty, offer `/commit` and **stop**; don't auto-commit.
|
|
65
|
+
2. **Plan + execute.** Run `skitterspec spec-env integrate <name>` and run the
|
|
66
|
+
printed commands **in order**:
|
|
67
|
+
- `git -C <worktree> rebase <base>` — replay the branch onto base.
|
|
68
|
+
- `git -C <mainRepoPath> merge --ff-only <branch>` — fast-forward base.
|
|
69
|
+
On a **rebase conflict** (non-zero exit), run
|
|
70
|
+
`git -C <worktree> rebase --abort`, relay the conflict, and **stop** — leave it
|
|
71
|
+
to the user; do not offer teardown.
|
|
72
|
+
On a **no-op** ("already landed"), just say so and continue.
|
|
73
|
+
3. **Re-test on base.** Run the project's test command from the primary checkout;
|
|
74
|
+
it must be **green** before you call the landing done.
|
|
75
|
+
4. **Report** the landing (base branch, fast-forward result). It **never pushes** —
|
|
76
|
+
mention the user can `git push` the base branch themselves.
|
|
77
|
+
|
|
78
|
+
## 7. Offer teardown (opt-in, only if configured)
|
|
55
79
|
|
|
56
80
|
**Only when `specs/.core/env.config.json` exists**, offer — don't force — to
|
|
57
81
|
reclaim the finished spec's environment: "Want me to run `/spec-env-down <name>`
|
|
58
|
-
to remove its worktree, stack, volumes, and free its slot?"
|
|
59
|
-
|
|
60
|
-
`
|
|
82
|
+
to remove its worktree, delete its branch, stack, volumes, and free its slot?"
|
|
83
|
+
Post-integrate the branch is merged into base, so teardown needs **no `--force`**
|
|
84
|
+
and deletes the branch (`git branch -d`) as part of the plan. It still respects
|
|
85
|
+
the guards (won't destroy a dirty or unpushed-and-unmerged worktree without
|
|
86
|
+
`--force`). If `env.config.json` is absent, skip this entirely — behave exactly as
|
|
87
|
+
before.
|
|
@@ -47,6 +47,12 @@ Run the printed commands **in order**, exactly as printed:
|
|
|
47
47
|
namespaced stack up in the spec's reserved port block.
|
|
48
48
|
4. **Opener** — if an `open.command` line was printed, run it (e.g. opens the
|
|
49
49
|
worktree in your editor/terminal). Skipped silently when unset.
|
|
50
|
+
5. **Trust the worktree root for this session** — the engine already wrote the
|
|
51
|
+
printed `trusted:` root into `.claude/settings.local.json` (gitignored, so it
|
|
52
|
+
persists for future sessions). That file likely won't hot-reload mid-session,
|
|
53
|
+
so also run `/add-dir <trusted root>` now to lift `Edit`/`Write` prompts for
|
|
54
|
+
the **current** session. (If the engine printed a `trusted: ! …` warning,
|
|
55
|
+
`settings.local.json` isn't valid JSON — fix it, then re-run.)
|
|
50
56
|
|
|
51
57
|
## 4. Report
|
|
52
58
|
|
|
@@ -31,10 +31,13 @@ skitterspec spec-env down <spec> [--keep-volumes] [--force]
|
|
|
31
31
|
|
|
32
32
|
## 3. Handle a guard block
|
|
33
33
|
|
|
34
|
-
If the CLI reports **blocked** (the worktree has uncommitted changes or unpushed
|
|
35
|
-
commits
|
|
36
|
-
the user `--force` (and suggest
|
|
37
|
-
`--force` when the user explicitly
|
|
34
|
+
If the CLI reports **blocked** (the worktree has uncommitted changes, or unpushed
|
|
35
|
+
commits that aren't yet merged into the base branch), **relay the reason and
|
|
36
|
+
stop** — do not destroy unreviewed work. Offer the user `--force` (and suggest
|
|
37
|
+
committing/pushing first). Only re-run with `--force` when the user explicitly
|
|
38
|
+
asks. **A branch already merged into the base needs no `--force`** — the unpushed
|
|
39
|
+
guard treats "landed on base" as safe, so a completed spec (post-`/spec-complete`
|
|
40
|
+
integrate) tears down cleanly even with no remote.
|
|
38
41
|
|
|
39
42
|
## 4. Execute the printed side effects
|
|
40
43
|
|
|
@@ -45,12 +48,17 @@ When not blocked, run the printed commands **in order**, exactly as printed:
|
|
|
45
48
|
destroyed.
|
|
46
49
|
2. **`docker compose … down`** — with `--volumes` unless `--keep-volumes`.
|
|
47
50
|
3. **`git worktree remove …`** — removes the sibling worktree.
|
|
51
|
+
4. **`git branch -d <branch>`** — deletes the spec's branch (freed by the worktree
|
|
52
|
+
removal above). It's `-d` (merged-only), never `-D`: if it reports the branch
|
|
53
|
+
isn't fully merged, **relay that and stop** — don't `-D` it. That only happens
|
|
54
|
+
on a `--force` teardown of unmerged work; the user can delete it by hand if
|
|
55
|
+
they're sure.
|
|
48
56
|
|
|
49
57
|
The slot is already freed by the CLI.
|
|
50
58
|
|
|
51
59
|
## 5. Report
|
|
52
60
|
|
|
53
|
-
Confirm what happened: worktree removed, containers down, volumes
|
|
54
|
-
**dropped|kept**, slot freed, and the backup path (if any). If
|
|
55
|
-
|
|
56
|
-
that; it's not an error.
|
|
61
|
+
Confirm what happened: worktree removed, branch deleted, containers down, volumes
|
|
62
|
+
**dropped|kept**, slot freed, and the backup path (if any). If a `git branch -d`
|
|
63
|
+
was refused (unmerged), say so. If the spec wasn't provisioned / was already torn
|
|
64
|
+
down, the CLI reports a clean **no-op** — relay that; it's not an error.
|
|
@@ -26,6 +26,11 @@ housekeeping below lands on the spec's branch and never on `main`:
|
|
|
26
26
|
worktree on a branch forked from `main`, and — only when the spec's
|
|
27
27
|
`> **Stack:**` header is `worktree + docker` — also brings up its Docker stack.
|
|
28
28
|
Print the worktree path and the opener command it emits.
|
|
29
|
+
- **Trust the worktree for this session.** The engine wrote the printed
|
|
30
|
+
`trusted:` root into `.claude/settings.local.json` (gitignored) so future
|
|
31
|
+
sessions trust it automatically — but that file likely won't hot-reload now,
|
|
32
|
+
so run `/add-dir <trusted root>` before editing into the worktree, or the
|
|
33
|
+
first edits will prompt.
|
|
29
34
|
- **Do the rest in the worktree**, on the branch: open it (the printed opener, or
|
|
30
35
|
a fresh Claude session rooted there) or, staying in this session, act on the
|
|
31
36
|
worktree path with absolute paths / `git -C <worktreePath>`. The spec move,
|
|
@@ -45,7 +50,8 @@ Then move the spec (in the worktree when isolated, in place otherwise):
|
|
|
45
50
|
- Append a **State log** row:
|
|
46
51
|
`| <YYYY-MM-DD> | In Progress | in-progress | <git user.name> |`.
|
|
47
52
|
- **When isolated:** commit the move and **push the branch** now — that records
|
|
48
|
-
the in-progress state for everyone and fires
|
|
53
|
+
the in-progress state for everyone and fires the tracker's automation (when a
|
|
54
|
+
ticketing provider is linked).
|
|
49
55
|
|
|
50
56
|
A spec ideally reaches here as `Ready` (via `/spec-ready`), but `/spec-go` works
|
|
51
57
|
on a `Draft` too — just sanity-check it's well-formed before building.
|
|
@@ -71,22 +77,14 @@ Before writing any code for this phase, get the workspace and context clean:
|
|
|
71
77
|
the spec file on disk is the source of truth, so nothing is lost. Pause for the
|
|
72
78
|
`/compact`, then implement the phase.
|
|
73
79
|
|
|
74
|
-
## 3b.
|
|
75
|
-
|
|
76
|
-
**Only when `
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
current shared state, not a stale snapshot. On a conflict it refuses — relay
|
|
83
|
-
that and let the user resolve before continuing; do not `--force` for them.
|
|
84
|
-
- **Commit the refreshed snapshot** into the feature branch (a small
|
|
85
|
-
`chore(spec): pull latest from Linear`-style commit) so the frozen spec rides
|
|
86
|
-
in the PR alongside the code it describes.
|
|
87
|
-
- Linear's GitHub branch/PR automation may now drive status transitions off the
|
|
88
|
-
branch and PR you pushed in step 2 — expect state to move on the Linear side;
|
|
89
|
-
keep any manual status edits minimal to avoid fighting it.
|
|
80
|
+
## 3b. Pull from the tracker first (only if a provider is installed)
|
|
81
|
+
|
|
82
|
+
**Only when a ticketing provider with a `/spec-pull` skill is installed** and the
|
|
83
|
+
spec is linked to the tracker. Otherwise skip this step — no provider means zero
|
|
84
|
+
change to the flow below. Follow the provider's pull steps below (nothing to do
|
|
85
|
+
here without one).
|
|
86
|
+
|
|
87
|
+
|
|
90
88
|
|
|
91
89
|
## 4. Implement the phase
|
|
92
90
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skitterbyte/skitterspec",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Spec-driven
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Spec-driven development for Claude Code — a tracker-free filesystem workflow: lifecycle skills and per-spec isolation. For Linear sync, install @skitterbyte/skitterspec-linear instead.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
7
7
|
"claude-code",
|
|
@@ -27,16 +27,11 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"prompts": "^2.4.2"
|
|
29
29
|
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"prepack": "node ../../scripts/build-dist.js skitterspec"
|
|
32
|
+
},
|
|
30
33
|
"repository": {
|
|
31
34
|
"type": "git",
|
|
32
35
|
"url": "git+https://github.com/skitterbyte/skitterspec.git"
|
|
33
|
-
},
|
|
34
|
-
"scripts": {
|
|
35
|
-
"test": "node --test",
|
|
36
|
-
"changelog": "node scripts/generate-changelog.js",
|
|
37
|
-
"changelog:retro": "node scripts/generate-changelog.js --retro",
|
|
38
|
-
"releases": "node scripts/generate-releases.js",
|
|
39
|
-
"releases:retro": "node scripts/generate-releases.js --retro",
|
|
40
|
-
"version": "node scripts/generate-changelog.js && node scripts/generate-releases.js && git add CHANGELOG.md RELEASES.md"
|
|
41
36
|
}
|
|
42
|
-
}
|
|
37
|
+
}
|