@geraldmaron/construct 1.1.1 → 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 (408) hide show
  1. package/README.md +29 -19
  2. package/bin/construct +311 -81
  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 +1 -1
  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 +129 -15
  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 +598 -140
  115. package/lib/diagram-export.mjs +192 -0
  116. package/lib/diagram.mjs +13 -11
  117. package/lib/docs-verify.mjs +25 -7
  118. package/lib/doctor/index.mjs +3 -1
  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/document-export.mjs +360 -36
  124. package/lib/document-extract.mjs +44 -4
  125. package/lib/document-ingest.mjs +29 -20
  126. package/lib/embed/cli.mjs +2 -2
  127. package/lib/embed/daemon.mjs +18 -46
  128. package/lib/embed/worker.mjs +2 -2
  129. package/lib/embedded-contract/model-resolve.mjs +10 -9
  130. package/lib/env-config.mjs +26 -0
  131. package/lib/evals/dataset.mjs +137 -0
  132. package/lib/evals/gates.mjs +175 -0
  133. package/lib/graph/build-co-change.mjs +54 -0
  134. package/lib/graph/build-from-registry.mjs +136 -0
  135. package/lib/graph/build-import-graph.mjs +153 -0
  136. package/lib/graph/cli.mjs +110 -0
  137. package/lib/graph/impact-cli.mjs +89 -0
  138. package/lib/graph/impact.mjs +108 -0
  139. package/lib/graph/staleness.mjs +44 -0
  140. package/lib/graph/store.mjs +172 -0
  141. package/lib/health-check.mjs +90 -76
  142. package/lib/hooks/artifact-release-gate.mjs +43 -0
  143. package/lib/hooks/brand-prose-lint.mjs +38 -0
  144. package/lib/hooks/graph-impact-advisory.mjs +62 -0
  145. package/lib/hooks/session-optimize.mjs +84 -207
  146. package/lib/hooks/session-start.mjs +4 -5
  147. package/lib/host-disposition.mjs +7 -0
  148. package/lib/improvement/cli.mjs +189 -0
  149. package/lib/improvement/controller.mjs +137 -0
  150. package/lib/improvement/proposal.mjs +120 -0
  151. package/lib/improvement/specialist-loop.mjs +192 -0
  152. package/lib/improvement/store.mjs +89 -0
  153. package/lib/improvement/surface.mjs +219 -0
  154. package/lib/ingest-tooling.mjs +97 -0
  155. package/lib/init/doc-lanes.mjs +165 -0
  156. package/lib/init-docs.mjs +31 -178
  157. package/lib/init-unified.mjs +27 -97
  158. package/lib/init-update-guide.mjs +102 -0
  159. package/lib/init-update.mjs +32 -1
  160. package/lib/init.mjs +8 -0
  161. package/lib/install/desktop-binary-download.mjs +85 -0
  162. package/lib/install/legacy-global-cleanup.mjs +189 -0
  163. package/lib/intake/daemon.mjs +2 -0
  164. package/lib/intake/git-queue.mjs +9 -8
  165. package/lib/intake/queue.mjs +2 -1
  166. package/lib/intake/session-prelude.mjs +90 -1
  167. package/lib/libreoffice-export.mjs +97 -0
  168. package/lib/logging/rotate.mjs +9 -0
  169. package/lib/maintenance/docker-reclaim.mjs +206 -0
  170. package/lib/mcp/external-schema-cost.mjs +74 -0
  171. package/lib/mcp/server.mjs +84 -9
  172. package/lib/mcp/stdio-mcp-probe.mjs +188 -0
  173. package/lib/mcp/tool-budget.mjs +55 -4
  174. package/lib/mcp/tools/document.mjs +18 -4
  175. package/lib/mcp/tools/skills.mjs +32 -3
  176. package/lib/mcp/tools/workflow.mjs +13 -1
  177. package/lib/model-free-selector.mjs +18 -0
  178. package/lib/model-registry.mjs +13 -229
  179. package/lib/model-router.mjs +373 -92
  180. package/lib/models/behavior-matrix.mjs +289 -0
  181. package/lib/models/catalog.mjs +209 -0
  182. package/lib/models/execution-capability-profile.mjs +196 -0
  183. package/lib/models/execution-policy.mjs +307 -0
  184. package/lib/models/provider-poll.mjs +383 -0
  185. package/lib/npm-spawn-env.mjs +17 -0
  186. package/lib/ollama/installed-models.mjs +129 -0
  187. package/lib/oracle/actions.mjs +187 -0
  188. package/lib/oracle/artifact-gate.mjs +99 -0
  189. package/lib/oracle/cli.mjs +204 -0
  190. package/lib/oracle/daemon-entry.mjs +14 -0
  191. package/lib/oracle/dispatch.mjs +81 -0
  192. package/lib/oracle/execute.mjs +143 -0
  193. package/lib/oracle/gaps.mjs +76 -0
  194. package/lib/oracle/index.mjs +84 -0
  195. package/lib/oracle/issues.mjs +164 -0
  196. package/lib/oracle/org-graph.mjs +170 -0
  197. package/lib/oracle/policy.mjs +80 -0
  198. package/lib/oracle/read-model.mjs +398 -0
  199. package/lib/oracle/reconcile.mjs +191 -0
  200. package/lib/oracle/routing.mjs +89 -0
  201. package/lib/oracle/synthesize.mjs +384 -0
  202. package/lib/oracle/verdicts.mjs +51 -0
  203. package/lib/orchestration/worker.mjs +88 -27
  204. package/lib/orchestration-policy.mjs +50 -0
  205. package/lib/parity.mjs +96 -2
  206. package/lib/playwright-demo.mjs +252 -0
  207. package/lib/project-init-shared.mjs +4 -1
  208. package/lib/prompt-composer.js +7 -3
  209. package/lib/prompt-validation-contract.mjs +24 -0
  210. package/lib/provider-capabilities.js +57 -11
  211. package/lib/providers/contract/adapters/confluence/index.mjs +181 -0
  212. package/lib/providers/contract/adapters/git/index.mjs +115 -0
  213. package/lib/providers/contract/adapters/github/index.mjs +166 -0
  214. package/lib/providers/contract/adapters/jira/index.mjs +187 -0
  215. package/lib/providers/contract/adapters/slack/index.mjs +175 -0
  216. package/lib/providers/contract/contract-tests.mjs +57 -0
  217. package/lib/providers/contract/errors.mjs +48 -0
  218. package/lib/providers/contract/interface.mjs +50 -0
  219. package/lib/providers/contract/registry.mjs +102 -0
  220. package/lib/providers/copilot-auth.mjs +297 -0
  221. package/lib/providers/credential-bootstrap.mjs +178 -0
  222. package/lib/providers/credential-catalog.mjs +46 -0
  223. package/lib/providers/credential-sources.mjs +63 -0
  224. package/lib/providers/creds.mjs +5 -2
  225. package/lib/providers/op-run.mjs +59 -0
  226. package/lib/providers/secret-resolver.mjs +159 -0
  227. package/lib/publish-template.mjs +163 -0
  228. package/lib/publish-tooling.mjs +119 -0
  229. package/lib/publish.mjs +305 -0
  230. package/lib/registry/cli.mjs +82 -0
  231. package/lib/registry/consolidation.mjs +147 -0
  232. package/lib/registry/generate-docs.mjs +75 -0
  233. package/lib/registry/skill-verification.mjs +57 -0
  234. package/lib/registry/surface-map.mjs +76 -0
  235. package/lib/registry/validate.mjs +135 -0
  236. package/lib/resources/budget.mjs +82 -0
  237. package/lib/resources/process-budget.mjs +45 -0
  238. package/lib/rules-delivery.mjs +9 -2
  239. package/lib/rules-read.mjs +26 -0
  240. package/lib/runtime-env.mjs +23 -0
  241. package/lib/runtime-pressure.mjs +51 -7
  242. package/lib/schema-infer.mjs +13 -2
  243. package/lib/server/chat-loop.mjs +622 -0
  244. package/lib/server/demo-preview.mjs +63 -0
  245. package/lib/server/index.mjs +228 -7
  246. package/lib/service-manager.mjs +60 -8
  247. package/lib/setup.mjs +70 -7
  248. package/lib/specialists/prompt-schema.mjs +19 -10
  249. package/lib/specialists/roster.mjs +43 -0
  250. package/lib/specialists/scaffold.mjs +56 -0
  251. package/lib/storage/backend.mjs +6 -6
  252. package/lib/storage/hybrid-query.mjs +7 -4
  253. package/lib/storage/state-source.mjs +5 -5
  254. package/lib/storage/sync.mjs +2 -3
  255. package/lib/telemetry/rule-calls.mjs +52 -0
  256. package/lib/template-registry.mjs +2 -2
  257. package/lib/templates/visual-requirements.mjs +27 -51
  258. package/lib/test-corpus-inventory.mjs +313 -0
  259. package/lib/uninstall/uninstall.mjs +19 -7
  260. package/lib/update.mjs +12 -0
  261. package/lib/upgrade.mjs +14 -0
  262. package/lib/wireframe.mjs +20 -14
  263. package/lib/worker/run.mjs +17 -4
  264. package/lib/worker/trace.mjs +11 -3
  265. package/package.json +28 -14
  266. package/personas/construct.md +2 -2
  267. package/platforms/claude/settings.template.json +36 -0
  268. package/rules/common/patterns.md +1 -1
  269. package/rules/common/release-gates.md +4 -3
  270. package/scripts/sync-specialists.mjs +41 -28
  271. package/skills/devops/data-engineering.md +1 -1
  272. package/skills/docs/adr-workflow.md +1 -0
  273. package/skills/docs/backlog-proposal-workflow.md +1 -0
  274. package/skills/docs/codebase-research-workflow.md +40 -0
  275. package/skills/docs/customer-profile-workflow.md +1 -0
  276. package/skills/docs/document-ingest-workflow.md +1 -0
  277. package/skills/docs/evidence-ingest-workflow.md +1 -0
  278. package/skills/docs/init-docs.md +2 -2
  279. package/skills/docs/init-project.md +7 -2
  280. package/skills/docs/prd-workflow.md +24 -1
  281. package/skills/docs/prfaq-workflow.md +1 -0
  282. package/skills/docs/product-intelligence-workflow.md +1 -0
  283. package/skills/docs/product-signal-workflow.md +1 -0
  284. package/skills/docs/research-workflow.md +54 -37
  285. package/skills/docs/runbook-workflow.md +1 -0
  286. package/skills/docs/strategy-workflow.md +1 -0
  287. package/skills/docs/user-research-workflow.md +40 -0
  288. package/skills/operating/orchestration-reference.md +1 -1
  289. package/skills/roles/architect.md +5 -0
  290. package/skills/roles/operator.docs.md +4 -0
  291. package/skills/routing.md +4 -2
  292. package/specialists/artifact-manifest.json +480 -0
  293. package/specialists/artifact-manifest.schema.json +53 -0
  294. package/specialists/audit-enrichments.json +454 -0
  295. package/specialists/contracts.json +37 -25
  296. package/specialists/prompts/_shared/validation-contract.md +26 -0
  297. package/specialists/prompts/cx-accessibility.md +21 -2
  298. package/specialists/prompts/cx-ai-engineer.md +24 -1
  299. package/specialists/prompts/cx-architect.md +4 -1
  300. package/specialists/prompts/cx-business-strategist.md +23 -2
  301. package/specialists/prompts/cx-data-analyst.md +22 -1
  302. package/specialists/prompts/cx-data-engineer.md +23 -2
  303. package/specialists/prompts/cx-debugger.md +21 -2
  304. package/specialists/prompts/cx-designer.md +26 -3
  305. package/specialists/prompts/cx-devil-advocate.md +19 -2
  306. package/specialists/prompts/cx-docs-keeper.md +28 -1
  307. package/specialists/prompts/cx-engineer.md +22 -1
  308. package/specialists/prompts/cx-evaluator.md +18 -1
  309. package/specialists/prompts/cx-explorer.md +21 -2
  310. package/specialists/prompts/cx-legal-compliance.md +21 -2
  311. package/specialists/prompts/cx-operations.md +21 -2
  312. package/specialists/prompts/cx-oracle.md +94 -0
  313. package/specialists/prompts/cx-orchestrator.md +20 -1
  314. package/specialists/prompts/cx-platform-engineer.md +25 -2
  315. package/specialists/prompts/cx-product-manager.md +32 -1
  316. package/specialists/prompts/cx-qa.md +24 -2
  317. package/specialists/prompts/cx-rd-lead.md +23 -2
  318. package/specialists/prompts/cx-release-manager.md +23 -2
  319. package/specialists/prompts/cx-researcher.md +22 -2
  320. package/specialists/prompts/cx-reviewer.md +18 -1
  321. package/specialists/prompts/cx-security.md +22 -2
  322. package/specialists/prompts/cx-sre.md +30 -3
  323. package/specialists/prompts/cx-test-automation.md +7 -1
  324. package/specialists/prompts/cx-trace-reviewer.md +22 -3
  325. package/specialists/prompts/cx-ux-researcher.md +21 -2
  326. package/specialists/registry.json +52 -173
  327. package/specialists/tone-profiles.json +42 -0
  328. package/templates/demos/playwright/demo-recording.config.mjs +47 -0
  329. package/templates/demos/recordings/agentic-platforms-prd.json +19 -0
  330. package/templates/demos/scripts/agentic-platforms-prd.json +44 -0
  331. package/templates/demos/specs/_helpers/scroll-artifact.ts +89 -0
  332. package/templates/demos/tapes/agentic-platforms-prd.tape +49 -0
  333. package/templates/demos/tapes/resource-guard-rails.tape +49 -0
  334. package/templates/demos/vhs/construct-cockpit.json +24 -0
  335. package/templates/distribution/construct-brand.typ +446 -0
  336. package/templates/distribution/construct-decision.typ +38 -0
  337. package/templates/distribution/construct-deck.html +95 -0
  338. package/templates/distribution/construct-pdf.typ +38 -0
  339. package/templates/distribution/construct-prd.typ +38 -0
  340. package/templates/distribution/construct-research.typ +38 -0
  341. package/templates/distribution/construct-web.html +92 -0
  342. package/templates/distribution/fonts/Geist-Bold.ttf +0 -0
  343. package/templates/distribution/fonts/Geist-Medium.ttf +0 -0
  344. package/templates/distribution/fonts/Geist-Regular.ttf +0 -0
  345. package/templates/distribution/fonts/Geist-SemiBold.ttf +0 -0
  346. package/templates/distribution/fonts/GeistMono-Medium.ttf +0 -0
  347. package/templates/distribution/fonts/GeistMono-Regular.ttf +0 -0
  348. package/templates/distribution/fonts/GeistMono-SemiBold.ttf +0 -0
  349. package/templates/distribution/fonts/IBMPlexMono-Regular.otf +0 -0
  350. package/templates/distribution/fonts/JetBrainsMono-Medium.ttf +0 -0
  351. package/templates/distribution/fonts/JetBrainsMono-Regular.ttf +0 -0
  352. package/templates/distribution/fonts/JetBrainsMono-SemiBold.ttf +0 -0
  353. package/templates/distribution/fonts/PlusJakartaSans-Bold.ttf +0 -0
  354. package/templates/distribution/fonts/PlusJakartaSans-Medium.ttf +0 -0
  355. package/templates/distribution/fonts/PlusJakartaSans-Regular.ttf +0 -0
  356. package/templates/distribution/fonts/PlusJakartaSans-SemiBold.ttf +0 -0
  357. package/templates/distribution/fonts/README.md +36 -0
  358. package/templates/distribution/fonts/SpaceGrotesk-Variable.ttf +0 -0
  359. package/templates/distribution/fonts/handwritten/Caveat.ttf +0 -0
  360. package/templates/distribution/fonts/legacy/IBMPlexMono-Regular.otf +0 -0
  361. package/templates/distribution/fonts/legacy/Inter-Medium.otf +0 -0
  362. package/templates/distribution/fonts/legacy/Inter-Regular.otf +0 -0
  363. package/templates/distribution/fonts/legacy/Inter-SemiBold.otf +0 -0
  364. package/templates/distribution/fonts/legacy/InterDisplay-SemiBold.otf +0 -0
  365. package/templates/distribution/fonts/legacy/SourceSerif4-Regular.otf +0 -0
  366. package/templates/distribution/fonts/legacy/SourceSerif4-Semibold.otf +0 -0
  367. package/templates/distribution/icons/activity.svg +15 -0
  368. package/templates/distribution/icons/alert-triangle.svg +17 -0
  369. package/templates/distribution/icons/book-open.svg +16 -0
  370. package/templates/distribution/icons/bot.svg +20 -0
  371. package/templates/distribution/icons/brain.svg +22 -0
  372. package/templates/distribution/icons/circle-check.svg +16 -0
  373. package/templates/distribution/icons/clipboard-check.svg +17 -0
  374. package/templates/distribution/icons/cpu.svg +28 -0
  375. package/templates/distribution/icons/database.svg +17 -0
  376. package/templates/distribution/icons/eye.svg +16 -0
  377. package/templates/distribution/icons/file-text.svg +19 -0
  378. package/templates/distribution/icons/gauge.svg +16 -0
  379. package/templates/distribution/icons/git-branch.svg +17 -0
  380. package/templates/distribution/icons/key.svg +17 -0
  381. package/templates/distribution/icons/layers.svg +17 -0
  382. package/templates/distribution/icons/list-checks.svg +19 -0
  383. package/templates/distribution/icons/lock.svg +16 -0
  384. package/templates/distribution/icons/message-square.svg +15 -0
  385. package/templates/distribution/icons/network.svg +19 -0
  386. package/templates/distribution/icons/route.svg +17 -0
  387. package/templates/distribution/icons/scale.svg +19 -0
  388. package/templates/distribution/icons/search.svg +16 -0
  389. package/templates/distribution/icons/send.svg +16 -0
  390. package/templates/distribution/icons/server.svg +18 -0
  391. package/templates/distribution/icons/shield-check.svg +16 -0
  392. package/templates/distribution/icons/users.svg +18 -0
  393. package/templates/distribution/icons/webhook.svg +17 -0
  394. package/templates/distribution/icons/workflow.svg +17 -0
  395. package/templates/distribution/icons/wrench.svg +15 -0
  396. package/templates/distribution/run.mjs +18 -1
  397. package/templates/docs/adr.md +7 -0
  398. package/templates/docs/construct_guide.md +8 -8
  399. package/templates/docs/customer-profile.md +4 -0
  400. package/templates/docs/one-pager.md +4 -0
  401. package/templates/docs/postmortem.md +31 -0
  402. package/templates/docs/prd-platform.md +19 -0
  403. package/templates/docs/prd.md +15 -0
  404. package/templates/docs/prfaq.md +7 -0
  405. package/templates/docs/qa-strategy.md +103 -0
  406. package/templates/docs/research-brief.md +8 -0
  407. package/templates/docs/rfc-platform.md +12 -0
  408. package/templates/docs/test-plan.md +7 -0
