@geraldmaron/construct 1.1.1 → 1.2.1

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 (426) hide show
  1. package/README.md +30 -19
  2. package/bin/construct +487 -87
  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 +258 -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/artifact-workflow.mjs +252 -0
  20. package/lib/audit-skills.mjs +4 -0
  21. package/lib/audit-specialists.mjs +285 -0
  22. package/lib/audit-trail.mjs +1 -1
  23. package/lib/auto-docs.mjs +90 -19
  24. package/lib/beads/drift.mjs +27 -6
  25. package/lib/beads-client.mjs +49 -121
  26. package/lib/beads-optimistic.mjs +5 -12
  27. package/lib/brand-fonts.mjs +93 -0
  28. package/lib/brand-prose.mjs +214 -0
  29. package/lib/brand-tokens.mjs +92 -0
  30. package/lib/bridges/copilot-proxy.mjs +13 -26
  31. package/lib/capability-ledger.mjs +156 -0
  32. package/lib/certification/artifact-fixtures.mjs +132 -0
  33. package/lib/certification/artifact-gates.mjs +63 -0
  34. package/lib/certification/artifact-provenance.mjs +97 -0
  35. package/lib/certification/canonical-scenarios.mjs +78 -0
  36. package/lib/certification/cli.mjs +191 -0
  37. package/lib/certification/dashboard-api.mjs +71 -0
  38. package/lib/certification/demo-parity.mjs +116 -0
  39. package/lib/certification/document-io-fixtures.mjs +246 -0
  40. package/lib/certification/document-workflow.mjs +97 -0
  41. package/lib/certification/eval-bridge.mjs +77 -0
  42. package/lib/certification/model-routing.mjs +149 -0
  43. package/lib/certification/prompt-budget.mjs +119 -0
  44. package/lib/certification/rc-gate.mjs +206 -0
  45. package/lib/certification/real-llm-scenarios.mjs +303 -0
  46. package/lib/certification/role-cards.mjs +113 -0
  47. package/lib/certification/role-overlays.mjs +117 -0
  48. package/lib/certification/run.mjs +122 -0
  49. package/lib/certification/runner.mjs +323 -0
  50. package/lib/certification/scenarios.mjs +51 -0
  51. package/lib/certification/skill-inventory.mjs +289 -0
  52. package/lib/certification/skill-scenarios.mjs +147 -0
  53. package/lib/certification/specialist-contracts.mjs +85 -0
  54. package/lib/certification/specialist-scenarios.mjs +175 -0
  55. package/lib/certification/stale-impact.mjs +146 -0
  56. package/lib/certification/status.mjs +252 -0
  57. package/lib/certification/store.mjs +77 -0
  58. package/lib/chat/cli.mjs +333 -0
  59. package/lib/chat/command-suggest.mjs +161 -0
  60. package/lib/chat/commands.mjs +215 -0
  61. package/lib/chat/config.mjs +142 -0
  62. package/lib/chat/context-compactor.mjs +250 -0
  63. package/lib/chat/context-continuation.mjs +253 -0
  64. package/lib/chat/continuation-source.mjs +58 -0
  65. package/lib/chat/demo-guide.mjs +61 -0
  66. package/lib/chat/design-tokens.mjs +91 -0
  67. package/lib/chat/desktop-binary.mjs +79 -0
  68. package/lib/chat/desktop-build.mjs +130 -0
  69. package/lib/chat/desktop-launcher.mjs +133 -0
  70. package/lib/chat/evidence.mjs +145 -0
  71. package/lib/chat/export.mjs +74 -0
  72. package/lib/chat/harness/driver.mjs +91 -0
  73. package/lib/chat/list-picker.mjs +112 -0
  74. package/lib/chat/model-picker.mjs +356 -0
  75. package/lib/chat/openrouter-fallback.mjs +151 -0
  76. package/lib/chat/permission-prompt.mjs +33 -0
  77. package/lib/chat/picker-catalog.mjs +45 -0
  78. package/lib/chat/policy-telemetry.mjs +34 -0
  79. package/lib/chat/present.mjs +246 -0
  80. package/lib/chat/session-context.mjs +39 -0
  81. package/lib/chat/session-persist.mjs +73 -0
  82. package/lib/chat/session-restore.mjs +71 -0
  83. package/lib/chat/session-settings.mjs +53 -0
  84. package/lib/chat/system-prompt.mjs +52 -0
  85. package/lib/chat/transparency.mjs +93 -0
  86. package/lib/chat/tui/color-scheme.mjs +42 -0
  87. package/lib/chat/tui/markdown.mjs +123 -0
  88. package/lib/chat/tui/presentation.mjs +100 -0
  89. package/lib/chat/tui/render.mjs +500 -0
  90. package/lib/chat/tui/turn-block.mjs +284 -0
  91. package/lib/chat/tui/turn-present.mjs +18 -0
  92. package/lib/chat/tui/turn-state.mjs +88 -0
  93. package/lib/chat/tui/usage.mjs +122 -0
  94. package/lib/chat/web-commands.mjs +146 -0
  95. package/lib/chat/web-launcher.mjs +63 -0
  96. package/lib/chat/web-picker-keys.mjs +46 -0
  97. package/lib/chat/web-session.mjs +159 -0
  98. package/lib/cli-commands.mjs +146 -15
  99. package/lib/cli-service-inventory.mjs +42 -0
  100. package/lib/comment-lint.mjs +6 -4
  101. package/lib/config/intake-policy.mjs +209 -0
  102. package/lib/config/project-config.mjs +11 -2
  103. package/lib/config/schema.mjs +86 -6
  104. package/lib/config/source-targets.mjs +311 -0
  105. package/lib/contract-schemas/decision.json +50 -0
  106. package/lib/contract-schemas/implementation.json +51 -0
  107. package/lib/contract-schemas/review-report.json +32 -0
  108. package/lib/contract-schemas/test-report.json +43 -0
  109. package/lib/contracts/construct-handoff.mjs +60 -0
  110. package/lib/contracts/validate.mjs +32 -3
  111. package/lib/contracts/violation-log.mjs +58 -5
  112. package/lib/dashboard-demo.mjs +71 -0
  113. package/lib/dashboard-static.mjs +19 -5
  114. package/lib/decisions/registry.mjs +5 -3
  115. package/lib/deck-export-pptx.mjs +1152 -0
  116. package/lib/demo-recording.mjs +142 -0
  117. package/lib/demo-script.mjs +114 -0
  118. package/lib/demo-surface.mjs +249 -0
  119. package/lib/demo.mjs +598 -140
  120. package/lib/diagram-export.mjs +192 -0
  121. package/lib/diagram.mjs +13 -11
  122. package/lib/docs-verify.mjs +25 -7
  123. package/lib/doctor/index.mjs +3 -1
  124. package/lib/doctor/project-adapters.mjs +44 -0
  125. package/lib/doctor/source-checkout.mjs +16 -0
  126. package/lib/doctor/watchers/cx-budget.mjs +98 -0
  127. package/lib/doctor/watchers/graph-staleness.mjs +52 -0
  128. package/lib/doctor/watchers/process-pressure.mjs +4 -3
  129. package/lib/document-export.mjs +384 -38
  130. package/lib/document-extract.mjs +44 -4
  131. package/lib/document-ingest.mjs +29 -20
  132. package/lib/embed/auto-sources.mjs +44 -0
  133. package/lib/embed/cli.mjs +4 -3
  134. package/lib/embed/daemon.mjs +18 -46
  135. package/lib/embed/demand-fetch.mjs +50 -19
  136. package/lib/embed/inbox.mjs +6 -10
  137. package/lib/embed/worker.mjs +4 -3
  138. package/lib/embedded-contract/index.mjs +11 -0
  139. package/lib/embedded-contract/model-resolve.mjs +10 -9
  140. package/lib/env-config.mjs +26 -0
  141. package/lib/evals/dataset.mjs +137 -0
  142. package/lib/evals/gates.mjs +175 -0
  143. package/lib/export-branding.mjs +32 -0
  144. package/lib/graph/build-co-change.mjs +54 -0
  145. package/lib/graph/build-from-registry.mjs +136 -0
  146. package/lib/graph/build-import-graph.mjs +153 -0
  147. package/lib/graph/cli.mjs +110 -0
  148. package/lib/graph/impact-cli.mjs +89 -0
  149. package/lib/graph/impact.mjs +108 -0
  150. package/lib/graph/staleness.mjs +44 -0
  151. package/lib/graph/store.mjs +172 -0
  152. package/lib/health-check.mjs +90 -76
  153. package/lib/hooks/artifact-release-gate.mjs +43 -0
  154. package/lib/hooks/brand-prose-lint.mjs +38 -0
  155. package/lib/hooks/graph-impact-advisory.mjs +62 -0
  156. package/lib/hooks/session-optimize.mjs +84 -207
  157. package/lib/hooks/session-start.mjs +18 -16
  158. package/lib/host-disposition.mjs +7 -0
  159. package/lib/improvement/cli.mjs +189 -0
  160. package/lib/improvement/controller.mjs +137 -0
  161. package/lib/improvement/proposal.mjs +120 -0
  162. package/lib/improvement/specialist-loop.mjs +192 -0
  163. package/lib/improvement/store.mjs +89 -0
  164. package/lib/improvement/surface.mjs +219 -0
  165. package/lib/ingest-tooling.mjs +97 -0
  166. package/lib/init/doc-lanes.mjs +165 -0
  167. package/lib/init-docs.mjs +31 -178
  168. package/lib/init-unified.mjs +50 -119
  169. package/lib/init-update-guide.mjs +102 -0
  170. package/lib/init-update.mjs +32 -1
  171. package/lib/init.mjs +8 -0
  172. package/lib/install/desktop-binary-download.mjs +85 -0
  173. package/lib/install/legacy-global-cleanup.mjs +189 -0
  174. package/lib/intake/constants.mjs +29 -0
  175. package/lib/intake/daemon.mjs +2 -0
  176. package/lib/intake/git-queue.mjs +9 -8
  177. package/lib/intake/intake-config.mjs +52 -105
  178. package/lib/intake/legacy-paths.mjs +5 -0
  179. package/lib/intake/queue.mjs +2 -1
  180. package/lib/intake/session-prelude.mjs +90 -1
  181. package/lib/libreoffice-export.mjs +97 -0
  182. package/lib/logging/rotate.mjs +9 -0
  183. package/lib/maintenance/docker-reclaim.mjs +206 -0
  184. package/lib/mcp/external-schema-cost.mjs +74 -0
  185. package/lib/mcp/server.mjs +103 -10
  186. package/lib/mcp/stdio-mcp-probe.mjs +188 -0
  187. package/lib/mcp/tool-budget.mjs +55 -4
  188. package/lib/mcp/tools/document.mjs +18 -4
  189. package/lib/mcp/tools/embedded-contract.mjs +14 -0
  190. package/lib/mcp/tools/skills.mjs +32 -3
  191. package/lib/mcp/tools/workflow.mjs +13 -1
  192. package/lib/model-free-selector.mjs +18 -0
  193. package/lib/model-registry.mjs +13 -229
  194. package/lib/model-router.mjs +373 -92
  195. package/lib/models/behavior-matrix.mjs +289 -0
  196. package/lib/models/catalog.mjs +209 -0
  197. package/lib/models/execution-capability-profile.mjs +196 -0
  198. package/lib/models/execution-policy.mjs +307 -0
  199. package/lib/models/provider-poll.mjs +383 -0
  200. package/lib/npm-spawn-env.mjs +17 -0
  201. package/lib/ollama/installed-models.mjs +129 -0
  202. package/lib/oracle/actions.mjs +187 -0
  203. package/lib/oracle/artifact-gate.mjs +99 -0
  204. package/lib/oracle/cli.mjs +204 -0
  205. package/lib/oracle/daemon-entry.mjs +14 -0
  206. package/lib/oracle/dispatch.mjs +81 -0
  207. package/lib/oracle/execute.mjs +143 -0
  208. package/lib/oracle/gaps.mjs +76 -0
  209. package/lib/oracle/index.mjs +84 -0
  210. package/lib/oracle/issues.mjs +164 -0
  211. package/lib/oracle/org-graph.mjs +170 -0
  212. package/lib/oracle/policy.mjs +80 -0
  213. package/lib/oracle/read-model.mjs +398 -0
  214. package/lib/oracle/reconcile.mjs +191 -0
  215. package/lib/oracle/routing.mjs +89 -0
  216. package/lib/oracle/synthesize.mjs +384 -0
  217. package/lib/oracle/verdicts.mjs +51 -0
  218. package/lib/orchestration/worker.mjs +88 -27
  219. package/lib/orchestration-policy.mjs +50 -0
  220. package/lib/parity.mjs +96 -2
  221. package/lib/playwright-demo.mjs +254 -0
  222. package/lib/project-init-shared.mjs +4 -1
  223. package/lib/prompt-composer.js +7 -3
  224. package/lib/prompt-validation-contract.mjs +24 -0
  225. package/lib/provider-capabilities.js +57 -11
  226. package/lib/providers/contract/adapters/confluence/index.mjs +181 -0
  227. package/lib/providers/contract/adapters/git/index.mjs +115 -0
  228. package/lib/providers/contract/adapters/github/index.mjs +166 -0
  229. package/lib/providers/contract/adapters/jira/index.mjs +187 -0
  230. package/lib/providers/contract/adapters/slack/index.mjs +175 -0
  231. package/lib/providers/contract/contract-tests.mjs +57 -0
  232. package/lib/providers/contract/errors.mjs +48 -0
  233. package/lib/providers/contract/interface.mjs +50 -0
  234. package/lib/providers/contract/registry.mjs +102 -0
  235. package/lib/providers/copilot-auth.mjs +297 -0
  236. package/lib/providers/credential-bootstrap.mjs +178 -0
  237. package/lib/providers/credential-catalog.mjs +46 -0
  238. package/lib/providers/credential-sources.mjs +63 -0
  239. package/lib/providers/creds.mjs +5 -2
  240. package/lib/providers/op-run.mjs +59 -0
  241. package/lib/providers/secret-resolver.mjs +159 -0
  242. package/lib/publish-template.mjs +163 -0
  243. package/lib/publish-tooling.mjs +119 -0
  244. package/lib/publish.mjs +305 -0
  245. package/lib/registry/cli.mjs +82 -0
  246. package/lib/registry/consolidation.mjs +147 -0
  247. package/lib/registry/generate-docs.mjs +75 -0
  248. package/lib/registry/skill-verification.mjs +57 -0
  249. package/lib/registry/surface-map.mjs +76 -0
  250. package/lib/registry/validate.mjs +135 -0
  251. package/lib/resources/budget.mjs +82 -0
  252. package/lib/resources/process-budget.mjs +45 -0
  253. package/lib/rules-delivery.mjs +9 -2
  254. package/lib/rules-read.mjs +26 -0
  255. package/lib/runtime-env.mjs +23 -0
  256. package/lib/runtime-pressure.mjs +51 -7
  257. package/lib/schema-infer.mjs +13 -2
  258. package/lib/server/chat-loop.mjs +622 -0
  259. package/lib/server/demo-preview.mjs +63 -0
  260. package/lib/server/index.mjs +259 -9
  261. package/lib/server/rate-limit.mjs +5 -3
  262. package/lib/server/webhook.mjs +3 -2
  263. package/lib/service-manager.mjs +60 -8
  264. package/lib/setup.mjs +70 -7
  265. package/lib/specialists/prompt-schema.mjs +19 -10
  266. package/lib/specialists/roster.mjs +43 -0
  267. package/lib/specialists/scaffold.mjs +56 -0
  268. package/lib/storage/backend.mjs +6 -6
  269. package/lib/storage/hybrid-query.mjs +7 -4
  270. package/lib/storage/state-source.mjs +5 -5
  271. package/lib/storage/sync.mjs +2 -3
  272. package/lib/telemetry/rule-calls.mjs +52 -0
  273. package/lib/template-registry.mjs +2 -2
  274. package/lib/templates/visual-requirements.mjs +27 -51
  275. package/lib/test-corpus-inventory.mjs +313 -0
  276. package/lib/uninstall/uninstall.mjs +19 -7
  277. package/lib/update.mjs +12 -0
  278. package/lib/upgrade.mjs +14 -0
  279. package/lib/wireframe.mjs +20 -14
  280. package/lib/worker/run.mjs +17 -4
  281. package/lib/worker/trace.mjs +11 -3
  282. package/package.json +28 -14
  283. package/personas/construct.md +2 -2
  284. package/platforms/claude/settings.template.json +36 -0
  285. package/rules/common/patterns.md +1 -1
  286. package/rules/common/release-gates.md +4 -3
  287. package/scripts/sync-specialists.mjs +41 -28
  288. package/skills/devops/data-engineering.md +1 -1
  289. package/skills/docs/adr-workflow.md +1 -0
  290. package/skills/docs/backlog-proposal-workflow.md +1 -0
  291. package/skills/docs/codebase-research-workflow.md +40 -0
  292. package/skills/docs/customer-profile-workflow.md +1 -0
  293. package/skills/docs/document-ingest-workflow.md +1 -0
  294. package/skills/docs/evidence-ingest-workflow.md +1 -0
  295. package/skills/docs/init-docs.md +2 -2
  296. package/skills/docs/init-project.md +7 -2
  297. package/skills/docs/prd-workflow.md +24 -1
  298. package/skills/docs/prfaq-workflow.md +1 -0
  299. package/skills/docs/product-intelligence-workflow.md +1 -0
  300. package/skills/docs/product-signal-workflow.md +1 -0
  301. package/skills/docs/research-workflow.md +54 -37
  302. package/skills/docs/runbook-workflow.md +1 -0
  303. package/skills/docs/strategy-workflow.md +1 -0
  304. package/skills/docs/user-research-workflow.md +40 -0
  305. package/skills/operating/orchestration-reference.md +1 -1
  306. package/skills/roles/architect.md +5 -0
  307. package/skills/roles/operator.docs.md +4 -0
  308. package/skills/routing.md +4 -2
  309. package/specialists/artifact-manifest.json +489 -0
  310. package/specialists/artifact-manifest.schema.json +83 -0
  311. package/specialists/audit-enrichments.json +454 -0
  312. package/specialists/contracts.json +37 -25
  313. package/specialists/prompts/_shared/validation-contract.md +26 -0
  314. package/specialists/prompts/cx-accessibility.md +21 -2
  315. package/specialists/prompts/cx-ai-engineer.md +24 -1
  316. package/specialists/prompts/cx-architect.md +4 -1
  317. package/specialists/prompts/cx-business-strategist.md +23 -2
  318. package/specialists/prompts/cx-data-analyst.md +22 -1
  319. package/specialists/prompts/cx-data-engineer.md +23 -2
  320. package/specialists/prompts/cx-debugger.md +21 -2
  321. package/specialists/prompts/cx-designer.md +26 -3
  322. package/specialists/prompts/cx-devil-advocate.md +19 -2
  323. package/specialists/prompts/cx-docs-keeper.md +28 -1
  324. package/specialists/prompts/cx-engineer.md +22 -1
  325. package/specialists/prompts/cx-evaluator.md +18 -1
  326. package/specialists/prompts/cx-explorer.md +21 -2
  327. package/specialists/prompts/cx-legal-compliance.md +21 -2
  328. package/specialists/prompts/cx-operations.md +21 -2
  329. package/specialists/prompts/cx-oracle.md +94 -0
  330. package/specialists/prompts/cx-orchestrator.md +20 -1
  331. package/specialists/prompts/cx-platform-engineer.md +25 -2
  332. package/specialists/prompts/cx-product-manager.md +32 -1
  333. package/specialists/prompts/cx-qa.md +24 -2
  334. package/specialists/prompts/cx-rd-lead.md +23 -2
  335. package/specialists/prompts/cx-release-manager.md +23 -2
  336. package/specialists/prompts/cx-researcher.md +22 -2
  337. package/specialists/prompts/cx-reviewer.md +18 -1
  338. package/specialists/prompts/cx-security.md +22 -2
  339. package/specialists/prompts/cx-sre.md +30 -3
  340. package/specialists/prompts/cx-test-automation.md +7 -1
  341. package/specialists/prompts/cx-trace-reviewer.md +22 -3
  342. package/specialists/prompts/cx-ux-researcher.md +21 -2
  343. package/specialists/registry.json +52 -173
  344. package/specialists/tone-profiles.json +42 -0
  345. package/templates/demos/playwright/demo-recording.config.mjs +47 -0
  346. package/templates/demos/recordings/agentic-platforms-prd.json +19 -0
  347. package/templates/demos/scripts/agentic-platforms-prd.json +44 -0
  348. package/templates/demos/specs/_helpers/scroll-artifact.ts +89 -0
  349. package/templates/demos/tapes/agentic-platforms-prd.tape +49 -0
  350. package/templates/demos/tapes/resource-guard-rails.tape +49 -0
  351. package/templates/demos/vhs/construct-cockpit.json +24 -0
  352. package/templates/distribution/construct-brand.typ +446 -0
  353. package/templates/distribution/construct-decision.typ +38 -0
  354. package/templates/distribution/construct-deck.html +95 -0
  355. package/templates/distribution/construct-pdf.typ +38 -0
  356. package/templates/distribution/construct-prd.typ +38 -0
  357. package/templates/distribution/construct-reference.docx +0 -0
  358. package/templates/distribution/construct-research.typ +38 -0
  359. package/templates/distribution/construct-web.html +92 -0
  360. package/templates/distribution/fonts/Geist-Bold.ttf +0 -0
  361. package/templates/distribution/fonts/Geist-Medium.ttf +0 -0
  362. package/templates/distribution/fonts/Geist-Regular.ttf +0 -0
  363. package/templates/distribution/fonts/Geist-SemiBold.ttf +0 -0
  364. package/templates/distribution/fonts/GeistMono-Medium.ttf +0 -0
  365. package/templates/distribution/fonts/GeistMono-Regular.ttf +0 -0
  366. package/templates/distribution/fonts/GeistMono-SemiBold.ttf +0 -0
  367. package/templates/distribution/fonts/IBMPlexMono-Regular.otf +0 -0
  368. package/templates/distribution/fonts/JetBrainsMono-Medium.ttf +0 -0
  369. package/templates/distribution/fonts/JetBrainsMono-Regular.ttf +0 -0
  370. package/templates/distribution/fonts/JetBrainsMono-SemiBold.ttf +0 -0
  371. package/templates/distribution/fonts/PlusJakartaSans-Bold.ttf +0 -0
  372. package/templates/distribution/fonts/PlusJakartaSans-Medium.ttf +0 -0
  373. package/templates/distribution/fonts/PlusJakartaSans-Regular.ttf +0 -0
  374. package/templates/distribution/fonts/PlusJakartaSans-SemiBold.ttf +0 -0
  375. package/templates/distribution/fonts/README.md +36 -0
  376. package/templates/distribution/fonts/SpaceGrotesk-Variable.ttf +0 -0
  377. package/templates/distribution/fonts/handwritten/Caveat.ttf +0 -0
  378. package/templates/distribution/fonts/legacy/IBMPlexMono-Regular.otf +0 -0
  379. package/templates/distribution/fonts/legacy/Inter-Medium.otf +0 -0
  380. package/templates/distribution/fonts/legacy/Inter-Regular.otf +0 -0
  381. package/templates/distribution/fonts/legacy/Inter-SemiBold.otf +0 -0
  382. package/templates/distribution/fonts/legacy/InterDisplay-SemiBold.otf +0 -0
  383. package/templates/distribution/fonts/legacy/SourceSerif4-Regular.otf +0 -0
  384. package/templates/distribution/fonts/legacy/SourceSerif4-Semibold.otf +0 -0
  385. package/templates/distribution/icons/activity.svg +15 -0
  386. package/templates/distribution/icons/alert-triangle.svg +17 -0
  387. package/templates/distribution/icons/book-open.svg +16 -0
  388. package/templates/distribution/icons/bot.svg +20 -0
  389. package/templates/distribution/icons/brain.svg +22 -0
  390. package/templates/distribution/icons/circle-check.svg +16 -0
  391. package/templates/distribution/icons/clipboard-check.svg +17 -0
  392. package/templates/distribution/icons/cpu.svg +28 -0
  393. package/templates/distribution/icons/database.svg +17 -0
  394. package/templates/distribution/icons/eye.svg +16 -0
  395. package/templates/distribution/icons/file-text.svg +19 -0
  396. package/templates/distribution/icons/gauge.svg +16 -0
  397. package/templates/distribution/icons/git-branch.svg +17 -0
  398. package/templates/distribution/icons/key.svg +17 -0
  399. package/templates/distribution/icons/layers.svg +17 -0
  400. package/templates/distribution/icons/list-checks.svg +19 -0
  401. package/templates/distribution/icons/lock.svg +16 -0
  402. package/templates/distribution/icons/message-square.svg +15 -0
  403. package/templates/distribution/icons/network.svg +19 -0
  404. package/templates/distribution/icons/route.svg +17 -0
  405. package/templates/distribution/icons/scale.svg +19 -0
  406. package/templates/distribution/icons/search.svg +16 -0
  407. package/templates/distribution/icons/send.svg +16 -0
  408. package/templates/distribution/icons/server.svg +18 -0
  409. package/templates/distribution/icons/shield-check.svg +16 -0
  410. package/templates/distribution/icons/users.svg +18 -0
  411. package/templates/distribution/icons/webhook.svg +17 -0
  412. package/templates/distribution/icons/workflow.svg +17 -0
  413. package/templates/distribution/icons/wrench.svg +15 -0
  414. package/templates/distribution/run.mjs +18 -1
  415. package/templates/docs/adr.md +7 -0
  416. package/templates/docs/construct_guide.md +8 -8
  417. package/templates/docs/customer-profile.md +4 -0
  418. package/templates/docs/one-pager.md +4 -0
  419. package/templates/docs/postmortem.md +31 -0
  420. package/templates/docs/prd-platform.md +19 -0
  421. package/templates/docs/prd.md +15 -0
  422. package/templates/docs/prfaq.md +7 -0
  423. package/templates/docs/qa-strategy.md +103 -0
  424. package/templates/docs/research-brief.md +8 -0
  425. package/templates/docs/rfc-platform.md +12 -0
  426. package/templates/docs/test-plan.md +7 -0
