@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,188 @@
1
+ /**
2
+ * lib/demo-project.mjs — project-level demo plug-in scaffold (.cx/demos).
3
+ *
4
+ * `construct demo init --from-project` calls scaffoldProjectDemo to seed a
5
+ * project-defined demo from real project signals (no fabricated scenario
6
+ * content): a manifest (<name>.project.json) plus the demo script and terminal
7
+ * tape stub it ships with, all under .cx/demos/. The manifest is
8
+ * validated against schemas/project-demo.schema.json by a hand-rolled checker
9
+ * (Construct stays dependency-free at startup — no ajv).
10
+ */
11
+
12
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
13
+ import { join, dirname, basename } from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+ import { readContextState } from './context-state.mjs';
16
+
17
+ const REPO_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
18
+
19
+ export const PROJECT_DEMO_SCHEMA = 'construct/project-demo/1';
20
+
21
+ export function projectDemosDir(cwd) {
22
+ return join(cwd, '.cx', 'demos');
23
+ }
24
+
25
+ export function projectDemoManifestPath(cwd, name) {
26
+ return join(projectDemosDir(cwd), `${name}.project.json`);
27
+ }
28
+
29
+ export function projectDemoScriptRel(name) {
30
+ return `.cx/demos/scripts/${name}.json`;
31
+ }
32
+
33
+ export function projectDemoTapeRel(name) {
34
+ return `.cx/demos/tapes/${name}.tape`;
35
+ }
36
+
37
+ // The project name is read, never invented: prefer the durable context state,
38
+ // then package.json, and only then the directory basename as a last resort.
39
+
40
+ export function resolveProjectName(cwd) {
41
+ const state = readContextState(cwd);
42
+ if (state?.projectName && typeof state.projectName === 'string') return state.projectName;
43
+
44
+ const pkgPath = join(cwd, 'package.json');
45
+ if (existsSync(pkgPath)) {
46
+ try {
47
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
48
+ if (pkg?.name && typeof pkg.name === 'string') return pkg.name;
49
+ } catch { /* unreadable package.json falls through to the directory name */ }
50
+ }
51
+
52
+ return basename(cwd) || 'project';
53
+ }
54
+
55
+ function projectScriptSource(name, projectName) {
56
+ return {
57
+ name,
58
+ title: `${projectName} demo — ${name}`,
59
+ summary: `Project demo for ${projectName}: walk Construct's read-only status and capability surfaces.`,
60
+ systemOverlay: `Demo mode for the ${projectName} project. Run the exact construct CLI commands from each step. Every command here is read-only and secret-free; report exactly what each command emits and do not invent output.`,
61
+ tape: name,
62
+ fallbackSurface: 'tape',
63
+ steps: [
64
+ {
65
+ title: 'Status — system health',
66
+ prompt: `Run construct status to show ${projectName}'s system health and configured credentials.`,
67
+ command: 'node bin/construct status',
68
+ },
69
+ {
70
+ title: 'Capability — what this install can do',
71
+ prompt: 'Run construct capability describe --json to emit the read-only capability contract for this install.',
72
+ command: 'node bin/construct capability describe --json',
73
+ },
74
+ ],
75
+ };
76
+ }
77
+
78
+ function projectTapeSource(name, projectName) {
79
+ return `Require node
80
+ Require vhs
81
+ # construct demo: ${name} — ${projectName} project plug-in
82
+ Output ${projectDemoTapeRel(name)}
83
+ Set FontSize 18
84
+ Set Width 1100
85
+ Set Height 600
86
+ Set TypingSpeed 60ms
87
+
88
+ Type "node bin/construct status"
89
+ Sleep 500ms
90
+ Enter
91
+ Sleep 2s
92
+
93
+ Type "node bin/construct capability describe --json"
94
+ Sleep 500ms
95
+ Enter
96
+ Sleep 2s
97
+ `;
98
+ }
99
+
100
+ export function buildProjectDemoManifest(name, { cwd, projectName, now = () => new Date() } = {}) {
101
+ const resolvedProject = projectName || resolveProjectName(cwd);
102
+ return {
103
+ $schema: '../../schemas/project-demo.schema.json',
104
+ schema: PROJECT_DEMO_SCHEMA,
105
+ name,
106
+ title: `${resolvedProject} demo — ${name}`,
107
+ summary: `Project-defined Construct demo for ${resolvedProject}.`,
108
+ project: resolvedProject,
109
+ script: projectDemoScriptRel(name),
110
+ tape: projectDemoTapeRel(name),
111
+ fallbackSurface: 'tape',
112
+ createdAt: now().toISOString(),
113
+ };
114
+ }
115
+
116
+ // A hand-rolled validator against schemas/project-demo.schema.json. Construct
117
+ // stays dependency-free at startup, so this mirrors the schema's required
118
+ // fields, const, and patterns rather than loading ajv.
119
+
120
+ export function validateProjectDemoManifest(manifest) {
121
+ const errors = [];
122
+ if (!manifest || typeof manifest !== 'object') return { valid: false, errors: ['manifest is not an object'] };
123
+
124
+ if (manifest.schema !== PROJECT_DEMO_SCHEMA) errors.push(`schema must equal ${PROJECT_DEMO_SCHEMA}`);
125
+ if (typeof manifest.name !== 'string' || !/^[a-z0-9][a-z0-9-]*$/.test(manifest.name)) {
126
+ errors.push('name required, lowercase slug ([a-z0-9][a-z0-9-]*)');
127
+ }
128
+ if (typeof manifest.title !== 'string' || !manifest.title) errors.push('title required');
129
+ if (typeof manifest.project !== 'string' || !manifest.project) errors.push('project required');
130
+ if (typeof manifest.script !== 'string' || !/^\.cx\/demos\/scripts\/.+\.json$/.test(manifest.script)) {
131
+ errors.push('script must be a .cx/demos/scripts/*.json path');
132
+ }
133
+ if (manifest.fallbackSurface && !['tape', 'playwright'].includes(manifest.fallbackSurface)) {
134
+ errors.push('fallbackSurface must be tape or playwright');
135
+ }
136
+
137
+ return { valid: errors.length === 0, errors };
138
+ }
139
+
140
+ export function loadProjectDemoManifest(name, { cwd } = {}) {
141
+ const manifestPath = projectDemoManifestPath(cwd, name);
142
+ if (!existsSync(manifestPath)) return { ok: false, errors: [`project demo not found: ${name}`], manifestPath };
143
+ try {
144
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
145
+ const validated = validateProjectDemoManifest(manifest);
146
+ if (!validated.valid) return { ok: false, errors: validated.errors, manifestPath };
147
+ return { ok: true, manifest, manifestPath };
148
+ } catch (err) {
149
+ return { ok: false, errors: [err.message || 'invalid JSON'], manifestPath };
150
+ }
151
+ }
152
+
153
+ export function scaffoldProjectDemo(name, { cwd, projectName, repoRoot = REPO_ROOT, now = () => new Date() } = {}) {
154
+ if (!name) return { ok: false, message: 'A demo name is required: construct demo init <name> --from-project' };
155
+
156
+ const manifestPath = projectDemoManifestPath(cwd, name);
157
+ if (existsSync(manifestPath)) return { ok: false, message: `Project demo already exists: ${manifestPath}` };
158
+
159
+ const resolvedProject = projectName || resolveProjectName(cwd);
160
+ const demosDir = projectDemosDir(cwd);
161
+ const scriptsDir = join(demosDir, 'scripts');
162
+ const tapesDir = join(demosDir, 'tapes');
163
+ mkdirSync(scriptsDir, { recursive: true });
164
+ mkdirSync(tapesDir, { recursive: true });
165
+
166
+ const scriptPath = join(cwd, projectDemoScriptRel(name));
167
+ const tapePath = join(cwd, projectDemoTapeRel(name));
168
+
169
+ const script = projectScriptSource(name, resolvedProject);
170
+ writeFileSync(scriptPath, `${JSON.stringify(script, null, 2)}\n`, 'utf8');
171
+ writeFileSync(tapePath, projectTapeSource(name, resolvedProject), 'utf8');
172
+
173
+ const manifest = buildProjectDemoManifest(name, { cwd, projectName: resolvedProject, now });
174
+ const validated = validateProjectDemoManifest(manifest);
175
+ if (!validated.valid) {
176
+ return { ok: false, message: `Scaffolded manifest failed validation: ${validated.errors.join('; ')}` };
177
+ }
178
+ writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
179
+
180
+ return {
181
+ ok: true,
182
+ manifestPath,
183
+ scriptPath,
184
+ tapePath,
185
+ project: resolvedProject,
186
+ message: `Scaffolded project demo ${name} for ${resolvedProject}:\n ${manifestPath}\n ${scriptPath}\n ${tapePath}`,
187
+ };
188
+ }
@@ -1,14 +1,12 @@
1
1
  /**
2
2
  * lib/demo-recording.mjs — load and validate Playwright recording manifests.
3
3
  *
4
- * Search order: .cx/demos/recordings/ then templates/demos/recordings/. Legacy
5
- * dashboardDemo scripts synthesize a recording manifest when no JSON exists.
4
+ * Search order: .cx/demos/recordings/ then templates/demos/recordings/.
6
5
  */
