@geraldmaron/construct 1.2.2 → 1.3.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 (497) hide show
  1. package/README.md +7 -10
  2. package/bin/construct +286 -220
  3. package/bin/construct-postinstall.mjs +0 -21
  4. package/commands/work/optimize-prompts.md +1 -1
  5. package/examples/distribution/sources/adr.md +2 -2
  6. package/examples/seed-observations/decisions.md +1 -1
  7. package/lib/artifact-loop-core.mjs +412 -0
  8. package/lib/artifact-manifest.mjs +6 -0
  9. package/lib/artifact-release-gate.mjs +115 -49
  10. package/lib/audit-rules.mjs +2 -4
  11. package/lib/audit-skills.mjs +32 -20
  12. package/lib/audit-specialists.mjs +44 -26
  13. package/lib/auto-docs.mjs +6 -5
  14. package/lib/brand-prose.mjs +3 -3
  15. package/lib/brand-tokens.mjs +2 -2
  16. package/lib/certification/artifact-fixtures.mjs +4 -1
  17. package/lib/certification/demo-parity.mjs +6 -32
  18. package/lib/certification/real-llm-scenarios.mjs +58 -13
  19. package/lib/certification/role-cards.mjs +7 -6
  20. package/lib/certification/role-overlays.mjs +4 -4
  21. package/lib/certification/runner.mjs +9 -2
  22. package/lib/certification/skill-inventory.mjs +34 -22
  23. package/lib/certification/skill-scenarios.mjs +21 -8
  24. package/lib/certification/specialist-contracts.mjs +4 -3
  25. package/lib/certification/specialist-scenarios.mjs +7 -6
  26. package/lib/certification/status.mjs +5 -4
  27. package/lib/cli-commands.mjs +56 -57
  28. package/lib/comment-lint.mjs +1 -5
  29. package/lib/completions.mjs +20 -2
  30. package/lib/config/legacy-config-migration.mjs +125 -0
  31. package/lib/config/schema.mjs +5 -5
  32. package/lib/config/source-targets.mjs +80 -0
  33. package/lib/contracts/validate.mjs +119 -51
  34. package/lib/decisions/golden.mjs +3 -2
  35. package/lib/decisions/registry.mjs +11 -6
  36. package/lib/demo-project.mjs +188 -0
  37. package/lib/demo-recording.mjs +9 -34
  38. package/lib/demo-script.mjs +2 -2
  39. package/lib/demo-surface.mjs +13 -131
  40. package/lib/demo-tour-renderer.mjs +92 -0
  41. package/lib/demo.mjs +69 -54
  42. package/lib/diagram-export.mjs +63 -14
  43. package/lib/doctor/source-checkout.mjs +3 -4
  44. package/lib/doctor/watchers/consistency.mjs +90 -22
  45. package/lib/doctor/watchers/credential-parity.mjs +97 -0
  46. package/lib/doctor/watchers/mcp-protocol.mjs +1 -1
  47. package/lib/doctor/watchers/service-health.mjs +4 -32
  48. package/lib/document-export.mjs +73 -9
  49. package/lib/document-extract.mjs +10 -198
  50. package/lib/embed/artifact.mjs +2 -3
  51. package/lib/embed/cli.mjs +1 -1
  52. package/lib/embed/config.mjs +1 -2
  53. package/lib/embed/customer-profiles.mjs +1 -1
  54. package/lib/embed/daemon.mjs +5 -6
  55. package/lib/embed/demand-fetch.mjs +114 -2
  56. package/lib/embed/docs-lifecycle.mjs +9 -5
  57. package/lib/embed/intake-metrics.mjs +2 -2
  58. package/lib/embed/notifications.mjs +2 -3
  59. package/lib/embed/recommendation-store.mjs +25 -22
  60. package/lib/embed/role-framing.mjs +4 -9
  61. package/lib/embedded-contract/capability.mjs +4 -2
  62. package/lib/embedded-contract/contract-version.mjs +1 -1
  63. package/lib/env-config.mjs +2 -2
  64. package/lib/flavors/loader.mjs +21 -19
  65. package/lib/graph/build-from-registry.mjs +31 -7
  66. package/lib/graph/staleness.mjs +1 -1
  67. package/lib/headhunt.mjs +82 -35
  68. package/lib/hooks/agent-tracker.mjs +2 -2
  69. package/lib/hooks/config-protection.mjs +1 -1
  70. package/lib/hooks/registry-sync.mjs +3 -3
  71. package/lib/host-capabilities.mjs +5 -5
  72. package/lib/improvement/controller.mjs +2 -2
  73. package/lib/init-unified.mjs +19 -21
  74. package/lib/intake/classify.mjs +26 -0
  75. package/lib/intake/daemon.mjs +2 -3
  76. package/lib/intake/prepare.mjs +10 -3
  77. package/lib/intake/session-prelude.mjs +1 -1
  78. package/lib/intake/tables/creative.mjs +9 -9
  79. package/lib/intake/tables/operations.mjs +4 -4
  80. package/lib/intake/tables/research.mjs +2 -2
  81. package/lib/integrations/intake-integrations.mjs +9 -10
  82. package/lib/knowledge/research-store.mjs +2 -2
  83. package/lib/mcp/server.mjs +104 -61
  84. package/lib/mcp/tool-budget.mjs +1 -46
  85. package/lib/mcp/tool-recovery.mjs +44 -0
  86. package/lib/mcp/tools/artifact-author.mjs +36 -0
  87. package/lib/mcp/tools/find-tool.mjs +86 -0
  88. package/lib/mcp/tools/orchestration-run.mjs +107 -69
  89. package/lib/mcp/tools/project.mjs +4 -3
  90. package/lib/mcp/tools/{profile.mjs → scope.mjs} +54 -52
  91. package/lib/mcp/tools/skills.mjs +39 -22
  92. package/lib/mcp/tools/telemetry.mjs +2 -1
  93. package/lib/mcp/tools/workflow.mjs +1 -1
  94. package/lib/mcp-platform-config.mjs +7 -5
  95. package/lib/migrations/index.mjs +4 -2
  96. package/lib/migrations/v2-unified-registry.mjs +35 -0
  97. package/lib/model-router.mjs +203 -36
  98. package/lib/models/catalog.mjs +25 -9
  99. package/lib/models/execution-capability-profile.mjs +3 -3
  100. package/lib/models/execution-policy.mjs +7 -6
  101. package/lib/models/provider-poll.mjs +18 -4
  102. package/lib/opencode-config.mjs +56 -1
  103. package/lib/opencode-runtime-plugin.mjs +13 -10
  104. package/lib/oracle/artifact-gate.mjs +14 -4
  105. package/lib/oracle/cli.mjs +2 -0
  106. package/lib/oracle/dispatch.mjs +18 -1
  107. package/lib/oracle/execute.mjs +39 -7
  108. package/lib/oracle/index.mjs +1 -1
  109. package/lib/oracle/org-graph.mjs +10 -5
  110. package/lib/oracle/policy.mjs +6 -0
  111. package/lib/oracle/read-model.mjs +103 -6
  112. package/lib/oracle/reconcile.mjs +31 -4
  113. package/lib/oracle/remediation-dispatch.mjs +53 -0
  114. package/lib/oracle/routing.mjs +5 -0
  115. package/lib/oracle/synthesize.mjs +80 -3
  116. package/lib/orchestration/routing-tables.mjs +44 -10
  117. package/lib/orchestration/runtime.mjs +2 -0
  118. package/lib/orchestration/worker.mjs +1 -1
  119. package/lib/orchestration-policy.mjs +351 -62
  120. package/lib/overrides/resolver.mjs +1 -12
  121. package/lib/parity.mjs +37 -11
  122. package/lib/policy/engine.mjs +80 -15
  123. package/lib/prompt-composer.js +51 -15
  124. package/lib/prompt-metadata.mjs +3 -2
  125. package/lib/providers/connection-probe.mjs +58 -0
  126. package/lib/providers/copilot-auth.mjs +1 -1
  127. package/lib/providers/credential-bootstrap.mjs +1 -1
  128. package/lib/providers/op-run.mjs +3 -4
  129. package/lib/providers/secret-resolver.mjs +31 -0
  130. package/lib/publish-tooling.mjs +3 -11
  131. package/lib/publish.mjs +12 -29
  132. package/lib/reconcile/mcp-entry-reconcile.mjs +14 -9
  133. package/lib/reflect.mjs +2 -2
  134. package/lib/registry/agent-manifest.mjs +190 -0
  135. package/lib/registry/assemble.mjs +133 -0
  136. package/lib/registry/catalog.mjs +171 -0
  137. package/lib/registry/cli.mjs +590 -6
  138. package/lib/registry/consolidation.mjs +5 -4
  139. package/lib/registry/docs-sync.mjs +67 -0
  140. package/lib/registry/loader.mjs +147 -0
  141. package/lib/registry/org-io.mjs +76 -0
  142. package/lib/registry/retired-paths.mjs +71 -0
  143. package/lib/registry/surface-map.mjs +5 -7
  144. package/lib/registry/validator.mjs +438 -0
  145. package/lib/research-execution-policy.mjs +173 -0
  146. package/lib/roles/catalog.mjs +4 -9
  147. package/lib/roles/fence.mjs +107 -1
  148. package/lib/roles/flavor-bindings.mjs +68 -0
  149. package/lib/roles/gateway.mjs +140 -29
  150. package/lib/roles/manifest.mjs +22 -13
  151. package/lib/roles/router.mjs +1 -1
  152. package/lib/runtime-env.mjs +1 -1
  153. package/lib/scopes/enrich.mjs +67 -0
  154. package/lib/scopes/hooks.mjs +12 -0
  155. package/lib/{profiles → scopes}/lifecycle.mjs +70 -70
  156. package/lib/scopes/loader.mjs +104 -0
  157. package/lib/scopes/rebrand.mjs +32 -0
  158. package/lib/scopes/research-profile.mjs +16 -0
  159. package/lib/scopes/teams.mjs +98 -0
  160. package/lib/service-manager.mjs +1 -117
  161. package/lib/setup-prompts.mjs +1 -1
  162. package/lib/setup.mjs +17 -28
  163. package/lib/skills/composition-graph.mjs +98 -0
  164. package/lib/skills/router.mjs +80 -0
  165. package/lib/specialist-contracts.mjs +17 -18
  166. package/lib/specialists/postconditions.mjs +2 -2
  167. package/lib/specialists/prompt-schema.mjs +6 -5
  168. package/lib/specialists/roster.mjs +7 -12
  169. package/lib/specialists/schema.mjs +9 -6
  170. package/lib/status.mjs +25 -90
  171. package/lib/storage/file-lock.mjs +6 -3
  172. package/lib/telemetry/skill-calls.mjs +1 -1
  173. package/lib/templates/doc-presentation.mjs +63 -0
  174. package/lib/templates/visual-requirements.mjs +35 -2
  175. package/lib/term-format.mjs +107 -2
  176. package/lib/test-env-setup.mjs +21 -0
  177. package/lib/ui/components.mjs +42 -0
  178. package/lib/ui/glyphs.mjs +58 -0
  179. package/lib/ui/links.mjs +115 -0
  180. package/lib/ui/theme.mjs +108 -0
  181. package/lib/uninstall/uninstall.mjs +2 -1
  182. package/lib/validator.mjs +45 -8
  183. package/lib/validators/skill-effectiveness.mjs +174 -0
  184. package/lib/validators/skills.mjs +1 -1
  185. package/package.json +12 -7
  186. package/personas/construct.md +12 -3
  187. package/platforms/claude/CLAUDE.md +1 -1
  188. package/rules/common/beads-hygiene.md +52 -0
  189. package/rules/common/neurodivergent-output.md +4 -7
  190. package/rules/common/research.md +2 -0
  191. package/scripts/sync-specialists.mjs +260 -49
  192. package/skills/ai/prompt-optimizer.md +3 -3
  193. package/skills/brand/output-vibe.md +48 -0
  194. package/skills/docs/adr-workflow.md +3 -0
  195. package/skills/docs/backlog-proposal-workflow.md +3 -0
  196. package/skills/docs/codebase-research-workflow.md +5 -2
  197. package/skills/docs/customer-profile-workflow.md +3 -0
  198. package/skills/docs/document-ingest-workflow.md +3 -0
  199. package/skills/docs/evidence-ingest-workflow.md +4 -1
  200. package/skills/docs/init-project.md +1 -1
  201. package/skills/docs/prd-workflow.md +1 -1
  202. package/skills/docs/prfaq-workflow.md +3 -0
  203. package/skills/docs/product-intelligence-workflow.md +4 -1
  204. package/skills/docs/product-signal-workflow.md +3 -0
  205. package/skills/docs/research-workflow.md +13 -6
  206. package/skills/docs/runbook-workflow.md +3 -0
  207. package/skills/docs/strategy-workflow.md +3 -0
  208. package/skills/docs/user-research-workflow.md +6 -3
  209. package/skills/operating/orchestration-reference.md +1 -1
  210. package/skills/roles/{engineer.ai.md → ai-engineer.md} +2 -2
  211. package/skills/roles/architect.ai-systems.md +1 -1
  212. package/skills/roles/architect.data.md +1 -1
  213. package/skills/roles/architect.enterprise.md +1 -1
  214. package/skills/roles/architect.integration.md +1 -1
  215. package/skills/roles/architect.md +1 -1
  216. package/skills/roles/architect.platform.md +1 -1
  217. package/skills/roles/{product-manager.business-strategy.md → business-strategist.md} +3 -3
  218. package/skills/roles/data-analyst.experiment.md +1 -1
  219. package/skills/roles/data-analyst.md +1 -1
  220. package/skills/roles/data-analyst.product-intelligence.md +1 -1
  221. package/skills/roles/data-analyst.product.md +1 -1
  222. package/skills/roles/data-analyst.telemetry.md +1 -1
  223. package/skills/roles/{engineer.data.md → data-engineer.md} +2 -2
  224. package/skills/roles/data-engineer.pipeline.md +2 -2
  225. package/skills/roles/data-engineer.vector-retrieval.md +2 -2
  226. package/skills/roles/data-engineer.warehouse.md +2 -2
  227. package/skills/roles/debugger.md +1 -1
  228. package/skills/roles/designer.accessibility.md +1 -1
  229. package/skills/roles/designer.md +1 -1
  230. package/skills/roles/{reviewer.devil-advocate.md → devil-advocate.md} +2 -2
  231. package/skills/roles/{operator.docs.md → docs-keeper.md} +3 -3
  232. package/skills/roles/engineer.md +3 -7
  233. package/skills/roles/{reviewer.evaluator.md → evaluator.md} +2 -2
  234. package/skills/roles/{researcher.explorer.md → explorer.md} +2 -2
  235. package/skills/roles/{operator.md → operations.md} +2 -5
  236. package/skills/roles/orchestrator.md +1 -1
  237. package/skills/roles/{engineer.platform.md → platform-engineer.md} +2 -2
  238. package/skills/roles/product-manager.ai-product.md +1 -1
  239. package/skills/roles/product-manager.enterprise.md +1 -1
  240. package/skills/roles/product-manager.growth.md +1 -2
  241. package/skills/roles/product-manager.md +1 -2
  242. package/skills/roles/product-manager.platform.md +1 -1
  243. package/skills/roles/product-manager.product.md +1 -1
  244. package/skills/roles/qa.ai-eval.md +1 -1
  245. package/skills/roles/qa.api-contract.md +1 -1
  246. package/skills/roles/qa.data-pipeline.md +1 -1
  247. package/skills/roles/qa.md +1 -1
  248. package/skills/roles/qa.web-ui.md +1 -1
  249. package/skills/roles/{operator.release.md → release-manager.md} +3 -3
  250. package/skills/roles/researcher.md +1 -1
  251. package/skills/roles/reviewer.md +1 -1
  252. package/skills/roles/security.ai.md +1 -1
  253. package/skills/roles/security.appsec.md +1 -1
  254. package/skills/roles/security.cloud.md +1 -1
  255. package/skills/roles/security.legal-compliance.md +1 -1
  256. package/skills/roles/security.md +1 -1
  257. package/skills/roles/security.privacy.md +1 -1
  258. package/skills/roles/security.supply-chain.md +1 -1
  259. package/skills/roles/{operator.sre.md → sre.md} +3 -3
  260. package/skills/roles/{qa.test-automation.md → test-automation.md} +2 -2
  261. package/skills/roles/{reviewer.trace.md → trace-reviewer.md} +2 -2
  262. package/skills/roles/{researcher.ux.md → ux-researcher.md} +2 -2
  263. package/skills/routing.json +18 -0
  264. package/skills/routing.md +1 -1
  265. package/specialists/artifact-manifest.json +2 -1
  266. package/specialists/audit-enrichments.json +14 -14
  267. package/specialists/org/contracts/accessibility-to-qa.json +37 -0
  268. package/specialists/org/contracts/any-to-business-strategist.json +57 -0
  269. package/specialists/org/contracts/any-to-debugger.json +38 -0
  270. package/specialists/org/contracts/any-to-designer.json +33 -0
  271. package/specialists/org/contracts/any-to-docs-keeper.json +34 -0
  272. package/specialists/org/contracts/any-to-explorer.json +39 -0
  273. package/specialists/org/contracts/any-to-sre-incident.json +33 -0
  274. package/specialists/org/contracts/any-to-trace-reviewer.json +32 -0
  275. package/specialists/org/contracts/architect-to-ai-engineer.json +32 -0
  276. package/specialists/org/contracts/architect-to-data-engineer.json +52 -0
  277. package/specialists/org/contracts/architect-to-devil-advocate.json +38 -0
  278. package/specialists/org/contracts/architect-to-engineer.json +34 -0
  279. package/specialists/org/contracts/architect-to-evaluator.json +59 -0
  280. package/specialists/org/contracts/architect-to-legal-compliance.json +55 -0
  281. package/specialists/org/contracts/architect-to-operations.json +45 -0
  282. package/specialists/org/contracts/architect-to-platform-engineer.json +42 -0
  283. package/specialists/org/contracts/business-strategist-to-product-manager.json +39 -0
  284. package/specialists/org/contracts/construct-to-orchestrator.json +36 -0
  285. package/specialists/org/contracts/construct-to-rd-lead.json +53 -0
  286. package/specialists/org/contracts/data-analyst-to-product-manager.json +43 -0
  287. package/specialists/org/contracts/data-engineer-to-platform-engineer.json +36 -0
  288. package/specialists/org/contracts/designer-to-accessibility.json +36 -0
  289. package/specialists/org/contracts/engineer-to-qa.json +34 -0
  290. package/specialists/org/contracts/engineer-to-reviewer.json +52 -0
  291. package/specialists/org/contracts/explorer-to-engineer.json +38 -0
  292. package/specialists/org/contracts/legal-compliance-to-release-manager.json +43 -0
  293. package/specialists/org/contracts/platform-engineer-to-engineer.json +31 -0
  294. package/specialists/org/contracts/product-manager-to-architect.json +71 -0
  295. package/specialists/org/contracts/product-manager-to-data-analyst.json +49 -0
  296. package/specialists/org/contracts/product-manager-to-ux-researcher.json +51 -0
  297. package/specialists/org/contracts/qa-to-release-manager.json +42 -0
  298. package/specialists/org/contracts/qa-to-test-automation.json +42 -0
  299. package/specialists/org/contracts/rd-lead-to-architect.json +38 -0
  300. package/specialists/org/contracts/researcher-to-architect.json +40 -0
  301. package/specialists/org/contracts/researcher-to-product-manager.json +57 -0
  302. package/specialists/org/contracts/reviewer-to-security.json +41 -0
  303. package/specialists/org/contracts/sre-to-release-manager.json +36 -0
  304. package/specialists/org/contracts/test-automation-to-engineer.json +34 -0
  305. package/specialists/org/contracts/trace-reviewer-to-sre.json +41 -0
  306. package/specialists/org/contracts/user-to-construct.json +30 -0
  307. package/specialists/org/groups/engineering-group.json +43 -0
  308. package/specialists/org/groups/governance-group.json +38 -0
  309. package/specialists/org/groups/operations-group.json +41 -0
  310. package/specialists/org/groups/product-group.json +46 -0
  311. package/specialists/org/groups/quality-group.json +42 -0
  312. package/specialists/org/groups/strategy-group.json +41 -0
  313. package/specialists/org/policies/action-approval.json +13 -0
  314. package/specialists/org/policies/agents-routing.json +13 -0
  315. package/specialists/org/policies/architecture.json +20 -0
  316. package/specialists/org/policies/bash-safety.json +13 -0
  317. package/specialists/org/policies/beads-hygiene.json +13 -0
  318. package/specialists/org/policies/bootstrap-state.json +13 -0
  319. package/specialists/org/policies/code-review.json +13 -0
  320. package/specialists/org/policies/coding-style.json +13 -0
  321. package/specialists/org/policies/comments.json +13 -0
  322. package/specialists/org/policies/commit-approval.json +13 -0
  323. package/specialists/org/policies/contract-preconditions.json +13 -0
  324. package/specialists/org/policies/deployment.json +20 -0
  325. package/specialists/org/policies/description.json +14 -0
  326. package/specialists/org/policies/design-approval.json +19 -0
  327. package/specialists/org/policies/doc-ownership.json +13 -0
  328. package/specialists/org/policies/file-path-fence.json +13 -0
  329. package/specialists/org/policies/framing.json +13 -0
  330. package/specialists/org/policies/git-workflow.json +13 -0
  331. package/specialists/org/policies/incident-response.json +15 -0
  332. package/specialists/org/policies/intake-triage.json +15 -0
  333. package/specialists/org/policies/neurodivergent-output.json +13 -0
  334. package/specialists/org/policies/no-fabrication.json +13 -0
  335. package/specialists/org/policies/patterns.json +13 -0
  336. package/specialists/org/policies/quality-gate-approval.json +17 -0
  337. package/specialists/org/policies/release-gates.json +13 -0
  338. package/specialists/org/policies/research-evidence.json +13 -0
  339. package/specialists/org/policies/review-before-change.json +13 -0
  340. package/specialists/org/policies/rollback.json +15 -0
  341. package/specialists/org/policies/scope-change.json +20 -0
  342. package/specialists/org/policies/secret-scan.json +13 -0
  343. package/specialists/org/policies/security-approval.json +17 -0
  344. package/specialists/org/policies/security.json +13 -0
  345. package/specialists/org/policies/session-efficiency.json +13 -0
  346. package/specialists/org/policies/skill-routing.json +13 -0
  347. package/specialists/org/policies/strategic-prioritization.json +17 -0
  348. package/specialists/org/policies/testing.json +13 -0
  349. package/specialists/org/policies/tool-invisibility.json +14 -0
  350. package/specialists/org/scopes/creative.json +54 -0
  351. package/specialists/org/scopes/operations.json +51 -0
  352. package/specialists/org/scopes/research.json +60 -0
  353. package/specialists/org/scopes/rnd.json +81 -0
  354. package/specialists/org/specialists/cx-accessibility.json +69 -0
  355. package/specialists/org/specialists/cx-ai-engineer.json +75 -0
  356. package/specialists/org/specialists/cx-architect.json +89 -0
  357. package/specialists/org/specialists/cx-business-strategist.json +72 -0
  358. package/specialists/org/specialists/cx-data-analyst.json +68 -0
  359. package/specialists/org/specialists/cx-data-engineer.json +71 -0
  360. package/specialists/org/specialists/cx-debugger.json +75 -0
  361. package/specialists/org/specialists/cx-designer.json +85 -0
  362. package/specialists/org/specialists/cx-devil-advocate.json +71 -0
  363. package/specialists/org/specialists/cx-docs-keeper.json +82 -0
  364. package/specialists/org/specialists/cx-engineer.json +106 -0
  365. package/specialists/org/specialists/cx-evaluator.json +69 -0
  366. package/specialists/org/specialists/cx-explorer.json +69 -0
  367. package/specialists/org/specialists/cx-legal-compliance.json +74 -0
  368. package/specialists/org/specialists/cx-operations.json +72 -0
  369. package/specialists/org/specialists/cx-oracle.json +46 -0
  370. package/specialists/org/specialists/cx-orchestrator.json +81 -0
  371. package/specialists/org/specialists/cx-platform-engineer.json +80 -0
  372. package/specialists/org/specialists/cx-product-manager.json +102 -0
  373. package/specialists/org/specialists/cx-qa.json +79 -0
  374. package/specialists/org/specialists/cx-rd-lead.json +73 -0
  375. package/specialists/org/specialists/cx-release-manager.json +77 -0
  376. package/specialists/org/specialists/cx-researcher.json +82 -0
  377. package/specialists/org/specialists/cx-reviewer.json +71 -0
  378. package/specialists/org/specialists/cx-security.json +91 -0
  379. package/specialists/org/specialists/cx-sre.json +94 -0
  380. package/specialists/org/specialists/cx-test-automation.json +69 -0
  381. package/specialists/org/specialists/cx-trace-reviewer.json +69 -0
  382. package/specialists/org/specialists/cx-ux-researcher.json +68 -0
  383. package/specialists/org/teams/accessibility-team.json +39 -0
  384. package/specialists/org/teams/design-team.json +40 -0
  385. package/specialists/org/teams/engineering-team.json +50 -0
  386. package/specialists/org/teams/governance-team.json +41 -0
  387. package/specialists/org/teams/operations-team.json +46 -0
  388. package/specialists/org/teams/product-management-team.json +45 -0
  389. package/specialists/org/teams/quality-team.json +49 -0
  390. package/specialists/org/teams/research-team.json +39 -0
  391. package/specialists/org/teams/strategy-team.json +48 -0
  392. package/specialists/org/teams/ux-research-team.json +39 -0
  393. package/specialists/prompts/_shared/validation-contract.md +1 -1
  394. package/specialists/prompts/_team-template.md +10 -0
  395. package/specialists/prompts/cx-accessibility.md +1 -0
  396. package/specialists/prompts/cx-ai-engineer.md +1 -1
  397. package/specialists/prompts/cx-business-strategist.md +1 -1
  398. package/specialists/prompts/cx-data-engineer.md +1 -1
  399. package/specialists/prompts/cx-designer.md +1 -0
  400. package/specialists/prompts/cx-devil-advocate.md +1 -1
  401. package/specialists/prompts/cx-docs-keeper.md +1 -1
  402. package/specialists/prompts/cx-evaluator.md +1 -1
  403. package/specialists/prompts/cx-explorer.md +1 -1
  404. package/specialists/prompts/cx-operations.md +1 -1
  405. package/specialists/prompts/cx-oracle.md +7 -3
  406. package/specialists/prompts/cx-orchestrator.md +17 -1
  407. package/specialists/prompts/cx-platform-engineer.md +1 -1
  408. package/specialists/prompts/cx-product-manager.md +2 -0
  409. package/specialists/prompts/cx-release-manager.md +1 -1
  410. package/specialists/prompts/cx-researcher.md +7 -4
  411. package/specialists/prompts/cx-sre.md +1 -1
  412. package/specialists/prompts/cx-test-automation.md +2 -2
  413. package/specialists/prompts/cx-trace-reviewer.md +3 -3
  414. package/specialists/prompts/cx-ux-researcher.md +2 -1
  415. package/templates/demos/scripts/architecture-review-adr.json +30 -0
  416. package/templates/demos/scripts/capability-contract.json +25 -0
  417. package/templates/demos/scripts/intake-triage.json +25 -0
  418. package/templates/demos/scripts/profile-doctor-health.json +25 -0
  419. package/templates/demos/tapes/agentic-platforms-prd.tape +2 -2
  420. package/templates/demos/tapes/architecture-review-adr.tape +44 -0
  421. package/templates/demos/tapes/capability-contract.tape +39 -0
  422. package/templates/demos/tapes/intake-triage.tape +39 -0
  423. package/templates/demos/tapes/profile-doctor-health.tape +39 -0
  424. package/templates/distribution/construct-brand.typ +23 -18
  425. package/templates/distribution/construct-decision.typ +1 -1
  426. package/templates/distribution/construct-pdf.typ +1 -1
  427. package/templates/distribution/construct-prd.typ +1 -1
  428. package/templates/distribution/construct-research.typ +1 -1
  429. package/templates/distribution/mermaid-puppeteer.json +8 -0
  430. package/templates/docs/persona-artifact.md +1 -1
  431. package/templates/docs/prd.md +6 -0
  432. package/lib/boundary.mjs +0 -127
  433. package/lib/certification/dashboard-api.mjs +0 -71
  434. package/lib/chat/cli.mjs +0 -333
  435. package/lib/chat/command-suggest.mjs +0 -161
  436. package/lib/chat/commands.mjs +0 -215
  437. package/lib/chat/config.mjs +0 -142
  438. package/lib/chat/context-compactor.mjs +0 -250
  439. package/lib/chat/context-continuation.mjs +0 -253
  440. package/lib/chat/continuation-source.mjs +0 -58
  441. package/lib/chat/demo-guide.mjs +0 -61
  442. package/lib/chat/design-tokens.mjs +0 -91
  443. package/lib/chat/desktop-binary.mjs +0 -81
  444. package/lib/chat/desktop-build.mjs +0 -130
  445. package/lib/chat/desktop-launcher.mjs +0 -133
  446. package/lib/chat/evidence.mjs +0 -145
  447. package/lib/chat/export.mjs +0 -74
  448. package/lib/chat/harness/driver.mjs +0 -91
  449. package/lib/chat/list-picker.mjs +0 -112
  450. package/lib/chat/model-picker.mjs +0 -356
  451. package/lib/chat/openrouter-fallback.mjs +0 -151
  452. package/lib/chat/permission-prompt.mjs +0 -33
  453. package/lib/chat/picker-catalog.mjs +0 -45
  454. package/lib/chat/policy-telemetry.mjs +0 -34
  455. package/lib/chat/present.mjs +0 -246
  456. package/lib/chat/session-context.mjs +0 -39
  457. package/lib/chat/session-persist.mjs +0 -73
  458. package/lib/chat/session-restore.mjs +0 -71
  459. package/lib/chat/session-settings.mjs +0 -53
  460. package/lib/chat/system-prompt.mjs +0 -52
  461. package/lib/chat/transparency.mjs +0 -93
  462. package/lib/chat/tui/color-scheme.mjs +0 -42
  463. package/lib/chat/tui/markdown.mjs +0 -123
  464. package/lib/chat/tui/presentation.mjs +0 -100
  465. package/lib/chat/tui/render.mjs +0 -500
  466. package/lib/chat/tui/turn-block.mjs +0 -284
  467. package/lib/chat/tui/turn-present.mjs +0 -18
  468. package/lib/chat/tui/turn-state.mjs +0 -88
  469. package/lib/chat/tui/usage.mjs +0 -122
  470. package/lib/chat/web-commands.mjs +0 -146
  471. package/lib/chat/web-launcher.mjs +0 -63
  472. package/lib/chat/web-picker-keys.mjs +0 -46
  473. package/lib/chat/web-session.mjs +0 -159
  474. package/lib/config/alias.mjs +0 -57
  475. package/lib/dashboard-demo.mjs +0 -71
  476. package/lib/dashboard-static.mjs +0 -175
  477. package/lib/install/desktop-binary-download.mjs +0 -88
  478. package/lib/profiles/loader.mjs +0 -123
  479. package/lib/profiles/rebrand.mjs +0 -46
  480. package/lib/server/auth.mjs +0 -169
  481. package/lib/server/chat-loop.mjs +0 -622
  482. package/lib/server/chat.mjs +0 -336
  483. package/lib/server/cors.mjs +0 -77
  484. package/lib/server/csrf.mjs +0 -103
  485. package/lib/server/demo-preview.mjs +0 -63
  486. package/lib/server/index.mjs +0 -2641
  487. package/lib/server/insights.mjs +0 -780
  488. package/lib/server/langfuse-login.mjs +0 -58
  489. package/lib/server/rate-limit.mjs +0 -93
  490. package/lib/server/telemetry-login.mjs +0 -100
  491. package/lib/server/webhook.mjs +0 -512
  492. package/specialists/contracts.json +0 -1032
  493. package/specialists/contracts.schema.json +0 -83
  494. package/specialists/policy-inventory.json +0 -188
  495. package/specialists/registry.json +0 -1412
  496. package/specialists/role-manifests.json +0 -217
  497. package/specialists/teams.json +0 -94