@@ -8,15 +8,20 @@
8
8
  * 2. which external MCP servers are `enabled` in opencode.json — sync disables
9
9
  * the heavy ones for local-capable setups (they cannot be trimmed at runtime).
10
10
  * Holds the shared token-sizing helper and the external-server id list both
11
- * levers reason about.
11
+ * levers reason about, plus the owned-loop tool-budget filter that trims the
12
+ * Vercel AI SDK tool set to a compiled execution policy's allowed groups and
13
+ * schema cap (lib/models/execution-policy.mjs).
12
14
  */
13
15
 
16
+ import { getMeasuredHeavyExternalMcpTokenCost } from './external-schema-cost.mjs';
17
+
14
18
  const TOKENS_PER_CHAR = 0.25;
15
19
 
16
20
  // Heavy external MCP servers Construct registers globally. On a local-capable
17
- // setup these alone serialize ~12k tokens into every agent's window (including
18
- // the built-in Build/Plan agents) and cannot be filtered per request, so sync
19
- // disables them in opencode.json. construct-mcp's own knowledge_search /
21
+ // setup these serialize a measured ~37k tokens of tool schema into every agent's
22
+ // window (github alone ~30k as of 2026-06-22; see tests/fixtures/mcp-tool-schemas/
23
+ // and scripts/measure-external-mcp-schemas.mjs) and cannot be filtered per request,
24
+ // so sync disables them in opencode.json. construct-mcp's own knowledge_search /
20
25
  // memory_search cover the search/memory cases.
