@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
@@ -7,14 +7,17 @@
7
7
  */
8
8
 
9
9
  import fs from 'node:fs';
10
+ import { loadRegistry } from '../registry/loader.mjs';
10
11
  import path from 'node:path';
11
12
  import { createHash } from 'node:crypto';
12
13
  import { artifactTypes, loadArtifactManifest } from '../artifact-manifest.mjs';
14
+ import { validateSkillEffectiveness } from '../validators/skill-effectiveness.mjs';
15
+ import { collectScopeRoleIds } from '../scopes/enrich.mjs';
13
16
 
14
17
  function findConstructRoot(startPath = process.cwd()) {
15
18
  let current = path.resolve(startPath);
16
19
  while (true) {
17
- if (fs.existsSync(path.join(current, 'specialists', 'registry.json'))) return current;
20
+ if (fs.existsSync(path.join(current, 'specialists', 'org'))) return current;
18
21
  const parent = path.dirname(current);
19
22
  if (parent === current) return path.resolve(startPath);
20
23
  current = parent;
@@ -37,22 +40,18 @@ function collectSkillFiles(skillsDir) {
37
40
  return results.sort();
38
41
  }
39
42
 
40
- function collectProfileRoles(root) {
41
- const dir = path.join(root, 'profiles');
43
+ function collectScopeRoles(root) {
44
+ const dir = path.join(root, 'specialists', 'org', 'scopes');
42
45
  const roles = new Set();
43
- const scan = (node) => {
44
- if (Array.isArray(node)) { node.forEach(scan); return; }
45
- if (node && typeof node === 'object') {
46
- if (Array.isArray(node.roles)) for (const r of node.roles) if (typeof r === 'string') roles.add(r);
47
- for (const v of Object.values(node)) scan(v);
48
- }
49
- };
50
46
  try {
51
47
  for (const f of fs.readdirSync(dir)) {
52
48
  if (!f.endsWith('.json')) continue;
53
- try { scan(JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8'))); } catch { /* skip malformed */ }
49
+ try {
50
+ const raw = JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8'));
51
+ for (const r of collectScopeRoleIds(raw, { rootDir: root })) roles.add(r);
52
+ } catch { /* skip malformed */ }
54
53
  }
55
- } catch { /* no profiles dir */ }
54
+ } catch { /* no scopes dir */ }
56
55
  return roles;
57
56
  }
58
57
 
@@ -96,19 +95,20 @@ function verificationHooks(meta, body) {
96
95
  }
97
96
 
98
97
  function resolveOwners(skillId, { registry, declaredSkills, ownerBases }) {
98
+ if (skillId.startsWith('brand/')) return ['cx-docs-keeper'];
99
99
  const owners = new Set();
100
- for (const agent of registry.specialists ?? []) {
100
+ for (const agent of Object.values(registry.specialists ?? {})) {
101
101
  if ((agent.skills ?? []).includes(skillId)) owners.add(`cx-${agent.name}`);
102
102
  }
103
103
  const base = roleBase(skillId);
104
104
  if (base && ownerBases.has(base)) {
105
- for (const agent of registry.specialists ?? []) {
105
+ for (const agent of Object.values(registry.specialists ?? {})) {
106
106
  if (agent.name === base || agent.name.replace(/^cx-/, '') === base) owners.add(`cx-${agent.name}`);
107
107
  }
108
- if (!owners.size) owners.add(`profile-role:${base}`);
108
+ if (!owners.size) owners.add(`scope-role:${base}`);
109
109
  }
110
110
  if (declaredSkills.has(skillId) && owners.size === 0) {
111
- for (const agent of registry.specialists ?? []) {
111
+ for (const agent of Object.values(registry.specialists ?? {})) {
112
112
  if ((agent.skills ?? []).includes(skillId)) owners.add(`cx-${agent.name}`);
113
113
  }
114
114
  }
@@ -138,10 +138,11 @@ function detectConflictingOutputs(skills) {
138
138
  for (const [artifactType, entries] of byType) {
139
139
  if (entries.length < 2) continue;
140
140
  const bars = new Set(entries.map((e) => e.verificationHooks.find((h) => h.kind === 'verificationBar')?.value ?? ''));
141
+ if (bars.size <= 1) continue;
141
142
  conflicts.push({
142
143
  artifactType,
143
144
  skillIds: entries.map((e) => e.id),
144
- reason: bars.size > 1 ? 'divergent verificationBar across workflow skills' : 'multiple workflow skills claim the same manifest artifact type',
145
+ reason: 'divergent verificationBar across workflow skills',
145
146
  });
146
147
  }
147
148
  return conflicts;
@@ -154,15 +155,15 @@ export function defaultSkillInventoryPath(rootDir = process.cwd()) {
154
155
  export function buildSkillInventory({ rootDir } = {}) {
155
156
  const root = findConstructRoot(rootDir);
156
157
  const skillsDir = path.join(root, 'skills');
157
- const registry = JSON.parse(fs.readFileSync(path.join(root, 'specialists', 'registry.json'), 'utf8'));
158
+ const registry = loadRegistry({ rootDir: root });
158
159
  const manifest = loadArtifactManifest({ rootDir: root });
159
160
  const declaredSkills = new Set();
160
- for (const agent of registry.specialists ?? []) {
161
+ for (const agent of Object.values(registry.specialists ?? {})) {
161
162
  for (const skill of agent.skills ?? []) declaredSkills.add(skill);
162
163
  }
163
164
  const ownerBases = new Set([
164
- ...(registry.specialists ?? []).map((a) => a.name.replace(/^cx-/, '')),
165
- ...collectProfileRoles(root),
165
+ ...Object.values(registry.specialists ?? {}).map((a) => a.name.replace(/^cx-/, '')),
166
+ ...collectScopeRoles(root),
166
167
  ]);
167
168
  const workflowIds = workflowSkillIds(manifest);
168
169
 
@@ -202,6 +203,14 @@ export function buildSkillInventory({ rootDir } = {}) {
202
203
  });
203
204
  }
204
205
 
206
+ const effectiveness = validateSkillEffectiveness({ rootDir: root });
207
+ const effectivenessFindings = effectiveness.errors.map((message) => ({
208
+ kind: 'effectiveness',
209
+ severity: 'blocking',
210
+ message,
211
+ }));
212
+ blockingFindings.push(...effectivenessFindings);
213
+
205
214
  const payload = {
206
215
  version: 1,
207
216
  generatedAt: new Date().toISOString(),
@@ -209,6 +218,7 @@ export function buildSkillInventory({ rootDir } = {}) {
209
218
  skillCount: skills.length,
210
219
  skills,
211
220
  blockingFindings,
221
+ effectivenessFindings,
212
222
  digest: null,
213
223
  };
214
224
  const canonical = { ...payload, digest: undefined };
@@ -238,7 +248,9 @@ export function validateSkillInventory({ rootDir, inventory: supplied, checkFres
238
248
  }
239
249
 
240
250
  for (const finding of inventory.blockingFindings ?? []) {
241
- if (finding.severity === 'blocking') warnings.push(`blocking finding recorded: ${finding.kind} ${finding.skillId ?? finding.artifactType ?? ''}`.trim());
251
+ if (finding.severity === 'blocking') {
252
+ errors.push(`blocking finding: ${finding.kind} ${finding.skillId ?? finding.message ?? finding.artifactType ?? ''}`.trim());
253
+ }
242
254
  }
243
255
 
244
256
  if (checkFreshness) {
@@ -8,16 +8,19 @@
8
8
  import fs from 'node:fs';
9
9
  import path from 'node:path';
10
10
 
11
+ import { validateSkillEffectiveness } from '../validators/skill-effectiveness.mjs';
12
+ import { auditSkillComposition } from '../skills/composition-graph.mjs';
11
13
  import { defaultSkillInventoryPath } from './skill-inventory.mjs';
12
14
  import { defaultScenarioCatalogPath } from './scenarios.mjs';
13
15
 
14
- const HIGH_RISK_SKILLS = new Set([
15
- 'docs/prd-workflow',
16
- 'docs/product-intelligence-workflow',
17
- 'docs/evidence-ingest-workflow',
18
- 'quality-gates/verify-security',
19
- 'security/threat-intel',
20
- ]);
16
+ const HIGH_RISK_SKILLS = null;
17
+
18
+ function highRiskSkillIds(root) {
19
+ if (HIGH_RISK_SKILLS) return HIGH_RISK_SKILLS;
20
+ const file = defaultSkillInventoryPath(root);
21
+ const inventory = JSON.parse(fs.readFileSync(file, 'utf8'));
22
+ return new Set((inventory.skills ?? []).filter((s) => s.workflowSkill).map((s) => s.id));
23
+ }
21
24
 
22
25
  function findConstructRoot(startPath = process.cwd()) {
23
26
  let current = path.resolve(startPath);
@@ -50,6 +53,8 @@ export function buildPositiveScenario(skill) {
50
53
  verificationSteps: [
51
54
  'skill file exists under skills/',
52
55
  'inventory lists owners and workflowSkill=true',
56
+ 'effectiveness rubric passes (lint:scopes)',
57
+ 'get_skill and specialist refs resolve (composition graph)',
53
58
  ],
54
59
  owners: skill.owners ?? [],
55
60
  };
@@ -80,6 +85,14 @@ export function validateSkillScenarioFixture(fixture, { rootDir } = {}) {
80
85
  if (fixture.mode === 'positive' && !Array.isArray(fixture.verificationSteps)) {
81
86
  errors.push('positive fixture requires verificationSteps');
82
87
  }
88
+ if (fixture.mode === 'positive' && fixture.verificationSteps?.includes('effectiveness rubric passes (lint:scopes)')) {
89
+ const eff = validateSkillEffectiveness({ rootDir: root });
90
+ if (!eff.valid) errors.push(`effectiveness: ${eff.errors.find((e) => e.startsWith(fixture.skillId)) ?? eff.errors[0]}`);
91
+ }
92
+ if (fixture.mode === 'positive' && fixture.verificationSteps?.includes('get_skill and specialist refs resolve (composition graph)')) {
93
+ const comp = auditSkillComposition({ rootDir: root });
94
+ if (!comp.pass) errors.push(`composition: ${comp.blocking[0] ?? 'blocking'}`);
95
+ }
83
96
  if (fixture.mode === 'negative') {
84
97
  if (fixture.expected?.silentSuccess !== false) errors.push('negative fixture must forbid silent success');
85
98
  if (!fixture.expected?.errorType) errors.push('negative fixture requires expected.errorType');
@@ -121,7 +134,7 @@ export function writeSkillScenarioFixtures({ rootDir } = {}) {
121
134
  model: { provider: 'hermetic', requestedId: 'fixture/skill', resolvedId: 'fixture/skill', tier: 'hermetic' },
122
135
  });
123
136
  lines.push(`- \`${skill.id}\` — positive: \`${relPositive}\``);
124
- if (HIGH_RISK_SKILLS.has(skill.id)) {
137
+ if (highRiskSkillIds(root).has(skill.id)) {
125
138
  const negative = buildNegativeScenario(skill);
126
139
  const negativePath = path.join(dir, 'negative.json');
127
140
  fs.writeFileSync(negativePath, `${JSON.stringify(negative, null, 2)}\n`);
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import fs from 'node:fs';
9
+ import { loadRegistry } from '../registry/loader.mjs';
9
10
  import path from 'node:path';
10
11
 
11
12
  import { validatePromptContent } from '../specialists/prompt-schema.mjs';
@@ -13,7 +14,7 @@ import { validatePromptContent } from '../specialists/prompt-schema.mjs';
13
14
  function findConstructRoot(startPath = process.cwd()) {
14
15
  let current = path.resolve(startPath);
15
16
  while (true) {
16
- if (fs.existsSync(path.join(current, 'specialists', 'registry.json'))) return current;
17
+ if (fs.existsSync(path.join(current, 'specialists', 'org'))) return current;
17
18
  const parent = path.dirname(current);
18
19
  if (parent === current) return path.resolve(startPath);
19
20
  current = parent;
@@ -73,8 +74,8 @@ export function checkSpecialistContract(agent, { rootDir } = {}) {
73
74
 
74
75
  export function auditSpecialistContracts({ rootDir } = {}) {
75
76
  const root = findConstructRoot(rootDir);
76
- const registry = JSON.parse(fs.readFileSync(path.join(root, 'specialists', 'registry.json'), 'utf8'));
77
- const results = (registry.specialists ?? []).map((agent) => checkSpecialistContract(agent, { rootDir: root }));
77
+ const registry = loadRegistry({ rootDir: root });
78
+ const results = Object.values(registry.specialists ?? {}).map((agent) => checkSpecialistContract(agent, { rootDir: root }));
78
79
  const failures = results.filter((r) => !r.pass);
79
80
  return {
80
81
  pass: failures.length === 0,
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import fs from 'node:fs';
10
+ import { loadRegistry } from '../registry/loader.mjs';
10
11
  import path from 'node:path';
11
12
 
12
13
  import { roleCardsDir } from './role-cards.mjs';
@@ -16,7 +17,7 @@ import { defaultScenarioCatalogPath } from './scenarios.mjs';
16
17
  function findConstructRoot(startPath = process.cwd()) {
17
18
  let current = path.resolve(startPath);
18
19
  while (true) {
19
- if (fs.existsSync(path.join(current, 'specialists', 'registry.json'))) return current;
20
+ if (fs.existsSync(path.join(current, 'specialists', 'org'))) return current;
20
21
  const parent = path.dirname(current);
21
22
  if (parent === current) return path.resolve(startPath);
22
23
  current = parent;
@@ -75,8 +76,8 @@ export function validateSpecialistScenarioFixture(fixture, { rootDir } = {}) {
75
76
  if (!fixture?.capabilityId) errors.push('capabilityId required');
76
77
  if (!['normal', 'adversarial'].includes(fixture.mode)) errors.push('mode must be normal or adversarial');
77
78
 
78
- const registry = JSON.parse(fs.readFileSync(path.join(root, 'specialists', 'registry.json'), 'utf8'));
79
- const agent = (registry.specialists ?? []).find((entry) => `cx-${entry.name}` === fixture.specialistId);
79
+ const registry = loadRegistry({ rootDir: root });
80
+ const agent = Object.values(registry.specialists ?? {}).find((entry) => `cx-${entry.name}` === fixture.specialistId);
80
81
  if (!agent) errors.push(`unknown specialist: ${fixture.specialistId}`);
81
82
 
82
83
  const cardPath = path.join(roleCardsDir(root), `${fixture.specialistId}.role-card.json`);
@@ -110,7 +111,7 @@ export function validateSpecialistScenarioFixture(fixture, { rootDir } = {}) {
110
111
 
111
112
  export function writeSpecialistScenarioFixtures({ rootDir } = {}) {
112
113
  const root = findConstructRoot(rootDir);
113
- const registry = JSON.parse(fs.readFileSync(path.join(root, 'specialists', 'registry.json'), 'utf8'));
114
+ const registry = loadRegistry({ rootDir: root });
114
115
  const readme = path.join(root, 'tests', 'certification', 'scenarios', 'specialists', 'README.md');
115
116
  fs.mkdirSync(path.dirname(readme), { recursive: true });
116
117
  const lines = [
@@ -127,7 +128,7 @@ export function writeSpecialistScenarioFixtures({ rootDir } = {}) {
127
128
  ];
128
129
  const catalogEntries = [];
129
130
 
130
- for (const agent of registry.specialists ?? []) {
131
+ for (const agent of Object.values(registry.specialists ?? {})) {
131
132
  const specialistId = `cx-${agent.name}`;
132
133
  const cardPath = path.join(roleCardsDir(root), `${specialistId}.role-card.json`);
133
134
  if (!fs.existsSync(cardPath)) continue;
@@ -171,5 +172,5 @@ export function writeSpecialistScenarioFixtures({ rootDir } = {}) {
171
172
  && !String(s.id).startsWith('specialist.adversarial.'));
172
173
  catalog.scenarios = [...keep, ...catalogEntries];
173
174
  fs.writeFileSync(catalogPath, `${JSON.stringify(catalog, null, 2)}\n`);
174
- return { specialistCount: (registry.specialists ?? []).length, catalogEntries: catalogEntries.length, readme };
175
+ return { specialistCount: Object.values(registry.specialists ?? {}).length, catalogEntries: catalogEntries.length, readme };
175
176
  }
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import fs from 'node:fs';
9
+ import { loadRegistry } from '../registry/loader.mjs';
9
10
  import path from 'node:path';
10
11
 
11
12
  import { loadCapabilityLedger } from '../capability-ledger.mjs';
@@ -28,13 +29,13 @@ function findConstructRoot(startPath = process.cwd()) {
28
29
  }
29
30
 
30
31
  function readRegistry(rootDir) {
31
- const file = path.join(rootDir, 'specialists', 'registry.json');
32
- if (!fs.existsSync(file)) return { specialists: [] };
33
- return JSON.parse(fs.readFileSync(file, 'utf8'));
32
+ const dir = path.join(rootDir, 'specialists', 'org');
33
+ if (!fs.existsSync(dir)) return { specialists: [] };
34
+ return loadRegistry({ rootDir });
34
35
  }
35
36
 
36
37
  function listSpecialists(registry) {
37
- return registry.specialists ?? [];
38
+ return Object.values(registry.specialists ?? {});
38
39
  }
39
40
 
40
41
  function normalizeStatus({ verdict = null, stale = false, neverRun = true }) {
@@ -4,7 +4,6 @@
4
4
  * Consumed by:
5
5
  * - bin/construct (usage text + emoji output)
6
6
  * - lib/completions.mjs (bash + zsh completion generation)
7
- * - lib/server/index.mjs (/api/status → dashboard)
8
7
  *
9
8
  * Command Visibility:
10
9
  * - core: true → shown in default --help (90% of users)
@@ -13,6 +12,8 @@
13
12
  */
14
13
 
15
14
  import { resolveColors } from './term-format.mjs';
15
+ import { resolveUiColors } from './ui/theme.mjs';
16
+ import { formatPathLink, terminalLinksEnabled } from './ui/links.mjs';
16
17
  import { surfaceForCommand } from './registry/surface-map.mjs';
17
18
 
18
19
  export const CLI_COMMANDS = [
@@ -23,26 +24,14 @@ export const CLI_COMMANDS = [
23
24
  category: 'Core',
24
25
  core: true,
25
26
  description: 'Start services for development',
26
- usage: 'construct dev [--select] [--only=postgres,dashboard,...]',
27
+ usage: 'construct dev [--select] [--only=postgres,...]',
27
28
  examples: [
28
29
  { cmd: 'construct dev', desc: 'Start the default service set' },
29
- { cmd: 'construct dev --only=dashboard', desc: 'Start just the dashboard' },
30
+ { cmd: 'construct dev --only=postgres', desc: 'Start just Postgres' },
30
31
  ],
31
32
  options: [
32
33
  { flag: '--select', desc: 'Pick which services to start from an interactive checklist' },
33
- { flag: '--only=<a,b,c>', desc: 'Start only the named services (postgres, dashboard, telemetry, memory, opencode)' },
34
- ],
35
- },
36
- {
37
- name: 'dashboard',
38
- emoji: '📊',
39
- category: 'Core',
40
- core: true,
41
- description: 'Start the local dashboard/orchestration daemon (or --token to mint a dashboard token)',
42
- usage: 'construct dashboard [--token]',
43
- examples: [
44
- { cmd: 'construct dashboard', desc: 'Launch the local dashboard daemon' },
45
- { cmd: 'construct dashboard --token', desc: 'Mint a dashboard access token' },
34
+ { flag: '--only=<a,b,c>', desc: 'Start only the named services (postgres, telemetry, memory, opencode)' },
46
35
  ],
47
36
  },
48
37
  {
@@ -230,24 +219,24 @@ export const CLI_COMMANDS = [
230
219
  ],
231
220
  },
232
221
  {
233
- name: 'profile',
222
+ name: 'scope',
234
223
  emoji: '🧭',
235
224
  category: 'Core',
236
225
  core: true,
237
- description: 'Manage the active org profile and its lifecycle (draft, promote, archive, health)',
238
- usage: 'construct profile show|list|set|create|drafts|archive|health',
226
+ description: 'Manage the active org scope and its lifecycle (draft, promote, archive, health)',
227
+ usage: 'construct scope show|list|set|create|drafts|archive|health',
239
228
  examples: [
240
- { cmd: 'construct profile list', desc: 'List curated profiles' },
241
- { cmd: 'construct profile set <id>', desc: 'Switch the active profile' },
229
+ { cmd: 'construct scope list', desc: 'List curated scopes' },
230
+ { cmd: 'construct scope set <id>', desc: 'Switch the active scope' },
242
231
  ],
243
232
  subcommands: [
244
- { name: 'show', desc: 'Show the active profile' },
245
- { name: 'list', desc: 'List curated profiles' },
246
- { name: 'set <id>', desc: 'Switch the active profile (writes construct.config.json)' },
247
- { name: 'create <id> [--display=…] [--role=…] [--department=…] [--yes|--dry-run]', desc: 'Scaffold a draft profile; previews and confirms by default, prompts interactively when no flags' },
248
- { name: 'drafts', desc: 'List in-progress draft profiles' },
249
- { name: 'archive <id> --reason="..."', desc: 'Move a curated profile into archive/profiles/<id>/' },
250
- { name: 'health <id> [--days=N]', desc: 'Per-profile observation + outcome rollup' },
233
+ { name: 'show', desc: 'Show the active scope' },
234
+ { name: 'list', desc: 'List curated scopes' },
235
+ { name: 'set <id>', desc: 'Switch the active scope (writes construct.config.json)' },
236
+ { name: 'create <id> [--display=…] [--role=…] [--department=…] [--yes|--dry-run]', desc: 'Scaffold a draft scope; previews and confirms by default, prompts interactively when no flags' },
237
+ { name: 'drafts', desc: 'List in-progress draft scopes' },
238
+ { name: 'archive <id> --reason="..."', desc: 'Move a curated scope into archive/scopes/<id>/' },
239
+ { name: 'health <id> [--days=N]', desc: 'Per-scope observation + outcome rollup' },
251
240
  ],
252
241
  },
253
242
  {
@@ -354,7 +343,7 @@ export const CLI_COMMANDS = [
354
343
  { flag: '--output=<path>', desc: 'Output path (default: .cx/publish/<name>.<format>)' },
355
344
  { flag: '--type=<doc-type>', desc: 'Manifest doc type for release gate (inferred when omitted)' },
356
345
  { flag: '--demo=<name>', desc: 'Terminal VHS tape to record (repeatable)' },
357
- { flag: '--dashboard-demo=<name>', desc: 'Playwright demo spec basename (repeatable)' },
346
+ { flag: '--recording=<name>', desc: 'Playwright recording manifest (repeatable)' },
358
347
  { flag: '--figures', desc: 'Render d2/mermaid via diagram filter (default on)' },
359
348
  { flag: '--no-figures', desc: 'Skip diagram filter' },
360
349
  { flag: '--no-gate', desc: 'Skip artifact release gate (escape hatch only)' },
@@ -377,7 +366,6 @@ export const CLI_COMMANDS = [
377
366
  { flag: '--figures', desc: 'Include figure tooling (default on)' },
378
367
  { flag: '--no-figures', desc: 'Skip figure binaries' },
379
368
  { flag: '--demo=<name>', desc: 'Include terminal demo recorder check' },
380
- { flag: '--dashboard-demo=<name>', desc: 'Include dashboard Playwright check' },
381
369
  ],
382
370
  },
383
371
  {
@@ -429,14 +417,12 @@ export const CLI_COMMANDS = [
429
417
  category: 'Models & Integrations',
430
418
  core: false,
431
419
  description: 'Show or update model tier assignments',
432
- usage: 'construct models <list|set|free|reset|usage|cost|resolve>',
420
+ usage: 'construct models <list|set|free|reset|resolve>',
433
421
  subcommands: [
434
422
  { name: 'list', desc: 'Show current tier assignments' },
435
423
  { name: 'set --tier=<reasoning|standard|fast> --model=<model>', desc: 'Set a model for a tier' },
436
424
  { name: 'free', desc: 'List available free models' },
437
425
  { name: 'reset', desc: 'Reset all tier assignments' },
438
- { name: 'usage', desc: 'Show token usage per tier' },
439
- { name: 'cost', desc: 'Show cost breakdown' },
440
426
  { name: 'resolve --json', desc: 'Resolve the model for an embedded workflow given host context' },
441
427
  ],
442
428
  },
@@ -482,14 +468,6 @@ export const CLI_COMMANDS = [
482
468
  description: 'Run Construct as an Agent Client Protocol (ACP) server over stdio for Zed/JetBrains/VS Code ACP clients',
483
469
  usage: 'construct acp',
484
470
  },
485
- {
486
- name: 'chat',
487
- emoji: '💬',
488
- category: 'Models & Integrations',
489
- core: false,
490
- description: "Interactive terminal chat on Construct's owned agent loop, with a multi-pane transparency surface (live token/cost/context usage, reasoning, tool timeline, and the planned specialist route); switch models, change settings, and see a token/cost breakdown in-session. Use --accessible or --plain for the linear, screen-reader-friendly renderer",
491
- usage: 'construct chat [--model <id>] [--free] [--web] [--list] [--resume[=file]] [--ascii] [--plain] [--accessible] [--no-thinking] [--no-path] [--no-specialists] [--no-tools] [--no-observability] [--quiet]',
492
- },
493
471
  {
494
472
  name: 'mcp',
495
473
  emoji: '🔌',
@@ -596,7 +574,7 @@ export const CLI_COMMANDS = [
596
574
  emoji: '🔁',
597
575
  category: 'Observability',
598
576
  core: false,
599
- surface: 'dashboard',
577
+ surface: 'thin-cli',
600
578
  description: 'Governed improvement loop — review, approve, and record apply/rollback for proposals',
601
579
  usage: 'construct improvement submit|review|pending|show|approve|apply|rollback|list',
602
580
  },
@@ -621,8 +599,14 @@ export const CLI_COMMANDS = [
621
599
  emoji: '👥',
622
600
  category: 'Work',
623
601
  core: false,
624
- description: 'Team review and template listing',
625
- usage: 'construct team <list|review>',
602
+ description: 'Team review and template listing (`team:add` / `team:remove` are internal registry editors)',
603
+ usage: 'construct team <list|show|review|templates>',
604
+ subcommands: [
605
+ { name: 'list', desc: 'List macro groups and squads (--kind group|squad)' },
606
+ { name: 'show', desc: 'Show one group or squad by id' },
607
+ { name: 'review', desc: 'Team review workflow' },
608
+ { name: 'templates', desc: 'List team doc templates' },
609
+ ],
626
610
  },
627
611
  {
628
612
  name: 'audit',
@@ -688,7 +672,7 @@ export const CLI_COMMANDS = [
688
672
  emoji: '🪞',
689
673
  category: 'Work',
690
674
  core: false,
691
- description: 'Capture improvement feedback from chat session and update Construct core',
675
+ description: 'Capture improvement feedback and update Construct core',
692
676
  usage: 'construct reflect',
693
677
  },
694
678
  {
@@ -736,18 +720,17 @@ export const CLI_COMMANDS = [
736
720
  emoji: '🎬',
737
721
  category: 'Work',
738
722
  core: false,
739
- description: 'Run guided demos via construct chat (default) or record VHS/asciinema tapes',
740
- usage: 'construct demo <list|init|record|name> [--surface=chat|web|tape|dashboard] [--format=gif|mp4|webm] [--out=<path>] [--source-only]',
723
+ description: 'Run guided tours or record VHS/asciinema tapes',
724
+ usage: 'construct demo <list|init|record|tour|name> [--surface=tape|playwright] [--format=gif|mp4|webm] [--out=<path>] [--source-only]',
741
725
  strictFlags: true,
742
726
  options: [
743
- { flag: '--surface=<s>', desc: 'chat (default) | web | tape | dashboard' },
744
- { flag: '--model=<id>', desc: 'Pin model for chat demo' },
745
- { flag: '--web', desc: 'Open web chat (/chat/) instead of terminal Ink' },
746
- { flag: '--plain', desc: 'Linear chat renderer (accessibility / non-TTY)' },
747
- { flag: '--free', desc: 'OpenRouter free-router mode for chat demo' },
727
+ { flag: '--surface=<s>', desc: 'tape (default) | playwright' },
728
+ { flag: '--accessible', desc: 'Screen-reader-friendly linear tour renderer' },
729
+ { flag: '--skip-input', desc: 'Tour: auto-advance without waiting for Enter (headless/CI)' },
748
730
  { flag: '--format=<f>', desc: 'gif (default) | mp4 | webm (tape surface only)' },
749
731
  { flag: '--out=<path>', desc: 'Output path (tape recording)' },
750
732
  { flag: '--from=<t>', desc: 'Template for init: quickstart | diagram' },
733
+ { flag: '--from-project', desc: 'init: scaffold a project demo plug-in under .cx/demos/' },
751
734
  { flag: '--source-only', desc: 'Tape: write .tape only; skip recording' },
752
735
  ],
753
736
  },
@@ -890,7 +873,13 @@ export const CLI_COMMANDS = [
890
873
  category: 'Advanced',
891
874
  core: false,
892
875
  description: 'Skill relevance detection',
893
- usage: 'construct skills scope|apply',
876
+ usage: 'construct skills <scope|apply|suggest|routing>',
877
+ subcommands: [
878
+ { name: 'scope', desc: 'Show skill scope for the active profile' },
879
+ { name: 'apply', desc: 'Apply skill profile to host config' },
880
+ { name: 'suggest', desc: 'Rank skills for an intent string' },
881
+ { name: 'routing', desc: 'Dump machine-readable routing table' },
882
+ ],
894
883
  },
895
884
  {
896
885
  name: 'hooks:health',
@@ -1135,7 +1124,6 @@ export const CLI_COMMANDS = [
1135
1124
  { name: 'hook', category: 'Internal', core: false, internal: true, description: 'Hook dispatch — invoked by the harness, not by users', usage: 'construct hook <event>' },
1136
1125
  { name: 'seed-traces', category: 'Internal', core: false, internal: true, description: 'Dev fixture: seed traces for testing', usage: 'construct seed-traces' },
1137
1126
  { name: 'migrate', category: 'Internal', core: false, internal: true, description: 'Internal: run pending storage migrations', usage: 'construct migrate' },
1138
- { name: 'dashboard:sync', category: 'Internal', core: false, internal: true, description: 'Internal: refresh dashboard state from the registry', usage: 'construct dashboard:sync' },
1139
1127
  { name: 'init:update', category: 'Internal', core: false, internal: true, description: 'Internal: re-run init scaffolding for an existing project', usage: 'construct init:update' },
1140
1128
  { name: 'lint:agents', category: 'Internal', core: false, internal: true, description: 'Internal lint: agent definitions', usage: 'construct lint:agents' },
1141
1129
  { name: 'lint:comments', category: 'Internal', core: false, internal: true, description: 'Internal lint: source comments', usage: 'construct lint:comments' },
@@ -1145,8 +1133,17 @@ export const CLI_COMMANDS = [
1145
1133
  { name: 'lint:prompts', category: 'Internal', core: false, internal: true, description: 'Internal lint: specialist prompt frontmatter + sections', usage: 'construct lint:prompts' },
1146
1134
  { name: 'specialist', category: 'Internal', core: false, internal: true, description: 'Maintainer tool: scaffold, edit, and lint specialist prompts', usage: 'construct specialist <create|edit|lint>' },
1147
1135
  { name: 'registry:status', category: 'Internal', core: false, internal: true, surface: 'internal', description: 'Dev: capability registry inspector', usage: 'construct registry:status [--json]' },
1148
- { name: 'registry:validate', category: 'Internal', core: false, internal: true, surface: 'internal', description: 'Validate registry/capabilities.json against repo reality', usage: 'construct registry:validate [--json]' },
1136
+ { name: 'registry:validate', category: 'Internal', core: false, internal: true, surface: 'internal', strictFlags: true, description: 'Validate registry/capabilities.json against repo reality; --unified validates specialists/org (+ .cx overlay) invariants', usage: 'construct registry:validate [--json] [--unified]', options: [
1137
+ { flag: '--json', desc: 'Emit the validation report as JSON' },
1138
+ { flag: '--unified', desc: 'Validate the unified registry (specialists/org + optional .cx overlay) against its 13 invariants instead of the capability registry' },
1139
+ ] },
1149
1140
  { name: 'registry:generate-docs', category: 'Internal', core: false, internal: true, surface: 'internal', description: 'Generate docs/guides/reference/capabilities.md from registry', usage: 'construct registry:generate-docs' },
1141
+ { name: 'registry:diff', category: 'Internal', core: false, internal: true, surface: 'internal', description: 'Show unified registry changes vs. last commit', usage: 'construct registry:diff [--json]' },
1142
+ { name: 'registry:prune', category: 'Internal', core: false, internal: true, surface: 'internal', description: 'List orphaned specialist prompts and skills', usage: 'construct registry:prune [--json]' },
1143
+ { name: 'team:add', category: 'Internal', core: false, internal: true, surface: 'internal', description: 'Add a new team to the unified registry', usage: 'construct team:add <team-id>' },
1144
+ { name: 'team:remove', category: 'Internal', core: false, internal: true, surface: 'internal', description: 'Remove a team from the unified registry', usage: 'construct team:remove <team-id> [--force]' },
1145
+ { name: 'specialist:add', category: 'Internal', core: false, internal: true, surface: 'internal', description: 'Add a new specialist to a team', usage: 'construct specialist:add <specialist-id> --team <team-id>' },
1146
+ { name: 'specialist:remove', category: 'Internal', core: false, internal: true, surface: 'internal', description: 'Remove a specialist from the unified registry', usage: 'construct specialist:remove <specialist-id> [--force]' },
1150
1147
  { name: 'oracle', category: 'Internal', core: false, internal: true, surface: 'internal', description: 'Oracle meta-controller — fleet health review and bounded-auto maintenance', usage: 'construct oracle start|status|review|pending|approve|gaps|reconcile' },
1151
1148
  { name: 'matrix', category: 'Internal', core: false, internal: true, surface: 'internal', description: 'Living dependency matrix — build/inspect the typed file↔capability↔workflow↔test graph', usage: 'construct matrix build|stat|query <node-id> [--json]' },
1152
1149
  { name: 'impact', category: 'Diagnostics', core: false, internal: false, surface: 'thin-cli', description: 'Change-impact analysis — map changed files to affected tests, capabilities, and workflows', usage: 'construct impact [files…] [--stdin] [--run] [--json]' },
@@ -1222,7 +1219,8 @@ export function getCommandSpec(name) {
1222
1219
  // every command. Internal commands fall back to a minimal block.
1223
1220
 
1224
1221
  export function formatCommandHelp(name, { colors = false } = {}) {
1225
- const c = resolveColors({ enabled: colors });
1222
+ const c = resolveUiColors({ enabled: colors });
1223
+ const linksOn = terminalLinksEnabled(process.env);
1226
1224
  const spec = getCommandSpec(name);
1227
1225
  if (!spec) {
1228
1226
  return `Unknown command: ${name}\n\nRun 'construct --help' for available commands.\n`;
@@ -1271,7 +1269,8 @@ export function formatCommandHelp(name, { colors = false } = {}) {
1271
1269
  lines.push(`${c.dim}Next:${c.reset} ${next}`);
1272
1270
  lines.push('');
1273
1271
  }
1274
- lines.push(`${c.dim}See also:${c.reset} run \`construct --help\` for all commands; full reference in docs/guides/reference/cli/.`);
1272
+ const cliDocs = formatPathLink('docs/guides/reference/cli/', c, { enabled: linksOn });
1273
+ lines.push(`${c.dim}See also:${c.reset} run \`construct --help\` for all commands; full reference in ${cliDocs}.`);
1275
1274
  lines.push('');
1276
1275
  return lines.join('\n');
1277
1276
  }
@@ -93,10 +93,6 @@ function requiresHeader(rel) {
93
93
  if (['.yaml', '.yml', '.json', '.jsonl', '.toml', '.txt'].includes(ext)) return { required: false, type: null };
94
94
  const jsMatch = JS_HEADER_GLOBS.some(r => r.test(rel));
95
95
  const mdMatch = MD_HEADER_GLOBS.some(r => r.test(rel));
96
- // Exclude static assets under lib/server/static from requiring headers
97
- if (rel.startsWith('lib/server/static/')) {
98
- return { required: false, type: null };
99
- }
100
96
  // Exclude test fixture data files — these are project artifacts, not source files.
101
97
  if (rel.startsWith('tests/fixtures/projects/')
102
98
  || rel.startsWith('tests/fixtures/artifacts/')
@@ -324,7 +320,7 @@ function checkArtifactBanned(content, rel) {
324
320
 
325
321
  // Internal specialist role ids (cx-business-strategist, cx-product-manager, …) are
326
322
  // implementation detail; in a consuming project's deliverable they read as a tool-identity
327
- // leak. The set is anchored to the real role ids from specialists/registry.json — an
323
+ // leak. The set is anchored to the real role ids from specialists/org — an
328
324
  // open-ended /cx-[a-z-]+/ also matches unrelated npm packages (cx-ray, cx-pro, cx-widget)
329
325
  // and would fail the release gate on legitimate user content. Drift from the registry is
330
326
  // caught by tests/tool-invisibility.test.mjs, not at runtime: this module sits on the hook
@@ -31,6 +31,7 @@ function subcommandDesc(subcommand) {
31
31
 
32
32
  function buildBash() {
33
33
  const names = CLI_COMMANDS.map(c => c.name).join(' ');
34
+ const globalFlags = '--help -h --all -a --version -V';
34
35
 
35
36
  const subcommandCases = CLI_COMMANDS
36
37
  .filter(c => c.subcommands?.length)
@@ -68,15 +69,28 @@ _construct_completions() {
68
69
  }
69
70
 
70
71
  local commands="${names}"
72
+ local global_flags="${globalFlags}"
71
73
 
72
- # First argument: complete command names
74
+ # First argument: subcommand names or global flags
73
75
  if [[ $cword -eq 1 ]]; then
74
- COMPREPLY=($(compgen -W "$commands" -- "$cur"))
76
+ if [[ "$cur" == -* ]]; then
77
+ COMPREPLY=($(compgen -W "$global_flags" -- "$cur"))
78
+ else
79
+ COMPREPLY=($(compgen -W "$commands" -- "$cur"))
80
+ fi
75
81
  return 0
76
82
  fi
77
83
 
78
84
  local cmd="\${words[1]}"
79
85
 
86
+ # Global flag continuation
87
+ if [[ "$cmd" == -* ]]; then
88
+ if [[ "$cur" == -* ]]; then
89
+ COMPREPLY=($(compgen -W "$global_flags" -- "$cur"))
90
+ return 0
91
+ fi
92
+ fi
93
+
80
94
  # Subcommands
81
95
  if [[ $cword -eq 2 ]]; then
82
96
  case "$cmd" in
@@ -100,6 +114,9 @@ complete -F _construct_completions construct
100
114
  }
101
115
 
102
116
  function buildZsh() {
117
+ const globalFlagDescriptions = ['--help', '-h', '--all', '-a', '--version', '-V']
118
+ .map((f) => ` '${f}:global flag' \\`)
119
+ .join('\n');
103
120
  const commandDescriptions = CLI_COMMANDS
104
121
  .map(c => ` '${c.name}:${c.emoji || ''} ${(c.description || '').replace(/'/g, "\\'")}' \\`)
105
122
  .join('\n');
@@ -140,6 +157,7 @@ ${subcommandFunctions}
140
157
  _construct_commands() {
141
158
  local commands
142
159
  commands=(
160
+ ${globalFlagDescriptions}
143
161
  ${commandDescriptions}
144
162
  )
145
163
  _describe -t commands 'construct command' commands