contextdevkit 1.8.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 (345) hide show
  1. package/CHANGELOG.md +592 -0
  2. package/LICENSE +21 -0
  3. package/README.md +401 -0
  4. package/docs/AGENT-PACKAGE-FORMAT.md +140 -0
  5. package/docs/ARCHITECTURE.md +258 -0
  6. package/docs/CHANGELOG.md +559 -0
  7. package/docs/CUSTOMIZING.md +211 -0
  8. package/docs/LEVELS.md +151 -0
  9. package/docs/ROADMAP.md +385 -0
  10. package/docs/SQUAD-PIPELINE-FORMAT.md +258 -0
  11. package/docs/SQUADS/agent-forge.md +65 -0
  12. package/docs/SQUADS/design-team.md +161 -0
  13. package/docs/token-economy-plan.md +135 -0
  14. package/install.mjs +273 -0
  15. package/instrucoes.md +274 -0
  16. package/package.json +46 -0
  17. package/templates/CLAUDE.md.tpl +133 -0
  18. package/templates/claude/agents/_TEMPLATE.md +52 -0
  19. package/templates/claude/agents/accessibility.md +36 -0
  20. package/templates/claude/agents/agent-architect.md +37 -0
  21. package/templates/claude/agents/architect.md +39 -0
  22. package/templates/claude/agents/code-reviewer.md +43 -0
  23. package/templates/claude/agents/code-security.md +59 -0
  24. package/templates/claude/agents/context-keeper.md +40 -0
  25. package/templates/claude/agents/devops.md +40 -0
  26. package/templates/claude/agents/eval-designer.md +40 -0
  27. package/templates/claude/agents/forge-orchestrator.md +42 -0
  28. package/templates/claude/agents/governance-officer.md +45 -0
  29. package/templates/claude/agents/growth.md +92 -0
  30. package/templates/claude/agents/infra-security.md +53 -0
  31. package/templates/claude/agents/landing-architect.md +154 -0
  32. package/templates/claude/agents/model-router.md +34 -0
  33. package/templates/claude/agents/packager.md +38 -0
  34. package/templates/claude/agents/privacy-lgpd.md +64 -0
  35. package/templates/claude/agents/product-owner.md +51 -0
  36. package/templates/claude/agents/prompt-engineer.md +33 -0
  37. package/templates/claude/agents/qa-e2e.md +52 -0
  38. package/templates/claude/agents/qa-fuzzer.md +24 -0
  39. package/templates/claude/agents/qa-integration.md +21 -0
  40. package/templates/claude/agents/qa-orchestrator.md +40 -0
  41. package/templates/claude/agents/qa-perf.md +40 -0
  42. package/templates/claude/agents/qa-unit.md +39 -0
  43. package/templates/claude/agents/rag-designer.md +54 -0
  44. package/templates/claude/agents/retention.md +85 -0
  45. package/templates/claude/agents/security.md +48 -0
  46. package/templates/claude/agents/seo-specialist.md +106 -0
  47. package/templates/claude/agents/test-engineer.md +48 -0
  48. package/templates/claude/agents/tool-designer.md +32 -0
  49. package/templates/claude/agents/ui-designer.md +37 -0
  50. package/templates/claude/agents/ux-designer.md +38 -0
  51. package/templates/claude/commands/README.md +95 -0
  52. package/templates/claude/commands/advise.md +80 -0
  53. package/templates/claude/commands/audit/analyze-code-ia-practices.md +75 -0
  54. package/templates/claude/commands/audit/audit.md +35 -0
  55. package/templates/claude/commands/audit/contract-check.md +21 -0
  56. package/templates/claude/commands/audit/deep-analysis.md +48 -0
  57. package/templates/claude/commands/audit/deps-audit.md +49 -0
  58. package/templates/claude/commands/audit/security-setup.md +35 -0
  59. package/templates/claude/commands/audit/seo-audit.md +63 -0
  60. package/templates/claude/commands/audit/tech-debt-sweep.md +35 -0
  61. package/templates/claude/commands/bug-hunt.md +42 -0
  62. package/templates/claude/commands/claude-md.md +36 -0
  63. package/templates/claude/commands/close-version.md +25 -0
  64. package/templates/claude/commands/context-refresh.md +19 -0
  65. package/templates/claude/commands/context-stats.md +15 -0
  66. package/templates/claude/commands/dashboard.md +66 -0
  67. package/templates/claude/commands/distill-apply.md +19 -0
  68. package/templates/claude/commands/distill-sessions.md +26 -0
  69. package/templates/claude/commands/fleet.md +47 -0
  70. package/templates/claude/commands/forge/forge-audit.md +16 -0
  71. package/templates/claude/commands/forge/forge-budget.md +16 -0
  72. package/templates/claude/commands/forge/forge-deprecate.md +16 -0
  73. package/templates/claude/commands/forge/forge-doctor.md +17 -0
  74. package/templates/claude/commands/forge/forge-eval.md +16 -0
  75. package/templates/claude/commands/forge/forge-fallback-test.md +17 -0
  76. package/templates/claude/commands/forge/forge-killswitch.md +17 -0
  77. package/templates/claude/commands/forge/forge-list.md +17 -0
  78. package/templates/claude/commands/forge/forge-new.md +41 -0
  79. package/templates/claude/commands/forge/forge-policy.md +16 -0
  80. package/templates/claude/commands/forge/forge-redteam.md +17 -0
  81. package/templates/claude/commands/forge/forge-refresh-matrix.md +20 -0
  82. package/templates/claude/commands/forge/forge-route.md +17 -0
  83. package/templates/claude/commands/forge/forge-show.md +16 -0
  84. package/templates/claude/commands/landing-page.md +71 -0
  85. package/templates/claude/commands/log-session.md +59 -0
  86. package/templates/claude/commands/media-gen.md +93 -0
  87. package/templates/claude/commands/new-adr.md +30 -0
  88. package/templates/claude/commands/pipeline/dev-start.md +64 -0
  89. package/templates/claude/commands/pipeline/pipeline.md +36 -0
  90. package/templates/claude/commands/pipeline/resume.md +70 -0
  91. package/templates/claude/commands/pipeline/retro.md +34 -0
  92. package/templates/claude/commands/pipeline/runs.md +63 -0
  93. package/templates/claude/commands/pipeline/ship.md +54 -0
  94. package/templates/claude/commands/pipeline/workflow.md +85 -0
  95. package/templates/claude/commands/playbook.md +27 -0
  96. package/templates/claude/commands/predictions-review.md +28 -0
  97. package/templates/claude/commands/qa/qa-signoff.md +24 -0
  98. package/templates/claude/commands/qa/scaffold-tests.md +27 -0
  99. package/templates/claude/commands/qa/test-plan.md +26 -0
  100. package/templates/claude/commands/qa/visual-test.md +42 -0
  101. package/templates/claude/commands/roadmap.md +48 -0
  102. package/templates/claude/commands/setup/aidevtool-from0.md +104 -0
  103. package/templates/claude/commands/setup/context-config.md +25 -0
  104. package/templates/claude/commands/setup/context-doctor.md +21 -0
  105. package/templates/claude/commands/setup/context-level.md +17 -0
  106. package/templates/claude/commands/setup/setupcontextdevkit.md +121 -0
  107. package/templates/claude/commands/simulate-impact.md +32 -0
  108. package/templates/claude/commands/squad.md +44 -0
  109. package/templates/claude/commands/state.md +21 -0
  110. package/templates/claude/commands/token-report.md +29 -0
  111. package/templates/claude/commands/tune-agents.md +35 -0
  112. package/templates/claude/commands/vcs/claim.md +18 -0
  113. package/templates/claude/commands/vcs/git.md +83 -0
  114. package/templates/claude/commands/vcs/release.md +15 -0
  115. package/templates/claude/commands/vcs/worktree-new.md +18 -0
  116. package/templates/claude/commands/watch.md +47 -0
  117. package/templates/contextkit/.env.example +36 -0
  118. package/templates/contextkit/CLAUDE.child.md.tpl +38 -0
  119. package/templates/contextkit/README.md +74 -0
  120. package/templates/contextkit/behaviors-examples.md +183 -0
  121. package/templates/contextkit/behaviors.md +116 -0
  122. package/templates/contextkit/best-practices.md +323 -0
  123. package/templates/contextkit/config.json +66 -0
  124. package/templates/contextkit/detectors/README.md +45 -0
  125. package/templates/contextkit/detectors/example-detector.mjs.example +25 -0
  126. package/templates/contextkit/instrucoes.md +114 -0
  127. package/templates/contextkit/memory/GLOSSARY.md +13 -0
  128. package/templates/contextkit/memory/SESSIONS.md +9 -0
  129. package/templates/contextkit/memory/WORKSPACE.md +7 -0
  130. package/templates/contextkit/memory/business-rules/_TEMPLATE.md +33 -0
  131. package/templates/contextkit/memory/decisions/0000-record-architecture-decisions.md +34 -0
  132. package/templates/contextkit/memory/decisions/_TEMPLATE.md +25 -0
  133. package/templates/contextkit/memory/predictions/.gitkeep +0 -0
  134. package/templates/contextkit/memory/roadmap.md +28 -0
  135. package/templates/contextkit/memory/sessions/.gitkeep +0 -0
  136. package/templates/contextkit/memory/workflows/.gitkeep +0 -0
  137. package/templates/contextkit/pipeline/backlog/.gitkeep +0 -0
  138. package/templates/contextkit/pipeline/conclusion/.gitkeep +0 -0
  139. package/templates/contextkit/pipeline/devpipeline.md +9 -0
  140. package/templates/contextkit/pipeline/testing/.gitkeep +0 -0
  141. package/templates/contextkit/pipeline/working/.gitkeep +0 -0
  142. package/templates/contextkit/review-protocol.md +214 -0
  143. package/templates/contextkit/runtime/config/defaults.mjs +215 -0
  144. package/templates/contextkit/runtime/config/levels.mjs +42 -0
  145. package/templates/contextkit/runtime/config/load.mjs +105 -0
  146. package/templates/contextkit/runtime/config/paths.mjs +92 -0
  147. package/templates/contextkit/runtime/config/presets.mjs +47 -0
  148. package/templates/contextkit/runtime/config/schema.mjs +88 -0
  149. package/templates/contextkit/runtime/config/settings-compose.mjs +55 -0
  150. package/templates/contextkit/runtime/git-hooks/commit-msg.mjs +55 -0
  151. package/templates/contextkit/runtime/git-hooks/pre-commit.mjs +47 -0
  152. package/templates/contextkit/runtime/git-hooks/pre-push.mjs +102 -0
  153. package/templates/contextkit/runtime/hooks/boot-context-readers.mjs +111 -0
  154. package/templates/contextkit/runtime/hooks/boot-signals.mjs +135 -0
  155. package/templates/contextkit/runtime/hooks/check-registration.mjs +228 -0
  156. package/templates/contextkit/runtime/hooks/concurrency-guard.mjs +110 -0
  157. package/templates/contextkit/runtime/hooks/ledger.mjs +231 -0
  158. package/templates/contextkit/runtime/hooks/md-extract.mjs +65 -0
  159. package/templates/contextkit/runtime/hooks/path-classification.mjs +62 -0
  160. package/templates/contextkit/runtime/hooks/safe-io.mjs +84 -0
  161. package/templates/contextkit/runtime/hooks/session-digest-core.mjs +85 -0
  162. package/templates/contextkit/runtime/hooks/session-start.mjs +248 -0
  163. package/templates/contextkit/runtime/hooks/simulate-gate.mjs +108 -0
  164. package/templates/contextkit/runtime/hooks/track-edits.mjs +154 -0
  165. package/templates/contextkit/runtime/providers/media/_adapter.mjs +120 -0
  166. package/templates/contextkit/runtime/providers/media/nano-banana.mjs +110 -0
  167. package/templates/contextkit/runtime/providers/media/veo.mjs +162 -0
  168. package/templates/contextkit/runtime/providers/review/_adapter.mjs +71 -0
  169. package/templates/contextkit/runtime/providers/review/detect.mjs +115 -0
  170. package/templates/contextkit/runtime/providers/review/gh.mjs +103 -0
  171. package/templates/contextkit/runtime/state/state-io.mjs +172 -0
  172. package/templates/contextkit/runtime/statusline.mjs +51 -0
  173. package/templates/contextkit/squads/README.md +115 -0
  174. package/templates/contextkit/squads/_BRIEFING.md.tpl +27 -0
  175. package/templates/contextkit/squads/agent-forge/README.md +69 -0
  176. package/templates/contextkit/squads/agent-forge/ROADMAP.md +108 -0
  177. package/templates/contextkit/squads/agent-forge/best-practices.md +89 -0
  178. package/templates/contextkit/squads/agent-forge/cli/forge-admin.mjs +132 -0
  179. package/templates/contextkit/squads/agent-forge/cli/forge-eval-cli.mjs +163 -0
  180. package/templates/contextkit/squads/agent-forge/cli/forge-new.mjs +97 -0
  181. package/templates/contextkit/squads/agent-forge/cli/forge-ops.mjs +177 -0
  182. package/templates/contextkit/squads/agent-forge/lib/architect.mjs +112 -0
  183. package/templates/contextkit/squads/agent-forge/lib/eval-designer.mjs +133 -0
  184. package/templates/contextkit/squads/agent-forge/lib/eval-runner.mjs +167 -0
  185. package/templates/contextkit/squads/agent-forge/lib/governance-officer.mjs +178 -0
  186. package/templates/contextkit/squads/agent-forge/lib/package-ops.mjs +101 -0
  187. package/templates/contextkit/squads/agent-forge/lib/packager.mjs +219 -0
  188. package/templates/contextkit/squads/agent-forge/lib/prompt-gen.mjs +122 -0
  189. package/templates/contextkit/squads/agent-forge/lib/rag-designer.mjs +102 -0
  190. package/templates/contextkit/squads/agent-forge/lib/router.mjs +165 -0
  191. package/templates/contextkit/squads/agent-forge/lib/tool-gen.mjs +113 -0
  192. package/templates/contextkit/squads/agent-forge/lib/yaml.mjs +47 -0
  193. package/templates/contextkit/squads/agent-forge/pipeline.yaml +65 -0
  194. package/templates/contextkit/squads/agent-forge/router/capability-matrix.json +112 -0
  195. package/templates/contextkit/squads/agent-forge/router/decision-rules.json +120 -0
  196. package/templates/contextkit/squads/agent-forge/templates/agent-package/.agentforgerc +12 -0
  197. package/templates/contextkit/squads/agent-forge/templates/agent-package/CHANGELOG.md +13 -0
  198. package/templates/contextkit/squads/agent-forge/templates/agent-package/LICENSE +5 -0
  199. package/templates/contextkit/squads/agent-forge/templates/agent-package/README.md +39 -0
  200. package/templates/contextkit/squads/agent-forge/templates/agent-package/adapters/go/README.md +10 -0
  201. package/templates/contextkit/squads/agent-forge/templates/agent-package/adapters/go/agent.go +14 -0
  202. package/templates/contextkit/squads/agent-forge/templates/agent-package/adapters/go/go.mod +3 -0
  203. package/templates/contextkit/squads/agent-forge/templates/agent-package/adapters/node/README.md +11 -0
  204. package/templates/contextkit/squads/agent-forge/templates/agent-package/adapters/node/index.js +53 -0
  205. package/templates/contextkit/squads/agent-forge/templates/agent-package/adapters/node/package.json +9 -0
  206. package/templates/contextkit/squads/agent-forge/templates/agent-package/adapters/python/README.md +10 -0
  207. package/templates/contextkit/squads/agent-forge/templates/agent-package/adapters/python/agent.py +16 -0
  208. package/templates/contextkit/squads/agent-forge/templates/agent-package/adapters/python/pyproject.toml +10 -0
  209. package/templates/contextkit/squads/agent-forge/templates/agent-package/evals/golden.jsonl +1 -0
  210. package/templates/contextkit/squads/agent-forge/templates/agent-package/evals/red-team.jsonl +3 -0
  211. package/templates/contextkit/squads/agent-forge/templates/agent-package/evals/rubric.yaml +14 -0
  212. package/templates/contextkit/squads/agent-forge/templates/agent-package/evals/run-eval.md +17 -0
  213. package/templates/contextkit/squads/agent-forge/templates/agent-package/evals/thresholds.yaml +18 -0
  214. package/templates/contextkit/squads/agent-forge/templates/agent-package/examples/basic.node.md +17 -0
  215. package/templates/contextkit/squads/agent-forge/templates/agent-package/examples/with-fallback.node.md +24 -0
  216. package/templates/contextkit/squads/agent-forge/templates/agent-package/examples/with-rag.python.md +20 -0
  217. package/templates/contextkit/squads/agent-forge/templates/agent-package/governance/audit.schema.json +23 -0
  218. package/templates/contextkit/squads/agent-forge/templates/agent-package/governance/compliance.policy.yaml +43 -0
  219. package/templates/contextkit/squads/agent-forge/templates/agent-package/governance/cost.policy.yaml +36 -0
  220. package/templates/contextkit/squads/agent-forge/templates/agent-package/governance/fallback-chain.yaml +16 -0
  221. package/templates/contextkit/squads/agent-forge/templates/agent-package/governance/quality.policy.yaml +43 -0
  222. package/templates/contextkit/squads/agent-forge/templates/agent-package/manifest.yaml +91 -0
  223. package/templates/contextkit/squads/agent-forge/templates/agent-package/prompts/system.anthropic.md +19 -0
  224. package/templates/contextkit/squads/agent-forge/templates/agent-package/prompts/system.canonical.md +25 -0
  225. package/templates/contextkit/squads/agent-forge/templates/agent-package/prompts/system.deepseek.md +21 -0
  226. package/templates/contextkit/squads/agent-forge/templates/agent-package/prompts/system.google.md +19 -0
  227. package/templates/contextkit/squads/agent-forge/templates/agent-package/prompts/system.ollama.md +21 -0
  228. package/templates/contextkit/squads/agent-forge/templates/agent-package/prompts/system.openai.md +20 -0
  229. package/templates/contextkit/squads/agent-forge/templates/agent-package/rag/config.yaml +17 -0
  230. package/templates/contextkit/squads/agent-forge/templates/agent-package/rag/index/.gitkeep +3 -0
  231. package/templates/contextkit/squads/agent-forge/templates/agent-package/rag/ingestion/chunker.config.yaml +6 -0
  232. package/templates/contextkit/squads/agent-forge/templates/agent-package/rag/ingestion/sources.yaml +8 -0
  233. package/templates/contextkit/squads/agent-forge/templates/agent-package/rag/retrieval/query-template.md +16 -0
  234. package/templates/contextkit/squads/agent-forge/templates/agent-package/rag/retrieval/rerank.config.yaml +6 -0
  235. package/templates/contextkit/squads/agent-forge/templates/agent-package/tools/adapters/anthropic.tools.json +11 -0
  236. package/templates/contextkit/squads/agent-forge/templates/agent-package/tools/adapters/deepseek.tools.json +14 -0
  237. package/templates/contextkit/squads/agent-forge/templates/agent-package/tools/adapters/google.tools.json +11 -0
  238. package/templates/contextkit/squads/agent-forge/templates/agent-package/tools/adapters/ollama.tools.json +14 -0
  239. package/templates/contextkit/squads/agent-forge/templates/agent-package/tools/adapters/openai.tools.json +14 -0
  240. package/templates/contextkit/squads/agent-forge/templates/agent-package/tools/schemas.canonical.json +25 -0
  241. package/templates/contextkit/starters/tanstack/README.md +86 -0
  242. package/templates/contextkit/starters/tanstack/index.html +12 -0
  243. package/templates/contextkit/starters/tanstack/package.json +25 -0
  244. package/templates/contextkit/starters/tanstack/src/main.tsx +40 -0
  245. package/templates/contextkit/starters/tanstack/src/router.tsx +12 -0
  246. package/templates/contextkit/starters/tanstack/src/routes/__root.tsx +10 -0
  247. package/templates/contextkit/starters/tanstack/src/routes/index.tsx +17 -0
  248. package/templates/contextkit/starters/tanstack/tsconfig.json +19 -0
  249. package/templates/contextkit/starters/tanstack/vite.config.ts +10 -0
  250. package/templates/contextkit/tools/scripts/adr-digest-core.mjs +42 -0
  251. package/templates/contextkit/tools/scripts/adr-digest.mjs +78 -0
  252. package/templates/contextkit/tools/scripts/agent-tuning.mjs +74 -0
  253. package/templates/contextkit/tools/scripts/aiso-audit.mjs +174 -0
  254. package/templates/contextkit/tools/scripts/audit-shared.mjs +129 -0
  255. package/templates/contextkit/tools/scripts/claim.mjs +133 -0
  256. package/templates/contextkit/tools/scripts/claude-md.mjs +123 -0
  257. package/templates/contextkit/tools/scripts/clean-drive.mjs +78 -0
  258. package/templates/contextkit/tools/scripts/context-config.mjs +111 -0
  259. package/templates/contextkit/tools/scripts/context-level.mjs +98 -0
  260. package/templates/contextkit/tools/scripts/context-pack.mjs +120 -0
  261. package/templates/contextkit/tools/scripts/contract-scan.mjs +186 -0
  262. package/templates/contextkit/tools/scripts/dashboard-data.mjs +198 -0
  263. package/templates/contextkit/tools/scripts/dashboard-html.mjs +215 -0
  264. package/templates/contextkit/tools/scripts/dashboard-server.mjs +129 -0
  265. package/templates/contextkit/tools/scripts/dashboard.mjs +107 -0
  266. package/templates/contextkit/tools/scripts/deep-analysis.mjs +62 -0
  267. package/templates/contextkit/tools/scripts/deps-audit.mjs +201 -0
  268. package/templates/contextkit/tools/scripts/detect-stack.mjs +164 -0
  269. package/templates/contextkit/tools/scripts/distill-detect.mjs +90 -0
  270. package/templates/contextkit/tools/scripts/doctor.mjs +165 -0
  271. package/templates/contextkit/tools/scripts/fleet.mjs +170 -0
  272. package/templates/contextkit/tools/scripts/generate-context.mjs +142 -0
  273. package/templates/contextkit/tools/scripts/gh-alerts.mjs +117 -0
  274. package/templates/contextkit/tools/scripts/git.mjs +97 -0
  275. package/templates/contextkit/tools/scripts/home.mjs +106 -0
  276. package/templates/contextkit/tools/scripts/mark-simulation.mjs +78 -0
  277. package/templates/contextkit/tools/scripts/media-gen.mjs +154 -0
  278. package/templates/contextkit/tools/scripts/pipeline-board.mjs +74 -0
  279. package/templates/contextkit/tools/scripts/pipeline-prioritize.mjs +68 -0
  280. package/templates/contextkit/tools/scripts/pipeline-session.mjs +99 -0
  281. package/templates/contextkit/tools/scripts/pipeline-validate.mjs +136 -0
  282. package/templates/contextkit/tools/scripts/pipeline.mjs +302 -0
  283. package/templates/contextkit/tools/scripts/playbook.mjs +123 -0
  284. package/templates/contextkit/tools/scripts/predictions-review.mjs +113 -0
  285. package/templates/contextkit/tools/scripts/release.mjs +60 -0
  286. package/templates/contextkit/tools/scripts/resume.mjs +114 -0
  287. package/templates/contextkit/tools/scripts/roadmap.mjs +86 -0
  288. package/templates/contextkit/tools/scripts/runs.mjs +116 -0
  289. package/templates/contextkit/tools/scripts/seo-audit.mjs +150 -0
  290. package/templates/contextkit/tools/scripts/session-digest.mjs +89 -0
  291. package/templates/contextkit/tools/scripts/session-reindex.mjs +91 -0
  292. package/templates/contextkit/tools/scripts/setup-complete.mjs +69 -0
  293. package/templates/contextkit/tools/scripts/squad-meta.mjs +23 -0
  294. package/templates/contextkit/tools/scripts/squad-pipeline-condition.mjs +192 -0
  295. package/templates/contextkit/tools/scripts/squad-pipeline.mjs +301 -0
  296. package/templates/contextkit/tools/scripts/squad.mjs +80 -0
  297. package/templates/contextkit/tools/scripts/stats.mjs +138 -0
  298. package/templates/contextkit/tools/scripts/sync-check.mjs +235 -0
  299. package/templates/contextkit/tools/scripts/tech-debt-detectors.mjs +76 -0
  300. package/templates/contextkit/tools/scripts/tech-debt-scan.mjs +164 -0
  301. package/templates/contextkit/tools/scripts/token-report.mjs +153 -0
  302. package/templates/contextkit/tools/scripts/visual-test.mjs +132 -0
  303. package/templates/contextkit/tools/scripts/watch.mjs +106 -0
  304. package/templates/contextkit/tools/scripts/workflow.mjs +136 -0
  305. package/templates/contextkit/tools/scripts/workspace-sync.mjs +220 -0
  306. package/templates/contextkit/tools/scripts/worktree-new.mjs +50 -0
  307. package/templates/contextkit/workflows/L1-static-loading.md +59 -0
  308. package/templates/contextkit/workflows/L2-session-ledger.md +86 -0
  309. package/templates/contextkit/workflows/L3-multi-session.md +80 -0
  310. package/templates/contextkit/workflows/L4-squads.md +68 -0
  311. package/templates/contextkit/workflows/L5-proactive.md +88 -0
  312. package/templates/contextkit/workflows/README.md +47 -0
  313. package/templates/contextkit/workflows/playbooks/distillation-cycle.md +74 -0
  314. package/templates/contextkit/workflows/playbooks/landing-page.md +197 -0
  315. package/templates/contextkit/workflows/playbooks/security-batch.md +68 -0
  316. package/templates/contextkit/workflows/playbooks/seo-aiso.md +288 -0
  317. package/templates/contextkit/workflows/playbooks/simulate-impact.md +83 -0
  318. package/templates/contextkit/workflows/playbooks/tanstack.md +164 -0
  319. package/templates/contextkit/workflows/playbooks/tech-debt-sweep.md +77 -0
  320. package/templates/docs/CHANGELOG.md.tpl +11 -0
  321. package/templates/gitattributes +3 -0
  322. package/templates/github/ISSUE_TEMPLATE/bug_report.md +30 -0
  323. package/templates/github/ISSUE_TEMPLATE/feature_request.md +22 -0
  324. package/templates/github/PULL_REQUEST_TEMPLATE.md +27 -0
  325. package/templates/github/dependabot.yml +27 -0
  326. package/templates/github/workflows/quality.yml +36 -0
  327. package/templates/github/workflows/security.yml +54 -0
  328. package/tools/install/cli.mjs +62 -0
  329. package/tools/install/fs.mjs +56 -0
  330. package/tools/install/git.mjs +114 -0
  331. package/tools/install/project.mjs +51 -0
  332. package/tools/install/uninstall.mjs +54 -0
  333. package/tools/integration-test-compozy.mjs +88 -0
  334. package/tools/integration-test-guards.mjs +269 -0
  335. package/tools/integration-test-tooling-agent-forge.mjs +189 -0
  336. package/tools/integration-test-tooling-pipeline.mjs +164 -0
  337. package/tools/integration-test-tooling.mjs +172 -0
  338. package/tools/integration-test.mjs +228 -0
  339. package/tools/it-helpers.mjs +60 -0
  340. package/tools/selfcheck-agent-forge-ops.mjs +107 -0
  341. package/tools/selfcheck-agent-forge.mjs +304 -0
  342. package/tools/selfcheck-config.mjs +80 -0
  343. package/tools/selfcheck-runtime.mjs +135 -0
  344. package/tools/selfcheck-source.mjs +326 -0
  345. package/tools/selfcheck.mjs +268 -0
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Agent-tuning signals — the deterministic half of /tune-agents.
4
+ *
5
+ * Aggregates the signals available for refining agent briefings: the roster +
6
+ * tier-2 briefing coverage, and how often each agent is referenced across the
7
+ * session history (a usage proxy). `/tune-agents` reads this, adds judgment, and
8
+ * PROPOSES briefing edits — it never auto-applies (mirrors /distill-sessions).
9
+ *
10
+ * agent-tuning.mjs # human summary
11
+ * agent-tuning.mjs --json # { agents: [...], sessionsAnalyzed, withoutBriefing }
12
+ *
13
+ * Zero-dependency, defensive: degrades to empty signals, never throws.
14
+ */
15
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
16
+ import { resolve } from 'node:path';
17
+ import { squadOf } from './squad-meta.mjs';
18
+ import { pathsFor } from '../../runtime/config/paths.mjs';
19
+
20
+ const ROOT = process.cwd();
21
+ const P = pathsFor(ROOT);
22
+ const AGENTS = resolve(ROOT, '.claude/agents');
23
+ const SQUADS = P.squads;
24
+ const SESSIONS = P.sessions;
25
+
26
+ function read(p) {
27
+ try {
28
+ return readFileSync(p, 'utf-8');
29
+ } catch {
30
+ return '';
31
+ }
32
+ }
33
+
34
+ function listMd(dir) {
35
+ try {
36
+ return readdirSync(dir).filter((f) => f.endsWith('.md'));
37
+ } catch {
38
+ return [];
39
+ }
40
+ }
41
+
42
+ function collect() {
43
+ const agentFiles = listMd(AGENTS).filter((f) => f !== '_TEMPLATE.md');
44
+ const sessions = listMd(SESSIONS).map((f) => read(resolve(SESSIONS, f)));
45
+ const agents = agentFiles.map((f) => {
46
+ const name = f.slice(0, -3);
47
+ const squad = squadOf(AGENTS, name);
48
+ return {
49
+ name,
50
+ squad,
51
+ hasBriefing: existsSync(resolve(SQUADS, squad, `${name}.md`)),
52
+ mentions: sessions.filter((s) => s.includes(name)).length,
53
+ };
54
+ });
55
+ agents.sort((a, b) => b.mentions - a.mentions);
56
+ return { agents, sessionsAnalyzed: sessions.length, withoutBriefing: agents.filter((a) => !a.hasBriefing).map((a) => a.name) };
57
+ }
58
+
59
+ function main() {
60
+ const s = collect();
61
+ if (process.argv.includes('--json')) {
62
+ process.stdout.write(JSON.stringify(s, null, 2) + '\n');
63
+ return;
64
+ }
65
+ if (!s.agents.length) {
66
+ console.log('🎯 agent-tuning: no agents found (Level < 4?).');
67
+ return;
68
+ }
69
+ console.log(`🎯 agent-tuning signals — ${s.agents.length} agents, ${s.sessionsAnalyzed} sessions analyzed\n`);
70
+ for (const a of s.agents) console.log(` ${a.hasBriefing ? '📄' : ' '} ${a.name} (${a.squad}) ${a.mentions} mention(s)`);
71
+ console.log(`\n ${s.withoutBriefing.length} agent(s) without a tier-2 briefing. Run /tune-agents to propose refinements.`);
72
+ }
73
+
74
+ main();
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * AISO audit — static analyser for AI Search Optimization smells (ADR-0025).
4
+ *
5
+ * AISO is the discoverability surface for LLM answer engines
6
+ * (ChatGPT search, Perplexity, Claude search, Gemini). A site can rank
7
+ * well on Google and never appear in LLM answers — this audit catches
8
+ * the missing patterns: `llms.txt`, FAQ schema, semantic HTML5,
9
+ * author + date stamps, and a `robots.txt` that does not block AI
10
+ * crawlers by accident.
11
+ *
12
+ * Same shape as seo-audit.mjs: walks page files, emits findings,
13
+ * supports `--json`. Zero deps. Defensive.
14
+ */
15
+ import { readFileSync, existsSync } from 'node:fs';
16
+ import { resolve, relative } from 'node:path';
17
+ import { lineOf, renderFindings, exitCodeFor, walkProject } from './audit-shared.mjs';
18
+
19
+ const PAGE_EXTS = ['.html', '.astro', '.jsx', '.tsx', '.vue', '.svelte', '.mdx'];
20
+
21
+ const SEVERITY = {
22
+ MISSING_LLMS_TXT: 'high',
23
+ MISSING_FAQ_SCHEMA: 'high',
24
+ MISSING_ORG_SCHEMA: 'medium',
25
+ DIV_SOUP: 'medium',
26
+ JS_RENDERED_CONTENT: 'high',
27
+ MISSING_AUTHOR_SCHEMA: 'low',
28
+ MISSING_DATE_STAMP: 'low',
29
+ BLOCKS_AI_CRAWLERS: 'high',
30
+ };
31
+
32
+ const finding = (rel) => (code, line, message) =>
33
+ ({ file: rel, code, line, severity: SEVERITY[code] || 'medium', message });
34
+
35
+ const AI_CRAWLERS = ['GPTBot', 'ClaudeBot', 'PerplexityBot', 'Google-Extended', 'OAI-SearchBot'];
36
+
37
+ /** robots.txt smells: missing file (handled by seo-audit) and AI-crawler disallow without explicit override. */
38
+ function checkRobots(root) {
39
+ const out = [];
40
+ const robotsPaths = ['robots.txt', 'public/robots.txt', 'static/robots.txt'];
41
+ const robotsPath = robotsPaths.map((p) => resolve(root, p)).find((p) => existsSync(p));
42
+ if (!robotsPath) return out;
43
+ let txt = '';
44
+ try { txt = readFileSync(robotsPath, 'utf8'); } catch { return out; }
45
+ for (const ua of AI_CRAWLERS) {
46
+ const block = new RegExp(`User-agent:\\s*${ua}[\\s\\S]*?Disallow:\\s*/`, 'i');
47
+ if (block.test(txt)) {
48
+ out.push({
49
+ file: relative(root, robotsPath).replaceAll('\\', '/'),
50
+ code: 'BLOCKS_AI_CRAWLERS',
51
+ line: 0,
52
+ severity: SEVERITY.BLOCKS_AI_CRAWLERS,
53
+ message: `robots.txt blocks ${ua}; the site will not appear in that LLM's answers. Add a project ADR if this is intentional.`,
54
+ });
55
+ }
56
+ }
57
+ return out;
58
+ }
59
+
60
+ /** llms.txt presence — the 2024-vintage AISO convention. */
61
+ function checkLlmsTxt(root) {
62
+ const paths = ['llms.txt', 'public/llms.txt', 'static/llms.txt'];
63
+ if (paths.some((p) => existsSync(resolve(root, p)))) return [];
64
+ return [{
65
+ file: 'public/llms.txt',
66
+ code: 'MISSING_LLMS_TXT',
67
+ line: 0,
68
+ severity: SEVERITY.MISSING_LLMS_TXT,
69
+ message: 'no llms.txt at root — LLM answer engines have no curated routing map. See llmstxt.org for the format.',
70
+ }];
71
+ }
72
+
73
+ /** Per-file AISO checks: FAQ + Org schema, div-soup, JS-rendered content, author + date stamps. */
74
+ function checkFile(file, text, rel) {
75
+ const f = finding(rel);
76
+ const out = [];
77
+ const isHtmlLike = /\.(html|astro)$/i.test(file);
78
+
79
+ if (!isHtmlLike) return out;
80
+
81
+ // FAQ schema — the load-bearing AISO move
82
+ if (!/"@type":\s*"FAQPage"/.test(text)) {
83
+ out.push(f('MISSING_FAQ_SCHEMA', 1, 'no FAQPage JSON-LD — LLMs cite FAQ entries near-verbatim; without it the page does not appear in answers'));
84
+ }
85
+
86
+ // Organization schema
87
+ if (!/"@type":\s*"Organization"/.test(text)) {
88
+ out.push(f('MISSING_ORG_SCHEMA', 1, 'no Organization JSON-LD — brand entity unknown to LLM rankers'));
89
+ }
90
+
91
+ // Author / person schema (low — only flags if completely absent)
92
+ if (!/"@type":\s*"(Person|Article|NewsArticle)"/.test(text) && /<article/i.test(text)) {
93
+ out.push(f('MISSING_AUTHOR_SCHEMA', 1, 'page has <article> but no Person/Article schema — LLMs weight authored content over anonymous'));
94
+ }
95
+
96
+ // Date stamps
97
+ if (!/(datePublished|dateModified|article:modified_time)/i.test(text)) {
98
+ out.push(f('MISSING_DATE_STAMP', 1, 'no published/modified timestamp — LLMs care about recency'));
99
+ }
100
+
101
+ // Div soup: ratio of <div> to semantic HTML5 tags
102
+ const divs = (text.match(/<div[\s>]/gi) || []).length;
103
+ const semantic = (text.match(/<(article|section|nav|main|aside|header|footer)[\s>]/gi) || []).length;
104
+ if (divs > 5 && semantic > 0 && divs / Math.max(semantic, 1) > 5) {
105
+ out.push(f('DIV_SOUP', 1, `${divs} <div> to ${semantic} semantic tags (ratio ${(divs / semantic).toFixed(1)}:1) — LLM extractors weight semantic HTML5`));
106
+ } else if (divs > 10 && semantic === 0) {
107
+ out.push(f('DIV_SOUP', 1, `${divs} <div> with no semantic HTML5 tags at all — use <article>, <section>, <nav>, <main>`));
108
+ }
109
+
110
+ // JS-rendered content heuristic: large <script> with template-like content but tiny <body>
111
+ const bodyMatch = /<body[^>]*>([\s\S]*?)<\/body>/i.exec(text);
112
+ if (bodyMatch) {
113
+ const bodyText = bodyMatch[1].replace(/<[^>]+>/g, '').trim();
114
+ const scripts = [...text.matchAll(/<script[^>]*>([\s\S]*?)<\/script>/gi)];
115
+ const heavyScript = scripts.some((m) => m[1].length > 2000);
116
+ if (bodyText.length < 200 && heavyScript) {
117
+ out.push(f('JS_RENDERED_CONTENT', lineOf(text, bodyMatch.index), 'body text < 200 chars but script body > 2 kB — content appears JS-rendered; LLM crawlers may miss it'));
118
+ }
119
+ }
120
+ return out;
121
+ }
122
+
123
+ /**
124
+ * Run the AISO audit against a project root.
125
+ *
126
+ * @param {string} root
127
+ * @returns {{ findings: object[] }}
128
+ */
129
+ export function runAisoAudit(root) {
130
+ const findings = [...checkLlmsTxt(root), ...checkRobots(root)];
131
+ for (const file of walkProject(root, PAGE_EXTS)) {
132
+ let text = '';
133
+ try { text = readFileSync(file, 'utf8'); } catch { continue; }
134
+ findings.push(...checkFile(file, text, relative(root, file).replaceAll('\\', '/')));
135
+ }
136
+ return { findings };
137
+ }
138
+
139
+ const argv = process.argv.slice(2);
140
+ const wantJson = argv.includes('--json');
141
+ const wantHelp = argv.includes('--help') || argv.includes('-h');
142
+
143
+ const isMain = (() => {
144
+ try {
145
+ const here = new URL(import.meta.url).pathname.toLowerCase();
146
+ const entry = process.argv[1]
147
+ ? new URL('file://' + process.argv[1].replace(/\\/g, '/')).pathname.toLowerCase()
148
+ : '';
149
+ return here === entry;
150
+ } catch { return false; }
151
+ })();
152
+
153
+ if (isMain) {
154
+ if (wantHelp) {
155
+ process.stdout.write(`Usage: aiso-audit.mjs [--json]
156
+
157
+ Scans the project for AI Search Optimization smells per ADR-0025.
158
+ AISO is about LLM answer-engine discoverability (ChatGPT, Perplexity,
159
+ Claude, Gemini) — orthogonal to classical SEO.
160
+
161
+ --json emit findings as JSON to stdout
162
+ --help this message
163
+ `);
164
+ process.exit(0);
165
+ }
166
+ const root = process.cwd();
167
+ const result = runAisoAudit(root);
168
+ if (wantJson) {
169
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
170
+ } else {
171
+ process.stdout.write(renderFindings(result.findings, { title: 'AISO audit' }));
172
+ }
173
+ process.exit(exitCodeFor(result.findings));
174
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Shared helpers for the SEO + AISO audit scripts (ADR-0025).
3
+ *
4
+ * - `walkProject(root, exts)` — recursive file walk that skips the usual
5
+ * noise (`node_modules`, `.git`, `dist`, `build`, framework caches).
6
+ * - `detectFramework(root)` — reads package.json and reports the
7
+ * rendering posture (`astro`, `next`, `nuxt`, `remix`, `sveltekit`,
8
+ * `vite-react`, or `null`).
9
+ * - `lineOf(text, idx)` — 1-based line number for a regex match index.
10
+ * - `renderFindings(findings)` — pretty terminal output, grouped by
11
+ * severity. JSON output handled by the calling script.
12
+ *
13
+ * Zero deps. Defensive: any failure returns a safe default (empty
14
+ * array, `null` framework) — rule 2.
15
+ */
16
+ import { readFileSync, readdirSync } from 'node:fs';
17
+ import { join, extname } from 'node:path';
18
+
19
+ const SKIP_DIRS = /(^|[\\/])(node_modules|\.git|dist|build|out|\.next|\.astro|\.nuxt|\.svelte-kit|\.cache|coverage|\.vercel|\.netlify|\.turbo)([\\/]|$)/;
20
+
21
+ /**
22
+ * Walk a directory recursively yielding files with one of the given
23
+ * extensions. Skips noise dirs. Defensive — unreadable subdirs are
24
+ * silently skipped.
25
+ *
26
+ * @param {string} root
27
+ * @param {string[]} exts e.g. ['.html', '.astro', '.jsx']
28
+ */
29
+ export function* walkProject(root, exts) {
30
+ const stack = [root];
31
+ while (stack.length > 0) {
32
+ const dir = stack.pop();
33
+ let entries;
34
+ try { entries = readdirSync(dir, { withFileTypes: true }); }
35
+ catch { continue; }
36
+ for (const e of entries) {
37
+ const full = join(dir, e.name);
38
+ if (SKIP_DIRS.test(full)) continue;
39
+ if (e.isDirectory()) stack.push(full);
40
+ else if (exts.includes(extname(e.name))) yield full;
41
+ }
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Best-effort framework detection from package.json. The result drives
47
+ * the SPA_ENTRYPOINT refusal in seo-audit.
48
+ *
49
+ * @param {string} root
50
+ * @returns {'astro' | 'next' | 'nuxt' | 'remix' | 'sveltekit' | 'vite-react' | 'gatsby' | 'eleventy' | null}
51
+ */
52
+ export function detectFramework(root) {
53
+ let pkg;
54
+ try {
55
+ pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8').replace(/^/, ''));
56
+ } catch { return null; }
57
+ const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
58
+ if (deps.astro) return 'astro';
59
+ if (deps.next) return 'next';
60
+ if (deps.nuxt) return 'nuxt';
61
+ if (deps['@remix-run/react'] || deps['@remix-run/node']) return 'remix';
62
+ if (deps['@sveltejs/kit']) return 'sveltekit';
63
+ if (deps.gatsby) return 'gatsby';
64
+ if (deps['@11ty/eleventy']) return 'eleventy';
65
+ if (deps.vite && (deps.react || deps['react-dom'])) return 'vite-react';
66
+ return null;
67
+ }
68
+
69
+ /** True when the detected framework leaves the user responsible for SSR/SSG wiring. */
70
+ export const isSpaFramework = (fw) => fw === 'vite-react' || fw === null;
71
+
72
+ /** True when the detected framework ships SSG / SSR by default. */
73
+ export const isIndexableFramework = (fw) =>
74
+ fw === 'astro' || fw === 'next' || fw === 'nuxt' || fw === 'remix' ||
75
+ fw === 'sveltekit' || fw === 'gatsby' || fw === 'eleventy';
76
+
77
+ /**
78
+ * 1-based line number for the character at `idx` in `text`.
79
+ *
80
+ * @param {string} text
81
+ * @param {number} idx
82
+ * @returns {number}
83
+ */
84
+ export function lineOf(text, idx) {
85
+ if (idx <= 0) return 1;
86
+ let n = 1;
87
+ for (let i = 0; i < idx; i++) if (text.charCodeAt(i) === 10) n++;
88
+ return n;
89
+ }
90
+
91
+ const SEV_ORDER = ['critical', 'high', 'medium', 'low'];
92
+ const SEV_LABEL = {
93
+ critical: '\x1b[31mCRITICAL\x1b[0m',
94
+ high: '\x1b[33mHIGH \x1b[0m',
95
+ medium: '\x1b[36mMEDIUM \x1b[0m',
96
+ low: '\x1b[90mLOW \x1b[0m',
97
+ };
98
+
99
+ /**
100
+ * Render findings as a coloured terminal table grouped by severity.
101
+ *
102
+ * @param {Array<{ code:string, file:string, line:number, severity:string, message:string }>} findings
103
+ * @param {object} opts
104
+ * @param {string} opts.title
105
+ * @returns {string}
106
+ */
107
+ export function renderFindings(findings, { title }) {
108
+ if (!findings.length) {
109
+ return `\n🔍 ${title}\n ✅ no findings — all checks passed.\n`;
110
+ }
111
+ const bySev = Object.fromEntries(SEV_ORDER.map((s) => [s, []]));
112
+ for (const f of findings) (bySev[f.severity] || bySev.medium).push(f);
113
+ const lines = [`\n🔍 ${title}`, ` ${findings.length} finding${findings.length === 1 ? '' : 's'}\n`];
114
+ for (const sev of SEV_ORDER) {
115
+ if (!bySev[sev].length) continue;
116
+ lines.push(` ${SEV_LABEL[sev]} (${bySev[sev].length})`);
117
+ for (const f of bySev[sev]) {
118
+ const loc = f.line ? `:${f.line}` : '';
119
+ lines.push(` ${f.code.padEnd(22, ' ')} ${f.file}${loc}`);
120
+ if (f.message) lines.push(` ↳ ${f.message}`);
121
+ }
122
+ lines.push('');
123
+ }
124
+ return lines.join('\n') + '\n';
125
+ }
126
+
127
+ /** Convenience: exit code from findings (1 if any critical, 0 otherwise). */
128
+ export const exitCodeFor = (findings) =>
129
+ findings.some((f) => f.severity === 'critical') ? 1 : 0;
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Reserves resources for the current session (Level >= 3) — paths and, since
4
+ * [ADR-0015 §B](../../memory/decisions/0015-pipeline-dsl-working-stage-and-multi-session-work-claims.md),
5
+ * DevPipeline task ids. Writes/updates `.claude/.workspace/<sid>.json`, then
6
+ * regenerates `contextkit/memory/WORKSPACE.md`. The current session id is read
7
+ * from the `.last-touched` pointer the hooks maintain.
8
+ *
9
+ * Usage: node contextkit/tools/scripts/claim.mjs <path> [path2 ...]
10
+ * API: attachTask(taskId) / detachTask(taskId) — used by pipeline.mjs
11
+ * start|stop so task ownership flows through the same single source
12
+ * of truth as path ownership.
13
+ */
14
+ import { execFileSync } from 'node:child_process';
15
+ import { existsSync } from 'node:fs';
16
+ import { mkdir, readFile } from 'node:fs/promises';
17
+ import { resolve } from 'node:path';
18
+ import { fileURLToPath } from 'node:url';
19
+ import { sanitizeSid } from '../../runtime/hooks/ledger.mjs';
20
+ import { writeFileAtomic } from '../../runtime/hooks/safe-io.mjs';
21
+
22
+ const ROOT = process.cwd();
23
+ const WS_DIR = resolve(ROOT, '.claude/.workspace');
24
+ const LAST_TOUCHED = resolve(ROOT, '.claude/.sessions/.last-touched');
25
+
26
+ // execFileSync (argv array, no shell) — consistent with the other git callers.
27
+ function gitOut(args, fallback) {
28
+ try {
29
+ return execFileSync('git', args, { cwd: ROOT, encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] }).trim() || fallback;
30
+ } catch {
31
+ return fallback;
32
+ }
33
+ }
34
+
35
+ async function sessionId() {
36
+ try {
37
+ return JSON.parse(await readFile(LAST_TOUCHED, 'utf-8')).sessionId;
38
+ } catch {
39
+ return `local_${process.pid}`;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Loads (or creates) the current session's workspace record. Backward-compat:
45
+ * records without `tasks[]` get an empty default so old sessions parse fine.
46
+ *
47
+ * @param {string} sid — already sanitized
48
+ * @returns {Promise<{ sessionId, branch, user, startedAt, lastHeartbeat, claims, tasks, file }>}
49
+ */
50
+ async function loadRecord(sid) {
51
+ await mkdir(WS_DIR, { recursive: true });
52
+ const file = resolve(WS_DIR, `${sid}.json`);
53
+ const fresh = { sessionId: sid, branch: gitOut(['symbolic-ref', '--short', 'HEAD'], 'detached'), user: gitOut(['config', 'user.name'], 'unknown'), startedAt: Date.now(), lastHeartbeat: Date.now(), claims: [], tasks: [] };
54
+ if (!existsSync(file)) return { ...fresh, file };
55
+ try {
56
+ const existing = JSON.parse(await readFile(file, 'utf-8'));
57
+ return { ...fresh, ...existing, tasks: Array.isArray(existing.tasks) ? existing.tasks : [], file };
58
+ } catch {
59
+ return { ...fresh, file };
60
+ }
61
+ }
62
+
63
+ async function persistRecord(record) {
64
+ record.lastHeartbeat = Date.now();
65
+ const { file, ...body } = record;
66
+ await writeFileAtomic(file, JSON.stringify(body, null, 2));
67
+ try {
68
+ execFileSync('node', ['contextkit/tools/scripts/workspace-sync.mjs'], { cwd: ROOT, stdio: 'ignore' });
69
+ } catch {
70
+ /* best effort */
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Attaches a DevPipeline task id to the current session's record (ADR-0015 §B).
76
+ * Called by `pipeline.mjs start <id>`. Idempotent: re-attaching a task id is a
77
+ * no-op (heartbeat refresh only).
78
+ *
79
+ * @param {string} taskId
80
+ */
81
+ export async function attachTask(taskId) {
82
+ const sid = sanitizeSid(await sessionId());
83
+ const record = await loadRecord(sid);
84
+ const id = String(taskId).padStart(3, '0');
85
+ const existing = record.tasks.find((t) => t.id === id);
86
+ if (existing) {
87
+ existing.lastHeartbeat = Date.now();
88
+ } else {
89
+ record.tasks.push({ id, startedAt: Date.now(), lastHeartbeat: Date.now() });
90
+ }
91
+ await persistRecord(record);
92
+ }
93
+
94
+ /**
95
+ * Detaches a DevPipeline task id from the current session's record. Called by
96
+ * `pipeline.mjs stop <id>` and by the stale-eviction sweep in workspace-sync.
97
+ * No-op if the task wasn't on this session's list.
98
+ *
99
+ * @param {string} taskId
100
+ */
101
+ export async function detachTask(taskId) {
102
+ const sid = sanitizeSid(await sessionId());
103
+ const record = await loadRecord(sid);
104
+ const id = String(taskId).padStart(3, '0');
105
+ const before = record.tasks.length;
106
+ record.tasks = record.tasks.filter((t) => t.id !== id);
107
+ if (record.tasks.length === before) return;
108
+ await persistRecord(record);
109
+ }
110
+
111
+ async function main() {
112
+ const paths = process.argv.slice(2).map((p) => p.replaceAll('\\', '/'));
113
+ if (paths.length === 0) {
114
+ console.error('Usage: claim.mjs <path> [path2 ...]');
115
+ process.exit(1);
116
+ }
117
+ const sid = sanitizeSid(await sessionId());
118
+ const record = await loadRecord(sid);
119
+ const existing = new Set((record.claims || []).map((c) => c.path));
120
+ for (const p of paths) {
121
+ if (!existing.has(p)) record.claims.push({ path: p, claimedAt: Date.now() });
122
+ }
123
+ await persistRecord(record);
124
+ console.log(`✅ Claimed ${paths.length} path(s) for session ${sid.slice(0, 8)}: ${paths.join(', ')}`);
125
+ }
126
+
127
+ // Only run the CLI when invoked directly; library imports stay side-effect-free.
128
+ if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
129
+ main().catch((err) => {
130
+ console.error('❌ claim failed:', err);
131
+ process.exit(1);
132
+ });
133
+ }
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Modular CLAUDE.md scaffolder.
4
+ *
5
+ * Like the source platform (root CLAUDE.md + apps/api/CLAUDE.md +
6
+ * apps/mobile/CLAUDE.md), each app / independent module should carry its OWN
7
+ * scoped CLAUDE.md so Claude Code loads the closest, most relevant rules. This
8
+ * detects module roots and ensures each has one.
9
+ *
10
+ * A "module root" is a directory that looks independently buildable: under a
11
+ * monorepo group (apps/ packages/ modules/ services/ libs/ apps-*), OR a
12
+ * conventional split dir (backend/ frontend/ client/ server/ api/ web/ mobile/
13
+ * desktop/ functions/ worker/), AND it contains a manifest or a src/.
14
+ *
15
+ * Usage:
16
+ * node contextkit/tools/scripts/claude-md.mjs find [--json]
17
+ * node contextkit/tools/scripts/claude-md.mjs scaffold # create missing (stub) CLAUDE.md
18
+ */
19
+ import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
20
+ import { dirname, join, resolve } from 'node:path';
21
+ import { fileURLToPath } from 'node:url';
22
+
23
+ const ROOT = process.cwd();
24
+ // Installed at <project>/contextkit/tools/scripts/ → the seeded template is at <project>/contextkit/.
25
+ const TPL = resolve(dirname(fileURLToPath(import.meta.url)), '../../CLAUDE.child.md.tpl');
26
+
27
+ const GROUPS = ['apps', 'packages', 'modules', 'services', 'libs', 'plugins'];
28
+ const SPLITS = ['backend', 'frontend', 'client', 'server', 'api', 'web', 'mobile', 'desktop', 'functions', 'worker', 'workers', 'app'];
29
+ const MANIFESTS = ['package.json', 'pyproject.toml', 'go.mod', 'Cargo.toml', 'tsconfig.json', 'composer.json', 'Gemfile', 'pom.xml'];
30
+
31
+ function looksBuildable(absDir) {
32
+ if (MANIFESTS.some((m) => existsSync(join(absDir, m)))) return true;
33
+ return existsSync(join(absDir, 'src'));
34
+ }
35
+
36
+ function dirsIn(absDir) {
37
+ try {
38
+ return readdirSync(absDir, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith('.')).map((e) => e.name);
39
+ } catch {
40
+ return [];
41
+ }
42
+ }
43
+
44
+ /** Detect module roots (depth 1 splits + depth 2 monorepo group children). */
45
+ function detectModuleRoots() {
46
+ const roots = new Set();
47
+ for (const name of SPLITS) {
48
+ const abs = resolve(ROOT, name);
49
+ if (existsSync(abs) && looksBuildable(abs)) roots.add(name);
50
+ }
51
+ for (const group of GROUPS) {
52
+ const groupAbs = resolve(ROOT, group);
53
+ if (!existsSync(groupAbs)) continue;
54
+ for (const child of dirsIn(groupAbs)) {
55
+ const abs = join(groupAbs, child);
56
+ if (looksBuildable(abs)) roots.add(`${group}/${child}`);
57
+ }
58
+ }
59
+ return [...roots].sort();
60
+ }
61
+
62
+ function moduleStack(absDir) {
63
+ if (existsSync(join(absDir, 'package.json'))) {
64
+ try {
65
+ const pkg = JSON.parse(readFileSync(join(absDir, 'package.json'), 'utf-8').replace(/^/, ''));
66
+ const deps = Object.keys({ ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) });
67
+ const known = ['react', 'next', 'expo', 'react-native', 'vue', 'svelte', 'hono', 'express', 'fastify', '@nestjs/core', 'drizzle-orm', 'prisma', 'vite'];
68
+ const found = known.filter((k) => deps.includes(k));
69
+ return `Node/TypeScript. ${found.length ? 'Detected: ' + found.join(', ') + '.' : '_TODO: fill in._'}`;
70
+ } catch {
71
+ /* ignore */
72
+ }
73
+ }
74
+ for (const [f, label] of [['pyproject.toml', 'Python'], ['go.mod', 'Go'], ['Cargo.toml', 'Rust']]) {
75
+ if (existsSync(join(absDir, f))) return `${label} (${f}).`;
76
+ }
77
+ return '_TODO: fill in this module\'s stack._';
78
+ }
79
+
80
+ function render(tpl, vars) {
81
+ return tpl.replace(/\{\{(\w+)\}\}/g, (_, k) => (k in vars ? vars[k] : `{{${k}}}`));
82
+ }
83
+
84
+ const cmd = process.argv[2];
85
+ const roots = detectModuleRoots();
86
+ const status = roots.map((r) => ({ module: r, hasClaudeMd: existsSync(resolve(ROOT, r, 'CLAUDE.md')) }));
87
+
88
+ if (cmd === 'find') {
89
+ if (process.argv.includes('--json')) {
90
+ console.log(JSON.stringify({ moduleRoots: status }, null, 2));
91
+ } else if (status.length === 0) {
92
+ console.log('No app/module roots detected (single-package project — root CLAUDE.md is enough).');
93
+ } else {
94
+ console.log('Module roots:');
95
+ for (const s of status) console.log(` ${s.hasClaudeMd ? '✓' : '✗'} ${s.module}/CLAUDE.md`);
96
+ const missing = status.filter((s) => !s.hasClaudeMd).length;
97
+ if (missing) console.log(`\n${missing} module(s) missing CLAUDE.md — run \`claude-md.mjs scaffold\` (or /claude-md).`);
98
+ }
99
+ } else if (cmd === 'scaffold') {
100
+ if (!existsSync(TPL)) {
101
+ console.error(`Child template not found at ${TPL}.`);
102
+ process.exit(1);
103
+ }
104
+ const tpl = readFileSync(TPL, 'utf-8');
105
+ let created = 0;
106
+ for (const s of status) {
107
+ if (s.hasClaudeMd) continue;
108
+ const abs = resolve(ROOT, s.module);
109
+ const content = render(tpl, {
110
+ MODULE_NAME: s.module,
111
+ MODULE_PATH: s.module,
112
+ DATE: new Date().toISOString().slice(0, 10),
113
+ MODULE_STACK: moduleStack(abs),
114
+ });
115
+ writeFileSync(resolve(abs, 'CLAUDE.md'), content, 'utf-8');
116
+ created++;
117
+ console.log(`✓ created ${s.module}/CLAUDE.md`);
118
+ }
119
+ console.log(created === 0 ? 'All module roots already have a CLAUDE.md.' : `\n✅ Scaffolded ${created} scoped CLAUDE.md. Fill in the TODOs (or let /claude-md do it).`);
120
+ } else {
121
+ console.error('Usage: claude-md.mjs <find|scaffold> [--json]');
122
+ process.exit(1);
123
+ }