@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
@@ -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);
@@ -3,7 +3,7 @@
3
3
  * lib/hooks/session-optimize.mjs โ€” Session end optimization hook โ€” triggers agent optimization for low-performers.
4
4
  *
5
5
  * Runs as a Stop hook at session end. Checks recent performance reviews for agents with avgScore < 0.7
6
- * and invokes `construct optimize <agent>` to generate and apply improvement patches.
6
+ * and schedules `construct optimize <agent> --dry-run` detached so the Stop hook returns within budget.
7
7
  *
8
8
  * @p95ms 300
9
9
  * @maxBlockingScope Stop (non-blocking, async)
@@ -12,110 +12,102 @@
12
12
  * @matcher *
13
13
  * @exits 0 = pass
14
14
  */
15
- import { readFileSync, existsSync, writeFileSync, mkdirSync } from 'fs';
15
+ import { readFileSync, existsSync, writeFileSync, mkdirSync, readdirSync, statSync } from 'fs';
16
16
  import { join, dirname } from 'path';
17
17
  import { homedir } from 'os';
18
- import { spawnSync } from 'child_process';
18
+ import { spawn } from 'child_process';
19
19
  import { fileURLToPath } from 'url';
20
20
 
21
21
  const __dirname = dirname(fileURLToPath(import.meta.url));
22
22
  const CONSTRUCT_BIN = join(__dirname, '..', '..', 'bin', 'construct');
23
23
 
24
- const OPTIMIZATION_THRESHOLD = 0.7; // Score below this triggers optimization
25
- const MIN_TRACES_PER_AGENT = 3; // Minimum traces needed for optimization
26
- const OPTIMIZATION_DAYS = 7; // Look back 7 days for performance data
24
+ const OPTIMIZATION_THRESHOLD = 0.7;
25
+ const MIN_TRACES_PER_AGENT = 3;
26
+ const OPTIMIZATION_DAYS = 7;
27
27
  const OPTIMIZATION_LOG_DIR = join(homedir(), '.cx', 'optimization-logs');
28
+ const REVIEWS_DIR = join(homedir(), '.cx', 'performance-reviews');
28
29
 
29
- // Ensure optimization log directory exists
30
30
  mkdirSync(OPTIMIZATION_LOG_DIR, { recursive: true });
31
31
 
32
32
  let input = {};
33
33
  try { input = JSON.parse(readFileSync(0, 'utf8')); } catch { process.exit(0); }
34
34
 
35
- // Only run for Construct sessions
36
35
  const cwd = input?.cwd || process.cwd();
37
36
  const projectName = cwd.split('/').pop() || 'project';
38
37
 
39
- // Skip if this is not a Construct project or in global mode
40
38
  if (projectName !== 'construct' && !existsSync(join(cwd, '.cx'))) {
41
39
  process.exit(0);
42
40
  }
43
41
 
44
- // Reviews older than this trigger an auto-refresh on the next session-stop.
45
- // Six hours keeps optimization signal current without re-running review on
46
- // every short session. Operators can still run `construct review` manually.
47
42
  const REVIEW_REFRESH_MS = 6 * 60 * 60 * 1000;
48
43
 
