@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,103 @@
1
+ ---
2
+ description: Distribution example — operational runbook with diagnostic flow and severity table.
3
+ artifactType: runbook
4
+ subtitle: Publish pipeline degraded — Pandoc or figure tooling missing
5
+ version: "1.1"
6
+ doc_id: RUNBOOK-PUBLISH-001
7
+ classification: internal
8
+ status: accepted
9
+ owner: cx-sre
10
+ last_verified_at: 2026-06-22
11
+ ---
12
+
13
+ # Runbook: construct publish / export failures
14
+
15
+ - **Service**: document-export (`construct publish`, `construct export`)
16
+ - **Owner**: platform on-call
17
+ - **Last tested**: 2026-06-22
18
+ - **Severity**: SEV-2 when exports block release; SEV-3 when examples only
19
+
20
+ ## Alert trigger
21
+
22
+ `construct publish` exits 2 with `missing: pandoc` or `Figure tooling` in `construct tools detect --figures` output. CI job `release:check` fails on export certification scenarios.
23
+
24
+ ## Symptoms
25
+
26
+ - PDF export fails before Typst invocation
27
+ - `--figures` renders empty diagram placeholders
28
+ - `npm run examples:distribution` reports partial failures in summary
29
+
30
+ ## Impact
31
+
32
+ | Audience | Impact |
33
+ |---|---|
34
+ | Release managers | Cannot ship branded PRDs/ADRs |
35
+ | Developers | Local previews blocked |
36
+ | Agents | `publish_run` MCP tool returns remediation hints |
37
+
38
+ ## Severity and response
39
+
40
+ | Severity | Trigger condition | Page within | Comms | Error budget |
41
+ |----------|-------------------|-------------|-------|--------------|
42
+ | SEV-1 | All publish paths down in CI | 5 min | #incident + status | breach → freeze |
43
+ | SEV-2 | PDF down, HTML OK | 15 min | #platform | partial spend |
44
+ | SEV-3 | Examples script only | business hours | team channel | none |
45
+
46
+ ## Diagnostic steps
47
+
48
+ 1. Run `construct tools detect --figures --json` and read `steps.export` + `steps.figures`.
49
+ 2. Confirm `pandoc` and `typst` on PATH; confirm `d2` and `mmdc` when `--figures` is set.
50
+ 3. Re-run a single fixture: `node bin/construct export examples/distribution/sources/adr.md --to=pdf --figures`.
51
+
52
+ ```mermaid
53
+ flowchart TD
54
+ A[Alert fires] --> B{export.present?}
55
+ B -->|No| C[Install pandoc + typst]
56
+ B -->|Yes| D{figures.present?}
57
+ D -->|No| E[brew install d2; npm i -g @mermaid-js/mermaid-cli]
58
+ D -->|Yes| F[Re-run examples:distribution]
59
+ C --> F
60
+ E --> F
61
+ ```
62
+
63
+ ```d2
64
+ direction: down
65
+
66
+ detect: tools detect
67
+ pandoc: pandoc + typst
68
+ figures: d2 + mmdc
69
+ ok: Green export
70
+
71
+ detect -> pandoc: if missing
72
+ detect -> figures: if --figures
73
+ pandoc -> ok
74
+ figures -> ok
75
+ ```
76
+
77
+ ## Remediation
78
+
79
+ ```bash
80
+ brew install pandoc typst d2 graphviz
81
+ npm install -g @mermaid-js/mermaid-cli
82
+ npm run examples:distribution
83
+ open .tmp/distribution-examples/index.html
84
+ ```
85
+
86
+ Expected: index lists PDF and HTML links for each artifact type; no `FAILED` lines in script output.
87
+
88
+ ## Rollback
89
+
90
+ Not applicable — this runbook restores tooling, not application state. If a bad template change caused failures, revert the Typst commit and re-run exports.
91
+
92
+ ## Escalation
93
+
94
+ After 30 minutes without restore: page cx-platform-engineer. After 60 minutes with release blocked: page cx-release-manager.
95
+
96
+ ## Post-incident
97
+
98
+ Preserve `construct tools detect --json` output and the failing pandoc/typst stderr in the incident ticket.
99
+
100
+ ## References
101
+
102
+ - `docs/reference/document-io.md`
103
+ - `docs/cookbook/diagram-and-demo.md`
@@ -0,0 +1,88 @@
1
+ ---
2
+ description: Distribution example — strategy memo with goals, metrics, and landscape diagram.
3
+ artifactType: strategy
4
+ subtitle: Make Construct the default governed layer for agentic IDE workflows
5
+ version: "1.0"
6
+ doc_id: STRATEGY-2026-H1
7
+ classification: internal
8
+ status: draft
9
+ owner: cx-product-manager
10
+ last_verified_at: 2026-06-22
11
+ ---
12
+
13
+ # Strategy: Governed agentic workflows for IDE-native teams
14
+
15
+ - **Date**: 2026-06-22
16
+ - **Owner**: cx-product-manager
17
+ - **Status**: draft
18
+ - **Horizon**: H1 2026 – H1 2027
19
+
20
+ > Platform and product teams want specialist routing without losing citation discipline, branded exports, or profile-aware terminology. Construct wins when evaluators can open one gallery of PDFs and see the same ink, typography, and diagram styling across document types.
21
+
22
+ ## Problem
23
+
24
+ Host-native agents optimize for single-session chat. Enterprise platform teams need durable invoke plans, manifest-enforced artifacts, and distribution that looks intentional — not markdown attachments with host fonts.
25
+
26
+ ## Goals
27
+
28
+ 1. **Prove value in five minutes** — `npm run examples:distribution` produces a browsable gallery with figures.
29
+ 2. **One brand contract** — `docs/reference/branding.md` + `03d-brand` audit ratchet.
30
+ 3. **Profile-aware language** — intake queue labels follow active profile rebrand in CLI, session prelude, and dashboard API.
31
+
32
+ ## Non-goals
33
+
34
+ Replacing host LLM execution. Mandating cloud render farms. Re-linting every historical doc for marketing voice.
35
+
36
+ ## Landscape
37
+
38
+ ```mermaid
39
+ flowchart TB
40
+ subgraph hosts [IDE hosts]
41
+ CC[Claude Code]
42
+ OC[OpenCode]
43
+ CX[Cursor]
44
+ end
45
+ subgraph construct [Construct]
46
+ R[Router]
47
+ V[Validate]
48
+ P[Publish]
49
+ end
50
+ hosts --> R --> V --> P
51
+ P --> PDF[Branded PDF]
52
+ P --> HTML[HTML / deck]
53
+ ```
54
+
55
+ ```d2
56
+ direction: down
57
+
58
+ construct: Construct {
59
+ style.fill: "#f3f4f6"
60
+ }
61
+
62
+ hosts: IDE hosts
63
+ validate: Release gate
64
+ export: Branded export
65
+
66
+ hosts -> construct -> validate -> export
67
+ ```
68
+
69
+ ## Success metrics
70
+
71
+ | Metric | Baseline | Target |
72
+ |---|---|---|
73
+ | Brand audit findings | unknown | 0 in `03d-brand` |
74
+ | Example types with figures | 1 (PRD) | ≥6 layouts |
75
+ | Dashboard rebrand coverage | partial | intake API + pages |
76
+
77
+ ## Risks and mitigations
78
+
79
+ | Risk | Mitigation |
80
+ |---|---|
81
+ | Toolchain missing on contributor laptops | `construct tools detect --figures` in runbook |
82
+ | Template drift | Functional tests on `construct-brand.typ` + HTML decks |
83
+ | Profile label regressions | `intake-rebrand.functional.test.mjs` |
84
+
85
+ ## References
86
+
87
+ - `examples/distribution/manifest.json`
88
+ - https://langchain-ai.github.io/langgraph/ (accessed 2026-06-22)
@@ -0,0 +1,67 @@
1
+ /**
2
+ * lib/adapters-sync.mjs — project adapter sync for the Construct tool repo and
3
+ * `npm run adapters`. Detects installed hosts, runs sync-specialists --project,
4
+ * and is invoked from postinstall when developing inside the package itself.
5
+ */
6
+
7
+ import { spawnSync } from 'node:child_process';
8
+ import { existsSync } from 'node:fs';
9
+ import path from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+
12
+ import { detectHostCapabilities } from './host-capabilities.mjs';
13
+ import { isConstructPackageRepo } from './host-disposition.mjs';
14
+ import { stageProjectAdapters } from './install/stage-project.mjs';
15
+
16
+ const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
17
+ const PKG_ROOT = path.resolve(MODULE_DIR, '..');
18
+
19
+ const HOST_ID_MAP = {
20
+ 'Claude Code': 'claude',
21
+ OpenCode: 'opencode',
22
+ Codex: 'codex',
23
+ 'VS Code': 'vscode',
24
+ Cursor: 'cursor',
25
+ Copilot: 'copilot',
26
+ };
27
+
28
+ export function resolveAdapterHosts({ forceAll = false, extra = [] } = {}) {
29
+ if (forceAll) return ['claude', 'opencode', 'codex', 'vscode', 'cursor'];
30
+ const hosts = new Set(extra);
31
+ for (const entry of detectHostCapabilities()) {
32
+ if (entry.availability !== 'installed') continue;
33
+ const id = HOST_ID_MAP[entry.host];
34
+ if (id) hosts.add(id);
35
+ }
36
+ if (hosts.size === 0) hosts.add('claude');
37
+ return [...hosts];
38
+ }
39
+
40
+ export function syncProjectAdapters({
41
+ projectRoot = process.cwd(),
42
+ packageRoot = PKG_ROOT,
43
+ hosts = null,
44
+ log = () => {},
45
+ } = {}) {
46
+ const resolvedHosts = hosts ?? resolveAdapterHosts({ forceAll: isConstructPackageRepo(projectRoot) });
47
+ return stageProjectAdapters({
48
+ projectRoot,
49
+ packageRoot,
50
+ pkgVersion: null,
51
+ log,
52
+ hosts: resolvedHosts,
53
+ });
54
+ }
55
+
56
+ export function runAdaptersScript({ cwd = process.cwd(), hosts = null } = {}) {
57
+ const result = syncProjectAdapters({ projectRoot: cwd, hosts, log: (m) => process.stdout.write(`[adapters] ${m}\n`) });
58
+ return result.synced ? 0 : 1;
59
+ }
60
+
61
+ if (import.meta.url === `file://${process.argv[1]}`) {
62
+ const args = new Set(process.argv.slice(2));
63
+ const forceAll = args.has('--all-hosts');
64
+ const hosts = forceAll ? resolveAdapterHosts({ forceAll: true }) : null;
65
+ const code = runAdaptersScript({ cwd: process.cwd(), hosts });
66
+ process.exit(code);
67
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * lib/artifact-gate-notice.mjs — Shared PostToolUse artifact structure/visual check.
3
+ *
4
+ * Returns violations for typed docs under docs/** and .cx/research/** without
5
+ * applying TTY/CI suppression (callers decide whether to emit).
6
+ */
7
+
8
+ import path from 'node:path';
9
+ import { inferArtifactTypeFromPath, isArtifactGatePath } from './artifact-type-from-path.mjs';
10
+ import { lintDocStructure, lintDocVisuals } from './templates/visual-requirements.mjs';
11
+
12
+ export function checkArtifactGateNotice(filePath, { cwd = process.cwd() } = {}) {
13
+ const rel = filePath.startsWith(cwd)
14
+ ? filePath.slice(cwd.length).replace(/^\/+/, '')
15
+ : filePath;
16
+ if (!isArtifactGatePath(rel)) return null;
17
+
18
+ const type = inferArtifactTypeFromPath(filePath, { rootDir: cwd });
19
+ if (!type) return null;
20
+
21
+ const errors = [
22
+ ...lintDocStructure(filePath, type),
23
+ ...lintDocVisuals(filePath, type),
24
+ ];
25
+ if (errors.length === 0) return null;
26
+
27
+ return { type, rel, errors };
28
+ }
29
+
30
+ export function formatArtifactGateNotice({ type, rel, errors }) {
31
+ const lines = [
32
+ `\nartifact-release-gate (${type}): ${errors.length} structure/visual issue(s). `
33
+ + 'Fix before calling the artifact done; full gate: '
34
+ + `\`construct artifact validate ${rel} --type=${type}\`\n`,
35
+ ];
36
+ for (const e of errors) lines.push(` - ${e}\n`);
37
+ return lines.join('');
38
+ }
@@ -0,0 +1,258 @@
1
+ /**
2
+ * lib/artifact-manifest.mjs — Load and resolve the artifact capability manifest.
3
+ *
4
+ * specialists/artifact-manifest.json is the single source of truth for document
5
+ * type metadata: templates, tone, structure, visuals, and release gates.
6
+ * Consumer projects without a local copy resolve the shipped manifest from the
7
+ * Construct package root.
8
+ */
9
+
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+
14
+ const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
15
+ const EMPTY_MANIFEST = { version: 1, artifacts: {} };
16
+
17
+ // Kept here, next to the manifest resolver, so every public workflow surface
18
+ // uses one answer for what can carry Construct styling. Source formats remain
19
+ // deliberately plain unless a caller explicitly asks otherwise.
20
+ export const BRAND_CAPABLE_FORMATS = Object.freeze(['pdf', 'docx', 'doc', 'deck', 'pptx', 'html', 'rtf', 'odt', 'epub', 'tex']);
21
+ export const PLAIN_OUTPUT_FORMATS = Object.freeze(['txt', 'md', 'mdx']);
22
+
23
+ let cached = null;
24
+ let cachedRoot = null;
25
+
26
+ function manifestPathForRoot(root) {
27
+ return path.join(root, 'specialists', 'artifact-manifest.json');
28
+ }
29
+
30
+ export function findConstructRoot(startPath = process.cwd()) {
31
+ let current = path.resolve(startPath);
32
+ while (true) {
33
+ const manifest = manifestPathForRoot(current);
34
+ if (fs.existsSync(manifest)) return current;
35
+ const parent = path.dirname(current);
36
+ if (parent === current) break;
37
+ current = parent;
38
+ }
39
+ if (fs.existsSync(manifestPathForRoot(PACKAGE_ROOT))) return PACKAGE_ROOT;
40
+ return null;
41
+ }
42
+
43
+ export function loadArtifactManifest({ rootDir, force = false, cwd = process.cwd() } = {}) {
44
+ const resolvedRoot = rootDir ?? findConstructRoot(cwd) ?? PACKAGE_ROOT;
45
+ if (cached && !force && cachedRoot === resolvedRoot) return cached;
46
+
47
+ const p = manifestPathForRoot(resolvedRoot);
48
+ if (!fs.existsSync(p)) {
49
+ cached = EMPTY_MANIFEST;
50
+ cachedRoot = resolvedRoot;
51
+ return cached;
52
+ }
53
+
54
+ cached = JSON.parse(fs.readFileSync(p, 'utf8'));
55
+ cachedRoot = resolvedRoot;
56
+ return cached;
57
+ }
58
+
59
+ export function getArtifactEntry(type, opts = {}) {
60
+ const manifest = loadArtifactManifest(opts);
61
+ return manifest.artifacts?.[type] ?? null;
62
+ }
63
+
64
+ export function artifactTypes(opts = {}) {
65
+ const manifest = loadArtifactManifest(opts);
66
+ return Object.keys(manifest.artifacts ?? {});
67
+ }
68
+
69
+ function isObject(value) {
70
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
71
+ }
72
+
73
+ function mergeObjects(...values) {
74
+ const out = {};
75
+ for (const value of values) {
76
+ if (!isObject(value)) continue;
77
+ for (const [key, next] of Object.entries(value)) {
78
+ out[key] = isObject(next) && isObject(out[key]) ? mergeObjects(out[key], next) : structuredClone(next);
79
+ }
80
+ }
81
+ return out;
82
+ }
83
+
84
+ function unique(items) {
85
+ return [...new Set(items.filter(Boolean))];
86
+ }
87
+
88
+ /**
89
+ * Resolve a user-facing type or registered alias. The result intentionally
90
+ * distinguishes an unknown request from a fallback: callers must ask for
91
+ * classification/registration instead of silently turning it into a PRD.
92
+ */
93
+ export function resolveArtifactType(type, opts = {}) {
94
+ const requestedType = String(type ?? '').trim().toLowerCase();
95
+ const manifest = loadArtifactManifest(opts);
96
+ if (!requestedType) {
97
+ return {
98
+ status: 'unrecognized',
99
+ requestedType: null,
100
+ type: null,
101
+ guidance: 'Specify a registered document class or register one in specialists/artifact-manifest.json.',
102
+ };
103
+ }
104
+ for (const [registeredType, entry] of Object.entries(manifest.artifacts ?? {})) {
105
+ const aliases = [registeredType, ...(entry.aliases ?? [])].map((value) => String(value).toLowerCase());
106
+ if (aliases.includes(requestedType)) {
107
+ return { status: 'registered', requestedType, type: registeredType, entry };
108
+ }
109
+ }
110
+ return {
111
+ status: 'unrecognized',
112
+ requestedType,
113
+ type: null,
114
+ guidance: `Document class '${requestedType}' is not registered. Classify the request or add it to specialists/artifact-manifest.json.`,
115
+ };
116
+ }
117
+
118
+ /**
119
+ * Resolve the canonical workflow contract for a registered document class.
120
+ * Precedence is deliberately explicit: invocation > project config > manifest
121
+ * defaults. Existing entries remain compatible: primaryOwners and releaseGate
122
+ * are the fallback author and reviewer/validation declarations.
123
+ */
124
+ export function resolveArtifactWorkflowContract(type, {
125
+ rootDir,
126
+ cwd = process.cwd(),
127
+ projectConfig = null,
128
+ overrides = null,
129
+ } = {}) {
130
+ const resolved = resolveArtifactType(type, { rootDir, cwd });
131
+ if (resolved.status !== 'registered') return resolved;
132
+
133
+ const manifest = loadArtifactManifest({ rootDir, cwd });
134
+ const entry = resolved.entry;
135
+ const config = projectConfig?.artifactWorkflow ?? {};
136
+ const manifestDefaults = manifest.workflowDefaults ?? {};
137
+ const projectType = config.types?.[resolved.type] ?? {};
138
+ const appliedOverrides = [];
139
+ if (isObject(config.defaults) && Object.keys(config.defaults).length) appliedOverrides.push('project.defaults');
140
+ if (isObject(projectType) && Object.keys(projectType).length) appliedOverrides.push(`project.types.${resolved.type}`);
141
+ if (isObject(overrides) && Object.keys(overrides).length) appliedOverrides.push('invocation');
142
+
143
+ const configured = mergeObjects(manifestDefaults, entry, config.defaults, projectType, overrides);
144
+ const releaseGate = configured.releaseGate ?? {};
145
+ const authorChain = unique(configured.authorChain ?? configured.primaryOwners ?? []);
146
+ const reviewerChain = unique(configured.reviewerChain ?? [
147
+ ...(releaseGate.requiredReviewers ?? []),
148
+ ...(releaseGate.optionalReviewers ?? []),
149
+ ]);
150
+ const outputs = mergeObjects({
151
+ formats: [...BRAND_CAPABLE_FORMATS, ...PLAIN_OUTPUT_FORMATS],
152
+ branding: 'construct',
153
+ }, configured.outputs);
154
+ const validation = mergeObjects({ releaseGate: true }, configured.validation);
155
+
156
+ return {
157
+ status: 'registered',
158
+ requestedType: resolved.requestedType,
159
+ type: resolved.type,
160
+ documentClass: configured.documentClass ?? resolved.type,
161
+ template: configured.template ?? null,
162
+ workflowSkill: configured.workflowSkill ?? null,
163
+ authorChain,
164
+ reviewerChain,
165
+ requiredReviewers: unique(releaseGate.requiredReviewers ?? []),
166
+ optionalReviewers: unique(releaseGate.optionalReviewers ?? []),
167
+ researchProfile: configured.researchProfile ?? null,
168
+ validation,
169
+ releaseGate,
170
+ outputs,
171
+ appliedOverrides,
172
+ };
173
+ }
174
+
175
+ /** A dependency-free validator used by startup checks and tests. */
176
+ export function validateArtifactManifest(manifest) {
177
+ const errors = [];
178
+ if (!isObject(manifest)) return { valid: false, errors: ['root: must be an object'] };
179
+ if (!Number.isInteger(manifest.version) || manifest.version < 1) errors.push('version: must be an integer >= 1');
180
+ if (!isObject(manifest.artifacts)) errors.push('artifacts: must be an object');
181
+ for (const [type, entry] of Object.entries(manifest.artifacts ?? {})) {
182
+ const prefix = `artifacts.${type}`;
183
+ if (!isObject(entry)) { errors.push(`${prefix}: must be an object`); continue; }
184
+ if (typeof entry.template !== 'string' || !entry.template) errors.push(`${prefix}.template: required string missing`);
185
+ if (!Array.isArray(entry.primaryOwners) || entry.primaryOwners.some((owner) => typeof owner !== 'string')) {
186
+ errors.push(`${prefix}.primaryOwners: required string array missing`);
187
+ }
188
+ for (const field of ['authorChain', 'reviewerChain', 'aliases']) {
189
+ if (entry[field] !== undefined && (!Array.isArray(entry[field]) || entry[field].some((value) => typeof value !== 'string'))) {
190
+ errors.push(`${prefix}.${field}: must be an array of strings`);
191
+ }
192
+ }
193
+ if (entry.outputs?.formats !== undefined && (!Array.isArray(entry.outputs.formats) || entry.outputs.formats.length === 0)) {
194
+ errors.push(`${prefix}.outputs.formats: must be a non-empty array`);
195
+ }
196
+ if (entry.outputs?.branding !== undefined && !['construct', 'plain'].includes(entry.outputs.branding)) {
197
+ errors.push(`${prefix}.outputs.branding: must be construct or plain`);
198
+ }
199
+ }
200
+ return errors.length ? { valid: false, errors } : { valid: true, errors: [] };
201
+ }
202
+
203
+ export function structureRequirementsFromManifest(opts = {}) {
204
+ const manifest = loadArtifactManifest(opts);
205
+ const out = {};
206
+ for (const [type, entry] of Object.entries(manifest.artifacts ?? {})) {
207
+ if (entry.structureRequirements?.length) out[type] = entry.structureRequirements;
208
+ }
209
+ return out;
210
+ }
211
+
212
+ export function visualRequirementsFromManifest(opts = {}) {
213
+ const manifest = loadArtifactManifest(opts);
214
+ const out = {};
215
+ for (const [type, entry] of Object.entries(manifest.artifacts ?? {})) {
216
+ if (entry.visualRequirements?.length) out[type] = entry.visualRequirements;
217
+ }
218
+ return out;
219
+ }
220
+
221
+ export function loadBrandVoice(cwd = process.cwd()) {
222
+ const p = path.join(cwd, '.cx', 'brand-voice.json');
223
+ if (!fs.existsSync(p)) return null;
224
+ try {
225
+ return JSON.parse(fs.readFileSync(p, 'utf8'));
226
+ } catch {
227
+ return null;
228
+ }
229
+ }
230
+
231
+ export function resolveToneForArtifact(type, { cwd = process.cwd(), rootDir } = {}) {
232
+ const entry = getArtifactEntry(type, { rootDir, cwd });
233
+ const brand = loadBrandVoice(cwd);
234
+ const override = brand?.toneOverride?.[type];
235
+ if (override) return override;
236
+ return entry?.toneDefault ?? 'direct';
237
+ }
238
+
239
+ export function templateMetadata(type, { cwd = process.cwd(), rootDir } = {}) {
240
+ const workflow = resolveArtifactWorkflowContract(type, { rootDir, cwd });
241
+ if (workflow.status !== 'registered') return null;
242
+ const entry = getArtifactEntry(workflow.type, { rootDir, cwd });
243
+ return {
244
+ type: workflow.type,
245
+ documentClass: workflow.documentClass,
246
+ tone: resolveToneForArtifact(workflow.type, { cwd, rootDir }),
247
+ toneAllowed: entry.toneAllowed ?? [],
248
+ structureRequirements: entry.structureRequirements ?? [],
249
+ visualRequirements: entry.visualRequirements ?? [],
250
+ primaryOwners: entry.primaryOwners ?? [],
251
+ workflowSkill: entry.workflowSkill ?? null,
252
+ releaseGate: entry.releaseGate ?? null,
253
+ authorChain: workflow.authorChain,
254
+ reviewerChain: workflow.reviewerChain,
255
+ validation: workflow.validation,
256
+ outputs: workflow.outputs,
257
+ };
258
+ }