@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,472 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -e
3
- set -o pipefail
4
-
5
- SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)"
6
- PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
7
- export PROJECT_ROOT
8
-
9
- source "$PROJECT_ROOT/scripts/config.sh"
10
- source "$PROJECT_ROOT/scripts/lib/ui.sh"
11
- source "$PROJECT_ROOT/scripts/lib/sandbox-path.sh"
12
- source "$PROJECT_ROOT/scripts/lib/sandbox-git.sh"
13
- source "$PROJECT_ROOT/scripts/lib/sandbox-fixtures.sh"
14
-
15
- # Without this, git falls back to a terminal prompt when no helper supplies a
16
- # credential, and an unauthenticated run blocks on /dev/tty instead of failing.
17
- # The harness must never require a TTY.
18
- export GIT_TERMINAL_PROMPT=0
19
-
20
- show_help() {
21
- echo -e "${GREY}┌${NC}"
22
- echo -e "${GREY}├${NC} ${WHITE}Usage:${NC} canon sandbox [cat:cmd]"
23
- echo -e "${GREY}│${NC}"
24
- echo -e "${GREY}│${NC} ${WHITE}Arguments:${NC}"
25
- echo -e "${GREY}│${NC} cat:cmd ${GREY}# Scenario to provision (e.g. git:commit)${NC}"
26
- echo -e "${GREY}│${NC}"
27
- echo -e "${GREY}│${NC} ${WHITE}Commands:${NC}"
28
- echo -e "${GREY}│${NC} reset ${GREY}# Reset sandbox to baseline${NC}"
29
- echo -e "${GREY}│${NC} clean ${GREY}# Wipe the sandbox${NC}"
30
- echo -e "${GREY}│${NC}"
31
- echo -e "${GREY}│${NC} ${WHITE}Options:${NC}"
32
- echo -e "${GREY}│${NC} -h, --help ${GREY}# Show this help message${NC}"
33
- echo -e "${GREY}│${NC}"
34
- echo -e "${GREY}│${NC} ${WHITE}Examples:${NC}"
35
- echo -e "${GREY}│${NC} canon sandbox"
36
- echo -e "${GREY}│${NC} canon sandbox git:commit"
37
- echo -e "${GREY}│${NC} canon sandbox reset"
38
- echo -e "${GREY}│${NC} canon sandbox clean"
39
- echo -e "${GREY}└${NC}"
40
- exit 0
41
- }
42
-
43
- ANCHOR_FIXTURE_DIR="$PROJECT_ROOT/sandbox/fixtures/anchor/create"
44
-
45
- # Where an anchor scenario's tree comes from and where its remote points are two
46
- # concerns, and only the remote needs a network. Taking the tree from a fixture
47
- # stops an arm provisioning from whatever the previous arm force-pushed, which is
48
- # what made two identical sweeps of the sixteen arms disagree on eleven.
49
- stage_anchor_tree() {
50
- # The nine anchor scenarios configure a real remote from their own stage_setup,
51
- # and sandbox_anchor_url runs inside a command substitution there, where
52
- # log_error would exit only the subshell and leave an empty remote behind. The
53
- # guard has to fire here, in the main shell, before any of them run.
54
- require_sandbox_anchor_config
55
-
56
- log_step "Staging anchor tree from fixture"
57
-
58
- assert_fixtures_suffixed "$ANCHOR_FIXTURE_DIR"
59
- if [ "$(count_fixture_files "$ANCHOR_FIXTURE_DIR")" -eq 0 ]; then
60
- log_error "Anchor fixture provisions nothing: $ANCHOR_FIXTURE_DIR"
61
- fi
62
-
63
- if [ -d "$SANDBOX" ]; then
64
- rm -rf "$SANDBOX"
65
- fi
66
- mkdir -p "$SANDBOX"
67
-
68
- (
69
- cd "$SANDBOX"
70
- create_from_fixtures "$ANCHOR_FIXTURE_DIR"
71
- git init >/dev/null
72
- git add .
73
- git commit -m "feat(sandbox): initial sandbox setup from anchor" --no-verify >/dev/null
74
- )
75
- log_info "Anchor tree staged from fixture, remote stays $ANCHOR_REPO"
76
- }
77
-
78
- select_sandbox_category() {
79
- local categories=()
80
- if ls -d "$SANDBOX_DIR"/*/ >/dev/null 2>&1; then
81
- # Twin of FIXTURES_DIR in src/commands/sandbox.ts. The exec boundary rules out
82
- # a shared constant, so a new exclusion has to land on both sides.
83
- mapfile -t categories < <(find "$SANDBOX_DIR" -mindepth 1 -maxdepth 1 -type d -not -name fixtures -exec basename {} \; | sort)
84
- fi
85
-
86
- if [ ${#categories[@]} -eq 0 ]; then
87
- log_error "No sandbox categories found in $SANDBOX_DIR"
88
- fi
89
-
90
- select_option "Select category:" "${categories[@]}"
91
- _CATEGORY=$SELECTED_OPTION
92
- }
93
-
94
- select_sandbox_command() {
95
- local commands=()
96
- if ls "$SANDBOX_DIR/$_CATEGORY/"*.sh >/dev/null 2>&1; then
97
- mapfile -t commands < <(find "$SANDBOX_DIR/$_CATEGORY" -maxdepth 1 -name '*.sh' -exec basename {} .sh \; | sort)
98
- fi
99
-
100
- if [ ${#commands[@]} -eq 0 ]; then
101
- log_error "No sandbox scripts found in $_CATEGORY"
102
- fi
103
-
104
- select_option "Select command:" "${commands[@]}"
105
- _COMMAND=$SELECTED_OPTION
106
- }
107
-
108
- prompt_for_category_and_command() {
109
- select_sandbox_category
110
- select_sandbox_command
111
- }
112
-
113
- parse_command_argument() {
114
- local input_arg="$1"
115
-
116
- if [[ "$input_arg" != *":"* ]]; then
117
- log_error "Invalid format. Use <category>:<command>, 'reset', 'clean', or --help"
118
- fi
119
- IFS=':' read -r _CATEGORY _COMMAND <<<"$input_arg"
120
- }
121
-
122
- resolve_command_and_category() {
123
- local input_arg="$1"
124
- if [ -z "$input_arg" ]; then
125
- prompt_for_category_and_command
126
- else
127
- parse_command_argument "$input_arg"
128
- fi
129
- }
130
-
131
- validate_environment() {
132
- local current_category="$1"
133
- local current_command="$2"
134
-
135
- if [ ! -d "$SANDBOX_DIR" ]; then
136
- log_error "Sandbox directory not found at: $SANDBOX_DIR"
137
- fi
138
-
139
- if [[ "$PWD" == "$SANDBOX" || "$PWD" == "$SANDBOX"/* ]]; then
140
- log_warn "Detected execution inside the sandbox. Switching to project root..."
141
- cd "$PROJECT_ROOT" || log_error "Failed to switch to project root."
142
- fi
143
- }
144
-
145
- load_sandbox_script() {
146
- local current_category="$1"
147
- local current_command="$2"
148
-
149
- local sandbox_file="$SANDBOX_DIR/$current_category/$current_command.sh"
150
-
151
- if [ ! -f "$sandbox_file" ]; then
152
- log_error "Sandbox script not found: $current_category/$current_command"
153
- fi
154
-
155
- # shellcheck source=/dev/null
156
- source "$sandbox_file"
157
- }
158
-
159
- init_empty_sandbox() {
160
- if [ -d "$SANDBOX" ]; then
161
- rm -rf "$SANDBOX"
162
- fi
163
- mkdir -p "$SANDBOX"
164
-
165
- cat <<EOF >"$SANDBOX/.gitignore"
166
- .canon/tmp/
167
- node_modules
168
- EOF
169
- (
170
- cd "$SANDBOX"
171
- git init >/dev/null
172
- git add .gitignore >/dev/null 2>/dev/null
173
- git commit -m "feat(sandbox): initial empty sandbox setup" --no-verify >/dev/null
174
- )
175
- }
176
-
177
- provision_sandbox() {
178
- log_step "Provisioning $1:$2"
179
-
180
- if [[ "$(type -t use_anchor)" == "function" ]]; then
181
- use_anchor
182
- stage_anchor_tree
183
- else
184
- init_empty_sandbox
185
- fi
186
-
187
- # Scoped to the throwaway sandbox repo so the operator's global git config is
188
- # never written. Covers both the scenario pushes and the ones the agent makes
189
- # itself once it is running inside the sandbox.
190
- (cd "$SANDBOX" && configure_sandbox_git_credentials)
191
- }
192
-
193
- # Runs the real installer rather than copying the source tree. The copies these
194
- # replaced, for gov rules and for seeds, each reimplemented an installer's
195
- # selection rules, so a change to what install produces left the sandbox
196
- # provisioning the old shape and no scenario could observe the difference.
197
- run_sandbox_install() {
198
- local label="$1"
199
- shift
200
-
201
- local install_log
202
- install_log="$(mktemp)"
203
-
204
- # The installer's own frame would bury the provisioning timeline, so it stays
205
- # captured unless it fails, where it is the only thing naming the cause.
206
- if ! CANON_NON_INTERACTIVE=1 bun "$PROJECT_ROOT/src/cli.ts" "$@" \
207
- >/dev/null 2>"$install_log"; then
208
- cat "$install_log" >&2
209
- rm -f "$install_log"
210
- log_error "Could not install $label into the sandbox."
211
- fi
212
-
213
- rm -f "$install_log"
214
- }
215
-
216
- # The stack decides which rules land. `base` carries the language-agnostic core,
217
- # which is what a scenario asserting on rule behavior reads. A scenario needing a
218
- # framework's rules overrides the variable in `use_config`.
219
- inject_gov_rules() {
220
- [ ! -d "$PROJECT_ROOT/governance/rules" ] && return
221
-
222
- run_sandbox_install "gov rules" gov install "${SANDBOX_GOV_STACK:-base}" "$SANDBOX"
223
- }
224
-
225
- inject_seeds() {
226
- [ ! -d "$PROJECT_ROOT/tooling/claude/seeds" ] && return
227
-
228
- run_sandbox_install "seeds" claude init "$SANDBOX"
229
- }
230
-
231
- commit_environment_setup() {
232
- (
233
- cd "$SANDBOX"
234
- git add .
235
- if ! git diff --cached --quiet; then
236
- git commit -m "chore(sandbox): initial environment setup" --no-verify >/dev/null
237
- fi
238
- )
239
- }
240
-
241
- # No standards injection. The corpus installs into no target, so a sandbox
242
- # without one is the shape a scaffolded project has, and a scenario driving a
243
- # skill that reads a standard exercises the `canon standards <name>` path a real
244
- # project takes.
245
- setup_sandbox_assets() {
246
- [ -n "$SANDBOX_INJECT_SEEDS" ] && inject_seeds
247
- [ -n "$SANDBOX_INJECT_GOV" ] && inject_gov_rules
248
- commit_environment_setup
249
- }
250
-
251
- initialize_sandbox_environment() {
252
- local current_category="$1"
253
- local current_command="$2"
254
-
255
- load_sandbox_script "$current_category" "$current_command"
256
- [[ "$(type -t use_config)" == "function" ]] && use_config
257
- validate_environment "$current_category" "$current_command"
258
- provision_sandbox "$current_category" "$current_command"
259
- setup_sandbox_assets
260
- }
261
-
262
- commit_sandbox_changes() {
263
- if [ -z "$SANDBOX_SKIP_AUTO_COMMIT" ]; then
264
- log_step "Staging environment changes"
265
- (
266
- cd "$SANDBOX"
267
- git add . >/dev/null 2>/dev/null
268
- git commit -m 'chore(sandbox): apply scenario specific setup' --no-verify >/dev/null
269
- )
270
- log_info "Git state clean after setup"
271
- else
272
- log_info "Skipping auto-commit"
273
- fi
274
- }
275
-
276
- tag_sandbox_baseline() {
277
- (
278
- cd "$SANDBOX"
279
- git update-ref refs/sandbox/baseline HEAD >/dev/null 2>&1
280
- git write-tree | xargs -I {} git update-ref refs/sandbox/baseline-index {} >/dev/null 2>&1
281
- )
282
- }
283
-
284
- inject_changed_skills() {
285
- local base
286
- base=$(resolve_sandbox_skill_diff_base)
287
- base="${base:-main}"
288
-
289
- local changed untracked
290
- changed=$(git -C "$PROJECT_ROOT" diff "$base" --name-only -- 'claude/skills/**/SKILL.md' 2>/dev/null)
291
- untracked=$(git -C "$PROJECT_ROOT" ls-files --others --exclude-standard -- 'claude/skills/**/SKILL.md' 2>/dev/null)
292
-
293
- local combined
294
- combined=$(printf '%s\n%s\n' "$changed" "$untracked" | awk 'NF && !seen[$0]++')
295
-
296
- [ -z "$combined" ] && return
297
-
298
- while IFS= read -r skill_path; do
299
- # The diff against the base lists a deleted skill alongside a changed one,
300
- # and there is nothing left to inject for a name this branch removed.
301
- [ -f "$PROJECT_ROOT/$skill_path" ] || continue
302
-
303
- local skill_name
304
- skill_name=$(basename "$(dirname "$skill_path")")
305
- local target_dir="$SANDBOX/.claude/skills/$skill_name"
306
- mkdir -p "$target_dir"
307
- cp "$PROJECT_ROOT/$skill_path" "$target_dir/SKILL.md"
308
- if [ -n "$SANDBOX_SKIP_AUTO_COMMIT" ]; then
309
- echo ".claude/skills/$skill_name/SKILL.md" >>"$SANDBOX/.git/info/exclude"
310
- fi
311
- log_info "Injected dev skill: $skill_name"
312
- done <<<"$combined"
313
- }
314
-
315
- execute_sandbox_and_commit() {
316
- pushd "$SANDBOX" >/dev/null
317
- stage_setup
318
- popd >/dev/null
319
-
320
- inject_changed_skills
321
- commit_sandbox_changes
322
- tag_sandbox_baseline
323
- }
324
-
325
- finalize_sandbox_run() {
326
- local GREEN RED YELLOW WHITE GREY NC
327
- set_palette 2
328
- trap - EXIT
329
- close_timeline
330
- echo "" >&2
331
- echo -e "${GREEN}✓ Sandbox Ready${NC}" >&2
332
- }
333
-
334
- cmd_clean() {
335
- local GREEN RED YELLOW WHITE GREY NC
336
- set_palette 2
337
- log_step "Removing sandbox"
338
- rm -rf "$SANDBOX"
339
- log_rem "$SANDBOX"
340
- trap - EXIT
341
- close_timeline
342
- echo "" >&2
343
- echo -e "${GREEN}✓ Sandbox clean${NC}" >&2
344
- }
345
-
346
- reset_sandbox() {
347
- local GREEN RED YELLOW WHITE GREY NC
348
- set_palette 2
349
- if [ ! -d "$SANDBOX/.git" ]; then
350
- log_error "No sandbox found. Run \`canon sandbox\` first."
351
- fi
352
-
353
- log_step "Sandbox state"
354
-
355
- local has_baseline=0
356
- (cd "$SANDBOX" && git rev-parse refs/sandbox/baseline >/dev/null 2>&1) && has_baseline=1
357
-
358
- if [ "$has_baseline" -eq 0 ]; then
359
- log_error "No baseline found. Re-provision with \`canon sandbox <cat>:<cmd>\`."
360
- fi
361
-
362
- local is_dirty=0
363
- (
364
- cd "$SANDBOX"
365
- if [ "$(git rev-parse HEAD)" != "$(git rev-parse refs/sandbox/baseline)" ]; then
366
- exit 1
367
- fi
368
- local current_index baseline_index
369
- current_index=$(git write-tree)
370
- baseline_index=$(git rev-parse refs/sandbox/baseline-index 2>/dev/null || echo "")
371
- if [ -n "$baseline_index" ] && [ "$current_index" != "$baseline_index" ]; then
372
- exit 1
373
- fi
374
- if ! git diff --quiet 2>/dev/null; then
375
- exit 1
376
- fi
377
- if [ -n "$(git ls-files --others --exclude-standard 2>/dev/null)" ]; then
378
- exit 1
379
- fi
380
- ) || is_dirty=1
381
-
382
- if [ "$is_dirty" -eq 0 ]; then
383
- log_info "At baseline"
384
- trap - EXIT
385
- close_timeline
386
- echo "" >&2
387
- echo -e "${GREEN}✓ Sandbox at baseline${NC}" >&2
388
- exit 0
389
- fi
390
-
391
- log_warn "Uncommitted changes detected"
392
-
393
- select_option "Reset sandbox to initial state?" "Yes" "No"
394
- if [ "$SELECTED_OPTION" == "No" ]; then
395
- log_warn "Reset cancelled"
396
- exit 0
397
- fi
398
-
399
- log_step "Resetting sandbox"
400
- (
401
- cd "$SANDBOX"
402
- git reset --hard refs/sandbox/baseline --quiet
403
- git clean -fd --quiet
404
- local baseline_index
405
- baseline_index=$(git rev-parse refs/sandbox/baseline-index 2>/dev/null || echo "")
406
- if [ -n "$baseline_index" ] && [ "$baseline_index" != "$(git write-tree)" ]; then
407
- git read-tree "$baseline_index"
408
- git checkout-index -a -f
409
- fi
410
- )
411
- log_info "Sandbox reset to baseline"
412
-
413
- trap - EXIT
414
- close_timeline
415
- echo "" >&2
416
- echo -e "${GREEN}✓ Sandbox reset complete${NC}" >&2
417
- }
418
-
419
- main() {
420
- local no_header=0
421
- if [[ "$1" == "--no-header" ]]; then
422
- no_header=1
423
- shift
424
- fi
425
-
426
- if [[ "$1" == "-h" || "$1" == "--help" ]]; then
427
- show_help
428
- fi
429
-
430
- if [[ "$no_header" -eq 0 ]]; then
431
- open_timeline "canon sandbox"
432
- fi
433
- trap close_timeline EXIT
434
-
435
- if [[ "$PWD" != "$PROJECT_ROOT"* ]]; then
436
- log_error "Context error: you must run this command from inside the toolkit repository."
437
- fi
438
-
439
- SANDBOX="$(resolve_sandbox_dir)"
440
- SANDBOX_DIR="$PROJECT_ROOT/sandbox"
441
-
442
- local unsafe
443
- if ! unsafe="$(assert_sandbox_dir_safe "$SANDBOX" "$PROJECT_ROOT")"; then
444
- log_error "$unsafe"
445
- fi
446
-
447
- if [[ "$1" == "reset" ]]; then
448
- reset_sandbox
449
- exit 0
450
- fi
451
-
452
- if [[ "$1" == "clean" ]]; then
453
- cmd_clean
454
- exit 0
455
- fi
456
-
457
- resolve_command_and_category "$1"
458
- local category="$_CATEGORY"
459
- local command="$_COMMAND"
460
-
461
- if [ -n "${2:-}" ]; then
462
- export SANDBOX_SCENARIO="$2"
463
- export CANON_NON_INTERACTIVE="1"
464
- fi
465
-
466
- initialize_sandbox_environment "$category" "$command"
467
- execute_sandbox_and_commit
468
-
469
- finalize_sandbox_run
470
- }
471
-
472
- main "$@"