@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
@@ -0,0 +1,86 @@
1
+ /**
2
+ * lib/mcp/tools/find-tool.mjs — find_tool: intent-driven tool discovery.
3
+ *
4
+ * Ranks the tool catalog against a natural-language query so an agent that knows
5
+ * what it wants to do, but not the exact tool name, gets the right tools with
6
+ * their schemas. Hybrid scoring: normalized BM25 (always, offline) merged with
7
+ * local-embedding cosine when a real semantic model is available. Degraded
8
+ * hashing embeddings are ignored so they cannot pollute the lexical ranking.
9
+ */
10
+ import { rankByBm25, cosineSimilarity } from '../../storage/embeddings.mjs';
11
+
12
+ let toolVectorCache = null;
13
+
14
+ function buildCorpus(toolDefs) {
15
+ return toolDefs.map((t) => ({
16
+ name: t.name,
17
+ description: t.description || '',
18
+ inputSchema: t.inputSchema,
19
+ text: `${t.name} ${t.description || ''}`,
20
+ }));
21
+ }
22
+
23
+ async function mergeCosine(scoreMap, corpus, query, env) {
24
+ const { embedText, embedBatch } = await import('../../storage/embeddings-engine.mjs');
25
+ const q = await embedText(query, { env });
26
+
27
+ // A degraded result means the real model was unavailable and hashing stood in;
28
+ // those vectors carry no semantic signal, so leave the BM25 ranking unmerged.
29
+
30
+ if (q?.degraded || !q?.embedding) return;
31
+
32
+ // Embed the catalog once per process: the tool set is static for the server's
33
+ // lifetime, so size-equality is a sufficient cache key. If tools ever become
34
+ // dynamic (MCP list_changed), this needs a real version key. embedBatch
35
+ // degrades all-or-nothing, so one degraded vector means the whole batch fell
36
+ // back to hashing — skip the merge rather than mix real and hashed vectors.
37
+
38
+ if (!toolVectorCache || toolVectorCache.size !== corpus.length) {
39
+ const vecs = await embedBatch(corpus.map((c) => c.text), { env });
40
+ if (vecs.some((v) => v?.degraded)) return;
41
+ toolVectorCache = new Map(corpus.map((c, i) => [c.name, vecs[i].embedding]));
42
+ }
43
+ for (const c of corpus) {
44
+ const cos = cosineSimilarity(q.embedding, toolVectorCache.get(c.name) || []);
45
+ if (cos > 0.05) scoreMap.set(c.name, Math.max(scoreMap.get(c.name) || 0, cos));
46
+ }
47
+ }
48
+
49
+ export async function findTool(args = {}, { toolDefs = [], env = process.env } = {}) {
50
+ const query = String(args.query || args.intent || '').trim();
51
+ if (!query) return { error: 'find_tool requires a `query` describing what you want to do.' };
52
+ const limit = Math.min(Math.max(Number(args.limit) || 5, 1), 20);
53
+
54
+ const corpus = buildCorpus(toolDefs);
55
+
56
+ // BM25 is unbounded; normalize against its own max so it merges fairly with
57
+ // cosine on [0,1].
58
+
59
+ const bm25 = rankByBm25(corpus, query, { limit: 20 });
60
+ const bm25Max = bm25[0]?.score || 1;
61
+ const scoreMap = new Map();
62
+ for (const d of bm25) scoreMap.set(d.name, Math.min(d.score / bm25Max, 1));
63
+
64
+ try {
65
+ await mergeCosine(scoreMap, corpus, query, env);
66
+ } catch {
67
+ /* embeddings unavailable — BM25-only is the offline-safe path */
68
+ }
69
+
70
+ const byName = new Map(corpus.map((c) => [c.name, c]));
71
+ const tools = [...scoreMap.entries()]
72
+ .sort((a, b) => b[1] - a[1])
73
+ .slice(0, limit)
74
+ .map(([name, score]) => ({
75
+ name,
76
+ description: byName.get(name).description,
77
+ inputSchema: byName.get(name).inputSchema,
78
+ score: Number(score.toFixed(3)),
79
+ }));
80
+
81
+ return {
82
+ query,
83
+ tools,
84
+ note: 'Invoke a result with { name: "call", arguments: { tool: "<name>", args: {…} } }, or call it directly if it is already a flat tool.',
85
+ };
86
+ }
@@ -1,123 +1,161 @@
1
1
  /**
2
- * lib/mcp/tools/orchestration-run.mjs — MCP client for the local orchestration daemon.
2
+ * lib/mcp/tools/orchestration-run.mjs — MCP entry for Construct orchestration.
3
3
  *
4
- * Executes a real multi-specialist run by driving the daemon's HTTP API
5
- * (ADR-0022), so MCP hosts with no subagent primitive VS Code/Copilot, Cursor
6
- * reach orchestrated outcomes through a tool they actually expose: the engine
7
- * owns orchestration; this tool is the thin client. The daemon URL resolves from
8
- * dashboard state (else the default loopback port) and the bearer token from
9
- * ~/.construct/config.env. When the daemon is unreachable the tool fails fast
10
- * with an actionable error rather than silently degrading to a single-persona
11
- * pass the absence of an executable path is made loud, not hidden.
4
+ * Runs a real multi-specialist orchestration so MCP hosts with no subagent
5
+ * primitive (VS Code/Copilot, Cursor) reach orchestrated outcomes through a tool
6
+ * they already expose. The engine is the in-process orchestration runtime
7
+ * (lib/orchestration/runtime.mjs) solo runs need no daemon, no port, no token
8
+ * (ADR-0022: the engine owns orchestration; ADR-0041 owned loop). When
9
+ * CONSTRUCT_ORCHESTRATION_URL points at a remote/team orchestration service the
10
+ * same call is proxied over HTTP instead, with the bearer token resolved from the
11
+ * Construct env (CONSTRUCT_ORCHESTRATION_TOKEN / CONSTRUCT_DASHBOARD_TOKEN), not
12
+ * from the dashboard server.
12
13
  */
