@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.
- package/MIGRATION.md +1266 -0
- package/README.md +44 -2
- package/assets/claude-md-section.md +85 -0
- package/assets/commands/allow-main.md +33 -0
- package/assets/commands/spec-connect.md +13 -0
- package/assets/commands/spec-live.md +14 -0
- package/assets/commands/spec-remote-review.md +37 -0
- package/assets/commands/spec-skip.md +24 -0
- package/assets/core/SETUP.md +192 -0
- package/assets/core/env.config.json.example +56 -0
- package/assets/core/env.config.md +606 -0
- package/assets/core/gating.config.json.example +4 -0
- package/assets/core/gating.config.md +81 -0
- package/assets/core/jira.config.json.example +18 -0
- package/assets/core/jira.config.md +88 -0
- package/assets/hooks/main-guard.cjs +162 -0
- package/assets/hooks/review-gate.cjs +146 -0
- package/assets/review/page.html +3384 -0
- package/assets/rules/commit-trailers.md +96 -0
- package/assets/rules/negative-checks.md +69 -0
- package/assets/rules/offered-unblocks.md +100 -0
- package/assets/rules/spec-planning.md +702 -0
- package/assets/rules/spec-reports.md +572 -0
- package/assets/skills/no-spec/SKILL.md +249 -0
- package/assets/skills/spec/SKILL.md +714 -0
- package/assets/skills/spec-bug/SKILL.md +585 -0
- package/assets/skills/spec-cancel/SKILL.md +261 -0
- package/assets/skills/spec-complete/SKILL.md +451 -0
- package/assets/skills/spec-diff/SKILL.md +870 -0
- package/assets/skills/spec-hotfix/SKILL.md +607 -0
- package/assets/skills/spec-init/SKILL.md +178 -0
- package/assets/skills/spec-jira-setup/SKILL.md +221 -0
- package/assets/skills/spec-list/SKILL.md +173 -0
- package/assets/skills/spec-next/SKILL.md +678 -0
- package/assets/skills/spec-push/SKILL.md +306 -0
- package/assets/skills/spec-review/SKILL.md +256 -0
- package/assets/skills/spec-reviewed/SKILL.md +284 -0
- package/assets/skills/spec-start/SKILL.md +496 -0
- package/assets/skills/spec-status/SKILL.md +114 -0
- package/assets/skills/spec-sync/SKILL.md +220 -0
- package/assets/skills/spec-to-main/SKILL.md +141 -0
- package/bin/skitterspec-jira.js +68 -0
- package/package.json +40 -4
- package/src/cli.js +5967 -0
- package/src/deprecate.js +138 -0
- package/src/env/bootstrap.js +188 -0
- package/src/env/building.js +143 -0
- package/src/env/classify.js +176 -0
- package/src/env/commitcmd.js +108 -0
- package/src/env/config.js +850 -0
- package/src/env/dev.js +63 -0
- package/src/env/glob.js +34 -0
- package/src/env/hooks.js +226 -0
- package/src/env/hotfix.js +413 -0
- package/src/env/hotfixland.js +302 -0
- package/src/env/integrate.js +167 -0
- package/src/env/live.js +479 -0
- package/src/env/mainguard.js +299 -0
- package/src/env/provision.js +466 -0
- package/src/env/proxy.js +225 -0
- package/src/env/prune.js +119 -0
- package/src/env/registry.js +159 -0
- package/src/env/render.js +17 -0
- package/src/env/resolve.js +716 -0
- package/src/env/review.js +2622 -0
- package/src/env/reviewers/coderabbit.js +228 -0
- package/src/env/reviewers.js +517 -0
- package/src/env/serve.js +1044 -0
- package/src/env/supervise.js +157 -0
- package/src/env/teardown.js +248 -0
- package/src/env/trust.js +87 -0
- package/src/gating.js +155 -0
- package/src/init.js +1150 -0
- package/src/lines-diff.js +114 -0
- package/src/prompts.js +129 -0
- package/src/vendor/jira/adf.js +414 -0
- package/src/vendor/jira/api.js +295 -0
- package/src/vendor/jira/cli-sync.js +1252 -0
- package/src/vendor/jira/commands.js +44 -0
- package/src/vendor/jira/config.js +286 -0
- package/src/vendor/jira/credentials.js +108 -0
- package/src/vendor/jira/dialect.js +45 -0
- package/src/vendor/jira/mcp.js +129 -0
- package/src/vendor/provider-kit/index.js +41 -0
- package/src/vendor/provider-kit/src/adapter.js +23 -0
- package/src/vendor/provider-kit/src/apply.js +207 -0
- package/src/vendor/provider-kit/src/args.js +68 -0
- package/src/vendor/provider-kit/src/assignment.js +277 -0
- package/src/vendor/provider-kit/src/config.js +251 -0
- package/src/vendor/provider-kit/src/credentials.js +404 -0
- package/src/vendor/provider-kit/src/doctor.js +35 -0
- package/src/vendor/provider-kit/src/http.js +63 -0
- package/src/vendor/provider-kit/src/identity.js +93 -0
- package/src/vendor/provider-kit/src/list-assignee.js +71 -0
- package/src/vendor/provider-kit/src/list-rows.js +35 -0
- package/src/vendor/provider-kit/src/md-compare.js +39 -0
- package/src/vendor/provider-kit/src/md-tables.js +94 -0
- package/src/vendor/provider-kit/src/prompt.js +50 -0
- package/src/vendor/provider-kit/src/released.js +219 -0
- package/src/vendor/provider-kit/src/specs.js +248 -0
- package/src/vendor/provider-kit/src/transport.js +22 -0
- package/src/vendor/sync-core/index.js +92 -0
- package/src/vendor/sync-core/src/base.js +81 -0
- package/src/vendor/sync-core/src/compare.js +338 -0
- package/src/vendor/sync-core/src/dialect.js +51 -0
- package/src/vendor/sync-core/src/fields.js +36 -0
- package/src/vendor/sync-core/src/legacy.js +100 -0
- package/src/vendor/sync-core/src/normalize.js +971 -0
- package/src/vendor/sync-core/src/push.js +85 -0
- package/src/vendor/sync-core/src/retarget.js +278 -0
- package/src/vendor/sync-core/src/task-block.js +308 -0
- package/src/vendor/sync-core/src/verify.js +67 -0
- package/src/vendor/sync-core/src/write.js +191 -0
package/README.md
CHANGED
|
@@ -1,3 +1,45 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @skitterbyte/skitterspec-jira
|
|
2
2
|
|
|
3
|
-
|
|
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
|
+
}
|