@@ -67,3 +67,55 @@ There is no authorized bypass. Beads hygiene is a release gate. If the tooling i
67
67
  ## Automation
68
68
 
69
69
  Project-level automation is tracked in the beads queue: auto-close on merge, pre-push `bd preflight` gate, weekly drift report, memory-contradiction detection. Until that ships, hygiene is a per-session discipline.
70
+
71
+ ## Execution protocol (construct-m7k2 / feature/team-redesign)
72
+
73
+ Every child bead under epic `construct-m7k2` uses **double validation** before close. Definitions are normative for agents on any model tier (including smaller models).
74
+
75
+ ### Pass A — implementer
76
+
77
+ The agent that claimed the bead and wrote the code:
78
+
79
+ 1. Run scoped tests cited in the bead acceptance criteria (`npm test` or `node --test tests/...`).
80
+ 2. Run bead-specific grep or CLI assertions listed in the bead body.
81
+ 3. Record command output in `bd note <id>` or the close reason (exit codes, failure counts).
82
+
83
+ Pass A is not done if tests were not executed in this environment.
84
+
85
+ ### Pass B — reviewer (different model or fresh session)
86
+
87
+ A different agent, or the same agent in a new session with no assumed memory of Pass A:
88
+
89
+ 1. Re-run every Pass A command independently (do not trust prior transcripts).
90
+ 2. Run `npm run release:check`.
91
+ 3. Run `construct doctor` — no new team/auth/MCP warnings attributable to the change.
92
+ 4. Confirm no `CONSTRUCT_SKIP_*` / `CONSTRUCT_ALLOW_*` env vars were introduced.
93
+
94
+ Pass B is not done if `release:check` was not run.
95
+
96
+ ### Parallel runs — verify the committed branch, never a stray worktree
97
+
98
+ When beads are implemented by parallel subagents in isolated git worktrees, the verify loop must check the **implementer's committed work**, not a fresh tree branched from the base:
99
+
100
+ 1. **Implementers commit to a named branch before review.** No uncommitted worktree state may gate a close — uncommitted work is invisible to a fresh reviewer and is lost when the worktree is pruned.
101
+ 2. **Reviewers check out and verify that exact branch** (or the integration branch after merge). A reviewer that branches from the base commit is verifying the *wrong tree*.
102
+ 3. **Integration merges the reviewed branch**, then re-runs the close evidence on the merged result.
103
+ 4. **Close evidence is reproduced on the merged branch** — e.g. grep the new symbol on the branch, not in a worktree. A symbol that exists only in a worktree is not landed.
104
+
105
+ Failure mode this prevents (observed 2026-06-25): a bead was closed with a reason naming files that were never committed — the work was stranded uncommitted in a worktree, the reviewer branched from base and "passed" the pre-existing tests, and pruning the worktree discarded the work. A close reason must name only what is reproducible on the branch.
106
+
107
+ ### Baseline-diff close gate
108
+
109
+ When the suite is not green (pre-existing unrelated failures), "all tests pass" is unsatisfiable and invites premature closes. Use a baseline-diff instead:
110
+
111
+ 1. Capture the failing-test set once at the start (e.g. `.cx/artifacts/baseline-fails.json`).
112
+ 2. A bead may close only if its scoped tests pass **and** the full suite introduces **zero new failures** vs the baseline. Newly-passing tests are a bonus, not a requirement.
113
+ 3. A test that fails on genuinely out-of-scope incomplete logic is marked `node:test` `todo` with a tracking bead id — flagged, not hidden — never silently skipped.
114
+
115
+ ### Bead body template
116
+
117
+ Child beads must include sections: Goal, Read first, Out of scope, Steps, Acceptance criteria, Pass A checklist, Pass B checklist, Do not touch. See `construct-m7k2.9` for the gate bead that introduced this contract.
118
+
119
+ ### Gate bead
120
+
121
+ Claim `construct-m7k2.9` first on the epic branch to land this section, then proceed to `construct-m7k2-fix-tests` and downstream work per the dependency graph.
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  description: every human-facing Construct output is formatted for neurodivergent readers — without reshaping machine-readable output.
3
- enforced_by: (persona prompt), tests/term-format.test.mjs, construct dashboard:sync --check
3
+ enforced_by: (persona prompt), tests/term-format.test.mjs
4
4
  precedence_tier: style
