@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
package/lib/init-docs.mjs CHANGED
@@ -27,6 +27,16 @@ import { suggestDocsLaneForFile } from './docs-routing.mjs';
27
27
  import { stampFrontmatter } from "./doc-stamp.mjs";
28
28
  import { multiSelect, selectOption } from './tty-prompts.mjs';
29
29
  import readline from "node:readline";
30
+ import {
31
+ DOC_LANES,
32
+ DOC_PRESETS,
33
+ DEFAULT_LANES,
34
+ LANE_ORDER,
35
+ normalizeCustomLaneName,
36
+ normalizeLaneKey,
37
+ parseCsvList,
38
+ parseSelectableLanes,
39
+ } from './init/doc-lanes.mjs';
30
40
 
31
41
  const __dirname = fileURLToPath(new URL(".", import.meta.url));
32
42
  const ROOT_DIR = path.join(__dirname, "..");
@@ -52,170 +62,40 @@ const docsDir = path.join(target, docsRootRelative);
52
62
  const created = [];
53
63
  const skipped = [];
54
64
 
55
- const DOC_LANES = {
56
- adrs: {
57
- title: "ADRs",
58
- dir: "adr",
59
- description: "Architecture decision records for decisions that have already been made.",
60
- templates: ["adr.md"],
61
- },
62
- briefs: {
63
- title: "Briefs",
64
- dir: "briefs",
65
- description: "Research, evidence, signal, and one-pager style documents.",
66
- templates: [
67
- "research-brief.md",
68
- "evidence-brief.md",
69
- "signal-brief.md",
70
- "one-pager.md",
71
- "customer-profile.md",
72
- "product-intelligence-report.md",
73
- "backlog-proposal.md",
74
- ],
75
- },
76
- changelogs: {
77
- title: "Changelogs",
78
- dir: "changelogs",
79
- description: "User-facing release notes and version history entries.",
80
- templates: ["changelog-entry.md"],
81
- },
82
- intake: {
83
- title: "Intake",
84
- dir: "intake",
85
- description: "Intake batch records that explain what arrived, why it matters, and how it should be ingested.",
86
- templates: ["__intake-template__"],
87
- },
88
- memos: {
89
- title: "Memos",
90
- dir: "memos",
91
- description: "Decision memos and internal arguments for alignment and approval.",
92
- templates: ["memo.md"],
93
- },
94
- meetings: {
95
- title: 'Meetings',
96
- dir: 'meetings',
97
- description: 'Meeting notes, minutes, retros, standups, planning sessions, and agendas.',
98
- templates: ['__meeting-notes-template__'],
99
- },
100
- notes: {
101
- title: "Notes",
102
- dir: "notes",
103
- description: "Working notes and lightweight durable context outside formal docs or meetings.",
104
- templates: ["__notes-template__"],
105
- },
106
- onboarding: {
107
- title: "Onboarding",
108
- dir: "onboarding",
109
- description: "Runnable setup guides and first-day workflows for engineers, product, or ops.",
110
- templates: ["onboarding.md"],
111
- },
112
- postmortems: {
113
- title: "Postmortems",
114
- dir: "postmortems",
115
- description: "Blameless incident reports: timeline, root cause, contributing factors, and corrective actions.",
116
- templates: ["incident-report.md"],
117
- },
118
- prds: {
119
- title: "PRDs",
120
- dir: "prds",
121
- description: "Product and capability requirement documents.",
122
- templates: ["prd.md", "meta-prd.md", "prd-business.md", "prd-platform.md", "prfaq.md"],
123
- },
124
- rfcs: {
125
- title: "RFCs",
126
- dir: "rfcs",
127
- description: "Architecture and implementation proposals that need review before a decision.",
128
- templates: ["rfc.md", "rfc-platform.md"],
129
- },
130
- runbooks: {
131
- title: "Runbooks",
132
- dir: "runbooks",
133
- description: "Operational procedures, diagnostics, remediation, and escalation paths.",
134
- templates: ["runbook.md"],
135
- },
136
- };
137
-
138
- const DOC_PRESETS = {
139
- lean: ["adrs", "intake", "memos", "meetings", "notes", "prds"],
140
- product: ["adrs", "intake", "memos", "meetings", "notes", "prds", "rfcs"],
141
- full: ["adrs", "briefs", "changelogs", "intake", "memos", "meetings", "notes", "onboarding", "postmortems", "prds", "rfcs", "runbooks"],
142
- };
143
-
144
- const DEFAULT_LANES = DOC_PRESETS.lean;
145
- const LANE_ORDER = ["adrs", "briefs", "changelogs", "intake", "memos", "meetings", "notes", "onboarding", "postmortems", "prds", "rfcs", "runbooks"];
146
- const LANE_ALIASES = {
147
- adr: "adrs",
148
- adrs: "adrs",
149
- brief: "briefs",
150
- briefs: "briefs",
151
- changelog: "changelogs",
152
- changelogs: "changelogs",
153
- releases: "changelogs",
154
- release: "changelogs",
155
- intake: "intake",
156
- memo: "memos",
157
- memos: "memos",
158
- meeting: 'meetings',
159
- meetings: 'meetings',
160
- minutes: 'meetings',
161
- retro: 'meetings',
162
- note: "notes",
163
- notes: "notes",
164
- onboard: "onboarding",
165
- onboarding: "onboarding",
166
- postmortem: "postmortems",
167
- postmortems: "postmortems",
168
- incident: "postmortems",
169
- incidents: "postmortems",
170
- prd: "prds",
171
- prds: "prds",
172
- rfc: "rfcs",
173
- rfcs: "rfcs",
174
- runbook: "runbooks",
175
- runbooks: "runbooks",
176
- };
177
-
178
65
  function inferProjectName(targetPath) {
179
- const packageJsonPath = path.join(targetPath, "package.json");
66
+ const packageJsonPath = path.join(targetPath, 'package.json');
180
67
  if (fs.existsSync(packageJsonPath)) {
181
68
  try {
182
- const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
69
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
183
70
  return pkg.name || path.basename(targetPath);
184
- } catch {}
71
+ } catch { /* ignore */ }
185
72
  }
