@erclx/canon 5.39.0 → 5.41.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 (162) hide show
  1. package/README.md +39 -22
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/auto-ship/REQUIREMENT.md +1 -0
  4. package/claude/skills/auto-ship/SKILL.md +6 -0
  5. package/claude/skills/auto-ship/references/fan-in.md +59 -0
  6. package/claude/skills/canvas-board/SKILL.md +1 -1
  7. package/claude/skills/create-skill/references/skill.md +1 -1
  8. package/claude/skills/decision-escalate/REQUIREMENT.md +1 -1
  9. package/claude/skills/decision-escalate/SKILL.md +1 -2
  10. package/claude/skills/draft-slides/SKILL.md +2 -3
  11. package/claude/skills/draft-slides/references/deck.md +183 -0
  12. package/claude/skills/git-branch/REQUIREMENT.md +17 -6
  13. package/claude/skills/git-branch/SKILL.md +63 -7
  14. package/claude/skills/git-pr/references/evidence.md +2 -2
  15. package/claude/skills/git-pr/references/final-command.md +1 -1
  16. package/claude/skills/git-pr/references/labels.md +1 -1
  17. package/claude/skills/git-pr/references/publish.md +1 -1
  18. package/claude/skills/plan-feature/references/batches.md +48 -1
  19. package/claude/skills/plan-feature/references/plan.md +2 -0
  20. package/claude/skills/plan-intake-answer/REQUIREMENT.md +1 -1
  21. package/claude/skills/plan-intake-answer/SKILL.md +1 -2
  22. package/claude/skills/review-address/references/rebase-conflicts.md +7 -0
  23. package/claude/skills/review-ui/SKILL.md +3 -0
  24. package/claude/skills/review-ui/references/close-out.md +47 -0
  25. package/claude/skills/review-ui/references/post.md +3 -1
  26. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +1 -0
  27. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
  28. package/claude/skills/role-worker/REQUIREMENT.md +1 -0
  29. package/claude/skills/role-worker/SKILL.md +9 -0
  30. package/claude/skills/task-board/references/board.md +1 -1
  31. package/claude/skills/task-board/references/tasks.md +1 -1
  32. package/claude/skills/teach-workspace/REQUIREMENT.md +1 -1
  33. package/claude/skills/teach-workspace/SKILL.md +1 -2
  34. package/claude/skills/ux-measure/SKILL.md +1 -1
  35. package/docs/agents/audits.md +61 -60
  36. package/docs/agents/context-audit.md +40 -58
  37. package/docs/agents/index.md +5 -51
  38. package/docs/agents/install-and-sync.md +1 -0
  39. package/docs/agents/markdown-audit.md +53 -99
  40. package/docs/agents/records.md +37 -79
  41. package/docs/agents/sessions-transfer.md +26 -70
  42. package/docs/target/index.md +0 -1
  43. package/docs/target/migrations.md +57 -66
  44. package/docs/target/projects.md +36 -78
  45. package/docs/target/scenarios.md +9 -19
  46. package/docs/target/sync.md +27 -68
  47. package/docs/workflow/ai-workflow.md +22 -68
  48. package/docs/workflow/operating-model.md +32 -186
  49. package/docs/workflow/parallel-features.md +30 -66
  50. package/docs/workflow/skill-map-on-request.md +22 -24
  51. package/docs/workflow/skill-map.md +72 -76
  52. package/docs/workflow/visual-design-workflow.md +25 -118
  53. package/docs/workflow/zshrc-aliases.md +18 -37
  54. package/package.json +1 -1
  55. package/src/canvas/client/inspector/fields.css +4 -0
  56. package/src/canvas/index.html +6 -0
  57. package/src/claude/cases/git.ts +4 -0
  58. package/src/claude/skills-reach.ts +2 -2
  59. package/src/cli/ui.ts +3 -1
  60. package/src/commands/canvas.ts +23 -0
  61. package/src/commands/capture.ts +14 -0
  62. package/src/commands/claude.ts +13 -0
  63. package/src/commands/demo.ts +22 -0
  64. package/src/commands/deps.ts +6 -0
  65. package/src/commands/driver.ts +28 -2
  66. package/src/commands/gov.ts +52 -18
  67. package/src/commands/intake.ts +17 -0
  68. package/src/commands/labels.ts +38 -5
  69. package/src/commands/pr.ts +192 -52
  70. package/src/commands/records.ts +19 -3
  71. package/src/commands/secrets.ts +6 -0
  72. package/src/commands/sessions.ts +8 -0
  73. package/src/commands/tasks.ts +52 -0
  74. package/src/commands/teach.ts +13 -0
  75. package/src/commands/upstream.ts +25 -1
  76. package/src/commands/worktrees.ts +5 -0
  77. package/src/context/audit.ts +22 -4
  78. package/src/context/folders.ts +1 -1
  79. package/src/context/shape-gated.ts +5 -2
  80. package/src/counts/scan.ts +1 -1
  81. package/src/design/components.ts +1 -1
  82. package/src/design/document.ts +1 -1
  83. package/src/design/tokens.ts +1 -1
  84. package/src/gate/boundaries.ts +4 -1
  85. package/src/gate/measures.ts +1 -1
  86. package/src/gate/stages.ts +7 -7
  87. package/src/git/env.ts +6 -0
  88. package/src/markdown/scope.ts +11 -19
  89. package/src/pr/frames.ts +53 -9
  90. package/src/pr/lift.ts +153 -19
  91. package/src/pr/painted.ts +87 -0
  92. package/src/records/validate.ts +1 -0
  93. package/src/roots/record.ts +18 -1
  94. package/src/sandbox/census.ts +1 -1
  95. package/src/sandbox/headless/run.ts +3 -2
  96. package/src/sandbox/headless/watch.ts +3 -4
  97. package/src/slides/convert/master.ts +3 -0
  98. package/src/sync/format.ts +46 -0
  99. package/src/sync/stamp.ts +6 -0
  100. package/src/tasks/answers.ts +79 -2
  101. package/src/tasks/fan-in.ts +176 -0
  102. package/src/tasks/stack.ts +1 -1
  103. package/tooling/astro/reference.md +80 -39
  104. package/tooling/base/reference.md +63 -32
  105. package/tooling/base/seeds/canon/context/ci.md +7 -15
  106. package/tooling/base/seeds/canon/context/development.md +14 -14
  107. package/tooling/claude/reference.md +40 -22
  108. package/tooling/cloudflare/reference.md +31 -12
  109. package/tooling/go/reference.md +47 -23
  110. package/tooling/nextjs/reference.md +52 -19
  111. package/tooling/php/reference.md +35 -16
  112. package/tooling/python/reference.md +47 -30
  113. package/tooling/shell/reference.md +6 -5
  114. package/tooling/vite-react/reference.md +49 -21
  115. package/tooling/web/reference.md +131 -71
  116. package/docs/agents/canvas.md +0 -86
  117. package/docs/agents/capture.md +0 -65
  118. package/docs/agents/census.md +0 -23
  119. package/docs/agents/comments.md +0 -34
  120. package/docs/agents/context-audit-checks.md +0 -108
  121. package/docs/agents/context-classify.md +0 -101
  122. package/docs/agents/counts.md +0 -74
  123. package/docs/agents/demo.md +0 -140
  124. package/docs/agents/driver.md +0 -103
  125. package/docs/agents/gate.md +0 -90
  126. package/docs/agents/hooks.md +0 -78
  127. package/docs/agents/intake.md +0 -81
  128. package/docs/agents/key-changes.md +0 -116
  129. package/docs/agents/label-coverage.md +0 -86
  130. package/docs/agents/markdown-audit-density.md +0 -39
  131. package/docs/agents/markdown-audit-weight.md +0 -83
  132. package/docs/agents/pr-evidence-layout.md +0 -70
  133. package/docs/agents/pr-evidence.md +0 -273
  134. package/docs/agents/pr-frames.md +0 -131
  135. package/docs/agents/pr-lift.md +0 -132
  136. package/docs/agents/pr-local.md +0 -111
  137. package/docs/agents/pr-preview.md +0 -155
  138. package/docs/agents/pr-reads.md +0 -111
  139. package/docs/agents/pr-tick.md +0 -72
  140. package/docs/agents/records-push.md +0 -61
  141. package/docs/agents/records-stale.md +0 -40
  142. package/docs/agents/records-validate.md +0 -57
  143. package/docs/agents/restated.md +0 -83
  144. package/docs/agents/routing.md +0 -61
  145. package/docs/agents/rule-citations.md +0 -90
  146. package/docs/agents/sandbox.md +0 -126
  147. package/docs/agents/sessions.md +0 -128
  148. package/docs/agents/skills-audit.md +0 -120
  149. package/docs/agents/skills-reach.md +0 -64
  150. package/docs/agents/slides.md +0 -176
  151. package/docs/agents/state-scoped-risk.md +0 -105
  152. package/docs/agents/superseded.md +0 -85
  153. package/docs/agents/tasks-archive.md +0 -83
  154. package/docs/agents/tasks-plans.md +0 -152
  155. package/docs/agents/tasks-validate.md +0 -133
  156. package/docs/agents/tasks.md +0 -123
  157. package/docs/agents/teach-authoring.md +0 -118
  158. package/docs/agents/teach.md +0 -157
  159. package/docs/agents/test-order.md +0 -58
  160. package/docs/agents/upstream.md +0 -94
  161. package/docs/agents/worktrees.md +0 -118
  162. package/docs/target/standards-moved.md +0 -34