5
5
  ---
6
6
  # Neurodivergent-Friendly Output
7
7
 
8
- Construct's output is read under load — in a terminal, mid-task, often by people who process text differently. Dense walls of prose, buried conclusions, inconsistent structure, and meaning carried only by color cost every reader attention, and cost neurodivergent readers more. This rule applies to **every human-facing output** Construct produces: terminal output, agent prose to the user, reports, error messages, and the dashboard.
8
+ Construct's output is read under load — in a terminal, mid-task, often by people who process text differently. Dense walls of prose, buried conclusions, inconsistent structure, and meaning carried only by color cost every reader attention, and cost neurodivergent readers more. This rule applies to **every human-facing output** Construct produces: terminal output, agent prose to the user, reports, and error messages.
9
9
 
10
10
  It is a `style` rule by design. The precedence resolver (`rules/common/precedence.md`) guarantees a style rule never overrides a higher tier — so when accessible formatting would conflict with correct machine output, correctness wins automatically. Section 0 states that boundary explicitly.
11
11
 
@@ -13,8 +13,8 @@ It is a `style` rule by design. The precedence resolver (`rules/common/precedenc
13
13
 
14
14
  Two layers, treated differently:
15
15
 
16
- - **Presentation layer (human-facing)** — section titles, prose, help text, report narratives, guidance notes, error wording, dashboard markup and CSS. This is what the rest of this rule shapes.
17
- - **Data layer (machine-readable)** — never reshape it for readability. Off-limits: any `--json` / `--plain` output; `specialists/registry.json`, `contracts.json`, `role-manifests.json`; the parsed tokens hooks emit (for example the session-start `## Working branch: **<name>**` line, the efficiency status enum `degraded` / `configured` / `healthy`, commit hashes, counts); auxiliary state JSON; any text one component parses out of another.
16
+ - **Presentation layer (human-facing)** — section titles, prose, help text, report narratives, guidance notes, error wording. This is what the rest of this rule shapes.
17
+ - **Data layer (machine-readable)** — never reshape it for readability. Off-limits: any `--json` / `--plain` output; `specialists/org`, `contracts.json`, `role-manifests.json`; the parsed tokens hooks emit (for example the session-start `## Working branch: **<name>**` line, the efficiency status enum `degraded` / `configured` / `healthy`, commit hashes, counts); auxiliary state JSON; any text one component parses out of another.
18
18
 
19
19
  When a format choice would change a value, key, ordering, or token that something downstream parses, do not make it. Accessibility is presentation; it never edits the contract.
20
20
 
@@ -47,19 +47,16 @@ End human-facing output with the current state and the next action in plain word
47
47
  - Meaning carried by color must also be carried by text, shape, or label. A red number is also labelled `error`; a green check is also the word `passed`.
48
48
  - Honor `NO_COLOR`, non-TTY, and `TERM=dumb`: degrade to plain text, never to nothing.
49
49
  - Respect terminal width — wrap rather than overflow.
50
- - On the dashboard, honor `prefers-reduced-motion` and `prefers-color-scheme`; motion is never required to understand state, and animation can be fully disabled.
51
50
 
52
51
  ## Applies to
53
52
 
54
53
  - **Terminal / CLI** — help text, status, reports, hook display. Routed through the shared formatter (`lib/term-format.mjs`); color and width handled there, never per-call.
55
54
  - **Agent prose** — every response Construct and its specialists return to the user.
56
- - **Dashboard** — `apps/dashboard/` source (semantic landmarks, labels, focus, contrast, reduced-motion), rebuilt into `lib/server/static/`.
57
55
 
58
56
  ## Enforcement
59
57
 
60
58
  - Persona prompt: `personas/construct.md` carries the output-style directive; specialists inherit the standard through the front-door agent.
61
59
  - `tests/term-format.test.mjs` asserts the terminal formatter honors `NO_COLOR` / non-TTY and that machine-readable `--json` output is unchanged by presentation changes.
62
- - `construct dashboard:sync --check` keeps the built dashboard in step with the accessible source.
63
60
 
64
61
  ## Bypass
65
62
 
@@ -58,6 +58,8 @@ Start with the narrowest authoritative source that can answer the question:
58
58
  4. **Tertiary sources last**
59
59
  - blogs, forums, Q&A, analyst summaries, AI-generated summaries
60
60
 
61
+ When the research is specifically about a library/framework/API/cloud-docs surface, a docs MCP such as Context7 is an acceptable way to reach the official documentation. It is a retrieval path, not the evidence standard. If that MCP is unavailable or incomplete, fall back to direct web search and fetch against the official docs, changelog, and release notes. Do not block research on the presence of a single docs integration.
62
+
61
63
  ## 4. Required metadata for every source
62
64
 
63
65
  Record:
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * sync-specialists.mjs — regenerate agent adapters for all platforms from specialists/registry.json.
3
+ * sync-specialists.mjs — regenerate agent adapters for all platforms from specialists/org.
4
4
  *
5
5
  * Two scopes, modelled on every host's own convention (global = personal default,
6
6
  * project = team-shared):
