@erclx/canon 4.53.0 → 4.55.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 (33) hide show
  1. package/README.md +8 -0
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/canon-cli/SKILL.md +1 -1
  4. package/claude/skills/claude-feature/SKILL.md +1 -1
  5. package/claude/skills/claude-orchestrate/references/orchestrator-dispatch.md +7 -3
  6. package/claude/skills/claude-planner/SKILL.md +1 -1
  7. package/claude/skills/context-draft/REQUIREMENT.md +37 -0
  8. package/claude/skills/context-draft/SKILL.md +59 -0
  9. package/claude/skills/create-skill/REQUIREMENT.md +1 -1
  10. package/claude/skills/create-standard/SKILL.md +1 -1
  11. package/claude/skills/index-lookup/REQUIREMENT.md +35 -0
  12. package/claude/skills/index-lookup/SKILL.md +42 -0
  13. package/claude/skills/wireframe-draft/REQUIREMENT.md +38 -0
  14. package/claude/skills/wireframe-draft/SKILL.md +64 -0
  15. package/claude/skills/youtube-transcripts/SKILL.md +1 -1
  16. package/docs/agents/commands.md +1 -0
  17. package/docs/agents/demo.md +11 -8
  18. package/docs/agents/index.md +1 -1
  19. package/docs/agents/indexes.md +17 -1
  20. package/docs/workflow/ai-workflow.md +15 -12
  21. package/governance/rules/core/085-worktrees.md +2 -0
  22. package/package.json +1 -1
  23. package/scripts/core/regen-agent-fixture.sh +235 -0
  24. package/src/claude/cases/authoring.ts +10 -0
  25. package/src/claude/cases/misc.ts +4 -0
  26. package/src/commands/demo.ts +25 -1
  27. package/src/commands/indexes.ts +67 -0
  28. package/src/demo/container.ts +61 -0
  29. package/src/demo/drive.ts +67 -9
  30. package/src/indexes/list.ts +64 -0
  31. package/src/indexes/render.ts +1 -1
  32. package/tooling/claude/seeds/.claude/hooks/index-reminder.sh +1 -1
  33. package/tooling/web/configs/scripts/screenshot.sh +1 -1
package/README.md CHANGED
@@ -10,6 +10,14 @@ canon is a CLI and Claude Code plugin that stops your AI conventions drifting ap
10
10
 
11
11
  The counts above are read from the catalogs when the image is built, so they're what the repo actually ships today.
12
12
 