13
14
 
14
- import { readDashboardState } from '../../service-manager.mjs';
15
- import { getDashboardToken } from '../../server/auth.mjs';
15
+ import { runOrchestration, startRun, getRun, getRuns } from '../../orchestration/runtime.mjs';
16
16
 
17
17
  const TERMINAL = ['completed', 'completed-with-failures', 'cancelled', 'error'];
18
18
 
19
- function resolveDaemon(env = process.env) {
20
- const state = readDashboardState();
21
- const base = (env.CONSTRUCT_ORCHESTRATION_URL || state?.url || `http://${env.BIND_HOST || '127.0.0.1'}:${env.PORT || '4242'}`).replace(/\/$/, '');
22
- return { base, token: getDashboardToken(), daemonRunning: Boolean(state) };
19
+ function shapeRun(run) {
20
+ return {
21
+ runId: run.runId,
22
+ status: run.status,
23
+ executionMode: run.execution?.executionMode,
24
+ degraded: run.execution?.degraded ?? false,
25
+ degradationReason: run.execution?.degradationReason ?? null,
26
+ intent: run.plan?.intent ?? null,
27
+ track: run.plan?.track ?? null,
28
+ suggestedWorkflowType: run.plan?.suggestedWorkflowType ?? null,
29
+ researchExecutionPolicy: run.plan?.researchExecutionPolicy ?? null,
30
+ specialists: run.plan?.specialists ?? [],
31
+ tasks: (run.tasks || []).map((t) => ({
32
+ id: t.id, role: t.role, status: t.status, executor: t.executor,
33
+ output: t.output ?? null, reasoning: t.reasoning ?? null, error: t.error ?? null,
34
+ })),
35
+ };
36
+ }
37
+
38
+ function toRequest(args) {
39
+ return {
40
+ request: args.request,
41
+ workflowType: args.workflow_type,
42
+ requestedStrategy: args.requested_strategy ?? 'auto',
43
+ host: args.host,
44
+ hostModel: args.host_model,
45
+ hostProvider: args.host_provider,
46
+ fileCount: args.file_count,
47
+ moduleCount: args.module_count,
48
+ };
23
49
  }
24
50
 
25
- // A reachability failure is the fail-fast signal MCP hosts need: report how to
26
- // start the engine instead of letting the host fall back to a manual pass.
51
+ // A remote orchestration service is opt-in: only when CONSTRUCT_ORCHESTRATION_URL
52
+ // is set (team / enterprise). The token comes from the Construct env, not from
53
+ // the dashboard server, so the in-process default path carries no dashboard coupling.
54
+
55
+ function remoteService(env) {
56
+ const url = env.CONSTRUCT_ORCHESTRATION_URL;
57
+ if (!url) return null;
58
+ const token = env.CONSTRUCT_ORCHESTRATION_TOKEN || env.CONSTRUCT_DASHBOARD_TOKEN || null;
59
+ return { base: url.replace(/\/$/, ''), token };
60
+ }
27
61
 
28
62
  function unreachable(base, detail) {
29
63
  return {
30
- error: `Orchestration daemon not reachable at ${base}: ${detail}. Start it with \`construct dashboard\`, then retry. The engine runs orchestration; this host is a thin client (ADR-0022).`,
31
- daemon: base,
64
+ error: `Remote orchestration service not reachable at ${base}: ${detail}. Unset CONSTRUCT_ORCHESTRATION_URL to run in-process, or start the service and retry.`,
65
+ service: base,
32
66
  failFast: true,
33
67
  };
34
68
  }
35
69
 
36
- export async function orchestrationRun(args = {}, { fetchImpl = fetch, env = process.env } = {}) {
37
- const {
38
- request,
39
- workflow_type,
40
- requested_strategy = 'auto',
41
- worker_backend,
42
- host,
43
- host_model,
44
- host_provider,
45
- file_count,
46
- module_count,
47
- wait = true,
48
- timeout_ms = 120000,
49
- } = args;
50
-
51
- if (!request || typeof request !== 'string') return { error: 'Missing "request" string describing the work to orchestrate.' };
52
-
53
- const { base, token } = resolveDaemon(env);
54
- const headers = { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) };
55
- const body = JSON.stringify({
56
- request,
57
- workflowType: workflow_type,
58
- requestedStrategy: requested_strategy,
59
- workerBackend: worker_backend,
60
- host,
61
- hostModel: host_model,
62
- hostProvider: host_provider,
63
- fileCount: file_count,
64
- moduleCount: module_count,
65
- });
70
+ export async function orchestrationRun(args = {}, { env = process.env, cwd = process.cwd(), fetchImpl = fetch } = {}) {
71
+ const { request, worker_backend, wait = true, timeout_ms = 120000 } = args;
72
+ if (!request || typeof request !== 'string') {
73
+ return { error: 'Missing "request" string describing the work to orchestrate.' };
74
+ }
75
+
76
+ const reqObj = toRequest(args);
77
+ const remote = remoteService(env);
78
+ if (remote) return runViaService(remote, reqObj, { wait, timeout_ms, workerBackend: worker_backend, fetchImpl });
79
+
80
+ const opts = { env, cwd, workerBackend: worker_backend, fetchImpl };
81
+ try {
82
+ if (!wait) {
83
+ const planned = await startRun(reqObj, opts);
84
+ return { runId: planned.runId, status: planned.status, executionMode: planned.execution?.executionMode, poll: 'orchestration_status with run_id' };
85
+ }
86
+ return shapeRun(await runOrchestration(reqObj, opts));
87
+ } catch (err) {
88
+ return { error: `Orchestration failed: ${err.message}`, failFast: true };
89
+ }
90
+ }
91
+
92
+ export async function orchestrationStatus(args = {}, { env = process.env, cwd = process.cwd(), fetchImpl = fetch } = {}) {
93
+ const { run_id, limit = 20 } = args;
94
+ const remote = remoteService(env);
95
+ if (remote) return statusViaService(remote, { run_id, limit }, { fetchImpl });
96
+ try {
97
+ if (run_id) {
98
+ const run = await getRun(cwd, run_id, { env });
99
+ return run || { error: `No run found with id ${run_id}.` };
100
+ }
101
+ return await getRuns(cwd, { env, limit });
102
+ } catch (err) {
103
+ return { error: `Failed to read orchestration runs: ${err.message}` };
104
+ }
105
+ }
106
+
107
+ // Remote (team / enterprise) HTTP path. Reached only when CONSTRUCT_ORCHESTRATION_URL
108
+ // is set; the wire shape matches the standalone orchestration service's
109
+ // /api/orchestration/runs contract (ADR-0022).
66
110
 
111
+ async function runViaService(remote, reqObj, { wait, timeout_ms, workerBackend, fetchImpl }) {
112
+ const { base, token } = remote;
113
+ const headers = { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) };
114
+ const body = JSON.stringify({ ...reqObj, workerBackend });
67
115
  let started;
68
116
  try {
69
117
  const res = await fetchImpl(`${base}/api/orchestration/runs`, { method: 'POST', headers, body });
70
118
  if (res.status === 401 || res.status === 403) {
71
- return { error: `Daemon rejected the request (HTTP ${res.status}). The dashboard token is missing or wrong — check CONSTRUCT_DASHBOARD_TOKEN in ~/.construct/config.env.`, daemon: base, failFast: true };
119
+ return { error: `Remote service rejected the request (HTTP ${res.status}). Set CONSTRUCT_ORCHESTRATION_TOKEN (or CONSTRUCT_DASHBOARD_TOKEN).`, service: base, failFast: true };
72
120
  }
73
121
  const envelope = await res.json();
74
- if (!res.ok) return { error: `Daemon error (HTTP ${res.status}): ${envelope.error || 'unknown'}`, daemon: base };
122
+ if (!res.ok) return { error: `Remote service error (HTTP ${res.status}): ${envelope.error || 'unknown'}`, service: base };
75
123
  started = envelope.data;
76
124
  } catch (err) {
77
125
  return unreachable(base, err.message);
78
126
  }
79
127
 
80
128
  if (!wait) {
81
- return { runId: started.runId, status: started.status, executionMode: started.execution?.executionMode, daemon: base, poll: 'orchestration_status with run_id' };
129
+ return { runId: started.runId, status: started.status, executionMode: started.execution?.executionMode, service: base, poll: 'orchestration_status with run_id' };
82
130
  }
83
131
 
84
132
  const deadline = Date.now() + timeout_ms;
85
133
  let run = started;
86
134
  while (!TERMINAL.includes(run.status)) {
87
135
  if (Date.now() > deadline) {
88
- return { runId: started.runId, status: run.status, timedOut: true, daemon: base, note: 'Run is still executing; poll it with orchestration_status.' };
136
+ return { runId: started.runId, status: run.status, timedOut: true, service: base, note: 'Run is still executing; poll it with orchestration_status.' };
89
137
  }
90
138
  await new Promise((r) => setTimeout(r, 250));
91
139
  try {
92
140
  const res = await fetchImpl(`${base}/api/orchestration/runs/${encodeURIComponent(started.runId)}`, { headers });
93
141
  const envelope = await res.json();
94
- if (!res.ok) return { error: `Daemon error while polling (HTTP ${res.status}): ${envelope.error || 'unknown'}`, runId: started.runId, daemon: base };
142
+ if (!res.ok) return { error: `Remote service error while polling (HTTP ${res.status}): ${envelope.error || 'unknown'}`, runId: started.runId, service: base };
95
143
  run = envelope.data;
96
144
  } catch (err) {
97
- return { error: `Lost connection to the daemon while polling run ${started.runId}: ${err.message}`, runId: started.runId, daemon: base, failFast: true };
145
+ return { error: `Lost connection to the remote service while polling run ${started.runId}: ${err.message}`, runId: started.runId, service: base, failFast: true };
98
146
  }
99
147
  }
100
-
101
- return {
102
- runId: run.runId,
103
- status: run.status,
104
- executionMode: run.execution?.executionMode,
105
- degraded: run.execution?.degraded ?? false,
106
- degradationReason: run.execution?.degradationReason ?? null,
107
- tasks: (run.tasks || []).map((t) => ({ id: t.id, role: t.role, status: t.status, executor: t.executor, output: t.output ?? null, reasoning: t.reasoning ?? null, error: t.error ?? null })),
108
- daemon: base,
109
- };
148
+ return { ...shapeRun(run), service: base };
110
149
  }
