@geraldmaron/construct 1.0.0 → 1.0.1

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 (471) hide show
  1. package/.env.example +10 -7
  2. package/LICENSE +201 -21
  3. package/README.md +132 -257
  4. package/agents/contracts.json +387 -0
  5. package/agents/prompts/cx-accessibility.md +8 -0
  6. package/agents/prompts/cx-ai-engineer.md +92 -0
  7. package/agents/prompts/cx-architect.md +10 -2
  8. package/agents/prompts/cx-business-strategist.md +13 -0
  9. package/agents/prompts/cx-data-analyst.md +80 -0
  10. package/agents/prompts/cx-data-engineer.md +4 -0
  11. package/agents/prompts/cx-debugger.md +8 -0
  12. package/agents/prompts/cx-designer.md +19 -0
  13. package/agents/prompts/cx-devil-advocate.md +4 -0
  14. package/agents/prompts/cx-docs-keeper.md +128 -0
  15. package/agents/prompts/cx-engineer.md +13 -0
  16. package/agents/prompts/cx-evaluator.md +4 -0
  17. package/agents/prompts/cx-explorer.md +12 -0
  18. package/agents/prompts/cx-legal-compliance.md +13 -0
  19. package/agents/prompts/cx-operations.md +13 -0
  20. package/agents/prompts/cx-orchestrator.md +107 -4
  21. package/agents/prompts/cx-platform-engineer.md +75 -0
  22. package/agents/prompts/cx-product-manager.md +8 -0
  23. package/agents/prompts/cx-qa.md +100 -0
  24. package/agents/prompts/cx-rd-lead.md +13 -0
  25. package/agents/prompts/cx-release-manager.md +8 -0
  26. package/agents/prompts/cx-researcher.md +21 -1
  27. package/agents/prompts/cx-reviewer.md +8 -0
  28. package/agents/prompts/cx-security.md +104 -0
  29. package/agents/prompts/cx-sre.md +104 -0
  30. package/agents/prompts/cx-test-automation.md +4 -0
  31. package/agents/prompts/cx-trace-reviewer.md +7 -3
  32. package/agents/prompts/cx-ux-researcher.md +4 -0
  33. package/agents/registry.json +365 -90
  34. package/agents/role-manifests.json +217 -0
  35. package/bin/construct +3345 -141
  36. package/bin/construct-postinstall.mjs +87 -0
  37. package/commands/build/feature.md +6 -6
  38. package/commands/plan/feature.md +6 -5
  39. package/commands/plan/requirements.md +1 -1
  40. package/commands/ship/status.md +1 -1
  41. package/commands/work/drive.md +3 -3
  42. package/commands/work/optimize-prompts.md +3 -3
  43. package/db/{migrations → schema}/001_init.sql +2 -0
  44. package/db/schema/002_pgvector.sql +182 -0
  45. package/db/schema/003_intake.sql +47 -0
  46. package/examples/README.md +85 -0
  47. package/examples/internal/roles/architect/bad/clever-plan-without-contracts.md +30 -0
  48. package/examples/internal/roles/architect/golden/explicit-tradeoff-before-plan.md +23 -0
  49. package/examples/internal/roles/engineer/bad/speculative-abstraction.md +30 -0
  50. package/examples/internal/roles/engineer/golden/read-before-write.md +28 -0
  51. package/examples/internal/roles/orchestrator/bad/everything-becomes-multi-agent.md +29 -0
  52. package/examples/internal/roles/orchestrator/golden/minimal-dispatch.md +22 -0
  53. package/examples/internal/roles/qa/bad/coverage-theater.md +30 -0
  54. package/examples/internal/roles/qa/golden/regression-gate.md +23 -0
  55. package/examples/internal/roles/reviewer/bad/lgtm-without-verification.md +29 -0
  56. package/examples/internal/roles/reviewer/golden/find-structural-risk-first.md +28 -0
  57. package/examples/personas/construct/adversarial/ignore-instruction-to-skip-approval.md +22 -0
  58. package/examples/personas/construct/bad/commit-without-approval.md +30 -0
  59. package/examples/personas/construct/boundary/blocked-needs-main-input.md +29 -0
  60. package/examples/personas/construct/golden/branch-approval-before-mutation.md +36 -0
  61. package/examples/personas/construct/golden/focused-direct-answer.md +28 -0
  62. package/examples/provider-plugin/README.md +34 -0
  63. package/examples/provider-plugin/index.mjs +74 -0
  64. package/examples/provider-plugin/package.json +15 -0
  65. package/examples/seed-observations/README.md +38 -0
  66. package/examples/seed-observations/anti-patterns.md +44 -0
  67. package/examples/seed-observations/decisions.md +36 -0
  68. package/examples/seed-observations/patterns.md +42 -0
  69. package/lib/agent-contracts-enforce.mjs +158 -0
  70. package/lib/agent-contracts.mjs +231 -0
  71. package/lib/agents/postconditions.mjs +126 -0
  72. package/lib/agents/schema.mjs +124 -0
  73. package/lib/artifact-capture.mjs +183 -0
  74. package/lib/audit-trail.mjs +149 -0
  75. package/lib/auto-docs.mjs +245 -65
  76. package/lib/beads/auto-close.mjs +126 -0
  77. package/lib/beads/drift.mjs +171 -0
  78. package/lib/beads-automation.mjs +542 -0
  79. package/lib/beads-client.mjs +518 -0
  80. package/lib/beads-lock.mjs +377 -0
  81. package/lib/beads-optimistic.mjs +365 -0
  82. package/lib/bootstrap/built-ins.mjs +136 -0
  83. package/lib/bootstrap/lazy-install.mjs +161 -0
  84. package/lib/bootstrap/resources.mjs +120 -0
  85. package/lib/bootstrap.mjs +105 -0
  86. package/lib/cache-governor.js +213 -0
  87. package/lib/cache-strategy-anthropic.js +62 -0
  88. package/lib/cache-strategy-google.js +79 -0
  89. package/lib/cache-strategy-none.js +30 -0
  90. package/lib/cache-strategy-openai.js +48 -0
  91. package/lib/cache-strategy.js +91 -0
  92. package/lib/claude-allow.mjs +149 -0
  93. package/lib/cli-commands.mjs +413 -258
  94. package/lib/codex-config.mjs +3 -1
  95. package/lib/comment-lint.mjs +55 -6
  96. package/lib/completions.mjs +21 -6
  97. package/lib/config/alias.mjs +56 -0
  98. package/lib/config/project-config.mjs +335 -0
  99. package/lib/config/schema.mjs +159 -0
  100. package/lib/context-router.mjs +308 -0
  101. package/lib/cost-ledger.mjs +177 -0
  102. package/lib/cost.mjs +171 -10
  103. package/lib/dashboard-static.mjs +158 -0
  104. package/lib/deployment-mode.mjs +86 -0
  105. package/lib/deprecate.mjs +49 -0
  106. package/lib/dispatch-batch.js +183 -0
  107. package/lib/distill.mjs +21 -8
  108. package/lib/doc-stamp.mjs +164 -0
  109. package/lib/doc-verify.mjs +119 -0
  110. package/lib/docs-routing.mjs +89 -0
  111. package/lib/docs-verify.mjs +417 -0
  112. package/lib/doctor/audit.mjs +71 -0
  113. package/lib/doctor/cli.mjs +99 -0
  114. package/lib/doctor/escalate.mjs +29 -0
  115. package/lib/doctor/index.mjs +140 -0
  116. package/lib/doctor/report.mjs +170 -0
  117. package/lib/doctor/watchers/bd-watch.mjs +117 -0
  118. package/lib/doctor/watchers/cost.mjs +130 -0
  119. package/lib/doctor/watchers/disk.mjs +122 -0
  120. package/lib/doctor/watchers/handoffs.mjs +33 -0
  121. package/lib/doctor/watchers/process-pressure.mjs +60 -0
  122. package/lib/doctor/watchers/service-health.mjs +188 -0
  123. package/lib/document-extract.mjs +288 -0
  124. package/lib/document-ingest.mjs +230 -0
  125. package/lib/drop.mjs +282 -0
  126. package/lib/embed/approval-queue.mjs +176 -0
  127. package/lib/embed/artifact.mjs +349 -0
  128. package/lib/embed/authority-guard.mjs +155 -0
  129. package/lib/embed/cli.mjs +408 -0
  130. package/lib/embed/config.mjs +355 -0
  131. package/lib/embed/conflict-detection.mjs +264 -0
  132. package/lib/embed/customer-profiles.mjs +480 -0
  133. package/lib/embed/daemon.mjs +1309 -0
  134. package/lib/embed/demand-fetch.mjs +449 -0
  135. package/lib/embed/docs-lifecycle.mjs +349 -0
  136. package/lib/embed/inbox-live-watcher.mjs +119 -0
  137. package/lib/embed/inbox.mjs +343 -0
  138. package/lib/embed/intake-metrics.mjs +190 -0
  139. package/lib/embed/jobs/vector-sync.mjs +198 -0
  140. package/lib/embed/notifications.mjs +75 -0
  141. package/lib/embed/output.mjs +79 -0
  142. package/lib/embed/providers/github.mjs +295 -0
  143. package/lib/embed/providers/jira.mjs +192 -0
  144. package/lib/embed/providers/linear.mjs +186 -0
  145. package/lib/embed/providers/registry.mjs +115 -0
  146. package/lib/embed/providers/slack.mjs +203 -0
  147. package/lib/embed/recommendation-store.mjs +378 -0
  148. package/lib/embed/roadmap.mjs +374 -0
  149. package/lib/embed/role-framing.mjs +110 -0
  150. package/lib/embed/scheduler.mjs +99 -0
  151. package/lib/embed/semantic.mjs +325 -0
  152. package/lib/embed/snapshot.mjs +191 -0
  153. package/lib/embed/supervision.mjs +235 -0
  154. package/lib/embed/target-resolver.mjs +186 -0
  155. package/lib/embed/worker.mjs +62 -0
  156. package/lib/embed/workspaces.mjs +297 -0
  157. package/lib/engine/chunker-headings.mjs +110 -0
  158. package/lib/engine/compressor-heuristic.mjs +100 -0
  159. package/lib/engine/consolidate.mjs +287 -0
  160. package/lib/engine/contracts.mjs +126 -0
  161. package/lib/engine/defaults.mjs +129 -0
  162. package/lib/engine/eval-retrieval.mjs +148 -0
  163. package/lib/engine/fuser-rrf.mjs +62 -0
  164. package/lib/engine/index.mjs +37 -0
  165. package/lib/engine/registry.mjs +146 -0
  166. package/lib/engine/reranker-mmr.mjs +90 -0
  167. package/lib/engine/tokens.mjs +77 -0
  168. package/lib/entity-store.mjs +280 -0
  169. package/lib/env-config.mjs +69 -4
  170. package/lib/evals/retrieval-bench.mjs +159 -0
  171. package/lib/evaluator-optimizer.mjs +317 -0
  172. package/lib/features.mjs +159 -29
  173. package/lib/gates-audit.mjs +236 -0
  174. package/lib/git-hooks/prepare-commit-msg +58 -0
  175. package/lib/handoffs/cleanup.mjs +159 -0
  176. package/lib/handoffs/contract.mjs +162 -0
  177. package/lib/handoffs/inventory.mjs +120 -0
  178. package/lib/headhunt.mjs +28 -47
  179. package/lib/health-check.mjs +399 -0
  180. package/lib/hook-health.mjs +442 -0
  181. package/lib/hooks/_lib/log.mjs +82 -0
  182. package/lib/hooks/adaptive-lint.mjs +26 -2
  183. package/lib/hooks/agent-tracker.mjs +171 -14
  184. package/lib/hooks/audit-reads.mjs +109 -0
  185. package/lib/hooks/audit-trail.mjs +153 -0
  186. package/lib/hooks/bash-output-logger.mjs +71 -0
  187. package/lib/hooks/block-no-verify.mjs +41 -0
  188. package/lib/hooks/ci-status-check.mjs +82 -0
  189. package/lib/hooks/comment-lint.mjs +29 -7
  190. package/lib/hooks/config-protection.mjs +39 -18
  191. package/lib/hooks/context-watch.mjs +137 -0
  192. package/lib/hooks/context-window-recovery.mjs +4 -20
  193. package/lib/hooks/dep-audit.mjs +16 -0
  194. package/lib/hooks/doc-coupling-check.mjs +80 -0
  195. package/lib/hooks/edit-accumulator.mjs +3 -0
  196. package/lib/hooks/edit-error-recovery.mjs +3 -0
  197. package/lib/hooks/edit-guard.mjs +45 -4
  198. package/lib/hooks/env-check.mjs +3 -0
  199. package/lib/hooks/guard-bash.mjs +58 -1
  200. package/lib/hooks/mcp-audit.mjs +18 -20
  201. package/lib/hooks/mcp-health-check.mjs +36 -0
  202. package/lib/hooks/model-fallback.mjs +42 -56
  203. package/lib/hooks/policy-engine.mjs +209 -0
  204. package/lib/hooks/post-merge-docs-check.mjs +63 -0
  205. package/lib/hooks/pre-compact.mjs +4 -24
  206. package/lib/hooks/pre-push-gate.mjs +200 -37
  207. package/lib/hooks/proactive-activation.mjs +284 -0
  208. package/lib/hooks/read-tracker.mjs +27 -4
  209. package/lib/hooks/readme-age-check.mjs +77 -0
  210. package/lib/hooks/registry-sync.mjs +12 -5
  211. package/lib/hooks/scan-secrets.mjs +50 -7
  212. package/lib/hooks/session-optimize.mjs +311 -0
  213. package/lib/hooks/session-start.mjs +287 -28
  214. package/lib/hooks/stop-notify.mjs +236 -96
  215. package/lib/hooks/stop-typecheck.mjs +13 -1
  216. package/lib/hooks/test-watch.mjs +68 -0
  217. package/lib/host-capabilities.mjs +31 -10
  218. package/lib/init-docs.mjs +731 -291
  219. package/lib/init-unified.mjs +1116 -0
  220. package/lib/init-update.mjs +168 -0
  221. package/lib/init.mjs +107 -0
  222. package/lib/install/first-invocation.mjs +119 -0
  223. package/lib/install/stage-project.mjs +69 -0
  224. package/lib/intake/classify.mjs +278 -0
  225. package/lib/intake/feedback.mjs +273 -0
  226. package/lib/intake/filesystem-queue.mjs +158 -0
  227. package/lib/intake/intake-config.mjs +132 -0
  228. package/lib/intake/postgres-queue.mjs +198 -0
  229. package/lib/intake/prepare.mjs +139 -0
  230. package/lib/intake/queue.mjs +83 -0
  231. package/lib/intake/session-prelude.mjs +50 -0
  232. package/lib/integrations/intake-integrations.mjs +740 -0
  233. package/lib/intent-classifier.mjs +253 -0
  234. package/lib/knowledge/layout.mjs +72 -0
  235. package/lib/knowledge/rag.mjs +331 -0
  236. package/lib/knowledge/search.mjs +315 -0
  237. package/lib/knowledge/trends.mjs +261 -0
  238. package/lib/logger.mjs +85 -0
  239. package/lib/mcp/broker.mjs +124 -0
  240. package/lib/mcp/server.mjs +554 -854
  241. package/lib/mcp/tools/document.mjs +132 -0
  242. package/lib/mcp/tools/memory.mjs +209 -0
  243. package/lib/mcp/tools/project.mjs +349 -0
  244. package/lib/mcp/tools/skills.mjs +409 -0
  245. package/lib/mcp/tools/storage.mjs +60 -0
  246. package/lib/mcp/tools/telemetry.mjs +315 -0
  247. package/lib/mcp/tools/workflow.mjs +112 -0
  248. package/lib/mcp-catalog.json +54 -3
  249. package/lib/mcp-manager.mjs +96 -48
  250. package/lib/mcp-platform-config.mjs +22 -22
  251. package/lib/memory-stats.mjs +121 -0
  252. package/lib/mode-commands.mjs +124 -0
  253. package/lib/model-free-selector.mjs +184 -0
  254. package/lib/model-pricing.mjs +152 -0
  255. package/lib/model-registry.mjs +226 -0
  256. package/lib/model-router.mjs +362 -386
  257. package/lib/observation-store.mjs +391 -0
  258. package/lib/ollama-manager.mjs +428 -0
  259. package/lib/opencode-config.mjs +13 -3
  260. package/lib/opencode-runtime-plugin.mjs +70 -38
  261. package/lib/opencode-telemetry.mjs +64 -6
  262. package/lib/orchestration-policy.mjs +509 -6
  263. package/lib/overrides/resolver.mjs +207 -0
  264. package/lib/parity.mjs +147 -0
  265. package/lib/paths.mjs +33 -0
  266. package/lib/performance/generate.mjs +212 -0
  267. package/lib/plugin-registry.mjs +268 -0
  268. package/lib/policy/engine.mjs +130 -0
  269. package/lib/policy/unified-gates.mjs +96 -0
  270. package/lib/project-detection.mjs +129 -0
  271. package/lib/project-init-shared.mjs +272 -0
  272. package/lib/project-profile.mjs +447 -0
  273. package/lib/prompt-composer.js +434 -0
  274. package/lib/prompt-metadata.mjs +1 -1
  275. package/lib/provider-capabilities-anthropic.js +44 -0
  276. package/lib/provider-capabilities-deepseek.js +37 -0
  277. package/lib/provider-capabilities-generic.js +26 -0
  278. package/lib/provider-capabilities-google.js +47 -0
  279. package/lib/provider-capabilities-openai.js +45 -0
  280. package/lib/provider-capabilities.js +142 -0
  281. package/lib/providers/atlassian-confluence/index.mjs +103 -0
  282. package/lib/providers/atlassian-jira/index.mjs +100 -0
  283. package/lib/providers/auth-manager.mjs +126 -0
  284. package/lib/providers/circuit-breaker.mjs +124 -0
  285. package/lib/providers/contract.mjs +93 -0
  286. package/lib/providers/github/index.mjs +126 -0
  287. package/lib/providers/registry.mjs +184 -0
  288. package/lib/providers/salesforce/index.mjs +100 -0
  289. package/lib/providers/slack/index.mjs +80 -0
  290. package/lib/reflect.mjs +137 -0
  291. package/lib/research-lint.mjs +164 -0
  292. package/lib/resources/budget.mjs +259 -0
  293. package/lib/role-preload.mjs +21 -6
  294. package/lib/roles/approval-surface.mjs +54 -0
  295. package/lib/roles/cli.mjs +118 -0
  296. package/lib/roles/event-bus.mjs +79 -0
  297. package/lib/roles/fence.mjs +84 -0
  298. package/lib/roles/gateway.mjs +260 -0
  299. package/lib/roles/hook-emit.mjs +37 -0
  300. package/lib/roles/manifest.mjs +48 -0
  301. package/lib/roles/router.mjs +27 -0
  302. package/lib/runtime-pressure.mjs +360 -0
  303. package/lib/schema-artifact.mjs +134 -0
  304. package/lib/schema-infer.mjs +551 -0
  305. package/lib/server/auth.mjs +168 -0
  306. package/lib/server/chat.mjs +336 -0
  307. package/lib/server/cors.mjs +77 -0
  308. package/lib/server/csrf.mjs +91 -0
  309. package/lib/server/index.mjs +1927 -78
  310. package/lib/server/insights.mjs +765 -0
  311. package/lib/server/rate-limit.mjs +91 -0
  312. package/lib/server/static/assets/index-ab25c707.js +70 -0
  313. package/lib/server/static/assets/index-f0c80a2b.css +1 -0
  314. package/lib/server/static/index.html +12 -817
  315. package/lib/server/telemetry-login.mjs +108 -0
  316. package/lib/server/webhook.mjs +510 -0
  317. package/lib/service-manager.mjs +522 -58
  318. package/lib/services/pattern-promotion-service.mjs +167 -0
  319. package/lib/services/telemetry-backend.mjs +178 -0
  320. package/lib/session-store.mjs +374 -0
  321. package/lib/setup-prompts.mjs +96 -0
  322. package/lib/setup.mjs +525 -38
  323. package/lib/skills-apply.mjs +280 -0
  324. package/lib/skills-scope.mjs +118 -0
  325. package/lib/status.mjs +261 -70
  326. package/lib/storage/admin.mjs +355 -0
  327. package/lib/storage/backend.mjs +2 -1
  328. package/lib/storage/backup.mjs +347 -0
  329. package/lib/storage/embeddings-engine.mjs +133 -0
  330. package/lib/storage/embeddings-legacy.mjs +85 -0
  331. package/lib/storage/embeddings-local.mjs +108 -0
  332. package/lib/storage/embeddings-ollama.mjs +78 -0
  333. package/lib/storage/embeddings-openai.mjs +85 -0
  334. package/lib/storage/embeddings.mjs +92 -33
  335. package/lib/storage/file-lock.mjs +130 -0
  336. package/lib/storage/fusion.mjs +95 -0
  337. package/lib/storage/hybrid-query.mjs +34 -27
  338. package/lib/storage/migrations.mjs +187 -0
  339. package/lib/storage/postgres-backup.mjs +124 -0
  340. package/lib/storage/sql-store.mjs +5 -15
  341. package/lib/storage/state-source.mjs +12 -13
  342. package/lib/storage/store-version.mjs +115 -0
  343. package/lib/storage/sync.mjs +144 -35
  344. package/lib/storage/unified-storage.mjs +550 -0
  345. package/lib/storage/vector-client.mjs +286 -0
  346. package/lib/storage/vector-store.mjs +71 -30
  347. package/lib/task-graph/generate.mjs +135 -0
  348. package/lib/task-graph/schema.mjs +81 -0
  349. package/lib/task-graph/store.mjs +71 -0
  350. package/lib/telemetry/backends/local.mjs +62 -0
  351. package/lib/telemetry/backends/{langfuse.mjs → remote.mjs} +27 -14
  352. package/lib/telemetry/backfill.mjs +180 -0
  353. package/lib/telemetry/eval-datasets.mjs +203 -0
  354. package/lib/telemetry/{langfuse-ingest.mjs → ingest.mjs} +26 -20
  355. package/lib/telemetry/intent-verifications.mjs +86 -0
  356. package/lib/telemetry/llm-judge.mjs +350 -0
  357. package/lib/telemetry/model-pricing-catalog.mjs +557 -0
  358. package/lib/telemetry/setup.mjs +151 -0
  359. package/lib/telemetry/skill-calls.mjs +78 -0
  360. package/lib/telemetry/team-rollup.mjs +4 -4
  361. package/lib/token-engine.js +117 -0
  362. package/lib/token-estimator-anthropic.js +15 -0
  363. package/lib/token-estimator-deepseek.js +13 -0
  364. package/lib/token-estimator-default.js +13 -0
  365. package/lib/token-estimator-google.js +13 -0
  366. package/lib/token-estimator-openai.js +13 -0
  367. package/lib/toolkit-env.mjs +1 -1
  368. package/lib/tty-prompts.mjs +211 -0
  369. package/lib/uninstall/uninstall.mjs +423 -0
  370. package/lib/update.mjs +115 -0
  371. package/lib/upgrade.mjs +141 -0
  372. package/lib/validator.mjs +51 -2
  373. package/lib/validators/skills.mjs +142 -0
  374. package/lib/wireframe.mjs +422 -0
  375. package/lib/worker/entrypoint.mjs +241 -0
  376. package/lib/worker/evidence.mjs +107 -0
  377. package/lib/worker/run.mjs +154 -0
  378. package/lib/worker/trace.mjs +182 -0
  379. package/lib/workflow-state.mjs +14 -18
  380. package/package.json +21 -8
  381. package/personas/construct.md +53 -51
  382. package/platforms/claude/CLAUDE.md +1 -1
  383. package/platforms/claude/settings.template.json +115 -68
  384. package/platforms/opencode/config.template.json +3 -7
  385. package/rules/common/agents.md +11 -38
  386. package/rules/common/beads-hygiene.md +75 -0
  387. package/rules/common/code-review.md +11 -100
  388. package/rules/common/coding-style.md +5 -3
  389. package/rules/common/comments.md +30 -111
  390. package/rules/common/commit-approval.md +53 -0
  391. package/rules/common/cx-agent-routing.md +1 -0
  392. package/rules/common/development-workflow.md +23 -41
  393. package/rules/common/doc-ownership.md +54 -0
  394. package/rules/common/efficiency.md +57 -0
  395. package/rules/common/framing.md +76 -0
  396. package/rules/common/git-workflow.md +2 -4
  397. package/rules/common/patterns.md +3 -7
  398. package/rules/common/performance.md +15 -31
  399. package/rules/common/release-gates.md +69 -0
  400. package/rules/common/research.md +107 -0
  401. package/rules/common/security.md +6 -6
  402. package/rules/common/skill-composition.md +67 -0
  403. package/rules/common/testing.md +15 -27
  404. package/rules/golang/hooks.md +0 -4
  405. package/rules/policy/bootstrap.yaml +11 -0
  406. package/rules/policy/drive.yaml +8 -0
  407. package/rules/policy/task.yaml +9 -0
  408. package/rules/policy/workflow.yaml +9 -0
  409. package/rules/python/hooks.md +0 -4
  410. package/rules/swift/hooks.md +0 -4
  411. package/rules/typescript/hooks.md +0 -4
  412. package/rules/web/hooks.md +0 -2
  413. package/{sync-agents.mjs → scripts/sync-agents.mjs} +306 -61
  414. package/skills/ai/prompt-optimizer.md +7 -7
  415. package/skills/compliance/ai-disclosure.md +58 -0
  416. package/skills/compliance/data-privacy.md +46 -0
  417. package/skills/compliance/license-audit.md +40 -0
  418. package/skills/compliance/regulatory-review.md +61 -0
  419. package/skills/docs/document-ingest-workflow.md +52 -0
  420. package/skills/docs/evidence-ingest-workflow.md +9 -0
  421. package/skills/docs/init-docs.md +51 -18
  422. package/skills/docs/product-intelligence-workflow.md +12 -0
  423. package/skills/docs/product-signal-workflow.md +4 -0
  424. package/skills/docs/research-workflow.md +23 -8
  425. package/skills/docs/runbook-workflow.md +1 -1
  426. package/skills/operating/orchestration-reference.md +151 -0
  427. package/skills/roles/architect.md +5 -0
  428. package/skills/roles/engineer.md +32 -0
  429. package/skills/roles/operator.md +12 -0
  430. package/skills/roles/reviewer.md +23 -0
  431. package/skills/routing.md +1 -1
  432. package/templates/devcontainer/Dockerfile.devcontainer +38 -0
  433. package/templates/devcontainer/devcontainer.json +31 -0
  434. package/templates/distribution/bootstrap.ps1 +142 -0
  435. package/templates/distribution/bootstrap.sh +196 -0
  436. package/templates/distribution/run.mjs +187 -0
  437. package/templates/docs/adr.md +44 -8
  438. package/templates/docs/changelog-entry.md +43 -0
  439. package/templates/docs/construct_guide.md +149 -0
  440. package/templates/docs/evidence-brief.md +2 -2
  441. package/templates/docs/meta-prd.md +140 -19
  442. package/templates/docs/onboarding.md +57 -0
  443. package/templates/docs/prd.md +159 -15
  444. package/templates/docs/research-brief.md +4 -4
  445. package/templates/homebrew/construct.rb +67 -0
  446. package/langfuse/docker-compose.yml +0 -82
  447. package/lib/eval-harness.mjs +0 -59
  448. package/lib/hooks/bootstrap-guard.mjs +0 -90
  449. package/lib/hooks/console-warn.mjs +0 -43
  450. package/lib/hooks/continuation-enforcer.mjs +0 -72
  451. package/lib/hooks/drive-guard.mjs +0 -89
  452. package/lib/hooks/mcp-task-scope.mjs +0 -47
  453. package/lib/hooks/task-completed-guard.mjs +0 -43
  454. package/lib/hooks/teammate-idle-guard.mjs +0 -54
  455. package/lib/hooks/workflow-guard.mjs +0 -62
  456. package/lib/prompt-composer.mjs +0 -196
  457. package/lib/review.mjs +0 -429
  458. package/lib/server/static/app.js +0 -841
  459. package/lib/telemetry/langfuse-model-sync.mjs +0 -270
  460. package/rules/common/hooks.md +0 -35
  461. package/rules/zh/README.md +0 -113
  462. package/rules/zh/agents.md +0 -55
  463. package/rules/zh/code-review.md +0 -129
  464. package/rules/zh/coding-style.md +0 -53
  465. package/rules/zh/development-workflow.md +0 -49
  466. package/rules/zh/git-workflow.md +0 -29
  467. package/rules/zh/hooks.md +0 -35
  468. package/rules/zh/patterns.md +0 -36
  469. package/rules/zh/performance.md +0 -60
  470. package/rules/zh/security.md +0 -34
  471. package/rules/zh/testing.md +0 -34
