@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,391 @@
1
+ /**
2
+ * lib/observation-store.mjs — Role-scoped observation store with vector indexing.
3
+ *
4
+ * Stores distilled insights that specialists learn during work:
5
+ * - patterns, anti-patterns, dependency relationships, decisions, insights
6
+ * - each scoped to a role (cx-engineer, cx-architect, etc.)
7
+ * - vector-indexed for semantic search via hashing-bow-v1
8
+ *
9
+ * Storage layout:
10
+ * .cx/observations/index.json — lightweight listing for fast filtering
11
+ * .cx/observations/<id>.json — full observation record
12
+ * .cx/observations/vectors.json — local vector index for semantic search
13
+ */
14
+ import crypto from 'node:crypto';
15
+ import fs from 'node:fs';
16
+ import path from 'node:path';
17
+ import { cosineSimilarity, rankByBm25 } from './storage/embeddings.mjs';
18
+ import { embedSync as embedText } from './storage/embeddings-legacy.mjs';
19
+ import { embedText as embedTextEngine } from './storage/embeddings-engine.mjs';
20
+ import { VectorClient } from './storage/vector-client.mjs';
21
+ import { withFileLockSync } from './storage/file-lock.mjs';
22
+
23
+ const OBS_DIR = '.cx/observations';
24
+ const INDEX_FILE = 'index.json';
25
+ const VECTORS_FILE = 'vectors.json';
26
+ const MAX_INDEX = 1000;
27
+ const MAX_SUMMARY = 500;
28
+ const MAX_CONTENT = 2000;
29
+ const MAX_TAGS = 10;
30
+ // Size guardrail: surfaces in `construct doctor` when the observation tree
31
+ // exceeds this byte cap. Doesn't drop data — the operator decides whether to
32
+ // run `construct memory consolidate` (which prunes archive) or raise the cap.
33
+ const MAX_BYTES = 50 * 1024 * 1024;
34
+
35
+ const VALID_CATEGORIES = new Set([
36
+ 'pattern', 'anti-pattern', 'dependency', 'decision', 'insight', 'session-summary',
37
+ ]);
38
+
39
+ function ensureDir(dir) {
40
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
41
+ }
42
+
43
+ function obsDir(rootDir) {
44
+ return path.join(rootDir, OBS_DIR);
45
+ }
46
+
47
+ function indexPath(rootDir) {
48
+ return path.join(obsDir(rootDir), INDEX_FILE);
49
+ }
50
+
51
+ function vectorsPath(rootDir) {
52
+ return path.join(obsDir(rootDir), VECTORS_FILE);
53
+ }
54
+
55
+ function generateId() {
56
+ const ts = Date.now().toString(36);
57
+ const rand = crypto.randomBytes(4).toString('hex');
58
+ return `obs-${ts}-${rand}`;
59
+ }
60
+
61
+ function clamp(str, max) {
62
+ if (!str || str.length <= max) return str || null;
63
+ return str.slice(0, max - 1) + '\u2026';
64
+ }
65
+
66
+ function readIndex(rootDir) {
67
+ const p = indexPath(rootDir);
68
+ if (!fs.existsSync(p)) return [];
69
+ try {
70
+ return JSON.parse(fs.readFileSync(p, 'utf8'));
71
+ } catch {
72
+ return [];
73
+ }
74
+ }
75
+
76
+ function logCapDrop(rootDir, kind, dropped, total) {
77
+ if (dropped <= 0) return;
78
+ try {
79
+ const logPath = path.join(rootDir, '.cx', 'observation-cap-warnings.jsonl');
80
+ fs.mkdirSync(path.dirname(logPath), { recursive: true });
81
+ const entry = JSON.stringify({
82
+ ts: new Date().toISOString(),
83
+ kind,
84
+ dropped,
85
+ total,
86
+ cap: MAX_INDEX,
87
+ remedy: 'run `construct memory consolidate` or raise --max-stored',
88
+ });
89
+ fs.appendFileSync(logPath, entry + '\n');
90
+ } catch { /* cap warnings are best-effort */ }
91
+ }
92
+
93
+ function writeIndex(rootDir, entries) {
94
+ ensureDir(obsDir(rootDir));
95
+ const trimmed = entries.slice(0, MAX_INDEX);
96
+ if (entries.length > MAX_INDEX) {
97
+ logCapDrop(rootDir, 'observation-index', entries.length - MAX_INDEX, entries.length);
98
+ }
99
+ fs.writeFileSync(indexPath(rootDir), JSON.stringify(trimmed, null, 2) + '\n');
100
+ }
101
+
102
+ function readVectors(rootDir) {
103
+ const p = vectorsPath(rootDir);
104
+ if (!fs.existsSync(p)) return [];
105
+ try {
106
+ return JSON.parse(fs.readFileSync(p, 'utf8'));
107
+ } catch {
108
+ return [];
109
+ }
110
+ }
111
+
112
+ function writeVectors(rootDir, records) {
113
+ ensureDir(obsDir(rootDir));
114
+ if (records.length > MAX_INDEX) {
115
+ logCapDrop(rootDir, 'observation-vectors', records.length - MAX_INDEX, records.length);
116
+ }
117
+ fs.writeFileSync(vectorsPath(rootDir), JSON.stringify(records, null, 2) + '\n');
118
+ }
119
+
120
+ /**
121
+ * Add a new observation and vectorize it for semantic search.
122
+ * Uses SQL + neural embeddings when available, falls back to local JSON + hashing.
123
+ */
124
+ export async function addObservation(rootDir, {
125
+ role = 'unknown',
126
+ category = 'insight',
127
+ summary = '',
128
+ content = '',
129
+ tags = [],
130
+ project = null,
131
+ confidence = 0.8,
132
+ source = null,
133
+ gitSha = null,
134
+ } = {}) {
135
+ const id = generateId();
136
+ const now = new Date().toISOString();
137
+
138
+ const effectiveCategory = VALID_CATEGORIES.has(category) ? category : 'insight';
139
+ const clampedSummary = clamp(String(summary), MAX_SUMMARY);
140
+ const clampedContent = clamp(String(content), MAX_CONTENT);
141
+ const clampedTags = (Array.isArray(tags) ? tags : []).slice(0, MAX_TAGS).map(String);
142
+
143
+ const record = {
144
+ id,
145
+ role: String(role),
146
+ category: effectiveCategory,
147
+ summary: clampedSummary,
148
+ content: clampedContent,
149
+ tags: clampedTags,
150
+ project: project ? String(project) : null,
151
+ confidence: Math.max(0, Math.min(1, Number(confidence) || 0.8)),
152
+ source: source || null,
153
+ gitSha: gitSha ? String(gitSha).slice(0, 40) : null,
154
+ createdAt: now,
155
+ updatedAt: now,
156
+ };
157
+
158
+ // Lock the per-store index so concurrent writers (CLI + hooks) cannot
159
+ // corrupt the index/vectors JSON pair. The observation JSON file is
160
+ // unique-per-id and doesn't need the lock; the shared structures do.
161
+ ensureDir(obsDir(rootDir));
162
+ fs.writeFileSync(
163
+ path.join(obsDir(rootDir), `${id}.json`),
164
+ JSON.stringify(record, null, 2) + '\n',
165
+ );
166
+
167
+ withFileLockSync(indexPath(rootDir), () => {
168
+ const index = readIndex(rootDir);
169
+ index.unshift({
170
+ id,
171
+ role: record.role,
172
+ category: record.category,
173
+ summary: record.summary,
174
+ project: record.project,
175
+ createdAt: now,
176
+ });
177
+ writeIndex(rootDir, index);
178
+ });
179
+
180
+ // Try SQL storage with neural embeddings
181
+ try {
182
+ const client = new VectorClient();
183
+ if (await client.isHealthy() && await client.isPgvectorEnabled()) {
184
+ const searchText = [record.summary, record.content, ...record.tags].filter(Boolean).join(' ');
185
+ const { embedding } = await embedTextEngine(searchText);
186
+ await client.storeObservation({
187
+ ...record,
188
+ embedding,
189
+ });
190
+ await client.close();
191
+ return record;
192
+ }
193
+ await client.close();
194
+ } catch {
195
+ // Fall through to local vector storage
196
+ }
197
+
198
+ // Fallback: local JSON vector index with hashing-bow-v1.
199
+ const searchText = [record.summary, record.content, ...record.tags].filter(Boolean).join(' ');
200
+ const embedding = embedText(searchText);
201
+ withFileLockSync(vectorsPath(rootDir), () => {
202
+ const vectors = readVectors(rootDir);
203
+ vectors.push({ id, embedding, role: record.role, category: record.category, project: record.project });
204
+ writeVectors(rootDir, vectors.slice(-MAX_INDEX));
205
+ });
206
+
207
+ return record;
208
+ }
209
+
210
+ /**
211
+ * Search observations using SQL + neural embeddings when available,
212
+ * falling back to hybrid cosine + BM25 on local JSON.
213
+ */
214
+ export async function searchObservations(rootDir, query, {
215
+ role = null,
216
+ category = null,
217
+ project = null,
218
+ limit = 10,
219
+ } = {}) {
220
+ if (!query) return [];
221
+
222
+ // Try SQL + neural embeddings first
223
+ try {
224
+ const client = new VectorClient();
225
+ if (await client.isHealthy() && await client.isPgvectorEnabled()) {
226
+ const { embedding } = await embedTextEngine(String(query));
227
+ const results = await client.searchObservations({
228
+ project: project || rootDir.split('/').pop(),
229
+ queryEmbedding: embedding,
230
+ limit,
231
+ minSimilarity: 0.1,
232
+ role,
233
+ category,
234
+ });
235
+ await client.close();
236
+ return results.map((r) => ({
237
+ id: r.id,
238
+ role: r.role,
239
+ category: r.category,
240
+ summary: r.summary,
241
+ content: r.content,
242
+ tags: r.tags || [],
243
+ confidence: r.confidence,
244
+ source: r.source,
245
+ createdAt: r.created_at,
246
+ score: r.similarity,
247
+ }));
248
+ }
249
+ await client.close();
250
+ } catch {
251
+ // Fall through to local JSON search
252
+ }
253
+
254
+ // Fallback: local JSON + hashing-bow-v1
255
+ const queryEmbedding = embedText(String(query));
256
+ let vectors = readVectors(rootDir);
257
+
258
+ if (role) vectors = vectors.filter((v) => v.role === role);
259
+ if (category) vectors = vectors.filter((v) => v.category === category);
260
+ if (project) vectors = vectors.filter((v) => v.project === project);
261
+
262
+ // Cosine pass
263
+ const cosineScored = vectors
264
+ .map((v) => ({
265
+ id: v.id,
266
+ score: cosineSimilarity(queryEmbedding, v.embedding || []),
267
+ }))
268
+ .filter(({ score }) => score > 0.05);
269
+
270
+ // Load full records for BM25 pass
271
+ const candidateIds = new Set(cosineScored.map((v) => v.id));
272
+ const recentIndex = readIndex(rootDir);
273
+ const filtered = role ? recentIndex.filter((e) => e.role === role) : recentIndex;
274
+ const filteredCat = category ? filtered.filter((e) => e.category === category) : filtered;
275
+ const filteredProj = project ? filteredCat.filter((e) => e.project === project) : filteredCat;
276
+ for (const entry of filteredProj.slice(0, Math.max(limit * 3, 30))) candidateIds.add(entry.id);
277
+
278
+ const candidateRecords = [...candidateIds]
279
+ .map((id) => getObservation(rootDir, id))
280
+ .filter(Boolean)
281
+ .map((r) => ({ ...r, text: [r.summary, r.content, ...(r.tags || [])].filter(Boolean).join(' ') }));
282
+
283
+ const bm25Scored = rankByBm25(candidateRecords, query, { limit: limit * 2 });
284
+
285
+ // Merge: highest score wins per id
286
+ const scoreMap = new Map();
287
+ for (const { id, score } of cosineScored) {
288
+ scoreMap.set(id, score);
289
+ }
290
+ for (const item of bm25Scored) {
291
+ const prev = scoreMap.get(item.id) || 0;
292
+ const bm25Max = bm25Scored[0]?.score || 1;
293
+ const normalized = Math.min(item.score / bm25Max, 1);
294
+ scoreMap.set(item.id, Math.max(prev, normalized));
295
+ }
296
+
297
+ return [...scoreMap.entries()]
298
+ .sort((a, b) => b[1] - a[1])
299
+ .slice(0, limit)
300
+ .map(([id, score]) => {
301
+ const record = getObservation(rootDir, id);
302
+ return record ? { ...record, score } : null;
303
+ })
304
+ .filter(Boolean);
305
+ }
306
+
307
+ /**
308
+ * List observations from the index with optional filters.
309
+ */
310
+ export function listObservations(rootDir, {
311
+ role = null,
312
+ category = null,
313
+ project = null,
314
+ limit = 20,
315
+ } = {}) {
316
+ let entries = readIndex(rootDir);
317
+ if (role) entries = entries.filter((e) => e.role === role);
318
+ if (category) entries = entries.filter((e) => e.category === category);
319
+ if (project) entries = entries.filter((e) => e.project === project);
320
+ return entries.slice(0, limit);
321
+ }
322
+
323
+ /**
324
+ * Load a full observation record by ID.
325
+ */
326
+ export function getObservation(rootDir, id) {
327
+ const filePath = path.join(obsDir(rootDir), `${id}.json`);
328
+ if (!fs.existsSync(filePath)) return null;
329
+ try {
330
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
331
+ } catch {
332
+ return null;
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Delete an observation by id.
338
+ */
339
+ export function deleteObservation(rootDir, id) {
340
+ const filePath = path.join(obsDir(rootDir), `${id}.json`);
341
+ if (fs.existsSync(filePath)) fs.rmSync(filePath, { force: true });
342
+
343
+ withFileLockSync(indexPath(rootDir), () => {
344
+ const index = readIndex(rootDir);
345
+ const filtered = index.filter((e) => e.id !== id);
346
+ if (filtered.length !== index.length) writeIndex(rootDir, filtered);
347
+ });
348
+
349
+ withFileLockSync(vectorsPath(rootDir), () => {
350
+ const vectors = readVectors(rootDir);
351
+ const filteredVec = vectors.filter((v) => v.id !== id);
352
+ if (filteredVec.length !== vectors.length) writeVectors(rootDir, filteredVec);
353
+ });
354
+
355
+ return true;
356
+ }
357
+
358
+ /**
359
+ * Count observations, optionally filtered.
360
+ */
361
+ export function countObservations(rootDir, { role = null, project = null } = {}) {
362
+ let entries = readIndex(rootDir);
363
+ if (role) entries = entries.filter((e) => e.role === role);
364
+ if (project) entries = entries.filter((e) => e.project === project);
365
+ return entries.length;
366
+ }
367
+
368
+ // On-disk byte size of the observation tree (live + archive) at rootDir.
369
+ // Backs the `Observation size` doctor surface so size pressure shows up
370
+ // independently of count-based caps.
371
+ export function getObservationsSize(rootDir) {
372
+ const root = obsDir(rootDir);
373
+ if (!fs.existsSync(root)) return 0;
374
+ let total = 0;
375
+ const walk = (dir) => {
376
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
377
+ const full = path.join(dir, entry.name);
378
+ if (entry.isDirectory()) walk(full);
379
+ else if (entry.isFile()) {
380
+ try { total += fs.statSync(full).size; } catch { /* best effort */ }
381
+ }
382
+ }
383
+ };
384
+ walk(root);
385
+ return total;
386
+ }
387
+
388
+ export function checkObservationsSize(rootDir, { cap = MAX_BYTES } = {}) {
389
+ const size = getObservationsSize(rootDir);
390
+ return { size, cap, ok: size <= cap };
391
+ }