@@ -31,6 +31,7 @@
31
31
  import fs from "node:fs";
32
32
  import path from "node:path";
33
33
  import os from "node:os";
34
+ import { fileURLToPath } from "node:url";
34
35
  import { generateCompletions } from "../lib/completions.mjs";
35
36
  import {
36
37
  buildCodexMcpEntry,
@@ -43,7 +44,7 @@ import {
43
44
  tomlString,
44
45
  writeCodexConfig,
45
46
  } from "../lib/codex-config.mjs";
46
- import { findOpenCodeConfigPath, readOpenCodeConfig, writeOpenCodeConfig } from "../lib/opencode-config.mjs";
47
+ import { findOpenCodeConfigPath, readOpenCodeConfig, writeOpenCodeConfig, ensureOpenRouterProviderAuth } from "../lib/opencode-config.mjs";
47
48
  import { HEAVY_EXTERNAL_MCP_IDS, LOCAL_SURFACE_MODES, decideTrim, isLocalModel } from "../lib/mcp/tool-budget.mjs";
48
49
  import { emitCursorRules } from "../lib/rules-delivery.mjs";
49
50
  import { resolvePromptContract, readPromptBody } from "../lib/prompt-composer.js";
@@ -59,21 +60,67 @@ import { configDir } from "../lib/config/xdg.mjs";
59
60
  import { inlineRoleAntiPatterns, PROMPT_WORD_CAP } from "../lib/role-preload.mjs";
60
61
  import { inlineValidationContract } from "../lib/prompt-validation-contract.mjs";
61
62
  import { loadManifest } from "../lib/roles/manifest.mjs";
62
- import { resolveActiveProfile } from "../lib/profiles/loader.mjs";
63
+ import { resolveActiveScope } from "../lib/scopes/loader.mjs";
63
64
  import { resolveTiersForPrimary, resolveCapabilityTier, selectLocalEditorModel } from "../lib/model-router.mjs";
64
65
  import { stampFrontmatter } from "../lib/doc-stamp.mjs";
65
66
  import { buildSkillFrontmatter, stripLeadingFrontmatter } from "../lib/sync/skill-frontmatter.mjs";
67
+ import { loadRegistry, clearCache } from "../lib/registry/loader.mjs";
68
+ import { loadPluginRegistry } from "../lib/plugin-registry.mjs";
66
69
 
67
70
  const home = os.homedir();
68
71
  const root = path.resolve(import.meta.dirname, "..");
72
+ const isMain = process.argv[1] ? path.resolve(process.argv[1]) === fileURLToPath(import.meta.url) : false;
69
73
 
70
74
  const mergedEnv = loadConstructEnv({ rootDir: root, homeDir: home, env: process.env });
71
75
  for (const [key, value] of Object.entries(mergedEnv)) {
72
76
  if (!(key in process.env)) process.env[key] = value;
73
77
  }
74
78
  if (!process.env.CX_TOOLKIT_DIR) process.env.CX_TOOLKIT_DIR = root;
75
- const registryPath = path.join(root, "specialists", "registry.json");
76
- const registry = JSON.parse(fs.readFileSync(registryPath, "utf8"));
79
+
80
+ function unifiedToLegacyRegistry(unified) {
81
+ const orchestrator = Object.values(unified.specialists || {}).find(s => s.role === 'orchestrator') || {};
82
+ const specialists = Object.values(unified.specialists || {}).filter(s => s.role !== 'orchestrator');
83
+ return {
84
+ version: 1,
85
+ system: 'construct',
86
+ prefix: 'cx',
87
+ orchestrator: {
88
+ name: 'construct',
89
+ displayName: orchestrator.displayName || orchestrator.description?.split('—')[0].trim() || 'Construct',
90
+ description: orchestrator.description || '',
91
+ promptFile: orchestrator.promptFile || 'specialists/prompts/cx-orchestrator.md',
92
+ modelTier: orchestrator.modelTier || 'standard',
93
+ skills: orchestrator.skills || [],
94
+ injectAgentRoster: orchestrator.injectAgentRoster ?? false,
95
+ },
96
+ specialists: specialists.map(s => ({
97
+ name: s.name,
98
+ displayName: s.displayName || s.description?.split('—')[0].trim() || s.name,
99
+ description: s.description || '',
100
+ promptFile: s.promptFile,
101
+ modelTier: s.modelTier || 'standard',
102
+ claudeTools: s.claudeTools || 'Read,Grep,Glob,LS',
103
+ internal: s.internal,
104
+ wordCapOverride: s.wordCapOverride,
105
+ skills: s.skills || [],
106
+ team: s.team,
107
+ role: s.role,
108
+ docArtifacts: s.docArtifacts || [],
109
+ subscriptions: s.events || [],
110
+ watchConditions: s.watchConditions || [],
111
+ fence: s.fence || {},
112
+ })),
113
+ sharedGuidance: unified.sharedGuidance || [],
114
+ models: unified.models || { reasoning: { primary: null }, standard: { primary: null }, fast: { primary: null } },
115
+ providers: unified.providers || {},
116
+ mcpServers: unified.mcpServers || {},
117
+ };
118
+ }
119
+
120
+ clearCache();
121
+ clearCache();
122
+ const unified = loadRegistry({ rootDir: root });
123
+ const registry = unifiedToLegacyRegistry(unified);
77
124
 
78
125
  function validateRegistry(registry) {
79
126
  const errors = [];
@@ -378,15 +425,15 @@ const sharedGuidance = registry.sharedGuidance ?? [];
378
425
  const platformGuidance = registry.platformGuidance ?? {};
379
426
  const globalModelGuidance = registry.modelGuidance ?? {};
380
427
 
381
- const generatedHeader = `# Generated by ${systemName}/sync-specialists.mjs. Edit specialists/registry.json instead.`;
428
+ const generatedHeader = `# Generated by ${systemName}/sync-specialists.mjs. Edit specialists/org instead.`;
382
429
  const generatedMarkdownNote = [
383
430
  '<!--',
384
- `Generated by construct sync from specialists/registry.json.`,
431
+ `Generated by construct sync from specialists/org.`,
385
432
  'Do not edit this file directly — changes will be overwritten on next sync.',
386
433
  'Regenerate: construct sync',
387
434
  '-->',
388
435
  '',
389
- '> Generated from `specialists/registry.json`. Edit the registry, then run `construct sync`.',
436
+ '> Generated from `specialists/org`. Edit the registry, then run `construct sync`.',
390
437
  ].join('\n');
391
438
 
392
439
  const standardConstructTools = [
@@ -432,6 +479,96 @@ function resolveArgs(args) {
432
479
  : a));
433
480
  }
434
481
 
482
+ function resolveTemplateStrings(value) {
483
+ if (typeof value === "string") {
484
+ return value.replace(/__([A-Z0-9_]+)__/g, (_, name) => {
485
+ if (process.env[name] !== undefined && process.env[name] !== "") return process.env[name];
486
+ if (name === "CX_TOOLKIT_DIR") return root;
487
+ if (name === "MEMORY_PORT") return process.env.MEMORY_PORT || "8765";
488
+ if (name === "CONSTRUCT_MEMORY_BRIDGE_URL") {
489
+ return process.env.CONSTRUCT_MEMORY_BRIDGE_URL || "http://127.0.0.1:8765/";
490
+ }
491
+ if (name === "GITHUB_TOKEN" && process.env.GITHUB_PERSONAL_ACCESS_TOKEN) {
492
+ return process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
493
+ }
494
+ if (/(?:TOKEN|SECRET|API_KEY|PUBLIC_KEY|PRIVATE_KEY)$/.test(name)) {
495
+ return `{env:${name}}`;
496
+ }
497
+ return `__${name}__`;
498
+ });
499
+ }
500
+ if (Array.isArray(value)) return value.map((item) => resolveTemplateStrings(item));
501
+ if (value && typeof value === "object") {
502
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, resolveTemplateStrings(item)]));
503
+ }
504
+ return value;
505
+ }
506
+
507
+ function constructMcpDefinition() {
508
+ return {
509
+ id: "construct-mcp",
510
+ name: "Construct MCP",
511
+ category: "core",
512
+ description: "Construct stdio MCP server for orchestration, skills, templates, and project context.",
513
+ command: "node",
514
+ args: ["__CX_TOOLKIT_DIR__/lib/mcp/server.mjs"],
515
+ env: {
516
+ CONSTRUCT_TRACE_BACKEND: "__CONSTRUCT_TRACE_BACKEND__",
517
+ CONSTRUCT_TELEMETRY_URL: "__CONSTRUCT_TELEMETRY_URL__",
518
+ CONSTRUCT_TELEMETRY_PUBLIC_KEY: "__CONSTRUCT_TELEMETRY_PUBLIC_KEY__",
519
+ CONSTRUCT_TELEMETRY_SECRET_KEY: "__CONSTRUCT_TELEMETRY_SECRET_KEY__",
520
+ },
521
+ requiredEnv: [],
522
+ setupModes: ["auto"],
523
+ hostSupport: {
524
+ claude: { mode: "managed" },
525
+ opencode: { mode: "managed" },
526
+ codex: { mode: "managed" },
527
+ },
528
+ usedBy: ["construct"],
529
+ };
530
+ }
531
+
532
+ function managedMcpDefs() {
533
+ const pluginRegistry = loadPluginRegistry({ cwd: root, homeDir: home, rootDir: root, env: process.env });
534
+ const defs = Object.fromEntries(
535
+ (pluginRegistry.mcps ?? []).map((mcp) => [mcp.id, {
536
+ type: mcp.type,
537
+ url: mcp.url,
538
+ command: mcp.command,
539
+ args: mcp.args,
540
+ env: mcp.env,
541
+ headers: mcp.headers,
542
+ hostSupport: mcp.hostSupport,
543
+ category: mcp.category,
544
+ }]),
545
+ );
546
+ defs["construct-mcp"] = constructMcpDefinition();
547
+ return { ...defs, ...(registry.mcpServers ?? {}) };
548
+ }
549
+
550
+ function scopedManagedMcpDefs({ projectScope = false } = {}) {
551
+ const defs = managedMcpDefs();
552
+ if (!projectScope) return defs;
553
+ return Object.fromEntries(
554
+ Object.entries(defs).filter(([id]) => PROJECT_DEFAULT_MCP_IDS.has(id)),
555
+ );
556
+ }
557
+
558
+ function mergeMissingObjectDefaults(current, defaults) {
559
+ if (Array.isArray(defaults)) {
560
+ return current === undefined ? [...defaults] : current;
561
+ }
562
+ if (!defaults || typeof defaults !== "object") {
563
+ return current === undefined ? defaults : current;
564
+ }
565
+ const next = current && typeof current === "object" && !Array.isArray(current) ? { ...current } : {};
566
+ for (const [key, value] of Object.entries(defaults)) {
567
+ next[key] = mergeMissingObjectDefaults(next[key], value);
568
+ }
569
+ return next;
570
+ }
571
+
435
572
  function extractFallbackChain(tierDef) {
436
573
  if (typeof tierDef === "string") return [tierDef];
437
574
  if (tierDef && typeof tierDef === "object") {
@@ -554,7 +691,7 @@ function buildRoleFooter(entry) {
554
691
  return `\n\n${lines.join("\n")}`;
555
692
  }
556
693
 
557
- // Fence + handoff data is the source of truth in specialists/role-manifests.json
694
+ // Fence + handoff data is the source of truth in specialists/org
558
695
  // (events, fence.allowedPaths, fence.allowedBdLabels, fence.approvalRequired,
559
696
  // handoffCandidates, outputs.docTypes). Restating that JSON inside each
560
697
  // specialist prompt would invite drift across 28 files; the renderer below
@@ -599,7 +736,7 @@ export function renderRoleFrameworkSection(entry) {
599
736
  "",
600
737
  `Construct may dispatch you in response to ${eventList} events. A bd issue with the event payload exists when dispatched: read it first via \`bd show <id>\`.`,
601
738
  "",
602
- `**Fence** (source of truth: \`specialists/role-manifests.json\` → \`${personaName}\`):`,
739
+ `**Fence** (source of truth: \`specialists/org\` → \`${personaName}\`):`,
603
740
  `- Allowed paths: ${pathList}`,
604
741
  `- Allowed bd labels: ${labelList}`,
605
742
  `- Approval required: ${approvalList}`,
@@ -616,11 +753,21 @@ export function renderRoleFrameworkSection(entry) {
616
753
  // small local models' tool-use reliability sharply (bead construct-c16l). Shared by the
617
754
  // full path and the capability-tiered local path so both stay in sync.
618
755
 
619
- const ORCHESTRATION_MICRO_PROMPT =
620
- `You are the primary orchestrator. To discover available specialist agents, you MUST call the \`orchestration_policy\` MCP tool. Do not guess agent names.\n\n` +
621
- `Example — the user says "add rate limiting to the API". Your first action is a tool call, not prose:\n` +
622
- ` call orchestration_policy { "task": "add rate limiting to the API" }\n` +
623
- `Then dispatch the specialists it returns. Always call the tool before answering.`;
756
+ function orchestrationToolName(platform, toolName) {
757
+ if (platform === "opencode") return `construct-mcp_${toolName}`;
758
+ return toolName;
759
+ }
760
+
761
+ function orchestrationMicroPrompt(platform) {
762
+ const policyTool = orchestrationToolName(platform, "orchestration_policy");
763
+ const runTool = orchestrationToolName(platform, "orchestration_run");
764
+ return (
765
+ `You are the primary orchestrator. Before any non-trivial answer, call \`${policyTool}\` with the user's \`request\`. Do not guess agent names or workflow types.\n\n` +
766
+ `Example — the user says "add rate limiting to the API". Your first action is a tool call, not prose:\n` +
767
+ ` call ${policyTool} { "request": "add rate limiting to the API" }\n` +
768
+ `If the route is focused/orchestrated specialist work, call \`${runTool}\` with the same request. If the route suggests a workflow such as \`research-synthesis\`, pass it as \`workflow_type\`. Do not narrate completed research unless \`${runTool}\` or evidence tools actually ran.`
769
+ );
770
+ }
624
771
 
625
772
  // Directive for the local editor agent (construct-local). It executes bounded work on
626
773
  // the cheap local model and hands planning/reasoning back to the construct architect —
@@ -661,7 +808,7 @@ function enforcePromptWordCap(prompt, entry) {
661
808
  console.error(
662
809
  `[sync] Hard cap exceeded. Options:\n` +
663
810
  ` - trim the prompt body or move detail to a skill (preferred)\n` +
664
- ` - set "wordCapOverride": <N> on this entry in specialists/registry.json with a written reason\n` +
811
+ ` - set "wordCapOverride": <N> on this entry in specialists/org with a written reason\n` +
665
812
  ` - re-run with --force or CONSTRUCT_SYNC_FORCE=1 as a temporary escape hatch\n` +
666
813
  `Prompt budget is a hard contract because every over-cap agent degrades every session that dispatches it.`,
667
814
  );
@@ -684,7 +831,7 @@ function buildPrompt(entry, allEntries, platform, { capabilityTier = 'full' } =
684
831
  if (capabilityTier && capabilityTier !== 'full' && entry.promptFile) {
685
832
  let slim = renderPersonaForTier(readPromptBody(entry.promptFile, root), capabilityTier);
686
833
  if (entry.injectAgentRoster) {
687
- slim = `${ORCHESTRATION_MICRO_PROMPT}\n\n${slim}`;
834
+ slim = `${orchestrationMicroPrompt(platform)}\n\n${slim}`;
688
835
  }
689
836
  return enforcePromptWordCap(slim, entry);
690
837
  }
@@ -707,7 +854,7 @@ function buildPrompt(entry, allEntries, platform, { capabilityTier = 'full' } =
707
854
  // orchestration_policy / orchestration_run — never inject the static roster.
708
855
 
709
856
  if (entry.injectAgentRoster) {
710
- prompt = `${ORCHESTRATION_MICRO_PROMPT}\n\n${prompt}`;
857
+ prompt = `${orchestrationMicroPrompt(platform)}\n\n${prompt}`;
711
858
  }
712
859
 
713
860
  prompt += buildRoleFooter(entry);
@@ -811,7 +958,7 @@ function buildEntries() {
811
958
  // so behavior is unchanged for the default; non-RND profiles emit only the
812
959
  // specialists they declare. Profiles whose role list is empty (legacy/test)
813
960
  // fall through to no filter so callers don't get a silent empty registry.
814
- const activeProfile = resolveActiveProfile(process.cwd());
961
+ const activeProfile = resolveActiveScope(process.cwd());
815
962
  const profileRoles = Array.isArray(activeProfile?.roles) && activeProfile.roles.length > 0
816
963
  ? new Set(activeProfile.roles)
817
964
  : null;
@@ -920,12 +1067,6 @@ function filterGlobalClaudeHooks(hooksJson) {
920
1067
 
921
1068
  function syncGlobalClaudeMcpServers(settings, registryMcp) {
922
1069
  settings.mcpServers ??= {};
923
- for (const id of Object.keys(settings.mcpServers)) {
924
- if (id in registryMcp && !GLOBAL_CLAUDE_MCP_IDS.has(id)) {
925
- delete settings.mcpServers[id];
926
- }
927
- }
928
-
929
1070
  for (const [id, mcpDef] of Object.entries(registryMcp)) {
930
1071
  if (!GLOBAL_CLAUDE_MCP_IDS.has(id)) continue;
931
1072
  const existingEntry = settings.mcpServers[id];
@@ -979,7 +1120,7 @@ function writeProjectClaudeSettings(targetDir) {
979
1120
  // Claude Code as the only selected tool without the construct config. Merge the
980
1121
  // registry on top of the template seed; existing entries win so user edits stick,
981
1122
  // and buildClaudeMcpEntry resolves each server's path/env.
982
- const registryMcp = registry.mcpServers ?? {};
1123
+ const registryMcp = scopedManagedMcpDefs({ projectScope: true });
983
1124
  existing.mcpServers ??= {};
984
1125
  for (const [id, mcpDef] of Object.entries(registryMcp)) {
985
1126
  if (existing.mcpServers[id]) continue;
@@ -1057,7 +1198,7 @@ ${personaList}
1057
1198
  settings.hooks = JSON.parse(hookStr);
1058
1199
  }
1059
1200
  }
1060
- const registryMcp = registry.mcpServers ?? {};
1201
+ const registryMcp = scopedManagedMcpDefs({ projectScope: false });
1061
1202
  syncGlobalClaudeMcpServers(settings, registryMcp);
1062
1203
  if (!DRY_RUN) fs.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 2) + "\n");
1063
1204
  }
@@ -1135,7 +1276,7 @@ function syncCodex(entries, targetDir = null, wants = true) {
1135
1276
  : getCodexConfigPath(home);
1136
1277
  const existing = removeDanglingConstructMcpMarkers(removeDanglingConstructMcpTimeouts(readCodexConfig(configPath)));
1137
1278
  const entryNames = writeEntries.map(adapterName);
1138
- const registryMcp = registry.mcpServers ?? {};
1279
+ const registryMcp = scopedManagedMcpDefs({ projectScope: Boolean(targetDir) });
1139
1280
  // Seed every supported MCP server (parity with Claude/OpenCode/VS Code/Cursor),
1140
1281
  // not just tables already present — otherwise Codex never receives `construct-mcp`
1141
1282
  // and the orchestration tool is unreachable there. existingMcpIds still drives
@@ -1285,7 +1426,7 @@ function getVSCodeUserMcpPaths() {
1285
1426
  }
1286
1427
 
1287
1428
  function syncVSCode(targetDir = null, wants = true) {
1288
- const registryMcp = registry.mcpServers ?? {};
1429
+ const registryMcp = scopedManagedMcpDefs({ projectScope: Boolean(targetDir) });
1289
1430
  if (Object.keys(registryMcp).length === 0) return false;
1290
1431
 
1291
1432
  // Project scope writes a dedicated `.vscode/mcp.json` (VS Code's documented
@@ -1369,7 +1510,7 @@ function syncVSCode(targetDir = null, wants = true) {
1369
1510
  // --- Cursor adapter ---
1370
1511
 
1371
1512
  function syncCursor(targetDir = null, wants = true) {
1372
- const registryMcp = registry.mcpServers ?? {};
1513
+ const registryMcp = scopedManagedMcpDefs({ projectScope: Boolean(targetDir) });
1373
1514
  if (Object.keys(registryMcp).length === 0) return false;
1374
1515
 
1375
1516
  const cursorDir = targetDir
@@ -1567,16 +1708,72 @@ function syncOpencode(entries, targetDir = null, wants = true) {
1567
1708
 
1568
1709
  const writeEntries = globalEntries(entries);
1569
1710
 
1711
+ const hadExistingConfig = fs.existsSync(configPath);
1570
1712
  const { config } = targetDir
1571
- ? { config: fs.existsSync(configPath) ? JSON.parse(fs.readFileSync(configPath, "utf8")) : {} }
1713
+ ? { config: hadExistingConfig ? JSON.parse(fs.readFileSync(configPath, "utf8")) : {} }
1572
1714
  : readOpenCodeConfig();
1715
+ const opencodeTemplatePath = path.join(root, "platforms", "opencode", "config.template.json");
1716
+ const opencodeTemplate = fs.existsSync(opencodeTemplatePath)
1717
+ ? JSON.parse(fs.readFileSync(opencodeTemplatePath, "utf8"))
1718
+ : {};
1573
1719
  if (!config.agent) config.agent = {};
1720
+ if (!config.mcp) config.mcp = {};
1574
1721
  if (!Array.isArray(config.plugin)) config.plugin = [];
1575
1722
  config.plugin = config.plugin.filter((entry) => {
1576
1723
  if (typeof entry !== "string") return true;
1577
1724
  return entry !== managedPluginPath && entry !== toolkitPluginPath;
1578
1725
  });
1579
1726
 
1727
+ if (opencodeTemplate.$schema && !config.$schema) config.$schema = opencodeTemplate.$schema;
1728
+ if (opencodeTemplate.model && !config.model && !config.defaultModel && !hadExistingConfig) {
1729
+ config.model = opencodeTemplate.model;
1730
+ }
1731
+ if (Array.isArray(opencodeTemplate.enabled_providers) && !Array.isArray(config.enabled_providers)) {
1732
+ config.enabled_providers = [...opencodeTemplate.enabled_providers];
1733
+ }
1734
+ if (opencodeTemplate.provider && typeof opencodeTemplate.provider === "object") {
1735
+ config.provider ??= {};
1736
+ for (const [id, providerDef] of Object.entries(opencodeTemplate.provider)) {
1737
+ config.provider[id] = mergeMissingObjectDefaults(config.provider[id], resolveTemplateStrings(providerDef));
1738
+ }
1739
+ }
1740
+ if (config.provider?.ollama?.options?.headers?.Authorization === "Bearer ollama") {
1741
+ delete config.provider.ollama.options.headers.Authorization;
1742
+ if (Object.keys(config.provider.ollama.options.headers).length === 0) {
1743
+ delete config.provider.ollama.options.headers;
1744
+ }
1745
+ }
1746
+
1747
+ const templateMcp = opencodeTemplate.mcp && typeof opencodeTemplate.mcp === "object"
1748
+ ? resolveTemplateStrings(opencodeTemplate.mcp)
1749
+ : {};
1750
+ for (const [id, entry] of Object.entries(templateMcp)) {
1751
+ if (!PROJECT_DEFAULT_MCP_IDS.has(id)) continue;
1752
+ if (!config.mcp[id]) config.mcp[id] = entry;
1753
+ }
1754
+ if (config.mcp["construct-mcp"]) {
1755
+ config.mcp["construct-mcp"] = templateMcp["construct-mcp"] ?? config.mcp["construct-mcp"];
1756
+ }
1757
+
1758
+ const memoryBridgeEntry = buildOpenCodeMcpEntry("memory", {
1759
+ command: "node",
1760
+ args: ["__CX_TOOLKIT_DIR__/lib/mcp/memory-bridge.mjs"],
1761
+ env: { CONSTRUCT_MEMORY_BRIDGE_URL: "__CONSTRUCT_MEMORY_BRIDGE_URL__" },
1762
+ }, {
1763
+ ...process.env,
1764
+ CONSTRUCT_MEMORY_BRIDGE_URL:
1765
+ config.mcp.memory?.url
1766
+ || config.mcp.cass?.url
1767
+ || process.env.CONSTRUCT_MEMORY_BRIDGE_URL
1768
+ || "http://127.0.0.1:8765/",
1769
+ }).entry;
1770
+ const staleMemoryRemote = config.mcp.memory && (config.mcp.memory.type === "remote" || config.mcp.memory.type === "http");
1771
+ const staleCassRemote = config.mcp.cass && (config.mcp.cass.type === "remote" || config.mcp.cass.type === "http");
1772
+ if (staleMemoryRemote || staleCassRemote) {
1773
+ config.mcp.memory = memoryBridgeEntry;
1774
+ delete config.mcp.cass;
1775
+ }
1776
+
1580
1777
  // Sync providers
1581
1778
  const registryProviders = registry.providers ?? {};
1582
1779
  if (Object.keys(registryProviders).length > 0) {
@@ -1589,8 +1786,10 @@ function syncOpencode(entries, targetDir = null, wants = true) {
1589
1786
  ...providerDef,
1590
1787
  options: {
1591
1788
  ...providerDef.options,
1789
+ ...existing.options,
1592
1790
  headers: {
1593
1791
  ...providerDef.options?.headers,
1792
+ ...existing.options?.headers,
1594
1793
  ...(existingAuth ? { Authorization: existingAuth } : {}),
1595
1794
  },
1596
1795
  },
@@ -1602,6 +1801,8 @@ function syncOpencode(entries, targetDir = null, wants = true) {
1602
1801
  }
1603
1802
  }
1604
1803
 
1804
+ ensureOpenRouterProviderAuth(config);
1805
+
1605
1806
  // Derive anthropic models from registry tier definitions
1606
1807
  const tierModels = Object.values(registry.models ?? {}).flatMap((t) =>
1607
1808
  [t.primary, ...(t.fallback ?? [])].filter((m) => m?.startsWith('anthropic/'))
@@ -1627,7 +1828,7 @@ function syncOpencode(entries, targetDir = null, wants = true) {
1627
1828
  }
1628
1829
 
1629
1830
  // Sync MCP servers
1630
- const registryMcp = registry.mcpServers ?? {};
1831
+ const registryMcp = scopedManagedMcpDefs({ projectScope: Boolean(targetDir) });
1631
1832
  if (Object.keys(registryMcp).length > 0) {
1632
1833
  if (!config.mcp) config.mcp = {};
1633
1834
  for (const [id, mcpDef] of Object.entries(registryMcp)) {
@@ -1656,7 +1857,7 @@ function syncOpencode(entries, targetDir = null, wants = true) {
1656
1857
  // into EVERY agent's request — github ~30k alone (fixtures 2026-06-22) —
1657
1858
  // agent's request — including the built-in Build/Plan agents the per-agent
1658
1859
  // permission prune cannot reach. OpenCode 1.15.4 has no per-session tool
1659
- // filter (chat.params carries no tool list), so disabling the whole server in
1860
+ // filter (OpenCode chat.params carries no tool list), so disabling the whole server in
1660
1861
  // opencode.json is the only lever. The decision is INTENT-driven: trim only
1661
1862
  // when this config's own default model is local (or a local Ollama provider is
1662
1863
  // registered in it), so a cloud session on a machine that merely also has
@@ -1689,7 +1890,8 @@ function syncOpencode(entries, targetDir = null, wants = true) {
1689
1890
  for (const key of Object.keys(config.agent)) {
1690
1891
  const isManaged = prefixes.some((p) => key.startsWith(p));
1691
1892
  const isOrchestrator = registry.orchestrator?.name === key;
1692
- if ((isManaged || isOrchestrator) && !writeEntries.find((e) => adapterName(e) === key)) {
1893
+ const isStaleOrchestratorAlias = key === 'orchestrator' && registry.orchestrator?.name === 'construct';
1894
+ if ((isManaged || isOrchestrator || isStaleOrchestratorAlias) && !writeEntries.find((e) => adapterName(e) === key)) {
1693
1895
  delete config.agent[key];
1694
1896
  }
1695
1897
  }
@@ -1780,21 +1982,28 @@ function syncOpencode(entries, targetDir = null, wants = true) {
1780
1982
  config.construct = config.construct || {};
1781
1983
  config.construct.models = { ...resolvedModels };
1782
1984
 
1783
- // Seed a cheap auxiliary model for titles and summaries when the user has not
1784
- // chosen one a cost lever only. Global scope only; project configs inherit it.
1785
-
1786
- if (!targetDir && config.small_model === undefined) {
1787
- config.small_model = resolvedModels.fast || "anthropic/claude-haiku-4-5-20251001";
1788
- }
1789
-
1790
- // Seed a text-capable primary model when the user has not pinned one, so chat
1791
- // and routing never fall through to a host provider-default that happens to be
1792
- // an image/non-text model. Seeds only when absent; an explicit user choice is
1793
- // never overwritten. resolvedModels.standard is family-/registry-aware and
1794
- // free-biased. Global scope only; project configs inherit it.
1795
-
1796
- if (!targetDir && (config.model === undefined || config.model === null || config.model === "")) {
1797
- config.model = resolvedModels.standard || resolvedModels.reasoning || resolvedModels.fast || "openrouter/qwen/qwen3-coder:free";
1985
+ // OpenCode's primary `model` is user-owned. Remove the legacy Construct seed
1986
+ // when we find it so the app can fall back to its own remembered selection
1987
+ // instead of a stale pin. New syncs never write this key back.
1988
+ const legacyPinnedModels = new Set([
1989
+ opencodeTemplate.model,
1990
+ hardDefaults.standard,
1991
+ ].filter(Boolean));
1992
+ if (!targetDir && legacyPinnedModels.has(config.model)) {
1993
+ delete config.model;
1994
+ }
1995
+
1996
+ // OpenCode's built-in helper agents drive session naming, summaries, and
1997
+ // compaction. Keep them on a stronger auxiliary model so those surfaces do not
1998
+ // inherit the main chat model's selection history or a low-end coder default.
1999
+ const auxiliaryModel = resolvedModels.reasoning || resolvedModels.standard || resolvedModels.fast;
2000
+ if (auxiliaryModel) {
2001
+ for (const key of ["title", "summary", "compaction"]) {
2002
+ config.agent[key] = {
2003
+ ...(config.agent[key] && typeof config.agent[key] === "object" ? config.agent[key] : {}),
2004
+ model: auxiliaryModel,
2005
+ };
2006
+ }
1798
2007
  }
1799
2008
 
1800
2009
  writeOpenCodeConfig(config, configPath);
@@ -1943,6 +2152,7 @@ function syncSkills(targetDir = null) {
1943
2152
 
1944
2153
  // --- Main ---
1945
2154
 
2155
+ if (isMain) {
1946
2156
  const entries = buildEntries();
1947
2157
 
1948
2158
  if (COMPRESS_PERSONAS) {
@@ -2035,3 +2245,4 @@ try {
2035
2245
  } finally {
2036
2246
  releaseLock();
2037
2247
  }
2248
+ }