@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
@@ -84,6 +84,7 @@ const CANON_RULES_REL = join('.claude', 'rules', 'canon')
84
84
 
85
85
  interface InstallOptions {
86
86
  readonly add?: string
87
+ readonly yes?: boolean
87
88
  }
88
89
 
89
90
  interface RegenOptions {
@@ -147,6 +148,7 @@ export function register(program: Command): void {
147
148
  .argument('[target]', 'Target directory', '.')
148
149
  .helpOption('-h, --help', 'Show this help message')
149
150
  .option('--add <rules>', 'Comma-separated rules to layer on the stack')
151
+ .option('--yes', 'Answer the install confirm without a prompt')
150
152
  .addHelpText(
151
153
  'after',
152
154
  [
@@ -156,6 +158,7 @@ export function register(program: Command): void {
156
158
  ' canon gov install node ../my-app',
157
159
  ' canon gov install node --add astro,shadcn ../my-app',
158
160
  ' canon gov install --add tasks,plan ../my-app',
161
+ ' canon gov install node --yes ../my-app',
159
162
  '',
160
163
  'With --add and no stack, the named rules install alone. A first',
161
164
  'positional naming a stack is read as the stack, so pass . or a path',
@@ -164,6 +167,10 @@ export function register(program: Command): void {
164
167
  'Copies each rule into .claude/rules/canon/, replacing a copy already',
165
168
  'there. Rules under .claude/rules/project/ are never read.',
166
169
  '',
170
+ 'The install confirm needs a TTY. --yes answers it, so a headless call',
171
+ 'installs without setting CANON_NON_INTERACTIVE=1. It does not pick a',
172
+ 'stack, so a headless call still names one.',
173
+ '',
167
174
  'Exit codes:',
168
175
  ' 0 installed, the stack held no rules, or the prompt was cancelled',
169
176
  ' 1 refused: no stack named headlessly, an unknown stack, no --add',
@@ -349,6 +356,10 @@ export function register(program: Command): void {
349
356
  ' 1 refused, with the reason on stderr or in the JSON record',
350
357
  ' 2 at least one stated figure disagrees with the tree',
351
358
  '',
359
+ 'Reasons:',
360
+ ' no-git git could not list the tree',
361
+ ' no-markdown the tree holds no markdown file',
362
+ '',
352
363
  'It reports and never gates. The false-positive rate is read off the',
353
364
  'first run rather than assumed, and a push failing on a stated figure',
354
365
  'that reads correctly to a person would teach a contributor to route',
@@ -415,6 +426,9 @@ export function register(program: Command): void {
415
426
  ' 1 refused, with the reason on stderr or in the JSON record',
416
427
  ' 2 at least one declaration asserts either',
417
428
  '',
429
+ 'Refuses an empty superseded value, a superseded value equal to its',
430
+ 'replacement, and a tree git cannot list. An empty replacement is allowed.',
431
+ '',
418
432
  'Examples:',
419
433
  ' canon gov superseded feature-feat- feature-',
420
434
  ' canon gov superseded feature-feat- feature- --json',
@@ -496,6 +510,9 @@ export function register(program: Command): void {
496
510
  ' 2 at least one cited path resolves to nothing, or one glob matches',
497
511
  ' nothing in this tree',
498
512
  '',
513
+ 'Refuses a tree holding neither rule corpus, and a `git check-ignore`',
514
+ 'read that fails.',
515
+ '',
499
516
  'Examples:',
500
517
  ' canon gov citations',
501
518
  ' canon gov citations --json',
@@ -543,6 +560,10 @@ export function register(program: Command): void {
543
560
  ' 1 refused, with the reason on stderr or in the JSON record',
544
561
  ' 2 at least one instruction is restated outside a declared mirror',
545
562
  '',
563
+ 'Reasons:',
564
+ ' no-instructions the tree holds no always-loaded file or seed to read',
565
+ ' no-surfaces the tree holds no skills tree or rules tree to match against',
566
+ '',
546
567
  'Examples:',
547
568
  ' canon gov restated',
548
569
  ' canon gov restated --json',
@@ -1268,8 +1289,9 @@ async function runInstall(
1268
1289
  intro('canon gov install')
1269
1290
 
1270
1291
  const add = opts.add ?? ''
1292
+ const yes = opts.yes === true
1271
1293
  if (add !== '' && isAddWithoutStack(stack, target)) {
1272
- return installExtras(add, stack ?? target)
1294
+ return installExtras(add, stack ?? target, yes)
1273
1295
  }
1274
1296
 
1275
1297
  const resolved = resolveTarget(target, PROJECT_ROOT)
@@ -1314,14 +1336,7 @@ async function runInstall(
1314
1336
  for (const rule of missing) logWarn(`${rule} (source not found, skipping)`)
1315
1337
  for (const source of found) logInfo(source.rule)
1316
1338
 
1317
- const shouldInstall = await select({
1318
- message: `Install ${found.length} rules to ${displayPath(target, CANON_RULES_REL)}?`,
1319
- options: [
1320
- { value: true, label: 'Yes' },
1321
- { value: false, label: 'No' },
1322
- ],
1323
- nonInteractiveDefault: true,
1324
- })
1339
+ const shouldInstall = await confirmInstall(found.length, target, yes)
1325
1340
 
1326
1341
  if (!shouldInstall) {
1327
1342
  logWarn('Cancelled')
@@ -1347,6 +1362,28 @@ async function runInstall(
1347
1362
  return 0
1348
1363
  }
1349
1364
 
1365
+ /**
1366
+ * The install confirm. `--yes` answers it without a prompt, so the line it
1367
+ * would have printed is skipped rather than duplicated under
1368
+ * `CANON_NON_INTERACTIVE=1`.
1369
+ */
1370
+ async function confirmInstall(
1371
+ count: number,
1372
+ target: string,
1373
+ yes: boolean,
1374
+ ): Promise<boolean> {
1375
+ if (yes) return true
1376
+ return select({
1377
+ message: `Install ${count} rules to ${displayPath(target, CANON_RULES_REL)}?`,
1378
+ options: [
1379
+ { value: true, label: 'Yes' },
1380
+ { value: false, label: 'No' },
1381
+ ],
1382
+ nonInteractiveDefault: true,
1383
+ ttyHint: 'Pass --yes',
1384
+ })
1385
+ }
1386
+
1350
1387
  /**
1351
1388
  * Whether `--add` runs with no stack under it. Commander fills the first
1352
1389
  * positional before the second, so `--add a,b ../my-app` arrives with the
@@ -1366,7 +1403,11 @@ function isAddWithoutStack(stack: string | undefined, target: string): boolean {
1366
1403
  * extras are not a stack, and overwriting the chain would cost a target the
1367
1404
  * missing-rule report its stack install recorded.
1368
1405
  */
1369
- async function installExtras(add: string, target: string): Promise<number> {
1406
+ async function installExtras(
1407
+ add: string,
1408
+ target: string,
1409
+ yes: boolean,
1410
+ ): Promise<number> {
1370
1411
  const resolved = resolveTarget(target, PROJECT_ROOT)
1371
1412
  if (typeof resolved === 'number') return resolved
1372
1413
 
@@ -1382,14 +1423,7 @@ async function installExtras(add: string, target: string): Promise<number> {
1382
1423
  return 1
1383
1424
  }
1384
1425
 
1385
- const shouldInstall = await select({
1386
- message: `Install ${found.length} rules to ${displayPath(target, CANON_RULES_REL)}?`,
1387
- options: [
1388
- { value: true, label: 'Yes' },
1389
- { value: false, label: 'No' },
1390
- ],
1391
- nonInteractiveDefault: true,
1392
- })
1426
+ const shouldInstall = await confirmInstall(found.length, target, yes)
1393
1427
 
1394
1428
  if (!shouldInstall) {
1395
1429
  logWarn('Cancelled')
@@ -64,6 +64,12 @@ export function register(program: Command): void {
64
64
  .addHelpText(
65
65
  'after',
66
66
  [
67
+ '',
68
+ 'Refusals, read off `reason`:',
69
+ ' no-intake the project holds no intake folder',
70
+ ' no-folder no folder matches the slug',
71
+ ' ambiguous-slug a bare slug matches more than one ordinal-prefixed',
72
+ ' folder, all named in `detail`',
67
73
  '',
68
74
  'Exit codes:',
69
75
  ' 0 the folders or items were listed',
@@ -105,6 +111,17 @@ export function register(program: Command): void {
105
111
  .addHelpText(
106
112
  'after',
107
113
  [
114
+ '',
115
+ 'Refusals, read off `reason`:',
116
+ ' no-intake the project holds no intake folder',
117
+ ' no-folder no folder matches the slug',
118
+ ' ambiguous-slug a bare slug matches more than one ordinal-prefixed',
119
+ ' folder, all named in `detail`',
120
+ ' no-cluster the cluster file named does not exist',
121
+ ' no-item the item number named does not exist in the cluster',
122
+ ' answered the item already carries an answer',
123
+ ' bad-input a malformed command line, such as no selection, an empty',
124
+ ' answer, or two answers for one item',
108
125
  '',
109
126
  'Exit codes:',
110
127
  ' 0 every named item now carries its answer',
@@ -111,6 +111,10 @@ export function register(program: Command): void {
111
111
  ' 1 refused, with the reason on stderr or in the JSON record',
112
112
  ' 2 at least one changed path is reached by no row',
113
113
  '',
114
+ 'Reasons:',
115
+ ' no-map the project declares no label map, so it labels nothing. An',
116
+ ' expected absence for `canon audits run`, not a fault',
117
+ '',
114
118
  'Examples:',
115
119
  ' canon labels audit',
116
120
  ' canon labels audit --json',
@@ -130,7 +134,10 @@ export function register(program: Command): void {
130
134
  )
131
135
  .argument('<number>', "This branch's open pull request")
132
136
  .helpOption('-h, --help', 'Show this help message')
133
- .option('--base <ref>', 'Far side of the range, defaulting to the trunk')
137
+ .option(
138
+ '--base <ref>',
139
+ "Read the git range back to this ref instead of the pull request's files",
140
+ )
134
141
  .option('--root <path>', 'Repository to read, defaulting to the cwd')
135
142
  .option('--check', 'Report the labels owed and write nothing')
136
143
  .option('--json', 'Add a machine-readable record on stdout')
@@ -138,9 +145,10 @@ export function register(program: Command): void {
138
145
  'after',
139
146
  [
140
147
  '',
141
- 'Resolves the earned set the way `canon labels audit` does, binds to the',
142
- 'pull request only when its head is the current branch and it is open,',
143
- 'and adds each earned label the pull request lacks over REST.',
148
+ "Resolves the earned set from the pull request's own files listing, or",
149
+ 'from the git range when --base is passed, binds to the pull request only',
150
+ 'when its head is the current branch and it is open, and adds each earned',
151
+ 'label the pull request lacks over REST.',
144
152
  '',
145
153
  'It adds and never removes, so a label a person applied by hand stays.',
146
154
  'A label the remote does not carry is reported under missingOnRemote and',
@@ -155,6 +163,12 @@ export function register(program: Command): void {
155
163
  ' 2 labels are still owed, being any under --check, or a label the',
156
164
  ' remote lacks or a failed add otherwise',
157
165
  '',
166
+ 'Reasons:',
167
+ ' head-mismatch the pull request is not open on the current branch, under',
168
+ ' --check as well',
169
+ ' gh-failed the files listing or the labels could not be read, and the',
170
+ ' run does not fall back to git',
171
+ '',
158
172
  'Examples:',
159
173
  ' canon labels apply 123 --json',
160
174
  ' canon labels apply 123 --check',
@@ -396,7 +410,26 @@ async function runApply(number: string, opts: ApplyOptions): Promise<number> {
396
410
  if (target === null) return refuse('gh-failed')
397
411
  if (target.trim() !== `${head} open`) return refuse('head-mismatch')
398
412
 
399
- const report = await auditLabels(root, { base: opts.base })
413
+ // An explicit --base is an explicit request for the git route. Otherwise the
414
+ // pull request's own file listing is the diff the labels describe, and a
415
+ // failed read refuses rather than falling back, since binding needed gh too.
416
+ let paths: string[] | undefined
417
+ if (opts.base === undefined) {
418
+ const listing = await gh(root, [
419
+ 'api',
420
+ '--paginate',
421
+ `repos/{owner}/{repo}/pulls/${number}/files`,
422
+ '--jq',
423
+ '.[].filename',
424
+ ])
425
+ if (listing === null) return refuse('gh-failed')
426
+ paths = nonEmptyLines(listing)
427
+ }
428
+
429
+ const report = await auditLabels(root, {
430
+ base: opts.base,
431
+ ...(paths !== undefined && { paths }),
432
+ })
400
433
 
401
434
  // A project declaring no map earns nothing and is labelled silently, which
402
435
  // is an answer and exits clean rather than reading as a break.