@geraldmaron/construct 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (352) hide show
  1. package/.env.example +69 -0
  2. package/LICENSE +21 -0
  3. package/README.md +344 -0
  4. package/agents/prompts/cx-accessibility.md +29 -0
  5. package/agents/prompts/cx-ai-engineer.md +31 -0
  6. package/agents/prompts/cx-architect.md +40 -0
  7. package/agents/prompts/cx-business-strategist.md +25 -0
  8. package/agents/prompts/cx-data-analyst.md +30 -0
  9. package/agents/prompts/cx-data-engineer.md +32 -0
  10. package/agents/prompts/cx-debugger.md +27 -0
  11. package/agents/prompts/cx-designer.md +33 -0
  12. package/agents/prompts/cx-devil-advocate.md +36 -0
  13. package/agents/prompts/cx-docs-keeper.md +50 -0
  14. package/agents/prompts/cx-engineer.md +36 -0
  15. package/agents/prompts/cx-evaluator.md +26 -0
  16. package/agents/prompts/cx-explorer.md +38 -0
  17. package/agents/prompts/cx-legal-compliance.md +25 -0
  18. package/agents/prompts/cx-operations.md +28 -0
  19. package/agents/prompts/cx-orchestrator.md +30 -0
  20. package/agents/prompts/cx-platform-engineer.md +25 -0
  21. package/agents/prompts/cx-product-manager.md +30 -0
  22. package/agents/prompts/cx-qa.md +42 -0
  23. package/agents/prompts/cx-rd-lead.md +25 -0
  24. package/agents/prompts/cx-release-manager.md +31 -0
  25. package/agents/prompts/cx-researcher.md +31 -0
  26. package/agents/prompts/cx-reviewer.md +36 -0
  27. package/agents/prompts/cx-security.md +37 -0
  28. package/agents/prompts/cx-sre.md +26 -0
  29. package/agents/prompts/cx-test-automation.md +35 -0
  30. package/agents/prompts/cx-trace-reviewer.md +76 -0
  31. package/agents/prompts/cx-ux-researcher.md +30 -0
  32. package/agents/registry.json +879 -0
  33. package/agents/teams.json +94 -0
  34. package/bin/construct +613 -0
  35. package/commands/build/feature.md +21 -0
  36. package/commands/build/fix.md +20 -0
  37. package/commands/design/access.md +19 -0
  38. package/commands/design/flow.md +16 -0
  39. package/commands/design/ui.md +18 -0
  40. package/commands/measure/experiment.md +18 -0
  41. package/commands/measure/metrics.md +18 -0
  42. package/commands/measure/results.md +18 -0
  43. package/commands/plan/api.md +19 -0
  44. package/commands/plan/challenge.md +18 -0
  45. package/commands/plan/decide.md +20 -0
  46. package/commands/plan/feature.md +49 -0
  47. package/commands/plan/requirements.md +24 -0
  48. package/commands/remember/context.md +26 -0
  49. package/commands/remember/handoff.md +19 -0
  50. package/commands/remember/runbook.md +20 -0
  51. package/commands/review/code.md +21 -0
  52. package/commands/review/quality.md +19 -0
  53. package/commands/review/security.md +16 -0
  54. package/commands/ship/ready.md +19 -0
  55. package/commands/ship/release.md +19 -0
  56. package/commands/ship/status.md +18 -0
  57. package/commands/understand/docs.md +22 -0
  58. package/commands/understand/research.md +22 -0
  59. package/commands/understand/this.md +23 -0
  60. package/commands/understand/why.md +18 -0
  61. package/commands/work/clean.md +32 -0
  62. package/commands/work/drive.md +69 -0
  63. package/commands/work/optimize-prompts.md +46 -0
  64. package/commands/work/parallel-review.md +40 -0
  65. package/db/migrations/001_init.sql +38 -0
  66. package/langfuse/docker-compose.yml +82 -0
  67. package/lib/audit-skills.mjs +118 -0
  68. package/lib/auto-docs.mjs +293 -0
  69. package/lib/cli-commands.mjs +409 -0
  70. package/lib/codex-config.mjs +114 -0
  71. package/lib/comment-lint.mjs +191 -0
  72. package/lib/completions.mjs +170 -0
  73. package/lib/context-state.mjs +99 -0
  74. package/lib/cost.mjs +213 -0
  75. package/lib/distill.mjs +403 -0
  76. package/lib/efficiency.mjs +139 -0
  77. package/lib/env-config.mjs +53 -0
  78. package/lib/eval-harness.mjs +59 -0
  79. package/lib/features.mjs +209 -0
  80. package/lib/headhunt.mjs +597 -0
  81. package/lib/hooks/adaptive-lint.mjs +112 -0
  82. package/lib/hooks/agent-tracker.mjs +50 -0
  83. package/lib/hooks/bootstrap-guard.mjs +90 -0
  84. package/lib/hooks/comment-lint.mjs +26 -0
  85. package/lib/hooks/config-protection.mjs +52 -0
  86. package/lib/hooks/console-warn.mjs +43 -0
  87. package/lib/hooks/context-window-recovery.mjs +90 -0
  88. package/lib/hooks/continuation-enforcer.mjs +72 -0
  89. package/lib/hooks/dep-audit.mjs +155 -0
  90. package/lib/hooks/drive-guard.mjs +89 -0
  91. package/lib/hooks/edit-accumulator.mjs +36 -0
  92. package/lib/hooks/edit-error-recovery.mjs +46 -0
  93. package/lib/hooks/edit-guard.mjs +109 -0
  94. package/lib/hooks/env-check.mjs +80 -0
  95. package/lib/hooks/guard-bash.mjs +83 -0
  96. package/lib/hooks/mcp-audit.mjs +57 -0
  97. package/lib/hooks/mcp-health-check.mjs +51 -0
  98. package/lib/hooks/mcp-task-scope.mjs +47 -0
  99. package/lib/hooks/model-fallback.mjs +105 -0
  100. package/lib/hooks/pre-compact.mjs +212 -0
  101. package/lib/hooks/pre-push-gate.mjs +129 -0
  102. package/lib/hooks/read-tracker.mjs +129 -0
  103. package/lib/hooks/registry-sync.mjs +23 -0
  104. package/lib/hooks/scan-secrets.mjs +76 -0
  105. package/lib/hooks/session-start.mjs +95 -0
  106. package/lib/hooks/stop-notify.mjs +191 -0
  107. package/lib/hooks/stop-typecheck.mjs +83 -0
  108. package/lib/hooks/task-completed-guard.mjs +43 -0
  109. package/lib/hooks/teammate-idle-guard.mjs +54 -0
  110. package/lib/hooks/workflow-guard.mjs +62 -0
  111. package/lib/host-capabilities.mjs +123 -0
  112. package/lib/init-docs.mjs +382 -0
  113. package/lib/mcp/server.mjs +1123 -0
  114. package/lib/mcp-catalog.json +243 -0
  115. package/lib/mcp-manager.mjs +433 -0
  116. package/lib/mcp-platform-config.mjs +104 -0
  117. package/lib/model-router.mjs +810 -0
  118. package/lib/opencode-config.mjs +44 -0
  119. package/lib/opencode-runtime-plugin.mjs +909 -0
  120. package/lib/opencode-telemetry.mjs +433 -0
  121. package/lib/orchestration-policy.mjs +258 -0
  122. package/lib/prompt-composer.mjs +196 -0
  123. package/lib/prompt-metadata.mjs +68 -0
  124. package/lib/review.mjs +429 -0
  125. package/lib/role-preload.mjs +52 -0
  126. package/lib/schemas/decision.json +50 -0
  127. package/lib/schemas/implementation.json +51 -0
  128. package/lib/schemas/review-report.json +32 -0
  129. package/lib/schemas/test-report.json +43 -0
  130. package/lib/server/index.mjs +334 -0
  131. package/lib/server/static/app.js +841 -0
  132. package/lib/server/static/index.html +820 -0
  133. package/lib/service-manager.mjs +225 -0
  134. package/lib/setup.mjs +319 -0
  135. package/lib/status.mjs +707 -0
  136. package/lib/storage/backend.mjs +44 -0
  137. package/lib/storage/embeddings.mjs +70 -0
  138. package/lib/storage/hybrid-query.mjs +184 -0
  139. package/lib/storage/sql-store.mjs +55 -0
  140. package/lib/storage/state-source.mjs +101 -0
  141. package/lib/storage/sync.mjs +164 -0
  142. package/lib/storage/vector-store.mjs +59 -0
  143. package/lib/telemetry/backends/langfuse.mjs +58 -0
  144. package/lib/telemetry/backends/noop.mjs +16 -0
  145. package/lib/telemetry/langfuse-ingest.mjs +108 -0
  146. package/lib/telemetry/langfuse-model-sync.mjs +270 -0
  147. package/lib/telemetry/team-rollup.mjs +143 -0
  148. package/lib/toolkit-env.mjs +30 -0
  149. package/lib/validator.mjs +181 -0
  150. package/lib/workflow-state.mjs +794 -0
  151. package/package.json +53 -0
  152. package/personas/construct.md +94 -0
  153. package/platforms/claude/CLAUDE.md +30 -0
  154. package/platforms/claude/settings.template.json +472 -0
  155. package/platforms/opencode/config.template.json +65 -0
  156. package/platforms/opencode/plugins/construct-fallback.js +5 -0
  157. package/platforms/opencode/sync-config.mjs +69 -0
  158. package/rules/common/agents.md +55 -0
  159. package/rules/common/code-review.md +129 -0
  160. package/rules/common/coding-style.md +95 -0
  161. package/rules/common/comments.md +139 -0
  162. package/rules/common/cx-agent-routing.md +61 -0
  163. package/rules/common/cx-skill-routing.md +11 -0
  164. package/rules/common/development-workflow.md +49 -0
  165. package/rules/common/git-workflow.md +29 -0
  166. package/rules/common/hooks.md +35 -0
  167. package/rules/common/patterns.md +36 -0
  168. package/rules/common/performance.md +71 -0
  169. package/rules/common/security.md +34 -0
  170. package/rules/common/testing.md +62 -0
  171. package/rules/golang/coding-style.md +37 -0
  172. package/rules/golang/hooks.md +22 -0
  173. package/rules/golang/patterns.md +50 -0
  174. package/rules/golang/security.md +39 -0
  175. package/rules/golang/testing.md +36 -0
  176. package/rules/python/coding-style.md +47 -0
  177. package/rules/python/hooks.md +24 -0
  178. package/rules/python/patterns.md +44 -0
  179. package/rules/python/security.md +35 -0
  180. package/rules/python/testing.md +43 -0
  181. package/rules/swift/coding-style.md +52 -0
  182. package/rules/swift/hooks.md +25 -0
  183. package/rules/swift/patterns.md +71 -0
  184. package/rules/swift/security.md +38 -0
  185. package/rules/swift/testing.md +50 -0
  186. package/rules/typescript/coding-style.md +204 -0
  187. package/rules/typescript/hooks.md +27 -0
  188. package/rules/typescript/patterns.md +57 -0
  189. package/rules/typescript/security.md +33 -0
  190. package/rules/typescript/testing.md +23 -0
  191. package/rules/web/coding-style.md +101 -0
  192. package/rules/web/design-quality.md +68 -0
  193. package/rules/web/hooks.md +125 -0
  194. package/rules/web/patterns.md +84 -0
  195. package/rules/web/performance.md +69 -0
  196. package/rules/web/security.md +62 -0
  197. package/rules/web/testing.md +60 -0
  198. package/rules/zh/README.md +113 -0
  199. package/rules/zh/agents.md +55 -0
  200. package/rules/zh/code-review.md +129 -0
  201. package/rules/zh/coding-style.md +53 -0
  202. package/rules/zh/development-workflow.md +49 -0
  203. package/rules/zh/git-workflow.md +29 -0
  204. package/rules/zh/hooks.md +35 -0
  205. package/rules/zh/patterns.md +36 -0
  206. package/rules/zh/performance.md +60 -0
  207. package/rules/zh/security.md +34 -0
  208. package/rules/zh/testing.md +34 -0
  209. package/skills/ai/agent-dev.md +102 -0
  210. package/skills/ai/llm-security.md +105 -0
  211. package/skills/ai/ml-ops.md +169 -0
  212. package/skills/ai/orchestration-workflow.md +87 -0
  213. package/skills/ai/prompt-and-eval.md +114 -0
  214. package/skills/ai/prompt-optimizer.md +114 -0
  215. package/skills/ai/rag-system.md +104 -0
  216. package/skills/architecture/api-design.md +100 -0
  217. package/skills/architecture/caching.md +101 -0
  218. package/skills/architecture/cloud-native.md +97 -0
  219. package/skills/architecture/message-queue.md +100 -0
  220. package/skills/architecture/security-arch.md +105 -0
  221. package/skills/development/cpp.md +127 -0
  222. package/skills/development/go.md +129 -0
  223. package/skills/development/java.md +134 -0
  224. package/skills/development/kotlin.md +140 -0
  225. package/skills/development/mobile-crossplatform.md +144 -0
  226. package/skills/development/python.md +109 -0
  227. package/skills/development/rust.md +127 -0
  228. package/skills/development/shell.md +127 -0
  229. package/skills/development/swift.md +129 -0
  230. package/skills/development/typescript.md +129 -0
  231. package/skills/devops/ci-cd.md +108 -0
  232. package/skills/devops/containerization.md +106 -0
  233. package/skills/devops/cost-optimization.md +105 -0
  234. package/skills/devops/data-engineering.md +181 -0
  235. package/skills/devops/database.md +95 -0
  236. package/skills/devops/dependency-management.md +91 -0
  237. package/skills/devops/devsecops.md +96 -0
  238. package/skills/devops/git-workflow.md +100 -0
  239. package/skills/devops/incident-response.md +167 -0
  240. package/skills/devops/monorepo.md +87 -0
  241. package/skills/devops/observability.md +103 -0
  242. package/skills/devops/performance.md +104 -0
  243. package/skills/devops/testing.md +104 -0
  244. package/skills/docs/adr-workflow.md +32 -0
  245. package/skills/docs/backlog-proposal-workflow.md +19 -0
  246. package/skills/docs/customer-profile-workflow.md +22 -0
  247. package/skills/docs/evidence-ingest-workflow.md +23 -0
  248. package/skills/docs/init-docs.md +160 -0
  249. package/skills/docs/init-project.md +50 -0
  250. package/skills/docs/prd-workflow.md +57 -0
  251. package/skills/docs/prfaq-workflow.md +25 -0
  252. package/skills/docs/product-intelligence-review.md +22 -0
  253. package/skills/docs/product-intelligence-workflow.md +62 -0
  254. package/skills/docs/product-signal-workflow.md +27 -0
  255. package/skills/docs/research-workflow.md +28 -0
  256. package/skills/docs/runbook-workflow.md +24 -0
  257. package/skills/exploration/repo-map.md +297 -0
  258. package/skills/frameworks/django.md +159 -0
  259. package/skills/frameworks/nextjs.md +148 -0
  260. package/skills/frameworks/react.md +132 -0
  261. package/skills/frameworks/spring-boot.md +165 -0
  262. package/skills/frontend-design/accessibility.md +153 -0
  263. package/skills/frontend-design/component-patterns.md +111 -0
  264. package/skills/frontend-design/engineering.md +122 -0
  265. package/skills/frontend-design/state-management.md +118 -0
  266. package/skills/frontend-design/ui-aesthetics.md +102 -0
  267. package/skills/frontend-design/ux-principles.md +126 -0
  268. package/skills/quality-gates/review-work.md +90 -0
  269. package/skills/quality-gates/verify-change.md +94 -0
  270. package/skills/quality-gates/verify-module.md +96 -0
  271. package/skills/quality-gates/verify-quality.md +93 -0
  272. package/skills/quality-gates/verify-security.md +95 -0
  273. package/skills/roles/architect.ai-systems.md +37 -0
  274. package/skills/roles/architect.data.md +37 -0
  275. package/skills/roles/architect.enterprise.md +37 -0
  276. package/skills/roles/architect.integration.md +37 -0
  277. package/skills/roles/architect.md +68 -0
  278. package/skills/roles/architect.platform.md +37 -0
  279. package/skills/roles/data-analyst.experiment.md +37 -0
  280. package/skills/roles/data-analyst.md +68 -0
  281. package/skills/roles/data-analyst.product-intelligence.md +37 -0
  282. package/skills/roles/data-analyst.product.md +37 -0
  283. package/skills/roles/data-analyst.telemetry.md +37 -0
  284. package/skills/roles/data-engineer.pipeline.md +37 -0
  285. package/skills/roles/data-engineer.vector-retrieval.md +37 -0
  286. package/skills/roles/data-engineer.warehouse.md +37 -0
  287. package/skills/roles/debugger.md +68 -0
  288. package/skills/roles/designer.accessibility.md +43 -0
  289. package/skills/roles/designer.md +68 -0
  290. package/skills/roles/engineer.ai.md +49 -0
  291. package/skills/roles/engineer.data.md +49 -0
  292. package/skills/roles/engineer.md +85 -0
  293. package/skills/roles/engineer.platform.md +49 -0
  294. package/skills/roles/operator.docs.md +43 -0
  295. package/skills/roles/operator.md +68 -0
  296. package/skills/roles/operator.release.md +43 -0
  297. package/skills/roles/operator.sre.md +43 -0
  298. package/skills/roles/orchestrator.md +66 -0
  299. package/skills/roles/product-manager.ai-product.md +37 -0
  300. package/skills/roles/product-manager.business-strategy.md +43 -0
  301. package/skills/roles/product-manager.enterprise.md +37 -0
  302. package/skills/roles/product-manager.growth.md +37 -0
  303. package/skills/roles/product-manager.md +67 -0
  304. package/skills/roles/product-manager.platform.md +37 -0
  305. package/skills/roles/product-manager.product.md +37 -0
  306. package/skills/roles/qa.ai-eval.md +37 -0
  307. package/skills/roles/qa.api-contract.md +37 -0
  308. package/skills/roles/qa.data-pipeline.md +37 -0
  309. package/skills/roles/qa.md +68 -0
  310. package/skills/roles/qa.test-automation.md +49 -0
  311. package/skills/roles/qa.web-ui.md +37 -0
  312. package/skills/roles/researcher.explorer.md +43 -0
  313. package/skills/roles/researcher.md +68 -0
  314. package/skills/roles/researcher.ux.md +43 -0
  315. package/skills/roles/reviewer.devil-advocate.md +43 -0
  316. package/skills/roles/reviewer.evaluator.md +43 -0
  317. package/skills/roles/reviewer.md +68 -0
  318. package/skills/roles/reviewer.trace.md +43 -0
  319. package/skills/roles/security.ai.md +37 -0
  320. package/skills/roles/security.appsec.md +37 -0
  321. package/skills/roles/security.cloud.md +37 -0
  322. package/skills/roles/security.legal-compliance.md +49 -0
  323. package/skills/roles/security.md +68 -0
  324. package/skills/roles/security.privacy.md +37 -0
  325. package/skills/roles/security.supply-chain.md +37 -0
  326. package/skills/routing.md +139 -0
  327. package/skills/security/blue-team.md +83 -0
  328. package/skills/security/code-audit.md +94 -0
  329. package/skills/security/pentest.md +84 -0
  330. package/skills/security/red-team.md +81 -0
  331. package/skills/security/threat-intel.md +89 -0
  332. package/skills/security/vuln-research.md +87 -0
  333. package/skills/utility/clean-code.md +165 -0
  334. package/sync-agents.mjs +899 -0
  335. package/templates/docs/adr.md +27 -0
  336. package/templates/docs/backlog-proposal.md +26 -0
  337. package/templates/docs/customer-profile.md +34 -0
  338. package/templates/docs/evidence-brief.md +37 -0
  339. package/templates/docs/incident-report.md +46 -0
  340. package/templates/docs/memo.md +27 -0
  341. package/templates/docs/meta-prd.md +56 -0
  342. package/templates/docs/one-pager.md +24 -0
  343. package/templates/docs/prd-business.md +61 -0
  344. package/templates/docs/prd-platform.md +62 -0
  345. package/templates/docs/prd.md +47 -0
  346. package/templates/docs/prfaq.md +31 -0
  347. package/templates/docs/product-intelligence-report.md +31 -0
  348. package/templates/docs/research-brief.md +29 -0
  349. package/templates/docs/rfc-platform.md +60 -0
  350. package/templates/docs/rfc.md +46 -0
  351. package/templates/docs/runbook.md +33 -0
  352. package/templates/docs/signal-brief.md +25 -0
