@erclx/canon 4.0.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 (643) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/claude/.claude-plugin/plugin.json +19 -0
  4. package/claude/skills/bash-cli-script/REQUIREMENT.md +42 -0
  5. package/claude/skills/bash-cli-script/SKILL.md +48 -0
  6. package/claude/skills/bash-cli-script/references/template.md +43 -0
  7. package/claude/skills/bash-script/REQUIREMENT.md +36 -0
  8. package/claude/skills/bash-script/SKILL.md +100 -0
  9. package/claude/skills/bash-script/references/patterns.md +349 -0
  10. package/claude/skills/canon-cli/REQUIREMENT.md +41 -0
  11. package/claude/skills/canon-cli/SKILL.md +103 -0
  12. package/claude/skills/canon-feedback-file/REQUIREMENT.md +40 -0
  13. package/claude/skills/canon-feedback-file/SKILL.md +80 -0
  14. package/claude/skills/canon-feedback-triage/REQUIREMENT.md +40 -0
  15. package/claude/skills/canon-feedback-triage/SKILL.md +63 -0
  16. package/claude/skills/canon-operator/REQUIREMENT.md +61 -0
  17. package/claude/skills/canon-operator/SKILL.md +108 -0
  18. package/claude/skills/canon-rollout/REQUIREMENT.md +59 -0
  19. package/claude/skills/canon-rollout/SKILL.md +147 -0
  20. package/claude/skills/canon-screencast/REQUIREMENT.md +39 -0
  21. package/claude/skills/canon-screencast/SKILL.md +167 -0
  22. package/claude/skills/canon-slides-draft/REQUIREMENT.md +39 -0
  23. package/claude/skills/canon-slides-draft/SKILL.md +62 -0
  24. package/claude/skills/ci-workflow/REQUIREMENT.md +40 -0
  25. package/claude/skills/ci-workflow/SKILL.md +65 -0
  26. package/claude/skills/ci-workflow/references/workflows.md +98 -0
  27. package/claude/skills/claude-address-review/REQUIREMENT.md +57 -0
  28. package/claude/skills/claude-address-review/SKILL.md +212 -0
  29. package/claude/skills/claude-address-review/references/rebase-conflicts.md +39 -0
  30. package/claude/skills/claude-autoship/REQUIREMENT.md +50 -0
  31. package/claude/skills/claude-autoship/SKILL.md +207 -0
  32. package/claude/skills/claude-design-extract/REQUIREMENT.md +42 -0
  33. package/claude/skills/claude-design-extract/SKILL.md +102 -0
  34. package/claude/skills/claude-diagram/REQUIREMENT.md +45 -0
  35. package/claude/skills/claude-diagram/SKILL.md +177 -0
  36. package/claude/skills/claude-docs/REQUIREMENT.md +60 -0
  37. package/claude/skills/claude-docs/SKILL.md +287 -0
  38. package/claude/skills/claude-docs/references/anchor-sweep.md +58 -0
  39. package/claude/skills/claude-docs/references/wireframe-sweep.md +45 -0
  40. package/claude/skills/claude-feature/REQUIREMENT.md +36 -0
  41. package/claude/skills/claude-feature/SKILL.md +115 -0
  42. package/claude/skills/claude-groundwork/REQUIREMENT.md +48 -0
  43. package/claude/skills/claude-groundwork/SKILL.md +142 -0
  44. package/claude/skills/claude-intake/REQUIREMENT.md +49 -0
  45. package/claude/skills/claude-intake/SKILL.md +114 -0
  46. package/claude/skills/claude-intake-answer/REQUIREMENT.md +48 -0
  47. package/claude/skills/claude-intake-answer/SKILL.md +90 -0
  48. package/claude/skills/claude-markdown-propose/REQUIREMENT.md +48 -0
  49. package/claude/skills/claude-markdown-propose/SKILL.md +118 -0
  50. package/claude/skills/claude-markdown-propose/references/format.md +107 -0
  51. package/claude/skills/claude-memory-capture/REQUIREMENT.md +50 -0
  52. package/claude/skills/claude-memory-capture/SKILL.md +101 -0
  53. package/claude/skills/claude-memory-review/REQUIREMENT.md +50 -0
  54. package/claude/skills/claude-memory-review/SKILL.md +210 -0
  55. package/claude/skills/claude-memory-review/references/receipt-format.md +48 -0
  56. package/claude/skills/claude-orchestrate/REQUIREMENT.md +121 -0
  57. package/claude/skills/claude-orchestrate/SKILL.md +241 -0
  58. package/claude/skills/claude-orchestrate/references/orchestrator-dispatch.md +132 -0
  59. package/claude/skills/claude-orchestrate/references/orchestrator-handoff.md +32 -0
  60. package/claude/skills/claude-orchestrate/references/orchestrator-parked.md +72 -0
  61. package/claude/skills/claude-orchestrate/references/orchestrator-poll.md +87 -0
  62. package/claude/skills/claude-orchestrate/references/orchestrator-resume.md +30 -0
  63. package/claude/skills/claude-orchestrate/references/orchestrator-sweep.md +21 -0
  64. package/claude/skills/claude-orchestrate/scripts/poll.sh +373 -0
  65. package/claude/skills/claude-orchestrate/scripts/watch.sh +181 -0
  66. package/claude/skills/claude-pr-review/REQUIREMENT.md +47 -0
  67. package/claude/skills/claude-pr-review/SKILL.md +295 -0
  68. package/claude/skills/claude-review/REQUIREMENT.md +39 -0
  69. package/claude/skills/claude-review/SKILL.md +142 -0
  70. package/claude/skills/claude-seed-sync/REQUIREMENT.md +45 -0
  71. package/claude/skills/claude-seed-sync/SKILL.md +156 -0
  72. package/claude/skills/claude-standards-audit/REQUIREMENT.md +33 -0
  73. package/claude/skills/claude-standards-audit/SKILL.md +99 -0
  74. package/claude/skills/claude-tasks/REQUIREMENT.md +43 -0
  75. package/claude/skills/claude-tasks/SKILL.md +161 -0
  76. package/claude/skills/claude-teach/REQUIREMENT.md +56 -0
  77. package/claude/skills/claude-teach/SKILL.md +196 -0
  78. package/claude/skills/claude-teach/references/lesson-craft.md +59 -0
  79. package/claude/skills/claude-teach/references/pedagogy.md +67 -0
  80. package/claude/skills/claude-teach/references/promotion.md +54 -0
  81. package/claude/skills/claude-ui-test/REQUIREMENT.md +40 -0
  82. package/claude/skills/claude-ui-test/SKILL.md +77 -0
  83. package/claude/skills/claude-ux-audit/REQUIREMENT.md +40 -0
  84. package/claude/skills/claude-ux-audit/SKILL.md +79 -0
  85. package/claude/skills/claude-ux-measure/REQUIREMENT.md +48 -0
  86. package/claude/skills/claude-ux-measure/SKILL.md +122 -0
  87. package/claude/skills/claude-worker/REQUIREMENT.md +54 -0
  88. package/claude/skills/claude-worker/SKILL.md +96 -0
  89. package/claude/skills/claude-worktree/REQUIREMENT.md +58 -0
  90. package/claude/skills/claude-worktree/SKILL.md +136 -0
  91. package/claude/skills/create-rule/REQUIREMENT.md +45 -0
  92. package/claude/skills/create-rule/SKILL.md +68 -0
  93. package/claude/skills/create-skill/REQUIREMENT.md +38 -0
  94. package/claude/skills/create-skill/SKILL.md +32 -0
  95. package/claude/skills/create-snippet/REQUIREMENT.md +39 -0
  96. package/claude/skills/create-snippet/SKILL.md +30 -0
  97. package/claude/skills/create-standard/REQUIREMENT.md +35 -0
  98. package/claude/skills/create-standard/SKILL.md +29 -0
  99. package/claude/skills/decision-escalate/REQUIREMENT.md +45 -0
  100. package/claude/skills/decision-escalate/SKILL.md +79 -0
  101. package/claude/skills/docs-sync/REQUIREMENT.md +41 -0
  102. package/claude/skills/docs-sync/SKILL.md +95 -0
  103. package/claude/skills/git-branch/REQUIREMENT.md +38 -0
  104. package/claude/skills/git-branch/SKILL.md +60 -0
  105. package/claude/skills/git-commit/REQUIREMENT.md +36 -0
  106. package/claude/skills/git-commit/SKILL.md +49 -0
  107. package/claude/skills/git-followup/REQUIREMENT.md +43 -0
  108. package/claude/skills/git-followup/SKILL.md +48 -0
  109. package/claude/skills/git-issue/REQUIREMENT.md +38 -0
  110. package/claude/skills/git-issue/SKILL.md +65 -0
  111. package/claude/skills/git-pr/REQUIREMENT.md +50 -0
  112. package/claude/skills/git-pr/SKILL.md +164 -0
  113. package/claude/skills/git-pr/references/labels.md +95 -0
  114. package/claude/skills/git-ship/REQUIREMENT.md +42 -0
  115. package/claude/skills/git-ship/SKILL.md +55 -0
  116. package/claude/skills/git-split/REQUIREMENT.md +39 -0
  117. package/claude/skills/git-split/SKILL.md +162 -0
  118. package/claude/skills/git-stage/REQUIREMENT.md +39 -0
  119. package/claude/skills/git-stage/SKILL.md +73 -0
  120. package/claude/skills/git-worktree/REQUIREMENT.md +38 -0
  121. package/claude/skills/git-worktree/SKILL.md +130 -0
  122. package/claude/skills/migration-claude-md/REQUIREMENT.md +40 -0
  123. package/claude/skills/migration-claude-md/SKILL.md +76 -0
  124. package/claude/skills/migration-context/REQUIREMENT.md +36 -0
  125. package/claude/skills/migration-context/SKILL.md +95 -0
  126. package/claude/skills/migration-standards-drop/REQUIREMENT.md +55 -0
  127. package/claude/skills/migration-standards-drop/SKILL.md +113 -0
  128. package/claude/skills/migration-superseded/REQUIREMENT.md +44 -0
  129. package/claude/skills/migration-superseded/SKILL.md +115 -0
  130. package/claude/skills/project-commands/REQUIREMENT.md +42 -0
  131. package/claude/skills/project-commands/SKILL.md +85 -0
  132. package/claude/skills/restate-plainly/REQUIREMENT.md +41 -0
  133. package/claude/skills/restate-plainly/SKILL.md +39 -0
  134. package/claude/skills/session-map/REQUIREMENT.md +57 -0
  135. package/claude/skills/session-map/SKILL.md +70 -0
  136. package/claude/skills/session-resume/REQUIREMENT.md +49 -0
  137. package/claude/skills/session-resume/SKILL.md +51 -0
  138. package/claude/skills/setup-gov/REQUIREMENT.md +37 -0
  139. package/claude/skills/setup-gov/SKILL.md +77 -0
  140. package/claude/skills/setup-indexes/REQUIREMENT.md +45 -0
  141. package/claude/skills/setup-indexes/SKILL.md +153 -0
  142. package/claude/skills/setup-init/REQUIREMENT.md +45 -0
  143. package/claude/skills/setup-init/SKILL.md +127 -0
  144. package/claude/skills/setup-plugins/REQUIREMENT.md +42 -0
  145. package/claude/skills/setup-plugins/SKILL.md +81 -0
  146. package/claude/skills/setup-plugins/references/plugin-catalog.md +53 -0
  147. package/claude/skills/setup-verify/REQUIREMENT.md +39 -0
  148. package/claude/skills/setup-verify/SKILL.md +51 -0
  149. package/claude/skills/systematic-debugging/REQUIREMENT.md +41 -0
  150. package/claude/skills/systematic-debugging/SKILL.md +70 -0
  151. package/claude/skills/write-human/REQUIREMENT.md +46 -0
  152. package/claude/skills/write-human/SKILL.md +68 -0
  153. package/claude/skills/write-human/references/density.md +38 -0
  154. package/claude/skills/write-human/references/machine-tells.md +107 -0
  155. package/claude/skills/write-human/references/source-material.md +37 -0
  156. package/claude/skills/youtube-transcripts/REQUIREMENT.md +38 -0
  157. package/claude/skills/youtube-transcripts/SKILL.md +34 -0
  158. package/docs/agents/audits.md +98 -0
  159. package/docs/agents/capture.md +37 -0
  160. package/docs/agents/census.md +23 -0
  161. package/docs/agents/commands.md +146 -0
  162. package/docs/agents/comments.md +34 -0
  163. package/docs/agents/context-audit-checks.md +120 -0
  164. package/docs/agents/context-audit.md +83 -0
  165. package/docs/agents/counts.md +76 -0
  166. package/docs/agents/demo.md +86 -0
  167. package/docs/agents/docs.md +17 -0
  168. package/docs/agents/gate.md +84 -0
  169. package/docs/agents/index.md +47 -0
  170. package/docs/agents/indexes.md +35 -0
  171. package/docs/agents/install-and-sync.md +385 -0
  172. package/docs/agents/intake.md +81 -0
  173. package/docs/agents/key-changes.md +103 -0
  174. package/docs/agents/label-coverage.md +73 -0
  175. package/docs/agents/markdown-audit.md +197 -0
  176. package/docs/agents/output-shape.md +70 -0
  177. package/docs/agents/overview.md +26 -0
  178. package/docs/agents/records.md +170 -0
  179. package/docs/agents/restated.md +81 -0
  180. package/docs/agents/review-classification.md +77 -0
  181. package/docs/agents/routing.md +61 -0
  182. package/docs/agents/rule-citations.md +98 -0
  183. package/docs/agents/sandbox.md +71 -0
  184. package/docs/agents/scripting.md +149 -0
  185. package/docs/agents/sessions.md +120 -0
  186. package/docs/agents/skills-audit.md +94 -0
  187. package/docs/agents/skills-reach.md +64 -0
  188. package/docs/agents/standards-audit.md +38 -0
  189. package/docs/agents/state-scoped-risk.md +105 -0
  190. package/docs/agents/superseded.md +85 -0
  191. package/docs/agents/targets.md +83 -0
  192. package/docs/agents/tasks.md +200 -0
  193. package/docs/agents/teach.md +158 -0
  194. package/docs/agents/test-order.md +56 -0
  195. package/docs/agents/worktrees.md +62 -0
  196. package/docs/ai-workflow.md +317 -0
  197. package/docs/index.md +23 -0
  198. package/docs/operating-model.md +223 -0
  199. package/docs/target-projects.md +258 -0
  200. package/docs/visual-design-workflow.md +151 -0
  201. package/docs/zshrc-aliases.md +65 -0
  202. package/governance/rules/ci/700-ci-workflow.md +44 -0
  203. package/governance/rules/claude/500-prose.md +15 -0
  204. package/governance/rules/claude/501-markdown.md +14 -0
  205. package/governance/rules/claude/510-context.md +28 -0
  206. package/governance/rules/claude/511-indexes.md +14 -0
  207. package/governance/rules/claude/520-wireframes.md +12 -0
  208. package/governance/rules/claude/530-requirements.md +11 -0
  209. package/governance/rules/claude/540-architecture.md +11 -0
  210. package/governance/rules/claude/550-design.md +11 -0
  211. package/governance/rules/claude/555-tasks.md +12 -0
  212. package/governance/rules/claude/556-groundwork.md +11 -0
  213. package/governance/rules/claude/557-intake.md +11 -0
  214. package/governance/rules/claude/558-plan.md +22 -0
  215. package/governance/rules/claude/559-memory.md +11 -0
  216. package/governance/rules/claude/560-diagrams.md +18 -0
  217. package/governance/rules/claude/561-teach.md +13 -0
  218. package/governance/rules/claude/562-session.md +15 -0
  219. package/governance/rules/claude/570-skill.md +24 -0
  220. package/governance/rules/claude/575-hooks.md +17 -0
  221. package/governance/rules/claude/576-settings.md +14 -0
  222. package/governance/rules/claude/580-readme.md +11 -0
  223. package/governance/rules/claude/590-rule-authoring.md +12 -0
  224. package/governance/rules/claude/591-standard-authoring.md +12 -0
  225. package/governance/rules/claude/592-claude-md.md +19 -0
  226. package/governance/rules/core/000-constitution.md +30 -0
  227. package/governance/rules/core/005-behavior.md +27 -0
  228. package/governance/rules/core/010-testing.md +35 -0
  229. package/governance/rules/core/015-output.md +20 -0
  230. package/governance/rules/core/020-concurrency.md +22 -0
  231. package/governance/rules/core/025-indexes.md +9 -0
  232. package/governance/rules/core/030-error-handling.md +31 -0
  233. package/governance/rules/core/035-tasks.md +13 -0
  234. package/governance/rules/core/040-performance.md +20 -0
  235. package/governance/rules/core/045-memory.md +12 -0
  236. package/governance/rules/core/050-logging.md +20 -0
  237. package/governance/rules/core/055-scratch.md +9 -0
  238. package/governance/rules/core/060-naming.md +19 -0
  239. package/governance/rules/core/065-spelling.md +19 -0
  240. package/governance/rules/core/070-planning.md +18 -0
  241. package/governance/rules/core/075-dependencies.md +25 -0
  242. package/governance/rules/core/080-config-comments.md +22 -0
  243. package/governance/rules/core/085-worktrees.md +17 -0
  244. package/governance/rules/core/087-git.md +11 -0
  245. package/governance/rules/core/090-code-comments.md +39 -0
  246. package/governance/rules/framework/200-react.md +51 -0
  247. package/governance/rules/framework/210-astro.md +41 -0
  248. package/governance/rules/framework/220-fastapi.md +43 -0
  249. package/governance/rules/framework/230-nextjs.md +48 -0
  250. package/governance/rules/framework/250-tailwind.md +32 -0
  251. package/governance/rules/framework/260-shadcn.md +34 -0
  252. package/governance/rules/lang/100-typescript.md +40 -0
  253. package/governance/rules/lang/110-python.md +42 -0
  254. package/governance/rules/lang/120-bash.md +19 -0
  255. package/governance/rules/lib/300-testing-ts.md +39 -0
  256. package/governance/rules/lib/305-e2e-reliability.md +34 -0
  257. package/governance/rules/lib/306-test-scope.md +25 -0
  258. package/governance/rules/lib/310-zod.md +25 -0
  259. package/governance/rules/lib/320-tanstack-query.md +32 -0
  260. package/governance/rules/lib/330-testing-py.md +44 -0
  261. package/governance/rules/lib/340-pydantic.md +38 -0
  262. package/governance/rules/lib/350-security-web.md +32 -0
  263. package/governance/rules/lib/360-security-server.md +39 -0
  264. package/governance/rules/lib/370-database.md +35 -0
  265. package/governance/rules/snippets/505-at-references.md +9 -0
  266. package/governance/rules/ui/400-ui.md +36 -0
  267. package/governance/rules/ui/410-a11y.md +48 -0
  268. package/governance/rules/ui/420-forms.md +36 -0
  269. package/governance/rules/ui/430-ux-completeness.md +65 -0
  270. package/governance/rules/ui/440-surface-capture.md +34 -0
  271. package/governance/rules/ui/450-link-behavior.md +19 -0
  272. package/governance/stacks/astro.toml +2 -0
  273. package/governance/stacks/base.toml +8 -0
  274. package/governance/stacks/node-server.toml +2 -0
  275. package/governance/stacks/node.toml +2 -0
  276. package/governance/stacks/python-fastapi.toml +2 -0
  277. package/governance/stacks/python.toml +2 -0
  278. package/governance/stacks/react.toml +2 -0
  279. package/package.json +69 -0
  280. package/scripts/config.sh +11 -0
  281. package/scripts/core/bootstrap.sh +81 -0
  282. package/scripts/core/check-color-source.sh +41 -0
  283. package/scripts/core/check-ignore-parity.sh +162 -0
  284. package/scripts/core/check-plugin-boundary.sh +45 -0
  285. package/scripts/core/check-seed-independence.sh +59 -0
  286. package/scripts/core/check-skill-paths.sh +24 -0
  287. package/scripts/core/clean.sh +36 -0
  288. package/scripts/core/install-check.sh +101 -0
  289. package/scripts/core/list-seed-roots.sh +18 -0
  290. package/scripts/core/regen-claude-copies.sh +10 -0
  291. package/scripts/core/regen-hero.sh +217 -0
  292. package/scripts/core/regen-indexes.sh +10 -0
  293. package/scripts/core/regen-tooling-paths.sh +61 -0
  294. package/scripts/core/repair-bare-flag.sh +19 -0
  295. package/scripts/core/snapshot.sh +134 -0
  296. package/scripts/core/update.sh +35 -0
  297. package/scripts/docs/list.sh +165 -0
  298. package/scripts/lib/frontmatter.sh +30 -0
  299. package/scripts/lib/gov.sh +14 -0
  300. package/scripts/lib/sandbox-fixtures.sh +191 -0
  301. package/scripts/lib/sandbox-git.sh +125 -0
  302. package/scripts/lib/sandbox-path.sh +206 -0
  303. package/scripts/lib/tooling.sh +35 -0
  304. package/scripts/lib/ui.sh +266 -0
  305. package/scripts/lib/worktree.sh +20 -0
  306. package/scripts/manage-sandbox.sh +466 -0
  307. package/scripts/snippets/create.sh +156 -0
  308. package/scripts/standards/list.sh +115 -0
  309. package/scripts/tooling/create.sh +109 -0
  310. package/scripts/tooling/verify.sh +179 -0
  311. package/snippets/align.md +12 -0
  312. package/snippets/claude/decision-memo.md +39 -0
  313. package/snippets/claude/feature-recap.md +19 -0
  314. package/snippets/claude/figma-steps.md +24 -0
  315. package/snippets/compact-summary.md +5 -0
  316. package/snippets/decision-help.md +6 -0
  317. package/snippets/meta-prompt.md +14 -0
  318. package/snippets/research-prompt.md +7 -0
  319. package/snippets/session-notes.md +11 -0
  320. package/snippets/snippets.toml +5 -0
  321. package/snippets/step-by-step.md +10 -0
  322. package/snippets/web-research.md +21 -0
  323. package/src/audits/baseline.ts +201 -0
  324. package/src/audits/catalog.ts +876 -0
  325. package/src/audits/run.ts +204 -0
  326. package/src/autoship/classify.ts +75 -0
  327. package/src/autoship/paths.ts +51 -0
  328. package/src/binary.ts +16 -0
  329. package/src/browser/engine.ts +40 -0
  330. package/src/census/count.ts +113 -0
  331. package/src/claude/cases/all.ts +24 -0
  332. package/src/claude/cases/authoring.ts +53 -0
  333. package/src/claude/cases/claude-workflow.ts +158 -0
  334. package/src/claude/cases/git.ts +44 -0
  335. package/src/claude/cases/misc.ts +27 -0
  336. package/src/claude/cases/setup.ts +94 -0
  337. package/src/claude/gitignore.ts +51 -0
  338. package/src/claude/routing.ts +283 -0
  339. package/src/claude/seeds-list.ts +47 -0
  340. package/src/claude/seeds.ts +150 -0
  341. package/src/claude/settings.ts +151 -0
  342. package/src/claude/skills-audit.ts +228 -0
  343. package/src/claude/skills-drift.ts +156 -0
  344. package/src/claude/skills-list.ts +99 -0
  345. package/src/claude/skills-rank.ts +320 -0
  346. package/src/claude/skills-reach.ts +227 -0
  347. package/src/cli-run.ts +43 -0
  348. package/src/cli.ts +200 -0
  349. package/src/commands/audits.ts +350 -0
  350. package/src/commands/autoship.ts +129 -0
  351. package/src/commands/capture.ts +133 -0
  352. package/src/commands/census.ts +105 -0
  353. package/src/commands/claude.ts +1286 -0
  354. package/src/commands/comments.ts +240 -0
  355. package/src/commands/context.ts +857 -0
  356. package/src/commands/demo.ts +389 -0
  357. package/src/commands/deps.ts +173 -0
  358. package/src/commands/design.ts +36 -0
  359. package/src/commands/docs.ts +60 -0
  360. package/src/commands/feedback-format.ts +23 -0
  361. package/src/commands/feedback.ts +112 -0
  362. package/src/commands/gate.ts +189 -0
  363. package/src/commands/gov.ts +1265 -0
  364. package/src/commands/indexes.ts +184 -0
  365. package/src/commands/init.ts +113 -0
  366. package/src/commands/intake.ts +406 -0
  367. package/src/commands/inventory.ts +256 -0
  368. package/src/commands/labels.ts +361 -0
  369. package/src/commands/markdown.ts +544 -0
  370. package/src/commands/migrate.ts +175 -0
  371. package/src/commands/pass-through.ts +39 -0
  372. package/src/commands/pr.ts +411 -0
  373. package/src/commands/records.ts +728 -0
  374. package/src/commands/sandbox.ts +468 -0
  375. package/src/commands/secrets.ts +132 -0
  376. package/src/commands/serve.ts +159 -0
  377. package/src/commands/sessions.ts +408 -0
  378. package/src/commands/slides.ts +126 -0
  379. package/src/commands/snippets.ts +84 -0
  380. package/src/commands/standards.ts +247 -0
  381. package/src/commands/sync.ts +428 -0
  382. package/src/commands/targets.ts +319 -0
  383. package/src/commands/tasks.ts +743 -0
  384. package/src/commands/teach.ts +786 -0
  385. package/src/commands/tooling.ts +573 -0
  386. package/src/commands/transcripts.ts +44 -0
  387. package/src/commands/upgrade.ts +231 -0
  388. package/src/commands/wiki.ts +100 -0
  389. package/src/commands/worktrees.ts +191 -0
  390. package/src/comments/scan.ts +338 -0
  391. package/src/comments/trend.ts +207 -0
  392. package/src/comments/vocabulary.ts +85 -0
  393. package/src/context/architecture.ts +364 -0
  394. package/src/context/audit.ts +790 -0
  395. package/src/context/citations.ts +196 -0
  396. package/src/context/folders.ts +186 -0
  397. package/src/context/gate.ts +57 -0
  398. package/src/context/index-drift.ts +64 -0
  399. package/src/context/narration.ts +99 -0
  400. package/src/copy.ts +30 -0
  401. package/src/counts/catalogs.ts +96 -0
  402. package/src/counts/numbers.ts +79 -0
  403. package/src/counts/scan.ts +314 -0
  404. package/src/demo/beats.ts +135 -0
  405. package/src/demo/compile.ts +326 -0
  406. package/src/demo/container.ts +63 -0
  407. package/src/demo/cursors.ts +55 -0
  408. package/src/demo/drive.ts +357 -0
  409. package/src/demo/pointer.ts +178 -0
  410. package/src/demo/theme.ts +112 -0
  411. package/src/deps/audit.ts +153 -0
  412. package/src/design/parse.ts +116 -0
  413. package/src/design/render.ts +249 -0
  414. package/src/docs/read.ts +77 -0
  415. package/src/exec.ts +16 -0
  416. package/src/exempt-marker.ts +43 -0
  417. package/src/frontmatter.ts +13 -0
  418. package/src/gate/measures.ts +682 -0
  419. package/src/gate/sequencer.ts +386 -0
  420. package/src/gate/stages.ts +412 -0
  421. package/src/git-env.ts +36 -0
  422. package/src/git-files.ts +105 -0
  423. package/src/git-ignore.ts +46 -0
  424. package/src/github-format.ts +13 -0
  425. package/src/github.ts +24 -0
  426. package/src/gov/adapter.ts +103 -0
  427. package/src/gov/citations.ts +514 -0
  428. package/src/gov/consumed.ts +129 -0
  429. package/src/gov/install.ts +132 -0
  430. package/src/gov/list.ts +106 -0
  431. package/src/gov/payload.ts +39 -0
  432. package/src/gov/restated.ts +814 -0
  433. package/src/gov/stacks.ts +205 -0
  434. package/src/gov/superseded.ts +415 -0
  435. package/src/gov/test-order.ts +407 -0
  436. package/src/indexes/frontmatter.ts +46 -0
  437. package/src/indexes/regen.ts +84 -0
  438. package/src/indexes/render.ts +201 -0
  439. package/src/indexes/walk.ts +83 -0
  440. package/src/init/flags.ts +60 -0
  441. package/src/init/plan.ts +114 -0
  442. package/src/init/run.ts +46 -0
  443. package/src/init/steps.ts +77 -0
  444. package/src/intake/folder.ts +320 -0
  445. package/src/intake/items.ts +174 -0
  446. package/src/inventory/config.ts +117 -0
  447. package/src/inventory/group.ts +76 -0
  448. package/src/inventory/subjects.ts +114 -0
  449. package/src/inventory/walk.ts +129 -0
  450. package/src/labels/audit.ts +82 -0
  451. package/src/labels/coverage.ts +79 -0
  452. package/src/labels/map.ts +101 -0
  453. package/src/labels/phase.ts +95 -0
  454. package/src/markdown/bans.ts +94 -0
  455. package/src/markdown/files.ts +102 -0
  456. package/src/markdown/gate.ts +28 -0
  457. package/src/markdown/scan.ts +295 -0
  458. package/src/markdown/structure.ts +730 -0
  459. package/src/migrate/apply.ts +115 -0
  460. package/src/migrate/plan.ts +103 -0
  461. package/src/migrate/rename.ts +183 -0
  462. package/src/pr/bijection.ts +145 -0
  463. package/src/pr/paths.ts +335 -0
  464. package/src/process/harness.ts +167 -0
  465. package/src/project-root.ts +19 -0
  466. package/src/records/backup.ts +455 -0
  467. package/src/records/migrate.ts +78 -0
  468. package/src/records/size.ts +260 -0
  469. package/src/records/validate.ts +1162 -0
  470. package/src/sandbox/census.ts +228 -0
  471. package/src/sandbox/coverage.ts +115 -0
  472. package/src/sandbox/expect.ts +629 -0
  473. package/src/sandbox/tree.ts +47 -0
  474. package/src/secrets/marker.ts +30 -0
  475. package/src/secrets/patterns.ts +142 -0
  476. package/src/secrets/scan.ts +123 -0
  477. package/src/secrets/shipped.ts +111 -0
  478. package/src/seed-marker.ts +74 -0
  479. package/src/serve/static.ts +322 -0
  480. package/src/sessions/claim.ts +85 -0
  481. package/src/sessions/live.ts +79 -0
  482. package/src/sessions/registry.ts +137 -0
  483. package/src/sessions/resolve.ts +333 -0
  484. package/src/slides/layouts.ts +391 -0
  485. package/src/slides/open.ts +18 -0
  486. package/src/slides/parse.ts +84 -0
  487. package/src/slides/render.ts +88 -0
  488. package/src/slides/styles.ts +44 -0
  489. package/src/snippets/categories.ts +66 -0
  490. package/src/snippets/list.ts +32 -0
  491. package/src/snippets/presets.ts +50 -0
  492. package/src/standards/audit.ts +132 -0
  493. package/src/standards/read.ts +100 -0
  494. package/src/sync/check.ts +692 -0
  495. package/src/sync/engine.ts +576 -0
  496. package/src/sync/git.ts +225 -0
  497. package/src/sync/history.ts +123 -0
  498. package/src/sync/layout.ts +140 -0
  499. package/src/sync/reverse.ts +268 -0
  500. package/src/sync/seeds-report.ts +126 -0
  501. package/src/sync/stamp.ts +358 -0
  502. package/src/sync/target.ts +70 -0
  503. package/src/sync/workflow.ts +200 -0
  504. package/src/target.ts +43 -0
  505. package/src/targets/pulls.ts +250 -0
  506. package/src/targets/registry.ts +203 -0
  507. package/src/targets/resolve.ts +145 -0
  508. package/src/targets/sweep.ts +246 -0
  509. package/src/tasks/archive.ts +506 -0
  510. package/src/tasks/record.ts +311 -0
  511. package/src/tasks/trunk.ts +89 -0
  512. package/src/tasks/validate.ts +1001 -0
  513. package/src/teach/lesson.ts +180 -0
  514. package/src/teach/workspace.ts +842 -0
  515. package/src/tooling/gitignore.ts +122 -0
  516. package/src/tooling/inject.ts +193 -0
  517. package/src/tooling/list.ts +39 -0
  518. package/src/tooling/manifest.ts +176 -0
  519. package/src/tooling/package.ts +166 -0
  520. package/src/tooling/read.ts +65 -0
  521. package/src/tooling/scan.ts +147 -0
  522. package/src/tooling/stamp.ts +44 -0
  523. package/src/transcripts/fetch.ts +156 -0
  524. package/src/transcripts/metadata.ts +54 -0
  525. package/src/transcripts/vtt.ts +114 -0
  526. package/src/ui.ts +266 -0
  527. package/src/version/compare.ts +53 -0
  528. package/src/version/installed.ts +40 -0
  529. package/src/version/manager.ts +67 -0
  530. package/src/version/skew.ts +192 -0
  531. package/src/wiki/init.ts +85 -0
  532. package/src/worktree.ts +143 -0
  533. package/src/worktrees/reclaim.ts +306 -0
  534. package/standards/architecture.md +72 -0
  535. package/standards/branch.md +59 -0
  536. package/standards/commit.md +72 -0
  537. package/standards/context.md +151 -0
  538. package/standards/design.md +93 -0
  539. package/standards/diagrams.md +152 -0
  540. package/standards/glossary.md +75 -0
  541. package/standards/groundwork.md +211 -0
  542. package/standards/index.md +36 -0
  543. package/standards/intake.md +192 -0
  544. package/standards/issue.md +94 -0
  545. package/standards/markdown.md +137 -0
  546. package/standards/memory.md +144 -0
  547. package/standards/plan.md +172 -0
  548. package/standards/pr.md +139 -0
  549. package/standards/publish.md +51 -0
  550. package/standards/readme.md +208 -0
  551. package/standards/requirements.md +70 -0
  552. package/standards/rule.md +118 -0
  553. package/standards/session.md +109 -0
  554. package/standards/skill.md +300 -0
  555. package/standards/slug.md +39 -0
  556. package/standards/snippets.md +76 -0
  557. package/standards/standard.md +170 -0
  558. package/standards/tasks.md +254 -0
  559. package/standards/teach.md +153 -0
  560. package/standards/versioning.md +71 -0
  561. package/standards/wireframes.md +113 -0
  562. package/tooling/astro/configs/astro.config.mjs +31 -0
  563. package/tooling/astro/configs/eslint.config.js +79 -0
  564. package/tooling/astro/configs/playwright.config.ts +26 -0
  565. package/tooling/astro/configs/tsconfig.json +12 -0
  566. package/tooling/astro/configs/vitest.config.ts +22 -0
  567. package/tooling/astro/manifest.toml +34 -0
  568. package/tooling/astro/reference.md +60 -0
  569. package/tooling/base/configs/.editorconfig +5 -0
  570. package/tooling/base/configs/.github/pull_request_template.md +18 -0
  571. package/tooling/base/configs/.github/workflows/verify.yml +35 -0
  572. package/tooling/base/configs/.husky/commit-msg +1 -0
  573. package/tooling/base/configs/.husky/post-merge +61 -0
  574. package/tooling/base/configs/.husky/post-rewrite +21 -0
  575. package/tooling/base/configs/.husky/pre-commit +1 -0
  576. package/tooling/base/configs/.husky/pre-push +1 -0
  577. package/tooling/base/configs/.prettierrc +12 -0
  578. package/tooling/base/configs/.shellcheckrc +1 -0
  579. package/tooling/base/configs/.vscode/extensions.json +9 -0
  580. package/tooling/base/configs/.vscode/settings.json +3 -0
  581. package/tooling/base/configs/commitlint.config.js +11 -0
  582. package/tooling/base/configs/scripts/verify.sh +64 -0
  583. package/tooling/base/manifest.toml +30 -0
  584. package/tooling/base/reference.md +98 -0
  585. package/tooling/base/seeds/.claude/context/ci.md +33 -0
  586. package/tooling/base/seeds/.claude/context/development.md +37 -0
  587. package/tooling/base/seeds/.claude/context/index.md +11 -0
  588. package/tooling/base/seeds/.cspell/project-terms.txt +0 -0
  589. package/tooling/base/seeds/.cspell/tech-stack.txt +19 -0
  590. package/tooling/base/seeds/.lintstagedrc +8 -0
  591. package/tooling/base/seeds/.prettierignore +0 -0
  592. package/tooling/base/seeds/cspell.json +20 -0
  593. package/tooling/claude/manifest.toml +14 -0
  594. package/tooling/claude/reference.md +79 -0
  595. package/tooling/claude/seeds/.claude/ARCHITECTURE.md +13 -0
  596. package/tooling/claude/seeds/.claude/DESIGN.md +62 -0
  597. package/tooling/claude/seeds/.claude/REQUIREMENTS.md +18 -0
  598. package/tooling/claude/seeds/.claude/diagrams/index.md +8 -0
  599. package/tooling/claude/seeds/.claude/hooks/index-reminder.sh +50 -0
  600. package/tooling/claude/seeds/.claude/hooks/memory-index.sh +68 -0
  601. package/tooling/claude/seeds/.claude/hooks/path-form.sh +57 -0
  602. package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +52 -0
  603. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +86 -0
  604. package/tooling/claude/seeds/.claude/hooks/tasks-index.sh +71 -0
  605. package/tooling/claude/seeds/.claude/memory/index.md +8 -0
  606. package/tooling/claude/seeds/.claude/settings.json +47 -0
  607. package/tooling/claude/seeds/.claude/tasks/index.md +8 -0
  608. package/tooling/claude/seeds/.claude/wireframes/index.md +8 -0
  609. package/tooling/claude/seeds/CLAUDE.md +29 -0
  610. package/tooling/claude/user/settings.template.json +10 -0
  611. package/tooling/claude/user/statusline-command.sh +53 -0
  612. package/tooling/python/configs/.coveragerc +14 -0
  613. package/tooling/python/configs/.python-version +1 -0
  614. package/tooling/python/configs/mypy.ini +6 -0
  615. package/tooling/python/configs/pytest.ini +4 -0
  616. package/tooling/python/configs/ruff.toml +15 -0
  617. package/tooling/python/configs/scripts/verify.sh +77 -0
  618. package/tooling/python/manifest.toml +16 -0
  619. package/tooling/python/reference.md +66 -0
  620. package/tooling/python/seeds/.cspell/tech-stack.txt +19 -0
  621. package/tooling/python/seeds/tests/test_smoke.py +2 -0
  622. package/tooling/vite-react/configs/playwright.config.ts +26 -0
  623. package/tooling/vite-react/configs/tsconfig.json +35 -0
  624. package/tooling/vite-react/configs/vite.config.ts +24 -0
  625. package/tooling/vite-react/configs/vitest.config.ts +26 -0
  626. package/tooling/vite-react/manifest.toml +23 -0
  627. package/tooling/vite-react/reference.md +55 -0
  628. package/tooling/vite-react/seeds/.cspell/project-terms.txt +1 -0
  629. package/tooling/vite-react/seeds/.cspell/tech-stack.txt +1 -0
  630. package/tooling/web/configs/.github/workflows/verify.yml +134 -0
  631. package/tooling/web/configs/.vscode/extensions.json +13 -0
  632. package/tooling/web/configs/.vscode/settings.json +10 -0
  633. package/tooling/web/configs/e2e/home.spec.ts +6 -0
  634. package/tooling/web/configs/e2e/screenshot.ts +53 -0
  635. package/tooling/web/configs/eslint.config.js +82 -0
  636. package/tooling/web/configs/scripts/screenshot.sh +28 -0
  637. package/tooling/web/configs/scripts/verify.sh +80 -0
  638. package/tooling/web/configs/scripts/worktree-port.sh +74 -0
  639. package/tooling/web/configs/src/test/setup.ts +8 -0
  640. package/tooling/web/manifest.toml +58 -0
  641. package/tooling/web/reference.md +115 -0
  642. package/tooling/web/seeds/.cspell/tech-stack.txt +18 -0
  643. package/tsconfig.json +14 -0
