@skitterbyte/skitterspec-jira 0.0.0-stage → 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.
Files changed (113) hide show
  1. package/MIGRATION.md +1266 -0
  2. package/README.md +44 -2
  3. package/assets/claude-md-section.md +85 -0
  4. package/assets/commands/allow-main.md +33 -0
  5. package/assets/commands/spec-connect.md +13 -0
  6. package/assets/commands/spec-live.md +14 -0
  7. package/assets/commands/spec-remote-review.md +37 -0
  8. package/assets/commands/spec-skip.md +24 -0
  9. package/assets/core/SETUP.md +192 -0
  10. package/assets/core/env.config.json.example +56 -0
  11. package/assets/core/env.config.md +606 -0
  12. package/assets/core/gating.config.json.example +4 -0
  13. package/assets/core/gating.config.md +81 -0
  14. package/assets/core/jira.config.json.example +18 -0
  15. package/assets/core/jira.config.md +88 -0
  16. package/assets/hooks/main-guard.cjs +162 -0
  17. package/assets/hooks/review-gate.cjs +146 -0
  18. package/assets/review/page.html +3384 -0
  19. package/assets/rules/commit-trailers.md +96 -0
  20. package/assets/rules/negative-checks.md +69 -0
  21. package/assets/rules/offered-unblocks.md +100 -0
  22. package/assets/rules/spec-planning.md +702 -0
  23. package/assets/rules/spec-reports.md +572 -0
  24. package/assets/skills/no-spec/SKILL.md +249 -0
  25. package/assets/skills/spec/SKILL.md +714 -0
  26. package/assets/skills/spec-bug/SKILL.md +585 -0
  27. package/assets/skills/spec-cancel/SKILL.md +261 -0
  28. package/assets/skills/spec-complete/SKILL.md +451 -0
  29. package/assets/skills/spec-diff/SKILL.md +870 -0
  30. package/assets/skills/spec-hotfix/SKILL.md +607 -0
  31. package/assets/skills/spec-init/SKILL.md +178 -0
  32. package/assets/skills/spec-jira-setup/SKILL.md +221 -0
  33. package/assets/skills/spec-list/SKILL.md +173 -0
  34. package/assets/skills/spec-next/SKILL.md +678 -0
  35. package/assets/skills/spec-push/SKILL.md +306 -0
  36. package/assets/skills/spec-review/SKILL.md +256 -0
  37. package/assets/skills/spec-reviewed/SKILL.md +284 -0
  38. package/assets/skills/spec-start/SKILL.md +496 -0
  39. package/assets/skills/spec-status/SKILL.md +114 -0
  40. package/assets/skills/spec-sync/SKILL.md +220 -0
  41. package/assets/skills/spec-to-main/SKILL.md +141 -0
  42. package/bin/skitterspec-jira.js +68 -0
  43. package/package.json +40 -4
  44. package/src/cli.js +5967 -0
  45. package/src/deprecate.js +138 -0
  46. package/src/env/bootstrap.js +188 -0
  47. package/src/env/building.js +143 -0
  48. package/src/env/classify.js +176 -0
  49. package/src/env/commitcmd.js +108 -0
  50. package/src/env/config.js +850 -0
  51. package/src/env/dev.js +63 -0
  52. package/src/env/glob.js +34 -0
  53. package/src/env/hooks.js +226 -0
  54. package/src/env/hotfix.js +413 -0
  55. package/src/env/hotfixland.js +302 -0
  56. package/src/env/integrate.js +167 -0
  57. package/src/env/live.js +479 -0
  58. package/src/env/mainguard.js +299 -0
  59. package/src/env/provision.js +466 -0
  60. package/src/env/proxy.js +225 -0
  61. package/src/env/prune.js +119 -0
  62. package/src/env/registry.js +159 -0
  63. package/src/env/render.js +17 -0
  64. package/src/env/resolve.js +716 -0
  65. package/src/env/review.js +2622 -0
  66. package/src/env/reviewers/coderabbit.js +228 -0
  67. package/src/env/reviewers.js +517 -0
  68. package/src/env/serve.js +1044 -0
  69. package/src/env/supervise.js +157 -0
  70. package/src/env/teardown.js +248 -0
  71. package/src/env/trust.js +87 -0
  72. package/src/gating.js +155 -0
  73. package/src/init.js +1150 -0
  74. package/src/lines-diff.js +114 -0
  75. package/src/prompts.js +129 -0
  76. package/src/vendor/jira/adf.js +414 -0
  77. package/src/vendor/jira/api.js +295 -0
  78. package/src/vendor/jira/cli-sync.js +1252 -0
  79. package/src/vendor/jira/commands.js +44 -0
  80. package/src/vendor/jira/config.js +286 -0
  81. package/src/vendor/jira/credentials.js +108 -0
  82. package/src/vendor/jira/dialect.js +45 -0
  83. package/src/vendor/jira/mcp.js +129 -0
  84. package/src/vendor/provider-kit/index.js +41 -0
  85. package/src/vendor/provider-kit/src/adapter.js +23 -0
  86. package/src/vendor/provider-kit/src/apply.js +207 -0
  87. package/src/vendor/provider-kit/src/args.js +68 -0
  88. package/src/vendor/provider-kit/src/assignment.js +277 -0
  89. package/src/vendor/provider-kit/src/config.js +251 -0
  90. package/src/vendor/provider-kit/src/credentials.js +404 -0
  91. package/src/vendor/provider-kit/src/doctor.js +35 -0
  92. package/src/vendor/provider-kit/src/http.js +63 -0
  93. package/src/vendor/provider-kit/src/identity.js +93 -0
  94. package/src/vendor/provider-kit/src/list-assignee.js +71 -0
  95. package/src/vendor/provider-kit/src/list-rows.js +35 -0
  96. package/src/vendor/provider-kit/src/md-compare.js +39 -0
  97. package/src/vendor/provider-kit/src/md-tables.js +94 -0
  98. package/src/vendor/provider-kit/src/prompt.js +50 -0
  99. package/src/vendor/provider-kit/src/released.js +219 -0
  100. package/src/vendor/provider-kit/src/specs.js +248 -0
  101. package/src/vendor/provider-kit/src/transport.js +22 -0
  102. package/src/vendor/sync-core/index.js +92 -0
  103. package/src/vendor/sync-core/src/base.js +81 -0
  104. package/src/vendor/sync-core/src/compare.js +338 -0
  105. package/src/vendor/sync-core/src/dialect.js +51 -0
  106. package/src/vendor/sync-core/src/fields.js +36 -0
  107. package/src/vendor/sync-core/src/legacy.js +100 -0
  108. package/src/vendor/sync-core/src/normalize.js +971 -0
  109. package/src/vendor/sync-core/src/push.js +85 -0
  110. package/src/vendor/sync-core/src/retarget.js +278 -0
  111. package/src/vendor/sync-core/src/task-block.js +308 -0
  112. package/src/vendor/sync-core/src/verify.js +67 -0
  113. package/src/vendor/sync-core/src/write.js +191 -0
