@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
|
@@ -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.
|