7
6
 
8
7
  import fs from 'node:fs';
9
8
  import path from 'node:path';
10
9
  import { fileURLToPath } from 'node:url';
11
- import { loadDemoScript } from './demo-script.mjs';
12
10
 
13
11
  const HERE = path.dirname(fileURLToPath(import.meta.url));
14
12
  const REPO_ROOT = path.resolve(HERE, '..');
@@ -90,45 +88,22 @@ export function normalizeRecording(raw, name) {
90
88
  };
91
89
  }
92
90
 
93
- export function recordingFromDemoScript(name, { cwd = process.cwd(), repoRoot = REPO_ROOT } = {}) {
94
- const script = loadDemoScript(name, { cwd, repoRoot });
95
- if (!script?.dashboardDemo) return null;
96
- const demoName = script.dashboardDemo;
97
- const repo = repoRoot || REPO_ROOT;
98
- return normalizeRecording({
99
- name: script.name || name,
100
- title: script.title || name,
101
- engine: 'playwright',
102
- workspace: 'apps/dashboard',
103
- spec: `apps/dashboard/e2e/demo/${demoName}.spec.ts`,
104
- baseUrl: 'http://127.0.0.1:4242',
105
- project: 'demo-recording',
106
- artifactReveal: script.artifactReveal,
107
- output: { format: 'mp4', path: `.cx/demos/dashboard/${demoName}.mp4` },
108
- _legacy: true,
109
- }, name);
110
- }
111
-
112
91
  export function loadDemoRecording(name, { cwd = process.cwd(), repoRoot = REPO_ROOT } = {}) {
113
92
  const filePath = resolveDemoRecordingPath(name, { cwd, repoRoot });
114
- if (filePath) {
115
- try {
116
- const raw = JSON.parse(fs.readFileSync(filePath, 'utf8'));
117
- const validated = validateRecording(raw, name);
118
- if (!validated.ok) return null;
119
- return { ...validated.recording, sourcePath: filePath };
120
- } catch {
121
- return null;
122
- }
93
+ if (!filePath) return null;
94
+ try {
95
+ const raw = JSON.parse(fs.readFileSync(filePath, 'utf8'));
96
+ const validated = validateRecording(raw, name);
97
+ if (!validated.ok) return null;
98
+ return { ...validated.recording, sourcePath: filePath };
99
+ } catch {
100
+ return null;
123
101
  }
124
- return recordingFromDemoScript(name, { cwd, repoRoot });
125
102
  }
126
103
 
127
104
  export function loadDemoRecordingValidated(name, opts = {}) {
128
105
  const filePath = resolveDemoRecordingPath(name, opts);
129
106
  if (!filePath) {
130
- const legacy = recordingFromDemoScript(name, opts);
131
- if (legacy) return { ok: true, recording: legacy };
132
107
  return { ok: false, errors: [`recording not found: ${name}`] };
133
108
  }
134
109
  try {
@@ -1,9 +1,9 @@
1
1
  /**
2
- * lib/demo-script.mjs — load committed demo scripts for chat-guided demos.
2
+ * lib/demo-script.mjs — load committed demo scripts.
3
3
  *
4
4
  * Scripts live in templates/demos/scripts/ (shipped) and .cx/demos/scripts/
5
5
  * (project overrides). Each script defines steps, system overlay, and optional
6
- * tape/dashboard fallbacks when chat is unavailable.
6
+ * recording fallbacks.
7
7
  */
8
8
 
9
9
  import fs from 'node:fs';
@@ -1,119 +1,31 @@
1
1
  /**
2
- * lib/demo-surface.mjs — chat-first demo execution with surface targeting and fallbacks.
2
+ * lib/demo-surface.mjs — demo execution with recording surfaces and fallbacks.
3
3
  *
4
- * Default surface is construct chat (Ink on capable TTY, linear otherwise). Users
5
- * may target web, tape (VHS), or dashboard explicitly. When chat is unavailable
6
- * (no provider, non-interactive), the runner degrades through the script's
7
- * fallback chain without failing the command.
4
+ * Default surface is VHS tape recording. Users may target Playwright explicitly.
5
+ * When a recorder is unavailable, the runner prints the demo script steps.
8
6
  */
9
7
 
10
- import { spawnSync } from 'node:child_process';
11
- import { hasAnySecret } from './providers/secret-resolver.mjs';
12
- import { loadConstructEnv } from './env-config.mjs';
13
8
  import { loadDemoScript, formatDemoWelcome, createDemoGuide } from './demo-script.mjs';
14
9
  import { loadDemoRecording } from './demo-recording.mjs';
15
- import { recordDashboardDemo } from './dashboard-demo.mjs';
16
10
  import { recordPlaywrightDemo } from './playwright-demo.mjs';
17
11
 
18
- export const DEMO_SURFACES = ['chat', 'web', 'tape', 'dashboard', 'playwright'];
19
-
20
- const PROVIDER_KEYS = ['ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'OPENROUTER_API_KEY', 'GITHUB_TOKEN'];
21
-
22
- function which(bin) {
23
- const result = spawnSync(process.platform === 'win32' ? 'where' : 'which', [bin], { encoding: 'utf8' });
24
- if (result.status !== 0) return null;
25
- return result.stdout.trim().split('\n')[0] || null;
26
- }
27
-
28
- export function detectChatDemoReady({ env = process.env, cwd = process.cwd() } = {}) {
29
- const effective = loadConstructEnv({ rootDir: cwd, env, warn: false });
30
- if (hasAnySecret(PROVIDER_KEYS, { env: effective, cwd })) {
31
- return { ready: true, reason: 'provider configured' };
32
- }
33
- if (which('ollama')) {
34
- return { ready: true, reason: 'local Ollama detected' };
35
- }
36
- return {
37
- ready: false,
38
- reason: 'No LLM provider configured. Set a key in ~/.construct/config.env, run `construct creds login copilot`, or start Ollama.',
39
- };
40
- }
12
+ export const DEMO_SURFACES = ['tape', 'playwright'];
41
13
 
42
14
  export function buildDemoAttemptChain(requestedSurface, { script, recording = null, interactive = true } = {}) {
43
15
  const chain = [];
44
16
  const push = (surface) => { if (!chain.includes(surface)) chain.push(surface); };
45
17
 
46
- if (requestedSurface === 'dashboard') {
47
- push('dashboard');
48
- push('playwright');
49
- push('chat');
50
- push('web');
51
- } else if (requestedSurface === 'playwright') {
18
+ if (requestedSurface === 'playwright') {
52
19
  push('playwright');
53
- push('dashboard');
54
- } else if (requestedSurface === 'web') {
55
- push('web');
56
- push('chat');
57
- } else if (requestedSurface === 'tape') {
58
- push('tape');
59
- return chain;
60
20
  } else {
61
- push('chat');
62
- push('web');
21
+ push('tape');
63
22
  }
64
23
 
65
- if (script?.dashboardDemo && requestedSurface !== 'tape') push('dashboard');
66
24
  if (recording?.engine === 'playwright' && requestedSurface !== 'tape') push('playwright');
67
25
  push('tape');
68
-
69
- if (!interactive && recording?.engine === 'playwright') {
70
- const filtered = chain.filter((s) => s !== 'chat' && s !== 'web');
71
- return filtered.length ? filtered : ['tape'];
72
- }
73
- if (!interactive && chain[0] === 'chat') {
74
- const filtered = chain.filter((s) => s !== 'chat' && s !== 'web');
75
- return filtered.length ? filtered : ['tape'];
76
- }
77
26
  return chain;
78
27
  }
79
28
 
80
- function buildChatArgs(name, opts) {
81
- const args = [`--demo=${name}`];
82
- if (opts.web) args.push('--web');
83
- if (opts.plain) args.push('--plain');
84
- if (opts.accessible) args.push('--accessible');
85
- if (opts.free) args.push('--free');
86
- if (opts.model) args.push(`--model=${opts.model}`);
87
- return args;
88
- }
89
-
90
- async function tryChatDemo(name, opts, { web = false } = {}) {
91
- const readiness = detectChatDemoReady({ env: opts.env, cwd: opts.cwd });
92
- if (!readiness.ready) {
93
- return { ok: false, surface: web ? 'web' : 'chat', soft: true, message: readiness.reason };
94
- }
95
-
96
- const script = loadDemoScript(name, opts);
97
- if (!script) {
98
- return { ok: false, surface: web ? 'web' : 'chat', soft: true, message: `No demo script for ${name}` };
99
- }
100
-
101
- const chatArgs = buildChatArgs(name, { ...opts, web });
102
- const { runChat } = await import('./chat/cli.mjs');
103
- const code = await runChat(chatArgs, {
104
- cwd: opts.cwd,
105
- env: { ...opts.env, CONSTRUCT_DEMO: name },
106
- input: opts.input || process.stdin,
107
- output: opts.output || process.stdout,
108
- errorOutput: opts.errorOutput || process.stderr,
109
- });
110
-
111
- if (code === 0) {
112
- return { ok: true, surface: web ? 'web' : 'chat', message: `Demo ${name} finished in chat` };
113
- }
114
- return { ok: false, surface: web ? 'web' : 'chat', soft: true, message: `Chat exited ${code}` };
115
- }
116
-
117
29
  async function tryTapeDemo(name, opts, { sourceOnly = false } = {}) {
118
30
  const { runDemoRecord, resolveTapeSource, listProjectTapes } = await import('./demo.mjs');
119
31
  const tapeName = loadDemoScript(name, opts)?.tape || name;
@@ -157,17 +69,6 @@ function tryPlaywrightDemo(name, opts) {
157
69
  return { ok: false, surface: 'playwright', soft: true, ...result };
158
70
  }
159
71
 
160
- function tryDashboardDemo(name, opts) {
161
- const script = loadDemoScript(name, opts);
162
- const demoName = script?.dashboardDemo;
163
- if (!demoName) {
164
- return { ok: false, surface: 'dashboard', soft: true, message: 'No dashboard demo configured' };
165
- }
166
- const result = recordDashboardDemo(demoName, { cwd: opts.cwd, repoRoot: opts.repoRoot });
167
- if (result.ok) return { ok: true, surface: 'dashboard', ...result };
168
- return { ok: false, surface: 'dashboard', soft: true, ...result };
169
- }
170
-
171
72
  function printScriptFallback(name, opts, notices = []) {
172
73
  const script = loadDemoScript(name, opts);
173
74
  if (!script) {
@@ -192,39 +93,31 @@ function printScriptFallback(name, opts, notices = []) {
192
93
  export async function runDemoGuided(name, {
193
94
  cwd = process.cwd(),
194
95
  repoRoot,
195
- surface = 'chat',
96
+ surface = 'tape',
196
97
  format = 'mp4',
197
98
  out = null,
198
99
  sourceOnly = false,
199
100
  required = false,
200
- model = null,
201
- plain = false,
202
- accessible = false,
203
- web = false,
204
- free = false,
205
- env = process.env,
206
- input,
207
101
  output,
208
102
  errorOutput,
209
103
  } = {}) {
104
+ const errOut = errorOutput || process.stderr;
105
+
210
106
  const opts = {
211
- cwd, repoRoot, format, out, sourceOnly, required, model, plain, accessible, web, free, env, input, output, errorOutput,
107
+ cwd, repoRoot, format, out, sourceOnly, required, output, errorOutput: errOut,
212
108
  };
213
109
 
214
110
  const script = loadDemoScript(name, opts);
215
111
  const recording = loadDemoRecording(name, opts);
216
- const requested = web ? 'web' : (DEMO_SURFACES.includes(surface) ? surface : 'chat');
217
- const interactive = Boolean((input || process.stdin).isTTY && (output || process.stdout).isTTY);
112
+ const requested = DEMO_SURFACES.includes(surface) ? surface : 'tape';
113
+ const interactive = Boolean((output || process.stdout).isTTY);
218
114
  const chain = buildDemoAttemptChain(requested, { script, recording, interactive });
219
115
  const notices = [];
220
116
 
221
117
  for (const attempt of chain) {
222
118
  let result;
223
- if (attempt === 'chat') result = await tryChatDemo(name, opts, { web: false });
224
- else if (attempt === 'web') result = await tryChatDemo(name, opts, { web: true });
225
- else if (attempt === 'tape') result = await tryTapeDemo(name, opts);
119
+ if (attempt === 'tape') result = await tryTapeDemo(name, opts);
226
120
  else if (attempt === 'playwright') result = tryPlaywrightDemo(name, opts);
227
- else if (attempt === 'dashboard') result = tryDashboardDemo(name, opts);
228
121
  else continue;
229
122
 
230
123
  if (result.ok) {
@@ -236,14 +129,3 @@ export async function runDemoGuided(name, {
236
129
 
237
130
  return printScriptFallback(name, opts, notices);
238
131
  }
239
-
240
- export function resolveDemoGuideForChat(name, { cwd = process.cwd(), repoRoot } = {}) {
241
- const script = loadDemoScript(name, { cwd, repoRoot });
242
- if (!script) return null;
243
- return {
244
- script,
245
- guide: createDemoGuide(script),
246
- welcome: formatDemoWelcome(script),
247
- systemOverlay: script.systemOverlay,
248
- };
249
- }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * lib/demo-tour-renderer.mjs — linear, screen-reader-friendly demo tour renderer.
3
+ *
4
+ * Renders a loaded demo script (lib/demo-script.mjs) as a numbered, top-to-bottom
5
+ * walkthrough with no Ink/interactive widgets, so `construct demo tour --accessible`
6
+ * stays WCAG-plain: color is opt-in via the caller's palette and off under
7
+ * NO_COLOR / non-TTY, prose wraps at a readable width, and every step is announced
8
+ * as "Step N of M". Headless runs pass { skipInput: true } to auto-advance without
9
+ * touching stdin; an interactive run without that flag pauses for Enter between
10
+ * steps. This renderer stays independent of host UI code — the tour is built on the demo
11
+ * surface only.
12
+ */
13
+
14
+ import { resolveColors, termWidth, wrapText } from './term-format.mjs';
15
+
16
+ function writeWrapped(output, text, width) {
17
+ output.write(`${wrapText(String(text), width)}\n`);
18
+ }
19
+
20
+ function renderHeader(output, script, colors, width, total) {
21
+ output.write(`${colors.bold}Demo tour: ${script.title}${colors.reset}\n`);
22
+ if (script.summary) writeWrapped(output, `${colors.dim}${script.summary}${colors.reset}`, width);
23
+ output.write(`\n${colors.dim}${total} step${total === 1 ? '' : 's'}. Each step shows the prompt to ask and the command it runs.${colors.reset}\n`);
24
+ }
25
+
26
+ function renderStep(output, step, position, total, colors, width) {
27
+ const title = step.title || step.prompt?.slice(0, 60) || `Step ${position}`;
28
+ output.write(`\n${colors.bold}Step ${position} of ${total}: ${title}${colors.reset}\n`);
29
+ if (step.prompt) {
30
+ output.write(`${colors.dim}Prompt:${colors.reset}\n`);
31
+ writeWrapped(output, ` ${step.prompt}`, width);
32
+ }
33
+ if (step.command) {
34
+ output.write(`${colors.dim}Command:${colors.reset}\n`);
35
+ output.write(` ${step.command}\n`);
36
+ }
37
+ }
38
+
39
+ // Pause for Enter only when a real interactive TTY is driving and the caller did
40
+ // not request auto-advance. A single readable() line keeps the wait keyboard-only
41
+ // and free of cursor positioning so screen readers track it as plain output.
42
+
43
+ function waitForEnter(input, output, colors) {
44
+ return new Promise((resolve) => {
45
+ output.write(`${colors.dim}Press Enter for the next step…${colors.reset}`);
46
+ const onData = () => {
47
+ input.removeListener('data', onData);
48
+ input.pause?.();
49
+ output.write('\n');
50
+ resolve();
51
+ };
52
+ input.resume?.();
53
+ input.once('data', onData);
54
+ });
55
+ }
56
+
57
+ export async function renderTour({
58
+ script,
59
+ output = process.stdout,
60
+ input = process.stdin,
61
+ env = process.env,
62
+ accessible = false,
63
+ plain = false,
64
+ skipInput = false,
65
+ color,
66
+ } = {}) {
67
+ if (!script || !Array.isArray(script.steps)) {
68
+ return { ok: false, steps: 0, message: 'No demo script to tour' };
69
+ }
70
+
71
+ const linear = accessible || plain;
72
+ const useColor = color !== undefined ? color : (!linear);
73
+ const colors = resolveColors({ enabled: useColor, stream: output, env });
74
+ const width = termWidth(output);
75
+ const total = script.steps.length;
76
+
77
+ // Linear and headless contexts never block on input; interactivity requires a
78
+ // TTY on both ends so a piped or CI run advances on its own.
79
+ const interactive = !skipInput && !linear && Boolean(input?.isTTY) && Boolean(output?.isTTY);
80
+
81
+ renderHeader(output, script, colors, width, total);
82
+
83
+ for (let i = 0; i < total; i += 1) {
84
+ renderStep(output, script.steps[i], i + 1, total, colors, width);
85
+ if (interactive && i < total - 1) {
86
+ await waitForEnter(input, output, colors);
87
+ }
88
+ }
89
+
90
+ output.write(`\n${colors.bold}Tour complete.${colors.reset} ${colors.dim}Run any step above in OpenCode, or run \`construct demo ${script.name}\` to drive it live.${colors.reset}\n`);
91
+ return { ok: true, steps: total, surface: 'tour', accessible: linear };
92
+ }