@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
@@ -0,0 +1,96 @@
1
+ # Commit Trailers
2
+
3
+ Extends `commit-messages.md` — the format, length limits and `Release-Note:`
4
+ grammar there all still apply. This file adds one trailer, and it is only
5
+ installed when a ticketing provider is.
6
+
7
+ ## `Refs:` — the ticket this commit belongs to
8
+
9
+ A commit carries the Jira issue **its own changes** belong to, as the **last**
10
+ trailer in the message:
11
+
12
+ ```
13
+ fix(sync): verify no longer flags every update
14
+
15
+ - Key the apply read-back by ref, resolving id via the projection
16
+
17
+ Release-Note: Pushing a spec whose phases already exist in Jira no
18
+ longer reports every one of them as a possible stale reference.
19
+
20
+ Refs: PROJ-29
21
+ ```
22
+
23
+ Get the value from the engine rather than reading it off a spec by hand:
24
+
25
+ ```
26
+ pnpm exec skitterspec-jira spec-sync ref
27
+ ```
28
+
29
+ Bare, that answers **from the branch** — correct whenever you are committing
30
+ that branch's own implementation work, which is nearly always.
31
+
32
+ ## When the branch and the commit disagree
33
+
34
+ The branch is a proxy for "what this commit is about", and it is a good one
35
+ right up until you commit something that is not this branch's work.
36
+
37
+ **Where isolation is configured, `/spec` closes the usual way in.** It
38
+ provisions the new spec's own `--docs` worktree before writing a line of it and
39
+ moves the session there, so the spec commit happens on the new spec's branch —
40
+ and the **bare** command is then correct, because the branch and the commit's
41
+ subject are the same spec. This is the normal path, and it needs nothing typed.
42
+
43
+ **Without isolation the split is still real.** Part-way through a spec a design
44
+ question warrants its own spec, `/spec` writes it into `specs/backlog/` on the
45
+ branch you are standing on, and that commit is **entirely the new spec's** while
46
+ the bare command answers with the old spec's ticket.
47
+
48
+ Name the spec instead — still the engine, never a hand-written id:
49
+
50
+ ```
51
+ pnpm exec skitterspec-jira spec-sync ref feat-distance-prescriptions
52
+ ```
53
+
54
+ The branch is not consulted, so this works from `main` too. An unknown spec name
55
+ fails rather than falling back to the branch, so a typo can't become a
56
+ confidently wrong ref.
57
+
58
+ **A spec in ANOTHER spec's worktree is still the thing to avoid**, and the fix
59
+ is no longer "author from the base branch" — it is the provision above, which
60
+ moves the session out of that tree and into the new spec's own. What was always
61
+ true of the accident stays true: a spec written inside another spec's worktree
62
+ physically lives on that branch, so it is not on `main` until that spec lands,
63
+ and it is cancelled along with it.
64
+
65
+ ## Rules
66
+
67
+ - **The ref names the commit's subject, not your location.** If they disagree,
68
+ the commit's subject wins — resolve it with `spec-sync ref <spec>`.
69
+ - **Omit the trailer entirely when there is no ref.** `spec-sync ref` exits
70
+ non-zero and prints nothing on a commit that has no ticket — on `main`, or on
71
+ a spec kept deliberately local. Never invent one, and never write
72
+ `Refs: none`: the release report counts unreferenced commits, and a fabricated
73
+ ref is worse than an honest gap.
74
+ - **One ref per commit.** A commit belongs to one spec. If work genuinely spans
75
+ two tickets, that is two commits — and that is the fix when a single commit
76
+ would mix a branch's own work with another spec's files.
77
+ - **Never use Jira's smart-commit commands** — `#comment`, `#time`,
78
+ `#<transition>` (`#done`, `#in-progress`, …). Those act on the issue the
79
+ moment the commit is scanned — posting comments, logging time, transitioning
80
+ the ticket — which is wrong here: a ticket moves when its work is
81
+ **released**, not when it merges. `Refs:` is deliberately inert to Jira's
82
+ automation.
83
+ - **Blank line before it**, like `Release-Note:` — it is a git trailer, not body
84
+ text.
85
+ - Put it **after** `Release-Note:` when both are present, so the human-facing
86
+ note reads first.
87
+
88
+ ## Why it exists
89
+
90
+ The repo lands specs with `merge --ff-only`, so history is linear and branch
91
+ names never reach it. The commit message is the only artefact that survives into
92
+ the range a release scans, which is what lets
93
+ `spec-sync released <range>` report the tickets a release contains. A ref that
94
+ names the wrong ticket does not merely lose information — it moves a commit onto
95
+ another ticket's release report, which is worse than the gap it was meant to
96
+ close.
@@ -0,0 +1,69 @@
1
+ # Checks That Accuse
2
+
3
+ A check **accuses** when being wrong costs something: it deletes, it exits
4
+ non-zero, or it tells the user their code is broken. Those checks earn the four
5
+ rules below. An ordinary conditional does not — this is about the ones that act
6
+ on what they conclude.
7
+
8
+ Almost every accusation begins as an **absence**: a name not in a list, a
9
+ directory not on disk, a version not in a response. An absence is evidence only
10
+ once you have established that the lookup could have seen the thing. Three
11
+ times in one day, in unrelated code, we established nothing and acted anyway:
12
+
13
+ | Absence observed | Concluded | What had blinded the lookup |
14
+ |------------------|-----------|-----------------------------|
15
+ | ref not in the issue list | "it does not exist" | the query excluded archived issues, and capped at 250 |
16
+ | version not in the registry's list | "the publish failed" | the registry is eventually consistent |
17
+ | lifecycle folder not on disk | "half-installed" | git does not store an empty directory |
18
+
19
+ The bills: 146 healthy refs accused, a valid release tag deleted, a non-zero
20
+ exit on a healthy repo. Each blind spot was knowable in advance.
21
+
22
+ ## 1. Prefer a positive signal to an absence
23
+
24
+ Assert something that must be **present**, not something that must not be
25
+ missing. A positive signal fails loudly when you are wrong about it; an absence
26
+ fails silently whenever the lookup was narrower than you assumed.
27
+
28
+ The scaffold check above stopped asking "is the lifecycle folder there?" — a
29
+ folder git drops as soon as it empties — and started asking whether the config
30
+ folder the installer always writes into is there. Same intent, and the new
31
+ question has an answer.
32
+
33
+ Where no positive signal exists, widen the lookup until absence means
34
+ something (include the archived rows, ask the API for the one id rather than
35
+ scanning a page) — or do not conclude.
36
+
37
+ ## 2. Name the blind spot beside the check
38
+
39
+ A comment naming what could make this lookup lie is what makes the next reader
40
+ check it. Not what the code does — what would fool it:
41
+
42
+ ```js
43
+ // A LIFECYCLE FOLDER IS NOT CHECKED, deliberately. git does not track empty
44
+ // directories, so it disappears whenever the bucket empties and returns the
45
+ // moment something lands in it.
46
+ ```
47
+
48
+ Write it when you write the check, while you still know why it is safe.
49
+
50
+ ## 3. Pair every accusation with a stays-silent test
51
+
52
+ For each accusing check, a test that feeds it a **healthy but unusual** input
53
+ and asserts it says nothing: the empty bucket, the archived record, the
54
+ just-published version, the file the user edited on purpose. The positive test
55
+ proves the check can fire; only this one proves it does not fire at everyone
56
+ else. All three incidents would have been caught by it, and prose alone had
57
+ already failed to prevent them.
58
+
59
+ ## 4. Bias the unknown case toward inaction
60
+
61
+ Three states, not two: yes, no, and *cannot tell*. Route the third to the
62
+ harmless branch — skip, warn, keep, retry — never to the destructive one.
63
+
64
+ The install manifest classifies a file whose hash it does not recognise as
65
+ `customized` rather than stale, so a resync **keeps** it (`managedState`,
66
+ `packages/common/src/init.js`). An unrecognised hash could mean a user's edit or
67
+ a lost manifest; only one of those readings is safe to act on, so it takes that
68
+ one. Being wrong there costs a redundant file on disk. The opposite default
69
+ costs the user their work.
@@ -0,0 +1,100 @@
1
+ # Offered Unblocks
2
+
3
+ A refusal says what is wrong. Some refusals can also say **what would fix it**,
4
+ and where the engine declares that, a skill may offer it rather than leaving the
5
+ operator at a dead end.
6
+
7
+ This file is the whole contract. A skill's own text says only *that* it acts on
8
+ an offer, never how.
9
+
10
+ ## The engine declares; the skill relays
11
+
12
+ An offer is a field on a refusal, never a judgement a caller makes from the
13
+ reason text:
14
+
15
+ ```json
16
+ {
17
+ "blocked": true,
18
+ "reason": "feat-x's worktree has uncommitted changes — commit or stash them…",
19
+ "offer": { "kind": "satisfy", "label": "Commit first, then go live", "command": "/commit" }
20
+ }
21
+ ```
22
+
23
+ **Absent stays absent.** Most refusals carry no `offer` at all, and that is the
24
+ correct, common answer. Where there is none, relay the refusal and stop.
25
+
26
+ **Never infer one.** If the engine did not declare an offer, there is not one —
27
+ however obviously the reason seems to name a fix. The boundary that matters most
28
+ lives in that decision, and it must not be re-litigated per skill:
29
+ **work that is not the operator's is never offered up.** A live workbench another
30
+ spec holds is that spec's operator's to free, so it declares nothing, and a
31
+ helpful picker is exactly how that would creep back in.
32
+
33
+ ## Two kinds
34
+
35
+ | `kind` | Means | Recorded |
36
+ |--------|-------|----------|
37
+ | `satisfy` | do what the guard asked for | no — nothing was weakened |
38
+ | `bypass` | step past a guard still unsatisfied | **yes** |
39
+
40
+ ## Relay first, then offer
41
+
42
+ In that order, and never instead. A picker that replaced the refusal would hide
43
+ what is wrong behind a choice about what to do next — and the refusal text is
44
+ where every exit is named, including the ones the picker does not offer.
45
+
46
+ Then, only where `offer` is present, put **two** options to the operator: the
47
+ offer's `label`, and Cancel.
48
+
49
+ - **`satisfy`** — run the command, then retry the original operation.
50
+ - **`bypass`** — run the command, then carry on.
51
+ - **Cancel** — stop. Record nothing: declining to bypass is not a decision
52
+ anyone needs to audit.
53
+
54
+ **The label is the engine's, shown verbatim.** Never reword a bypass into
55
+ something easier-sounding, never pre-select it, and never add a third option. A
56
+ bypass that arrives pre-selected is a bypass by default wearing a question mark.
57
+
58
+ ## A bypass is offered once per obligation
59
+
60
+ The engine enforces this; a skill does not count anything. Spend the offer by
61
+ recording it **when you raise the picker**:
62
+
63
+ ```
64
+ skitterspec spec-env review gate <spec> --offered
65
+ ```
66
+
67
+ After that the gate declares no offer for the same arming, so the next refused
68
+ commit is a plain refusal with its exits named in the text. A new phase ending
69
+ arms the gate again and is asked in its own right.
70
+
71
+ **Spent by being made, not by being taken.** An operator who declines still saw
72
+ it, and re-offering on every attempt is how a deliberate step becomes the
73
+ default.
74
+
75
+ **Do not spend it by reading.** `review gate --json` is asked on every
76
+ `/spec-next` run; only raising the picker spends the offer.
77
+
78
+ ## What this is not
79
+
80
+ **Not a `--force`.** One was asked for and rejected: a reasonless lift is
81
+ indistinguishable from nobody having looked, where
82
+ `none: additive, nothing to revert` is a decision a reviewer can argue with. The
83
+ gate's exits are unchanged — a committing verdict, or a recorded skip — and a
84
+ bypass is the second of those at an address a person will reach.
85
+
86
+ **Not a replacement for the refusal.** A picker exists only in a harness. The
87
+ engine and the `PreToolUse` hook keep refusing regardless, so this is a layer on
88
+ top of a guard and never the guard itself.
89
+
90
+ **Not Claude lifting a guard aimed at Claude.** `spec-planning.md` draws that
91
+ line for `/allow-main` and `/spec-skip`, and a bypass offer survives it because
92
+ three things hold at once, where a `--force` had none of them:
93
+
94
+ 1. **The operator answers.** Claude may raise the question and cannot answer it.
95
+ 2. **The choice is recorded** — a fixed reason, distinct from anything typed,
96
+ which shows on the next render as history.
97
+ 3. **It is offered once**, held by the sidecar rather than by prose, so it
98
+ survives a `/clear`, a compaction and a fresh session.
99
+
100
+ Remove any one of those and the objection returns.