@@ -0,0 +1,40 @@
1
+ <!--
2
+ commands/work/parallel-review.md — Adversarial parallel review — 5 reviewers must agree before output ships
3
+
4
+ Adversarial parallel review — 5 reviewers must agree before output ships
5
+ -->
6
+ ---
7
+ description: Adversarial parallel review — 5 reviewers must agree before output ships
8
+ ---
9
+
10
+ You are Construct running a parallel adversarial review of: $ARGUMENTS
11
+
12
+ Dispatch the following 5 review roles concurrently:
13
+
14
+ 1. **cx-reviewer** — Correctness and logic: does it do what it claims? Are there off-by-ones, edge cases, or control flow bugs?
15
+ 2. **cx-security** — Vulnerabilities and data exposure: injection, auth bypass, secret leakage, SSRF, unvalidated input
16
+ 3. **cx-qa** — Test coverage and edge cases: what's untested? What inputs would break this?
17
+ 4. **cx-devil-advocate** — Assumption stress-test: what are we assuming that could be wrong? What failure modes are unaddressed?
18
+ 5. **cx-accessibility** (UI changes) or **cx-trace-reviewer** (non-UI) — Inclusive UX or performance bottlenecks
19
+
20
+ ## Merge Gate
21
+
22
+ All 5 roles must return **PASS** before output ships.
23
+
24
+ | Finding severity | Action |
25
+ |---|---|
26
+ | CRITICAL | Blocks merge. Must fix before proceeding. |
27
+ | HIGH | Blocks merge. Must fix before proceeding. |
28
+ | MEDIUM | Requires explicit acknowledgment. Document why it's acceptable or fix it. |
29
+ | LOW | Informational only. No action required. |
30
+
31
+ ## Output Format
32
+
33
+ For each role:
34
+ ```
35
+ [ROLE] PASS | FAIL
36
+ Findings:
37
+ - [severity] description
38
+ ```
39
+
40
+ Final verdict: **MERGE READY** or **BLOCKED** (list blocking findings)
@@ -0,0 +1,38 @@
1
+ create table if not exists construct_documents (
2
+ id text primary key,
3
+ project text not null,
4
+ kind text not null,
5
+ title text not null,
6
+ summary text,
7
+ body text not null,
8
+ source_path text,
9
+ tags jsonb not null default '[]'::jsonb,
10
+ content_hash text not null,
11
+ updated_at timestamptz not null default now(),
12
+ created_at timestamptz not null default now()
13
+ );
14
+
15
+ create index if not exists construct_documents_project_kind_idx on construct_documents (project, kind);
16
+ create index if not exists construct_documents_content_hash_idx on construct_documents (content_hash);
17
+
18
+ create table if not exists construct_embeddings (
19
+ document_id text primary key references construct_documents(id) on delete cascade,
20
+ model text not null,
21
+ embedding double precision[] not null,
22
+ content_hash text not null,
23
+ updated_at timestamptz not null default now(),
24
+ created_at timestamptz not null default now()
25
+ );
26
+
27
+ create index if not exists construct_embeddings_model_idx on construct_embeddings (model);
28
+
29
+ create table if not exists construct_sync_runs (
30
+ id bigserial primary key,
31
+ project text not null,
32
+ source text not null,
33
+ documents_synced integer not null default 0,
34
+ embeddings_synced integer not null default 0,
35
+ status text not null,
36
+ note text,
37
+ created_at timestamptz not null default now()
38
+ );
@@ -0,0 +1,82 @@
1
+ # Construct — Langfuse Self-Hosted
2
+ # Auto-provisioned: keys are seeded at container startup via LANGFUSE_INIT_* vars.
3
+ # No browser setup required. 'construct setup' writes keys to .env automatically.
4
+ #
5
+ # Manual usage:
6
+ # docker-compose -f langfuse/docker-compose.yml -p construct-langfuse up -d
7
+ # # Keys are ready immediately — see LANGFUSE_INIT_PROJECT_* below
8
+
9
+ name: construct-langfuse
10
+
11
+ services:
12
+ langfuse-server:
13
+ image: langfuse/langfuse:2
14
+ container_name: construct-langfuse
15
+ restart: unless-stopped
16
+ depends_on:
17
+ langfuse-db:
18
+ condition: service_healthy
19
+ ports:
20
+ - "3000:3000"
21
+ environment:
22
+ DATABASE_URL: postgresql://langfuse:langfuse@langfuse-db:5432/langfuse
23
+ NEXTAUTH_SECRET: ${LANGFUSE_NEXTAUTH_SECRET:-construct-nextauth-secret}
24
+ SALT: ${LANGFUSE_SALT:-construct-salt-value}
25
+ NEXTAUTH_URL: http://localhost:3000
26
+ TELEMETRY_ENABLED: "false"
27
+ LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES: "true"
28
+ AUTH_DISABLE_SIGNUP: "false"
29
+ # ── Auto-provisioning (no browser setup needed) ──────────────────
30
+ LANGFUSE_INIT_ORG_ID: "construct-org"
31
+ LANGFUSE_INIT_ORG_NAME: "Construct"
32
+ LANGFUSE_INIT_PROJECT_ID: "construct-project"
33
+ LANGFUSE_INIT_PROJECT_NAME: "Construct"
34
+ LANGFUSE_INIT_PROJECT_PUBLIC_KEY: "pk-lf-construct-local"
35
+ LANGFUSE_INIT_PROJECT_SECRET_KEY: "sk-lf-construct-local"
36
+ LANGFUSE_INIT_USER_EMAIL: "admin@construct.local"
37
+ LANGFUSE_INIT_USER_NAME: "Admin"
38
+ LANGFUSE_INIT_USER_PASSWORD: "construct-admin"
39
+ healthcheck:
40
+ test: ["CMD-SHELL", "curl -sf http://localhost:3000/api/public/health || exit 1"]
41
+ interval: 10s
42
+ timeout: 5s
43
+ retries: 10
44
+ start_period: 30s
45
+
46
+ langfuse-db:
47
+ image: postgres:15-alpine
48
+ container_name: construct-langfuse-db
49
+ restart: unless-stopped
50
+ environment:
51
+ POSTGRES_USER: langfuse
52
+ POSTGRES_PASSWORD: langfuse
53
+ POSTGRES_DB: langfuse
54
+ volumes:
55
+ - langfuse_db_data:/var/lib/postgresql/data
56
+ healthcheck:
57
+ test: ["CMD-SHELL", "pg_isready -U langfuse -d langfuse"]
58
+ interval: 3s
59
+ timeout: 3s
60
+ retries: 10
61
+
62
+ dspy-optimizer:
63
+ build:
64
+ context: ./dspy-optimizer
65
+ dockerfile: Dockerfile
66
+ container_name: construct-dspy-optimizer
67
+ restart: "no"
68
+ profiles:
69
+ - optimize
70
+ env_file:
71
+ - ../.env
72
+ environment:
73
+ LANGFUSE_BASEURL: http://langfuse-server:3000
74
+ depends_on:
75
+ langfuse-server:
76
+ condition: service_healthy
77
+ volumes:
78
+ - ../.cx:/app/.cx
79
+
80
+ volumes:
81
+ langfuse_db_data:
82
+ driver: local
@@ -0,0 +1,118 @@
1
+ /**
2
+ * lib/audit-skills.mjs — Audit agent↔skill bindings for orphans and missing files.
3
+ *
4
+ * Reports: (a) skills with no agent owner, (b) agents with no skill bindings,
5
+ * (c) skill paths declared in registry but missing on disk.
6
+ * Called by 'construct audit skills' and incorporated into 'construct doctor'.
7
+ */
8
+ import fs from 'node:fs';
9
+ import path from 'node:path';
10
+
11
+ function findConstructRoot(startPath = process.cwd()) {
12
+ let current = path.resolve(startPath);
13
+ while (true) {
14
+ if (fs.existsSync(path.join(current, 'agents', 'registry.json'))) return current;
15
+ const parent = path.dirname(current);
16
+ if (parent === current) return path.resolve(startPath);
17
+ current = parent;
18
+ }
19
+ }
20
+
21
+ function collectSkillFiles(skillsDir) {
22
+ const results = new Set();
23
+ function walk(dir, prefix = '') {
24
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
25
+ if (entry.isDirectory()) {
26
+ walk(path.join(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name);
27
+ } else if (entry.name.endsWith('.md') && entry.name !== 'routing.md') {
28
+ const rel = prefix ? `${prefix}/${entry.name.replace(/\.md$/, '')}` : entry.name.replace(/\.md$/, '');
29
+ results.add(rel);
30
+ }
31
+ }
32
+ }
33
+ try { walk(skillsDir); } catch { /* skills dir missing */ }
34
+ return results;
35
+ }
36
+
37
+ export function auditSkills({ rootDir, silent = false } = {}) {
38
+ const root = rootDir ?? findConstructRoot();
39
+ const registryPath = path.join(root, 'agents', 'registry.json');
40
+ const skillsDir = path.join(root, 'skills');
41
+
42
+ const registry = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
43
+ const allSkillFiles = collectSkillFiles(skillsDir);
44
+
45
+ const declaredSkills = new Set();
46
+ const agentsWithNoSkills = [];
47
+ const missingSkillFiles = [];
48
+
49
+ for (const agent of registry.agents ?? []) {
50
+ const skills = agent.skills ?? [];
51
+ if (skills.length === 0) {
52
+ agentsWithNoSkills.push(agent.name);
53
+ }
54
+ for (const skill of skills) {
55
+ declaredSkills.add(skill);
56
+ if (!allSkillFiles.has(skill)) {
57
+ missingSkillFiles.push({ agent: agent.name, skill });
58
+ }
59
+ }
60
+ }
61
+
62
+ const orphanSkills = [...allSkillFiles].filter((s) => !declaredSkills.has(s));
63
+
64
+ const issues = [];
65
+ if (agentsWithNoSkills.length > 0) issues.push({ kind: 'agents-no-skills', items: agentsWithNoSkills });
66
+ if (orphanSkills.length > 0) issues.push({ kind: 'orphan-skills', items: orphanSkills });
67
+ if (missingSkillFiles.length > 0) issues.push({ kind: 'missing-skill-files', items: missingSkillFiles });
68
+
69
+ if (!silent) {
70
+ const line = (msg) => process.stdout.write(`${msg}\n`);
71
+ line('Construct Skill Audit');
72
+ line('═════════════════════');
73
+ line('');
74
+
75
+ if (agentsWithNoSkills.length === 0) {
76
+ line(' ✓ All agents have at least one skill binding');
77
+ } else {
78
+ line(` ⚠ Agents with no skill bindings (${agentsWithNoSkills.length}):`);
79
+ for (const n of agentsWithNoSkills) line(` - cx-${n}`);
80
+ }
81
+ line('');
82
+
83
+ if (orphanSkills.length === 0) {
84
+ line(' ✓ All skills have at least one agent owner');
85
+ } else {
86
+ line(` ⚠ Skills with no agent owner (${orphanSkills.length}):`);
87
+ for (const s of orphanSkills) line(` - skills/${s}.md`);
88
+ }
89
+ line('');
90
+
91
+ if (missingSkillFiles.length === 0) {
92
+ line(' ✓ All declared skill paths exist on disk');
93
+ } else {
94
+ line(` ✗ Declared skills missing on disk (${missingSkillFiles.length}):`);
95
+ for (const { agent, skill } of missingSkillFiles) line(` - cx-${agent} → skills/${skill}.md`);
96
+ }
97
+ line('');
98
+
99
+ const hasErrors = missingSkillFiles.length > 0;
100
+ const hasWarnings = agentsWithNoSkills.length > 0 || orphanSkills.length > 0;
101
+ if (hasErrors) line(' Result: FAIL — fix missing skill files before syncing');
102
+ else if (hasWarnings) line(' Result: WARN — bindings incomplete but system is functional');
103
+ else line(' Result: PASS');
104
+ }
105
+
106
+ return {
107
+ agentsWithNoSkills,
108
+ orphanSkills,
109
+ missingSkillFiles,
110
+ pass: missingSkillFiles.length === 0,
111
+ };
112
+ }
113
+
114
+ export async function runAuditSkillsCli(args = []) {
115
+ const rootDir = args.find((a) => a.startsWith('--root='))?.split('=')[1] ?? undefined;
116
+ const result = auditSkills({ rootDir });
117
+ if (!result.pass) process.exit(1);
118
+ }
@@ -0,0 +1,293 @@
1
+ /**
2
+ * lib/auto-docs.mjs — regenerate managed regions in markdown docs and build the MkDocs site tree.
3
+ *
4
+ * Managed regions are HTML comment markers in the form:
5
+ * <!-- AUTO:region-name -->
6
+ * content
7
+ * <!-- /AUTO:region-name -->
8
+ *
9
+ * Running regenerateDocs() is idempotent. With check:true it returns whether
10
+ * anything would change without writing files, used by CI to detect drift.
11
+ *
12
+ * buildSite() writes site/docs/ from the same sources feeding the AUTO regions
13
+ * so the GitHub Pages site never drifts from the in-repo markdown.
14
+ */
15
+
16
+ import fs from 'node:fs';
17
+ import path from 'node:path';
18
+ import { execSync } from 'node:child_process';
19
+
20
+ import { CLI_COMMANDS_BY_CATEGORY, CATEGORY_ORDER } from './cli-commands.mjs';
21
+
22
+ // --- region helpers ---
23
+
24
+ function replaceRegion(content, regionName, newBody) {
25
+ const open = `<!-- AUTO:${regionName} -->`;
26
+ const close = `<!-- /AUTO:${regionName} -->`;
27
+ const before = content.indexOf(open);
28
+ const after = content.indexOf(close);
29
+ if (before === -1 || after === -1) return null;
30
+ return content.slice(0, before + open.length) + '\n' + newBody + '\n' + content.slice(after);
31
+ }
32
+
33
+ function readFile(p) {
34
+ try { return fs.readFileSync(p, 'utf8'); } catch { return null; }
35
+ }
36
+
37
+ function writeIfChanged(filePath, newContent) {
38
+ const existing = readFile(filePath);
39
+ if (existing === newContent) return false;
40
+ fs.writeFileSync(filePath, newContent, 'utf8');
41
+ return true;
42
+ }
43
+
44
+ // --- region generators ---
45
+
46
+ function buildCommandsTable() {
47
+ const rows = [];
48
+ for (const category of CATEGORY_ORDER) {
49
+ const cmds = CLI_COMMANDS_BY_CATEGORY[category] ?? [];
50
+ if (!cmds.length) continue;
51
+ rows.push(`### ${category}\n`);
52
+ rows.push('| Command | What it does |');
53
+ rows.push('|---|---|');
54
+ for (const cmd of cmds) {
55
+ rows.push(`| \`construct ${cmd.name}\` | ${cmd.description} |`);
56
+ }
57
+ rows.push('');
58
+ }
59
+ return rows.join('\n').trimEnd();
60
+ }
61
+
62
+ function buildCoreDocsContract() {
63
+ return [
64
+ '## Required core documents',
65
+ '',
66
+ '| File | Purpose | Update when |',
67
+ '|---|---|---|',
68
+ '| `.cx/context.md` | Human-readable resumable project context | Active work, decisions, architecture assumptions, or open questions change |',
69
+ '| `.cx/context.json` | Machine-readable resumable context | Context state needs to stay in sync with `.cx/context.md` |',
70
+ '| `.cx/workflow.json` | Canonical workflow/task state | Non-trivial work starts, changes phase, or completes |',
71
+ '| `docs/README.md` | Docs index and maintenance contract | Core docs set or maintenance expectations change |',
72
+ '| `docs/architecture.md` | Canonical architecture and invariants | Runtime shape, contracts, boundaries, or major dependencies change |',
73
+ '',
74
+ 'All LLMs working in the repo, including Construct, must read these as project state and keep them current when work changes project reality.',
75
+ ].join('\n');
76
+ }
77
+
78
+ const DIR_DESCRIPTIONS = {
79
+ agents: 'Registry and generated platform adapter chains',
80
+ bin: 'CLI entrypoint (`construct`)',
81
+ claude: 'Claude Code integration (agents, settings template)',
82
+ commands: 'Command prompt assets',
83
+ codex: 'GitHub Copilot / Codex integration',
84
+ docs: 'Architecture notes, runbooks, and documentation contract',
85
+ langfuse: 'Langfuse trace backend for agent observability',
86
+ lib: 'Core runtime: CLI, hooks, MCP, status, sync, workflow',
87
+ opencode: 'OpenCode integration config',
88
+ personas: 'Persona prompt definitions',
89
+ rules: 'Coding and quality standards',
90
+ site: 'MkDocs source for the GitHub Pages documentation site',
91
+ skills: 'Reusable domain knowledge files',
92
+ tests: 'Test suite',
93
+ };
94
+
95
+ function buildStructureSection(rootDir) {
96
+ let trackedDirs;
97
+ try {
98
+ const out = execSync('git ls-tree --name-only HEAD', { cwd: rootDir, encoding: 'utf8' });
99
+ trackedDirs = new Set(out.trim().split('\n').filter(Boolean));
100
+ } catch {
101
+ trackedDirs = null;
102
+ }
103
+ const entries = fs.readdirSync(rootDir, { withFileTypes: true })
104
+ .filter(e => e.isDirectory() && !e.name.startsWith('.') && e.name !== 'node_modules' && (trackedDirs === null || trackedDirs.has(e.name)))
105
+ .sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
106
+
107
+ const lines = ['```text', 'construct/'];
108
+ for (const entry of entries) {
109
+ const desc = DIR_DESCRIPTIONS[entry.name] ?? '';
110
+ lines.push(`├── ${entry.name.padEnd(16)} ${desc}`.trimEnd());
111
+ }
112
+ lines.push('```');
113
+ return lines.join('\n');
114
+ }
115
+
116
+ function extractHookSummary(hookPath) {
117
+ try {
118
+ const src = fs.readFileSync(hookPath, 'utf8');
119
+ const match = src.match(/\/\*\*[\s\S]*?\*\//);
120
+ if (!match) return '';
121
+ const block = match[0].replace(/^\/\*\*|\*\/$/g, '').trim();
122
+ const lines = block.split('\n').map(l => l.replace(/^\s*\*\s?/, '').trim());
123
+ const purposeLine = lines.find(l => l.includes('—'));
124
+ if (purposeLine) {
125
+ const idx = purposeLine.indexOf('—');
126
+ return purposeLine.slice(idx + 1).trim();
127
+ }
128
+ return lines.find(l => l.length > 0) ?? '';
129
+ } catch { return ''; }
130
+ }
131
+
132
+ function buildHooksTable(rootDir) {
133
+ const hooksDir = path.join(rootDir, 'lib', 'hooks');
134
+ const files = fs.readdirSync(hooksDir)
135
+ .filter(f => f.endsWith('.mjs'))
136
+ .sort();
137
+
138
+ const rows = ['| Hook | Description |', '|---|---|'];
139
+ for (const f of files) {
140
+ const name = f.replace(/\.mjs$/, '');
141
+ const desc = extractHookSummary(path.join(hooksDir, f));
142
+ rows.push(`| \`${name}\` | ${desc} |`);
143
+ }
144
+ return rows.join('\n');
145
+ }
146
+
147
+ function buildAgentsTable(rootDir) {
148
+ const registryPath = path.join(rootDir, 'agents', 'registry.json');
149
+ let registry;
150
+ try { registry = JSON.parse(fs.readFileSync(registryPath, 'utf8')); } catch { return ''; }
151
+
152
+ const agents = registry.agents ?? registry;
153
+ if (!Array.isArray(agents)) return '';
154
+
155
+ const rows = ['| Agent | Tier | Purpose |', '|---|---|---|'];
156
+ for (const agent of agents.slice(0, 30)) {
157
+ const name = agent.id ?? agent.name ?? '—';
158
+ const tier = agent.tier ?? agent.model_tier ?? '—';
159
+ const purpose = (agent.description ?? agent.purpose ?? '').split('\n')[0].slice(0, 80);
160
+ rows.push(`| \`${name}\` | ${tier} | ${purpose} |`);
161
+ }
162
+ if (agents.length > 30) rows.push(`| *(+${agents.length - 30} more)* | | |`);
163
+ return rows.join('\n');
164
+ }
165
+
166
+ // --- public API ---
167
+
168
+ /**
169
+ * Regenerate all AUTO regions in README.md and docs/README.md/docs/architecture.md.
170
+ * Returns { changed: string[], checked: boolean }.
171
+ * With check:true writes nothing and sets changed to files that would differ.
172
+ */
173
+ export async function regenerateDocs({ rootDir, check = false } = {}) {
174
+ rootDir = rootDir ?? process.cwd();
175
+ const changed = [];
176
+
177
+ const jobs = [
178
+ {
179
+ file: path.join(rootDir, 'README.md'),
180
+ regions: {
181
+ commands: buildCommandsTable(),
182
+ structure: buildStructureSection(rootDir),
183
+ hooks: buildHooksTable(rootDir),
184
+ },
185
+ },
186
+ {
187
+ file: path.join(rootDir, 'docs', 'architecture.md'),
188
+ regions: {
189
+ agents: buildAgentsTable(rootDir),
190
+ },
191
+ },
192
+ {
193
+ file: path.join(rootDir, 'docs', 'README.md'),
194
+ regions: {
195
+ 'core-docs': buildCoreDocsContract(),
196
+ },
197
+ },
198
+ ];
199
+
200
+ for (const { file, regions } of jobs) {
201
+ let content = readFile(file);
202
+ if (!content) continue;
203
+ let modified = false;
204
+ for (const [regionName, body] of Object.entries(regions)) {
205
+ if (!body) continue;
206
+ const updated = replaceRegion(content, regionName, body);
207
+ if (updated === null) continue;
208
+ if (updated !== content) {
209
+ content = updated;
210
+ modified = true;
211
+ }
212
+ }
213
+ if (!modified) continue;
214
+ if (check) {
215
+ changed.push(file);
216
+ } else {
217
+ writeIfChanged(file, content);
218
+ changed.push(file);
219
+ }
220
+ }
221
+
222
+ return { changed, checked: check };
223
+ }
224
+
225
+ /**
226
+ * Write site/docs/ from the same generators feeding the AUTO regions.
227
+ * Invoked by `construct docs:site` before `mkdocs build`.
228
+ */
229
+ export async function buildSite({ rootDir } = {}) {
230
+ rootDir = rootDir ?? process.cwd();
231
+ const siteDocsDir = path.join(rootDir, 'site', 'docs');
232
+ fs.mkdirSync(siteDocsDir, { recursive: true });
233
+ fs.mkdirSync(path.join(siteDocsDir, 'templates'), { recursive: true });
234
+
235
+ const readOrEmpty = (p) => readFile(p) ?? '';
236
+ const writeTransformed = (src, dest, transform = (content) => content) => {
237
+ const content = readFile(src);
238
+ if (content) fs.writeFileSync(path.join(siteDocsDir, dest), transform(content));
239
+ };
240
+
241
+ // index.md — strip the "For contributors" section onwards for a user-facing landing
242
+ const readme = readOrEmpty(path.join(rootDir, 'README.md'));
243
+ const cutAt = readme.indexOf('## For contributors');
244
+ const index = cutAt > 0 ? readme.slice(0, cutAt).trimEnd() + '\n' : readme;
245
+ fs.writeFileSync(path.join(siteDocsDir, 'index.md'), index);
246
+
247
+ // commands.md — full CLI reference
248
+ const cmdLines = [
249
+ '<!--\nsite/docs/commands.md — full CLI reference, generated by construct docs:site.\nSource: lib/cli-commands.mjs\n-->\n',
250
+ '# Command reference\n',
251
+ buildCommandsTable(),
252
+ ];
253
+ fs.writeFileSync(path.join(siteDocsDir, 'commands.md'), cmdLines.join('\n'));
254
+
255
+ // hooks.md
256
+ const hooksLines = [
257
+ '<!--\nsite/docs/hooks.md — hook reference, generated by construct docs:site.\nSource: lib/hooks/\n-->\n',
258
+ '# Hooks reference\n',
259
+ 'Hooks run in every Claude Code session. They are registered in `claude/settings.template.json`.\n',
260
+ buildHooksTable(rootDir),
261
+ ];
262
+ fs.writeFileSync(path.join(siteDocsDir, 'hooks.md'), hooksLines.join('\n'));
263
+
264
+ // agents.md
265
+ const agentsTable = buildAgentsTable(rootDir);
266
+ if (agentsTable) {
267
+ const agentLines = [
268
+ '<!--\nsite/docs/agents.md — agent reference, generated by construct docs:site.\nSource: agents/registry.json\n-->\n',
269
+ '# Agent reference\n',
270
+ agentsTable,
271
+ ];
272
+ fs.writeFileSync(path.join(siteDocsDir, 'agents.md'), agentLines.join('\n'));
273
+ }
274
+
275
+ // copy static docs
276
+ for (const [src, dest] of [
277
+ [path.join(rootDir, 'docs', 'architecture.md'), 'architecture.md'],
278
+ [path.join(rootDir, 'CONTRIBUTING.md'), 'contributing.md'],
279
+ [path.join(rootDir, 'CLAUDE.md'), 'claude.md'],
280
+ ]) {
281
+ const content = readFile(src);
282
+ if (!content) continue;
283
+ const transformed = dest === 'contributing.md'
284
+ ? content.replaceAll('[CLAUDE.md](CLAUDE.md)', '[CLAUDE.md](claude.md)')
285
+ : dest === 'claude.md'
286
+ ? content.replaceAll('[docs/templates/README.md](docs/templates/README.md)', '[docs/templates/README.md](templates/README.md)')
287
+ : content;
288
+ fs.writeFileSync(path.join(siteDocsDir, dest), transformed);
289
+ }
290
+
291
+ writeTransformed(path.join(rootDir, 'docs', 'templates', 'README.md'), path.join('templates', 'README.md'));
292
+
293
+ }