@@ -0,0 +1,159 @@
1
+ /**
2
+ * lib/providers/secret-resolver.mjs — single credential resolver for the LLM path.
3
+ *
4
+ * Resolves a canonical environment variable (for example ANTHROPIC_API_KEY) by
5
+ * walking the same sources the rest of Construct trusts, in priority order:
6
+ * process env, ~/.construct/config.env, ~/.env, the project .env, then shell rc
7
+ * files (.zshrc/.bashrc/.bash_profile/.profile). When the value carries a
8
+ * 1Password reference — a bare `op://vault/item/field` or the shell form
9
+ * `$(op read 'op://...')` — it is resolved through the `op` CLI and cached for
10
+ * the process lifetime so a single launch prompts at most once per reference.
11
+ *
12
+ * resolveSecret performs the `op read` (and may surface a structured
13
+ * SecretResolutionError); hasSecret only checks presence and never invokes the
14
+ * CLI, so detection stays fast and free of biometric prompts. The plaintext
15
+ * value lives only in this module's cache and is never logged. allowAmbient lets
16
+ * a hermetic caller (tests, embedded callers that inject their own env) suppress
17
+ * file/rc discovery so a developer key never bleeds into an isolated run.
18
+ */
19
+
20
+ import fs from 'node:fs';
21
+ import path from 'node:path';
22
+ import os from 'node:os';
23
+ import { spawnSync } from 'node:child_process';
24
+ import { discoverAlternateRawForVar } from './credential-sources.mjs';
25
+
26
+ // The 1Password desktop integration prompts for biometric unlock on the first
27
+ // read after the vault locks, and that interactive round-trip routinely takes
28
+ // 7–8s — well past a snappy 5s budget. Allow enough headroom that an approved
29
+ // Touch ID unlock resolves instead of being killed mid-prompt; cached reads
30
+ // afterward return in well under a second.
31
+ const OP_READ_TIMEOUT_MS = 20000;
32
+
33
+ const opCache = new Map();
34
+
35
+ export class SecretResolutionError extends Error {
36
+ constructor(message, code) {
37
+ super(message);
38
+ this.name = 'SecretResolutionError';
39
+ this.code = code;
40
+ }
41
+ }
42
+
43
+ function unquote(value) {
44
+ return String(value).trim().replace(/^(['"])([\s\S]*)\1$/, '$2').trim();
45
+ }
46
+
47
+ // Two op:// carriers are accepted: a bare reference, or the `$(op read '...')`
48
+ // command-substitution form commonly written into dotenv and shell rc files.
49
+
50
+ export function extractOpRef(rawValue) {
51
+ if (!rawValue) return null;
52
+ const value = unquote(rawValue);
53
+ if (value.startsWith('op://')) return value;
54
+ const m = value.match(/\$\(\s*op\s+read\s+(['"]?)(op:\/\/[^'")\s]+)\1\s*\)/);
55
+ return m ? m[2] : null;
56
+ }
57
+
58
+ function defaultOpRead(opRef) {
59
+ const result = spawnSync('op', ['read', opRef], { encoding: 'utf8', timeout: OP_READ_TIMEOUT_MS });
60
+ if (result.error && result.error.code === 'ENOENT') {
61
+ throw new SecretResolutionError('1Password CLI not found — install `op` or set the key directly.', 'OP_NOT_INSTALLED');
62
+ }
63
+ if (result.status !== 0) {
64
+ const stderr = String(result.stderr || '').toLowerCase();
65
+ if (/sign in|signin|session|not currently|authenticate|authorization|no account/.test(stderr)) {
66
+ throw new SecretResolutionError('1Password CLI is not signed in — run `op signin` and retry.', 'OP_NOT_SIGNED_IN');
67
+ }
68
+ throw new SecretResolutionError(`op read failed for ${opRef}: ${String(result.stderr || '').trim().slice(0, 160)}`, 'OP_READ_FAILED');
69
+ }
70
+ const secret = String(result.stdout || '').trim();
71
+ if (!secret) throw new SecretResolutionError(`op read returned an empty value for ${opRef}`, 'OP_EMPTY');
72
+ return secret;
73
+ }
74
+
75
+ export function resolveOpRef(opRef, { opRead = defaultOpRead } = {}) {
76
+ if (opCache.has(opRef)) return opCache.get(opRef);
77
+ const secret = opRead(opRef);
78
+ opCache.set(opRef, secret);
79
+ return secret;
80
+ }
81
+
82
+ function materialize(rawValue, opts) {
83
+ const ref = extractOpRef(rawValue);
84
+ return ref ? resolveOpRef(ref, opts) : unquote(rawValue);
85
+ }
86
+
87
+ function readDotenvVar(file, varName) {
88
+ try {
89
+ if (!fs.existsSync(file)) return null;
90
+ const m = fs.readFileSync(file, 'utf8').match(new RegExp(`^${varName}=(.+)$`, 'm'));
91
+ return m ? m[1].trim() : null;
92
+ } catch {
93
+ return null;
94
+ }
95
+ }
96
+
97
+ function readShellRcVar(varName, home) {
98
+ const files = ['.zshrc', '.bashrc', '.bash_profile', '.profile'].map((f) => path.join(home, f));
99
+ for (const rc of files) {
100
+ try {
101
+ if (!fs.existsSync(rc)) continue;
102
+ const m = fs.readFileSync(rc, 'utf8').match(new RegExp(`^\\s*export\\s+${varName}=(.+)$`, 'm'));
103
+ if (m) return m[1].trim();
104
+ } catch {
105
+ continue;
106
+ }
107
+ }
108
+ return null;
109
+ }
110
+
111
+ function rawCandidate(varName, { env, cwd, home }) {
112
+ const direct = env?.[varName];
113
+ if (typeof direct === 'string' && direct.length > 0) return direct;
114
+ const files = [path.join(home, '.construct', 'config.env'), path.join(home, '.env'), path.join(cwd, '.env')];
115
+ for (const file of files) {
116
+ const value = readDotenvVar(file, varName);
117
+ if (value) return value;
118
+ }
119
+ const alt = discoverAlternateRawForVar(varName, { home });
120
+ if (alt) return alt;
121
+ return readShellRcVar(varName, home);
122
+ }
123
+
124
+ export function resolveSecret(varName, { env = process.env, cwd = process.cwd(), allowAmbient = true, opRead } = {}) {
125
+ const direct = env?.[varName];
126
+ if (typeof direct === 'string' && direct.length > 0) return materialize(direct, { opRead });
127
+ if (!allowAmbient) return null;
128
+ const home = os.homedir();
129
+ const raw = rawCandidate(varName, { env, cwd, home });
130
+ return raw ? materialize(raw, { opRead }) : null;
131
+ }
132
+
133
+ export function resolveFirstSecret(varNames, opts = {}) {
134
+ for (const name of varNames) {
135
+ const value = resolveSecret(name, opts);
136
+ if (value) return value;
137
+ }
138
+ return null;
139
+ }
140
+
141
+ // Presence check that never runs `op read`: a stored op:// reference counts as
142
+ // configured because the plaintext is resolved lazily at call time.
143
+
144
+ export function hasSecret(varName, { env = process.env, cwd = process.cwd(), allowAmbient = true } = {}) {
145
+ const direct = env?.[varName];
146
+ if (typeof direct === 'string' && direct.length > 0) return true;
147
+ if (!allowAmbient) return false;
148
+ const home = os.homedir();
149
+ const raw = rawCandidate(varName, { env, cwd, home });
150
+ return typeof raw === 'string' && raw.length > 0;
151
+ }
152
+
153
+ export function hasAnySecret(varNames, opts = {}) {
154
+ return varNames.some((name) => hasSecret(name, opts));
155
+ }
156
+
157
+ export function __clearSecretCache() {
158
+ opCache.clear();
159
+ }
@@ -0,0 +1,163 @@
1
+ /**
2
+ * lib/publish-template.mjs — type-specific PDF template routing and brand tokens
3
+ * for Construct distribution exports.
4
+ *
5
+ * Projects override via `.cx/publish-theme.typ`. Otherwise artifactType selects
6
+ * bundled editorial (PRD), analytics (research), or decision (ADR/RFC) layouts.
7
+ */
8
+
9
+ import fs from 'node:fs';
10
+ import path from 'node:path';
11
+ import yaml from 'js-yaml';
12
+ import { BRAND, BRAND_TOKENS } from './brand-tokens.mjs';
13
+
14
+ export { BRAND, BRAND_TOKENS };
15
+
16
+ export const ARTIFACT_TEMPLATE_MAP = {
17
+ prd: 'construct-prd.typ',
18
+ 'prd-platform': 'construct-prd.typ',
19
+ 'prd-business': 'construct-prd.typ',
20
+ 'meta-prd': 'construct-prd.typ',
21
+ strategy: 'construct-prd.typ',
22
+ runbook: 'construct-prd.typ',
23
+ memo: 'construct-prd.typ',
24
+ 'one-pager': 'construct-prd.typ',
25
+ prfaq: 'construct-prd.typ',
26
+ 'backlog-proposal': 'construct-prd.typ',
27
+ 'customer-profile': 'construct-prd.typ',
28
+ 'test-plan': 'construct-prd.typ',
29
+ 'qa-strategy': 'construct-prd.typ',
30
+ 'changelog-entry': 'construct-prd.typ',
31
+ 'incident-report': 'construct-prd.typ',
32
+ postmortem: 'construct-prd.typ',
33
+ 'research-brief': 'construct-research.typ',
34
+ 'evidence-brief': 'construct-research.typ',
35
+ 'signal-brief': 'construct-research.typ',
36
+ 'research-finding': 'construct-research.typ',
37
+ 'product-intelligence-report': 'construct-research.typ',
38
+ adr: 'construct-decision.typ',
39
+ rfc: 'construct-decision.typ',
40
+ 'rfc-platform': 'construct-decision.typ',
41
+ 'security-audit-report': 'construct-decision.typ',
42
+ };
43
+
44
+ const FALLBACK_TEMPLATE = 'construct-pdf.typ';
45
+
46
+ export function bundledTemplateDir(repoRoot) {
47
+ return path.join(repoRoot, 'templates', 'distribution');
48
+ }
49
+
50
+ export function distributionFontsDir(repoRoot) {
51
+ return path.join(bundledTemplateDir(repoRoot), 'fonts');
52
+ }
53
+
54
+ export function resolvePdfTemplatePath({
55
+ artifactType = null,
56
+ cwd = process.cwd(),
57
+ repoRoot,
58
+ } = {}) {
59
+ const root = repoRoot || cwd;
60
+ const projectOverride = path.join(cwd, '.cx', 'publish-theme.typ');
61
+ if (fs.existsSync(projectOverride)) return projectOverride;
62
+
63
+ const dir = bundledTemplateDir(root);
64
+ const mapped = artifactType ? ARTIFACT_TEMPLATE_MAP[artifactType] : null;
65
+ if (mapped) {
66
+ const typePath = path.join(dir, mapped);
67
+ if (fs.existsSync(typePath)) return typePath;
68
+ }
69
+
70
+ const fallback = path.join(dir, FALLBACK_TEMPLATE);
71
+ if (fs.existsSync(fallback)) return fallback;
72
+ return null;
73
+ }
74
+
75
+ export function templateForArtifactType(artifactType) {
76
+ if (!artifactType) return FALLBACK_TEMPLATE;
77
+ return ARTIFACT_TEMPLATE_MAP[artifactType] || FALLBACK_TEMPLATE;
78
+ }
79
+
80
+ export function formatPublishDate(value) {
81
+ if (value == null || value === '') return '';
82
+ if (value instanceof Date) {
83
+ if (Number.isNaN(value.getTime())) return '';
84
+ return value.toISOString().slice(0, 10);
85
+ }
86
+ const text = String(value).trim();
87
+ if (/^\d{4}-\d{2}-\d{2}/.test(text)) return text.slice(0, 10);
88
+ const parsed = new Date(text);
89
+ if (!Number.isNaN(parsed.getTime())) return parsed.toISOString().slice(0, 10);
90
+ return text;
91
+ }
92
+
93
+ function escapeRegExp(text) {
94
+ return String(text).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
95
+ }
96
+
97
+ function stripCoverDuplicatesFromBody(body, metadata = {}) {
98
+ let out = String(body || '').replace(/^\s+/, '');
99
+ const title = metadata.title?.trim();
100
+ if (title) {
101
+ const h1Re = new RegExp(`^#\\s+${escapeRegExp(title)}\\s*\\n+`, 'i');
102
+ out = out.replace(h1Re, '');
103
+ }
104
+ const lines = out.split('\n');
105
+ while (lines.length && /^-\s+\*\*(Date|Owner|Status)\*\*:/i.test(lines[0].trim())) {
106
+ lines.shift();
107
+ }
108
+ while (lines.length && lines[0].trim() === '') lines.shift();
109
+ return `${lines.join('\n')}\n`;
110
+ }
111
+
112
+ export function preprocessMarkdownForPdfExport(content, metadata = {}) {
113
+ if (!metadata?.title) return content;
114
+ if (!content.startsWith('---')) return stripCoverDuplicatesFromBody(content, metadata);
115
+ const end = content.indexOf('\n---', 3);
116
+ if (end === -1) return stripCoverDuplicatesFromBody(content, metadata);
117
+ const frontmatter = content.slice(0, end + 4);
118
+ const body = stripCoverDuplicatesFromBody(content.slice(end + 4), metadata);
119
+ return `${frontmatter}\n${body}`;
120
+ }
121
+
122
+ export function parseArtifactMetadata(inputPath) {
123
+ const content = fs.readFileSync(inputPath, 'utf8');
124
+ let fm = {};
125
+ let body = content;
126
+ if (content.startsWith('---')) {
127
+ const end = content.indexOf('\n---', 3);
128
+ if (end !== -1) {
129
+ try {
130
+ fm = yaml.load(content.slice(3, end)) || {};
131
+ } catch {
132
+ fm = {};
133
+ }
134
+ body = content.slice(end + 4);
135
+ }
136
+ }
137
+
138
+ const h1 = body.match(/^#\s+(.+)$/m);
139
+ const ownerMatch = body.match(/\*\*Owner\*\*:\s*(\S+)/);
140
+ const dateMatch = body.match(/\*\*Date\*\*:\s*(\S+)/);
141
+
142
+ return {
143
+ title: fm.title || (h1 ? h1[1].trim() : path.basename(inputPath, path.extname(inputPath))),
144
+ subtitle: fm.subtitle || '',
145
+ date: formatPublishDate(fm.date || fm.last_verified_at || (dateMatch ? dateMatch[1] : '')),
146
+ status: fm.status || '',
147
+ owner: fm.owner || (ownerMatch ? ownerMatch[1] : ''),
148
+ artifactType: fm.artifactType || fm.artifact_type || '',
149
+ version: fm.version != null ? String(fm.version) : '',
150
+ docId: fm.doc_id || fm.docId || '',
151
+ classification: fm.classification || '',
152
+ };
153
+ }
154
+
155
+ export function pandocMetadataArgs(metadata = {}) {
156
+ const args = [];
157
+ for (const [key, value] of Object.entries(metadata)) {
158
+ if (value == null || value === '') continue;
159
+ const safe = String(value).replace(/"/g, '\\"');
160
+ args.push('-M', `${key}=${safe}`);
161
+ }
162
+ return args;
163
+ }
@@ -0,0 +1,119 @@
1
+ /**
2
+ * lib/publish-tooling.mjs — detect optional binaries for the publish pipeline.
3
+ *
4
+ * Composes export (Pandoc/Typst), diagram (D2/mmdc), terminal demo (VHS), and
5
+ * dashboard demo (Playwright workspace) probes for `construct tools detect`,
6
+ * `construct publish --strict`, and doctor one-shot checks.
7
+ */
8
+
9
+ import fs from 'node:fs';
10
+ import path from 'node:path';
11
+ import { spawnSync } from 'node:child_process';
12
+ import { fileURLToPath } from 'node:url';
13
+ import { detect as detectExport } from './document-export.mjs';
14
+ import { locateRenderer } from './diagram.mjs';
15
+ import { locateRecorder } from './demo.mjs';
16
+ import { detectDashboardDemo } from './dashboard-demo.mjs';
17
+ import { detectIngestPipeline } from './ingest-tooling.mjs';
18
+
19
+ const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
20
+
21
+ export function diagramFilterPath() {
22
+ return path.join(REPO_ROOT, 'vendor', 'pandoc-ext', 'diagram.lua');
23
+ }
24
+
25
+ function whichBin(name) {
26
+ const cmd = process.platform === 'win32' ? 'where' : 'which';
27
+ const result = spawnSync(cmd, [name], { encoding: 'utf8' });
28
+ if (result.status !== 0) return null;
29
+ return (result.stdout || '').trim().split('\n')[0] || null;
30
+ }
31
+
32
+ export function detectFigureBinaries(env = process.env) {
33
+ const d2 = whichBin('d2');
34
+ const mmdc = whichBin('mmdc');
35
+ const dot = whichBin('dot');
36
+ const filter = fs.existsSync(diagramFilterPath());
37
+ const missing = [];
38
+ if (!filter) missing.push('diagram.lua (vendor/pandoc-ext/diagram.lua)');
39
+ if (!d2 && !dot) missing.push('d2 or graphviz dot');
40
+ if (!mmdc) missing.push('mmdc (@mermaid-js/mermaid-cli)');
41
+ return {
42
+ ok: true,
43
+ present: missing.length === 0,
44
+ filter,
45
+ d2,
46
+ dot,
47
+ mmdc,
48
+ missing,
49
+ message: missing.length === 0
50
+ ? 'Figure tooling ready (pandoc-ext/diagram + d2/mmdc)'
51
+ : `Install figure tooling: ${missing.join('; ')}`,
52
+ };
53
+ }
54
+
55
+ export function detectPublishPipeline({
56
+ format = 'pdf',
57
+ includeFigures = true,
58
+ includeTerminalDemo = false,
59
+ includeDashboardDemo = false,
60
+ includeIngest = true,
61
+ cwd = process.cwd(),
62
+ repoRoot = REPO_ROOT,
63
+ env = process.env,
64
+ } = {}) {
65
+ const steps = {};
66
+ const missing = [];
67
+
68
+ const exportDetect = detectExport(format, env, { figures: includeFigures, cwd, repoRoot });
69
+ steps.export = exportDetect;
70
+ if (!exportDetect.present) missing.push(...(exportDetect.missing || []));
71
+
72
+ if (includeIngest) {
73
+ steps.ingest = detectIngestPipeline({ cwd, env, repoRoot });
74
+ }
75
+
76
+ if (includeFigures) {
77
+ const figures = detectFigureBinaries(env);
78
+ steps.figures = figures;
79
+ if (!figures.present) missing.push(...figures.missing);
80
+ }
81
+
82
+ if (includeTerminalDemo) {
83
+ const recorder = locateRecorder();
84
+ steps.terminalDemo = {
85
+ present: Boolean(recorder),
86
+ engine: recorder?.engine || null,
87
+ missing: recorder ? [] : ['vhs or asciinema'],
88
+ };
89
+ if (!recorder) missing.push('vhs');
90
+ }
91
+
92
+ if (includeDashboardDemo) {
93
+ const dashboard = detectDashboardDemo({ cwd, repoRoot: REPO_ROOT });
94
+ steps.dashboardDemo = dashboard;
95
+ if (!dashboard.present) missing.push(...dashboard.missing);
96
+ }
97
+
98
+ const uniqueMissing = [...new Set(missing)];
99
+ return {
100
+ ok: true,
101
+ present: uniqueMissing.length === 0,
102
+ format,
103
+ steps,
104
+ missing: uniqueMissing,
105
+ message: uniqueMissing.length === 0
106
+ ? 'Publish pipeline ready'
107
+ : `Missing: ${uniqueMissing.join(', ')}`,
108
+ };
109
+ }
110
+
111
+ export function formatToolsDetectReport(detection, { json = false } = {}) {
112
+ if (json) return JSON.stringify(detection, null, 2);
113
+ const lines = [`Publish pipeline: ${detection.present ? 'ready' : 'degraded'}`];
114
+ for (const [key, step] of Object.entries(detection.steps || {})) {
115
+ lines.push(` ${key}: ${step.present ? 'ok' : 'missing'}${step.message ? ` — ${step.message}` : ''}`);
116
+ }
117
+ if (detection.missing?.length) lines.push(`Install: ${detection.missing.join('; ')}`);
118
+ return lines.join('\n');
119
+ }