44
+ function spawnDetached(args, { cwd: workDir = cwd } = {}) {
45
+ try {
46
+ const child = spawn(process.execPath, args, {
47
+ cwd: workDir,
48
+ stdio: 'ignore',
49
+ detached: true,
50
+ env: process.env,
51
+ });
52
+ child.unref();
53
+ return true;
54
+ } catch {
55
+ return false;
56
+ }
57
+ }
58
+
59
+ function listReviewJsonFiles(dir) {
60
+ if (!existsSync(dir)) return [];
61
+ return readdirSync(dir)
62
+ .filter((name) => name.endsWith('.json'))
63
+ .map((name) => join(dir, name));
64
+ }
65
+
49
66
  function maybeRefreshReviews() {
50
- const reviewsDir = join(homedir(), '.cx', 'performance-reviews');
51
67
  let needsRefresh = false;
52
68
  try {
53
- if (!existsSync(reviewsDir)) {
69
+ const files = listReviewJsonFiles(REVIEWS_DIR);
70
+ if (files.length === 0) {
54
71
  needsRefresh = true;
55
72
  } else {
56
- const files = spawnSync('find', [reviewsDir, '-name', '*.json', '-type', 'f'], {
57
- encoding: 'utf8', timeout: 5000,
58
- }).stdout.trim().split('\n').filter(Boolean);
59
- if (files.length === 0) {
60
- needsRefresh = true;
61
- } else {
62
- const newestMs = files.reduce((acc, f) => {
63
- try {
64
- const m = Number(spawnSync('stat', ['-f', '%m', f], { encoding: 'utf8' }).stdout.trim());
65
- return Number.isFinite(m) ? Math.max(acc, m * 1000) : acc;
66
- } catch { return acc; }
67
- }, 0);
68
- if (Date.now() - newestMs > REVIEW_REFRESH_MS) needsRefresh = true;
69
- }
73
+ const newestMs = files.reduce((acc, filePath) => {
74
+ try {
75
+ return Math.max(acc, statSync(filePath).mtimeMs);
76
+ } catch {
77
+ return acc;
78
+ }
79
+ }, 0);
80
+ if (Date.now() - newestMs > REVIEW_REFRESH_MS) needsRefresh = true;
70
81
  }
71
- } catch { /* tolerate fs errors silently โ€” refresh is best-effort */ }
82
+ } catch {
83
+ /* refresh is best-effort */
84
+ }
72
85
 
73
86
  if (!needsRefresh) return;
74
87
 
75
- // Refresh detached so the Stop hook does not block on telemetry latency.
76
- try {
77
- spawnSync(process.execPath, [CONSTRUCT_BIN, 'review', '--quiet'], {
78
- stdio: 'ignore',
79
- timeout: 60_000,
80
- detached: true,
81
- });
82
- } catch { /* refresh is best-effort; manual `construct review` is always available */ }
88
+ spawnDetached([CONSTRUCT_BIN, 'review', '--quiet']);
83
89
  }
84
90
 
85
- // Read the most recent performance review
86
91
  function getLatestPerformanceReview() {
87
- const reviewsDir = join(homedir(), '.cx', 'performance-reviews');
88
- if (!existsSync(reviewsDir)) return null;
89
-
90
92
  try {
91
- const files = spawnSync('find', [reviewsDir, '-name', '*.json', '-type', 'f'], {
92
- encoding: 'utf8',
93
- timeout: 5000
94
- }).stdout.trim().split('\n').filter(Boolean);
95
-
93
+ const files = listReviewJsonFiles(REVIEWS_DIR);
96
94
  if (files.length === 0) return null;
97
-
98
- // Sort by modification time, newest first
95
+
99
96
  files.sort((a, b) => {
100
97
  try {
101
- const statA = spawnSync('stat', ['-f', '%m', a], { encoding: 'utf8' }).stdout.trim();
102
- const statB = spawnSync('stat', ['-f', '%m', b], { encoding: 'utf8' }).stdout.trim();
103
- return parseInt(statB) - parseInt(statA);
98
+ return statSync(b).mtimeMs - statSync(a).mtimeMs;
104
99
  } catch {
105
100
  return 0;
106
101
  }
107
102
  });
108
-
109
- const latestFile = files[0];
110
- const content = readFileSync(latestFile, 'utf8');
111
- return JSON.parse(content);
103
+
104
+ return JSON.parse(readFileSync(files[0], 'utf8'));
112
105
  } catch (error) {
113
106
  console.error(`Error reading performance reviews: ${error.message}`);
114
107
  return null;
115
108
  }
116
109
  }
117
110
 
