@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,506 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { mkdir, readdir, readFile, rename, writeFile } from 'node:fs/promises'
3
+ import { join, relative, resolve, sep } from 'node:path'
4
+ import { regenOne } from '@/indexes/regen'
5
+
6
+ const TASKS_DIR = join('.claude', 'tasks')
7
+ const ARCHIVE_DIR = join(TASKS_DIR, 'archive')
8
+ const PLANS_DIR = join('.claude', 'plans')
9
+ const PLANS_ARCHIVE_DIR = join(PLANS_DIR, 'archive')
10
+
11
+ /**
12
+ * Siblings that sit on the board without being tasks: the generated index, the
13
+ * hand-maintained ordering, the unordered backlog beside it, and a handoff a
14
+ * session wrote before the file took one name per session. `validate` reads the
15
+ * same list, so neither verb can count a sibling as a task the other does not.
16
+ */
17
+ export const RESERVED_STEMS = [
18
+ 'index',
19
+ 'priority',
20
+ 'backlog',
21
+ 'session',
22
+ ] as const
23
+
24
+ /** The pre-compaction handoff takes one file per session, so its stems vary. */
25
+ const SESSION_MAP_PREFIX = 'session-'
26
+
27
+ export function isReservedStem(stem: string): boolean {
28
+ const reserved: readonly string[] = RESERVED_STEMS
29
+
30
+ return reserved.includes(stem) || stem.startsWith(SESSION_MAP_PREFIX)
31
+ }
32
+
33
+ /**
34
+ * `bad-input` describes the command line rather than the board, which is the
35
+ * split `record.ts` draws for the same reason. A caller naming two selectors
36
+ * answered as `ambiguous` sends a session to repair a task citation that is
37
+ * fine, so the three task verbs answer one mistake one way.
38
+ */
39
+ export const ARCHIVE_REFUSALS = [
40
+ 'no-board',
41
+ 'no-match',
42
+ 'ambiguous',
43
+ 'no-outcomes',
44
+ 'open-outcomes',
45
+ 'plan-unswept',
46
+ 'bad-input',
47
+ ] as const
48
+
49
+ export type ArchiveRefusal = (typeof ARCHIVE_REFUSALS)[number]
50
+
51
+ export type TaskSelector =
52
+ | { readonly kind: 'stem'; readonly stem: string }
53
+ | { readonly kind: 'pull-request'; readonly number: number }
54
+
55
+ export interface ArchiveSuccess {
56
+ readonly ok: true
57
+ readonly stem: string
58
+ readonly from: string
59
+ readonly to: string
60
+ readonly priorityRowRemoved: boolean
61
+ readonly indexRegenerated: boolean
62
+ }
63
+
64
+ export interface ArchiveRefused {
65
+ readonly ok: false
66
+ readonly reason: ArchiveRefusal
67
+ readonly message: string
68
+ readonly detail: readonly string[]
69
+ }
70
+
71
+ export type ArchiveOutcome = ArchiveSuccess | ArchiveRefused
72
+
73
+ export interface TaskOutcomes {
74
+ readonly open: readonly string[]
75
+ readonly closed: readonly string[]
76
+ }
77
+
78
+ export function tasksDir(root: string): string {
79
+ return join(root, TASKS_DIR)
80
+ }
81
+
82
+ export function archiveDir(root: string): string {
83
+ return join(root, ARCHIVE_DIR)
84
+ }
85
+
86
+ export const OUTCOME_PATTERN = /^- \[([ xX])\] ?(.*)$/
87
+
88
+ /**
89
+ * Marks the lines sitting inside a fenced block, the fence delimiters included.
90
+ * A checkbox in a sample a task displays is not an outcome the task claims, and
91
+ * counting one shifts every position after it. Every reader of the outcome list
92
+ * shares this so none of them can disagree about what the list holds.
93
+ */
94
+ export function fenceMask(lines: readonly string[]): boolean[] {
95
+ let inside = false
96
+
97
+ return lines.map((line) => {
98
+ if (/^\s*(?:```|~~~)/.test(line)) {
99
+ inside = !inside
100
+ return true
101
+ }
102
+ return inside
103
+ })
104
+ }
105
+
106
+ /**
107
+ * Splits a task's outcome list by checkbox state. The board format puts every
108
+ * outcome at the top level of `## Outcomes`, so an anchored match is enough and
109
+ * no heading tracking is needed.
110
+ */
111
+ export function readOutcomes(text: string): TaskOutcomes {
112
+ const open: string[] = []
113
+ const closed: string[] = []
114
+ const lines = text.split('\n')
115
+ const fenced = fenceMask(lines)
116
+
117
+ for (const [index, line] of lines.entries()) {
118
+ if (fenced[index]) continue
119
+
120
+ const match = OUTCOME_PATTERN.exec(line)
121
+ if (!match) continue
122
+
123
+ const [, box, body] = match
124
+ if (box === ' ') {
125
+ open.push(body.trim())
126
+ } else {
127
+ closed.push(body.trim())
128
+ }
129
+ }
130
+
131
+ return { open, closed }
132
+ }
133
+
134
+ /**
135
+ * Reads the `Pull request:` line `git-pr` writes when a pull request opens.
136
+ * The number is a bare `#NNN` the way `Issue:` is, since neither is a path and
137
+ * a full URL would write the remote into a gitignored file.
138
+ */
139
+ export function readPullRequest(text: string): number | undefined {
140
+ const match = /^Pull request:\s*#(\d+)\s*$/m.exec(text)
141
+ return match ? Number(match[1]) : undefined
142
+ }
143
+
144
+ /**
145
+ * Reads the `Plan:` target out of a markdown link, falling back to the older
146
+ * bare-path form. The path is returned as written, relative to the board.
147
+ */
148
+ export function readPlanTarget(text: string): string | undefined {
149
+ const match = /^Plan:\s*(?:\[[^\]]*\]\(([^)]+)\)|(\S+))\s*$/m.exec(text)
150
+ if (!match) return undefined
151
+ return match[1] ?? match[2]
152
+ }
153
+
154
+ /**
155
+ * Drops the archived task's row from the ordering table. Rows are matched by
156
+ * the link they carry rather than by a line pattern, because a row holds links
157
+ * and prose that a stream edit against it would rewrite in place.
158
+ */
159
+ export function removePriorityRow(
160
+ text: string,
161
+ stem: string,
162
+ ): { readonly text: string; readonly removed: boolean } {
163
+ const target = `](${stem}.md)`
164
+ const lines = text.split('\n')
165
+ const kept = lines.filter((line) => !isRowFor(line, target))
166
+
167
+ return { text: kept.join('\n'), removed: kept.length !== lines.length }
168
+ }
169
+
170
+ /**
171
+ * A row is about a task when its first cell links to it. A later cell naming
172
+ * another task is a reference, such as a blocker pointing at what it waits on,
173
+ * and matching anywhere in the line would delete that task's row alongside it.
174
+ */
175
+ function isRowFor(line: string, target: string): boolean {
176
+ const trimmed = line.trimStart()
177
+ if (!trimmed.startsWith('|')) return false
178
+
179
+ const [, first] = trimmed.split('|')
180
+ return first !== undefined && first.includes(target)
181
+ }
182
+
183
+ /**
184
+ * Tests containment rather than a string prefix, so a sibling whose name merely
185
+ * extends the folder's is not read as being inside it.
186
+ */
187
+ function isUnder(path: string, dir: string): boolean {
188
+ return path === dir || path.startsWith(`${dir}${sep}`)
189
+ }
190
+
191
+ /**
192
+ * Resolves the `Plan:` target against the board and against the project root
193
+ * both, which is how `claude-docs` reads the same line. It accepts `../plans/x.md`
194
+ * and `.claude/plans/x.md` as one file, so a gate reading only the first form
195
+ * would pass the second and strand the plan this exists to protect.
196
+ *
197
+ * The resolved path is returned rather than a boolean, because the citation
198
+ * count below compares two tasks by where their targets land and not by the
199
+ * strings they wrote. A target outside the live plans folder yields nothing,
200
+ * which is an archived plan or a pointer into somewhere else entirely.
201
+ *
202
+ * The archive sits inside the folder it archives, so containment alone reads an
203
+ * archived plan as live. Subtracting it is what keeps a closed task from being
204
+ * counted as a citation the sweep has yet to make.
205
+ */
206
+ export function resolveLivePlan(
207
+ target: string,
208
+ dir: string,
209
+ root: string,
210
+ ): string | undefined {
211
+ const plans = join(root, PLANS_DIR)
212
+ const archive = join(root, PLANS_ARCHIVE_DIR)
213
+ const fromBoard = resolve(dir, target)
214
+ const fromRoot = resolve(root, target)
215
+
216
+ if (isUnder(fromBoard, plans) && !isUnder(fromBoard, archive)) {
217
+ return fromBoard
218
+ }
219
+ if (isUnder(fromRoot, plans) && !isUnder(fromRoot, archive)) return fromRoot
220
+ return undefined
221
+ }
222
+
223
+ /**
224
+ * Names the other live tasks whose `Plan:` line lands on the same file. This is
225
+ * the rule `claude-docs` applies before it archives a plan, held here so one
226
+ * question has one implementation: a plan another live task still cites is a
227
+ * plan the sweep is correct to leave, and a guard that read the folder instead
228
+ * refused every task sharing one plan and deadlocked the board against the
229
+ * sweep that was behaving correctly.
230
+ *
231
+ * The closing task is excluded by name. It cites the plan itself, so counting
232
+ * it would never reach zero and the count would answer nothing.
233
+ */
234
+ export async function otherTasksCitingPlan(
235
+ dir: string,
236
+ root: string,
237
+ plan: string,
238
+ closing: string,
239
+ ): Promise<string[]> {
240
+ const stems = (await listTaskStems(dir)).filter((stem) => stem !== closing)
241
+
242
+ const read = await Promise.all(
243
+ stems.map(async (stem) => {
244
+ const target = readPlanTarget(
245
+ await readFile(join(dir, `${stem}.md`), 'utf8'),
246
+ )
247
+ const resolved = target && resolveLivePlan(target, dir, root)
248
+ return resolved === plan ? stem : undefined
249
+ }),
250
+ )
251
+
252
+ return read.filter((stem): stem is string => stem !== undefined)
253
+ }
254
+
255
+ /**
256
+ * Where a task's `Plan:` target resolves, which is what decides whether the
257
+ * plan is the sweep's to move. `unstated` is a task carrying no line at all,
258
+ * and it is distinct from a line resolving somewhere unexpected.
259
+ */
260
+ export const CITATION_LOCATIONS = [
261
+ 'unstated',
262
+ 'live',
263
+ 'archived',
264
+ 'outside',
265
+ ] as const
266
+
267
+ export type CitationLocation = (typeof CITATION_LOCATIONS)[number]
268
+
269
+ export interface PlanCitations {
270
+ readonly ok: true
271
+ readonly stem: string
272
+ readonly target: string | undefined
273
+ readonly location: CitationLocation
274
+ /** Other live tasks landing on the same file. Empty unless `location` is `live`. */
275
+ readonly citedBy: readonly string[]
276
+ }
277
+
278
+ export type CitationOutcome = PlanCitations | ArchiveRefused
279
+
280
+ /**
281
+ * Answers where one task's plan sits and who else holds it, which is the whole
282
+ * of the last-live-citation rule. `claude-docs` reads this rather than scanning
283
+ * the board itself, so the sweep that moves a plan and the gate that refuses a
284
+ * task archive cannot drift into disagreeing about which plan is free.
285
+ *
286
+ * It reports and never writes. The move, the retarget, and the ordering the two
287
+ * happen in belong to the caller, and a verb that performed them would be
288
+ * deciding a question the sweep is there to decide.
289
+ */
290
+ export async function planCitations(
291
+ root: string,
292
+ stem: string,
293
+ ): Promise<CitationOutcome> {
294
+ const dir = tasksDir(root)
295
+
296
+ if (!existsSync(dir)) {
297
+ return refuse('no-board', `No task board at ${relative(root, dir)}.`)
298
+ }
299
+
300
+ const stems = await listTaskStems(dir)
301
+ if (!stems.includes(stem)) {
302
+ return refuse('no-match', `No task named ${stem} on the board.`, stems)
303
+ }
304
+
305
+ const target = readPlanTarget(await readFile(join(dir, `${stem}.md`), 'utf8'))
306
+ if (!target) {
307
+ return {
308
+ ok: true,
309
+ stem,
310
+ target: undefined,
311
+ location: 'unstated',
312
+ citedBy: [],
313
+ }
314
+ }
315
+
316
+ const live = resolveLivePlan(target, dir, root)
317
+ if (!live) {
318
+ const location = resolvesUnder(target, dir, root, PLANS_ARCHIVE_DIR)
319
+ ? 'archived'
320
+ : 'outside'
321
+ return { ok: true, stem, target, location, citedBy: [] }
322
+ }
323
+
324
+ return {
325
+ ok: true,
326
+ stem,
327
+ target,
328
+ location: 'live',
329
+ citedBy: await otherTasksCitingPlan(dir, root, live, stem),
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Runs the two-spelling resolution `resolveLivePlan` applies against a folder
335
+ * other than the live one, so an archived plan is read as archived whichever
336
+ * root the task wrote its path against.
337
+ */
338
+ function resolvesUnder(
339
+ target: string,
340
+ dir: string,
341
+ root: string,
342
+ folder: string,
343
+ ): boolean {
344
+ const resolved = join(root, folder)
345
+
346
+ return (
347
+ isUnder(resolve(dir, target), resolved) ||
348
+ isUnder(resolve(root, target), resolved)
349
+ )
350
+ }
351
+
352
+ export async function listTaskStems(dir: string): Promise<string[]> {
353
+ const entries = await readdir(dir)
354
+
355
+ return entries
356
+ .filter((entry) => entry.endsWith('.md'))
357
+ .map((entry) => entry.slice(0, -'.md'.length))
358
+ .filter((stem) => !isReservedStem(stem))
359
+ .sort()
360
+ }
361
+
362
+ async function matchByPullRequest(
363
+ dir: string,
364
+ stems: readonly string[],
365
+ number: number,
366
+ ): Promise<string[]> {
367
+ const read = await Promise.all(
368
+ stems.map(async (stem) => ({
369
+ stem,
370
+ number: readPullRequest(await readFile(join(dir, `${stem}.md`), 'utf8')),
371
+ })),
372
+ )
373
+
374
+ return read.filter((entry) => entry.number === number).map(({ stem }) => stem)
375
+ }
376
+
377
+ function refuse(
378
+ reason: ArchiveRefusal,
379
+ message: string,
380
+ detail: readonly string[] = [],
381
+ ): ArchiveRefused {
382
+ return { ok: false, reason, message, detail }
383
+ }
384
+
385
+ async function resolveStem(
386
+ dir: string,
387
+ selector: TaskSelector,
388
+ ): Promise<string | ArchiveRefused> {
389
+ const stems = await listTaskStems(dir)
390
+
391
+ if (selector.kind === 'stem') {
392
+ if (!stems.includes(selector.stem)) {
393
+ return refuse(
394
+ 'no-match',
395
+ `No task named ${selector.stem} on the board.`,
396
+ stems,
397
+ )
398
+ }
399
+ return selector.stem
400
+ }
401
+
402
+ const matched = await matchByPullRequest(dir, stems, selector.number)
403
+
404
+ if (matched.length === 0) {
405
+ return refuse('no-match', `No task names pull request #${selector.number}.`)
406
+ }
407
+
408
+ if (matched.length > 1) {
409
+ return refuse(
410
+ 'ambiguous',
411
+ `${matched.length} tasks name pull request #${selector.number}. One task, one pull request.`,
412
+ matched,
413
+ )
414
+ }
415
+
416
+ return matched[0]
417
+ }
418
+
419
+ /**
420
+ * Archives one task as a single unit: the move, the ordering-row removal, and
421
+ * the index regen. The hook and `claude-tasks` both call this, so every gate
422
+ * refuses rather than reports. A caller with nobody watching cannot act on a
423
+ * warning, and two callers gating differently is the drift this exists to stop.
424
+ */
425
+ export async function archiveTask(
426
+ root: string,
427
+ selector: TaskSelector,
428
+ ): Promise<ArchiveOutcome> {
429
+ const dir = tasksDir(root)
430
+
431
+ if (!existsSync(dir)) {
432
+ return refuse('no-board', `No task board at ${relative(root, dir)}.`)
433
+ }
434
+
435
+ const resolved = await resolveStem(dir, selector)
436
+ if (typeof resolved !== 'string') return resolved
437
+
438
+ const stem = resolved
439
+ const from = join(dir, `${stem}.md`)
440
+ const text = await readFile(from, 'utf8')
441
+
442
+ const { open, closed } = readOutcomes(text)
443
+
444
+ if (open.length > 0) {
445
+ return refuse(
446
+ 'open-outcomes',
447
+ `${stem} has ${open.length} open outcome(s). Close them or cut them from the task, then archive.`,
448
+ open,
449
+ )
450
+ }
451
+
452
+ if (closed.length === 0) {
453
+ return refuse(
454
+ 'no-outcomes',
455
+ `${stem} carries no outcomes, so nothing marks it shipped.`,
456
+ )
457
+ }
458
+
459
+ const planTarget = readPlanTarget(text)
460
+ const livePlan = planTarget && resolveLivePlan(planTarget, dir, root)
461
+
462
+ // A live plan is unswept only when nothing else on the board holds it. A plan
463
+ // several tasks share stays live by design, so refusing on the folder alone
464
+ // parked every one of those tasks behind a sweep that was right to decline.
465
+ if (livePlan) {
466
+ const shared = await otherTasksCitingPlan(dir, root, livePlan, stem)
467
+
468
+ if (shared.length === 0) {
469
+ return refuse(
470
+ 'plan-unswept',
471
+ `${stem} is the last task pointing at a live plan. Run /claude-docs to sweep it first, then archive.`,
472
+ [planTarget],
473
+ )
474
+ }
475
+ }
476
+
477
+ const destination = archiveDir(root)
478
+ await mkdir(destination, { recursive: true })
479
+ const to = join(destination, `${stem}.md`)
480
+ await rename(from, to)
481
+
482
+ const priorityRowRemoved = await clearPriorityRow(dir, stem)
483
+ const regen = await regenOne(dir, { dryRun: false })
484
+
485
+ return {
486
+ ok: true,
487
+ stem,
488
+ from,
489
+ to,
490
+ priorityRowRemoved,
491
+ indexRegenerated: regen.action === 'written',
492
+ }
493
+ }
494
+
495
+ async function clearPriorityRow(dir: string, stem: string): Promise<boolean> {
496
+ const path = join(dir, 'priority.md')
497
+ if (!existsSync(path)) return false
498
+
499
+ const { text, removed } = removePriorityRow(
500
+ await readFile(path, 'utf8'),
501
+ stem,
502
+ )
503
+ if (removed) await writeFile(path, text)
504
+
505
+ return removed
506
+ }