@geraldmaron/construct 1.0.0 → 1.0.2

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 +9 -6
  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 +1000 -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 +523 -36
  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,480 @@
1
+ /**
2
+ * lib/embed/customer-profiles.mjs — Global customer profile store.
3
+ *
4
+ * Maintains durable customer/account memory in ~/.cx/product-intel/customer-profiles/.
5
+ * Profiles are additive — history is preserved unless explicitly deleted.
6
+ * Used by intake triage to link signals to customers and detect account-level patterns.
7
+ *
8
+ * Storage:
9
+ * ~/.cx/product-intel/customer-profiles/<customer-id>.md
10
+ * ~/.cx/product-intel/customer-profiles/index.json — quick lookup by name/email
11
+ *
12
+ * Each profile follows templates/docs/customer-profile.md schema.
13
+ */
14
+
15
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, statSync, appendFileSync } from 'node:fs';
16
+ import { join } from 'node:path';
17
+ import { randomUUID } from 'node:crypto';
18
+ import { homedir } from 'node:os';
19
+
20
+ const PROFILES_DIR = join(homedir(), '.cx', 'product-intel', 'customer-profiles');
21
+ const INDEX_FILE = join(PROFILES_DIR, 'index.json');
22
+
23
+ /**
24
+ * Ensure profile directory exists.
25
+ */
26
+ function ensureDir() {
27
+ if (!existsSync(PROFILES_DIR)) {
28
+ mkdirSync(PROFILES_DIR, { recursive: true });
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Read the index file (quick lookup by name/email/domain).
34
+ * @returns {{ [key: string]: { id: string, name: string, status: string, updatedAt: string } }}
35
+ */
36
+ function readIndex() {
37
+ if (!existsSync(INDEX_FILE)) return {};
38
+ try {
39
+ return JSON.parse(readFileSync(INDEX_FILE, 'utf8'));
40
+ } catch (err) {
41
+ process.stderr.write('[customer-profiles.mjs] readIndex: ' + (err?.message ?? String(err)) + '\n');
42
+ return {};
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Write the index file.
48
+ * @param {object} index
49
+ */
50
+ function writeIndex(index) {
51
+ ensureDir();
52
+ writeFileSync(INDEX_FILE, JSON.stringify(index, null, 2) + '\n');
53
+ }
54
+
55
+ /**
56
+ * Generate a unique customer ID.
57
+ * @returns {string}
58
+ */
59
+ function generateCustomerId() {
60
+ return `cust-${randomUUID().slice(0, 8)}`;
61
+ }
62
+
63
+ /**
64
+ * Slugify a name for filename use.
65
+ * @param {string} name
66
+ * @returns {string}
67
+ */
68
+ function slugify(name) {
69
+ return name
70
+ .toLowerCase()
71
+ .replace(/[^a-z0-9]+/g, '-')
72
+ .replace(/^-+|-+$/g, '')
73
+ .slice(0, 60);
74
+ }
75
+
76
+ /**
77
+ * Create a new customer profile.
78
+ *
79
+ * @param {object} opts
80
+ * @param {string} opts.name - Customer/account name (required)
81
+ * @param {string} [opts.owner] - PM or owner name
82
+ * @param {string} [opts.workspace] - Workspace assignment
83
+ * @param {string} [opts.status] - 'active' (default), 'inactive', 'archived'
84
+ * @param {string} [opts.domain] - Company domain for email matching
85
+ * @param {string[]} [opts.aliases] - Alternative names/ spellings
86
+ * @returns {{ id: string, path: string, profile: CustomerProfile }}
87
+ */
88
+ export function createCustomerProfile({ name, owner, workspace, status = 'active', domain, aliases = [] }) {
89
+ if (!name || typeof name !== 'string' || !name.trim()) {
90
+ throw new Error('name is required');
91
+ }
92
+
93
+ ensureDir();
94
+
95
+ const id = generateCustomerId();
96
+ const slug = slugify(name);
97
+ const filename = `${slug}-${id}.md`;
98
+ const filePath = join(PROFILES_DIR, filename);
99
+
100
+ const date = new Date().toISOString().slice(0, 10);
101
+ const profile = {
102
+ id,
103
+ name: name.trim(),
104
+ owner: owner || 'TBD',
105
+ workspace: workspace || 'default',
106
+ status,
107
+ domain,
108
+ aliases,
109
+ createdAt: new Date().toISOString(),
110
+ updatedAt: new Date().toISOString(),
111
+ };
112
+
113
+ const content = `---
114
+ cx_doc_id: ${randomUUID()}
115
+ created_at: ${profile.createdAt}
116
+ updated_at: ${profile.updatedAt}
117
+ generator: construct/customer-profiles
118
+ customer_id: ${id}
119
+ ---
120
+
121
+ # Customer Profile: ${profile.name}
122
+
123
+ - **Updated**: ${date}
124
+ - **Owner**: ${profile.owner}
125
+ - **Status**: ${profile.status}
126
+ - **Workspace**: ${profile.workspace}
127
+ ${domain ? `- **Domain**: ${domain}` : ''}
128
+ ${aliases.length ? `- **Aliases**: ${aliases.join(', ')}` : ''}
129
+
130
+ <!--
131
+ Profiles are additive memory. Do not delete history unless the user explicitly
132
+ asks for cleanup. Keep facts tied to source evidence.
133
+ -->
134
+
135
+ ## Snapshot
136
+ <!-- One paragraph. Who this customer is, what they use the product for, and why they matter to the current work. -->
137
+
138
+ TODO
139
+
140
+ ## Environment
141
+ <!-- Stack, tools, integrations, scale, constraints, and relevant operating model. -->
142
+
143
+ TODO
144
+
145
+ ## Active pain points
146
+ <!-- Current friction, with source links or dates. Distinguish severe blockers from preferences. -->
147
+
148
+ | Date | Pain point | Severity | Source |
149
+ |---|---|---|---|
150
+ | ${date} | TODO | medium | TODO |
151
+
152
+ ## Open asks
153
+ <!-- Table: ask, first raised, times mentioned, source, linked issue, status. -->
154
+
155
+ | Ask | First raised | Times mentioned | Source | Linked issue | Status |
156
+ |---|---|---|---|---|---|
157
+ | TODO | ${date} | 1 | TODO | TODO | open |
158
+
159
+ ## Key contacts
160
+ <!-- Roles and responsibilities. Avoid unnecessary personal data. -->
161
+
162
+ | Name | Role | Contact | Notes |
163
+ |---|---|---|---|
164
+ | TODO | TODO | TODO | TODO |
165
+
166
+ ## Product areas
167
+ <!-- Areas of the product this customer touches or influences. -->
168
+
169
+ | Area | Usage level | Notes |
170
+ |---|---|---|
171
+ | TODO | low/medium/high | TODO |
172
+
173
+ ## Evidence links
174
+ <!-- Notes, tickets, calls, Slack threads, PRDs, or research tied to this customer. -->
175
+
176
+ | Date | Type | Link | Summary |
177
+ |---|---|---|---|
178
+ | ${date} | TODO | TODO | TODO |
179
+
180
+ ## Change log
181
+ <!-- Dated additions. Preserve history so future synthesis can see how the account evolved. -->
182
+
183
+ | Date | Change |
184
+ |---|---|
185
+ | ${date} | Profile created |
186
+ `;
187
+
188
+ writeFileSync(filePath, content, 'utf8');
189
+
190
+ // Update index
191
+ const index = readIndex();
192
+ const indexKey = slugify(name);
193
+ index[indexKey] = {
194
+ id,
195
+ name: profile.name,
196
+ status: profile.status,
197
+ updatedAt: profile.updatedAt,
198
+ };
199
+
200
+ // Add aliases to index
201
+ for (const alias of aliases) {
202
+ index[slugify(alias)] = { id, name: profile.name, status: profile.status, updatedAt: profile.updatedAt };
203
+ }
204
+
205
+ // Add domain to index
206
+ if (domain) {
207
+ index[slugify(domain)] = { id, name: profile.name, status: profile.status, updatedAt: profile.updatedAt };
208
+ }
209
+
210
+ writeIndex(index);
211
+
212
+ return { id, path: filePath, profile };
213
+ }
214
+
215
+ /**
216
+ * Get a customer profile by ID.
217
+ * @param {string} customerId
218
+ * @returns {CustomerProfile | null}
219
+ */
220
+ export function getCustomerProfile(customerId) {
221
+ ensureDir();
222
+
223
+ // Try direct lookup by ID in filename
224
+ const files = readdirSync(PROFILES_DIR).filter(f => f.endsWith('.md'));
225
+ for (const file of files) {
226
+ if (file.includes(customerId)) {
227
+ const filePath = join(PROFILES_DIR, file);
228
+ return parseProfileFile(filePath);
229
+ }
230
+ }
231
+
232
+ return null;
233
+ }
234
+
235
+ /**
236
+ * Search for customer profiles by name, domain, or alias.
237
+ * @param {string} query
238
+ * @returns {Array<{ id: string, name: string, status: string, path: string }>}
239
+ */
240
+ export function searchCustomerProfiles(query) {
241
+ ensureDir();
242
+
243
+ const index = readIndex();
244
+ const querySlug = slugify(query);
245
+ const results = [];
246
+
247
+ // Exact match in index
248
+ if (index[querySlug]) {
249
+ const entry = index[querySlug];
250
+ const profile = getCustomerProfile(entry.id);
251
+ if (profile) {
252
+ results.push({
253
+ id: entry.id,
254
+ name: entry.name,
255
+ status: entry.status,
256
+ path: join(PROFILES_DIR, `${slugify(entry.name)}-${entry.id}.md`),
257
+ });
258
+ }
259
+ }
260
+
261
+ // Partial match
262
+ for (const [key, entry] of Object.entries(index)) {
263
+ if (key.includes(querySlug) || entry.name.toLowerCase().includes(query.toLowerCase())) {
264
+ if (!results.find(r => r.id === entry.id)) {
265
+ const profile = getCustomerProfile(entry.id);
266
+ if (profile) {
267
+ results.push({
268
+ id: entry.id,
269
+ name: entry.name,
270
+ status: entry.status,
271
+ path: join(PROFILES_DIR, `${slugify(entry.name)}-${entry.id}.md`),
272
+ });
273
+ }
274
+ }
275
+ }
276
+ }
277
+
278
+ return results;
279
+ }
280
+
281
+ /**
282
+ * List all customer profiles.
283
+ * @param {object} [opts]
284
+ * @param {string} [opts.status] - Filter by status
285
+ * @param {string} [opts.workspace] - Filter by workspace
286
+ * @returns {Array<{ id: string, name: string, status: string, workspace: string, updatedAt: string }>}
287
+ */
288
+ export function listCustomerProfiles(opts = {}) {
289
+ ensureDir();
290
+
291
+ const index = readIndex();
292
+ let results = Object.values(index).map(entry => ({
293
+ id: entry.id,
294
+ name: entry.name,
295
+ status: entry.status,
296
+ workspace: getCustomerProfile(entry.id)?.workspace || 'default',
297
+ updatedAt: entry.updatedAt,
298
+ }));
299
+
300
+ if (opts.status) {
301
+ results = results.filter(r => r.status === opts.status);
302
+ }
303
+
304
+ if (opts.workspace) {
305
+ results = results.filter(r => r.workspace === opts.workspace);
306
+ }
307
+
308
+ return results.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
309
+ }
310
+
311
+ /**
312
+ * Update a customer profile (append to change log, update fields).
313
+ *
314
+ * @param {string} customerId
315
+ * @param {object} updates
316
+ * @param {string} [updates.owner]
317
+ * @param {string} [updates.status]
318
+ * @param {string} [updates.workspace]
319
+ * @param {string} [updates.snapshot] - New snapshot text
320
+ * @param {object[]} [updates.painPoints] - Array of { date, pain, severity, source }
321
+ * @param {object[]} [updates.asks] - Array of { ask, firstRaised, timesMentioned, source, linkedIssue, status }
322
+ * @param {string} [updates.changeLogEntry] - Text to append to change log
323
+ * @returns {{ success: boolean, path: string, updatedAt: string }}
324
+ */
325
+ export function updateCustomerProfile(customerId, updates) {
326
+ const profile = getCustomerProfile(customerId);
327
+ if (!profile) {
328
+ throw new Error(`Customer profile not found: ${customerId}`);
329
+ }
330
+
331
+ const filePath = join(PROFILES_DIR, `${slugify(profile.name)}-${customerId}.md`);
332
+ let content = readFileSync(filePath, 'utf8');
333
+ const date = new Date().toISOString().slice(0, 10);
334
+
335
+ // Update frontmatter timestamp
336
+ content = content.replace(
337
+ /updated_at: \d{4}-\d{2}-\d{2}T[\d:]+Z/,
338
+ `updated_at: ${new Date().toISOString()}`
339
+ );
340
+
341
+ // Update status if provided
342
+ if (updates.status) {
343
+ content = content.replace(
344
+ /\*\*Status\*\*: \w+/,
345
+ `**Status**: ${updates.status}`
346
+ );
347
+ // Update index
348
+ const index = readIndex();
349
+ const key = slugify(profile.name);
350
+ if (index[key]) {
351
+ index[key].status = updates.status;
352
+ index[key].updatedAt = new Date().toISOString();
353
+ writeIndex(index);
354
+ }
355
+ }
356
+
357
+ // Update owner if provided
358
+ if (updates.owner) {
359
+ content = content.replace(
360
+ /\*\*Owner\*\*: .+/,
361
+ `**Owner**: ${updates.owner}`
362
+ );
363
+ }
364
+
365
+ // Update workspace if provided
366
+ if (updates.workspace) {
367
+ content = content.replace(
368
+ /\*\*Workspace\*\*: .+/,
369
+ `**Workspace**: ${updates.workspace}`
370
+ );
371
+ }
372
+
373
+ // Append to change log
374
+ if (updates.changeLogEntry) {
375
+ const changeLogMatch = content.match(/(## Change log[\s\S]*$)/);
376
+ if (changeLogMatch) {
377
+ const newEntry = `| ${date} | ${updates.changeLogEntry} |`;
378
+ content = content.replace(
379
+ changeLogMatch[0],
380
+ `## Change log\n\n${changeLogMatch[0].split('\n').slice(0, 3).join('\n')}\n${newEntry}\n`
381
+ );
382
+ }
383
+ }
384
+
385
+ writeFileSync(filePath, content, 'utf8');
386
+
387
+ return { success: true, path: filePath, updatedAt: new Date().toISOString() };
388
+ }
389
+
390
+ /**
391
+ * Link a signal/intake to a customer profile.
392
+ * Appends to evidence links and change log.
393
+ *
394
+ * @param {string} customerId
395
+ * @param {object} signal
396
+ * @param {string} signal.id - Signal/intake ID
397
+ * @param {string} signal.type - 'intake' | 'observation' | 'ticket' | etc.
398
+ * @param {string} signal.sourcePath - Path to signal file
399
+ * @param {string} signal.summary - Brief summary
400
+ * @param {string} [signal.date] - Date (default: today)
401
+ * @returns {{ success: boolean, customerId: string, signalId: string }}
402
+ */
403
+ export function linkSignalToCustomer(customerId, signal) {
404
+ const date = signal.date || new Date().toISOString().slice(0, 10);
405
+
406
+ return updateCustomerProfile(customerId, {
407
+ changeLogEntry: `Linked ${signal.type}: ${signal.id} — ${signal.summary}`,
408
+ });
409
+ }
410
+
411
+ /**
412
+ * Parse a profile markdown file into a structured object.
413
+ * @param {string} filePath
414
+ * @returns {CustomerProfile | null}
415
+ */
416
+ function parseProfileFile(filePath) {
417
+ try {
418
+ const content = readFileSync(filePath, 'utf8');
419
+
420
+ // Extract frontmatter
421
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
422
+ if (!fmMatch) return null;
423
+
424
+ const frontmatter = {};
425
+ for (const line of fmMatch[1].split('\n')) {
426
+ const [key, ...valueParts] = line.split(':');
427
+ if (key && valueParts.length) {
428
+ frontmatter[key.trim()] = valueParts.join(':').trim();
429
+ }
430
+ }
431
+
432
+ // Extract key fields from content
433
+ const nameMatch = content.match(/^# Customer Profile: (.+)$/m);
434
+ const statusMatch = content.match(/\*\*Status\*\*: (\w+)/);
435
+ const ownerMatch = content.match(/\*\*Owner\*\*: (.+)$/m);
436
+ const workspaceMatch = content.match(/\*\*Workspace\*\*: (.+)$/m);
437
+
438
+ return {
439
+ id: frontmatter.customer_id,
440
+ name: nameMatch?.[1]?.trim() || 'Unknown',
441
+ status: statusMatch?.[1] || 'unknown',
442
+ owner: ownerMatch?.[1]?.trim() || 'TBD',
443
+ workspace: workspaceMatch?.[1]?.trim() || 'default',
444
+ createdAt: frontmatter.created_at,
445
+ updatedAt: frontmatter.updated_at,
446
+ rawContent: content,
447
+ };
448
+ } catch (err) {
449
+ process.stderr.write('[customer-profiles.mjs] parseProfileFile: ' + (err?.message ?? String(err)) + '\n');
450
+ return null;
451
+ }
452
+ }
453
+
454
+ /**
455
+ * Detect customer mentions in text and return matching customer IDs.
456
+ * Uses name, domain, and aliases for matching.
457
+ *
458
+ * @param {string} text - Text to scan
459
+ * @returns {Array<{ customerId: string, name: string, confidence: number }>}
460
+ */
461
+ export function detectCustomerMentions(text) {
462
+ const index = readIndex();
463
+ const mentions = [];
464
+ const seen = new Set();
465
+
466
+ for (const [key, entry] of Object.entries(index)) {
467
+ // Check for name/alias mentions
468
+ const namePattern = new RegExp(`\\b${key.replace(/-/g, '[- ]')}\\b`, 'i');
469
+ if (namePattern.test(text) && !seen.has(entry.id)) {
470
+ mentions.push({
471
+ customerId: entry.id,
472
+ name: entry.name,
473
+ confidence: 0.8, // High confidence for exact name match
474
+ });
475
+ seen.add(entry.id);
476
+ }
477
+ }
478
+
479
+ return mentions;
480
+ }