@erclx/canon 5.57.0 → 5.59.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/CHANGELOG.md +29 -0
- package/README.md +1 -1
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/context-fold/references/context.md +1 -1
- package/claude/skills/document-health/REQUIREMENT.md +2 -2
- package/claude/skills/document-health/SKILL.md +14 -0
- package/claude/skills/document-health/references/axes.md +12 -0
- package/claude/skills/document-repair/REQUIREMENT.md +6 -1
- package/claude/skills/document-repair/SKILL.md +21 -0
- package/claude/skills/plan-feature/SKILL.md +1 -1
- package/claude/skills/target-update/SKILL.md +5 -1
- package/docs/agents/context-audit.md +1 -1
- package/docs/agents/install-and-sync.md +1 -1
- package/docs/agents/migrate.md +6 -0
- package/docs/target/sync.md +3 -1
- package/docs/workflow/operating-model.md +1 -1
- package/governance/rules/code/routing.md +5 -1
- package/governance/rules/ui/design-taste.md +4 -0
- package/package.json +3 -3
- package/scripts/core/bootstrap.ts +120 -0
- package/scripts/core/install-check.ts +193 -0
- package/src/cli/retired.ts +63 -0
- package/src/cli/ui.ts +3 -5
- package/src/cli.ts +8 -2
- package/src/commands/gov.ts +8 -2
- package/src/commands/migrate.ts +7 -1
- package/src/commands/pr.ts +10 -1
- package/src/context/audit.ts +26 -1
- package/src/gov/adapter.ts +2 -0
- package/src/gov/coverage.ts +62 -0
- package/src/migrate/callers.ts +192 -0
- package/src/migrate/status.ts +11 -3
- package/src/pr/merge.ts +77 -1
- package/src/pr/rest.ts +1 -0
- package/src/sandbox/expect.ts +1 -1
- package/src/sync/engine.ts +84 -0
- package/src/sync/stamp.ts +51 -1
- package/scripts/core/bootstrap.sh +0 -86
- package/scripts/core/install-check.sh +0 -113
- package/scripts/lib/ui.sh +0 -45
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [5.59.0](https://github.com/erclx/canon/compare/v5.58.0...v5.59.0) (2026-10-24)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **cli:** name the release that retired a verb and find its callers ([#2662](https://github.com/erclx/canon/issues/2662)) ([32fddb5](https://github.com/erclx/canon/commit/32fddb5f8c363441fc18ffb4243c08a65a863a67))
|
|
9
|
+
* **gov:** report rule coverage after gov sync ([#2660](https://github.com/erclx/canon/issues/2660)) ([c087965](https://github.com/erclx/canon/commit/c087965acd64249f8dfbe9ca1d98a7857621d380))
|
|
10
|
+
* **pr:** compose bot pull request merge messages ([#2659](https://github.com/erclx/canon/issues/2659)) ([698991c](https://github.com/erclx/canon/commit/698991c71b6423279756f023c86cd2537ae46017))
|
|
11
|
+
* **sandbox:** add a canon-feedback-triage anchor scenario ([#2656](https://github.com/erclx/canon/issues/2656)) ([ae45573](https://github.com/erclx/canon/commit/ae45573e2b385b97cdfbf300b4d1f9bb0f3db839))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* **sync:** write the stamp in the formatter shape ([#2655](https://github.com/erclx/canon/issues/2655)) ([a34c5fb](https://github.com/erclx/canon/commit/a34c5fb2d18314ec31d83640be387057b68638a7))
|
|
17
|
+
|
|
18
|
+
## [5.58.0](https://github.com/erclx/canon/compare/v5.57.0...v5.58.0) (2026-10-24)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
* **governance:** widen design-taste paths to component files ([#2645](https://github.com/erclx/canon/issues/2645)) ([01b1445](https://github.com/erclx/canon/commit/01b144514eced1150c90b18acc78f0be33990a23))
|
|
24
|
+
* **rules:** route interface changes to api-design ([#2647](https://github.com/erclx/canon/issues/2647)) ([09a073c](https://github.com/erclx/canon/commit/09a073ca38324a399de5c2c2e702fbcede6bb38c))
|
|
25
|
+
* **skills:** offer a restructure before document-repair rewrites ([#2648](https://github.com/erclx/canon/issues/2648)) ([7c2776a](https://github.com/erclx/canon/commit/7c2776a95b86a6626c44e399056b9eaa22418d65))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Bug Fixes
|
|
29
|
+
|
|
30
|
+
* **context:** count table rows toward no folder paragraph ([#2646](https://github.com/erclx/canon/issues/2646)) ([a99b020](https://github.com/erclx/canon/commit/a99b02064105b81bc3a4f6e4820c73198942785e))
|
|
31
|
+
|
|
3
32
|
## [5.57.0](https://github.com/erclx/canon/compare/v5.56.0...v5.57.0) (2026-10-24)
|
|
4
33
|
|
|
5
34
|
|
package/README.md
CHANGED
|
@@ -140,7 +140,7 @@ Bun, Git, and a shell are required, and the GitHub CLI is optional.
|
|
|
140
140
|
- [Bun](https://bun.sh) for the CLI runtime and scripts
|
|
141
141
|
- [Git](https://git-scm.com) with worktree support
|
|
142
142
|
- [GitHub CLI](https://cli.github.com) (optional) for ship flows
|
|
143
|
-
- Shell: `zsh
|
|
143
|
+
- Shell: `zsh`, since bootstrap appends its aliases to `~/.zshrc`.
|
|
144
144
|
|
|
145
145
|
Clone the repo, then run the bootstrap script. It installs dependencies, links the CLI globally, and appends a marked block of Claude Code shell aliases to your `~/.zshrc`.
|
|
146
146
|
|
|
@@ -71,7 +71,7 @@ An entry carries three H2 sections and no other, being `Overview`, `Layout`, and
|
|
|
71
71
|
|
|
72
72
|
Layout gives each folder doing real work its own H3, titled with the folder path in a code span, such as ``### `src/design/` ``.
|
|
73
73
|
|
|
74
|
-
- Open the H3 with a one-paragraph lead stating what the folder does, then at most five bullets on what it does now
|
|
74
|
+
- Open the H3 with a one-paragraph lead stating what the folder does, then at most five bullets on what it does now. A table after the lead counts toward neither, with one sentence to lead the bullets
|
|
75
75
|
- Put every trivial folder under `### Other folders` as a one-line bullet in owns-X form
|
|
76
76
|
- Name folders only, never files, and write no decisions and no history under any H3
|
|
77
77
|
- Give each folder under the code roots one H3 or `Other folders` line, since a second entry may cite it but never claims it
|
|
@@ -13,8 +13,8 @@ Length, placement, and staleness answer to different evidence, and one list hand
|
|
|
13
13
|
|
|
14
14
|
## Must
|
|
15
15
|
|
|
16
|
-
- Report per document across the length, placement, and staleness axes, marking each finding with its evidence class
|
|
17
|
-
-
|
|
16
|
+
- Report per document across the length, placement, and staleness axes, marking each finding with its evidence class, read through the verb owning each measurement and its record's checkpoints
|
|
17
|
+
- Propose a split or an owner as a structure line for each overgrown entry and doubled or unnamed coverage finding, with the headings and the reason
|
|
18
18
|
- Ship the staleness axis as a reading rather than cutting it for lacking a verb
|
|
19
19
|
- Report a refusal, an absent key, or a missing subcommand as an unread axis naming the cause, and say which layer answered the classifier sweep
|
|
20
20
|
- Run standalone, read the requirements record for identity drift when the scope reaches it, and print the `canon records stale canonical` reminder
|
|
@@ -47,6 +47,12 @@ The first names the backend, the model, and the source that decided both. Report
|
|
|
47
47
|
|
|
48
48
|
Take the checkpoints from the record each verb returns rather than from a number written here. A checkpoint the CLI moves reaches this report with no edit, and a number held in this body goes stale on its own cadence.
|
|
49
49
|
|
|
50
|
+
### The structure line
|
|
51
|
+
|
|
52
|
+
The context audit record already carries what a restructure needs, so the placement axis gains a structure line per area folder and runs no further verb. `${CLAUDE_SKILL_DIR}/references/axes.md` holds how to read `length` and `coverage` into a proposed split or owner.
|
|
53
|
+
|
|
54
|
+
Emit the line only for a folder with an entry past the line checkpoint whose Layout headings group into two or more domains, or with a doubled or unnamed coverage finding. A folder with neither gets no line, and `document-repair` goes straight to its rewrite.
|
|
55
|
+
|
|
50
56
|
### An unread axis is not a healthy one
|
|
51
57
|
|
|
52
58
|
A refusal, an absent key, and a subcommand the project's binary does not carry each leave an axis unread. Report it as unread against the documents it would have covered, name the read that could not be taken, and name the cause.
|
|
@@ -106,4 +112,12 @@ When Step 3 read the canonical records, add one reminder line per doc the verb r
|
|
|
106
112
|
|
|
107
113
|
Write `reviewed value is not a date: <value>` for a doc carrying `invalidReviewed`, and `no release tags to count` in place of the count when `tagged` is false. Name the unread cause instead when the verb refused or the binary carries no `canonical` kind.
|
|
108
114
|
|
|
115
|
+
Under the table, add one structure line per area folder that earned one, naming the entry and the split, or the finding and its owner:
|
|
116
|
+
|
|
117
|
+
```markdown
|
|
118
|
+
Structure `canon/context/surfaces/`: `cli.md` (300 lines) splits into `cli.md` taking <headings> and `cli-<domain>.md` taking <headings>, because <reason>.
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The line is a proposal for the operator, with the headings each entry takes and why. Health applies nothing, and `document-repair` offers the proposal before it rewrites.
|
|
122
|
+
|
|
109
123
|
Keep the evidence line under the table on every run. A table read without it flattens a judgment into a measurement, which is the one shape the three classes exist to prevent.
|
|
@@ -54,6 +54,18 @@ The sweep classifies each section as keep, replace or rewrite, history, or move.
|
|
|
54
54
|
- A low-confidence `move` is a prompt to look rather than an instruction to cut
|
|
55
55
|
- **Routes to:** the document the verdict names as the owner. Where the move crosses a tier rather than a file, the project's own content-ownership rules decide the destination.
|
|
56
56
|
|
|
57
|
+
### The structure line
|
|
58
|
+
|
|
59
|
+
The audit record's `length` array lists each entry past the line checkpoint, and `coverage.findings` lists the `doubled` and `unnamed` ones. The structure line reads both, so it carries the `measured` class for the finding and `read` for the proposal built on it.
|
|
60
|
+
|
|
61
|
+
- **Overgrown entry:** read its `causes` in `length` first, since `history: yes` points at cutting provenance and one domain at a trim, so neither earns a split
|
|
62
|
+
- **Split:** group the Layout headings by domain, one new entry per group beyond the first, naming each entry's headings and one reason
|
|
63
|
+
- **Domain boundaries:** no verb measures one, so state the grouping as a judgment.
|
|
64
|
+
- **Doubled coverage:** two entries describe one source area. Propose the one that owns it, from which entry already holds the area's traps, and the other points at it.
|
|
65
|
+
- **Unnamed coverage:** no entry names the area. Propose an owner among the existing entries before a new one, since a new entry is the larger change.
|
|
66
|
+
|
|
67
|
+
A proposal names headings and a reason, so an operator judges a concrete split. A split nobody can act on names none of those.
|
|
68
|
+
|
|
57
69
|
### When a verb resolves nothing
|
|
58
70
|
|
|
59
71
|
Both verbs resolve their folders by surface root rather than by a path written into them, so a project laid out differently resolves nothing. The two degrade differently.
|
|
@@ -15,14 +15,19 @@ A folder repaired by hand also drifts back, since a target gates nothing until a
|
|
|
15
15
|
|
|
16
16
|
- Each batch takes one folder and leaves it at zero findings or reports it ungated
|
|
17
17
|
- A folder gates only through `canon markdown gated add`, which refuses one with a finding left
|
|
18
|
-
- A rewrite keeps every stated reason, command, and path the original carried
|
|
18
|
+
- A rewrite keeps every stated reason, command, and path the original carried, and leaves a context entry no more shape findings than it started with
|
|
19
19
|
- The body names the verbs and states no limit number
|
|
20
20
|
- A requirement gates by its own path, never through a folder prefix
|
|
21
21
|
|
|
22
|
+
## Restructure first
|
|
23
|
+
|
|
24
|
+
An overgrown context folder is offered the restructure `document-health` proposes before any rewrite, and the restructure applies only on the operator's word.
|
|
25
|
+
|
|
22
26
|
## Must not
|
|
23
27
|
|
|
24
28
|
- Edit `canon/config/markdown.toml` by hand
|
|
25
29
|
- Report a folder gated that the verb refused
|
|
30
|
+
- Reach zero density findings by growing the context shape findings
|
|
26
31
|
- Change what a document claims to pass the audit
|
|
27
32
|
- Be reachable only by its name, since `document-health` names it for every length finding
|
|
28
33
|
|
|
@@ -18,6 +18,15 @@ The CLI holds every number. `canon markdown audit` measures, and `canon markdown
|
|
|
18
18
|
- `gated add` refusing with `canon-checkout`: stop. Canon's own checkout gates through a constant in its source, which a code change edits.
|
|
19
19
|
- Branch on each record's own fields rather than on an exit status a shell wrapper can flatten. A call with no terminal on stdin already runs headless, so no command takes a prefix.
|
|
20
20
|
|
|
21
|
+
## Step 0: offer the restructure
|
|
22
|
+
|
|
23
|
+
Restructure an overgrown context folder before rewriting it, so the effort is spent once. Run this step only when `canon/context/` is a candidate, meaning it is not yet listed in `canon/config/markdown.toml`, and skip it silently otherwise.
|
|
24
|
+
|
|
25
|
+
- Take the structure line `document-health` reports for each area folder, or read it from `canon context audit --json` the way that skill does
|
|
26
|
+
- A folder with no overgrown entry and no doubled or unnamed coverage finding has no line, so go straight to Step 1 with no prompt
|
|
27
|
+
- Show the operator each proposal and ask before applying it, since a split moves files other entries cite. Re-run the audit afterward to confirm the citations still resolve.
|
|
28
|
+
- Take a declined proposal as a decision and rewrite the entry as it stands
|
|
29
|
+
|
|
21
30
|
## Step 1: read the audit
|
|
22
31
|
|
|
23
32
|
Run the audit over the whole tree and take the density and reference shape findings from its record:
|
|
@@ -26,6 +35,12 @@ Run the audit over the whole tree and take the density and reference shape findi
|
|
|
26
35
|
canon markdown audit --json
|
|
27
36
|
```
|
|
28
37
|
|
|
38
|
+
When `canon/context/` is a candidate, read the context shape beside it and keep each entry's `shapeFindings` issues as the baseline Step 3 holds the rewrite to:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
canon context audit --json
|
|
42
|
+
```
|
|
43
|
+
|
|
29
44
|
Take the candidate folders from the order table in `${CLAUDE_SKILL_DIR}/references/batches.md` rather than from the findings, since a clean folder carries no finding and would never be gated. Drop every folder `canon/config/markdown.toml` already lists, since a listed folder already gates.
|
|
30
45
|
|
|
31
46
|
Sort what is left in two. A candidate the findings never name needs no rewrite, so gate it through Step 4 before the first batch. Group the findings under the rest, which Steps 2 and 3 rewrite.
|
|
@@ -46,6 +61,12 @@ canon markdown audit <prefix> --json
|
|
|
46
61
|
|
|
47
62
|
Read `${CLAUDE_SKILL_DIR}/references/batches.md` for what a rewrite may cut. A stated reason survives every rewrite, and the audit reaching zero cannot see one that went missing.
|
|
48
63
|
|
|
64
|
+
### Hold the context shape
|
|
65
|
+
|
|
66
|
+
For a file under a folder the context audit resolves, also re-run `canon context audit --json` and compare the entry's `shapeFindings` issues with the baseline. A density fix that splits a Layout paragraph into several can grow `layout-paragraphs`.
|
|
67
|
+
|
|
68
|
+
A rewrite leaving the entry with more shape issues than it started with is not done. Undo it, or fold the split into a table or a linked page, and report the entry as stopped when neither reaches the baseline. Zero density findings reached by growing shape findings only moves the failure.
|
|
69
|
+
|
|
49
70
|
## Step 4: gate the folder
|
|
50
71
|
|
|
51
72
|
```bash
|
|
@@ -84,7 +84,7 @@ What this skill adds on top of the standard:
|
|
|
84
84
|
Every `- Suggested:` line takes senior judgment, picking the best option in one line with its reason or main tradeoff, and naming alternatives only when they change the pick.
|
|
85
85
|
|
|
86
86
|
- Suggest a real default when best practice, the codebase, or prior context points to one
|
|
87
|
-
-
|
|
87
|
+
- Before `## Files to touch`, load `canon:codebase-layout` for a new path (its reason) and `canon:api-design` for a changed verb, flag, output, record shape, or endpoint. Report either if missing.
|
|
88
88
|
- Fill `## Design references` with the `canon/design/<surface>/` folder for each surface a UI file in `## Files to touch` renders, and write `None identified.` in a project with no folder
|
|
89
89
|
- Write `## Verification` as one bullet per outcome naming the test or command that proves it
|
|
90
90
|
- Write `## Review focus` as the inputs or states that would break the change, carrying the risk you understood to the executor and the reviewer
|
|
@@ -64,6 +64,8 @@ Target update: `<path>`, canon `<installed>` to `<running>`.
|
|
|
64
64
|
|
|
65
65
|
**Migrations, in run order:** <each pending or refused entry from migrate status, with its verb>
|
|
66
66
|
|
|
67
|
+
**Retired calls:** <each `retiredCallers` entry from migrate status, with its file, line, the release that retired the verb, and where the work went, or omitted when the list is empty>
|
|
68
|
+
|
|
67
69
|
**Drift:** <each non-empty cause from sync --check, with its count and paths>
|
|
68
70
|
|
|
69
71
|
**Where it lands:** <the linked worktree this run is already in, or the worktree `.claude/worktrees/<name>/` it will enter, or the branch it will create in place when the operator declines a worktree>
|
|
@@ -73,11 +75,13 @@ Target update: `<path>`, canon `<installed>` to `<running>`.
|
|
|
73
75
|
**Commands, in order:** <the exact commands Steps 4 to 6 will run>
|
|
74
76
|
```
|
|
75
77
|
|
|
78
|
+
A retired call is no migration, so no step here fixes it. Name it in the preview and leave the edit to the operator, who owns the script.
|
|
79
|
+
|
|
76
80
|
Summarize the changelog for this project, not for the toolkit. Drop an entry that touches nothing the target holds, and lead with anything a migration or a sync will change.
|
|
77
81
|
|
|
78
82
|
### Nothing pending
|
|
79
83
|
|
|
80
|
-
When the changelog range is empty, `migrate status` lists nothing pending or refused
|
|
84
|
+
When the changelog range is empty, `migrate status` lists nothing pending or refused and no `retiredCallers`, and the drift lines hold nothing a step here would take, write nothing. Invoke `canon:target-check` and report the target current with its table.
|
|
81
85
|
|
|
82
86
|
### Take a branch after the go-ahead
|
|
83
87
|
|
|
@@ -46,7 +46,7 @@ The shape kinds are `retired-section`, `gotcha-count`, `gotcha-heading`, `gotcha
|
|
|
46
46
|
|
|
47
47
|
- A `## Decisions` H2, or a `Gotchas` section past five bullets or holding a heading or paragraph
|
|
48
48
|
- A `Layout` H3 titled neither a code-spanned path nor `Other folders`, or a heading below H3 under `Layout`
|
|
49
|
-
- A folder H3 past five bullets or its one-paragraph lead
|
|
49
|
+
- A folder H3 past five bullets or its one-paragraph lead, where table rows and the sentence leading the bullets after a table count toward neither
|
|
50
50
|
|
|
51
51
|
The development entry may carry a `Scripts` H3 exempt from both folder caps. `SHAPE_GATED_FOLDERS` lives in `src/context/shape-gated.ts`.
|
|
52
52
|
|
|
@@ -24,7 +24,7 @@ A flat installed-rule layout makes `gov sync` report "No governance surfaces fou
|
|
|
24
24
|
|
|
25
25
|
## Governance sync
|
|
26
26
|
|
|
27
|
-
`canon gov sync [target]` updates what `.claude/rules/canon/` holds and adds nothing. `.claude/rules/project/` is never read, so put project rules there.
|
|
27
|
+
`canon gov sync [target]` updates what `.claude/rules/canon/` holds and adds nothing. `.claude/rules/project/` is never read, so put project rules there. After an apply it lists each copied rule with its globs and matching file count, and `--json` puts `{ "coverage": [...] }` on stdout.
|
|
28
28
|
|
|
29
29
|
- A rule the toolkit no longer ships is deleted, edited or not
|
|
30
30
|
- A rule renamed in `governance/renames.toml` moves, and its local edits are dropped
|
package/docs/agents/migrate.md
CHANGED
|
@@ -38,6 +38,12 @@ kind: reference
|
|
|
38
38
|
|
|
39
39
|
`status` runs each verb as a dry run and exits 2 while any is pending, with a refusal outranking that. Each entry's `verb` carries the flags its dry run used, so append `--write` to it. A clean entry can turn pending once an earlier one applies, so run it again after each `--write`.
|
|
40
40
|
|
|
41
|
+
`status --json` also carries `retiredCallers`, the project calls to a verb a release retired.
|
|
42
|
+
|
|
43
|
+
- Each entry has `file`, `line`, `verb`, `release`, and `movedTo`
|
|
44
|
+
- The scan reads the workflow folder, the `package.json` scripts, `scripts/`, and the files those name, matching a command line or an argument list and never prose
|
|
45
|
+
- A caller changes no exit code, since no `--write` fixes one, so edit the script by hand
|
|
46
|
+
|
|
41
47
|
Three markers keep an old spelling on a line. Each also covers the nearest non-blank line, below it for `canon-keep-record-root` and above it for `canon-keep-surface-root`.
|
|
42
48
|
|
|
43
49
|
| Marker | Keeps |
|
package/docs/target/sync.md
CHANGED
|
@@ -43,10 +43,12 @@ Add `--json` for a machine-readable report and `--exit-code` to fail CI when a t
|
|
|
43
43
|
## Targeted
|
|
44
44
|
|
|
45
45
|
- Seed docs such as `CLAUDE.md` and `canon/REQUIREMENTS.md`: merge by hand, keeping the project's customizations
|
|
46
|
-
- Installed governance rules: `canon gov sync <path>`, which adds no new rules
|
|
46
|
+
- Installed governance rules: `canon gov sync <path>`, which adds no new rules and reports coverage afterward
|
|
47
47
|
- Tooling configs and seeds: read differences with `canon tooling diff <stack> <path>`, then `canon tooling sync <stack> <path> --write`, which overwrites golden configs and copies each seed once
|
|
48
48
|
- Index regeneration: `canon indexes regen`
|
|
49
49
|
|
|
50
|
+
After `canon gov sync` applies, it lists each rule it copied with its globs and how many project files match. A `ui/` rule adds a pointer to `/canon:ux-audit`. `--json` writes `{ "coverage": [{ "rule", "paths", "files", "ui" }] }` to stdout, empty when nothing was copied, with `files` null outside git. The report never changes the exit code.
|
|
51
|
+
|
|
50
52
|
Try a scenario from [sandbox](../../canon/context/development/sandbox.md) before syncing a real project.
|
|
51
53
|
|
|
52
54
|
## Running sync from an agent session
|
|
@@ -36,7 +36,7 @@ Each role keeps to its own writes, and five rules cover how the roles meet.
|
|
|
36
36
|
7. A pass posts `## Review` while anything is open and `## Review closed` when nothing is. The reviewer then runs `canon pr lift`.
|
|
37
37
|
8. The human merges with `canon pr merge <number>`. The orchestrator tells any trailing worker whose files the merge touched to run `review-address`, which rebases.
|
|
38
38
|
|
|
39
|
-
Each worker halts at the PR, and the merge stays a human gate. `canon pr merge` squashes with the body's Summary, Key changes, and Technical context as the message, and the seeded deny rule covers the forms an agent writes by default.
|
|
39
|
+
Each worker halts at the PR, and the merge stays a human gate. `canon pr merge` squashes with the body's Summary, Key changes, and Technical context as the message, and the seeded deny rule covers the forms an agent writes by default. A release-please or Dependabot pull request merges the same way, from the bot's own body.
|
|
40
40
|
|
|
41
41
|
## The review layer
|
|
42
42
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Routes code placement and
|
|
2
|
+
description: Routes code placement, shape, and interface decisions to the skills that carry them, and library calls to the official docs
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Code standards
|
|
@@ -12,6 +12,10 @@ description: Routes code placement and shape decisions to the skills that carry
|
|
|
12
12
|
|
|
13
13
|
- Load the `canon:code-craft` skill before deciding the shape of a new function, class, or module, and report it rather than proceeding silently when it does not resolve.
|
|
14
14
|
|
|
15
|
+
## Interfaces
|
|
16
|
+
|
|
17
|
+
- Load `canon:api-design` before shaping a public function, flag or output, record format, endpoint, or caller-handled error, and report it rather than proceeding silently when it does not resolve.
|
|
18
|
+
|
|
15
19
|
## Libraries
|
|
16
20
|
|
|
17
21
|
- Before calling a library API the codebase does not already use, fetch the official docs for the installed version and match the call to them, since recall of an API goes stale.
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Route edits that decide how a surface looks to the design-taste skill for the layer ordering, the coherence locks, and the defaults a model reaches for
|
|
3
3
|
paths:
|
|
4
|
+
- '**/*.tsx'
|
|
5
|
+
- '**/*.jsx'
|
|
6
|
+
- '**/*.astro'
|
|
7
|
+
- '**/*.html'
|
|
4
8
|
- '**/*.css'
|
|
5
9
|
- '**/*.scss'
|
|
6
10
|
- 'canon/DESIGN.md'
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@erclx/canon",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.59.0",
|
|
5
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": {
|
|
@@ -30,14 +30,14 @@
|
|
|
30
30
|
"check:shell": "find scripts tooling claude .claude/hooks -name '*.sh' -exec shellcheck --severity=warning {} + && find .husky -maxdepth 1 -type f -exec shellcheck --severity=warning --shell=sh {} +",
|
|
31
31
|
"check:lint": "oxlint --disable-nested-config --max-warnings 0",
|
|
32
32
|
"check:types": "tsc --noEmit",
|
|
33
|
-
"check:install": "
|
|
33
|
+
"check:install": "bun scripts/core/install-check.ts",
|
|
34
34
|
"test": "bun --bun vitest run",
|
|
35
35
|
"prepare": "husky",
|
|
36
36
|
"check": "bun src/cli.ts gate run",
|
|
37
37
|
"check:ci": "bun src/cli.ts gate run --all",
|
|
38
38
|
"update": "bun update --interactive && bun run check",
|
|
39
39
|
"clean": "rm -rf node_modules && bun pm cache rm && bun install",
|
|
40
|
-
"bootstrap": "
|
|
40
|
+
"bootstrap": "bun scripts/core/bootstrap.ts",
|
|
41
41
|
"canon:sandbox": "canon sandbox",
|
|
42
42
|
"canon:sandbox:reset": "canon sandbox reset",
|
|
43
43
|
"web:tokens": "bun src/cli.ts design css --no-components > web/src/styles/tokens.css.new && (echo '/* Generated by `canon design css --no-components`. Regenerate with `bun run web:tokens`. Do not hand-edit. */'; cat web/src/styles/tokens.css.new) > web/src/styles/tokens.css && rm web/src/styles/tokens.css.new",
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installs dependencies, links the canon CLI globally, and appends the marked
|
|
3
|
+
* alias block to the user's `.zshrc`, the only file this writes outside the
|
|
4
|
+
* repository. Runs on Bun alone, so a stock macOS bash 3.2 never enters it.
|
|
5
|
+
*/
|
|
6
|
+
import { appendFileSync, existsSync, readFileSync } from 'node:fs'
|
|
7
|
+
import { homedir } from 'node:os'
|
|
8
|
+
import { join } from 'node:path'
|
|
9
|
+
import { fileURLToPath } from 'node:url'
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
intro,
|
|
13
|
+
logAdd,
|
|
14
|
+
logError,
|
|
15
|
+
logInfo,
|
|
16
|
+
logStep,
|
|
17
|
+
logWarn,
|
|
18
|
+
outro,
|
|
19
|
+
} from '../../src/cli/ui'
|
|
20
|
+
|
|
21
|
+
const root = fileURLToPath(new URL('../..', import.meta.url)).replace(/\/$/, '')
|
|
22
|
+
|
|
23
|
+
const MARKER_OPEN = '# >>> canon aliases >>>'
|
|
24
|
+
const MARKER_CLOSE = '# <<< canon aliases <<<'
|
|
25
|
+
|
|
26
|
+
function zshrcPath(): string {
|
|
27
|
+
const dir = process.env.ZDOTDIR
|
|
28
|
+
return join(dir !== undefined && dir !== '' ? dir : homedir(), '.zshrc')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** `$TOOLKIT` stays a literal in the block, so the shell expands it on load. */
|
|
32
|
+
function aliasBlock(): string {
|
|
33
|
+
return `${MARKER_OPEN}
|
|
34
|
+
TOOLKIT="${root}"
|
|
35
|
+
export CLAUDE_CODE_ENABLE_TODO_TOOLS=1
|
|
36
|
+
|
|
37
|
+
alias cl='claude'
|
|
38
|
+
alias clr='cl -r'
|
|
39
|
+
alias clc='cl -c'
|
|
40
|
+
alias clw='cl -w'
|
|
41
|
+
alias cls='cl --model sonnet'
|
|
42
|
+
|
|
43
|
+
alias clp='claude --plugin-dir $TOOLKIT/claude'
|
|
44
|
+
alias clpc='clp -c'
|
|
45
|
+
alias clps='clp --model sonnet'
|
|
46
|
+
alias clpa='clp agents'
|
|
47
|
+
alias clpac='clpa --cwd .'
|
|
48
|
+
${MARKER_CLOSE}`
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function run(step: string, cmd: string[]): void {
|
|
52
|
+
const result = Bun.spawnSync(cmd, {
|
|
53
|
+
cwd: root,
|
|
54
|
+
stdout: 'ignore',
|
|
55
|
+
stderr: 'pipe',
|
|
56
|
+
})
|
|
57
|
+
if (result.exitCode !== 0) {
|
|
58
|
+
throw new Error(`${step} failed: ${result.stderr.toString().trim()}`)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function installCli(): void {
|
|
63
|
+
logStep('Installing the canon CLI')
|
|
64
|
+
run('bun install', ['bun', 'install', '--cwd', root])
|
|
65
|
+
run('bun link', ['bun', 'link', '--cwd', root])
|
|
66
|
+
logInfo('Dependencies installed and canon linked')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function installAliases(): void {
|
|
70
|
+
const zshrc = zshrcPath()
|
|
71
|
+
logStep('Installing Claude Code shell aliases')
|
|
72
|
+
logInfo(
|
|
73
|
+
`This step manages a marked alias block in ${zshrc}, the only file outside the repo this script writes.`,
|
|
74
|
+
)
|
|
75
|
+
logInfo(
|
|
76
|
+
'Delete the block and its two marker comments to remove the aliases. Re-running never adds a second copy.',
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
const existing = existsSync(zshrc) ? readFileSync(zshrc, 'utf8') : ''
|
|
80
|
+
if (existing.includes(MARKER_OPEN)) {
|
|
81
|
+
logInfo(`Aliases already present in ${zshrc}, skipping`)
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
if (/^alias clp=/m.test(existing)) {
|
|
85
|
+
logWarn(
|
|
86
|
+
`An unmanaged clp alias is already in ${zshrc}. Leaving it untouched.`,
|
|
87
|
+
)
|
|
88
|
+
logWarn('Remove it and re-run to switch to the managed block.')
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
appendFileSync(zshrc, `\n${aliasBlock()}\n`)
|
|
93
|
+
logAdd(`Appended canon alias block to ${zshrc}`)
|
|
94
|
+
logInfo(`Run 'source ${zshrc}' or open a new shell to load them`)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function verifyCli(): void {
|
|
98
|
+
logStep('Verifying the install')
|
|
99
|
+
if (Bun.which('canon') !== null) {
|
|
100
|
+
logInfo('canon resolves on PATH')
|
|
101
|
+
} else {
|
|
102
|
+
logWarn(
|
|
103
|
+
'canon not on PATH yet. Ensure $BUN_INSTALL/bin is exported, then re-open your shell.',
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
intro('canon bootstrap')
|
|
109
|
+
let failed = false
|
|
110
|
+
try {
|
|
111
|
+
installCli()
|
|
112
|
+
installAliases()
|
|
113
|
+
verifyCli()
|
|
114
|
+
} catch (error) {
|
|
115
|
+
failed = true
|
|
116
|
+
logError(error instanceof Error ? error.message : String(error))
|
|
117
|
+
} finally {
|
|
118
|
+
outro()
|
|
119
|
+
}
|
|
120
|
+
process.exit(failed ? 1 : 0)
|