@geraldmaron/construct 1.2.1 → 1.2.2

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 (198) hide show
  1. package/README.md +6 -6
  2. package/bin/construct +41 -67
  3. package/commands/plan/decide.md +1 -1
  4. package/commands/remember/runbook.md +1 -1
  5. package/examples/README.md +1 -1
  6. package/examples/distribution/README.md +1 -1
  7. package/examples/distribution/sources/adr.md +2 -2
  8. package/examples/distribution/sources/deck-one-pager.md +1 -1
  9. package/examples/distribution/sources/prd-platform.md +1 -1
  10. package/examples/distribution/sources/research-brief.md +2 -2
  11. package/examples/distribution/sources/rfc-platform.md +1 -1
  12. package/examples/distribution/sources/runbook.md +2 -2
  13. package/examples/distribution/sources/strategy.md +1 -1
  14. package/lib/agent-instructions/inject.mjs +1 -1
  15. package/lib/artifact-type-from-path.mjs +9 -5
  16. package/lib/audit-specialists.mjs +1 -1
  17. package/lib/audit-trail.mjs +3 -3
  18. package/lib/auto-docs.mjs +10 -10
  19. package/lib/bootstrap/built-ins.mjs +3 -3
  20. package/lib/boundary.mjs +3 -2
  21. package/lib/brand-prose.mjs +1 -1
  22. package/lib/cache-governor.js +4 -4
  23. package/lib/certification/document-io-fixtures.mjs +1 -1
  24. package/lib/chat/config.mjs +2 -2
  25. package/lib/chat/desktop-binary.mjs +5 -3
  26. package/lib/cli-commands.mjs +3 -3
  27. package/lib/cli-service-inventory.mjs +1 -1
  28. package/lib/comment-lint.mjs +9 -5
  29. package/lib/config/alias.mjs +3 -2
  30. package/lib/config/intake-policy.mjs +8 -78
  31. package/lib/config/project-config.mjs +95 -5
  32. package/lib/config/schema.mjs +9 -19
  33. package/lib/config/xdg.mjs +63 -0
  34. package/lib/contracts/violation-log.mjs +2 -2
  35. package/lib/cost-ledger.mjs +4 -3
  36. package/lib/cost.mjs +3 -2
  37. package/lib/decisions/registry.mjs +3 -3
  38. package/lib/distill.mjs +2 -2
  39. package/lib/docs-verify.mjs +2 -0
  40. package/lib/doctor/audit.mjs +4 -3
  41. package/lib/doctor/cli.mjs +0 -1
  42. package/lib/doctor/index.mjs +4 -4
  43. package/lib/doctor/report.mjs +3 -3
  44. package/lib/doctor/watchers/bd-watch.mjs +5 -5
  45. package/lib/doctor/watchers/cost.mjs +4 -4
  46. package/lib/doctor/watchers/disk.mjs +4 -3
  47. package/lib/doctor/watchers/service-health.mjs +5 -5
  48. package/lib/document-export.mjs +1 -1
  49. package/lib/document-extract.mjs +3 -3
  50. package/lib/document-ingest.mjs +13 -3
  51. package/lib/efficiency.mjs +2 -1
  52. package/lib/embed/approval-queue.mjs +3 -2
  53. package/lib/embed/cli.mjs +7 -5
  54. package/lib/embed/config.mjs +6 -5
  55. package/lib/embed/daemon.mjs +6 -5
  56. package/lib/embed/inbox-live-watcher.mjs +1 -1
  57. package/lib/embed/inbox.mjs +52 -37
  58. package/lib/embed/intake-metrics.mjs +3 -2
  59. package/lib/embed/reconcile.mjs +1 -1
  60. package/lib/embed/semantic.mjs +3 -2
  61. package/lib/embed/supervision.mjs +4 -2
  62. package/lib/engine/registry.mjs +3 -3
  63. package/lib/env-config.mjs +34 -12
  64. package/lib/features.mjs +4 -3
  65. package/lib/health-check.mjs +7 -6
  66. package/lib/hook-health.mjs +5 -4
  67. package/lib/hooks/_lib/log.mjs +4 -3
  68. package/lib/hooks/_lib/output-mode.mjs +2 -1
  69. package/lib/hooks/agent-tracker.mjs +3 -4
  70. package/lib/hooks/audit-reads.mjs +2 -2
  71. package/lib/hooks/audit-trail.mjs +2 -2
  72. package/lib/hooks/bash-output-logger.mjs +3 -3
  73. package/lib/hooks/brand-prose-lint.mjs +1 -1
  74. package/lib/hooks/ci-status-check.mjs +3 -2
  75. package/lib/hooks/context-watch.mjs +2 -2
  76. package/lib/hooks/context-window-recovery.mjs +5 -5
  77. package/lib/hooks/dep-audit.mjs +2 -2
  78. package/lib/hooks/doc-coupling-check.mjs +3 -2
  79. package/lib/hooks/edit-accumulator.mjs +3 -2
  80. package/lib/hooks/edit-guard.mjs +3 -3
  81. package/lib/hooks/guard-bash.mjs +2 -2
  82. package/lib/hooks/model-fallback.mjs +2 -1
  83. package/lib/hooks/policy-engine.mjs +4 -3
  84. package/lib/hooks/pre-compact.mjs +7 -7
  85. package/lib/hooks/readme-age-check.mjs +2 -2
  86. package/lib/hooks/session-optimize.mjs +4 -3
  87. package/lib/hooks/session-start.mjs +6 -5
  88. package/lib/hooks/stop-notify.mjs +9 -8
  89. package/lib/hooks/stop-typecheck.mjs +4 -3
  90. package/lib/hooks/test-watch.mjs +2 -2
  91. package/lib/host-disposition.mjs +1 -1
  92. package/lib/ingest/provider-extract.mjs +2 -1
  93. package/lib/init/detect-existing-structure.mjs +2 -2
  94. package/lib/init/doc-lanes.mjs +3 -10
  95. package/lib/init-docs.mjs +12 -54
  96. package/lib/init-unified.mjs +35 -81
  97. package/lib/init.mjs +7 -5
  98. package/lib/install/desktop-binary-download.mjs +5 -2
  99. package/lib/intake/daemon.mjs +31 -8
  100. package/lib/intake/intake-config.mjs +5 -32
  101. package/lib/integrations/intake-integrations.mjs +8 -7
  102. package/lib/knowledge/rag.mjs +2 -2
  103. package/lib/knowledge/search.mjs +15 -15
  104. package/lib/maintenance/cleanup.mjs +14 -10
  105. package/lib/mcp/server.mjs +1 -1
  106. package/lib/mcp/tools/project.mjs +2 -1
  107. package/lib/model-cheapest-provider.mjs +4 -3
  108. package/lib/model-pricing.mjs +3 -2
  109. package/lib/model-router.mjs +4 -3
  110. package/lib/models/catalog.mjs +2 -1
  111. package/lib/models/execution-capability-profile.mjs +2 -1
  112. package/lib/models/provider-poll.mjs +2 -1
  113. package/lib/ollama-manager.mjs +0 -1
  114. package/lib/op-log.mjs +2 -1
  115. package/lib/opencode-runtime-plugin.mjs +3 -2
  116. package/lib/oracle/artifact-gate.mjs +3 -0
  117. package/lib/oracle/cli.mjs +2 -1
  118. package/lib/oracle/execute.mjs +3 -2
  119. package/lib/oracle/index.mjs +2 -1
  120. package/lib/oracle/read-model.mjs +3 -2
  121. package/lib/performance/generate.mjs +4 -3
  122. package/lib/platforms/capabilities.mjs +1 -1
  123. package/lib/plugin-registry.mjs +2 -1
  124. package/lib/profiles/lifecycle.mjs +3 -3
  125. package/lib/project-profile.mjs +2 -1
  126. package/lib/project-root.mjs +9 -7
  127. package/lib/provider-capabilities.js +4 -3
  128. package/lib/providers/auth-manager.mjs +2 -1
  129. package/lib/providers/copilot-auth.mjs +3 -2
  130. package/lib/providers/creds.mjs +3 -2
  131. package/lib/providers/registry.mjs +3 -3
  132. package/lib/providers/secret-resolver.mjs +3 -2
  133. package/lib/read-tracker-store.mjs +2 -1
  134. package/lib/reconcile/mcp-entry-reconcile.mjs +2 -2
  135. package/lib/reflect.mjs +2 -1
  136. package/lib/registry/generate-docs.mjs +2 -2
  137. package/lib/roles/approval-surface.mjs +5 -4
  138. package/lib/roles/event-bus.mjs +0 -1
  139. package/lib/roles/gateway.mjs +6 -2
  140. package/lib/rules-delivery.mjs +1 -1
  141. package/lib/runtime-pressure.mjs +5 -3
  142. package/lib/sandbox.mjs +3 -2
  143. package/lib/scheduler/solo.mjs +6 -4
  144. package/lib/server/auth.mjs +4 -3
  145. package/lib/server/index.mjs +24 -26
  146. package/lib/server/insights.mjs +5 -4
  147. package/lib/server/webhook.mjs +2 -1
  148. package/lib/service-manager.mjs +5 -3
  149. package/lib/setup.mjs +9 -8
  150. package/lib/status.mjs +6 -5
  151. package/lib/storage/embeddings-local.mjs +3 -2
  152. package/lib/storage/sync.mjs +2 -2
  153. package/lib/telemetry/client.mjs +0 -1
  154. package/lib/telemetry/intent-verifications.mjs +6 -6
  155. package/lib/telemetry/model-pricing-catalog.mjs +4 -3
  156. package/lib/telemetry/rule-calls.mjs +3 -3
  157. package/lib/telemetry/skill-calls.mjs +3 -3
  158. package/lib/template-registry.mjs +1 -0
  159. package/lib/templates/visual-requirements.mjs +1 -1
  160. package/lib/test-corpus-inventory.mjs +1 -1
  161. package/lib/uninstall/uninstall.mjs +12 -10
  162. package/package.json +3 -3
  163. package/platforms/claude/settings.template.json +43 -43
  164. package/rules/common/no-fabrication.md +1 -1
  165. package/rules/common/release-gates.md +1 -1
  166. package/rules/common/research.md +1 -1
  167. package/rules/common/review-before-change.md +1 -1
  168. package/scripts/sync-specialists.mjs +11 -8
  169. package/skills/docs/adr-workflow.md +3 -3
  170. package/skills/docs/init-docs.md +9 -9
  171. package/skills/docs/prd-workflow.md +5 -5
  172. package/skills/docs/product-intelligence-review.md +1 -1
  173. package/skills/docs/product-intelligence-workflow.md +2 -2
  174. package/skills/docs/product-signal-workflow.md +1 -1
  175. package/skills/docs/runbook-workflow.md +4 -4
  176. package/skills/exploration/unknown-codebase-onboarding.md +1 -1
  177. package/skills/operating/orchestration-reference.md +1 -1
  178. package/skills/routing.md +3 -3
  179. package/specialists/prompts/cx-architect.md +1 -1
  180. package/specialists/prompts/cx-docs-keeper.md +1 -1
  181. package/specialists/prompts/cx-researcher.md +1 -1
  182. package/specialists/prompts/cx-sre.md +1 -1
  183. package/specialists/role-manifests.json +6 -6
  184. package/templates/docs/README.md +125 -0
  185. package/templates/docs/construct_guide.md +3 -4
  186. package/templates/docs/persona-artifact.md +1 -1
  187. package/templates/docs/prds/README.md +25 -0
  188. package/templates/docs/prds/templates/_template.md +206 -0
  189. package/templates/docs/prds/templates/meta-prd.template.md +177 -0
  190. package/templates/docs/prds/templates/prd-business.template.md +61 -0
  191. package/templates/docs/prds/templates/prd-platform.template.md +81 -0
  192. package/templates/docs/prds/templates/prfaq.template.md +38 -0
  193. package/templates/docs/rfcs/README.md +22 -0
  194. package/templates/docs/rfcs/templates/_template.md +58 -0
  195. package/templates/docs/rfcs/templates/rfc-platform.template.md +72 -0
  196. package/templates/homebrew/construct.rb +1 -1
  197. package/templates/workflows/new-feature.yml +8 -8
  198. package/lib/intake/legacy-paths.mjs +0 -5