111
150
 
112
- export async function orchestrationStatus(args = {}, { fetchImpl = fetch, env = process.env } = {}) {
113
- const { run_id, limit = 20 } = args;
114
- const { base, token } = resolveDaemon(env);
151
+ async function statusViaService(remote, { run_id, limit }, { fetchImpl }) {
152
+ const { base, token } = remote;
115
153
  const headers = token ? { Authorization: `Bearer ${token}` } : {};
116
154
  const path = run_id ? `/api/orchestration/runs/${encodeURIComponent(run_id)}` : `/api/orchestration/runs?limit=${encodeURIComponent(limit)}`;
117
155
  try {
118
156
  const res = await fetchImpl(`${base}${path}`, { headers });
119
157
  const envelope = await res.json();
120
- if (!res.ok) return { error: `Daemon error (HTTP ${res.status}): ${envelope.error || 'unknown'}`, daemon: base };
158
+ if (!res.ok) return { error: `Remote service error (HTTP ${res.status}): ${envelope.error || 'unknown'}`, service: base };
121
159
  return envelope.data;
122
160
  } catch (err) {
123
161
  return unreachable(base, err.message);
@@ -5,13 +5,14 @@
5
5
  * All tools are synchronous except where noted. Requires ROOT_DIR and homedir from the caller.
6
6
  */
7
7
  import { readFileSync, readdirSync, existsSync } from 'node:fs';
8
+ import { loadRegistry } from '../../registry/loader.mjs';
8
9
  import { join, resolve } from 'node:path';
9
10
  import { homedir } from 'node:os';
10
11
  import { execSync } from 'node:child_process';
11
12
  import { loadConstructEnv } from '../../env-config.mjs';
12
13
  import { inspectContextState } from '../../context-state.mjs';
13
14
  import { loadWorkflow, summarizeWorkflow, inspectWorkflowHealth } from '../../workflow-state.mjs';
14
- import { readCurrentModels, resolveExecutionContractModelMetadata, selectModelTierForWorkCategory } from '../../model-router.mjs';
15
+ import { resolveExecutionContractModelMetadata, selectModelTierForWorkCategory } from '../../model-router.mjs';
15
16
  import { buildPublicHealthSurface } from '../../status.mjs';
16
17
  import { doctorRoot } from '../../config/xdg.mjs';
17
18
 
@@ -282,7 +283,7 @@ export function projectContext(args, { ROOT_DIR }) {
282
283
  const has_context_file = contextInspection.hasFile;
283
284
  const workflow = loadWorkflow(cwd);
284
285
  const mergedEnv = loadConstructEnv({ rootDir: ROOT_DIR, homeDir: homedir(), env: process.env });
285
- const registry = readJSON(join(ROOT_DIR, 'specialists', 'registry.json')) ?? {};
286
+ const registry = loadRegistry({ rootDir: ROOT_DIR });
286
287
  const executionContractModel = resolveExecutionContractModelMetadata({
287
288
  envValues: mergedEnv,
288
289
  registryModels: registry.models ?? {},
@@ -324,7 +325,7 @@ export function workflowStatus(args, { ROOT_DIR }) {
324
325
  const workflow = loadWorkflow(cwd);
325
326
  const workflowHealth = inspectWorkflowHealth(workflow, { cwd });
326
327
  const mergedEnv = loadConstructEnv({ rootDir: ROOT_DIR, homeDir: homedir(), env: process.env });
327
- const registry = readJSON(join(ROOT_DIR, 'specialists', 'registry.json')) ?? {};
328
+ const registry = loadRegistry({ rootDir: ROOT_DIR });
328
329
  const executionContractModel = resolveExecutionContractModelMetadata({
329
330
  envValues: mergedEnv,
330
331
  registryModels: registry.models ?? {},
@@ -1,7 +1,7 @@
1
1
  /**
2
- * lib/mcp/tools/profile.mjs — MCP wrappers for the org-profile + learning surfaces.
2
+ * lib/mcp/tools/scope.mjs — MCP wrappers for the org-scope + learning surfaces.
3
3
  *
4
- * Bridges PR #67 capabilities (profiles, outcomes, sandboxes, knowledge_add,
4
+ * Bridges PR #67 capabilities (scopes, outcomes, sandboxes, knowledge_add,
5
5
  * learning status) so subagents talking through the construct-mcp server can
6
6
  * read the same state the CLI exposes. Operator-only surfaces (optimize_apply,
7
7
  * optimize_rollback) remain CLI-only and are not registered here.
@@ -9,7 +9,7 @@
9
9
  * Pattern: each export is a thin schema-validated wrapper around the existing
10
10
  * lib/* function the CLI already uses. Errors return `{ error: string }` so
11
11
  * the MCP dispatcher hands a structured failure back instead of crashing.
12
- * Destructive operations (profile_archive) require `confirm: true`, matching
12
+ * Destructive operations (scope_archive) require `confirm: true`, matching
13
13
  * the storage_reset / delete_ingested_artifacts pattern in tools/storage.mjs.
14
14
  */
15
15
  import { resolve } from 'node:path';
@@ -17,17 +17,17 @@ import { existsSync, readFileSync, readdirSync } from 'node:fs';
17
17
  import { join } from 'node:path';
18
18
 
19
19
  import {
20
- loadProfile,
21
- listProfiles,
22
- loadCustomProfile,
23
- resolveActiveProfile,
24
- } from '../../profiles/loader.mjs';
20
+ loadScope,
21
+ listScopes,
22
+ loadCustomScope,
23
+ resolveActiveScope,
24
+ } from '../../scopes/loader.mjs';
25
25
  import {
26
- createDraftProfile,
26
+ createDraftScope,
27
27
  listDrafts,
28
- archiveProfile,
29
- profileHealth,
30
- } from '../../profiles/lifecycle.mjs';
28
+ archiveScope,
29
+ scopeHealth,
30
+ } from '../../scopes/lifecycle.mjs';
31
31
  import { recordOutcome } from '../../outcomes/record.mjs';
32
32
  import { aggregateOutcomes, readSummary } from '../../outcomes/aggregate.mjs';
33
33
  import { addResearchFinding } from '../../knowledge/research-store.mjs';
@@ -38,73 +38,75 @@ function cwdOf(args) {
38
38
  return args && args.cwd ? resolve(String(args.cwd)) : process.cwd();
39
39
  }
40
40
 
41
- // Read-only: active profile.
41
+ // Read-only: active scope.
42
42
 
43
- export function profileShow(args = {}) {
43
+ export function scopeShow(args = {}) {
44
44
  const cwd = cwdOf(args);
45
- const profile = resolveActiveProfile(cwd, args.id ? String(args.id) : null);
45
+ const scope = resolveActiveScope(cwd, args.id ? String(args.id) : null);
46
46
  return {
47
- id: profile.id,
48
- displayName: profile.displayName ?? profile.id,
49
- tagline: profile.tagline ?? null,
50
- custom: profile.custom === true,
51
- roles: Array.isArray(profile.roles) ? profile.roles : [],
52
- departments: Array.isArray(profile.departments)
53
- ? profile.departments.map((d) => ({ id: d.id, displayName: d.displayName, roleCount: Array.isArray(d.roles) ? d.roles.length : 0 }))
47
+ id: scope.id,
48
+ displayName: scope.displayName ?? scope.id,
49
+ tagline: scope.tagline ?? null,
50
+ custom: scope.custom === true,
51
+ roles: Array.isArray(scope.roles) ? scope.roles : [],
52
+ teams: Array.isArray(scope.teams)
53
+ ? scope.teams.map((t) => ({ id: t.id, name: t.name, roleCount: Array.isArray(t.roles) ? t.roles.length : 0 }))
54
54
  : [],
55
+ experimental: scope.experimental === true,
55
56
  intake: {
56
- types: profile.intake?.types ?? [],
57
- stages: profile.intake?.stages ?? [],
57
+ types: scope.intake?.types ?? [],
58
+ stages: scope.intake?.stages ?? [],
58
59
  },
59
- docTemplates: profile.docTemplates ?? [],
60
- hooks: profile.hooks ?? null,
61
- rebrand: profile.rebrand ?? null,
60
+ docTemplates: scope.docTemplates ?? [],
61
+ hooks: scope.hooks ?? null,
62
+ rebrand: scope.rebrand ?? null,
62
63
  };
63
64
  }
64
65
 
65
66
  // Read-only: catalog summary.
66
67
 
67
- export function profileList() {
68
+ export function scopeList() {
68
69
  return {
69
- profiles: listProfiles().map((id) => {
70
- const p = loadProfile(id);
70
+ scopes: listScopes().map((id) => {
71
+ const p = loadScope(id);
71
72
  return p
72
73
  ? {
73
74
  id: p.id,
74
75
  displayName: p.displayName ?? p.id,
75
76
  tagline: p.tagline ?? null,
76
77
  roleCount: Array.isArray(p.roles) ? p.roles.length : 0,
77
- departmentCount: Array.isArray(p.departments) ? p.departments.length : 0,
78
+ teamCount: Array.isArray(p.teams) ? p.teams.length : 0,
79
+ experimental: p.experimental === true,
78
80
  }
79
81
  : { id, error: 'failed to parse' };
80
82
  }),
81
83
  };
82
84
  }
83
85
 
84
- // Read-only: drafts + custom profile under .cx/.
86
+ // Read-only: drafts + custom scope under .cx/.
85
87
 
86
- export function profileDrafts(args = {}) {
88
+ export function scopeDrafts(args = {}) {
87
89
  const cwd = cwdOf(args);
88
90
  const drafts = listDrafts(cwd).map((d) => ({
89
91
  id: d.id,
90
92
  dir: d.dir,
91
- hasProfile: d.hasProfile,
93
+ hasScope: d.hasScope,
92
94
  hasBrief: d.hasBrief,
93
95
  }));
94
- const custom = loadCustomProfile(cwd);
96
+ const custom = loadCustomScope(cwd);
95
97
  return {
96
98
  drafts,
97
99
  custom: custom ? { id: custom.id, displayName: custom.displayName ?? custom.id } : null,
98
100
  };
99
101
  }
100
102
 
101
- // Read-only: per-profile health rollup.
103
+ // Read-only: per-scope health rollup.
102
104
 
103
- export function profileHealthTool(args = {}) {
105
+ export function scopeHealthTool(args = {}) {
104
106
  const cwd = cwdOf(args);
105
- const id = args.id ? String(args.id) : resolveActiveProfile(cwd).id;
107
+ const id = args.id ? String(args.id) : resolveActiveScope(cwd).id;
106
108
  const windowDays = Number.isFinite(args.window_days) ? Number(args.window_days) : 30;
107
- return { id, windowDays, ...profileHealth(cwd, id, { windowDays }) };
109
+ return { id, windowDays, ...scopeHealth(cwd, id, { windowDays }) };
108
110
  }
109
111
 
110
112
  // Read-only: outcomes rollup. Optional aggregate=true rebuilds _summary.json.
@@ -129,7 +131,7 @@ export function outcomesRecord(args = {}) {
129
131
  if (!args.role || typeof args.success !== 'boolean') {
130
132
  return { error: 'outcomes_record requires role:string and success:boolean' };
131
133
  }
132
- const profile = args.profile ? String(args.profile) : resolveActiveProfile(cwd)?.id;
134
+ const profile = args.profile ? String(args.profile) : resolveActiveScope(cwd)?.id;
133
135
  const file = recordOutcome(cwd, {
134
136
  role: String(args.role),
135
137
  intakeId: args.intake_id ? String(args.intake_id) : null,
@@ -167,16 +169,16 @@ export async function knowledgeAdd(args = {}) {
167
169
  }
168
170
  }
169
171
 
170
- // Mutating, gated: scaffold a draft profile.
172
+ // Mutating, gated: scaffold a draft scope.
171
173
 
172
- export function profileCreate(args = {}) {
174
+ export function scopeCreate(args = {}) {
173
175
  if (args.confirm !== true) {
174
- return { error: 'profile_create requires confirm=true to scaffold files' };
176
+ return { error: 'scope_create requires confirm=true to scaffold files' };
175
177
  }
176
178
  const cwd = cwdOf(args);
177
- if (!args.id) return { error: 'profile_create requires id:string' };
179
+ if (!args.id) return { error: 'scope_create requires id:string' };
178
180
  try {
179
- const res = createDraftProfile({
181
+ const res = createDraftScope({
180
182
  cwd,
181
183
  id: String(args.id),
182
184
  displayName: args.display_name ? String(args.display_name) : undefined,
@@ -196,17 +198,17 @@ export function profileCreate(args = {}) {
196
198
  }
197
199
  }
198
200
 
199
- // Mutating, gated, destructive: archive a curated profile.
201
+ // Mutating, gated, destructive: archive a curated scope.
200
202
 
201
- export function profileArchive(args = {}) {
203
+ export function scopeArchive(args = {}) {
202
204
  if (args.confirm !== true) {
203
- return { error: 'profile_archive requires confirm=true (destructive: moves files into archive/profiles/)' };
205
+ return { error: 'scope_archive requires confirm=true (destructive: moves files into archive/scopes/)' };
204
206
  }
205
207
  if (!args.id || !args.reason || String(args.reason).trim().length < 8) {
206
- return { error: 'profile_archive requires id:string and reason:string (>=8 chars)' };
208
+ return { error: 'scope_archive requires id:string and reason:string (>=8 chars)' };
207
209
  }
208
210
  try {
209
- return { ok: true, ...archiveProfile({ id: String(args.id), reason: String(args.reason) }) };
211
+ return { ok: true, ...archiveScope({ id: String(args.id), reason: String(args.reason) }) };
210
212
  } catch (err) {
211
213
  return { error: err.message ?? String(err) };
212
214
  }
@@ -258,11 +260,11 @@ export function learningStatus(args = {}) {
258
260
  }
259
261
  } catch { /* best effort */ }
260
262
 
261
- const profile = resolveActiveProfile(cwd);
263
+ const scope = resolveActiveScope(cwd);
262
264
  const outcomes = readSummary(cwd) || { roles: {} };
263
265
 
264
266
  return {
265
- profile: { id: profile.id, displayName: profile.displayName ?? profile.id, custom: profile.custom === true },
267
+ scope: { id: scope.id, displayName: scope.displayName ?? scope.id, custom: scope.custom === true },
266
268
  observations,
267
269
  research: { count: researchCount },
268
270
  outcomes,