@erclx/canon 5.25.0 → 5.27.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 (315) hide show
  1. package/README.md +4 -3
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/api-design/SKILL.md +2 -0
  4. package/claude/skills/auto-ship/SKILL.md +3 -11
  5. package/claude/skills/auto-ship/references/review-findings.md +20 -0
  6. package/claude/skills/backlog-triage/SKILL.md +2 -0
  7. package/claude/skills/bash-cli-script/SKILL.md +2 -0
  8. package/claude/skills/build-in-slices/SKILL.md +2 -0
  9. package/claude/skills/canon-cli/SKILL.md +2 -0
  10. package/claude/skills/canon-feedback/SKILL.md +2 -0
  11. package/claude/skills/canon-feedback-triage/SKILL.md +2 -0
  12. package/claude/skills/canon-operator/SKILL.md +2 -0
  13. package/claude/skills/canon-rollout/SKILL.md +3 -11
  14. package/claude/skills/canon-rollout/references/failure-recovery.md +20 -0
  15. package/claude/skills/canvas/SKILL.md +14 -1
  16. package/claude/skills/ci-workflow/SKILL.md +2 -0
  17. package/claude/skills/code-craft/SKILL.md +2 -0
  18. package/claude/skills/codebase-layout/SKILL.md +2 -0
  19. package/claude/skills/context-fold/REQUIREMENT.md +6 -2
  20. package/claude/skills/context-fold/SKILL.md +9 -21
  21. package/claude/skills/context-fold/references/anchor-sweep.md +2 -2
  22. package/claude/skills/context-fold/references/completion-report.md +26 -0
  23. package/claude/skills/create-rule/SKILL.md +2 -0
  24. package/claude/skills/create-skill/SKILL.md +2 -0
  25. package/claude/skills/create-standard/SKILL.md +2 -0
  26. package/claude/skills/decision-escalate/SKILL.md +2 -0
  27. package/claude/skills/deploy-app/SKILL.md +2 -0
  28. package/claude/skills/deprecation-migration/SKILL.md +2 -0
  29. package/claude/skills/design-extract/SKILL.md +2 -0
  30. package/claude/skills/design-taste/REQUIREMENT.md +1 -1
  31. package/claude/skills/design-taste/SKILL.md +6 -2
  32. package/claude/skills/design-taste/references/adopted.md +40 -0
  33. package/claude/skills/design-taste/references/kinds.md +1 -1
  34. package/claude/skills/design-taste/references/systems.md +1 -7
  35. package/claude/skills/design-taste/references/tells.md +2 -24
  36. package/claude/skills/docs-sync/REQUIREMENT.md +2 -1
  37. package/claude/skills/docs-sync/SKILL.md +2 -0
  38. package/claude/skills/document-health/REQUIREMENT.md +2 -0
  39. package/claude/skills/document-health/SKILL.md +23 -0
  40. package/claude/skills/document-health/references/axes.md +12 -0
  41. package/claude/skills/draft-and-pick/SKILL.md +3 -1
  42. package/claude/skills/draft-doc/SKILL.md +2 -0
  43. package/claude/skills/draft-doc/references/wiki.md +1 -1
  44. package/claude/skills/draft-figure/SKILL.md +2 -0
  45. package/claude/skills/draft-identity/SKILL.md +3 -1
  46. package/claude/skills/draft-ready/SKILL.md +2 -0
  47. package/claude/skills/draft-screencast/SKILL.md +11 -7
  48. package/claude/skills/draft-slides/REQUIREMENT.md +17 -12
  49. package/claude/skills/draft-slides/SKILL.md +33 -20
  50. package/claude/skills/git-branch/SKILL.md +2 -0
  51. package/claude/skills/git-commit/SKILL.md +2 -0
  52. package/claude/skills/git-followup/SKILL.md +2 -0
  53. package/claude/skills/git-issue/SKILL.md +2 -0
  54. package/claude/skills/git-issue/references/issue.md +10 -0
  55. package/claude/skills/git-pr/SKILL.md +2 -0
  56. package/claude/skills/git-pr/references/evidence.md +14 -6
  57. package/claude/skills/git-pr/references/labels.md +12 -0
  58. package/claude/skills/git-ship/SKILL.md +2 -0
  59. package/claude/skills/git-split/SKILL.md +21 -8
  60. package/claude/skills/git-stage/SKILL.md +2 -0
  61. package/claude/skills/git-worktree/SKILL.md +2 -0
  62. package/claude/skills/index-lookup/SKILL.md +2 -0
  63. package/claude/skills/markdown-propose/SKILL.md +2 -0
  64. package/claude/skills/markdown-propose/references/format.md +9 -0
  65. package/claude/skills/memory-capture/SKILL.md +2 -0
  66. package/claude/skills/memory-review/SKILL.md +2 -0
  67. package/claude/skills/memory-review/references/later-phases.md +7 -0
  68. package/claude/skills/plan-feature/SKILL.md +2 -0
  69. package/claude/skills/plan-groundwork/SKILL.md +2 -0
  70. package/claude/skills/plan-intake/SKILL.md +2 -0
  71. package/claude/skills/plan-intake-answer/SKILL.md +2 -0
  72. package/claude/skills/project-commands/SKILL.md +2 -0
  73. package/claude/skills/read-frames/SKILL.md +2 -0
  74. package/claude/skills/record-screencast/SKILL.md +2 -0
  75. package/claude/skills/repo-metadata/SKILL.md +2 -0
  76. package/claude/skills/restate-plainly/SKILL.md +2 -0
  77. package/claude/skills/review-address/SKILL.md +3 -7
  78. package/claude/skills/review-address/references/post-review-findings.md +16 -0
  79. package/claude/skills/review-branch/SKILL.md +2 -0
  80. package/claude/skills/review-craft/SKILL.md +2 -0
  81. package/claude/skills/review-craft/references/ui.md +2 -2
  82. package/claude/skills/review-pr/SKILL.md +4 -8
  83. package/claude/skills/review-pr/references/body-variants.md +52 -0
  84. package/claude/skills/review-pr/references/key-changes.md +16 -0
  85. package/claude/skills/review-pr/references/post.md +15 -43
  86. package/claude/skills/review-ui/SKILL.md +3 -1
  87. package/claude/skills/review-ui/references/post.md +4 -2
  88. package/claude/skills/role-orchestrator/SKILL.md +5 -14
  89. package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +8 -0
  90. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +13 -0
  91. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
  92. package/claude/skills/role-orchestrator/references/orchestrator-inbound.md +23 -0
  93. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +5 -33
  94. package/claude/skills/role-orchestrator/references/orchestrator-poll.md +1 -21
  95. package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +1 -1
  96. package/claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md +38 -0
  97. package/claude/skills/role-orchestrator/references/orchestrator-watch.md +26 -0
  98. package/claude/skills/role-planner/SKILL.md +2 -0
  99. package/claude/skills/role-reviewer/REQUIREMENT.md +1 -1
  100. package/claude/skills/role-reviewer/SKILL.md +2 -0
  101. package/claude/skills/role-worker/SKILL.md +2 -0
  102. package/claude/skills/search-craft/REQUIREMENT.md +1 -1
  103. package/claude/skills/search-craft/SKILL.md +5 -1
  104. package/claude/skills/search-craft/references/adopted.md +30 -0
  105. package/claude/skills/seed-sync/SKILL.md +2 -0
  106. package/claude/skills/session-compact/SKILL.md +2 -0
  107. package/claude/skills/session-map/SKILL.md +2 -0
  108. package/claude/skills/session-relay/SKILL.md +2 -0
  109. package/claude/skills/session-resume/SKILL.md +2 -0
  110. package/claude/skills/session-worktree/SKILL.md +3 -11
  111. package/claude/skills/session-worktree/references/install-command.md +20 -0
  112. package/claude/skills/sketch-design/SKILL.md +2 -0
  113. package/claude/skills/systematic-debugging/SKILL.md +2 -0
  114. package/claude/skills/target-check/SKILL.md +2 -0
  115. package/claude/skills/target-setup/SKILL.md +2 -0
  116. package/claude/skills/target-setup/references/detect.md +8 -0
  117. package/claude/skills/target-setup/references/indexes.md +13 -0
  118. package/claude/skills/target-setup/references/verify.md +8 -0
  119. package/claude/skills/task-board/SKILL.md +2 -0
  120. package/claude/skills/teach-workspace/SKILL.md +5 -15
  121. package/claude/skills/teach-workspace/references/lesson-build.md +75 -0
  122. package/claude/skills/teach-workspace/references/lesson-craft.md +13 -69
  123. package/claude/skills/teach-workspace/references/revisit.md +22 -0
  124. package/claude/skills/test-craft/SKILL.md +2 -0
  125. package/claude/skills/test-first/SKILL.md +4 -0
  126. package/claude/skills/test-first/references/adopted.md +36 -0
  127. package/claude/skills/ui-checklist/SKILL.md +2 -0
  128. package/claude/skills/ux-audit/SKILL.md +2 -0
  129. package/claude/skills/ux-measure/SKILL.md +2 -0
  130. package/claude/skills/ux-walkthrough/SKILL.md +2 -0
  131. package/claude/skills/video-craft/SKILL.md +2 -0
  132. package/claude/skills/write-human/SKILL.md +3 -1
  133. package/claude/skills/write-human/references/machine-tells.md +13 -0
  134. package/claude/skills/youtube-transcripts/SKILL.md +2 -0
  135. package/docs/agents/canvas.md +24 -6
  136. package/docs/agents/capture.md +6 -6
  137. package/docs/agents/commands.md +5 -5
  138. package/docs/agents/index.md +5 -5
  139. package/docs/agents/install-and-sync.md +7 -0
  140. package/docs/agents/key-changes.md +1 -1
  141. package/docs/agents/pr-evidence.md +38 -15
  142. package/docs/agents/pr-local.md +19 -13
  143. package/docs/agents/pr-preview.md +44 -21
  144. package/docs/agents/records-stale.md +22 -9
  145. package/docs/agents/records.md +1 -1
  146. package/docs/agents/sandbox.md +29 -5
  147. package/docs/agents/scripting.md +8 -1
  148. package/docs/agents/skills-audit.md +11 -3
  149. package/docs/agents/slides.md +15 -3
  150. package/docs/workflow/ai-workflow.md +1 -1
  151. package/docs/workflow/index.md +1 -0
  152. package/docs/workflow/operating-model.md +5 -6
  153. package/docs/workflow/parallel-features.md +23 -2
  154. package/docs/workflow/skill-map-on-request.md +42 -0
  155. package/docs/workflow/skill-map.md +3 -34
  156. package/docs/workflow/zshrc-aliases.md +1 -1
  157. package/package.json +1 -1
  158. package/scripts/core/regen-hero.ts +1 -1
  159. package/scripts/lib/sandbox-git.sh +1 -9
  160. package/scripts/lib/sandbox-path.sh +6 -162
  161. package/scripts/sandbox-hook.sh +99 -0
  162. package/src/audits/run.ts +1 -1
  163. package/src/canvas/address.ts +12 -3
  164. package/src/canvas/capture.ts +1 -9
  165. package/src/canvas/client/inspector/align-grid.tsx +162 -0
  166. package/src/canvas/client/inspector/color-field.tsx +398 -0
  167. package/src/canvas/client/inspector/color-inputs.tsx +139 -0
  168. package/src/canvas/client/inspector/color-picker.tsx +668 -0
  169. package/src/canvas/client/inspector/color-space.ts +240 -0
  170. package/src/canvas/client/inspector/field.tsx +5 -1
  171. package/src/canvas/client/inspector/fields.css +547 -17
  172. package/src/canvas/client/inspector/size-field.tsx +69 -0
  173. package/src/canvas/client/inspector/values.ts +166 -1
  174. package/src/canvas/client/inspector.tsx +236 -73
  175. package/src/canvas/client/selection.tsx +197 -0
  176. package/src/canvas/client/shell.css +104 -12
  177. package/src/canvas/client/state.ts +69 -9
  178. package/src/canvas/client/surface.tsx +147 -22
  179. package/src/canvas/content.ts +50 -1
  180. package/src/canvas/edit.ts +37 -4
  181. package/src/canvas/editing.ts +203 -0
  182. package/src/canvas/global.d.ts +15 -0
  183. package/src/canvas/server.ts +149 -12
  184. package/src/canvas/tokens.ts +59 -17
  185. package/src/capture/render.ts +45 -7
  186. package/src/capture/sources.ts +43 -0
  187. package/src/census/count.ts +2 -2
  188. package/src/claude/cases/workflow.ts +5 -0
  189. package/src/claude/seeds.ts +2 -2
  190. package/src/claude/skills-audit.ts +96 -14
  191. package/src/claude/skills-drift.ts +1 -1
  192. package/src/claude/skills-families.ts +36 -0
  193. package/src/claude/skills-list.ts +76 -15
  194. package/src/commands/audits.ts +2 -2
  195. package/src/commands/canvas.ts +179 -2
  196. package/src/commands/claude.ts +94 -11
  197. package/src/commands/design.ts +142 -2
  198. package/src/commands/feedback.ts +3 -3
  199. package/src/commands/gov.ts +1 -1
  200. package/src/commands/hooks.ts +1 -1
  201. package/src/commands/indexes.ts +1 -1
  202. package/src/commands/init.ts +1 -1
  203. package/src/commands/intake.ts +1 -1
  204. package/src/commands/migrate.ts +1 -1
  205. package/src/commands/pr.ts +105 -58
  206. package/src/commands/records.ts +89 -7
  207. package/src/commands/repo.ts +1 -1
  208. package/src/commands/sandbox.ts +103 -11
  209. package/src/commands/sessions.ts +1 -1
  210. package/src/commands/slides.ts +31 -77
  211. package/src/commands/sync.ts +1 -1
  212. package/src/commands/tasks.ts +2 -2
  213. package/src/commands/teach.ts +1 -1
  214. package/src/commands/transcripts.ts +1 -1
  215. package/src/commands/worktrees.ts +1 -1
  216. package/src/comments/scan.ts +1 -1
  217. package/src/comments/trend.ts +1 -1
  218. package/src/context/audit.ts +1 -1
  219. package/src/context/citations.ts +1 -1
  220. package/src/context/classify/extract.ts +2 -2
  221. package/src/design/project-fonts.ts +392 -0
  222. package/src/design/tokens.ts +2 -18
  223. package/src/docs/read.ts +1 -1
  224. package/src/{github.ts → feedback/github.ts} +2 -2
  225. package/src/{client-commands.ts → gate/client-commands.ts} +1 -1
  226. package/src/gate/measures.ts +53 -13
  227. package/src/gate/sequencer.ts +1 -1
  228. package/src/gate/stages.ts +9 -0
  229. package/src/{git-files.ts → git/files.ts} +2 -2
  230. package/src/{git-ignore.ts → git/ignore.ts} +1 -1
  231. package/src/{worktree.ts → git/worktree.ts} +1 -1
  232. package/src/gov/citations.ts +2 -2
  233. package/src/gov/install.ts +1 -1
  234. package/src/gov/payload.ts +1 -1
  235. package/src/gov/superseded.ts +3 -3
  236. package/src/gov/test-order.ts +2 -2
  237. package/src/help.ts +1 -1
  238. package/src/hooks/post-merge.ts +1 -1
  239. package/src/indexes/walk.ts +1 -1
  240. package/src/labels/audit.ts +1 -1
  241. package/src/markdown/ceiling.ts +1 -1
  242. package/src/markdown/files.ts +1 -1
  243. package/src/migrate/apply.ts +1 -1
  244. package/src/pr/bijection.ts +1 -1
  245. package/src/pr/evidence.ts +96 -43
  246. package/src/pr/local.ts +16 -10
  247. package/src/pr/preview.ts +78 -2
  248. package/src/record-root.ts +1 -0
  249. package/src/records/backup.ts +2 -1
  250. package/src/records/push-guard.ts +1 -1
  251. package/src/records/stale-canonical.ts +146 -0
  252. package/src/records/stale.ts +1 -1
  253. package/src/sandbox/headless/dispatch.ts +264 -0
  254. package/src/sandbox/headless/run.ts +653 -0
  255. package/src/sandbox/headless/watch.ts +145 -0
  256. package/src/sandbox/hooks.ts +128 -0
  257. package/src/sandbox/provision.ts +607 -0
  258. package/src/sandbox/tree.ts +99 -4
  259. package/src/secrets/marker.ts +2 -2
  260. package/src/secrets/scan.ts +2 -2
  261. package/src/sessions/claim.ts +1 -1
  262. package/src/sessions/resolve.ts +1 -1
  263. package/src/sessions/transfer/bundle.ts +1 -1
  264. package/src/shipped/references.ts +2 -2
  265. package/src/slides/convert/export.ts +2 -16
  266. package/src/slides/decks.ts +110 -0
  267. package/src/slides/package/fonts.ts +6 -2
  268. package/src/standards/audit.ts +2 -2
  269. package/src/standards/read.ts +1 -1
  270. package/src/sync/check.ts +2 -2
  271. package/src/sync/engine.ts +2 -2
  272. package/src/sync/git.ts +1 -1
  273. package/src/sync/history.ts +1 -1
  274. package/src/sync/reverse.ts +1 -1
  275. package/src/sync/seeds-report.ts +1 -1
  276. package/src/sync/target.ts +1 -1
  277. package/src/sync/workflow.ts +1 -1
  278. package/src/targets/pulls.ts +2 -2
  279. package/src/targets/sweep.ts +2 -2
  280. package/src/tasks/answers.ts +1 -1
  281. package/src/tasks/archive.ts +1 -1
  282. package/src/tasks/reach.ts +1 -1
  283. package/src/tasks/trunk.ts +1 -1
  284. package/src/teach/nav.ts +1 -1
  285. package/src/tooling/inject.ts +2 -2
  286. package/src/tooling/subfolder.ts +1 -1
  287. package/src/web/readme-citations.ts +2 -2
  288. package/src/worktrees/bare-flag.ts +1 -1
  289. package/src/worktrees/reclaim.ts +2 -2
  290. package/src/worktrees/remove.ts +1 -1
  291. package/standards/architecture.md +1 -0
  292. package/standards/canvas.md +3 -2
  293. package/standards/requirements.md +2 -0
  294. package/standards/skill-practice.md +2 -1
  295. package/standards/skill.md +2 -0
  296. package/standards/wiki.md +12 -3
  297. package/tooling/base/configs/.github/workflows/pr-local-preview.yml +2 -2
  298. package/scripts/lib/sandbox-dispatch.sh +0 -190
  299. package/scripts/manage-sandbox.sh +0 -472
  300. package/src/slides/layouts.ts +0 -478
  301. package/src/slides/parse.ts +0 -84
  302. package/src/slides/render.ts +0 -110
  303. package/src/slides/styles.ts +0 -96
  304. /package/claude/skills/write-human/references/{source-material.md → adopted.md} +0 -0
  305. /package/src/{commands/feedback-format.ts → feedback/body.ts} +0 -0
  306. /package/src/{github-format.ts → feedback/github-format.ts} +0 -0
  307. /package/src/{git-base.ts → git/base.ts} +0 -0
  308. /package/src/{git-env.ts → git/env.ts} +0 -0
  309. /package/src/{frontmatter.ts → markdown/frontmatter.ts} +0 -0
  310. /package/src/{binary.ts → sweep/binary.ts} +0 -0
  311. /package/src/{exempt-marker.ts → sweep/exempt-marker.ts} +0 -0
  312. /package/src/{copy.ts → sync/copy.ts} +0 -0
  313. /package/src/{target.ts → targets/validate.ts} +0 -0
  314. /package/src/{paths.ts → tasks/paths.ts} +0 -0
  315. /package/src/{seed-marker.ts → tooling/seed-marker.ts} +0 -0
