@geraldmaron/construct 1.2.0 → 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 (214) hide show
  1. package/README.md +8 -7
  2. package/bin/construct +206 -62
  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-manifest.mjs +150 -4
  16. package/lib/artifact-type-from-path.mjs +9 -5
  17. package/lib/artifact-workflow.mjs +252 -0
  18. package/lib/audit-specialists.mjs +1 -1
  19. package/lib/audit-trail.mjs +3 -3
  20. package/lib/auto-docs.mjs +13 -11
  21. package/lib/bootstrap/built-ins.mjs +3 -3
  22. package/lib/boundary.mjs +3 -2
  23. package/lib/brand-prose.mjs +1 -1
  24. package/lib/cache-governor.js +4 -4
  25. package/lib/certification/document-io-fixtures.mjs +1 -1
  26. package/lib/chat/config.mjs +2 -2
  27. package/lib/chat/desktop-binary.mjs +5 -3
  28. package/lib/chat/present.mjs +1 -0
  29. package/lib/cli-commands.mjs +23 -6
  30. package/lib/cli-service-inventory.mjs +42 -0
  31. package/lib/comment-lint.mjs +9 -5
  32. package/lib/config/alias.mjs +3 -2
  33. package/lib/config/intake-policy.mjs +139 -0
  34. package/lib/config/project-config.mjs +106 -7
  35. package/lib/config/schema.mjs +45 -6
  36. package/lib/config/source-targets.mjs +311 -0
  37. package/lib/config/xdg.mjs +63 -0
  38. package/lib/contracts/violation-log.mjs +2 -2
  39. package/lib/cost-ledger.mjs +4 -3
  40. package/lib/cost.mjs +3 -2
  41. package/lib/decisions/registry.mjs +3 -3
  42. package/lib/distill.mjs +2 -2
  43. package/lib/docs-verify.mjs +2 -0
  44. package/lib/doctor/audit.mjs +4 -3
  45. package/lib/doctor/cli.mjs +0 -1
  46. package/lib/doctor/index.mjs +4 -4
  47. package/lib/doctor/report.mjs +3 -3
  48. package/lib/doctor/source-checkout.mjs +16 -0
  49. package/lib/doctor/watchers/bd-watch.mjs +5 -5
  50. package/lib/doctor/watchers/cost.mjs +4 -4
  51. package/lib/doctor/watchers/disk.mjs +4 -3
  52. package/lib/doctor/watchers/service-health.mjs +5 -5
  53. package/lib/document-export.mjs +39 -17
  54. package/lib/document-extract.mjs +3 -3
  55. package/lib/document-ingest.mjs +13 -3
  56. package/lib/efficiency.mjs +2 -1
  57. package/lib/embed/approval-queue.mjs +3 -2
  58. package/lib/embed/auto-sources.mjs +44 -0
  59. package/lib/embed/cli.mjs +9 -6
  60. package/lib/embed/config.mjs +6 -5
  61. package/lib/embed/daemon.mjs +6 -5
  62. package/lib/embed/demand-fetch.mjs +50 -19
  63. package/lib/embed/inbox-live-watcher.mjs +1 -1
  64. package/lib/embed/inbox.mjs +52 -41
  65. package/lib/embed/intake-metrics.mjs +3 -2
  66. package/lib/embed/reconcile.mjs +1 -1
  67. package/lib/embed/semantic.mjs +3 -2
  68. package/lib/embed/supervision.mjs +4 -2
  69. package/lib/embed/worker.mjs +2 -1
  70. package/lib/embedded-contract/index.mjs +11 -0
  71. package/lib/engine/registry.mjs +3 -3
  72. package/lib/env-config.mjs +34 -12
  73. package/lib/export-branding.mjs +32 -0
  74. package/lib/features.mjs +4 -3
  75. package/lib/health-check.mjs +7 -6
  76. package/lib/hook-health.mjs +5 -4
  77. package/lib/hooks/_lib/log.mjs +4 -3
  78. package/lib/hooks/_lib/output-mode.mjs +2 -1
  79. package/lib/hooks/agent-tracker.mjs +3 -4
  80. package/lib/hooks/audit-reads.mjs +2 -2
  81. package/lib/hooks/audit-trail.mjs +2 -2
  82. package/lib/hooks/bash-output-logger.mjs +3 -3
  83. package/lib/hooks/brand-prose-lint.mjs +1 -1
  84. package/lib/hooks/ci-status-check.mjs +3 -2
  85. package/lib/hooks/context-watch.mjs +2 -2
  86. package/lib/hooks/context-window-recovery.mjs +5 -5
  87. package/lib/hooks/dep-audit.mjs +2 -2
  88. package/lib/hooks/doc-coupling-check.mjs +3 -2
  89. package/lib/hooks/edit-accumulator.mjs +3 -2
  90. package/lib/hooks/edit-guard.mjs +3 -3
  91. package/lib/hooks/guard-bash.mjs +2 -2
  92. package/lib/hooks/model-fallback.mjs +2 -1
  93. package/lib/hooks/policy-engine.mjs +4 -3
  94. package/lib/hooks/pre-compact.mjs +7 -7
  95. package/lib/hooks/readme-age-check.mjs +2 -2
  96. package/lib/hooks/session-optimize.mjs +4 -3
  97. package/lib/hooks/session-start.mjs +20 -16
  98. package/lib/hooks/stop-notify.mjs +9 -8
  99. package/lib/hooks/stop-typecheck.mjs +4 -3
  100. package/lib/hooks/test-watch.mjs +2 -2
  101. package/lib/host-disposition.mjs +1 -1
  102. package/lib/ingest/provider-extract.mjs +2 -1
  103. package/lib/init/detect-existing-structure.mjs +2 -2
  104. package/lib/init/doc-lanes.mjs +3 -10
  105. package/lib/init-docs.mjs +12 -54
  106. package/lib/init-unified.mjs +47 -92
  107. package/lib/init.mjs +7 -5
  108. package/lib/install/desktop-binary-download.mjs +5 -2
  109. package/lib/intake/constants.mjs +29 -0
  110. package/lib/intake/daemon.mjs +31 -8
  111. package/lib/intake/intake-config.mjs +32 -112
  112. package/lib/integrations/intake-integrations.mjs +8 -7
  113. package/lib/knowledge/rag.mjs +2 -2
  114. package/lib/knowledge/search.mjs +15 -15
  115. package/lib/maintenance/cleanup.mjs +14 -10
  116. package/lib/mcp/server.mjs +20 -2
  117. package/lib/mcp/tools/embedded-contract.mjs +14 -0
  118. package/lib/mcp/tools/project.mjs +2 -1
  119. package/lib/model-cheapest-provider.mjs +4 -3
  120. package/lib/model-pricing.mjs +3 -2
  121. package/lib/model-router.mjs +4 -3
  122. package/lib/models/catalog.mjs +2 -1
  123. package/lib/models/execution-capability-profile.mjs +2 -1
  124. package/lib/models/provider-poll.mjs +2 -1
  125. package/lib/ollama-manager.mjs +0 -1
  126. package/lib/op-log.mjs +2 -1
  127. package/lib/opencode-runtime-plugin.mjs +3 -2
  128. package/lib/oracle/artifact-gate.mjs +3 -0
  129. package/lib/oracle/cli.mjs +2 -1
  130. package/lib/oracle/execute.mjs +3 -2
  131. package/lib/oracle/index.mjs +2 -1
  132. package/lib/oracle/read-model.mjs +3 -2
  133. package/lib/performance/generate.mjs +4 -3
  134. package/lib/platforms/capabilities.mjs +1 -1
  135. package/lib/playwright-demo.mjs +2 -0
  136. package/lib/plugin-registry.mjs +2 -1
  137. package/lib/profiles/lifecycle.mjs +3 -3
  138. package/lib/project-profile.mjs +2 -1
  139. package/lib/project-root.mjs +9 -7
  140. package/lib/provider-capabilities.js +4 -3
  141. package/lib/providers/auth-manager.mjs +2 -1
  142. package/lib/providers/copilot-auth.mjs +3 -2
  143. package/lib/providers/creds.mjs +3 -2
  144. package/lib/providers/registry.mjs +3 -3
  145. package/lib/providers/secret-resolver.mjs +3 -2
  146. package/lib/read-tracker-store.mjs +2 -1
  147. package/lib/reconcile/mcp-entry-reconcile.mjs +2 -2
  148. package/lib/reflect.mjs +2 -1
  149. package/lib/registry/generate-docs.mjs +2 -2
  150. package/lib/roles/approval-surface.mjs +5 -4
  151. package/lib/roles/event-bus.mjs +0 -1
  152. package/lib/roles/gateway.mjs +6 -2
  153. package/lib/rules-delivery.mjs +1 -1
  154. package/lib/runtime-pressure.mjs +5 -3
  155. package/lib/sandbox.mjs +3 -2
  156. package/lib/scheduler/solo.mjs +6 -4
  157. package/lib/server/auth.mjs +4 -3
  158. package/lib/server/index.mjs +53 -26
  159. package/lib/server/insights.mjs +5 -4
  160. package/lib/server/rate-limit.mjs +5 -3
  161. package/lib/server/webhook.mjs +5 -3
  162. package/lib/service-manager.mjs +5 -3
  163. package/lib/setup.mjs +9 -8
  164. package/lib/status.mjs +6 -5
  165. package/lib/storage/embeddings-local.mjs +3 -2
  166. package/lib/storage/sync.mjs +2 -2
  167. package/lib/telemetry/client.mjs +0 -1
  168. package/lib/telemetry/intent-verifications.mjs +6 -6
  169. package/lib/telemetry/model-pricing-catalog.mjs +4 -3
  170. package/lib/telemetry/rule-calls.mjs +3 -3
  171. package/lib/telemetry/skill-calls.mjs +3 -3
  172. package/lib/template-registry.mjs +1 -0
  173. package/lib/templates/visual-requirements.mjs +1 -1
  174. package/lib/test-corpus-inventory.mjs +1 -1
  175. package/lib/uninstall/uninstall.mjs +12 -10
  176. package/package.json +3 -3
  177. package/platforms/claude/settings.template.json +43 -43
  178. package/rules/common/no-fabrication.md +1 -1
  179. package/rules/common/release-gates.md +1 -1
  180. package/rules/common/research.md +1 -1
  181. package/rules/common/review-before-change.md +1 -1
  182. package/scripts/sync-specialists.mjs +11 -8
  183. package/skills/docs/adr-workflow.md +3 -3
  184. package/skills/docs/init-docs.md +9 -9
  185. package/skills/docs/prd-workflow.md +5 -5
  186. package/skills/docs/product-intelligence-review.md +1 -1
  187. package/skills/docs/product-intelligence-workflow.md +2 -2
  188. package/skills/docs/product-signal-workflow.md +1 -1
  189. package/skills/docs/runbook-workflow.md +4 -4
  190. package/skills/exploration/unknown-codebase-onboarding.md +1 -1
  191. package/skills/operating/orchestration-reference.md +1 -1
  192. package/skills/routing.md +3 -3
  193. package/specialists/artifact-manifest.json +11 -2
  194. package/specialists/artifact-manifest.schema.json +31 -1
  195. package/specialists/prompts/cx-architect.md +1 -1
  196. package/specialists/prompts/cx-docs-keeper.md +1 -1
  197. package/specialists/prompts/cx-researcher.md +1 -1
  198. package/specialists/prompts/cx-sre.md +1 -1
  199. package/specialists/role-manifests.json +6 -6
  200. package/templates/distribution/construct-reference.docx +0 -0
  201. package/templates/docs/README.md +125 -0
  202. package/templates/docs/construct_guide.md +3 -4
  203. package/templates/docs/persona-artifact.md +1 -1
  204. package/templates/docs/prds/README.md +25 -0
  205. package/templates/docs/prds/templates/_template.md +206 -0
  206. package/templates/docs/prds/templates/meta-prd.template.md +177 -0
  207. package/templates/docs/prds/templates/prd-business.template.md +61 -0
  208. package/templates/docs/prds/templates/prd-platform.template.md +81 -0
  209. package/templates/docs/prds/templates/prfaq.template.md +38 -0
  210. package/templates/docs/rfcs/README.md +22 -0
  211. package/templates/docs/rfcs/templates/_template.md +58 -0
  212. package/templates/docs/rfcs/templates/rfc-platform.template.md +72 -0
  213. package/templates/homebrew/construct.rb +1 -1
  214. package/templates/workflows/new-feature.yml +8 -8
