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,78 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * OPTIONAL utility — removes cloud-sync contamination from a git repo.
4
+ *
5
+ * Google Drive / OneDrive desktop clients sometimes seed `desktop.ini` files
6
+ * recursively inside `.git/` (refs, objects, hooks). git then tries to read
7
+ * them as refs and dies with `fatal: bad object`. They also create `.tmp.drive*`
8
+ * working folders at the repo root. This removes both. Idempotent, defensive.
9
+ *
10
+ * Mostly relevant on Windows when the repo lives inside a synced folder.
11
+ *
12
+ * Usage (CLI): node contextkit/tools/scripts/clean-drive.mjs
13
+ * Library: import { cleanCloudContamination } from './clean-drive.mjs'
14
+ */
15
+ import { readdirSync, rmSync, unlinkSync } from 'node:fs';
16
+ import { join, resolve } from 'node:path';
17
+ import { fileURLToPath } from 'node:url';
18
+
19
+ /** @param {string} [root] @returns {{ desktopIniInGit: number, tmpFolders: number }} */
20
+ export function cleanCloudContamination(root = process.cwd()) {
21
+ return {
22
+ desktopIniInGit: removeDesktopIniRecursive(resolve(root, '.git')),
23
+ tmpFolders: removeTmpFolders(root),
24
+ };
25
+ }
26
+
27
+ function removeDesktopIniRecursive(dir) {
28
+ let count = 0;
29
+ let entries;
30
+ try {
31
+ entries = readdirSync(dir, { withFileTypes: true });
32
+ } catch {
33
+ return 0;
34
+ }
35
+ for (const e of entries) {
36
+ const full = join(dir, e.name);
37
+ if (e.isDirectory()) count += removeDesktopIniRecursive(full);
38
+ else if (e.name === 'desktop.ini') {
39
+ try {
40
+ unlinkSync(full);
41
+ count++;
42
+ } catch {
43
+ /* best effort */
44
+ }
45
+ }
46
+ }
47
+ return count;
48
+ }
49
+
50
+ function removeTmpFolders(root) {
51
+ let count = 0;
52
+ let entries;
53
+ try {
54
+ entries = readdirSync(root, { withFileTypes: true });
55
+ } catch {
56
+ return 0;
57
+ }
58
+ for (const e of entries) {
59
+ if (e.isDirectory() && (e.name.startsWith('.tmp.drive') || e.name.startsWith('.tmp.onedrive'))) {
60
+ try {
61
+ rmSync(join(root, e.name), { recursive: true, force: true });
62
+ count++;
63
+ } catch {
64
+ /* Drive may hold a lock */
65
+ }
66
+ }
67
+ }
68
+ return count;
69
+ }
70
+
71
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
72
+ const r = cleanCloudContamination(process.cwd());
73
+ if (r.desktopIniInGit > 0 || r.tmpFolders > 0) {
74
+ console.log(`✓ cleaned ${r.desktopIniInGit} desktop.ini in .git/, ${r.tmpFolders} .tmp.* folder(s)`);
75
+ } else {
76
+ console.log('✓ already clean — no cloud-sync contamination found');
77
+ }
78
+ }
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Inspect or edit `contextkit/config.json` reliably (backs the `/context-config`
4
+ * command). Zero-dependency; uses optional zod validation if installed.
5
+ *
6
+ * Usage:
7
+ * node contextkit/tools/scripts/context-config.mjs show [dotted.path]
8
+ * node contextkit/tools/scripts/context-config.mjs set <dotted.path> <value>
9
+ *
10
+ * Values are coerced to the existing leaf type: numbers, booleans, and JSON
11
+ * arrays/objects parse; everything else stays a string. Writing always
12
+ * pretty-prints with 2-space indent and validates structure first.
13
+ */
14
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
15
+ import { pathsFor } from '../../runtime/config/paths.mjs';
16
+
17
+ const ROOT = process.cwd();
18
+ const CONFIG = pathsFor(ROOT).config;
19
+
20
+ function load() {
21
+ if (!existsSync(CONFIG)) return {};
22
+ return JSON.parse(readFileSync(CONFIG, 'utf-8').replace(/^/, ''));
23
+ }
24
+
25
+ function getAt(obj, path) {
26
+ return path.split('.').reduce((cur, k) => (cur == null ? undefined : cur[k]), obj);
27
+ }
28
+
29
+ function setAt(obj, path, value) {
30
+ const segs = path.split('.');
31
+ let cur = obj;
32
+ for (let i = 0; i < segs.length - 1; i++) {
33
+ if (cur[segs[i]] == null || typeof cur[segs[i]] !== 'object') cur[segs[i]] = {};
34
+ cur = cur[segs[i]];
35
+ }
36
+ cur[segs[segs.length - 1]] = value;
37
+ return obj;
38
+ }
39
+
40
+ function coerce(existing, raw) {
41
+ if (typeof existing === 'number') {
42
+ const n = Number(raw);
43
+ if (Number.isNaN(n)) throw new Error(`expected a number, got "${raw}"`);
44
+ return n;
45
+ }
46
+ if (typeof existing === 'boolean') {
47
+ if (raw === 'true') return true;
48
+ if (raw === 'false') return false;
49
+ throw new Error(`expected true/false, got "${raw}"`);
50
+ }
51
+ if (Array.isArray(existing) || raw.trim().startsWith('[') || raw.trim().startsWith('{')) {
52
+ return JSON.parse(raw);
53
+ }
54
+ return raw;
55
+ }
56
+
57
+ async function maybeValidate(cfg) {
58
+ try {
59
+ const { validateConfig, formatZodError } = await import('../../runtime/config/schema.mjs');
60
+ const res = validateConfig(cfg);
61
+ if (!res.ok) {
62
+ console.error('Refusing to write — schema validation failed:\n' + formatZodError(res.error));
63
+ process.exit(1);
64
+ }
65
+ return true;
66
+ } catch {
67
+ return false; // zod not installed — skip strict validation
68
+ }
69
+ }
70
+
71
+ async function main() {
72
+ const [cmd, ...rest] = process.argv.slice(2);
73
+ const cfg = load();
74
+
75
+ if (!cmd || cmd === 'show') {
76
+ const path = rest[0];
77
+ const value = path ? getAt(cfg, path) : cfg;
78
+ console.log(JSON.stringify(value, null, 2));
79
+ return;
80
+ }
81
+
82
+ if (cmd === 'set') {
83
+ const [path, ...valParts] = rest;
84
+ const raw = valParts.join(' ');
85
+ if (!path || raw === '') {
86
+ console.error('Usage: context-config.mjs set <dotted.path> <value>');
87
+ process.exit(1);
88
+ }
89
+ const existing = getAt(cfg, path);
90
+ let value;
91
+ try {
92
+ value = coerce(existing, raw);
93
+ } catch (err) {
94
+ console.error(`Bad value: ${err.message}`);
95
+ process.exit(1);
96
+ }
97
+ setAt(cfg, path, value);
98
+ const validated = await maybeValidate(cfg);
99
+ writeFileSync(CONFIG, JSON.stringify(cfg, null, 2) + '\n', 'utf-8');
100
+ console.log(`✅ set ${path} = ${JSON.stringify(value)}${validated ? ' (zod-validated)' : ''}`);
101
+ return;
102
+ }
103
+
104
+ console.error(`Unknown command "${cmd}". Use: show [path] | set <path> <value>`);
105
+ process.exit(1);
106
+ }
107
+
108
+ main().catch((err) => {
109
+ console.error('context-config failed:', err.message);
110
+ process.exit(1);
111
+ });
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Shows or changes the ContextDevKit activation level FROM INSIDE a project —
4
+ * no need for the kit repo to be present. Updates `contextkit/config.json`
5
+ * `level` AND recomposes `.claude/settings.json` hook wiring.
6
+ *
7
+ * Usage:
8
+ * node contextkit/tools/scripts/context-level.mjs # show current level
9
+ * node contextkit/tools/scripts/context-level.mjs 3 # move to Level 3
10
+ *
11
+ * After changing level, restart Claude Code so it reloads the hooks.
12
+ */
13
+ import { existsSync } from 'node:fs';
14
+ import { readFile, writeFile, mkdir, chmod, rename } from 'node:fs/promises';
15
+ import { resolve, dirname } from 'node:path';
16
+ import { composeSettings } from '../../runtime/config/settings-compose.mjs';
17
+ import { loadConfigSync } from '../../runtime/config/load.mjs';
18
+ import { LEVEL_LABELS as LABELS, MAX_LEVEL, MIN_LEVEL, isValidLevel } from '../../runtime/config/levels.mjs';
19
+ import { pathsFor } from '../../runtime/config/paths.mjs';
20
+
21
+ const ROOT = process.cwd();
22
+ const CONFIG = pathsFor(ROOT).config;
23
+ const SETTINGS = resolve(ROOT, '.claude/settings.json');
24
+
25
+ async function installGitHooks() {
26
+ const hooksDir = resolve(ROOT, '.git/hooks');
27
+ if (!existsSync(resolve(ROOT, '.git'))) return false;
28
+ await mkdir(hooksDir, { recursive: true });
29
+ const wrappers = {
30
+ 'pre-commit': '#!/bin/sh\nnode contextkit/runtime/git-hooks/pre-commit.mjs\n',
31
+ 'commit-msg': '#!/bin/sh\nnode contextkit/runtime/git-hooks/commit-msg.mjs "$1"\n',
32
+ 'pre-push': '#!/bin/sh\nnode contextkit/runtime/git-hooks/pre-push.mjs\n',
33
+ };
34
+ for (const [name, body] of Object.entries(wrappers)) {
35
+ const p = resolve(hooksDir, name);
36
+ // Back up a pre-existing non-ours hook (husky/lint-staged) before replacing it.
37
+ if (existsSync(p) && !(await readFile(p, 'utf-8').catch(() => '')).includes('contextkit/runtime/git-hooks')) {
38
+ const backup = `${p}.bak`;
39
+ if (!existsSync(backup)) await rename(p, backup);
40
+ }
41
+ await writeFile(p, body, 'utf-8');
42
+ await chmod(p, 0o755).catch(() => {});
43
+ }
44
+ return true;
45
+ }
46
+
47
+ async function main() {
48
+ const current = loadConfigSync(ROOT).level;
49
+ const arg = process.argv[2];
50
+
51
+ if (!arg) {
52
+ console.log(`Current ContextDevKit level: L${current}\n`);
53
+ for (const [k, v] of Object.entries(LABELS)) console.log(`${Number(k) <= current ? '✓' : ' '} ${v}`);
54
+ console.log(`\nChange with: node contextkit/tools/scripts/context-level.mjs <${MIN_LEVEL}-${MAX_LEVEL}>`);
55
+ return;
56
+ }
57
+
58
+ const level = Number(arg);
59
+ if (!isValidLevel(level)) {
60
+ console.error(`Level must be an integer ${MIN_LEVEL}–${MAX_LEVEL}.`);
61
+ process.exit(1);
62
+ }
63
+
64
+ // 1. config.json level
65
+ let cfg = {};
66
+ try {
67
+ cfg = JSON.parse((await readFile(CONFIG, 'utf-8')).replace(/^/, ''));
68
+ } catch {
69
+ /* will create */
70
+ }
71
+ cfg.level = level;
72
+ await mkdir(dirname(CONFIG), { recursive: true });
73
+ await writeFile(CONFIG, JSON.stringify(cfg, null, 2) + '\n', 'utf-8');
74
+
75
+ // 2. settings.json hooks
76
+ let existing = null;
77
+ try {
78
+ existing = JSON.parse((await readFile(SETTINGS, 'utf-8')).replace(/^/, ''));
79
+ } catch {
80
+ /* fresh */
81
+ }
82
+ await mkdir(dirname(SETTINGS), { recursive: true });
83
+ await writeFile(SETTINGS, JSON.stringify(composeSettings(existing, level), null, 2) + '\n', 'utf-8');
84
+
85
+ // 3. git hooks at L >= 3
86
+ if (level >= 3) await installGitHooks();
87
+
88
+ console.log(`✅ ContextDevKit moved from L${current} → L${level}.`);
89
+ if (level >= 4 && !existsSync(resolve(ROOT, '.claude/agents'))) {
90
+ console.log('ℹ️ Level 4 uses sub-agents. Copy them with the kit installer or add your own to .claude/agents/.');
91
+ }
92
+ console.log('↻ Restart Claude Code to load the new hook wiring.');
93
+ }
94
+
95
+ main().catch((err) => {
96
+ console.error('❌ context-level failed:', err);
97
+ process.exit(1);
98
+ });
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Context pack — one bounded "start of work" bundle. [ADR-0027]
4
+ *
5
+ * Collapses the 3–5 sequential reads `/dev-start`, `/state` and `/ship` each do
6
+ * (latest session + CHANGELOG `[Unreleased]` + immutable rules + open tasks +
7
+ * recent ADRs) into a SINGLE script call — fewer tokens AND fewer round-trips.
8
+ * Reuses `digestLatestSession` (the same digest the boot hook shows — single
9
+ * source) and the ADR core. Read-only, zero third-party deps. Every section is
10
+ * best-effort: a missing source is skipped with a note, never an error.
11
+ *
12
+ * Usage:
13
+ * node contextkit/tools/scripts/context-pack.mjs # human bundle
14
+ * node contextkit/tools/scripts/context-pack.mjs --json
15
+ */
16
+ import { readFile, readdir } from 'node:fs/promises';
17
+ import { resolve } from 'node:path';
18
+ import { pathsFor } from '../../runtime/config/paths.mjs';
19
+ import { digestLatestSession, extractUnreleased, readChangelog } from '../../runtime/hooks/boot-context-readers.mjs';
20
+ import { section } from '../../runtime/hooks/md-extract.mjs';
21
+ import { ADR_FILENAME_RE, parseAdr, renderCatalogLine } from './adr-digest-core.mjs';
22
+
23
+ const ROOT = process.cwd();
24
+ const P = pathsFor(ROOT);
25
+ const args = process.argv.slice(2);
26
+ const flag = (name) => args.includes(name);
27
+ const readSafe = (abs) => readFile(abs, 'utf-8').catch(() => null);
28
+
29
+ /** The numbered `⛔ Immutable rules` block from CLAUDE.md (capped), or null. */
30
+ async function immutableRules() {
31
+ const text = await readSafe(resolve(ROOT, 'CLAUDE.md'));
32
+ if (!text) return null;
33
+ const body = section(text.split('\n'), 'immutable rules')
34
+ .filter((l) => l.trim())
35
+ .slice(0, 12)
36
+ .join('\n')
37
+ .trim();
38
+ return body || null;
39
+ }
40
+
41
+ /** The N most recent ADRs as catalog lines, or null. */
42
+ async function recentAdrs(limit = 5) {
43
+ let files = [];
44
+ try {
45
+ files = await readdir(P.decisions);
46
+ } catch {
47
+ return null;
48
+ }
49
+ const names = files.filter((f) => ADR_FILENAME_RE.test(f) && f !== '_TEMPLATE.md').sort().reverse().slice(0, limit);
50
+ const lines = [];
51
+ for (const name of names) {
52
+ const text = await readSafe(resolve(P.decisions, name));
53
+ if (text !== null) lines.push(renderCatalogLine(parseAdr(text, name)));
54
+ }
55
+ return lines.length ? lines.join('\n') : null;
56
+ }
57
+
58
+ const FM = (raw, key) => (new RegExp(`^${key}:\\s*(.+)$`, 'm').exec(raw)?.[1] || '').replace(/^["']|["']$/g, '').trim();
59
+ const PRIORITY_ORDER = { P0: 0, P1: 1, P2: 2, P3: 3 };
60
+
61
+ /** Open backlog tasks (id · priority · title), highest priority first, capped. */
62
+ async function openBacklog(cap = 8) {
63
+ let files = [];
64
+ try {
65
+ files = await readdir(resolve(P.pipeline, 'backlog'));
66
+ } catch {
67
+ return null;
68
+ }
69
+ const tasks = [];
70
+ for (const name of files.filter((f) => /^\d+-.*\.md$/.test(f))) {
71
+ const text = await readSafe(resolve(P.pipeline, 'backlog', name));
72
+ if (text === null) continue;
73
+ tasks.push({ id: FM(text, 'id') || name.split('-')[0], priority: FM(text, 'priority') || 'P3', title: FM(text, 'title') || name });
74
+ }
75
+ if (tasks.length === 0) return null;
76
+ tasks.sort((a, b) => (PRIORITY_ORDER[a.priority] ?? 9) - (PRIORITY_ORDER[b.priority] ?? 9) || String(a.id).localeCompare(String(b.id)));
77
+ return tasks.slice(0, cap).map((t) => `- **${t.priority}** · #${t.id} · ${t.title}`).join('\n');
78
+ }
79
+
80
+ async function build() {
81
+ const [session, unreleased, rules, adrs, backlog] = await Promise.all([
82
+ digestLatestSession(ROOT),
83
+ readChangelog(ROOT).then((c) => extractUnreleased(c)),
84
+ immutableRules(),
85
+ recentAdrs(),
86
+ openBacklog(),
87
+ ]);
88
+ return { session, unreleased, rules, adrs, backlog };
89
+ }
90
+
91
+ function render(pack) {
92
+ const out = ['# 🧭 Context pack — start of work\n'];
93
+ const block = (title, body, emptyNote) => {
94
+ out.push(`## ${title}`);
95
+ out.push('');
96
+ out.push(body && body.trim() ? body.trim() : `_${emptyNote}_`);
97
+ out.push('');
98
+ };
99
+ block('🗓️ Last session', pack.session?.content, 'no registered sessions yet');
100
+ block('📝 Unreleased (CHANGELOG)', pack.unreleased, 'nothing unreleased');
101
+ block('⛔ Immutable rules', pack.rules, 'no CLAUDE.md immutable-rules block found');
102
+ block('🛠️ Open backlog (top by priority)', pack.backlog, 'no open backlog tasks');
103
+ block('🏛️ Recent decisions (ADRs)', pack.adrs, 'no ADRs yet');
104
+ out.push('> Reason over this pack; open a full session log / ADR / ticket only on demand. [ADR-0027]');
105
+ return out.join('\n');
106
+ }
107
+
108
+ async function main() {
109
+ const pack = await build();
110
+ if (flag('--json')) {
111
+ process.stdout.write(JSON.stringify(pack, null, 2) + '\n');
112
+ return;
113
+ }
114
+ console.log(render(pack));
115
+ }
116
+
117
+ main().catch((err) => {
118
+ console.error('❌ context-pack failed:', err?.message ?? err);
119
+ process.exit(1);
120
+ });
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Generic Contract Drift detector (L5/L6).
4
+ *
5
+ * "Contract" = the public surface other code/consumers depend on. You declare
6
+ * which files form it via `contextkit/config.json` → `l5.contractGlobs` (prefixes
7
+ * or exact paths). This extracts exported identifiers from those files and
8
+ * compares against a committed baseline, flagging REMOVALS/RENAMES (the
9
+ * breaking changes) — additions are fine.
10
+ *
11
+ * Usage:
12
+ * node contextkit/tools/scripts/contract-scan.mjs --save # write/refresh baseline
13
+ * node contextkit/tools/scripts/contract-scan.mjs # diff vs baseline (exit 1 on removals)
14
+ * node contextkit/tools/scripts/contract-scan.mjs --json
15
+ *
16
+ * Baseline: contextkit/memory/contract-baseline.json (commit it).
17
+ * Regex-based, language-agnostic-ish (JS/TS export forms): named/declaration
18
+ * exports incl. `declare`/`abstract`/generators, `export default`, namespace
19
+ * re-exports (`export * [as N] from`), and type-only `export type { … }`. Good
20
+ * signal by default; for AST precision install `acorn` (or point
21
+ * `CONTEXT_CONTRACT_PARSER` at a parser) — used **only if importable**, so the
22
+ * zero-dep default holds. Advisory by default; wire into CI to block breaking
23
+ * changes without an intentional version bump.
24
+ */
25
+ import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
26
+ import { join, relative, resolve } from 'node:path';
27
+ import { pathToFileURL } from 'node:url';
28
+ import { loadConfigSync } from '../../runtime/config/load.mjs';
29
+ import { pathsFor } from '../../runtime/config/paths.mjs';
30
+
31
+ const ROOT = process.cwd();
32
+ const BASELINE = pathsFor(ROOT).contractBaseline;
33
+ const GLOBS = loadConfigSync(ROOT).l5?.contractGlobs || [];
34
+
35
+ // Declarations: export [declare] [abstract] [async] <kw> NAME (function may be a generator).
36
+ const EXPORT_RE = /export\s+(?:declare\s+)?(?:abstract\s+)?(?:async\s+)?(?:const|let|var|function\*?|class|interface|type|enum)\s+([A-Za-z_$][\w$]*)/g;
37
+ // Named (re-)exports incl. type-only blocks: export [type] { a, b as c, type D }.
38
+ const NAMED_RE = /export\s*(?:type\s+)?\{([^}]*)\}/g;
39
+ // Default export — tracked as the `default` symbol (removing it is breaking).
40
+ const DEFAULT_RE = /export\s+default\b/;
41
+ // Namespace re-export: export * [as NS] from '...'.
42
+ const STAR_RE = /export\s*\*\s*(?:as\s+([A-Za-z_$][\w$]*)\s+)?from\s*['"]([^'"]+)['"]/g;
43
+
44
+ function matchesGlob(rel) {
45
+ return GLOBS.some((g) => rel === g || rel.startsWith(g));
46
+ }
47
+
48
+ function walk(dir, acc) {
49
+ let entries;
50
+ try {
51
+ entries = readdirSync(dir, { withFileTypes: true });
52
+ } catch {
53
+ return acc;
54
+ }
55
+ for (const e of entries) {
56
+ const abs = join(dir, e.name);
57
+ const rel = relative(ROOT, abs).replaceAll('\\', '/');
58
+ if (rel.startsWith('node_modules') || rel.startsWith('.git')) continue;
59
+ if (e.isDirectory()) walk(abs, acc);
60
+ else if (/\.(ts|tsx|js|jsx|mjs)$/.test(e.name) && matchesGlob(rel)) acc.push(rel);
61
+ }
62
+ return acc;
63
+ }
64
+
65
+ function extractExports(content) {
66
+ const seen = new Set();
67
+ let m;
68
+ EXPORT_RE.lastIndex = 0;
69
+ while ((m = EXPORT_RE.exec(content)) !== null) seen.add(m[1]);
70
+ NAMED_RE.lastIndex = 0;
71
+ while ((m = NAMED_RE.exec(content)) !== null) {
72
+ for (const raw of m[1].split(',')) {
73
+ // Drop an inline `type ` modifier and any `as` alias, keep the exposed name.
74
+ const part = raw.trim().replace(/^type\s+/, '').split(/\s+as\s+/).pop().replace(/[^A-Za-z0-9_$]/g, '');
75
+ if (part) seen.add(part);
76
+ }
77
+ }
78
+ STAR_RE.lastIndex = 0;
79
+ while ((m = STAR_RE.exec(content)) !== null) seen.add(m[1] || `* from ${m[2]}`);
80
+ if (DEFAULT_RE.test(content)) seen.add('default');
81
+ return [...seen];
82
+ }
83
+
84
+ /**
85
+ * Optional AST parser — zero-dep DEFAULT (regex). If a parser module is importable
86
+ * (name from `CONTEXT_CONTRACT_PARSER`, else `acorn`), use it for precise extraction;
87
+ * otherwise fall back to regex. Installing `acorn` upgrades JS precision opt-in —
88
+ * the kit ships nothing, preserving the zero-dep invariant. [→ ADR-0003]
89
+ */
90
+ async function loadParser() {
91
+ const name = process.env.CONTEXT_CONTRACT_PARSER || 'acorn';
92
+ const spec = /[\\/]/.test(name) ? pathToFileURL(resolve(ROOT, name)).href : name;
93
+ try {
94
+ return await import(spec);
95
+ } catch {
96
+ return null;
97
+ }
98
+ }
99
+
100
+ /** Exported names via AST (precise). Returns null when the file can't be parsed. */
101
+ function extractExportsAst(content, parser) {
102
+ let ast;
103
+ try {
104
+ ast = parser.parse(content, { sourceType: 'module', ecmaVersion: 'latest', allowHashBang: true });
105
+ } catch {
106
+ return null;
107
+ }
108
+ const names = new Set();
109
+ for (const node of ast.body || []) {
110
+ if (node.type === 'ExportDefaultDeclaration') {
111
+ names.add('default');
112
+ } else if (node.type === 'ExportAllDeclaration') {
113
+ names.add(node.exported ? node.exported.name : `* from ${node.source?.value ?? ''}`);
114
+ } else if (node.type === 'ExportNamedDeclaration') {
115
+ for (const spec of node.specifiers || []) names.add(spec.exported?.name ?? spec.exported?.value);
116
+ if (node.declaration?.id?.name) names.add(node.declaration.id.name);
117
+ for (const d of node.declaration?.declarations || []) if (d.id?.name) names.add(d.id.name);
118
+ }
119
+ }
120
+ return [...names].filter(Boolean);
121
+ }
122
+
123
+ /** Exported names for one file: AST when a parser is present and the file parses, else regex. */
124
+ function extractFor(content, parser) {
125
+ if (parser) {
126
+ const viaAst = extractExportsAst(content, parser);
127
+ if (viaAst) return viaAst;
128
+ }
129
+ return extractExports(content);
130
+ }
131
+
132
+ function snapshot(parser) {
133
+ const surface = {};
134
+ for (const rel of walk(ROOT, [])) {
135
+ try {
136
+ surface[rel] = extractFor(readFileSync(resolve(ROOT, rel), 'utf-8'), parser).sort();
137
+ } catch {
138
+ /* skip */
139
+ }
140
+ }
141
+ return surface;
142
+ }
143
+
144
+ function diff(base, current) {
145
+ const removals = [];
146
+ for (const [file, names] of Object.entries(base)) {
147
+ const now = new Set(current[file] || []);
148
+ for (const name of names) if (!now.has(name)) removals.push(`${file} → ${name}`);
149
+ }
150
+ return removals;
151
+ }
152
+
153
+ async function main() {
154
+ const args = process.argv.slice(2);
155
+ if (GLOBS.length === 0) {
156
+ console.log('ℹ️ No l5.contractGlobs configured — nothing to track. Set them via /context-config to enable.');
157
+ return;
158
+ }
159
+ const current = snapshot(await loadParser());
160
+ if (args.includes('--save')) {
161
+ writeFileSync(BASELINE, JSON.stringify(current, null, 2) + '\n', 'utf-8');
162
+ const total = Object.values(current).reduce((n, a) => n + a.length, 0);
163
+ console.log(`✅ contract baseline saved — ${Object.keys(current).length} files, ${total} exported symbols.`);
164
+ return;
165
+ }
166
+ if (!existsSync(BASELINE)) {
167
+ console.log('No baseline yet. Run with --save to create contextkit/memory/contract-baseline.json, then commit it.');
168
+ return;
169
+ }
170
+ const removals = diff(JSON.parse(readFileSync(BASELINE, 'utf-8')), current);
171
+ if (args.includes('--json')) {
172
+ process.stdout.write(JSON.stringify({ removals }, null, 2) + '\n');
173
+ } else if (removals.length === 0) {
174
+ console.log('✅ No contract drift — no exported symbols removed/renamed.');
175
+ } else {
176
+ console.error(`🛑 Contract drift — ${removals.length} removed/renamed export(s):`);
177
+ for (const r of removals) console.error(` - ${r}`);
178
+ console.error('\nIf intentional, bump the version (BREAKING CHANGE) and refresh the baseline with --save.');
179
+ }
180
+ process.exit(removals.length > 0 ? 1 : 0);
181
+ }
182
+
183
+ main().catch((err) => {
184
+ console.error('contract-scan failed:', err?.message ?? err);
185
+ process.exit(1);
186
+ });