118
- // Check if agent needs optimization
119
111
  function needsOptimization(agentStats) {
120
112
  return (
121
113
  agentStats.scoredInvocations >= MIN_TRACES_PER_AGENT &&
@@ -124,101 +116,25 @@ function needsOptimization(agentStats) {
124
116
  );
125
117
  }
126
118
 
127
- // Run optimization for a specific agent
128
- function optimizeAgent(agentName) {
129
- console.log(`๐Ÿ”ง Optimizing ${agentName} (score < ${OPTIMIZATION_THRESHOLD})`);
130
-
131
- const logFile = join(OPTIMIZATION_LOG_DIR, `${new Date().toISOString().split('T')[0]}-${agentName}.log`);
132
- const startTime = Date.now();
133
-
134
- try {
135
- // Preview only. The new contract requires an explicit --apply from the
136
- // operator; hook-driven runs must never modify prompt files.
137
- const result = spawnSync(CONSTRUCT_BIN, ['optimize', agentName, `--days=${OPTIMIZATION_DAYS}`, '--dry-run'], {
138
- cwd,
139
- encoding: 'utf8',
140
- timeout: 300000, // 5 minutes timeout for optimization
141
- stdio: ['pipe', 'pipe', 'pipe']
142
- });
143
-
144
- const duration = Date.now() - startTime;
145
-
146
- // Log the optimization attempt
147
- const logEntry = {
148
- timestamp: new Date().toISOString(),
149
- agent: agentName,
150
- durationMs: duration,
151
- success: result.status === 0,
152
- stdout: result.stdout,
153
- stderr: result.stderr,
154
- exitCode: result.status,
155
- signal: result.signal
156
- };
157
-
158
- writeFileSync(logFile, JSON.stringify(logEntry, null, 2) + '\n');
159
-
160
- if (result.status === 0) {
161
- console.log(`โœ… Optimization for ${agentName} completed in ${duration}ms`);
162
-
163
- // Record observation about the optimization
164
- recordOptimizationObservation(agentName, true, duration);
165
- } else {
166
- console.log(`โŒ Optimization for ${agentName} failed with code ${result.status}`);
167
- console.log(`Stderr: ${result.stderr.slice(0, 500)}...`);
168
-
169
- // Record observation about the failure
170
- recordOptimizationObservation(agentName, false, duration, result.stderr);
171
- }
172
-
173
- return result.status === 0;
174
- } catch (error) {
175
- console.error(`โŒ Optimization for ${agentName} threw error: ${error.message}`);
176
-
177
- const logEntry = {
178
- timestamp: new Date().toISOString(),
179
- agent: agentName,
180
- durationMs: Date.now() - startTime,
181
- success: false,
182
- error: error.message,
183
- stack: error.stack
184
- };
185
-
186
- writeFileSync(logFile, JSON.stringify(logEntry, null, 2) + '\n');
187
- return false;
188
- }
189
- }
190
-
191
- // Record optimization observation for learning
192
- function recordOptimizationObservation(agentName, success, durationMs, error = null) {
193
- const observation = {
194
- role: 'construct',
195
- category: success ? 'pattern' : 'anti-pattern',
196
- summary: success
197
- ? `Auto-optimized ${agentName} in ${durationMs}ms`
198
- : `Auto-optimization failed for ${agentName}: ${error?.slice(0, 200) || 'unknown error'}`,
199
- content: success
200
- ? `Automatic optimization triggered for ${agentName} after performance review showed avgScore < ${OPTIMIZATION_THRESHOLD}. Optimization completed successfully in ${durationMs}ms.`
201
- : `Automatic optimization failed for ${agentName}. Error: ${error || 'Unknown'}. System should notify maintainer.`,
202
- tags: ['auto-optimization', 'performance-review', agentName],
203
- confidence: 0.9
204
- };
205
-
206
- try {
207
- // Use existing observation recording if available
208
- const observationFile = join(homedir(), '.cx', 'observations', `${Date.now()}-${agentName}-optimization.json`);
209
- mkdirSync(dirname(observationFile), { recursive: true });
210
- writeFileSync(observationFile, JSON.stringify(observation, null, 2) + '\n');
211
- } catch (error) {
212
- // Non-critical - observation recording failed
213
- console.error(`Failed to record optimization observation: ${error.message}`);
214
- }
119
+ function scheduleAgentOptimization(agentName) {
120
+ const logFile = join(OPTIMIZATION_LOG_DIR, `${new Date().toISOString().split('T')[0]}-${agentName}-scheduled.json`);
121
+ const scheduled = spawnDetached([
122
+ CONSTRUCT_BIN,
123
+ 'optimize',
124
+ agentName,
125
+ `--days=${OPTIMIZATION_DAYS}`,
126
+ '--dry-run',
127
+ ]);
128
+ writeFileSync(logFile, `${JSON.stringify({
129
+ timestamp: new Date().toISOString(),
130
+ agent: agentName,
131
+ scheduled,
132
+ mode: 'detached-dry-run',
133
+ })}\n`);
134
+ return scheduled;
215
135
  }
216
136
 
217
- // Main optimization logic
218
137
  function main() {
219
- // If the most recent review is missing or stale (>6h), kick off a refresh
220
- // detached so the Stop hook returns promptly. The refresh runs on the next
221
- // session's Stop if it's still in flight; nothing here blocks user work.
222
138
  maybeRefreshReviews();
223
139
 
224
140
  const review = getLatestPerformanceReview();
@@ -226,92 +142,53 @@ function main() {
226
142
  console.log('๐Ÿ“Š No performance review data found. Run `construct review` first.');
227
143
  process.exit(0);
228
144
  }
229
-
230
- console.log(`๐Ÿ“Š Checking ${review.agentStats.length} agents from performance review ${review.generated}`);
231
-
145
+
232
146
  const agentsNeedingOptimization = review.agentStats.filter(needsOptimization);
233
-
147
+
234
148
  if (agentsNeedingOptimization.length === 0) {
235
149
  console.log(`โœ… All ${review.agentStats.length} agents have scores >= ${OPTIMIZATION_THRESHOLD} or insufficient data`);
236
150
  process.exit(0);
237
151
  }
238
-
239
- console.log(`๐Ÿ”ง Found ${agentsNeedingOptimization.length} agents needing optimization:`);
240
- agentsNeedingOptimization.forEach(agent => {
241
- console.log(` โ€ข ${agent.name}: avgScore=${agent.avgScore}, invocations=${agent.invocations}`);
242
- });
243
-
244
- // Sequential optimization keeps prompt-tuning deterministic and avoids
245
- // colliding writes to per-agent prompt files.
246
- let optimizedCount = 0;
247
- let failedCount = 0;
248
-
152
+
153
+ console.log(`๐Ÿ”ง Scheduling ${agentsNeedingOptimization.length} detached optimization preview(s):`);
154
+ let scheduledCount = 0;
249
155
  for (const agent of agentsNeedingOptimization) {
250
- const success = optimizeAgent(agent.name);
251
- if (success) {
252
- optimizedCount++;
253
- } else {
254
- failedCount++;
255
- }
256
-
257
- // Small delay between optimizations to avoid rate limits
258
- if (agentsNeedingOptimization.length > 1) {
259
- spawnSync('sleep', ['10'], { encoding: 'utf8' });
260
- }
261
- }
262
-
263
- // Summary
264
- console.log(`\n๐Ÿ“ˆ Optimization Summary:`);
265
- console.log(` โ€ข Agents checked: ${review.agentStats.length}`);
266
- console.log(` โ€ข Needing optimization: ${agentsNeedingOptimization.length}`);
267
- console.log(` โ€ข Successfully optimized: ${optimizedCount}`);
268
- console.log(` โ€ข Failed: ${failedCount}`);
269
-
270
- if (optimizedCount > 0) {
271
- console.log(`\n๐Ÿ’ก Optimized agents will be evaluated in the next performance review.`);
272
- console.log(` Run \`construct review\` after next session to see improvements.`);
156
+ console.log(` โ€ข ${agent.name}: avgScore=${agent.avgScore}, invocations=${agent.invocations}`);
157
+ if (scheduleAgentOptimization(agent.name)) scheduledCount += 1;
273
158
  }
274
-
275
- // Log summary to file
276
- const summary = {
159
+
160
+ const summaryFile = join(OPTIMIZATION_LOG_DIR, `${new Date().toISOString().split('T')[0]}-summary.json`);
161
+ writeFileSync(summaryFile, `${JSON.stringify({
277
162
  timestamp: new Date().toISOString(),
278
163
  project: projectName,
279
164
  reviewDate: review.generated,
280
165
  agentsChecked: review.agentStats.length,
281
166
  agentsNeedingOptimization: agentsNeedingOptimization.length,
282
- optimizedCount,
283
- failedCount,
284
- agents: agentsNeedingOptimization.map(a => ({
167
+ scheduledCount,
168
+ agents: agentsNeedingOptimization.map((a) => ({
285
169
  name: a.name,
286
170
  avgScore: a.avgScore,
287
171
  invocations: a.invocations,
288
- optimized: optimizeAgent.includes ? 'yes' : 'no'
289
- }))
290
- };
291
-
292
- const summaryFile = join(OPTIMIZATION_LOG_DIR, `${new Date().toISOString().split('T')[0]}-summary.json`);
293
- writeFileSync(summaryFile, JSON.stringify(summary, null, 2) + '\n');
172
+ })),
173
+ }, null, 2)}\n`);
174
+
175
+ console.log(`\n๐Ÿ“ˆ Optimization previews scheduled: ${scheduledCount}/${agentsNeedingOptimization.length}`);
294
176
  }
295
177
 
296
- // Run main logic
297
178
  try {
298
179
  main();
299
180
  } catch (error) {
300
181
  console.error(`โŒ Session optimization hook crashed: ${error.message}`);
301
- console.error(error.stack);
302
-
303
- // Log the crash
304
182
  const crashLog = join(OPTIMIZATION_LOG_DIR, 'crashes', `${Date.now()}-crash.json`);
305
183
  mkdirSync(dirname(crashLog), { recursive: true });
306
- writeFileSync(crashLog, JSON.stringify({
184
+ writeFileSync(crashLog, `${JSON.stringify({
307
185
  timestamp: new Date().toISOString(),
308
186
  error: error.message,
309
187
  stack: error.stack,
310
188
  cwd,
311
- projectName
312
- }, null, 2) + '\n');
313
-
189
+ projectName,
190
+ }, null, 2)}\n`);
314
191
  process.exit(1);
315
192
  }
316
193
 
317
- process.exit(0);
194
+ process.exit(0);
@@ -319,11 +319,10 @@ try {
319
319
  // plugin so both platforms surface the same prelude on session start.
320
320
 
321
321
  let intakeReviewNote = '';
322
- let brokerStatusNote = '';
323
322
  try {
324
- const { buildIntakePrelude, buildBrokerStatusLine } = await import('../intake/session-prelude.mjs');
325
- intakeReviewNote = buildIntakePrelude({ cwd, env: process.env });
326
- brokerStatusNote = `\n${buildBrokerStatusLine({ env: process.env })}\n`;
323
+ const { buildSessionPrelude } = await import('../intake/session-prelude.mjs');
324
+ const prelude = buildSessionPrelude({ cwd, env: process.env });
325
+ intakeReviewNote = prelude ? `\n${prelude}\n` : '';
327
326
  } catch { /* best effort */ }
328
327
 
329
328
  // Missing-env-vars notice. Reads .env.example from the project root,
@@ -383,7 +382,7 @@ try {
383
382
  // that pollutes the caller's output contract, so the resolved output mode routes
384
383
  // the payload to stdout (interactive), stderr, or a debug log (silent).
385
384
 
386
- const payload = `${header}\n${body}${stateNote}${efficiencyNote}${observationsNote}${concurrencyNote}${skillScopeNote}${dropNote}${embedStatusNote}${sourcesNote}${selfKnowledgeNote}${rolesNote}${intakeReviewNote}${brokerStatusNote}${permissionPostureNote}${envCheckNote}\n${footer}${pendingNote}\n`;
385
+ const payload = `${header}\n${body}${stateNote}${efficiencyNote}${observationsNote}${concurrencyNote}${skillScopeNote}${dropNote}${embedStatusNote}${sourcesNote}${selfKnowledgeNote}${rolesNote}${intakeReviewNote}${permissionPostureNote}${envCheckNote}\n${footer}${pendingNote}\n`;
387
386
  try {
388
387
  const { mode } = resolveHookOutputMode({ cwd, env: process.env });
389
388
  writeHookContext({ payload, mode });
@@ -2,6 +2,13 @@
2
2
  * lib/host-disposition.mjs โ€” single source of truth for the disposition of
3
3
  * every artifact Construct creates in a host project (ADR-0027 ยง1).
4
4
  *
5
+ * Scope model (see docs/concepts/project-scopes.md):
6
+ * templates/, docs/ โ€” package/repo durable sources (committed)
7
+ * .construct/ โ€” per-project launcher (ignored)
8
+ * .cx/ โ€” per-project runtime: intake, oracle, observations, demos (ignored)
9
+ * ~/.construct/ โ€” user credentials and doctor state
10
+ * ~/.cx/ โ€” user-scope telemetry fallback
11
+ *
5
12
  * `IGNORED_PATTERNS` enumerates machine-specific, sync-regenerated artifacts
6
13
  * that must never be committed; the init `.gitignore` writer and the
7
14
  * `gitignore-coverage` reconciliation both read from here, and uninstall /