package/lib/status.mjs CHANGED
@@ -22,6 +22,7 @@ import { readCostLog, summarizeCostData, normalizeCostEntry } from './cost.mjs';
22
22
  import { readEfficiencyLog, summarizeEfficiencyData } from './efficiency.mjs';
23
23
  import { triggerAutoBackfillIfSparse } from './telemetry/backfill.mjs';
24
24
  import { resolveTraceBackend, telemetryProviderLabel } from './telemetry/client.mjs';
25
+ import { doctorRoot } from './config/xdg.mjs';
25
26
  const TOTAL_BYTES_WARNING_THRESHOLD = 750_000;
26
27
 
27
28
  function readJSON(path) {
@@ -35,7 +36,7 @@ function readJSON(path) {
35
36
 
36
37
 
37
38
  function readSessionEfficiency(homeDir) {
38
- const stats = readJSON(join(homeDir, '.cx', 'session-efficiency.json'));
39
+ const stats = readJSON(join(doctorRoot(homeDir), 'session-efficiency.json'));
39
40
  if (!stats) return null;
40
41
 
41
42
  const readCount = Number(stats.readCount || 0);
@@ -85,7 +86,7 @@ function readSessionEfficiency(homeDir) {
85
86
  }
86
87
 
87
88
  function readTelemetryRichness(homeDir) {
88
- const stats = readJSON(join(homeDir, '.cx', 'session-telemetry.json'));
89
+ const stats = readJSON(join(doctorRoot(homeDir), 'session-telemetry.json'));
89
90
  if (!stats) return null;
90
91
 
91
92
  const total = Number(stats.total || 0);
@@ -134,7 +135,7 @@ export function readSessionUsage(homeDir) {
134
135
  totalCostUsd: 0,
135
136
  summary: 'No token usage recorded yet',
136
137
  lastInteraction: null,
137
- source: join(homeDir, '.cx', 'session-cost.jsonl'),
138
+ source: join(doctorRoot(homeDir), 'session-cost.jsonl'),
138
139
  lastUpdatedAt: null,
139
140
  };
140
141
  }
@@ -180,7 +181,7 @@ export function readSessionUsage(homeDir) {
180
181
  totalCostUsd: data.totalCostUsd,
181
182
  summary: `${data.interactions} interaction${data.interactions === 1 ? '' : 's'} · ${data.providerTotalTokens.toLocaleString()} provider · ${billed.toLocaleString()} billed (${cacheReadPct}% cache read · ${cacheWritePct}% cache write · ${freshPct}% fresh)`,
182
183
  lastInteraction,
183
- source: join(homeDir, '.cx', 'session-cost.jsonl'),
184
+ source: join(doctorRoot(homeDir), 'session-cost.jsonl'),
184
185
  lastUpdatedAt: lastInteraction.timestamp,
185
186
  };
186
187
  }
@@ -577,7 +578,7 @@ function buildSelfCheck() {
577
578
 
578
579
  // Embed daemon state
579
580
  try {
580
- const stateFile = join(homedir(), '.cx', 'runtime', 'embed-daemon.json');
581
+ const stateFile = join(doctorRoot(), 'runtime', 'embed-daemon.json');
581
582
  if (existsSync(stateFile)) {
582
583
  const state = JSON.parse(readFileSync(stateFile, 'utf8'));
583
584
  if (state.status === 'running' && state.pid) {
@@ -10,9 +10,10 @@
10
10
  * Model: Xenova/all-MiniLM-L6-v2 (384 dimensions, quantized)
11
11
  */
12
12
  import { join } from 'node:path';
13
- import { homedir } from 'node:os';
14
13
  import { mkdirSync, existsSync } from 'node:fs';
15
14
 
15
+ import { cacheDir as xdgCacheDir } from '../config/xdg.mjs';
16
+
16
17
  let modelPromise = null;
17
18
  let extractor = null;
18
19
 
@@ -23,7 +24,7 @@ async function getExtractor(cacheDir) {
23
24
  if (extractor) return extractor;
24
25
  if (modelPromise) return modelPromise;
25
26
 
26
- const cachePath = cacheDir || join(homedir(), '.construct', 'cache', 'embeddings');
27
+ const cachePath = cacheDir || join(xdgCacheDir(), 'embeddings');
27
28
  if (!existsSync(cachePath)) {
28
29
  mkdirSync(cachePath, { recursive: true });
29
30
  }
@@ -40,7 +40,7 @@ function toDocumentRows(rootDir, snapshot, project = 'construct') {
40
40
  title: 'Architecture docs',
41
41
  summary: body.slice(0, 240),
42
42
  body,
43
- source_path: 'docs/concepts/architecture.md',
43
+ source_path: 'docs/guides/concepts/architecture.md',
44
44
  tags: ['architecture', 'docs'],
45
45
  content_hash: hashContent(body),
46
46
  });
@@ -64,7 +64,7 @@ function toDocumentRows(rootDir, snapshot, project = 'construct') {
64
64
  for (const doc of snapshot.productIntelDocs ?? []) {
65
65
  const body = doc.body;
66
66
  const rel = doc.path;
67
- const kind = rel.startsWith('docs/prd/')
67
+ const kind = rel.startsWith('docs/specs/prd/')
68
68
  ? 'prd'
69
69
  : rel.startsWith('docs/meta-prd/')
70
70
  ? 'meta-prd'
@@ -5,7 +5,6 @@
5
5
  * CONSTRUCT_TRACE_BACKEND and never throws into callers.
6
6
  */
7
7
  import { existsSync, mkdirSync, appendFileSync } from 'node:fs';
8
- import { homedir } from 'node:os';
9
8
  import { join } from 'node:path';
10
9
  import { randomUUID } from 'node:crypto';
11
10
 
@@ -13,18 +13,18 @@
13
13
  */
14
14
 
15
15
  import fs from 'node:fs';
16
- import os from 'node:os';
17
16
  import path from 'node:path';
18
17
  import { appendBounded } from '../logging/rotate.mjs';
19
18
  import { resolveProjectScopedPath } from '../project-root.mjs';
19
+ import { doctorRoot } from '../config/xdg.mjs';
20
20
 
21
21
  // intent-verifications are PROJECT-SCOPED — a verification belongs to a
22
- // specific routing decision in a specific project. DEFAULT_LOG_PATH stays
23
- // at the legacy user-scope location for back-compat with readers that
24
- // imported the constant; the writer below resolves per-call so each
25
- // invocation lands in the project's .cx/ when one is detected.
22
+ // specific routing decision in a specific project. DEFAULT_LOG_PATH resolves
23
+ // to the global doctor root for back-compat with readers that imported the
24
+ // constant; the writer below resolves per-call so each invocation lands in
25
+ // the project's .cx/ when one is detected.
26
26
 
27
- export const DEFAULT_LOG_PATH = path.join(os.homedir(), '.cx', 'intent-verifications.jsonl');
27
+ export const DEFAULT_LOG_PATH = path.join(doctorRoot(), 'intent-verifications.jsonl');
28
28
 
29
29
  /**
30
30
  * Fire-and-forget append of one verification event.
@@ -15,13 +15,14 @@
15
15
  */
16
16
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
17
17
  import { join } from "path";
18
- import { homedir } from "os";
18
+
19
+ import { doctorRoot } from "../config/xdg.mjs";
19
20
 
20
21
  const OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
21
22
  const LITELLM_PRICING_URL =
22
23
  "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
23
24
  const PRICING_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
24
- const PRICING_CACHE_PATH = join(homedir(), ".cx", "pricing-cache.json");
25
+ const PRICING_CACHE_PATH = join(doctorRoot(), "pricing-cache.json");
25
26
 
26
27
  export function describePricingCatalogFreshness() {
27
28
  try {
@@ -133,7 +134,7 @@ async function fetchLiteLLMPricing(fetchImpl = globalThis.fetch) {
133
134
  });
134
135
  }
135
136
  try {
136
- mkdirSync(join(homedir(), ".cx"), { recursive: true });
137
+ mkdirSync(doctorRoot(), { recursive: true });
137
138
  writeFileSync(PRICING_CACHE_PATH, JSON.stringify({ fetchedAt: Date.now(), models }));
138
139
  } catch { /* non-critical */ }
139
140
  return models;
@@ -1,16 +1,16 @@
1
1
  /**
2
2
  * lib/telemetry/rule-calls.mjs — record when a rule path is referenced at runtime.
3
3
  *
4
- * Mirrors hook-calls telemetry: append-only JSONL at ~/.cx/rule-calls.jsonl.
4
+ * Mirrors hook-calls telemetry: append-only JSONL at <doctorRoot>/rule-calls.jsonl.
5
5
  * Consumed by `construct rules usage` for consolidation decisions.
6
6
  */
7
7
 
8
8
  import fs from 'node:fs';
9
- import os from 'node:os';
10
9
  import path from 'node:path';
11
10
  import { appendBounded } from '../logging/rotate.mjs';
11
+ import { doctorRoot } from '../config/xdg.mjs';
12
12
 
13
- export const DEFAULT_LOG_PATH = path.join(os.homedir(), '.cx', 'rule-calls.jsonl');
13
+ export const DEFAULT_LOG_PATH = path.join(doctorRoot(), 'rule-calls.jsonl');
14
14
 
15
15
  /**
16
16
  * @param {object} event
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * lib/telemetry/skill-calls.mjs — measure which skill files actually get loaded.
3
3
  *
4
- * Appends one JSON line per skill load to ~/.cx/skill-calls.jsonl so
4
+ * Appends one JSON line per skill load to <doctorRoot>/skill-calls.jsonl so
5
5
  * `construct skills usage/orphans/hot/correlate-quality` can answer:
6
6
  * which skills have no load events in a 30-day window (pruning candidates),
7
7
  * which skills are loaded from many agents (hot paths), and which skills
@@ -15,17 +15,17 @@
15
15
  */
16
16
 
17
17
  import fs from 'node:fs';
18
- import os from 'node:os';
19
18
  import path from 'node:path';
20
19
  import { appendBounded } from '../logging/rotate.mjs';
21
20
  import { resolveProjectScope } from '../project-root.mjs';
21
+ import { doctorRoot } from '../config/xdg.mjs';
22
22
 
23
23
  // Skill calls are CROSS-PROJECT telemetry — a single log lets you see which
24
24
  // skills load hot across every project the user has run Construct in. Stays
25
25
  // at user scope; each entry is tagged with `projectId` so a reader can
26
26
  // attribute usage to a specific project without losing the global view.
27
27
 
28
- export const DEFAULT_LOG_PATH = path.join(os.homedir(), '.cx', 'skill-calls.jsonl');
28
+ export const DEFAULT_LOG_PATH = path.join(doctorRoot(), 'skill-calls.jsonl');
29
29
 
30
30
  /**
31
31
  * Fire-and-forget log of a skill load event.
@@ -70,4 +70,5 @@ export const TEMPLATE_OWNERSHIP_EXEMPTIONS = new Set([
70
70
  'rfc-platform',
71
71
  'prd-platform',
72
72
  'prd-business',
73
+ 'README',
73
74
  ]);
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Loads structure and visual requirements from specialists/artifact-manifest.json
5
5
  * (single source of truth) and merges legacy types not yet in the manifest.
6
- * Paired with docs/concepts/doc-visual-matrix.md; pinned by template tests.
6
+ * Paired with docs/guides/concepts/doc-visual-matrix.md; pinned by template tests.
7
7
  */
8
8
 
9
9
  import { validateArtifactPostconditions } from '../contracts/validate.mjs';
@@ -173,7 +173,7 @@ export function computeReleaseCriticalGaps(ledger) {
173
173
  for (const row of SHIPPED_MATRIX_CAPABILITIES) {
174
174
  if (ledgerCoversHint(allLedgerIds, row.ledgerHints)) continue;
175
175
  gaps.push({
176
- source: 'docs/audit/capability-matrix.md',
176
+ source: 'docs/operations/audit/capability-matrix.md',
177
177
  capability: row.id,
178
178
  status: 'shipped',
179
179
  reason: 'no ledger entry matches shipped capability hints',
@@ -24,7 +24,7 @@
24
24
  * --dry-run print the plan, change nothing
25
25
  * --yes non-interactive; remove auto-risk items, skip ask-risk items
26
26
  * --yes --all non-interactive; remove both auto- and ask-risk items
27
- * --keep-state only remove project-scope launcher + adapters; preserve .cx/, ~/.construct, Postgres
27
+ * --keep-state only remove project-scope launcher + adapters; preserve .cx/, the XDG user dirs, Postgres
28
28
  * --with-images also remove the shared pgvector Docker image (off by default; ADR-0027)
29
29
  * --scope=project|machine|all limit which categories are evaluated (default: all)
30
30
  */
@@ -37,6 +37,7 @@ import { fileURLToPath } from 'node:url';
37
37
 
38
38
  import { multiSelect } from '../tty-prompts.mjs';
39
39
  import { getUserConfigDir, getUserEnvPath } from '../env-config.mjs';
40
+ import { stateDir, cacheDir, doctorRoot } from '../config/xdg.mjs';
40
41
  import { removeTomlTables, tomlString } from '../codex-config.mjs';
41
42
  import { postgresContainerName, LEGACY_PG_CONTAINER } from '../home-namespace.mjs';
42
43
 
@@ -84,12 +85,13 @@ function buildCategories({ cwd, home, withImages = false }) {
84
85
  const projectPlanMd = path.join(cwd, 'plan.md');
85
86
  const userHome = home;
86
87
  const userConstructDir = getUserConfigDir(userHome);
88
+ const userStateDir = stateDir(userHome);
87
89
  const userConfigEnv = getUserEnvPath(userHome);
88
- const userEmbedCache = path.join(userConstructDir, 'cache', 'embeddings');
89
- const userVectorIndex = path.join(userConstructDir, 'vector');
90
- const userWorkspace = path.join(userConstructDir, 'workspace');
90
+ const userEmbedCache = path.join(cacheDir(userHome), 'embeddings');
91
+ const userVectorIndex = path.join(userStateDir, 'vector');
92
+ const userWorkspace = path.join(userStateDir, 'workspace');
91
93
  const userPgComposeDir = path.join(userConstructDir, 'services', 'postgres');
92
- const userCxDir = path.join(userHome, '.cx');
94
+ const userCxDir = doctorRoot(userHome);
93
95
  const launchAgentPlist = path.join(userHome, 'Library', 'LaunchAgents', `${PRESSURE_GUARD_LABEL}.plist`);
94
96
  const claudeSettings = path.join(userHome, '.claude', 'settings.json');
95
97
  const opencodeConfig = path.join(userHome, '.config', 'opencode', 'opencode.json');
@@ -172,7 +174,7 @@ function buildCategories({ cwd, home, withImages = false }) {
172
174
  id: 'machine-workspace',
173
175
  scope: 'machine',
174
176
  risk: 'auto',
175
- label: '~/.construct/workspace and ~/.construct/vector',
177
+ label: `${rel(userHome, userWorkspace)} and ${rel(userHome, userVectorIndex)}`,
176
178
  detect: () => existsAny(userWorkspace) || existsAny(userVectorIndex),
177
179
  describe: () => 'Removes the per-user working dir and fallback JSON vector index. Both are regenerated on next use.',
178
180
  execute: () => {
@@ -186,7 +188,7 @@ function buildCategories({ cwd, home, withImages = false }) {
186
188
  id: 'machine-cx',
187
189
  scope: 'machine',
188
190
  risk: 'auto',
189
- label: '~/.cx (daemon state, logs)',
191
+ label: `${rel(userHome, userCxDir)} (daemon state, logs)`,
190
192
  detect: () => existsAny(userCxDir),
191
193
  describe: () => `Removes ${rel(userHome, userCxDir)}. Daemon PIDs, telemetry, setup logs; all regenerated on next setup.`,
192
194
  execute: () => removePath(userCxDir),
@@ -195,7 +197,7 @@ function buildCategories({ cwd, home, withImages = false }) {
195
197
  id: 'machine-embedding-cache',
196
198
  scope: 'machine',
197
199
  risk: 'ask',
198
- label: '~/.construct/cache/embeddings (~22 MB ONNX model)',
200
+ label: `${rel(userHome, userEmbedCache)} (~22 MB ONNX model)`,
199
201
  detect: () => existsAny(userEmbedCache),
200
202
  describe: () => 'Removes the cached ONNX embedding model. Skip if you plan to reinstall soon — re-downloading takes a minute.',
201
203
  execute: () => removePath(userEmbedCache),
@@ -204,7 +206,7 @@ function buildCategories({ cwd, home, withImages = false }) {
204
206
  id: 'machine-config-env',
205
207
  scope: 'machine',
206
208
  risk: 'ask',
207
- label: '~/.construct/config.env (API keys + consent flags)',
209
+ label: `${rel(userHome, userConfigEnv)} (API keys + consent flags)`,
208
210
  detect: () => existsAny(userConfigEnv),
209
211
  describe: () => 'Removes saved API keys (Anthropic/OpenAI/telemetry/etc.) and bootstrap consent. Skip if you intend to reinstall and reuse the keys.',
210
212
  execute: () => removePath(userConfigEnv),
@@ -231,7 +233,7 @@ function buildCategories({ cwd, home, withImages = false }) {
231
233
  id: 'machine-postgres-compose',
232
234
  scope: 'machine',
233
235
  risk: 'auto',
234
- label: '~/.construct/services/postgres/ (compose file)',
236
+ label: `${rel(userHome, userPgComposeDir)} (compose file)`,
235
237
  detect: () => existsAny(userPgComposeDir),
236
238
  describe: () => 'Removes the local docker-compose.yml. Run after removing the container.',
237
239
  execute: () => removePath(userPgComposeDir),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geraldmaron/construct",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "type": "module",
5
5
  "packageManager": "npm@11.5.1",
6
6
  "description": "Construct — agent orchestration layer for OpenCode, Claude Code, and other coding surfaces",
@@ -52,7 +52,7 @@
52
52
  "scripts": {
53
53
  "mcp": "node lib/mcp/server.mjs",
54
54
  "test": "node scripts/run-tests.mjs",
55
- "test:functional": "node --test --test-timeout=120000 --test-concurrency=1 tests/functional/*.functional.test.mjs",
55
+ "test:functional": "XDG_CONFIG_HOME= XDG_STATE_HOME= XDG_CACHE_HOME= node --test --test-timeout=120000 --test-concurrency=1 tests/functional/*.functional.test.mjs",
56
56
  "test:unit": "node scripts/run-tests.mjs --exclude=tests/functional",
57
57
  "lint:js": "eslint bin/construct \"lib/**/*.mjs\" \"scripts/**/*.mjs\" \"tests/**/*.mjs\"",
58
58
  "coverage": "c8 --reporter=text-summary --reporter=lcov --src=lib --src=bin node scripts/run-tests.mjs --exclude=tests/functional",
@@ -80,7 +80,7 @@
80
80
  "release:check": "node ./bin/construct doctor && npm test && node ./bin/construct docs:update --check && node ./bin/construct docs:site --check && node ./bin/construct registry:generate-docs --check && node ./bin/construct docs:verify --strict && node scripts/alignment/census.mjs --ratchet && node ./bin/construct dashboard:sync --check && node ./bin/construct lint:comments && node ./bin/construct certify gate && node scripts/lint-commits-pr.mjs",
81
81
  "release:gate": "node --test tests/functional/release-gate.functional.test.mjs",
82
82
  "audit:published": "node scripts/audit-published-artifact.mjs",
83
- "version": "node scripts/sync-construct-version.mjs && git add .construct/version",
83
+ "version": "node scripts/sync-construct-version.mjs && git add -f .construct/version",
84
84
  "release:preflight": "node scripts/pre-release-check.mjs",
85
85
  "release:preflight:no-auth": "node scripts/pre-release-check.mjs --skip-auth",
86
86
  "npm:publish": "npm run release:check && npm publish --access public",