@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,115 @@
1
+ /**
2
+ * lib/ui/links.mjs — shared OSC-8 hyperlink layer for every construct surface.
3
+ *
4
+ * Canonical link authority for CLI output and generated adapter diagnostics.
5
+ * Renders repo-relative paths and URLs as clickable terminal links (OSC-8) when
6
+ * the stream supports them, and always keeps the raw path/URL as the visible
7
+ * label so terminals that ignore OSC-8 (notably macOS Terminal.app) can still
8
+ * Cmd-click the visible text via their own path/URL detection. Falls back to
9
+ * colored text when links are disabled.
10
+ */
11
+
12
+ import fs from 'node:fs';
13
+ import path from 'node:path';
14
+ import { pathToFileURL } from 'node:url';
15
+
16
+ const OSC8_OPEN = '\x1b]8;;';
17
+ const OSC8_MID = '\x07';
18
+ const OSC8_CLOSE = '\x1b]8;;\x07';
19
+
20
+ export const REPO_PATH_PATTERN =
21
+ /(`?)((?:\.cx\/|docs\/|inbox\/|skills\/|rules\/|lib\/|templates\/|specialists\/|tests\/|platforms\/|personas\/|schemas\/)?[\w][\w./-]*\.(?:md|mdx|json|mjs|ts|tsx|yml|yaml)|construct\.config\.json|package\.json|[A-Z][A-Z0-9_]*\.md)(`?)/g;
22
+
23
+ const REPO_PATH =
24
+ /(^|[\s(,])(`?)((?:\.cx\/|docs\/|inbox\/|skills\/|rules\/|lib\/|templates\/|specialists\/|tests\/|platforms\/|personas\/|schemas\/)?[\w][\w./-]*\.(?:md|mdx|json|mjs|ts|tsx|yml|yaml)|construct\.config\.json|package\.json|[A-Z][A-Z0-9_]*\.md)(`?)(?=$|[\s),.:;])/g;
25
+
26
+ const URL_PATTERN = /\bhttps?:\/\/[^\s<>()\][]+[^\s<>()\][.,;:!?'"]/g;
27
+
28
+ export function terminalLinksEnabled(env = process.env, { plain = false, stream = process.stdout } = {}) {
29
+ if (plain || env.NO_COLOR === '1' || env.CX_PLAIN_COPY === '1') return false;
30
+ if (env.CX_LINKS === '0') return false;
31
+ if (stream?.isTTY) return true;
32
+ if (env.TERM_PROGRAM === 'vscode' || env.TERM_PROGRAM === 'cursor' || env.WT_SESSION) return true;
33
+ return false;
34
+ }
35
+
36
+ export function fileUriForPath(filePath, { cwd = process.cwd() } = {}) {
37
+ const abs = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);
38
+ return pathToFileURL(abs).href;
39
+ }
40
+
41
+ // OSC-8 wraps a visible label with an invisible href. Terminals that honor the
42
+ // sequence make the label clickable; terminals that ignore it (Terminal.app)
43
+ // simply print the label — so callers must pass a label that is itself a
44
+ // clickable string (a raw path or URL), never a friendly title that hides it.
45
+
46
+ export function formatTerminalLink(label, href, colors, { enabled = true } = {}) {
47
+ const styled = `${colors?.link || ''}${label}${colors?.reset || ''}`;
48
+ if (!enabled || !href) return styled;
49
+ return `${OSC8_OPEN}${href}${OSC8_MID}${styled}${OSC8_CLOSE}`;
50
+ }
51
+
52
+ export function formatPathLink(relPath, colors, { cwd = process.cwd(), enabled = true } = {}) {
53
+ const display = relPath.replace(/^`/, '').replace(/`$/, '');
54
+ const abs = path.resolve(cwd, display);
55
+ const href = fileUriForPath(abs, { cwd });
56
+ return formatTerminalLink(display, href, colors, { enabled });
57
+ }
58
+
59
+ // A bare URL is its own visible label, so OSC-8 terminals get one-click and
60
+ // Terminal.app's URL detection still works on the printed text.
61
+
62
+ export function formatUrlLink(url, colors, { enabled = true } = {}) {
63
+ return formatTerminalLink(url, url, colors, { enabled });
64
+ }
65
+
66
+ // A titled link ([text](href)) must keep the destination visible for terminals
67
+ // that drop OSC-8: render the title as the clickable label, then append the raw
68
+ // destination so it is both readable and Cmd-clickable everywhere.
69
+
70
+ export function formatTitledLink(label, href, colors, { enabled = true, display = href } = {}) {
71
+ if (!label || label === display) {
72
+ return formatTerminalLink(display, href, colors, { enabled });
73
+ }
74
+ const linked = formatTerminalLink(label, href, colors, { enabled });
75
+ const tail = `${colors?.dim || ''} (${display})${colors?.reset || ''}`;
76
+ return `${linked}${tail}`;
77
+ }
78
+
79
+ export function linkifyRepoPaths(text, colors, { cwd = process.cwd(), enabled = true } = {}) {
80
+ if (!enabled || text == null) return String(text ?? '');
81
+ const source = String(text);
82
+ return source.replace(REPO_PATH_PATTERN, (full, openTick, relPath, closeTick, offset) => {
83
+ if (offset > 0) {
84
+ const prev = source[offset - 1];
85
+ if (!/[\s(,]/.test(prev)) return full;
86
+ }
87
+ return formatPathLink(`${openTick || ''}${relPath}${closeTick || ''}`, colors, { cwd, enabled });
88
+ });
89
+ }
90
+
91
+ export function applyPathLinks(text, colors, { cwd = process.cwd(), enabled = true } = {}) {
92
+ if (!enabled || !text) return String(text);
93
+ return String(text).replace(REPO_PATH, (match, prefix, openTick, relPath, closeTick) => {
94
+ const linked = formatPathLink(`${openTick || ''}${relPath}${closeTick || ''}`, colors, { cwd, enabled });
95
+ return `${prefix}${linked}`;
96
+ });
97
+ }
98
+
99
+ export function linkifyUrls(text, colors, { enabled = true } = {}) {
100
+ if (!enabled || text == null) return String(text ?? '');
101
+ return String(text).replace(URL_PATTERN, (url) => formatUrlLink(url, colors, { enabled }));
102
+ }
103
+
104
+ // Linkify both repo paths and URLs in one pass; URLs first so a URL containing
105
+ // a path-like suffix is not split by the path matcher.
106
+
107
+ export function applyLinks(text, colors, { cwd = process.cwd(), enabled = true } = {}) {
108
+ if (!enabled || !text) return String(text ?? '');
109
+ return applyPathLinks(linkifyUrls(text, colors, { enabled }), colors, { cwd, enabled });
110
+ }
111
+
112
+ export function writeLinkedLine(output, line, colors, { cwd, enabled, prefix = '' } = {}) {
113
+ const body = applyLinks(line, colors, { cwd, enabled });
114
+ output.write(prefix ? `${prefix}${body}\n` : `${body}\n`);
115
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * lib/ui/theme.mjs — shared semantic color resolution for every construct surface.
3
+ *
4
+ * Surface-neutral palette for the CLI and generated adapter diagnostics.
5
+ * Terminal codes are basic 16-color SGR, which renders on every
6
+ * terminal including macOS Terminal.app; the truecolor guard degrades any 24-bit
7
+ * value so no raw escape leaks where the terminal cannot honor it.
8
+ */
9
+
10
+ import { shouldUseColor } from '../term-format.mjs';
11
+
12
+ const ESC = '[';
13
+
14
+ function resolveTerminalColorScheme(env = process.env, configTheme = null) {
15
+ const explicit = String(configTheme || env.CONSTRUCT_COLOR_SCHEME || '').toLowerCase();
16
+ if (explicit === 'light' || explicit === 'dark') return explicit;
17
+ const termProgram = String(env.TERM_PROGRAM || '').toLowerCase();
18
+ if (termProgram.includes('apple_terminal')) return 'light';
19
+ return 'dark';
20
+ }
21
+
22
+ function terminalSemantic(scheme) {
23
+ const dark = scheme !== 'light';
24
+ return {
25
+ text: { code: dark ? '97' : '30' },
26
+ muted: { code: dark ? '90' : '90' },
27
+ accent: { code: dark ? '96' : '36' },
28
+ accentAlt: { code: dark ? '95' : '35' },
29
+ ok: { code: dark ? '92' : '32' },
30
+ warn: { code: dark ? '93' : '33' },
31
+ danger: { code: dark ? '91' : '31' },
32
+ badgeFg: { code: dark ? '30' : '97' },
33
+ surface: { code: dark ? '40' : '47' },
34
+ surfaceMuted: { code: dark ? '100' : '47' },
35
+ border: { code: dark ? '90' : '90' },
36
+ brandAccent: { code: dark ? '97' : '30' },
37
+ highlight: { code: dark ? '96' : '36' },
38
+ heading: { code: dark ? '97' : '30' },
39
+ emphasis: { code: dark ? '93' : '33' },
40
+ code: { code: dark ? '92' : '32' },
41
+ link: { code: dark ? '94' : '34' },
42
+ panel: { code: dark ? '90' : '90' },
43
+ };
44
+ }
45
+
46
+ // COLORTERM=truecolor|24bit advertises 24-bit support. Terminal.app sets neither
47
+ // and tops out at 256 colors, so absent the flag we treat the stream as non-truecolor.
48
+
49
+ export function supportsTrueColor(env = process.env) {
50
+ const flag = String(env.COLORTERM || '').toLowerCase();
51
+ return flag === 'truecolor' || flag === '24bit';
52
+ }
53
+
54
+ // SGR codes from design-tokens are already basic 16-color (30-37/90-97). A 24-bit
55
+ // sequence (38;2;r;g;b) only survives when the terminal advertises truecolor;
56
+ // otherwise it is dropped to dim so meaning never rides on an unrenderable color.
57
+
58
+ function degradeCode(code, trueColor) {
59
+ if (!code) return code;
60
+ if (/^38;2;/.test(String(code)) && !trueColor) return '2';
61
+ return code;
62
+ }
63
+
64
+ export function resolveUiColors({
65
+ enabled = true,
66
+ stream = process.stdout,
67
+ env = process.env,
68
+ scheme: explicitScheme = null,
69
+ configTheme = null,
70
+ } = {}) {
71
+ const scheme = explicitScheme || resolveTerminalColorScheme(env, configTheme);
72
+ const useColor = shouldUseColor({ enabled, stream, env });
73
+ const trueColor = supportsTrueColor(env);
74
+ const semantic = terminalSemantic(scheme);
75
+ const codes = Object.fromEntries(Object.entries(semantic).map(([k, v]) => [k, degradeCode(v.code, trueColor)]));
76
+ const reset = useColor ? `${ESC}0m` : '';
77
+ const wrap = (code) => (useColor && code ? `${ESC}${code}m` : '');
78
+
79
+ return {
80
+ scheme,
81
+ reset,
82
+ bold: wrap('1'),
83
+ dim: wrap('2'),
84
+ underline: wrap('4'),
85
+ text: wrap(codes.text),
86
+ muted: wrap(codes.muted),
87
+ accent: wrap(codes.accent),
88
+ accentAlt: wrap(codes.accentAlt),
89
+ ok: wrap(codes.ok),
90
+ warn: wrap(codes.warn),
91
+ danger: wrap(codes.danger),
92
+ badgeFg: wrap(codes.badgeFg),
93
+ surface: wrap(codes.surface),
94
+ surfaceMuted: wrap(codes.surfaceMuted),
95
+ border: wrap(codes.border),
96
+ brandAccent: wrap(codes.brandAccent),
97
+ highlight: wrap(codes.highlight),
98
+ heading: wrap(codes.heading),
99
+ emphasis: wrap(codes.emphasis),
100
+ code: wrap(codes.code),
101
+ link: wrap(codes.link),
102
+ panel: wrap(codes.panel),
103
+ green: wrap(codes.ok),
104
+ yellow: wrap(codes.warn),
105
+ cyan: wrap(codes.highlight),
106
+ red: wrap(codes.danger),
107
+ };
108
+ }
@@ -30,6 +30,7 @@
30
30
  */
31
31
 
32
32
  import fs from 'node:fs';
33
+ import { loadRegistry } from '../registry/loader.mjs';
33
34
  import path from 'node:path';
34
35
  import os from 'node:os';
35
36
  import { spawnSync } from 'node:child_process';
@@ -466,7 +467,7 @@ function unmergeSettings(filePath) {
466
467
 
467
468
  function constructMcpKeys() {
468
469
  try {
469
- const registry = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, 'specialists', 'registry.json'), 'utf8'));
470
+ const registry = loadRegistry({ rootDir: PKG_ROOT });
470
471
  return Object.keys(registry.mcpServers || {});
471
472
  } catch { return []; }
472
473
  }