@@ -6,147 +6,54 @@ category: Workflow
6
6
 
7
7
  # Visual design workflow
8
8
 
9
- Three tiers cover the range from prose-only design docs to a fully graphical design source of truth. Pick one per project based on how UI-heavy the work is, whether stakeholders review visuals, and whether a designer is involved. Tiers stack, so moving up does not invalidate work done at a lower tier.
10
-
11
- The tier framework sits alongside [Claude Design](../../wiki/claude/design.md), the one catalog page in this range. Excalidraw and the surrounding MCP and skill ecosystem are named inline below rather than catalogued separately. This page decides when to reach for what.
12
-
13
- [Claude Design](../../wiki/claude/design.md), released 2026-04-17 and priced inside Claude subscriptions, is the ceiling tool for tier 1 and tier 2. Reserve it for codebase extraction and the richly annotated handoff bundle.
9
+ Three tiers run from prose-only design docs to a graphical source of truth, and tiers stack. [Claude Design](../../wiki/claude/design.md) is the ceiling tool for tiers 1 and 2.
14
10
 
15
11
  ## Tier 0: prose only
16
12
 
17
- The default. `canon/DESIGN.md` holds visual intent, being one Overview paragraph, at most three one-line rules a section, and a table naming what each color and shape role is for. Its lead names the stylesheet the values live in, since a hand-typed copy of them drifts from the CSS.
18
-
19
- Claude Code reads it and writes the implementation. Works for CLI tools, internal dashboards, admin panels, and backend-focused projects.
20
-
21
- ### Seed shape
22
-
23
- The toolkit seed in `tooling/claude/seeds/canon/DESIGN.md` ships an intent template with a starting set of roles, each naming its custom property, and `claude/skills/canvas-board/references/design.md` carries the same tables under `## Template` with placeholder rows. The column headers are what the parser reads, so they stay verbatim in either, while the rows are the project's own. The seed's lead asks for the project's own stylesheet, such as `src/styles/tokens.css` or a Tailwind `@theme` block, and falls back to `.claude/design/project/` only when the project has none.
24
-
25
- `canon design render` writes an HTML plus CSS preview to `.canon/tmp/render/design/` for eyeballing the current system without leaving Claude Code. See `canon/context/design.md`.
26
-
27
- A project wanting a neutral starting point to override runs `canon design install`, which copies one stylesheet to `.claude/design/base.css` carrying an achromatic token set as custom properties and two components built on them. Greys run from near-black to near-white and the accent is monochrome, so the only palette the project renders is one it writes itself. That file is toolkit-owned and `canon design sync` refreshes it, so a project overrides a value in `.claude/design/project/` instead, which sync never touches. Nothing arrives without that install, and the two channels are independent: a record the project wrote is its own, and the installed stylesheet is the toolkit's.
28
-
29
- A record generated from a token source carries the values in tables instead, which is the shape this toolkit's own record takes. There a cell no source anchors ends in `? verify`, and the preview shows that marker beside the value rather than folding it in, so a swatch and a font sample stay built from the value alone. A confidence line above the sections names how many cells are anchored against how many are tagged, which is what tells a reader whether they are looking at a record of the code or a proposal about it. It reads the columns a source could anchor and leaves out the row names, so the ratio is not diluted by cells no tag could ever reach. The proposal path tags nearly all of them, so that count reads low on day one by design.
30
-
31
- ### Tools
32
-
33
- - None beyond Claude Code itself
34
- - Playwright CLI optional for verifying journeys across routes and browser-only behavior. See [`test-craft`](../../claude/skills/test-craft/SKILL.md) for which layer a journey belongs at.
13
+ The default. `canon/DESIGN.md` holds visual intent as one Overview paragraph, at most three one-line rules a section, and a table naming each color and shape role, with a lead naming the stylesheet. Claude Code alone suffices, and [`test-craft`](../../claude/skills/test-craft/SKILL.md) says which layer a Playwright journey belongs at.
35
14
 