@@ -0,0 +1,333 @@
1
+ import { basename } from 'node:path'
2
+ import { $ } from 'bun'
3
+ import { gitEnv } from '@/git-env'
4
+ import {
5
+ type Confidence,
6
+ liveness,
7
+ type LivenessProbes,
8
+ SYSTEM_PROBES,
9
+ } from '@/sessions/live'
10
+ import { readRegistry, type SessionRecord } from '@/sessions/registry'
11
+
12
+ /** Why a row carries no branch, stated rather than left as an absent key. */
13
+ export type Unresolved =
14
+ | 'not-a-repository'
15
+ | 'detached-head'
16
+ | 'git-unavailable'
17
+
18
+ export interface ResolvedSession {
19
+ readonly name: string
20
+ readonly pid: number
21
+ readonly sessionId: string | null
22
+ readonly cwd: string
23
+ readonly kind: string
24
+ readonly status: string
25
+ readonly startedAt: string | null
26
+ /**
27
+ * The stamp the record carries beside `status`, kept alongside the dwell it
28
+ * computes below for a caller comparing two sessions rather than reading the
29
+ * age of one.
30
+ */
31
+ readonly statusUpdatedAt: string | null
32
+ /**
33
+ * Elapsed milliseconds since `statusUpdatedAt`, falling back to the coarser
34
+ * `updatedAt` when the record predates the narrower field, computed once at
35
+ * read time so three callers do not each convert the raw stamp and each get
36
+ * the clock-skew case wrong. The one record ever measured carrying
37
+ * `status: "waiting"` has exactly this shape: no `statusUpdatedAt`, an
38
+ * `updatedAt` beside it, so a caller reading this field alone would report
39
+ * the single real instance as unmeasured rather than as stalled.
40
+ * Clamped at zero rather than reported negative: a record stamped
41
+ * by a clock running ahead of this one is a skew to absorb, not a session
42
+ * that has not started waiting yet.
43
+ */
44
+ readonly statusDwellMs: number | null
45
+ /**
46
+ * The shared git directory every worktree of one repository resolves to,
47
+ * which is what identifies the repository a row belongs to. A branch name is
48
+ * unique inside one and says nothing across a machine.
49
+ */
50
+ readonly repository: string | null
51
+ readonly worktree: string | null
52
+ readonly branch: string | null
53
+ /** Null exactly when `branch` is set. The two are written together. */
54
+ readonly unresolved: Unresolved | null
55
+ }
56
+
57
+ export type SessionReport =
58
+ | { readonly kind: 'absent'; readonly dir: string }
59
+ | {
60
+ readonly kind: 'resolved'
61
+ readonly dir: string
62
+ /**
63
+ * The weakest confidence any surviving row was decided at, so one read
64
+ * tells a caller whether the roster can be trusted as an identity.
65
+ */
66
+ readonly confidence: Confidence
67
+ readonly sessions: readonly ResolvedSession[]
68
+ }
69
+
70
+ export interface ResolveOptions {
71
+ readonly dir?: string
72
+ readonly probes?: LivenessProbes
73
+ readonly locate?: (cwd: string) => Promise<Located>
74
+ /** The instant the dwell is computed against. Defaults to `Date.now`. */
75
+ readonly now?: () => number
76
+ }
77
+
78
+ export interface Located {
79
+ readonly repository: string | null
80
+ readonly worktree: string | null
81
+ readonly branch: string | null
82
+ readonly unresolved: Unresolved | null
83
+ }
84
+
85
+ /**
86
+ * Asks git what a directory is checked out to.
87
+ *
88
+ * A detached HEAD and a directory outside any repository are separated here
89
+ * rather than collapsed into one empty answer, because the first is a session
90
+ * that has a worktree and no branch and the second has neither.
91
+ */
92
+ async function locate(cwd: string): Promise<Located> {
93
+ const top = await $`git -C ${cwd} rev-parse --show-toplevel`
94
+ .env(gitEnv())
95
+ .quiet()
96
+ .nothrow()
97
+
98
+ if (top.exitCode !== 0) {
99
+ // git absent and git refusing the directory are both non-zero here. The
100
+ // first is the platform report the plan asked for and the second is an
101
+ // ordinary answer, so the distinguishing read is whether git ran at all.
102
+ const version = await $`git --version`.quiet().nothrow()
103
+ return {
104
+ repository: null,
105
+ worktree: null,
106
+ branch: null,
107
+ unresolved:
108
+ version.exitCode === 0 ? 'not-a-repository' : 'git-unavailable',
109
+ }
110
+ }
111
+
112
+ const worktree = top.stdout.toString().trim()
113
+ const repository = await repositoryOf(cwd)
114
+ const head = await $`git -C ${cwd} branch --show-current`
115
+ .env(gitEnv())
116
+ .quiet()
117
+ .nothrow()
118
+ const branch = head.stdout.toString().trim()
119
+
120
+ if (head.exitCode !== 0 || branch.length === 0) {
121
+ return { repository, worktree, branch: null, unresolved: 'detached-head' }
122
+ }
123
+
124
+ return { repository, worktree, branch, unresolved: null }
125
+ }
126
+
127
+ /**
128
+ * Resolves the shared git directory a working directory belongs to.
129
+ *
130
+ * The common directory is what a linked worktree and its main checkout agree
131
+ * on, so two rows in one repository match here while the toplevel would place
132
+ * every worktree in a repository of its own.
133
+ */
134
+ export async function repositoryOf(cwd: string): Promise<string | null> {
135
+ const dir =
136
+ await $`git -C ${cwd} rev-parse --path-format=absolute --git-common-dir`
137
+ .env(gitEnv())
138
+ .quiet()
139
+ .nothrow()
140
+
141
+ if (dir.exitCode !== 0) return null
142
+
143
+ const resolved = dir.stdout.toString().trim()
144
+ return resolved.length > 0 ? resolved : null
145
+ }
146
+
147
+ /**
148
+ * Falls back to `updatedAt` when `statusUpdatedAt` is absent, since the
149
+ * narrower field is the newer of the two and a record predating it still
150
+ * carries the coarser one. The one record measured with `status: "waiting"`
151
+ * takes exactly this path: no `statusUpdatedAt`, an `updatedAt` beside it.
152
+ */
153
+ function dwellMs(record: SessionRecord, now: number): number | null {
154
+ const stamp = record.statusUpdatedAt ?? record.updatedAt
155
+ return stamp === undefined ? null : Math.max(0, now - stamp)
156
+ }
157
+
158
+ /**
159
+ * An absent field is rendered as an absence rather than as a value.
160
+ *
161
+ * A missing start time formatted from zero reads as a session launched in 1970
162
+ * and a missing identifier as an empty one, both of which a caller would take
163
+ * for data. Null says the record did not carry it, which is the same
164
+ * distinction the registry draws between an absent folder and an empty one.
165
+ */
166
+ function present(
167
+ record: SessionRecord,
168
+ located: Located,
169
+ now: number,
170
+ ): ResolvedSession {
171
+ return {
172
+ name: record.name,
173
+ pid: record.pid,
174
+ sessionId: record.sessionId ?? null,
175
+ cwd: record.cwd,
176
+ kind: record.kind ?? 'unknown',
177
+ status: record.status ?? 'unknown',
178
+ startedAt:
179
+ record.startedAt === undefined
180
+ ? null
181
+ : new Date(record.startedAt).toISOString(),
182
+ statusUpdatedAt:
183
+ record.statusUpdatedAt === undefined
184
+ ? null
185
+ : new Date(record.statusUpdatedAt).toISOString(),
186
+ statusDwellMs: dwellMs(record, now),
187
+ repository: located.repository,
188
+ worktree: located.worktree,
189
+ branch: located.branch,
190
+ unresolved: located.unresolved,
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Resolves every live session to the worktree and branch it holds.
196
+ *
197
+ * The registry is the whole source. Each session writes its own working
198
+ * directory beside its own name, so a caller matching a listing row to a branch
199
+ * reads both from one record instead of ordering the roster by start time and
200
+ * hoping the order holds.
201
+ *
202
+ * A row whose branch cannot be read is kept and marked. Dropping it would leave
203
+ * a caller unable to tell a session that holds no branch from one the resolver
204
+ * never saw, and the second is the failure this replaces.
205
+ */
206
+ export async function resolveSessions(
207
+ opts: ResolveOptions = {},
208
+ ): Promise<SessionReport> {
209
+ const probes = opts.probes ?? SYSTEM_PROBES
210
+ const find = opts.locate ?? locate
211
+ const now = opts.now?.() ?? Date.now()
212
+ const registry = readRegistry(opts.dir)
213
+
214
+ if (registry.kind === 'absent') return { kind: 'absent', dir: registry.dir }
215
+
216
+ const live: SessionRecord[] = []
217
+ let confidence: Confidence = 'confirmed'
218
+
219
+ for (const record of registry.records) {
220
+ const state = liveness(record, probes)
221
+ if (!state.alive) continue
222
+ if (state.confidence === 'unverified') confidence = 'unverified'
223
+ live.push(record)
224
+ }
225
+
226
+ const sessions = await Promise.all(
227
+ live.map(async (record) => present(record, await find(record.cwd), now)),
228
+ )
229
+
230
+ return { kind: 'resolved', dir: registry.dir, confidence, sessions }
231
+ }
232
+
233
+ /** The environment slice the caller's own identity is read from. */
234
+ export type Env = Record<string, string | undefined>
235
+
236
+ /** Why the caller's own row could not be named, rather than an empty result. */
237
+ export type SelfUnresolved = 'no-identity' | 'no-row'
238
+
239
+ /**
240
+ * What the environment offers about the session making the call.
241
+ *
242
+ * Both fields are candidates rather than a pair, since a client sets them
243
+ * independently and a caller can arrive carrying either one alone.
244
+ */
245
+ export interface SelfIdentity {
246
+ readonly sessionId: string | null
247
+ readonly pid: number | null
248
+ }
249
+
250
+ export type SelfReport =
251
+ | { readonly kind: 'self'; readonly session: ResolvedSession }
252
+ | {
253
+ readonly kind: 'unresolved'
254
+ readonly reason: SelfUnresolved
255
+ readonly identity: SelfIdentity
256
+ }
257
+
258
+ /**
259
+ * A positive integer, which is what separates a pid from a socket named for
260
+ * something else. Signal zero addresses the caller's own process group, so a
261
+ * zero would match a row rather than failing to.
262
+ */
263
+ function pidOf(value: string | undefined): number | null {
264
+ if (value === undefined) return null
265
+ const pid = Number(value.trim())
266
+ return Number.isInteger(pid) && pid > 0 ? pid : null
267
+ }
268
+
269
+ /**
270
+ * Reads whatever the environment states about the calling session.
271
+ *
272
+ * Three identifier namespaces are in play and only two of them join to a
273
+ * roster row. `CLAUDE_CODE_SESSION_ID` is the roster's own `sessionId` and is
274
+ * read first because it survives a rename, where the name a session carries is
275
+ * derived from what it turned out to be doing and rotates while a build runs.
276
+ * `CLAUDE_CODE_HOST_SESSION_ID` is deliberately never read: it holds a
277
+ * `local_`-prefixed value from the harness namespace that matches no row, and
278
+ * it is the variable a reader searching the environment for a session id finds
279
+ * first.
280
+ *
281
+ * The socket path is the last rung because its basename is the caller's pid by
282
+ * a client convention rather than a published interface, so a client that
283
+ * moves it drops this rung while leaving the two above it standing.
284
+ */
285
+ export function callerIdentity(env: Env = process.env): SelfIdentity {
286
+ const stated = env.CLAUDE_CODE_SESSION_ID?.trim()
287
+ const socket = env.CLAUDE_CODE_MESSAGING_SOCKET
288
+
289
+ return {
290
+ sessionId: stated !== undefined && stated.length > 0 ? stated : null,
291
+ pid:
292
+ pidOf(env.CLAUDE_PID) ??
293
+ pidOf(
294
+ socket === undefined
295
+ ? undefined
296
+ : basename(socket).replace(/\.sock$/, ''),
297
+ ),
298
+ }
299
+ }
300
+
301
+ /**
302
+ * Names which row of a roster belongs to the caller.
303
+ *
304
+ * The read that already returns every field marks none of them as the caller,
305
+ * so this performs the join rather than adding a source. An identity the
306
+ * environment does not carry and an identity no row matches are separated,
307
+ * because the first is a client that states nothing and the second is a
308
+ * session the roster cannot see. A session driving from Remote Control is the
309
+ * measured instance of the second: it is addressable on the message channel
310
+ * and holds no local process record for the roster to report.
311
+ */
312
+ export function selfOf(
313
+ sessions: readonly ResolvedSession[],
314
+ identity: SelfIdentity,
315
+ ): SelfReport {
316
+ if (identity.sessionId === null && identity.pid === null) {
317
+ return { kind: 'unresolved', reason: 'no-identity', identity }
318
+ }
319
+
320
+ const byId =
321
+ identity.sessionId === null
322
+ ? undefined
323
+ : sessions.find((session) => session.sessionId === identity.sessionId)
324
+ const byPid =
325
+ identity.pid === null
326
+ ? undefined
327
+ : sessions.find((session) => session.pid === identity.pid)
328
+ const match = byId ?? byPid
329
+
330
+ return match === undefined
331
+ ? { kind: 'unresolved', reason: 'no-row', identity }
332
+ : { kind: 'self', session: match }
333
+ }
@@ -0,0 +1,391 @@
1
+ import type PptxGenJS from 'pptxgenjs'
2
+ import type { Slide } from '@/slides/parse'
3
+ import { FONTS, type Theme, TYPE } from '@/slides/styles'
4
+
5
+ type Deck = InstanceType<typeof PptxGenJS>
6
+ type PptxSlide = ReturnType<Deck['addSlide']>
7
+
8
+ export interface LayoutInfo {
9
+ name: string
10
+ description: string
11
+ }
12
+
13
+ export interface NavEntry {
14
+ title: string
15
+ slideNumber: number
16
+ }
17
+
18
+ export interface DeckNav {
19
+ deckTitle: string
20
+ tocSlide?: number
21
+ sections: NavEntry[]
22
+ }
23
+
24
+ export const LAYOUTS: LayoutInfo[] = [
25
+ { name: 'title', description: 'Cover slide with deck title and subtitle' },
26
+ { name: 'toc', description: 'Contents slide linking to each section' },
27
+ { name: 'section', description: 'Section divider with one large heading' },
28
+ { name: 'bullets', description: 'Title above a bullet list' },
29
+ { name: 'two-column', description: 'Two labeled columns of bullets' },
30
+ { name: 'stat-callout', description: 'Row of large stats with captions' },
31
+ { name: 'grid', description: 'Two-by-two cards, each a term and detail' },
32
+ { name: 'quote', description: 'Large pull quote with attribution' },
33
+ ]
34
+
35
+ const W = 13.333
36
+ const H = 7.5
37
+ const MX = 0.7
38
+ const CW = W - MX * 2
39
+ const BODY_Y = 1.7
40
+
41
+ const RENDERERS: Record<
42
+ string,
43
+ (s: PptxSlide, slide: Slide, theme: Theme) => void
44
+ > = {
45
+ title: renderTitle,
46
+ section: renderSection,
47
+ bullets: renderBullets,
48
+ 'two-column': renderTwoColumn,
49
+ 'stat-callout': renderStatCallout,
50
+ grid: renderGrid,
51
+ quote: renderQuote,
52
+ }
53
+
54
+ export function renderDeckSlide(
55
+ s: PptxSlide,
56
+ slide: Slide,
57
+ theme: Theme,
58
+ ): void {
59
+ const renderer = RENDERERS[slide.layout] ?? renderBullets
60
+ renderer(s, slide, theme)
61
+ }
62
+
63
+ export function renderToc(
64
+ s: PptxSlide,
65
+ slide: Slide,
66
+ theme: Theme,
67
+ nav: DeckNav,
68
+ ): void {
69
+ addTitle(s, slide.title || 'Contents', theme)
70
+ if (nav.sections.length === 0) return
71
+ s.addText(
72
+ nav.sections.map((entry) => ({
73
+ text: entry.title,
74
+ options: { breakLine: true, hyperlink: { slide: entry.slideNumber } },
75
+ })),
76
+ {
77
+ x: MX,
78
+ y: BODY_Y,
79
+ w: CW,
80
+ h: 5,
81
+ fontFace: FONTS.body,
82
+ fontSize: TYPE.body + 2,
83
+ color: theme.accent,
84
+ valign: 'top',
85
+ lineSpacingMultiple: 1.6,
86
+ },
87
+ )
88
+ }
89
+
90
+ export function addFooter(
91
+ s: PptxSlide,
92
+ slide: Slide,
93
+ theme: Theme,
94
+ nav: DeckNav,
95
+ ): void {
96
+ if (slide.layout === 'title' || slide.layout === 'toc') return
97
+ if (nav.deckTitle) {
98
+ s.addText(nav.deckTitle, {
99
+ x: MX,
100
+ y: H - 0.5,
101
+ w: CW / 2,
102
+ h: 0.3,
103
+ fontFace: FONTS.body,
104
+ fontSize: TYPE.caption,
105
+ color: theme.muted,
106
+ valign: 'middle',
107
+ })
108
+ }
109
+ if (nav.tocSlide) {
110
+ s.addText('Contents', {
111
+ x: MX + CW / 2,
112
+ y: H - 0.5,
113
+ w: CW / 2,
114
+ h: 0.3,
115
+ fontFace: FONTS.body,
116
+ fontSize: TYPE.caption,
117
+ color: theme.muted,
118
+ align: 'right',
119
+ valign: 'middle',
120
+ hyperlink: { slide: nav.tocSlide },
121
+ })
122
+ }
123
+ }
124
+
125
+ function renderTitle(s: PptxSlide, slide: Slide, theme: Theme): void {
126
+ s.addText(clean(slide.title), {
127
+ x: MX,
128
+ y: 2.6,
129
+ w: CW,
130
+ h: 1.4,
131
+ fontFace: FONTS.heading,
132
+ fontSize: TYPE.cover,
133
+ bold: true,
134
+ color: theme.ink,
135
+ valign: 'top',
136
+ })
137
+ const subtitle = slide.content.join(' ').trim()
138
+ if (subtitle) {
139
+ s.addText(clean(subtitle), {
140
+ x: MX,
141
+ y: 4.1,
142
+ w: CW,
143
+ h: 1,
144
+ fontFace: FONTS.body,
145
+ fontSize: TYPE.body + 4,
146
+ color: theme.muted,
147
+ valign: 'top',
148
+ })
149
+ }
150
+ }
151
+
152
+ function renderSection(s: PptxSlide, slide: Slide, theme: Theme): void {
153
+ s.addText(clean(slide.title), {
154
+ x: MX,
155
+ y: 2.9,
156
+ w: CW,
157
+ h: 1.6,
158
+ fontFace: FONTS.heading,
159
+ fontSize: TYPE.section,
160
+ bold: true,
161
+ color: theme.accent,
162
+ valign: 'middle',
163
+ })
164
+ }
165
+
166
+ function renderBullets(s: PptxSlide, slide: Slide, theme: Theme): void {
167
+ addTitle(s, slide.title, theme)
168
+ const items = bullets(slide.content)
169
+ if (items.length === 0) return
170
+ s.addText(
171
+ items.map((text) => ({
172
+ text,
173
+ options: { bullet: { code: '2022' }, breakLine: true },
174
+ })),
175
+ {
176
+ x: MX,
177
+ y: BODY_Y,
178
+ w: CW,
179
+ h: 5,
180
+ fontFace: FONTS.body,
181
+ fontSize: TYPE.body,
182
+ color: theme.ink,
183
+ valign: 'top',
184
+ lineSpacingMultiple: 1.3,
185
+ paraSpaceAfter: 10,
186
+ },
187
+ )
188
+ }
189
+
190
+ function renderTwoColumn(s: PptxSlide, slide: Slide, theme: Theme): void {
191
+ addTitle(s, slide.title, theme)
192
+ const cols = columns(slide.content)
193
+ const gap = 0.6
194
+ const colWidth = (CW - gap) / 2
195
+ cols.slice(0, 2).forEach((col, index) => {
196
+ const x = MX + index * (colWidth + gap)
197
+ s.addText(col.heading, {
198
+ x,
199
+ y: BODY_Y,
200
+ w: colWidth,
201
+ h: 0.6,
202
+ fontFace: FONTS.heading,
203
+ fontSize: TYPE.body + 4,
204
+ bold: true,
205
+ color: theme.accent,
206
+ valign: 'top',
207
+ })
208
+ if (col.items.length === 0) return
209
+ s.addText(
210
+ col.items.map((text) => ({
211
+ text,
212
+ options: { bullet: { code: '2022' }, breakLine: true },
213
+ })),
214
+ {
215
+ x,
216
+ y: BODY_Y + 0.7,
217
+ w: colWidth,
218
+ h: 4.3,
219
+ fontFace: FONTS.body,
220
+ fontSize: TYPE.body,
221
+ color: theme.ink,
222
+ valign: 'top',
223
+ lineSpacingMultiple: 1.3,
224
+ paraSpaceAfter: 8,
225
+ },
226
+ )
227
+ })
228
+ }
229
+
230
+ function renderStatCallout(s: PptxSlide, slide: Slide, theme: Theme): void {
231
+ addTitle(s, slide.title, theme)
232
+ const stats = pairs(slide.content).slice(0, 4)
233
+ if (stats.length === 0) return
234
+ const gap = 0.5
235
+ const cellWidth = (CW - gap * (stats.length - 1)) / stats.length
236
+ stats.forEach((stat, index) => {
237
+ const x = MX + index * (cellWidth + gap)
238
+ s.addText(stat.left, {
239
+ x,
240
+ y: 2.6,
241
+ w: cellWidth,
242
+ h: 1.3,
243
+ fontFace: FONTS.heading,
244
+ fontSize: TYPE.stat,
245
+ bold: true,
246
+ color: theme.accent,
247
+ align: 'left',
248
+ valign: 'top',
249
+ })
250
+ s.addText(stat.right, {
251
+ x,
252
+ y: 3.9,
253
+ w: cellWidth,
254
+ h: 1.4,
255
+ fontFace: FONTS.body,
256
+ fontSize: TYPE.body,
257
+ color: theme.muted,
258
+ align: 'left',
259
+ valign: 'top',
260
+ })
261
+ })
262
+ }
263
+
264
+ function renderGrid(s: PptxSlide, slide: Slide, theme: Theme): void {
265
+ addTitle(s, slide.title, theme)
266
+ const cards = pairs(slide.content).slice(0, 4)
267
+ const gap = 0.5
268
+ const cardWidth = (CW - gap) / 2
269
+ const cardHeight = 2.1
270
+ cards.forEach((card, index) => {
271
+ const col = index % 2
272
+ const row = Math.floor(index / 2)
273
+ const x = MX + col * (cardWidth + gap)
274
+ const y = BODY_Y + row * (cardHeight + gap)
275
+ s.addShape('roundRect', {
276
+ x,
277
+ y,
278
+ w: cardWidth,
279
+ h: cardHeight,
280
+ fill: { color: theme.surface },
281
+ line: { type: 'none' },
282
+ rectRadius: 0.08,
283
+ })
284
+ s.addText(card.left, {
285
+ x: x + 0.3,
286
+ y: y + 0.25,
287
+ w: cardWidth - 0.6,
288
+ h: 0.6,
289
+ fontFace: FONTS.heading,
290
+ fontSize: TYPE.body + 4,
291
+ bold: true,
292
+ color: theme.ink,
293
+ valign: 'top',
294
+ })
295
+ s.addText(card.right, {
296
+ x: x + 0.3,
297
+ y: y + 0.95,
298
+ w: cardWidth - 0.6,
299
+ h: cardHeight - 1.2,
300
+ fontFace: FONTS.body,
301
+ fontSize: TYPE.body - 2,
302
+ color: theme.ink,
303
+ valign: 'top',
304
+ lineSpacingMultiple: 1.2,
305
+ })
306
+ })
307
+ }
308
+
309
+ function renderQuote(s: PptxSlide, slide: Slide, theme: Theme): void {
310
+ const lines = slide.content
311
+ const attribution = lines.find((line) => /^(—|-)\s/.test(line.trim()))
312
+ const quote = lines
313
+ .filter((line) => line !== attribution)
314
+ .join(' ')
315
+ .trim()
316
+ s.addText(clean(quote), {
317
+ x: MX,
318
+ y: 2.2,
319
+ w: CW,
320
+ h: 2.6,
321
+ fontFace: FONTS.heading,
322
+ fontSize: TYPE.quote,
323
+ italic: true,
324
+ color: theme.ink,
325
+ valign: 'top',
326
+ lineSpacingMultiple: 1.25,
327
+ })
328
+ if (attribution) {
329
+ s.addText(clean(attribution.replace(/^(—|-)\s+/, '')), {
330
+ x: MX,
331
+ y: 5,
332
+ w: CW,
333
+ h: 0.6,
334
+ fontFace: FONTS.body,
335
+ fontSize: TYPE.body,
336
+ bold: true,
337
+ color: theme.accent,
338
+ valign: 'top',
339
+ })
340
+ }
341
+ }
342
+
343
+ function addTitle(s: PptxSlide, title: string, theme: Theme): void {
344
+ if (!title) return
345
+ s.addText(clean(title), {
346
+ x: MX,
347
+ y: 0.5,
348
+ w: CW,
349
+ h: 0.9,
350
+ fontFace: FONTS.heading,
351
+ fontSize: TYPE.title,
352
+ bold: true,
353
+ color: theme.ink,
354
+ valign: 'top',
355
+ })
356
+ }
357
+
358
+ function clean(text: string): string {
359
+ return text.replace(/\*\*/g, '').replace(/`/g, '').trim()
360
+ }
361
+
362
+ function bullets(content: string[]): string[] {
363
+ return content
364
+ .filter((line) => line.trim().startsWith('-'))
365
+ .map((line) => clean(line.replace(/^\s*-\s+/, '')))
366
+ }
367
+
368
+ function pairs(content: string[]): { left: string; right: string }[] {
369
+ return content
370
+ .filter((line) => line.trim().startsWith('-'))
371
+ .map((line) => line.replace(/^\s*-\s+/, ''))
372
+ .map((line) => {
373
+ const [left, ...rest] = line.split(/\s*:\s*/)
374
+ return { left: clean(left), right: clean(rest.join(': ')) }
375
+ })
376
+ }
377
+
378
+ function columns(content: string[]): { heading: string; items: string[] }[] {
379
+ const cols: { heading: string; items: string[] }[] = []
380
+ for (const line of content) {
381
+ const heading = line.match(/^##\s+(.+)/)
382
+ if (heading) {
383
+ cols.push({ heading: clean(heading[1]), items: [] })
384
+ continue
385
+ }
386
+ if (line.trim().startsWith('-') && cols.length > 0) {
387
+ cols[cols.length - 1].items.push(clean(line.replace(/^\s*-\s+/, '')))
388
+ }
389
+ }
390
+ return cols
391
+ }