186
73
  return path.basename(targetPath);
187
74
  }
188
75
 
189
- function parseCsvList(value) {
190
- return value
191
- .split(",")
192
- .map((entry) => entry.trim())
193
- .filter(Boolean);
194
- }
195
-
196
76
  const NO_ANSWER_PATTERNS = new Set([
197
- "",
198
- "n",
199
- "no",
200
- "none",
201
- "nope",
202
- "nah",
203
- "nothing",
204
- "blank",
205
- "skip",
206
- "no thanks",
77
+ '',
78
+ 'n',
79
+ 'no',
80
+ 'none',
81
+ 'nope',
82
+ 'nah',
83
+ 'nothing',
84
+ 'blank',
85
+ 'skip',
86
+ 'no thanks',
207
87
  ]);
208
88
 
209
89
  const ALL_ANSWER_PATTERNS = new Set([
210
- "all",
211
- "all of them",
212
- "everything",
213
- "default",
214
- "defaults",
90
+ 'all',
91
+ 'all of them',
92
+ 'everything',
93
+ 'default',
94
+ 'defaults',
215
95
  ]);
216
96
 
217
97
  function normalizeAnswer(value) {
218
- return value.trim().toLowerCase().replace(/\s+/g, " ");
98
+ return value.trim().toLowerCase().replace(/\s+/g, ' ');
219
99
  }
220
100
 