13
+ ## It runs on itself
14
+
15
+ The workflow this toolkit ships is the workflow that built it. Several Claude Code sessions run at once, each in its own git worktree on its own branch, and each opens its own pull request.
16
+
17
+ ![The canon landing page's session list, with the orchestrator pinned above bands of working and completed sessions, workers and planners in both, each naming what it is doing and the pull request it opened, and one row moving from Working into Completed](demos/agent-view.gif)
18
+
19
+ That is the landing page's own agent view, recorded from a local build by `canon demo run`. The session rows behind it are a real `canon sessions list --json` read. The activity text and the pull request number are transcribed, because no verb here reports either.
20
+
13
21
  ## Why
14
22
 
15
23
  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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "canon",
3
3
  "description": "Automated governance, versioning, and discovery tools for Claude Code.",
4
- "version": "4.53.0",
4
+ "version": "4.55.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -102,4 +102,4 @@ Run `canon tooling sync <stack> <target> --check` for the list resolved against
102
102
 
103
103
  ## Source of truth
104
104
 
105
- - Full semantics live in the toolkit's `.claude/context/tooling.md`, `.claude/context/standards/`, and `.claude/context/claude-plugin/`. This skill is the target-session summary. When they disagree, the context docs win.
105
+ - Full semantics live in the toolkit's own context entries. This skill is the target-session summary. When they disagree, the context docs win.
@@ -93,7 +93,7 @@ Omit empty sections. Do not print `None identified.` in chat.
93
93
 
94
94
  Derive a 2-to-4-word kebab-case slug from the feature description. Write the full plan to `.canon/plans/feature-<slug>.md` at the main worktree root, not the current worktree. Resolve that root the way `claude-worktree` does. Create the directory if it does not exist.
95
95
 
96
- From a linked worktree the file-editing tools refuse that path, so the plan goes out through `Bash`. Send the `mkdir -p` and the heredoc as two plain commands rather than joining them with `&&`, which is refused as compound.
96
+ From a linked worktree, or from a background session sitting at the main root with none entered, the file-editing tools refuse that path, so the plan goes out through `Bash`. Send the `mkdir -p` and the heredoc as two plain commands rather than joining them with `&&`, which is refused as compound.
97
97
 
98
98
  The file follows the template in `${CLAUDE_SKILL_DIR}/../../standards/plan.md`. Copy the shape from there rather than from this body, so one edit to the standard moves every plan.
99
99
 
@@ -157,9 +157,10 @@ controller that hands an operator two chained blocks to paste as separate
157
157
  messages risks both landing as one, where everything after the first
158
158
  command's name is read as that command's own argument and the second command
159
159
  never fires, measured four times out of four on 2026-09-02.
160
- `.claude/context/claude-plugin/skill-lifecycle.md` carries the mechanism. The
161
- fix is what the template above already takes: one message, one command, at
162
- position zero.
160
+ A slash command expands as a user invocation only at position zero of a
161
+ prompt, and a later one in the same text reaches the session as prose instead.
162
+ The fix is what the template above already takes: one message, one command,
163
+ at position zero.
163
164
 
164
165
  ### What the brief may carry
165
166
 
@@ -227,6 +228,9 @@ build shape above reaches `claude-worker`.
227
228
  No branch and no worktree exist here and none is created. A planner writes one
228
229
  gitignored file at the main worktree root, so this shape names the row's task
229
230
  file rather than a branch and opens with the role instead of a worktree call.
231
+ That write meets the isolation guard the same way a linked worktree's
232
+ main-root write does, with no worktree here to redirect it to, so
233
+ `claude-planner` sends it as a `Bash` heredoc rather than through `Write`.
230
234
 
231
235
  ```bash
232
236
  claude --bg --model <model> -n "planner-<project>-<slug>" "Run /canon:claude-planner, then /canon:claude-feature <task>. Your controller is the session whose sessionId is <dispatcher-id>. Resolve its current name from that id through canon sessions list --json, which carries sessionId per row, at the moment you send, and never resolve an addressee by name prefix. Message it when the plan lands, carrying the path and what the task file got wrong, and message it again if you stop on a question."
@@ -21,7 +21,7 @@ from here.
21
21
 
22
22
  ## Where the session stands
23
23
 
24
- - Write one file, the plan, at `.canon/plans/feature-<slug>.md` on the main worktree root. Everything else this session touches is a read.
24
+ - Write one file, the plan, at `.canon/plans/feature-<slug>.md` on the main worktree root. Everything else this session touches is a read. This session holds no worktree, so send the file as a plain `Bash` heredoc past the same main-root refusal a linked worktree meets. The route skips the write-matched hooks, and they are no-ops on a plan file.
25
25
  - Never enter a worktree, never create a branch, and never write a tracked file. A planner that builds has stopped being one, and the row loses the independent read the worker's own session was going to bring to it.
26
26
  - Resolve `.canon/plans/`, `.canon/tasks/`, and `.canon/review/` at the main worktree root. Those folders are gitignored, so a copy beside a linked worktree is absent rather than empty.
27
27
  - Plan the row the launch named. Report a second row met while reading rather than planning it, since which rows run is the controller's call.
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: context-draft
3
+ description: Why a brand-new .claude/context/<domain>.md entry needs a catalog collision check and a confirm step, not the refresh path claude-docs already owns
4
+ ---
5
+
6
+ # Context draft requirement
7
+
8
+ ## Gap
9
+
10
+ Without this skill, a session documenting a domain that has no context entry yet either invents a shape from memory or reaches for the refresh mechanism, which reads an existing entry and a diff and has neither to work from for a domain that never had one. Either way the entry ships with no read of `standards/context.md` and no check against the catalog for a sibling already covering the same ground under a different name.
11
+
12
+ ## Must
13
+
14
+ - Read `standards/context.md` before drafting, since the frontmatter contract and the three-question test are what make the entry arguable against a sibling
15
+ - Check the domain against the existing catalog by slug and by title-and-description, not by slug alone, since a domain can be covered under a name the topic phrase does not guess
16
+ - Default a brand-new entry to a flat file, since a fresh domain never holds the three or more sub-areas the standard requires before it earns a folder
17
+ - Confirm the resolved path and the full content with the user before writing, since placement is a judgment call with no diff to preview it against
18
+ - Run `canon indexes regen` on `.claude/context/` after writing, so the catalog picks up the new domain immediately rather than drifting until the next unrelated regen
19
+
20
+ ## Must not
21
+
22
+ - Refresh or rewrite an existing entry. A domain already covered refuses toward the refresh mechanism.
23
+ - Hand-edit `.claude/context/index.md`. It regenerates from sibling frontmatter, and a hand edit is overwritten on the next regen.
24
+ - Assume this skill's own invocation frequency needs no check. Whether anything reaches for it beyond an author typing its name has no answer at creation time, so a review pass some months in should read that back rather than take the assumption on faith.
25
+
26
+ ## Guards
27
+
28
+ - No domain given: stop and ask what domain the entry should cover.
29
+ - The derived slug already resolves to `.claude/context/<slug>.md` or `.claude/context/<slug>/index.md`: stop and point at the refresh mechanism instead.
30
+ - The catalog already covers the domain under a different name: stop the same way, checked against the titles and descriptions the Placement step already reads.
31
+
32
+ ## Out of scope
33
+
34
+ - Refreshing an existing `.claude/context/<domain>.md` entry against a diff: `claude-docs`
35
+ - Drafting a `.claude/wireframes/<surface>.md` file: `wireframe-draft`
36
+ - Drafting a `docs/*.md` page: `docs-draft`
37
+ - Drafting a standard, a snippet, or a governance rule: `create-standard`, `create-snippet`, `create-rule`
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: context-draft
3
+ description: Drafts a brand-new `.claude/context/<domain>.md` entry against `standards/context.md`, checks the catalog for a name-or-topic collision, decides flat-file placement, confirms with the user, then writes. Use when asked to "write a context entry for X", "document the X domain", "add a context entry for X", or "create a .claude/context page for X" where no existing entry covers the domain. Do NOT use to refresh an existing entry against a diff, which is `claude-docs`.
4
+ ---
5
+
6
+ # Context draft
7
+
8
+ Drafts one brand-new `.claude/context/<domain>.md` entry end to end: read the standard, decide whether the domain is already covered under a different name, confirm the draft with the user, then write.
9
+
10
+ Read these files in parallel:
11
+
12
+ - `${CLAUDE_SKILL_DIR}/../../standards/context.md`: the three questions an entry must answer, its frontmatter, required and expected sections, and what stays out of it
13
+ - `${CLAUDE_SKILL_DIR}/../../standards/markdown.md`: banned words, punctuation, and formatting for all generated text
14
+ - The `write-human` skill: voice, rhythm, and sentence construction for all generated text
15
+
16
+ ## Guards
17
+
18
+ - If no domain is given, stop: `❌ No domain given. Name the domain this entry should cover.`
19
+ - Derive a kebab-case slug from the domain and check whether `.claude/context/<slug>.md` or `.claude/context/<slug>/index.md` already exists. Either resolving means the domain is already covered under that exact name. Stop: `❌ <slug> already has a context entry. Use claude-docs to refresh it instead.`
20
+
21
+ ## Placement
22
+
23
+ - Read `.claude/context/index.md` and check every title and description it lists against the domain. The Guards check above only catches an exact-slug collision, and a domain already covered under a different name still resolves here at no extra cost, since this read already runs.
24
+ - Stop the same way on a match: `❌ <path> already covers this domain under a different name. Use claude-docs to refresh it instead.`
25
+ - Default a brand-new entry to a flat file, `.claude/context/<slug>.md`. A domain starts as one page's worth of narrative, and the standard only splits it into a folder once it holds three or more sub-areas, which a fresh domain never does on day one.
26
+
27
+ ## Draft
28
+
29
+ - Read the domain's own folders and files well enough to answer the standard's three questions: where things live, why they are that way, and how to add one more of what the domain holds.
30
+ - Draft `title` and `description` frontmatter, then `## Overview`, `## Layout` (folder ownership lines only, never a file-by-file list), and `## Decisions` or `## Gotchas` wherever the domain's history supplies a non-obvious choice or a workaround worth preserving. Omit an expected section with nothing to put in it rather than padding it.
31
+ - Draft against `${CLAUDE_SKILL_DIR}/../../standards/context.md`'s template and ordering: `Overview`, `Layout`, `Decisions`, `Gotchas`, then anything else.
32
+
33
+ ## Confirm
34
+
35
+ - Show the resolved path and the full drafted content before writing.
36
+ - Confirm both with the user. This skill waits for that answer rather than treating the tool permission dialog as the gate, since placement here is a judgment call with no diff to preview it against.
37
+
38
+ ## Write
39
+
40
+ - Write the file at the confirmed path, creating the folder when it is absent.
41
+ - Run `canon markdown audit <path>`.
42
+ - Run `canon indexes regen .claude/context`, so the domain appears in `.claude/context/index.md` immediately rather than drifting until the next unrelated regen.
43
+
44
+ ## Response format
45
+
46
+ ### Preview
47
+
48
+ **Domain:** `<domain>`
49
+ **Placement:** `<path>`
50
+
51
+ ```markdown
52
+ <drafted frontmatter and body>
53
+ ```
54
+
55
+ ### After confirmation
56
+
57
+ ```plaintext
58
+ ✅ Drafted: <path>
59
+ ```
@@ -35,4 +35,4 @@ A third failure is a folder created for a moment a rule, a verb, or an existing
35
35
  ## Out of scope
36
36
 
37
37
  - Editing an existing skill, which the authoring standard and the skill's own requirement govern
38
- - Whether anything ends up invoking the new skill: the usage census in `.claude/context/claude-plugin/skill-strategy.md`, read once the skill has run
38
+ - Whether anything ends up invoking the new skill: the usage census the toolkit's own context entry tracks, read once the skill has run
@@ -23,7 +23,7 @@ Creates one standard file. Read these files in parallel:
23
23
 
24
24
  Emit the full path on its own line.
25
25
 
26
- - In the toolkit: this is the authoring source. Remind the user to run `bun run check` to regenerate the consumed copy under `.claude/`. That pass also regenerates the `standards/index.md` entry, and the user adds a row to the standards table in `.claude/context/standards/overview.md`.
26
+ - In the toolkit: this is the authoring source. Remind the user to run `bun run check` to regenerate the consumed copy under `.claude/`. That pass also regenerates the `standards/index.md` entry, and the user adds a row to the standards table in `.claude/context/standards/overview.md`. <!-- canon-allow-reference: audience-branched, only a toolkit-repo reader reaches this line and that reader can open the path -->
27
27
  - In a project: the file is project-local and no toolkit command touches it. Remind the user to copy it to the toolkit repo, under `standards/<slug>.md`, if it should ship to every project.
28
28
 
29
29
  Separate the two by whether the folder already held toolkit-authored standards before this write, since a project authoring its own uses the same path.
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: index-lookup
3
+ description: Why a topic search over the tracked index catalog needs its own skill, and why a folder outside `list`'s walk gets a named pointer instead of a search hit
4
+ ---
5
+
6
+ # Index lookup requirement
7
+
8
+ ## Gap
9
+
10
+ Without this skill, `canon indexes list --json` exists as a verb with nobody wrapping it into a topic-search request. A session either greps the tree by hand, which misses everything the catalog already states in `title` and `description` rather than in file contents, or answers from whichever folder it happened to open, which reports a topic as undocumented when the topic only sits outside the walked corpus: a gitignored indexed folder such as `.canon/tasks/` or `.canon/memory/`, or a README-based record catalog such as `.canon/groundwork/`, neither of which `list` ever reaches.
11
+
12
+ ## Must
13
+
14
+ - Run `canon indexes list --json` and match the topic against every entry's `title`, `description`, and `path`
15
+ - Check whether the project carries a gitignored indexed folder or a README-based record catalog outside `list`'s walk before naming any of them, since a folder absent from the project names nothing
16
+ - Report zero hits inside the walked corpus as zero hits, never as "not documented," since a folder outside the walk can still hold the answer
17
+
18
+ ## Must not
19
+
20
+ - Search file contents. A miss against `title`, `description`, and `path` is the whole answer, and grepping the tree is a different request.
21
+ - Extend `canon indexes list` to read a `README.md` catalog or to bypass the gitignore filter. Both are accepted trade-offs the verb already carries, and widening either is a CLI change, not a skill-body change.
22
+ - Regenerate an index on its own initiative. Reporting a stale-looking entry is this skill's job, and writing the fix belongs to `canon indexes regen`.
23
+ - Fire from a request to browse a file whose path is already known, or to search source code, both of which this skill answers nothing for
24
+ - Assume this skill's own invocation frequency needs no check. The reminder hook names it beside `canon indexes list --json`, and a session recognizing a topic-lookup request can trigger it by description, but whether anything reaches for it beyond an operator typing its name has no answer at creation time, so a review pass some months in should read that back rather than take it on faith.
25
+
26
+ ## Guards
27
+
28
+ - `canon indexes list` does not resolve on an older install. Report that rather than falling back to a manual walk, since a hand-rolled walk here restates the CLI logic this skill exists to call.
29
+ - A folder failing frontmatter validation drops out of `entries` and its message lands in `errors`, per the verb's own per-folder isolation. Report the error alongside the match rather than treating it as a reason to stop.
30
+
31
+ ## Out of scope
32
+
33
+ - Regenerating or scaffolding an index, which `canon indexes regen` and the `setup-indexes` skill own
34
+ - Reading a folder's contents once a hit names it, which is an ordinary file read rather than part of the lookup
35
+ - Chaining this skill into `setup-indexes`'s bootstrap flow as a verification step. Left independent: `setup-indexes` already validates through `canon indexes regen --dry-run`, and a second skill call inside a bootstrap flow duplicates a check that already runs.
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: index-lookup
3
+ description: Answers a topic search over every tracked index.md catalog in the project by running `canon indexes list --json` and matching the topic against each entry's title, description, and path, then reports the hits or names why there are none. Names the gitignored task, memory, and diagram catalogs and the groundwork and intake record folders as pointers outside its walked corpus, so it never claims a topic is undocumented when the topic only sits there. Use when asked "where is X documented", "is there a page about X", "search the docs for X", "find the index entry for X", or "what covers X". Do NOT use to grep source code, browse a file whose path is already known, or regenerate an index, which is `canon indexes regen`.
4
+ ---
5
+
6
+ # Index lookup
7
+
8
+ Answers a topic search over the project's tracked `index.md` catalogs, not a text search over file contents. Matches a topic string against every entry's `title`, `description`, and `path`, drawn from wherever `canon indexes list --json` walks.
9
+
10
+ ## Steps
11
+
12
+ 1. Resolve the project root with `git rev-parse --show-toplevel`, falling back to the working directory when the read fails.
13
+ 2. Run `canon indexes list --json` from that root.
14
+ 3. Read `errors` first. A folder failing frontmatter validation drops out of `entries` and lands its message in `errors` instead, per the verb's own per-folder isolation, so report an error alongside the match rather than reading it as a reason to stop.
15
+ 4. Match the topic case-insensitively as a substring against each entry's `title`, `description`, and `path`. Report every match. A lookup with no ranking is honest about what it found, and a single best guess is not.
16
+ 5. Check whether the project carries a gitignored indexed folder or a README-based record catalog outside `list`'s walk: `.canon/tasks/`, `.canon/memory/`, `.canon/diagrams/`, `.canon/groundwork/`, `.canon/intake/`. Test each for existence and at least one file inside it before naming it. A folder absent from this project names nothing.
17
+ 6. Report the hits, plus a pointer to any outside-the-walk folder found present, per Output below.
18
+
19
+ ## Rules
20
+
21
+ - Report zero hits as zero hits, never as "not documented." The topic can sit inside a folder step 5 found and named, so an empty walked corpus is a fact about the search, not about the project.
22
+ - Never search file contents. A miss against `title`, `description`, and `path` is the whole answer, and a request that needs a text search calls for grepping the tree instead.
23
+ - Never regenerate or edit an index. Report an entry that reads as stale rather than running `canon indexes regen` on this skill's own initiative.
24
+ - Report an absent `canon indexes list` subcommand rather than falling back to a manual walk. It ships with the CLI, so a project on an older install meets a missing verb, and a hand-rolled walk here restates the CLI logic this skill exists to call instead.
25
+
26
+ ## Output
27
+
28
+ ```plaintext
29
+ Index lookup: "<topic>"
30
+
31
+ Hits:
32
+ - <path>: <title> — <description>
33
+
34
+ Outside the walked corpus (check by hand):
35
+ - <folder>: <one-line reason it sits outside list's walk>
36
+ ```
37
+
38
+ Replace the "Hits" block with `No catalog entry matched "<topic>".` when nothing matched. Omit "Outside the walked corpus" when the project carries none of the five folders from step 5, or when every one it carries is empty.
39
+
40
+ ## Reference
41
+
42
+ Run `canon docs indexes` for the `canon indexes list` flags, exit codes, and JSON shape. It resolves from the toolkit rather than from the target's own tree.
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: wireframe-draft
3
+ description: Why a brand-new .claude/wireframes/<surface>.md file needs a real draft and a tier-detect step, not the stub the wireframe-sweep already writes
4
+ ---
5
+
6
+ # Wireframe draft requirement
7
+
8
+ ## Gap
9
+
10
+ Without this skill, a session drafting a wireframe for a surface with no file yet either invents an ASCII layout from memory with no read of `standards/wireframes.md`, or waits on the wireframe-sweep, which only fires against a diff and only ever writes a bare `TODO` stub rather than a real layout, copy, and behavior draft. Neither path checks whether a visual-design tier above ASCII is already in play for the project before drafting the tier-0 file.
11
+
12
+ ## Must
13
+
14
+ - Read `standards/wireframes.md` before drafting, since the frontmatter contract, the layout and variant rules, and the Transcription-wireframes branch are what make the file arguable against a sibling
15
+ - Walk the whole `.claude/wireframes/` tree, including a grouped surface's own subfolder, before drafting, since a top-level-only check misses a nested match
16
+ - Detect an existing higher tier from `.claude/DESIGN.md` and the wireframes tree and report it, never build a companion render for it, since no shipped mechanism produces one
17
+ - Draft in transcription mode, citing the real source, when the surface names an already-built component. Draft in role-intent mode otherwise.
18
+ - Confirm the resolved path, the detected tier, and the full content with the user before writing, since the mode decision and the tier read are judgment calls with no diff to preview either against
19
+ - Run `canon indexes regen` on `.claude/wireframes/` after writing, so the catalog picks up the new surface immediately rather than drifting until the next unrelated regen
20
+
21
+ ## Must not
22
+
23
+ - Build a tier-1 or tier-2 rendering mechanism, an Excalidraw round trip, or any companion render. Detecting and reporting a higher tier is the whole of this skill's reach past ASCII.
24
+ - Hand-edit `.claude/wireframes/index.md`. It regenerates from sibling frontmatter, and a hand edit is overwritten on the next regen.
25
+ - Assume this skill's own invocation frequency needs no check. Whether anything reaches for it beyond an author typing its name has no answer at creation time, so a review pass some months in should read that back rather than take the assumption on faith.
26
+
27
+ ## Guards
28
+
29
+ - No surface given: stop and ask what surface the wireframe should cover.
30
+ - The derived slug already matches a file anywhere in the `.claude/wireframes/` tree, flat or nested: stop and point at editing that file directly, since no dedicated skill owns a full rewrite of an existing surface.
31
+
32
+ ## Out of scope
33
+
34
+ - Stubbing a surface a diff touched, or reporting drift in an existing wireframe against a diff: `claude/skills/claude-docs/references/wireframe-sweep.md`
35
+ - Drafting a `.claude/context/<domain>.md` entry: `context-draft`
36
+ - Drafting a `docs/*.md` page: `docs-draft`
37
+ - Drafting a standard, a snippet, or a governance rule: `create-standard`, `create-snippet`, `create-rule`
38
+ - Building or wiring an actual tier-1 or tier-2 rendering pipeline: the visual design workflow's own tier guidance, out of reach of a hand-drafting skill
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: wireframe-draft
3
+ description: Drafts a brand-new `.claude/wireframes/<surface>.md` file against `standards/wireframes.md`, walks the tree for a name collision, detects an existing higher visual-design tier without building one, confirms with the user, then writes. Use when asked to "draft a wireframe for X", "write the wireframe for this surface", "add a .claude/wireframes entry for X", or "wireframe this screen" where no surface file covers it yet. Do NOT use to fix a stale TODO stub or report wireframe drift against a diff, which is `claude-docs`'s wireframe-sweep step.
4
+ ---
5
+
6
+ # Wireframe draft
7
+
8
+ Drafts one brand-new `.claude/wireframes/<surface>.md` file end to end: read the standard, decide whether the surface is already covered, detect but never build a higher visual-design tier, confirm the draft with the user, then write.
9
+
10
+ Read these files in parallel:
11
+
12
+ - `${CLAUDE_SKILL_DIR}/../../standards/wireframes.md`: the three questions a wireframe must answer, its frontmatter, layout and variant rules, the Transcription-wireframes branch, and what moves to a context entry instead
13
+ - `${CLAUDE_SKILL_DIR}/../../standards/markdown.md`: banned words, punctuation, and formatting for the prose around the fences
14
+ - The `write-human` skill: voice, rhythm, and sentence construction for the prose around the fences
15
+
16
+ This skill stays fully independent of `claude/skills/claude-docs/references/wireframe-sweep.md`, which only ever writes a bare `TODO` stub for a surface a diff touched and reports drift against one a wireframe already covers. Neither the stub nor the drift check is a draft, and this skill never reads or writes through that mechanism.
17
+
18
+ ## Guards
19
+
20
+ - If no surface is given, stop: `❌ No surface given. Name the surface this wireframe should cover.`
21
+ - Derive a kebab-case slug from the surface. Walk the whole `.claude/wireframes/` tree, including a surface nested inside a grouped subfolder, rather than checking the top level alone. A match at any depth means the surface already has a file. Stop: `❌ <path> already covers this surface. Edit it directly; this skill only drafts a surface with no file yet.`
22
+
23
+ ## Tier detection
24
+
25
+ - Read `.claude/DESIGN.md` and every existing `.claude/wireframes/` file for a tier signal: a Stitch, Excalidraw, or Figma reference, or a marker naming one of them.
26
+ - State the detected tier at the confirm step. Always draft the tier-0 ASCII file regardless of what is detected, since that is the only shape this skill or any other shipped mechanism produces. Report a higher tier rather than attempting a companion render for it.
27
+ - Default silently to tier 0 when nothing is detected.
28
+
29
+ ## Draft
30
+
31
+ - Decide the mode before drafting. When the surface names an already-built component or file, open that source and draft in transcription mode, citing the render function, the stylesheet rule, or the built file each region and label traces to, per the standard's Transcription-wireframes section.
32
+ - Draft in role-intent mode otherwise: label each region by its role, never by a class name or a token value.
33
+ - Draft `title` and `description` frontmatter, then one `##` heading per layout variant, each holding its own ASCII `plaintext` fence with `←` role annotations, followed by `## Copy` and `## Behavior` sections against `${CLAUDE_SKILL_DIR}/../../standards/wireframes.md`'s template.
34
+ - Add a second layout variant only when the layout itself changes across a breakpoint or state, never for a spacing difference alone.
35
+ - Leave out algorithms, event-handler code, framework prop or class names outside transcription mode, and anything else the standard sends to a context entry instead.
36
+
37
+ ## Confirm
38
+
39
+ - Show the resolved path, the detected tier, and the full drafted content before writing.
40
+ - Confirm all three with the user. This skill waits for that answer rather than treating the tool permission dialog as the gate, since the mode decision and the tier read are judgment calls with no diff to preview either against.
41
+
42
+ ## Write
43
+
44
+ - Write the file at the confirmed path, creating a grouped subfolder only when the surface belongs beside siblings that already share one.
45
+ - Run `canon markdown audit <path>` against the prose outside the fenced block.
46
+ - Run `canon indexes regen .claude/wireframes`, so the surface appears in `.claude/wireframes/index.md` immediately rather than drifting until the next unrelated regen.
47
+
48
+ ## Response format
49
+
50
+ ### Preview
51
+
52
+ **Surface:** `<surface>`
53
+ **Placement:** `<path>`
54
+ **Detected tier:** `<tier-0 | tier-1 | tier-2 | none detected>`
55
+
56
+ ```markdown
57
+ <drafted frontmatter and body>
58
+ ```
59
+
60
+ ### After confirmation
61
+
62
+ ```plaintext
63
+ ✅ Drafted: <path>
64
+ ```
@@ -5,7 +5,7 @@ description: Fetches a YouTube video transcript with metadata frontmatter into t
5
5
 
6
6
  # YouTube transcripts
7
7
 
8
- Turn a pasted YouTube URL into a markdown file with YAML frontmatter and a cleaned prose body. The `canon transcripts` command owns the fetch, VTT cleanup, and frontmatter. Do not reimplement that logic. The output format and field list live in the toolkit's `.claude/context/transcripts.md`, which a target does not receive.
8
+ Turn a pasted YouTube URL into a markdown file with YAML frontmatter and a cleaned prose body. The `canon transcripts` command owns the fetch, the VTT cleanup, and the frontmatter shape. Do not reimplement or restate any of it here.
9
9
 
10
10
  ## Guards
11
11
 
@@ -20,6 +20,7 @@ Full help: `canon <command> --help`. Behavior notes for the install and sync ver
20
20
  | `canon sandbox check` | Score a provisioned sandbox against a scenario expectation (`--json` for the verdict) |
21
21
  | `canon sandbox coverage` | Report which scenarios declare expectations (`--json`, `--strict`, `--skills`) |
22
22
  | `canon indexes regen` | Regenerate `index.md` files from sibling frontmatter |
23
+ | `canon indexes list [path]` | Flatten every folder index under a path into one `{path, title, description}` catalog (`--json`) |
23
24
  | `canon docs [topic]` | Emit toolkit reference docs (`list`, or a topic by name) |
24
25
  | `canon design render` | Render `.claude/DESIGN.md` tokens to HTML and CSS |
25
26
  | `canon design regen` | Rewrite this repository's `.claude/DESIGN.md` and `src/design/base.css` from `src/design/tokens.ts` |
@@ -38,19 +38,22 @@ A step's caption from the draft renders as an overlay while its hold plays, so t
38
38
 
39
39
  When `ffmpeg` is on PATH, the run also writes an mp4 beside the webm, since webm plays in a `<video>` tag but nothing else accepts it. A target without `ffmpeg` still gets the webm and a line naming what to install, and the run does not fail over the missing converter.
40
40
 
41
- | Option | Behavior |
42
- | ---------------- | ----------------------------------------------------------- |
43
- | `--out <dir>` | Directory to write into, overriding what the plan names |
44
- | `--cursor <dir>` | Cursor theme folder to draw the pointer from |
45
- | `--no-video` | Write only the still |
46
- | `--no-still` | Write only the recording |
47
- | `--json` | Add a record on stdout carrying both paths and the duration |
41
+ `--gif` adds a third file from the same converter, and it is opt-in where the mp4 is not. A gif runs several times the size of the webm it derives from, and the destination that needs one is a README on a host that strips `<video>`, which GitHub does. The filter generates a palette from the source and then applies it, rather than quantizing per frame, since a per-frame palette is what makes a recording of flat interface colors band and shimmer. It applies that palette without dithering, because the command records applications rather than photographs and the dither writes noise the encoder then stores. One recording measured 3,193,715 bytes dithered against 2,701,941 without, with the same text region cropped from both and read as identical. A missing converter is reported the same way it is for the mp4, so an absent `ffmpeg` costs the gif and not the run.
42
+
43
+ | Option | Behavior |
44
+ | ---------------- | ------------------------------------------------------------------- |
45
+ | `--out <dir>` | Directory to write into, overriding what the plan names |
46
+ | `--cursor <dir>` | Cursor theme folder to draw the pointer from |
47
+ | `--no-video` | Write only the still |
48
+ | `--no-still` | Write only the recording |
49
+ | `--gif` | Also write a gif, for a host that strips video |
50
+ | `--json` | Add a record on stdout carrying every path written and the duration |
48
51
 
49
52
  A step waits on its `waitFor` selector becoming visible and then holds for its own `holdMs`, which is what puts a finished state on screen long enough to read. `navigate` uses the plan's URL unless the step names its own.
50
53
 
51
54
  ## The pointer is painted inside the page
52
55
 
53
- The browser engine's own annotation draws a red dot at the moment of a click and an action label in a corner. It paints no cursor, so a run without more looks like the pointer teleports between targets.
56
+ The browser engine offers an annotation of its own that draws a dot on the interacted element and a title naming the API call it made, and this recorder does not turn it on. It paints no cursor, so a run relying on it looks like the pointer teleports between targets, and the two overlays below supersede it: a real cursor where the dot is a marker, and the beat's narration where the title reads `Mouse move`. Running both put four overlays on the frame, and the two the engine drew were the two a viewer reads as noise.
54
57
 
55
58
  The recorder injects a pointer element before navigation and moves it through the engine's pointer with interpolated steps rather than through the element-clicking helper, which resolves a target and jumps to it. The step count is the whole difference between a cursor that travels and one that appears, and how many steps a move takes is derived from the machine's own round-trip cost rather than fixed, so the same plan glides at roughly the same pace on a loaded machine as an idle one. It also reads the element under it on every move and switches between an arrow, a hand, and a text beam, so it reflects the page the way a real cursor does.
56
59
 
@@ -20,7 +20,7 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
20
20
  - [Docs](docs.md): How canon docs resolves the toolkit's own reference surface from an install root, and how a split domain is named
21
21
  - [Driver](driver.md): Walking a page through named interactions, the probe catalog and the false finding each one carries, why viewport heights are never defaulted, and what each refusal reports
22
22
  - [Merge gate](gate.md): Running the gate this repository verifies a branch with, what the stage table holds and what stays a script, how the changed set scopes three stages, and why a stage that cannot read its input reports rather than passing
23
- - [Indexes](indexes.md): Flags, exit codes, and JSON shape for canon indexes regen, plus when it auto-stages what it rewrote
23
+ - [Indexes](indexes.md): Flags, exit codes, and JSON shape for canon indexes regen and canon indexes list, plus when regen auto-stages what it rewrote
24
24
  - [Install and sync](install-and-sync.md): What each install and sync verb writes, refuses, or leaves alone, and how drift is attributed in a target project
25
25
  - [Intake](intake.md): Reading intake folder counts and items, the three read states an item can be in, landing a batch of selections in one cluster, the refusal reasons, and why a call is scoped to one file
26
26
  - [Key Changes bijection](key-changes.md): Comparing the files a pull request body's Key Changes names against its own diff, the two directions and the split inside each that decides what is worth raising, the span rules the extractor was measured into, and the three refusals that separate a clean pass from a read that produced nothing
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Indexes
3
- description: Flags, exit codes, and JSON shape for canon indexes regen, plus when it auto-stages what it rewrote
3
+ description: Flags, exit codes, and JSON shape for canon indexes regen and canon indexes list, plus when regen auto-stages what it rewrote
4
4
  ---
5
5
 
6
6
  # Indexes
@@ -32,4 +32,20 @@ Skills can parse drift without branching on exit code:
32
32
  canon indexes regen --dry-run --json | jq '.results[] | select(.action == "would-write")'
33
33
  ```
34
34
 
35
+ ## List
36
+
37
+ `canon indexes list [path]` walks every folder index under `path` (default: cwd) and flattens each folder's own `title`/`subtitle` plus every sibling's `title`/`description` into one catalog, sorted by path relative to the walk root.
38
+
39
+ | Option | Behavior |
40
+ | -------- | ------------------------------------------ |
41
+ | `--json` | Emit `{ root, entries, errors }` on stdout |
42
+
43
+ Each entry is `{ path, title, description }`. `path` is relative to the resolved root, and an `index.md` file's own row uses its `subtitle` as `description`.
44
+
45
+ A folder failing frontmatter validation lands its message in `errors` and drops out of `entries` rather than failing the whole walk, matching `regen`'s per-folder isolation. Every mode writes one `ERROR:` line per error to stderr.
46
+
47
+ Exit codes: `0` no errors, `1` root not a directory, or a folder failed frontmatter validation.
48
+
49
+ A git-ignored indexed folder, such as `.canon/tasks/` or `.canon/memory/`, never appears in the output. `listIndexes` filters candidates through `git check-ignore` the same way `regen`'s whole-repo walk does, which is the right default for a documentation lookup since those rows are session scratch, but it is a gap against `regen`'s positional-path mode, which bypasses that filter for those two folders.
50
+
35
51
  For the system rationale, frontmatter contract, when to adopt, and bootstrap path, see `.claude/context/indexes.md`.
@@ -284,24 +284,27 @@ This section is the corpus the coverage claim is measured against: every name `c
284
284
 
285
285
  ### Generate an artifact on demand
286
286
 
287
- | Skill | When to use |
288
- | -------------------------- | --------------------------------------------------------------------- |
289
- | `canon:create-rule` | For a project-specific governance rule the toolkit does not ship |
290
- | `canon:create-skill` | For a new `SKILL.md` |
291
- | `canon:create-snippet` | For a reusable prompt |
292
- | `canon:create-standard` | For a new authoring convention |
293
- | `canon:docs-draft` | For a brand-new `docs/*.md` page, drafted against `standards/docs.md` |
294
- | `canon:bash-script` | For an interactive, human-facing shell tool |
295
- | `canon:bash-cli-script` | For a non-interactive automation, CI, or pipeline script |
296
- | `canon:ci-workflow` | For a GitHub Actions workflow file |
297
- | `canon:canon-slides-draft` | For a deck, drafted as `.claude/SLIDES.md` and rendered to PowerPoint |
298
- | `canon:canon-screencast` | For a recording script with beats and defaults already seeded |
287
+ | Skill | When to use |
288
+ | -------------------------- | ------------------------------------------------------------------------------------------------- |
289
+ | `canon:create-rule` | For a project-specific governance rule the toolkit does not ship |
290
+ | `canon:create-skill` | For a new `SKILL.md` |
291
+ | `canon:create-snippet` | For a reusable prompt |
292
+ | `canon:create-standard` | For a new authoring convention |
293
+ | `canon:docs-draft` | For a brand-new `docs/*.md` page, drafted against `standards/docs.md` |
294
+ | `canon:context-draft` | For a brand-new `.claude/context/<domain>.md` entry, drafted against `standards/context.md` |
295
+ | `canon:wireframe-draft` | For a brand-new `.claude/wireframes/<surface>.md` file, drafted against `standards/wireframes.md` |
296
+ | `canon:bash-script` | For an interactive, human-facing shell tool |
297
+ | `canon:bash-cli-script` | For a non-interactive automation, CI, or pipeline script |
298
+ | `canon:ci-workflow` | For a GitHub Actions workflow file |
299
+ | `canon:canon-slides-draft` | For a deck, drafted as `.claude/SLIDES.md` and rendered to PowerPoint |
300
+ | `canon:canon-screencast` | For a recording script with beats and defaults already seeded |
299
301
 
300
302
  ### Answer a question at any point
301
303
 
302
304
  | Skill | When to use |
303
305
  | --------------------------- | ------------------------------------------------------------------------------ |
304
306
  | `canon:canon-cli` | Before a sync or install, to learn what it overwrites, merges, or leaves alone |
307
+ | `canon:index-lookup` | To find where a topic is documented across the tracked `index.md` catalogs |
305
308
  | `canon:youtube-transcripts` | When a video transcript is wanted in the repo as context |
306
309
  | `canon:claude-teach` | To learn a subject across sessions, in a workspace that holds the progress |
307
310
  | `canon:write-human` | Before drafting or revising prose, for voice, rhythm, and density |
@@ -15,3 +15,5 @@ description: Route tracked-file writes and shared session scratch correctly from
15
15
  - From a linked worktree, `Edit` and `Write` are refused for every main-root path, session scratch included. The refusal names session isolation and points at the worktree copy, which is a second gitignored file no later session reads, so never take that redirect.
16
16
  - `Read` resolves against the main root normally from a linked worktree. A main-root write reaches it only through `Bash`, as one plain command rather than a compound one, which is refused for complexity.
17
17
  - Route a main-root write by what it does to the file. Creating a whole file goes out as one plain `Bash` command carrying a heredoc. Changing a line inside a file that already exists goes through a command that resolves the main root in-process, because the shell route for that case is the stream editor this file bans.
18
+ - A background session that entered no worktree at all meets the identical refusal writing to the main root, since the guard gates on isolation rather than on sitting inside a linked worktree. A planner writing its plan file from no worktree takes the same heredoc route for that reason.
19
+ - The heredoc route costs nothing on `.canon/plans/` and `.canon/review/`, which no `PostToolUse` hook watches, and costs the index on `.canon/tasks/` and `.canon/memory/`, since a hook matching `Write|Edit|MultiEdit` never fires on `Bash`. Regenerate positionally: in-process where a verb such as `canon tasks archive` already calls it, elsewhere by an explicit `canon indexes regen` call.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/canon",
3
3
  "type": "module",
4
- "version": "4.53.0",
4
+ "version": "4.55.0",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {