@erclx/canon 4.126.0 → 5.1.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 (107) hide show
  1. package/README.md +5 -7
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/auto-ship/references/verb-absent.md +0 -1
  4. package/claude/skills/canon-feedback/REQUIREMENT.md +1 -1
  5. package/claude/skills/canon-feedback/SKILL.md +2 -2
  6. package/claude/skills/canon-operator/REQUIREMENT.md +2 -2
  7. package/claude/skills/canon-operator/SKILL.md +3 -3
  8. package/claude/skills/codebase-layout/REQUIREMENT.md +1 -0
  9. package/claude/skills/codebase-layout/SKILL.md +6 -0
  10. package/claude/skills/create-rule/REQUIREMENT.md +1 -1
  11. package/claude/skills/create-standard/REQUIREMENT.md +0 -2
  12. package/claude/skills/decision-escalate/REQUIREMENT.md +1 -2
  13. package/claude/skills/draft-context/REQUIREMENT.md +1 -1
  14. package/claude/skills/draft-docs/REQUIREMENT.md +1 -1
  15. package/claude/skills/draft-wireframes/REQUIREMENT.md +1 -1
  16. package/claude/skills/git-pr/references/task-number.md +2 -0
  17. package/claude/skills/plan-intake/SKILL.md +1 -1
  18. package/claude/skills/target-setup/REQUIREMENT.md +2 -2
  19. package/claude/skills/target-setup/SKILL.md +1 -1
  20. package/claude/skills/target-setup/references/detect.md +1 -1
  21. package/claude/skills/target-setup/references/indexes.md +1 -5
  22. package/claude/skills/task-board/references/archive.md +1 -0
  23. package/docs/agents/commands.md +1 -3
  24. package/docs/agents/counts.md +1 -2
  25. package/docs/agents/docs.md +1 -1
  26. package/docs/agents/drift-surfaces.md +6 -4
  27. package/docs/agents/index.md +1 -1
  28. package/docs/agents/indexes.md +1 -1
  29. package/docs/agents/install-and-sync.md +1 -8
  30. package/docs/agents/markdown-audit.md +2 -2
  31. package/docs/agents/restated.md +7 -5
  32. package/docs/agents/review-classification.md +1 -2
  33. package/docs/agents/scripting.md +2 -3
  34. package/docs/agents/skills-reach.md +3 -3
  35. package/docs/agents/state-scoped-risk.md +1 -1
  36. package/docs/agents/tasks-archive.md +1 -1
  37. package/docs/agents/tasks.md +4 -0
  38. package/docs/target/index.md +4 -4
  39. package/docs/target/{target-migrations.md → migrations.md} +17 -1
  40. package/docs/target/{target-projects.md → projects.md} +5 -7
  41. package/docs/target/{target-scenarios.md → scenarios.md} +1 -1
  42. package/docs/target/{target-sync.md → sync.md} +4 -2
  43. package/docs/workflow/ai-workflow.md +3 -7
  44. package/docs/workflow/operating-model.md +4 -4
  45. package/docs/workflow/parallel-features.md +1 -1
  46. package/docs/workflow/skill-map.md +1 -2
  47. package/docs/workflow/visual-design-workflow.md +5 -5
  48. package/docs/workflow/zshrc-aliases.md +1 -1
  49. package/package.json +1 -2
  50. package/scripts/core/check-plugin-boundary.sh +1 -1
  51. package/scripts/core/install-check.sh +3 -4
  52. package/scripts/core/regen-hero.sh +2 -9
  53. package/src/autoship/paths.ts +3 -5
  54. package/src/claude/cases/authoring.ts +1 -5
  55. package/src/claude/skills-reach.ts +1 -2
  56. package/src/cli.ts +0 -2
  57. package/src/commands/feedback-format.ts +0 -1
  58. package/src/commands/gov.ts +8 -7
  59. package/src/commands/labels.ts +3 -3
  60. package/src/commands/markdown.ts +2 -2
  61. package/src/commands/tasks.ts +82 -2
  62. package/src/counts/catalogs.ts +0 -16
  63. package/src/counts/scan.ts +1 -1
  64. package/src/docs/read.ts +25 -25
  65. package/src/gate/measures.ts +52 -0
  66. package/src/gate/stages.ts +9 -0
  67. package/src/gov/restated.ts +60 -22
  68. package/src/help.ts +0 -2
  69. package/src/init/steps.ts +1 -1
  70. package/src/labels/format.ts +2 -2
  71. package/src/migrate/skill-names.ts +4 -5
  72. package/src/secrets/shipped.ts +2 -2
  73. package/src/serve/static.ts +1 -2
  74. package/src/shipped/references.ts +7 -8
  75. package/src/sync/engine.ts +2 -2
  76. package/src/tasks/archive.ts +30 -0
  77. package/src/tasks/record.ts +106 -9
  78. package/standards/commit.md +1 -1
  79. package/standards/context.md +2 -0
  80. package/standards/docs.md +1 -0
  81. package/standards/index.md +0 -1
  82. package/standards/pr.md +1 -1
  83. package/standards/skill.md +1 -2
  84. package/standards/tasks.md +2 -2
  85. package/standards/wiki.md +2 -1
  86. package/tooling/base/reference.md +2 -2
  87. package/tooling/claude/reference.md +1 -1
  88. package/claude/skills/create-snippet/REQUIREMENT.md +0 -39
  89. package/claude/skills/create-snippet/SKILL.md +0 -30
  90. package/scripts/snippets/create.sh +0 -156
  91. package/snippets/align.md +0 -12
  92. package/snippets/claude/decision-memo.md +0 -39
  93. package/snippets/claude/feature-recap.md +0 -19
  94. package/snippets/claude/figma-steps.md +0 -24
  95. package/snippets/compact-summary.md +0 -5
  96. package/snippets/decision-help.md +0 -6
  97. package/snippets/meta-prompt.md +0 -14
  98. package/snippets/research-prompt.md +0 -7
  99. package/snippets/session-notes.md +0 -11
  100. package/snippets/snippets.toml +0 -5
  101. package/snippets/step-by-step.md +0 -10
  102. package/snippets/web-research.md +0 -21
  103. package/src/commands/snippets.ts +0 -90
  104. package/src/snippets/categories.ts +0 -66
  105. package/src/snippets/list.ts +0 -32
  106. package/src/snippets/presets.ts +0 -50
  107. package/standards/snippets.md +0 -76
@@ -1,19 +0,0 @@
1
- Recap the implementation this session completed. Read the plan file referenced in the session (usually `.canon/plans/feature-<slug>.md`) and produce a verification block.
2
-
3
- Output this shape, nothing else:
4
-
5
- ```markdown
6
- **Deliverable 1**: <name from the plan>
7
-
8
- - `path/to/file`: one-line reason
9
-
10
- **Deliverable 2**: <name from the plan>
11
-
12
- - `path/to/file`: one-line reason
13
-
14
- **Verified**: <what was smoke-tested or `Nothing tested in session.`>
15
-
16
- **Open**: <unresolved questions, deferred items, or `None.`>
17
- ```
18
-
19
- Group files by deliverable, not by directory. Keep each line scannable. Do not restate the plan body.
@@ -1,24 +0,0 @@
1
- Read the relevant spec from `canon/DESIGN.md` and any other design files needed. Write step-by-step Figma instructions to produce the component exactly.
2
-
3
- For each step, specify:
4
-
5
- - The exact tool or keyboard shortcut to activate
6
- - Which panel or field to click
7
- - The exact value to enter
8
-
9
- When a step has multiple sub-actions or lists several items, use nested bullets instead of stacking them in one sentence.
10
-
11
- Cover in order:
12
-
13
- - Frame or group setup (dimensions, corner radius, fill)
14
- - Each layer or shape (type, size, position, fill, stroke)
15
- - Alignment and spacing rules
16
- - Export settings
17
-
18
- Throughout:
19
-
20
- - Use exact values from the spec
21
- - Do not restate design rationale
22
- - Use Title Case for all layer and frame names
23
- - Use Figma terminology throughout, not code or CSS equivalents
24
- - Reuse existing frames or components from the same Figma document by name instead of rebuilding from scratch
@@ -1,5 +0,0 @@
1
- Summarize the research or findings above into a compact, scannable block.
2
-
3
- - State the main takeaways directly
4
- - Skip background context the reader already has
5
- - Use short headings and tight bullets where multiple items exist
@@ -1,6 +0,0 @@
1
- Apply senior-level judgment. Pick the best option from the discussion so far. State the pick in one sentence, then the reason and the main tradeoff in one or two sentences.
2
-
3
- - Minimal and necessary only
4
- - No nice-to-haves
5
- - No padding
6
- - No alternatives unless they change the recommendation
@@ -1,14 +0,0 @@
1
- Turn the idea below into a production-grade system prompt.
2
-
3
- Give it a role of one to three lines, a constraints section, and an output format carrying a template plus one worked example. Add a validation checklist when the prompt involves multi-step logic, edge cases, or error handling. Omit that checklist for a single-purpose prompt.
4
-
5
- Group constraints under domain headings when they span two or more domains and total five or more. Use flat must-do and must-not-do lists otherwise. Keep dos and don'ts together under the topic they belong to.
6
-
7
- Use `{{DOUBLE_BRACES}}` for values the end user fills in at runtime.
8
-
9
- Constraints:
10
-
11
- - Use imperative voice. Write `Do X`, never `You should` or `Try to`.
12
- - Make every constraint verifiable from the output. Reject subjective qualifiers like `appropriate`, `concise`, or `well-structured`.
13
- - Match prompt length to task complexity. A single-purpose task gets a minimal prompt.
14
- - Never write `maybe`, `consider`, or `depending on`. Be definitive.
@@ -1,7 +0,0 @@
1
- Generate a self-contained research prompt for another AI chat. Include:
2
-
3
- - State what I am trying to do and why
4
- - List what I have already tried and the results
5
- - Specify questions that need answers
6
-
7
- Write it in first person. Assume the reader has no context from this session.
@@ -1,11 +0,0 @@
1
- Write session notes for this conversation using this format:
2
-
3
- ```markdown
4
- # Session notes: <topic>
5
-
6
- ## <title>
7
-
8
- <what was decided and why>
9
- ```
10
-
11
- Only include decisions made in this session. Output only the markdown block, nothing else.
@@ -1,5 +0,0 @@
1
- [essentials]
2
- names = [
3
- "align",
4
- "decision-help",
5
- ]
@@ -1,10 +0,0 @@
1
- Give me step-by-step instructions.
2
-
3
- For each step:
4
-
5
- - State what to do in one sentence.
6
- - Include the exact value, command, or input where precision matters.
7
- - Note any output to save or verify before moving on.
8
- - When the step has multiple sub-actions or lists several items, use nested bullets instead of stacking them in one sentence.
9
-
10
- Group related steps under a heading if the process has distinct phases. Flag any step that is irreversible or that blocks later steps.
@@ -1,21 +0,0 @@
1
- Search the web for the topic or question added after this snippet. Fetch the most relevant pages. Synthesize findings into a decision-ready block using this format:
2
-
3
- ```markdown
4
- Topic: [what was researched]
5
- Recommendation: [one clear answer or choice]
6
-
7
- Options considered:
8
-
9
- - [Option A]: why considered, why chosen or rejected
10
- - [Option B]: why considered, why chosen or rejected
11
-
12
- Key constraints:
13
-
14
- - [constraint that shaped the decision]
15
-
16
- Sources:
17
-
18
- - [URL]: what it confirmed
19
- ```
20
-
21
- Output only the block. Do not add commentary before or after it.
@@ -1,90 +0,0 @@
1
- import type { Command } from 'commander'
2
- import { registerPassThroughVerbs } from '@/commands/pass-through'
3
- import { checkoutMismatchWarning, PROJECT_ROOT } from '@/project-root'
4
- import { BASE_CATEGORY } from '@/snippets/categories'
5
- import { buildSnippetsCatalog } from '@/snippets/list'
6
- import { intro, logInfo, logStep, logWarn, outro } from '@/ui'
7
-
8
- const PASS_THROUGH_VERBS = ['create'] as const
9
-
10
- interface ListOptions {
11
- readonly categories?: boolean
12
- readonly entries?: boolean
13
- readonly json?: boolean
14
- }
15
-
16
- export function register(program: Command): void {
17
- const snippets = program
18
- .command('snippets')
19
- .description('Snippets commands (create, list)')
20
- .helpOption('-h, --help', 'Show this help message')
21
-
22
- snippets
23
- .command('list')
24
- .description('List snippet presets, categories, and entries')
25
- .helpOption('-h, --help', 'Show this help message')
26
- .option('--categories', 'Only list presets and category names')
27
- .option('--entries', 'Only list entry slugs grouped by category')
28
- .option('--json', 'Emit machine-readable JSON')
29
- .action((opts: ListOptions) => {
30
- process.exitCode = runList(opts)
31
- })
32
-
33
- registerPassThroughVerbs(snippets, 'snippets', PASS_THROUGH_VERBS)
34
- }
35
-
36
- /**
37
- * `JSON.stringify` replaces a `printf` template that interpolated names into a
38
- * JSON string literal unescaped, so a slug or category carrying a quote emitted
39
- * output no consuming skill could parse.
40
- *
41
- * The frame opens after the `--json` return, since the bash verb only ever
42
- * emitted section headers and the pass-through above it owned the `┌`.
43
- */
44
- function runList(opts: ListOptions): number {
45
- // The warning is a frame-interior line, so it goes out after `intro` on the
46
- // path that opens one and bare on the `--json` path, which returns first and
47
- // opens none. One position cannot serve both.
48
- const mismatch = checkoutMismatchWarning(process.cwd())
49
- const catalog = buildSnippetsCatalog(PROJECT_ROOT)
50
-
51
- if (opts.json) {
52
- if (mismatch !== undefined) logWarn(mismatch)
53
- process.stdout.write(`${JSON.stringify(catalog)}\n`)
54
- return 0
55
- }
56
-
57
- intro('canon snippets list')
58
- if (mismatch !== undefined) logWarn(mismatch)
59
-
60
- const showCategories = opts.entries !== true
61
- const showEntries = opts.categories !== true
62
-
63
- if (showCategories) {
64
- if (catalog.presets.length > 0) {
65
- logStep('Presets')
66
- for (const preset of catalog.presets) {
67
- logInfo(`${preset.name} (${preset.slugs.length} entries)`)
68
- }
69
- }
70
-
71
- logStep('Categories')
72
- for (const category of catalog.categories) {
73
- logInfo(`${category.name} (${category.entries.length} entries)`)
74
- }
75
- }
76
-
77
- if (showEntries) {
78
- logStep('Entries')
79
- for (const category of catalog.categories) {
80
- for (const entry of category.entries) {
81
- logInfo(
82
- category.name === BASE_CATEGORY ? entry : `${category.name}/${entry}`,
83
- )
84
- }
85
- }
86
- }
87
-
88
- outro()
89
- return 0
90
- }
@@ -1,66 +0,0 @@
1
- import { readdirSync } from 'node:fs'
2
- import { join } from 'node:path'
3
- import { isDirectory } from '@/target'
4
-
5
- export const BASE_CATEGORY = 'base'
6
- /** A real category resolving to zero entries, for a caller declining on purpose. */
7
- export const NONE_CATEGORY = 'none'
8
-
9
- export function snippetsSourceDir(root: string): string {
10
- return join(root, 'snippets')
11
- }
12
-
13
- /**
14
- * Lists the folder categories. `base` is the synthetic name for snippets
15
- * sitting directly under `snippets/`, so it is not a directory and callers
16
- * prepend it themselves.
17
- *
18
- * Nothing is filtered: toolkit-internal snippets are authored under `internal/`,
19
- * which this never reads, so every category found here is publishable.
20
- */
21
- export function listFolderCategories(root: string): string[] {
22
- const dir = snippetsSourceDir(root)
23
- if (!isDirectory(dir)) return []
24
-
25
- return readdirSync(dir, { withFileTypes: true })
26
- .filter((entry) => entry.isDirectory())
27
- .map((entry) => entry.name)
28
- .sort()
29
- }
30
-
31
- export function listCategories(root: string): string[] {
32
- return [BASE_CATEGORY, ...listFolderCategories(root)]
33
- }
34
-
35
- export function categoryDir(root: string, category: string): string {
36
- const source = snippetsSourceDir(root)
37
- return category === BASE_CATEGORY ? source : join(source, category)
38
- }
39
-
40
- /**
41
- * Tests for a directory rather than mere existence. `snippets/` holds files
42
- * alongside its category folders, so an argument naming one of them resolves
43
- * to a real path that cannot be scanned as a category.
44
- *
45
- * An empty category refuses rather than resolving, since `join(source, '')`
46
- * is `source` and would otherwise report the base directory as the category,
47
- * widening an unset value to every base entry instead of naming none.
48
- */
49
- export function categoryExists(root: string, category: string): boolean {
50
- if (category === '') return false
51
- return isDirectory(categoryDir(root, category))
52
- }
53
-
54
- /**
55
- * Lists one category's entry slugs. Only the immediate level is read, matching
56
- * the `find -maxdepth 1` the bash used, so a nested folder is its own category
57
- * rather than part of its parent.
58
- */
59
- export function listEntries(root: string, category: string): string[] {
60
- const dir = categoryDir(root, category)
61
- if (!isDirectory(dir)) return []
62
-
63
- return [...new Bun.Glob('*.md').scanSync({ cwd: dir, onlyFiles: true })]
64
- .map((name) => name.slice(0, -'.md'.length))
65
- .sort()
66
- }
@@ -1,32 +0,0 @@
1
- import { listCategories, listEntries } from '@/snippets/categories'
2
- import { loadPresets } from '@/snippets/presets'
3
-
4
- export interface CategoryCatalogEntry {
5
- readonly name: string
6
- readonly entries: readonly string[]
7
- }
8
-
9
- export interface SnippetsCatalog {
10
- readonly presets: readonly {
11
- readonly name: string
12
- readonly slugs: readonly string[]
13
- }[]
14
- readonly categories: readonly CategoryCatalogEntry[]
15
- }
16
-
17
- /**
18
- * Builds the catalog both output modes read, so the human list and the `--json`
19
- * one cannot describe different corpora.
20
- */
21
- export function buildSnippetsCatalog(root: string): SnippetsCatalog {
22
- return {
23
- presets: loadPresets(root).map((preset) => ({
24
- name: preset.name,
25
- slugs: preset.slugs,
26
- })),
27
- categories: listCategories(root).map((name) => ({
28
- name,
29
- entries: listEntries(root, name),
30
- })),
31
- }
32
- }
@@ -1,50 +0,0 @@
1
- import { existsSync, readFileSync } from 'node:fs'
2
- import { join } from 'node:path'
3
- import { snippetsSourceDir } from '@/snippets/categories'
4
-
5
- export interface Preset {
6
- readonly name: string
7
- readonly slugs: readonly string[]
8
- }
9
-
10
- export function presetsPath(root: string): string {
11
- return join(snippetsSourceDir(root), 'snippets.toml')
12
- }
13
-
14
- /**
15
- * Reads every preset from `snippets.toml`. The bash read section headers with
16
- * `grep '^\['` and then re-walked the file per preset with a `BASH_REMATCH`
17
- * loop, so a name carrying a bracket or a slug split across lines parsed by
18
- * accident rather than by rule.
19
- */
20
- export function loadPresets(root: string): Preset[] {
21
- const path = presetsPath(root)
22
- if (!existsSync(path)) return []
23
-
24
- const parsed = Bun.TOML.parse(readFileSync(path, 'utf8')) as Record<
25
- string,
26
- unknown
27
- >
28
- const presets: Preset[] = []
29
-
30
- for (const [name, table] of Object.entries(parsed)) {
31
- if (typeof table !== 'object' || table === null || Array.isArray(table)) {
32
- continue
33
- }
34
- const names = (table as Record<string, unknown>).names
35
- presets.push({
36
- name,
37
- slugs: Array.isArray(names)
38
- ? names.filter(
39
- (slug): slug is string => typeof slug === 'string' && slug !== '',
40
- )
41
- : [],
42
- })
43
- }
44
-
45
- return presets.sort((left, right) => left.name.localeCompare(right.name))
46
- }
47
-
48
- export function findPreset(root: string, name: string): Preset | undefined {
49
- return loadPresets(root).find((preset) => preset.name === name)
50
- }
@@ -1,76 +0,0 @@
1
- ---
2
- title: Snippet reference
3
- description: Snippet reference and authoring conventions
4
- ---
5
-
6
- # Snippet reference
7
-
8
- ## Scope
9
-
10
- Governs a snippet file: what one is for, whether a prompt qualifies as one, how it is invoked, and the structure of its body.
11
-
12
- Does not govern:
13
-
14
- - Skill folders, which carry frontmatter, references, and scripts a snippet has none of: `skill.md`
15
- - Voice, rhythm, and sentence construction in snippet prose: the `write-human` skill
16
- - Punctuation, formatting, and word choice in snippet prose: `markdown.md`
17
-
18
- ## What a snippet is
19
-
20
- A snippet is a short, focused prompt stored as a plain markdown file. Invoke one to insert a prepared instruction into any AI chat without retyping it. Each snippet covers one purpose. If a prompt needs headers or multiple goals, use a system prompt instead.
21
-
22
- ## Admission
23
-
24
- Two tests decide whether a prompt becomes a snippet, and both have to pass. Apply them when adding one and when auditing the catalog.
25
-
26
- - Cadence: a prompt invoked many times across sessions qualifies. A one-shot audit, migration, or bootstrap prompt does not, and belongs in notes outside the catalog.
27
- - Audience: a prompt the consuming project would invoke ships in `snippets/`. One only the authoring repository can run stays outside every installable folder, which is a rule for a repository that authors snippets for others rather than for one that only consumes them.
28
-
29
- A subfolder under `snippets/` names where a prompt is invoked rather than what it is about. A prompt that reads or writes the project's own files goes in a folder, and one carrying its whole context in the message goes at the root.
30
-
31
- Overlapping a skill that does the same job is not disqualifying on its own. A snippet fires when a person asks for it by name and a skill fires on a description match, so the two coexist when those paths differ and the outputs do. Record the reason where the project keeps its decisions, or drop the snippet.
32
-
33
- ## Invocation channels
34
-
35
- - Chrome extension: type `>slug` in a supported chat UI (claude.ai, gemini.google.com) to insert the snippet text inline
36
- - Claude Code terminal: prefix the plugin path with `@` (e.g. `@claude/snippets/claude/feature-recap`). `claude/snippets` is a symlink to the authoring root, so the folder a snippet is written in is the folder it is invoked from
37
-
38
- ## Use patterns
39
-
40
- - Run-as-is: invoke and send immediately. The snippet is self-contained and needs no extra context.
41
- - Invoke-then-add-context: invoke the snippet, then append specifics in the same message (e.g. invoke `plan-feature`, then add the feature name or extra constraints)
42
- - Invoke-on-history: invoke after a discussion. The snippet uses prior conversation as implicit context with no additional input needed (e.g. invoke `claude-figma` after discussing a design).
43
-
44
- ## Authoring
45
-
46
- - One focused purpose per snippet. If it needs headers or multiple goals, use a system prompt instead.
47
- - Self-contained. No references to external files or assumed prior context.
48
- - No user fill-in placeholders. If a value depends on context, the user adds it after invocation.
49
- - Plain markdown only. No YAML frontmatter, no headers, no nested structure.
50
- - Filename is the slug: kebab-case, no capitals, no underscores
51
-
52
- ## Structure
53
-
54
- - Lead with a verb. Open with an imperative that states the job immediately.
55
- - One instruction per sentence. Do not stack multiple actions into one sentence.
56
- - For sequential steps, use a numbered list with one action per item.
57
- - When the output has a fixed shape, show it in a fenced code block with a language identifier.
58
- - Put constraints and exclusions last, not inline with the main instructions.
59
-
60
- ## Examples
61
-
62
- ### Correct
63
-
64
- ```markdown
65
- I want to implement the following. Scan relevant files and list conflicts. Do not implement. # user adds feature after invocation
66
- Scan relevant files and list conflicts. Do not implement. # run-as-is, no context needed
67
- ```
68
-
69
- ### Incorrect
70
-
71
- ```markdown
72
- I want to implement: <feature or task name> # redundant fill-in, add context after invocation instead
73
- See ARCHITECTURE.md before starting. # external dependency, not self-contained
74
-
75
- ## Overview\n## Steps # headers belong in a system prompt, not a snippet
76
- ```