@geraldmaron/construct 1.1.0 → 1.2.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 (425) hide show
  1. package/README.md +30 -18
  2. package/bin/construct +392 -91
  3. package/bin/construct-postinstall.mjs +30 -2
  4. package/examples/distribution/README.md +42 -0
  5. package/examples/distribution/manifest.json +53 -0
  6. package/examples/distribution/sources/adr.md +84 -0
  7. package/examples/distribution/sources/deck-one-pager.md +65 -0
  8. package/examples/distribution/sources/prd-platform.md +161 -0
  9. package/examples/distribution/sources/research-brief.md +80 -0
  10. package/examples/distribution/sources/rfc-platform.md +82 -0
  11. package/examples/distribution/sources/runbook.md +103 -0
  12. package/examples/distribution/sources/strategy.md +88 -0
  13. package/lib/adapters-sync.mjs +67 -0
  14. package/lib/artifact-gate-notice.mjs +38 -0
  15. package/lib/artifact-manifest.mjs +112 -0
  16. package/lib/artifact-release-gate.mjs +171 -0
  17. package/lib/artifact-reviewers.mjs +68 -0
  18. package/lib/artifact-type-from-path.mjs +79 -0
  19. package/lib/audit-skills.mjs +4 -0
  20. package/lib/audit-specialists.mjs +285 -0
  21. package/lib/audit-trail.mjs +77 -10
  22. package/lib/auto-docs.mjs +87 -18
  23. package/lib/beads/drift.mjs +27 -6
  24. package/lib/beads-client.mjs +49 -121
  25. package/lib/beads-optimistic.mjs +5 -12
  26. package/lib/brand-fonts.mjs +93 -0
  27. package/lib/brand-prose.mjs +214 -0
  28. package/lib/brand-tokens.mjs +92 -0
  29. package/lib/bridges/copilot-proxy.mjs +13 -26
  30. package/lib/capability-ledger.mjs +156 -0
  31. package/lib/certification/artifact-fixtures.mjs +132 -0
  32. package/lib/certification/artifact-gates.mjs +63 -0
  33. package/lib/certification/artifact-provenance.mjs +97 -0
  34. package/lib/certification/canonical-scenarios.mjs +78 -0
  35. package/lib/certification/cli.mjs +191 -0
  36. package/lib/certification/dashboard-api.mjs +71 -0
  37. package/lib/certification/demo-parity.mjs +116 -0
  38. package/lib/certification/document-io-fixtures.mjs +246 -0
  39. package/lib/certification/document-workflow.mjs +97 -0
  40. package/lib/certification/eval-bridge.mjs +77 -0
  41. package/lib/certification/model-routing.mjs +149 -0
  42. package/lib/certification/prompt-budget.mjs +119 -0
  43. package/lib/certification/rc-gate.mjs +206 -0
  44. package/lib/certification/real-llm-scenarios.mjs +303 -0
  45. package/lib/certification/role-cards.mjs +113 -0
  46. package/lib/certification/role-overlays.mjs +117 -0
  47. package/lib/certification/run.mjs +122 -0
  48. package/lib/certification/runner.mjs +323 -0
  49. package/lib/certification/scenarios.mjs +51 -0
  50. package/lib/certification/skill-inventory.mjs +289 -0
  51. package/lib/certification/skill-scenarios.mjs +147 -0
  52. package/lib/certification/specialist-contracts.mjs +85 -0
  53. package/lib/certification/specialist-scenarios.mjs +175 -0
  54. package/lib/certification/stale-impact.mjs +146 -0
  55. package/lib/certification/status.mjs +252 -0
  56. package/lib/certification/store.mjs +77 -0
  57. package/lib/chat/cli.mjs +333 -0
  58. package/lib/chat/command-suggest.mjs +161 -0
  59. package/lib/chat/commands.mjs +215 -0
  60. package/lib/chat/config.mjs +142 -0
  61. package/lib/chat/context-compactor.mjs +250 -0
  62. package/lib/chat/context-continuation.mjs +253 -0
  63. package/lib/chat/continuation-source.mjs +58 -0
  64. package/lib/chat/demo-guide.mjs +61 -0
  65. package/lib/chat/design-tokens.mjs +91 -0
  66. package/lib/chat/desktop-binary.mjs +79 -0
  67. package/lib/chat/desktop-build.mjs +130 -0
  68. package/lib/chat/desktop-launcher.mjs +133 -0
  69. package/lib/chat/evidence.mjs +145 -0
  70. package/lib/chat/export.mjs +74 -0
  71. package/lib/chat/harness/driver.mjs +91 -0
  72. package/lib/chat/list-picker.mjs +112 -0
  73. package/lib/chat/model-picker.mjs +356 -0
  74. package/lib/chat/openrouter-fallback.mjs +151 -0
  75. package/lib/chat/permission-prompt.mjs +33 -0
  76. package/lib/chat/picker-catalog.mjs +45 -0
  77. package/lib/chat/policy-telemetry.mjs +34 -0
  78. package/lib/chat/present.mjs +245 -0
  79. package/lib/chat/session-context.mjs +39 -0
  80. package/lib/chat/session-persist.mjs +73 -0
  81. package/lib/chat/session-restore.mjs +71 -0
  82. package/lib/chat/session-settings.mjs +53 -0
  83. package/lib/chat/system-prompt.mjs +52 -0
  84. package/lib/chat/transparency.mjs +93 -0
  85. package/lib/chat/tui/color-scheme.mjs +42 -0
  86. package/lib/chat/tui/markdown.mjs +123 -0
  87. package/lib/chat/tui/presentation.mjs +100 -0
  88. package/lib/chat/tui/render.mjs +500 -0
  89. package/lib/chat/tui/turn-block.mjs +284 -0
  90. package/lib/chat/tui/turn-present.mjs +18 -0
  91. package/lib/chat/tui/turn-state.mjs +88 -0
  92. package/lib/chat/tui/usage.mjs +122 -0
  93. package/lib/chat/web-commands.mjs +146 -0
  94. package/lib/chat/web-launcher.mjs +63 -0
  95. package/lib/chat/web-picker-keys.mjs +46 -0
  96. package/lib/chat/web-session.mjs +159 -0
  97. package/lib/cli-commands.mjs +232 -11
  98. package/lib/comment-lint.mjs +6 -4
  99. package/lib/config/schema.mjs +36 -5
  100. package/lib/contract-schemas/decision.json +50 -0
  101. package/lib/contract-schemas/implementation.json +51 -0
  102. package/lib/contract-schemas/review-report.json +32 -0
  103. package/lib/contract-schemas/test-report.json +43 -0
  104. package/lib/contracts/construct-handoff.mjs +60 -0
  105. package/lib/contracts/validate.mjs +32 -3
  106. package/lib/contracts/violation-log.mjs +58 -5
  107. package/lib/dashboard-demo.mjs +71 -0
  108. package/lib/dashboard-static.mjs +19 -5
  109. package/lib/decisions/registry.mjs +5 -3
  110. package/lib/deck-export-pptx.mjs +1152 -0
  111. package/lib/demo-recording.mjs +142 -0
  112. package/lib/demo-script.mjs +114 -0
  113. package/lib/demo-surface.mjs +249 -0
  114. package/lib/demo.mjs +703 -0
  115. package/lib/diagram-export.mjs +192 -0
  116. package/lib/diagram.mjs +302 -0
  117. package/lib/docs-verify.mjs +25 -7
  118. package/lib/doctor/index.mjs +4 -2
  119. package/lib/doctor/project-adapters.mjs +44 -0
  120. package/lib/doctor/watchers/cx-budget.mjs +98 -0
  121. package/lib/doctor/watchers/graph-staleness.mjs +52 -0
  122. package/lib/doctor/watchers/process-pressure.mjs +4 -3
  123. package/lib/doctor/watchers/service-health.mjs +1 -1
  124. package/lib/document-export.mjs +360 -36
  125. package/lib/document-extract.mjs +88 -0
  126. package/lib/document-ingest.mjs +32 -18
  127. package/lib/embed/cli.mjs +2 -2
  128. package/lib/embed/daemon.mjs +18 -46
  129. package/lib/embed/semantic.mjs +5 -2
  130. package/lib/embed/worker.mjs +2 -2
  131. package/lib/embedded-contract/model-resolve.mjs +10 -9
  132. package/lib/env-config.mjs +26 -0
  133. package/lib/evals/dataset.mjs +137 -0
  134. package/lib/evals/gates.mjs +175 -0
  135. package/lib/graph/build-co-change.mjs +54 -0
  136. package/lib/graph/build-from-registry.mjs +136 -0
  137. package/lib/graph/build-import-graph.mjs +153 -0
  138. package/lib/graph/cli.mjs +110 -0
  139. package/lib/graph/impact-cli.mjs +89 -0
  140. package/lib/graph/impact.mjs +108 -0
  141. package/lib/graph/staleness.mjs +44 -0
  142. package/lib/graph/store.mjs +172 -0
  143. package/lib/handoffs/cleanup.mjs +1 -1
  144. package/lib/health-check.mjs +90 -76
  145. package/lib/hooks/artifact-release-gate.mjs +43 -0
  146. package/lib/hooks/audit-trail.mjs +14 -28
  147. package/lib/hooks/brand-prose-lint.mjs +38 -0
  148. package/lib/hooks/graph-impact-advisory.mjs +62 -0
  149. package/lib/hooks/session-optimize.mjs +84 -207
  150. package/lib/hooks/session-start.mjs +4 -5
  151. package/lib/host-disposition.mjs +7 -0
  152. package/lib/improvement/cli.mjs +189 -0
  153. package/lib/improvement/controller.mjs +137 -0
  154. package/lib/improvement/proposal.mjs +120 -0
  155. package/lib/improvement/specialist-loop.mjs +192 -0
  156. package/lib/improvement/store.mjs +89 -0
  157. package/lib/improvement/surface.mjs +219 -0
  158. package/lib/ingest-tooling.mjs +97 -0
  159. package/lib/init/doc-lanes.mjs +165 -0
  160. package/lib/init-docs.mjs +31 -178
  161. package/lib/init-unified.mjs +27 -97
  162. package/lib/init-update-guide.mjs +102 -0
  163. package/lib/init-update.mjs +32 -1
  164. package/lib/init.mjs +8 -0
  165. package/lib/install/desktop-binary-download.mjs +85 -0
  166. package/lib/install/legacy-global-cleanup.mjs +189 -0
  167. package/lib/intake/daemon.mjs +2 -0
  168. package/lib/intake/git-queue.mjs +9 -8
  169. package/lib/intake/queue.mjs +2 -1
  170. package/lib/intake/session-prelude.mjs +90 -1
  171. package/lib/libreoffice-export.mjs +97 -0
  172. package/lib/logging/rotate.mjs +9 -0
  173. package/lib/maintenance/docker-reclaim.mjs +206 -0
  174. package/lib/mcp/external-schema-cost.mjs +74 -0
  175. package/lib/mcp/server.mjs +84 -9
  176. package/lib/mcp/stdio-mcp-probe.mjs +188 -0
  177. package/lib/mcp/tool-budget.mjs +55 -4
  178. package/lib/mcp/tools/document.mjs +18 -4
  179. package/lib/mcp/tools/skills.mjs +32 -3
  180. package/lib/mcp/tools/workflow.mjs +13 -1
  181. package/lib/model-free-selector.mjs +18 -0
  182. package/lib/model-registry.mjs +13 -229
  183. package/lib/model-router.mjs +425 -93
  184. package/lib/models/behavior-matrix.mjs +289 -0
  185. package/lib/models/catalog.mjs +209 -0
  186. package/lib/models/execution-capability-profile.mjs +196 -0
  187. package/lib/models/execution-policy.mjs +307 -0
  188. package/lib/models/provider-poll.mjs +383 -0
  189. package/lib/npm-spawn-env.mjs +17 -0
  190. package/lib/ollama/installed-models.mjs +129 -0
  191. package/lib/oracle/actions.mjs +187 -0
  192. package/lib/oracle/artifact-gate.mjs +99 -0
  193. package/lib/oracle/cli.mjs +204 -0
  194. package/lib/oracle/daemon-entry.mjs +14 -0
  195. package/lib/oracle/dispatch.mjs +81 -0
  196. package/lib/oracle/execute.mjs +143 -0
  197. package/lib/oracle/gaps.mjs +76 -0
  198. package/lib/oracle/index.mjs +84 -0
  199. package/lib/oracle/issues.mjs +164 -0
  200. package/lib/oracle/org-graph.mjs +170 -0
  201. package/lib/oracle/policy.mjs +80 -0
  202. package/lib/oracle/read-model.mjs +398 -0
  203. package/lib/oracle/reconcile.mjs +191 -0
  204. package/lib/oracle/routing.mjs +89 -0
  205. package/lib/oracle/synthesize.mjs +384 -0
  206. package/lib/oracle/verdicts.mjs +51 -0
  207. package/lib/orchestration/worker.mjs +88 -27
  208. package/lib/orchestration-policy.mjs +50 -0
  209. package/lib/parity.mjs +96 -2
  210. package/lib/persona-sections.mjs +66 -0
  211. package/lib/playwright-demo.mjs +252 -0
  212. package/lib/project-init-shared.mjs +4 -1
  213. package/lib/prompt-composer.js +9 -4
  214. package/lib/prompt-validation-contract.mjs +24 -0
  215. package/lib/provider-capabilities.js +57 -11
  216. package/lib/providers/contract/adapters/confluence/index.mjs +181 -0
  217. package/lib/providers/contract/adapters/git/index.mjs +115 -0
  218. package/lib/providers/contract/adapters/github/index.mjs +166 -0
  219. package/lib/providers/contract/adapters/jira/index.mjs +187 -0
  220. package/lib/providers/contract/adapters/slack/index.mjs +175 -0
  221. package/lib/providers/contract/contract-tests.mjs +57 -0
  222. package/lib/providers/contract/errors.mjs +48 -0
  223. package/lib/providers/contract/interface.mjs +50 -0
  224. package/lib/providers/contract/registry.mjs +102 -0
  225. package/lib/providers/copilot-auth.mjs +297 -0
  226. package/lib/providers/credential-bootstrap.mjs +178 -0
  227. package/lib/providers/credential-catalog.mjs +46 -0
  228. package/lib/providers/credential-sources.mjs +63 -0
  229. package/lib/providers/creds.mjs +5 -2
  230. package/lib/providers/op-run.mjs +59 -0
  231. package/lib/providers/secret-resolver.mjs +159 -0
  232. package/lib/publish-template.mjs +163 -0
  233. package/lib/publish-tooling.mjs +119 -0
  234. package/lib/publish.mjs +305 -0
  235. package/lib/registry/cli.mjs +82 -0
  236. package/lib/registry/consolidation.mjs +147 -0
  237. package/lib/registry/generate-docs.mjs +75 -0
  238. package/lib/registry/skill-verification.mjs +57 -0
  239. package/lib/registry/surface-map.mjs +76 -0
  240. package/lib/registry/validate.mjs +135 -0
  241. package/lib/resources/budget.mjs +82 -0
  242. package/lib/resources/process-budget.mjs +45 -0
  243. package/lib/rules-delivery.mjs +9 -2
  244. package/lib/rules-read.mjs +26 -0
  245. package/lib/runtime-env.mjs +23 -0
  246. package/lib/runtime-pressure.mjs +51 -7
  247. package/lib/schema-infer.mjs +13 -2
  248. package/lib/server/chat-loop.mjs +622 -0
  249. package/lib/server/demo-preview.mjs +63 -0
  250. package/lib/server/index.mjs +229 -8
  251. package/lib/server/langfuse-login.mjs +3 -3
  252. package/lib/service-manager.mjs +61 -9
  253. package/lib/session-store.mjs +1 -1
  254. package/lib/setup.mjs +102 -2
  255. package/lib/specialists/prompt-schema.mjs +19 -10
  256. package/lib/specialists/roster.mjs +43 -0
  257. package/lib/specialists/scaffold.mjs +56 -0
  258. package/lib/storage/backend.mjs +6 -6
  259. package/lib/storage/embeddings-local.mjs +6 -3
  260. package/lib/storage/file-lock.mjs +18 -11
  261. package/lib/storage/hybrid-query.mjs +7 -4
  262. package/lib/storage/state-source.mjs +5 -5
  263. package/lib/storage/sync.mjs +2 -3
  264. package/lib/telemetry/rule-calls.mjs +52 -0
  265. package/lib/template-registry.mjs +2 -2
  266. package/lib/templates/visual-requirements.mjs +27 -51
  267. package/lib/test-corpus-inventory.mjs +313 -0
  268. package/lib/uninstall/uninstall.mjs +19 -7
  269. package/lib/update.mjs +12 -0
  270. package/lib/upgrade.mjs +14 -0
  271. package/lib/wireframe.mjs +20 -14
  272. package/lib/worker/run.mjs +17 -4
  273. package/lib/worker/trace.mjs +11 -3
  274. package/package.json +29 -13
  275. package/personas/construct.md +13 -13
  276. package/platforms/claude/settings.template.json +36 -0
  277. package/rules/common/patterns.md +1 -1
  278. package/rules/common/release-gates.md +4 -3
  279. package/scripts/sync-specialists.mjs +187 -60
  280. package/skills/devops/data-engineering.md +1 -1
  281. package/skills/docs/adr-workflow.md +1 -0
  282. package/skills/docs/backlog-proposal-workflow.md +1 -0
  283. package/skills/docs/codebase-research-workflow.md +40 -0
  284. package/skills/docs/customer-profile-workflow.md +1 -0
  285. package/skills/docs/document-ingest-workflow.md +1 -0
  286. package/skills/docs/evidence-ingest-workflow.md +1 -0
  287. package/skills/docs/init-docs.md +2 -2
  288. package/skills/docs/init-project.md +8 -3
  289. package/skills/docs/prd-workflow.md +24 -1
  290. package/skills/docs/prfaq-workflow.md +1 -0
  291. package/skills/docs/product-intelligence-workflow.md +1 -0
  292. package/skills/docs/product-signal-workflow.md +1 -0
  293. package/skills/docs/research-workflow.md +54 -37
  294. package/skills/docs/runbook-workflow.md +1 -0
  295. package/skills/docs/strategy-workflow.md +1 -0
  296. package/skills/docs/user-research-workflow.md +40 -0
  297. package/skills/operating/orchestration-reference.md +1 -1
  298. package/skills/roles/architect.md +5 -0
  299. package/skills/roles/operator.docs.md +4 -0
  300. package/skills/routing.md +4 -2
  301. package/specialists/artifact-manifest.json +480 -0
  302. package/specialists/artifact-manifest.schema.json +53 -0
  303. package/specialists/audit-enrichments.json +454 -0
  304. package/specialists/contracts.json +37 -25
  305. package/specialists/prompts/_shared/validation-contract.md +26 -0
  306. package/specialists/prompts/cx-accessibility.md +21 -2
  307. package/specialists/prompts/cx-ai-engineer.md +24 -1
  308. package/specialists/prompts/cx-architect.md +4 -1
  309. package/specialists/prompts/cx-business-strategist.md +23 -2
  310. package/specialists/prompts/cx-data-analyst.md +22 -1
  311. package/specialists/prompts/cx-data-engineer.md +23 -2
  312. package/specialists/prompts/cx-debugger.md +21 -2
  313. package/specialists/prompts/cx-designer.md +26 -3
  314. package/specialists/prompts/cx-devil-advocate.md +19 -2
  315. package/specialists/prompts/cx-docs-keeper.md +28 -1
  316. package/specialists/prompts/cx-engineer.md +22 -1
  317. package/specialists/prompts/cx-evaluator.md +18 -1
  318. package/specialists/prompts/cx-explorer.md +21 -2
  319. package/specialists/prompts/cx-legal-compliance.md +21 -2
  320. package/specialists/prompts/cx-operations.md +21 -2
  321. package/specialists/prompts/cx-oracle.md +94 -0
  322. package/specialists/prompts/cx-orchestrator.md +20 -1
  323. package/specialists/prompts/cx-platform-engineer.md +25 -2
  324. package/specialists/prompts/cx-product-manager.md +32 -1
  325. package/specialists/prompts/cx-qa.md +24 -2
  326. package/specialists/prompts/cx-rd-lead.md +23 -2
  327. package/specialists/prompts/cx-release-manager.md +23 -2
  328. package/specialists/prompts/cx-researcher.md +22 -2
  329. package/specialists/prompts/cx-reviewer.md +18 -1
  330. package/specialists/prompts/cx-security.md +22 -2
  331. package/specialists/prompts/cx-sre.md +30 -3
  332. package/specialists/prompts/cx-test-automation.md +7 -1
  333. package/specialists/prompts/cx-trace-reviewer.md +22 -3
  334. package/specialists/prompts/cx-ux-researcher.md +21 -2
  335. package/specialists/registry.json +52 -173
  336. package/specialists/tone-profiles.json +42 -0
  337. package/templates/demos/playwright/demo-recording.config.mjs +47 -0
  338. package/templates/demos/recordings/agentic-platforms-prd.json +19 -0
  339. package/templates/demos/scripts/agentic-platforms-prd.json +44 -0
  340. package/templates/demos/specs/_helpers/scroll-artifact.ts +89 -0
  341. package/templates/demos/tapes/agentic-platforms-prd.tape +49 -0
  342. package/templates/demos/tapes/resource-guard-rails.tape +49 -0
  343. package/templates/demos/vhs/construct-cockpit.json +24 -0
  344. package/templates/distribution/construct-brand.typ +446 -0
  345. package/templates/distribution/construct-decision.typ +38 -0
  346. package/templates/distribution/construct-deck.html +95 -0
  347. package/templates/distribution/construct-pdf.typ +38 -0
  348. package/templates/distribution/construct-prd.typ +38 -0
  349. package/templates/distribution/construct-research.typ +38 -0
  350. package/templates/distribution/construct-web.html +92 -0
  351. package/templates/distribution/fonts/Geist-Bold.ttf +0 -0
  352. package/templates/distribution/fonts/Geist-Medium.ttf +0 -0
  353. package/templates/distribution/fonts/Geist-Regular.ttf +0 -0
  354. package/templates/distribution/fonts/Geist-SemiBold.ttf +0 -0
  355. package/templates/distribution/fonts/GeistMono-Medium.ttf +0 -0
  356. package/templates/distribution/fonts/GeistMono-Regular.ttf +0 -0
  357. package/templates/distribution/fonts/GeistMono-SemiBold.ttf +0 -0
  358. package/templates/distribution/fonts/IBMPlexMono-Regular.otf +0 -0
  359. package/templates/distribution/fonts/JetBrainsMono-Medium.ttf +0 -0
  360. package/templates/distribution/fonts/JetBrainsMono-Regular.ttf +0 -0
  361. package/templates/distribution/fonts/JetBrainsMono-SemiBold.ttf +0 -0
  362. package/templates/distribution/fonts/PlusJakartaSans-Bold.ttf +0 -0
  363. package/templates/distribution/fonts/PlusJakartaSans-Medium.ttf +0 -0
  364. package/templates/distribution/fonts/PlusJakartaSans-Regular.ttf +0 -0
  365. package/templates/distribution/fonts/PlusJakartaSans-SemiBold.ttf +0 -0
  366. package/templates/distribution/fonts/README.md +36 -0
  367. package/templates/distribution/fonts/SpaceGrotesk-Variable.ttf +0 -0
  368. package/templates/distribution/fonts/handwritten/Caveat.ttf +0 -0
  369. package/templates/distribution/fonts/legacy/IBMPlexMono-Regular.otf +0 -0
  370. package/templates/distribution/fonts/legacy/Inter-Medium.otf +0 -0
  371. package/templates/distribution/fonts/legacy/Inter-Regular.otf +0 -0
  372. package/templates/distribution/fonts/legacy/Inter-SemiBold.otf +0 -0
  373. package/templates/distribution/fonts/legacy/InterDisplay-SemiBold.otf +0 -0
  374. package/templates/distribution/fonts/legacy/SourceSerif4-Regular.otf +0 -0
  375. package/templates/distribution/fonts/legacy/SourceSerif4-Semibold.otf +0 -0
  376. package/templates/distribution/icons/activity.svg +15 -0
  377. package/templates/distribution/icons/alert-triangle.svg +17 -0
  378. package/templates/distribution/icons/book-open.svg +16 -0
  379. package/templates/distribution/icons/bot.svg +20 -0
  380. package/templates/distribution/icons/brain.svg +22 -0
  381. package/templates/distribution/icons/circle-check.svg +16 -0
  382. package/templates/distribution/icons/clipboard-check.svg +17 -0
  383. package/templates/distribution/icons/cpu.svg +28 -0
  384. package/templates/distribution/icons/database.svg +17 -0
  385. package/templates/distribution/icons/eye.svg +16 -0
  386. package/templates/distribution/icons/file-text.svg +19 -0
  387. package/templates/distribution/icons/gauge.svg +16 -0
  388. package/templates/distribution/icons/git-branch.svg +17 -0
  389. package/templates/distribution/icons/key.svg +17 -0
  390. package/templates/distribution/icons/layers.svg +17 -0
  391. package/templates/distribution/icons/list-checks.svg +19 -0
  392. package/templates/distribution/icons/lock.svg +16 -0
  393. package/templates/distribution/icons/message-square.svg +15 -0
  394. package/templates/distribution/icons/network.svg +19 -0
  395. package/templates/distribution/icons/route.svg +17 -0
  396. package/templates/distribution/icons/scale.svg +19 -0
  397. package/templates/distribution/icons/search.svg +16 -0
  398. package/templates/distribution/icons/send.svg +16 -0
  399. package/templates/distribution/icons/server.svg +18 -0
  400. package/templates/distribution/icons/shield-check.svg +16 -0
  401. package/templates/distribution/icons/users.svg +18 -0
  402. package/templates/distribution/icons/webhook.svg +17 -0
  403. package/templates/distribution/icons/workflow.svg +17 -0
  404. package/templates/distribution/icons/wrench.svg +15 -0
  405. package/templates/distribution/run.mjs +18 -1
  406. package/templates/docs/adr.md +7 -0
  407. package/templates/docs/construct_guide.md +10 -10
  408. package/templates/docs/customer-profile.md +4 -0
  409. package/templates/docs/one-pager.md +4 -0
  410. package/templates/docs/postmortem.md +31 -0
  411. package/templates/docs/prd-platform.md +19 -0
  412. package/templates/docs/prd.md +15 -0
  413. package/templates/docs/prfaq.md +7 -0
  414. package/templates/docs/qa-strategy.md +103 -0
  415. package/templates/docs/research-brief.md +8 -0
  416. package/templates/docs/rfc-platform.md +12 -0
  417. package/templates/docs/test-plan.md +7 -0
  418. package/lib/ingest/chunker.mjs +0 -94
  419. package/lib/ingest/pipeline.mjs +0 -53
  420. package/lib/ingest/store.mjs +0 -82
  421. package/lib/mode-commands.mjs +0 -122
  422. package/lib/policy/unified-gates.mjs +0 -96
  423. package/lib/profiles/validate-custom.mjs +0 -114
  424. package/lib/services/telemetry-backend.mjs +0 -177
  425. package/lib/storage/fusion.mjs +0 -95