36
- ### Skills
15
+ - The seed is `tooling/claude/seeds/canon/DESIGN.md`, and its column headers stay verbatim
16
+ - `canon design render` previews it in `.canon/tmp/render/design/`
17
+ - `canon design install` copies a neutral base to `.claude/design/base.css`, so override values in `.claude/design/project/`. See `canon/context/features/design.md`.
37
18
 
38
- #### Reviewing and measuring
19
+ These skills cover the tier:
39
20
 
40
- - `canon:ui-checklist` for what a reviewer has to look at after UI changes, and what ships without a test
41
- - `canon:ux-audit` for UX gap detection on existing surfaces
42
- - `canon:ux-measure` for what a running surface costs to paint, read against published thresholds
21
+ - `canon:ui-checklist`, `canon:ux-audit`, and `canon:ux-measure` review and measure a UI
22
+ - `canon:draft-and-pick` drafts candidates as frames on the canvas and takes your pick
23
+ - `canon:canvas-board` works a direction out as HTML frames you edit in the browser. Only the pick moves into `canon/DESIGN.md`.
24
+ - `canon:draft-identity` drafts a logo mark and social card
43
25
 
44
- #### Drafting
45
-
46
- - `canon:draft-and-pick` for a call settled by looking, drafting several candidates as frames on the canvas and taking your pick
47
- - `canon:canvas-board` for a direction worked out over several turns, drawn as HTML frames you drag, select, and restyle in the browser while the session reads your changes back. Only the pick moves into `canon/DESIGN.md`, since the canvas folder is gitignored
48
- - `canon:draft-identity` to draft a project's logo mark and compose it into an icon sequence and a social card, through `draft-and-pick`'s own render-and-pick loop. On an Astro project holding the card route scaffold, the card lands as a route the project keeps and is captured through a running server, so it reads the project's real stylesheet rather than typed copies of every color. Everywhere else it ships as a PNG and the run says so.
49
- - Anthropic's `frontend-design` plugin optional for light visual steering
50
-
51
- ### When to pick
52
-
53
- - UI is not the core product
54
- - Design decisions are few and can be written as sentences
55
- - No designer on the project
56
- - Stakeholders review code or behavior rather than mockups
26
+ Pick tier 0 when the UI is not the core product, decisions fit in sentences, and stakeholders review code.
57
27
 
58
28
  ## Tier 1: visual companion
59
29
 