package/README.md CHANGED
@@ -28,7 +28,7 @@ Bootstrap local services (once per machine, opt-in to machine-scope writes):
28
28
  construct install --scope=user --yes
29
29
  ```
30
30
 
31
- `construct install` defaults to `--scope=project`, which writes nothing and prints scope guidance — see the [footprint contract](#footprint-contract) below or [ADR 0029](docs/adr/0029-install-scopes-and-hook-budgets.md). Use `--scope=user` for machine setup, `--scope=both` for both.
31
+ `construct install` defaults to `--scope=project`, which writes nothing and prints scope guidance — see the [footprint contract](#footprint-contract) below or [ADR 0029](docs/decisions/adr/0029-install-scopes-and-hook-budgets.md). Use `--scope=user` for machine setup, `--scope=both` for both.
32
32
 
33
33
  Initialize a project:
34
34
 
@@ -87,7 +87,7 @@ Pick or change modes with `construct config mode [solo|team|enterprise]`. [Deplo
87
87
 
88
88
  ## Intake
89
89
 
90
- Anything dropped into `.cx/inbox/` (a bug report, a customer comment, a competitor PDF, a postmortem draft) is classified by the active profile's intake taxonomy. The default `rnd` profile uses bug, user-signal, experiment, architecture, incident, security, requirement, research, ops, eval-finding, launch-asset, legal-compliance. The `operations` profile uses request, incident, ops, security, docs. The `creative` profile uses brief, content-request, asset, experiment, report. The `research` profile uses question, study, synthesis, report.
90
+ Anything dropped into `inbox/` (a bug report, a customer comment, a competitor PDF, a postmortem draft) is classified by the active profile's intake taxonomy. The default `rnd` profile uses bug, user-signal, experiment, architecture, incident, security, requirement, research, ops, eval-finding, launch-asset, legal-compliance. The `operations` profile uses request, incident, ops, security, docs. The `creative` profile uses brief, content-request, asset, experiment, report. The `research` profile uses question, study, synthesis, report.
91
91
 
92
92
  Each signal gets a primary owner and a recommended handoff chain. Inspect with `construct intake list` and `construct intake show <id>`. Generate a task graph with `construct graph from-intake <id>`. The classifier runs in the daemon and is deterministic. The agent in your editor does the actual analysis. [Intake and triage](https://geraldmaron.github.io/construct/concepts/intake-and-triage).
93
93
 
@@ -111,11 +111,11 @@ Construct's writes are scoped and disclosed up front. The default `construct ins
111
111
  | Machine | `construct install --scope=user` | `~/.construct/config.env`, `~/.construct/lib` (symlink), `~/.construct/services/`, `~/Library/LaunchAgents/` (macOS), MCP entries in `~/.config/opencode/opencode.json` and `~/.codex/config.toml`, marker block in `~/.claude/CLAUDE.md`, hook injection in `~/.claude/settings.json` (last two require interactive consent or `--yes`) |