21
26
 
22
27
  export const HEAVY_EXTERNAL_MCP_IDS = ['context7', 'github', 'memory', 'sequential-thinking', 'playwright'];
@@ -29,6 +34,7 @@ export const LOCAL_SURFACE_MODES = ['auto', 'on', 'off'];
29
34
 
30
35
  export function isLocalModel(model) {
31
36
  const m = (model || "").toLowerCase();
37
+ if (m.startsWith("local/")) return true;
32
38
  return m.includes("ollama") || m.includes("localhost") || m.includes("127.0.0.1");
33
39
  }
34
40
 
@@ -51,3 +57,48 @@ export function estimateToolTokens(tools) {
51
57
  const arr = Array.isArray(tools) ? tools : Object.values(tools);
52
58
  return Math.round(JSON.stringify(arr).length * TOKENS_PER_CHAR);
53
59
  }
60
+
61
+ export { getMeasuredHeavyExternalMcpTokenCost };
62
+
63
+ // Maps the owned loop's agent tool names (apps/chat/engine/tools/registry.mjs)
64
+ // onto the execution-policy tool groups (lib/models/execution-policy.mjs) so a
65
+ // compiled policy can trim the serialized tool surface for a capability-
66
+ // constrained model. construct_tool is the single MCP gateway, so it stands in
67
+ // for the whole 'construct' group. An unrecognized name falls back to
68
+ // 'construct' — a group present in every tier — so an unclassified tool is never
69
+ // silently dropped from a constrained surface.
70
+
71
+ const AGENT_TOOL_GROUPS = Object.freeze({
72
+ read: 'read',
73
+ glob: 'search',
74
+ grep: 'search',
75
+ write: 'edit',
76
+ edit: 'edit',
77
+ shell: 'shell',
78
+ construct_tool: 'construct',
79
+ });
80
+
81
+ export function toolGroupForName(name) {
82
+ return AGENT_TOOL_GROUPS[name] || 'construct';
83
+ }
84
+
85
+ // Trim a tool set to the compiled policy's envelope: drop any tool whose group is
86
+ // outside allowedToolGroups, then cap the survivors at maxToolSchemas in stable
87
+ // insertion order. A null allowedToolGroups means "no group filter" and an
88
+ // unbounded cap means "no count limit", so the rich/hosted-direct envelope (all
89
+ // groups, cap 32) returns the input set unchanged.
90
+
91
+ export function applyToolBudget(tools, { allowedToolGroups = null, maxToolSchemas = Infinity } = {}) {
92
+ if (!tools || typeof tools !== 'object') return tools;
93
+ const allow = Array.isArray(allowedToolGroups) ? new Set(allowedToolGroups) : null;
94
+ const cap = Number.isFinite(maxToolSchemas) ? Math.max(0, Math.floor(maxToolSchemas)) : Infinity;
95
+ const out = {};
96
+ let kept = 0;
97
+ for (const [name, def] of Object.entries(tools)) {
98
+ if (allow && !allow.has(toolGroupForName(name))) continue;
99
+ if (kept >= cap) break;
100
+ out[name] = def;
101
+ kept += 1;
102
+ }
103
+ return out;
104
+ }
@@ -1,17 +1,31 @@
1
1
  /**
2
2
  * lib/mcp/tools/document.mjs — Document-handling MCP tools: extract text, ingest, infer schema, list schema artifacts.
3
3
  *
4
- * All functions are async-safe; extractDocumentText and listSchemaArtifactsTool are synchronous.
4
+ * All functions are async-safe; listSchemaArtifactsTool is synchronous.
5
5
  * Depends on lib/document-extract.mjs, lib/document-ingest.mjs, lib/schema-infer.mjs, lib/schema-artifact.mjs.
6
6
  */
