@skitterbyte/skitterspec 1.0.1 → 2.0.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.
Files changed (42) hide show
  1. package/README.md +27 -244
  2. package/assets/claude-md-section.md +0 -6
  3. package/assets/core/env.config.json.example +5 -1
  4. package/assets/core/env.config.md +21 -5
  5. package/assets/rules/spec-planning.md +14 -10
  6. package/assets/skills/spec/SKILL.md +11 -38
  7. package/assets/skills/spec-complete/SKILL.md +31 -4
  8. package/assets/skills/spec-env/SKILL.md +6 -0
  9. package/assets/skills/spec-env-down/SKILL.md +16 -8
  10. package/assets/skills/spec-go/SKILL.md +15 -17
  11. package/package.json +6 -11
  12. package/src/cli.js +186 -318
  13. package/src/deprecate.js +138 -0
  14. package/src/env/config.js +17 -4
  15. package/src/env/integrate.js +46 -0
  16. package/src/env/resolve.js +70 -52
  17. package/src/env/teardown.js +19 -4
  18. package/src/env/trust.js +87 -0
  19. package/src/init.js +78 -170
  20. package/src/prompts.js +26 -63
  21. package/LICENSE +0 -21
  22. package/assets/core/linear.config.json.example +0 -39
  23. package/assets/core/linear.config.md +0 -121
  24. package/assets/rules/commit-messages.md +0 -85
  25. package/assets/scripts/generate-changelog.js +0 -274
  26. package/assets/scripts/generate-releases.js +0 -360
  27. package/assets/scripts/lib/config.js +0 -127
  28. package/assets/scripts/lib/git-commits.js +0 -265
  29. package/assets/skills/commit/SKILL.md +0 -28
  30. package/assets/skills/spec-pull/SKILL.md +0 -46
  31. package/assets/skills/spec-push/SKILL.md +0 -53
  32. package/assets/skills/spec-status/SKILL.md +0 -46
  33. package/src/config.js +0 -13
  34. package/src/sync/apply.js +0 -66
  35. package/src/sync/base.js +0 -83
  36. package/src/sync/compare.js +0 -99
  37. package/src/sync/config.js +0 -198
  38. package/src/sync/mcp.js +0 -112
  39. package/src/sync/normalize.js +0 -249
  40. package/src/sync/pull.js +0 -84
  41. package/src/sync/push.js +0 -106
  42. package/src/sync/write.js +0 -86
package/README.md CHANGED
@@ -1,259 +1,42 @@
1
1
  # @skitterbyte/skitterspec
2
2
 
3
- Spec-driven-development (SDD) workflow for [Claude Code](https://claude.com/claude-code),
4
- packaged so you can drop the same spec lifecycle into any project.
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
- It installs the **eight spec-lifecycle skills** plus a general **`/commit`**
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
- On a terminal it runs an **interactive setup** (skip it with `--yes` or drive it
32
- with the flags below). It's idempotent it creates only what's missing and
33
- never clobbers customised files. It writes:
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
- `/spec-go` on a linked spec runs `/spec-pull` first, so you always build against
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
- This supersedes the earlier one-way `/spec-from-issue` intake design (cancelled):
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
- ## After install tailor it
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
- The shipped skills are **stack-agnostic**. They say things like "run the
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
- ## How it's distributed
28
+ ## v2 Linear removed from the base
243
29
 
244
- The skills, rule, and generator scripts are plain assets under
245
- [`assets/`](./assets). The CLI ([`bin/skitterspec.js`](./bin/skitterspec.js)
246
- [`src/`](./src)) copies them into place, patches `CLAUDE.md`, and (for the release
247
- tooling) writes `skitterspec.config.json` and npm scripts. It needs Node 18+ and
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
- Because the files are copied into the consumer repo (not symlinked), each project
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
- ## License
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
- "linkLinear": true,
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
- // When true and specs/.core/linear.config.json is present, derive branch
66
- // names from Linear's branch.pattern so pushing fires Linear's GitHub
67
- // automation. Otherwise branches fall back to {type}/{slug}.
68
- "linkLinear": true,
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
- **Linear hybrid sync (opt-in).** When `specs/.core/linear.config.json` is present,
38
- three git-like skills sync a spec with its linked Linear project: `/spec-status`
39
- (read-only per-field divergence), `/spec-pull` (Linear→repo), and `/spec-push`
40
- (repo→Linear) three-way merged against a committed base sidecar, with
41
- field-ownership collapsing conflicts and a `--force` + backup escape hatch. Linked
42
- `/spec` also creates the Linear Project + a Milestone per phase, and `/spec-go`
43
- pulls first. All of it is inert without the config every skill then behaves as
44
- it does today. Full model + field reference: `specs/.core/linear.config.md`.
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 Linear when it's linked. (There are no `00-index.md`
139
- summary files — the folder tree, headers, and State logs are queried directly.)
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 Linear (only if configured)
188
-
189
- **Only when `specs/.core/linear.config.json` exists** (Linear sync is opted in).
190
- If it's absent, skip this phase entirely the spec stays local-only and `/spec`
191
- behaves exactly as above. When present, after writing the spec, link it to Linear
192
- so status and discussion live there while the repo stays the co-authoring surface:
193
-
194
- - **Discover the Linear MCP tools at runtime** (don't hardcode names). If Linear
195
- isn't connected/authed, relay the fix and stop — leave the spec written and
196
- local; the user can link it later with `/spec-push`. Do nothing destructive.
197
- - **Create the Project** from the spec: name from the title, description from the
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. Offer teardown (opt-in, only if configured)
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?" It respects the
59
- teardown guards (won't destroy a dirty/unpushed worktree without `--force`). If
60
- `env.config.json` is absent, skip this entirely behave exactly as before.
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), **relay the reason and stop** do not destroy unreviewed work. Offer
36
- the user `--force` (and suggest committing/pushing first). Only re-run with
37
- `--force` when the user explicitly asks.
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 the spec wasn't
55
- provisioned / was already torn down, the CLI reports a clean **no-op** relay
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 Linear's automation (when linked).
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. Sync from Linear first (opt-in)
75
-
76
- **Only when `specs/.core/linear.config.json` exists** and the spec carries a
77
- `linear_project_id` in its `00-overview.md` frontmatter. Otherwise skip this
78
- step no config means zero change to the flow below.
79
-
80
- - **Run `/spec-pull` first.** Bring down anything Linear changed since the last
81
- sync (status, priority, discussion-driven fields) so you build against the
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": "1.0.1",
4
- "description": "Spec-driven-development workflow for Claude Code — installs the spec lifecycle skills, rule, and specs/ folders into any project.",
3
+ "version": "2.0.1",
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
+ }