221
101
  function isNegativeAnswer(value) {
@@ -241,41 +121,14 @@ function parseExtraLaneSelection(value) {
241
121
  .filter(Boolean);
242
122
  }
243
123
 
244
- function parseSelectableLanes(value) {
245
- return value
246
- .split(/[,\n]/)
247
- .map((entry) => entry.trim())
248
- .filter(Boolean)
249
- .map((entry) => {
250
- if (/^\d+$/.test(entry)) {
251
- const lane = LANE_ORDER[Number(entry) - 1];
252
- return lane ?? "";
253
- }
254
- return normalizeLaneKey(entry);
255
- })
256
- .filter((lane) => lane in DOC_LANES);
257
- }
258
-
259
124
  function parseBooleanAnswer(value, defaultValue = false) {
260
125
  if (!value.trim()) return defaultValue;
261
126
  const normalized = normalizeAnswer(value);
262
- if (["y", "yes", "true"].includes(normalized)) return true;
263
- if (["n", "no", "false", "nope", "nah"].includes(normalized)) return false;
127
+ if (['y', 'yes', 'true'].includes(normalized)) return true;
128
+ if (['n', 'no', 'false', 'nope', 'nah'].includes(normalized)) return false;
264
129
  return defaultValue;
265
130
  }
266
131
 
267
- function normalizeCustomLaneName(name) {
268
- return name
269
- .trim()
270
- .toLowerCase()
271
- .replace(/[^a-z0-9]+/g, "-")
272
- .replace(/^-+|-+$/g, "");
273
- }
274
-
275
- function normalizeLaneKey(name) {
276
- return LANE_ALIASES[name.trim().toLowerCase()] ?? name.trim().toLowerCase();
277
- }
278
-
279
132
  function repoHasAny(targetDir, candidates) {
280
133
  return candidates.some((candidate) => fs.existsSync(path.join(targetDir, candidate)));
281
134
  }
@@ -24,6 +24,8 @@ import { execSync, spawnSync } from 'node:child_process';
24
24
  import { stageProjectAdapters } from './install/stage-project.mjs';
25
25
  import { missingIgnorePatterns, isConstructPackageRepo } from './host-disposition.mjs';
26
26
  import { HOST_KEYS, displayNameToKey } from './platforms/capabilities.mjs';
27
+ import { DOC_LANES, LANE_ORDER, DOC_PRESETS } from './init/doc-lanes.mjs';
28
+ import { initWorkflow } from './workflow-state.mjs';
27
29
 
28
30
  const __dirname = fileURLToPath(new URL(".", import.meta.url));
29
31
  const ROOT_DIR = path.join(__dirname, "..");
@@ -133,92 +135,9 @@ async function checkPrerequisites() {
133
135
  return result.ok;
134
136
  }
135
137
 
136
- // Documentation system configuration
137
- const DOC_LANES = {
138
- adrs: {
139
- title: "ADRs",
140
- dir: "adr",
141
- description: "Architecture decision records for decisions that have already been made.",
142
- templates: ["adr.md"],
143
- },
144
- briefs: {
145
- title: "Briefs",
146
- dir: "briefs",
147
- description: "Research, evidence, signal, and one-pager style documents.",
148
- templates: [
149
- "research-brief.md",
150
- "evidence-brief.md",
151
- "signal-brief.md",
152
- "one-pager.md",
153
- "customer-profile.md",
154
- "product-intelligence-report.md",
155
- "backlog-proposal.md",
156
- ],
157
- },
158
- changelogs: {
159
- title: "Changelogs",
160
- dir: "changelogs",
161
- description: "User-facing release notes and version history entries.",
162
- templates: ["changelog-entry.md"],
163
- },
164
- intake: {
165
- title: "Intake",
166
- dir: "intake",
167
- description: "Intake batch records that explain what arrived, why it matters, and how it should be ingested.",
168
- templates: ["__intake-template__"],
169
- },
170
- memos: {
171
- title: "Memos",
172
- dir: "memos",
173
- description: "Decision memos and internal arguments for alignment and approval.",
174
- templates: ["memo.md"],
175
- },
176
- meetings: {
177
- title: 'Meetings',
178
- dir: 'meetings',
179
- description: 'Meeting notes, minutes, retros, standups, planning sessions, and agendas.',
180
- templates: ['__meeting-notes-template__'],
181
- },
182
- notes: {
183
- title: "Notes",
184
- dir: "notes",
185
- description: "Working notes and lightweight durable context outside formal docs or meetings.",
186
- templates: ["__notes-template__"],
187
- },
188
- onboarding: {
189
- title: "Onboarding",
190
- dir: "onboarding",
191
- description: "Runnable setup guides and first-day workflows for engineers, product, or ops.",
192
- templates: ["onboarding.md"],
193
- },
194
- postmortems: {
195
- title: "Postmortems",
196
- dir: "postmortems",
197
- description: "Blameless incident reports: timeline, root cause, contributing factors, and corrective actions.",
198
- templates: ["incident-report.md"],
199
- },
200
- prds: {
201
- title: "PRDs",
202
- dir: "prds",
203
- description: "Product and capability requirement documents.",
204
- templates: ["prd.md", "meta-prd.md", "prd-business.md", "prd-platform.md", "prfaq.md"],
205
- },
206
- rfcs: {
207
- title: "RFCs",
208
- dir: "rfcs",
209
- description: "Architecture and implementation proposals that need review before a decision.",
210
- templates: ["rfc.md", "rfc-platform.md"],
211
- },
212
- runbooks: {
213
- title: "Runbooks",
214
- dir: "runbooks",
215
- description: "Operational procedures, diagnostics, remediation, and escalation paths.",
216
- templates: ["runbook.md"],
217
- },
218
- };
219
-
220
- // Lane order for display (alphabetical by title)
221
- const LANE_ORDER = ["adrs", "briefs", "changelogs", "intake", "meetings", "memos", "notes", "onboarding", "postmortems", "prds", "rfcs", "runbooks"];
138
+ const docsPresetArg = args.find((arg) => arg.startsWith('--docs-preset='));
139
+ const docsLanesArg = args.find((arg) => arg.startsWith('--docs-lanes='));
140
+ const docsPresetName = docsPresetArg ? docsPresetArg.split('=')[1].toLowerCase() : 'lean';
222
141
 
223
142
  // Project type detection
224
143
  function detectProjectType(targetPath) {
@@ -515,27 +434,32 @@ function copyLaneTemplates(laneKey) {
515
434
  }
516
435
 
517
436
  async function askDocumentationQuestions() {
518
- // Non-interactive mode: use flags or defaults
437
+ // Non-interactive mode: use flags or defaults (lean preset by default)
519
438
  if (skipInteractive) {
520
439
  let lanes = [];
521
-
522
- // Build lanes from explicit flags
523
- if (withAllDocsFlag) {
440
+
441
+ if (docsLanesArg) {
442
+ lanes = parseWithDocs(docsLanesArg.split('=')[1]);
443
+ } else if (withAllDocsFlag) {
524
444
  lanes = LANE_ORDER;
525
445
  } else if (withDocsFlag) {
526
- lanes = parseWithDocs(withDocsFlag.split("=")[1]);
446
+ lanes = parseWithDocs(withDocsFlag.split('=')[1]);
527
447
  } else {
528
- // Check individual flags
529
448
  if (withAdrsFlag) lanes.push('adrs');
530
449
  if (withRfcsFlag) lanes.push('rfcs');
531
450
  if (withRunbooksFlag) lanes.push('runbooks');
532
451
  if (withPostmortemsFlag) lanes.push('postmortems');
533
452
  }
534
-
453
+
454
+ if (lanes.length === 0) {
455
+ lanes = DOC_PRESETS[docsPresetName] ?? DOC_PRESETS.lean;
456
+ }
457
+
535
458
  return {
536
- lanes: [...new Set(lanes)], // dedupe
459
+ lanes: [...new Set(lanes)],
537
460
  withArchitecture: withArchitectureFlag,
538
461
  withReadme: withReadmeFlag || !fs.existsSync(path.join(target, "README.md")),
462
+ docsPreset: docsPresetName,
539
463
  };
540
464
  }
541
465
 
@@ -559,13 +483,16 @@ async function askDocumentationQuestions() {
559
483
  }
560
484
 
561
485
  // Build options for multi-select with suggestions pre-checked
562
- const suggestedLaneNames = new Set(suggestions.map(s => s.lane));
486
+ const suggestedLaneNames = new Set([
487
+ ...(DOC_PRESETS[docsPresetName] ?? DOC_PRESETS.lean),
488
+ ...suggestions.filter((s) => s.recommended).map((s) => s.lane),
489
+ ]);
563
490
  const laneOptions = LANE_ORDER.map(lane => {
564
491
  const suggestion = suggestions.find(s => s.lane === lane);
565
492
  return {
566
493
  label: DOC_LANES[lane].title,
567
494
  value: lane,
568
- checked: suggestion?.recommended ?? false,
495
+ checked: suggestion?.recommended ?? suggestedLaneNames.has(lane),
569
496
  description: DOC_LANES[lane].description,
570
497
  suggestion: suggestion?.reason,
571
498
  };
@@ -592,7 +519,7 @@ async function askDocumentationQuestions() {
592
519
  withArchitecture = archResponse;
593
520
  }
594
521
 
595
- return { lanes: selectedLanes, withArchitecture, withReadme };
522
+ return { lanes: selectedLanes, withArchitecture, withReadme, docsPreset: docsPresetName };
596
523
  }
597
524
 
598
525
  // Intake collection — suggest directories to watch for context
@@ -930,6 +857,9 @@ async function main() {
930
857
  generator: "construct/init",
931
858
  attribution,
932
859
  });
860
+
861
+ const { created: workflowCreated } = initWorkflow(target, projectName || "Project workflow");
862
+ if (workflowCreated) created.push(".cx/workflow.json");
933
863
 
934
864
  // `.cx/` already exists from the context writes above and is gitignored in full
935
865
  // (ADR-0027), so a keep file there could never be tracked. No dead keep file is written.
@@ -0,0 +1,102 @@
1
+ /**
2
+ * lib/init-update-guide.mjs — detect and migrate stale .cx/construct_guide.md copies.
3
+ *
4
+ * Older inits wrote R&D-specific language, fixed port URLs, or root-level guides.
5
+ * init:update proposes or applies the current templates/docs/construct_guide.md.
6
+ */
7
+
8
+ import fs from 'node:fs';
9
+ import path from 'node:path';
10
+ import crypto from 'node:crypto';
11
+ import { fileURLToPath } from 'node:url';
12
+ import { constructDir } from './paths.mjs';
13
+
14
+ const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
15
+
16
+ const STALE_GUIDE_MARKERS = [
17
+ /R&D\s+(intake|classification)/i,
18
+ /review-queue/i,
19
+ /localhost:54330\b/,
20
+ /54329-54339/,
21
+ /construct init.*per.?machine/i,
22
+ /construct init.*machine setup/i,
23
+ /Welcome to Construct[\s\S]{0,400}repo root/i,
24
+ ];
25
+
26
+ export function locateConstructGuide(targetDir) {
27
+ const cxGuide = path.join(targetDir, '.cx', 'construct_guide.md');
28
+ if (fs.existsSync(cxGuide)) return { path: cxGuide, location: '.cx' };
29
+ const rootGuide = path.join(targetDir, 'construct_guide.md');
30
+ if (fs.existsSync(rootGuide)) return { path: rootGuide, location: 'root' };
31
+ return null;
32
+ }
33
+
34
+ export function shippedConstructGuidePath() {
35
+ const fromInstall = path.join(constructDir(), 'templates', 'docs', 'construct_guide.md');
36
+ if (fs.existsSync(fromInstall)) return fromInstall;
37
+ return path.join(REPO_ROOT, 'templates', 'docs', 'construct_guide.md');
38
+ }
39
+
40
+ function sha256(content) {
41
+ return crypto.createHash('sha256').update(content).digest('hex');
42
+ }
43
+
44
+ export function isStaleConstructGuide(content) {
45
+ if (!content || !content.trim()) return false;
46
+ if (!content.includes('construct intake --help')) return true;
47
+ return STALE_GUIDE_MARKERS.some((re) => re.test(content));
48
+ }
49
+
50
+ export function needsConstructGuideUpdate(targetDir) {
51
+ const located = locateConstructGuide(targetDir);
52
+ if (!located) return { needed: false };
53
+ const content = fs.readFileSync(located.path, 'utf8');
54
+ const shippedPath = shippedConstructGuidePath();
55
+ if (!fs.existsSync(shippedPath)) return { needed: false };
56
+ const shipped = fs.readFileSync(shippedPath, 'utf8');
57
+ if (sha256(content) === sha256(shipped)) return { needed: false };
58
+ if (!isStaleConstructGuide(content)) return { needed: false };
59
+ return { needed: true, located, content, shipped };
60
+ }
61
+
62
+ export function writeConstructGuideProposal(targetDir, { located, shipped }) {
63
+ const proposalDir = path.join(targetDir, '.cx', 'proposals');
64
+ fs.mkdirSync(proposalDir, { recursive: true });
65
+ const proposalPath = path.join(proposalDir, 'construct_guide.construct-update.md');
66
+ const body = [
67
+ '# Proposed construct_guide.md update',
68
+ '',
69
+ 'Your orientation guide predates current Construct branding and intake terminology.',
70
+ 'Merge the sections you still need from your copy, then replace `.cx/construct_guide.md`',
71
+ 'with the shipped template below (or run `construct init:update --apply-guide`).',
72
+ '',
73
+ located.location === 'root'
74
+ ? '**Note:** the guide still lives at repo root; relocate to `.cx/construct_guide.md` (gitignored).'
75
+ : '',
76
+ '',
77
+ '## Shipped template (current standard)',
78
+ '',
79
+ shipped.trimEnd(),
80
+ '',
81
+ ].filter(Boolean).join('\n');
82
+ fs.writeFileSync(proposalPath, body, 'utf8');
83
+ return proposalPath;
84
+ }
85
+
86
+ export function applyConstructGuideUpdate(targetDir, shippedContent) {
87
+ const cxDir = path.join(targetDir, '.cx');
88
+ fs.mkdirSync(cxDir, { recursive: true });
89
+ const dest = path.join(cxDir, 'construct_guide.md');
90
+ const rootLegacy = path.join(targetDir, 'construct_guide.md');
91
+
92
+ if (fs.existsSync(dest)) {
93
+ fs.copyFileSync(dest, `${dest}.bak`);
94
+ }
95
+ fs.writeFileSync(dest, shippedContent, 'utf8');
96
+
97
+ if (fs.existsSync(rootLegacy)) {
98
+ fs.rmSync(rootLegacy);
99
+ }
100
+
101
+ return dest;
102
+ }
@@ -4,7 +4,8 @@
4
4
  *
5
5
  * `construct init:update` helps projects adopt current Construct standards without replacing
6
6
  * user-managed instruction files. Proposed updates are written under .cx/proposals/ for
7
- * manual review and merge.
7
+ * manual review and merge. Stale `.cx/construct_guide.md` copies can be refreshed via
8
+ * `--apply-guide`.
8
9
  *
9
10
  * checkTemplateConflicts(targetDir) compares project templates against the construct install's
10
11
  * templates/docs/ directory. resolveTemplateConflict(conflict, resolution, targetDir) applies
@@ -15,9 +16,15 @@ import fs from "node:fs";
15
16
  import path from "node:path";
16
17
  import crypto from "node:crypto";
17
18
  import { constructDir } from "./paths.mjs";
19
+ import {
20
+ applyConstructGuideUpdate,
21
+ needsConstructGuideUpdate,
22
+ writeConstructGuideProposal,
23
+ } from "./init-update-guide.mjs";
18
24
 
19
25
  const args = process.argv.slice(2);
20
26
  const dryRun = args.includes("--dry-run");
27
+ const applyGuide = args.includes("--apply-guide");
21
28
  const cwdArg = args.find((arg) => arg.startsWith("--cwd="));
22
29
  const cwd = cwdArg ? path.resolve(cwdArg.split("=")[1]) : process.cwd();
23
30
 
@@ -168,6 +175,18 @@ async function main() {
168
175
  }
169
176
  }
170
177
 
178
+ const guideState = needsConstructGuideUpdate(cwd);
179
+ if (guideState.needed) {
180
+ planned.push({
181
+ label: "construct_guide.md refresh (profile-neutral intake language)",
182
+ write() {
183
+ return writeConstructGuideProposal(cwd, guideState);
184
+ },
185
+ relativePath: path.join(".cx", "proposals", "construct_guide.construct-update.md"),
186
+ applyGuide: () => applyConstructGuideUpdate(cwd, guideState.shipped),
187
+ });
188
+ }
189
+
171
190
  if (planned.length === 0) {
172
191
  console.log("No proposals needed. The project already satisfies the current update checks.");
173
192
  return;
@@ -187,12 +206,24 @@ async function main() {
187
206
 
188
207
  console.log("");
189
208
  for (const item of planned) {
209
+ if (applyGuide && item.applyGuide) {
210
+ const dest = item.applyGuide();
211
+ console.log(`Applied construct_guide.md refresh: ${dest} (backup: ${dest}.bak when prior .cx copy existed)`);
212
+ continue;
213
+ }
190
214
  const proposalPath = item.write();
191
215
  console.log(`Wrote proposal: ${proposalPath}`);
192
216
  }
193
217
 
218
+ if (applyGuide) {
219
+ console.log("");
220
+ console.log("Guide refresh applied. Review .cx/construct_guide.md and restore any team-specific notes from .bak if needed.");
221
+ return;
222
+ }
223
+
194
224
  console.log("");
195
225
  console.log("Review the proposal files under .cx/proposals/ and merge the needed changes manually.");
226
+ console.log("To replace .cx/construct_guide.md with the shipped template: construct init:update --apply-guide");
196
227
  }
197
228
 
198
229
  if (import.meta.url === `file://${process.argv[1]}`) {
package/lib/init.mjs CHANGED
@@ -69,6 +69,14 @@ function main() {
69
69
  content: buildContextMarkdown(),
70
70
  generator: "construct/init",
71
71
  });
72
+ writeStampedIfMissing({
73
+ targetRoot: target,
74
+ created,
75
+ skipped,
76
+ filePath: path.join(target, ".cx", "brand-voice.json"),
77
+ content: `${JSON.stringify({ version: 1, formality: 3, toneOverride: {}, audienceLexicon: { prefer: [], avoid: [] } }, null, 2)}\n`,
78
+ generator: "construct/init",
79
+ });
72
80
  // `.cx/` already exists from the context writes above and is gitignored in full
73
81
  // (ADR-0027), so a keep file there could never be tracked; `.cx/inbox/` is the local
74
82
  // intake drop zone the watcher polls. Create the directories; write no dead keep file.