@dotdotgod/hermes 0.6.5 → 0.6.6

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.
package/mcp/cli.mjs CHANGED
@@ -538,7 +538,7 @@ var init_languages = __esm({
538
538
  ".zsh",
539
539
  ".fish"
540
540
  ]);
541
- EXTRACTOR_ID = "outline-v3:vscode-tree-sitter-wasm-0.3.1";
541
+ EXTRACTOR_ID = "outline-v4:vscode-0.3.1:typescript-import-types-1";
542
542
  DECLARATIONS = {
543
543
  function_declaration: "function",
544
544
  generator_function_declaration: "function",
@@ -576,19 +576,24 @@ var init_languages = __esm({
576
576
 
577
577
  // packages/cli/src/symbols/runtime.mjs
578
578
  import { createRequire } from "node:module";
579
- import { existsSync as existsSync9, readFileSync as readFileSync9 } from "node:fs";
580
- import { dirname as dirname5, join as join8 } from "node:path";
579
+ import { existsSync as existsSync10, readFileSync as readFileSync10 } from "node:fs";
580
+ import { dirname as dirname6, join as join9 } from "node:path";
581
581
  import { fileURLToPath } from "node:url";
582
582
  import { createHash as createHash4 } from "node:crypto";
583
+ function languageAsset(language) {
584
+ const patched = fileURLToPath(new URL(`./parsers/tree-sitter-${language}.wasm`, import.meta.url));
585
+ return existsSync10(patched) ? patched : join9(dirname6(parserEntry()), `tree-sitter-${language}.wasm`);
586
+ }
583
587
  function parserEntry() {
584
588
  const bundled = fileURLToPath(new URL("./parsers/tree-sitter.cjs", import.meta.url));
585
- return existsSync9(bundled) ? bundled : require2.resolve("@vscode/tree-sitter-wasm");
589
+ return existsSync10(bundled) ? bundled : require2.resolve("@vscode/tree-sitter-wasm");
586
590
  }
587
591
  function parserIdentity() {
588
592
  if (!identity) {
589
593
  const entry = parserEntry();
590
- const hash2 = createHash4("sha256").update(readFileSync9(entry));
591
- for (const name of ["tree-sitter.wasm", ...Object.keys(LANGUAGE_EXTENSIONS).map((language) => `tree-sitter-${language}.wasm`)]) hash2.update(name).update(readFileSync9(join8(dirname5(entry), name)));
594
+ const hash2 = createHash4("sha256").update(readFileSync10(entry));
595
+ hash2.update(readFileSync10(join9(dirname6(entry), "tree-sitter.wasm")));
596
+ for (const language of Object.keys(LANGUAGE_EXTENSIONS)) hash2.update(language).update(readFileSync10(languageAsset(language)));
592
597
  identity = hash2.digest("hex");
593
598
  }
594
599
  return identity;
@@ -609,17 +614,17 @@ var parser_exports = {};
609
614
  __export(parser_exports, {
610
615
  extractSymbols: () => extractSymbols
611
616
  });
612
- import { dirname as dirname6, join as join10 } from "node:path";
617
+ import { dirname as dirname7 } from "node:path";
613
618
  import { createHash as createHash5 } from "node:crypto";
614
619
  async function parserFor(language) {
615
620
  if (!runtime) runtime = (async () => {
616
621
  const entry = parserEntry();
617
622
  const api2 = loadParserApi();
618
623
  await api2.Parser.init();
619
- return { api: api2, directory: dirname6(entry) };
624
+ return { api: api2, directory: dirname7(entry) };
620
625
  })();
621
626
  const { api, directory } = await runtime;
622
- if (!languages.has(language)) languages.set(language, api.Language.load(join10(directory, `tree-sitter-${language}.wasm`)));
627
+ if (!languages.has(language)) languages.set(language, api.Language.load(languageAsset(language)));
623
628
  const loaded = await languages.get(language);
624
629
  const parser = new api.Parser();
625
630
  parser.setLanguage(loaded);
@@ -756,8 +761,8 @@ var init_parser = __esm({
756
761
  });
757
762
 
758
763
  // packages/cli/src/init.mjs
759
- import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync4, statSync, writeFileSync } from "node:fs";
760
- import { basename, dirname, join as join4, resolve as resolve2 } from "node:path";
764
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync5, statSync, writeFileSync as writeFileSync2 } from "node:fs";
765
+ import { basename, dirname as dirname2, join as join5, resolve as resolve3 } from "node:path";
761
766
 
762
767
  // packages/cli/src/config/templates.mjs
763
768
  import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
@@ -896,6 +901,7 @@ function validateEmbeddingProfile(profile, label = "embedding") {
896
901
  return { ...profile, model: profile.model.trim(), ...profile.baseUrl ? { baseUrl: profile.baseUrl.replace(/\/$/, "") } : {} };
897
902
  }
898
903
  function sanitizeEmbeddingProfile(profile) {
904
+ if (!profile) return null;
899
905
  const { apiKey, ...safe } = profile;
900
906
  return { ...safe, ...apiKey ? { apiKey: "[redacted]" } : {} };
901
907
  }
@@ -916,7 +922,7 @@ function resolveEmbeddingProfile(root = ".", options = {}) {
916
922
  if (projectConfig && Object.hasOwn(projectConfig, "embedding")) return { profile: validateEmbeddingProfile(projectConfig.embedding, "project embedding"), source: "project", path: projectPath };
917
923
  const globalConfig = readJson(globalPath, "global config");
918
924
  if (globalConfig && Object.hasOwn(globalConfig, "embedding")) return { profile: validateEmbeddingProfile(globalConfig.embedding, "global embedding"), source: "global", path: globalPath };
919
- return { profile: { ...DEFAULT_EMBEDDING_PROFILE }, source: "default", path: null };
925
+ return { profile: null, source: "not-configured", path: null };
920
926
  }
921
927
 
922
928
  // packages/cli/src/memory/config.mjs
@@ -1702,6 +1708,60 @@ function resolveInitializationTemplate(explicitName = null, home = dotdotgodHome
1702
1708
  ` };
1703
1709
  }
1704
1710
 
1711
+ // packages/cli/src/claude-plans.mjs
1712
+ import { existsSync as existsSync4, lstatSync, readFileSync as readFileSync4, realpathSync, writeFileSync, mkdirSync } from "node:fs";
1713
+ import { dirname, join as join4, relative as relative2, resolve as resolve2, isAbsolute } from "node:path";
1714
+ function prepareClaudePlans(root, documentationRoot) {
1715
+ const path = join4(root, ".claude", "settings.json");
1716
+ for (const target of [join4(root, ".claude"), path]) {
1717
+ try {
1718
+ if (lstatSync(target).isSymbolicLink()) throw new Error(`Refusing symlink: ${target}`);
1719
+ } catch (error) {
1720
+ if (error.code !== "ENOENT") throw error;
1721
+ }
1722
+ }
1723
+ const base = resolve2(root);
1724
+ if (existsSync4(dirname(path))) {
1725
+ const rel2 = relative2(realpathSync(base), realpathSync(dirname(path)));
1726
+ if (rel2.startsWith("..") || isAbsolute(rel2)) throw new Error("Claude settings must stay inside the project.");
1727
+ }
1728
+ const current = existsSync4(path) ? readFileSync4(path, "utf8") : null;
1729
+ const settings = current === null ? {} : JSON.parse(current);
1730
+ if (!settings || typeof settings !== "object" || Array.isArray(settings)) throw new Error("Claude settings must be a JSON object.");
1731
+ const value = `./${documentationRoot}/plan`;
1732
+ if (Object.hasOwn(settings, "plansDirectory")) return {
1733
+ status: "skipped",
1734
+ path,
1735
+ reason: "Existing plansDirectory preserved.",
1736
+ value: settings.plansDirectory
1737
+ };
1738
+ return {
1739
+ status: current === null ? "created" : "updated",
1740
+ path,
1741
+ value,
1742
+ current,
1743
+ content: `${JSON.stringify({ ...settings, plansDirectory: value }, null, 2)}
1744
+ `
1745
+ };
1746
+ }
1747
+ function applyClaudePlans(prepared, dryRun) {
1748
+ const { content, current, ...result } = prepared;
1749
+ if (!content) return result;
1750
+ if (dryRun) return { ...result, status: `would_${result.status === "created" ? "create" : "update"}` };
1751
+ for (const target of [dirname(prepared.path), prepared.path]) {
1752
+ try {
1753
+ if (lstatSync(target).isSymbolicLink()) throw new Error(`Refusing symlink: ${target}`);
1754
+ } catch (error) {
1755
+ if (error.code !== "ENOENT") throw error;
1756
+ }
1757
+ }
1758
+ const latest = existsSync4(prepared.path) ? readFileSync4(prepared.path, "utf8") : null;
1759
+ if (latest !== current) throw new Error("Claude settings changed during initialization; retry without overwriting them.");
1760
+ mkdirSync(dirname(prepared.path), { recursive: true });
1761
+ writeFileSync(prepared.path, content, { flag: current === null ? "wx" : "w" });
1762
+ return result;
1763
+ }
1764
+
1705
1765
  // packages/cli/src/init.mjs
1706
1766
  function action(status, path, extra = {}) {
1707
1767
  return { status, path, ...extra };
@@ -1717,6 +1777,7 @@ function parseInitOptions(argv) {
1717
1777
  if (arg === "--json") options.json = true;
1718
1778
  else if (arg === "--dotdot-setting") options.dotdotSetting = true;
1719
1779
  else if (arg === "--dry-run") options.dryRun = true;
1780
+ else if (arg === "--claude-plans") options.claudePlans = true;
1720
1781
  else if (arg === "--project-name" || arg === "--template" || arg === "--documentation-root") {
1721
1782
  const value = argv[i + 1];
1722
1783
  if (!value || value.startsWith("-")) throw new Error(`${arg} requires a value`);
@@ -1737,7 +1798,7 @@ function parseInitOptions(argv) {
1737
1798
  }
1738
1799
  if (!options.root) throw new Error("Missing required argument: <root>");
1739
1800
  if (!isValidDocumentationRoot(options.documentationRoot)) throw new Error("Invalid --documentation-root: expected a safe repository-relative directory");
1740
- options.root = resolve2(options.root);
1801
+ options.root = resolve3(options.root);
1741
1802
  if (!options.projectName) options.projectName = basename(options.root);
1742
1803
  return options;
1743
1804
  }
@@ -1750,8 +1811,8 @@ function initError(options, code, message) {
1750
1811
  process.exit(2);
1751
1812
  }
1752
1813
  function writeInitFile(options, relativePath, content, actions) {
1753
- const target = join4(options.root, relativePath);
1754
- if (existsSync4(target)) {
1814
+ const target = join5(options.root, relativePath);
1815
+ if (existsSync5(target)) {
1755
1816
  actions.push(action("skipped", target));
1756
1817
  return;
1757
1818
  }
@@ -1759,14 +1820,14 @@ function writeInitFile(options, relativePath, content, actions) {
1759
1820
  actions.push(action("would_create", target));
1760
1821
  return;
1761
1822
  }
1762
- mkdirSync(dirname(target), { recursive: true });
1763
- writeFileSync(target, content.endsWith("\n") ? content : `${content}
1823
+ mkdirSync2(dirname2(target), { recursive: true });
1824
+ writeFileSync2(target, content.endsWith("\n") ? content : `${content}
1764
1825
  `);
1765
1826
  actions.push(action("created", target));
1766
1827
  }
1767
1828
  function ensureInitDirectory(options, relativePath, actions) {
1768
- const target = join4(options.root, relativePath);
1769
- if (existsSync4(target)) {
1829
+ const target = join5(options.root, relativePath);
1830
+ if (existsSync5(target)) {
1770
1831
  actions.push(action("skipped", target));
1771
1832
  return;
1772
1833
  }
@@ -1774,22 +1835,22 @@ function ensureInitDirectory(options, relativePath, actions) {
1774
1835
  actions.push(action("would_create", target));
1775
1836
  return;
1776
1837
  }
1777
- mkdirSync(target, { recursive: true });
1838
+ mkdirSync2(target, { recursive: true });
1778
1839
  actions.push(action("created", target));
1779
1840
  }
1780
1841
  function ensureGitignoreEntry(options, entry, actions) {
1781
- const target = join4(options.root, ".gitignore");
1782
- const existed = existsSync4(target);
1783
- let current = existed ? readFileSync4(target, "utf8") : "";
1842
+ const target = join5(options.root, ".gitignore");
1843
+ const existed = existsSync5(target);
1844
+ let current = existed ? readFileSync5(target, "utf8") : "";
1784
1845
  if (current.split(/\r?\n/).includes(entry)) return;
1785
1846
  if (options.dryRun) {
1786
1847
  actions.push(action(existed ? "would_update" : "would_create", target, { add: entry }));
1787
1848
  return;
1788
1849
  }
1789
- mkdirSync(options.root, { recursive: true });
1850
+ mkdirSync2(options.root, { recursive: true });
1790
1851
  if (current.length > 0 && !current.endsWith("\n")) current = `${current}
1791
1852
  `;
1792
- writeFileSync(target, `${current}${entry}
1853
+ writeFileSync2(target, `${current}${entry}
1793
1854
  `);
1794
1855
  actions.push(action(existed ? "updated" : "created", target, { add: entry }));
1795
1856
  }
@@ -1993,15 +2054,26 @@ function runInit(argv, usage2) {
1993
2054
  usage2(error instanceof Error ? error.message : String(error), "init");
1994
2055
  return;
1995
2056
  }
1996
- if (!options.dryRun && existsSync4(options.root)) {
2057
+ if (!options.dryRun && existsSync5(options.root)) {
1997
2058
  try {
1998
2059
  if (!statSync(options.root).isDirectory()) initError(options, "ROOT_NOT_DIRECTORY", `Project root is not a directory: ${options.root}`);
1999
2060
  } catch {
2000
2061
  initError(options, "ROOT_NOT_FOUND", `Project root not found: ${options.root}`);
2001
2062
  }
2002
2063
  }
2003
- if (!options.dryRun) mkdirSync(options.root, { recursive: true });
2004
- const configExists = existsSync4(join4(options.root, "dotdotgod.config.json"));
2064
+ if (options.claudePlans && !options.documentationRootExplicit && existsSync5(join5(options.root, "dotdotgod.config.json"))) {
2065
+ options.documentationRoot = readMemoryConfig(options.root).documentation.root;
2066
+ }
2067
+ let claudePlans;
2068
+ if (options.claudePlans) {
2069
+ try {
2070
+ claudePlans = prepareClaudePlans(options.root, options.documentationRoot);
2071
+ } catch (error) {
2072
+ initError(options, "CLAUDE_SETTINGS_INVALID", error.message);
2073
+ }
2074
+ }
2075
+ if (!options.dryRun) mkdirSync2(options.root, { recursive: true });
2076
+ const configExists = existsSync5(join5(options.root, "dotdotgod.config.json"));
2005
2077
  if (configExists && options.documentationRootExplicit) {
2006
2078
  const existingRoot = readMemoryConfig(options.root).documentation.root;
2007
2079
  if (existingRoot !== options.documentationRoot) initError(options, "DOCUMENTATION_ROOT_CONFLICT", `Existing config uses documentation root ${existingRoot}; initialization does not migrate documentation.`);
@@ -2024,6 +2096,13 @@ function runInit(argv, usage2) {
2024
2096
  }
2025
2097
  writeInitFile(options, "dotdotgod.config.json", template?.text ?? "", actions);
2026
2098
  for (const entry of [`${options.documentationRoot}/plan`, `${options.documentationRoot}/archive`, ".dotdotgod"]) ensureGitignoreEntry(options, entry, actions);
2099
+ if (claudePlans) {
2100
+ try {
2101
+ actions.push(applyClaudePlans(claudePlans, options.dryRun));
2102
+ } catch (error) {
2103
+ initError(options, "CLAUDE_SETTINGS_CHANGED", error.message);
2104
+ }
2105
+ }
2027
2106
  const payload = { ok: true, command: "init", root: options.root, projectName: options.projectName, documentationRoot: options.documentationRoot, dryRun: options.dryRun, dotdotSetting: options.dotdotSetting, template: template ? { name: template.name, source: template.source, selectedBy: template.selectedBy } : null, actions };
2028
2107
  if (options.json) console.log(JSON.stringify(payload, null, 2));
2029
2108
  else {
@@ -2033,8 +2112,8 @@ function runInit(argv, usage2) {
2033
2112
  }
2034
2113
 
2035
2114
  // packages/cli/src/cli/usage.mjs
2036
- import { readFileSync as readFileSync5 } from "node:fs";
2037
- import { resolve as resolve3 } from "node:path";
2115
+ import { readFileSync as readFileSync6 } from "node:fs";
2116
+ import { resolve as resolve4 } from "node:path";
2038
2117
  var HELP_TOKENS = /* @__PURE__ */ new Set(["help", "--help", "-h"]);
2039
2118
  var VERSION_TOKENS = /* @__PURE__ */ new Set(["version", "--version", "-v"]);
2040
2119
  function commandUsage(command = "root") {
@@ -2044,9 +2123,10 @@ function commandUsage(command = "root") {
2044
2123
  dotdotgod validate <root> [--include-local-memory] [--check-index] [--max-lines n] [--max-chars n] [--no-link-check] [--json]`;
2045
2124
  case "init":
2046
2125
  return `Usage:
2047
- dotdotgod init <root> [--project-name NAME] [--template NAME] [--documentation-root PATH] [--dotdot-setting] [--dry-run] [--json]
2126
+ dotdotgod init <root> [--project-name NAME] [--template NAME] [--documentation-root PATH] [--dotdot-setting] [--claude-plans] [--dry-run] [--json]
2048
2127
 
2049
- Create AGENTS.md, agent entrypoints, docs indexes, a project config from an initialization template, and local memory gitignore entries.`;
2128
+ Create AGENTS.md, agent entrypoints, docs indexes, a project config from an initialization template, and local memory gitignore entries.
2129
+ Use --claude-plans only after opting into project-local native plan storage; existing Claude settings are preserved.`;
2050
2130
  case "index":
2051
2131
  return `Usage:
2052
2132
  dotdotgod index <root> [--json]`;
@@ -2066,9 +2146,9 @@ Create dotdotgod.config.json from a bundled or user template. Without --template
2066
2146
  dotdotgod status <root> [--json]`;
2067
2147
  case "query":
2068
2148
  return `Usage:
2069
- dotdotgod query <root> <query> [--limit n] [--scope docs|code|all] [--search hybrid|keyword|vector] [--allow-code-embedding] [--json]
2149
+ dotdotgod query <root> <query> [--limit n] [--scope docs|code|all] [--search hybrid|keyword|vector] [--json]
2070
2150
 
2071
- Search full documentation and code outlines/comments. Default: hybrid docs+code. Remote code metadata requires explicit consent.`;
2151
+ Search full documentation and code outlines/comments. Default: hybrid docs+code. Selecting a remote embedding profile authorizes code metadata transmission.`;
2072
2152
  case "embedding":
2073
2153
  return `Usage:
2074
2154
  dotdotgod embedding status [<root>] [--json]
@@ -2173,7 +2253,7 @@ function isVersionToken(value) {
2173
2253
  function printVersion() {
2174
2254
  let version = "unknown";
2175
2255
  try {
2176
- const data = JSON.parse(readFileSync5(new URL("../../package.json", import.meta.url), "utf8"));
2256
+ const data = JSON.parse(readFileSync6(new URL("../../package.json", import.meta.url), "utf8"));
2177
2257
  if (typeof data.version === "string") version = data.version;
2178
2258
  } catch {
2179
2259
  }
@@ -2195,17 +2275,17 @@ function parseCommon(argv) {
2195
2275
  if (arg === "--json") options.json = true;
2196
2276
  else if (!arg.startsWith("-") && options.root === ".") options.root = arg;
2197
2277
  }
2198
- options.root = resolve3(options.root);
2278
+ options.root = resolve4(options.root);
2199
2279
  return options;
2200
2280
  }
2201
2281
 
2202
2282
  // packages/cli/src/validate/run.mjs
2203
- import { existsSync as existsSync13, readFileSync as readFileSync13, readdirSync as readdirSync3 } from "node:fs";
2204
- import { basename as basename7, dirname as dirname10, extname as extname6, join as join13, resolve as resolve8 } from "node:path";
2283
+ import { existsSync as existsSync14, readFileSync as readFileSync14, readdirSync as readdirSync3 } from "node:fs";
2284
+ import { basename as basename7, dirname as dirname11, extname as extname6, join as join13, resolve as resolve9 } from "node:path";
2205
2285
 
2206
2286
  // packages/cli/src/docs/traceability.mjs
2207
- import { existsSync as existsSync5 } from "node:fs";
2208
- import { basename as basename2, dirname as dirname2, extname, relative as relative2, resolve as resolve4 } from "node:path";
2287
+ import { existsSync as existsSync6 } from "node:fs";
2288
+ import { basename as basename2, dirname as dirname3, extname, relative as relative3, resolve as resolve5 } from "node:path";
2209
2289
  function isSecretIndexPath(path) {
2210
2290
  return /(^|\/)(\.env|\.npmrc|\.pypirc|id_rsa|id_dsa|id_ed25519|credentials|secrets?)(\.|\/|$)/i.test(path);
2211
2291
  }
@@ -2304,9 +2384,9 @@ function stripTraceabilityLinksRegion(content) {
2304
2384
  return stripped.replace(/(?:^[ \t]*\r?\n)*^##[ \t]+Traceability[ \t]*\r?(?:\n(?:[ \t]*\r?\n)*|$)/gm, "");
2305
2385
  }
2306
2386
  function markdownLinkPath(root, file, target) {
2307
- const fromDir = dirname2(file);
2308
- const absoluteTarget = resolve4(root, target);
2309
- let link = relative2(fromDir, absoluteTarget).split("\\").join("/");
2387
+ const fromDir = dirname3(file);
2388
+ const absoluteTarget = resolve5(root, target);
2389
+ let link = relative3(fromDir, absoluteTarget).split("\\").join("/");
2310
2390
  if (!link.startsWith(".") && !link.startsWith("/")) link = link || basename2(target);
2311
2391
  return link;
2312
2392
  }
@@ -2405,7 +2485,7 @@ function validateTraceabilityPathArray(data, field2, add, root, config, { requir
2405
2485
  add("TRACEABILITY_LOCAL_MEMORY_TARGET", displayField, `target points to local memory area "${area2.id}": ${value}. Traceability must point to shared durable files, not active plans or archive memory.`);
2406
2486
  continue;
2407
2487
  }
2408
- if (!existsSync5(resolve4(root, value))) add("TRACEABILITY_MISSING_TARGET", displayField, `target does not exist: ${value}.`);
2488
+ if (!existsSync6(resolve5(root, value))) add("TRACEABILITY_MISSING_TARGET", displayField, `target does not exist: ${value}.`);
2409
2489
  }
2410
2490
  }
2411
2491
  function validateTraceabilityCommandArray(data, field2, add, { required = true, displayField = field2 } = {}) {
@@ -2473,9 +2553,9 @@ function validateTraceabilityBlock(data, root, file, line = null, config = defau
2473
2553
 
2474
2554
  // packages/cli/src/index/files.mjs
2475
2555
  import { createHash } from "node:crypto";
2476
- import { existsSync as existsSync6, readFileSync as readFileSync6, readdirSync, statSync as statSync2 } from "node:fs";
2556
+ import { existsSync as existsSync7, readFileSync as readFileSync7, readdirSync, statSync as statSync2 } from "node:fs";
2477
2557
  import { spawnSync } from "node:child_process";
2478
- import { basename as basename3, extname as extname2, join as join5 } from "node:path";
2558
+ import { basename as basename3, extname as extname2, join as join6 } from "node:path";
2479
2559
 
2480
2560
  // packages/cli/src/index/constants.mjs
2481
2561
  var CACHE_VERSION = 15;
@@ -2636,7 +2716,7 @@ function walkIndexCandidates(root, config = readMemoryConfig(root)) {
2636
2716
  const files = [];
2637
2717
  const walk = (dir) => {
2638
2718
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
2639
- const path = join5(dir, entry.name);
2719
+ const path = join6(dir, entry.name);
2640
2720
  const pathRel = rel(root, path);
2641
2721
  if (entry.isDirectory()) {
2642
2722
  const area2 = resolveMemoryArea(pathRel, config);
@@ -2650,7 +2730,7 @@ function walkIndexCandidates(root, config = readMemoryConfig(root)) {
2650
2730
  function addConfiguredLocalMemoryCandidates(root, candidates, config, limit = 5e3) {
2651
2731
  let added = 0;
2652
2732
  const addPath = (path) => {
2653
- if (added >= limit || !existsSync6(path)) return;
2733
+ if (added >= limit || !existsSync7(path)) return;
2654
2734
  let stat;
2655
2735
  try {
2656
2736
  stat = statSync2(path);
@@ -2666,7 +2746,7 @@ function addConfiguredLocalMemoryCandidates(root, candidates, config, limit = 5e
2666
2746
  if (!stat.isDirectory() || isExcludedIndexDir(pathRel)) return;
2667
2747
  for (const entry of readdirSync(path, { withFileTypes: true })) {
2668
2748
  if (added >= limit) return;
2669
- addPath(join5(path, entry.name));
2749
+ addPath(join6(path, entry.name));
2670
2750
  }
2671
2751
  };
2672
2752
  for (const area2 of config.areas ?? []) {
@@ -2674,30 +2754,30 @@ function addConfiguredLocalMemoryCandidates(root, candidates, config, limit = 5e
2674
2754
  for (const pattern of area2.paths ?? []) {
2675
2755
  if (pattern.startsWith("**/")) continue;
2676
2756
  const base = pattern.endsWith("/**") ? pattern.slice(0, -3) : pattern;
2677
- addPath(join5(root, base));
2757
+ addPath(join6(root, base));
2678
2758
  }
2679
2759
  }
2680
2760
  }
2681
2761
  function collectIndexFiles(root, config = readMemoryConfig(root)) {
2682
2762
  const candidates = new Set(gitIndexCandidates(root) ?? walkIndexCandidates(root, config));
2683
2763
  addConfiguredLocalMemoryCandidates(root, candidates, config);
2684
- return [...candidates].filter((path) => shouldIndexPath(path, config)).map((path) => join5(root, path)).filter(existsSync6).sort();
2764
+ return [...candidates].filter((path) => shouldIndexPath(path, config)).map((path) => join6(root, path)).filter(existsSync7).sort();
2685
2765
  }
2686
2766
  function fingerprint(file) {
2687
- const content = readFileSync6(file);
2767
+ const content = readFileSync7(file);
2688
2768
  return createHash("sha256").update(content).digest("hex");
2689
2769
  }
2690
2770
  function cacheFile(root) {
2691
- return join5(root, CACHE_DIR, MANIFEST_FILE);
2771
+ return join6(root, CACHE_DIR, MANIFEST_FILE);
2692
2772
  }
2693
2773
 
2694
2774
  // packages/cli/src/index/cache.mjs
2695
- import { existsSync as existsSync12, readFileSync as readFileSync12, statSync as statSync4 } from "node:fs";
2775
+ import { existsSync as existsSync13, readFileSync as readFileSync13, statSync as statSync4 } from "node:fs";
2696
2776
  import { basename as basename6, join as join12 } from "node:path";
2697
2777
 
2698
2778
  // packages/cli/src/graph/store.mjs
2699
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, statSync as statSync3, writeFileSync as writeFileSync2 } from "node:fs";
2700
- import { dirname as dirname3, join as join6 } from "node:path";
2779
+ import { existsSync as existsSync8, mkdirSync as mkdirSync3, statSync as statSync3, writeFileSync as writeFileSync3 } from "node:fs";
2780
+ import { dirname as dirname4, join as join7 } from "node:path";
2701
2781
  var GRAPH_NODE_SHARDS = ["docs", "packages", "source"];
2702
2782
  var GRAPH_EDGE_SHARDS = ["docs-links", "packages", "other"];
2703
2783
  function graphNodeShard(node2) {
@@ -2728,14 +2808,14 @@ function expandEdge(row) {
2728
2808
  return { source, target, relation, ...data };
2729
2809
  }
2730
2810
  function shardFile(root, kind, name) {
2731
- return join6(root, CACHE_DIR, "graph", kind, `${name}.json`);
2811
+ return join7(root, CACHE_DIR, "graph", kind, `${name}.json`);
2732
2812
  }
2733
2813
  function jsonSize(file) {
2734
- return existsSync7(file) ? statSync3(file).size : 0;
2814
+ return existsSync8(file) ? statSync3(file).size : 0;
2735
2815
  }
2736
2816
  function writeJson(file, value) {
2737
- mkdirSync2(dirname3(file), { recursive: true });
2738
- writeFileSync2(file, `${JSON.stringify(value)}
2817
+ mkdirSync3(dirname4(file), { recursive: true });
2818
+ writeFileSync3(file, `${JSON.stringify(value)}
2739
2819
  `);
2740
2820
  }
2741
2821
  function compactGraph(graph) {
@@ -2798,13 +2878,13 @@ function addEdge(graph, source, target, relation, data = {}) {
2798
2878
  }
2799
2879
 
2800
2880
  // packages/cli/src/graph/extract.mjs
2801
- import { readFileSync as readFileSync7 } from "node:fs";
2802
- import { basename as basename5, dirname as dirname4, resolve as resolve6 } from "node:path";
2881
+ import { readFileSync as readFileSync8 } from "node:fs";
2882
+ import { basename as basename5, dirname as dirname5, resolve as resolve7 } from "node:path";
2803
2883
 
2804
2884
  // packages/cli/src/graph/metadata.mjs
2805
2885
  import { createHash as createHash2 } from "node:crypto";
2806
- import { existsSync as existsSync8 } from "node:fs";
2807
- import { basename as basename4, extname as extname3, resolve as resolve5 } from "node:path";
2886
+ import { existsSync as existsSync9 } from "node:fs";
2887
+ import { basename as basename4, extname as extname3, resolve as resolve6 } from "node:path";
2808
2888
  function isReadmeIndexPath(path = "") {
2809
2889
  return basename4(path) === "README.md";
2810
2890
  }
@@ -2863,7 +2943,7 @@ function addReferenceEvidence(graph, sourceId, reference) {
2863
2943
  function addTraceabilityTarget(graph, sourceId, root, relation, targetPath, data = {}, config = defaultMemoryConfig()) {
2864
2944
  if (!isLocalRelativeTraceabilityPath(targetPath)) return;
2865
2945
  addReferenceEvidence(graph, sourceId, { targetPath, href: targetPath, relation, data: { confidence: "CURATED_TRACEABILITY", ...data } });
2866
- if (!existsSync8(resolve5(root, targetPath))) return;
2946
+ if (!existsSync9(resolve6(root, targetPath))) return;
2867
2947
  const targetId = `file:${targetPath}`;
2868
2948
  addNode(graph, targetId, "file", fileNodeMetadata(targetPath, null, config));
2869
2949
  addEdge(graph, sourceId, targetId, relation, { confidence: "CURATED_TRACEABILITY", ...data });
@@ -2915,7 +2995,7 @@ function headingId(path, title) {
2915
2995
  // packages/cli/src/graph/extract.mjs
2916
2996
  function extractMarkdownGraph(root, file, graph, config = defaultMemoryConfig()) {
2917
2997
  const path = rel(root, file);
2918
- const content = readFileSync7(file, "utf8");
2998
+ const content = readFileSync8(file, "utf8");
2919
2999
  const fileId = `file:${path}`;
2920
3000
  const headingRe = /^(#{1,6})\s+(.+)$/gm;
2921
3001
  let match;
@@ -2929,7 +3009,7 @@ function extractMarkdownGraph(root, file, graph, config = defaultMemoryConfig())
2929
3009
  const hashIndex = href.indexOf("#");
2930
3010
  const pathPart = hashIndex < 0 ? href : href.slice(0, hashIndex);
2931
3011
  const anchor = hashIndex < 0 ? "" : href.slice(hashIndex + 1);
2932
- const targetPath = pathPart ? rel(root, resolve6(dirname4(file), pathPart)) : path;
3012
+ const targetPath = pathPart ? rel(root, resolve7(dirname5(file), pathPart)) : path;
2933
3013
  const data = { line, confidence: "EXTRACTED" };
2934
3014
  addReferenceEvidence(graph, fileId, { targetPath, href, anchor, relation: "links_to", data });
2935
3015
  if (pathPart && isReadmeIndexPath(path)) addReferenceEvidence(graph, fileId, { targetPath, href, anchor, relation: "routes_to", data: { line, confidence: "CURATED_INDEX", sourceRole: "readme-index" } });
@@ -2946,7 +3026,7 @@ function extractPackageGraph(root, file, graph) {
2946
3026
  const fileId = `file:${path}`;
2947
3027
  let pkg;
2948
3028
  try {
2949
- pkg = JSON.parse(readFileSync7(file, "utf8"));
3029
+ pkg = JSON.parse(readFileSync8(file, "utf8"));
2950
3030
  } catch {
2951
3031
  return;
2952
3032
  }
@@ -2992,18 +3072,18 @@ function buildGraph(root, files, config = defaultMemoryConfig()) {
2992
3072
  }
2993
3073
 
2994
3074
  // packages/cli/src/symbols/store.mjs
2995
- import { existsSync as existsSync11, lstatSync as lstatSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync11, renameSync, writeFileSync as writeFileSync3 } from "node:fs";
3075
+ import { existsSync as existsSync12, lstatSync as lstatSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync12, renameSync, writeFileSync as writeFileSync4 } from "node:fs";
2996
3076
  import { randomUUID } from "node:crypto";
2997
- import { dirname as dirname7, join as join11 } from "node:path";
3077
+ import { dirname as dirname8, join as join11 } from "node:path";
2998
3078
 
2999
3079
  // packages/cli/src/symbols/discovery.mjs
3000
3080
  var import_ignore = __toESM(require_ignore(), 1);
3001
- import { existsSync as existsSync10, lstatSync, readFileSync as readFileSync10, realpathSync } from "node:fs";
3002
- import { extname as extname5, join as join9, relative as relative4, isAbsolute } from "node:path";
3081
+ import { existsSync as existsSync11, lstatSync as lstatSync2, readFileSync as readFileSync11, realpathSync as realpathSync2 } from "node:fs";
3082
+ import { extname as extname5, join as join10, relative as relative5, isAbsolute as isAbsolute2 } from "node:path";
3003
3083
 
3004
3084
  // packages/cli/src/query/chunks.mjs
3005
- import { readFileSync as readFileSync8, readdirSync as readdirSync2 } from "node:fs";
3006
- import { join as join7, relative as relative3 } from "node:path";
3085
+ import { readFileSync as readFileSync9, readdirSync as readdirSync2 } from "node:fs";
3086
+ import { join as join8, relative as relative4 } from "node:path";
3007
3087
  import { createHash as createHash3 } from "node:crypto";
3008
3088
  var MAX_CHUNK_CHARS = 1600;
3009
3089
  var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", ".git", ".dotdotgod"]);
@@ -3011,7 +3091,7 @@ function textFingerprint(text) {
3011
3091
  return createHash3("sha256").update(text).digest("hex");
3012
3092
  }
3013
3093
  function collectDocumentationMarkdown(root, exclude = ["docs/plan", "docs/archive"], documentationRoot = "docs") {
3014
- const docsRoot = join7(root, documentationRoot);
3094
+ const docsRoot = join8(root, documentationRoot);
3015
3095
  const files = [];
3016
3096
  const walk = (directory) => {
3017
3097
  let entries;
@@ -3023,8 +3103,8 @@ function collectDocumentationMarkdown(root, exclude = ["docs/plan", "docs/archiv
3023
3103
  entries.sort((a, b) => a.name.localeCompare(b.name));
3024
3104
  for (const entry of entries) {
3025
3105
  if (entry.name.startsWith(".")) continue;
3026
- const absolute = join7(directory, entry.name);
3027
- const path = relative3(root, absolute).replaceAll("\\", "/");
3106
+ const absolute = join8(directory, entry.name);
3107
+ const path = relative4(root, absolute).replaceAll("\\", "/");
3028
3108
  if (isSecretLikePathPattern(path) || exclude.some((pattern) => matchPathPattern(path, pattern) || path === pattern || path.startsWith(`${pattern}/`))) continue;
3029
3109
  if (entry.isDirectory()) {
3030
3110
  if (!SKIPPED_DIRECTORIES.has(entry.name)) walk(absolute);
@@ -3086,7 +3166,7 @@ ${piece}`;
3086
3166
  }
3087
3167
  function collectDocumentationChunks(root, exclude, documentationRoot = "docs") {
3088
3168
  return collectDocumentationMarkdown(root, exclude, documentationRoot).flatMap((path) => {
3089
- const content = readFileSync8(join7(root, path), "utf8");
3169
+ const content = readFileSync9(join8(root, path), "utf8");
3090
3170
  return chunkMarkdown(path, content);
3091
3171
  });
3092
3172
  }
@@ -3095,14 +3175,14 @@ function collectDocumentationChunks(root, exclude, documentationRoot = "docs") {
3095
3175
  init_languages();
3096
3176
  init_runtime();
3097
3177
  function inside(root, file) {
3098
- if (!lstatSync(file).isFile()) return false;
3099
- const path = relative4(realpathSync(root), realpathSync(file));
3100
- return !path.startsWith("..") && !isAbsolute(path);
3178
+ if (!lstatSync2(file).isFile()) return false;
3179
+ const path = relative5(realpathSync2(root), realpathSync2(file));
3180
+ return !path.startsWith("..") && !isAbsolute2(path);
3101
3181
  }
3102
3182
  function ignoreRules(root, directory, cache) {
3103
3183
  if (!cache.has(directory)) {
3104
- const file = join9(root, directory, ".gitignore");
3105
- cache.set(directory, (0, import_ignore.default)().add(existsSync10(file) && inside(root, file) ? readFileSync10(file, "utf8") : ""));
3184
+ const file = join10(root, directory, ".gitignore");
3185
+ cache.set(directory, (0, import_ignore.default)().add(existsSync11(file) && inside(root, file) ? readFileSync11(file, "utf8") : ""));
3106
3186
  }
3107
3187
  return cache.get(directory);
3108
3188
  }
@@ -3125,18 +3205,18 @@ function ignored(root, path, cache) {
3125
3205
  function sourceFiles(root) {
3126
3206
  const cache = /* @__PURE__ */ new Map();
3127
3207
  return collectIndexFiles(root).filter((file) => {
3128
- const path = relative4(root, file).replaceAll("\\", "/");
3208
+ const path = relative5(root, file).replaceAll("\\", "/");
3129
3209
  if (!SOURCE_EXTENSIONS.has(extname5(path)) || path.split("/").some((part) => part.startsWith(".")) || isSecretLikePathPattern(path)) return false;
3130
3210
  if (!inside(root, file) || ignored(root, path, cache)) return false;
3131
- return !/^#![^\n]*\n\/\/ Generated|^\/\/ Generated|@generated/m.test(readFileSync10(file, "utf8").slice(0, 256));
3211
+ return !/^#![^\n]*\n\/\/ Generated|^\/\/ Generated|@generated/m.test(readFileSync11(file, "utf8").slice(0, 256));
3132
3212
  }).map((file) => {
3133
- const source = readFileSync10(file, "utf8");
3134
- return { path: relative4(root, file).replaceAll("\\", "/"), hash: fingerprint(file), language: languageForPath(file), sourceLength: source.length, lineCount: source.split("\n").length };
3213
+ const source = readFileSync11(file, "utf8");
3214
+ return { path: relative5(root, file).replaceAll("\\", "/"), hash: fingerprint(file), language: languageForPath(file), sourceLength: source.length, lineCount: source.split("\n").length };
3135
3215
  });
3136
3216
  }
3137
3217
  function outlinePolicy(root) {
3138
3218
  const config = readMemoryConfig(root);
3139
- const ignores = collectIndexFiles(root).filter((file) => file.endsWith("/.gitignore") && inside(root, file)).map((file) => [relative4(root, file), fingerprint(file)]);
3219
+ const ignores = collectIndexFiles(root).filter((file) => file.endsWith("/.gitignore") && inside(root, file)).map((file) => [relative5(root, file), fingerprint(file)]);
3140
3220
  return textFingerprint(JSON.stringify({ areas: config.areas, ignores, extractor: EXTRACTOR_ID, declarations: DECLARATIONS, extensions: LANGUAGE_EXTENSIONS, parser: parserIdentity() }));
3141
3221
  }
3142
3222
 
@@ -3145,10 +3225,10 @@ init_languages();
3145
3225
  var OUTLINE_SCHEMA = 1;
3146
3226
  var outlinePath = (root) => join11(root, ".dotdotgod", "outlines.json");
3147
3227
  function assertCachePath(root) {
3148
- for (const path of [dirname7(outlinePath(root)), outlinePath(root)]) {
3228
+ for (const path of [dirname8(outlinePath(root)), outlinePath(root)]) {
3149
3229
  let stat;
3150
3230
  try {
3151
- stat = lstatSync2(path);
3231
+ stat = lstatSync3(path);
3152
3232
  } catch (error) {
3153
3233
  if (error.code !== "ENOENT") throw error;
3154
3234
  }
@@ -3158,7 +3238,7 @@ function assertCachePath(root) {
3158
3238
  function readOutlines(root) {
3159
3239
  try {
3160
3240
  assertCachePath(root);
3161
- const index = JSON.parse(readFileSync11(outlinePath(root), "utf8"));
3241
+ const index = JSON.parse(readFileSync12(outlinePath(root), "utf8"));
3162
3242
  if (index.schema !== OUTLINE_SCHEMA || index.extractor !== EXTRACTOR_ID || !Array.isArray(index.files)) return null;
3163
3243
  const paths = /* @__PURE__ */ new Set();
3164
3244
  const ids = /* @__PURE__ */ new Set();
@@ -3191,7 +3271,7 @@ function outlineStatus(root) {
3191
3271
  assertCachePath(root);
3192
3272
  const files = sourceFiles(root);
3193
3273
  const index = readOutlines(root);
3194
- if (!index) return { ok: files.length === 0 && !existsSync11(outlinePath(root)), reason: existsSync11(outlinePath(root)) ? "missing-or-invalid" : "missing", paths: files.map((file) => file.path) };
3274
+ if (!index) return { ok: files.length === 0 && !existsSync12(outlinePath(root)), reason: existsSync12(outlinePath(root)) ? "missing-or-invalid" : "missing", paths: files.map((file) => file.path) };
3195
3275
  const old = new Map(index.files.map((file) => [file.path, file]));
3196
3276
  const current = new Set(files.map((file) => file.path));
3197
3277
  const changed = files.filter((file) => old.get(file.path)?.hash !== file.hash || old.get(file.path)?.sourceLength !== file.sourceLength || old.get(file.path)?.lineCount !== file.lineCount).map((file) => file.path);
@@ -3220,7 +3300,7 @@ async function buildOutlines(root) {
3220
3300
  continue;
3221
3301
  }
3222
3302
  const { extractSymbols: extractSymbols2 } = await Promise.resolve().then(() => (init_parser(), parser_exports));
3223
- const source = readFileSync11(join11(root, file.path), "utf8");
3303
+ const source = readFileSync12(join11(root, file.path), "utf8");
3224
3304
  const extracted = await extractSymbols2(file.path, source, file.language);
3225
3305
  if (fingerprint(join11(root, file.path)) !== file.hash) throw new Error(`Source changed during outline extraction: ${file.path}`);
3226
3306
  records.push({ ...file, ...extracted });
@@ -3228,9 +3308,9 @@ async function buildOutlines(root) {
3228
3308
  }
3229
3309
  const index = { schema: OUTLINE_SCHEMA, extractor: EXTRACTOR_ID, policy: outlinePolicy(root), files: records, refresh: { parsed, reused } };
3230
3310
  const target = outlinePath(root);
3231
- mkdirSync3(dirname7(target), { recursive: true });
3311
+ mkdirSync4(dirname8(target), { recursive: true });
3232
3312
  const temporary = `${target}.tmp-${process.pid}-${randomUUID()}`;
3233
- writeFileSync3(temporary, JSON.stringify(index), { flag: "wx" });
3313
+ writeFileSync4(temporary, JSON.stringify(index), { flag: "wx" });
3234
3314
  renameSync(temporary, target);
3235
3315
  return index;
3236
3316
  }
@@ -3248,10 +3328,10 @@ ${text}`;
3248
3328
  }
3249
3329
 
3250
3330
  // packages/cli/src/symbols/graph.mjs
3251
- import { dirname as dirname8, posix } from "node:path";
3331
+ import { dirname as dirname9, posix } from "node:path";
3252
3332
  function importTarget(file, specifier, paths) {
3253
3333
  if (!specifier.startsWith(".")) return null;
3254
- const base = posix.normalize(posix.join(dirname8(file).replaceAll("\\", "/"), specifier));
3334
+ const base = posix.normalize(posix.join(dirname9(file).replaceAll("\\", "/"), specifier));
3255
3335
  if (base.startsWith("../")) return null;
3256
3336
  const stem = base.replace(/\.(js|jsx|mjs|cjs)$/, "");
3257
3337
  const candidates = [base, ...[".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py"].map((extension) => `${stem}${extension}`), ...[".ts", ".js", ".py"].map((extension) => `${base}/index${extension}`)];
@@ -3338,14 +3418,14 @@ function writeIndex(root, index) {
3338
3418
  }
3339
3419
  function readIndex(root) {
3340
3420
  const file = cacheFile(root);
3341
- if (!existsSync12(file)) return null;
3421
+ if (!existsSync13(file)) return null;
3342
3422
  try {
3343
- const manifest = JSON.parse(readFileSync12(file, "utf8"));
3423
+ const manifest = JSON.parse(readFileSync13(file, "utf8"));
3344
3424
  const shards = manifest.graph?.shards;
3345
3425
  if (!shards) return manifest;
3346
3426
  const compact = { nodes: {}, edges: {} };
3347
- for (const [name, path] of Object.entries(shards.nodes ?? {})) compact.nodes[name] = existsSync12(join12(root, path)) ? JSON.parse(readFileSync12(join12(root, path), "utf8")) : [];
3348
- for (const [name, path] of Object.entries(shards.edges ?? {})) compact.edges[name] = existsSync12(join12(root, path)) ? JSON.parse(readFileSync12(join12(root, path), "utf8")) : [];
3427
+ for (const [name, path] of Object.entries(shards.nodes ?? {})) compact.nodes[name] = existsSync13(join12(root, path)) ? JSON.parse(readFileSync13(join12(root, path), "utf8")) : [];
3428
+ for (const [name, path] of Object.entries(shards.edges ?? {})) compact.edges[name] = existsSync13(join12(root, path)) ? JSON.parse(readFileSync13(join12(root, path), "utf8")) : [];
3349
3429
  return { ...manifest, graph: expandGraph(compact) };
3350
3430
  } catch {
3351
3431
  return null;
@@ -3401,7 +3481,7 @@ function graphSummary(index) {
3401
3481
 
3402
3482
  // packages/cli/src/validate/references.mjs
3403
3483
  import { statSync as statSync5 } from "node:fs";
3404
- import { dirname as dirname9, relative as relative5, resolve as resolve7 } from "node:path";
3484
+ import { dirname as dirname10, relative as relative6, resolve as resolve8 } from "node:path";
3405
3485
  var blank = (text) => text.replace(/[^\n\r]/g, " ");
3406
3486
  var unescape = (text) => text.replace(/\\([!"#$%&'()*+,\-./:;<=>?@[\]\\^_`{|}~])/g, "$1");
3407
3487
  var labelKey = (text) => unescape(text).trim().replace(/\s+/g, " ").toLowerCase();
@@ -3501,7 +3581,7 @@ function extractMemoryReferences(content) {
3501
3581
  return references;
3502
3582
  }
3503
3583
  function sharedLocalReferences(content, root, file, config) {
3504
- const source = resolveMemoryArea(relative5(root, file).split("\\").join("/"), config);
3584
+ const source = resolveMemoryArea(relative6(root, file).split("\\").join("/"), config);
3505
3585
  if (source?.scope !== "shared") return [];
3506
3586
  const errors = [];
3507
3587
  for (const item of extractMemoryReferences(content)) {
@@ -3513,14 +3593,14 @@ function sharedLocalReferences(content, root, file, config) {
3513
3593
  continue;
3514
3594
  }
3515
3595
  if (!path) continue;
3516
- const base = item.kind === "markdown-link" || /^\.\.?\//.test(path) ? dirname9(file) : root;
3517
- const target = relative5(root, resolve7(base, path)).split("\\").join("/");
3596
+ const base = item.kind === "markdown-link" || /^\.\.?\//.test(path) ? dirname10(file) : root;
3597
+ const target = relative6(root, resolve8(base, path)).split("\\").join("/");
3518
3598
  if (target === ".." || target.startsWith("../")) continue;
3519
3599
  if (item.kind === "inline-code") {
3520
3600
  const configuredDirectory = (config.areas ?? []).some((area3) => (area3.paths ?? []).some((pattern) => pattern === `${target}/**`));
3521
3601
  let directory = configuredDirectory;
3522
3602
  try {
3523
- directory ||= statSync5(resolve7(root, target)).isDirectory();
3603
+ directory ||= statSync5(resolve8(root, target)).isDirectory();
3524
3604
  } catch {
3525
3605
  }
3526
3606
  if (directory) continue;
@@ -3545,7 +3625,7 @@ function runValidate(argv) {
3545
3625
  else if (!arg.startsWith("-")) options.root = arg;
3546
3626
  else usage(`Unknown option: ${arg}`, "validate");
3547
3627
  }
3548
- const root = resolve8(options.root);
3628
+ const root = resolve9(options.root);
3549
3629
  const memoryConfig = readMemoryConfig(root);
3550
3630
  const documentationRoot = memoryConfig.documentation?.root ?? "docs";
3551
3631
  const docs = join13(root, documentationRoot);
@@ -3566,7 +3646,7 @@ Fix: ${fix}` : message, ...prompt ? { prompt } : {} });
3566
3646
  return false;
3567
3647
  };
3568
3648
  const walk = (dir) => {
3569
- if (!existsSync13(dir) || shouldSkipDir(dir)) return;
3649
+ if (!existsSync14(dir) || shouldSkipDir(dir)) return;
3570
3650
  for (const entry of readdirSync3(dir, { withFileTypes: true })) {
3571
3651
  const path = join13(dir, entry.name);
3572
3652
  if (entry.isDirectory()) {
@@ -3578,7 +3658,7 @@ Fix: ${fix}` : message, ...prompt ? { prompt } : {} });
3578
3658
  } else if (entry.isFile() && entry.name.endsWith(".md")) markdownFiles.push(path);
3579
3659
  }
3580
3660
  };
3581
- if (!existsSync13(docs)) usage(`docs directory not found: ${docs}`);
3661
+ if (!existsSync14(docs)) usage(`docs directory not found: ${docs}`);
3582
3662
  const validationPolicy = cloneValidationPolicy(memoryConfig.validation ?? DEFAULT_VALIDATION_POLICY);
3583
3663
  const maxLines = options.maxLines ?? validationPolicy.markdown.maxLines;
3584
3664
  const maxChars = options.maxChars ?? validationPolicy.markdown.maxChars;
@@ -3609,7 +3689,7 @@ README.md directory indexes, clear HTTP-status filenames such as RESPONSE_200_OK
3609
3689
  walk(docs);
3610
3690
  const dirSiblingMap = /* @__PURE__ */ new Map();
3611
3691
  for (const f of markdownFiles) {
3612
- const dir = dirname10(f);
3692
+ const dir = dirname11(f);
3613
3693
  if (!dirSiblingMap.has(dir)) dirSiblingMap.set(dir, []);
3614
3694
  dirSiblingMap.get(dir).push(basename7(f));
3615
3695
  }
@@ -3618,14 +3698,14 @@ README.md directory indexes, clear HTTP-status filenames such as RESPONSE_200_OK
3618
3698
  const docsRel = rel(docs, file);
3619
3699
  const rootRel = rel(root, file);
3620
3700
  if (docsRel && !docsRel.startsWith("..") && !isUpperSnakeMarkdown(name)) addError(file, "FILE_NAMING", `Markdown file must be UPPER_SNAKE_CASE.md or README.md: ${name}`, null, "rename the markdown file to UPPER_SNAKE_CASE.md or README.md and update any links that reference it.");
3621
- const content = readFileSync13(file, "utf8");
3701
+ const content = readFileSync14(file, "utf8");
3622
3702
  fileCache.set(file, content);
3623
3703
  const docArea = docsRel?.split("/")[0];
3624
3704
  if (name !== "README.md" && isUpperSnakeMarkdown(name) && FILENAME_QUALITY_AREAS.has(docArea)) {
3625
3705
  const fp = validationPolicy.markdown?.filename ?? {};
3626
3706
  const allow = fp.allow ?? [];
3627
3707
  if (!allow.some((p) => matchPathPattern(rootRel, p)) && (fp.warnNumberedSeries ?? true)) {
3628
- const siblings = dirSiblingMap.get(dirname10(file)) ?? [];
3708
+ const siblings = dirSiblingMap.get(dirname11(file)) ?? [];
3629
3709
  if (isNumberedSeriesFilename(name, siblings)) {
3630
3710
  const heading = extractFirstHeading(content);
3631
3711
  const suggestion = suggestFilenameFromHeading(heading);
@@ -3667,7 +3747,7 @@ ${traceabilityExample()}`, block.line);
3667
3747
  }
3668
3748
  }
3669
3749
  const byDir = /* @__PURE__ */ new Map();
3670
- for (const file of markdownFiles) byDir.set(dirname10(file), [...byDir.get(dirname10(file)) ?? [], file]);
3750
+ for (const file of markdownFiles) byDir.set(dirname11(file), [...byDir.get(dirname11(file)) ?? [], file]);
3671
3751
  for (const [dir, files] of byDir) {
3672
3752
  if (files.length > 1 && !files.some((file) => basename7(file) === "README.md")) addError(dir, "MISSING_README", "Directory with multiple markdown files must include README.md", null, "add a README.md in this directory that indexes the important markdown files and their purpose.");
3673
3753
  }
@@ -3678,18 +3758,18 @@ ${traceabilityExample()}`, block.line);
3678
3758
  }
3679
3759
  if (options.linkCheck) {
3680
3760
  for (const [file, content] of fileCache) {
3681
- const fileDir = dirname10(file);
3761
+ const fileDir = dirname11(file);
3682
3762
  for (const { href, line } of extractLinks(content)) {
3683
3763
  const hashIndex = href.indexOf("#");
3684
3764
  const pathPart = hashIndex === -1 ? href : href.slice(0, hashIndex);
3685
3765
  const anchor = hashIndex === -1 ? "" : href.slice(hashIndex + 1);
3686
- const target = pathPart ? resolve8(fileDir, pathPart) : file;
3687
- if (pathPart && !existsSync13(target)) {
3766
+ const target = pathPart ? resolve9(fileDir, pathPart) : file;
3767
+ if (pathPart && !existsSync14(target)) {
3688
3768
  addError(file, "BROKEN_LINK", `Local link target does not exist: ${pathPart}`, line, "update the link target to an existing local file, create the intended file, or remove the stale link.");
3689
3769
  continue;
3690
3770
  }
3691
3771
  if (anchor && extname6(target) === ".md") {
3692
- const targetContent = fileCache.get(target) ?? (existsSync13(target) ? readFileSync13(target, "utf8") : "");
3772
+ const targetContent = fileCache.get(target) ?? (existsSync14(target) ? readFileSync14(target, "utf8") : "");
3693
3773
  if (targetContent && !extractAnchors(targetContent).has(decodeURIComponent(anchor))) addError(file, "BROKEN_ANCHOR", `Local anchor target does not exist: ${href}`, line, "update the fragment to a heading that exists in the target markdown file, or add the missing heading.");
3694
3774
  }
3695
3775
  }
@@ -3724,19 +3804,19 @@ ${traceabilityExample()}`, block.line);
3724
3804
  if (options.includeLocalMemory) {
3725
3805
  for (const area2 of ["plan", "archive/plan", "archive/report"]) {
3726
3806
  const areaRoot = join13(docs, area2);
3727
- if (!existsSync13(areaRoot)) continue;
3807
+ if (!existsSync14(areaRoot)) continue;
3728
3808
  for (const entry of readdirSync3(areaRoot, { withFileTypes: true })) {
3729
3809
  if (!entry.isDirectory()) continue;
3730
3810
  if (!isKebabCase(entry.name)) addError(join13(areaRoot, entry.name), "SLUG_NAMING", `Archive/plan/report slug must be kebab-case: ${entry.name}`, null, "rename the task/report directory to kebab-case and update any README index links that reference it.");
3731
3811
  const readme = join13(areaRoot, entry.name, "README.md");
3732
- if (!existsSync13(readme)) addError(readme, "MISSING_README", `Expected README.md in ${documentationRoot}/${area2}/${entry.name}/`, null, "add a README.md that summarizes the task/report and links any supporting files.");
3812
+ if (!existsSync14(readme)) addError(readme, "MISSING_README", `Expected README.md in ${documentationRoot}/${area2}/${entry.name}/`, null, "add a README.md that summarizes the task/report and links any supporting files.");
3733
3813
  }
3734
3814
  }
3735
3815
  }
3736
3816
  const gitignore = join13(root, ".gitignore");
3737
- if (!existsSync13(gitignore)) addError(gitignore, "MISSING_GITIGNORE", "Expected .gitignore", null, `create .gitignore and include ${documentationRoot}/plan, ${documentationRoot}/archive, and .dotdotgod entries.`);
3817
+ if (!existsSync14(gitignore)) addError(gitignore, "MISSING_GITIGNORE", "Expected .gitignore", null, `create .gitignore and include ${documentationRoot}/plan, ${documentationRoot}/archive, and .dotdotgod entries.`);
3738
3818
  else {
3739
- const content = readFileSync13(gitignore, "utf8").split("\n").map((line) => line.trim());
3819
+ const content = readFileSync14(gitignore, "utf8").split("\n").map((line) => line.trim());
3740
3820
  for (const required of [`${documentationRoot}/plan`, `${documentationRoot}/archive`, CACHE_DIR]) if (!content.includes(required)) addError(gitignore, "MISSING_GITIGNORE_ENTRY", `Expected .gitignore entry: ${required}`, null, `add ${required} to .gitignore so local plans, archives, and cache files stay untracked.`);
3741
3821
  }
3742
3822
  if (options.json) console.log(JSON.stringify({ ok: errors.length === 0, errors, warnings }, null, 2));
@@ -3761,8 +3841,8 @@ Prompt: ${error.prompt}` : ""}`);
3761
3841
  }
3762
3842
 
3763
3843
  // packages/cli/src/commands/config.mjs
3764
- import { existsSync as existsSync14, statSync as statSync6, writeFileSync as writeFileSync4 } from "node:fs";
3765
- import { join as join14, resolve as resolve9 } from "node:path";
3844
+ import { existsSync as existsSync15, statSync as statSync6, writeFileSync as writeFileSync5 } from "node:fs";
3845
+ import { join as join14, resolve as resolve10 } from "node:path";
3766
3846
  function parseConfigOptions(argv, usageKey = "config") {
3767
3847
  const options = { root: ".", json: false, template: null };
3768
3848
  let rootSet = false;
@@ -3780,7 +3860,7 @@ function parseConfigOptions(argv, usageKey = "config") {
3780
3860
  } else if (!arg.startsWith("-")) usage(`Unexpected argument: ${arg}`, usageKey);
3781
3861
  else usage(`Unknown option: ${arg}`, usageKey);
3782
3862
  }
3783
- options.root = resolve9(options.root);
3863
+ options.root = resolve10(options.root);
3784
3864
  return options;
3785
3865
  }
3786
3866
  function configInitError(options, code, message, path = null) {
@@ -3798,7 +3878,7 @@ function formatConfigOutput(payload) {
3798
3878
  lines.push(`- path: ${payload.path ?? "none"}`);
3799
3879
  lines.push(`- documentation root: ${payload.config.documentation.root}`);
3800
3880
  lines.push(`- memory areas: ${payload.config.areas.length}`);
3801
- lines.push(`- embedding: ${payload.embedding.profile.provider}/${payload.embedding.profile.model} (${payload.embedding.source})`);
3881
+ lines.push(`- embedding: ${payload.embedding.profile ? `${payload.embedding.profile.provider}/${payload.embedding.profile.model}` : "not configured; keyword search"} (${payload.embedding.source})`);
3802
3882
  lines.push(`- traceability required: ${(payload.config.traceability.required ?? []).join(", ") || "none"}`);
3803
3883
  lines.push(`- traceability exclude: ${(payload.config.traceability.exclude ?? []).join(", ") || "none"}`);
3804
3884
  lines.push("- traceability keys:");
@@ -3822,17 +3902,17 @@ function runConfig(argv) {
3822
3902
  const isInit = argv[0] === "init";
3823
3903
  const options = parseConfigOptions(isInit ? argv.slice(1) : argv, isInit ? "config init" : "config");
3824
3904
  if (isInit) {
3825
- if (!existsSync14(options.root)) configInitError(options, "ROOT_NOT_FOUND", `Project root not found: ${options.root}`);
3905
+ if (!existsSync15(options.root)) configInitError(options, "ROOT_NOT_FOUND", `Project root not found: ${options.root}`);
3826
3906
  try {
3827
3907
  if (!statSync6(options.root).isDirectory()) configInitError(options, "ROOT_NOT_DIRECTORY", `Project root is not a directory: ${options.root}`);
3828
3908
  } catch {
3829
3909
  configInitError(options, "ROOT_NOT_FOUND", `Project root not found: ${options.root}`);
3830
3910
  }
3831
3911
  const target = join14(options.root, "dotdotgod.config.json");
3832
- if (existsSync14(target)) configInitError(options, "CONFIG_EXISTS", "dotdotgod.config.json already exists.", target);
3912
+ if (existsSync15(target)) configInitError(options, "CONFIG_EXISTS", "dotdotgod.config.json already exists.", target);
3833
3913
  const template = resolveInitializationTemplate(options.template);
3834
3914
  if (!template.ok) configInitError(options, template.error.code, template.error.message, template.error.path ?? target);
3835
- writeFileSync4(target, template.text);
3915
+ writeFileSync5(target, template.text);
3836
3916
  const payload2 = { ok: true, command: "config init", root: options.root, path: target, created: true, template: { name: template.name, source: template.source, selectedBy: template.selectedBy } };
3837
3917
  if (options.json) console.log(JSON.stringify(payload2, null, 2));
3838
3918
  else console.log(`dotdotgod config init: created ${target} from ${template.source}:${template.name} (${template.selectedBy})`);
@@ -3846,7 +3926,7 @@ function runConfig(argv) {
3846
3926
  embedding = { source: resolved.source, path: resolved.path, profile: sanitizeEmbeddingProfile(resolved.profile) };
3847
3927
  } catch (error) {
3848
3928
  errors.push({ file: "embedding config", code: "EMBEDDING_CONFIG_INVALID", message: error instanceof Error ? error.message : String(error) });
3849
- embedding = { source: "invalid", path: null, profile: sanitizeEmbeddingProfile({ provider: "local", model: "Xenova/multilingual-e5-small" }) };
3929
+ embedding = { source: "invalid", path: null, profile: null };
3850
3930
  }
3851
3931
  const payload = { ok: errors.length === 0, command: "config", root: options.root, source: config.source ?? "default", path: config.source && config.source !== "default" ? join14(options.root, config.source) : null, embedding, config: memoryConfigSummary(config), errors };
3852
3932
  if (options.json) console.log(JSON.stringify(payload, null, 2));
@@ -3855,10 +3935,10 @@ function runConfig(argv) {
3855
3935
  }
3856
3936
 
3857
3937
  // packages/cli/src/commands/query.mjs
3858
- import { resolve as resolve10 } from "node:path";
3938
+ import { resolve as resolve11 } from "node:path";
3859
3939
 
3860
3940
  // packages/cli/src/query/embedding-runtime.mjs
3861
- import { existsSync as existsSync15, mkdirSync as mkdirSync4, readFileSync as readFileSync14, rmSync, writeFileSync as writeFileSync5 } from "node:fs";
3941
+ import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync15, rmSync, writeFileSync as writeFileSync6 } from "node:fs";
3862
3942
  import { spawnSync as spawnSync2 } from "node:child_process";
3863
3943
  import { createRequire as createRequire2 } from "node:module";
3864
3944
  import { homedir as homedir3 } from "node:os";
@@ -3891,7 +3971,7 @@ function embeddingRuntimeStatus(options = {}) {
3891
3971
  const require3 = createRequire2(join15(root, "package.json"));
3892
3972
  require3.resolve(EMBEDDING_RUNTIME_PACKAGE);
3893
3973
  const packagePath = join15(root, "node_modules", ...EMBEDDING_RUNTIME_PACKAGE.split("/"), "package.json");
3894
- const version = JSON.parse(readFileSync14(packagePath, "utf8")).version;
3974
+ const version = JSON.parse(readFileSync15(packagePath, "utf8")).version;
3895
3975
  return { ok: true, installed: true, installAvailable: true, package: EMBEDDING_RUNTIME_PACKAGE, requiredVersion: EMBEDDING_RUNTIME_VERSION, packageVersion: version, location: root };
3896
3976
  } catch {
3897
3977
  return { ok: true, installed: false, installAvailable: true, package: EMBEDDING_RUNTIME_PACKAGE, requiredVersion: EMBEDDING_RUNTIME_VERSION, packageVersion: null, location: root };
@@ -3901,9 +3981,9 @@ function installEmbeddingRuntime(options = {}) {
3901
3981
  if (options.confirm !== true) throw new Error("Embedding runtime installation requires explicit confirmation.");
3902
3982
  const root = embeddingRuntimeRoot(options);
3903
3983
  const lock = join15(root, ".installing");
3904
- if (existsSync15(lock)) throw new Error("Embedding runtime installation is already in progress.");
3905
- mkdirSync4(root, { recursive: true });
3906
- writeFileSync5(lock, `${process.pid}
3984
+ if (existsSync16(lock)) throw new Error("Embedding runtime installation is already in progress.");
3985
+ mkdirSync5(root, { recursive: true });
3986
+ writeFileSync6(lock, `${process.pid}
3907
3987
  `, { flag: "wx" });
3908
3988
  try {
3909
3989
  const spawn = options.spawnImpl ?? spawnSync2;
@@ -4018,13 +4098,14 @@ async function createEmbedder(profile) {
4018
4098
  return withInputFormat(embedBatch, profile);
4019
4099
  }
4020
4100
  async function resolveEmbedder(root, options = {}) {
4021
- const resolved = options.profile ? { profile: options.profile, source: options.source ?? "injected", path: null } : resolveEmbeddingProfile(root, options);
4101
+ const resolved = options.profile || options.embed ? { profile: options.profile ?? DEFAULT_EMBEDDING_PROFILE, source: options.source ?? "injected", path: null } : resolveEmbeddingProfile(root, options);
4102
+ if (!resolved.profile) throw new Error("Embedding is not configured. Set embedding in project or global configuration to use vector search.");
4022
4103
  return { ...resolved, identity: embeddingProfileIdentity(resolved.profile), embed: options.embed ? withInputFormat(options.embed, resolved.profile) : await createEmbedder(resolved.profile) };
4023
4104
  }
4024
4105
 
4025
4106
  // packages/cli/src/query/store.mjs
4026
4107
  import { createHash as createHash6 } from "node:crypto";
4027
- import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync15, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
4108
+ import { existsSync as existsSync17, mkdirSync as mkdirSync6, readFileSync as readFileSync16, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "node:fs";
4028
4109
  import { join as join16 } from "node:path";
4029
4110
  var VECTOR_SCHEMA_VERSION = 2;
4030
4111
  var VECTOR_DIMENSIONS = 384;
@@ -4050,13 +4131,13 @@ function vectorCachePaths(root) {
4050
4131
  }
4051
4132
  function readVectorCache(root, identity2 = null) {
4052
4133
  const paths = vectorCachePaths(root);
4053
- if (!existsSync16(paths.manifest) || !existsSync16(paths.chunks) || !existsSync16(paths.embeddings)) return null;
4134
+ if (!existsSync17(paths.manifest) || !existsSync17(paths.chunks) || !existsSync17(paths.embeddings)) return null;
4054
4135
  try {
4055
- const manifest = JSON.parse(readFileSync15(paths.manifest, "utf8"));
4136
+ const manifest = JSON.parse(readFileSync16(paths.manifest, "utf8"));
4056
4137
  if (manifest.schemaVersion !== VECTOR_SCHEMA_VERSION || !Number.isInteger(manifest.dimensions) || manifest.dimensions < 1) return null;
4057
4138
  if (identity2 && manifest.profileFingerprint !== profileFingerprint(identity2)) return null;
4058
- const chunks = readFileSync15(paths.chunks, "utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line));
4059
- const buffer = readFileSync15(paths.embeddings);
4139
+ const chunks = readFileSync16(paths.chunks, "utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line));
4140
+ const buffer = readFileSync16(paths.embeddings);
4060
4141
  if (buffer.byteLength !== chunks.length * manifest.dimensions * 4) return null;
4061
4142
  const vectors = new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4);
4062
4143
  if (!isValidNormalizedVectorValues(vectors, manifest.dimensions)) return null;
@@ -4067,14 +4148,14 @@ function readVectorCache(root, identity2 = null) {
4067
4148
  }
4068
4149
  function atomicWrite(path, data) {
4069
4150
  const temporary = `${path}.tmp-${process.pid}`;
4070
- writeFileSync6(temporary, data);
4151
+ writeFileSync7(temporary, data);
4071
4152
  renameSync2(temporary, path);
4072
4153
  }
4073
4154
  function writeVectorCache(root, chunks, vectors, metadata = {}) {
4074
4155
  const paths = vectorCachePaths(root);
4075
4156
  const dimensions = metadata.dimensions;
4076
4157
  if (!Number.isInteger(dimensions) || dimensions < 1) throw new Error("Vector dimensions must be a positive integer.");
4077
- mkdirSync5(paths.directory, { recursive: true });
4158
+ mkdirSync6(paths.directory, { recursive: true });
4078
4159
  if (vectors.length !== chunks.length * dimensions) throw new Error("Vector count does not match chunk metadata.");
4079
4160
  if (!isValidNormalizedVectorValues(vectors, dimensions)) throw new Error("Vectors must contain finite normalized values.");
4080
4161
  const manifest = { schemaVersion: VECTOR_SCHEMA_VERSION, chunks: chunks.length, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), ...metadata };
@@ -4133,11 +4214,6 @@ async function projectCorpus(root, scope) {
4133
4214
  const outlines = scope === "docs" ? null : await buildOutlines(root);
4134
4215
  return { chunks: [...docs, ...outlines ? outlineChunks(outlines) : []], outlines };
4135
4216
  }
4136
- function assertCodeEmbeddingConsent(root, options, chunks) {
4137
- if (!chunks.some((chunk) => chunk.kind === "symbol")) return;
4138
- const profile = options.profile ?? resolveEmbeddingProfile(root).profile;
4139
- if (profile.provider !== "local" && options.allowCodeEmbedding !== true) throw new Error("Remote code-metadata embedding requires --allow-code-embedding; use --search keyword or --scope docs instead.");
4140
- }
4141
4217
  function attachRelationships(results, outlines) {
4142
4218
  if (!outlines) return results;
4143
4219
  const graph = addOutlineGraph({ nodes: [], edges: [] }, outlines);
@@ -4152,7 +4228,7 @@ var DEFAULT_LIMIT = 30;
4152
4228
  var MAX_LIMIT = 100;
4153
4229
  var EMBED_BATCH_SIZE = 16;
4154
4230
  function parseQueryOptions(argv) {
4155
- const options = { root: ".", query: "", limit: DEFAULT_LIMIT, json: false, scope: "all", search: "hybrid", allowCodeEmbedding: false };
4231
+ const options = { root: ".", query: "", limit: DEFAULT_LIMIT, json: false, scope: "all", search: "hybrid" };
4156
4232
  let rootSet = false;
4157
4233
  const queryParts = [];
4158
4234
  for (let i = 0; i < argv.length; i += 1) {
@@ -4164,14 +4240,13 @@ function parseQueryOptions(argv) {
4164
4240
  } else if (arg === "--search") {
4165
4241
  options.search = argv[++i];
4166
4242
  if (!["hybrid", "keyword", "vector"].includes(options.search)) usage("--search must be hybrid, keyword or vector.", "query");
4167
- } else if (arg === "--allow-code-embedding") options.allowCodeEmbedding = true;
4168
- else if (arg === "--limit") {
4243
+ } else if (arg === "--limit") {
4169
4244
  const value = Number(argv[++i]);
4170
4245
  if (!Number.isInteger(value) || value < 1 || value > MAX_LIMIT) usage(`--limit must be an integer from 1 to ${MAX_LIMIT}.`, "query");
4171
4246
  options.limit = value;
4172
4247
  } else if (arg.startsWith("-")) usage(`Unknown option: ${arg}`, "query");
4173
4248
  else if (!rootSet) {
4174
- options.root = resolve10(arg);
4249
+ options.root = resolve11(arg);
4175
4250
  rootSet = true;
4176
4251
  } else queryParts.push(arg);
4177
4252
  }
@@ -4251,9 +4326,18 @@ async function queryProject(root, query, options = {}) {
4251
4326
  let vector = [];
4252
4327
  let index;
4253
4328
  let vectorError;
4254
- if (search !== "keyword" && corpus.chunks.length) {
4329
+ let configured = false;
4330
+ if (search !== "keyword") {
4331
+ try {
4332
+ configured = Boolean(options.profile || options.embed || resolveEmbeddingProfile(root, options).profile);
4333
+ } catch (error) {
4334
+ if (search === "vector") throw error;
4335
+ vectorError = error.message;
4336
+ }
4337
+ if (!configured && !vectorError && search === "vector") throw new Error("Embedding is not configured. Set embedding in project or global configuration to use vector search.");
4338
+ }
4339
+ if (search !== "keyword" && configured && corpus.chunks.length) {
4255
4340
  try {
4256
- assertCodeEmbeddingConsent(root, options, corpus.chunks);
4257
4341
  index = await buildVectorIndex(root, { ...options, chunks: corpus.chunks });
4258
4342
  const [queryVector] = await index.embedding.embed([query], "query");
4259
4343
  vector = index.chunks.map((chunk, position) => ({ ...chunk, vectorScore: cosineScore(queryVector, index.vectors, position * index.manifest.dimensions, index.manifest.dimensions) })).sort((a, b) => b.vectorScore - a.vectorScore || a.id.localeCompare(b.id));
@@ -4304,7 +4388,7 @@ async function runQuery(argv) {
4304
4388
  }
4305
4389
 
4306
4390
  // packages/cli/src/commands/embedding.mjs
4307
- import { resolve as resolve11 } from "node:path";
4391
+ import { resolve as resolve12 } from "node:path";
4308
4392
  function parse(argv) {
4309
4393
  const [action2 = "status", ...rest] = argv;
4310
4394
  let root = ".";
@@ -4316,12 +4400,18 @@ function parse(argv) {
4316
4400
  else if (!arg.startsWith("-") && root === ".") root = arg;
4317
4401
  else usage(`Unknown embedding option: ${arg}`, `embedding ${action2}`);
4318
4402
  }
4319
- return { action: action2, root: resolve11(root), json: json2, confirm };
4403
+ return { action: action2, root: resolve12(root), json: json2, confirm };
4320
4404
  }
4321
4405
  function runEmbedding(argv, options = {}) {
4322
4406
  const parsed = parse(argv);
4323
4407
  if (!["status", "install"].includes(parsed.action)) usage(`Unknown embedding command: ${parsed.action}`, "embedding");
4324
4408
  const resolved = resolveEmbeddingProfile(parsed.root, options);
4409
+ if (!resolved.profile) {
4410
+ if (parsed.action === "install") throw new Error("Configure an explicit local embedding profile before installing its runtime.");
4411
+ const result2 = { ok: true, command: "embedding status", provider: null, required: false, installed: false, installAvailable: false, source: "not-configured" };
4412
+ console.log(parsed.json ? JSON.stringify(result2, null, 2) : "Embedding is not configured; keyword search is used.");
4413
+ return result2;
4414
+ }
4325
4415
  if (resolved.profile.provider !== "local") {
4326
4416
  const result2 = { ok: true, command: `embedding ${parsed.action}`, provider: resolved.profile.provider, required: false, installed: false, installAvailable: false };
4327
4417
  console.log(parsed.json ? JSON.stringify(result2, null, 2) : "Local embedding runtime is not required for the configured remote provider.");
@@ -4336,11 +4426,11 @@ function runEmbedding(argv, options = {}) {
4336
4426
  }
4337
4427
 
4338
4428
  // packages/cli/src/commands/map.mjs
4339
- import { resolve as resolve13 } from "node:path";
4429
+ import { resolve as resolve14 } from "node:path";
4340
4430
 
4341
4431
  // packages/cli/src/memory/documentation-map.mjs
4342
- import { existsSync as existsSync17, readdirSync as readdirSync4, statSync as statSync7 } from "node:fs";
4343
- import { join as join17, resolve as resolve12 } from "node:path";
4432
+ import { existsSync as existsSync18, readdirSync as readdirSync4, statSync as statSync7 } from "node:fs";
4433
+ import { join as join17, resolve as resolve13 } from "node:path";
4344
4434
  var SKIPPED_DIRECTORIES2 = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", ".git", ".dotdotgod"]);
4345
4435
  function node() {
4346
4436
  return { directories: /* @__PURE__ */ new Map(), files: [] };
@@ -4420,8 +4510,8 @@ function discoverMarkdown(root, documentationRoot, exclude) {
4420
4510
  return paths.sort();
4421
4511
  }
4422
4512
  function buildDocumentationMap(projectRoot = ".", { depth = 5 } = {}) {
4423
- const root = resolve12(projectRoot);
4424
- if (!existsSync17(root)) return { ok: false, error: { code: "ROOT_NOT_FOUND", message: `Project root not found: ${root}` } };
4513
+ const root = resolve13(projectRoot);
4514
+ if (!existsSync18(root)) return { ok: false, error: { code: "ROOT_NOT_FOUND", message: `Project root not found: ${root}` } };
4425
4515
  try {
4426
4516
  if (!statSync7(root).isDirectory()) return { ok: false, error: { code: "ROOT_NOT_FOUND", message: `Project root not found: ${root}` } };
4427
4517
  } catch {
@@ -4469,7 +4559,7 @@ function parseMapOptions(argv) {
4469
4559
  } else if (!arg.startsWith("-")) fail(options.json, "UNEXPECTED_ARGUMENT", `Unexpected argument: ${arg}`);
4470
4560
  else fail(options.json, "UNKNOWN_OPTION", `Unknown option: ${arg}`);
4471
4561
  }
4472
- options.root = resolve13(options.root);
4562
+ options.root = resolve14(options.root);
4473
4563
  return options;
4474
4564
  }
4475
4565
  function runMap(argv) {
@@ -4481,7 +4571,7 @@ function runMap(argv) {
4481
4571
  }
4482
4572
 
4483
4573
  // packages/cli/src/reference/resolve.mjs
4484
- import { resolve as resolve16 } from "node:path";
4574
+ import { resolve as resolve17 } from "node:path";
4485
4575
 
4486
4576
  // node_modules/.pnpm/leiden-ts@0.1.0/node_modules/leiden-ts/dist/index.js
4487
4577
  var GraphValidationError = class extends Error {
@@ -5852,8 +5942,8 @@ function compareImpactItems(seeds) {
5852
5942
  }
5853
5943
 
5854
5944
  // packages/cli/src/impact/references.mjs
5855
- import { readFileSync as readFileSync16, statSync as statSync8 } from "node:fs";
5856
- import { extname as extname7, resolve as resolve14 } from "node:path";
5945
+ import { readFileSync as readFileSync17, statSync as statSync8 } from "node:fs";
5946
+ import { extname as extname7, resolve as resolve15 } from "node:path";
5857
5947
  var WARNING_LIMIT = 20;
5858
5948
  var edgeKey = (source, target, relation) => `${source}\0${target}\0${relation}`;
5859
5949
  function prepareImpactReferences(index, root, changedPaths) {
@@ -5865,10 +5955,10 @@ function prepareImpactReferences(index, root, changedPaths) {
5865
5955
  if (checks.has(path)) return checks.get(path);
5866
5956
  let result;
5867
5957
  try {
5868
- const absolute = resolve14(root, path);
5958
+ const absolute = resolve15(root, path);
5869
5959
  const stat = statSync8(absolute);
5870
5960
  result = { status: "exists" };
5871
- if (stat.isFile() && extname7(absolute) === ".md") result.anchors = extractAnchors(readFileSync16(absolute, "utf8"));
5961
+ if (stat.isFile() && extname7(absolute) === ".md") result.anchors = extractAnchors(readFileSync17(absolute, "utf8"));
5872
5962
  } catch (error) {
5873
5963
  result = { status: ["ENOENT", "ENOTDIR"].includes(error.code) ? "missing" : "unavailable" };
5874
5964
  }
@@ -5930,15 +6020,15 @@ function prepareImpactReferences(index, root, changedPaths) {
5930
6020
  }
5931
6021
 
5932
6022
  // packages/cli/src/impact/vector-profile.mjs
5933
- import { lstatSync as lstatSync3, readFileSync as readFileSync17, realpathSync as realpathSync2 } from "node:fs";
5934
- import { extname as extname8, relative as relative6, resolve as resolve15, sep } from "node:path";
6023
+ import { lstatSync as lstatSync4, readFileSync as readFileSync18, realpathSync as realpathSync3 } from "node:fs";
6024
+ import { extname as extname8, relative as relative7, resolve as resolve16, sep } from "node:path";
5935
6025
  var MAX_VECTOR_PROFILE_FILE_BYTES = 64 * 1024;
5936
6026
  var MAX_VECTOR_PROFILE_CHARS = 4e3;
5937
6027
  var MAX_VECTOR_PROFILE_METADATA_ITEMS = 20;
5938
6028
  var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([".c", ".cc", ".cpp", ".css", ".go", ".h", ".hpp", ".html", ".java", ".js", ".json", ".jsx", ".md", ".mjs", ".py", ".rb", ".rs", ".sh", ".ts", ".tsx", ".txt", ".yaml", ".yml"]);
5939
6029
  var GENERATED_SEGMENTS = /* @__PURE__ */ new Set([".dotdotgod", "build", "coverage", "dist", "node_modules"]);
5940
6030
  function insideRoot(root, absolute) {
5941
- const normalizedRoot = resolve15(root);
6031
+ const normalizedRoot = resolve16(root);
5942
6032
  return absolute === normalizedRoot || absolute.startsWith(`${normalizedRoot}${sep}`);
5943
6033
  }
5944
6034
  function normalizeChangedPath(path) {
@@ -5949,17 +6039,17 @@ function normalizeChangedPath(path) {
5949
6039
  function canonicalizeChangedPath(root, path) {
5950
6040
  const normalized = normalizeChangedPath(path);
5951
6041
  if (!normalized) return null;
5952
- const rootAbsolute = resolve15(root);
5953
- const absolute = resolve15(rootAbsolute, normalized);
6042
+ const rootAbsolute = resolve16(root);
6043
+ const absolute = resolve16(rootAbsolute, normalized);
5954
6044
  if (!insideRoot(rootAbsolute, absolute)) return normalized;
5955
6045
  try {
5956
- const canonicalRoot = realpathSync2(rootAbsolute);
5957
- const canonicalAbsolute = realpathSync2(absolute);
6046
+ const canonicalRoot = realpathSync3(rootAbsolute);
6047
+ const canonicalAbsolute = realpathSync3(absolute);
5958
6048
  if (!insideRoot(canonicalRoot, canonicalAbsolute)) return normalized;
5959
- const canonical = relative6(canonicalRoot, canonicalAbsolute).replaceAll("\\", "/");
6049
+ const canonical = relative7(canonicalRoot, canonicalAbsolute).replaceAll("\\", "/");
5960
6050
  return canonical || normalized;
5961
6051
  } catch {
5962
- return relative6(rootAbsolute, absolute).replaceAll("\\", "/") || normalized;
6052
+ return relative7(rootAbsolute, absolute).replaceAll("\\", "/") || normalized;
5963
6053
  }
5964
6054
  }
5965
6055
  function unsafeProfilePath(path) {
@@ -5981,15 +6071,15 @@ function graphMetadata(graph, seedId) {
5981
6071
  function safeTextPrefix(root, path) {
5982
6072
  if (unsafeProfilePath(path) || !TEXT_EXTENSIONS.has(extname8(path).toLowerCase())) return "";
5983
6073
  try {
5984
- const absolute = resolve15(root, path);
6074
+ const absolute = resolve16(root, path);
5985
6075
  if (!insideRoot(root, absolute)) return "";
5986
- const canonicalAbsolute = realpathSync2(absolute);
5987
- if (!insideRoot(realpathSync2(root), canonicalAbsolute)) return "";
5988
- const canonicalPath = relative6(realpathSync2(root), canonicalAbsolute).replaceAll("\\", "/");
6076
+ const canonicalAbsolute = realpathSync3(absolute);
6077
+ if (!insideRoot(realpathSync3(root), canonicalAbsolute)) return "";
6078
+ const canonicalPath = relative7(realpathSync3(root), canonicalAbsolute).replaceAll("\\", "/");
5989
6079
  if (canonicalPath !== path || unsafeProfilePath(canonicalPath) || !TEXT_EXTENSIONS.has(extname8(canonicalPath).toLowerCase())) return "";
5990
- const stats = lstatSync3(canonicalAbsolute);
6080
+ const stats = lstatSync4(canonicalAbsolute);
5991
6081
  if (!stats.isFile() || stats.isSymbolicLink() || stats.size > MAX_VECTOR_PROFILE_FILE_BYTES) return "";
5992
- const buffer = readFileSync17(canonicalAbsolute);
6082
+ const buffer = readFileSync18(canonicalAbsolute);
5993
6083
  if (buffer.includes(0)) return "";
5994
6084
  const text = new TextDecoder("utf-8", { fatal: true }).decode(buffer);
5995
6085
  return text.slice(0, MAX_VECTOR_PROFILE_CHARS);
@@ -6023,6 +6113,7 @@ async function buildVectorImpactOverlay(root, index, changedPaths, options = {})
6023
6113
  const threshold = semantic.threshold ?? 0.5;
6024
6114
  const topK = semantic.topKPerFile ?? 5;
6025
6115
  try {
6116
+ if (!options.profile && !options.embed && !options.buildIndex && !resolveEmbeddingProfile(root, options).profile) return { status: "disabled", edges: [], diagnostics: { enabled: false, reason: "Embedding is not configured." } };
6026
6117
  const vectorIndex = await (options.buildIndex ?? buildVectorIndex)(root, options);
6027
6118
  const edges = [];
6028
6119
  const normalizedChangedPaths = [...new Set(changedPaths.map((path) => canonicalizeChangedPath(root, path)).filter(Boolean))];
@@ -6209,7 +6300,7 @@ function buildCompactImpactReport(impact, limits = {}) {
6209
6300
  }
6210
6301
 
6211
6302
  // packages/cli/src/reference/extract.mjs
6212
- import { basename as basename8, dirname as dirname11, extname as extname9 } from "node:path";
6303
+ import { basename as basename8, dirname as dirname12, extname as extname9 } from "node:path";
6213
6304
  var DEFAULT_REFERENCE_LIMIT = 5;
6214
6305
  function extractBracketReferences(prompt = "") {
6215
6306
  const refs = [];
@@ -6260,7 +6351,7 @@ function aliasEntriesForPath(path = "") {
6260
6351
  const parts = withoutMd.split("/").filter(Boolean);
6261
6352
  for (let i = 0; i < parts.length; i += 1) entries.push({ alias: parts.slice(i).join("/"), kind: "path-suffix" });
6262
6353
  if (base === "README.md") {
6263
- const dir = dirname11(path).replace(/\\/g, "/");
6354
+ const dir = dirname12(path).replace(/\\/g, "/");
6264
6355
  entries.push({ alias: basename8(dir), kind: "path" });
6265
6356
  }
6266
6357
  for (const alias of [path, withoutMd, base, stem]) entries.push({ alias, kind: "path" });
@@ -6390,7 +6481,7 @@ function parseReferenceOptions(argv, command) {
6390
6481
  else filtered.push(arg);
6391
6482
  }
6392
6483
  const operands = filtered.filter((arg) => !arg.startsWith("-"));
6393
- return { ...options, root: resolve16(operands[0] ?? "."), json: filtered.includes("--json"), rootArgv: operands.slice(1) };
6484
+ return { ...options, root: resolve17(operands[0] ?? "."), json: filtered.includes("--json"), rootArgv: operands.slice(1) };
6394
6485
  }
6395
6486
  function formatReferenceOutput(payload) {
6396
6487
  const refreshNote = payload.metadata.cacheRefreshed ? ", refreshed" : "";
@@ -6439,13 +6530,13 @@ function runExpand(argv) {
6439
6530
  }
6440
6531
 
6441
6532
  // packages/cli/src/commands/traceability.mjs
6442
- import { existsSync as existsSync18, readFileSync as readFileSync18, readdirSync as readdirSync5, writeFileSync as writeFileSync7 } from "node:fs";
6443
- import { join as join18, resolve as resolve17 } from "node:path";
6533
+ import { existsSync as existsSync19, readFileSync as readFileSync19, readdirSync as readdirSync5, writeFileSync as writeFileSync8 } from "node:fs";
6534
+ import { join as join18, resolve as resolve18 } from "node:path";
6444
6535
  function collectDocsMarkdownFiles(root) {
6445
6536
  const docs = join18(root, "docs");
6446
6537
  const files = [];
6447
6538
  const walk = (dir) => {
6448
- if (!existsSync18(dir)) return;
6539
+ if (!existsSync19(dir)) return;
6449
6540
  for (const entry of readdirSync5(dir, { withFileTypes: true })) {
6450
6541
  const path = join18(dir, entry.name);
6451
6542
  if (entry.isDirectory()) walk(path);
@@ -6472,7 +6563,7 @@ function parseTraceabilityOptions(argv) {
6472
6563
  }
6473
6564
  if (options.check && options.write) usage("Choose only one traceability links mode: --check or --write.", "traceability links");
6474
6565
  options.check = options.check || !options.write;
6475
- options.root = resolve17(options.root);
6566
+ options.root = resolve18(options.root);
6476
6567
  return options;
6477
6568
  }
6478
6569
  function runTraceability(argv) {
@@ -6483,7 +6574,7 @@ function runTraceability(argv) {
6483
6574
  const errors = [...config.errors ?? []];
6484
6575
  for (const file of files) {
6485
6576
  if (options.write && errors.length > 0) break;
6486
- const content = readFileSync18(file, "utf8");
6577
+ const content = readFileSync19(file, "utf8");
6487
6578
  for (const error of validateTraceabilityLinksRegion(content, options.root, file)) errors.push(error);
6488
6579
  const blocks = extractDotdotgodTraceabilityBlocks(content).filter((block) => !block.error);
6489
6580
  if (blocks.length === 0) continue;
@@ -6492,7 +6583,7 @@ function runTraceability(argv) {
6492
6583
  for (const error of result.errors ?? []) errors.push(error);
6493
6584
  continue;
6494
6585
  }
6495
- if (options.write && result.changed) writeFileSync7(file, result.content);
6586
+ if (options.write && result.changed) writeFileSync8(file, result.content);
6496
6587
  if (result.changed) results.push({ file: rel(options.root, file), changed: true });
6497
6588
  }
6498
6589
  const ok = errors.length === 0 && (!options.check || results.length === 0);
@@ -6513,8 +6604,8 @@ function runTraceability(argv) {
6513
6604
 
6514
6605
  // packages/cli/src/graph-view/server.mjs
6515
6606
  import { createServer } from "node:http";
6516
- import { readFileSync as readFileSync19 } from "node:fs";
6517
- import { dirname as dirname12, extname as extname10, join as join19 } from "node:path";
6607
+ import { readFileSync as readFileSync20 } from "node:fs";
6608
+ import { dirname as dirname13, extname as extname10, join as join19 } from "node:path";
6518
6609
  import { fileURLToPath as fileURLToPath2 } from "node:url";
6519
6610
 
6520
6611
  // packages/cli/src/graph-view/payload.mjs
@@ -6626,7 +6717,7 @@ function buildImpactGraphPayload(index, impact) {
6626
6717
  }
6627
6718
 
6628
6719
  // packages/cli/src/graph-view/server.mjs
6629
- var ASSET_ROOT = join19(dirname12(fileURLToPath2(import.meta.url)), "assets");
6720
+ var ASSET_ROOT = join19(dirname13(fileURLToPath2(import.meta.url)), "assets");
6630
6721
  var CONTENT_TYPES = { ".css": "text/css; charset=utf-8", ".html": "text/html; charset=utf-8", ".js": "text/javascript; charset=utf-8", ".map": "application/json; charset=utf-8" };
6631
6722
  var VENDOR_FILES = {
6632
6723
  "/vendor/sigma.js": new URL("../../node_modules/sigma/dist/sigma.min.js", import.meta.url),
@@ -6639,7 +6730,7 @@ function json(response, status, value) {
6639
6730
  }
6640
6731
  function asset(response, file) {
6641
6732
  try {
6642
- const body = readFileSync19(file);
6733
+ const body = readFileSync20(file);
6643
6734
  response.writeHead(200, { "content-type": CONTENT_TYPES[extname10(file)] ?? "application/octet-stream", "cache-control": "no-store" });
6644
6735
  response.end(body);
6645
6736
  } catch {
@@ -6676,16 +6767,16 @@ async function startImpactGraphServer({ root, changed, host = "127.0.0.1", port
6676
6767
  }
6677
6768
  }
6678
6769
  if (VENDOR_FILES[url.pathname]) return asset(response, fileURLToPath2(VENDOR_FILES[url.pathname]));
6679
- const relative7 = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
6680
- if (!["index.html", "app.css", "app.js"].includes(relative7)) {
6770
+ const relative8 = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
6771
+ if (!["index.html", "app.css", "app.js"].includes(relative8)) {
6681
6772
  response.writeHead(404);
6682
6773
  return response.end("Not found");
6683
6774
  }
6684
- return asset(response, join19(ASSET_ROOT, relative7));
6775
+ return asset(response, join19(ASSET_ROOT, relative8));
6685
6776
  });
6686
- await new Promise((resolve18, reject) => {
6777
+ await new Promise((resolve19, reject) => {
6687
6778
  server.once("error", reject);
6688
- server.listen(port, host, resolve18);
6779
+ server.listen(port, host, resolve19);
6689
6780
  });
6690
6781
  const address = server.address();
6691
6782
  const actualPort = typeof address === "object" && address ? address.port : port;