60
- Prose stays as source of truth. Add a visual render as a feedback surface for the agent and for human review.
61
-
62
- - Excalidraw handles agent-drawn layouts when the round-trip canvas loop matters
63
- - Claude Design joins only when codebase extraction or the annotated handoff bundle is worth the ceiling cost
64
-
65
- Both produce derived artifacts, so human edits are review annotations rather than source changes.
66
-
67
- ### Seed shape
68
-
69
- Same as tier 0. `DESIGN.md` stays human-maintained.
70
-
71
- Impeccable, if installed, keeps its own root `DESIGN.md` and `PRODUCT.md` in its own format. These are separate files from the toolkit's `canon/DESIGN.md` and are not synced with it. Treat impeccable's pair as its own source of truth for its commands, and the toolkit's `canon/DESIGN.md` as the source other toolkit skills read.
72
-
73
- ### Tools
74
-
75
- - Excalidraw canvas server on localhost plus the community [`yctimlin/mcp_excalidraw`](https://github.com/yctimlin/mcp_excalidraw) MCP shim, for projects that need an agent to draw, read back, and revise a canvas. MIT-licensed, plaintext JSON scene format. `describe_scene` and `get_canvas_screenshot` let the agent verify its own layout before claiming it is correct.
76
- - Playwright MCP ([`microsoft/playwright-mcp`](https://github.com/microsoft/playwright-mcp)) for browser-side verification, driven off the accessibility tree rather than screenshots.
77
- - Chrome DevTools MCP ([`ChromeDevTools/chrome-devtools-mcp`](https://github.com/ChromeDevTools/chrome-devtools-mcp)) for live frontend debugging: performance traces, network inspection, and DOM and CSS inspection.
78
- - Claude Design as the ceiling option for codebase extraction or polished handoff bundles. See [Claude Design](../../wiki/claude/design.md).
30
+ Prose stays the source of truth, with a visual render as a feedback surface. Human edits to these derived artifacts are review annotations.
79
31
 
80
- ### Skills
32
+ - Excalidraw canvas server plus the community [`yctimlin/mcp_excalidraw`](https://github.com/yctimlin/mcp_excalidraw) MCP, where the agent draws, reads back, and revises
33
+ - Playwright MCP ([`microsoft/playwright-mcp`](https://github.com/microsoft/playwright-mcp)) for browser checks off the accessibility tree
34
+ - Chrome DevTools MCP ([`ChromeDevTools/chrome-devtools-mcp`](https://github.com/ChromeDevTools/chrome-devtools-mcp)) for traces, network, DOM, and CSS
35
+ - Claude Design for codebase extraction or a polished handoff bundle
36
+ - A frontend design skill: [pbakaus/impeccable](https://github.com/pbakaus/impeccable), [nextlevelbuilder/ui-ux-pro-max-skill](https://github.com/nextlevelbuilder/ui-ux-pro-max-skill), or Anthropic's `frontend-design`
81
37
 
82
- - Everything from tier 0
83
- - A frontend design skill to steer visual quality. Pick one of [pbakaus/impeccable](https://github.com/pbakaus/impeccable), which bundles curated anti-pattern references and is the strongest default, [nextlevelbuilder/ui-ux-pro-max-skill](https://github.com/nextlevelbuilder/ui-ux-pro-max-skill), which maps product type to UI rules through a reasoning engine, or Anthropic's `frontend-design` plugin.
38
+ Impeccable keeps its own root `DESIGN.md` and `PRODUCT.md`, separate from `canon/DESIGN.md`. Use Excalidraw for agent-driven layout iteration, and Claude Design for one extraction pass or the final handoff. Most projects pick one.
84
39
 
85
- ### When to pick
86
-
87
- - UI is a meaningful part of the product but not the entire product
88
- - Stakeholders want to see layout proposals before code exists
89
- - Design decisions benefit from visual inspection
90
- - A single contributor owns both design and implementation
91
-
92
- ### Excalidraw vs Claude Design in tier 1
93
-
94
- The two tools solve non-overlapping halves of the visual companion problem.
95
-
96
- Excalidraw gives the agent a canvas it can read back over MCP. Output is agent-facing and persists as a JSON file in the repo. Pick it when Claude Code needs to iterate on layouts autonomously, not when a human needs polished review artifacts.
97
-
98
- Claude Design reads the raw codebase without prose curation and produces a polished handoff bundle with chat history. Weekly quota makes it expensive, so reserve it for the extraction pass or the final handoff moment, not daily iteration.
99
-
100
- Most tier 1 projects pick one. Projects with a messy codebase and no curated `DESIGN.md` may want Claude Design once for the initial extraction.
40
+ Pick tier 1 when stakeholders want layouts before code and one contributor owns design and implementation.
101
41
 
102
42
  ## Tier 2: visual as source of truth
103
43
 
104
- Design happens in a graphical tool. `canon/DESIGN.md` either regenerates from the graphical source or takes a secondary role as agent-facing summary. Implementation follows the graphical source, either through an MCP round-trip or a one-way handoff bundle. Fits teams with a dedicated designer or projects where design iteration outpaces code changes.
105
-
106
- ### Seed shape
107
-
108
- `canon/DESIGN.md` becomes a generated artifact. A top-of-file note identifies the upstream source, either a Claude Design project ID or a Figma file URL. Manual edits in the seed carry a warning tag because they will not survive regeneration.
109
-
110
- ### Tools
111
-
112
- - Figma desktop app with the official [Figma Dev Mode MCP](https://www.figma.com/blog/introducing-claude-code-to-figma/) for teams with a dedicated designer already on Figma. Bidirectional sync and Code to Canvas capture of a running Claude Code UI into editable Figma frames.
113
- - Claude Design with its Claude Code handoff bundle for teams without an existing Figma investment and for solo founders or PMs driving design themselves. One-way handoff, no bidirectional sync. See [Claude Design](../../wiki/claude/design.md).
114
- - Playwright and Chrome DevTools MCPs as in tier 1
44
+ Design happens in a graphical tool, and implementation follows it. `canon/DESIGN.md` becomes generated, with a top note naming the upstream Claude Design project ID or Figma file URL.
115
45
 
116
- ### Skills
46
+ - Figma desktop with the official [Figma Dev Mode MCP](https://www.figma.com/blog/introducing-claude-code-to-figma/) for teams with a designer already on Figma. It syncs both ways and captures a running UI into editable frames.
47
+ - Claude Design with its Claude Code handoff bundle for teams without Figma. The handoff is one-way.
48
+ - The Playwright and Chrome DevTools MCPs from tier 1
117
49
 
118
- - Everything from tier 1
119
-
120
- ### When to pick
121
-
122
- - UI is the product or a major differentiator
123
- - A dedicated designer is on the project or will join
124
- - Design review happens in the graphical tool rather than in code
125
- - Design changes more often than implementation
126
-
127
- ### Figma vs Claude Design in tier 2
128
-
129
- Figma wins when a dedicated designer already owns a Figma file, external collaborators expect Figma for review, or the workflow needs the agent to capture a running Claude Code UI and push it back as editable frames via Code to Canvas. Figma's MCP is bidirectional.
130
-
131
- Claude Design wins when nobody on the team has a Figma workflow yet and the handoff bundle with chat history is the intended implementation path. Claude Design's handoff is one-way, and its quota limits daily iteration, so it is most useful for the initial extraction pass and the final handoff moment rather than day-to-day design work.
50
+ Pick tier 2 when the UI is the product, a designer owns it, and design changes more often than code.
132
51
 
133
52
  ## Decision guide
134
53
 
135
- Four questions. Each yes weighs toward a higher tier.
54
+ Count yes answers to four questions. Zero or one is tier 0, two or three is tier 1, and four is tier 2.
136
55
 
137
56
  - Is the UI the product rather than a thin interface over a backend?
138
57
  - Do non-engineers review visual proposals before code exists?
139
58
  - Is there a dedicated designer, or will there be one?
140
59
  - Does design iterate independently of code?
141
-
142
- Zero or one yes: tier 0. Two or three: tier 1. Four: tier 2.
143
-
144
- Resist over-tiering early. Moving up is cheap because tiers stack. Moving down means abandoning tooling and confusing collaborators.
145
-
146
- ## References
147
-
148
- - [Claude Design](../../wiki/claude/design.md): first-party hosted design product and handoff bundle
149
- - `canon/context/claude-plugin/skill-strategy/overview.md`: how to decide between workflow and domain-knowledge skills
150
- - [`microsoft/playwright-mcp`](https://github.com/microsoft/playwright-mcp): browser automation MCP used in tier 1 and tier 2
151
- - [`ChromeDevTools/chrome-devtools-mcp`](https://github.com/ChromeDevTools/chrome-devtools-mcp): live frontend debugging MCP used in tier 1 and tier 2
152
- - [`yctimlin/mcp_excalidraw`](https://github.com/yctimlin/mcp_excalidraw): community MCP server behind the tier 1 visual companion
@@ -6,15 +6,13 @@ category: Workflow
6
6
 
7
7
  # Zshrc aliases for Claude Code
8
8
 
9
- Claude Code auto-discovers the toolkit plugin from `claude/.claude-plugin/plugin.json` when run inside the toolkit repository. Outside the toolkit, that discovery does not fire, so a separate set of aliases bakes in `--plugin-dir` explicitly. Two parallel sets cover both cases.
9
+ Claude Code auto-discovers the toolkit plugin inside the toolkit repository. Outside it, the `clp` aliases pass `--plugin-dir` explicitly.
10
10
 
11
11
  ## The aliases
12
12
 
13
- `bun run bootstrap` installs this block. It owns the canonical copy in `scripts/core/bootstrap.sh`, appends it to `~/.zshrc` wrapped in the two marker comments below, and sets `TOOLKIT` to the cloned path. The markers are how a re-run recognizes its own block and skips.
13
+ `bun run bootstrap` appends this block to `~/.zshrc` between the two marker comments and sets `TOOLKIT` to the cloned path. A re-run skips a block it finds and never updates it. To pick up a newer alias, delete the marked block and re-run, or paste the new lines in.
14
14
 
15
- Skipping means a re-run never updates a block already installed, so a shell that ran the bootstrap before the canonical block grew keeps whatever it got that day. To pick up an alias added since, delete the marked block and re-run the bootstrap, or paste the new lines into the block by hand.
16
-
17
- A block installed by hand before the managed one has no markers. Bootstrap detects that case through the `alias clp=` line instead, warns, and leaves the block untouched rather than appending a second copy. To switch over, delete the hand-rolled block and re-run the bootstrap.
15
+ A hand-written block without markers is detected through its `alias clp=` line, and bootstrap warns and leaves it. Delete it and re-run to switch.
18
16
 
19
17
  ```zsh
20
18
  # >>> canon aliases >>>
@@ -34,46 +32,29 @@ alias clpac='clpa --cwd .'
34
32
  # <<< canon aliases <<<
35
33
  ```
36
34
 
37
- The block sits after any `PATH` mutations and the `claude` CLI install. Zsh expands aliases recursively on the first word, so `clr`, `clc`, `clw`, and `cls` inherit their base through `cl`, and `clpc`, `clps`, and `clpa` inherit `--plugin-dir` through `clp`. `clpac` inherits it one level further, through `clpa`. `$TOOLKIT` expands at invocation time, so updating the variable and re-sourcing reroutes all `clp` calls without touching the alias definitions.
38
-
39
- To opt out, delete the block between the two markers. Bootstrap re-adds it on the next run, so skip that step by running `bun install` and `bun link` yourself instead.
40
-
41
- The block itself works unchanged in bash, and what differs is the file it goes in and who writes it. Paste it into `~/.bashrc` by hand, since `bun run bootstrap` appends to `~/.zshrc` alone and a run from a bash shell installs nothing that shell reads. Bash re-checks the first word of an alias expansion the way zsh does, so the `cl` and `clp` chains below still inherit their base, and the `bun install` and `bun link` step above is the opt-out either shell takes.
35
+ To opt out, delete the block and run `bun install` and `bun link` yourself. Bash takes the block unchanged in `~/.bashrc`, pasted by hand. Bootstrap writes `~/.zshrc` alone.
42
36
 
43
37
  ## What each one does
44
38
 
45
- `cl`, `clw`, and `cls` carry no explicit plugin dir and start a fresh session. Use them inside the toolkit repository, where Claude Code auto-discovers the plugin from `claude/.claude-plugin/plugin.json`. Loading `--plugin-dir` on top of auto-discovery registers every skill twice and produces duplicate entries in the slash command list.
46
-
47
- `clr` and `clc` resume a session rather than launching one, so auto-discovery never fires for them. A resume restores whatever plugin registration the session started with and recomputes nothing, which a bare `claude --resume` demonstrated by holding a stale registration through a full process restart until `--plugin-dir` was passed on the resume itself.
48
-
49
39
  - `cl`: plain session in the current directory
50
- - `clr`: opens the `/resume` picker scoped to the current directory. Trailing arguments filter by name. `clr auth` limits results to sessions containing "auth". Resuming does not reload the plugin, so reach for `clpc` when the session needs it.
51
- - `clc`: jumps straight into the most recent session for the current directory. No picker. Faster than `clr` when the terminal closed and you want back into the same session. Resuming does not reload the plugin here either.
52
- - `clw`: creates a worktree under `.claude/worktrees/<name>/` on a fresh branch and starts a Claude Code session in it. Pass the worktree name as the trailing arg: `clw feat-auth`.
53
- - `cls`: pins the session to Sonnet instead of the default Opus. Use for routine work where Opus cost is not justified.
40
+ - `clr`: opens the `/resume` picker for the current directory. `clr auth` filters to sessions containing "auth".
41
+ - `clc`: resumes the most recent session for the current directory, no picker
42
+ - `clw`: creates a worktree under `.claude/worktrees/<name>/` on a fresh branch and starts a session there, as in `clw feat-auth`
43
+ - `cls`: pins the session to Sonnet
54
44
 
55
- The `clp` family bakes in `--plugin-dir`. Use it outside the toolkit repository, where auto-discovery does not fire, and for any resume that needs the plugin.
45
+ The `clp` family loads the plugin explicitly.
56
46
 
57
- - `clp`: session with the toolkit plugin loaded explicitly
58
- - `clpc`: jumps straight into the most recent session for the current directory with the plugin loaded. The `clp` mirror of `clc`, and the one resume that carries the plugin.
47
+ - `clp`: session with the toolkit plugin loaded
48
+ - `clpc`: resumes the most recent session with the plugin loaded
59
49
  - `clps`: `clp` pinned to Sonnet
60
- - `clpa`: opens the agent view with the plugin loaded, covering background sessions from every directory
61
- - `clpac`: the same view filtered to background sessions started under the current directory
50
+ - `clpa`: opens the agent view with the plugin loaded, across every directory
51
+ - `clpac`: the same view filtered to the current directory
62
52
 
63
53
  ## When to use which
64
54
 
65
- Use `cl` by default when working in the toolkit repository. Auto-discovery loads the plugin and the two-key alias keeps it short.
66
-
67
- Use `clp` in any other repository where you want the toolkit skills available. Without the flag, those repos do not load the plugin.
68
-
69
- Use `cls` or `clps` to save Opus usage on routine sessions. Switch mid-session with `/model` to avoid restarting.
70
-
71
- Use `clw <name>` for features that will take more than one session. A worktree isolates the branch, the transcripts, and the `/resume` history. See [Claude Code and git worktrees](../../wiki/claude/worktrees.md) for the worktree mechanism, and [Parallel features](parallel-features.md) for fan-out rules.
72
-
73
- Use `clc` to resume the last session without a picker. Use `clr` when you have several sessions and need to pick by name or recency. Reach for `clpc` over `clc` wherever the resumed session needs the plugin, inside the toolkit repository as well as outside it, since neither resume re-runs discovery.
74
-
75
- Use `clpa` to see what is running across every directory, and `clpac` when only the current project matters.
76
-
77
- ## Why not a function
55
+ Use `cl` inside the toolkit repository and `clp` in any other repository that needs the toolkit skills. A resume does not reload the plugin, so use `clpc` over `clc` wherever the resumed session needs it.
78
56
 
79
- A function with subcommand dispatch (`cl r`, `cl c`, `cl w foo`) was considered and rejected. It loses shell completion and adds a layer of indirection. Separate aliases are self-contained and the shared `cl`/`clp` prefixes make them easy to recall.
57
+ - Use `cls` or `clps` for routine sessions, and `/model` to switch mid-session
58
+ - Use `clw <name>` for features that take more than one session. See [Claude Code and git worktrees](../../wiki/claude/worktrees.md) and [Parallel features](parallel-features.md).
59
+ - Use `clc` to resume without a picker and `clr` to pick by name
60
+ - Use `clpa` for every directory and `clpac` for the current project
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/canon",
3
3
  "type": "module",
4
- "version": "5.39.0",
4
+ "version": "5.41.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": {
@@ -2,6 +2,10 @@
2
2
  * The inspector's own rules. Every value reads a chrome token, the same set
3
3
  * shell.css reads. Each field's glyph sits inside its box, so two fields share
4
4
  * a row without a label column eating the width.
5
+ *
6
+ * These rules share one class namespace with shell.css, where the Theme tab
7
+ * owns `.token`, so a new class here takes its own prefix, as `.token-option`
8
+ * does.
5
9
  */
6
10
 
7
11
  .inspector-section {
@@ -4,6 +4,12 @@
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <title>Canvas</title>
7
+ <!--
8
+ Bun's bundler resolves every href and src here at start. A root-relative
9
+ route the server answers, such as /api/icon.svg, fails with "Could not
10
+ resolve" and leaves / serving an empty 200, so main.tsx points the icon
11
+ at it on load.
12
+ -->
7
13
  <link rel="icon" href="data:," />
8
14
  <link rel="stylesheet" href="./client/shell.css" />
9
15
  </head>
@@ -6,6 +6,10 @@ export const GIT_CASES: readonly SkillCase[] = [
6
6
  prompt: 'Come up with a conventional name for this branch.',
7
7
  expect: 'git-branch',
8
8
  },
9
+ {
10
+ prompt: 'I am on main and about to start a feature, create its branch.',
11
+ expect: 'git-branch',
12
+ },
9
13
  {
10
14
  prompt: "Turn what's staged into one commit message.",
11
15
  expect: 'git-commit',
@@ -13,8 +13,8 @@ import { SURFACE_ROOTS } from '@/roots/surface'
13
13
  * `src/`, `scripts/`, and bare `docs/` are deliberately absent. A body naming
14
14
  * one of those is describing the reader's own tree, so listing them would
15
15
  * report a correct citation on every run and bury the defect this measures.
16
- * `docs/agents/` is the exception, being the CLI contract pages that exist
17
- * here alone.
16
+ * `docs/agents/` is the exception, being the reference pages for verbs run by
17
+ * hand, which exist here alone.
18
18
  *
19
19
  * The context folder carries both surface-root spellings, since this list
20
20
  * decides whether a shipped body names this repository's own tree and a body
package/src/cli/ui.ts CHANGED
@@ -162,6 +162,8 @@ export async function select<Value>(opts: {
162
162
  message: string
163
163
  options: { value: Value; label: string; hint?: string }[]
164
164
  nonInteractiveDefault?: boolean
165
+ /** Replaces the "Pass an argument" clause of the TTY refusal. */
166
+ ttyHint?: string
165
167
  }): Promise<Value> {
166
168
  const { message, options } = opts
167
169
  const { GREEN, GREY, NC, RED, WHITE } = palette(process.stderr)
@@ -196,7 +198,7 @@ export async function select<Value>(opts: {
196
198
  return new Promise<Value>((resolve) => {
197
199
  if (!process.stdin.isTTY) {
198
200
  process.stderr.write(
199
- `${GREY}│${NC} ${RED}✗${NC} ${message} requires a TTY. Pass an argument or set CANON_NON_INTERACTIVE=1.\n${GREY}└${NC}\n`,
201
+ `${GREY}│${NC} ${RED}✗${NC} ${message} requires a TTY. ${opts.ttyHint ?? 'Pass an argument'} or set CANON_NON_INTERACTIVE=1.\n${GREY}└${NC}\n`,
200
202
  )
201
203
  process.exit(1)
202
204
  }
@@ -77,6 +77,11 @@ const CLIENT_DEPS = ['preact', '@preact/signals'] as const
77
77
  * the bundler then answers `/` with an empty page rather than an error. The
78
78
  * caller passes the package root the binary runs from, never the served
79
79
  * project's, so a global install serving a target finds its own copies.
80
+ *
81
+ * `Bun.resolveSync` and `createRequire(...).resolve` fall back to Bun's global
82
+ * install cache when no ancestor of the starting directory holds a
83
+ * `node_modules`, so a presence check from such a directory passes where the
84
+ * bundler fails. The package root always sits under one.
80
85
  */
81
86
  export function missingClientDeps(from: string): Refused | undefined {
82
87
  const missing = CLIENT_DEPS.filter((name) => {
@@ -161,6 +166,24 @@ export function register(program: Command): void {
161
166
  'it, and layout.json beside them holds each frame box. Edit a frame by',
162
167
  'editing its file, and an open canvas reloads that frame.',
163
168
  '',
169
+ 'Reasons, on stderr or in the JSON record:',
170
+ ' no-root no .canon/canvas/ folder under the root',
171
+ ' no-page, no-frame the target is not on disk',
172
+ ' invalid-name a name or target breaks the page or frame form',
173
+ ' invalid-size a width or height is not a whole number of pixels',
174
+ ' invalid-position an x or y is not a whole number',
175
+ ' exists the page or frame is already there',
176
+ ' malformed-layout layout.json does not read as a frame map',
177
+ ' busy another write holds the page',
178
+ ' invalid-address an element address is not a path of positions',
179
+ ' address-mismatch the file builds elements the address does not',
180
+ ' stale-address the file changed since the address was served',
181
+ ' invalid-edit an edit key or value is not one the writer takes',
182
+ ' not-text-only text targets an element holding other elements',
183
+ ' missing-client-deps preact or @preact/signals does not resolve, so run bun install',
184
+ ' no-server no canvas server answers at the address',
185
+ ' capture-failed a frame did not render, with the font or browser reason',
186
+ '',
164
187
  'Examples:',
165
188
  ' canon canvas serve',
166
189
  ' canon canvas page add drafts',
@@ -58,6 +58,20 @@ export function register(program: Command): void {
58
58
  '--width <px>',
59
59
  'Viewport width in pixels, so a media query resolves at that width',
60
60
  )
61
+ .addHelpText(
62
+ 'after',
63
+ [
64
+ '',
65
+ 'The selector has no default, since the element to crop to belongs to the',
66
+ 'page. Each element holding text inside it must resolve its first font',
67
+ 'family, and a generic keyword such as monospace or system-ui fails,',
68
+ 'since it resolves differently on each machine. The user agent sets',
69
+ 'monospace on pre, code, kbd, and samp, so each needs its own',
70
+ 'font-family. --width takes one whole number and sets the viewport, so',
71
+ 'run it once per breakpoint with its own --out.',
72
+ '',
73
+ ].join('\n'),
74
+ )
61
75
  .action(
62
76
  async (
63
77
  source: string,
@@ -319,6 +319,11 @@ export function register(program: Command): void {
319
319
  ' 0 the file was read',
320
320
  ' 1 refused, with the reason on stderr',
321
321
  '',
322
+ 'Reasons:',
323
+ ' no-claude-md the tree has no always-loaded file',
324
+ ' no-rules no path-scoped rule under .claude/rules/, so every path would',
325
+ ' report as uncovered',
326
+ '',
322
327
  'Reports rather than gates. Whether a bullet belongs in a rule is the',
323
328
  'judgment canon/context/context-model.md states, and naming a path is evidence for it',
324
329
  'rather than the answer.',
@@ -432,6 +437,8 @@ export function register(program: Command): void {
432
437
  ' 1 refused, with the reason on stderr',
433
438
  ' 2 a skill folder carries no REQUIREMENT.md',
434
439
  '',
440
+ 'Refuses when neither claude/skills/ nor .claude/skills/ resolves.',
441
+ '',
435
442
  'Only a missing REQUIREMENT.md sets a failing exit code. Name, description,',
436
443
  'folder, and requirement-section findings are advisory.',
437
444
  '',
@@ -460,6 +467,10 @@ export function register(program: Command): void {
460
467
  ' 0 history was read, whether or not a body moved',
461
468
  ' 1 the question could not be answered, with the reason on stderr',
462
469
  '',
470
+ 'Refuses on an absent shipped corpus, an absent repository, and a ref',
471
+ 'matching no commit, each with its own reason. A target loading the',
472
+ 'plugin from a marketplace cache has no history, so it refuses there.',
473
+ '',
463
474
  'A moved body means the file changed, not that a session holds a stale',
464
475
  'copy. Passing a ref older than the oldest load over-reports, which is',
465
476
  'the safe direction. Confirm a name by reading the body.',
@@ -502,6 +513,8 @@ export function register(program: Command): void {
502
513
  ' 1 refused, with the reason on stderr',
503
514
  ' 2 at least one citation is unqualified',
504
515
  '',
516
+ 'Refuses a tree carrying neither claude/skills/ nor .claude/skills/.',
517
+ '',
505
518
  'Reports rather than gates. A toolkit-scoped instruction is sometimes',
506
519
  'meant for a session in this repository, so the verdict is a reading',
507
520
  'and the repair is to name the owner in the sentence.',
@@ -76,6 +76,12 @@ export function register(program: Command): void {
76
76
  'wait condition, and the timing a beat lacks, and the timing is a',
77
77
  'starting point you tune, which is why a recompile refuses to overwrite.',
78
78
  '',
79
+ 'Reasons:',
80
+ ' draft-missing no file at the draft path',
81
+ ' draft-unreadable no beat sheet in the draft, or a sheet with no beats',
82
+ ' plan-exists a plan is already there and --force was not passed',
83
+ ' plan-unresolved a URL or a target is still empty, named field by field',
84
+ '',
79
85
  'Exit codes:',
80
86
  ' 0 a plan was written',
81
87
  ' 1 refused, with the reason on stderr',
@@ -114,9 +120,20 @@ export function register(program: Command): void {
114
120
  'Writes mp4 beside the webm when ffmpeg is on PATH, and skips it',
115
121
  `otherwise without failing the run. Install it with: ${INSTALL_CONVERTER}`,
116
122
  '',
123
+ 'Beside the webm it writes <video>.timeline.json, an entries array with',
124
+ 'one entry per step carrying index, kind, and startMs and endMs from the',
125
+ 'first frame. A click, fill, or hover entry adds box, the target in',
126
+ 'viewport pixels. The still is the hero beat, or the last beat.',
127
+ '',
117
128
  'A gif is opt-in behind --gif, since it is far larger than the webm',
118
129
  'and only a host that strips video needs one. GitHub is that host.',
119
130
  '',
131
+ 'Reasons:',
132
+ ' no-output-requested --no-video with --no-still, which would write nothing',
133
+ ' plan-unreadable the plan is not JSON, or a step names a kind nothing drives',
134
+ ' browser-missing the browser binary is not installed',
135
+ ' engine-missing the browser package did not resolve',
136
+ '',
120
137
  'Exit codes:',
121
138
  ' 0 the recording and the still were written',
122
139
  ' 1 refused, with the reason on stderr',
@@ -149,6 +166,11 @@ export function register(program: Command): void {
149
166
  'Needs ffmpeg on PATH, the same binary demo run already shells to for',
150
167
  `mp4 and gif conversion. Install it with: ${INSTALL_CONVERTER}`,
151
168
  '',
169
+ 'Reasons:',
170
+ ' video-missing no file at the video path',
171
+ ' converter-missing ffmpeg is not on PATH, so nothing was extracted',
172
+ ' extraction-failed ffmpeg exited non-zero, with its stderr in the message',
173
+ '',
152
174
  'Exit codes:',
153
175
  ' 0 frames were written',
154
176
  ' 1 refused, with the reason on stderr',
@@ -56,6 +56,12 @@ export function register(program: Command): void {
56
56
  ' 1 refused, with the reason on stderr',
57
57
  ' 2 at least one advisory was published',
58
58
  '',
59
+ 'Reasons:',
60
+ ' no-manifest no package.json in the project',
61
+ ' no-lockfile dependencies were never resolved, so install before auditing',
62
+ ' no-record the lookup returned no record that parses, as an unreachable',
63
+ ' index does',
64
+ '',
59
65
  'Examples:',
60
66
  ' canon deps audit',
61
67
  ' canon deps audit --json',
@@ -4,7 +4,7 @@ import type { Command } from 'commander'
4
4
  import { INSTALL_BROWSER, isEngineMissing } from '@/browser/engine'
5
5
  import type { DriverRefusal } from '@/driver/drive'
6
6
  import { describeViewport } from '@/driver/probes/viewport'
7
- import { PROBE_NAMES, readDriverPlan } from '@/driver/steps'
7
+ import { PROBE_NAMES, readDriverPlan, type ProbeName } from '@/driver/steps'
8
8
  import type { PlanRefusal } from '@/driver/steps'
9
9
  import {
10
10
  intro,
@@ -55,6 +55,15 @@ const DRIVE_REFUSALS: Record<DriverRefusal, string> = {
55
55
  'drive-failed': 'The drive failed against a reachable page.',
56
56
  }
57
57
 
58
+ const PROBE_NOTES: Record<ProbeName, string> = {
59
+ focus: 'keyboard focus that changes nothing visible, read after a Tab press',
60
+ details:
61
+ 'a details panel off the viewport or a row under the tap minimum, read open',
62
+ 'diagram-geometry':
63
+ 'an SVG label outside its frame, covered by a fill, or on another label',
64
+ 'diagram-strokes': 'an SVG label a stroke crosses, in either paint order',
65
+ }
66
+
58
67
  export function register(program: Command): void {
59
68
  program
60
69
  .command('drive')
@@ -103,7 +112,24 @@ export function register(program: Command): void {
103
112
  ` ${INSTALL_BROWSER}`,
104
113
  '',
105
114
  'Probes:',
106
- ...PROBE_NAMES.map((name) => ` ${name}`),
115
+ ...PROBE_NAMES.map(
116
+ (name) => ` ${name.padEnd(18)}${PROBE_NOTES[name]}`,
117
+ ),
118
+ '',
119
+ 'Reasons:',
120
+ ' no-run-file nothing readable at the run path',
121
+ ' unreadable-plan the run file is not valid JSON',
122
+ ' no-steps the run names no step',
123
+ ' no-probes the run names an empty probe list',
124
+ ' unknown-probe the run names a probe this build does not ship',
125
+ ' bad-step a step is missing what the driver needs to perform it',
126
+ ' no-viewport the run declares no viewport',
127
+ ' no-width the viewport has no width to wrap at',
128
+ ' no-heights the viewport names no height, which is never defaulted',
129
+ ' engine-missing the browser engine is not installed in this project',
130
+ ' browser-missing the engine is installed and its browser binary is not',
131
+ ' server-unreachable nothing answered at the URL, so no state was reached',
132
+ ' drive-failed the drive failed against a reachable page',
107
133
  '',
108
134
  'Step kinds:',
109
135
  ' click <target> press the first element the selector matches',