@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
@@ -10,6 +10,7 @@ import { spawnSync } from 'node:child_process';
10
10
  import os from 'node:os';
11
11
  import path from 'node:path';
12
12
  import { existsSync, readFileSync } from 'node:fs';
13
+ import { extractOpRef, hasAnySecret, hasSecret } from './providers/secret-resolver.mjs';
13
14
 
14
15
  const isWindows = os.platform() === 'win32';
15
16
 
@@ -165,37 +166,73 @@ export async function checkPrerequisites(options = {}) {
165
166
  }
166
167
 
167
168
  /**
168
- * Get a value from shell env by checking common sources.
169
- * Looks in process.env project .env ~/.env ~/.construct/config.env
170
- * shell rc files for op:// refs (resolved via 'op read').
169
+ * Read a credential raw value from dotenv files and shell rc without invoking
170
+ * `op read`. Returns op:// references as-is so callers can persist or report
171
+ * presence without biometric prompts (ADR-0042).
171
172
  */
172
- function getEnvFromShell(varName, homeDir) {
173
+ function readCredentialRaw(varName, homeDir) {
173
174
  if (process.env[varName]) return process.env[varName];
174
175
 
175
- const projectEnv = path.join(process.cwd(), '.env');
176
- if (existsSync(projectEnv)) {
176
+ for (const envPath of [
177
+ path.join(process.cwd(), '.env'),
178
+ path.join(homeDir, '.env'),
179
+ path.join(homeDir, '.construct', 'config.env'),
180
+ ]) {
181
+ if (!existsSync(envPath)) continue;
177
182
  try {
178
- const content = readFileSync(projectEnv, 'utf8');
183
+ const content = readFileSync(envPath, 'utf8');
179
184
  const m = content.match(new RegExp(`^${varName}=["']?(.+?)["']?$`, 'm'));
180
185
  if (m) return m[1].trim();
181
186
  } catch { /* skip */ }
182
187
  }
183
188
 
184
- const homeEnv = path.join(homeDir, '.env');
185
- if (existsSync(homeEnv)) {
189
+ const shellFiles = [
190
+ path.join(homeDir, '.zshrc'),
191
+ path.join(homeDir, '.bashrc'),
192
+ path.join(homeDir, '.bash_profile'),
193
+ path.join(homeDir, '.profile'),
194
+ ];
195
+ for (const rcPath of shellFiles) {
196
+ if (!existsSync(rcPath)) continue;
186
197
  try {
187
- const content = readFileSync(homeEnv, 'utf8');
188
- const m = content.match(new RegExp(`^${varName}=["']?(.+?)["']?$`, 'm'));
189
- if (m) return m[1].trim();
198
+ const content = readFileSync(rcPath, 'utf8');
199
+ const opRe = new RegExp(`export\\s+${varName}=["']?\\$\\(op read '([^']+)'\\)["']?`, 'm');
200
+ const opMatch = content.match(opRe);
201
+ if (opMatch) return opMatch[1];
202
+ const directRe = new RegExp(`^\\s*export\\s+${varName}=(.+)$`, 'm');
203
+ const directMatch = content.match(directRe);
204
+ if (directMatch) return directMatch[1].trim().replace(/^["']|["']$/g, '');
190
205
  } catch { /* skip */ }
191
206
  }
192
207
 
193
- const cfgPath = path.join(homeDir, '.construct', 'config.env');
194
- if (existsSync(cfgPath)) {
208
+ return null;
209
+ }
210
+
211
+ /**
212
+ * Describe where a credential is configured without resolving op:// refs.
213
+ */
214
+ export function describeCredentialPresence(varName, { homeDir = os.homedir(), cwd = process.cwd() } = {}) {
215
+ const sources = [];
216
+ const direct = process.env[varName];
217
+ if (direct) {
218
+ const ref = extractOpRef(direct);
219
+ sources.push(ref ? `process.env (op:// ref)` : `process.env (plain value)`);
220
+ }
221
+
222
+ for (const envPath of [
223
+ path.join(cwd, '.env'),
224
+ path.join(homeDir, '.env'),
225
+ path.join(homeDir, '.construct', 'config.env'),
226
+ ]) {
227
+ if (!existsSync(envPath)) continue;
195
228
  try {
196
- const content = readFileSync(cfgPath, 'utf8');
197
- const m = content.match(new RegExp(`^${varName}=(.+)$`, 'm'));
198
- if (m) return m[1].trim();
229
+ const content = readFileSync(envPath, 'utf8');
230
+ const m = content.match(new RegExp(`^${varName}=["']?(.+?)["']?$`, 'm'));
231
+ if (m) {
232
+ const ref = extractOpRef(m[1]);
233
+ const label = envPath.replace(homeDir, '~');
234
+ sources.push(ref ? `${label} (op:// ref)` : `${label} (plain value)`);
235
+ }
199
236
  } catch { /* skip */ }
200
237
  }
201
238
 
@@ -209,47 +246,18 @@ function getEnvFromShell(varName, homeDir) {
209
246
  if (!existsSync(rcPath)) continue;
210
247
  try {
211
248
  const content = readFileSync(rcPath, 'utf8');
212
- const re = new RegExp(`export\\s+${varName}=["']?\\$\\(op read '([^']+)'\\)["']?`, 'm');
213
- const m = content.match(re);
214
- if (m) {
215
- const opRef = m[1];
216
- try {
217
- const result = spawnSync('op', ['read', opRef], {
218
- encoding: 'utf8',
219
- stdio: ['pipe', 'pipe', 'pipe'],
220
- timeout: 5000,
221
- });
222
- if (result.status === 0) return result.stdout.trim();
223
- } catch { /* op read failed */ }
249
+ const opRe = new RegExp(`export\\s+${varName}=["']?\\$\\(op read '([^']+)'\\)["']?`, 'm');
250
+ if (opRe.test(content)) {
251
+ sources.push(`${rcPath.replace(homeDir, '~')} (op read in rc — use op:// in config.env instead)`);
224
252
  }
225
- } catch { /* skip unreadable files */ }
226
- }
227
-
228
- // If we found an op:// reference but couldn't read it, log the error
229
- // for diagnostic purposes when DEBUG is on.
230
- if (process.env.CONSTRUCT_DEBUG_CREDENTIALS === '1') {
231
- for (const rcPath of shellFiles) {
232
- if (!existsSync(rcPath)) continue;
233
- try {
234
- const content = readFileSync(rcPath, 'utf8');
235
- const re = new RegExp(`export\\s+${varName}=["']?\\$\\(op read '([^']+)'\\)["']?`, 'm');
236
- const m = content.match(re);
237
- if (m) {
238
- try {
239
- const result = spawnSync('op', ['read', m[1]], { encoding: 'utf8', timeout: 5000 });
240
- if (result.status !== 0) {
241
- const errMsg = (result.stderr || '').trim().slice(0, 200);
242
- process.stderr.write(`[credentials] ${varName}: op read '${m[1]}' failed: ${errMsg}\n`);
243
- }
244
- } catch (e) {
245
- process.stderr.write(`[credentials] ${varName}: op CLI error: ${e.message}\n`);
246
- }
247
- }
248
- } catch { /* skip */ }
249
- }
253
+ const directRe = new RegExp(`^\\s*export\\s+${varName}=`, 'm');
254
+ if (directRe.test(content) && !sources.some((s) => s.includes(rcPath.replace(homeDir, '~')))) {
255
+ sources.push(`${rcPath.replace(homeDir, '~')} (export found)`);
256
+ }
257
+ } catch { /* skip */ }
250
258
  }
251
259
 
252
- return null;
260
+ return { configured: hasSecret(varName, { env: process.env, cwd }), sources };
253
261
  }
254
262
 
255
263
  /**
@@ -287,30 +295,34 @@ const CREDENTIAL_VARS = [
287
295
  ];
288
296
 
289
297
  /**
290
- * Resolve all known credentials from env, .env files, and 1Password.
298
+ * Link discovered credential references into ~/.construct/config.env without
299
+ * invoking `op read`. Plain values and op:// refs are persisted as found.
291
300
  * @param {Object} options
292
301
  * @param {string} options.homeDir
293
- * @param {boolean} options.writeConfig - Write resolved keys to config.env (default: true)
294
- * @returns {Promise<Record<string, string>>} resolved key-value pairs
302
+ * @param {boolean} options.writeConfig - Write keys to config.env (default: false)
303
+ * @returns {Promise<Record<string, string>>} raw values linked (never plaintext from op)
295
304
  */
296
305
  export async function resolveCredentials(options = {}) {
297
- const { homeDir = os.homedir(), writeConfig = true } = options;
298
- const resolved = {};
306
+ const { homeDir = os.homedir(), writeConfig = false } = options;
307
+ const linked = {};
308
+ const { parseEnvFile, getUserEnvPath, writeEnvValues } = await import('./env-config.mjs');
309
+ const existing = parseEnvFile(getUserEnvPath(homeDir));
299
310
 
300
311
  for (const varName of CREDENTIAL_VARS) {
301
- const value = getEnvFromShell(varName, homeDir);
302
- if (value) resolved[varName] = value;
312
+ if (existing[varName]) continue;
313
+ const raw = readCredentialRaw(varName, homeDir);
314
+ if (!raw) continue;
315
+ const ref = extractOpRef(raw);
316
+ linked[varName] = ref || raw;
303
317
  }
304
318
 
305
- // Write resolved keys to config.env so subprocesses and the dashboard find them
306
- if (writeConfig && Object.keys(resolved).length > 0) {
319
+ if (writeConfig && Object.keys(linked).length > 0) {
307
320
  try {
308
- const { writeEnvValues, getUserEnvPath } = await import('./env-config.mjs');
309
- writeEnvValues(getUserEnvPath(homeDir), resolved);
321
+ writeEnvValues(getUserEnvPath(homeDir), linked);
310
322
  } catch { /* best effort */ }
311
323
  }
312
324
 
313
- return resolved;
325
+ return linked;
314
326
  }
315
327
 
316
328
  /**
@@ -327,22 +339,24 @@ export function detectCredentials(options = {}) {
327
339
  const ghAuth = spawnSync('gh', ['auth', 'status'], { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
328
340
  found.github_cli = ghAuth.status === 0;
329
341
 
330
- // GitHub Copilot (check VS Code extension state)
342
+ // GitHub Copilot: a device-flow credential (Construct's own store or the shared
343
+ // github-copilot store) is the usable signal; the VS Code extension and the
344
+ // copilot CLI are softer fallbacks.
345
+ const copilotAuthStore = existsSync(path.join(homeDir, '.construct', 'auth', 'github-copilot.json')) ||
346
+ existsSync(path.join(homeDir, '.config', 'github-copilot', 'apps.json')) ||
347
+ existsSync(path.join(homeDir, '.config', 'github-copilot', 'hosts.json'));
331
348
  const copilotDir = path.join(homeDir, '.vscode', 'extensions');
332
349
  const copilotExists = existsSync(copilotDir) && existsSync(path.join(homeDir, '.config', 'Code', 'User', 'globalStorage', 'github.copilot'));
333
- found.copilot = copilotExists || commandExists('copilot');
350
+ found.copilot = copilotAuthStore || copilotExists || commandExists('copilot');
334
351
 
335
352
  // OpenRouter API key
336
- found.openrouter_key = !!getEnvFromShell('OPENROUTER_API_KEY', homeDir);
353
+ found.openrouter_key = hasAnySecret(['OPENROUTER_API_KEY', 'OPEN_ROUTER_API_KEY'], { env: process.env, cwd: process.cwd() });
337
354
 
338
- // Anthropic API key
339
- found.anthropic_key = !!getEnvFromShell('ANTHROPIC_API_KEY', homeDir);
355
+ found.anthropic_key = hasAnySecret(['ANTHROPIC_API_KEY'], { env: process.env, cwd: process.cwd() });
340
356
 
341
- // OpenAI API key
342
- found.openai_key = !!getEnvFromShell('OPENAI_API_KEY', homeDir);
357
+ found.openai_key = hasAnySecret(['OPENAI_API_KEY'], { env: process.env, cwd: process.cwd() });
343
358
 
344
- // GitHub token
345
- found.github_token = !!getEnvFromShell('GITHUB_TOKEN', homeDir) || !!getEnvFromShell('GH_TOKEN', homeDir);
359
+ found.github_token = hasAnySecret(['GITHUB_TOKEN', 'GH_TOKEN'], { env: process.env, cwd: process.cwd() });
346
360
 
347
361
  // VS Code installed
348
362
  found.vscode = commandExists('code');
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * lib/hooks/artifact-release-gate.mjs — PostToolUse advisory structure/visual gate for typed docs.
4
+ *
5
+ * Runs after Write/Edit/MultiEdit on docs/** and .cx/research/** markdown artifacts.
6
+ * Surfaces manifest structure and visual violations on stderr during drafting; does not
7
+ * block the edit (full release gate runs via `construct artifact validate` and CI).
8
+ *
9
+ * Notice-only: suppressed when stderr is not a TTY, CI=true, or NODE_ENV=test.
10
+ *
11
+ * @lifecycle PostToolUse
12
+ * @matcher Write|Edit|MultiEdit
13
+ * @p95ms 80
14
+ * @maxBlockingScope none (PostToolUse, non-blocking)
15
+ * @exits 0 = pass
16
+ */
17
+
18
+ import { readFileSync } from 'node:fs';
19
+ import { checkArtifactGateNotice, formatArtifactGateNotice } from '../artifact-gate-notice.mjs';
20
+
21
+ function shouldEmitNotice() {
22
+ if (process.env.CI === 'true') return false;
23
+ if (process.env.NODE_ENV === 'test') return false;
24
+ if (!process.stderr.isTTY) return false;
25
+ return true;
26
+ }
27
+
28
+ let filePath = process.env.TOOL_INPUT_FILE_PATH;
29
+ let cwd = process.env.CLAUDE_PROJECT_DIR || process.cwd();
30
+ if (!filePath) {
31
+ try {
32
+ const input = JSON.parse(readFileSync(0, 'utf8'));
33
+ filePath = input?.tool_input?.file_path || input?.tool_input?.path;
34
+ if (input?.cwd) cwd = input.cwd;
35
+ } catch { /* no stdin */ }
36
+ }
37
+ if (!filePath) process.exit(0);
38
+
39
+ const result = checkArtifactGateNotice(filePath, { cwd });
40
+ if (!result || !shouldEmitNotice()) process.exit(0);
41
+
42
+ process.stderr.write(formatArtifactGateNotice(result));
43
+ process.exit(0);
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * lib/hooks/brand-prose-lint.mjs — PostToolUse hook: block brand drift at write time.
4
+ *
5
+ * Enforces marketing voice, retired typography, and Construct/cli naming on governed
6
+ * markdown and template surfaces. Mirrors scripts/audit/03d-brand.mjs scope.
7
+ *
8
+ * @lifecycle PostToolUse
9
+ * @matcher Write|Edit|MultiEdit
10
+ * @p95ms 40
11
+ * @maxBlockingScope PostToolUse
12
+ * @exits 0 = pass | 2 = block tool call
13
+ */
14
+
15
+ import { readFileSync } from 'node:fs';
16
+ import path from 'node:path';
17
+ import { fileURLToPath } from 'node:url';
18
+ import { formatBrandViolations, lintFile } from '../brand-prose.mjs';
19
+
20
+ let filePath = process.env.TOOL_INPUT_FILE_PATH;
21
+ if (!filePath) {
22
+ try {
23
+ const input = JSON.parse(readFileSync(0, 'utf8'));
24
+ filePath = input?.tool_input?.file_path || input?.tool_input?.path;
25
+ } catch {}
26
+ }
27
+ if (!filePath) process.exit(0);
28
+
29
+ const rootDir = path.resolve(fileURLToPath(import.meta.url), '..', '..', '..');
30
+ const result = lintFile(filePath, { rootDir });
31
+ if (!result.violations.length) process.exit(0);
32
+
33
+ const output = formatBrandViolations([result]);
34
+ process.stderr.write(`${output}\n`);
35
+ process.stderr.write(
36
+ '\nBrand policy blocked this edit (marketing voice, retired fonts, or Construct/cli naming). See docs/reference/branding.md and docs/STYLE.md.\n',
37
+ );
38
+ process.exit(2);
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * lib/hooks/graph-impact-advisory.mjs — PostToolUse hook: test-impact advisory on code edits.
4
+ *
5
+ * On Write|Edit under lib/, bin/, tests/, or src/, runs forward change-impact
6
+ * over the dependency graph and surfaces affected tests on stderr. Non-blocking;
7
+ * complements `construct impact` CLI and the doctor graph-staleness watcher.
8
+ *
9
+ * @p95ms 50
10
+ * @maxBlockingScope none (PostToolUse, non-blocking)
11
+ *
12
+ * @lifecycle PostToolUse
13
+ * @matcher Write|Edit|MultiEdit
14
+ * @exits 0 = pass
15
+ */
16
+
17
+ import { readFileSync } from 'node:fs';
18
+
19
+ import { computeImpact } from '../graph/impact.mjs';
20
+ import { checkGraphStaleness } from '../graph/staleness.mjs';
21
+
22
+ const CODE_RE = /^(lib|bin|src|tests)\/.*\.(?:m?js|jsx?|tsx?|cjs)$/;
23
+
24
+ let filePath = process.env.TOOL_INPUT_FILE_PATH;
25
+ let cwd = process.env.CLAUDE_PROJECT_DIR || process.cwd();
26
+ if (!filePath) {
27
+ try {
28
+ const input = JSON.parse(readFileSync(0, 'utf8'));
29
+ filePath = input?.tool_input?.file_path || input?.tool_input?.path;
30
+ if (input?.cwd) cwd = input.cwd;
31
+ } catch { /* stdin optional */ }
32
+ }
33
+ if (!filePath) process.exit(0);
34
+
35
+ const rel = filePath.startsWith(cwd)
36
+ ? filePath.slice(cwd.length).replace(/^\/+/, '')
37
+ : filePath;
38
+
39
+ if (!CODE_RE.test(rel)) process.exit(0);
40
+
41
+ const stale = checkGraphStaleness(cwd);
42
+ if (stale.stale) {
43
+ process.stderr.write(
44
+ `[graph-impact] dependency graph stale (${stale.staleReason}). Run \`construct graph build\` before trusting impact output.\n`,
45
+ );
46
+ }
47
+
48
+ const impact = computeImpact({ rootDir: cwd, changedFiles: [rel] });
49
+ if (!impact.graphPresent) {
50
+ process.stderr.write('[graph-impact] no .cx/graph/ present — run `construct graph build` to enable test-impact advisories.\n');
51
+ process.exit(0);
52
+ }
53
+
54
+ if (impact.affectedTests.length > 0) {
55
+ const preview = impact.affectedTests.slice(0, 5).map((t) => t.replace(/^test:/, '')).join(', ');
56
+ const more = impact.affectedTests.length > 5 ? ` (+${impact.affectedTests.length - 5} more)` : '';
57
+ process.stderr.write(`[graph-impact] ${rel} may affect: ${preview}${more}\n`);
58
+ } else if (impact.coverageGaps.length > 0) {
59
+ process.stderr.write(`[graph-impact] ${rel} has no capability edge in the dependency graph (coverage gap).\n`);
60
+ }
61
+
62
+ process.exit(0);