@geraldmaron/construct 1.2.3 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (505) hide show
  1. package/README.md +7 -10
  2. package/bin/construct +235 -209
  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 +55 -54
  28. package/lib/comment-lint.mjs +1 -5
  29. package/lib/completions.mjs +20 -2
  30. package/lib/config/legacy-config-migration.mjs +90 -24
  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/cli.mjs +12 -2
  44. package/lib/doctor/source-checkout.mjs +3 -4
  45. package/lib/doctor/watchers/consistency.mjs +148 -43
  46. package/lib/doctor/watchers/credential-parity.mjs +97 -0
  47. package/lib/doctor/watchers/mcp-protocol.mjs +1 -1
  48. package/lib/doctor/watchers/service-health.mjs +4 -32
  49. package/lib/document-export.mjs +73 -9
  50. package/lib/document-extract.mjs +10 -198
  51. package/lib/embed/artifact.mjs +2 -3
  52. package/lib/embed/cli.mjs +1 -1
  53. package/lib/embed/config.mjs +1 -2
  54. package/lib/embed/customer-profiles.mjs +1 -1
  55. package/lib/embed/daemon.mjs +5 -6
  56. package/lib/embed/demand-fetch.mjs +114 -2
  57. package/lib/embed/docs-lifecycle.mjs +9 -5
  58. package/lib/embed/intake-metrics.mjs +2 -2
  59. package/lib/embed/notifications.mjs +2 -3
  60. package/lib/embed/recommendation-store.mjs +25 -22
  61. package/lib/embed/role-framing.mjs +4 -9
  62. package/lib/embedded-contract/capability.mjs +4 -2
  63. package/lib/embedded-contract/contract-version.mjs +1 -1
  64. package/lib/env-config.mjs +2 -2
  65. package/lib/flavors/loader.mjs +21 -19
  66. package/lib/graph/build-from-registry.mjs +31 -7
  67. package/lib/graph/staleness.mjs +1 -1
  68. package/lib/headhunt.mjs +82 -35
  69. package/lib/hooks/agent-tracker.mjs +2 -2
  70. package/lib/hooks/config-protection.mjs +1 -1
  71. package/lib/hooks/registry-sync.mjs +3 -3
  72. package/lib/host-capabilities.mjs +5 -5
  73. package/lib/improvement/controller.mjs +2 -2
  74. package/lib/init-unified.mjs +19 -21
  75. package/lib/intake/classify.mjs +26 -0
  76. package/lib/intake/daemon.mjs +2 -3
  77. package/lib/intake/prepare.mjs +10 -3
  78. package/lib/intake/session-prelude.mjs +1 -1
  79. package/lib/intake/tables/creative.mjs +9 -9
  80. package/lib/intake/tables/operations.mjs +4 -4
  81. package/lib/intake/tables/research.mjs +2 -2
  82. package/lib/integrations/intake-integrations.mjs +9 -10
  83. package/lib/knowledge/research-store.mjs +2 -2
  84. package/lib/mcp/server.mjs +104 -61
  85. package/lib/mcp/tool-budget.mjs +1 -46
  86. package/lib/mcp/tool-recovery.mjs +44 -0
  87. package/lib/mcp/tools/artifact-author.mjs +36 -0
  88. package/lib/mcp/tools/find-tool.mjs +86 -0
  89. package/lib/mcp/tools/orchestration-run.mjs +107 -69
  90. package/lib/mcp/tools/project.mjs +4 -3
  91. package/lib/mcp/tools/{profile.mjs → scope.mjs} +54 -52
  92. package/lib/mcp/tools/skills.mjs +39 -22
  93. package/lib/mcp/tools/telemetry.mjs +2 -1
  94. package/lib/mcp/tools/workflow.mjs +1 -1
  95. package/lib/mcp-platform-config.mjs +7 -5
  96. package/lib/migrations/index.mjs +4 -2
  97. package/lib/migrations/v2-unified-registry.mjs +35 -0
  98. package/lib/model-router.mjs +203 -36
  99. package/lib/models/catalog.mjs +25 -9
  100. package/lib/models/execution-capability-profile.mjs +3 -3
  101. package/lib/models/execution-policy.mjs +7 -6
  102. package/lib/models/provider-poll.mjs +18 -4
  103. package/lib/opencode-config.mjs +56 -1
  104. package/lib/opencode-runtime-plugin.mjs +13 -10
  105. package/lib/oracle/artifact-gate.mjs +14 -4
  106. package/lib/oracle/cli.mjs +2 -0
  107. package/lib/oracle/dispatch.mjs +18 -1
  108. package/lib/oracle/execute.mjs +39 -7
  109. package/lib/oracle/index.mjs +1 -1
  110. package/lib/oracle/org-graph.mjs +10 -5
  111. package/lib/oracle/policy.mjs +6 -0
  112. package/lib/oracle/read-model.mjs +103 -6
  113. package/lib/oracle/reconcile.mjs +31 -4
  114. package/lib/oracle/remediation-dispatch.mjs +53 -0
  115. package/lib/oracle/routing.mjs +5 -0
  116. package/lib/oracle/synthesize.mjs +80 -3
  117. package/lib/orchestration/routing-tables.mjs +44 -10
  118. package/lib/orchestration/runtime.mjs +2 -0
  119. package/lib/orchestration/worker.mjs +1 -1
  120. package/lib/orchestration-policy.mjs +351 -62
  121. package/lib/overrides/resolver.mjs +1 -12
  122. package/lib/parity.mjs +37 -11
  123. package/lib/policy/engine.mjs +80 -15
  124. package/lib/prompt-composer.js +51 -15
  125. package/lib/prompt-metadata.mjs +3 -2
  126. package/lib/providers/connection-probe.mjs +58 -0
  127. package/lib/providers/copilot-auth.mjs +1 -1
  128. package/lib/providers/credential-bootstrap.mjs +1 -1
  129. package/lib/providers/op-run.mjs +3 -4
  130. package/lib/providers/secret-resolver.mjs +31 -0
  131. package/lib/publish-tooling.mjs +3 -11
  132. package/lib/publish.mjs +12 -29
  133. package/lib/reconcile/mcp-entry-reconcile.mjs +14 -9
  134. package/lib/reflect.mjs +2 -2
  135. package/lib/registry/agent-manifest.mjs +190 -0
  136. package/lib/registry/assemble.mjs +133 -0
  137. package/lib/registry/catalog.mjs +171 -0
  138. package/lib/registry/cli.mjs +590 -6
  139. package/lib/registry/consolidation.mjs +5 -4
  140. package/lib/registry/docs-sync.mjs +67 -0
  141. package/lib/registry/loader.mjs +147 -0
  142. package/lib/registry/org-io.mjs +76 -0
  143. package/lib/registry/retired-paths.mjs +71 -0
  144. package/lib/registry/surface-map.mjs +5 -7
  145. package/lib/registry/validator.mjs +438 -0
  146. package/lib/research-execution-policy.mjs +173 -0
  147. package/lib/roles/catalog.mjs +4 -9
  148. package/lib/roles/fence.mjs +107 -1
  149. package/lib/roles/flavor-bindings.mjs +68 -0
  150. package/lib/roles/gateway.mjs +140 -29
  151. package/lib/roles/manifest.mjs +22 -13
  152. package/lib/roles/router.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 +6 -26
  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 -8
  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 +269 -51
  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/apps/chat/engine/ai-sdk-agent.mjs +0 -183
  433. package/apps/chat/engine/loop-driver.mjs +0 -211
  434. package/apps/chat/engine/models.mjs +0 -122
  435. package/apps/chat/engine/provider-adapters.mjs +0 -171
  436. package/apps/chat/engine/tools/permission.mjs +0 -54
  437. package/apps/chat/engine/tools/primitives.mjs +0 -180
  438. package/apps/chat/engine/tools/registry.mjs +0 -122
  439. package/apps/chat/engine/turn-controls.mjs +0 -70
  440. package/lib/boundary.mjs +0 -127
  441. package/lib/certification/dashboard-api.mjs +0 -71
  442. package/lib/chat/cli.mjs +0 -333
  443. package/lib/chat/command-suggest.mjs +0 -161
  444. package/lib/chat/commands.mjs +0 -215
  445. package/lib/chat/config.mjs +0 -142
  446. package/lib/chat/context-compactor.mjs +0 -250
  447. package/lib/chat/context-continuation.mjs +0 -253
  448. package/lib/chat/continuation-source.mjs +0 -58
  449. package/lib/chat/demo-guide.mjs +0 -61
  450. package/lib/chat/design-tokens.mjs +0 -91
  451. package/lib/chat/desktop-binary.mjs +0 -81
  452. package/lib/chat/desktop-build.mjs +0 -130
  453. package/lib/chat/desktop-launcher.mjs +0 -133
  454. package/lib/chat/evidence.mjs +0 -145
  455. package/lib/chat/export.mjs +0 -74
  456. package/lib/chat/harness/driver.mjs +0 -91
  457. package/lib/chat/list-picker.mjs +0 -112
  458. package/lib/chat/model-picker.mjs +0 -356
  459. package/lib/chat/openrouter-fallback.mjs +0 -151
  460. package/lib/chat/permission-prompt.mjs +0 -33
  461. package/lib/chat/picker-catalog.mjs +0 -45
  462. package/lib/chat/policy-telemetry.mjs +0 -34
  463. package/lib/chat/present.mjs +0 -246
  464. package/lib/chat/session-context.mjs +0 -39
  465. package/lib/chat/session-persist.mjs +0 -73
  466. package/lib/chat/session-restore.mjs +0 -71
  467. package/lib/chat/session-settings.mjs +0 -53
  468. package/lib/chat/system-prompt.mjs +0 -52
  469. package/lib/chat/transparency.mjs +0 -93
  470. package/lib/chat/tui/color-scheme.mjs +0 -42
  471. package/lib/chat/tui/markdown.mjs +0 -123
  472. package/lib/chat/tui/presentation.mjs +0 -100
  473. package/lib/chat/tui/render.mjs +0 -500
  474. package/lib/chat/tui/turn-block.mjs +0 -284
  475. package/lib/chat/tui/turn-present.mjs +0 -18
  476. package/lib/chat/tui/turn-state.mjs +0 -88
  477. package/lib/chat/tui/usage.mjs +0 -122
  478. package/lib/chat/web-commands.mjs +0 -146
  479. package/lib/chat/web-launcher.mjs +0 -63
  480. package/lib/chat/web-picker-keys.mjs +0 -46
  481. package/lib/chat/web-session.mjs +0 -159
  482. package/lib/config/alias.mjs +0 -57
  483. package/lib/dashboard-demo.mjs +0 -71
  484. package/lib/dashboard-static.mjs +0 -175
  485. package/lib/install/desktop-binary-download.mjs +0 -88
  486. package/lib/profiles/loader.mjs +0 -123
  487. package/lib/profiles/rebrand.mjs +0 -46
  488. package/lib/server/auth.mjs +0 -169
  489. package/lib/server/chat-loop.mjs +0 -622
  490. package/lib/server/chat.mjs +0 -336
  491. package/lib/server/cors.mjs +0 -77
  492. package/lib/server/csrf.mjs +0 -103
  493. package/lib/server/demo-preview.mjs +0 -63
  494. package/lib/server/index.mjs +0 -2641
  495. package/lib/server/insights.mjs +0 -780
  496. package/lib/server/langfuse-login.mjs +0 -58
  497. package/lib/server/rate-limit.mjs +0 -93
  498. package/lib/server/telemetry-login.mjs +0 -100
  499. package/lib/server/webhook.mjs +0 -512
  500. package/specialists/contracts.json +0 -1032
  501. package/specialists/contracts.schema.json +0 -83
  502. package/specialists/policy-inventory.json +0 -188
  503. package/specialists/registry.json +0 -1412
  504. package/specialists/role-manifests.json +0 -217
  505. package/specialists/teams.json +0 -94