@@ -0,0 +1,442 @@
1
+ /**
2
+ * lib/hooks/health-monitor.mjs — Comprehensive hook health verification.
3
+ *
4
+ * Addresses silent hook failures by:
5
+ * 1. Runtime verification that hooks actually execute
6
+ * 2. Health check endpoints for each hook type
7
+ * 3. Automatic recovery and alerting
8
+ * 4. Integration with construct doctor for actionable diagnostics
9
+ *
10
+ * @p95ms 100
11
+ * @maxBlockingScope none
12
+ */
13
+
14
+ import fs from 'node:fs';
15
+ import path from 'node:path';
16
+ import { spawnSync } from 'node:child_process';
17
+ import { homedir } from 'node:os';
18
+
19
+ const CX_DIR = path.join(homedir(), '.cx');
20
+ const HOOK_HEALTH_DIR = path.join(CX_DIR, 'hook-health');
21
+ const HEALTH_CHECK_INTERVAL_MS = 60 * 1000; // 1 minute
22
+
23
+ // Ensure health tracking directory exists
24
+ try {
25
+ fs.mkdirSync(HOOK_HEALTH_DIR, { recursive: true });
26
+ } catch { /* ignore */ }
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Hook registration and tracking
30
+ // ---------------------------------------------------------------------------
31
+
32
+ const REGISTERED_HOOKS = {
33
+ 'session-start': {
34
+ name: 'Session Start',
35
+ description: 'Injects context at the beginning of each session',
36
+ critical: true, // Session is unusable without this
37
+ checkMethod: 'ping',
38
+ },
39
+ 'stop': {
40
+ name: 'Session Stop',
41
+ description: 'Persists session summary and cost data',
42
+ critical: true, // Cost tracking breaks without this
43
+ checkMethod: 'file',
44
+ evidenceFile: path.join(CX_DIR, 'session-memory-stats.json'),
45
+ },
46
+ 'audit-trail': {
47
+ name: 'Audit Trail',
48
+ description: 'Records all mutations for compliance',
49
+ critical: false,
50
+ checkMethod: 'file',
51
+ evidenceFile: path.join(CX_DIR, 'audit-trail.jsonl'),
52
+ },
53
+ 'comment-lint': {
54
+ name: 'Comment Lint',
55
+ description: 'Enforces comment standards on edits',
56
+ critical: false,
57
+ checkMethod: 'ping',
58
+ },
59
+ 'policy-engine': {
60
+ name: 'Policy Engine',
61
+ description: 'Enforces gates at session end',
62
+ critical: true,
63
+ checkMethod: 'ping',
64
+ },
65
+ };
66
+
67
+ // ---------------------------------------------------------------------------
68
+ // Health state management
69
+ // ---------------------------------------------------------------------------
70
+
71
+ function getHealthFilePath(hookName) {
72
+ return path.join(HOOK_HEALTH_DIR, `${hookName}.json`);
73
+ }
74
+
75
+ function readHookHealth(hookName) {
76
+ try {
77
+ const file = getHealthFilePath(hookName);
78
+ if (fs.existsSync(file)) {
79
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
80
+ }
81
+ } catch { /* ignore */ }
82
+
83
+ return {
84
+ status: 'unknown',
85
+ lastCheck: null,
86
+ lastSuccess: null,
87
+ consecutiveFailures: 0,
88
+ totalChecks: 0,
89
+ totalFailures: 0,
90
+ };
91
+ }
92
+
93
+ function writeHookHealth(hookName, health) {
94
+ try {
95
+ fs.writeFileSync(getHealthFilePath(hookName), JSON.stringify(health, null, 2));
96
+ } catch { /* ignore */ }
97
+ }
98
+
99
+ // ---------------------------------------------------------------------------
100
+ // Health check implementations
101
+ // ---------------------------------------------------------------------------
102
+
103
+ /**
104
+ * "Ping" check: Actually executes the hook with test input
105
+ * and verifies it completes successfully.
106
+ */
107
+ async function checkPingHook(hookName, hookPath) {
108
+ try {
109
+ // Create test input based on hook type
110
+ let testInput = {};
111
+
112
+ if (hookName === 'session-start') {
113
+ testInput = {
114
+ cwd: process.cwd(),
115
+ session_id: `health-check-${Date.now()}`,
116
+ platform: 'health-check',
117
+ };
118
+ } else if (hookName === 'audit-trail') {
119
+ testInput = {
120
+ tool_name: 'Write',
121
+ tool_input: { file_path: '/dev/null', content: 'health-check' },
122
+ cwd: process.cwd(),
123
+ session_id: `health-check-${Date.now()}`,
124
+ };
125
+ } else if (hookName === 'policy-engine') {
126
+ testInput = {
127
+ type: 'Stop',
128
+ cwd: process.cwd(),
129
+ session_id: `health-check-${Date.now()}`,
130
+ };
131
+ }
132
+
133
+ // Execute hook with test input
134
+ const result = spawnSync(
135
+ process.execPath,
136
+ [hookPath],
137
+ {
138
+ input: JSON.stringify(testInput),
139
+ encoding: 'utf8',
140
+ timeout: 5000,
141
+ env: process.env,
142
+ }
143
+ );
144
+
145
+ return {
146
+ healthy: result.status === 0,
147
+ exitCode: result.status,
148
+ stderr: result.stderr?.slice(0, 500),
149
+ stdout: result.stdout?.slice(0, 200),
150
+ };
151
+ } catch (error) {
152
+ return {
153
+ healthy: false,
154
+ error: error.message,
155
+ };
156
+ }
157
+ }
158
+
159
+ /**
160
+ * "File" check: Verifies evidence file is being updated.
161
+ */
162
+ async function checkFileHook(hookName, evidenceFile) {
163
+ try {
164
+ if (!fs.existsSync(evidenceFile)) {
165
+ return {
166
+ healthy: false,
167
+ error: `Evidence file not found: ${evidenceFile}`,
168
+ recommendation: 'Hook may not be running or file path is wrong',
169
+ };
170
+ }
171
+
172
+ const stats = fs.statSync(evidenceFile);
173
+ const ageMs = Date.now() - stats.mtimeMs;
174
+ const ageMinutes = Math.floor(ageMs / 60000);
175
+
176
+ // Consider unhealthy if no updates in 30 minutes for active sessions
177
+ const staleThreshold = 30 * 60 * 1000; // 30 minutes
178
+
179
+ return {
180
+ healthy: ageMs < staleThreshold,
181
+ lastUpdate: stats.mtime.toISOString(),
182
+ ageMinutes,
183
+ stale: ageMs >= staleThreshold,
184
+ error: ageMs >= staleThreshold
185
+ ? `No updates in ${ageMinutes} minutes`
186
+ : undefined,
187
+ };
188
+ } catch (error) {
189
+ return {
190
+ healthy: false,
191
+ error: error.message,
192
+ };
193
+ }
194
+ }
195
+
196
+ // ---------------------------------------------------------------------------
197
+ // Main health check orchestrator
198
+ // ---------------------------------------------------------------------------
199
+
200
+ /**
201
+ * Run a comprehensive health check on all registered hooks.
202
+ */
203
+ export async function checkAllHooksHealth(options = {}) {
204
+ const { verbose = false, fix = false } = options;
205
+ const results = [];
206
+
207
+ for (const [hookId, hookConfig] of Object.entries(REGISTERED_HOOKS)) {
208
+ const hookPath = findHookPath(hookId);
209
+ const previousHealth = readHookHealth(hookId);
210
+
211
+ const checkResult = await checkSingleHook(hookId, hookConfig, hookPath);
212
+
213
+ // Update health state
214
+ const newHealth = updateHealthState(previousHealth, checkResult);
215
+ writeHookHealth(hookId, newHealth);
216
+
217
+ results.push({
218
+ id: hookId,
219
+ name: hookConfig.name,
220
+ critical: hookConfig.critical,
221
+ ...checkResult,
222
+ health: newHealth,
223
+ });
224
+
225
+ if (verbose) {
226
+ const status = checkResult.healthy ? '✓' : (hookConfig.critical ? '✗' : '⚠');
227
+ console.error(`${status} ${hookConfig.name}: ${checkResult.healthy ? 'healthy' : (checkResult.error || 'unhealthy')}`);
228
+ }
229
+
230
+ // Attempt auto-fix if requested and hook is unhealthy
231
+ if (fix && !checkResult.healthy) {
232
+ const fixResult = await attemptHookFix(hookId, hookPath, checkResult);
233
+ if (fixResult.fixed) {
234
+ checkResult.fixed = true;
235
+ checkResult.fixMethod = fixResult.method;
236
+ }
237
+ }
238
+ }
239
+
240
+ return {
241
+ healthy: results.every(r => r.healthy || !r.critical),
242
+ criticalHealthy: results.filter(r => r.critical).every(r => r.healthy),
243
+ results,
244
+ timestamp: new Date().toISOString(),
245
+ };
246
+ }
247
+
248
+ async function checkSingleHook(hookId, config, hookPath) {
249
+ // First check: does the hook file exist?
250
+ if (!hookPath) {
251
+ return {
252
+ healthy: false,
253
+ error: `Hook file not found for ${hookId}`,
254
+ recommendation: `Run \`construct sync\` to regenerate hook configuration`,
255
+ };
256
+ }
257
+
258
+ // Run the appropriate check method
259
+ if (config.checkMethod === 'ping') {
260
+ return checkPingHook(hookId, hookPath);
261
+ } else if (config.checkMethod === 'file') {
262
+ return checkFileHook(hookId, config.evidenceFile);
263
+ }
264
+
265
+ return { healthy: false, error: 'Unknown check method' };
266
+ }
267
+
268
+ function findHookPath(hookName) {
269
+ // Check standard locations
270
+ const possiblePaths = [
271
+ path.join(homedir(), '.construct', 'lib', 'hooks', `${hookName}.mjs`),
272
+ path.join(process.cwd(), '.construct', 'lib', 'hooks', `${hookName}.mjs`),
273
+ path.join(homedir(), '.npm-global', 'lib', 'node_modules', '@geraldmaron/construct', 'lib', 'hooks', `${hookName}.mjs`),
274
+ // Try to find from PATH
275
+ ];
276
+
277
+ for (const p of possiblePaths) {
278
+ if (fs.existsSync(p)) return p;
279
+ }
280
+
281
+ // Try to resolve from construct binary location
282
+ try {
283
+ const which = spawnSync('which', ['construct'], { encoding: 'utf8' });
284
+ if (which.status === 0) {
285
+ const constructPath = which.stdout.trim();
286
+ const constructDir = path.dirname(constructPath);
287
+ const fromBin = path.join(constructDir, '..', 'lib', 'node_modules', '@geraldmaron/construct', 'lib', 'hooks', `${hookName}.mjs`);
288
+ if (fs.existsSync(fromBin)) return fromBin;
289
+ }
290
+ } catch { /* ignore */ }
291
+
292
+ return null;
293
+ }
294
+
295
+ function updateHealthState(previous, checkResult) {
296
+ const now = new Date().toISOString();
297
+
298
+ return {
299
+ status: checkResult.healthy ? 'healthy' : 'unhealthy',
300
+ lastCheck: now,
301
+ lastSuccess: checkResult.healthy ? now : previous.lastSuccess,
302
+ consecutiveFailures: checkResult.healthy ? 0 : previous.consecutiveFailures + 1,
303
+ totalChecks: previous.totalChecks + 1,
304
+ totalFailures: checkResult.healthy ? previous.totalFailures : previous.totalFailures + 1,
305
+ };
306
+ }
307
+
308
+ // ---------------------------------------------------------------------------
309
+ // Auto-fix attempts
310
+ // ---------------------------------------------------------------------------
311
+
312
+ async function attemptHookFix(hookId, hookPath, checkResult) {
313
+ const fixes = [];
314
+
315
+ // Fix 1: Missing hook file - regenerate
316
+ if (!hookPath) {
317
+ try {
318
+ const result = spawnSync('construct', ['sync'], { encoding: 'utf8', timeout: 30000 });
319
+ if (result.status === 0) {
320
+ fixes.push('regenerated hooks via construct sync');
321
+ // Re-check if hook now exists
322
+ const newPath = findHookPath(hookId);
323
+ if (newPath) {
324
+ return { fixed: true, method: 'regenerated hooks' };
325
+ }
326
+ }
327
+ } catch { /* ignore */ }
328
+ }
329
+
330
+ // Fix 2: Hook file exists but not executable - fix permissions
331
+ if (hookPath && checkResult.error?.includes('permission')) {
332
+ try {
333
+ fs.chmodSync(hookPath, 0o755);
334
+ fixes.push('fixed permissions');
335
+ return { fixed: true, method: 'fixed permissions' };
336
+ } catch { /* ignore */ }
337
+ }
338
+
339
+ // Fix 3: Settings.json misconfiguration
340
+ if (checkResult.error?.includes('not configured')) {
341
+ try {
342
+ const result = spawnSync('construct', ['sync'], { encoding: 'utf8', timeout: 30000 });
343
+ if (result.status === 0) {
344
+ return { fixed: true, method: 'resynced settings' };
345
+ }
346
+ } catch { /* ignore */ }
347
+ }
348
+
349
+ return { fixed: false, attempted: fixes };
350
+ }
351
+
352
+ // ---------------------------------------------------------------------------
353
+ // Public API for integration with doctor and CLI
354
+ // ---------------------------------------------------------------------------
355
+
356
+ /**
357
+ * Get a quick health summary for construct doctor.
358
+ */
359
+ export async function getHooksHealthSummary() {
360
+ const health = await checkAllHooksHealth({ verbose: false });
361
+
362
+ const criticalFailed = health.results.filter(r => r.critical && !r.healthy);
363
+ const nonCriticalFailed = health.results.filter(r => !r.critical && !r.healthy);
364
+
365
+ return {
366
+ overall: health.healthy ? 'healthy' : (health.criticalHealthy ? 'degraded' : 'critical'),
367
+ criticalFailed: criticalFailed.map(r => r.id),
368
+ nonCriticalFailed: nonCriticalFailed.map(r => r.id),
369
+ details: health.results.map(r => ({
370
+ name: r.name,
371
+ status: r.healthy ? 'healthy' : 'unhealthy',
372
+ critical: r.critical,
373
+ error: r.error,
374
+ })),
375
+ };
376
+ }
377
+
378
+ /**
379
+ * Verify a specific hook is working.
380
+ */
381
+ export async function verifyHook(hookName) {
382
+ const config = REGISTERED_HOOKS[hookName];
383
+ if (!config) {
384
+ return { exists: false, error: `Unknown hook: ${hookName}` };
385
+ }
386
+
387
+ const hookPath = findHookPath(hookName);
388
+ const result = await checkSingleHook(hookName, config, hookPath);
389
+
390
+ return {
391
+ exists: !!hookPath,
392
+ path: hookPath,
393
+ ...result,
394
+ };
395
+ }
396
+
397
+ /**
398
+ * Start continuous health monitoring (for daemon use).
399
+ */
400
+ export function startHealthMonitoring(options = {}) {
401
+ const { intervalMs = HEALTH_CHECK_INTERVAL_MS, onUnhealthy } = options;
402
+
403
+ const interval = setInterval(async () => {
404
+ const health = await checkAllHooksHealth({ verbose: false });
405
+
406
+ if (!health.healthy && onUnhealthy) {
407
+ onUnhealthy(health);
408
+ }
409
+ }, intervalMs);
410
+
411
+ return {
412
+ stop: () => clearInterval(interval),
413
+ };
414
+ }
415
+
416
+ /**
417
+ * Format health status for CLI output.
418
+ */
419
+ export function formatHealthStatus(health) {
420
+ let output = '';
421
+
422
+ output += `Hook Health: ${health.overall.toUpperCase()}\n`;
423
+ output += '=' .repeat(50) + '\n\n';
424
+
425
+ for (const detail of health.details) {
426
+ const icon = detail.status === 'healthy' ? '✓' : (detail.critical ? '✗' : '⚠');
427
+ output += `${icon} ${detail.name}\n`;
428
+ if (detail.error) {
429
+ output += ` Error: ${detail.error}\n`;
430
+ }
431
+ output += '\n';
432
+ }
433
+
434
+ if (health.criticalFailed.length > 0) {
435
+ output += `\nCRITICAL ISSUES:\n`;
436
+ output += `The following hooks are critical and not functioning:\n`;
437
+ output += health.criticalFailed.join(', ') + '\n';
438
+ output += `\nRecommendation: Run \`construct doctor hooks --fix\` to attempt repair\n`;
439
+ }
440
+
441
+ return output;
442
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * lib/hooks/_lib/log.mjs — Centralized hook failure telemetry.
3
+ *
4
+ * Hook scripts (lib/hooks/*.mjs) catch and swallow most stdin/parse errors so
5
+ * a regression in the hook harness or a malformed payload does not block the
6
+ * user. The trade-off is silent failure: until now, hook regressions were
7
+ * invisible. This helper writes a single JSONL entry per failure to
8
+ * ~/.cx/hook-failures.jsonl so `construct doctor` can surface the top
9
+ * failing hooks without changing the fail-open behavior of any individual
10
+ * hook.
11
+ *
12
+ * Usage from a hook:
13
+ * import { logHookFailure } from './_lib/log.mjs';
14
+ * try { ... } catch (err) { logHookFailure({ hook: 'session-start', err }); }
15
+ *
16
+ * Failure-of-the-failure is also tolerated — if writing the log itself fails,
17
+ * the helper swallows the error so the hook proceeds normally.
18
+ *
19
+ * The log file is rotated at LOG_MAX_BYTES to keep disk usage bounded.
20
+ */
21
+
22
+ import { appendFileSync, statSync, renameSync, mkdirSync } from 'node:fs';
23
+ import { join } from 'node:path';
24
+ import { homedir } from 'node:os';
25
+
26
+ const LOG_DIR = join(homedir(), '.cx');
27
+ const LOG_PATH = join(LOG_DIR, 'hook-failures.jsonl');
28
+ const LOG_ROTATED_PATH = join(LOG_DIR, 'hook-failures.1.jsonl');
29
+ const LOG_MAX_BYTES = 1_048_576; // 1 MiB
30
+
31
+ function rotateIfNeeded() {
32
+ try {
33
+ const size = statSync(LOG_PATH).size;
34
+ if (size >= LOG_MAX_BYTES) {
35
+ renameSync(LOG_PATH, LOG_ROTATED_PATH);
36
+ }
37
+ } catch { /* file does not exist yet, nothing to rotate */ }
38
+ }
39
+
40
+ /**
41
+ * Append a structured failure record. Never throws.
42
+ *
43
+ * @param {object} args
44
+ * @param {string} args.hook — hook id, e.g. 'session-start' or 'pre-push-gate'
45
+ * @param {Error|string} args.err — caught error or message
46
+ * @param {object} [args.input] — relevant subset of the hook input (avoid large payloads)
47
+ * @param {string} [args.phase] — hook lifecycle phase: 'parse' | 'execute' | 'cleanup'
48
+ */
49
+ export function logHookFailure({ hook, err, input, phase = 'execute' } = {}) {
50
+ try {
51
+ mkdirSync(LOG_DIR, { recursive: true });
52
+ rotateIfNeeded();
53
+ const entry = {
54
+ ts: new Date().toISOString(),
55
+ hook: String(hook || 'unknown'),
56
+ phase,
57
+ message: err instanceof Error ? err.message : String(err),
58
+ stack: err instanceof Error ? err.stack?.split('\n').slice(0, 4).join('\n') : null,
59
+ input: input ? safeShallow(input) : null,
60
+ pid: process.pid,
61
+ };
62
+ appendFileSync(LOG_PATH, JSON.stringify(entry) + '\n');
63
+ } catch { /* hook telemetry is best-effort and must never block the hook itself */ }
64
+ }
65
+
66
+ function safeShallow(obj) {
67
+ try {
68
+ const out = {};
69
+ for (const [k, v] of Object.entries(obj)) {
70
+ if (v == null || typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {
71
+ out[k] = typeof v === 'string' && v.length > 200 ? v.slice(0, 200) + '…' : v;
72
+ } else if (Array.isArray(v)) {
73
+ out[k] = `[${v.length} items]`;
74
+ } else {
75
+ out[k] = '[object]';
76
+ }
77
+ }
78
+ return out;
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
@@ -1,8 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * lib/hooks/adaptive-lint.mjs — Adaptive lint escalation hook — tracks repeated violations and escalates severity.
3
+ * lib/hooks/adaptive-lint.mjs — PostToolUse auto-formatter and debug-log detector.
4
4
  *
5
- * Runs as PostToolUse after Edit/Write. Reads the lint history log and escalates warnings to errors for files with repeated violations. Writes updated history to ~/.cx/lint-history.json.
5
+ * Runs after Edit/Write. Attempts the best available formatter for the edited file
6
+ * (eslint --fix, prettier, ruff, gofmt, rustfmt). Also flags console.log/debug
7
+ * statements as lint warnings. Non-blocking — exits 0 regardless of outcome.
8
+ *
9
+ * @p95ms 800
10
+ * @maxBlockingScope none (PostToolUse, non-blocking)
6
11
  */
7
12
  import { execSync } from 'child_process';
8
13
  import { existsSync, readFileSync } from 'fs';
@@ -109,4 +114,23 @@ if (!acted && ext === '.rs') {
109
114
  }
110
115
  }
111
116
 
117
+ // Flag debug logging left in source — non-blocking, informational only.
118
+ if (['.js', '.mjs', '.cjs', '.ts', '.tsx', '.jsx'].includes(ext)) {
119
+ try {
120
+ const src = readFileSync(filePath, 'utf8');
121
+ const lines = src.split('\n');
122
+ const hits = lines
123
+ .map((line, i) => ({ line: line.trimStart(), n: i + 1 }))
124
+ .filter(({ line }) =>
125
+ /\bconsole\.(log|debug)\s*\(/.test(line) && !line.startsWith('//')
126
+ );
127
+ if (hits.length > 0) {
128
+ const locs = hits.map(({ n }) => ` line ${n}`).join('\n');
129
+ process.stderr.write(
130
+ `[adaptive-lint] console.log/debug in ${filePath}:\n${locs}\n`
131
+ );
132
+ }
133
+ } catch { /* unreadable — skip */ }
134
+ }
135
+
112
136
  process.exit(0);