package/lib/validator.mjs CHANGED
@@ -6,6 +6,7 @@
6
6
  * field-level detail. Backs workflow-state, cli-commands, and MCP tools
7
7
  * before they mutate persistent state.
8
8
  */
9
+ import { loadRegistry } from './registry/loader.mjs';
9
10
  import { fileURLToPath, pathToFileURL } from 'node:url';
10
11
  import { existsSync, readFileSync } from 'node:fs';
11
12
  import { join, dirname } from 'node:path';
@@ -55,19 +56,55 @@ export function validateRegistry(reg, options = {}) {
55
56
  const errors = [];
56
57
  const rootDir = options.rootDir ?? join(__dirname, '..');
57
58
 
58
- if (!reg.version && reg.version !== 0) errors.push('registry: version is missing');
59
- if (!reg.system) errors.push('registry: system is missing');
60
- if (!reg.prefix) errors.push('registry: prefix is missing');
59
+ // Handle unified registry format (v2) - convert specialists from object to array
60
+ // and extract orchestrator from specialists
61
+ let registry = reg;
62
+ if (reg.version === 2 && typeof reg.specialists === 'object' && !Array.isArray(reg.specialists)) {
63
+ const orchestrator = Object.values(reg.specialists || {}).find(s => s.role === 'orchestrator');
64
+ const otherSpecialists = Object.values(reg.specialists || {}).filter(s => s.role !== 'orchestrator');
65
+ registry = {
66
+ version: 1,
67
+ system: 'construct',
68
+ prefix: 'cx',
69
+ orchestrator: orchestrator ? {
70
+ name: orchestrator.name,
71
+ displayName: orchestrator.displayName || orchestrator.description?.split('—')[0].trim(),
72
+ description: orchestrator.description || '',
73
+ promptFile: orchestrator.promptFile,
74
+ modelTier: orchestrator.modelTier || 'standard',
75
+ skills: orchestrator.skills || [],
76
+ } : null,
77
+ specialists: otherSpecialists.map(s => ({
78
+ name: s.name,
79
+ displayName: s.displayName || s.description?.split('—')[0].trim(),
80
+ description: s.description || '',
81
+ promptFile: s.promptFile,
82
+ modelTier: s.modelTier || 'standard',
83
+ skills: s.skills || [],
84
+ team: s.team,
85
+ role: s.role,
86
+ docArtifacts: s.docArtifacts || [],
87
+ subscriptions: s.events || [],
88
+ watchConditions: s.watchConditions || [],
89
+ fence: s.fence || {},
90
+ })),
91
+ mcpServers: reg.mcpServers || {},
92
+ };
93
+ }
94
+
95
+ if (!registry.version && registry.version !== 0) errors.push('registry: version is missing');
96
+ if (!registry.system) errors.push('registry: system is missing');
97
+ if (!registry.prefix) errors.push('registry: prefix is missing');
61
98
 
62
- if (!Array.isArray(reg.specialists) || reg.specialists.length === 0) {
99
+ if (!Array.isArray(registry.specialists) || registry.specialists.length === 0) {
63
100
  errors.push('registry: specialists must be a non-empty array');
64
101
  }
65
- if (!reg.orchestrator || typeof reg.orchestrator !== 'object') {
102
+ if (!registry.orchestrator || typeof registry.orchestrator !== 'object') {
66
103
  errors.push('registry: orchestrator must be a non-empty object');
67
104
  }
68
105
 
69
- const specialists = Array.isArray(reg.specialists) ? reg.specialists : [];
70
- const orchestrator = reg.orchestrator && typeof reg.orchestrator === 'object' ? reg.orchestrator : null;
106
+ const specialists = Array.isArray(registry.specialists) ? registry.specialists : [];
107
+ const orchestrator = registry.orchestrator && typeof registry.orchestrator === 'object' ? registry.orchestrator : null;
71
108
 
72
109
  const allIds = new Set();
73
110
  const specialistNames = new Set(specialists.map((s) => s?.name).filter(Boolean));
@@ -218,7 +255,7 @@ export function validateRegistry(reg, options = {}) {
218
255
  const __dirname = dirname(fileURLToPath(import.meta.url));
219
256
 
220
257
  if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
221
- const reg = JSON.parse(readFileSync(join(__dirname, '..', 'specialists', 'registry.json'), 'utf8'));
258
+ const reg = loadRegistry();
222
259
  const result = validateRegistry(reg);
223
260
  if (result.valid) {
224
261
  console.log(`✓ Registry valid (${result.summary})`);
@@ -0,0 +1,174 @@
1
+ /**
2
+ * lib/validators/skill-effectiveness.mjs — Content-depth validation beyond structure.
3
+ *
4
+ * Checks role failure modes, workflow chains, domain boundaries, and resolvable
5
+ * skill references for scope lint and release-gate enforcement.
6
+ */
7
+ import fs from 'node:fs';
8
+ import path from 'node:path';
9
+
10
+ import { loadRegistry } from '../registry/loader.mjs';
11
+ import { artifactTypes } from '../artifact-manifest.mjs';
12
+
13
+ const GET_SKILL_RE = /get_skill\("([^"]+)"\)/g;
14
+ const CX_SPECIALIST_RE = /\bcx-[a-z][a-z0-9-]+\b/g;
15
+
16
+ function walkSkills(skillsDir, prefix = '') {
17
+ const out = [];
18
+ if (!fs.existsSync(skillsDir)) return out;
19
+ for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
20
+ const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
21
+ const full = path.join(skillsDir, entry.name);
22
+ if (entry.isDirectory()) out.push(...walkSkills(full, rel));
23
+ else if (entry.name.endsWith('.md') && entry.name !== 'routing.md') {
24
+ out.push({ rel: rel.replace(/\.md$/, ''), full });
25
+ }
26
+ }
27
+ return out;
28
+ }
29
+
30
+ function parseFrontmatter(content) {
31
+ const m = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
32
+ if (!m) return { meta: {}, body: content };
33
+ const meta = {};
34
+ for (const line of m[1].split('\n')) {
35
+ const kv = line.match(/^([a-zA-Z0-9_-]+):\s*(.*)$/);
36
+ if (!kv) continue;
37
+ const [, key, raw] = kv;
38
+ if (raw.startsWith('[')) {
39
+ try { meta[key] = JSON.parse(raw); } catch { meta[key] = raw; }
40
+ } else {
41
+ meta[key] = raw.replace(/^["']|["']$/g, '');
42
+ }
43
+ }
44
+ return { meta, body: content.slice(m[0].length) };
45
+ }
46
+
47
+ function isRoleSkill(rel) {
48
+ return rel.startsWith('roles/');
49
+ }
50
+
51
+ function isWorkflowSkill(rel) {
52
+ return rel.startsWith('docs/') && rel.endsWith('-workflow');
53
+ }
54
+
55
+ function isDomainSkill(rel) {
56
+ return !isRoleSkill(rel) && !isWorkflowSkill(rel) && rel !== 'routing';
57
+ }
58
+
59
+ function validateRoleDepth(rel, body, errors) {
60
+ const numberedH3 = (body.match(/^###\s+\d+\./gm) || []).length;
61
+ const numberedList = (body.match(/^\d+\.\s+\*\*/gm) || []).length;
62
+ if (numberedH3 + numberedList < 3) {
63
+ errors.push(`${rel}: role overlay needs ≥3 numbered failure-mode sections (### N. or 1. **Title**)`);
64
+ }
65
+ const hasSymptom = /\*\*Symptom\*\*:|Symptom:/i.test(body);
66
+ const hasCounter = /\*\*Counter-move\*\*:|Counter-move:|Counter:/i.test(body);
67
+ if (!hasSymptom || !hasCounter) {
68
+ errors.push(`${rel}: role overlay missing Symptom/Counter-move pattern`);
69
+ }
70
+ const isBase = !rel.includes('.') && !/^roles\/[^/]+\.[^/]+$/.test(rel);
71
+ if (isBase && !/## Self-check|## Methodology|## Ship Check/i.test(body)) {
72
+ errors.push(`${rel}: base role file should include Self-check or Methodology section`);
73
+ }
74
+ }
75
+
76
+ function countWorkflowSteps(body) {
77
+ const lines = body.split(/\r?\n/);
78
+ let inFence = false;
79
+ let count = 0;
80
+ for (const line of lines) {
81
+ if (/^```/.test(line.trim())) inFence = !inFence;
82
+ if (inFence) continue;
83
+ if (/^\d+\.\s+/.test(line)) count++;
84
+ }
85
+ return count;
86
+ }
87
+
88
+ function validateWorkflowDepth(rel, meta, body, errors) {
89
+ if (!meta.verificationBar || String(meta.verificationBar).length < 40) {
90
+ errors.push(`${rel}: workflow missing verificationBar (≥40 chars)`);
91
+ }
92
+ if (!meta.artifactType) {
93
+ errors.push(`${rel}: workflow missing artifactType`);
94
+ }
95
+ if (!/construct artifact validate/i.test(body)) {
96
+ errors.push(`${rel}: workflow must mention construct artifact validate`);
97
+ }
98
+ if (countWorkflowSteps(body) < 4) {
99
+ errors.push(`${rel}: workflow needs ≥4 numbered steps`);
100
+ }
101
+ }
102
+
103
+ function validateDomainDepth(rel, body, meta, errors) {
104
+ const stripped = body.replace(/^---[\s\S]*?---/, '').trim();
105
+ if (stripped.length < 400) {
106
+ errors.push(`${rel}: domain skill body too short (<400 chars)`);
107
+ }
108
+ const desc = String(meta.description || '');
109
+ const boundary =
110
+ /when not to use|when to use|do not use|use this skill when|use this skill to|use when:/i.test(body) ||
111
+ /use when|use this skill when|use this skill to|do not use/i.test(desc);
112
+ if (!boundary) {
113
+ errors.push(`${rel}: domain skill missing boundary section (When not to use / When to use)`);
114
+ }
115
+ }
116
+
117
+ function validateReferences(rel, body, skillsSet, registry, errors) {
118
+ for (const m of body.matchAll(GET_SKILL_RE)) {
119
+ const id = m[1];
120
+ if (!skillsSet.has(id)) {
121
+ errors.push(`${rel}: unresolved get_skill("${id}")`);
122
+ }
123
+ }
124
+ if (isWorkflowSkill(rel)) {
125
+ for (const id of body.match(CX_SPECIALIST_RE) || []) {
126
+ const key = id.replace(/^cx-/, '');
127
+ const found = Object.values(registry.specialists || {}).some(
128
+ (s) => s.name === key || `cx-${s.name}` === id,
129
+ );
130
+ if (!found) errors.push(`${rel}: unknown specialist ${id}`);
131
+ }
132
+ const types = artifactTypes();
133
+ for (const m of body.matchAll(/`([a-z][a-z0-9-]*)`/g)) {
134
+ const t = m[1];
135
+ if (t.startsWith('prd') || t === 'rfc' || t === 'adr' || t === 'memo') {
136
+ if (!types.includes(t) && !types.includes(t.replace(/-platform$/, ''))) {
137
+ /* template ids vary — only warn on explicit get_template */
138
+ }
139
+ }
140
+ }
141
+ }
142
+ }
143
+
144
+ export function validateSkillEffectiveness({ rootDir } = {}) {
145
+ const root = rootDir || path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', '..');
146
+ const skillsDir = path.join(root, 'skills');
147
+ const errors = [];
148
+ const warnings = [];
149
+ const files = walkSkills(skillsDir);
150
+ const skillsSet = new Set(files.map((f) => f.rel));
151
+ let registry;
152
+ try {
153
+ registry = loadRegistry({ rootDir: root });
154
+ } catch {
155
+ registry = { specialists: {} };
156
+ }
157
+
158
+ for (const { rel, full } of files) {
159
+ const raw = fs.readFileSync(full, 'utf8');
160
+ const { meta, body } = parseFrontmatter(raw);
161
+ if (isRoleSkill(rel) && meta.role?.includes('.')) {
162
+ validateRoleDepth(rel, body, errors);
163
+ } else if (isRoleSkill(rel)) {
164
+ if (body.length > 200) validateRoleDepth(rel, body, errors);
165
+ } else if (isWorkflowSkill(rel)) {
166
+ validateWorkflowDepth(rel, meta, body, errors);
167
+ } else if (isDomainSkill(rel)) {
168
+ validateDomainDepth(rel, body, meta, errors);
169
+ }
170
+ validateReferences(rel, body, skillsSet, registry, errors);
171
+ }
172
+
173
+ return { valid: errors.length === 0, errors, warnings, checked: files.length };
174
+ }
@@ -8,7 +8,7 @@
8
8
  * description: "<≤1024 chars, includes a 'use when' trigger>"
9
9
  * [inputs: [<string>, …] — optional, surfaced by capability discovery]
10
10
  * [artifactType: <string> — optional, surfaced by capability discovery]
11
- * [role/applies_to/inherits/version/profiles/cap for role files]
11
+ * [role/applies_to/inherits/version/scopes/cap for role files]
12
12
  * ---
13
13
  * # <Title>
14
14
  * Use when: <trigger condition>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geraldmaron/construct",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "packageManager": "npm@11.5.1",
6
6
  "description": "Construct — agent orchestration layer for OpenCode, Claude Code, and other coding surfaces",
@@ -51,12 +51,15 @@
51
51
  },
52
52
  "scripts": {
53
53
  "mcp": "node lib/mcp/server.mjs",
54
+ "doctor": "node ./bin/construct doctor",
54
55
  "test": "node scripts/run-tests.mjs",
55
56
  "test:functional": "XDG_CONFIG_HOME= XDG_STATE_HOME= XDG_CACHE_HOME= node --test --test-timeout=120000 --test-concurrency=1 tests/functional/*.functional.test.mjs",
57
+ "test:visual": "node -e \"console.log('visual surface tests removed')\"",
56
58
  "test:unit": "node scripts/run-tests.mjs --exclude=tests/functional",
57
59
  "lint:js": "eslint bin/construct \"lib/**/*.mjs\" \"scripts/**/*.mjs\" \"tests/**/*.mjs\"",
58
60
  "coverage": "c8 --reporter=text-summary --reporter=lcov --src=lib --src=bin node scripts/run-tests.mjs --exclude=tests/functional",
59
61
  "docs:init": "node -e \"console.error('docs:init is deprecated — use: construct init --docs-preset=lean|product|full'); process.exit(1)\"",
62
+ "docs:verify": "node ./bin/construct docs:verify",
60
63
  "docs:update": "node ./bin/construct docs:update",
61
64
  "docs:site": "node ./bin/construct docs:site",
62
65
  "docs:dev": "node scripts/npm-run.mjs npm --prefix apps/docs run dev",
@@ -64,20 +67,22 @@
64
67
  "lint:comments": "node ./bin/construct lint:comments",
65
68
  "lint:contracts": "node ./bin/construct lint:contracts",
66
69
  "lint:agents": "node ./bin/construct lint:agents",
70
+ "gates:audit": "node ./bin/construct gates:audit",
71
+ "evals": "node ./bin/construct evals",
72
+ "certify:demos": "node ./bin/construct certify demos",
67
73
  "build:sea": "esbuild bin/construct --bundle --platform=node --target=node20 --packages=external --outfile=dist/construct-bundle.cjs 2>&1 || (echo 'esbuild not found — install with: npm install -g esbuild' && exit 1)",
68
- "build:chat": "esbuild apps/chat/tui/index.jsx --bundle --platform=node --format=esm --packages=external --jsx=automatic --jsx-import-source=react --outfile=apps/chat/dist/tui.mjs",
69
- "build:chat-desktop": "node scripts/build-chat-desktop.mjs",
70
- "build:chat-desktop:bundle": "node scripts/npm-run.mjs npm install --prefix apps/chat/desktop --include=dev && node scripts/npm-run.mjs npm --prefix apps/chat/desktop run build",
71
- "build:dashboard": "node scripts/npm-run.mjs npm install --prefix apps/dashboard && node scripts/npm-run.mjs npm --prefix apps/dashboard run build",
72
74
  "examples:deck": "node scripts/generate-deck-examples.mjs",
73
75
  "examples:distribution": "node scripts/generate-distribution-examples.mjs",
74
76
  "clean:artifacts": "node scripts/clean-artifacts.mjs",
75
- "lint:profiles": "node scripts/lint-profiles.mjs",
77
+ "lint:scopes": "node scripts/lint-scopes.mjs",
78
+ "catalog:regen": "node scripts/catalog-regen.mjs",
79
+ "catalog:validate": "node scripts/catalog-validate.mjs",
80
+ "docs:sync": "node scripts/docs-sync.mjs",
76
81
  "learning:status": "node scripts/learning-status.mjs",
77
82
  "lint:templates": "node scripts/lint-commits-pr.mjs",
78
83
  "eval:routing": "node scripts/eval/score-intent-classifier.mjs",
79
84
  "adapters": "node lib/adapters-sync.mjs",
80
- "release:check": "node ./bin/construct doctor && npm test && node ./bin/construct docs:update --check && node ./bin/construct docs:site --check && node ./bin/construct registry:generate-docs --check && node ./bin/construct docs:verify --strict && node scripts/alignment/census.mjs --ratchet && node ./bin/construct dashboard:sync --check && node ./bin/construct lint:comments && node ./bin/construct certify gate && node scripts/lint-commits-pr.mjs",
85
+ "release:check": "npm run doctor && node ./bin/construct registry:validate --unified && npm run catalog:validate -- --check && npm test && npm run docs:update -- --check && npm run docs:site -- --check && npm run docs:sync -- --check && node ./bin/construct registry:generate-docs --check && npm run docs:verify -- --strict && node scripts/alignment/census.mjs --ratchet && npm run lint:comments && node ./bin/construct certify gate && npm run lint:templates",
81
86
  "release:gate": "node --test tests/functional/release-gate.functional.test.mjs",
82
87
  "audit:published": "node scripts/audit-published-artifact.mjs",
83
88
  "version": "node scripts/sync-construct-version.mjs && git add -f .construct/version",
@@ -31,13 +31,22 @@ Tracks: immediate (act directly), focused (one bounded specialist), orchestrated
31
31
 
32
32
  Orchestrated dispatches emit a task-packet with `goal`, `intent`, `workCategory`, `riskFlags`, `acceptanceCriteria` before naming specialists (`specialists/contracts.json:construct-to-orchestrator`).
33
33
 
34
+ Research-shaped requests and artifact-drafting requests are never "answer from memory" work. If the request is asking for current evidence, comparison, standards, or a typed output, route it through the matching execution path first:
35
+ - research / compare / explore / explain external state → call `orchestration_run` with the original request and `workflow_type: "research-synthesis"` unless the user explicitly supplied a raw evidence bundle, in which case use the evidence-ingest path
36
+ - draft or revise a typed artifact/output → use the canonical template for that artifact and route through the matching workflow before writing the final draft; use `workflow_invoke` only when the user is asking for the plan / contract preview rather than execution
37
+ - never claim research was completed unless `orchestration_run`, evidence tools, or a user-supplied evidence bundle actually produced the evidence
38
+ - if evidence is missing or execution is unavailable, say what is missing and ask for the minimal next input instead of inventing a process narrative or conclusion
39
+
40
+ General conversation is still valid for scoping, clarification, and lightweight discussion, but the default for substantive research is evidence-first execution, not free-form synthesis.
41
+
34
42
  ## Gates and contracts (org-in-a-box) <!-- cx:prio=2 -->
35
43
 
36
- `orchestration_policy` returns three artifacts; honor all three:
44
+ `orchestration_policy` returns four artifacts; honor all four:
37
45
 
38
46
  1. **Gates**. `framingChallenge`, `externalResearch`, `docAuthoring`
39
47
  2. **Contract chain**. typed handoffs from `specialists/contracts.json`. Call `agent_contract` with `handoffPacket` from `orchestration_policy`.
40
48
  3. **Specialist sequence**. dispatch plan with ordering/parallel markers.
49
+ 4. **Team routing**. name `teamRouting.primaryTeam` in the dispatch plan; route through `teamRouting.requiredApprovals` before DONE; if `teamRouting.blockedStatus` is set, stop and escalate along its `escalationPath` rather than proceeding.
41
50
 
42
51
  Before DONE: postconditions met · sources cited · framing logged · ADRs have Rejected alternatives.
43
52
 
@@ -48,12 +57,12 @@ Before DONE: postconditions met · sources cited · framing logged · ADRs have
48
57
 
49
58
  ## Intake surface <!-- cx:prio=3 -->
50
59
 
51
- The active profile (`construct profile show`) sets the intake taxonomy. Session-start surfaces pending intake at `.cx/intake/pending/<id>.json`. Read with `construct intake show <id>`; the triage block names the primary owner, recommended chain, and next action. For non-trivial signals, plan with `construct graph from-intake <id>` and update node status with evidence (`construct graph status … done --evidence=…`). A node cannot reach `done` without an evidence record. Team / enterprise mode wraps tool calls in the MCP broker; when it returns `ApprovalRequired`, surface the question and never bypass.
60
+ The active profile (`construct scope show`) sets the intake taxonomy. Session-start surfaces pending intake at `.cx/intake/pending/<id>.json`. Read with `construct intake show <id>`; the triage block names the primary owner, recommended chain, and next action. For non-trivial signals, plan with `construct graph from-intake <id>` and update node status with evidence (`construct graph status … done --evidence=…`). A node cannot reach `done` without an evidence record. Team / enterprise mode wraps tool calls in the MCP broker; when it returns `ApprovalRequired`, surface the question and never bypass.
52
61
 
53
62
  ## Action discipline <!-- cx:prio=1 -->
54
63
 
55
64
  - Dispatch, don't solo-plan: 3+ files, 2+ modules, or a new contract → cx-architect owns the plan.
56
- - Ask or look up, don't speculate: call `context7_query-docs` / `WebFetch`, ask, or commit to a default. Never a fourth round of internal debate.
65
+ - Ask or look up, don't speculate: use the route's `researchExecutionPolicy`. For library/framework/API docs, prefer Context7 when available; otherwise search and fetch official docs directly. For broader research, go to domain-primary sources. Never a fourth round of internal debate.
57
66
  - Deliberation cap: two passes. Same decision twice without a new read, tool call, or user input = hand off, query, or ask.
58
67
  - Probe before bulk read: check size via `Glob` / `wc -l` or a `limit: 50` probe before `Read` with `limit > 200`.
59
68
  - Start-of-task: parallel bootstrap (above) + `cx_trace` before anything mutating.
@@ -2,7 +2,7 @@
2
2
 
3
3
  This file ships as a reference template alongside the Construct npm package. The canonical project-instructions file is `/CLAUDE.md` at your repo root — that's the file Claude Code actually reads at session start. The structure here mirrors that canonical file so users know what shape their own `/CLAUDE.md` should take after `construct init` scaffolds a project.
4
4
 
5
- Personas and specialists are defined in `specialists/registry.json` and rendered into Claude Code agents on every `construct sync`. The single user-facing persona is `construct`; the 28 specialists (cx-engineer, cx-security, cx-devil-advocate, …) are routed internally — you address Construct, Construct dispatches.
5
+ Personas and specialists are defined in `specialists/org` and rendered into Claude Code agents on every `construct sync`. The single user-facing persona is `construct`; the 28 specialists (cx-engineer, cx-security, cx-devil-advocate, …) are routed internally — you address Construct, Construct dispatches.
6
6
 
7
7
  ## Critical rules (mirror in your project /CLAUDE.md)
8
8