@skitterbyte/skitterspec-linear 10.8.0 → 12.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 +88 -0
- package/README.md +3 -3
- package/assets/claude-md-section.md +20 -48
- package/assets/core/SETUP.md +1 -1
- package/assets/core/env.config.json.example +7 -1
- package/assets/core/env.config.md +56 -11
- package/assets/core/gating.config.json.example +4 -0
- package/assets/core/gating.config.md +81 -0
- package/assets/core/linear.config.md +12 -11
- package/assets/rules/spec-planning.md +53 -13
- package/assets/skills/spec/SKILL.md +46 -18
- package/assets/skills/spec-bug/SKILL.md +32 -27
- package/assets/skills/spec-cancel/SKILL.md +26 -0
- package/assets/skills/spec-complete/SKILL.md +70 -17
- package/assets/skills/spec-hotfix/SKILL.md +40 -19
- package/assets/skills/spec-init/SKILL.md +31 -8
- package/assets/skills/spec-linear-setup/SKILL.md +32 -7
- package/assets/skills/spec-next/SKILL.md +141 -0
- package/assets/skills/spec-push/SKILL.md +15 -16
- package/assets/skills/spec-review/SKILL.md +24 -11
- package/assets/skills/spec-start/SKILL.md +226 -0
- package/assets/skills/spec-status/SKILL.md +2 -2
- package/assets/skills/spec-sync/SKILL.md +8 -8
- package/assets/skills/spec-to-main/SKILL.md +21 -19
- package/package.json +1 -1
- package/src/cli.js +405 -15
- package/src/env/classify.js +91 -0
- package/src/env/config.js +39 -1
- package/src/env/integrate.js +61 -1
- package/src/env/live.js +27 -3
- package/src/env/provision.js +196 -5
- package/src/env/resolve.js +1 -0
- package/src/env/teardown.js +41 -3
- package/src/gating.js +155 -0
- package/src/init.js +45 -9
- package/src/prompts.js +41 -4
- package/src/vendor/linear/cli-sync.js +22 -2
- package/src/vendor/linear/config.js +1 -1
- package/src/vendor/sync-core/src/compare.js +25 -2
- package/assets/skills/spec-go/SKILL.md +0 -233
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: spec-linear-setup
|
|
3
|
-
description: Configure Linear sync for this repo by interview rather than by hand
|
|
3
|
+
description: Configure Linear sync for this repo by interview rather than by hand — discovers the workspace, asks how the work is organised, then validates the answers and writes specs/.core/linear.config.json. Safe to re-run; an existing config is reviewed, never overwritten without consent. Use when the user says "/spec-linear-setup", "set up Linear sync", "connect this repo to Linear", "configure linear.config.json", or "check my Linear setup".
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# /spec-linear-setup — configure Linear sync by interview
|
|
@@ -198,6 +198,31 @@ project's own, so ask rather than guess.
|
|
|
198
198
|
the workspace — that report is the evidence the setup is right, so don't
|
|
199
199
|
paraphrase it into "done".
|
|
200
200
|
|
|
201
|
+
## 8b. Let `/spec-start` commit the snapshot with its spec
|
|
202
|
+
|
|
203
|
+
**Only when `specs/.core/env.config.json` exists** (per-spec isolation is on). If
|
|
204
|
+
it is absent, skip this — there is nothing to configure.
|
|
205
|
+
|
|
206
|
+
`/spec-start` commits an uncommitted spec for you, but only the paths it can
|
|
207
|
+
prove belong to that spec. Its own folder always does; this repo's per-spec
|
|
208
|
+
Linear snapshot (`sync.baseDir`, `specs/.core/linear-base/<ID>.base.json`) does
|
|
209
|
+
not, because the base engine is deliberately tracker-free and cannot know that
|
|
210
|
+
path exists. Declare it once, in `env.config.json`:
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
"spec": {
|
|
214
|
+
"companionPaths": ["specs/.core/linear-base/{identifier}.base.json"]
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`{identifier}` resolves through `branch.identifierField`, so **set that too** if
|
|
219
|
+
it is still empty — `"linear_identifier"` is the field `/spec` stamps. Without
|
|
220
|
+
it the pattern expands to nothing and the snapshot is treated as another spec's
|
|
221
|
+
file: `/spec-start` then refuses the tree instead of committing it, which is the
|
|
222
|
+
safe failure but a needless one.
|
|
223
|
+
|
|
224
|
+
Leave both alone if the user has already set them.
|
|
225
|
+
|
|
201
226
|
## 9. Report and hand off
|
|
202
227
|
|
|
203
228
|
**Finish by checking, not by describing.** First write down what the MCP server
|
|
@@ -254,12 +279,12 @@ more detail if you need it. If the key is missing, tell the user to run this
|
|
|
254
279
|
skitterspec spec-sync credentials set
|
|
255
280
|
```
|
|
256
281
|
|
|
257
|
-
**Do not ask the user to paste an API key into this conversation, and do not
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
282
|
+
**Do not ask the user to paste an API key into this conversation, and do not offer to store one for them.**
|
|
283
|
+
A key pasted into a chat enters the transcript, is sent to the model, and may
|
|
284
|
+
be logged — moving where a key is stored is pointless if it travels through the
|
|
285
|
+
conversation to get there. Your job is to confirm the setup is ready and name
|
|
286
|
+
the command; the value is theirs alone. The key is optional: without one,
|
|
287
|
+
pushes go over MCP.
|
|
263
288
|
|
|
264
289
|
Mention what setup did **not** configure, so the defaults aren't mistaken for
|
|
265
290
|
decisions: phase mapping (`mapping.phases` — sub-issue per phase by default) and
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spec-next
|
|
3
|
+
description: Build the next unfinished phase of the spec in flight for this session — pre-flight, implement with tests, record progress and refresh the tracker. Refuses when no spec is in flight rather than guessing one, and never builds a spec it is not standing in. Use when the user says "/spec-next", "build the next phase", "continue the spec", or "carry on with this spec".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /spec-next — build the next phase of the spec in flight
|
|
7
|
+
|
|
8
|
+
It assumes the workbench is already set up: a spec is **in flight** on this
|
|
9
|
+
checkout, and this skill implements its next unfinished phase. Putting a spec in
|
|
10
|
+
flight — provisioning, moving it to `in-progress`, getting its branch here — is
|
|
11
|
+
`/spec-start`'s job. Re-run this per phase until the spec is done, then
|
|
12
|
+
`/spec-complete`.
|
|
13
|
+
|
|
14
|
+
## 1. Identify the spec in flight
|
|
15
|
+
|
|
16
|
+
Resolve **in this order**, and stop at the first that answers:
|
|
17
|
+
|
|
18
|
+
1. **The live spec of this checkout** — run
|
|
19
|
+
`skitterspec spec-env live status` and read its `live:` line. `live: yes`
|
|
20
|
+
names the spec whose branch is checked out here; that is the one to build.
|
|
21
|
+
2. **The worktree you are standing in** — if this session's cwd is inside a
|
|
22
|
+
spec's worktree, that spec is in flight *for this session*. This is the
|
|
23
|
+
manual-parallel path: several specs may be provisioned, and a terminal tab
|
|
24
|
+
opened in one is its own workbench.
|
|
25
|
+
3. **The current branch, in `checkout` mode** — no worktrees exist, so the
|
|
26
|
+
branch the checkout is on names the spec.
|
|
27
|
+
|
|
28
|
+
**If none answers, refuse and stop:**
|
|
29
|
+
`no spec in flight — run /spec-start <name> to start one`.
|
|
30
|
+
|
|
31
|
+
**Never fall back to the spec "in context".** A spec discussed in conversation
|
|
32
|
+
is not a spec in flight, and this skill writes real code: building the wrong
|
|
33
|
+
spec's phase produces commits on a branch nobody asked for. The refusal is
|
|
34
|
+
cheap; the mistake is not.
|
|
35
|
+
|
|
36
|
+
A **name argument** is accepted, but it must *match* the spec in flight — it
|
|
37
|
+
narrows a re-run, it does not select a different spec. A mismatch refuses,
|
|
38
|
+
naming both.
|
|
39
|
+
|
|
40
|
+
## 2. Pre-flight — commit prior work
|
|
41
|
+
|
|
42
|
+
Before writing any code for this phase, get the workspace clean:
|
|
43
|
+
|
|
44
|
+
- **Confirm the last-worked phase is committed.** Run `git status` and
|
|
45
|
+
`git log --oneline -5`. The most recently *implemented* phase (not necessarily
|
|
46
|
+
the numerically previous one) should already be committed. If prior-phase work
|
|
47
|
+
is still uncommitted, **stop and suggest committing it first** (e.g. via
|
|
48
|
+
`/commit`) so each phase lands as its own reviewable commit — don't build the
|
|
49
|
+
next phase on top of an uncommitted one. (Skip if this is the first phase —
|
|
50
|
+
there's nothing prior to commit.)
|
|
51
|
+
|
|
52
|
+
## 3. Implement the phase
|
|
53
|
+
|
|
54
|
+
Identify the **first unfinished phase** from the `00-overview.md` phase index,
|
|
55
|
+
then open its phase file (`0N-<slug>.md`) — that file holds the tasks. Mark it
|
|
56
|
+
started: set the phase-file heading to `🔄` and its `> **Status:**` to
|
|
57
|
+
`In progress`, and flip the matching row in the overview phase index to `🔄`.
|
|
58
|
+
|
|
59
|
+
**Then sync with the tracker (only if a provider is installed).** The phase has
|
|
60
|
+
just changed state, so refresh the mirror before the build starts — that is what
|
|
61
|
+
makes the phase show as in progress *while* it is being built rather than only
|
|
62
|
+
once it is over. Without a provider this is a no-op and nothing below changes.
|
|
63
|
+
|
|
64
|
+
**Only when `specs/.core/linear.config.json` exists** and the spec carries a
|
|
65
|
+
`linear_identifier`. Otherwise skip this step — no config means zero change.
|
|
66
|
+
|
|
67
|
+
- **No pull.** Linear is a generated mirror in one-way sync, so there is nothing
|
|
68
|
+
to bring down before building — the repo is already the source of truth. (A
|
|
69
|
+
workflow-state a teammate moved in Linear is advisory only; `/spec-status`
|
|
70
|
+
surfaces it. It is overwritten on the next push.)
|
|
71
|
+
- **Refresh the mirror now, without asking.** Run `/spec-push`. The spec has just
|
|
72
|
+
moved to `in-progress` and its phase to `🔄` — both real state changes, and the
|
|
73
|
+
tracker is a generated mirror of them. This holds under **both**
|
|
74
|
+
`mapping.phases` modes, for different reasons:
|
|
75
|
+
- `"subissue"` (the default) — the phase sub-issues already exist, and this
|
|
76
|
+
push is what moves the current one into its in-progress state. Skip it and
|
|
77
|
+
every sub-issue sits in Backlog until the spec completes.
|
|
78
|
+
- `"deferred"` — the sub-issues do not exist yet, and this push is what mints
|
|
79
|
+
them. Skip it and a started spec stays mirrored as a phase-less issue.
|
|
80
|
+
- **Never mint the spec issue.** An unlinked spec is skipped, not created —
|
|
81
|
+
`/spec-push` is how someone opts in.
|
|
82
|
+
- **Never fatal.** If the push fails — offline, no key, a Linear error — say so
|
|
83
|
+
and **carry on with the build**. The repo is correct regardless; the mirror is
|
|
84
|
+
disposable and the next push repairs it.
|
|
85
|
+
- **Expect a dirty `specs/.core/` afterwards.** The push writes a base snapshot
|
|
86
|
+
and stamps any new ids, and `/spec-next` does not commit. The next `/commit`
|
|
87
|
+
sweeps it up with the phase's own work.
|
|
88
|
+
- Linear's GitHub branch/PR automation may drive status transitions off the
|
|
89
|
+
branch/PR you pushed in step 2; that's expected and the repo still wins on the
|
|
90
|
+
next `/spec-push`.
|
|
91
|
+
|
|
92
|
+
Then build it, following the project rules in `.claude/rules/*.md` and `CLAUDE.md`:
|
|
93
|
+
|
|
94
|
+
- Work task by task through the phase file. Make focused edits that match
|
|
95
|
+
surrounding code.
|
|
96
|
+
- Honour the project's conventions (see `.claude/rules/spec-planning.md` and the
|
|
97
|
+
rules it links).
|
|
98
|
+
- **Tests are part of the phase, not after it.** Create/extend tests for the
|
|
99
|
+
work, then run the project's typecheck and test commands. Do not declare the
|
|
100
|
+
phase done until green.
|
|
101
|
+
- Never hardcode dates in tests; never run destructive commands against a real
|
|
102
|
+
database — use the project's test database only.
|
|
103
|
+
|
|
104
|
+
## 4. Record progress
|
|
105
|
+
|
|
106
|
+
- In the **phase file**: tick completed tasks (`- [x]`), flip its heading to `✅`,
|
|
107
|
+
and set its `> **Status:**` to `Done`.
|
|
108
|
+
- In **`00-overview.md`**: flip the matching phase-index row to `✅`.
|
|
109
|
+
- If anything changed from the plan (a decision, a deviation, a discovered
|
|
110
|
+
constraint), add a dated **Changelog** entry in `00-overview.md`.
|
|
111
|
+
- If new work surfaced, add it as tasks to the appropriate phase file (or add a
|
|
112
|
+
new phase file + index row) rather than doing it silently.
|
|
113
|
+
|
|
114
|
+
**Then refresh the mirror (only if a provider is installed).** The phase is done
|
|
115
|
+
in the repo now; leaving the tracker to catch up at `/spec-complete` is what makes
|
|
116
|
+
a mirror lag a whole spec behind. Without a provider this is a no-op.
|
|
117
|
+
|
|
118
|
+
**Only when `specs/.core/linear.config.json` exists** and the spec carries a
|
|
119
|
+
`linear_identifier`. Either missing → **skip**, in one line
|
|
120
|
+
(`not linked to Linear — /spec-push to mirror it`), and carry on.
|
|
121
|
+
|
|
122
|
+
**Refresh the mirror now, without asking.** Run `/spec-push`. The repo has just
|
|
123
|
+
become the truth about this phase's progress, and progress is what the mirror
|
|
124
|
+
exists to show. Deferring it to `/spec-complete` is what makes every phase
|
|
125
|
+
sub-issue jump from Backlog straight to Done, with nothing visible in between.
|
|
126
|
+
|
|
127
|
+
- **Never mint.** An unlinked spec is skipped, not created.
|
|
128
|
+
- **Never fatal.** If the push fails — offline, no key, a Linear error — say so
|
|
129
|
+
and **finish the operation anyway**. The phase is done in the repo regardless;
|
|
130
|
+
the mirror is disposable and the next push repairs it.
|
|
131
|
+
- **Expect a dirty `specs/.core/` afterwards.** The push writes a base snapshot
|
|
132
|
+
and stamps any new ids, and these skills do not commit. The next `/commit`
|
|
133
|
+
sweeps it up with the phase's own work.
|
|
134
|
+
- **Say what happened** in the skill's report: mirror updated, skipped as
|
|
135
|
+
unlinked, or failed with the reason.
|
|
136
|
+
|
|
137
|
+
## 5. Report
|
|
138
|
+
|
|
139
|
+
Summarise what was implemented, the test result (quote failures if any), and
|
|
140
|
+
which phase is next. Do **not** `git commit` unless the user asks — finish,
|
|
141
|
+
verify, and wait.
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: spec-push
|
|
3
|
-
description: Push a spec up to its linked Linear issue (repo → Linear, one-way). The repo is the source of truth
|
|
3
|
+
description: Push a spec up to its linked Linear issue (repo → Linear, one-way). The repo is the source of truth and Linear a generated mirror, so content is never read back or merged. A spec is an issue and each phase a sub-issue. Opt-in — needs specs/.core/linear.config.json. Use when the user says "/spec-push", "push to Linear", "update the Linear issue from this spec".
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# /spec-push — send a spec up to Linear (one-way)
|
|
7
7
|
|
|
8
|
-
Repo → Linear. The repo is the **source of truth**; Linear is a
|
|
9
|
-
mirror**. A spec is a Linear **issue**; each phase is a
|
|
10
|
-
issue), and the phase file is mirrored into that
|
|
11
|
-
written — its prose, its sections, and its tasks as
|
|
12
|
-
as issues of their own. Only the phase's h1 and
|
|
13
|
-
out, because both are pushed as fields of their
|
|
14
|
-
state). This skill
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
overwritten on the next push.
|
|
8
|
+
Repo → Linear. The repo is the **source of truth**; Linear is a
|
|
9
|
+
**generated mirror**. A spec is a Linear **issue**; each phase is a
|
|
10
|
+
**sub-issue** (a child issue), and the phase file is mirrored into that
|
|
11
|
+
sub-issue's description as written — its prose, its sections, and its tasks as
|
|
12
|
+
a read-only checklist, never as issues of their own. Only the phase's h1 and
|
|
13
|
+
`> **Status:**` line are left out, because both are pushed as fields of their
|
|
14
|
+
own (the sub-issue's title and state). This skill computes what changed since
|
|
15
|
+
the last push and applies it — it never reads Linear content back or merges. A
|
|
16
|
+
person editing the mirror in Linear will see it overwritten on the next push.
|
|
18
17
|
|
|
19
18
|
**Opt-in**: only runs when `specs/.core/linear.config.json` exists. If absent,
|
|
20
19
|
tell the user how to enable Linear sync and stop.
|
|
@@ -90,7 +89,7 @@ issue-state NAME via `config.states` at apply time.
|
|
|
90
89
|
|
|
91
90
|
A **`phasesDeferred`** field means `mapping.phases` is `"deferred"` and this spec
|
|
92
91
|
has not started, so its phases are deliberately absent from the plan — the issue
|
|
93
|
-
pushes alone and the sub-issues are minted by the push that follows `/spec-
|
|
92
|
+
pushes alone and the sub-issues are minted by the push that follows `/spec-start`.
|
|
94
93
|
Relay the count; it is not a sign the phase files failed to parse. Nothing else
|
|
95
94
|
about applying the plan changes.
|
|
96
95
|
|
|
@@ -190,10 +189,10 @@ skitterspec spec-sync verify <spec> --stored <file>
|
|
|
190
189
|
|
|
191
190
|
It compares against what the engine sent, ignoring the reformatting Linear
|
|
192
191
|
legitimately applies (renumbered ordered lists, `-`→`*`, collapsed table
|
|
193
|
-
separators, checkbox case, whitespace) and reporting only lost or altered
|
|
194
|
-
characters**. Relay any divergence — it prints both sides around the
|
|
195
|
-
difference. It exits 0 either way: the repo is unaffected and still
|
|
196
|
-
this is a warning, not a failure.
|
|
192
|
+
separators, checkbox case, whitespace) and reporting only lost or altered
|
|
193
|
+
**word characters**. Relay any divergence — it prints both sides around the
|
|
194
|
+
first difference. It exits 0 either way: the repo is unaffected and still
|
|
195
|
+
correct, so this is a warning, not a failure.
|
|
197
196
|
|
|
198
197
|
This is **not a pull**. Nothing read here is merged, stamped, or written
|
|
199
198
|
anywhere; the repo remains the only source of truth. Do it before step 5 so a
|
|
@@ -8,7 +8,7 @@ description: Re-validate an existing spec against the current codebase — detec
|
|
|
8
8
|
Specs rot: the code moves on while a spec sits in the backlog or pauses
|
|
9
9
|
mid-build. This skill re-validates a spec against the **current** code and
|
|
10
10
|
rewrites the stale parts so it's safe to act on. It plans only — it does not
|
|
11
|
-
implement anything (that's `/spec-
|
|
11
|
+
implement anything (that's `/spec-next`).
|
|
12
12
|
|
|
13
13
|
## 1. Identify the target spec
|
|
14
14
|
|
|
@@ -44,22 +44,35 @@ trust the spec's own wording — verify:
|
|
|
44
44
|
- **Backward compatibility.** Re-confirm the planned API/schema changes are still
|
|
45
45
|
additive/safe given the current code (see the project's compatibility rules).
|
|
46
46
|
|
|
47
|
+
### Release gating (only when configured)
|
|
48
|
+
|
|
49
|
+
**Only when `specs/.core/gating.config.json` exists.** Run
|
|
50
|
+
`skitterspec gating check <spec>`. A spec it names has no recorded gating
|
|
51
|
+
decision — or a bare `none` that records nothing — and that is
|
|
52
|
+
**drift like any other**: the question was never asked, or its answer was lost. Grill for it in
|
|
53
|
+
step 3 and write the header in step 4, exactly as you would a stale file path.
|
|
54
|
+
|
|
55
|
+
**This check is advisory.** It reports; it never refuses, and nothing below is
|
|
56
|
+
conditional on it. A spec written before the project adopted gating has no header
|
|
57
|
+
and is not broken — turning this into a gate would accuse the very specs the
|
|
58
|
+
feature was designed not to disturb.
|
|
59
|
+
|
|
47
60
|
## 3. Grill only where a decision is needed
|
|
48
61
|
|
|
49
62
|
Where the drift forces a choice (the old approach no longer fits, a referenced
|
|
50
63
|
thing is gone, scope is now ambiguous), grill the user like `/spec` Phase A —
|
|
51
|
-
**
|
|
52
|
-
the drift. Don't re-litigate decisions that still hold. If you can
|
|
53
|
-
reading the code, do that instead of asking.
|
|
64
|
+
**batching independent questions, each with a recommended answer** — but scoped
|
|
65
|
+
tightly to the drift. Don't re-litigate decisions that still hold. If you can
|
|
66
|
+
resolve it by reading the code, do that instead of asking.
|
|
54
67
|
|
|
55
68
|
## 4. Update the spec
|
|
56
69
|
|
|
57
|
-
- Rewrite stale **Decisions** / **Solution overview** and refresh the
|
|
58
|
-
map** in `00-overview.md`, and stale **tasks** in the phase files,
|
|
59
|
-
match the current code and the resolved questions. Add/remove tasks
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
completed `[x]` history**.
|
|
70
|
+
- Rewrite stale **Decisions** / **Solution overview** and refresh the
|
|
71
|
+
**Impact map** in `00-overview.md`, and stale **tasks** in the phase files,
|
|
72
|
+
so they match the current code and the resolved questions. Add/remove tasks
|
|
73
|
+
within a phase file; add a new phase by creating a `0N-<slug>.md` file
|
|
74
|
+
**and** a matching overview index row, or drop a dead phase by removing both.
|
|
75
|
+
Keep the index and files in sync; **preserve completed `[x]` history**.
|
|
63
76
|
- **A phase's status is the `⬜`/`🔄`/`✅` on its H1** — `# Phase 2 — <goal> 🔄`.
|
|
64
77
|
It is mirrored in that file's `> **Status:**` line and in the matching
|
|
65
78
|
`00-overview.md` phase-index row, but the **heading is the authoritative one**:
|
|
@@ -119,5 +132,5 @@ In `/spec-complete` and `/spec-cancel` this step is deliberately pinned
|
|
|
119
132
|
## 5. Report
|
|
120
133
|
|
|
121
134
|
Summarise the drift found, what you changed, any questions still open, and
|
|
122
|
-
whether the spec is now safe to `/spec-
|
|
135
|
+
whether the spec is now safe to `/spec-start` (or should drop back to `Draft` until
|
|
123
136
|
the open questions are resolved). Do **not** `git commit` unless the user asks.
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spec-start
|
|
3
|
+
description: Put a spec in flight — provision its branch, move it to in-progress, refresh the tracker, then build phase 1. Commits the spec itself when that is all that is uncommitted, and refuses to touch anyone else's unfinished work. Use when the user says "/spec-start", "start this spec", or "begin implementing <spec>".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /spec-start — put a spec in flight
|
|
7
|
+
|
|
8
|
+
One checkout, one spec in flight. This skill is how a spec gets there:
|
|
9
|
+
provision, move it to `in-progress`, refresh the tracker, then hand straight on
|
|
10
|
+
to `/spec-next` for phase 1. Continuing a spec afterwards is `/spec-next`;
|
|
11
|
+
finishing it is `/spec-complete`.
|
|
12
|
+
|
|
13
|
+
## 1. The gate — refuse unless the workbench is free
|
|
14
|
+
|
|
15
|
+
**Check this first, before resolving anything or touching a file.** What the
|
|
16
|
+
gate demands depends on the mode, because the two modes hold work in different
|
|
17
|
+
places — read `mode` from `specs/.core/env.config.json` (default `worktree`).
|
|
18
|
+
|
|
19
|
+
**`worktree` mode — the tree must be clean, and that is all.** The spec is built
|
|
20
|
+
in its own worktree, so another spec being in flight is not a conflict; it is the
|
|
21
|
+
parallelism the mode exists for. The only requirement is that this checkout has
|
|
22
|
+
no uncommitted work — *except* the spec you are starting, which `spec-env up`
|
|
23
|
+
commits for you (see below). Nothing is switched here and nothing is parked.
|
|
24
|
+
|
|
25
|
+
**`checkout` mode — the workbench must be free**: on the base branch (`main`, or
|
|
26
|
+
the configured `baseBranch`) and clean, since the branch is built right here and
|
|
27
|
+
this mode holds one spec at a time. If it isn't, relay what is in flight and
|
|
28
|
+
stop — name the spec holding the checkout and the two ways out, then end your
|
|
29
|
+
turn:
|
|
30
|
+
|
|
31
|
+
- **`/spec-complete`** — it's finished; land it and free the workbench.
|
|
32
|
+
- **`/spec-cancel`** — it isn't wanted; record why and free the workbench.
|
|
33
|
+
|
|
34
|
+
**Never get past the gate yourself.** Do not stash, do not commit
|
|
35
|
+
**another spec's** work, do not switch branches for them. An uncommitted tree
|
|
36
|
+
and a half-built phase are each a decision someone must make deliberately — and
|
|
37
|
+
the cost of guessing is another spec's work moved without its author asking. A
|
|
38
|
+
refusal costs one command; the alternative can cost an afternoon.
|
|
39
|
+
|
|
40
|
+
**The one exception is the spec you are starting.** `spec-env up` classifies the
|
|
41
|
+
uncommitted tree against the target spec and answers one of three ways — relay
|
|
42
|
+
what it says rather than deciding for yourself:
|
|
43
|
+
|
|
44
|
+
| What it found | What it does |
|
|
45
|
+
|---------------|--------------|
|
|
46
|
+
| clean | provisions, as always |
|
|
47
|
+
| every path belongs to this spec | plans `git add` + `git commit` **first**, then the fork |
|
|
48
|
+
| any path does not | refuses, naming the paths that disqualified it |
|
|
49
|
+
|
|
50
|
+
That is membership in an exactly-known set — the spec's own folder plus the
|
|
51
|
+
project's `spec.companionPaths` — and **not** a judgement about whether the
|
|
52
|
+
changes look important. The gate still never decides that. When it plans the
|
|
53
|
+
commit, the paths are printed above the commands, so run them as printed; when it
|
|
54
|
+
refuses, relay the reason and stop.
|
|
55
|
+
|
|
56
|
+
It also refuses a **clean** tree whose spec is not in the commit the worktree
|
|
57
|
+
would fork from — otherwise you get a branch missing the very spec it is for.
|
|
58
|
+
|
|
59
|
+
## 2. Identify the spec
|
|
60
|
+
|
|
61
|
+
- Use the name/path argument when given.
|
|
62
|
+
- Otherwise use the spec **in context** (the one just created or discussed).
|
|
63
|
+
Unlike `/spec-next`, that fallback is safe here: this skill starts nothing
|
|
64
|
+
without the gate above passing, and a wrong guess is caught by the operator
|
|
65
|
+
before any code is written.
|
|
66
|
+
- Locate it under `specs/` — `specs/backlog/` first, then the other buckets. A
|
|
67
|
+
spec is a `<name>/` folder whose entry point is `00-overview.md`, with one
|
|
68
|
+
file per phase beside it (`01-<slug>.md`, `02-…`). Legacy specs may be a bare
|
|
69
|
+
`<name>.md`, or a `00-overview.md` with inline phases — handle those too.
|
|
70
|
+
- A spec already in `specs/in-progress/` was started before. Its worktree
|
|
71
|
+
probably still exists, so this is a re-attach: say so rather than reporting a
|
|
72
|
+
fresh start, and skip the housekeeping that is already done.
|
|
73
|
+
|
|
74
|
+
## 3. Build its branch
|
|
75
|
+
|
|
76
|
+
### `worktree` mode
|
|
77
|
+
|
|
78
|
+
1. **Provision.** Run `skitterspec spec-env up <name>` — a planner, so run the
|
|
79
|
+
`to provision, run:` commands it prints and confirm they succeeded, **except**
|
|
80
|
+
the `open.command` line, which belongs to the hand-off in 3 below.
|
|
81
|
+
|
|
82
|
+
2. **Enter the worktree — this session, no new window.** The spec is built in its
|
|
83
|
+
worktree; that is what the mode is for, and it is why `main` stays free and
|
|
84
|
+
several specs run at once. So put *this* session there rather than opening
|
|
85
|
+
another one: call **`EnterWorktree`** with the worktree path.
|
|
86
|
+
|
|
87
|
+
**Do it immediately after `git worktree add`, before bootstrap and before the step 4 housekeeping.**
|
|
88
|
+
Once the session is the worktree, `setup` runs in place and the housekeeping
|
|
89
|
+
is plain `git` — the `cd` and the `git -C <worktreePath>` prefix both
|
|
90
|
+
disappear. **Never `cd` into the worktree first:** `EnterWorktree` refuses a
|
|
91
|
+
path that is already the working directory
|
|
92
|
+
(`is the current working directory`), so a bootstrap `cd` does not merely
|
|
93
|
+
make the call redundant, it makes it fail.
|
|
94
|
+
|
|
95
|
+
Then run the planner's **`then, in the worktree, run:`** steps in order (file
|
|
96
|
+
seeding, then `setup`) — a fresh worktree has no dependencies and none of the
|
|
97
|
+
repo's gitignored files, so hooks, typechecks and tests fail until they are
|
|
98
|
+
there. Do the step 4 housekeeping, then say to run **`/spec-next`**, here.
|
|
99
|
+
|
|
100
|
+
**Do not move the branch into this checkout**, and do not ask the operator to.
|
|
101
|
+
Entering the worktree is the opposite of moving the branch out of it — the
|
|
102
|
+
branch never leaves. `/spec-live` is for testing a finished-enough spec on the
|
|
103
|
+
already-running dev server; it is not the way work gets started, and reaching
|
|
104
|
+
for it here is what used to split a start across two invocations.
|
|
105
|
+
|
|
106
|
+
3. **When you cannot enter, hand off as before.** Two cases, and
|
|
107
|
+
**decide from cwd before calling**, not by calling and catching — an error
|
|
108
|
+
surfaced mid-skill reads as a bug, and both conditions are knowable in
|
|
109
|
+
advance:
|
|
110
|
+
|
|
111
|
+
- **This session's cwd is already inside a worktree.** `EnterWorktree` only
|
|
112
|
+
allows a worktree→worktree switch when the target lives under
|
|
113
|
+
`.claude/worktrees/`, and a project's worktree root usually does not
|
|
114
|
+
(`../{repo}-wt` is the shipped shape). Do not "fix" this by relocating the
|
|
115
|
+
root: starting a spec from the base branch is the documented path anyway.
|
|
116
|
+
- **`EnterWorktree` is unavailable** — a harness without it. There is no CLI
|
|
117
|
+
counterpart to fall back on, so the skill degrades rather than fails.
|
|
118
|
+
|
|
119
|
+
On either, do exactly what this skill did before: bootstrap with
|
|
120
|
+
`cd "<worktreePath>"`, housekeep with `git -C <worktreePath>`, run
|
|
121
|
+
`open.command` if one is configured, print the worktree path, and say to run
|
|
122
|
+
**`/spec-next`** from a session in it.
|
|
123
|
+
**Trust the worktree first** on this path: `spec-env up` wrote the printed
|
|
124
|
+
`trusted:` root into `.claude/settings.local.json`, but that file will not
|
|
125
|
+
hot-reload in this session — run `/add-dir <trusted root>` before editing into
|
|
126
|
+
the worktree, or the first write prompts. (Entering the worktree makes both
|
|
127
|
+
moot: the writes are then in-cwd.)
|
|
128
|
+
|
|
129
|
+
`EnterWorktree` is gated on being told to work in a worktree by the user or by
|
|
130
|
+
project instructions. A lifecycle skill directing it **is** project
|
|
131
|
+
instruction, so the call is in contract here.
|
|
132
|
+
|
|
133
|
+
**`/spec-next` is unchanged by this.** Its rule 2 — "the worktree you are
|
|
134
|
+
standing in" — is what answers once the session has actually moved; nothing about
|
|
135
|
+
its resolution is loosened, and it must not be. The refusal exists so the wrong
|
|
136
|
+
branch is never built.
|
|
137
|
+
|
|
138
|
+
### `checkout` mode
|
|
139
|
+
|
|
140
|
+
Run `skitterspec spec-env up <name>` and the single `git switch` it prints.
|
|
141
|
+
There is no worktree, no bootstrap and no hand-off — the checkout is already the
|
|
142
|
+
workbench. Its planner enforces the same gate from the engine side, so relay any
|
|
143
|
+
refusal and stop.
|
|
144
|
+
|
|
145
|
+
## 4. Move the spec into development
|
|
146
|
+
|
|
147
|
+
**Do this before you report anything**, so no path can end with a provisioned
|
|
148
|
+
worktree and a spec still reading `Ready` in `specs/backlog/`. Having entered the
|
|
149
|
+
worktree (step 3.2) this is plain `git` — you are standing in it. On the hand-off
|
|
150
|
+
path (step 3.3) run it against the worktree with `git -C <worktreePath>` instead;
|
|
151
|
+
in `checkout` mode the branch is already here.
|
|
152
|
+
|
|
153
|
+
- `git mv "specs/backlog/<name>" "specs/in-progress/<name>"` if it isn't there
|
|
154
|
+
already (`mkdir -p specs/in-progress` first). Use `git mv` to keep history.
|
|
155
|
+
- Set the **Status** header: `> **Status:** In Progress — Phase 1 (started <YYYY-MM-DD>)`.
|
|
156
|
+
- Set **Developer** if it is still `—` (`git config user.name`).
|
|
157
|
+
- Append a **State log** row: `| <YYYY-MM-DD> | In Progress | in-progress | <git user.name> |`.
|
|
158
|
+
- **Commit it, and push the branch.** One commit, the spec's own — it records the
|
|
159
|
+
in-progress state for everyone and fires the tracker's automation. Do this
|
|
160
|
+
*before* the tracker refresh below, so the snapshot that refresh writes is
|
|
161
|
+
swept up by the phase's own commit rather than left dirty.
|
|
162
|
+
|
|
163
|
+
A spec ideally arrives `Ready` from `/spec`; a `Draft` works too — sanity-check
|
|
164
|
+
it is well-formed first.
|
|
165
|
+
|
|
166
|
+
## 4b. Note a missing gating decision (only if configured)
|
|
167
|
+
|
|
168
|
+
**Only when `specs/.core/gating.config.json` exists.** Run
|
|
169
|
+
`skitterspec gating check <name>` and, if it names this spec, mention it **once**
|
|
170
|
+
before phase 1 starts — the cheapest moment to decide is before any code exists.
|
|
171
|
+
|
|
172
|
+
**This check is advisory.** It reports; it never refuses, and nothing below is
|
|
173
|
+
conditional on it. A spec written before the project adopted gating has no header
|
|
174
|
+
and is not broken — turning this into a gate would accuse the very specs the
|
|
175
|
+
feature was designed not to disturb.
|
|
176
|
+
|
|
177
|
+
## 5. Bring the spec's dev servers up — confirm before heavy steps
|
|
178
|
+
|
|
179
|
+
**Only when the project configures host dev servers** (`env.config.json` → a
|
|
180
|
+
non-empty `dev` array). Show what will start — the commands, the ports, any
|
|
181
|
+
Docker stack — and get a yes; on **`--plan`**, print it and stop. On
|
|
182
|
+
confirmation run `skitterspec spec-env dev up <name>`. With none configured this
|
|
183
|
+
is a clean no-op.
|
|
184
|
+
|
|
185
|
+
To reach the spec at your normal `localhost` URL afterwards, the **user** types
|
|
186
|
+
**`/spec-connect <name>`** (`/spec-connect main` hands the ports back). Never
|
|
187
|
+
invoke it yourself.
|
|
188
|
+
|
|
189
|
+
## 6. Build phase 1
|
|
190
|
+
|
|
191
|
+
**`checkout` mode — carry straight on into `/spec-next`** in this session: it
|
|
192
|
+
marks phase 1 started, refreshes the mirror again, builds it with tests and
|
|
193
|
+
reports. Do not stop and ask the operator to run it: the branch is here and they
|
|
194
|
+
asked to start the spec.
|
|
195
|
+
|
|
196
|
+
**`worktree` mode — the spec is built in its worktree**, and step 3.2 put this
|
|
197
|
+
session there, so end by saying the worktree path is now the session's and to run
|
|
198
|
+
**`/spec-next`** here. On the hand-off path (step 3.3) the session did not move:
|
|
199
|
+
say so plainly, print the path, and tell them to run `/spec-next` from a session
|
|
200
|
+
in it.
|
|
201
|
+
|
|
202
|
+
`/spec-next` resolves the spec it is *standing in* — the live spec of the
|
|
203
|
+
checkout, the worktree its cwd is inside, or the branch in `checkout` mode — and
|
|
204
|
+
a name argument narrows a re-run rather than selecting a spec elsewhere. That
|
|
205
|
+
refusal is deliberate: building the wrong spec's phase writes commits on a branch
|
|
206
|
+
nobody asked for. **Do not work around it**, and do not offer to build the phase
|
|
207
|
+
from here.
|
|
208
|
+
|
|
209
|
+
## Opt-outs
|
|
210
|
+
|
|
211
|
+
- **`--plan`** — print the provisioning and dev-server plan, change nothing.
|
|
212
|
+
- **`--no-worktree`** — skip provisioning and build on the current branch. Warn
|
|
213
|
+
that the work lands wherever you are (usually the base branch); reserve it for
|
|
214
|
+
a trivial change or an explicit request.
|
|
215
|
+
|
|
216
|
+
There is no `--here`: `/spec-start` **is** here. It puts the branch in the
|
|
217
|
+
checkout you are in, which is what the old opt-out was reaching for.
|
|
218
|
+
|
|
219
|
+
## Why there is no tracker seam here
|
|
220
|
+
|
|
221
|
+
This skill creates no spec and mints no issue, so it has nothing to link — the
|
|
222
|
+
intake and picker steps belong to `/spec`, `/spec-bug` and `/spec-hotfix`. The
|
|
223
|
+
state change it *does* make (the spec moving to `in-progress`) is mirrored by
|
|
224
|
+
the refresh `/spec-next` runs the moment it starts, which pushes the issue state
|
|
225
|
+
and the phase states together. Adding a push here would send the same thing
|
|
226
|
+
twice, one commit apart.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: spec-status
|
|
3
|
-
description: Show a spec's one-way sync status against Linear — a read-only drift report
|
|
3
|
+
description: Show a spec's one-way sync status against Linear — a read-only drift report naming what would push, and whether the tracker's workflow-state has drifted from the spec's. Changes nothing. Opt-in — needs specs/.core/linear.config.json. Use when the user says "/spec-status", "is this spec in sync with Linear", "what would push", or "show spec sync status".
|
|
4
4
|
disable-model-invocation: true
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -77,4 +77,4 @@ given spec got is stated. Under **`inline`** the phases live in the spec issue's
|
|
|
77
77
|
description rather than as sub-issues, so "0 to create" is the expected shape
|
|
78
78
|
rather than a sign the phase files failed to parse; under **`deferred`** the
|
|
79
79
|
`N phase(s) deferred` line above it says how many are still waiting on
|
|
80
|
-
`/spec-
|
|
80
|
+
`/spec-next`. Relay both lines as printed.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: spec-sync
|
|
3
|
-
description: Run the repo-wide spec-sync operations against Linear — what
|
|
3
|
+
description: Run the repo-wide spec-sync operations against Linear — what is linked, the workspace states and projects, read-back verification, manual stamping, and bulk apply. Run it bare for the repo-wide overview. Defers to /spec-push and /spec-status for per-spec work. Opt-in — needs specs/.core/linear.config.json. Use when the user says "/spec-sync", "run spec-sync", "what's linked to Linear", "which states does the workspace have", or "mirror the whole backlog".
|
|
4
4
|
disable-model-invocation: true
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -142,10 +142,10 @@ pnpm exec skitterspec-linear spec-sync released [<range>] [--json]
|
|
|
142
142
|
```
|
|
143
143
|
|
|
144
144
|
`ref` prints the ticket for the branch you are on, so a commit can carry
|
|
145
|
-
`Refs: <KEY-N>` — see `.claude/rules/commit-trailers.md`.
|
|
146
|
-
on a spec that is not linked, it prints nothing and exits non-zero.**
|
|
147
|
-
correct answer, not an error to work around: do not invent a ref,
|
|
148
|
-
`Refs: none`.
|
|
145
|
+
`Refs: <KEY-N>` — see `.claude/rules/commit-trailers.md`.
|
|
146
|
+
**Off a spec branch, or on a spec that is not linked, it prints nothing and exits non-zero.**
|
|
147
|
+
That is the correct answer, not an error to work around: do not invent a ref,
|
|
148
|
+
and never write `Refs: none`.
|
|
149
149
|
|
|
150
150
|
`released` reports the tickets in a commit range — by default since the most
|
|
151
151
|
recent tag, and it always prints the range it chose so a wrong default is
|
|
@@ -170,9 +170,9 @@ the report saying which commits it set aside.
|
|
|
170
170
|
pnpm exec skitterspec-linear spec-sync retarget [--yes]
|
|
171
171
|
```
|
|
172
172
|
|
|
173
|
-
Renaming a Linear team rewrites the key in every issue identifier, and
|
|
174
|
-
in the repo moves**: the frontmatter stamps, the config `teamKey`,
|
|
175
|
-
snapshot filenames and their sub-issue keys all keep the old prefix, so
|
|
173
|
+
Renaming a Linear team rewrites the key in every issue identifier, and
|
|
174
|
+
**nothing in the repo moves**: the frontmatter stamps, the config `teamKey`,
|
|
175
|
+
and the snapshot filenames and their sub-issue keys all keep the old prefix, so
|
|
176
176
|
`/spec-push` starts failing with `no Linear issue found for SKI-7`.
|
|
177
177
|
|
|
178
178
|
`retarget` detects the rename (the team id survives it; the key does not) and
|