package/README.md CHANGED
@@ -1,3 +1,45 @@
1
- # Temporary Holding Version
1
+ # @skitterbyte/skitterspec-jira
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Spec-driven development for Claude Code, with **one-way Jira Cloud sync** — a
4
+ superset of [`@skitterbyte/skitterspec`](https://www.npmjs.com/package/@skitterbyte/skitterspec):
5
+ the base filesystem workflow plus `/spec-push` · `/spec-status` and the
6
+ `spec-sync` CLI. The repo is canonical; Jira is a generated mirror.
7
+
8
+ Install exactly one distribution — this, the base, or
9
+ `@skitterbyte/skitterspec-linear` — never two:
10
+
11
+ ```
12
+ npx @skitterbyte/skitterspec-jira init
13
+ ```
14
+
15
+ ## What the sync is
16
+
17
+ - A spec is a Jira **issue**; each phase a **subtask**; tasks ride along as a
18
+ live ADF checklist (still a read-only mirror — the repo wins on every push).
19
+ - **One-way**: nothing is read back into the repo. A description edited on Jira
20
+ is reported (`spec-sync status`) and replaced on the next push, loudly.
21
+ - Descriptions are stored as ADF; the engine converts markdown at the adapter
22
+ boundary and verifies round-trips **by words, not bytes**.
23
+ - Workflow moves happen by **transition** — an unreachable status refuses
24
+ loudly and names what is available; the engine never path-finds a workflow.
25
+
26
+ ## Setup
27
+
28
+ `/spec-jira-setup` interviews you and writes `specs/.core/jira.config.json`
29
+ (site URL, project key, issue/subtask types, bucket→status names). Credentials
30
+ are an email + API token pair, kept out of the repo:
31
+
32
+ ```
33
+ skitterspec-jira spec-sync credentials set
34
+ ```
35
+
36
+ `skitterspec-jira spec-sync doctor --check-remote` reports readiness across
37
+ every layer.
38
+
39
+ Not yet shipped for Jira (the Linear superset has them): assignment/`/spec-claim`,
40
+ `retarget`, `reattach`, `spec-sanitise`. Releases and deploys are the host
41
+ project's to record on every provider: `spec-sync released <range> --json`
42
+ lists a range's tickets for its CI to act on.
43
+
44
+ This package's `bin/`, `src/` and `assets/` are composed at build time from the
45
+ skitterspec monorepo — see the repo for sources and development.
@@ -0,0 +1,85 @@
1
+ ## Spec workflow
2
+
3
+ Spec-driven development runs through the lifecycle **skills** — use them so
4
+ structure and lifecycle stay consistent. The everyday loop is
5
+ **`spec → start → next → commit → complete`**, with **`/spec-diff`** to read what
6
+ a phase changed and `/spec-connect` when you want to test the spec in a browser.
7
+
8
+ **Skills vs commands.** The lifecycle skills are read by Claude, which exercises
9
+ judgment. `/spec-connect` and `/spec-live` are **slash commands** instead — each
10
+ pre-executes one `spec-env` verb and relays it, so only you can run them; a
11
+ skill that wants one will tell you to type it.
12
+
13
+ **Seeing the work.** A phase is built in its own worktree, so `git diff` in your
14
+ terminal answers about the base branch. **`/spec-diff`** renders that worktree's
15
+ diff as a self-contained HTML page you open locally — or publish, and read on a
16
+ phone. The diff never passes through the model, so the page costs no context
17
+ tokens however large it is; the optional written review is the part that costs,
18
+ and it is offered rather than assumed. `/spec-next` writes the page at the end of
19
+ every phase.
20
+
21
+ **Handing the review back.** The page takes marks: tick `✓ accept` per file as
22
+ you read, note anything against a line or a whole file, answer the questions a
23
+ written review asked — then **end it in a decision**. At the end of a phase the
24
+ buttons are `✓ Commit`, `✓ Commit & Continue` (commit, then build the next
25
+ phase), `↺ Request changes` and `… Discuss first`. A spec that has just been
26
+ written gets `✓ Commit & Start` instead — the `commit-start` verdict, which is
27
+ the `commit && /spec-start` you would otherwise type. A **served** page hands
28
+ the pass to the engine, which **holds** it and shows a six-digit code. Committing
29
+ is unavailable while a note is open — you asked for something, so it cannot also
30
+ be fine — and it hands off to your own commit skill (`review.commitWith`,
31
+ `/commit` by default) rather than a copy living here. Fixes come back as
32
+ resolutions, so the next render shows each note struck through with what changed.
33
+ An accept remembers the file's content, so it lapses by itself when that file
34
+ changes again. The marks are information — nothing gates on them; the verdict is
35
+ the one thing you choose, once.
36
+
37
+ **A second opinion, when you want one** (`review.reviewers`, empty by default).
38
+ Point it at a code reviewer that runs from your terminal — CodeRabbit's CLI has
39
+ a free tier and `{ "use": "coderabbit" }` is the whole setup; anything printing
40
+ [rdjsonl](https://github.com/reviewdog/reviewdog) works too — and its findings
41
+ land on the page beside your own notes, each badged with the tool that found it
42
+ and linked to the line it is about. `/spec-next` runs them once per phase;
43
+ `/spec-diff --reviewers` is the mid-phase ask. **They never block a commit** —
44
+ reply to one and your reply does, because then a person has asked for something.
45
+ A reviewer that could not run says `did not run — <why>` rather than passing as
46
+ clean, because on the page you commit from those two must not look alike.
47
+ Adding one sends your diff to whatever the command talks to, which is why the
48
+ list starts empty and `init` never writes one.
49
+
50
+ **Above the verdicts sits one line that is not a verdict.** `▶ Put it live`
51
+ commits the phase, checks the branch out where your dev server can see it, and
52
+ hands you back the same page with the same options — so you can judge the change
53
+ by *using* it, not only by reading it. It clears no gate: you have looked at it
54
+ running and concluded nothing, so the phase still owes an answer.
55
+
56
+ **Every render lists where the page can be read**, labelled, in a fixed order —
57
+ `local`, `network`, `remote` — each either a URL or the one command that turns it
58
+ on, plus a `live:` line saying whether the change is also running. `local` and
59
+ `network` are two doors into one room: the same server and the same waiting
60
+ verdict, so one wait covers both. `remote` is a published page, a second store —
61
+ a verdict there needs `/spec-reviewed`, and it stays off until someone types
62
+ **`/spec-remote-review`**, which toggles it. Publishing is permitted by that
63
+ command, never performed by it: a published page is one this tooling cannot
64
+ remove.
65
+
66
+ **One ending.** Every spec skill finishes with the same block — a verdict
67
+ (`✅` · `⚠️` · `❌` · `⏸`), then a table of the fields that skill declares,
68
+ ending on the one thing to do next. A refusal emits it too, so "nothing
69
+ happened" is reported rather than absent, and `Follow-ups` is always there
70
+ because a recorded `none` is a decision. Skills stay quiet while they run. The
71
+ shape lives in **`.claude/rules/spec-reports.md`**.
72
+
73
+ The skill table, the spec type/folder conventions and the per-spec isolation
74
+ model all live in **`.claude/rules/spec-planning.md`**, the canonical reference
75
+ every spec skill points at. Tailor its per-phase test commands to this stack.
76
+
77
+ **Release gating** *(only when `specs/.core/gating.config.json` exists)* — each
78
+ spec records whether it ships behind a feature flag, as
79
+ `> **Gating:** <flag name>` or `> **Gating:** none: <one-line reason>`. `/spec`,
80
+ `/spec-bug` and `/spec-hotfix` ask; `/spec-review`, `/spec-start` and
81
+ `/spec-complete` report a spec that has no answer, and never block over it.
82
+ `skitterspec gating check` lists them and always exits 0. The point is that the
83
+ question is **on the record**: a missing line is an oversight, a reason is a
84
+ decision. Skitterspec never reads your flag system — it asks and cites the doc
85
+ you point it at. Without that config, none of this appears.
@@ -0,0 +1,33 @@
1
+ ---
2
+ description: Allow writes on the base branch for this session — bare allows it with no reason, a reason records why, `off` clears it
3
+ argument-hint: "[<reason> | off]"
4
+ allowed-tools: Bash({{exec}} skitterspec spec-env main allow:*)
5
+ disable-model-invocation: true
6
+ ---
7
+ !`{{exec}} skitterspec spec-env main allow $ARGUMENTS`
8
+
9
+ Relay the engine output above verbatim. Add nothing and run nothing else.
10
+
11
+ **Only you can run this, and that is the whole guard.** The main-guard hook
12
+ refuses an `Edit` or a `Write` in the primary checkout while it sits on the base
13
+ branch, and it names two exits: move the work with `/no-spec` or `/spec-start`,
14
+ or have you lift the guard. Claude can take the first exit on its own and can
15
+ never take the second — a guard the model can lift is decoration, which is the
16
+ same line `/spec-reviewed` draws.
17
+
18
+ **Give it a reason.** `/allow-main fixing the lockfile by hand` records why; bare
19
+ records `none given`. The reason is the part a reviewer can argue with, exactly
20
+ as with a `Gating:` header — and it is what you will read when you wonder later
21
+ why the base branch has an uncommitted file on it.
22
+
23
+ **It lapses with the session where it can.** The allow is recorded against this
24
+ session, so it ends when the session does. Where the harness exposes no session
25
+ id it degrades to a repo-wide allow that outlives the session and is cleared only
26
+ by `/allow-main off`; the engine says which kind you got, so do not read a bare
27
+ "allowed" as temporary.
28
+
29
+ **It is machine-local and gitignored.** Unlike `/spec-remote-review`, this writes
30
+ nothing anyone else pulls: it is your decision about your checkout, not the
31
+ project's policy. The project-wide switch is `guards.mainIsLandingZone` in
32
+ `specs/.core/env.config.json`, and turning that off is a different, committed
33
+ decision.
@@ -0,0 +1,13 @@
1
+ ---
2
+ description: Expose one spec's dev servers on the canonical localhost ports — bare uses the spec you are on, `main` hands them back
3
+ argument-hint: "[<spec> | main]"
4
+ allowed-tools: Bash({{exec}} skitterspec spec-env connect:*)
5
+ disable-model-invocation: true
6
+ ---
7
+ !`{{exec}} skitterspec spec-env connect $ARGUMENTS`
8
+
9
+ Relay the engine output above verbatim. Add nothing and run nothing else.
10
+
11
+ Only if it reports a **canonical port is in use**: say that the main checkout's
12
+ dev server still holds that port, and that stopping it and re-running is the fix
13
+ (the proxy cannot share a port main is bound to).
@@ -0,0 +1,14 @@
1
+ ---
2
+ description: Put one spec live on the already-running dev server — bare takes the spec you are on, `<spec>` takes that one, `main` hands the instance back
3
+ argument-hint: "[<spec> | main | status | release | abort]"
4
+ allowed-tools: Bash({{exec}} skitterspec spec-env live:*)
5
+ disable-model-invocation: true
6
+ ---
7
+ !`{{exec}} skitterspec spec-env live $ARGUMENTS`
8
+
9
+ Relay the engine output above verbatim. Add nothing and run nothing else.
10
+
11
+ The engine enforces every refusal itself and prints why — a hotfix, a stateful
12
+ spec (`Stack: worktree + docker`), a branch touching migrations, a dirty tree, a
13
+ rebase conflict, or another spec already holding the instance. Do not re-explain
14
+ or work around any of them.
@@ -0,0 +1,37 @@
1
+ ---
2
+ description: Permit or forbid publishing this spec's review for a reader off your network — bare toggles it, `on`/`off` set it explicitly
3
+ argument-hint: "[on | off]"
4
+ allowed-tools: Bash({{exec}} skitterspec spec-env review allow remote:*)
5
+ disable-model-invocation: true
6
+ ---
7
+ !`{{exec}} skitterspec spec-env review allow remote --set "$ARGUMENTS"`
8
+
9
+ Relay the engine output above verbatim. Add nothing to it.
10
+
11
+ **Then one thing, and only when the engine asked for it.** Where the output
12
+ carries a `review:` and a `publish:` line, typing this command **is** the ask
13
+ to publish that page: the banner named this command beside a review the reader
14
+ could not reach, so the link is what they are waiting for. Publish it exactly
15
+ as `/spec-diff` §6 describes (run the `publish:` command, publish the copy it
16
+ names with a store, write the URL file), then re-emit the review banner with
17
+ all three tiers, `remote` now carrying the URL. A wait already running keeps
18
+ running; do not restart it or the server. If the harness refuses the publish,
19
+ say so in one line and keep the banner as it was, because local and network
20
+ still work.
21
+
22
+ With no `publish:` line, run nothing else. Nothing has a page to publish, or
23
+ the tier was turned off.
24
+
25
+ **Bare toggles.** That is the common case: you are looking at a render whose
26
+ `remote:` line says which way it currently is, and what you want is the other
27
+ one. `on` and `off` are there for when you are not looking at it.
28
+
29
+ **It is per spec.** The engine resolves the spec from the worktree you are
30
+ standing in and records the permission in the gitignored review store, so
31
+ the base branch is untouched and no other spec may publish because of it.
32
+ `skitterspec spec-env review allow remote --project` is the committed,
33
+ every-spec form, for a team that wants it.
34
+
35
+ **A published page is one skitterspec cannot delete**, and a verdict pressed
36
+ on one needs `/spec-reviewed`, because nothing pushes from an artifact's store
37
+ into a conversation.
@@ -0,0 +1,24 @@
1
+ ---
2
+ description: Move on from a phase without a verdict, with the reason on the record — clears the review gate
3
+ argument-hint: "\"<reason>\""
4
+ allowed-tools: Bash({{exec}} skitterspec spec-env review skip:*)
5
+ disable-model-invocation: true
6
+ ---
7
+ !`{{exec}} skitterspec spec-env review skip "$ARGUMENTS"`
8
+
9
+ Relay the engine output above verbatim. Add nothing and run nothing else.
10
+
11
+ **It refuses with no reason, and that refusal is the feature.** A skip with no
12
+ reason is indistinguishable from nobody having looked — where
13
+ `none: additive, nothing to revert` is a decision a reviewer can argue with. If
14
+ the engine asked for a reason, relay that and stop; do not supply one on the
15
+ operator's behalf.
16
+
17
+ **It is one of exactly two exits**, and the other is a committing verdict on the
18
+ page. Nothing else discharges a phase's obligation — not a mid-run `Continue`,
19
+ not an action, not a render.
20
+
21
+ **Only you can run it.** Like `/allow-main`, this lifts a guard aimed at Claude,
22
+ so Claude must not be able to lift it. `/spec-connect` and `/spec-live` are
23
+ user-only for the milder reason that there is no judgement to apply; this one is
24
+ user-only because of who the guard is for.
@@ -0,0 +1,192 @@
1
+ # Setting up Jira sync (one-way)
2
+
3
+ A start-to-finish guide to getting `/spec-status` and `/spec-push` working
4
+ against a real Jira Cloud site. Sync is **one-way**: the repo is the source of
5
+ truth and the linked Jira **issue** is a **generated mirror** — content is
6
+ pushed up, never read back or merged. A spec is a Jira issue and each phase a
7
+ **subtask**, with the phase's tasks mirrored into that subtask's description as
8
+ a live (but read-only — the repo wins) ADF checklist. Covers the **Jira side**
9
+ (credentials, finding your project) that the config reference
10
+ (`jira.config.md`) assumes you already have.
11
+
12
+ > The whole feature is **opt-in**: until `specs/.core/jira.config.json` exists,
13
+ > everything below is inert and the package behaves exactly like the base
14
+ > `@skitterbyte/skitterspec`.
15
+
16
+ Two things about Jira Cloud shape everything below:
17
+
18
+ - **Descriptions are ADF** (Atlassian Document Format), not markdown. The
19
+ adapter converts markdown at the boundary in both directions, and every
20
+ verification compares **words, not bytes**.
21
+ - **Statuses move by workflow transition** — there is no "set status". The
22
+ engine resolves the transition that lands on the configured status and
23
+ refuses loudly (naming what IS available) when none does. It never
24
+ path-finds a workflow.
25
+
26
+ ---
27
+
28
+ ## 1. Install the package
29
+
30
+ Install the Jira superset (or, if you already run the base or the Linear
31
+ superset, switch — every distribution contains the entire base, so install
32
+ exactly one):
33
+
34
+ ```sh
35
+ npx @skitterbyte/skitterspec-jira init
36
+ ```
37
+
38
+ ## 2. Credentials — an email + API-token pair
39
+
40
+ Jira Cloud authenticates REST calls with basic auth: your Atlassian account
41
+ email plus an API token. Create the token at
42
+ <https://id.atlassian.com/manage-profile/security/api-tokens>.
43
+
44
+ Store the pair in the user-level credentials store (outside every repo,
45
+ `0600`), **in your own terminal — not through an assistant**:
46
+
47
+ ```sh
48
+ pnpm exec skitterspec-jira spec-sync credentials set --site-url https://<site>.atlassian.net
49
+ # Jira account email for https://<site>.atlassian.net: you@example.com
50
+ # Jira API token for you@example.com (hidden): ●●●●●●●●
51
+ ```
52
+
53
+ (`--site-url` keys the store entry before the repo has a config; once
54
+ `jira.config.json` exists it can be omitted.)
55
+
56
+ Or export `JIRA_EMAIL` and `JIRA_API_TOKEN` (both — an email without a token
57
+ authenticates nothing). The environment wins over the store.
58
+
59
+ > **MCP alternative:** with no credentials at all, the sync skills fall back to
60
+ > Atlassian's official remote MCP server — `claude mcp add --transport http
61
+ > atlassian https://mcp.atlassian.com/v1/sse`, then authenticate via `/mcp`.
62
+ > The engine itself never calls MCP tools; the skills fetch and hand results to
63
+ > the engine as files (`--workspace-states`, `--stored`, `--text`).
64
+
65
+ ## 3. Configure — run `/spec-jira-setup`
66
+
67
+ With credentials in place, let the skill interview you:
68
+
69
+ ```
70
+ /spec-jira-setup
71
+ ```
72
+
73
+ It asks for the site URL, the project key, the issue and subtask type names,
74
+ and the four bucket→status names — validating them against the project's real
75
+ statuses (`spec-sync states`) — then writes `specs/.core/jira.config.json`
76
+ via `spec-sync init-config`. By hand instead:
77
+
78
+ ```sh
79
+ pnpm exec skitterspec-jira spec-sync init-config \
80
+ --site-url https://your-site.atlassian.net \
81
+ --project-key PROJ \
82
+ --state complete=Done
83
+ ```
84
+
85
+ **Commit the file it writes.** Spec worktrees fork from commits, so an
86
+ uncommitted `jira.config.json` is invisible inside every worktree — which is
87
+ where the sync skills actually run — and sync reports itself off there.
88
+
89
+ `jira.config.md` (installed beside this file) is the full field reference.
90
+ Two defaults worth knowing: the issue type is `Task` and the subtask type
91
+ `Subtask` — site admins rename these, so check yours; and `cancelled` maps to
92
+ `Done` because default Jira workflows carry no Cancelled status.
93
+
94
+ **Map buckets to work statuses, not deployment stages.** The buckets mirror
95
+ the repo's work lifecycle, so To Do / In Progress / Done translate honestly —
96
+ On Test / On Live do not: a finished phase would show as deployed before
97
+ anything was released. A workflow that encodes a pipeline has standard outs
98
+ (a separate subtask workflow, fix versions, the deployments panel) —
99
+ `jira.config.md` → **Choosing your status map** carries the reasoning.
100
+
101
+ ## 4. Check — `spec-sync doctor`
102
+
103
+ ```sh
104
+ pnpm exec skitterspec-jira spec-sync doctor --check-remote
105
+ ```
106
+
107
+ Every row reports `ok`, or names the exact command that fixes it. `missing`
108
+ is an opt-in not taken (fine); `broken` is configured-but-wrong (exit 1).
109
+ `--check-remote` additionally proves the credentials reach the site and that
110
+ every configured status exists in the project's workflows.
111
+
112
+ ## 5. Push a spec
113
+
114
+ `/spec-push <spec>` computes the plan, applies it, stamps the returned keys
115
+ into the spec's frontmatter (`jira_identifier` / `jira_url` /
116
+ `jira_issue_id`), verifies the round-trip by words, and records the snapshot
117
+ later pushes diff against. From then on the lifecycle skills keep the
118
+ mirror fresh on their own.
119
+
120
+ ---
121
+
122
+ ## Release smoke checklist
123
+
124
+ Re-runnable, against a real Jira Cloud site — a throwaway project is ideal.
125
+ Run before cutting a release that touches the adapter, the ADF layer or the
126
+ apply path. `states.json` below is the project's status names.
127
+
128
+ 1. **Doctor** — every row ok:
129
+
130
+ ```sh
131
+ pnpm exec skitterspec-jira spec-sync doctor --check-remote
132
+ ```
133
+
134
+ 2. **States** — the project's statuses list:
135
+
136
+ ```sh
137
+ pnpm exec skitterspec-jira spec-sync states --json > states.json
138
+ ```
139
+
140
+ 3. **Mint** — on a test spec with at least one phase and a task checklist:
141
+
142
+ ```sh
143
+ pnpm exec skitterspec-jira spec-sync plan <spec> --workspace-states states.json --json > plan.json
144
+ pnpm exec skitterspec-jira spec-sync apply <spec> --plan plan.json
145
+ ```
146
+
147
+ In Jira: the issue and its subtasks exist, statuses match the buckets, and
148
+ the task checklist renders as live checkboxes. (If a surface renders the
149
+ task list as plain text, the renderer has a `taskLists: false` fallback —
150
+ file it.)
151
+
152
+ 4. **Update** — edit the spec's prose and a phase's status, then plan + apply
153
+ again: the description is replaced and the subtask's status transitions.
154
+
155
+ 5. **Adopt + preserve** — hand-create an issue with a description, stamp it
156
+ onto a spec, and preserve before the linking push:
157
+
158
+ ```sh
159
+ pnpm exec skitterspec-jira spec-sync stamp <spec> --issue PROJ-123
160
+ pnpm exec skitterspec-jira spec-sync preserve <spec>
161
+ ```
162
+
163
+ The original description appears as a comment headed **Original report**;
164
+ a second `preserve` reports "already preserved".
165
+
166
+ 6. **Verify** — save a read-back (`{ "issue": "<description text>",
167
+ "subIssues": { "<ref>": "<text>" } }`; a ref is the phase file's basename
168
+ without `.md`, e.g. `01-date-logic` — keyed with the extension it reports
169
+ every sub-issue as a stale ref) and:
170
+
171
+ ```sh
172
+ pnpm exec skitterspec-jira spec-sync verify <spec> --stored readback.json
173
+ ```
174
+
175
+ "round-tripped intact", exit 0.
176
+
177
+ 7. **Released** — over a range with `Refs:` trailers:
178
+
179
+ ```sh
180
+ pnpm exec skitterspec-jira spec-sync released <tag>..HEAD
181
+ ```
182
+
183
+ Tickets counted, unreferenced commits reported, bookkeeping ignored.
184
+
185
+ 8. **Drift** — edit the issue's description **in Jira**, fetch the issue JSON,
186
+ and:
187
+
188
+ ```sh
189
+ pnpm exec skitterspec-jira spec-sync status <spec> --remote issue.json
190
+ ```
191
+
192
+ The edit is reported; the next apply replaces it and says so loudly.
@@ -0,0 +1,56 @@
1
+ {
2
+ "mode": "worktree",
3
+ "worktree": {
4
+ "root": "../{repo}-wt",
5
+ "folderPattern": "{slug}"
6
+ },
7
+ "docker": {
8
+ "enabled": true,
9
+ "composeFile": "docker-compose.yml",
10
+ "projectNamePattern": "{repoSlug}_{slug}",
11
+ "portBase": 3000,
12
+ "portsPerSpec": 10,
13
+ "envFile": ".env",
14
+ "backupCommand": ""
15
+ },
16
+ "seedFiles": {
17
+ "mode": "symlink",
18
+ "files": [
19
+ ".env"
20
+ ]
21
+ },
22
+ "setup": [],
23
+ "dev": [],
24
+ "proxy": {
25
+ "enabled": true,
26
+ "host": "127.0.0.1"
27
+ },
28
+ "registry": ".spec-env/registry.json",
29
+ "branch": {
30
+ "pattern": "{type}/{slug}",
31
+ "identifierField": "jira_identifier"
32
+ },
33
+ "spec": {
34
+ "companionPaths": [
35
+ "specs/.core/jira-base/{identifier}.base.json"
36
+ ]
37
+ },
38
+ "baseBranch": "",
39
+ "guards": {
40
+ "refuseTeardownIfDirty": true,
41
+ "refuseTeardownIfUnpushed": true
42
+ },
43
+ "review": {
44
+ "reader": "detect",
45
+ "servePort": "auto",
46
+ "serve": "always",
47
+ "allowNetwork": true,
48
+ "allowRemote": false,
49
+ "commitWith": "/commit",
50
+ "required": true,
51
+ "reviewers": []
52
+ },
53
+ "live": {
54
+ "migrations": []
55
+ }
56
+ }