112
112
  | Never touched | — | Shell rc files (`~/.bashrc`, `~/.zshrc`), npm global config, `git config --global` |
113
113
 
114
- Full table with file:line citations and the per-hook performance budget contract: [Architecture — Footprint contract](https://geraldmaron.github.io/construct/concepts/architecture#footprint-contract) and [ADR 0029](docs/adr/0029-install-scopes-and-hook-budgets.md).
114
+ Full table with file:line citations and the per-hook performance budget contract: [Architecture — Footprint contract](https://geraldmaron.github.io/construct/concepts/architecture#footprint-contract) and [ADR 0029](docs/decisions/adr/0029-install-scopes-and-hook-budgets.md).
115
115
 
116
116
  ## Learning loops
117
117
 
118
- Construct gets smarter on its own. Every session ends with an automatic capture: tools used, files touched, what the final reply said. That goes into `.cx/observations/` and is searchable from the next session. See [`docs/concepts/learning-loops.mdx`](./docs/concepts/learning-loops.mdx) for what's wired, what's coming, and how to turn pieces off.
118
+ Construct gets smarter on its own. Every session ends with an automatic capture: tools used, files touched, what the final reply said. That goes into `.cx/observations/` and is searchable from the next session. See [`docs/guides/concepts/learning-loops.mdx`](./docs/guides/concepts/learning-loops.mdx) for what's wired, what's coming, and how to turn pieces off.
119
119
 
120
120
  ## `.cx/` is local-only runtime state
121
121
 
@@ -226,7 +226,7 @@ The embed daemon writes its supervisor stdout log to `~/.cx/runtime/embed-daemon
226
226
  | `construct doc` | Verify or inspect auditability stamps on Construct-generated markdown files |
227
227
  | `construct docs:check` | Check for missing how-to guides (alias for `docs check`) |
228
228
  | `construct docs:reconcile` | Reconcile docs against the registry |
229
- | `construct docs:site` | Regenerate generated reference pages under docs/reference/ |
229
+ | `construct docs:site` | Regenerate generated reference pages under docs/guides/reference/ |
230
230
  | `construct docs:update` | Regenerate AUTO-managed doc regions (alias for `docs update`) |
231
231
  | `construct docs:verify` | Validate documentation quality (alias for `docs verify`) |
232
232
  | `construct impact` | Change-impact analysis — map changed files to affected tests, capabilities, and workflows |
@@ -269,7 +269,7 @@ The embed daemon writes its supervisor stdout log to `~/.cx/runtime/embed-daemon
269
269
 
270
270
  - [`CONTRIBUTING.md`](./CONTRIBUTING.md). Branch workflow, gates, review expectations.
271
271
  - [`CHANGELOG.md`](./CHANGELOG.md). Release history.
272
- - [`docs/concepts/architecture.mdx`](./docs/concepts/architecture.mdx). Canonical architecture.
272
+ - [`docs/guides/concepts/architecture.mdx`](./docs/guides/concepts/architecture.mdx). Canonical architecture.
273
273
  - [`AGENTS.md`](./AGENTS.md). Agent operating contract.
274
274
 
275
275
  ## Project structure
package/bin/construct CHANGED
@@ -38,6 +38,7 @@ import {
38
38
  PROJECT_CONFIG_FILENAME,
39
39
  } from '../lib/config/project-config.mjs';
40
40
  import { validateProjectConfig } from '../lib/config/schema.mjs';
41
+ import { configDir, doctorRoot } from '../lib/config/xdg.mjs';
41
42
  import {
42
43
  DEPLOYMENT_MODES,
43
44
  DEPLOYMENT_MODE_ENV_KEY,
@@ -400,7 +401,7 @@ async function cmdDoctor() {
400
401
  }
401
402
 
402
403
  // .env files
403
- for (const envPath of [join(process.cwd(), '.env'), join(homedir(), '.env'), join(homedir(), '.construct', 'config.env')]) {
404
+ for (const envPath of [join(process.cwd(), '.env'), join(homedir(), '.env'), getUserEnvPath(homedir())]) {
404
405
  if (fs.existsSync(envPath)) {
405
406
  try {
406
407
  const content = fs.readFileSync(envPath, 'utf8');
@@ -592,7 +593,7 @@ async function cmdDoctor() {
592
593
  // minute at 50MB, but a freshly-upgraded install with a 34GB legacy
593
594
  // log shouldn't wait 60s. --fix-embed-log forces rotation now.
594
595
 
595
- const embedLogPath = path.join(userHome, '.cx', 'runtime', 'embed-daemon.log');
596
+ const embedLogPath = path.join(doctorRoot(userHome), 'runtime', 'embed-daemon.log');
596
597
  if (fs.existsSync(embedLogPath)) {
597
598
  const sizeMb = fs.statSync(embedLogPath).size / 1024 / 1024;
598
599
  const FORCE_THRESHOLD_MB = Number(process.env.CONSTRUCT_DOCTOR_EMBED_LOG_FORCE_MB) || 500;
@@ -627,11 +628,11 @@ async function cmdDoctor() {
627
628
  'intent-verifications.jsonl',
628
629
  ];
629
630
  const legacyState = projectScopedNames
630
- .map((name) => ({ name, path: path.join(userHome, '.cx', name) }))
631
+ .map((name) => ({ name, path: path.join(doctorRoot(userHome), name) }))
631
632
  .filter((p) => fs.existsSync(p.path) && fs.statSync(p.path).size > 0);
632
633
 
633
634
  if (legacyState.length > 0 && wantsFixMigrateState) {
634
- const archiveDir = path.join(userHome, '.cx', 'legacy');
635
+ const archiveDir = path.join(doctorRoot(userHome), 'legacy');
635
636
  fs.mkdirSync(archiveDir, { recursive: true });
636
637
  const ts = new Date().toISOString().replace(/[:.]/g, '-');
637
638
  const moved = [];
@@ -739,13 +740,13 @@ async function cmdDoctor() {
739
740
  const vscodeSettingsOk = vscodeSettingsPaths.some((candidate) => fs.existsSync(candidate));
740
741
  if (vscodeSettingsOk) add('VS Code settings file', true, true);
741
742
  }
742
- add('User config ready', fs.existsSync(getUserEnvPath(HOME)) || fs.existsSync(path.join(HOME, '.construct')), true);
743
+ add('User config ready', fs.existsSync(getUserEnvPath(HOME)) || fs.existsSync(configDir(HOME)), true);
743
744
  add('skills/ directory', fs.existsSync(path.join(ROOT_DIR, 'skills')));
744
745
  add('rules/common/', fs.existsSync(path.join(ROOT_DIR, 'rules', 'common')));
745
746
  add('specialists/prompts/ directory', fs.existsSync(path.join(ROOT_DIR, 'specialists', 'prompts')));
746
747
  add('Hybrid storage env keys documented', fs.existsSync(path.join(ROOT_DIR, '.env.example')));
747
- fs.mkdirSync(path.join(HOME, '.cx', 'performance-reviews'), { recursive: true });
748
- add('~/.cx/performance-reviews/ ready', true);
748
+ fs.mkdirSync(path.join(doctorRoot(), 'performance-reviews'), { recursive: true });
749
+ add('performance-reviews/ ready', true);
749
750
 
750
751
  // construct.config.json — optional today (config is permitted to be absent;
751
752
  // env vars + defaults still work). The check enforces that when the file
@@ -765,33 +766,29 @@ async function cmdDoctor() {
765
766
 
766
767
  // Claude Code Stop-hook installation check. The settings template
767
768
  // wires several Stop hooks (policy-engine / stop-typecheck /
768
- // stop-notify / readme-age-check) that reference
769
- // $HOME/.construct/lib/hooks/*. If that path doesn't resolve, every
770
- // Stop hook fails silently — including stop-notify, which is what
771
- // appends session cost data. Result: the dashboard stops updating
772
- // and the user sees stale data. Surface loudly so the fix is one
773
- // command away.
769
+ // stop-notify / readme-age-check) whose commands sync resolves to the
770
+ // hook lib symlink. If those scripts don't resolve, every Stop hook
771
+ // fails silently — including stop-notify, which is what appends session
772
+ // cost data. Result: the dashboard stops updating and the user sees stale
773
+ // data. Surface loudly so the fix is one command away.
774
774
  try {
775
775
  const settingsPath = path.join(HOME, '.claude', 'settings.json');
776
776
  if (fs.existsSync(settingsPath)) {
777
777
  const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
778
778
  const stopHooks = settings?.hooks?.Stop || [];
779
- const installRoot = path.join(HOME, '.construct', 'lib', 'hooks');
780
- const installed = fs.existsSync(installRoot);
779
+ const installRoot = path.join(configDir(HOME), 'lib', 'hooks');
781
780
  const referenced = [];
782
781
  for (const group of stopHooks) {
783
782
  for (const h of (group?.hooks || [])) {
784
783
  const cmd = String(h?.command || '');
785
- const m = cmd.match(/\.construct\/lib\/hooks\/([a-zA-Z0-9_-]+\.mjs)/);
784
+ const m = cmd.match(/(?:^|["\s])(\/[^"\s]*\/lib\/hooks\/[a-zA-Z0-9_-]+\.mjs)/);
786
785
  if (m) referenced.push(m[1]);
787
786
  }
788
787
  }
789
- const missing = installed
790
- ? referenced.filter((f) => !fs.existsSync(path.join(installRoot, f)))
791
- : referenced;
788
+ const missing = referenced.filter((scriptPath) => !fs.existsSync(scriptPath));
792
789
  if (referenced.length > 0 && missing.length > 0) {
793
790
  add(
794
- `Claude Stop hooks broken: ${missing.length} hook${missing.length === 1 ? '' : 's'} missing in ~/.construct/lib/hooks/. Symlink the install: ln -sfn ${ROOT_DIR}/lib ~/.construct/lib`,
791
+ `Claude Stop hooks broken: ${missing.length} hook${missing.length === 1 ? '' : 's'} do not resolve. Re-run \`construct install --scope=user\` (symlinks ${ROOT_DIR}/lib → ${installRoot.replace(/\/hooks$/, '')}).`,
795
792
  false,
796
793
  false,
797
794
  );
@@ -1073,7 +1070,7 @@ async function cmdDoctor() {
1073
1070
  }
1074
1071
 
1075
1072
  try {
1076
- const failuresPath = path.join(HOME, '.cx', 'hook-failures.jsonl');
1073
+ const failuresPath = path.join(doctorRoot(), 'hook-failures.jsonl');
1077
1074
  if (fs.existsSync(failuresPath)) {
1078
1075
  const lines = fs.readFileSync(failuresPath, 'utf8').trim().split('\n').filter(Boolean);
1079
1076
  const cutoff = Date.now() - 24 * 60 * 60 * 1000;
@@ -1519,7 +1516,7 @@ async function cmdMigrate(args) {
1519
1516
  }
1520
1517
 
1521
1518
  async function cmdReview(args) {
1522
- fs.mkdirSync(path.join(HOME, '.cx', 'performance-reviews'), { recursive: true });
1519
+ fs.mkdirSync(path.join(doctorRoot(), 'performance-reviews'), { recursive: true });
1523
1520
  const sub = args[0];
1524
1521
 
1525
1522
  if (!sub || sub === 'run') {
@@ -2018,7 +2015,7 @@ async function _cmdPricing(args) {
2018
2015
  const f = describePricingCatalogFreshness();
2019
2016
  if (!f.present) { info(f.message); return; }
2020
2017
  try {
2021
- const cached = JSON.parse(fs.readFileSync(path.join(HOME, '.cx', 'pricing-cache.json'), 'utf8'));
2018
+ const cached = JSON.parse(fs.readFileSync(path.join(doctorRoot(), 'pricing-cache.json'), 'utf8'));
2022
2019
  const divergences = checkPricingDivergence(cached.models || []);
2023
2020
  if (divergences.length === 0) { info('No pricing divergences (>25%) between LiteLLM and the static fallback.'); return; }
2024
2021
  println(`⚠ ${divergences.length} divergence(s):`);
@@ -2116,15 +2113,13 @@ async function printIntakeHelp() {
2116
2113
  println(' construct intake metrics Show pipeline volume, velocity, and processing stats.');
2117
2114
  println(' construct intake integrate <id> <system> Push intake to external system (github, jira, confluence).');
2118
2115
  println(' construct intake integrate <id> <system> --create-issue Create an issue from the intake packet.');
2119
- println(' construct intake config Show current watch dirs + scan depth.');
2116
+ println(' construct intake config Show the inbox/ drop zone + scan depth.');
2120
2117
  println(' construct intake config set --depth=N [--add-dir=PATH] [--remove-dir=PATH]');
2118
+ println(' Update intake config. --depth caps at the hard limit.');
2121
2119
  println(' construct intake extraction-warnings [--since=7d] [--kind=table] [--limit=50]');
2122
2120
  println(' Summarize ~/.cx/intake/extraction-warnings.jsonl (dropped content log).');
2123
2121
  println(' construct intake needs-asr list List audio/video files awaiting ASR transcription.');
2124
2122
  println(' construct intake needs-asr show <id> Show a specific ASR-pending packet.');
2125
- println(' Update intake config. --depth caps at the hard limit.');
2126
- println(' construct intake config set --include-project-inbox=on|off');
2127
- println(' construct intake config set --include-docs-intake=on|off');
2128
2123
  }
2129
2124
 
2130
2125
  function parseKvFlags(args, keys) {
@@ -2618,7 +2613,6 @@ async function cmdIntakeReroute(args, cwd) {
2618
2613
 
2619
2614
  async function cmdIntakeExtractionWarnings(args) {
2620
2615
  const { createReadStream, existsSync } = await import('node:fs');
2621
- const { homedir } = await import('node:os');
2622
2616
  const { join } = await import('node:path');
2623
2617
  const { createInterface } = await import('node:readline');
2624
2618
 
@@ -2636,7 +2630,7 @@ async function cmdIntakeExtractionWarnings(args) {
2636
2630
  }
2637
2631
  }
2638
2632
 
2639
- const logPath = join(homedir(), '.cx', 'intake', 'extraction-warnings.jsonl');
2633
+ const logPath = join(doctorRoot(), 'intake', 'extraction-warnings.jsonl');
2640
2634
  if (!existsSync(logPath)) {
2641
2635
  println('No extraction warnings recorded yet.');
2642
2636
  return;
@@ -2687,11 +2681,10 @@ async function cmdIntakeExtractionWarnings(args) {
2687
2681
 
2688
2682
  async function cmdIntakeNeedsAsr(args) {
2689
2683
  const { existsSync, readdirSync, readFileSync } = await import('node:fs');
2690
- const { homedir } = await import('node:os');
2691
2684
  const { join } = await import('node:path');
2692
2685
 
2693
2686
  const sub = args[0] || 'list';
2694
- const asrDir = join(homedir(), '.cx', 'intake', 'needs-asr');
2687
+ const asrDir = join(doctorRoot(), 'intake', 'needs-asr');
2695
2688
 
2696
2689
  if (sub === 'list') {
2697
2690
  if (!existsSync(asrDir)) { println('No audio/video files awaiting ASR.'); return; }
@@ -2856,33 +2849,23 @@ async function cmdIntakeConfig(args) {
2856
2849
  const cwd = process.cwd();
2857
2850
 
2858
2851
  const action = args[0];
2859
- if (action === 'migrate') {
2860
- const { migrateLegacyIntakeConfig } = await import('../lib/intake/intake-config.mjs');
2861
- const result = migrateLegacyIntakeConfig(cwd);
2862
- if (!result.migrated) {
2863
- info(result.reason ?? 'nothing to migrate');
2864
- return;
2865
- }
2866
- info('Migrated .cx/intake-config.json → construct.config.json intakePolicy');
2867
- println(JSON.stringify(result.policy, null, 2));
2868
- return;
2869
- }
2852
+
2853
+ // Single-zone model (ADR-0045 §C): the canonical `inbox/` is always watched.
2854
+ // Only maxDepth and additionalDirs are configurable.
2870
2855
 
2871
2856
  if (!action || action === 'show') {
2872
2857
  const cfg = loadIntakeConfig(cwd, process.env);
2873
2858
  const desc = describeIntakeDepth(cfg.maxDepth);
2874
2859
  println(`Intake config (root: ${cwd})`);
2875
2860
  println('');
2876
- println(` includeProjectInbox: ${cfg.includeProjectInbox ? 'on' : 'off'} (.cx/inbox)`);
2877
- println(` includeRootInbox: ${(cfg.includeRootInbox ?? cfg.includeArchetypeInbox) ? 'on' : 'off'} (inbox/)`);
2878
- println(` includeDocsIntake: ${cfg.includeDocsIntake ? 'on' : 'off'} (docs/intake)`);
2861
+ println(' drop zone: inbox/ (always watched)');
2879
2862
  println(` maxDepth: ${cfg.maxDepth} — ${desc.label}`);
2880
2863
  println(` ${desc.detail}`);
2881
2864
  println(` hardMaxDepth: ${INTAKE_HARD_MAX_DEPTH}`);
2882
2865
  if (cfg.parentDirs.length === 0) {
2883
- println(' parentDirs: (none — only the built-in zones are watched)');
2866
+ println(' additionalDirs: (none — only inbox/ is watched)');
2884
2867
  } else {
2885
- println(' parentDirs:');
2868
+ println(' additionalDirs:');
2886
2869
  for (const dir of cfg.parentDirs) println(` - ${dir}`);
2887
2870
  }
2888
2871
  println('');
@@ -2903,15 +2886,6 @@ async function cmdIntakeConfig(args) {
2903
2886
  const depthFlag = tail.find((a) => a.startsWith('--depth='));
2904
2887
  if (depthFlag) patch.maxDepth = Number(depthFlag.split('=')[1]);
2905
2888
 
2906
- const projectFlag = tail.find((a) => a.startsWith('--include-project-inbox='));
2907
- if (projectFlag) patch.includeProjectInbox = /^(on|true|1|yes)$/i.test(projectFlag.split('=')[1] || '');
2908
-
2909
- const docsFlag = tail.find((a) => a.startsWith('--include-docs-intake='));
2910
- if (docsFlag) patch.includeDocsIntake = /^(on|true|1|yes)$/i.test(docsFlag.split('=')[1] || '');
2911
-
2912
- const rootFlag = tail.find((a) => a.startsWith('--include-root-inbox='));
2913
- if (rootFlag) patch.includeRootInbox = /^(on|true|1|yes)$/i.test(rootFlag.split('=')[1] || '');
2914
-
2915
2889
  const addFlags = tail.filter((a) => a.startsWith('--add-dir='));
2916
2890
  const removeFlags = tail.filter((a) => a.startsWith('--remove-dir='));
2917
2891
 
@@ -2924,7 +2898,7 @@ async function cmdIntakeConfig(args) {
2924
2898
  }
2925
2899
 
2926
2900
  if (Object.keys(patch).length === 0) {
2927
- errorln('Nothing to change. Pass --depth=N, --add-dir=PATH, --remove-dir=PATH, --include-project-inbox=on|off, or --include-docs-intake=on|off.');
2901
+ errorln('Nothing to change. Pass --depth=N, --add-dir=PATH, or --remove-dir=PATH.');
2928
2902
  process.exit(1);
2929
2903
  }
2930
2904
 
@@ -4034,7 +4008,7 @@ async function cmdPluginEngine(args) {
4034
4008
  const [action = 'list', ...rest] = args;
4035
4009
  const isGlobal = rest.includes('--global');
4036
4010
  const targetPath = isGlobal
4037
- ? path.join(HOME, '.construct', 'plugins.json')
4011
+ ? path.join(configDir(HOME), 'plugins.json')
4038
4012
  : path.join(process.cwd(), '.cx', 'plugins.json');
4039
4013
 
4040
4014
  function readPluginsJson() {
@@ -4241,7 +4215,7 @@ async function cmdEvals(args) {
4241
4215
  const report = await evaluateFixture(fixturePath);
4242
4216
 
4243
4217
  try {
4244
- const cacheDir = path.join(HOME, '.cx', 'evals');
4218
+ const cacheDir = path.join(doctorRoot(), 'evals');
4245
4219
  fs.mkdirSync(cacheDir, { recursive: true });
4246
4220
  const ts = new Date().toISOString();
4247
4221
  fs.writeFileSync(
@@ -4438,7 +4412,7 @@ async function cmdDocsCheck(args) {
4438
4412
  println('Commands with no linked how-to in docs/README.md:');
4439
4413
  for (const name of result.uncovered) println(` ✗ construct ${name}`);
4440
4414
  println('');
4441
- println(`Add cookbook recipes under docs/cookbook/ and link them from the cookbook index.`);
4415
+ println(`Add cookbook recipes under docs/guides/cookbook/ and link them from the cookbook index.`);
4442
4416
  process.exitCode = 1;
4443
4417
  }
4444
4418
  }
@@ -4449,7 +4423,7 @@ async function cmdDocsSite(args = []) {
4449
4423
  if (check) {
4450
4424
  const { drift } = checkFumadocsReferenceDrift({ rootDir: ROOT_DIR });
4451
4425
  if (drift.length === 0) {
4452
- ok('docs/reference/ is up to date.');
4426
+ ok('docs/guides/reference/ is up to date.');
4453
4427
  return;
4454
4428
  }
4455
4429
  println('');
@@ -4460,9 +4434,9 @@ async function cmdDocsSite(args = []) {
4460
4434
  }
4461
4435
  const { written } = buildFumadocsReference({ rootDir: ROOT_DIR });
4462
4436
  if (written.length === 0) {
4463
- ok('docs/reference/ already up to date.');
4437
+ ok('docs/guides/reference/ already up to date.');
4464
4438
  } else {
4465
- ok(`docs/reference/ regenerated (${written.length} file${written.length === 1 ? '' : 's'}).`);
4439
+ ok(`docs/guides/reference/ regenerated (${written.length} file${written.length === 1 ? '' : 's'}).`);
4466
4440
  }
4467
4441
  }
4468
4442
 
@@ -4961,7 +4935,7 @@ async function cmdProvider(args) {
4961
4935
  if (!id) { errorln(`Usage: construct provider plugins ${action} <id> [<package>]`); process.exit(1); }
4962
4936
  const isGlobal = args.includes('--global');
4963
4937
  const targetPath = isGlobal
4964
- ? path.join(HOME, '.construct', 'providers.json')
4938
+ ? path.join(configDir(HOME), 'providers.json')
4965
4939
  : path.join(process.cwd(), '.cx', 'providers.json');
4966
4940
  let data = { providers: [] };
4967
4941
  try { data = JSON.parse(fs.readFileSync(targetPath, 'utf8')); } catch { /* fresh */ }
@@ -5486,7 +5460,7 @@ async function cmdTelemetryQuery(args) {
5486
5460
  println(' errors [--agent=<name>] [--since=24h] error span summary');
5487
5461
  println(' trace <traceId> full span tree');
5488
5462
  println('');
5489
- println('For Langfuse/Honeycomb/Grafana Tempo/Datadog, see docs/concepts/observability.md');
5463
+ println('For Langfuse/Honeycomb/Grafana Tempo/Datadog, see docs/guides/concepts/observability.md');
5490
5464
  }
5491
5465
 
5492
5466
  async function cmdHook(args) {
@@ -6553,7 +6527,7 @@ const handlers = new Map([
6553
6527
 
6554
6528
  println('');
6555
6529
  println(`Scaffolding a new profile draft. Three questions, then a preview.`);
6556
- println(`Background: docs/concepts/persona-research.md and docs/concepts/profile-lifecycle.md`);
6530
+ println(`Background: docs/guides/concepts/persona-research.md and docs/guides/concepts/profile-lifecycle.md`);
6557
6531
  println('');
6558
6532
  if (!displayName) displayName = await ask('Display name?', id);
6559
6533
  if (seedDepartments.length === 0) {
@@ -6612,7 +6586,7 @@ const handlers = new Map([
6612
6586
  println('');
6613
6587
  println(`✓ draft created at ${pathMod.relative(process.cwd(), result.dir)}`);
6614
6588
  println('');
6615
- println(`Next steps (in order, per docs/concepts/profile-lifecycle.md):`);
6589
+ println(`Next steps (in order, per docs/guides/concepts/profile-lifecycle.md):`);
6616
6590
  println(` 1. Discover (cx-ux-researcher): fill personas/<role>.md from interviews + primary sources.`);
6617
6591
  println(` 2. Frame (cx-product-manager): fill departments/<dept>.md charters and intake taxonomy.`);
6618
6592
  println(` 3. Architect (cx-architect): reconcile role reuse vs new; populate departments[] in profile.json.`);
@@ -11,4 +11,4 @@ ADR format:
11
11
  - FILES AFFECTED: paths future contributors should inspect
12
12
  - FOLLOW-UP: docs, tests, migrations, or risks to track
13
13
 
14
- Save to `docs/adr/ADR-{NNN}-{slug}.md` and `.cx/decisions/` if those directories exist.
14
+ Save to `docs/decisions/adr/ADR-{NNN}-{slug}.md` and `.cx/decisions/` if those directories exist.
@@ -11,4 +11,4 @@ Structure:
11
11
  - ROLLBACK: how to undo if something goes wrong
12
12
  - ESCALATION: who to contact and when
13
13
 
14
- Save to `docs/runbooks/{service}-{operation}.md` if the directory exists.
14
+ Save to `docs/operations/runbooks/{service}-{operation}.md` if the directory exists.
@@ -78,7 +78,7 @@ Required internal coverage:
78
78
  - Name the expected behavior in repo terms: branch confirmation, approval boundary, routing, verification, blocker surfacing, anti-pattern avoidance.
79
79
  - For bad examples, show the failure plainly and explain why it violates the surface contract.
80
80
 
81
- See `docs/concepts/prompt-surfaces.md` for the canonical public-vs-internal taxonomy.
81
+ See `docs/guides/concepts/prompt-surfaces.md` for the canonical public-vs-internal taxonomy.
82
82
 
83
83
  ## Scope
84
84
 
@@ -39,4 +39,4 @@ Edit sources, then re-run the generator. Outputs land in `.tmp/distribution-exam
39
39
  node bin/construct export examples/distribution/sources/adr.md --to=pdf --figures
40
40
  ```
41
41
 
42
- See [branding.md](../../docs/reference/branding.md) and [diagram-and-demo.md](../../docs/cookbook/diagram-and-demo.md).
42
+ See [branding.md](../../docs/guides/reference/branding.md) and [diagram-and-demo.md](../../docs/guides/cookbook/diagram-and-demo.md).
@@ -51,7 +51,7 @@ tokens -> dash
51
51
 
52
52
  ## Decision
53
53
 
54
- Adopt `lib/brand-tokens.mjs` as the single source of truth for visual brand primitives. Document naming, voice, tone, and profile rebrand in `docs/reference/branding.md`. Enforce retired-font drift with `scripts/audit/03d-brand.mjs` in the audit ratchet.
54
+ Adopt `lib/brand-tokens.mjs` as the single source of truth for visual brand primitives. Document naming, voice, tone, and profile rebrand in `docs/guides/reference/branding.md`. Enforce retired-font drift with `scripts/audit/03d-brand.mjs` in the audit ratchet.
55
55
 
56
56
  ## Rationale
57
57
 
@@ -79,6 +79,6 @@ Two-way door for documentation and audit scope; one-way for shipped PDFs already
79
79
 
80
80
  ## References
81
81
 
82
- - `docs/reference/branding.md`
82
+ - `docs/guides/reference/branding.md`
83
83
  - `lib/brand-tokens.mjs`
84
84
  - https://fonts.google.com/specimen/Space+Grotesk (accessed 2026-06-22)
@@ -60,6 +60,6 @@ One token set across all distributable formats:
60
60
 
61
61
  - Export: `construct export … --to=pptx`
62
62
  - Regenerate: `npm run examples:deck`
63
- - Matrix: [Document I/O reference](/reference/document-io)
63
+ - Matrix: [Document I/O reference](/guides/reference/document-io)
64
64
 
65
65
  Review outputs in `.tmp/distribution-examples/`.
@@ -157,5 +157,5 @@ https://langchain-ai.github.io/langgraph/ (accessed 2026-06-19).
157
157
 
158
158
  - Construct artifact manifest: `specialists/artifact-manifest.json`
159
159
  - PRD workflow skill: `skills/docs/prd-workflow.md`
160
- - Publish cookbook: `docs/cookbook/diagram-and-demo.md`
160
+ - Publish cookbook: `docs/guides/cookbook/diagram-and-demo.md`
161
161
  - Golden D2 sources: `tests/fixtures/publish/diagrams/`
@@ -32,7 +32,7 @@ Searched arXiv and vendor docs (2024–2026), reviewed three internal ADRs on in
32
32
  |---|---|---|---|---|---|---|---|
33
33
  | Multi-agent survey | secondary | B | 3 | 2023-08-01 | https://arxiv.org/abs/2308.08155 | yes | Patterns for specialist routing |
34
34
  | LangGraph overview | primary | A | 4 | 2025-01-15 | https://langchain-ai.github.io/langgraph/ | yes | Stateful agent graphs |
35
- | Construct architecture | primary | A | 5 | 2026-06-01 | docs/concepts/architecture.mdx | yes | Internal contract |
35
+ | Construct architecture | primary | A | 5 | 2026-06-01 | docs/guides/concepts/architecture.mdx | yes | Internal contract |
36
36
 
37
37
  ## Findings
38
38
 
@@ -77,4 +77,4 @@ Ship distribution examples (`npm run examples:distribution`) so evaluators can c
77
77
  ## References
78
78
 
79
79
  - https://arxiv.org/abs/2308.08155 (accessed 2026-06-22)
80
- - `docs/cookbook/diagram-and-demo.md`
80
+ - `docs/guides/cookbook/diagram-and-demo.md`
@@ -78,5 +78,5 @@ sequenceDiagram
78
78
 
79
79
  ## References
80
80
 
81
- - `docs/reference/branding.md`
81
+ - `docs/guides/reference/branding.md`
82
82
  - `scripts/generate-deck-examples.mjs` (prior art)
@@ -99,5 +99,5 @@ Preserve `construct tools detect --json` output and the failing pandoc/typst std
99
99
 
100
100
  ## References
101
101
 
102
- - `docs/reference/document-io.md`
103
- - `docs/cookbook/diagram-and-demo.md`
102
+ - `docs/guides/reference/document-io.md`
103
+ - `docs/guides/cookbook/diagram-and-demo.md`
@@ -26,7 +26,7 @@ Host-native agents optimize for single-session chat. Enterprise platform teams n
26
26
  ## Goals
27
27
 
28
28
  1. **Prove value in five minutes** — `npm run examples:distribution` produces a browsable gallery with figures.
29
- 2. **One brand contract** — `docs/reference/branding.md` + `03d-brand` audit ratchet.
29
+ 2. **One brand contract** — `docs/guides/reference/branding.md` + `03d-brand` audit ratchet.
30
30
  3. **Profile-aware language** — intake queue labels follow active profile rebrand in CLI, session prelude, and dashboard API.
31
31
 
32
32
  ## Non-goals
@@ -71,7 +71,7 @@ export function buildConstructIntegrationBody({ hasBeadsBlock = false, variant =
71
71
  '- **Durable state** lives in `.cx/` (context, knowledge, intake, traces) and Beads.',
72
72
  ' If services are down, resume from `plan.md`, `.cx/context.md`, the latest',
73
73
  ' `.cx/handoffs/` file, Beads, and git.',
74
- '- **Signals**: drop a file into `.cx/inbox/`; `construct intake` classifies and routes it.',
74
+ '- **Signals**: drop a file into `inbox/`; `construct intake` classifies and routes it.',
75
75
  '- **Specialists** (architect, reviewer, security, …) are dispatched by Construct — you',
76
76
  ' do not call them directly.',
77
77
  tracker,
@@ -41,12 +41,16 @@ function inferRfcSubtype(rel, base) {
41
41
  return 'rfc';
42
42
  }
43
43
 
44
+ // Match both this repo's bucketed layout (docs/specs/prd, docs/decisions/adr, …)
45
+ // and the init-lane layout that `construct init` scaffolds downstream (docs/prd,
46
+ // docs/adr, …), so artifact-type inference works in either project shape.
47
+
44
48
  const PREFIX_RULES = [
45
- [/^docs\/prd\//, inferPrdSubtype],
46
- [/^docs\/adr\//, () => 'adr'],
47
- [/^docs\/rfc\//, inferRfcSubtype],
48
- [/^docs\/research\//, () => 'research-brief'],
49
- [/^docs\/runbooks?\//, () => 'runbook'],
49
+ [/^docs\/(?:specs\/)?prd\//, inferPrdSubtype],
50
+ [/^docs\/(?:decisions\/)?adr\//, () => 'adr'],
51
+ [/^docs\/(?:decisions\/)?rfc\//, inferRfcSubtype],
52
+ [/^docs\/(?:notes\/)?research\//, () => 'research-brief'],
53
+ [/^docs\/(?:operations\/)?runbooks?\//, () => 'runbook'],
50
54
  [/^\.cx\/research\//, () => 'research-brief'],
51
55
  ];
52
56
 
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Merges live registry, contracts, skills, prompts, and audit-enrichments into
5
5
  * one machine-readable matrix. Powers `construct audit specialists` and the
6
- * human-readable docs/concepts/specialist-skill-audit.md companion.
6
+ * human-readable docs/guides/concepts/specialist-skill-audit.md companion.
7
7
  */
8
8
 
9
9
  import fs from 'node:fs';
@@ -16,17 +16,17 @@
16
16
  * Corrupted links cannot be re-derived, so a `chain_reset` boundary line seals
17
17
  * the damaged prefix as immutable legacy and re-bases the live chain;
18
18
  * verifyChain validates only from the last boundary forward. See
19
- * docs/concepts/observability.md.
19
+ * docs/guides/concepts/observability.md.
20
20
  */
21
21
  import { readFileSync, existsSync, statSync, mkdirSync } from 'node:fs';
22
22
  import { createHash } from 'node:crypto';
23
23
  import { join, dirname } from 'node:path';
24
- import { homedir } from 'node:os';
25
24
 
26
25
  import { appendBounded, readLastLineAcrossSegments } from './logging/rotate.mjs';
27
26
  import { withFileLockSync } from './storage/file-lock.mjs';
27
+ import { doctorRoot } from './config/xdg.mjs';
28
28
 
29
- const AUDIT_FILE = join(homedir(), '.cx', 'audit-trail.jsonl');
29
+ const AUDIT_FILE = join(doctorRoot(), 'audit-trail.jsonl');
30
30
 
31
31
  function sha256(input) {
32
32
  return createHash('sha256').update(input).digest('hex');
package/lib/auto-docs.mjs CHANGED
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * buildSite() is retained for compatibility; the public site now renders docs/
13
13
  * directly through Next.js (apps/docs/) and buildFumadocsReference() emits
14
- * generated reference Markdown into docs/reference/.
14
+ * generated reference Markdown into docs/guides/reference/.
15
15
  */
16
16
 
17
17
  import fs from 'node:fs';
@@ -78,7 +78,7 @@ function buildCoreDocsContract() {
78
78
  '| `.cx/context.md` | Human-readable resumable project context | Active work, decisions, architecture assumptions, or open questions change |',
79
79
  '| `.cx/context.json` | Machine-readable resumable context | Context state needs to stay in sync with `.cx/context.md` |',
80
80
  '| `docs/README.md` | Docs index and maintenance contract | Core docs set or maintenance expectations change |',
81
- '| `docs/concepts/architecture.mdx` | Canonical architecture and invariants | Runtime shape, contracts, boundaries, or major dependencies change |',
81
+ '| `docs/guides/concepts/architecture.mdx` | Canonical architecture and invariants | Runtime shape, contracts, boundaries, or major dependencies change |',
82
82
  '',
83
83
  '`plan.md` is a local working document. `construct init` creates it for the active session, but it is gitignored and not committed; durable work belongs in the tracker (Beads or external).',
84
84
  '',
@@ -191,7 +191,7 @@ function buildAgentsTable(rootDir) {
191
191
  // --- public API ---
192
192
 
193
193
  /**
194
- * Regenerate all AUTO regions in README.md, docs/README.md, and docs/concepts/architecture.mdx.
194
+ * Regenerate all AUTO regions in README.md, docs/README.md, and docs/guides/concepts/architecture.mdx.
195
195
  * Returns { changed: string[], checked: boolean }.
196
196
  * With check:true writes nothing and sets changed to files that would differ.
197
197
  */
@@ -209,7 +209,7 @@ export async function regenerateDocs({ rootDir, check = false } = {}) {
209
209
  },
210
210
  },
211
211
  {
212
- file: path.join(rootDir, 'docs', 'concepts', 'architecture.mdx'),
212
+ file: path.join(rootDir, 'docs', 'guides', 'concepts', 'architecture.mdx'),
213
213
  regions: {
214
214
  agents: buildAgentsTable(rootDir),
215
215
  },
@@ -268,7 +268,7 @@ export function checkDocsCoverage({ rootDir } = {}) {
268
268
  // Build a combined corpus: docs/README.md + every linked guide file
269
269
  let corpus = docsReadme;
270
270
  for (const target of linkTargets) {
271
- if (!target.startsWith('./how-to/') && !target.startsWith('./cookbook/')) continue;
271
+ if (!target.startsWith('./guides/cookbook/')) continue;
272
272
  const filePath = path.join(rootDir, 'docs', target.replace(/^\.\//, ''));
273
273
  const content = readFile(filePath);
274
274
  if (content) corpus += '\n' + content;
@@ -423,7 +423,7 @@ function renderAgentsPage(rootDir) {
423
423
  }
424
424
 
425
425
  /**
426
- * Emit MDX reference pages into docs/reference/ for the Next.js docs site
426
+ * Emit MDX reference pages into docs/guides/reference/ for the Next.js docs site
427
427
  * in apps/docs/. Mirrors the data sources behind AUTO regions and buildSite()
428
428
  * but writes Markdown files the docs app can pull in directly (no MkDocs
429
429
  * intermediate). Name kept for backwards compatibility with callers that
@@ -432,7 +432,7 @@ function renderAgentsPage(rootDir) {
432
432
  */
433
433
  export function buildFumadocsReference({ rootDir } = {}) {
434
434
  rootDir = rootDir ?? process.cwd();
435
- const refDir = path.join(rootDir, 'docs', 'reference');
435
+ const refDir = path.join(rootDir, 'docs', 'guides', 'reference');
436
436
  const cliDir = path.join(refDir, 'cli');
437
437
  fs.mkdirSync(cliDir, { recursive: true });
438
438
 
@@ -468,7 +468,7 @@ export function buildFumadocsReference({ rootDir } = {}) {
468
468
  const slug = slugify(cat);
469
469
  const commands = CLI_COMMANDS_BY_CATEGORY[cat] ?? [];
470
470
  if (!commands.length) return null;
471
- return `- [${cat}](/reference/cli/${slug}) — ${commands.length} command${commands.length === 1 ? '' : 's'}`;
471
+ return `- [${cat}](/guides/reference/cli/${slug}) — ${commands.length} command${commands.length === 1 ? '' : 's'}`;
472
472
  }).filter(Boolean),
473
473
  '',
474
474
  ].join('\n');
@@ -499,7 +499,7 @@ export function buildFumadocsReference({ rootDir } = {}) {
499
499
 
500
500
  export function checkFumadocsReferenceDrift({ rootDir } = {}) {
501
501
  rootDir = rootDir ?? process.cwd();
502
- const refDir = path.join(rootDir, 'docs', 'reference');
502
+ const refDir = path.join(rootDir, 'docs', 'guides', 'reference');
503
503
  const cliDir = path.join(refDir, 'cli');
504
504
  const drift = [];
505
505
 
@@ -534,7 +534,7 @@ export function checkFumadocsReferenceDrift({ rootDir } = {}) {
534
534
  const slug = slugify(cat);
535
535
  const commands = CLI_COMMANDS_BY_CATEGORY[cat] ?? [];
536
536
  if (!commands.length) return null;
537
- return `- [${cat}](/reference/cli/${slug}) — ${commands.length} command${commands.length === 1 ? '' : 's'}`;
537
+ return `- [${cat}](/guides/reference/cli/${slug}) — ${commands.length} command${commands.length === 1 ? '' : 's'}`;
538
538
  }).filter(Boolean),
539
539
  '',
540
540
  ].join('\n');