@@ -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;
@@ -855,13 +1002,20 @@ ${buildPrompt(entry, allEntries, "claude")}
855
1002
  * Rewrite the home-mode hook command pattern
856
1003
  * node "$HOME/.config/construct/lib/hooks/<name>.mjs"
857
1004
  * into the project-portable form
858
- * node .construct/run.mjs hook <name>
1005
+ * node "${CLAUDE_PROJECT_DIR:-.}/.construct/run.mjs" hook <name>
859
1006
  * so the resulting settings.json works on any clone where the project ships
860
1007
  * the .construct/ launcher (committed by `npm install`'s postinstall or by
861
1008
  * `construct init`). The launcher resolves Construct via node_modules → npx
862
1009
  * → globally-installed CLI → cached binary → docker, in that order, so it
863
1010
  * works for non-Node ecosystems too. Other commands (inline node -e
864
1011
  * snippets, npx block-no-verify@…) are left untouched.
1012
+ *
1013
+ * The `${CLAUDE_PROJECT_DIR:-.}` anchor matters: Claude Code invokes hooks with
1014
+ * a working directory that is not guaranteed to be the project root (observed:
1015
+ * $HOME), and a bare relative `.construct/run.mjs` then fails with MODULE_NOT_FOUND
1016
+ * at node:internal/modules/cjs/loader. CLAUDE_PROJECT_DIR is the project root Claude
1017
+ * Code exports to every hook (same var lib/hooks/*.mjs already read); the `:-.`
1018
+ * fallback preserves the prior relative behavior wherever the var is unset.
865
1019
  */
866
1020
  function makeHooksPortable(hooksJson) {
867
1021
  // Operate on the in-memory object so we don't fight JSON string escaping.
@@ -870,7 +1024,7 @@ function makeHooksPortable(hooksJson) {
870
1024
  const m = cmd.match(/^node\s+"?\$HOME\/\.config\/construct\/lib\/hooks\/([a-z0-9-]+)\.mjs"?\s*(.*)$/);
871
1025
  if (!m) return cmd;
872
1026
  const [, name, rest] = m;
873
- return `node .construct/run.mjs hook ${name}${rest ? ' ' + rest.trim() : ''}`;
1027
+ return `node "\${CLAUDE_PROJECT_DIR:-.}/.construct/run.mjs" hook ${name}${rest ? ' ' + rest.trim() : ''}`;
874
1028
  };
875
1029
 
876
1030
  const walk = (node) => {
@@ -920,12 +1074,6 @@ function filterGlobalClaudeHooks(hooksJson) {
920
1074
 
921
1075
  function syncGlobalClaudeMcpServers(settings, registryMcp) {
922
1076
  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
1077
  for (const [id, mcpDef] of Object.entries(registryMcp)) {
930
1078
  if (!GLOBAL_CLAUDE_MCP_IDS.has(id)) continue;
931
1079
  const existingEntry = settings.mcpServers[id];
@@ -979,7 +1127,7 @@ function writeProjectClaudeSettings(targetDir) {
979
1127
  // Claude Code as the only selected tool without the construct config. Merge the
980
1128
  // registry on top of the template seed; existing entries win so user edits stick,
981
1129
  // and buildClaudeMcpEntry resolves each server's path/env.
982
- const registryMcp = registry.mcpServers ?? {};
1130
+ const registryMcp = scopedManagedMcpDefs({ projectScope: true });
983
1131
  existing.mcpServers ??= {};
984
1132
  for (const [id, mcpDef] of Object.entries(registryMcp)) {
985
1133
  if (existing.mcpServers[id]) continue;
@@ -1057,7 +1205,7 @@ ${personaList}
1057
1205
  settings.hooks = JSON.parse(hookStr);
1058
1206
  }
1059
1207
  }
1060
- const registryMcp = registry.mcpServers ?? {};
1208
+ const registryMcp = scopedManagedMcpDefs({ projectScope: false });
1061
1209
  syncGlobalClaudeMcpServers(settings, registryMcp);
1062
1210
  if (!DRY_RUN) fs.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 2) + "\n");
1063
1211
  }
@@ -1135,7 +1283,7 @@ function syncCodex(entries, targetDir = null, wants = true) {
1135
1283
  : getCodexConfigPath(home);
1136
1284
  const existing = removeDanglingConstructMcpMarkers(removeDanglingConstructMcpTimeouts(readCodexConfig(configPath)));
1137
1285
  const entryNames = writeEntries.map(adapterName);
1138
- const registryMcp = registry.mcpServers ?? {};
1286
+ const registryMcp = scopedManagedMcpDefs({ projectScope: Boolean(targetDir) });
1139
1287
  // Seed every supported MCP server (parity with Claude/OpenCode/VS Code/Cursor),
1140
1288
  // not just tables already present — otherwise Codex never receives `construct-mcp`
1141
1289
  // and the orchestration tool is unreachable there. existingMcpIds still drives
@@ -1285,7 +1433,7 @@ function getVSCodeUserMcpPaths() {
1285
1433
  }
1286
1434
 
1287
1435
  function syncVSCode(targetDir = null, wants = true) {
1288
- const registryMcp = registry.mcpServers ?? {};
1436
+ const registryMcp = scopedManagedMcpDefs({ projectScope: Boolean(targetDir) });
1289
1437
  if (Object.keys(registryMcp).length === 0) return false;
1290
1438
 
1291
1439
  // Project scope writes a dedicated `.vscode/mcp.json` (VS Code's documented
@@ -1369,7 +1517,7 @@ function syncVSCode(targetDir = null, wants = true) {
1369
1517
  // --- Cursor adapter ---
1370
1518
 
1371
1519
  function syncCursor(targetDir = null, wants = true) {
1372
- const registryMcp = registry.mcpServers ?? {};
1520
+ const registryMcp = scopedManagedMcpDefs({ projectScope: Boolean(targetDir) });
1373
1521
  if (Object.keys(registryMcp).length === 0) return false;
1374
1522
 
1375
1523
  const cursorDir = targetDir
@@ -1567,16 +1715,72 @@ function syncOpencode(entries, targetDir = null, wants = true) {
1567
1715
 
1568
1716
  const writeEntries = globalEntries(entries);
1569
1717
 
1718
+ const hadExistingConfig = fs.existsSync(configPath);
1570
1719
  const { config } = targetDir
1571
- ? { config: fs.existsSync(configPath) ? JSON.parse(fs.readFileSync(configPath, "utf8")) : {} }
1720
+ ? { config: hadExistingConfig ? JSON.parse(fs.readFileSync(configPath, "utf8")) : {} }
1572
1721
  : readOpenCodeConfig();
1722
+ const opencodeTemplatePath = path.join(root, "platforms", "opencode", "config.template.json");
1723
+ const opencodeTemplate = fs.existsSync(opencodeTemplatePath)
1724
+ ? JSON.parse(fs.readFileSync(opencodeTemplatePath, "utf8"))
1725
+ : {};
1573
1726
  if (!config.agent) config.agent = {};
1727
+ if (!config.mcp) config.mcp = {};
1574
1728
  if (!Array.isArray(config.plugin)) config.plugin = [];
1575
1729
  config.plugin = config.plugin.filter((entry) => {
1576
1730
  if (typeof entry !== "string") return true;
1577
1731
  return entry !== managedPluginPath && entry !== toolkitPluginPath;
1578
1732
  });
1579
1733
 
1734
+ if (opencodeTemplate.$schema && !config.$schema) config.$schema = opencodeTemplate.$schema;
1735
+ if (opencodeTemplate.model && !config.model && !config.defaultModel && !hadExistingConfig) {
1736
+ config.model = opencodeTemplate.model;
1737
+ }
1738
+ if (Array.isArray(opencodeTemplate.enabled_providers) && !Array.isArray(config.enabled_providers)) {
1739
+ config.enabled_providers = [...opencodeTemplate.enabled_providers];
1740
+ }
1741
+ if (opencodeTemplate.provider && typeof opencodeTemplate.provider === "object") {
1742
+ config.provider ??= {};
1743
+ for (const [id, providerDef] of Object.entries(opencodeTemplate.provider)) {
1744
+ config.provider[id] = mergeMissingObjectDefaults(config.provider[id], resolveTemplateStrings(providerDef));
1745
+ }
1746
+ }
1747
+ if (config.provider?.ollama?.options?.headers?.Authorization === "Bearer ollama") {
1748
+ delete config.provider.ollama.options.headers.Authorization;
1749
+ if (Object.keys(config.provider.ollama.options.headers).length === 0) {
1750
+ delete config.provider.ollama.options.headers;
1751
+ }
1752
+ }
1753
+
1754
+ const templateMcp = opencodeTemplate.mcp && typeof opencodeTemplate.mcp === "object"
1755
+ ? resolveTemplateStrings(opencodeTemplate.mcp)
1756
+ : {};
1757
+ for (const [id, entry] of Object.entries(templateMcp)) {
1758
+ if (!PROJECT_DEFAULT_MCP_IDS.has(id)) continue;
1759
+ if (!config.mcp[id]) config.mcp[id] = entry;
1760
+ }
1761
+ if (config.mcp["construct-mcp"]) {
1762
+ config.mcp["construct-mcp"] = templateMcp["construct-mcp"] ?? config.mcp["construct-mcp"];
1763
+ }
1764
+
1765
+ const memoryBridgeEntry = buildOpenCodeMcpEntry("memory", {
1766
+ command: "node",
1767
+ args: ["__CX_TOOLKIT_DIR__/lib/mcp/memory-bridge.mjs"],
1768
+ env: { CONSTRUCT_MEMORY_BRIDGE_URL: "__CONSTRUCT_MEMORY_BRIDGE_URL__" },
1769
+ }, {
1770
+ ...process.env,
1771
+ CONSTRUCT_MEMORY_BRIDGE_URL:
1772
+ config.mcp.memory?.url
1773
+ || config.mcp.cass?.url
1774
+ || process.env.CONSTRUCT_MEMORY_BRIDGE_URL
1775
+ || "http://127.0.0.1:8765/",
1776
+ }).entry;
1777
+ const staleMemoryRemote = config.mcp.memory && (config.mcp.memory.type === "remote" || config.mcp.memory.type === "http");
1778
+ const staleCassRemote = config.mcp.cass && (config.mcp.cass.type === "remote" || config.mcp.cass.type === "http");
1779
+ if (staleMemoryRemote || staleCassRemote) {
1780
+ config.mcp.memory = memoryBridgeEntry;
1781
+ delete config.mcp.cass;
1782
+ }
1783
+
1580
1784
  // Sync providers
1581
1785
  const registryProviders = registry.providers ?? {};
1582
1786
  if (Object.keys(registryProviders).length > 0) {
@@ -1589,8 +1793,10 @@ function syncOpencode(entries, targetDir = null, wants = true) {
1589
1793
  ...providerDef,
1590
1794
  options: {
1591
1795
  ...providerDef.options,
1796
+ ...existing.options,
1592
1797
  headers: {
1593
1798
  ...providerDef.options?.headers,
1799
+ ...existing.options?.headers,
1594
1800
  ...(existingAuth ? { Authorization: existingAuth } : {}),
1595
1801
  },
1596
1802
  },
@@ -1602,6 +1808,8 @@ function syncOpencode(entries, targetDir = null, wants = true) {
1602
1808
  }
1603
1809
  }
1604
1810
 
1811
+ ensureOpenRouterProviderAuth(config);
1812
+
1605
1813
  // Derive anthropic models from registry tier definitions
1606
1814
  const tierModels = Object.values(registry.models ?? {}).flatMap((t) =>
1607
1815
  [t.primary, ...(t.fallback ?? [])].filter((m) => m?.startsWith('anthropic/'))
@@ -1627,7 +1835,7 @@ function syncOpencode(entries, targetDir = null, wants = true) {
1627
1835
  }
1628
1836
 
1629
1837
  // Sync MCP servers
1630
- const registryMcp = registry.mcpServers ?? {};
1838
+ const registryMcp = scopedManagedMcpDefs({ projectScope: Boolean(targetDir) });
1631
1839
  if (Object.keys(registryMcp).length > 0) {
1632
1840
  if (!config.mcp) config.mcp = {};
1633
1841
  for (const [id, mcpDef] of Object.entries(registryMcp)) {
@@ -1656,7 +1864,7 @@ function syncOpencode(entries, targetDir = null, wants = true) {
1656
1864
  // into EVERY agent's request — github ~30k alone (fixtures 2026-06-22) —
1657
1865
  // agent's request — including the built-in Build/Plan agents the per-agent
1658
1866
  // 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
1867
+ // filter (OpenCode chat.params carries no tool list), so disabling the whole server in
1660
1868
  // opencode.json is the only lever. The decision is INTENT-driven: trim only
1661
1869
  // when this config's own default model is local (or a local Ollama provider is
1662
1870
  // registered in it), so a cloud session on a machine that merely also has
@@ -1689,7 +1897,8 @@ function syncOpencode(entries, targetDir = null, wants = true) {
1689
1897
  for (const key of Object.keys(config.agent)) {
1690
1898
  const isManaged = prefixes.some((p) => key.startsWith(p));
1691
1899
  const isOrchestrator = registry.orchestrator?.name === key;
1692
- if ((isManaged || isOrchestrator) && !writeEntries.find((e) => adapterName(e) === key)) {
1900
+ const isStaleOrchestratorAlias = key === 'orchestrator' && registry.orchestrator?.name === 'construct';
1901
+ if ((isManaged || isOrchestrator || isStaleOrchestratorAlias) && !writeEntries.find((e) => adapterName(e) === key)) {
1693
1902
  delete config.agent[key];
1694
1903
  }
1695
1904
  }
@@ -1780,21 +1989,28 @@ function syncOpencode(entries, targetDir = null, wants = true) {
1780
1989
  config.construct = config.construct || {};
1781
1990
  config.construct.models = { ...resolvedModels };
1782
1991
 
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";
1992
+ // OpenCode's primary `model` is user-owned. Remove the legacy Construct seed
1993
+ // when we find it so the app can fall back to its own remembered selection
1994
+ // instead of a stale pin. New syncs never write this key back.
1995
+ const legacyPinnedModels = new Set([
1996
+ opencodeTemplate.model,
1997
+ hardDefaults.standard,
1998
+ ].filter(Boolean));
1999
+ if (!targetDir && legacyPinnedModels.has(config.model)) {
2000
+ delete config.model;
2001
+ }
2002
+
2003
+ // OpenCode's built-in helper agents drive session naming, summaries, and
2004
+ // compaction. Keep them on a stronger auxiliary model so those surfaces do not
2005
+ // inherit the main chat model's selection history or a low-end coder default.
2006
+ const auxiliaryModel = resolvedModels.reasoning || resolvedModels.standard || resolvedModels.fast;
2007
+ if (auxiliaryModel) {
2008
+ for (const key of ["title", "summary", "compaction"]) {
2009
+ config.agent[key] = {
2010
+ ...(config.agent[key] && typeof config.agent[key] === "object" ? config.agent[key] : {}),
2011
+ model: auxiliaryModel,
2012
+ };
2013
+ }
1798
2014
  }
1799
2015
 
1800
2016
  writeOpenCodeConfig(config, configPath);
@@ -1943,6 +2159,7 @@ function syncSkills(targetDir = null) {
1943
2159
 
1944
2160
  // --- Main ---
1945
2161
 
2162
+ if (isMain) {
1946
2163
  const entries = buildEntries();
1947
2164
 
1948
2165
  if (COMPRESS_PERSONAS) {
@@ -2035,3 +2252,4 @@ try {
2035
2252
  } finally {
2036
2253
  releaseLock();
2037
2254
  }
2255
+ }
@@ -6,7 +6,7 @@ artifactType: prompt
6
6
  ---
7
7
  # Prompt Auto-Optimization Loop
8
8
 
9
- Construct's prompt improvement system uses telemetry traces and quality scores as the feedback signal, Claude as the optimizer, and the agent registry (`specialists/registry.json`) as the deployment layer. This is a closed loop: production data → failure analysis → improved prompt → staging → promotion.
9
+ Construct's prompt improvement system uses telemetry traces and quality scores as the feedback signal, Claude as the optimizer, and the modular org registry (`specialists/org/`, assembled at runtime via `loadRegistry()`) as the deployment layer. This is a closed loop: production data → failure analysis → improved prompt → staging → promotion.
10
10
 
11
11
  ## Running the optimizer
12
12
 
@@ -34,7 +34,7 @@ The optimizer requires Python 3.12+ (`pip3` must be available). It will auto-ins
34
34
 
35
35
  ## Step 1: Gather signal
36
36
 
37
- Retrieve the current production prompt for the target agent from `specialists/registry.json` (or the corresponding `promptFile` if using extracted prompts).
37
+ Retrieve the current production prompt for the target agent from the specialist entry under `specialists/org/specialists/` (or the corresponding `promptFile` if using extracted prompts).
38
38
 
39
39
  Then retrieve recent traces via the telemetry backend REST API:
40
40
 
@@ -76,7 +76,7 @@ Write an improved prompt that directly addresses the diagnosed failures. Rules:
76
76
 
77
77
  ## Step 4: Push to staging
78
78
 
79
- Update the agent's prompt in `specialists/registry.json` (or the corresponding `promptFile`) with a staging marker comment. Tag the version by writing to `.cx/decisions/prompt-staging-{agent}-{date}.md`.
79
+ Update the agent's prompt in `specialists/org/specialists/<agent>.json` (or the corresponding `promptFile`) with a staging marker comment. Tag the version by writing to `.cx/decisions/prompt-staging-{agent}-{date}.md`.
80
80
 
81
81
  Log the candidate prompt as a span attribute on a test run batch using `cx_trace`. Tag spans with `promptVersion: staging-{timestamp}` and score them with `cx_score` as traces complete.
82
82
 
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: brand-output-vibe
3
+ description: Use when drafting or exporting customer-facing artifacts (PRD, deck, brief, runbook). Sets typography, prose rhythm, sketch diagrams, and export consistency.
4
+ inputs: [artifact-draft, export-target]
5
+ artifactType: style-guide
6
+ ---
7
+
8
+ # Output vibe (construct-modern)
9
+
10
+ Apply this skill before drafting or exporting any customer-facing artifact (PRD, deck, brief, runbook, video script). User instructions always win; this sets the default feel.
11
+
12
+ ## Typography and color
13
+
14
+ - Sans: Space Grotesk (400–700). Mono: JetBrains Mono for code, IDs, and tables.
15
+ - Monochrome ink ramp only for page furniture: near-black body (`#23272e`), muted labels (`#565c66`), hairline rules (`#e3e4e8`). Color belongs in diagrams and data, not chrome.
16
+ - Do not stamp "Construct" on external deliverables unless the user asks.
17
+
18
+ ## Prose rhythm
19
+
20
+ - Lead with a short declarative paragraph, then structure (headings, one compact table, selective bullets).
21
+ - Avoid bullet walls; never more than seven bullets in a row without a prose bridge.
22
+ - Rare em dashes; prefer commas or parentheses.
23
+
24
+ ## Diagrams (sketch-forward)
25
+
26
+ - Prefer hand-drawn / sketch aesthetic over rigid box-and-arrow clipart.
27
+ - In Mermaid, open with a sketch theme block:
28
+
29
+ ```mermaid
30
+ %%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#fafafa', 'primaryBorderColor': '#565c66', 'lineColor': '#23272e', 'fontFamily': 'Space Grotesk'}}}%%
31
+ flowchart LR
32
+ User --> Product
33
+ ```
34
+
35
+ - For high-touch user flows, an Excalidraw embed (`*.excalidraw` beside the doc) is acceptable when Mermaid feels too mechanical.
36
+ - Every flowchart shows at least one non-happy path (error, escalation, or rollback).
37
+
38
+ ## Tables and metrics
39
+
40
+ - Use tables for metrics, acceptance criteria, and comparisons — not for narrative.
41
+ - Pin baselines and targets; cite sources or mark `[unverified]`.
42
+
43
+ ## Export parity
44
+
45
+ - PDF/HTML/PPTX inherit the same fonts and ink ramp from distribution templates.
46
+ - No stock clip-art icons; use simple line icons or typographic labels.
47
+
48
+ When invoked at the start of a workflow skill, restate which vibe choices apply to this artifact type in one sentence, then proceed.
@@ -30,3 +30,6 @@ Use when: an architectural decision is made that affects the system structure, d
30
30
  ## Cross-referencing
31
31
  - If this ADR supersedes another: update the old ADR's status field
32
32
  - If this ADR depends on another: add a References link
33
+ ## Release gate
34
+
35
+ Run `construct artifact validate <path> --type=<type>` before marking the artifact approved.
@@ -21,3 +21,6 @@ Use when: product evidence should create or update Jira, Linear, GitHub Issues,
21
21
  ## Rules
22
22
 
23
23
  Never write externally from weak evidence without making the risk explicit. Never create duplicate issues when an existing issue can be updated.
24
+ ## Release gate
25
+
26
+ Run `construct artifact validate <path> --type=<type>` before marking the artifact approved.