@@ -1,7 +1,6 @@
1
1
  import { existsSync, readFileSync, readdirSync } from 'node:fs'
2
2
  import { join } from 'node:path'
3
3
  import type { Command } from 'commander'
4
- import { execScript } from '@/exec'
5
4
  import { checkoutMismatchWarning, PROJECT_ROOT } from '@/project-root'
6
5
  import {
7
6
  assertedPercent,
@@ -26,6 +25,8 @@ import {
26
25
  type RunEnvelope,
27
26
  type Verdict,
28
27
  } from '@/sandbox/expect'
28
+ import { runHeadless } from '@/sandbox/headless/run'
29
+ import { runSandbox } from '@/sandbox/provision'
29
30
  import { sandboxTree } from '@/sandbox/tree'
30
31
  import {
31
32
  frameError,
@@ -36,6 +37,7 @@ import {
36
37
  logStep,
37
38
  logWarn,
38
39
  outro,
40
+ palette,
39
41
  select,
40
42
  } from '@/ui'
41
43
 
@@ -69,11 +71,39 @@ function reportAbsentScenarioTree(): boolean {
69
71
  return true
70
72
  }
71
73
 
74
+ /** Holds fixture content for scenarios rather than scenarios of its own. */
75
+ const FIXTURES_DIR = 'fixtures'
76
+
72
77
  /**
73
- * Holds fixture content for scenarios rather than scenarios of its own.
74
- * Twin of the `-not -name fixtures` filter in `scripts/manage-sandbox.sh`.
78
+ * The provision verb takes positional words rather than options, since `reset`
79
+ * and `clean` share its slot with a target, so its usage is written out here
80
+ * rather than generated by commander.
75
81
  */
76
- const FIXTURES_DIR = 'fixtures'
82
+ function showProvisionHelp(): void {
83
+ const { GREY, NC, WHITE } = palette(process.stdout)
84
+ const lines = [
85
+ `${GREY}┌${NC}`,
86
+ `${GREY}├${NC} ${WHITE}Usage:${NC} canon sandbox [cat:cmd]`,
87
+ `${GREY}│${NC}`,
88
+ `${GREY}│${NC} ${WHITE}Arguments:${NC}`,
89
+ `${GREY}│${NC} cat:cmd ${GREY}# Scenario to provision (e.g. git:commit)${NC}`,
90
+ `${GREY}│${NC}`,
91
+ `${GREY}│${NC} ${WHITE}Commands:${NC}`,
92
+ `${GREY}│${NC} reset ${GREY}# Reset sandbox to baseline${NC}`,
93
+ `${GREY}│${NC} clean ${GREY}# Wipe the sandbox${NC}`,
94
+ `${GREY}│${NC}`,
95
+ `${GREY}│${NC} ${WHITE}Options:${NC}`,
96
+ `${GREY}│${NC} -h, --help ${GREY}# Show this help message${NC}`,
97
+ `${GREY}│${NC}`,
98
+ `${GREY}│${NC} ${WHITE}Examples:${NC}`,
99
+ `${GREY}│${NC} canon sandbox`,
100
+ `${GREY}│${NC} canon sandbox git:commit`,
101
+ `${GREY}│${NC} canon sandbox reset`,
102
+ `${GREY}│${NC} canon sandbox clean`,
103
+ `${GREY}└${NC}`,
104
+ ]
105
+ process.stdout.write(`${lines.join('\n')}\n`)
106
+ }
77
107
 
78
108
  /** A run that reported nothing counts as clean, so only real signals fail. */
79
109
  const CLEAN_ENVELOPE: RunEnvelope = { isError: false, turns: 0, denials: 0 }
@@ -477,16 +507,78 @@ export function register(program: Command): void {
477
507
  .action(async (_opts: unknown, cmd: Command) => {
478
508
  if (reportAbsentScenarioTree()) return
479
509
 
480
- const args = cmd.args
510
+ const mismatch = checkoutMismatchWarning(process.cwd())
511
+ if (mismatch !== undefined) logWarn(mismatch)
512
+
513
+ // `--no-header` is the frame opt-out for a caller that already opened
514
+ // one, which `canon sandbox run` and the picker below both do.
515
+ const isHeader = cmd.args[0] !== '--no-header'
516
+ const args = isHeader ? cmd.args : cmd.args.slice(1)
481
517
 
482
- if (args.length === 0) {
483
- const resolved = await interactivePicker()
484
- await execScript('manage-sandbox.sh', ['--no-header', resolved])
485
- } else {
486
- await execScript('manage-sandbox.sh', args)
518
+ if (args[0] === '-h' || args[0] === '--help') {
519
+ showProvisionHelp()
520
+ return
487
521
  }
522
+
523
+ const target = args[0] ?? (await interactivePicker())
524
+ process.exitCode = await runSandbox(PROJECT_ROOT, {
525
+ isHeader: isHeader && args[0] !== undefined,
526
+ target,
527
+ arm: args[1] === '' ? undefined : args[1],
528
+ })
488
529
  })
489
530
 
531
+ sandbox
532
+ .command('run')
533
+ .description(
534
+ 'Provision a scenario, drive one claude -p session over it, and print the envelope with its verdict',
535
+ )
536
+ .argument('[target]', 'Scenario as <category>:<command>, e.g. git:commit')
537
+ .argument('[prompt]', 'Skill invocation, e.g. "/canon:git-commit"')
538
+ .argument('[arm]', 'Named scenario arm, e.g. small')
539
+ .helpOption('-h, --help', 'Show this help message')
540
+ .addHelpText(
541
+ 'after',
542
+ [
543
+ '',
544
+ 'Env overrides:',
545
+ ' CANON_SKILL_TEST_MODEL default sonnet',
546
+ ' CANON_SKILL_TEST_TOOLS default Bash,Read,Glob,Grep,Edit,Write',
547
+ ' CANON_SKILL_TEST_MAX_TURNS default 30',
548
+ ' CANON_SKILL_TEST_PERMISSION_MODE default bypassPermissions',
549
+ '',
550
+ 'Examples:',
551
+ ' canon sandbox run git:commit "/canon:git-commit"',
552
+ ' canon sandbox run claude:plan-feature "/canon:plan-feature add a widget" small',
553
+ '',
554
+ 'Bills a real session through the claude binary on PATH.',
555
+ 'Exit codes: the verdict, 1 when the envelope does not parse, or the',
556
+ 'provisioning or session code when either stops the run first.',
557
+ ].join('\n'),
558
+ )
559
+ .action(
560
+ async (
561
+ target: string | undefined,
562
+ prompt: string | undefined,
563
+ arm: string | undefined,
564
+ _opts: unknown,
565
+ cmd: Command,
566
+ ) => {
567
+ if (target === undefined) {
568
+ cmd.help()
569
+ return
570
+ }
571
+ if (reportAbsentScenarioTree()) return
572
+
573
+ process.exitCode = await runHeadless({
574
+ projectRoot: PROJECT_ROOT,
575
+ target,
576
+ prompt,
577
+ arm,
578
+ })
579
+ },
580
+ )
581
+
490
582
  sandbox
491
583
  .command('check')
492
584
  .description('Check a provisioned sandbox against a scenario expectation')
@@ -588,7 +680,7 @@ export function register(program: Command): void {
588
680
  ' canon sandbox equivalence claude --json',
589
681
  ' canon sandbox equivalence claude:plan-feature --base origin/main',
590
682
  '',
591
- 'Provisions offline only and never runs sandbox/run.sh or a claude binary.',
683
+ 'Provisions offline only and never runs canon sandbox run or a claude binary.',
592
684
  '--stub-remote also runs the anchor arms, and wins over --include-anchor.',
593
685
  'Exit codes: 0 when every compared arm is identical or red the same way',
594
686
  'on both sides, 1 on any difference, exit mismatch, or enumeration error.',
@@ -24,7 +24,7 @@ import {
24
24
  pipeOutput,
25
25
  plural,
26
26
  } from '@/ui'
27
- import { mainWorktreeRoot } from '@/worktree'
27
+ import { mainWorktreeRoot } from '@/git/worktree'
28
28
 
29
29
  interface ListCommandOptions {
30
30
  readonly json?: boolean
@@ -1,14 +1,13 @@
1
- import { existsSync, statSync } from 'node:fs'
2
1
  import { resolve } from 'node:path'
3
2
  import type { Command } from 'commander'
4
3
  import { INSTALL_BROWSER } from '@/browser/engine'
5
4
  import { creationRel, SCRATCH } from '@/record-root'
6
- import { LAYOUTS } from '@/slides/layouts'
5
+ import { listDecks, resolveDeck, SLIDES_FOLDER } from '@/slides/decks'
7
6
  import { openDeck } from '@/slides/open'
8
- import { renderSlidesDoc } from '@/slides/render'
9
- import type { Variant } from '@/slides/styles'
10
7
  import { intro, outro, palette } from '@/ui'
11
8
 
9
+ const SLIDES_AT = creationRel(process.cwd(), SLIDES_FOLDER)
10
+
12
11
  export function register(program: Command): void {
13
12
  const slides = program
14
13
  .command('slides')
@@ -19,77 +18,34 @@ export function register(program: Command): void {
19
18
  .command('render')
20
19
  .helpOption('-h, --help', 'Show this help message')
21
20
  .description(
22
- 'Render a SLIDES.md source, or a folder of HTML slides, into a PowerPoint deck',
21
+ `Render a deck under ${SLIDES_AT}/, or a folder of HTML slides, into a PowerPoint deck`,
23
22
  )
24
- .option(
25
- '-s, --source <path>',
26
- 'Source SLIDES.md file, or a folder of .html slides read in filename order',
27
- '.claude/SLIDES.md',
23
+ .argument(
24
+ '[deck]',
25
+ 'Deck name or folder, defaulting to the only deck when there is one',
28
26
  )
29
27
  .option(
30
28
  '-o, --out <path>',
31
29
  'Output directory',
32
30
  creationRel(process.cwd(), SCRATCH, 'render', 'slides'),
33
31
  )
34
- .option('-v, --variant <variant>', 'Override variant (light or dark)')
35
32
  .option(
36
33
  '-m, --mirror <path>',
37
34
  'Copy the deck to this directory after writing',
38
35
  )
39
36
  .option('--open', 'Open the deck after writing')
40
37
  .action(
41
- async (opts: {
42
- source: string
43
- out: string
44
- variant?: string
45
- mirror?: string
46
- open?: boolean
47
- }) => {
38
+ async (
39
+ deck: string | undefined,
40
+ opts: { out: string; mirror?: string; open?: boolean },
41
+ ) => {
48
42
  try {
49
- const sourcePath = resolve(process.cwd(), opts.source)
50
- const outDir = resolve(process.cwd(), opts.out)
51
- if (!existsSync(sourcePath)) {
52
- fail(`${opts.source} not found`)
53
- }
43
+ const root = process.cwd()
44
+ const resolved = resolveDeck(root, deck)
45
+ if (resolved.status === 'refused') fail(resolved.message)
46
+ const outDir = resolve(root, opts.out)
54
47
  const mirror = resolveMirror(opts.mirror)
55
- if (statSync(sourcePath).isDirectory()) {
56
- if (opts.variant !== undefined) {
57
- fail('--variant applies to a SLIDES.md source, not a folder')
58
- }
59
- await renderHtmlFolder(sourcePath, outDir, mirror, opts.open)
60
- return
61
- }
62
- const variant = parseVariant(opts.variant)
63
- const { GREEN, GREY, NC, RED } = palette(process.stderr)
64
- intro('Render slides')
65
- const result = await renderSlidesDoc(sourcePath, outDir, {
66
- variant,
67
- mirror,
68
- })
69
- process.stderr.write(
70
- `${GREY}│${NC} ${GREEN}✓${NC} ${result.slideCount} slides\n${GREY}│${NC} ${GREEN}✓${NC} ${result.pptxPath}\n`,
71
- )
72
- const validNames = LAYOUTS.map((layout) => layout.name).join(', ')
73
- for (const { value, slideNumbers } of result.unrecognizedLayouts) {
74
- const noun = slideNumbers.length === 1 ? 'slide' : 'slides'
75
- process.stderr.write(
76
- `${GREY}│${NC} ${RED}✗${NC} unrecognized layout "${value}" on ${noun} ${slideNumbers.join(', ')}. Valid layouts: ${validNames}\n`,
77
- )
78
- }
79
- if (result.mirrorPath) {
80
- process.stderr.write(
81
- `${GREY}│${NC} ${GREEN}✓${NC} mirrored to ${result.mirrorPath}\n`,
82
- )
83
- }
84
- if (opts.open) {
85
- const target = result.mirrorPath ?? result.pptxPath
86
- const opened = await openDeck(target)
87
- const mark = opened ? `${GREEN}✓${NC}` : `${RED}✗${NC}`
88
- process.stderr.write(
89
- `${GREY}│${NC} ${mark} ${opened ? 'opened' : 'could not open'} ${target}\n`,
90
- )
91
- }
92
- outro()
48
+ await renderHtmlFolder(resolved.dir, outDir, mirror, opts.open)
93
49
  } catch (error) {
94
50
  reportFailure(error)
95
51
  }
@@ -99,18 +55,23 @@ export function register(program: Command): void {
99
55
  slides
100
56
  .command('list')
101
57
  .helpOption('-h, --help', 'Show this help message')
102
- .description('List the available slide layouts')
103
- .option('--json', 'Output the layout catalog as JSON')
58
+ .description(`List the decks under ${SLIDES_AT}/`)
59
+ .option('--json', 'Output the decks as JSON')
104
60
  .action((opts: { json?: boolean }) => {
61
+ const decks = listDecks(process.cwd())
105
62
  if (opts.json) {
106
- process.stdout.write(`${JSON.stringify(LAYOUTS)}\n`)
63
+ process.stdout.write(`${JSON.stringify(decks)}\n`)
107
64
  return
108
65
  }
109
66
  const { GREEN, GREY, NC } = palette(process.stderr)
110
- intro('Slide layouts')
111
- for (const layout of LAYOUTS) {
67
+ intro('Slide decks')
68
+ if (decks.length === 0) {
69
+ process.stderr.write(`${GREY}│${NC} No deck under ${SLIDES_AT}/\n`)
70
+ }
71
+ for (const { name, title, slides: count, path } of decks) {
72
+ const noun = count === 1 ? 'slide' : 'slides'
112
73
  process.stderr.write(
113
- `${GREY}│${NC} ${GREEN}✓${NC} ${layout.name} ${GREY}${layout.description}${NC}\n`,
74
+ `${GREY}│${NC} ${GREEN}✓${NC} ${name} ${GREY}${title}, ${count} ${noun}, ${path}${NC}\n`,
114
75
  )
115
76
  }
116
77
  outro()
@@ -121,8 +82,7 @@ export function register(program: Command): void {
121
82
  * The converter loads on demand, so no other command resolves pptxgenjs,
122
83
  * jszip, or the token module at startup. A fallback, a refused link, chart, or
123
84
  * effect, or a token the master fell back on reports one line each and keeps
124
- * the exit at 0, as an unrecognized layout does on the markdown path, since the
125
- * deck is written either way. A malformed `deck.json` refuses before any slide is laid
85
+ * the exit at 0, since the deck is written either way. A malformed `deck.json` refuses before any slide is laid
126
86
  * out and exits 1.
127
87
  */
128
88
  async function renderHtmlFolder(
@@ -192,12 +152,6 @@ async function renderHtmlFolder(
192
152
  outro()
193
153
  }
194
154
 
195
- function parseVariant(value: string | undefined): Variant | undefined {
196
- if (value === undefined) return undefined
197
- if (value === 'light' || value === 'dark') return value
198
- fail(`Invalid variant "${value}". Use light or dark.`)
199
- }
200
-
201
155
  function resolveMirror(value: string | undefined): string | undefined {
202
156
  const mirror = value ?? process.env.CANON_SLIDES_MIRROR
203
157
  return mirror ? resolve(process.cwd(), mirror) : undefined
@@ -205,9 +159,9 @@ function resolveMirror(value: string | undefined): string | undefined {
205
159
 
206
160
  /**
207
161
  * Carries a fail-fast message from a validation helper to the action that
208
- * called it. `fail` has to keep its `never` return, since that is what makes
209
- * `parseVariant` exhaustive to the compiler, and a helper deep in the call
210
- * stack cannot set `process.exitCode` and unwind on its own.
162
+ * called it. `fail` keeps its `never` return so the compiler narrows past it,
163
+ * and a helper deep in the call stack cannot set `process.exitCode` and unwind
164
+ * on its own.
211
165
  */
212
166
  class SlidesError extends Error {}
213
167
 
@@ -20,7 +20,7 @@ import {
20
20
  } from '@/sync/target'
21
21
  import type { UnclaimedFolder } from '@/sync/reverse'
22
22
  import { runGitWorkflow } from '@/sync/workflow'
23
- import { resolveTarget } from '@/target'
23
+ import { resolveTarget } from '@/targets/validate'
24
24
  import {
25
25
  intro,
26
26
  isNonInteractive,
@@ -1,7 +1,7 @@
1
1
  import { relative } from 'node:path'
2
2
  import type { Command } from 'commander'
3
3
  import { execa } from 'execa'
4
- import { gitEnv } from '@/git-env'
4
+ import { gitEnv } from '@/git/env'
5
5
  import { type AnswersOutcome, planAnswers } from '@/tasks/answers'
6
6
  import { type BranchOutcome, planBranch } from '@/tasks/branch'
7
7
  import {
@@ -59,7 +59,7 @@ import {
59
59
  outro,
60
60
  pipeOutput,
61
61
  } from '@/ui'
62
- import { mainWorktreeRoot } from '@/worktree'
62
+ import { mainWorktreeRoot } from '@/git/worktree'
63
63
 
64
64
  /** Returned when the board carries a finding, which is the gating result. */
65
65
  const EXIT_FINDINGS = 2
@@ -43,7 +43,7 @@ import {
43
43
  outro,
44
44
  pipeOutput,
45
45
  } from '@/ui'
46
- import { mainWorktreeRoot } from '@/worktree'
46
+ import { mainWorktreeRoot } from '@/git/worktree'
47
47
 
48
48
  /**
49
49
  * A pair as the command line spells it, split on the first `=` so a value
@@ -3,7 +3,7 @@ import type { Command } from 'commander'
3
3
  import { recordDir } from '@/record-root'
4
4
  import { ensureYtDlp, fetchOne } from '@/transcripts/fetch'
5
5
  import { palette } from '@/ui'
6
- import { mainWorktreeRoot } from '@/worktree'
6
+ import { mainWorktreeRoot } from '@/git/worktree'
7
7
 
8
8
  interface TranscriptOptions {
9
9
  out?: string
@@ -16,7 +16,7 @@ import {
16
16
  type Unreadable,
17
17
  type WorktreeVerdict,
18
18
  } from '@/worktrees/reclaim'
19
- import { mainWorktreeRoot } from '@/worktree'
19
+ import { mainWorktreeRoot } from '@/git/worktree'
20
20
  import { repairBareFlag } from '@/worktrees/bare-flag'
21
21
  import {
22
22
  removeReclaimable,
@@ -1,6 +1,6 @@
1
1
  import { readFile } from 'node:fs/promises'
2
2
  import { resolve } from 'node:path'
3
- import { listIgnored } from '@/git-ignore'
3
+ import { listIgnored } from '@/git/ignore'
4
4
 
5
5
  export type Language = 'ts' | 'sh'
6
6
 
@@ -1,5 +1,5 @@
1
1
  import { $ } from 'bun'
2
- import { gitEnv } from '@/git-env'
2
+ import { gitEnv } from '@/git/env'
3
3
  import {
4
4
  countFiles,
5
5
  isPruned,
@@ -4,7 +4,7 @@ import { BARE_NAME, IGNORE_MARKER } from '@/context/citations'
4
4
  import type { AuditedFolder } from '@/context/folders'
5
5
  import { type BodyLine, bodyLines, maskDisplayed } from '@/markdown/scan'
6
6
  import { documentHeight } from '@/markdown/structure'
7
- import { isStubSeed } from '@/seed-marker'
7
+ import { isStubSeed } from '@/tooling/seed-marker'
8
8
 
9
9
  /**
10
10
  * Checkpoint quoted from the standard stating it. It is not a cap.
@@ -1,7 +1,7 @@
1
1
  import { existsSync } from 'node:fs'
2
2
  import { readFile } from 'node:fs/promises'
3
3
  import { resolve } from 'node:path'
4
- import { listRepositoryFiles } from '@/git-files'
4
+ import { listRepositoryFiles } from '@/git/files'
5
5
  import { RECORD_ROOTS } from '@/record-root'
6
6
  import { SURFACE_ROOTS } from '@/surface-root'
7
7
 
@@ -2,8 +2,8 @@ import { existsSync, readFileSync } from 'node:fs'
2
2
  import { basename, relative, resolve } from 'node:path'
3
3
  import { $ } from 'bun'
4
4
  import { resolveFolders } from '@/context/folders'
5
- import { gitEnv } from '@/git-env'
6
- import { listChangedFiles, resolveBaseRef } from '@/git-files'
5
+ import { gitEnv } from '@/git/env'
6
+ import { listChangedFiles, resolveBaseRef } from '@/git/files'
7
7
  import { surfaceDir } from '@/surface-root'
8
8
 
9
9
  /**