7
7
  import { existsSync } from 'node:fs';
8
8
  import { resolve } from 'node:path';
9
- import { extractDocumentText as extractLocalDocumentText } from '../../document-extract.mjs';
9
+ import {
10
+ extractDocumentTextAsync,
11
+ extractDocumentTextNodeNative,
12
+ } from '../../document-extract.mjs';
10
13
  import { ingestDocuments } from '../../document-ingest.mjs';
11
14
  import { inferDocumentSchema, inferUnifiedSchema } from '../../schema-infer.mjs';
12
15
  import { writeSchemaArtifact, listSchemaArtifacts, SCHEMA_DIR } from '../../schema-artifact.mjs';
13
16
 
14
- export function extractDocumentText(args) {
17
+ async function extractViaIngestPipeline(filePath, { maxChars }) {
18
+ try {
19
+ return await extractDocumentTextNodeNative(filePath, { maxChars });
20
+ } catch (err) {
21
+ if (err.code === 'OFFICE_REQUIRES_DOCLING' || err.code === 'PDF_REQUIRES_DOCLING') {
22
+ return extractDocumentTextAsync(filePath, { maxChars });
23
+ }
24
+ throw err;
25
+ }
26
+ }
27
+
28
+ export async function extractDocumentText(args) {
15
29
  const filePath = resolve(String(args.file_path || ''));
16
30
  const maxChars = Number.isFinite(Number(args.max_chars)) && Number(args.max_chars) > 0
17
31
  ? Math.min(Number(args.max_chars), 200_000)
@@ -22,7 +36,7 @@ export function extractDocumentText(args) {
22
36
  }
23
37
 
24
38
  try {
25
- const extracted = extractLocalDocumentText(filePath, { maxChars });
39
+ const extracted = await extractViaIngestPipeline(filePath, { maxChars });
26
40
  return {
27
41
  file_path: extracted.filePath,
28
42
  extension: extracted.extension,
@@ -8,9 +8,17 @@
8
8
  import { readFileSync, readdirSync, existsSync } from 'node:fs';
9
9
  import { join } from 'node:path';
10
10
  import { execFileSync } from 'node:child_process';
11
- import { routeRequest, routeRequestVerified, requiresExecutiveApproval, TERMINAL_STATES } from '../../orchestration-policy.mjs';
11
+ import {
12
+ buildConstructToOrchestratorPacket,
13
+ routeRequest,
14
+ routeRequestVerified,
15
+ requiresExecutiveApproval,
16
+ TERMINAL_STATES,
17
+ } from '../../orchestration-policy.mjs';
12
18
  import { buildTaskPacketFromIntent } from '../../workflow-state.mjs';
13
19
  import { logSkillCall } from '../../telemetry/skill-calls.mjs';
20
+ import { templateMetadata } from '../../artifact-manifest.mjs';
21
+ import { buildSpecialistCatalog } from '../../specialists/roster.mjs';
14
22
 
15
23
  export function listSkills({ ROOT_DIR }) {
16
24
  const skillsDir = join(ROOT_DIR, 'skills');
@@ -120,14 +128,21 @@ export function getTemplate(args, { ROOT_DIR }) {
120
128
  ];
121
129
  for (const { source, path: p } of candidates) {
122
130
  if (existsSync(p)) {
123
- return { source, path: p, content: readFileSync(p, 'utf8') };
131
+ const meta = templateMetadata(safe, { cwd, rootDir: ROOT_DIR });
132
+ return { source, path: p, content: readFileSync(p, 'utf8'), metadata: meta };
124
133
  }
125
134
  }
126
135
  return { error: `Template not found: ${safe}`, available: listTemplatesRaw({ ROOT_DIR }) };
127
136
  }
128
137
 
129
138
  export function listTemplates({ ROOT_DIR }) {
130
- return listTemplatesRaw({ ROOT_DIR });
139
+ const raw = listTemplatesRaw({ ROOT_DIR });
140
+ const cwd = process.cwd();
141
+ const metadata = {};
142
+ for (const name of [...new Set([...raw.shipped, ...raw.overridden])]) {
143
+ metadata[name] = templateMetadata(name, { cwd, rootDir: ROOT_DIR });
144
+ }
145
+ return { ...raw, metadata };
131
146
  }
132
147
 
133
148
  export async function agentContract(args = {}) {
@@ -171,6 +186,18 @@ export async function orchestrationPolicy(args) {
171
186
  const draftTask = route.track !== 'immediate'
172
187
  ? buildTaskPacketFromIntent(args?.request || '', { fileCount: args?.fileCount, moduleCount: args?.moduleCount })
173
188
  : null;
189
+ const handoffPacket = route.track !== 'immediate'
190
+ ? buildConstructToOrchestratorPacket({
191
+ request: args?.request || '',
192
+ route,
193
+ goal: args?.goal,
194
+ acceptanceCriteria: args?.acceptanceCriteria,
195
+ fileCount: args?.fileCount,
196
+ moduleCount: args?.moduleCount,
197
+ introducesContract: args?.introducesContract,
198
+ explicitDrive: args?.explicitDrive,
199
+ })
200
+ : null;
174
201
 
175
202
  // Per-specialist context packets: when the caller supplied a candidate
176
203
  // artifact list, build the role-filtered bundle each specialist should
@@ -249,9 +276,11 @@ export async function orchestrationPolicy(args) {
249
276
 
250
277
  return {
251
278
  ...route,
279
+ specialistCatalog: buildSpecialistCatalog(),
252
280
  approvalRequired,
253
281
  terminalStates: TERMINAL_STATES,
254
282
  draftTask,
283
+ handoffPacket,
255
284
  ...(contextPackets ? { contextPackets } : {}),
256
285
  ...(taskGraph ? { taskGraph } : {}),
257
286
  ...(intakeError ? { intakeError } : {}),
@@ -115,11 +115,23 @@ export function workflowValidate(args) {
115
115
  */
116
116
  export async function workflowContractValidate(args) {
117
117
  const { validateHandoff } = await import('../../contracts/validate.mjs');
118
+ const { enrichConstructOrchestratorHandoff } = await import('../../contracts/construct-handoff.mjs');
119
+ const contractId = args.id
120
+ ?? (args.producer === 'construct' && args.consumer === 'cx-orchestrator' ? 'construct-to-orchestrator' : null);
121
+ let artifact = args.artifact;
122
+ if (contractId === 'construct-to-orchestrator' && artifact && typeof artifact === 'object') {
123
+ artifact = enrichConstructOrchestratorHandoff(artifact, {
124
+ request: args.request,
125
+ fileCount: args.fileCount,
126
+ moduleCount: args.moduleCount,
127
+ introducesContract: args.introducesContract,
128
+ });
129
+ }
118
130
  return validateHandoff({
119
131
  producer: args.producer,
120
132
  consumer: args.consumer,
121
133
  id: args.id,
122
- artifact: args.artifact,
134
+ artifact,
123
135
  packet: args.packet,
124
136
  enforcement: args.enforcement || 'block',
125
137
  });
@@ -62,6 +62,7 @@ export async function pollFreeModels(apiKey) {
62
62
  (m) =>
63
63
  m?.pricing?.prompt === "0" && m?.pricing?.completion === "0"
64
64
  )
65
+ .filter(isTextOutputModel)
65
66
  .map((m) => ({
66
67
  id: `openrouter/${m.id}`,
67
68
  name: m.name ?? m.id,
@@ -72,6 +73,23 @@ export async function pollFreeModels(apiKey) {
72
73
  return models.sort((a, b) => score(b, "standard") - score(a, "standard"));
73
74
  }
74
75
 
76
+ // A coding/chat tier must emit text; image-generation models (output modality
77
+ // "image") are never valid tier defaults. OpenRouter reports modalities under
78
+ // `architecture`; absent metadata is treated as text so unknown models are not
79
+ // dropped. Vision models (image INPUT, text OUTPUT) remain eligible.
80
+
81
+ export function isTextOutputModel(model = {}) {
82
+ const arch = model.architecture || {};
83
+ let output = arch.output_modalities;
84
+ if (!Array.isArray(output) && typeof arch.modality === "string" && arch.modality.includes("->")) {
85
+ output = arch.modality.split("->")[1].split("+").map((s) => s.trim());
86
+ }
87
+ if (Array.isArray(output) && output.length) {
88
+ return output.includes("text") && !output.includes("image");
89
+ }
90
+ return true;
91
+ }
92
+
75
93
  /**
76
94
  * Score a model for a given tier (higher is better).
77
95
  * @param {object} model – Model object as returned by `pollFreeModels`.
@@ -1,233 +1,17 @@
1
1
  /**
2
- * lib/model-registry.mjs — Single source of truth for model tier resolution.
2
+ * lib/model-registry.mjs — compatibility re-exports for model tier resolution.
3
3
  *
4
- * Addresses model tier complexity by:
5
- * 1. ONE resolution path with clear precedence
6
- * 2. Validated at config time, not runtime
7
- * 3. Simple env-only overrides for emergencies
8
- * 4. Clear error messages when config is missing
4
+ * Canonical implementation lives in lib/model-router.mjs. This module keeps
5
+ * the legacy import path stable while CONSTRUCT_MODEL_* env aliases remain
6
+ * supported for one release cycle via normalizeEnvAssignments in the router.
9
7
  */
10
8
 
11
- import fs from 'node:fs';
12
- import path from 'node:path';
13
-
14
- // ---------------------------------------------------------------------------
15
- // Configuration precedence (single source of truth)
16
- // ---------------------------------------------------------------------------
17
-
18
- /**
19
- * RESOLUTION ORDER (highest to lowest):
20
- * 1. Environment variables (CX_MODEL_*): Emergency overrides
21
- * 2. User config (~/.construct/config.env): Personal defaults
22
- * 3. Registry (specialists/registry.json): Project recommendations
23
- *
24
- * Construct ships with NO implicit model defaults (ADR-0027,
25
- * tests/model-router-no-defaults.test.mjs). When none of the three sources
26
- * resolves a tier, that tier returns null with source 'not configured'.
27
- * Callers must surface a clear remediation hint rather than silently
28
- * substituting a vendor-specific default.
29
- */
30
-
31
- const ENV_PREFIX = 'CX_MODEL';
32
-
33
- // ---------------------------------------------------------------------------
34
- // Core resolution function
35
- // ---------------------------------------------------------------------------
36
-
37
- export function resolveModelTiers(options = {}) {
38
- const {
39
- env = process.env,
40
- registryPath = null,
41
- strict = false, // If true, require all tiers to be configured
42
- } = options;
43
-
44
- const result = {
45
- reasoning: null,
46
- standard: null,
47
- fast: null,
48
- };
49
-
50
- const sources = {};
51
- const errors = [];
52
-
53
- for (const tier of ['reasoning', 'standard', 'fast']) {
54
- const envKey = `${ENV_PREFIX}_${tier.toUpperCase()}`;
55
-
56
- // Environment variable (highest priority)
57
- if (env[envKey]) {
58
- result[tier] = env[envKey];
59
- sources[tier] = 'env';
60
- continue;
61
- }
62
-
63
- // User config (read from env which was loaded by env-config)
64
- const configKey = `CONSTRUCT_MODEL_${tier.toUpperCase()}`;
65
- if (env[configKey]) {
66
- result[tier] = env[configKey];
67
- sources[tier] = 'config';
68
- continue;
69
- }
70
-
71
- // Registry
72
- if (registryPath && fs.existsSync(registryPath)) {
73
- try {
74
- const registry = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
75
- const registryModel = registry.models?.[tier]?.primary;
76
- if (registryModel) {
77
- result[tier] = registryModel;
78
- sources[tier] = 'registry';
79
- continue;
80
- }
81
- } catch (error) {
82
- errors.push(`Failed to read registry: ${error.message}`);
83
- }
84
- }
85
-
86
- // No source resolved this tier — leave null + 'not configured' so callers
87
- // can surface a clear remediation hint. Aligns with the wired path
88
- // (lib/model-router.mjs:resolveTierAssignments) and the no-defaults
89
- // contract locked in by tests/model-router-no-defaults.test.mjs.
90
-
91
- result[tier] = null;
92
- sources[tier] = 'not configured';
93
- }
94
-
95
- const configured = Object.values(result).filter(Boolean).length;
96
-
97
- if (strict && configured < 3) {
98
- const missing = Object.entries(result)
99
- .filter(([, v]) => !v)
100
- .map(([k]) => k);
101
- errors.push(`Missing configuration for tiers: ${missing.join(', ')}`);
102
- }
103
-
104
- return {
105
- models: result,
106
- sources,
107
- configured,
108
- complete: configured === 3,
109
- errors: errors.length > 0 ? errors : null,
110
- };
111
- }
112
-
113
- // ---------------------------------------------------------------------------
114
- // Quick access helpers
115
- // ---------------------------------------------------------------------------
116
-
117
- export function getModelForTier(tier, options = {}) {
118
- const resolved = resolveModelTiers(options);
119
- return resolved.models[tier];
120
- }
121
-
122
- export function getModelSource(tier, options = {}) {
123
- const resolved = resolveModelTiers(options);
124
- return resolved.sources[tier] || 'unknown';
125
- }
126
-
127
- // ---------------------------------------------------------------------------
128
- // Configuration helpers
129
- // ---------------------------------------------------------------------------
130
-
131
- export function validateModelTiers(options = {}) {
132
- const resolved = resolveModelTiers({ ...options, strict: true });
133
- const unconfigured = Object.entries(resolved.sources)
134
- .filter(([, source]) => source === 'not configured')
135
- .map(([tier]) => tier);
136
-
137
- return {
138
- valid: resolved.complete && !resolved.errors,
139
- errors: resolved.errors,
140
- warnings: unconfigured.length > 0
141
- ? [`Unconfigured tier${unconfigured.length === 1 ? '' : 's'}: ${unconfigured.join(', ')}. Run 'construct models --apply' or set CX_MODEL_<TIER>.`]
142
- : [],
143
- resolution: resolved,
144
- };
145
- }
146
-
147
- export function formatModelStatus(options = {}) {
148
- const resolved = resolveModelTiers(options);
149
-
150
- let output = 'Model Configuration:\n\n';
151
-
152
- for (const tier of ['reasoning', 'standard', 'fast']) {
153
- const model = resolved.models[tier];
154
- const source = resolved.sources[tier];
155
- const icon = source === 'not configured' ? '⚠' : '✓';
156
- const display = model ?? '(not configured)';
157
-
158
- output += `${icon} ${tier.padEnd(10)} ${display}\n`;
159
- output += ` Source: ${source}\n\n`;
160
- }
161
-
162
- const unconfigured = Object.entries(resolved.sources)
163
- .filter(([, source]) => source === 'not configured')
164
- .map(([tier]) => tier);
165
- if (unconfigured.length > 0) {
166
- output += `\nUnconfigured: ${unconfigured.join(', ')}. To configure:\n`;
167
- output += ' construct models --apply (interactive, picks from available providers)\n';
168
- output += ' export CX_MODEL_REASONING=... (env override)\n';
169
- output += ' export CX_MODEL_STANDARD=...\n';
170
- output += ' export CX_MODEL_FAST=...\n';
171
- }
172
-
173
- return output;
174
- }
175
-
176
- // ---------------------------------------------------------------------------
177
- // Backwards compatibility
178
- // ---------------------------------------------------------------------------
179
-
180
- /**
181
- * Legacy wrapper for code that expects the old interface.
182
- * New code should use resolveModelTiers() directly.
183
- */
184
- export function readCurrentModels(envPath, registryModels = {}) {
185
- // Maintains backwards compatibility with existing code
186
- // while using the new resolution logic internally
187
-
188
- const env = {};
189
-
190
- // Load from env file if provided
191
- if (envPath && fs.existsSync(envPath)) {
192
- const content = fs.readFileSync(envPath, 'utf8');
193
- for (const line of content.split('\n')) {
194
- const match = line.match(/^([^=]+)=(.*)$/);
195
- if (match) {
196
- env[match[1].trim()] = match[2].trim();
197
- }
198
- }
199
- }
200
-
201
- // Merge with process.env
202
- Object.assign(env, process.env);
203
-
204
- const resolved = resolveModelTiers({ env });
205
-
206
- // Return in legacy format
207
- return {
208
- reasoning: resolved.models.reasoning,
209
- standard: resolved.models.standard,
210
- fast: resolved.models.fast,
211
- // Legacy code expects these fields
212
- _resolved: resolved,
213
- };
214
- }
215
-
216
- export function setTierModel(tier, model, envPath) {
217
- // Simplified interface for setting a model
218
- // In practice, this should update the user config file
219
-
220
- if (!['reasoning', 'standard', 'fast'].includes(tier)) {
221
- throw new Error(`Invalid tier: ${tier}. Must be reasoning, standard, or fast.`);
222
- }
223
-
224
- const envKey = `${ENV_PREFIX}_${tier.toUpperCase()}`;
225
-
226
- return {
227
- success: true,
228
- tier,
229
- model,
230
- envKey,
231
- instruction: `Add to your shell profile: export ${envKey}="${model}"`,
232
- };
233
- }
9
+ export {
10
+ resolveModelTiers,
11
+ getModelForTier,
12
+ getModelSource,
13
+ validateModelTiers,
14
+ formatModelStatus,
15
+ readCurrentModels,
16
+ setTierModel,
17
+ } from './model-router.mjs';