@@ -0,0 +1,142 @@
1
+ /**
2
+ * lib/demo-recording.mjs — load and validate Playwright recording manifests.
3
+ *
4
+ * Search order: .cx/demos/recordings/ then templates/demos/recordings/. Legacy
5
+ * dashboardDemo scripts synthesize a recording manifest when no JSON exists.
6
+ */
7
+
8
+ import fs from 'node:fs';
9
+ import path from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ import { loadDemoScript } from './demo-script.mjs';
12
+
13
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
14
+ const REPO_ROOT = path.resolve(HERE, '..');
15
+
16
+ export function demoRecordingSearchDirs({ cwd = process.cwd(), repoRoot = REPO_ROOT } = {}) {
17
+ return [
18
+ path.join(cwd, '.cx', 'demos', 'recordings'),
19
+ path.join(repoRoot, 'templates', 'demos', 'recordings'),
20
+ ];
21
+ }
22
+
23
+ export function resolveDemoRecordingPath(name, { cwd = process.cwd(), repoRoot = REPO_ROOT } = {}) {
24
+ for (const dir of demoRecordingSearchDirs({ cwd, repoRoot })) {
25
+ const candidate = path.join(dir, `${name}.json`);
26
+ if (fs.existsSync(candidate)) return candidate;
27
+ }
28
+ return null;
29
+ }
30
+
31
+ export function listDemoRecordings({ cwd = process.cwd(), repoRoot = REPO_ROOT } = {}) {
32
+ const seen = new Set();
33
+ const out = [];
34
+ for (const dir of demoRecordingSearchDirs({ cwd, repoRoot })) {
35
+ if (!fs.existsSync(dir)) continue;
36
+ for (const file of fs.readdirSync(dir).filter((f) => f.endsWith('.json')).sort()) {
37
+ const name = path.basename(file, '.json');
38
+ if (seen.has(name)) continue;
39
+ seen.add(name);
40
+ out.push(name);
41
+ }
42
+ }
43
+ return out.sort();
44
+ }
45
+
46
+ export function normalizeArtifactReveal(raw) {
47
+ if (!raw || typeof raw !== 'object') return null;
48
+ const file = raw.file || raw.path;
49
+ if (!file) return null;
50
+ return {
51
+ mode: raw.mode || 'constructPreview',
52
+ staticDir: raw.staticDir || '.',
53
+ file,
54
+ scroll: raw.scroll !== false,
55
+ };
56
+ }
57
+
58
+ function validateRecording(raw, name) {
59
+ const errors = [];
60
+ if (!raw || typeof raw !== 'object') errors.push('recording must be an object');
61
+ if (raw.engine && raw.engine !== 'playwright') errors.push(`unsupported engine: ${raw.engine}`);
62
+ if (!raw.spec) errors.push('spec is required');
63
+ if (!raw.skipWebServer && !raw.webServer && !raw.baseUrl) {
64
+ errors.push('baseUrl or webServer required when skipWebServer is false');
65
+ }
66
+ return {
67
+ ok: errors.length === 0,
68
+ errors,
69
+ recording: errors.length === 0 ? normalizeRecording(raw, name) : null,
70
+ };
71
+ }
72
+
73
+ export function normalizeRecording(raw, name) {
74
+ return {
75
+ name: raw.name || name,
76
+ title: raw.title || name,
77
+ engine: raw.engine || 'playwright',
78
+ workspace: raw.workspace || '.',
79
+ spec: raw.spec,
80
+ baseUrl: raw.baseUrl || raw.webServer?.url || null,
81
+ webServer: raw.webServer || null,
82
+ skipWebServer: Boolean(raw.skipWebServer),
83
+ project: raw.project || null,
84
+ playwrightConfig: raw.playwrightConfig || null,
85
+ artifactReveal: normalizeArtifactReveal(raw.artifactReveal),
86
+ output: raw.output || { format: 'mp4', path: `.cx/demos/${name}.mp4` },
87
+ timeout: raw.timeout || null,
88
+ sourcePath: raw.sourcePath || null,
89
+ _legacy: Boolean(raw._legacy),
90
+ };
91
+ }
92
+
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
+ export function loadDemoRecording(name, { cwd = process.cwd(), repoRoot = REPO_ROOT } = {}) {
113
+ 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
+ }
123
+ }
124
+ return recordingFromDemoScript(name, { cwd, repoRoot });
125
+ }
126
+
127
+ export function loadDemoRecordingValidated(name, opts = {}) {
128
+ const filePath = resolveDemoRecordingPath(name, opts);
129
+ if (!filePath) {
130
+ const legacy = recordingFromDemoScript(name, opts);
131
+ if (legacy) return { ok: true, recording: legacy };
132
+ return { ok: false, errors: [`recording not found: ${name}`] };
133
+ }
134
+ try {
135
+ const raw = JSON.parse(fs.readFileSync(filePath, 'utf8'));
136
+ const validated = validateRecording(raw, name);
137
+ if (!validated.ok) return { ok: false, errors: validated.errors, sourcePath: filePath };
138
+ return { ok: true, recording: { ...validated.recording, sourcePath: filePath } };
139
+ } catch (err) {
140
+ return { ok: false, errors: [err.message || 'invalid JSON'], sourcePath: filePath };
141
+ }
142
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * lib/demo-script.mjs — load committed demo scripts for chat-guided demos.
3
+ *
4
+ * Scripts live in templates/demos/scripts/ (shipped) and .cx/demos/scripts/
5
+ * (project overrides). Each script defines steps, system overlay, and optional
6
+ * tape/dashboard fallbacks when chat is unavailable.
7
+ */
8
+
9
+ import fs from 'node:fs';
10
+ import path from 'node:path';
11
+ import { fileURLToPath } from 'node:url';
12
+
13
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
14
+ const REPO_ROOT = path.resolve(HERE, '..');
15
+
16
+ export function demoScriptSearchDirs({ cwd = process.cwd(), repoRoot = REPO_ROOT } = {}) {
17
+ return [
18
+ path.join(cwd, '.cx', 'demos', 'scripts'),
19
+ path.join(repoRoot, 'templates', 'demos', 'scripts'),
20
+ ];
21
+ }
22
+
23
+ export function listDemoScripts({ cwd = process.cwd(), repoRoot = REPO_ROOT } = {}) {
24
+ const seen = new Set();
25
+ const out = [];
26
+ for (const dir of demoScriptSearchDirs({ cwd, repoRoot })) {
27
+ if (!fs.existsSync(dir)) continue;
28
+ for (const file of fs.readdirSync(dir).filter((f) => f.endsWith('.json')).sort()) {
29
+ const name = path.basename(file, '.json');
30
+ if (seen.has(name)) continue;
31
+ seen.add(name);
32
+ out.push(name);
33
+ }
34
+ }
35
+ return out.sort();
36
+ }
37
+
38
+ export function resolveDemoScriptPath(name, { cwd = process.cwd(), repoRoot = REPO_ROOT } = {}) {
39
+ for (const dir of demoScriptSearchDirs({ cwd, repoRoot })) {
40
+ const candidate = path.join(dir, `${name}.json`);
41
+ if (fs.existsSync(candidate)) return candidate;
42
+ }
43
+ return null;
44
+ }
45
+
46
+ export function loadDemoScript(name, { cwd = process.cwd(), repoRoot = REPO_ROOT } = {}) {
47
+ const filePath = resolveDemoScriptPath(name, { cwd, repoRoot });
48
+ if (!filePath) return null;
49
+ try {
50
+ const raw = JSON.parse(fs.readFileSync(filePath, 'utf8'));
51
+ if (!raw || typeof raw !== 'object') return null;
52
+ const steps = Array.isArray(raw.steps) ? raw.steps.filter((s) => s && (s.prompt || s.command)) : [];
53
+ const artifactReveal = raw.artifactReveal && typeof raw.artifactReveal === 'object'
54
+ ? {
55
+ mode: raw.artifactReveal.mode || 'constructPreview',
56
+ staticDir: raw.artifactReveal.staticDir || '.',
57
+ file: raw.artifactReveal.file || raw.artifactReveal.path || null,
58
+ scroll: raw.artifactReveal.scroll !== false,
59
+ }
60
+ : null;
61
+
62
+ return {
63
+ name: raw.name || name,
64
+ title: raw.title || name,
65
+ summary: raw.summary || '',
66
+ systemOverlay: raw.systemOverlay || '',
67
+ tape: raw.tape || name,
68
+ dashboardDemo: raw.dashboardDemo || null,
69
+ recording: raw.recording || null,
70
+ engine: raw.engine || null,
71
+ artifactReveal,
72
+ fallbackSurface: raw.fallbackSurface || 'tape',
73
+ fixtures: raw.fixtures || {},
74
+ steps,
75
+ sourcePath: filePath,
76
+ };
77
+ } catch {
78
+ return null;
79
+ }
80
+ }
81
+
82
+ export function formatDemoWelcome(script) {
83
+ const lines = [
84
+ `Demo: ${script.title}`,
85
+ script.summary,
86
+ '',
87
+ 'Steps (use /demo next for the next prompt, /demo steps to replay):',
88
+ ];
89
+ script.steps.forEach((step, i) => {
90
+ lines.push(` ${i + 1}. ${step.title || step.prompt?.slice(0, 60) || 'step'}`);
91
+ });
92
+ lines.push('', 'Type /demo next to see the first prompt, or describe what you want in your own words.');
93
+ return lines.filter(Boolean).join('\n');
94
+ }
95
+
96
+ export function createDemoGuide(script) {
97
+ let index = 0;
98
+ return {
99
+ script,
100
+ currentIndex: () => index,
101
+ steps: () => script.steps,
102
+ reset() { index = 0; },
103
+ next() {
104
+ if (index >= script.steps.length) return null;
105
+ const step = script.steps[index];
106
+ index += 1;
107
+ return { index, ...step };
108
+ },
109
+ peek() {
110
+ if (index >= script.steps.length) return null;
111
+ return { index: index + 1, ...script.steps[index] };
112
+ },
113
+ };
114
+ }
@@ -0,0 +1,249 @@
1
+ /**
2
+ * lib/demo-surface.mjs — chat-first demo execution with surface targeting and fallbacks.
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.
8
+ */
9
+
10
+ import { spawnSync } from 'node:child_process';
11
+ import { hasAnySecret } from './providers/secret-resolver.mjs';
12
+ import { loadConstructEnv } from './env-config.mjs';
13
+ import { loadDemoScript, formatDemoWelcome, createDemoGuide } from './demo-script.mjs';
14
+ import { loadDemoRecording } from './demo-recording.mjs';
15
+ import { recordDashboardDemo } from './dashboard-demo.mjs';
16
+ import { recordPlaywrightDemo } from './playwright-demo.mjs';
17
+
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
+ }
41
+
42
+ export function buildDemoAttemptChain(requestedSurface, { script, recording = null, interactive = true } = {}) {
43
+ const chain = [];
44
+ const push = (surface) => { if (!chain.includes(surface)) chain.push(surface); };
45
+
46
+ if (requestedSurface === 'dashboard') {
47
+ push('dashboard');
48
+ push('playwright');
49
+ push('chat');
50
+ push('web');
51
+ } else if (requestedSurface === 'playwright') {
52
+ 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
+ } else {
61
+ push('chat');
62
+ push('web');
63
+ }
64
+
65
+ if (script?.dashboardDemo && requestedSurface !== 'tape') push('dashboard');
66
+ if (recording?.engine === 'playwright' && requestedSurface !== 'tape') push('playwright');
67
+ 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
+ return chain;
78
+ }
79
+
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
+ async function tryTapeDemo(name, opts, { sourceOnly = false } = {}) {
118
+ const { runDemoRecord, resolveTapeSource, listProjectTapes } = await import('./demo.mjs');
119
+ const tapeName = loadDemoScript(name, opts)?.tape || name;
120
+ const hasTape = resolveTapeSource(tapeName, { cwd: opts.cwd })
121
+ || listProjectTapes(opts.cwd).includes(tapeName);
122
+ if (!hasTape) {
123
+ return { ok: false, surface: 'tape', soft: true, message: `No tape for ${tapeName}` };
124
+ }
125
+ const result = runDemoRecord(tapeName, {
126
+ cwd: opts.cwd,
127
+ format: opts.format || 'mp4',
128
+ out: opts.out,
129
+ sourceOnly: sourceOnly || opts.sourceOnly,
130
+ required: opts.required,
131
+ });
132
+ if (result.ok) {
133
+ return {
134
+ ok: true,
135
+ surface: 'tape',
136
+ ...result,
137
+ message: result.artifactPath
138
+ ? `Recorded tape demo to ${result.artifactPath}`
139
+ : `Tape source: ${result.tapePath}`,
140
+ };
141
+ }
142
+ return { ok: false, surface: 'tape', soft: !opts.required, ...result };
143
+ }
144
+
145
+ function tryPlaywrightDemo(name, opts) {
146
+ const recording = loadDemoRecording(name, opts);
147
+ if (!recording || recording.engine !== 'playwright') {
148
+ return { ok: false, surface: 'playwright', soft: true, message: 'No Playwright recording configured' };
149
+ }
150
+ const result = recordPlaywrightDemo(recording, {
151
+ cwd: opts.cwd,
152
+ repoRoot: opts.repoRoot,
153
+ outputPath: opts.out ? opts.out : null,
154
+ format: opts.format || 'mp4',
155
+ });
156
+ if (result.ok) return { ok: true, surface: 'playwright', ...result };
157
+ return { ok: false, surface: 'playwright', soft: true, ...result };
158
+ }
159
+
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
+ function printScriptFallback(name, opts, notices = []) {
172
+ const script = loadDemoScript(name, opts);
173
+ if (!script) {
174
+ return {
175
+ ok: false,
176
+ surface: 'script',
177
+ message: `Unknown demo: ${name}. Run \`construct demo list\` or add templates/demos/scripts/${name}.json`,
178
+ };
179
+ }
180
+ const out = opts.output || process.stdout;
181
+ for (const line of notices) out.write(`${line}\n`);
182
+ out.write(`\n${formatDemoWelcome(script)}\n\n`);
183
+ script.steps.forEach((step, i) => {
184
+ out.write(`${i + 1}. ${step.title || 'Step'}\n`);
185
+ if (step.prompt) out.write(` prompt: ${step.prompt}\n`);
186
+ if (step.command) out.write(` command: ${step.command}\n`);
187
+ out.write('\n');
188
+ });
189
+ return { ok: true, surface: 'script', message: 'Printed demo script (all surfaces unavailable)' };
190
+ }
191
+
192
+ export async function runDemoGuided(name, {
193
+ cwd = process.cwd(),
194
+ repoRoot,
195
+ surface = 'chat',
196
+ format = 'mp4',
197
+ out = null,
198
+ sourceOnly = false,
199
+ required = false,
200
+ model = null,
201
+ plain = false,
202
+ accessible = false,
203
+ web = false,
204
+ free = false,
205
+ env = process.env,
206
+ input,
207
+ output,
208
+ errorOutput,
209
+ } = {}) {
210
+ const opts = {
211
+ cwd, repoRoot, format, out, sourceOnly, required, model, plain, accessible, web, free, env, input, output, errorOutput,
212
+ };
213
+
214
+ const script = loadDemoScript(name, opts);
215
+ 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);
218
+ const chain = buildDemoAttemptChain(requested, { script, recording, interactive });
219
+ const notices = [];
220
+
221
+ for (const attempt of chain) {
222
+ 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);
226
+ else if (attempt === 'playwright') result = tryPlaywrightDemo(name, opts);
227
+ else if (attempt === 'dashboard') result = tryDashboardDemo(name, opts);
228
+ else continue;
229
+
230
+ if (result.ok) {
231
+ return { ...result, notices, demoGuide: script ? createDemoGuide(script) : null };
232
+ }
233
+ if (result.message) notices.push(`${attempt} unavailable: ${result.message}`);
234
+ if (!result.soft && !required) break;
235
+ }
236
+
237
+ return printScriptFallback(name, opts, notices);
238
+ }
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
+ }