@erclx/canon 5.7.0 → 5.8.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/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
  [![CI status](https://img.shields.io/github/actions/workflow/status/erclx/canon/verify.yml?branch=main)](https://github.com/erclx/canon/actions/workflows/verify.yml)
12
12
  [![License MIT](https://img.shields.io/npm/l/@erclx/canon)](LICENSE)
13
13
 
14
- canon is a CLI and Claude Code plugin that stops your AI conventions drifting apart across repositories. It keeps one authoritative copy and installs it into each project on demand.
14
+ canon is a CLI and Claude Code plugin that runs your agent's work from plan to merge, holds it to the practices that make code worth keeping, and installs the same setup in every repository.
15
15
 
16
16
  **[See it run at canon.erclx.dev](https://canon.erclx.dev)**
17
17
 
@@ -21,7 +21,9 @@ That page is one real session the toolkit ran on itself, and every count on it i
21
21
 
22
22
  ## Why
23
23
 
24
- If you work across more than one repository and your AI setup has started to drift between them, this is for you. Every AI coding setup accumulates the same assets. Prompts to reuse, rules agents should follow, slash commands, skills, seed docs, sync scripts. Once you have enough projects, your copies drift and your agents stop getting consistent signals.
24
+ If an agent writes code for you in more than one repository, this is for you. canon runs that work from plan to merge. A planner writes the plan, a worker builds it on its own branch, and a separate session reviews the pull request. It holds the work to a practice, with rules that load when a matching path is edited and checks that fail when a rule is ignored. Both run on one setup, installed the same way in every repository you keep.
25
+
26
+ That shared setup is what the rest of this page describes, and it is the part that drifts without a source. Every AI coding setup accumulates the same assets. Prompts to reuse, rules agents should follow, slash commands, skills, seed docs, sync scripts. Once you have enough projects, your copies drift and your agents stop getting consistent signals.
25
27
 
26
28
  Three design choices shape the toolkit.
27
29
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "canon",
3
- "description": "Automated governance, versioning, and discovery tools for Claude Code.",
4
- "version": "5.7.0",
3
+ "description": "Runs your agent's work and keeps its code good, on one setup for every repo.",
4
+ "version": "5.8.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -143,6 +143,8 @@ Invoke `canon:git-ship`. That body owns the sequence, being the verify gate, mem
143
143
 
144
144
  One thing this chain adds. Mark the pull request as a draft as soon as `git-ship`'s pull request step returns, ahead of its CI watch, naming the number that step returned rather than one resolved by branch. Read `${CLAUDE_SKILL_DIR}/references/draft-mark.md` on reaching that point for the one command that proves the target, marks it, and reads the flag back. A `false` read stops the chain, and never re-issue the mark on it.
145
145
 
146
+ Emit the Output block on the wake after `git-ship`'s background CI watch exits on `passing`, not when the turn ends on its watch line.
147
+
146
148
  `git-ship` verifies again at its own gate, repeating Step 3 on a clean run. Keep that repeat, since a resumed run enters at that gate.
147
149
 
148
150
  ## Output
@@ -29,6 +29,7 @@ Without this skill, a session facing a decision nobody can settle from a diff:
29
29
  - Judges a lower layer through a finished higher one, so an operator asked about composition looks at the palette instead and answers about that.
30
30
  - Hands over the combined page alone for a decision about layout, where every arm sits in a column while the media queries answer to the whole window, so no arm is ever seen at its own viewport and a fault that appears only narrower survives the pick.
31
31
  - Rebuilds a navigation harness by hand on a run with several rounds, since each round writes its own folder and nothing reaches all of them from one address. Two design tracks hand-wrote the same page picker and theme toggle.
32
+ - Puts the pick question with no address in its output, after capturing the page and reading the image back. The operator then has to ask for a served page before they can look, so the question goes unanswered or gets answered blind.
32
33
  - Sets the theme on an arm that persists its own and reads it back on load, so the control reports one theme while the arm renders the other and the set is compared across two.
33
34
 
34
35
  ## Must
@@ -49,7 +50,7 @@ Without this skill, a session facing a decision nobody can settle from a diff:
49
50
  - Apply the winning arm in one change.
50
51
  - Capture every arm from the final round as an image before deleting the run's scratch folder, rather than discarding the losing arms with it.
51
52
  - Resolve the archival capture's destination against the main worktree root, never against a linked worktree the run happens to be building in.
52
- - Serve the candidates live where the decision is one the operator has to drive, since a still cannot answer how a gesture feels or how a pace reads.
53
+ - Serve the candidates on every run, whatever the decision's layer, and emit the local link ahead of the first pick question, so the operator can open the page the question is about and a still is never the only address.
53
54
 
54
55
  ## Must not
55
56
 
@@ -45,17 +45,19 @@ Write every arm side by side on one self-contained HTML page at `<dest>/candidat
45
45
 
46
46
  ## Step 3: render and hand off
47
47
 
48
- Render the page, then look at what came back:
48
+ Serve the page first, then render it and look at what came back. Every run serves, whatever the decision's layer, since a pick asked about a page the operator cannot open is a question nobody can answer.
49
49
 
50
50
  ```bash
51
+ canon serve <dest> --entry candidates.html --json
51
52
  canon capture <dest>/candidates.html --selector <element>
52
53
  ```
53
54
 
55
+ - Start `canon serve` in the background and read the link off its record, never assuming a port, since the printed link opens `index.html` without `--entry` and `<dest>` holds no such file. Keep each server up through the loop and note its process, since Step 5 re-enters this step and Step 6 stops them all. Serving needs no browser, so the link exists the moment the page is written.
56
+ - Where Step 2 wrote a frame, hand over the frame's link instead, served from the parent folder as Step 2 says, and let no earlier round's `<dest>`-only link stand as the address a later round cites.
54
57
  - `--selector` has no default and the command refuses without it. Name the element wrapping the arms rather than `body`, which crops to whatever the page's own margins leave.
55
- - `canon capture` and `canon drive` both need a browser binary the toolkit does not install. When either refuses for that reason, report the refusal and name `bunx playwright install chromium` as the repair, then stop rather than describing an arm nobody has seen.
56
- - Serve the page instead of capturing it when the operator has to drive the decision, such as a hover response, a scroll-linked position, or a pace. Start `canon serve <dest> --entry candidates.html` in the background and read the link off its record, since the printed link opens `index.html` without that flag and `<dest>` holds no such file. A still answers how a thing looks and answers none of those.
58
+ - `canon capture` and `canon drive` both need a browser binary the toolkit does not install. When either refuses for that reason, report the refusal and name `bunx playwright install chromium` as the repair, stop every server Step 3 started, then stop rather than describing an arm nobody has seen.
57
59
  - Write the render inside the record that cites it wherever one exists, by pointing `--out` at `<dest>/renders/` rather than at a session scratch path. A pick taken from an image the record does not hold is a judgment nobody but this session can check, and the archival capture in Step 6 covers the final round alone.
58
- - Hand over the address rather than a description. Emit the PNG path on its own line, and the link beside it where the page is served.
60
+ - Hand over the address rather than a description. Emit the link first on its own line, then the PNG path on its own line. A still answers how a thing looks and answers none of a hover response, a scroll-linked position, or a pace, which the served page does.
59
61
  - Never report a visual result you have not looked at. A claim about appearance with no render behind it is a guess.
60
62
  - Look to judge rather than to confirm. Reading the image back to check it rendered satisfies the rule above and still hands over weak work, so name the weakest thing on the page in a sentence. Where that sentence would embarrass the work, fix it and hand over the second version. Say the remaining weakness out loud either way, so the operator is not hunting for what you already know.
61
63
 
@@ -63,6 +65,7 @@ canon capture <dest>/candidates.html --selector <element>
63
65
 
64
66
  Put the choice to the operator through the structured question surface, since a call the operator's preference decides always routes through it rather than through prose.
65
67
 
68
+ - Emit the link in the response text ahead of the question call, and name it again in the question's own text, since some surfaces render the question card alone. A question that goes out with no address is one the operator cannot answer by looking.
66
69
  - One option per arm, labeled with the arm's id and carrying its cost as the description.
67
70
  - Rank the recommendation first and mark it `(Recommended)`.
68
71
  - Author the real arms only. The surface appends its own escapes for a free-text answer and for reopening the question, so writing either as an option ships a duplicate the tool rejects.
@@ -82,8 +85,8 @@ Put the choice to the operator through the structured question surface, since a
82
85
  2. Close out whatever document stated the decision as open, in the same change, naming the arm that won and the ones that stayed defensible. A pick that changes a surface and records nothing about why leaves the next reader to re-derive it from a diff. Skip this where nothing stated the decision.
83
86
  3. Batch-capture the final round's arm files, when `<dest>` is the scratch path: `canon capture <dest>/arms --selector <wrapper-class> --out <archive-dir>`, naming Step 2's chosen class. This is the directory-batch convention `draft-identity` Step 6 already uses.
84
87
  4. Resolve `<archive-dir>` as `.canon/picks/<slug>/` against the main worktree root, since shared session scratch resolves there rather than against a linked worktree this run happens to be building in. The capture is what keeps every arm past the pick, the losing ones included, as a durable revert record distinct from the live comparison page.
85
- 5. Delete `<dest>` and every file inside it, when `<dest>` is the scratch path, now that every arm sits at the durable path above. A variant left behind there is a second design nobody maintains.
86
- 6. Leave `<dest>` in place when it is a live track's `evidence/<slug>/`: `plan-groundwork`'s write scope treats evidence as durable rather than as scratch a session may delete, and the arms already sit at a durable path there.
88
+ 5. Stop every background server Step 3 started, one per round where the loop re-entered it, then delete `<dest>` and every file inside it, when `<dest>` is the scratch path, now that every arm sits at the durable path above. A variant left behind there is a second design nobody maintains, and a server left running would serve a folder that no longer exists.
89
+ 6. Leave `<dest>` in place when it is a live track's `evidence/<slug>/`: `plan-groundwork`'s write scope treats evidence as durable rather than as scratch a session may delete, and the arms already sit at a durable path there. Stop every server there too.
87
90
  7. Report `<dest>` as still standing when the scratch-path delete is refused, naming the path for the operator to remove, rather than closing on a report the tree contradicts. The pick is applied either way, so the run has done its work and the folder is what outlives it.
88
91
  8. Report every surface that changed, each on its own line, name the arm that won by its id and its cost, and report the archival path from Step 3 where it ran.
89
92
 
@@ -20,7 +20,7 @@ The sequence also reaches the remote on work nothing re-checked. This skill is t
20
20
  - Emit no text between steps. The sequence is the unit and prose inside it reopens settled decisions.
21
21
  - Name the one point a wrapping caller may act at, so a chain built on this one is not left to pick a gap of its own
22
22
  - Name the condition under which the closing block is not emitted, since a caller that closes on its own block leaves two instructions about the last line and nothing deciding between them
23
- - Watch continuous integration to a terminal state, and stop on a failure naming the check
23
+ - Watch continuous integration to a terminal state in the background, so the session stays reachable while CI runs, and stop on a failure naming the check
24
24
  - Stop memory work at the Propose phase
25
25
 
26
26
  ## Must not
@@ -30,11 +30,12 @@ The sequence also reaches the remote on work nothing re-checked. This skill is t
30
30
  - Run the memory Apply phase. Promoting an entry changes how the agent operates and ships as its own change.
31
31
  - Attempt a fix for a failing verify. The user is already making one, which is what brought the run back here.
32
32
  - Implement or review. This chain starts from work already believed done.
33
+ - Report the chain done before the CI watch exits. A turn ending on the watch line leaves a pull request still waiting on CI, and a `✅ Shipped` there claims a result nothing has read.
33
34
 
34
35
  ## Guards
35
36
 
36
37
  - A failing verify stops the run before the sync skills write, so the tree is left as the user left it
37
- - A failing check stops the sequence. This is the one place text is allowed between steps.
38
+ - A failing check stops the sequence. A stop report and the line announcing the background watch are the two places text is allowed between steps.
38
39
 
39
40
  ## Out of scope
40
41
 
@@ -7,7 +7,7 @@ description: Runs the full post-feature workflow by syncing docs, staging commit
7
7
 
8
8
  Run the full post-feature workflow by invoking each skill in sequence using the Skill tool. After each skill returns, invoke the next step immediately in the same response.
9
9
 
10
- Do not output any text between steps and do not wait for user input. Tool permission dialogs are the only interrupts allowed. The final output is `✅ Shipped`, unless a wrapping caller states it closes on its own block, which `auto-ship` does.
10
+ Do not output any text between steps and do not wait for user input. Tool permission dialogs are the only interrupts allowed. The final output is `✅ Shipped`, emitted on the wake after the step 9 watch exits on `passing` rather than when the turn ends on the watch line, unless a wrapping caller states it closes on its own block, which `auto-ship` does.
11
11
 
12
12
  ## Verify
13
13
 
@@ -31,10 +31,33 @@ Run `git diff --cached --name-only 2>/dev/null` to check for staged files. If ou
31
31
  6. Invoke `canon:git-stage` to group staged changes and commit by concern
32
32
  7. Invoke `canon:git-branch` to rename branch to match conventional format
33
33
  8. Invoke `canon:git-pr` to push branch and open pull request
34
- 9. After the PR opens, watch CI. Poll `canon pr checks <number> --json` until the record's `state` leaves `pending` or the record carries `conflicted: true`, branching on those fields rather than on the exit, and fall back to `gh pr checks <number>` when no record comes back at all, which is a target whose CLI predates the verb. On `passing`, continue. On `failing`, stop the sequence and report the failing check with its URL. On `conflicted`, stop the sequence and report that the branch conflicts with its base, since no run will start for it and polling on never ends. A rebase is the repair. Do not auto-fix. This step may output on failure, the one exception to the no-text-between-steps rule.
34
+ 9. After the PR opens, watch CI in the background, so the session stays reachable while CI runs. Read `### Watching CI` below for the commands and the wake.
35
35
 
36
36
  A caller wrapping this sequence may act between step 8 and step 9, which is the one gap the order leaves open, since the pull request exists there and nothing has read its checks yet. `auto-ship` marks the pull request draft in it. Nothing else may go there, and a caller that needs a step anywhere else in the sequence is asking for a change to this body rather than for a place to stand.
37
37
 
38
+ ### Watching CI
39
+
40
+ Run `canon pr checks <number> --json` once in the foreground. When no record comes back at all, the target's CLI predates the verb, and the watch command is `gh pr checks <number> --watch`. The loop below would never exit on such a target, since an empty read never satisfies its test.
41
+
42
+ Otherwise start this loop through the Bash tool with `run_in_background` and a 60-minute timeout:
43
+
44
+ ```bash
45
+ until canon pr checks <number> --json | jq -e '.state != "pending" or .conflicted == true' >/dev/null; do sleep 30; done; canon pr checks <number> --json
46
+ ```
47
+
48
+ Emit `⏳ Watching CI on #<number> in the background` and end the turn. Print nothing more while the watch runs. The session wakes when the command exits, and the wake resumes this step rather than restarting the sequence at step 1. A wake that lands while the session is inside another skill, such as a `review-address` pass a message started during the watch, goes to whichever step is waiting on CI.
49
+
50
+ On the wake, branch on the final record's fields rather than on the exit:
51
+
52
+ - `state: passing`: continue to After completion.
53
+ - `state: failing`: stop the sequence and report the failing check with its URL.
54
+ - `conflicted: true`: stop the sequence and report that the branch conflicts with its base, since no run will start for it. A rebase is the repair.
55
+ - A `reason` and no `state`: the read was refused, such as `no-remote-branch` for a branch deleted on the remote. Stop and report that `reason`. A missing `state` is never a pass.
56
+ - The command hit its timeout and printed no final record: report CI unsettled after 60 minutes, and emit no `✅ Shipped`.
57
+ - The `gh pr checks --watch` fallback ran, which prints a check table rather than a record. Only a printed table carrying at least one check, every one passing, continues to After completion. Any failing check stops the sequence naming it with its URL. Empty or errored output stops the sequence and reports the message, since `no checks reported on the '<branch>' branch` is what a conflicted branch prints, and that fallback has no `conflicted` field to separate it from lag.
58
+
59
+ Do not auto-fix any of these. The watch line and a stop report from any branch above are the two exceptions to the no-text-between-steps rule.
60
+
38
61
  ### Why the reach reads at step 5
39
62
 
40
63
  The branch is whole there and nowhere earlier. Steps 2 and 3 write past whatever the plan declared, so a reading taken ahead of them misses the chain's own additions, and no pull request exists until step 8 to carry the answer anywhere.
@@ -170,10 +170,12 @@ echo "${comment_url##*issuecomment-}" > .canon/tmp/pr/reply/reply-<number>.id
170
170
 
171
171
  ## Step 7: confirm resolution
172
172
 
173
- After the follow-up push, watch CI on the PR. Poll
174
- `canon pr checks <number> --json` until the record's `state` leaves `pending`
175
- or it carries `conflicted: true`, then read it. On `conflicted`, stop and report
176
- that a rebase is the repair rather than polling on. When every finding is addressed and the state is `passing`, append
173
+ After the push, read Watching CI in `${CLAUDE_SKILL_DIR}/../git-ship/SKILL.md`
174
+ without invoking that skill, start its background watch, end the turn on its
175
+ line, and run the rest of this step and Step 8 on the wake. Reuse a watch
176
+ already running on this PR, since it reads the new tip. On the wake, stop on `conflicted` naming a rebase, on a `reason` naming it,
177
+ and on a timeout reporting CI unsettled. When every finding is addressed and
178
+ the state is `passing`, append
177
179
  the closing confirmation to the reply file Step 6 already posted, so the thread
178
180
  carries one terminal state rather than a second comment under no heading:
179
181
 
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@erclx/canon",
3
3
  "type": "module",
4
- "version": "5.7.0",
5
- "description": "Infrastructure and quality tooling for developer workflows",
4
+ "version": "5.8.0",
5
+ "description": "Runs your agent's work and keeps its code good, on one setup for every repo.",
6
6
  "license": "MIT",
7
7
  "bin": {
8
8
  "canon": "./src/cli.ts"