@unerr-ai/unerr 0.0.0-beta.7 → 0.0.0-beta.9

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/dist/cli.js CHANGED
@@ -750,7 +750,7 @@ async function getParser(language) {
750
750
  const langFile = `tree-sitter-${language}.wasm`;
751
751
  try {
752
752
  const { join: join68 } = await import("path");
753
- const { existsSync: existsSync63 } = await import("fs");
753
+ const { existsSync: existsSync64 } = await import("fs");
754
754
  const possiblePaths = [
755
755
  join68(
756
756
  process.cwd(),
@@ -762,7 +762,7 @@ async function getParser(language) {
762
762
  ];
763
763
  let wasmPath = null;
764
764
  for (const p of possiblePaths) {
765
- if (existsSync63(p)) {
765
+ if (existsSync64(p)) {
766
766
  wasmPath = p;
767
767
  break;
768
768
  }
@@ -1656,8 +1656,21 @@ async function createIfMissing(db, existing, name, schema) {
1656
1656
  if (existing.has(name)) return;
1657
1657
  await db.run(schema);
1658
1658
  }
1659
+ async function dropIfStale(db, existing, name, requiredColumns) {
1660
+ if (!existing.has(name)) return;
1661
+ try {
1662
+ const colList = ["key", ...requiredColumns].join(", ");
1663
+ await db.run(
1664
+ `?[] := *${name}{${colList}}, key = '__schema_probe__'`
1665
+ );
1666
+ } catch {
1667
+ await db.run(`::remove ${name}`);
1668
+ existing.delete(name);
1669
+ }
1670
+ }
1659
1671
  async function initSchema(db) {
1660
1672
  const existing = await getExistingRelations(db);
1673
+ await dropIfStale(db, existing, "entities", ["end_line", "is_test"]);
1661
1674
  await createIfMissing(
1662
1675
  db,
1663
1676
  existing,
@@ -1681,32 +1694,6 @@ async function initSchema(db) {
1681
1694
  }
1682
1695
  `
1683
1696
  );
1684
- if (existing.has("entities")) {
1685
- try {
1686
- await db.run(
1687
- `?[key, kind, name, file_path, start_line, end_line, signature, body, fan_in, fan_out, risk_level, community, is_test] :=
1688
- *entities{key, kind, name, file_path, start_line, signature, body, fan_in, fan_out, risk_level, community, is_test},
1689
- end_line = 0
1690
- :replace entities {
1691
- key: String
1692
- =>
1693
- kind: String,
1694
- name: String,
1695
- file_path: String,
1696
- start_line: Int default 0,
1697
- end_line: Int default 0,
1698
- signature: String default "",
1699
- body: String default "",
1700
- fan_in: Int default 0,
1701
- fan_out: Int default 0,
1702
- risk_level: String default "normal",
1703
- community: Int default -1,
1704
- is_test: Bool default false
1705
- }`
1706
- );
1707
- } catch {
1708
- }
1709
- }
1710
1697
  await createIfMissing(
1711
1698
  db,
1712
1699
  existing,
@@ -7240,9 +7227,9 @@ function extractTSEntities(node, lines, entities) {
7240
7227
  const nameNode = child.childForFieldName("name");
7241
7228
  const valueNode = child.childForFieldName("value");
7242
7229
  if (!nameNode || !valueNode) continue;
7230
+ const startLine = n.startPosition.row + 1;
7231
+ const endLine = n.endPosition.row + 1;
7243
7232
  if (valueNode.type === "arrow_function" || valueNode.type === "function_expression" || valueNode.type === "function") {
7244
- const startLine = n.startPosition.row + 1;
7245
- const endLine = n.endPosition.row + 1;
7246
7233
  entities.push({
7247
7234
  name: nameNode.text,
7248
7235
  kind: "function",
@@ -7251,11 +7238,53 @@ function extractTSEntities(node, lines, entities) {
7251
7238
  line_end: endLine,
7252
7239
  content_hash: hashLines(lines, startLine, endLine)
7253
7240
  });
7241
+ } else if (valueNode.type === "object" || valueNode.type === "array" || valueNode.type === "as_expression" || valueNode.type === "satisfies_expression" || valueNode.type === "new_expression" || valueNode.type === "call_expression" || valueNode.type === "template_string" || valueNode.type === "string" || valueNode.type === "number" || valueNode.type === "true" || valueNode.type === "false" || valueNode.type === "regex") {
7242
+ const isTopLevel = !n.parent || n.parent.type === "program" || n.parent.type === "export_statement";
7243
+ if (isTopLevel) {
7244
+ entities.push({
7245
+ name: nameNode.text,
7246
+ kind: "variable",
7247
+ signature: "",
7248
+ line_start: startLine,
7249
+ line_end: endLine,
7250
+ content_hash: hashLines(lines, startLine, endLine)
7251
+ });
7252
+ }
7254
7253
  }
7255
7254
  }
7256
7255
  }
7257
7256
  break;
7258
7257
  }
7258
+ case "type_alias_declaration": {
7259
+ const name = n.childForFieldName("name");
7260
+ if (!name) return;
7261
+ const startLine = n.startPosition.row + 1;
7262
+ const endLine = n.endPosition.row + 1;
7263
+ entities.push({
7264
+ name: name.text,
7265
+ kind: "interface",
7266
+ signature: "",
7267
+ line_start: startLine,
7268
+ line_end: endLine,
7269
+ content_hash: hashLines(lines, startLine, endLine)
7270
+ });
7271
+ break;
7272
+ }
7273
+ case "enum_declaration": {
7274
+ const name = n.childForFieldName("name");
7275
+ if (!name) return;
7276
+ const startLine = n.startPosition.row + 1;
7277
+ const endLine = n.endPosition.row + 1;
7278
+ entities.push({
7279
+ name: name.text,
7280
+ kind: "class",
7281
+ signature: "",
7282
+ line_start: startLine,
7283
+ line_end: endLine,
7284
+ content_hash: hashLines(lines, startLine, endLine)
7285
+ });
7286
+ break;
7287
+ }
7259
7288
  }
7260
7289
  });
7261
7290
  }
@@ -9383,6 +9412,42 @@ var init_downloader = __esm({
9383
9412
  archiveType: "gz",
9384
9413
  archiveBinaryPath: null,
9385
9414
  manualInstall: "rustup component add rust-analyzer"
9415
+ },
9416
+ ruby: {
9417
+ language: "ruby",
9418
+ binaryName: "scip-ruby",
9419
+ repo: "sourcegraph/scip-ruby",
9420
+ tag: "latest",
9421
+ // Verified assets: scip-ruby-arm64-darwin, scip-ruby-x86_64-linux (raw binaries)
9422
+ // No darwin-x86_64 or linux-arm64 builds available
9423
+ resolveAssetName: (p) => {
9424
+ if (p.os === "windows") return null;
9425
+ if (p.os === "darwin" && p.nodeArch !== "arm64") return null;
9426
+ if (p.os === "linux" && p.nodeArch !== "x64") return null;
9427
+ const arch2 = p.os === "darwin" ? "arm64" : "x86_64";
9428
+ return `scip-ruby-${arch2}-${p.os}`;
9429
+ },
9430
+ archiveType: "raw",
9431
+ archiveBinaryPath: null,
9432
+ manualInstall: "Download from https://github.com/sourcegraph/scip-ruby/releases"
9433
+ },
9434
+ cpp: {
9435
+ language: "cpp",
9436
+ binaryName: "scip-clang",
9437
+ repo: "sourcegraph/scip-clang",
9438
+ tag: "latest",
9439
+ // Verified assets: scip-clang-arm64-darwin, scip-clang-x86_64-linux (raw binaries)
9440
+ // No darwin-x86_64 or linux-arm64 builds available
9441
+ resolveAssetName: (p) => {
9442
+ if (p.os === "windows") return null;
9443
+ if (p.os === "darwin" && p.nodeArch !== "arm64") return null;
9444
+ if (p.os === "linux" && p.nodeArch !== "x64") return null;
9445
+ const arch2 = p.os === "darwin" ? "arm64" : "x86_64";
9446
+ return `scip-clang-${arch2}-${p.os}`;
9447
+ },
9448
+ archiveType: "raw",
9449
+ archiveBinaryPath: null,
9450
+ manualInstall: "Download from https://github.com/sourcegraph/scip-clang/releases (requires compile_commands.json)"
9386
9451
  }
9387
9452
  };
9388
9453
  }
@@ -9464,7 +9529,7 @@ async function detectScipBinary(language) {
9464
9529
  path: null
9465
9530
  };
9466
9531
  }
9467
- function detectPrimaryLanguage(files) {
9532
+ function detectProjectLanguages(files) {
9468
9533
  const counts = {};
9469
9534
  for (const file of files) {
9470
9535
  const dotIdx = file.lastIndexOf(".");
@@ -9475,15 +9540,7 @@ function detectPrimaryLanguage(files) {
9475
9540
  counts[lang] = (counts[lang] ?? 0) + 1;
9476
9541
  }
9477
9542
  }
9478
- let maxLang = null;
9479
- let maxCount = 0;
9480
- for (const [lang, count] of Object.entries(counts)) {
9481
- if (count > maxCount) {
9482
- maxCount = count;
9483
- maxLang = lang;
9484
- }
9485
- }
9486
- return maxLang;
9543
+ return Object.entries(counts).map(([language, fileCount]) => ({ language, fileCount })).sort((a, b) => b.fileCount - a.fileCount);
9487
9544
  }
9488
9545
  var BUNDLED_SCIP, EXTERNAL_SCIP, EXT_TO_SCIP_LANG;
9489
9546
  var init_detector = __esm({
@@ -9504,7 +9561,10 @@ var init_detector = __esm({
9504
9561
  EXTERNAL_SCIP = {
9505
9562
  go: ["scip-go"],
9506
9563
  java: ["scip-java"],
9507
- rust: ["rust-analyzer"]
9564
+ rust: ["rust-analyzer"],
9565
+ ruby: ["scip-ruby"],
9566
+ cpp: ["scip-clang"],
9567
+ csharp: ["scip-dotnet"]
9508
9568
  };
9509
9569
  EXT_TO_SCIP_LANG = {
9510
9570
  // TypeScript / JavaScript (scip-typescript handles all)
@@ -9529,7 +9589,19 @@ var init_detector = __esm({
9529
9589
  ".scala": "java",
9530
9590
  ".sc": "java",
9531
9591
  // Rust
9532
- ".rs": "rust"
9592
+ ".rs": "rust",
9593
+ // Ruby
9594
+ ".rb": "ruby",
9595
+ // C / C++ (scip-clang handles both)
9596
+ ".c": "cpp",
9597
+ ".cpp": "cpp",
9598
+ ".cc": "cpp",
9599
+ ".cxx": "cpp",
9600
+ ".h": "cpp",
9601
+ ".hpp": "cpp",
9602
+ ".hxx": "cpp",
9603
+ // C#
9604
+ ".cs": "csharp"
9533
9605
  };
9534
9606
  }
9535
9607
  });
@@ -9636,6 +9708,9 @@ async function runScipIndexer(options) {
9636
9708
  }
9637
9709
  const outputPath = join22(outputDir, "index.scip");
9638
9710
  const args = buildScipArgs(language, binaryPath, projectRoot, outputPath);
9711
+ if (options.extraArgs?.length) {
9712
+ args.push(...options.extraArgs);
9713
+ }
9639
9714
  try {
9640
9715
  log4.info(`Running SCIP indexer: ${args[0]} for ${language}`);
9641
9716
  const result = await exec(args[0], args.slice(1), {
@@ -9644,6 +9719,12 @@ async function runScipIndexer(options) {
9644
9719
  });
9645
9720
  const durationMs = performance.now() - start;
9646
9721
  if (result.exitCode !== 0) {
9722
+ if (existsSync17(outputPath)) {
9723
+ log4.warn(
9724
+ `SCIP indexer exited with code ${result.exitCode} but output file was created \u2014 treating as success`
9725
+ );
9726
+ return { success: true, outputPath, durationMs, error: null };
9727
+ }
9647
9728
  log4.warn(
9648
9729
  `SCIP indexer failed (exit ${result.exitCode}): ${result.stderr.slice(0, 500)}`
9649
9730
  );
@@ -9690,6 +9771,12 @@ function buildScipArgs(language, binaryPath, projectRoot, outputPath) {
9690
9771
  return [binaryPath, "index", "--output", outputPath];
9691
9772
  case "rust":
9692
9773
  return [binaryPath, "scip", projectRoot, "--output", outputPath];
9774
+ case "ruby":
9775
+ return [binaryPath, "--output", outputPath];
9776
+ case "cpp":
9777
+ return [binaryPath, "--output", outputPath];
9778
+ case "csharp":
9779
+ return [binaryPath, "index", "--output", outputPath];
9693
9780
  default:
9694
9781
  return [binaryPath, "--output", outputPath];
9695
9782
  }
@@ -9705,83 +9792,266 @@ var init_runner = __esm({
9705
9792
  });
9706
9793
 
9707
9794
  // src/intelligence/indexer/scip/orchestrator.ts
9795
+ import { existsSync as existsSync18, readFileSync as readFileSync18, writeFileSync as writeFileSync9 } from "fs";
9708
9796
  import { join as join23 } from "path";
9709
- async function enrichWithScip(files, projectRoot, existingEdges, entities) {
9710
- const language = detectPrimaryLanguage(files);
9711
- if (!language) {
9797
+ async function enrichWithScip(files, projectRoot, existingEdges, entities, options) {
9798
+ const languages = detectProjectLanguages(files);
9799
+ if (languages.length === 0) {
9712
9800
  return skipResult(existingEdges, "No supported language detected");
9713
9801
  }
9714
- let binaryInfo = await detectScipBinary(language);
9715
- if (!binaryInfo.available && isAutoDownloadSupported(language)) {
9716
- log5.info(`SCIP binary not found for ${language}, downloading...`);
9717
- const downloadResult = await downloadScipBinary(language, (msg) => {
9718
- log5.info(msg);
9719
- });
9720
- if (downloadResult.success && downloadResult.binaryPath) {
9721
- binaryInfo = await detectScipBinary(language);
9722
- } else {
9723
- const manualInstr = getManualInstallInstructions(language);
9724
- return skipResult(
9725
- existingEdges,
9726
- `SCIP auto-download failed for ${language}: ${downloadResult.error}${manualInstr ? `
9802
+ let currentEdges = markAsStructural(existingEdges);
9803
+ let lastRunResult = null;
9804
+ let lastDecodeResult = null;
9805
+ let lastMergeResult = null;
9806
+ let anySucceeded = false;
9807
+ const processedLanguages = [];
9808
+ for (const { language, fileCount } of languages) {
9809
+ log5.info(`SCIP: processing ${language} (${fileCount} files)`);
9810
+ let binaryInfo = await detectScipBinary(language);
9811
+ if (!binaryInfo.available) {
9812
+ if (language === "csharp") {
9813
+ const installed = await installScipDotnet();
9814
+ if (installed) {
9815
+ binaryInfo = await detectScipBinary(language);
9816
+ } else {
9817
+ continue;
9818
+ }
9819
+ } else if (isAutoDownloadSupported(language)) {
9820
+ log5.info(`SCIP binary not found for ${language}, downloading...`);
9821
+ const downloadResult = await downloadScipBinary(language, (msg) => {
9822
+ log5.info(msg);
9823
+ });
9824
+ if (downloadResult.success && downloadResult.binaryPath) {
9825
+ binaryInfo = await detectScipBinary(language);
9826
+ } else {
9827
+ const manualInstr = getManualInstallInstructions(language);
9828
+ log5.warn(
9829
+ `SCIP auto-download failed for ${language}: ${downloadResult.error}${manualInstr ? `
9727
9830
  Manual install: ${manualInstr}` : ""}`
9831
+ );
9832
+ continue;
9833
+ }
9834
+ }
9835
+ }
9836
+ if (!binaryInfo.available) {
9837
+ log5.info(
9838
+ `SCIP binary not available for ${language}, skipping`
9728
9839
  );
9840
+ continue;
9841
+ }
9842
+ log5.info(
9843
+ `SCIP enrichment: ${language} (${binaryInfo.bundled ? "bundled" : "external"}: ${binaryInfo.binaryName})`
9844
+ );
9845
+ const outputDir = join23(projectRoot, ".unerr", "scip");
9846
+ const binaryPath = binaryInfo.path ?? binaryInfo.binaryName;
9847
+ let extraArgs;
9848
+ if (language === "typescript") {
9849
+ if (!existsSync18(join23(projectRoot, "tsconfig.json")) && !existsSync18(join23(projectRoot, "jsconfig.json"))) {
9850
+ log5.info(
9851
+ `SCIP skipping TypeScript: no tsconfig.json or jsconfig.json found in project root. Create one to enable SCIP indexing.`
9852
+ );
9853
+ continue;
9854
+ }
9729
9855
  }
9856
+ if (language === "java") {
9857
+ const buildToolResult = await resolveJavaBuildTool(projectRoot, options);
9858
+ if (buildToolResult) {
9859
+ extraArgs = buildToolResult.extraArgs;
9860
+ }
9861
+ }
9862
+ if (language === "cpp") {
9863
+ const compdbPath = resolveCompileCommandsJson(projectRoot);
9864
+ if (!compdbPath) {
9865
+ log5.info(
9866
+ `SCIP skipping C/C++: no compile_commands.json found. Generate one with CMake (-DCMAKE_EXPORT_COMPILE_COMMANDS=ON), Bear, or your build system.`
9867
+ );
9868
+ continue;
9869
+ }
9870
+ extraArgs = [`--compdb-path=${compdbPath}`];
9871
+ }
9872
+ const runResult = await runScipIndexer({
9873
+ language,
9874
+ binaryPath,
9875
+ projectRoot,
9876
+ outputDir,
9877
+ timeoutMs: 3e4,
9878
+ extraArgs
9879
+ });
9880
+ lastRunResult = runResult;
9881
+ if (!runResult.success || !runResult.outputPath) {
9882
+ log5.warn(`SCIP indexer failed for ${language}: ${runResult.error}`);
9883
+ continue;
9884
+ }
9885
+ const decodeResult = await decodeScipOutput(runResult.outputPath);
9886
+ lastDecodeResult = decodeResult;
9887
+ const indexedEdges = currentEdges.map((e) => ({
9888
+ from_key: e.from_key,
9889
+ to_key: e.to_key,
9890
+ type: e.type,
9891
+ file_path: e.file_path,
9892
+ line: e.line
9893
+ }));
9894
+ const { edges: enrichedEdges, result: mergeResult } = mergeScipResults(
9895
+ indexedEdges,
9896
+ decodeResult,
9897
+ entities
9898
+ );
9899
+ currentEdges = enrichedEdges;
9900
+ lastMergeResult = mergeResult;
9901
+ anySucceeded = true;
9902
+ processedLanguages.push(language);
9903
+ log5.info(
9904
+ `SCIP ${language}: ${mergeResult.edgesUpgraded} edges upgraded to compiler-verified (${Math.round(runResult.durationMs)}ms)`
9905
+ );
9730
9906
  }
9731
- if (!binaryInfo.available) {
9907
+ if (!anySucceeded) {
9732
9908
  return skipResult(
9733
9909
  existingEdges,
9734
- `SCIP binary not available for ${language} (not bundled, not on PATH, no auto-download)`
9910
+ `No SCIP binary available for any detected language (${languages.map((l) => l.language).join(", ")})`
9735
9911
  );
9736
9912
  }
9737
- log5.info(
9738
- `SCIP enrichment: ${language} (${binaryInfo.bundled ? "bundled" : "external"}: ${binaryInfo.binaryName})`
9739
- );
9740
- const outputDir = join23(projectRoot, ".unerr", "scip");
9741
- const binaryPath = binaryInfo.path ?? binaryInfo.binaryName;
9742
- const runResult = await runScipIndexer({
9743
- language,
9744
- binaryPath,
9745
- projectRoot,
9746
- outputDir,
9747
- timeoutMs: 3e4
9748
- // 30s max for inline indexing
9749
- });
9750
- if (!runResult.success || !runResult.outputPath) {
9751
- log5.warn(`SCIP indexer failed for ${language}: ${runResult.error}`);
9752
- return {
9753
- language,
9754
- binaryAvailable: true,
9755
- bundled: binaryInfo.bundled,
9756
- runResult,
9757
- decodeResult: null,
9758
- mergeResult: null,
9759
- enrichedEdges: markAsStructural(existingEdges),
9760
- skipped: false,
9761
- skipReason: null
9762
- };
9763
- }
9764
- const decodeResult = await decodeScipOutput(runResult.outputPath);
9765
- const { edges: enrichedEdges, result: mergeResult } = mergeScipResults(
9766
- existingEdges,
9767
- decodeResult,
9768
- entities
9769
- );
9770
- log5.info(
9771
- `SCIP enrichment complete: ${mergeResult.edgesUpgraded} edges upgraded to compiler-verified (${Math.round(runResult.durationMs)}ms)`
9772
- );
9773
9913
  return {
9774
- language,
9914
+ language: processedLanguages.join("+"),
9775
9915
  binaryAvailable: true,
9776
- bundled: binaryInfo.bundled,
9777
- runResult,
9778
- decodeResult,
9779
- mergeResult,
9780
- enrichedEdges,
9916
+ bundled: false,
9917
+ runResult: lastRunResult,
9918
+ decodeResult: lastDecodeResult,
9919
+ mergeResult: lastMergeResult,
9920
+ enrichedEdges: currentEdges,
9781
9921
  skipped: false,
9782
9922
  skipReason: null
9783
9923
  };
9784
9924
  }
9925
+ function detectJavaBuildTools(projectRoot) {
9926
+ const detected = [];
9927
+ for (const [tool, files] of Object.entries(BUILD_TOOL_FILES)) {
9928
+ if (files.some((f) => existsSync18(join23(projectRoot, f)))) {
9929
+ detected.push(tool);
9930
+ }
9931
+ }
9932
+ return detected;
9933
+ }
9934
+ function getStoredBuildTool(projectRoot) {
9935
+ try {
9936
+ const configPath = join23(projectRoot, ".unerr", "config.json");
9937
+ const config = JSON.parse(readFileSync18(configPath, "utf-8"));
9938
+ return config.javaBuildTool ?? null;
9939
+ } catch {
9940
+ return null;
9941
+ }
9942
+ }
9943
+ function storeBuildTool(projectRoot, tool) {
9944
+ try {
9945
+ const configPath = join23(projectRoot, ".unerr", "config.json");
9946
+ let config = {};
9947
+ try {
9948
+ config = JSON.parse(readFileSync18(configPath, "utf-8"));
9949
+ } catch {
9950
+ }
9951
+ config.javaBuildTool = tool;
9952
+ writeFileSync9(configPath, `${JSON.stringify(config, null, 2)}
9953
+ `);
9954
+ } catch {
9955
+ }
9956
+ }
9957
+ async function promptBuildTool(tools, options) {
9958
+ if (!process.stdin.isTTY) {
9959
+ return null;
9960
+ }
9961
+ try {
9962
+ options?.onPromptStart?.();
9963
+ const clack2 = await import("@clack/prompts");
9964
+ const result = await clack2.select({
9965
+ message: "Multiple Java build tools detected. Which one should SCIP use?",
9966
+ options: tools.map((t) => ({
9967
+ value: t,
9968
+ label: t.charAt(0).toUpperCase() + t.slice(1)
9969
+ }))
9970
+ });
9971
+ options?.onPromptEnd?.();
9972
+ if (clack2.isCancel(result)) return null;
9973
+ return result;
9974
+ } catch {
9975
+ options?.onPromptEnd?.();
9976
+ return null;
9977
+ }
9978
+ }
9979
+ async function resolveJavaBuildTool(projectRoot, options) {
9980
+ const detected = detectJavaBuildTools(projectRoot);
9981
+ if (detected.length === 0) {
9982
+ return null;
9983
+ }
9984
+ if (detected.length === 1) {
9985
+ log5.info(`Java build tool: ${detected[0]} (auto-detected)`);
9986
+ return { tool: detected[0], extraArgs: [`--build-tool=${detected[0]}`] };
9987
+ }
9988
+ const stored = getStoredBuildTool(projectRoot);
9989
+ if (stored && detected.includes(stored)) {
9990
+ log5.info(`Java build tool: ${stored} (from config)`);
9991
+ return { tool: stored, extraArgs: [`--build-tool=${stored}`] };
9992
+ }
9993
+ const chosen = await promptBuildTool(detected, options);
9994
+ if (chosen) {
9995
+ storeBuildTool(projectRoot, chosen);
9996
+ log5.info(`Java build tool: ${chosen} (user selected, saved to config)`);
9997
+ return { tool: chosen, extraArgs: [`--build-tool=${chosen}`] };
9998
+ }
9999
+ const fallback = detected[0];
10000
+ log5.warn(
10001
+ `Multiple Java build tools detected (${detected.join(", ")}), using ${fallback}. Set "javaBuildTool" in .unerr/config.json to change.`
10002
+ );
10003
+ return { tool: fallback, extraArgs: [`--build-tool=${fallback}`] };
10004
+ }
10005
+ function resolveCompileCommandsJson(projectRoot) {
10006
+ for (const relPath of COMPDB_SEARCH_PATHS) {
10007
+ const absPath = join23(projectRoot, relPath);
10008
+ if (existsSync18(absPath)) {
10009
+ log5.info(`Found compile_commands.json at ${relPath}`);
10010
+ return absPath;
10011
+ }
10012
+ }
10013
+ return null;
10014
+ }
10015
+ async function installScipDotnet() {
10016
+ try {
10017
+ const dotnetCheck = await exec("which", ["dotnet"]);
10018
+ if (dotnetCheck.exitCode !== 0) {
10019
+ log5.warn(
10020
+ `SCIP skipping C#: dotnet CLI not found on PATH. Install the .NET SDK to enable SCIP indexing for C#.`
10021
+ );
10022
+ return false;
10023
+ }
10024
+ } catch {
10025
+ log5.warn(
10026
+ `SCIP skipping C#: dotnet CLI not found on PATH. Install the .NET SDK to enable SCIP indexing for C#.`
10027
+ );
10028
+ return false;
10029
+ }
10030
+ log5.info("Installing scip-dotnet via dotnet tool install...");
10031
+ try {
10032
+ const result = await exec("dotnet", [
10033
+ "tool",
10034
+ "install",
10035
+ "--global",
10036
+ "scip-dotnet"
10037
+ ]);
10038
+ if (result.exitCode === 0) {
10039
+ log5.info("scip-dotnet installed successfully");
10040
+ return true;
10041
+ }
10042
+ if (result.stderr.includes("already installed")) {
10043
+ log5.info("scip-dotnet already installed");
10044
+ return true;
10045
+ }
10046
+ log5.warn(`scip-dotnet install failed: ${result.stderr.slice(0, 300)}`);
10047
+ return false;
10048
+ } catch (err) {
10049
+ log5.warn(
10050
+ `scip-dotnet install failed: ${err instanceof Error ? err.message : String(err)}`
10051
+ );
10052
+ return false;
10053
+ }
10054
+ }
9785
10055
  function skipResult(existingEdges, reason) {
9786
10056
  log5.info(`SCIP skipped: ${reason}`);
9787
10057
  return {
@@ -9803,10 +10073,11 @@ function markAsStructural(edges) {
9803
10073
  scipVerified: false
9804
10074
  }));
9805
10075
  }
9806
- var log5;
10076
+ var log5, BUILD_TOOL_FILES, COMPDB_SEARCH_PATHS;
9807
10077
  var init_orchestrator = __esm({
9808
10078
  "src/intelligence/indexer/scip/orchestrator.ts"() {
9809
10079
  "use strict";
10080
+ init_exec();
9810
10081
  init_logger();
9811
10082
  init_decoder();
9812
10083
  init_detector();
@@ -9814,6 +10085,19 @@ var init_orchestrator = __esm({
9814
10085
  init_merger();
9815
10086
  init_runner();
9816
10087
  log5 = createModuleLogger("scip-orchestrator");
10088
+ BUILD_TOOL_FILES = {
10089
+ Maven: ["pom.xml"],
10090
+ Gradle: ["build.gradle", "build.gradle.kts", "settings.gradle", "settings.gradle.kts"],
10091
+ Bazel: ["BUILD", "BUILD.bazel", "WORKSPACE", "WORKSPACE.bazel"],
10092
+ Sbt: ["build.sbt"]
10093
+ };
10094
+ COMPDB_SEARCH_PATHS = [
10095
+ "compile_commands.json",
10096
+ "build/compile_commands.json",
10097
+ "cmake-build-debug/compile_commands.json",
10098
+ "cmake-build-release/compile_commands.json",
10099
+ "out/compile_commands.json"
10100
+ ];
9817
10101
  }
9818
10102
  });
9819
10103
 
@@ -10333,7 +10617,7 @@ __export(local_indexer_exports, {
10333
10617
  runCommunityDetection: () => runCommunityDetection,
10334
10618
  runConventionDetection: () => runConventionDetection
10335
10619
  });
10336
- import { readFileSync as readFileSync18, readdirSync as readdirSync6, statSync as statSync6 } from "fs";
10620
+ import { readFileSync as readFileSync19, readdirSync as readdirSync6, statSync as statSync6 } from "fs";
10337
10621
  import { basename as basename2, extname as extname2, join as join24, relative } from "path";
10338
10622
  async function indexLocalProject(projectRoot, graphStore, repoId, opts) {
10339
10623
  const startTime = Date.now();
@@ -10361,7 +10645,7 @@ async function indexLocalProject(projectRoot, graphStore, repoId, opts) {
10361
10645
  });
10362
10646
  let content;
10363
10647
  try {
10364
- content = readFileSync18(absPath, "utf-8");
10648
+ content = readFileSync19(absPath, "utf-8");
10365
10649
  } catch {
10366
10650
  filesProcessed++;
10367
10651
  continue;
@@ -10472,10 +10756,18 @@ async function indexLocalProject(projectRoot, graphStore, repoId, opts) {
10472
10756
  ...coChangeCompactEdges
10473
10757
  ];
10474
10758
  await populateCozoDB(graphStore, allEntities, allEdges);
10759
+ progress?.({
10760
+ processed: filesProcessed,
10761
+ total: files.length,
10762
+ phase: "documents",
10763
+ currentFile: null
10764
+ });
10765
+ const docKeys = await indexDocumentFiles(projectRoot, graphStore);
10475
10766
  for (const e of allEntities) indexedEntityKeys.add(e.key);
10476
10767
  for (const e of allEntities) {
10477
10768
  if (e.file_path) indexedEntityKeys.add(`file:${e.file_path}`);
10478
10769
  }
10770
+ for (const dk of docKeys) indexedEntityKeys.add(dk);
10479
10771
  await removeOrphanedEntities(graphStore, indexedEntityKeys);
10480
10772
  await materializeL1Edges(graphStore);
10481
10773
  progress?.({
@@ -10511,7 +10803,7 @@ async function indexLocalProject(projectRoot, graphStore, repoId, opts) {
10511
10803
  await persistLocalSnapshot(projectRoot, repoId, allEntities, resolvedEdges);
10512
10804
  const elapsedMs = Date.now() - startTime;
10513
10805
  log6.info(
10514
- `Indexed ${files.length} files \u2192 ${allEntities.length} entities, ${resolvedEdges.length} edges in ${elapsedMs}ms`
10806
+ `Indexed ${files.length} files \u2192 ${allEntities.length} entities, ${resolvedEdges.length} edges, ${docKeys.length} docs in ${elapsedMs}ms`
10515
10807
  );
10516
10808
  return {
10517
10809
  fileCount: files.length,
@@ -10520,6 +10812,7 @@ async function indexLocalProject(projectRoot, graphStore, repoId, opts) {
10520
10812
  communityCount,
10521
10813
  patternCount,
10522
10814
  ruleCount,
10815
+ docCount: docKeys.length,
10523
10816
  elapsedMs,
10524
10817
  scip: scipResult.mergeResult ? {
10525
10818
  language: scipResult.language,
@@ -10535,7 +10828,7 @@ async function reindexFile(projectRoot, filePath, graphStore, repoId) {
10535
10828
  await removeFileEntities(graphStore, relPath);
10536
10829
  let content;
10537
10830
  try {
10538
- content = readFileSync18(absPath, "utf-8");
10831
+ content = readFileSync19(absPath, "utf-8");
10539
10832
  } catch {
10540
10833
  return { entities: 0, edges: 0 };
10541
10834
  }
@@ -10969,7 +11262,8 @@ async function populateCozoDB(graphStore, entities, edges) {
10969
11262
  { key: `file:${fp}`, name: basename2(fp), fp, is_test: isTestFile(fp) }
10970
11263
  );
10971
11264
  } catch (err) {
10972
- process.stderr.write(`[unerr] \u26A0 File entity insert failed for ${fp}: ${err instanceof Error ? err.message : String(err)}
11265
+ const msg = err instanceof Error ? err.message : JSON.stringify(err);
11266
+ process.stderr.write(`[unerr] \u26A0 File entity insert failed for ${fp}: ${msg}
10973
11267
  `);
10974
11268
  }
10975
11269
  }
@@ -11341,7 +11635,113 @@ async function resolveEntityNameGlobal(name, graphStore) {
11341
11635
  }
11342
11636
  return null;
11343
11637
  }
11344
- var INDEXABLE_EXTENSIONS, EXCLUDED_DIRS2, MAX_FILE_SIZE, log6;
11638
+ function walkDirForDocs(dir, files, projectRoot) {
11639
+ let entries;
11640
+ try {
11641
+ entries = readdirSync6(dir);
11642
+ } catch {
11643
+ return;
11644
+ }
11645
+ for (const entry of entries) {
11646
+ const absPath = join24(dir, entry);
11647
+ let stats;
11648
+ try {
11649
+ stats = statSync6(absPath);
11650
+ } catch {
11651
+ continue;
11652
+ }
11653
+ if (stats.isDirectory()) {
11654
+ if (EXCLUDED_DIRS2.has(entry)) continue;
11655
+ if (entry.startsWith(".") && !CI_CD_DIRS.has(entry)) continue;
11656
+ walkDirForDocs(absPath, files, projectRoot);
11657
+ continue;
11658
+ }
11659
+ if (!stats.isFile()) continue;
11660
+ if (stats.size > MAX_FILE_SIZE) continue;
11661
+ const ext = extname2(entry).toLowerCase();
11662
+ const name = basename2(entry);
11663
+ if (INDEXABLE_EXTENSIONS.has(ext)) continue;
11664
+ if (DOCUMENT_EXTENSIONS.has(ext) || DOCUMENT_NAMES.has(name)) {
11665
+ files.push(absPath);
11666
+ }
11667
+ }
11668
+ }
11669
+ function discoverDocumentFiles(projectRoot) {
11670
+ const files = [];
11671
+ walkDirForDocs(projectRoot, files, projectRoot);
11672
+ return files;
11673
+ }
11674
+ function extractDocTokens(absPath, relPath) {
11675
+ const tokens = new Set(tokenize(basename2(relPath)));
11676
+ for (const segment of relPath.split("/").slice(0, -1)) {
11677
+ if (segment && !EXCLUDED_DIRS2.has(segment)) {
11678
+ for (const t of tokenize(segment)) tokens.add(t);
11679
+ }
11680
+ }
11681
+ const ext = extname2(absPath).toLowerCase();
11682
+ try {
11683
+ const content = readFileSync19(absPath, "utf-8").slice(0, DOC_READ_LIMIT);
11684
+ const pattern = DOC_TOKEN_PATTERNS[ext];
11685
+ if (pattern) {
11686
+ pattern.lastIndex = 0;
11687
+ let match;
11688
+ while ((match = pattern.exec(content)) !== null) {
11689
+ if (match[1]) for (const t of tokenize(match[1])) tokens.add(t);
11690
+ if (match[2]) for (const t of tokenize(match[2])) tokens.add(t);
11691
+ }
11692
+ }
11693
+ if (ext === ".yaml" || ext === ".yml") {
11694
+ const yamlKeyRegex = /^([a-zA-Z_][\w-]*)\s*:/gm;
11695
+ let match;
11696
+ while ((match = yamlKeyRegex.exec(content)) !== null) {
11697
+ if (match[1]) for (const t of tokenize(match[1])) tokens.add(t);
11698
+ }
11699
+ }
11700
+ } catch {
11701
+ }
11702
+ return [...tokens];
11703
+ }
11704
+ async function indexDocumentFiles(projectRoot, graphStore) {
11705
+ const docFiles = discoverDocumentFiles(projectRoot);
11706
+ if (docFiles.length === 0) return [];
11707
+ const docKeys = [];
11708
+ for (const absPath of docFiles) {
11709
+ const relPath = relative(projectRoot, absPath);
11710
+ const name = basename2(relPath);
11711
+ const key = `doc:${relPath}`;
11712
+ docKeys.push(key);
11713
+ try {
11714
+ await graphStore.db.run(
11715
+ '?[key, kind, name, file_path, fan_in, fan_out, risk_level] <- [[$key, "document", $name, $fp, 0, 0, "low"]] :put entities { key => kind, name, file_path, fan_in, fan_out, risk_level }',
11716
+ { key, name, fp: relPath }
11717
+ );
11718
+ } catch {
11719
+ continue;
11720
+ }
11721
+ const docTokens = extractDocTokens(absPath, relPath);
11722
+ for (const token of docTokens) {
11723
+ try {
11724
+ await graphStore.db.run(
11725
+ "?[token, entity_key] <- [[$token, $key]] :put search_tokens { token, entity_key }",
11726
+ { token, key }
11727
+ );
11728
+ } catch {
11729
+ }
11730
+ }
11731
+ try {
11732
+ await graphStore.db.run(
11733
+ "?[file_path, entity_key] <- [[$fp, $key]] :put file_index { file_path, entity_key }",
11734
+ { fp: relPath, key }
11735
+ );
11736
+ } catch {
11737
+ }
11738
+ }
11739
+ if (docKeys.length > 0) {
11740
+ log6.info(`Documents: ${docKeys.length} doc/config files indexed for search`);
11741
+ }
11742
+ return docKeys;
11743
+ }
11744
+ var INDEXABLE_EXTENSIONS, EXCLUDED_DIRS2, MAX_FILE_SIZE, DOCUMENT_EXTENSIONS, DOCUMENT_NAMES, CI_CD_DIRS, DOC_READ_LIMIT, log6, DOC_TOKEN_PATTERNS;
11345
11745
  var init_local_indexer = __esm({
11346
11746
  "src/intelligence/local-indexer.ts"() {
11347
11747
  "use strict";
@@ -11395,6 +11795,87 @@ var init_local_indexer = __esm({
11395
11795
  ".parcel-cache"
11396
11796
  ]);
11397
11797
  MAX_FILE_SIZE = 1048576;
11798
+ DOCUMENT_EXTENSIONS = /* @__PURE__ */ new Set([
11799
+ ".md",
11800
+ ".mdx",
11801
+ ".txt",
11802
+ ".rst",
11803
+ ".adoc",
11804
+ ".org",
11805
+ ".yaml",
11806
+ ".yml",
11807
+ ".toml",
11808
+ ".json",
11809
+ ".xml",
11810
+ ".tf",
11811
+ ".tfvars",
11812
+ ".hcl",
11813
+ ".proto",
11814
+ ".graphql",
11815
+ ".gql",
11816
+ ".sql",
11817
+ ".sh",
11818
+ ".bash",
11819
+ ".zsh",
11820
+ ".html",
11821
+ ".css",
11822
+ ".scss",
11823
+ ".sass",
11824
+ ".less",
11825
+ ".vue",
11826
+ ".svelte",
11827
+ ".astro",
11828
+ ".j2",
11829
+ ".jinja2",
11830
+ ".tmpl",
11831
+ ".hbs",
11832
+ ".ejs",
11833
+ ".pug",
11834
+ ".prisma",
11835
+ ".avsc",
11836
+ ".thrift",
11837
+ ".smithy",
11838
+ ".cmake",
11839
+ ".bazel",
11840
+ ".bzl",
11841
+ ".mk",
11842
+ ".csv",
11843
+ ".tsv",
11844
+ ".ini",
11845
+ ".cfg",
11846
+ ".conf",
11847
+ ".properties",
11848
+ ".env.example",
11849
+ ".editorconfig",
11850
+ ".dockerfile",
11851
+ ".tex",
11852
+ ".latex",
11853
+ ".eslintrc",
11854
+ ".prettierrc",
11855
+ ".stylelintrc",
11856
+ ".pylintrc",
11857
+ ".flake8"
11858
+ ]);
11859
+ DOCUMENT_NAMES = /* @__PURE__ */ new Set([
11860
+ "Dockerfile",
11861
+ "Makefile",
11862
+ "Procfile",
11863
+ "Vagrantfile",
11864
+ "Jenkinsfile",
11865
+ "Gemfile",
11866
+ "Rakefile",
11867
+ ".gitignore",
11868
+ ".dockerignore",
11869
+ ".prettierrc",
11870
+ ".editorconfig",
11871
+ ".eslintrc",
11872
+ ".stylelintrc",
11873
+ "biome.json",
11874
+ "tslint.json",
11875
+ ".gitlab-ci.yml"
11876
+ ]);
11877
+ CI_CD_DIRS = /* @__PURE__ */ new Set([".github", ".circleci"]);
11878
+ DOC_READ_LIMIT = 8192;
11398
11879
  log6 = {
11399
11880
  info: (msg) => process.stderr.write(`[unerr] ${msg}
11400
11881
  `),
@@ -11403,6 +11884,31 @@ var init_local_indexer = __esm({
11403
11884
  `);
11404
11885
  }
11405
11886
  };
11887
+ DOC_TOKEN_PATTERNS = {
11888
+ ".md": /^#{1,6}\s+(.+)$/gm,
11889
+ ".mdx": /^#{1,6}\s+(.+)$/gm,
11890
+ ".tf": /^(?:resource|module|variable|data|output)\s+"([^"]+)"(?:\s+"([^"]+)")?/gm,
11891
+ ".hcl": /^(?:resource|module|variable|data|output)\s+"([^"]+)"(?:\s+"([^"]+)")?/gm,
11892
+ ".sql": /CREATE\s+(?:TABLE|VIEW|FUNCTION|PROCEDURE|INDEX)\s+(?:IF\s+NOT\s+EXISTS\s+)?["'`]?(\w+)/gim,
11893
+ ".graphql": /^(?:type|query|mutation|subscription|input|enum|interface)\s+(\w+)/gm,
11894
+ ".gql": /^(?:type|query|mutation|subscription|input|enum|interface)\s+(\w+)/gm,
11895
+ ".sh": /^(?:function\s+)?(\w+)\s*\(\)/gm,
11896
+ ".bash": /^(?:function\s+)?(\w+)\s*\(\)/gm,
11897
+ ".zsh": /^(?:function\s+)?(\w+)\s*\(\)/gm,
11898
+ ".proto": /^(?:message|service|enum|rpc)\s+(\w+)/gm,
11899
+ ".prisma": /^(?:model|enum|type|datasource|generator)\s+(\w+)/gm,
11900
+ ".smithy": /^(?:service|resource|operation|structure|union|enum)\s+(\w+)/gm,
11901
+ ".cmake": /^(?:project|add_library|add_executable|find_package)\s*\(\s*(\w+)/gm,
11902
+ ".bazel": /^(?:cc_library|cc_binary|java_library|py_library|go_library)\s*\(\s*name\s*=\s*"([^"]+)"/gm,
11903
+ ".bzl": /^def\s+(\w+)\s*\(/gm,
11904
+ ".tex": /\\(?:section|subsection|chapter|title)\{([^}]+)\}/gm,
11905
+ ".latex": /\\(?:section|subsection|chapter|title)\{([^}]+)\}/gm,
11906
+ ".html": /<(?:h[1-6]|title)[^>]*>([^<]+)</gim,
11907
+ ".vue": /<(?:h[1-6]|title)[^>]*>([^<]+)</gim,
11908
+ ".svelte": /<(?:h[1-6]|title)[^>]*>([^<]+)</gim,
11909
+ ".thrift": /^(?:service|struct|enum|exception|typedef)\s+(\w+)/gm,
11910
+ ".avsc": /"name"\s*:\s*"([^"]+)"/gm
11911
+ };
11406
11912
  }
11407
11913
  });
11408
11914
 
@@ -11415,23 +11921,23 @@ __export(hook_installer_exports, {
11415
11921
  });
11416
11922
  import {
11417
11923
  chmodSync as chmodSync3,
11418
- existsSync as existsSync18,
11924
+ existsSync as existsSync19,
11419
11925
  mkdirSync as mkdirSync13,
11420
11926
  unlinkSync as unlinkSync2,
11421
- writeFileSync as writeFileSync9
11927
+ writeFileSync as writeFileSync10
11422
11928
  } from "fs";
11423
11929
  import { join as join25 } from "path";
11424
11930
  function installClaudeHook(cwd) {
11425
11931
  const hooksDir = join25(cwd, ".claude", "hooks");
11426
11932
  const hookPath = join25(hooksDir, "PostToolUse.sh");
11427
- if (existsSync18(hookPath)) {
11933
+ if (existsSync19(hookPath)) {
11428
11934
  return { path: hookPath, action: "already_exists" };
11429
11935
  }
11430
11936
  try {
11431
- if (!existsSync18(hooksDir)) {
11937
+ if (!existsSync19(hooksDir)) {
11432
11938
  mkdirSync13(hooksDir, { recursive: true });
11433
11939
  }
11434
- writeFileSync9(hookPath, HOOK_CONTENT, "utf-8");
11940
+ writeFileSync10(hookPath, HOOK_CONTENT, "utf-8");
11435
11941
  chmodSync3(hookPath, 493);
11436
11942
  return { path: hookPath, action: "installed" };
11437
11943
  } catch {
@@ -11440,7 +11946,7 @@ function installClaudeHook(cwd) {
11440
11946
  }
11441
11947
  function removeClaudeHook(cwd) {
11442
11948
  const hookPath = join25(cwd, ".claude", "hooks", "PostToolUse.sh");
11443
- if (!existsSync18(hookPath)) return false;
11949
+ if (!existsSync19(hookPath)) return false;
11444
11950
  try {
11445
11951
  unlinkSync2(hookPath);
11446
11952
  return true;
@@ -11449,7 +11955,7 @@ function removeClaudeHook(cwd) {
11449
11955
  }
11450
11956
  }
11451
11957
  function isClaudeHookInstalled(cwd) {
11452
- return existsSync18(join25(cwd, ".claude", "hooks", "PostToolUse.sh"));
11958
+ return existsSync19(join25(cwd, ".claude", "hooks", "PostToolUse.sh"));
11453
11959
  }
11454
11960
  var HOOK_CONTENT;
11455
11961
  var init_hook_installer = __esm({
@@ -11476,13 +11982,13 @@ __export(tool_detector_exports, {
11476
11982
  detectTools: () => detectTools,
11477
11983
  formatDetectedTools: () => formatDetectedTools
11478
11984
  });
11479
- import { existsSync as existsSync19 } from "fs";
11985
+ import { existsSync as existsSync20 } from "fs";
11480
11986
  import { join as join26 } from "path";
11481
11987
  function detectTools(cwd) {
11482
11988
  const detected = [];
11483
11989
  for (const agent of AGENT_REGISTRY) {
11484
11990
  const hasDirMarker = agent.dirMarkers.some(
11485
- (dir) => existsSync19(join26(cwd, dir))
11991
+ (dir) => existsSync20(join26(cwd, dir))
11486
11992
  );
11487
11993
  const hasEnvVar = agent.envVars.some((envVar) => !!process.env[envVar]);
11488
11994
  if (hasDirMarker || hasEnvVar) {
@@ -11490,7 +11996,7 @@ function detectTools(cwd) {
11490
11996
  detected.push({
11491
11997
  ide: agent.id,
11492
11998
  configDir: join26(cwd, agent.dirMarkers[0] ?? ""),
11493
- hasExistingConfig: existsSync19(configPath),
11999
+ hasExistingConfig: existsSync20(configPath),
11494
12000
  agent
11495
12001
  });
11496
12002
  }
@@ -11834,13 +12340,13 @@ __export(resolver_exports, {
11834
12340
  scanSkillDirectory: () => scanSkillDirectory
11835
12341
  });
11836
12342
  import {
11837
- existsSync as existsSync22,
12343
+ existsSync as existsSync23,
11838
12344
  mkdirSync as mkdirSync16,
11839
- readFileSync as readFileSync21,
12345
+ readFileSync as readFileSync22,
11840
12346
  readdirSync as readdirSync7,
11841
12347
  rmSync,
11842
12348
  unlinkSync as unlinkSync4,
11843
- writeFileSync as writeFileSync12
12349
+ writeFileSync as writeFileSync13
11844
12350
  } from "fs";
11845
12351
  import { homedir as homedir6 } from "os";
11846
12352
  import { dirname as dirname6, join as join29 } from "path";
@@ -11940,7 +12446,7 @@ function writeSkillFile(skill, ide, skillDir, ext, dirPerSkill) {
11940
12446
  filePath = join29(skillDir, `${skillName}${ext}`);
11941
12447
  content = formatMarkdownSkill(skill);
11942
12448
  }
11943
- writeFileSync12(filePath, content);
12449
+ writeFileSync13(filePath, content);
11944
12450
  return filePath;
11945
12451
  }
11946
12452
  function formatClaudeCodeSkill(skill) {
@@ -12117,13 +12623,13 @@ function parseFrontmatter(raw) {
12117
12623
  return { meta, content };
12118
12624
  }
12119
12625
  function scanSkillDirectory(dir) {
12120
- if (!existsSync22(dir)) return [];
12626
+ if (!existsSync23(dir)) return [];
12121
12627
  const skills = [];
12122
12628
  try {
12123
12629
  const files = readdirSync7(dir).filter((f) => f.endsWith(".md"));
12124
12630
  for (const file of files) {
12125
12631
  try {
12126
- const raw = readFileSync21(join29(dir, file), "utf-8");
12632
+ const raw = readFileSync22(join29(dir, file), "utf-8");
12127
12633
  const { meta, content } = parseFrontmatter(raw);
12128
12634
  const name = meta.name ?? file.replace(/\.md$/, "");
12129
12635
  skills.push({
@@ -12170,13 +12676,13 @@ async function resolveAndInstallSkills(opts) {
12170
12676
  }
12171
12677
  async function ensureSkillsPresent(opts) {
12172
12678
  const { dir, ext, dirPerSkill } = getSkillDir(opts.ide, opts.cwd);
12173
- if (existsSync22(dir)) {
12679
+ if (existsSync23(dir)) {
12174
12680
  try {
12175
12681
  const entries = readdirSync7(dir);
12176
12682
  let hasSkills;
12177
12683
  if (dirPerSkill) {
12178
12684
  hasSkills = entries.some(
12179
- (f) => f.startsWith("unerr-") && existsSync22(join29(dir, f, "SKILL.md"))
12685
+ (f) => f.startsWith("unerr-") && existsSync23(join29(dir, f, "SKILL.md"))
12180
12686
  );
12181
12687
  } else {
12182
12688
  hasSkills = entries.some(
@@ -12192,11 +12698,11 @@ async function ensureSkillsPresent(opts) {
12192
12698
  }
12193
12699
  function listInstalledSkills(ide, cwd) {
12194
12700
  const { dir, ext, dirPerSkill } = getSkillDir(ide, cwd);
12195
- if (!existsSync22(dir)) return [];
12701
+ if (!existsSync23(dir)) return [];
12196
12702
  try {
12197
12703
  if (dirPerSkill) {
12198
12704
  return readdirSync7(dir).filter(
12199
- (f) => f.startsWith("unerr-") && existsSync22(join29(dir, f, "SKILL.md"))
12705
+ (f) => f.startsWith("unerr-") && existsSync23(join29(dir, f, "SKILL.md"))
12200
12706
  ).map((f) => ({
12201
12707
  name: f.replace("unerr-", ""),
12202
12708
  path: join29(dir, f, "SKILL.md")
@@ -12239,7 +12745,7 @@ var correction_detector_exports = {};
12239
12745
  __export(correction_detector_exports, {
12240
12746
  detectCorrections: () => detectCorrections
12241
12747
  });
12242
- import { existsSync as existsSync24, readFileSync as readFileSync23 } from "fs";
12748
+ import { existsSync as existsSync25, readFileSync as readFileSync24 } from "fs";
12243
12749
  function detectCorrections(ledgerPath, options) {
12244
12750
  const MIN_CONFIDENCE = options?.min_confidence ?? 0.6;
12245
12751
  const CORRECTION_WINDOW = options?.correction_window_ms ?? 6e4;
@@ -12262,11 +12768,11 @@ function detectCorrections(ledgerPath, options) {
12262
12768
  return deduplicatePatterns(scored);
12263
12769
  }
12264
12770
  function readLedgerEntries(ledgerPath, sinceDays) {
12265
- if (!existsSync24(ledgerPath)) return [];
12771
+ if (!existsSync25(ledgerPath)) return [];
12266
12772
  const cutoff = Date.now() - sinceDays * 24 * 60 * 60 * 1e3;
12267
12773
  const entries = [];
12268
12774
  try {
12269
- const content = readFileSync23(ledgerPath, "utf-8");
12775
+ const content = readFileSync24(ledgerPath, "utf-8");
12270
12776
  const lines = content.split("\n");
12271
12777
  for (const line of lines) {
12272
12778
  if (line.trim().length === 0) continue;
@@ -12471,10 +12977,10 @@ __export(offline_rewind_exports, {
12471
12977
  import { randomBytes } from "crypto";
12472
12978
  import {
12473
12979
  appendFileSync as appendFileSync6,
12474
- existsSync as existsSync27,
12980
+ existsSync as existsSync28,
12475
12981
  mkdirSync as mkdirSync18,
12476
- readFileSync as readFileSync25,
12477
- writeFileSync as writeFileSync14
12982
+ readFileSync as readFileSync26,
12983
+ writeFileSync as writeFileSync15
12478
12984
  } from "fs";
12479
12985
  import { join as join33 } from "path";
12480
12986
  async function offlineRewind(input) {
@@ -12612,9 +13118,9 @@ async function computeLocalBlastRadius(graph, revertFiles, targetFiles, startTim
12612
13118
  }
12613
13119
  function readBranchCounter(unerrDir) {
12614
13120
  const contextPath = join33(unerrDir, "ledger", "branch_context.json");
12615
- if (!existsSync27(contextPath)) return 1;
13121
+ if (!existsSync28(contextPath)) return 1;
12616
13122
  try {
12617
- const data = JSON.parse(readFileSync25(contextPath, "utf-8"));
13123
+ const data = JSON.parse(readFileSync26(contextPath, "utf-8"));
12618
13124
  return data.timeline_branch ?? 1;
12619
13125
  } catch {
12620
13126
  return 1;
@@ -12623,13 +13129,13 @@ function readBranchCounter(unerrDir) {
12623
13129
  function incrementBranchCounter(unerrDir) {
12624
13130
  const contextPath = join33(unerrDir, "ledger", "branch_context.json");
12625
13131
  const ledgerDir = join33(unerrDir, "ledger");
12626
- if (!existsSync27(ledgerDir)) {
13132
+ if (!existsSync28(ledgerDir)) {
12627
13133
  mkdirSync18(ledgerDir, { recursive: true });
12628
13134
  }
12629
13135
  let data = {};
12630
- if (existsSync27(contextPath)) {
13136
+ if (existsSync28(contextPath)) {
12631
13137
  try {
12632
- data = JSON.parse(readFileSync25(contextPath, "utf-8"));
13138
+ data = JSON.parse(readFileSync26(contextPath, "utf-8"));
12633
13139
  } catch {
12634
13140
  }
12635
13141
  }
@@ -12637,25 +13143,25 @@ function incrementBranchCounter(unerrDir) {
12637
13143
  const next = current + 1;
12638
13144
  data.timeline_branch = next;
12639
13145
  data.last_rewind_at = (/* @__PURE__ */ new Date()).toISOString();
12640
- writeFileSync14(contextPath, JSON.stringify(data, null, 2), "utf-8");
13146
+ writeFileSync15(contextPath, JSON.stringify(data, null, 2), "utf-8");
12641
13147
  return next;
12642
13148
  }
12643
13149
  function markEntriesReverted(unerrDir, entryIds) {
12644
13150
  if (entryIds.length === 0) return;
12645
13151
  const revertedPath = join33(unerrDir, "ledger", "reverted_entries.json");
12646
13152
  const ledgerDir = join33(unerrDir, "ledger");
12647
- if (!existsSync27(ledgerDir)) {
13153
+ if (!existsSync28(ledgerDir)) {
12648
13154
  mkdirSync18(ledgerDir, { recursive: true });
12649
13155
  }
12650
13156
  let existing = [];
12651
- if (existsSync27(revertedPath)) {
13157
+ if (existsSync28(revertedPath)) {
12652
13158
  try {
12653
- existing = JSON.parse(readFileSync25(revertedPath, "utf-8"));
13159
+ existing = JSON.parse(readFileSync26(revertedPath, "utf-8"));
12654
13160
  } catch {
12655
13161
  }
12656
13162
  }
12657
13163
  const merged = Array.from(/* @__PURE__ */ new Set([...existing, ...entryIds]));
12658
- writeFileSync14(revertedPath, JSON.stringify(merged, null, 2), "utf-8");
13164
+ writeFileSync15(revertedPath, JSON.stringify(merged, null, 2), "utf-8");
12659
13165
  }
12660
13166
  function extractFilesFromEntry(entry) {
12661
13167
  const files = [];
@@ -12715,10 +13221,10 @@ __export(shadow_ledger_exports, {
12715
13221
  import { randomBytes as randomBytes2 } from "crypto";
12716
13222
  import {
12717
13223
  appendFileSync as appendFileSync7,
12718
- existsSync as existsSync28,
13224
+ existsSync as existsSync29,
12719
13225
  mkdirSync as mkdirSync19,
12720
- readFileSync as readFileSync26,
12721
- writeFileSync as writeFileSync15
13226
+ readFileSync as readFileSync27,
13227
+ writeFileSync as writeFileSync16
12722
13228
  } from "fs";
12723
13229
  import { join as join34 } from "path";
12724
13230
  function generateId2() {
@@ -12753,7 +13259,7 @@ var init_shadow_ledger = __esm({
12753
13259
  this.ledgerDir = join34(unerrDir, "ledger");
12754
13260
  this.filePath = join34(this.ledgerDir, "shadow.jsonl");
12755
13261
  this.sessionId = generateId2();
12756
- if (!existsSync28(this.ledgerDir)) {
13262
+ if (!existsSync29(this.ledgerDir)) {
12757
13263
  mkdirSync19(this.ledgerDir, { recursive: true });
12758
13264
  }
12759
13265
  this.recoverFile();
@@ -12860,9 +13366,9 @@ var init_shadow_ledger = __esm({
12860
13366
  * Get all entries (read from file). Used sparingly — prefer buffer.
12861
13367
  */
12862
13368
  readAllEntries() {
12863
- if (!existsSync28(this.filePath)) return [];
13369
+ if (!existsSync29(this.filePath)) return [];
12864
13370
  try {
12865
- const content = readFileSync26(this.filePath, "utf-8");
13371
+ const content = readFileSync27(this.filePath, "utf-8");
12866
13372
  const lines = content.split("\n").filter((l) => l.trim().length > 0);
12867
13373
  return lines.map((line) => JSON.parse(line));
12868
13374
  } catch {
@@ -12899,9 +13405,9 @@ var init_shadow_ledger = __esm({
12899
13405
  * Recover from file corruption: truncate to last valid JSONL line.
12900
13406
  */
12901
13407
  recoverFile() {
12902
- if (!existsSync28(this.filePath)) return;
13408
+ if (!existsSync29(this.filePath)) return;
12903
13409
  try {
12904
- const content = readFileSync26(this.filePath, "utf-8");
13410
+ const content = readFileSync27(this.filePath, "utf-8");
12905
13411
  const lines = content.split("\n");
12906
13412
  const validLines = [];
12907
13413
  for (const line of lines) {
@@ -12918,23 +13424,23 @@ var init_shadow_ledger = __esm({
12918
13424
  }
12919
13425
  }
12920
13426
  if (validLines.length < lines.filter((l) => l.trim().length > 0).length) {
12921
- writeFileSync15(
13427
+ writeFileSync16(
12922
13428
  this.filePath,
12923
13429
  validLines.join("\n") + (validLines.length > 0 ? "\n" : ""),
12924
13430
  "utf-8"
12925
13431
  );
12926
13432
  }
12927
13433
  } catch {
12928
- writeFileSync15(this.filePath, "", "utf-8");
13434
+ writeFileSync16(this.filePath, "", "utf-8");
12929
13435
  }
12930
13436
  }
12931
13437
  /**
12932
13438
  * Load the most recent entries from the file into the buffer.
12933
13439
  */
12934
13440
  loadRecentEntries() {
12935
- if (!existsSync28(this.filePath)) return;
13441
+ if (!existsSync29(this.filePath)) return;
12936
13442
  try {
12937
- const content = readFileSync26(this.filePath, "utf-8");
13443
+ const content = readFileSync27(this.filePath, "utf-8");
12938
13444
  const lines = content.split("\n").filter((l) => l.trim().length > 0);
12939
13445
  const start = Math.max(0, lines.length - MAX_BUFFER_SIZE);
12940
13446
  for (let i = start; i < lines.length; i++) {
@@ -12962,9 +13468,9 @@ var init_shadow_ledger = __esm({
12962
13468
  * Count total entries in the file.
12963
13469
  */
12964
13470
  countEntries() {
12965
- if (!existsSync28(this.filePath)) return 0;
13471
+ if (!existsSync29(this.filePath)) return 0;
12966
13472
  try {
12967
- const content = readFileSync26(this.filePath, "utf-8");
13473
+ const content = readFileSync27(this.filePath, "utf-8");
12968
13474
  return content.split("\n").filter((l) => l.trim().length > 0).length;
12969
13475
  } catch {
12970
13476
  return 0;
@@ -12989,7 +13495,7 @@ __export(timeline_fork_exports, {
12989
13495
  resetTimelineForks: () => resetTimelineForks,
12990
13496
  wasForkPoint: () => wasForkPoint
12991
13497
  });
12992
- import { existsSync as existsSync29, mkdirSync as mkdirSync20, readFileSync as readFileSync27, writeFileSync as writeFileSync16 } from "fs";
13498
+ import { existsSync as existsSync30, mkdirSync as mkdirSync20, readFileSync as readFileSync28, writeFileSync as writeFileSync17 } from "fs";
12993
13499
  import { join as join35 } from "path";
12994
13500
  function createTimelineFork(snapshotId, abandonedEntities, prompts, reason, unerrDir) {
12995
13501
  const state = unerrDir ? loadState(unerrDir) : { activeTimeline: inMemoryTimeline, forks: [] };
@@ -13120,17 +13626,17 @@ function getStatePath(unerrDir) {
13120
13626
  }
13121
13627
  function ensureStateDir(unerrDir) {
13122
13628
  const stateDir = join35(unerrDir, "state");
13123
- if (!existsSync29(stateDir)) {
13629
+ if (!existsSync30(stateDir)) {
13124
13630
  mkdirSync20(stateDir, { recursive: true });
13125
13631
  }
13126
13632
  }
13127
13633
  function loadState(unerrDir) {
13128
13634
  const path7 = getStatePath(unerrDir);
13129
- if (!existsSync29(path7)) {
13635
+ if (!existsSync30(path7)) {
13130
13636
  return { activeTimeline: 0, forks: [] };
13131
13637
  }
13132
13638
  try {
13133
- const raw = readFileSync27(path7, "utf-8");
13639
+ const raw = readFileSync28(path7, "utf-8");
13134
13640
  const parsed = JSON.parse(raw);
13135
13641
  return {
13136
13642
  activeTimeline: typeof parsed.activeTimeline === "number" ? parsed.activeTimeline : 0,
@@ -13144,7 +13650,7 @@ function loadState(unerrDir) {
13144
13650
  function saveState(unerrDir, state) {
13145
13651
  ensureStateDir(unerrDir);
13146
13652
  const path7 = getStatePath(unerrDir);
13147
- writeFileSync16(path7, JSON.stringify(state, null, 2), "utf-8");
13653
+ writeFileSync17(path7, JSON.stringify(state, null, 2), "utf-8");
13148
13654
  }
13149
13655
  function deduplicateStrings(arr) {
13150
13656
  return Array.from(new Set(arr));
@@ -13206,9 +13712,9 @@ function createEmptyAllTime() {
13206
13712
  function loadStats() {
13207
13713
  const currentWeek = getWeekStart();
13208
13714
  try {
13209
- const { readFileSync: readFileSync57 } = __require("fs");
13715
+ const { readFileSync: readFileSync58 } = __require("fs");
13210
13716
  const raw = JSON.parse(
13211
- readFileSync57(getStatsPath(), "utf-8")
13717
+ readFileSync58(getStatsPath(), "utf-8")
13212
13718
  );
13213
13719
  if (raw.version !== 1) {
13214
13720
  return {
@@ -13331,7 +13837,7 @@ __export(branch_context_exports, {
13331
13837
  getHeadSha: () => getHeadSha2,
13332
13838
  startBranchPoller: () => startBranchPoller
13333
13839
  });
13334
- import { existsSync as existsSync30, readFileSync as readFileSync28 } from "fs";
13840
+ import { existsSync as existsSync31, readFileSync as readFileSync29 } from "fs";
13335
13841
  import { join as join37 } from "path";
13336
13842
  async function computeBranchContext(cwd) {
13337
13843
  const dir = cwd ?? process.cwd();
@@ -13396,9 +13902,9 @@ function detectBranchSwitch(previousBranch, cwd) {
13396
13902
  function getCurrentBranch2(cwd) {
13397
13903
  const gitDir = cwd ? join37(cwd, ".git") : join37(process.cwd(), ".git");
13398
13904
  const headPath = join37(gitDir, "HEAD");
13399
- if (!existsSync30(headPath)) return null;
13905
+ if (!existsSync31(headPath)) return null;
13400
13906
  try {
13401
- const content = readFileSync28(headPath, "utf-8").trim();
13907
+ const content = readFileSync29(headPath, "utf-8").trim();
13402
13908
  if (content.startsWith("ref: refs/heads/")) {
13403
13909
  return content.slice("ref: refs/heads/".length);
13404
13910
  }
@@ -13921,9 +14427,9 @@ function getCumulativePath() {
13921
14427
  function loadCumulativeStats() {
13922
14428
  const currentWeek = getWeekStart2();
13923
14429
  try {
13924
- const { readFileSync: readFileSync57 } = __require("fs");
14430
+ const { readFileSync: readFileSync58 } = __require("fs");
13925
14431
  const raw = JSON.parse(
13926
- readFileSync57(getCumulativePath(), "utf-8")
14432
+ readFileSync58(getCumulativePath(), "utf-8")
13927
14433
  );
13928
14434
  if (raw.weekStart !== currentWeek) {
13929
14435
  return {
@@ -13984,9 +14490,9 @@ function createEmptyCumulativeLocal(weekStart) {
13984
14490
  function loadCumulativeLocalStats() {
13985
14491
  const currentWeek = getWeekStart2();
13986
14492
  try {
13987
- const { readFileSync: readFileSync57 } = __require("fs");
14493
+ const { readFileSync: readFileSync58 } = __require("fs");
13988
14494
  const raw = JSON.parse(
13989
- readFileSync57(getCumulativeLocalPath(), "utf-8")
14495
+ readFileSync58(getCumulativeLocalPath(), "utf-8")
13990
14496
  );
13991
14497
  if (raw.weekStartDate !== currentWeek) {
13992
14498
  return createEmptyCumulativeLocal(currentWeek);
@@ -14590,11 +15096,11 @@ __export(pid_lock_exports, {
14590
15096
  PidLock: () => PidLock
14591
15097
  });
14592
15098
  import {
14593
- existsSync as existsSync34,
15099
+ existsSync as existsSync35,
14594
15100
  mkdirSync as mkdirSync21,
14595
- readFileSync as readFileSync32,
15101
+ readFileSync as readFileSync33,
14596
15102
  unlinkSync as unlinkSync6,
14597
- writeFileSync as writeFileSync18
15103
+ writeFileSync as writeFileSync19
14598
15104
  } from "fs";
14599
15105
  import { createServer } from "http";
14600
15106
  import { join as join41 } from "path";
@@ -14674,12 +15180,12 @@ var init_pid_lock = __esm({
14674
15180
  */
14675
15181
  async acquire() {
14676
15182
  const stateDir = join41(this.pidPath, "..");
14677
- if (!existsSync34(stateDir)) {
15183
+ if (!existsSync35(stateDir)) {
14678
15184
  mkdirSync21(stateDir, { recursive: true });
14679
15185
  }
14680
- if (existsSync34(this.pidPath)) {
15186
+ if (existsSync35(this.pidPath)) {
14681
15187
  try {
14682
- const raw = readFileSync32(this.pidPath, "utf-8").trim();
15188
+ const raw = readFileSync33(this.pidPath, "utf-8").trim();
14683
15189
  const pidData = parsePidFile(raw);
14684
15190
  if (pidData && isProcessAlive(pidData.pid)) {
14685
15191
  if (pidData.healthPort) {
@@ -14739,7 +15245,7 @@ var init_pid_lock = __esm({
14739
15245
  startedAt: this.startedAt,
14740
15246
  healthPort: this.healthPort
14741
15247
  };
14742
- writeFileSync18(this.pidPath, JSON.stringify(data), "utf-8");
15248
+ writeFileSync19(this.pidPath, JSON.stringify(data), "utf-8");
14743
15249
  }
14744
15250
  async startHealthServer() {
14745
15251
  return new Promise((resolve3) => {
@@ -14784,8 +15290,8 @@ var init_pid_lock = __esm({
14784
15290
  this.healthServer = null;
14785
15291
  }
14786
15292
  try {
14787
- if (existsSync34(this.pidPath)) {
14788
- const raw = readFileSync32(this.pidPath, "utf-8").trim();
15293
+ if (existsSync35(this.pidPath)) {
15294
+ const raw = readFileSync33(this.pidPath, "utf-8").trim();
14789
15295
  const data = parsePidFile(raw);
14790
15296
  if (data && data.pid === process.pid) {
14791
15297
  unlinkSync6(this.pidPath);
@@ -14798,9 +15304,9 @@ var init_pid_lock = __esm({
14798
15304
  * Check if a proxy is currently running (for external callers).
14799
15305
  */
14800
15306
  isLocked() {
14801
- if (!existsSync34(this.pidPath)) return { locked: false };
15307
+ if (!existsSync35(this.pidPath)) return { locked: false };
14802
15308
  try {
14803
- const raw = readFileSync32(this.pidPath, "utf-8").trim();
15309
+ const raw = readFileSync33(this.pidPath, "utf-8").trim();
14804
15310
  const data = parsePidFile(raw);
14805
15311
  if (data && isProcessAlive(data.pid)) {
14806
15312
  return { locked: true, pid: data.pid, healthPort: data.healthPort };
@@ -14815,9 +15321,9 @@ var init_pid_lock = __esm({
14815
15321
  * Uses health check for full verification (unlike isLocked() which is sync-only).
14816
15322
  */
14817
15323
  async probe() {
14818
- if (!existsSync34(this.pidPath)) return { alive: false };
15324
+ if (!existsSync35(this.pidPath)) return { alive: false };
14819
15325
  try {
14820
- const raw = readFileSync32(this.pidPath, "utf-8").trim();
15326
+ const raw = readFileSync33(this.pidPath, "utf-8").trim();
14821
15327
  const pidData = parsePidFile(raw);
14822
15328
  if (!pidData || !isProcessAlive(pidData.pid)) return { alive: false };
14823
15329
  if (pidData.healthPort) {
@@ -14834,9 +15340,9 @@ var init_pid_lock = __esm({
14834
15340
  */
14835
15341
  static readPidFile(stateDir) {
14836
15342
  const pidPath = join41(stateDir, PID_FILENAME);
14837
- if (!existsSync34(pidPath)) return null;
15343
+ if (!existsSync35(pidPath)) return null;
14838
15344
  try {
14839
- const raw = readFileSync32(pidPath, "utf-8").trim();
15345
+ const raw = readFileSync33(pidPath, "utf-8").trim();
14840
15346
  const data = parsePidFile(raw);
14841
15347
  if (data && isProcessAlive(data.pid)) return data;
14842
15348
  return null;
@@ -15127,6 +15633,11 @@ var init_tool_definitions = __esm({
15127
15633
  description: "Direction of references: 'callers' (who calls this entity, default) or 'callees' (what this entity calls)",
15128
15634
  default: "callers"
15129
15635
  },
15636
+ limit: {
15637
+ type: "number",
15638
+ description: "Max number of references to return (default 25). Use higher values only when you need the full list.",
15639
+ default: 25
15640
+ },
15130
15641
  token_budget: TOKEN_BUDGET_PROP
15131
15642
  },
15132
15643
  required: ["key"]
@@ -15285,12 +15796,8 @@ var init_tool_definitions = __esm({
15285
15796
  },
15286
15797
  purpose: {
15287
15798
  type: "string",
15288
- enum: ["explore", "edit", "reference"],
15289
- description: "Read intent: 'explore' (default, budget-capped for browsing), 'edit' (full file, no gating \u2014 use when you plan to modify), 'reference' (entity/offset only, tight budget)"
15290
- },
15291
- force_full: {
15292
- type: "boolean",
15293
- description: "Force full file read even for large files (alias for purpose:'edit')"
15799
+ enum: ["explore", "reference"],
15800
+ description: "Read intent: 'explore' (default, budget-capped for browsing/understanding), 'reference' (entity/offset only, tight budget). To edit a file, use file_read(explore) to understand it, then built-in Read with offset/limit on the target lines, then Edit."
15294
15801
  },
15295
15802
  token_budget: TOKEN_BUDGET_PROP
15296
15803
  },
@@ -15643,7 +16150,7 @@ var init_deep_link = __esm({
15643
16150
  });
15644
16151
 
15645
16152
  // src/proxy/startup-renderer.ts
15646
- import { existsSync as existsSync35, readFileSync as readFileSync33, writeFileSync as writeFileSync19 } from "fs";
16153
+ import { existsSync as existsSync36, readFileSync as readFileSync34, writeFileSync as writeFileSync20 } from "fs";
15647
16154
  import { join as join42 } from "path";
15648
16155
  import React2 from "react";
15649
16156
  var StartupRenderer;
@@ -15756,9 +16263,9 @@ var init_startup_renderer = __esm({
15756
16263
  "state",
15757
16264
  "graph_version.json"
15758
16265
  );
15759
- if (!existsSync35(versionPath)) return true;
16266
+ if (!existsSync36(versionPath)) return true;
15760
16267
  try {
15761
- const data = JSON.parse(readFileSync33(versionPath, "utf-8"));
16268
+ const data = JSON.parse(readFileSync34(versionPath, "utf-8"));
15762
16269
  return !data.first_boot_shown;
15763
16270
  } catch {
15764
16271
  return true;
@@ -15769,11 +16276,11 @@ var init_startup_renderer = __esm({
15769
16276
  const versionPath = join42(stateDir, "graph_version.json");
15770
16277
  try {
15771
16278
  let data = {};
15772
- if (existsSync35(versionPath)) {
15773
- data = JSON.parse(readFileSync33(versionPath, "utf-8"));
16279
+ if (existsSync36(versionPath)) {
16280
+ data = JSON.parse(readFileSync34(versionPath, "utf-8"));
15774
16281
  }
15775
16282
  data.first_boot_shown = true;
15776
- writeFileSync19(versionPath, JSON.stringify(data, null, 2));
16283
+ writeFileSync20(versionPath, JSON.stringify(data, null, 2));
15777
16284
  } catch {
15778
16285
  }
15779
16286
  }
@@ -16044,13 +16551,13 @@ var init_lifecycle_actor = __esm({
16044
16551
  });
16045
16552
 
16046
16553
  // src/intelligence/facts-schema.ts
16047
- import { existsSync as existsSync36, mkdirSync as mkdirSync22 } from "fs";
16554
+ import { existsSync as existsSync37, mkdirSync as mkdirSync22 } from "fs";
16048
16555
  import { join as join43 } from "path";
16049
16556
  async function openFactsDb(projectRoot) {
16050
16557
  const unerrDir = join43(projectRoot, ".unerr");
16051
16558
  mkdirSync22(unerrDir, { recursive: true });
16052
16559
  const dbPath = join43(unerrDir, FACTS_DB_FILENAME);
16053
- const isNew = !existsSync36(dbPath);
16560
+ const isNew = !existsSync37(dbPath);
16054
16561
  const cozoModule = await import("cozo-node");
16055
16562
  const CozoDbConstructor = cozoModule.default ? cozoModule.default.CozoDb : cozoModule.CozoDb;
16056
16563
  const db = new CozoDbConstructor("sqlite", dbPath);
@@ -16651,7 +17158,7 @@ __export(fact_generator_exports, {
16651
17158
  generateFromSessionAnalysis: () => generateFromSessionAnalysis,
16652
17159
  runFactGenerationPipeline: () => runFactGenerationPipeline
16653
17160
  });
16654
- import { existsSync as existsSync37, readdirSync as readdirSync9, readFileSync as readFileSync34 } from "fs";
17161
+ import { existsSync as existsSync38, readdirSync as readdirSync9, readFileSync as readFileSync35 } from "fs";
16655
17162
  import { join as join44 } from "path";
16656
17163
  async function runFactGenerationPipeline(factStore2, unerrDir) {
16657
17164
  const results = [];
@@ -16804,13 +17311,13 @@ async function generateFromSessionAnalysis(factStore2, unerrDir) {
16804
17311
  }
16805
17312
  function loadRecentSessions(unerrDir, limit) {
16806
17313
  const sessionsDir = join44(unerrDir, "sessions");
16807
- if (!existsSync37(sessionsDir)) return [];
17314
+ if (!existsSync38(sessionsDir)) return [];
16808
17315
  try {
16809
17316
  const files = readdirSync9(sessionsDir).filter((f) => f.endsWith(".jsonl")).sort().slice(-limit);
16810
17317
  const summaries = [];
16811
17318
  for (const file of files) {
16812
17319
  try {
16813
- const content = readFileSync34(join44(sessionsDir, file), "utf-8");
17320
+ const content = readFileSync35(join44(sessionsDir, file), "utf-8");
16814
17321
  const lines = content.trim().split("\n").filter(Boolean);
16815
17322
  const lastLine = lines[lines.length - 1];
16816
17323
  if (lastLine) {
@@ -17334,7 +17841,7 @@ var init_model_pricing = __esm({
17334
17841
  });
17335
17842
 
17336
17843
  // src/tracking/entity-rewind.ts
17337
- import { readFileSync as readFileSync35, writeFileSync as writeFileSync20 } from "fs";
17844
+ import { readFileSync as readFileSync36, writeFileSync as writeFileSync21 } from "fs";
17338
17845
  import { join as join45 } from "path";
17339
17846
  async function revertEntity(entityName, localGraph, projectRoot, filePath) {
17340
17847
  const driftEntity = await findDriftEntity(localGraph, entityName, filePath);
@@ -17412,12 +17919,12 @@ async function findDriftEntity(localGraph, entityName, filePath) {
17412
17919
  }
17413
17920
  async function removeAddedEntity(absPath, entity, localGraph) {
17414
17921
  try {
17415
- const content = readFileSync35(absPath, "utf-8");
17922
+ const content = readFileSync36(absPath, "utf-8");
17416
17923
  const lines = content.split("\n");
17417
17924
  const startIdx = entity.line_start - 1;
17418
17925
  const endIdx = entity.line_end;
17419
17926
  lines.splice(startIdx, endIdx - startIdx);
17420
- writeFileSync20(absPath, lines.join("\n"), "utf-8");
17927
+ writeFileSync21(absPath, lines.join("\n"), "utf-8");
17421
17928
  await localGraph.removeDriftEntity(entity.key);
17422
17929
  return {
17423
17930
  reverted: true,
@@ -17447,12 +17954,12 @@ async function restoreDeletedEntity(absPath, entity, localGraph) {
17447
17954
  };
17448
17955
  }
17449
17956
  try {
17450
- const content = readFileSync35(absPath, "utf-8");
17957
+ const content = readFileSync36(absPath, "utf-8");
17451
17958
  const lines = content.split("\n");
17452
17959
  const previousLines = entity.previous_body.split("\n");
17453
17960
  const insertIdx = Math.min(entity.line_start - 1, lines.length);
17454
17961
  lines.splice(insertIdx, 0, ...previousLines);
17455
- writeFileSync20(absPath, lines.join("\n"), "utf-8");
17962
+ writeFileSync21(absPath, lines.join("\n"), "utf-8");
17456
17963
  await localGraph.removeDriftEntity(entity.key);
17457
17964
  const endLine = insertIdx + previousLines.length;
17458
17965
  return {
@@ -17483,13 +17990,13 @@ async function restoreModifiedEntity(absPath, entity, localGraph) {
17483
17990
  };
17484
17991
  }
17485
17992
  try {
17486
- const content = readFileSync35(absPath, "utf-8");
17993
+ const content = readFileSync36(absPath, "utf-8");
17487
17994
  const lines = content.split("\n");
17488
17995
  const previousLines = entity.previous_body.split("\n");
17489
17996
  const startIdx = entity.line_start - 1;
17490
17997
  const deleteCount = entity.line_end - entity.line_start + 1;
17491
17998
  lines.splice(startIdx, deleteCount, ...previousLines);
17492
- writeFileSync20(absPath, lines.join("\n"), "utf-8");
17999
+ writeFileSync21(absPath, lines.join("\n"), "utf-8");
17493
18000
  await localGraph.removeDriftEntity(entity.key);
17494
18001
  const endLine = startIdx + previousLines.length;
17495
18002
  return {
@@ -17895,7 +18402,7 @@ __export(file_outline_exports, {
17895
18402
  buildFileOutline: () => buildFileOutline,
17896
18403
  fileOutlineTool: () => fileOutlineTool
17897
18404
  });
17898
- import { existsSync as existsSync38, readFileSync as readFileSync36 } from "fs";
18405
+ import { existsSync as existsSync39, readFileSync as readFileSync37 } from "fs";
17899
18406
  import { relative as relative2, resolve } from "path";
17900
18407
  function normRisk(rl) {
17901
18408
  const x2 = (rl ?? "low").toLowerCase();
@@ -17954,10 +18461,10 @@ function detectExportedNames(lines, scanLimit) {
17954
18461
  async function buildFileOutline(params) {
17955
18462
  const abs = resolve(params.cwd, params.filePathArg);
17956
18463
  const rel = relative2(params.cwd, abs).replace(/\\/g, "/") || params.filePathArg;
17957
- if (!existsSync38(abs)) {
18464
+ if (!existsSync39(abs)) {
17958
18465
  throw new Error(`File not found: ${abs}`);
17959
18466
  }
17960
- const raw = readFileSync36(abs);
18467
+ const raw = readFileSync37(abs);
17961
18468
  const sampleEnd = Math.min(raw.length, 8192);
17962
18469
  for (let i = 0; i < sampleEnd; i++) {
17963
18470
  if (raw[i] === 0) {
@@ -18105,7 +18612,7 @@ __export(file_read_protocol_exports, {
18105
18612
  runFileReadForRouter: () => runFileReadForRouter,
18106
18613
  runFileReadTool: () => runFileReadTool
18107
18614
  });
18108
- import { existsSync as existsSync39, readFileSync as readFileSync37 } from "fs";
18615
+ import { existsSync as existsSync40, readFileSync as readFileSync38 } from "fs";
18109
18616
  import { relative as relative3, resolve as resolve2 } from "path";
18110
18617
  function isGeneratedPath(rel) {
18111
18618
  return /(?:^|\/)node_modules\/|(?:^|\/)dist\/|\/\.next\/|\.generated\./.test(
@@ -18166,9 +18673,8 @@ function logFileRead(cwd, file, mode, totalLines, returnedLines, entity, tokenEs
18166
18673
  async function runFileReadForRouter(args, ctx) {
18167
18674
  const filePathArg = args.file_path;
18168
18675
  if (!filePathArg) throw new Error("file_read requires file_path");
18169
- const forceFull = args.force_full === true;
18170
- const purpose = args.purpose?.trim() || "explore";
18171
- const isFullRead = forceFull || purpose === "edit";
18676
+ const rawPurpose = args.purpose?.trim() || "explore";
18677
+ const purpose = rawPurpose === "edit" ? "explore" : rawPurpose;
18172
18678
  const entityName = args.entity?.trim();
18173
18679
  let entityWindowApplied = false;
18174
18680
  let entityMatchInfo;
@@ -18176,14 +18682,14 @@ async function runFileReadForRouter(args, ctx) {
18176
18682
  let limit = args.limit != null ? Math.min(MAX_READ_LINES, Math.max(1, Number(args.limit))) : void 0;
18177
18683
  const defaultBudget = purpose === "reference" ? 1e3 : 2e3;
18178
18684
  const tokenBudget = typeof args.token_budget === "number" && args.token_budget >= 100 ? args.token_budget : defaultBudget;
18179
- const budgetLines = isFullRead ? MAX_READ_LINES : Math.floor(tokenBudget * CHARS_PER_TOKEN4 / AVG_CHARS_PER_LINE);
18685
+ const budgetLines = Math.floor(tokenBudget * CHARS_PER_TOKEN4 / AVG_CHARS_PER_LINE);
18180
18686
  const abs = resolve2(ctx.cwd, filePathArg);
18181
18687
  const rel = relative3(ctx.cwd, abs).replace(/\\/g, "/") || filePathArg;
18182
18688
  const isOutOfProject = rel.startsWith("..");
18183
- if (!existsSync39(abs)) {
18689
+ if (!existsSync40(abs)) {
18184
18690
  return { content: { error: `File not found: ${abs}` } };
18185
18691
  }
18186
- const raw = readFileSync37(abs);
18692
+ const raw = readFileSync38(abs);
18187
18693
  const sampleEnd = Math.min(raw.length, 8192);
18188
18694
  for (let i = 0; i < sampleEnd; i++) {
18189
18695
  if (raw[i] === 0) {
@@ -18199,7 +18705,7 @@ async function runFileReadForRouter(args, ctx) {
18199
18705
  const lines = text2.split("\n");
18200
18706
  const totalLines = lines.length;
18201
18707
  const effectiveGate = Math.max(LINE_GATE, Math.min(HARD_GATE_CEILING, budgetLines));
18202
- if (totalLines > effectiveGate && totalLines > LINE_GATE && offset === void 0 && !entityName && !isFullRead) {
18708
+ if (totalLines > effectiveGate && totalLines > LINE_GATE && offset === void 0 && !entityName && !entityName) {
18203
18709
  const outline = await buildFileOutline({
18204
18710
  cwd: ctx.cwd,
18205
18711
  filePathArg,
@@ -18210,7 +18716,7 @@ async function runFileReadForRouter(args, ctx) {
18210
18716
  content: {
18211
18717
  ...outline,
18212
18718
  gated: true,
18213
- _gate_reason: `File has ${totalLines} lines (> ${effectiveGate}). Structure shown \u2014 call file_read again with entity or offset/limit, or force_full:true. NOTE: If you plan to Edit this file, you MUST call built-in Read (not file_read) on the target lines first.`
18719
+ _gate_reason: `File has ${totalLines} lines (> ${effectiveGate}). Structure shown \u2014 call file_read again with entity or offset/limit for targeted access.`
18214
18720
  },
18215
18721
  _layer6_meta: {
18216
18722
  format: "outline",
@@ -18348,7 +18854,7 @@ async function runFileReadForRouter(args, ctx) {
18348
18854
  limit = LOG_TAIL_LINES;
18349
18855
  }
18350
18856
  const effOffset = offset ?? 1;
18351
- const effLimit = limit ?? (isFullRead ? totalLines : Math.min(budgetLines, totalLines));
18857
+ const effLimit = limit ?? Math.min(budgetLines, totalLines);
18352
18858
  const sliced = lines.slice(effOffset - 1, effOffset - 1 + effLimit);
18353
18859
  const numbered = sliced.map((line, i) => `${effOffset + i} ${line}`).join("\n");
18354
18860
  let body = effOffset > 1 || sliced.length < totalLines || entityWindowApplied ? `${numbered}
@@ -18378,10 +18884,7 @@ ${body}`;
18378
18884
  total_lines: totalLines,
18379
18885
  total_chars: text2.length
18380
18886
  };
18381
- if (isFullRead && sliced.length === totalLines) {
18382
- meta.optimization = `file_read full (purpose:${purpose})`;
18383
- meta._edit_note = "file_read does NOT satisfy Edit's Read prerequisite. You must still call built-in Read before using Edit on this file.";
18384
- } else if (effOffset > 1 || sliced.length < totalLines || entityWindowApplied) {
18887
+ if (effOffset > 1 || sliced.length < totalLines || entityWindowApplied) {
18385
18888
  meta.optimization = `file_read window lines ${effOffset}-${effOffset + sliced.length - 1}`;
18386
18889
  }
18387
18890
  if (entityWindowApplied) {
@@ -18393,6 +18896,10 @@ ${body}`;
18393
18896
  if (isProbableLogPath(rel) && offset !== void 0 && totalLines > LINE_GATE) {
18394
18897
  meta.optimization = `${meta.optimization ?? "file_read"} \xB7 log_tail`;
18395
18898
  }
18899
+ if (isOutOfProject) {
18900
+ meta.out_of_project = true;
18901
+ meta._hint = "File is outside the indexed project. Graph features (references, callers) unavailable.";
18902
+ }
18396
18903
  const returnedLineCount = sliced.length;
18397
18904
  const readMode = entityWindowApplied ? "entity" : isProbableLogPath(rel) && offset !== void 0 && totalLines > LINE_GATE ? "log_tail" : effOffset > 1 || returnedLineCount < totalLines ? "slice" : "full";
18398
18905
  logFileRead(
@@ -19946,11 +20453,11 @@ var init_query_router = __esm({
19946
20453
  const entity = await this.resolveEntityWithOverlay(key);
19947
20454
  if (entity && entity.file_path && entity.start_line > 0) {
19948
20455
  try {
19949
- const { readFileSync: readFileSync57 } = await import("fs");
20456
+ const { readFileSync: readFileSync58 } = await import("fs");
19950
20457
  const { resolve: resolve3 } = await import("path");
19951
20458
  const cwd = this.projectRoot ?? process.cwd();
19952
20459
  const abs = resolve3(cwd, entity.file_path);
19953
- const lines = readFileSync57(abs, "utf-8").split("\n");
20460
+ const lines = readFileSync58(abs, "utf-8").split("\n");
19954
20461
  const start = entity.start_line - 1;
19955
20462
  const end = entity.end_line ?? lines.length;
19956
20463
  entity.body = lines.slice(start, end).join("\n");
@@ -19962,15 +20469,29 @@ var init_query_router = __esm({
19962
20469
  case "get_references": {
19963
20470
  const key = await this.resolveKeyArg(args.key);
19964
20471
  const direction = args.direction ?? "callers";
19965
- return direction === "callees" ? await this.localGraph.getCalleesOf(key) : await this.localGraph.getCallersOf(key);
20472
+ const limit = typeof args.limit === "number" && args.limit > 0 ? args.limit : 25;
20473
+ const raw = direction === "callees" ? await this.localGraph.getCalleesOf(key) : await this.localGraph.getCallersOf(key);
20474
+ const totalCount = raw.length;
20475
+ const capped = raw.slice(0, limit);
20476
+ const results = capped.map(({ body: _body, ...rest }) => rest);
20477
+ return {
20478
+ references: results,
20479
+ direction,
20480
+ total: totalCount,
20481
+ returned: results.length,
20482
+ truncated: totalCount > limit,
20483
+ ...totalCount > limit ? { _hint: `Showing ${limit} of ${totalCount}. Pass limit: ${totalCount} to see all.` } : {}
20484
+ };
19966
20485
  }
19967
20486
  case "get_callers": {
19968
20487
  const key = await this.resolveKeyArg(args.key);
19969
- return await this.localGraph.getCallersOf(key);
20488
+ const rawCallers = await this.localGraph.getCallersOf(key);
20489
+ return rawCallers.slice(0, 25).map(({ body: _body, ...rest }) => rest);
19970
20490
  }
19971
20491
  case "get_callees": {
19972
20492
  const key = await this.resolveKeyArg(args.key);
19973
- return await this.localGraph.getCalleesOf(key);
20493
+ const rawCallees = await this.localGraph.getCalleesOf(key);
20494
+ return rawCallees.slice(0, 25).map(({ body: _body, ...rest }) => rest);
19974
20495
  }
19975
20496
  case "get_imports": {
19976
20497
  const filePath = args.file_path;
@@ -20003,6 +20524,14 @@ var init_query_router = __esm({
20003
20524
  { entityKey: entityKey2, exceptions }
20004
20525
  );
20005
20526
  }
20527
+ if (filePath && (filePath.endsWith("/") || !/\.\w+$/.test(filePath))) {
20528
+ const dirPrefix = filePath.endsWith("/") ? filePath : `${filePath}/`;
20529
+ const allRules = await this.localGraph.getRules();
20530
+ return allRules.filter((rule) => {
20531
+ if (!rule.file_glob) return true;
20532
+ return rule.file_glob.includes(dirPrefix) || rule.file_glob.startsWith(dirPrefix) || dirPrefix.startsWith(rule.file_glob.replace(/\*.*$/, ""));
20533
+ });
20534
+ }
20006
20535
  return await this.localGraph.getRules(filePath);
20007
20536
  }
20008
20537
  case "check_rules": {
@@ -20038,7 +20567,37 @@ var init_query_router = __esm({
20038
20567
  if (!raw)
20039
20568
  throw new Error("get_business_context requires key or entity_key");
20040
20569
  const key = await this.resolveKeyArg(raw);
20041
- return await this.localGraph.getBusinessContext(key);
20570
+ const stored = await this.localGraph.getBusinessContext(key);
20571
+ if (stored) return stored;
20572
+ const entity = await this.localGraph.getEntity(key);
20573
+ if (!entity) return { error: `Entity not found: ${raw}` };
20574
+ const callers = await this.localGraph.getCallersOf(key);
20575
+ const callees = await this.localGraph.getCalleesOf(key);
20576
+ const pathParts = entity.file_path.split("/");
20577
+ const dirHint = pathParts.slice(-3, -1).join("/");
20578
+ const featureArea = dirHint || "root";
20579
+ let purpose;
20580
+ if (callers.length === 0 && callees.length > 0)
20581
+ purpose = "Entry point / top-level orchestrator";
20582
+ else if (callers.length > 5)
20583
+ purpose = `Widely-used utility (${callers.length} callers)`;
20584
+ else if (callees.length === 0 && callers.length > 0)
20585
+ purpose = "Leaf function (no outgoing calls)";
20586
+ else if (callers.length > 0 && callees.length > 0)
20587
+ purpose = `Internal function (${callers.length} callers, ${callees.length} callees)`;
20588
+ else
20589
+ purpose = "Standalone / unused function";
20590
+ return {
20591
+ purpose,
20592
+ taxonomy: `${entity.kind}/${featureArea}`,
20593
+ feature_area: featureArea,
20594
+ confidence: 0.3,
20595
+ entity,
20596
+ _source: "inferred",
20597
+ _hint: "This context was inferred from graph structure. Use record_fact to store explicit business context.",
20598
+ callers_count: callers.length,
20599
+ callees_count: callees.length
20600
+ };
20042
20601
  }
20043
20602
  case "get_conventions": {
20044
20603
  return await this.localGraph.getConventions();
@@ -20167,6 +20726,18 @@ var init_query_router = __esm({
20167
20726
  throw new Error(`Unknown local tool: ${toolName}`);
20168
20727
  }
20169
20728
  }
20729
+ /**
20730
+ * Get entity keys for a file path (from file_index).
20731
+ * Used by fact injection to call recallForFile with entity-scoped facts.
20732
+ */
20733
+ async getEntityKeysForFile(filePath) {
20734
+ try {
20735
+ const results = await this.localGraph.getEntitiesByFile(filePath);
20736
+ return results.map((e) => e.key);
20737
+ } catch {
20738
+ return [];
20739
+ }
20740
+ }
20170
20741
  /**
20171
20742
  * Resolve a key argument: if it looks like a hex hash (entity key), use as-is.
20172
20743
  * Otherwise, search by name and return the best match's key.
@@ -21476,7 +22047,7 @@ __export(causal_bridge_exports, {
21476
22047
  assembleCausalChain: () => assembleCausalChain,
21477
22048
  computeDurability: () => computeDurability
21478
22049
  });
21479
- import { existsSync as existsSync40, readFileSync as readFileSync38 } from "fs";
22050
+ import { existsSync as existsSync41, readFileSync as readFileSync39 } from "fs";
21480
22051
  import { join as join46 } from "path";
21481
22052
  function computeAggregateDurability(interactions) {
21482
22053
  if (interactions.length === 0) return 1;
@@ -21635,8 +22206,8 @@ var init_causal_bridge = __esm({
21635
22206
  }
21636
22207
  loadEntityLedgerEntries(entityKey2) {
21637
22208
  const ledgerPath = join46(this.unerrDir, "ledger", "shadow.jsonl");
21638
- if (!existsSync40(ledgerPath)) return [];
21639
- const content = readFileSync38(ledgerPath, "utf-8");
22209
+ if (!existsSync41(ledgerPath)) return [];
22210
+ const content = readFileSync39(ledgerPath, "utf-8");
21640
22211
  const lines = content.split("\n").filter((l) => l.trim().length > 0);
21641
22212
  const entries = [];
21642
22213
  for (const line of lines) {
@@ -22122,7 +22693,7 @@ var context_ledger_exports = {};
22122
22693
  __export(context_ledger_exports, {
22123
22694
  createContextLedger: () => createContextLedger
22124
22695
  });
22125
- import { existsSync as existsSync41, mkdirSync as mkdirSync23, readFileSync as readFileSync39, writeFileSync as writeFileSync21 } from "fs";
22696
+ import { existsSync as existsSync42, mkdirSync as mkdirSync23, readFileSync as readFileSync40, writeFileSync as writeFileSync22 } from "fs";
22126
22697
  import { join as join47 } from "path";
22127
22698
  function createContextLedger(unerrDir) {
22128
22699
  const stateDir = join47(unerrDir, "state");
@@ -22130,7 +22701,7 @@ function createContextLedger(unerrDir) {
22130
22701
  let records = [];
22131
22702
  let index = /* @__PURE__ */ new Map();
22132
22703
  function ensureDir() {
22133
- if (!existsSync41(stateDir)) {
22704
+ if (!existsSync42(stateDir)) {
22134
22705
  mkdirSync23(stateDir, { recursive: true });
22135
22706
  }
22136
22707
  }
@@ -22139,13 +22710,13 @@ function createContextLedger(unerrDir) {
22139
22710
  }
22140
22711
  function load() {
22141
22712
  ensureDir();
22142
- if (!existsSync41(filePath)) {
22713
+ if (!existsSync42(filePath)) {
22143
22714
  records = [];
22144
22715
  index = /* @__PURE__ */ new Map();
22145
22716
  return /* @__PURE__ */ new Map();
22146
22717
  }
22147
22718
  try {
22148
- const raw = readFileSync39(filePath, "utf-8");
22719
+ const raw = readFileSync40(filePath, "utf-8");
22149
22720
  const parsed = JSON.parse(raw);
22150
22721
  records = Array.isArray(parsed) ? parsed : [];
22151
22722
  } catch {
@@ -22181,7 +22752,7 @@ function createContextLedger(unerrDir) {
22181
22752
  records = newRecords;
22182
22753
  index = delivered;
22183
22754
  try {
22184
- writeFileSync21(filePath, JSON.stringify(records, null, 2), "utf-8");
22755
+ writeFileSync22(filePath, JSON.stringify(records, null, 2), "utf-8");
22185
22756
  } catch {
22186
22757
  }
22187
22758
  }
@@ -23052,11 +23623,11 @@ __export(intent_correlator_exports, {
23052
23623
  IntentCorrelator: () => IntentCorrelator
23053
23624
  });
23054
23625
  import {
23055
- existsSync as existsSync42,
23626
+ existsSync as existsSync43,
23056
23627
  mkdirSync as mkdirSync24,
23057
- readFileSync as readFileSync40,
23628
+ readFileSync as readFileSync41,
23058
23629
  renameSync,
23059
- writeFileSync as writeFileSync22
23630
+ writeFileSync as writeFileSync23
23060
23631
  } from "fs";
23061
23632
  import { join as join48 } from "path";
23062
23633
  function extractFiles4(args) {
@@ -23108,7 +23679,7 @@ var init_intent_correlator = __esm({
23108
23679
  constructor(unerrDir) {
23109
23680
  this.ledgerDir = join48(unerrDir, "ledger");
23110
23681
  this.pendingPath = join48(this.ledgerDir, "pending_correlations.json");
23111
- if (!existsSync42(this.ledgerDir)) {
23682
+ if (!existsSync43(this.ledgerDir)) {
23112
23683
  mkdirSync24(this.ledgerDir, { recursive: true });
23113
23684
  }
23114
23685
  this.load();
@@ -23200,9 +23771,9 @@ var init_intent_correlator = __esm({
23200
23771
  }
23201
23772
  // ── Persistence ─────────────────────────────────────────────────
23202
23773
  load() {
23203
- if (!existsSync42(this.pendingPath)) return;
23774
+ if (!existsSync43(this.pendingPath)) return;
23204
23775
  try {
23205
- const raw = readFileSync40(this.pendingPath, "utf-8");
23776
+ const raw = readFileSync41(this.pendingPath, "utf-8");
23206
23777
  const parsed = JSON.parse(raw);
23207
23778
  if (Array.isArray(parsed)) {
23208
23779
  this.pending = parsed;
@@ -23214,7 +23785,7 @@ var init_intent_correlator = __esm({
23214
23785
  save() {
23215
23786
  try {
23216
23787
  const tmpPath = `${this.pendingPath}.tmp`;
23217
- writeFileSync22(tmpPath, JSON.stringify(this.pending, null, 2), "utf-8");
23788
+ writeFileSync23(tmpPath, JSON.stringify(this.pending, null, 2), "utf-8");
23218
23789
  renameSync(tmpPath, this.pendingPath);
23219
23790
  } catch (err) {
23220
23791
  process.stderr.write(
@@ -23234,12 +23805,12 @@ __export(working_snapshots_exports, {
23234
23805
  });
23235
23806
  import { randomBytes as randomBytes3 } from "crypto";
23236
23807
  import {
23237
- existsSync as existsSync43,
23808
+ existsSync as existsSync44,
23238
23809
  mkdirSync as mkdirSync25,
23239
- readFileSync as readFileSync41,
23810
+ readFileSync as readFileSync42,
23240
23811
  readdirSync as readdirSync10,
23241
23812
  rmSync as rmSync2,
23242
- writeFileSync as writeFileSync23
23813
+ writeFileSync as writeFileSync24
23243
23814
  } from "fs";
23244
23815
  import { join as join49 } from "path";
23245
23816
  var _log2, MAX_SNAPSHOTS, AUTO_SNAPSHOT_COOLDOWN_MS, WorkingSnapshotStore;
@@ -23260,7 +23831,7 @@ var init_working_snapshots = __esm({
23260
23831
  constructor(unerrDir) {
23261
23832
  this.unerrDir = unerrDir;
23262
23833
  this.snapshotDir = join49(unerrDir, "snapshots");
23263
- if (!existsSync43(this.snapshotDir)) {
23834
+ if (!existsSync44(this.snapshotDir)) {
23264
23835
  mkdirSync25(this.snapshotDir, { recursive: true });
23265
23836
  }
23266
23837
  }
@@ -23279,7 +23850,7 @@ var init_working_snapshots = __esm({
23279
23850
  sessionId: opts.sessionId,
23280
23851
  processed: false
23281
23852
  };
23282
- writeFileSync23(
23853
+ writeFileSync24(
23283
23854
  join49(this.snapshotDir, `${id}.json`),
23284
23855
  JSON.stringify(snapshot, null, 2),
23285
23856
  "utf-8"
@@ -23295,9 +23866,9 @@ var init_working_snapshots = __esm({
23295
23866
  */
23296
23867
  get(snapshotId) {
23297
23868
  const filePath = join49(this.snapshotDir, `${snapshotId}.json`);
23298
- if (!existsSync43(filePath)) return null;
23869
+ if (!existsSync44(filePath)) return null;
23299
23870
  try {
23300
- return JSON.parse(readFileSync41(filePath, "utf-8"));
23871
+ return JSON.parse(readFileSync42(filePath, "utf-8"));
23301
23872
  } catch {
23302
23873
  return null;
23303
23874
  }
@@ -23306,14 +23877,14 @@ var init_working_snapshots = __esm({
23306
23877
  * List all snapshots, most recent first.
23307
23878
  */
23308
23879
  list() {
23309
- if (!existsSync43(this.snapshotDir)) return [];
23880
+ if (!existsSync44(this.snapshotDir)) return [];
23310
23881
  const files = readdirSync10(this.snapshotDir).filter(
23311
23882
  (f) => f.endsWith(".json")
23312
23883
  );
23313
23884
  const snapshots = [];
23314
23885
  for (const file of files) {
23315
23886
  try {
23316
- const raw = readFileSync41(join49(this.snapshotDir, file), "utf-8");
23887
+ const raw = readFileSync42(join49(this.snapshotDir, file), "utf-8");
23317
23888
  snapshots.push(JSON.parse(raw));
23318
23889
  } catch {
23319
23890
  }
@@ -23353,7 +23924,7 @@ var init_working_snapshots = __esm({
23353
23924
  const snapshot = this.get(snapshotId);
23354
23925
  if (!snapshot) return;
23355
23926
  snapshot.processed = true;
23356
- writeFileSync23(
23927
+ writeFileSync24(
23357
23928
  join49(this.snapshotDir, `${snapshotId}.json`),
23358
23929
  JSON.stringify(snapshot, null, 2),
23359
23930
  "utf-8"
@@ -23370,7 +23941,7 @@ var init_working_snapshots = __esm({
23370
23941
  */
23371
23942
  delete(snapshotId) {
23372
23943
  const filePath = join49(this.snapshotDir, `${snapshotId}.json`);
23373
- if (!existsSync43(filePath)) return false;
23944
+ if (!existsSync44(filePath)) return false;
23374
23945
  rmSync2(filePath);
23375
23946
  return true;
23376
23947
  }
@@ -23379,9 +23950,9 @@ var init_working_snapshots = __esm({
23379
23950
  */
23380
23951
  getTimelineBranch() {
23381
23952
  const contextPath = join49(this.unerrDir, "branch_context.json");
23382
- if (!existsSync43(contextPath)) return 0;
23953
+ if (!existsSync44(contextPath)) return 0;
23383
23954
  try {
23384
- const ctx = JSON.parse(readFileSync41(contextPath, "utf-8"));
23955
+ const ctx = JSON.parse(readFileSync42(contextPath, "utf-8"));
23385
23956
  return ctx.timelineBranch ?? 0;
23386
23957
  } catch {
23387
23958
  return 0;
@@ -23393,16 +23964,16 @@ var init_working_snapshots = __esm({
23393
23964
  incrementTimelineBranch() {
23394
23965
  const contextPath = join49(this.unerrDir, "branch_context.json");
23395
23966
  let ctx = {};
23396
- if (existsSync43(contextPath)) {
23967
+ if (existsSync44(contextPath)) {
23397
23968
  try {
23398
- ctx = JSON.parse(readFileSync41(contextPath, "utf-8"));
23969
+ ctx = JSON.parse(readFileSync42(contextPath, "utf-8"));
23399
23970
  } catch {
23400
23971
  ctx = {};
23401
23972
  }
23402
23973
  }
23403
23974
  const current = ctx.timelineBranch ?? 0;
23404
23975
  ctx.timelineBranch = current + 1;
23405
- writeFileSync23(contextPath, JSON.stringify(ctx, null, 2), "utf-8");
23976
+ writeFileSync24(contextPath, JSON.stringify(ctx, null, 2), "utf-8");
23406
23977
  return current + 1;
23407
23978
  }
23408
23979
  /**
@@ -23568,7 +24139,7 @@ var quality_signals_exports = {};
23568
24139
  __export(quality_signals_exports, {
23569
24140
  QualitySignalTracker: () => QualitySignalTracker
23570
24141
  });
23571
- import { existsSync as existsSync44, readFileSync as readFileSync42, writeFileSync as writeFileSync24 } from "fs";
24142
+ import { existsSync as existsSync45, readFileSync as readFileSync43, writeFileSync as writeFileSync25 } from "fs";
23572
24143
  import { join as join50 } from "path";
23573
24144
  function computeDurabilityFromAge(survivalMs) {
23574
24145
  if (survivalMs < FRAGILE_THRESHOLD_MS) {
@@ -23689,11 +24260,11 @@ var init_quality_signals = __esm({
23689
24260
  save() {
23690
24261
  try {
23691
24262
  const dir = join50(this.signalsPath, "..");
23692
- if (!existsSync44(dir)) {
24263
+ if (!existsSync45(dir)) {
23693
24264
  const { mkdirSync: mkdirSync39 } = __require("fs");
23694
24265
  mkdirSync39(dir, { recursive: true });
23695
24266
  }
23696
- writeFileSync24(
24267
+ writeFileSync25(
23697
24268
  this.signalsPath,
23698
24269
  JSON.stringify(this.signals, null, 2),
23699
24270
  "utf-8"
@@ -23735,7 +24306,7 @@ var init_quality_signals = __esm({
23735
24306
  * Load signals from disk.
23736
24307
  */
23737
24308
  load() {
23738
- if (!existsSync44(this.signalsPath)) {
24309
+ if (!existsSync45(this.signalsPath)) {
23739
24310
  return {
23740
24311
  durabilityScores: {},
23741
24312
  corrections: [],
@@ -23744,7 +24315,7 @@ var init_quality_signals = __esm({
23744
24315
  }
23745
24316
  try {
23746
24317
  return JSON.parse(
23747
- readFileSync42(this.signalsPath, "utf-8")
24318
+ readFileSync43(this.signalsPath, "utf-8")
23748
24319
  );
23749
24320
  } catch {
23750
24321
  return {
@@ -24761,7 +25332,7 @@ var incomplete_work_exports = {};
24761
25332
  __export(incomplete_work_exports, {
24762
25333
  IncompleteWorkDetector: () => IncompleteWorkDetector
24763
25334
  });
24764
- import { existsSync as existsSync45, mkdirSync as mkdirSync26, readFileSync as readFileSync43, writeFileSync as writeFileSync25 } from "fs";
25335
+ import { existsSync as existsSync46, mkdirSync as mkdirSync26, readFileSync as readFileSync44, writeFileSync as writeFileSync26 } from "fs";
24765
25336
  import { join as join51 } from "path";
24766
25337
  function severityRank(severity) {
24767
25338
  switch (severity) {
@@ -24956,9 +25527,9 @@ var init_incomplete_work = __esm({
24956
25527
  if (!this.unerrDir) return false;
24957
25528
  try {
24958
25529
  const stateDir = join51(this.unerrDir, "state");
24959
- if (!existsSync45(stateDir)) mkdirSync26(stateDir, { recursive: true });
25530
+ if (!existsSync46(stateDir)) mkdirSync26(stateDir, { recursive: true });
24960
25531
  const filePath = join51(stateDir, PERSISTENCE_FILE);
24961
- writeFileSync25(
25532
+ writeFileSync26(
24962
25533
  filePath,
24963
25534
  JSON.stringify({
24964
25535
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -24978,8 +25549,8 @@ var init_incomplete_work = __esm({
24978
25549
  static readPersistedItems(unerrDir) {
24979
25550
  try {
24980
25551
  const filePath = join51(unerrDir, "state", PERSISTENCE_FILE);
24981
- if (!existsSync45(filePath)) return [];
24982
- const data = JSON.parse(readFileSync43(filePath, "utf-8"));
25552
+ if (!existsSync46(filePath)) return [];
25553
+ const data = JSON.parse(readFileSync44(filePath, "utf-8"));
24983
25554
  return data.items ?? [];
24984
25555
  } catch {
24985
25556
  return [];
@@ -26450,7 +27021,7 @@ var transport_mux_exports = {};
26450
27021
  __export(transport_mux_exports, {
26451
27022
  TransportMux: () => TransportMux
26452
27023
  });
26453
- import { existsSync as existsSync46, unlinkSync as unlinkSync7 } from "fs";
27024
+ import { existsSync as existsSync47, unlinkSync as unlinkSync7 } from "fs";
26454
27025
  import { createServer as createServer2 } from "net";
26455
27026
  var _log4, TransportMux;
26456
27027
  var init_transport_mux = __esm({
@@ -26490,7 +27061,7 @@ var init_transport_mux = __esm({
26490
27061
  * Start listening for secondary clients on the Unix domain socket.
26491
27062
  */
26492
27063
  start() {
26493
- if (existsSync46(this.sockPath)) {
27064
+ if (existsSync47(this.sockPath)) {
26494
27065
  try {
26495
27066
  unlinkSync7(this.sockPath);
26496
27067
  } catch {
@@ -26523,7 +27094,7 @@ var init_transport_mux = __esm({
26523
27094
  this.server.close();
26524
27095
  this.server = null;
26525
27096
  }
26526
- if (existsSync46(this.sockPath)) {
27097
+ if (existsSync47(this.sockPath)) {
26527
27098
  try {
26528
27099
  unlinkSync7(this.sockPath);
26529
27100
  } catch {
@@ -26671,7 +27242,7 @@ __export(git_trailers_exports, {
26671
27242
  parseTrailersFromMessage: () => parseTrailersFromMessage,
26672
27243
  uninstallPrepareCommitMsgHook: () => uninstallPrepareCommitMsgHook
26673
27244
  });
26674
- import { existsSync as existsSync47, readFileSync as readFileSync44, writeFileSync as writeFileSync26 } from "fs";
27245
+ import { existsSync as existsSync48, readFileSync as readFileSync45, writeFileSync as writeFileSync27 } from "fs";
26675
27246
  import { join as join52 } from "path";
26676
27247
  function getCommitTrailers(ledger, timelineBranch, branch) {
26677
27248
  const recent = ledger.getRecentEntries(1);
@@ -26693,14 +27264,14 @@ function formatTrailers(trailers) {
26693
27264
  }
26694
27265
  function installPrepareCommitMsgHook(projectRoot) {
26695
27266
  const hooksDir = join52(projectRoot, ".git", "hooks");
26696
- if (!existsSync47(hooksDir)) {
27267
+ if (!existsSync48(hooksDir)) {
26697
27268
  return false;
26698
27269
  }
26699
27270
  const hookPath = join52(hooksDir, "prepare-commit-msg");
26700
27271
  const marker = "# unerr-trailer-injection";
26701
- if (existsSync47(hookPath)) {
27272
+ if (existsSync48(hookPath)) {
26702
27273
  try {
26703
- const existing = readFileSync44(hookPath, "utf-8");
27274
+ const existing = readFileSync45(hookPath, "utf-8");
26704
27275
  if (existing.includes(marker)) {
26705
27276
  return true;
26706
27277
  }
@@ -26708,7 +27279,7 @@ function installPrepareCommitMsgHook(projectRoot) {
26708
27279
 
26709
27280
  ${marker}
26710
27281
  ${generateHookScript()}`;
26711
- writeFileSync26(hookPath, existing + appendSection, { mode: 493 });
27282
+ writeFileSync27(hookPath, existing + appendSection, { mode: 493 });
26712
27283
  _log5.info(
26713
27284
  "Appended trailer injection to existing prepare-commit-msg hook"
26714
27285
  );
@@ -26721,7 +27292,7 @@ ${generateHookScript()}`;
26721
27292
  ${marker}
26722
27293
  ${generateHookScript()}`;
26723
27294
  try {
26724
- writeFileSync26(hookPath, hookContent, { mode: 493 });
27295
+ writeFileSync27(hookPath, hookContent, { mode: 493 });
26725
27296
  _log5.info("Installed prepare-commit-msg hook for trailer injection");
26726
27297
  return true;
26727
27298
  } catch {
@@ -26730,9 +27301,9 @@ ${generateHookScript()}`;
26730
27301
  }
26731
27302
  function uninstallPrepareCommitMsgHook(projectRoot) {
26732
27303
  const hookPath = join52(projectRoot, ".git", "hooks", "prepare-commit-msg");
26733
- if (!existsSync47(hookPath)) return true;
27304
+ if (!existsSync48(hookPath)) return true;
26734
27305
  try {
26735
- const content = readFileSync44(hookPath, "utf-8");
27306
+ const content = readFileSync45(hookPath, "utf-8");
26736
27307
  const marker = "# unerr-trailer-injection";
26737
27308
  if (!content.includes(marker)) return true;
26738
27309
  const lines = content.split("\n");
@@ -26745,7 +27316,7 @@ function uninstallPrepareCommitMsgHook(projectRoot) {
26745
27316
  } else {
26746
27317
  const markerIdx = content.indexOf(marker);
26747
27318
  if (markerIdx > 0) {
26748
- writeFileSync26(hookPath, `${content.slice(0, markerIdx).trimEnd()}
27319
+ writeFileSync27(hookPath, `${content.slice(0, markerIdx).trimEnd()}
26749
27320
  `, {
26750
27321
  mode: 493
26751
27322
  });
@@ -26897,13 +27468,13 @@ var init_http_transport = __esm({
26897
27468
 
26898
27469
  // src/tracking/branch-snapshot.ts
26899
27470
  import {
26900
- existsSync as existsSync48,
27471
+ existsSync as existsSync49,
26901
27472
  mkdirSync as mkdirSync28,
26902
- readFileSync as readFileSync45,
27473
+ readFileSync as readFileSync46,
26903
27474
  readdirSync as readdirSync11,
26904
27475
  rmSync as rmSync3,
26905
27476
  statSync as statSync7,
26906
- writeFileSync as writeFileSync27
27477
+ writeFileSync as writeFileSync28
26907
27478
  } from "fs";
26908
27479
  import { join as join53 } from "path";
26909
27480
  function sanitizeBranchName(branch) {
@@ -26938,7 +27509,7 @@ var init_branch_snapshot = __esm({
26938
27509
  }
26939
27510
  const dirName = sanitizeBranchName(branch);
26940
27511
  const snapshotDir = join53(this.branchDir, dirName);
26941
- if (!existsSync48(snapshotDir)) {
27512
+ if (!existsSync49(snapshotDir)) {
26942
27513
  mkdirSync28(snapshotDir, { recursive: true });
26943
27514
  }
26944
27515
  const snapshot = {
@@ -26948,12 +27519,12 @@ var init_branch_snapshot = __esm({
26948
27519
  fileHashes: fileHashState,
26949
27520
  savedAt: (/* @__PURE__ */ new Date()).toISOString()
26950
27521
  };
26951
- writeFileSync27(
27522
+ writeFileSync28(
26952
27523
  join53(snapshotDir, OVERLAY_FILE),
26953
27524
  JSON.stringify(snapshot, null, 2),
26954
27525
  "utf-8"
26955
27526
  );
26956
- writeFileSync27(
27527
+ writeFileSync28(
26957
27528
  join53(snapshotDir, HASHES_FILE),
26958
27529
  JSON.stringify(fileHashState, null, 2),
26959
27530
  "utf-8"
@@ -26972,12 +27543,12 @@ var init_branch_snapshot = __esm({
26972
27543
  const dirName = sanitizeBranchName(branch);
26973
27544
  const snapshotDir = join53(this.branchDir, dirName);
26974
27545
  const overlayPath = join53(snapshotDir, OVERLAY_FILE);
26975
- if (!existsSync48(overlayPath)) {
27546
+ if (!existsSync49(overlayPath)) {
26976
27547
  log16.info(`No snapshot for branch ${branch} \u2014 first visit`);
26977
27548
  return null;
26978
27549
  }
26979
27550
  try {
26980
- const raw = readFileSync45(overlayPath, "utf-8");
27551
+ const raw = readFileSync46(overlayPath, "utf-8");
26981
27552
  const snapshot = JSON.parse(raw);
26982
27553
  for (const entity of snapshot.entities) {
26983
27554
  await localGraph.upsertDriftEntity(entity);
@@ -26988,7 +27559,7 @@ var init_branch_snapshot = __esm({
26988
27559
  }
26989
27560
  }
26990
27561
  const now = /* @__PURE__ */ new Date();
26991
- writeFileSync27(
27562
+ writeFileSync28(
26992
27563
  join53(snapshotDir, ".last_access"),
26993
27564
  now.toISOString(),
26994
27565
  "utf-8"
@@ -27009,7 +27580,7 @@ var init_branch_snapshot = __esm({
27009
27580
  */
27010
27581
  hasSnapshot(branch) {
27011
27582
  const dirName = sanitizeBranchName(branch);
27012
- return existsSync48(join53(this.branchDir, dirName, OVERLAY_FILE));
27583
+ return existsSync49(join53(this.branchDir, dirName, OVERLAY_FILE));
27013
27584
  }
27014
27585
  /**
27015
27586
  * Get the file hash state from a branch snapshot.
@@ -27017,9 +27588,9 @@ var init_branch_snapshot = __esm({
27017
27588
  getSnapshotFileHashes(branch) {
27018
27589
  const dirName = sanitizeBranchName(branch);
27019
27590
  const hashesPath = join53(this.branchDir, dirName, HASHES_FILE);
27020
- if (!existsSync48(hashesPath)) return null;
27591
+ if (!existsSync49(hashesPath)) return null;
27021
27592
  try {
27022
- const raw = readFileSync45(hashesPath, "utf-8");
27593
+ const raw = readFileSync46(hashesPath, "utf-8");
27023
27594
  return JSON.parse(raw);
27024
27595
  } catch {
27025
27596
  return null;
@@ -27031,7 +27602,7 @@ var init_branch_snapshot = __esm({
27031
27602
  deleteSnapshot(branch) {
27032
27603
  const dirName = sanitizeBranchName(branch);
27033
27604
  const snapshotDir = join53(this.branchDir, dirName);
27034
- if (!existsSync48(snapshotDir)) return false;
27605
+ if (!existsSync49(snapshotDir)) return false;
27035
27606
  rmSync3(snapshotDir, { recursive: true, force: true });
27036
27607
  log16.info(`Deleted branch snapshot: ${branch}`);
27037
27608
  return true;
@@ -27040,7 +27611,7 @@ var init_branch_snapshot = __esm({
27040
27611
  * Garbage-collect snapshots for branches that no longer exist in git.
27041
27612
  */
27042
27613
  async garbageCollect() {
27043
- if (!existsSync48(this.branchDir)) return 0;
27614
+ if (!existsSync49(this.branchDir)) return 0;
27044
27615
  const gitBranches = new Set(await listBranches(this.projectRoot));
27045
27616
  if (gitBranches.size === 0) return 0;
27046
27617
  const snapshots = this.listSnapshots();
@@ -27059,20 +27630,20 @@ var init_branch_snapshot = __esm({
27059
27630
  * List all branch snapshots, sorted by most recently accessed first.
27060
27631
  */
27061
27632
  listSnapshots() {
27062
- if (!existsSync48(this.branchDir)) return [];
27633
+ if (!existsSync49(this.branchDir)) return [];
27063
27634
  try {
27064
27635
  const entries = readdirSync11(this.branchDir, { withFileTypes: true });
27065
27636
  const snapshots = [];
27066
27637
  for (const entry of entries) {
27067
27638
  if (!entry.isDirectory()) continue;
27068
27639
  const overlayPath = join53(this.branchDir, entry.name, OVERLAY_FILE);
27069
- if (!existsSync48(overlayPath)) continue;
27640
+ if (!existsSync49(overlayPath)) continue;
27070
27641
  try {
27071
- const raw = readFileSync45(overlayPath, "utf-8");
27642
+ const raw = readFileSync46(overlayPath, "utf-8");
27072
27643
  const snapshot = JSON.parse(raw);
27073
27644
  const accessPath = join53(this.branchDir, entry.name, ".last_access");
27074
27645
  let accessedAt;
27075
- if (existsSync48(accessPath)) {
27646
+ if (existsSync49(accessPath)) {
27076
27647
  accessedAt = statSync7(accessPath).mtime;
27077
27648
  } else {
27078
27649
  accessedAt = statSync7(overlayPath).mtime;
@@ -27116,11 +27687,11 @@ __export(file_hash_state_exports, {
27116
27687
  });
27117
27688
  import { createHash as createHash3 } from "crypto";
27118
27689
  import {
27119
- existsSync as existsSync49,
27690
+ existsSync as existsSync50,
27120
27691
  mkdirSync as mkdirSync29,
27121
- readFileSync as readFileSync46,
27692
+ readFileSync as readFileSync47,
27122
27693
  renameSync as renameSync2,
27123
- writeFileSync as writeFileSync28
27694
+ writeFileSync as writeFileSync29
27124
27695
  } from "fs";
27125
27696
  import { join as join54 } from "path";
27126
27697
  function contentSha256(content) {
@@ -27182,11 +27753,11 @@ var init_file_hash_state = __esm({
27182
27753
  * Persist state to disk atomically (write .tmp → rename).
27183
27754
  */
27184
27755
  save() {
27185
- if (!existsSync49(this.stateDir)) {
27756
+ if (!existsSync50(this.stateDir)) {
27186
27757
  mkdirSync29(this.stateDir, { recursive: true });
27187
27758
  }
27188
27759
  const tmpPath = `${this.statePath}.tmp`;
27189
- writeFileSync28(tmpPath, JSON.stringify(this.state, null, 2), "utf-8");
27760
+ writeFileSync29(tmpPath, JSON.stringify(this.state, null, 2), "utf-8");
27190
27761
  renameSync2(tmpPath, this.statePath);
27191
27762
  }
27192
27763
  /**
@@ -27209,11 +27780,11 @@ var init_file_hash_state = __esm({
27209
27780
  this.state = { files: { ...snapshot.files } };
27210
27781
  }
27211
27782
  load() {
27212
- if (!existsSync49(this.statePath)) {
27783
+ if (!existsSync50(this.statePath)) {
27213
27784
  return { files: {} };
27214
27785
  }
27215
27786
  try {
27216
- const raw = readFileSync46(this.statePath, "utf-8");
27787
+ const raw = readFileSync47(this.statePath, "utf-8");
27217
27788
  return JSON.parse(raw);
27218
27789
  } catch {
27219
27790
  return { files: {} };
@@ -27225,13 +27796,13 @@ var init_file_hash_state = __esm({
27225
27796
 
27226
27797
  // src/tracking/stash-manager.ts
27227
27798
  import {
27228
- existsSync as existsSync50,
27799
+ existsSync as existsSync51,
27229
27800
  mkdirSync as mkdirSync30,
27230
- readFileSync as readFileSync47,
27801
+ readFileSync as readFileSync48,
27231
27802
  readdirSync as readdirSync12,
27232
27803
  rmSync as rmSync4,
27233
27804
  statSync as statSync8,
27234
- writeFileSync as writeFileSync29
27805
+ writeFileSync as writeFileSync30
27235
27806
  } from "fs";
27236
27807
  import { join as join55 } from "path";
27237
27808
  var MAX_STASH_SNAPSHOTS, OVERLAY_FILE2, HASHES_FILE2, _log6, StashManager;
@@ -27298,7 +27869,7 @@ var init_stash_manager = __esm({
27298
27869
  }
27299
27870
  const snapshotId = stashRef.slice(0, 12);
27300
27871
  const snapshotDir = join55(this.stashDir, snapshotId);
27301
- if (!existsSync50(snapshotDir)) {
27872
+ if (!existsSync51(snapshotDir)) {
27302
27873
  mkdirSync30(snapshotDir, { recursive: true });
27303
27874
  }
27304
27875
  const snapshot = {
@@ -27308,12 +27879,12 @@ var init_stash_manager = __esm({
27308
27879
  fileHashes: fileHashState,
27309
27880
  savedAt: (/* @__PURE__ */ new Date()).toISOString()
27310
27881
  };
27311
- writeFileSync29(
27882
+ writeFileSync30(
27312
27883
  join55(snapshotDir, OVERLAY_FILE2),
27313
27884
  JSON.stringify(snapshot, null, 2),
27314
27885
  "utf-8"
27315
27886
  );
27316
- writeFileSync29(
27887
+ writeFileSync30(
27317
27888
  join55(snapshotDir, HASHES_FILE2),
27318
27889
  JSON.stringify(fileHashState, null, 2),
27319
27890
  "utf-8"
@@ -27337,12 +27908,12 @@ var init_stash_manager = __esm({
27337
27908
  const latest = snapshots[0];
27338
27909
  const snapshotDir = join55(this.stashDir, latest.id);
27339
27910
  const overlayPath = join55(snapshotDir, OVERLAY_FILE2);
27340
- if (!existsSync50(overlayPath)) {
27911
+ if (!existsSync51(overlayPath)) {
27341
27912
  _log6.warn(`Snapshot ${latest.id} missing overlay file`);
27342
27913
  return 0;
27343
27914
  }
27344
27915
  try {
27345
- const raw = readFileSync47(overlayPath, "utf-8");
27916
+ const raw = readFileSync48(overlayPath, "utf-8");
27346
27917
  const snapshot = JSON.parse(raw);
27347
27918
  for (const entity of snapshot.entities) {
27348
27919
  await localGraph.upsertDriftEntity(entity);
@@ -27373,9 +27944,9 @@ var init_stash_manager = __esm({
27373
27944
  if (snapshots.length === 0) return null;
27374
27945
  const latest = snapshots[0];
27375
27946
  const hashesPath = join55(this.stashDir, latest.id, HASHES_FILE2);
27376
- if (!existsSync50(hashesPath)) return null;
27947
+ if (!existsSync51(hashesPath)) return null;
27377
27948
  try {
27378
- const raw = readFileSync47(hashesPath, "utf-8");
27949
+ const raw = readFileSync48(hashesPath, "utf-8");
27379
27950
  return JSON.parse(raw);
27380
27951
  } catch {
27381
27952
  return null;
@@ -27387,7 +27958,7 @@ var init_stash_manager = __esm({
27387
27958
  dropSnapshot(stashRef) {
27388
27959
  const snapshotId = stashRef.slice(0, 12);
27389
27960
  const snapshotDir = join55(this.stashDir, snapshotId);
27390
- if (!existsSync50(snapshotDir)) return false;
27961
+ if (!existsSync51(snapshotDir)) return false;
27391
27962
  rmSync4(snapshotDir, { recursive: true, force: true });
27392
27963
  _log6.info(`Dropped stash snapshot: ${snapshotId}`);
27393
27964
  return true;
@@ -27396,14 +27967,14 @@ var init_stash_manager = __esm({
27396
27967
  * List all stash snapshots, sorted by most recent first.
27397
27968
  */
27398
27969
  listSnapshots() {
27399
- if (!existsSync50(this.stashDir)) return [];
27970
+ if (!existsSync51(this.stashDir)) return [];
27400
27971
  try {
27401
27972
  const entries = readdirSync12(this.stashDir, { withFileTypes: true });
27402
27973
  const snapshots = [];
27403
27974
  for (const entry of entries) {
27404
27975
  if (!entry.isDirectory()) continue;
27405
27976
  const overlayPath = join55(this.stashDir, entry.name, OVERLAY_FILE2);
27406
- if (!existsSync50(overlayPath)) continue;
27977
+ if (!existsSync51(overlayPath)) continue;
27407
27978
  try {
27408
27979
  const stat2 = statSync8(overlayPath);
27409
27980
  snapshots.push({ id: entry.name, savedAt: stat2.mtime });
@@ -27421,9 +27992,9 @@ var init_stash_manager = __esm({
27421
27992
  */
27422
27993
  readStashRef() {
27423
27994
  const stashPath = join55(this.gitDir, "refs", "stash");
27424
- if (!existsSync50(stashPath)) return null;
27995
+ if (!existsSync51(stashPath)) return null;
27425
27996
  try {
27426
- return readFileSync47(stashPath, "utf-8").trim() || null;
27997
+ return readFileSync48(stashPath, "utf-8").trim() || null;
27427
27998
  } catch {
27428
27999
  return null;
27429
28000
  }
@@ -27433,9 +28004,9 @@ var init_stash_manager = __esm({
27433
28004
  */
27434
28005
  getStashCount() {
27435
28006
  const logPath = join55(this.gitDir, "logs", "refs", "stash");
27436
- if (!existsSync50(logPath)) return 0;
28007
+ if (!existsSync51(logPath)) return 0;
27437
28008
  try {
27438
- const content = readFileSync47(logPath, "utf-8");
28009
+ const content = readFileSync48(logPath, "utf-8");
27439
28010
  return content.split("\n").filter((line) => line.trim().length > 0).length;
27440
28011
  } catch {
27441
28012
  return 0;
@@ -27467,11 +28038,11 @@ __export(drift_tracker_exports, {
27467
28038
  determineOrigin: () => determineOrigin
27468
28039
  });
27469
28040
  import {
27470
- existsSync as existsSync51,
28041
+ existsSync as existsSync52,
27471
28042
  mkdirSync as mkdirSync31,
27472
- readFileSync as readFileSync48,
28043
+ readFileSync as readFileSync49,
27473
28044
  statSync as statSync9,
27474
- writeFileSync as writeFileSync30
28045
+ writeFileSync as writeFileSync31
27475
28046
  } from "fs";
27476
28047
  import { join as join56 } from "path";
27477
28048
  function determineOrigin(lastSyncTimestamp) {
@@ -27688,11 +28259,11 @@ var init_drift_tracker = __esm({
27688
28259
  const relPath = filePath.startsWith("/") ? filePath.slice(this.config.projectRoot.length + 1) : filePath;
27689
28260
  const language = detectLanguage2(relPath);
27690
28261
  if (!language) return result;
27691
- if (existsSync51(absPath) && !this.mtimeCache.check(absPath)) {
28262
+ if (existsSync52(absPath) && !this.mtimeCache.check(absPath)) {
27692
28263
  result.filesSkipped = 1;
27693
28264
  return result;
27694
28265
  }
27695
- if (!existsSync51(absPath)) {
28266
+ if (!existsSync52(absPath)) {
27696
28267
  const baseEntities2 = await this.localGraph.getEntitiesByFile(relPath);
27697
28268
  this.markFileDeleted(relPath, intentId);
27698
28269
  result.filesProcessed = 1;
@@ -27730,7 +28301,7 @@ var init_drift_tracker = __esm({
27730
28301
  this.maybeEmitDrift(relPath, result);
27731
28302
  return result;
27732
28303
  }
27733
- const content = readFileSync48(absPath, "utf-8");
28304
+ const content = readFileSync49(absPath, "utf-8");
27734
28305
  const sha = contentSha256(content);
27735
28306
  const decision = this.fileHashManager.shouldProcess(relPath, sha, headSha);
27736
28307
  if (decision === "skip") {
@@ -28050,11 +28621,11 @@ var init_drift_tracker = __esm({
28050
28621
  }
28051
28622
  async saveDriftSummary() {
28052
28623
  const driftDir = join56(this.config.unerrDir, "drift");
28053
- if (!existsSync51(driftDir)) {
28624
+ if (!existsSync52(driftDir)) {
28054
28625
  mkdirSync31(driftDir, { recursive: true });
28055
28626
  }
28056
28627
  const summary = await this.getDriftSummary();
28057
- writeFileSync30(
28628
+ writeFileSync31(
28058
28629
  join56(driftDir, "drift_summary.json"),
28059
28630
  JSON.stringify(summary, null, 2),
28060
28631
  "utf-8"
@@ -28464,7 +29035,7 @@ var incremental_indexer_exports = {};
28464
29035
  __export(incremental_indexer_exports, {
28465
29036
  indexFilesIncremental: () => indexFilesIncremental
28466
29037
  });
28467
- import { existsSync as existsSync52, readFileSync as readFileSync49 } from "fs";
29038
+ import { existsSync as existsSync53, readFileSync as readFileSync50 } from "fs";
28468
29039
  import { join as join57, relative as relative4 } from "path";
28469
29040
  async function indexFilesIncremental(projectRoot, changedFiles, graphStore, repoId) {
28470
29041
  const startMs = Date.now();
@@ -28482,7 +29053,7 @@ async function indexFilesIncremental(projectRoot, changedFiles, graphStore, repo
28482
29053
  for (const filePath of changedFiles) {
28483
29054
  const absPath = filePath.startsWith("/") ? filePath : join57(projectRoot, filePath);
28484
29055
  const relPath = filePath.startsWith("/") ? relative4(projectRoot, filePath) : filePath;
28485
- if (!existsSync52(absPath)) {
29056
+ if (!existsSync53(absPath)) {
28486
29057
  const deleted2 = await deleteFileFromGraph(db, relPath);
28487
29058
  filesDeleted++;
28488
29059
  totalEntitiesDeleted += deleted2.entitiesDeleted;
@@ -28495,7 +29066,7 @@ async function indexFilesIncremental(projectRoot, changedFiles, graphStore, repo
28495
29066
  }
28496
29067
  let content;
28497
29068
  try {
28498
- content = readFileSync49(absPath, "utf-8");
29069
+ content = readFileSync50(absPath, "utf-8");
28499
29070
  } catch {
28500
29071
  continue;
28501
29072
  }
@@ -29662,7 +30233,7 @@ var workspace_manifest_exports = {};
29662
30233
  __export(workspace_manifest_exports, {
29663
30234
  WorkspaceManifest: () => WorkspaceManifest
29664
30235
  });
29665
- import { existsSync as existsSync53, mkdirSync as mkdirSync32, readFileSync as readFileSync50, writeFileSync as writeFileSync31 } from "fs";
30236
+ import { existsSync as existsSync54, mkdirSync as mkdirSync32, readFileSync as readFileSync51, writeFileSync as writeFileSync32 } from "fs";
29666
30237
  import { join as join58 } from "path";
29667
30238
  var WorkspaceManifest;
29668
30239
  var init_workspace_manifest = __esm({
@@ -29768,7 +30339,7 @@ var init_workspace_manifest = __esm({
29768
30339
  }
29769
30340
  // ── Internal ─────────────────────────────────────────────────────
29770
30341
  load() {
29771
- if (!existsSync53(this.manifestPath)) {
30342
+ if (!existsSync54(this.manifestPath)) {
29772
30343
  return {
29773
30344
  version: 1,
29774
30345
  repoId: this.repoId,
@@ -29778,7 +30349,7 @@ var init_workspace_manifest = __esm({
29778
30349
  };
29779
30350
  }
29780
30351
  try {
29781
- const raw = readFileSync50(this.manifestPath, "utf-8");
30352
+ const raw = readFileSync51(this.manifestPath, "utf-8");
29782
30353
  const parsed = JSON.parse(raw);
29783
30354
  if (parsed.repoId !== this.repoId) {
29784
30355
  return {
@@ -29801,10 +30372,10 @@ var init_workspace_manifest = __esm({
29801
30372
  }
29802
30373
  }
29803
30374
  save() {
29804
- if (!existsSync53(this.unerrDir)) {
30375
+ if (!existsSync54(this.unerrDir)) {
29805
30376
  mkdirSync32(this.unerrDir, { recursive: true });
29806
30377
  }
29807
- writeFileSync31(
30378
+ writeFileSync32(
29808
30379
  this.manifestPath,
29809
30380
  JSON.stringify(this.data, null, 2),
29810
30381
  "utf-8"
@@ -29995,7 +30566,7 @@ var log_tailer_exports = {};
29995
30566
  __export(log_tailer_exports, {
29996
30567
  startLogTailer: () => startLogTailer
29997
30568
  });
29998
- import { existsSync as existsSync54, statSync as statSync10, openSync, readSync, closeSync, watch } from "fs";
30569
+ import { existsSync as existsSync55, statSync as statSync10, openSync, readSync, closeSync, watch } from "fs";
29999
30570
  import { join as join59 } from "path";
30000
30571
  function formatSize2(bytes) {
30001
30572
  if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
@@ -30110,22 +30681,22 @@ function startLogTailer(cwd, options) {
30110
30681
  const fileReadsPath = join59(logsDir, "file-reads.jsonl");
30111
30682
  const compressionState = {
30112
30683
  path: compressionPath,
30113
- offset: existsSync54(compressionPath) ? statSync10(compressionPath).size : 0,
30684
+ offset: existsSync55(compressionPath) ? statSync10(compressionPath).size : 0,
30114
30685
  watcher: null
30115
30686
  };
30116
30687
  const generalState = {
30117
30688
  path: generalPath,
30118
- offset: existsSync54(generalPath) ? statSync10(generalPath).size : 0,
30689
+ offset: existsSync55(generalPath) ? statSync10(generalPath).size : 0,
30119
30690
  watcher: null
30120
30691
  };
30121
30692
  const tokenFlowState = {
30122
30693
  path: tokenFlowPath,
30123
- offset: existsSync54(tokenFlowPath) ? statSync10(tokenFlowPath).size : 0,
30694
+ offset: existsSync55(tokenFlowPath) ? statSync10(tokenFlowPath).size : 0,
30124
30695
  watcher: null
30125
30696
  };
30126
30697
  const fileReadsState = {
30127
30698
  path: fileReadsPath,
30128
- offset: existsSync54(fileReadsPath) ? statSync10(fileReadsPath).size : 0,
30699
+ offset: existsSync55(fileReadsPath) ? statSync10(fileReadsPath).size : 0,
30129
30700
  watcher: null
30130
30701
  };
30131
30702
  function setupWatcher(state, handler) {
@@ -30153,7 +30724,7 @@ function startLogTailer(cwd, options) {
30153
30724
  ];
30154
30725
  const pollInterval = setInterval(() => {
30155
30726
  for (const { state, handler } of allStates) {
30156
- if (!state.watcher && existsSync54(state.path)) {
30727
+ if (!state.watcher && existsSync55(state.path)) {
30157
30728
  try {
30158
30729
  state.offset = 0;
30159
30730
  state.watcher = watch(state.path, () => {
@@ -31487,13 +32058,13 @@ function createSystemRoutes(deps) {
31487
32058
  });
31488
32059
  app.get("/config", async (c) => {
31489
32060
  const start = performance.now();
31490
- const { existsSync: existsSync63, readFileSync: readFileSync57 } = await import("fs");
32061
+ const { existsSync: existsSync64, readFileSync: readFileSync58 } = await import("fs");
31491
32062
  const { join: join68 } = await import("path");
31492
32063
  let config = {};
31493
32064
  const configPath = join68(deps.cwd, ".unerr", "config.json");
31494
- if (existsSync63(configPath)) {
32065
+ if (existsSync64(configPath)) {
31495
32066
  try {
31496
- config = JSON.parse(readFileSync57(configPath, "utf-8"));
32067
+ config = JSON.parse(readFileSync58(configPath, "utf-8"));
31497
32068
  } catch {
31498
32069
  config = { error: "unreadable" };
31499
32070
  }
@@ -31694,20 +32265,20 @@ __export(session_history_exports, {
31694
32265
  getWeeklyStats: () => getWeeklyStats,
31695
32266
  readSessionHistory: () => readSessionHistory
31696
32267
  });
31697
- import { appendFileSync as appendFileSync8, existsSync as existsSync55, mkdirSync as mkdirSync33, readFileSync as readFileSync51 } from "fs";
32268
+ import { appendFileSync as appendFileSync8, existsSync as existsSync56, mkdirSync as mkdirSync33, readFileSync as readFileSync52 } from "fs";
31698
32269
  import { join as join60 } from "path";
31699
32270
  function appendSessionHistory(unerrDir, entry) {
31700
32271
  const stateDir = join60(unerrDir, "state");
31701
- if (!existsSync55(stateDir)) mkdirSync33(stateDir, { recursive: true });
32272
+ if (!existsSync56(stateDir)) mkdirSync33(stateDir, { recursive: true });
31702
32273
  const historyPath = join60(stateDir, "session-history.jsonl");
31703
32274
  appendFileSync8(historyPath, `${JSON.stringify(entry)}
31704
32275
  `, "utf-8");
31705
32276
  }
31706
32277
  function readSessionHistory(unerrDir) {
31707
32278
  const historyPath = join60(unerrDir, "state", "session-history.jsonl");
31708
- if (!existsSync55(historyPath)) return [];
32279
+ if (!existsSync56(historyPath)) return [];
31709
32280
  try {
31710
- const content = readFileSync51(historyPath, "utf-8");
32281
+ const content = readFileSync52(historyPath, "utf-8");
31711
32282
  return content.split("\n").filter(Boolean).map((line) => {
31712
32283
  try {
31713
32284
  return JSON.parse(line);
@@ -32368,7 +32939,7 @@ var http_exports = {};
32368
32939
  __export(http_exports, {
32369
32940
  startDashboardServer: () => startDashboardServer
32370
32941
  });
32371
- import { existsSync as existsSync56, readFileSync as readFileSync52, unlinkSync as unlinkSync8, writeFileSync as writeFileSync32 } from "fs";
32942
+ import { existsSync as existsSync57, readFileSync as readFileSync53, unlinkSync as unlinkSync8, writeFileSync as writeFileSync33 } from "fs";
32372
32943
  import { createServer as createServer3 } from "net";
32373
32944
  import { dirname as dirname7, join as join61 } from "path";
32374
32945
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -32418,8 +32989,8 @@ async function startDashboardServer(opts) {
32418
32989
  }
32419
32990
  const distDir = join61(dirname7(fileURLToPath2(import.meta.url)), "ui");
32420
32991
  const spaIndex = join61(distDir, "index.html");
32421
- if (existsSync56(spaIndex)) {
32422
- const spaHtml = readFileSync52(spaIndex, "utf-8");
32992
+ if (existsSync57(spaIndex)) {
32993
+ const spaHtml = readFileSync53(spaIndex, "utf-8");
32423
32994
  app.use("*", serveStatic({ root: distDir }));
32424
32995
  app.get("*", (c) => {
32425
32996
  const path7 = c.req.path;
@@ -32456,7 +33027,7 @@ async function startDashboardServer(opts) {
32456
33027
  url: `http://localhost:${port}`
32457
33028
  };
32458
33029
  const tmpPath = `${serverJsonPath}.tmp.${process.pid}`;
32459
- writeFileSync32(tmpPath, JSON.stringify(serverInfo, null, 2));
33030
+ writeFileSync33(tmpPath, JSON.stringify(serverInfo, null, 2));
32460
33031
  const { renameSync: renameSync3 } = await import("fs");
32461
33032
  renameSync3(tmpPath, serverJsonPath);
32462
33033
  const close = () => {
@@ -32850,7 +33421,7 @@ var proxy_exports = {};
32850
33421
  __export(proxy_exports, {
32851
33422
  startProxy: () => startProxy
32852
33423
  });
32853
- import { existsSync as existsSync57, mkdirSync as mkdirSync34, readFileSync as readFileSync53, readdirSync as readdirSync13, writeFileSync as fsWriteFileSync } from "fs";
33424
+ import { existsSync as existsSync58, mkdirSync as mkdirSync34, readFileSync as readFileSync54, readdirSync as readdirSync13, writeFileSync as fsWriteFileSync } from "fs";
32854
33425
  import { join as join62 } from "path";
32855
33426
  async function getProxyFactStore(unerrDir) {
32856
33427
  if (proxyFactStore !== void 0) return proxyFactStore;
@@ -32937,8 +33508,8 @@ async function handleRecallFactsProxy(args, unerrDir) {
32937
33508
  function migrateAgentPermissions(cwd) {
32938
33509
  try {
32939
33510
  const settingsPath = join62(cwd, ".claude", "settings.json");
32940
- if (!existsSync57(settingsPath)) return;
32941
- const raw = readFileSync53(settingsPath, "utf-8");
33511
+ if (!existsSync58(settingsPath)) return;
33512
+ const raw = readFileSync54(settingsPath, "utf-8");
32942
33513
  const settings = JSON.parse(raw);
32943
33514
  const deny = settings?.permissions?.deny;
32944
33515
  if (!Array.isArray(deny)) return;
@@ -32959,7 +33530,7 @@ async function startProxy(opts = {}) {
32959
33530
  lifecycle.send({ type: "START_DETECT" });
32960
33531
  startup.setLocalMode(true);
32961
33532
  const stateDir = join62(process.cwd(), ".unerr", "state");
32962
- if (!existsSync57(stateDir)) {
33533
+ if (!existsSync58(stateDir)) {
32963
33534
  mkdirSync34(stateDir, { recursive: true });
32964
33535
  }
32965
33536
  const pidLock = new PidLock(stateDir);
@@ -32993,15 +33564,15 @@ async function startProxy(opts = {}) {
32993
33564
  repoIds = [opts.repoId];
32994
33565
  } else {
32995
33566
  const configPath = join62(process.cwd(), ".unerr", "config.json");
32996
- if (existsSync57(configPath)) {
33567
+ if (existsSync58(configPath)) {
32997
33568
  try {
32998
- const config = JSON.parse(readFileSync53(configPath, "utf-8"));
33569
+ const config = JSON.parse(readFileSync54(configPath, "utf-8"));
32999
33570
  if (config.repoId) repoIds = [config.repoId];
33000
33571
  } catch {
33001
33572
  }
33002
33573
  }
33003
33574
  const manifestsDir = join62(process.cwd(), ".unerr", "manifests");
33004
- if (repoIds.length === 0 && existsSync57(manifestsDir)) {
33575
+ if (repoIds.length === 0 && existsSync58(manifestsDir)) {
33005
33576
  repoIds = readdirSync13(manifestsDir).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
33006
33577
  }
33007
33578
  }
@@ -33435,8 +34006,8 @@ async function startProxy(opts = {}) {
33435
34006
  );
33436
34007
  process.env.UNERR_SESSION_ID = shadowLedger2.getSessionId();
33437
34008
  try {
33438
- const { writeFileSync: writeFileSync36 } = await import("fs");
33439
- writeFileSync36(join62(unerrDirForLedger, "state", "session.id"), shadowLedger2.getSessionId(), "utf-8");
34009
+ const { writeFileSync: writeFileSync37 } = await import("fs");
34010
+ writeFileSync37(join62(unerrDirForLedger, "state", "session.id"), shadowLedger2.getSessionId(), "utf-8");
33440
34011
  } catch {
33441
34012
  }
33442
34013
  router2.setTokenFlow(tokenFlowWriter2);
@@ -34266,7 +34837,7 @@ async function startProxy(opts = {}) {
34266
34837
  });
34267
34838
  for (const file of sourceFiles.slice(0, 500)) {
34268
34839
  try {
34269
- const content = readFileSync53(join62(process.cwd(), file), "utf-8");
34840
+ const content = readFileSync54(join62(process.cwd(), file), "utf-8");
34270
34841
  const entities = extractEntitiesFromSource2(file, content);
34271
34842
  parseIndex.addEntities(entities);
34272
34843
  } catch {
@@ -34428,7 +34999,7 @@ async function startProxy(opts = {}) {
34428
34999
  temporal: await (async () => {
34429
35000
  try {
34430
35001
  const { TemporalFactStore: TemporalFactStore2 } = await Promise.resolve().then(() => (init_temporal_facts(), temporal_facts_exports));
34431
- const { readdirSync: readdirSync15, readFileSync: readFileSync57 } = await import("fs");
35002
+ const { readdirSync: readdirSync15, readFileSync: readFileSync58 } = await import("fs");
34432
35003
  const factStore2 = await TemporalFactStore2.create(
34433
35004
  process.cwd()
34434
35005
  );
@@ -34439,7 +35010,7 @@ async function startProxy(opts = {}) {
34439
35010
  const sessDir = join62(unerrDirForApi, "sessions");
34440
35011
  const files = readdirSync15(sessDir).filter((f) => f.endsWith(".jsonl")).sort().slice(-limit);
34441
35012
  return files.map((f) => {
34442
- const content = readFileSync57(join62(sessDir, f), "utf-8").trim().split("\n").pop();
35013
+ const content = readFileSync58(join62(sessDir, f), "utf-8").trim().split("\n").pop();
34443
35014
  return JSON.parse(content);
34444
35015
  });
34445
35016
  } catch {
@@ -34861,7 +35432,7 @@ __export(session_logger_exports, {
34861
35432
  import { randomUUID as randomUUID2 } from "crypto";
34862
35433
  import {
34863
35434
  appendFileSync as appendFileSync9,
34864
- existsSync as existsSync58,
35435
+ existsSync as existsSync59,
34865
35436
  mkdirSync as mkdirSync35,
34866
35437
  readdirSync as readdirSync14,
34867
35438
  statSync as statSync11,
@@ -34875,7 +35446,7 @@ function formatTimestamp() {
34875
35446
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
34876
35447
  }
34877
35448
  function cleanupOldLogs(logsDir) {
34878
- if (!existsSync58(logsDir)) return;
35449
+ if (!existsSync59(logsDir)) return;
34879
35450
  try {
34880
35451
  const files = readdirSync14(logsDir).filter((f) => f.startsWith("session-") && f.endsWith(".log")).map((f) => {
34881
35452
  const fullPath = join63(logsDir, f);
@@ -34895,7 +35466,7 @@ function cleanupOldLogs(logsDir) {
34895
35466
  }
34896
35467
  }
34897
35468
  }
34898
- const remaining = files.filter((f) => existsSync58(f.path));
35469
+ const remaining = files.filter((f) => existsSync59(f.path));
34899
35470
  if (remaining.length > MAX_FILES) {
34900
35471
  for (const file of remaining.slice(MAX_FILES)) {
34901
35472
  try {
@@ -34988,7 +35559,7 @@ __export(setup_wizard_exports, {
34988
35559
  runSetup: () => runSetup
34989
35560
  });
34990
35561
  import { createHash as createHash4 } from "crypto";
34991
- import { existsSync as existsSync59, mkdirSync as mkdirSync36, writeFileSync as writeFileSync33 } from "fs";
35562
+ import { existsSync as existsSync60, mkdirSync as mkdirSync36, writeFileSync as writeFileSync34 } from "fs";
34992
35563
  import { join as join64 } from "path";
34993
35564
  import * as clack from "@clack/prompts";
34994
35565
  async function runSetup(cwd) {
@@ -35000,10 +35571,10 @@ async function runSetup(cwd) {
35000
35571
  mkdirSync36(configDir, { recursive: true });
35001
35572
  const configPath = join64(configDir, "config.json");
35002
35573
  const settingsPath = join64(configDir, "settings.json");
35003
- writeFileSync33(configPath, `${JSON.stringify({ repoId }, null, 2)}
35574
+ writeFileSync34(configPath, `${JSON.stringify({ repoId }, null, 2)}
35004
35575
  `);
35005
35576
  let existingSettings = {};
35006
- if (existsSync59(settingsPath)) {
35577
+ if (existsSync60(settingsPath)) {
35007
35578
  try {
35008
35579
  existingSettings = JSON.parse(
35009
35580
  __require("fs").readFileSync(settingsPath, "utf-8")
@@ -35011,7 +35582,7 @@ async function runSetup(cwd) {
35011
35582
  } catch {
35012
35583
  }
35013
35584
  }
35014
- writeFileSync33(
35585
+ writeFileSync34(
35015
35586
  settingsPath,
35016
35587
  `${JSON.stringify({ ...existingSettings }, null, 2)}
35017
35588
  `
@@ -35612,13 +36183,13 @@ __export(session_summary_writer_exports, {
35612
36183
  readLastSession: () => readLastSession,
35613
36184
  writeSessionSummary: () => writeSessionSummary
35614
36185
  });
35615
- import { existsSync as existsSync60, mkdirSync as mkdirSync37, appendFileSync as appendFileSync10, writeFileSync as writeFileSync34, readFileSync as readFileSync54 } from "fs";
36186
+ import { existsSync as existsSync61, mkdirSync as mkdirSync37, appendFileSync as appendFileSync10, writeFileSync as writeFileSync35, readFileSync as readFileSync55 } from "fs";
35616
36187
  import { join as join65 } from "path";
35617
36188
  function writeSessionSummary(unerrDir, ctx) {
35618
36189
  if (ctx.entries.length === 0) return null;
35619
36190
  try {
35620
36191
  const sessionsDir = join65(unerrDir, "sessions");
35621
- if (!existsSync60(sessionsDir)) {
36192
+ if (!existsSync61(sessionsDir)) {
35622
36193
  mkdirSync37(sessionsDir, { recursive: true });
35623
36194
  }
35624
36195
  const sorted = [...ctx.entries].sort(
@@ -35678,8 +36249,8 @@ function writeSessionSummary(unerrDir, ctx) {
35678
36249
  function readLastSession(unerrDir) {
35679
36250
  try {
35680
36251
  const pointerPath = join65(unerrDir, "state", "last_session.json");
35681
- if (!existsSync60(pointerPath)) return null;
35682
- const content = readFileSync54(pointerPath, "utf-8");
36252
+ if (!existsSync61(pointerPath)) return null;
36253
+ const content = readFileSync55(pointerPath, "utf-8");
35683
36254
  return JSON.parse(content);
35684
36255
  } catch {
35685
36256
  return null;
@@ -35688,11 +36259,11 @@ function readLastSession(unerrDir) {
35688
36259
  function writeLastSessionPointer(unerrDir, sessionId, record) {
35689
36260
  try {
35690
36261
  const stateDir = join65(unerrDir, "state");
35691
- if (!existsSync60(stateDir)) {
36262
+ if (!existsSync61(stateDir)) {
35692
36263
  mkdirSync37(stateDir, { recursive: true });
35693
36264
  }
35694
36265
  const pointerPath = join65(stateDir, "last_session.json");
35695
- writeFileSync34(pointerPath, JSON.stringify(record, null, 2), "utf-8");
36266
+ writeFileSync35(pointerPath, JSON.stringify(record, null, 2), "utf-8");
35696
36267
  } catch {
35697
36268
  }
35698
36269
  }
@@ -35889,20 +36460,20 @@ var mcp_server_exports = {};
35889
36460
  __export(mcp_server_exports, {
35890
36461
  startMcpServer: () => startMcpServer
35891
36462
  });
35892
- import { existsSync as existsSync61, mkdirSync as mkdirSync38, readFileSync as readFileSync55, writeFileSync as writeFileSync35 } from "fs";
36463
+ import { existsSync as existsSync62, mkdirSync as mkdirSync38, readFileSync as readFileSync56, writeFileSync as writeFileSync36 } from "fs";
35893
36464
  import { join as join66 } from "path";
35894
36465
  function migrateAgentPermissions2(cwd) {
35895
36466
  try {
35896
36467
  const settingsPath = join66(cwd, ".claude", "settings.json");
35897
- if (!existsSync61(settingsPath)) return;
35898
- const raw = readFileSync55(settingsPath, "utf-8");
36468
+ if (!existsSync62(settingsPath)) return;
36469
+ const raw = readFileSync56(settingsPath, "utf-8");
35899
36470
  const settings = JSON.parse(raw);
35900
36471
  const deny = settings?.permissions?.deny;
35901
36472
  if (!Array.isArray(deny)) return;
35902
36473
  const readIdx = deny.indexOf("Read");
35903
36474
  if (readIdx < 0) return;
35904
36475
  deny.splice(readIdx, 1);
35905
- writeFileSync35(settingsPath, JSON.stringify(settings, null, 2) + "\n");
36476
+ writeFileSync36(settingsPath, JSON.stringify(settings, null, 2) + "\n");
35906
36477
  process.stderr.write("[unerr] Migrated permissions: removed Read from deny list (required for Edit workflow)\n");
35907
36478
  } catch {
35908
36479
  }
@@ -35912,7 +36483,7 @@ async function startMcpServer(cwd) {
35912
36483
  const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
35913
36484
  const { ListToolsRequestSchema, CallToolRequestSchema } = await import("@modelcontextprotocol/sdk/types.js");
35914
36485
  const unerrDir = join66(cwd, ".unerr");
35915
- if (!existsSync61(unerrDir)) {
36486
+ if (!existsSync62(unerrDir)) {
35916
36487
  mkdirSync38(unerrDir, { recursive: true });
35917
36488
  }
35918
36489
  migrateAgentPermissions2(cwd);
@@ -36078,22 +36649,28 @@ async function startMcpServer(cwd) {
36078
36649
  const filePath = args.file_path ?? args.path ?? args.key?.split("::")[0] ?? null;
36079
36650
  if (filePath) {
36080
36651
  try {
36081
- const [fileFacts, negativeFacts] = await Promise.all([
36082
- factStore.recallByScope(filePath),
36083
- factStore.recallNegative(0.2)
36084
- ]);
36085
- const seen = /* @__PURE__ */ new Set();
36086
- const merged = [];
36087
- for (const f of fileFacts) {
36088
- if (!seen.has(f.fact_id)) {
36089
- seen.add(f.fact_id);
36090
- merged.push(f);
36652
+ let merged;
36653
+ if (name === "file_read" && router) {
36654
+ const entityKeys = await router.getEntityKeysForFile(filePath);
36655
+ merged = await factStore.recallForFile(filePath, entityKeys);
36656
+ } else {
36657
+ const [fileFacts, negativeFacts] = await Promise.all([
36658
+ factStore.recallByScope(filePath),
36659
+ factStore.recallNegative(0.2)
36660
+ ]);
36661
+ const seen = /* @__PURE__ */ new Set();
36662
+ merged = [];
36663
+ for (const f of fileFacts) {
36664
+ if (!seen.has(f.fact_id)) {
36665
+ seen.add(f.fact_id);
36666
+ merged.push(f);
36667
+ }
36091
36668
  }
36092
- }
36093
- for (const f of negativeFacts) {
36094
- if (!seen.has(f.fact_id)) {
36095
- seen.add(f.fact_id);
36096
- merged.push(f);
36669
+ for (const f of negativeFacts) {
36670
+ if (!seen.has(f.fact_id)) {
36671
+ seen.add(f.fact_id);
36672
+ merged.push(f);
36673
+ }
36097
36674
  }
36098
36675
  }
36099
36676
  if (merged.length > 0) {
@@ -36320,7 +36897,7 @@ async function initTier2Modules(unerrDir) {
36320
36897
  try {
36321
36898
  const stateDir = join66(unerrDir, "state");
36322
36899
  mkdirSync38(stateDir, { recursive: true });
36323
- writeFileSync35(join66(stateDir, "session.id"), sessionId, "utf-8");
36900
+ writeFileSync36(join66(stateDir, "session.id"), sessionId, "utf-8");
36324
36901
  } catch {
36325
36902
  }
36326
36903
  log24.info(`Token flow active (session ${sessionId.slice(0, 8)})`);
@@ -36462,7 +37039,7 @@ async function loadIntelligence(cwd) {
36462
37039
  const projectRoot = cwd;
36463
37040
  try {
36464
37041
  const unerrDir = join66(projectRoot, ".unerr");
36465
- if (!existsSync61(unerrDir)) {
37042
+ if (!existsSync62(unerrDir)) {
36466
37043
  mkdirSync38(unerrDir, { recursive: true });
36467
37044
  }
36468
37045
  const { openPersistentDb: openPersistentDb2 } = await Promise.resolve().then(() => (init_persistent_db(), persistent_db_exports));
@@ -36483,9 +37060,9 @@ async function loadIntelligence(cwd) {
36483
37060
  await runCommunityDetection2(localGraph);
36484
37061
  const configPath = join66(projectRoot, ".unerr", "config.json");
36485
37062
  let repoId = "unknown";
36486
- if (existsSync61(configPath)) {
37063
+ if (existsSync62(configPath)) {
36487
37064
  try {
36488
- const config = JSON.parse(readFileSync55(configPath, "utf-8"));
37065
+ const config = JSON.parse(readFileSync56(configPath, "utf-8"));
36489
37066
  repoId = config.repoId ?? repoId;
36490
37067
  } catch {
36491
37068
  }
@@ -36514,9 +37091,9 @@ async function loadIntelligence(cwd) {
36514
37091
  try {
36515
37092
  const configPath = join66(projectRoot, ".unerr", "config.json");
36516
37093
  let indexRepoId = "unknown";
36517
- if (existsSync61(configPath)) {
37094
+ if (existsSync62(configPath)) {
36518
37095
  try {
36519
- const cfg = JSON.parse(readFileSync55(configPath, "utf-8"));
37096
+ const cfg = JSON.parse(readFileSync56(configPath, "utf-8"));
36520
37097
  indexRepoId = cfg.repoId ?? indexRepoId;
36521
37098
  } catch {
36522
37099
  }
@@ -36626,7 +37203,7 @@ var init_mcp_server = __esm({
36626
37203
 
36627
37204
  // src/entrypoints/cli.ts
36628
37205
  init_startup_log();
36629
- import { existsSync as existsSync62, readFileSync as readFileSync56 } from "fs";
37206
+ import { existsSync as existsSync63, readFileSync as readFileSync57 } from "fs";
36630
37207
  import { join as join67 } from "path";
36631
37208
  import { Command } from "commander";
36632
37209
 
@@ -38631,19 +39208,19 @@ async function buildShellDiffRiskMap(graph, stdout) {
38631
39208
  }
38632
39209
  async function tryLoadGraphForShellBoost(cwd) {
38633
39210
  try {
38634
- const { existsSync: existsSync63, readFileSync: readFileSync57 } = await import("fs");
39211
+ const { existsSync: existsSync64, readFileSync: readFileSync58 } = await import("fs");
38635
39212
  const { join: join68 } = await import("path");
38636
39213
  const { gunzipSync: gunzipSync3 } = await import("zlib");
38637
39214
  const { unpack } = await import("msgpackr");
38638
39215
  const snapshotsDir = join68(cwd, ".unerr", "snapshots");
38639
39216
  let snapshotPath2 = join68(snapshotsDir, "graph.msgpack.gz");
38640
- if (!existsSync63(snapshotPath2)) {
39217
+ if (!existsSync64(snapshotPath2)) {
38641
39218
  snapshotPath2 = join68(snapshotsDir, "graph.msgpack");
38642
39219
  }
38643
- if (!existsSync63(snapshotPath2)) return null;
39220
+ if (!existsSync64(snapshotPath2)) return null;
38644
39221
  const { default: CozoDbConstructor } = await import("cozo-node");
38645
39222
  const { CozoGraphStore: CozoGraphStore2 } = await Promise.resolve().then(() => (init_local_graph(), local_graph_exports));
38646
- const raw = readFileSync57(snapshotPath2);
39223
+ const raw = readFileSync58(snapshotPath2);
38647
39224
  let buffer;
38648
39225
  try {
38649
39226
  buffer = gunzipSync3(raw);
@@ -41835,6 +42412,7 @@ function runPreToolUseHook(stdinJson, handler) {
41835
42412
  if (!payload) return "{}";
41836
42413
  const adapter = detectAdapter(payload);
41837
42414
  const normalized = adapter.normalize(payload);
42415
+ normalized.agentName = adapter.name;
41838
42416
  const result = handler(normalized);
41839
42417
  return adapter.formatPreToolUse(result);
41840
42418
  }
@@ -41843,6 +42421,7 @@ function runPostToolUseHook(stdinJson, handler) {
41843
42421
  if (!payload) return "{}";
41844
42422
  const adapter = detectAdapter(payload);
41845
42423
  const normalized = adapter.normalize(payload);
42424
+ normalized.agentName = adapter.name;
41846
42425
  const result = handler(normalized);
41847
42426
  return adapter.formatPostToolUse(result);
41848
42427
  }
@@ -41851,6 +42430,7 @@ function runPromptSubmitHook(stdinJson, handler) {
41851
42430
  if (!payload) return "{}";
41852
42431
  const adapter = detectAdapter(payload);
41853
42432
  const normalized = adapter.normalize(payload);
42433
+ normalized.agentName = adapter.name;
41854
42434
  const result = handler(normalized);
41855
42435
  return adapter.formatPromptSubmit(result);
41856
42436
  }
@@ -41879,17 +42459,27 @@ var preReadHandler = (normalized) => {
41879
42459
  const input = normalized.toolInput;
41880
42460
  const filePath = extractFilePath2(input);
41881
42461
  if (!filePath) return passthrough();
42462
+ const isClaudeCode = normalized.agentName === "claude-code";
41882
42463
  const hasOffset = input.offset !== void 0;
41883
42464
  const hasLimit = input.limit !== void 0;
41884
42465
  const isTargeted = hasOffset || hasLimit;
41885
- const targetedReadGuidance = isTargeted ? "" : `
41886
- IMPORTANT: Use offset/limit to read only the lines you plan to edit \u2014 do NOT read the entire file. You already know the target lines from file_read. Example: Read with offset=50, limit=30 to read lines 50-79.`;
41887
- return nudge(
41888
- `READ ROUTING: Built-in Read is ONLY for the Edit workflow (Read \u2192 Edit). For all other reading, use unerr tools instead:
41889
- - Reading to understand: \`file_read({ file_path: "${filePath}", purpose: "explore" })\`
41890
- - Reading before Edit: built-in Read is correct (proceed)
42466
+ if (isClaudeCode && isTargeted) {
42467
+ return passthrough();
42468
+ }
42469
+ if (isClaudeCode) {
42470
+ return nudge(
42471
+ `READ ROUTING: Built-in Read is ONLY for the Edit workflow (Read \u2192 Edit). Use offset/limit to read only the lines you plan to edit \u2014 do NOT read the entire file.
42472
+ For all other reading, use unerr tools instead:
42473
+ - Reading to understand: \`file_read({ file_path: "${filePath}" })\`
41891
42474
  - File structure: \`file_outline("${filePath}")\`
41892
- - Specific function: \`get_entity\` or \`file_read\` with \`entity\` param` + targetedReadGuidance
42475
+ - Specific function: \`get_entity\` or \`file_read\` with \`entity\` param`
42476
+ );
42477
+ }
42478
+ return nudge(
42479
+ `Use unerr tools instead of built-in Read:
42480
+ - \`file_read({ file_path: "${filePath}" })\` \u2014 auto-injects conventions, facts, drift status
42481
+ - \`file_outline("${filePath}")\` \u2014 file structure overview
42482
+ - \`get_entity\` or \`file_read\` with \`entity\` param \u2014 specific function/class`
41893
42483
  );
41894
42484
  };
41895
42485
  var preGrepHandler = (normalized) => {
@@ -41941,9 +42531,10 @@ var preEditHandler = (normalized) => {
41941
42531
  const input = normalized.toolInput;
41942
42532
  const filePath = extractFilePath2(input);
41943
42533
  if (!filePath || !isCodeFile(filePath)) return passthrough();
41944
- const readPrereq = `CRITICAL: Edit REQUIRES built-in Read to have been called on "${filePath}" first. file_read (MCP) does NOT satisfy this \u2014 the Edit tool will fail with "File has not been read yet". If you haven't called built-in Read on this file, do so now before attempting Edit. Use offset/limit to read only the lines you plan to edit \u2014 do NOT read the entire file.
42534
+ const isClaudeCode = normalized.agentName === "claude-code";
42535
+ const readPrereq = isClaudeCode ? `CRITICAL: Edit REQUIRES built-in Read to have been called on "${filePath}" first. file_read (MCP) does NOT satisfy this \u2014 the Edit tool will fail with "File has not been read yet". If you haven't called built-in Read (with offset/limit on the target lines) on this file, do so now before attempting Edit.
41945
42536
 
41946
- `;
42537
+ ` : "";
41947
42538
  const oldStr = input.old_string;
41948
42539
  const hasSignatureChange = oldStr && /^(export\s+)?(async\s+)?function\s+\w+|^(export\s+)?class\s+\w+/.test(oldStr.trim());
41949
42540
  if (hasSignatureChange) {
@@ -41963,9 +42554,16 @@ var postReadHandler = (normalized) => {
41963
42554
  const input = normalized.toolInput;
41964
42555
  const filePath = extractFilePath2(input);
41965
42556
  if (!filePath || !isCodeFile(filePath)) return passthrough();
41966
- return enrich(
41967
- `If you're about to Edit this file, this Read was correct (Edit requires built-in Read first).
42557
+ const isClaudeCode = normalized.agentName === "claude-code";
42558
+ if (isClaudeCode) {
42559
+ return enrich(
42560
+ `If you're about to Edit this file, this Read was correct (Edit requires built-in Read first).
41968
42561
  If you're exploring, use \`file_read\` instead \u2014 it auto-injects conventions, facts, and drift status.
42562
+ Next steps: \`get_references\` to check callers before modifying, \`get_rules\` to validate conventions.`
42563
+ );
42564
+ }
42565
+ return enrich(
42566
+ `Prefer \`file_read\` over built-in Read \u2014 it auto-injects conventions, facts, and drift status.
41969
42567
  Next steps: \`get_references\` to check callers before modifying, \`get_rules\` to validate conventions.`
41970
42568
  );
41971
42569
  };
@@ -42088,56 +42686,32 @@ function runPreBashHook(stdinJson) {
42088
42686
  }
42089
42687
 
42090
42688
  // src/commands/hook.ts
42689
+ function safeHookAction(handler) {
42690
+ return () => {
42691
+ try {
42692
+ const stdin = readFileSync15(0, "utf-8");
42693
+ process.stdout.write(handler(stdin));
42694
+ } catch (e) {
42695
+ process.stderr.write(`[unerr] hook error: ${e}
42696
+ `);
42697
+ process.stdout.write("{}");
42698
+ }
42699
+ };
42700
+ }
42091
42701
  function registerHookCommand(program2) {
42092
42702
  const hook = program2.command("hook").description("IDE hook handlers (stdin/stdout JSON)");
42093
- hook.command("pre-bash").description("Rewrite Bash tool input to pipe through unerr exec").action(() => {
42094
- const stdin = readFileSync15(0, "utf-8");
42095
- process.stdout.write(runPreBashHook(stdin));
42096
- });
42097
- hook.command("pre-read").description("Nudge agent to use file_outline/file_read instead of Read").action(() => {
42098
- const stdin = readFileSync15(0, "utf-8");
42099
- process.stdout.write(runPreReadHook(stdin));
42100
- });
42101
- hook.command("pre-grep").description("Nudge agent to use search_code/get_callers instead of Grep").action(() => {
42102
- const stdin = readFileSync15(0, "utf-8");
42103
- process.stdout.write(runPreGrepHook(stdin));
42104
- });
42105
- hook.command("pre-glob").description("Nudge agent to use search_code instead of Glob").action(() => {
42106
- const stdin = readFileSync15(0, "utf-8");
42107
- process.stdout.write(runPreGlobHook(stdin));
42108
- });
42109
- hook.command("pre-write").description("Blast radius + convention check before Write").action(() => {
42110
- const stdin = readFileSync15(0, "utf-8");
42111
- process.stdout.write(runPreWriteHook(stdin));
42112
- });
42113
- hook.command("pre-edit").description("Blast radius + convention check before Edit").action(() => {
42114
- const stdin = readFileSync15(0, "utf-8");
42115
- process.stdout.write(runPreEditHook(stdin));
42116
- });
42117
- hook.command("post-read").description("Enrich Read output with graph navigation suggestions").action(() => {
42118
- const stdin = readFileSync15(0, "utf-8");
42119
- process.stdout.write(runPostReadHook(stdin));
42120
- });
42121
- hook.command("post-grep").description("Enrich Grep output with graph navigation suggestions").action(() => {
42122
- const stdin = readFileSync15(0, "utf-8");
42123
- process.stdout.write(runPostGrepHook(stdin));
42124
- });
42125
- hook.command("post-glob").description("Enrich Glob output with graph navigation suggestions").action(() => {
42126
- const stdin = readFileSync15(0, "utf-8");
42127
- process.stdout.write(runPostGlobHook(stdin));
42128
- });
42129
- hook.command("post-write").description("Post-write convention check + caller verification reminder").action(() => {
42130
- const stdin = readFileSync15(0, "utf-8");
42131
- process.stdout.write(runPostWriteHook(stdin));
42132
- });
42133
- hook.command("post-edit").description("Post-edit convention check + caller verification reminder").action(() => {
42134
- const stdin = readFileSync15(0, "utf-8");
42135
- process.stdout.write(runPostEditHook(stdin));
42136
- });
42137
- hook.command("prompt-submit").description("Inject unerr tool reminder on each user prompt").action(() => {
42138
- const stdin = readFileSync15(0, "utf-8");
42139
- process.stdout.write(runUserPromptSubmitHook(stdin));
42140
- });
42703
+ hook.command("pre-bash").description("Rewrite Bash tool input to pipe through unerr exec").action(safeHookAction(runPreBashHook));
42704
+ hook.command("pre-read").description("Nudge agent to use file_outline/file_read instead of Read").action(safeHookAction(runPreReadHook));
42705
+ hook.command("pre-grep").description("Nudge agent to use search_code/get_callers instead of Grep").action(safeHookAction(runPreGrepHook));
42706
+ hook.command("pre-glob").description("Nudge agent to use search_code instead of Glob").action(safeHookAction(runPreGlobHook));
42707
+ hook.command("pre-write").description("Blast radius + convention check before Write").action(safeHookAction(runPreWriteHook));
42708
+ hook.command("pre-edit").description("Blast radius + convention check before Edit").action(safeHookAction(runPreEditHook));
42709
+ hook.command("post-read").description("Enrich Read output with graph navigation suggestions").action(safeHookAction(runPostReadHook));
42710
+ hook.command("post-grep").description("Enrich Grep output with graph navigation suggestions").action(safeHookAction(runPostGrepHook));
42711
+ hook.command("post-glob").description("Enrich Glob output with graph navigation suggestions").action(safeHookAction(runPostGlobHook));
42712
+ hook.command("post-write").description("Post-write convention check + caller verification reminder").action(safeHookAction(runPostWriteHook));
42713
+ hook.command("post-edit").description("Post-edit convention check + caller verification reminder").action(safeHookAction(runPostEditHook));
42714
+ hook.command("prompt-submit").description("Inject unerr tool reminder on each user prompt").action(safeHookAction(runUserPromptSubmitHook));
42141
42715
  }
42142
42716
 
42143
42717
  // src/commands/index.ts
@@ -42307,89 +42881,108 @@ function runInit(cwd) {
42307
42881
 
42308
42882
  // src/commands/install.ts
42309
42883
  init_agent_registry();
42310
- import { chmodSync as chmodSync4, existsSync as existsSync23, mkdirSync as mkdirSync17, readFileSync as readFileSync22, writeFileSync as writeFileSync13 } from "fs";
42884
+ import { chmodSync as chmodSync4, existsSync as existsSync24, mkdirSync as mkdirSync17, readFileSync as readFileSync23, writeFileSync as writeFileSync14 } from "fs";
42311
42885
  import { join as join30 } from "path";
42312
42886
 
42313
42887
  // src/config/claude-settings-hooks.ts
42314
- import { existsSync as existsSync20, mkdirSync as mkdirSync14, readFileSync as readFileSync19, writeFileSync as writeFileSync10 } from "fs";
42888
+ import { execSync as execSync2 } from "child_process";
42889
+ import { existsSync as existsSync21, mkdirSync as mkdirSync14, readFileSync as readFileSync20, writeFileSync as writeFileSync11 } from "fs";
42315
42890
  import { join as join27 } from "path";
42316
- var UNERR_MATCHER_HOOKS = [
42317
- // PreToolUse advisory nudges + Bash rewrite
42318
- { event: "PreToolUse", matcher: "Bash", command: "unerr hook pre-bash" },
42319
- { event: "PreToolUse", matcher: "Read", command: "unerr hook pre-read" },
42320
- { event: "PreToolUse", matcher: "Grep", command: "unerr hook pre-grep" },
42321
- { event: "PreToolUse", matcher: "Glob", command: "unerr hook pre-glob" },
42322
- // PreToolUse blast radius + convention validation for writes
42323
- { event: "PreToolUse", matcher: "Write", command: "unerr hook pre-write" },
42324
- { event: "PreToolUse", matcher: "Edit", command: "unerr hook pre-edit" },
42325
- // PostToolUse — enrich tool output with graph navigation suggestions
42326
- { event: "PostToolUse", matcher: "Read", command: "unerr hook post-read" },
42327
- { event: "PostToolUse", matcher: "Grep", command: "unerr hook post-grep" },
42328
- { event: "PostToolUse", matcher: "Glob", command: "unerr hook post-glob" },
42329
- { event: "PostToolUse", matcher: "Write", command: "unerr hook post-write" },
42330
- { event: "PostToolUse", matcher: "Edit", command: "unerr hook post-edit" }
42331
- ];
42332
- var UNERR_GLOBAL_HOOKS = [
42333
- { event: "UserPromptSubmit", command: "unerr hook prompt-submit" }
42334
- ];
42335
- function isUnerrHookEntry(entry, hookCmd, matcherName) {
42891
+ function resolveUnerrBinary() {
42892
+ const entryScript = process.argv[1];
42893
+ if (entryScript && existsSync21(entryScript)) {
42894
+ return entryScript;
42895
+ }
42896
+ try {
42897
+ const resolved = execSync2("which unerr", { encoding: "utf-8" }).trim();
42898
+ if (resolved && existsSync21(resolved)) {
42899
+ return resolved;
42900
+ }
42901
+ } catch {
42902
+ }
42903
+ return "unerr";
42904
+ }
42905
+ var _resolvedBinary;
42906
+ function getUnerrBinary() {
42907
+ if (_resolvedBinary === void 0) {
42908
+ _resolvedBinary = resolveUnerrBinary();
42909
+ }
42910
+ return _resolvedBinary;
42911
+ }
42912
+ function buildMatcherHooks() {
42913
+ const bin = getUnerrBinary();
42914
+ return [
42915
+ // PreToolUse — advisory nudges + Bash rewrite
42916
+ { event: "PreToolUse", matcher: "Bash", command: `${bin} hook pre-bash` },
42917
+ { event: "PreToolUse", matcher: "Read", command: `${bin} hook pre-read` },
42918
+ { event: "PreToolUse", matcher: "Grep", command: `${bin} hook pre-grep` },
42919
+ { event: "PreToolUse", matcher: "Glob", command: `${bin} hook pre-glob` },
42920
+ // PreToolUse — blast radius + convention validation for writes
42921
+ { event: "PreToolUse", matcher: "Write", command: `${bin} hook pre-write` },
42922
+ { event: "PreToolUse", matcher: "Edit", command: `${bin} hook pre-edit` },
42923
+ // PostToolUse — enrich tool output with graph navigation suggestions
42924
+ { event: "PostToolUse", matcher: "Read", command: `${bin} hook post-read` },
42925
+ { event: "PostToolUse", matcher: "Grep", command: `${bin} hook post-grep` },
42926
+ { event: "PostToolUse", matcher: "Glob", command: `${bin} hook post-glob` },
42927
+ { event: "PostToolUse", matcher: "Write", command: `${bin} hook post-write` },
42928
+ { event: "PostToolUse", matcher: "Edit", command: `${bin} hook post-edit` }
42929
+ ];
42930
+ }
42931
+ function buildGlobalHooks() {
42932
+ const bin = getUnerrBinary();
42933
+ return [
42934
+ { event: "UserPromptSubmit", command: `${bin} hook prompt-submit` }
42935
+ ];
42936
+ }
42937
+ function isAnyUnerrHook(entry) {
42336
42938
  if (!entry || typeof entry !== "object") return false;
42337
42939
  const e = entry;
42338
- if (matcherName !== void 0 && e.matcher !== matcherName) return false;
42339
42940
  const hs = e.hooks;
42340
42941
  if (!Array.isArray(hs)) return false;
42341
42942
  return hs.some((h) => {
42342
42943
  if (!h || typeof h !== "object") return false;
42343
- const x2 = h;
42344
- return x2.command === hookCmd || x2.command === `npx ${hookCmd}`;
42944
+ const cmd = h.command;
42945
+ return typeof cmd === "string" && /(?:^|\/)unerr\s+hook\s+/.test(cmd);
42345
42946
  });
42346
42947
  }
42347
- function isAnyUnerrHook(entry) {
42348
- for (const hookDef of UNERR_MATCHER_HOOKS) {
42349
- if (isUnerrHookEntry(entry, hookDef.command, hookDef.matcher)) return true;
42350
- }
42351
- for (const hookDef of UNERR_GLOBAL_HOOKS) {
42352
- if (isUnerrHookEntry(entry, hookDef.command)) return true;
42353
- }
42354
- return false;
42355
- }
42356
42948
  function mergePreToolUseBashHook(cwd) {
42357
42949
  const dir = join27(cwd, ".claude");
42358
42950
  const settingsPath = join27(dir, "settings.json");
42359
42951
  try {
42360
- if (!existsSync20(dir)) {
42952
+ if (!existsSync21(dir)) {
42361
42953
  mkdirSync14(dir, { recursive: true });
42362
42954
  }
42363
42955
  let settings = {};
42364
- if (existsSync20(settingsPath)) {
42956
+ if (existsSync21(settingsPath)) {
42365
42957
  try {
42366
- settings = JSON.parse(readFileSync19(settingsPath, "utf-8"));
42958
+ settings = JSON.parse(readFileSync20(settingsPath, "utf-8"));
42367
42959
  } catch {
42368
42960
  settings = {};
42369
42961
  }
42370
42962
  }
42371
42963
  const hooks = settings.hooks ?? {};
42372
42964
  let added = 0;
42373
- for (const hookDef of UNERR_MATCHER_HOOKS) {
42374
- const eventArray = Array.isArray(hooks[hookDef.event]) ? [...hooks[hookDef.event]] : [];
42375
- const alreadyPresent = eventArray.some(
42376
- (entry) => isUnerrHookEntry(entry, hookDef.command, hookDef.matcher)
42377
- );
42378
- if (!alreadyPresent) {
42379
- eventArray.push({
42380
- matcher: hookDef.matcher,
42381
- hooks: [{ type: "command", command: hookDef.command }]
42382
- });
42383
- hooks[hookDef.event] = eventArray;
42384
- added++;
42965
+ const matcherHooks = buildMatcherHooks();
42966
+ const globalHooks = buildGlobalHooks();
42967
+ for (const eventType of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
42968
+ if (Array.isArray(hooks[eventType])) {
42969
+ hooks[eventType] = hooks[eventType].filter(
42970
+ (entry) => !isAnyUnerrHook(entry)
42971
+ );
42385
42972
  }
42386
42973
  }
42387
- for (const hookDef of UNERR_GLOBAL_HOOKS) {
42974
+ for (const hookDef of matcherHooks) {
42388
42975
  const eventArray = Array.isArray(hooks[hookDef.event]) ? [...hooks[hookDef.event]] : [];
42389
- const alreadyPresent = eventArray.some(
42390
- (entry) => isUnerrHookEntry(entry, hookDef.command)
42391
- );
42392
- if (!alreadyPresent) {
42976
+ eventArray.push({
42977
+ matcher: hookDef.matcher,
42978
+ hooks: [{ type: "command", command: hookDef.command }]
42979
+ });
42980
+ hooks[hookDef.event] = eventArray;
42981
+ added++;
42982
+ }
42983
+ for (const hookDef of globalHooks) {
42984
+ const eventArray = Array.isArray(hooks[hookDef.event]) ? [...hooks[hookDef.event]] : [];
42985
+ if (!eventArray.some((entry) => isAnyUnerrHook(entry))) {
42393
42986
  eventArray.push({
42394
42987
  hooks: [{ type: "command", command: hookDef.command }]
42395
42988
  });
@@ -42401,7 +42994,7 @@ function mergePreToolUseBashHook(cwd) {
42401
42994
  return { ok: true, path: settingsPath, action: "already_present" };
42402
42995
  }
42403
42996
  settings.hooks = hooks;
42404
- writeFileSync10(
42997
+ writeFileSync11(
42405
42998
  settingsPath,
42406
42999
  `${JSON.stringify(settings, null, 2)}
42407
43000
  `,
@@ -42417,13 +43010,13 @@ function addDisallowedTools(cwd) {
42417
43010
  const dir = join27(cwd, ".claude");
42418
43011
  const settingsPath = join27(dir, "settings.json");
42419
43012
  try {
42420
- if (!existsSync20(dir)) {
43013
+ if (!existsSync21(dir)) {
42421
43014
  mkdirSync14(dir, { recursive: true });
42422
43015
  }
42423
43016
  let settings = {};
42424
- if (existsSync20(settingsPath)) {
43017
+ if (existsSync21(settingsPath)) {
42425
43018
  try {
42426
- settings = JSON.parse(readFileSync19(settingsPath, "utf-8"));
43019
+ settings = JSON.parse(readFileSync20(settingsPath, "utf-8"));
42427
43020
  } catch {
42428
43021
  settings = {};
42429
43022
  }
@@ -42446,7 +43039,7 @@ function addDisallowedTools(cwd) {
42446
43039
  }
42447
43040
  permissions.deny = deny;
42448
43041
  settings.permissions = permissions;
42449
- writeFileSync10(settingsPath, `${JSON.stringify(settings, null, 2)}
43042
+ writeFileSync11(settingsPath, `${JSON.stringify(settings, null, 2)}
42450
43043
  `, "utf-8");
42451
43044
  return { added, path: settingsPath };
42452
43045
  } catch {
@@ -42455,9 +43048,9 @@ function addDisallowedTools(cwd) {
42455
43048
  }
42456
43049
  function removeDisallowedTools(cwd) {
42457
43050
  const settingsPath = join27(cwd, ".claude", "settings.json");
42458
- if (!existsSync20(settingsPath)) return false;
43051
+ if (!existsSync21(settingsPath)) return false;
42459
43052
  try {
42460
- const settings = JSON.parse(readFileSync19(settingsPath, "utf-8"));
43053
+ const settings = JSON.parse(readFileSync20(settingsPath, "utf-8"));
42461
43054
  const permissions = settings.permissions;
42462
43055
  if (!permissions || !Array.isArray(permissions.deny)) return false;
42463
43056
  const before = permissions.deny.length;
@@ -42468,7 +43061,7 @@ function removeDisallowedTools(cwd) {
42468
43061
  if (removed === 0) return false;
42469
43062
  if (permissions.deny.length === 0) delete permissions.deny;
42470
43063
  if (Object.keys(permissions).length === 0) delete settings.permissions;
42471
- writeFileSync10(settingsPath, `${JSON.stringify(settings, null, 2)}
43064
+ writeFileSync11(settingsPath, `${JSON.stringify(settings, null, 2)}
42472
43065
  `, "utf-8");
42473
43066
  return true;
42474
43067
  } catch {
@@ -42477,9 +43070,9 @@ function removeDisallowedTools(cwd) {
42477
43070
  }
42478
43071
  function removePreToolUseBashHook(cwd) {
42479
43072
  const settingsPath = join27(cwd, ".claude", "settings.json");
42480
- if (!existsSync20(settingsPath)) return false;
43073
+ if (!existsSync21(settingsPath)) return false;
42481
43074
  try {
42482
- const settings = JSON.parse(readFileSync19(settingsPath, "utf-8"));
43075
+ const settings = JSON.parse(readFileSync20(settingsPath, "utf-8"));
42483
43076
  const hooks = settings.hooks;
42484
43077
  if (!hooks) return false;
42485
43078
  let totalRemoved = 0;
@@ -42496,7 +43089,7 @@ function removePreToolUseBashHook(cwd) {
42496
43089
  }
42497
43090
  if (totalRemoved === 0) return false;
42498
43091
  if (Object.keys(hooks).length === 0) delete settings.hooks;
42499
- writeFileSync10(
43092
+ writeFileSync11(
42500
43093
  settingsPath,
42501
43094
  `${JSON.stringify(settings, null, 2)}
42502
43095
  `,
@@ -42514,11 +43107,31 @@ init_mcp_config_writer();
42514
43107
 
42515
43108
  // src/config/instruction-writer.ts
42516
43109
  init_agent_registry();
42517
- import { existsSync as existsSync21, mkdirSync as mkdirSync15, readFileSync as readFileSync20, unlinkSync as unlinkSync3, writeFileSync as writeFileSync11 } from "fs";
43110
+ import { existsSync as existsSync22, mkdirSync as mkdirSync15, readFileSync as readFileSync21, unlinkSync as unlinkSync3, writeFileSync as writeFileSync12 } from "fs";
42518
43111
  import { dirname as dirname5, join as join28 } from "path";
42519
43112
  var SENTINEL_START = "<!-- unerr:start -->";
42520
43113
  var SENTINEL_END = "<!-- unerr:end -->";
42521
- function getInstructionContent() {
43114
+ function getInstructionContent(ide) {
43115
+ const isClaudeCode = ide === "claude-code";
43116
+ const readForEditRow = isClaudeCode ? `| Understand a file before editing | \`file_read\` with \`purpose:'explore'\` to understand, then built-in \`Read\` (offset/limit) on target lines before Edit | Reading entire file |` : `| Read a file before editing | \`file_read\` with \`entity\` param or offset/limit for targeted access | Reading entire file |`;
43117
+ const twoStepSection = isClaudeCode ? `
43118
+ ### IMPORTANT: Two-step Read Routing (Claude Code specific)
43119
+
43120
+ **Why this matters:** Claude Code's Edit tool requires built-in \`Read\` to have been called on the file first. \`file_read\` (unerr MCP) does NOT satisfy this because it's a separate MCP tool. Meanwhile, built-in Read misses project conventions and facts that \`file_read\` auto-injects.
43121
+
43122
+ **The rule \u2014 two paths, choose by intent:**
43123
+
43124
+ | Intent | Tool | Why |
43125
+ |--------|------|-----|
43126
+ | Reading to understand code | \`file_read\` (unerr MCP) | Auto-injects conventions, facts, drift status |
43127
+ | Reading immediately before Edit | Built-in \`Read\` with offset/limit | Required by Edit tool \u2014 \`file_read\` does NOT satisfy this. Use targeted reads (offset/limit) for only the lines you plan to edit. |
43128
+
43129
+ When your next action is Edit, use built-in Read with offset/limit on the target lines. For everything else, use \`file_read\`.
43130
+
43131
+ **Common failure mode:** Using \`file_read\` to understand a file, then attempting Edit without calling built-in Read first. The Edit tool WILL reject with "File has not been read yet". Always call built-in Read (with offset/limit) immediately before Edit.` : "";
43132
+ const summaryEditNote = isClaudeCode ? `
43133
+ NEVER use built-in Read/Grep/Glob for code navigation. EXCEPTION: built-in Read (with offset/limit) is REQUIRED immediately before Edit (file_read cannot substitute \u2014 Edit will fail without it).` : `
43134
+ NEVER use built-in Read/Grep/Glob for code navigation \u2014 use unerr MCP tools instead.`;
42522
43135
  return `## REQUIRED: Use unerr Graph Intelligence Tools (18 MCP tools)
42523
43136
 
42524
43137
  This project has unerr MCP tools installed. You MUST use these instead of built-in Read/Grep/Glob for code navigation. unerr tools are graph-backed, return results in <5ms, and include project context that built-in tools miss.
@@ -42529,8 +43142,8 @@ This project has unerr MCP tools installed. You MUST use these instead of built-
42529
43142
  |---|---|---|
42530
43143
  | Find a function, class, or type | \`search_code\` | Grep, Glob |
42531
43144
  | Find callers or callees | \`get_references\` (direction: callers/callees) | Grep for function name |
42532
- | Read a file for understanding | \`file_read\` with \`purpose:'explore'\` (default, auto-injects conventions/facts) | Built-in Read (use ONLY before Edit) |
42533
- | Understand a file before editing | \`file_read\` with \`purpose:'edit'\` (returns full file, no gating) | Multiple offset/limit calls |
43145
+ | Read a file for understanding | \`file_read\` with \`purpose:'explore'\` (default, auto-injects conventions/facts) | Built-in Read/Grep/Glob |
43146
+ ${readForEditRow}
42534
43147
  | Get file structure overview | \`file_outline\` | Reading the whole file |
42535
43148
  | Get a specific function or class | \`get_entity\` or \`file_read\` with \`entity\` param | Reading entire file |
42536
43149
  | Trace imports/dependencies | \`get_imports\` or \`get_references\` (direction: callees) | Manual import scanning |
@@ -42538,27 +43151,12 @@ This project has unerr MCP tools installed. You MUST use these instead of built-
42538
43151
 
42539
43152
  ### FORBIDDEN Patterns (these waste tokens and miss context)
42540
43153
 
42541
- - Calling file_read multiple times with offset/limit to read a whole file -> use \`file_read\` with \`purpose:'edit'\` (one call, full content)
42542
43154
  - Reading an entire file to find one function -> use \`get_entity\` or \`file_read\` with \`entity\` param
42543
43155
  - Grep for a function name to find callers -> use \`get_references\` (finds indirect refs too)
42544
43156
  - Glob + Grep to search for code -> use \`search_code\` (indexes ALL entities, <5ms)
42545
43157
  - Reading multiple files to understand conventions -> use \`get_conventions\`
42546
43158
  - Guessing code style for new code -> use \`get_rules\` (with \`content\` param to validate)
42547
-
42548
- ### IMPORTANT: Two-step Read Routing
42549
-
42550
- **Why this matters:** The Edit tool has a hard technical constraint \u2014 it requires built-in \`Read\` to have been called on the file first. \`file_read\` (unerr MCP) does NOT satisfy this requirement because it's a separate MCP tool, not the built-in Read. Meanwhile, built-in Read misses project conventions, facts, and drift status that \`file_read\` auto-injects. Using the wrong one at the wrong time either blocks your edit or loses critical context.
42551
-
42552
- **The rule \u2014 two paths, choose by intent:**
42553
-
42554
- | Intent | Tool | Why |
42555
- |--------|------|-----|
42556
- | Reading to understand code | \`file_read\` (unerr MCP) | Auto-injects conventions, facts, drift status |
42557
- | Reading immediately before Edit | Built-in \`Read\` with offset/limit | Required by Edit tool \u2014 \`file_read\` does NOT satisfy this. Use targeted reads (offset/limit) for only the lines you plan to edit. |
42558
-
42559
- When your next action is Edit, use built-in Read. For everything else, use \`file_read\`.
42560
-
42561
- **Common failure mode:** Using \`file_read\` to understand a file, then attempting Edit without calling built-in Read first. The Edit tool WILL reject with "File has not been read yet" \u2014 \`file_read\` (MCP) cannot satisfy this requirement. Always call built-in Read immediately before Edit.
43159
+ - Reading a full file when you only need a section -> use \`file_read\` with \`entity\` param or offset/limit${twoStepSection}
42562
43160
 
42563
43161
  ### Tool Reference
42564
43162
 
@@ -42584,9 +43182,8 @@ When your next action is Edit, use built-in Read. For everything else, use \`fil
42584
43182
 
42585
43183
  \`file_read\` auto-injects relevant facts and conventions. For files >50 lines, call \`file_outline\` first, then \`file_read\` with \`entity\` param for targeted access.
42586
43184
 
42587
- **\`purpose\` parameter (IMPORTANT):** Controls read behavior \u2014 set it to match your intent:
42588
- - \`purpose:'explore'\` (default) \u2014 budget-capped, returns outline for large files. Use for browsing/understanding.
42589
- - \`purpose:'edit'\` \u2014 returns full file content with NO gating or budget cap. Use when you plan to modify the file. **One call instead of 3-5 offset/limit calls.**
43185
+ **\`purpose\` parameter:** Controls read behavior \u2014 set it to match your intent:
43186
+ - \`purpose:'explore'\` (default) \u2014 budget-capped, returns outline for large files. Use for browsing and pre-edit understanding.
42590
43187
  - \`purpose:'reference'\` \u2014 tight budget, entity/offset reads only. Use for quick lookups.
42591
43188
 
42592
43189
  #### Shell Compression (automatic)
@@ -42625,8 +43222,7 @@ ONLY use built-in Read/Grep/Glob when:
42625
43222
 
42626
43223
  ### Summary (CRITICAL \u2014 read this even if you skimmed above)
42627
43224
 
42628
- ALWAYS use unerr MCP tools: \`search_code\`, \`get_references\`, \`file_read\`, \`file_outline\`, \`get_entity\`.
42629
- NEVER use built-in Read/Grep/Glob for code navigation. EXCEPTION: built-in Read is REQUIRED before Edit (file_read cannot substitute \u2014 Edit will fail without it).
43225
+ ALWAYS use unerr MCP tools: \`search_code\`, \`get_references\`, \`file_read\`, \`file_outline\`, \`get_entity\`.${summaryEditNote}
42630
43226
  unerr skills available: /audit, /blame, /test, /lint, /commit, /review-pr.
42631
43227
  Before writing code: \`get_conventions\`, \`get_rules\`. After writing: \`get_rules\` with content to validate.
42632
43228
  Before risky changes: \`unerr_mark_working\`. If things break: \`unerr_revert_to_working_state\`.`;
@@ -42637,7 +43233,7 @@ description: REQUIRED \u2014 use unerr graph intelligence tools instead of built
42637
43233
  alwaysApply: true
42638
43234
  ---
42639
43235
 
42640
- ${getInstructionContent()}
43236
+ ${getInstructionContent("cursor")}
42641
43237
  `;
42642
43238
  }
42643
43239
  function writeInstructionFile(cwd, ide) {
@@ -42651,7 +43247,7 @@ function writeInstructionFile(cwd, ide) {
42651
43247
  }
42652
43248
  if (agentDef.instructionFormat === "windsurf-rule") {
42653
43249
  mkdirSync15(dirname5(filePath), { recursive: true });
42654
- const content = getInstructionContent();
43250
+ const content = getInstructionContent(ide);
42655
43251
  const truncatedContent = content.length > 5800 ? `${content.slice(0, 5800)}
42656
43252
 
42657
43253
  [Truncated \u2014 full content exceeds Windsurf 6K limit]` : content;
@@ -42662,19 +43258,19 @@ description: "Tool routing instructions for unerr MCP integration"
42662
43258
 
42663
43259
  ${truncatedContent}
42664
43260
  `;
42665
- const existed = existsSync21(filePath);
43261
+ const existed = existsSync22(filePath);
42666
43262
  if (existed) {
42667
- const existing = readFileSync20(filePath, "utf-8");
43263
+ const existing = readFileSync21(filePath, "utf-8");
42668
43264
  if (existing === windsurfContent) {
42669
43265
  return { path: filePath, action: "skipped" };
42670
43266
  }
42671
43267
  }
42672
- writeFileSync11(filePath, windsurfContent, "utf-8");
43268
+ writeFileSync12(filePath, windsurfContent, "utf-8");
42673
43269
  return { path: filePath, action: existed ? "updated" : "created" };
42674
43270
  }
42675
43271
  if (agentDef.instructionFormat === "antigravity-rule") {
42676
43272
  mkdirSync15(dirname5(filePath), { recursive: true });
42677
- const content = getInstructionContent();
43273
+ const content = getInstructionContent(ide);
42678
43274
  const antigravityContent = `---
42679
43275
  name: unerr-instructions
42680
43276
  description: Tool routing instructions for unerr MCP integration
@@ -42683,34 +43279,34 @@ type: manual
42683
43279
 
42684
43280
  ${content}
42685
43281
  `;
42686
- const existed = existsSync21(filePath);
43282
+ const existed = existsSync22(filePath);
42687
43283
  if (existed) {
42688
- const existing = readFileSync20(filePath, "utf-8");
43284
+ const existing = readFileSync21(filePath, "utf-8");
42689
43285
  if (existing === antigravityContent) {
42690
43286
  return { path: filePath, action: "skipped" };
42691
43287
  }
42692
43288
  }
42693
- writeFileSync11(filePath, antigravityContent, "utf-8");
43289
+ writeFileSync12(filePath, antigravityContent, "utf-8");
42694
43290
  return { path: filePath, action: existed ? "updated" : "created" };
42695
43291
  }
42696
- return mergeMarkdownSection(filePath, getInstructionContent());
43292
+ return mergeMarkdownSection(filePath, getInstructionContent(ide));
42697
43293
  }
42698
43294
  function mergeMarkdownSection(filePath, content) {
42699
43295
  const wrappedContent = `${SENTINEL_START}
42700
43296
  ${content}
42701
43297
  ${SENTINEL_END}`;
42702
- if (!existsSync21(filePath)) {
43298
+ if (!existsSync22(filePath)) {
42703
43299
  const dir = dirname5(filePath);
42704
- if (!existsSync21(dir)) mkdirSync15(dir, { recursive: true });
42705
- writeFileSync11(filePath, `${wrappedContent}
43300
+ if (!existsSync22(dir)) mkdirSync15(dir, { recursive: true });
43301
+ writeFileSync12(filePath, `${wrappedContent}
42706
43302
  `);
42707
43303
  return { path: filePath, action: "created" };
42708
43304
  }
42709
- const existing = readFileSync20(filePath, "utf-8");
43305
+ const existing = readFileSync21(filePath, "utf-8");
42710
43306
  const startIdx = existing.indexOf(SENTINEL_START);
42711
43307
  const endIdx = existing.indexOf(SENTINEL_END);
42712
43308
  if (startIdx === -1 || endIdx === -1) {
42713
- writeFileSync11(filePath, `${wrappedContent}
43309
+ writeFileSync12(filePath, `${wrappedContent}
42714
43310
 
42715
43311
  ${existing}`);
42716
43312
  return { path: filePath, action: "updated" };
@@ -42721,21 +43317,21 @@ ${existing}`);
42721
43317
  }
42722
43318
  const before = existing.slice(0, startIdx);
42723
43319
  const after = existing.slice(endIdx + SENTINEL_END.length);
42724
- writeFileSync11(filePath, `${before}${wrappedContent}${after}`);
43320
+ writeFileSync12(filePath, `${before}${wrappedContent}${after}`);
42725
43321
  return { path: filePath, action: "updated" };
42726
43322
  }
42727
43323
  function writeMdcInstructionFile(filePath) {
42728
43324
  const content = getMdcContent();
42729
- const alreadyExists = existsSync21(filePath);
43325
+ const alreadyExists = existsSync22(filePath);
42730
43326
  if (alreadyExists) {
42731
- const existing = readFileSync20(filePath, "utf-8");
43327
+ const existing = readFileSync21(filePath, "utf-8");
42732
43328
  if (existing === content) {
42733
43329
  return { path: filePath, action: "skipped" };
42734
43330
  }
42735
43331
  }
42736
43332
  const dir = dirname5(filePath);
42737
- if (!existsSync21(dir)) mkdirSync15(dir, { recursive: true });
42738
- writeFileSync11(filePath, content);
43333
+ if (!existsSync22(dir)) mkdirSync15(dir, { recursive: true });
43334
+ writeFileSync12(filePath, content);
42739
43335
  return {
42740
43336
  path: filePath,
42741
43337
  action: alreadyExists ? "updated" : "created"
@@ -42745,26 +43341,26 @@ function removeInstructionSection(cwd, ide) {
42745
43341
  const agentDef = getAgent(ide);
42746
43342
  if (!agentDef?.instructionFilePath) return false;
42747
43343
  const filePath = join28(cwd, agentDef.instructionFilePath);
42748
- if (!existsSync21(filePath)) return false;
43344
+ if (!existsSync22(filePath)) return false;
42749
43345
  if (agentDef.instructionFormat === "mdc") {
42750
43346
  unlinkSync3(filePath);
42751
43347
  return true;
42752
43348
  }
42753
43349
  if (agentDef.instructionFormat === "windsurf-rule") {
42754
- if (existsSync21(filePath)) {
43350
+ if (existsSync22(filePath)) {
42755
43351
  unlinkSync3(filePath);
42756
43352
  return true;
42757
43353
  }
42758
43354
  return false;
42759
43355
  }
42760
43356
  if (agentDef.instructionFormat === "antigravity-rule") {
42761
- if (existsSync21(filePath)) {
43357
+ if (existsSync22(filePath)) {
42762
43358
  unlinkSync3(filePath);
42763
43359
  return true;
42764
43360
  }
42765
43361
  return false;
42766
43362
  }
42767
- const content = readFileSync20(filePath, "utf-8");
43363
+ const content = readFileSync21(filePath, "utf-8");
42768
43364
  const startIdx = content.indexOf(SENTINEL_START);
42769
43365
  const endIdx = content.indexOf(SENTINEL_END);
42770
43366
  if (startIdx === -1 || endIdx === -1) return false;
@@ -42775,12 +43371,12 @@ function removeInstructionSection(cwd, ide) {
42775
43371
  if (trimmed.length === 0) {
42776
43372
  unlinkSync3(filePath);
42777
43373
  } else {
42778
- writeFileSync11(filePath, cleaned);
43374
+ writeFileSync12(filePath, cleaned);
42779
43375
  }
42780
43376
  return true;
42781
43377
  }
42782
43378
  function generateCustomInstructions(ide) {
42783
- const content = getInstructionContent();
43379
+ const content = getInstructionContent(ide);
42784
43380
  if (ide && ide !== "other") {
42785
43381
  const agentDef = getAgent(ide);
42786
43382
  if (agentDef?.instructionFilePath) {
@@ -43152,16 +43748,16 @@ function installCursorHooks(cwd) {
43152
43748
  ]
43153
43749
  };
43154
43750
  try {
43155
- if (!existsSync23(hooksDir)) {
43751
+ if (!existsSync24(hooksDir)) {
43156
43752
  mkdirSync17(hooksDir, { recursive: true });
43157
43753
  }
43158
43754
  let config = {
43159
43755
  version: 1,
43160
43756
  hooks: {}
43161
43757
  };
43162
- if (existsSync23(hooksJsonPath)) {
43758
+ if (existsSync24(hooksJsonPath)) {
43163
43759
  try {
43164
- const existing = JSON.parse(readFileSync22(hooksJsonPath, "utf-8"));
43760
+ const existing = JSON.parse(readFileSync23(hooksJsonPath, "utf-8"));
43165
43761
  if (existing.version && existing.hooks) {
43166
43762
  config = existing;
43167
43763
  }
@@ -43178,7 +43774,7 @@ function installCursorHooks(cwd) {
43178
43774
  }
43179
43775
  config.hooks[event] = existing;
43180
43776
  }
43181
- writeFileSync13(hooksJsonPath, `${JSON.stringify(config, null, 2)}
43777
+ writeFileSync14(hooksJsonPath, `${JSON.stringify(config, null, 2)}
43182
43778
  `);
43183
43779
  writeCursorHookScript(hooksDir, "unerr-pre-tool.sh", CURSOR_PRE_TOOL_SCRIPT);
43184
43780
  writeCursorHookScript(hooksDir, "unerr-post-tool.sh", CURSOR_POST_TOOL_SCRIPT);
@@ -43191,15 +43787,15 @@ function installCursorHooks(cwd) {
43191
43787
  }
43192
43788
  function writeCursorHookScript(dir, filename, content) {
43193
43789
  const scriptPath = join30(dir, filename);
43194
- writeFileSync13(scriptPath, content, "utf-8");
43790
+ writeFileSync14(scriptPath, content, "utf-8");
43195
43791
  chmodSync4(scriptPath, 493);
43196
43792
  }
43197
43793
  function installClineHooks(cwd) {
43198
43794
  const hooksDir = join30(cwd, ".clinerules", "hooks");
43199
43795
  const hookPath = join30(hooksDir, "unerr-pre-tool.sh");
43200
- if (existsSync23(hookPath)) return true;
43796
+ if (existsSync24(hookPath)) return true;
43201
43797
  try {
43202
- if (!existsSync23(hooksDir)) {
43798
+ if (!existsSync24(hooksDir)) {
43203
43799
  const { mkdirSync: mkdirSync39 } = __require("fs");
43204
43800
  mkdirSync39(hooksDir, { recursive: true });
43205
43801
  }
@@ -43209,7 +43805,7 @@ function installClineHooks(cwd) {
43209
43805
  # Removed by: unerr uninstall cline
43210
43806
  cat /dev/stdin | unerr hook pre-read
43211
43807
  `;
43212
- writeFileSync13(hookPath, hookContent, "utf-8");
43808
+ writeFileSync14(hookPath, hookContent, "utf-8");
43213
43809
  chmodSync4(hookPath, 493);
43214
43810
  return true;
43215
43811
  } catch {
@@ -43218,15 +43814,15 @@ cat /dev/stdin | unerr hook pre-read
43218
43814
  }
43219
43815
  function ensureGitignore(cwd) {
43220
43816
  const gitignorePath = join30(cwd, ".gitignore");
43221
- if (!existsSync23(gitignorePath)) return false;
43817
+ if (!existsSync24(gitignorePath)) return false;
43222
43818
  try {
43223
- const content = readFileSync22(gitignorePath, "utf-8");
43819
+ const content = readFileSync23(gitignorePath, "utf-8");
43224
43820
  const lines = content.split("\n");
43225
43821
  if (lines.some((l) => l.trim() === ".unerr" || l.trim() === ".unerr/")) {
43226
43822
  return false;
43227
43823
  }
43228
43824
  const newline = content.endsWith("\n") ? "" : "\n";
43229
- writeFileSync13(
43825
+ writeFileSync14(
43230
43826
  gitignorePath,
43231
43827
  `${content}${newline}
43232
43828
  # unerr local artifacts
@@ -43241,7 +43837,7 @@ function ensureGitignore(cwd) {
43241
43837
 
43242
43838
  // src/commands/learn.ts
43243
43839
  init_correction_detector();
43244
- import { existsSync as existsSync25 } from "fs";
43840
+ import { existsSync as existsSync26 } from "fs";
43245
43841
  import { join as join31 } from "path";
43246
43842
  import { gunzipSync as gunzipSync2 } from "zlib";
43247
43843
  import pc3 from "picocolors";
@@ -43258,7 +43854,7 @@ function registerLearnCommand(program2) {
43258
43854
  const cwd = process.cwd();
43259
43855
  const unerrDir = join31(cwd, ".unerr");
43260
43856
  const ledgerPath = join31(unerrDir, "ledger", "shadow.jsonl");
43261
- if (!existsSync25(ledgerPath)) {
43857
+ if (!existsSync26(ledgerPath)) {
43262
43858
  fail("No shadow ledger found at .unerr/ledger/shadow.jsonl");
43263
43859
  info(
43264
43860
  "The shadow ledger is created when the unerr proxy runs. Start the proxy first."
@@ -43315,20 +43911,20 @@ async function persistPatterns(patterns, _unerrDir) {
43315
43911
  try {
43316
43912
  const cwd = process.cwd();
43317
43913
  const configPath = join31(cwd, ".unerr", "config.json");
43318
- if (!existsSync25(configPath)) {
43914
+ if (!existsSync26(configPath)) {
43319
43915
  warn(
43320
43916
  "No repo config found \u2014 cannot persist to CozoDB. Run 'unerr' to configure."
43321
43917
  );
43322
43918
  return;
43323
43919
  }
43324
- const { readFileSync: readFileSync57 } = await import("fs");
43325
- const config = JSON.parse(readFileSync57(configPath, "utf-8"));
43920
+ const { readFileSync: readFileSync58 } = await import("fs");
43921
+ const config = JSON.parse(readFileSync58(configPath, "utf-8"));
43326
43922
  if (!config.repoId || !config.orgId) {
43327
43923
  warn("Repo not configured \u2014 cannot persist to CozoDB.");
43328
43924
  return;
43329
43925
  }
43330
43926
  const snapshotPath2 = join31(cwd, ".unerr", "snapshots", "graph.msgpack.gz");
43331
- if (!existsSync25(snapshotPath2)) {
43927
+ if (!existsSync26(snapshotPath2)) {
43332
43928
  warn("No graph snapshot found \u2014 cannot persist to CozoDB.");
43333
43929
  return;
43334
43930
  }
@@ -43340,7 +43936,7 @@ async function persistPatterns(patterns, _unerrDir) {
43340
43936
  const { CozoGraphStore: CozoGraphStore2 } = await Promise.resolve().then(() => (init_local_graph(), local_graph_exports));
43341
43937
  const store = await CozoGraphStore2.create(db);
43342
43938
  const { unpack } = await import("msgpackr");
43343
- const raw = readFileSync57(snapshotPath2);
43939
+ const raw = readFileSync58(snapshotPath2);
43344
43940
  const buffer = gunzipSync2(raw);
43345
43941
  const envelope = unpack(buffer);
43346
43942
  await store.loadSnapshot(envelope);
@@ -43357,16 +43953,16 @@ async function persistPatterns(patterns, _unerrDir) {
43357
43953
 
43358
43954
  // src/commands/manifest.ts
43359
43955
  init_exec();
43360
- import { existsSync as existsSync26, readFileSync as readFileSync24 } from "fs";
43956
+ import { existsSync as existsSync27, readFileSync as readFileSync25 } from "fs";
43361
43957
  import { join as join32 } from "path";
43362
43958
  async function findRepoRoot() {
43363
43959
  return gitQuery(["rev-parse", "--show-toplevel"]);
43364
43960
  }
43365
43961
  function loadManifest(repoRoot) {
43366
43962
  const manifestPath = join32(repoRoot, ".unerr", "manifest.json");
43367
- if (!existsSync26(manifestPath)) return null;
43963
+ if (!existsSync27(manifestPath)) return null;
43368
43964
  try {
43369
- const raw = readFileSync24(manifestPath, "utf-8");
43965
+ const raw = readFileSync25(manifestPath, "utf-8");
43370
43966
  return JSON.parse(raw);
43371
43967
  } catch {
43372
43968
  return null;
@@ -43687,7 +44283,7 @@ function registerStatsCommand(program2) {
43687
44283
 
43688
44284
  // src/commands/status.ts
43689
44285
  init_git();
43690
- import { existsSync as existsSync31, readFileSync as readFileSync29 } from "fs";
44286
+ import { existsSync as existsSync32, readFileSync as readFileSync30 } from "fs";
43691
44287
  import { join as join38 } from "path";
43692
44288
  import React from "react";
43693
44289
  function buildSuggestions(data) {
@@ -43736,9 +44332,9 @@ function registerStatusCommand(program2) {
43736
44332
  const localDataDir = join38(cwd, ".unerr");
43737
44333
  let repoId = "";
43738
44334
  const configPath = join38(unerrDir, "config.json");
43739
- if (existsSync31(configPath)) {
44335
+ if (existsSync32(configPath)) {
43740
44336
  try {
43741
- const config = JSON.parse(readFileSync29(configPath, "utf-8"));
44337
+ const config = JSON.parse(readFileSync30(configPath, "utf-8"));
43742
44338
  repoId = config.repoId ?? "";
43743
44339
  } catch {
43744
44340
  }
@@ -43767,9 +44363,9 @@ function registerStatusCommand(program2) {
43767
44363
  let proxyStatus = "Not running";
43768
44364
  let proxyRunning = false;
43769
44365
  const pidPath = join38(unerrDir, "state", "proxy.pid");
43770
- if (existsSync31(pidPath)) {
44366
+ if (existsSync32(pidPath)) {
43771
44367
  try {
43772
- const pidStr = readFileSync29(pidPath, "utf-8").trim();
44368
+ const pidStr = readFileSync30(pidPath, "utf-8").trim();
43773
44369
  const pid = Number.parseInt(pidStr, 10);
43774
44370
  process.kill(pid, 0);
43775
44371
  proxyStatus = `Running (PID ${pid})`;
@@ -43780,10 +44376,10 @@ function registerStatusCommand(program2) {
43780
44376
  }
43781
44377
  let graphInfo = "No local graph";
43782
44378
  const manifestsDir = join38(localDataDir, "manifests");
43783
- if (repoId && existsSync31(join38(manifestsDir, `${repoId}.json`))) {
44379
+ if (repoId && existsSync32(join38(manifestsDir, `${repoId}.json`))) {
43784
44380
  try {
43785
44381
  const manifest = JSON.parse(
43786
- readFileSync29(join38(manifestsDir, `${repoId}.json`), "utf-8")
44382
+ readFileSync30(join38(manifestsDir, `${repoId}.json`), "utf-8")
43787
44383
  );
43788
44384
  const entityCount = manifest.entityCount ?? 0;
43789
44385
  const edgeCount = manifest.edgeCount ?? 0;
@@ -43802,10 +44398,10 @@ function registerStatusCommand(program2) {
43802
44398
  }
43803
44399
  let drift;
43804
44400
  const driftSummaryPath = join38(unerrDir, "drift", "drift_summary.json");
43805
- if (existsSync31(driftSummaryPath)) {
44401
+ if (existsSync32(driftSummaryPath)) {
43806
44402
  try {
43807
44403
  const summary = JSON.parse(
43808
- readFileSync29(driftSummaryPath, "utf-8")
44404
+ readFileSync30(driftSummaryPath, "utf-8")
43809
44405
  );
43810
44406
  drift = {
43811
44407
  modified: summary.modified ?? 0,
@@ -43818,9 +44414,9 @@ function registerStatusCommand(program2) {
43818
44414
  let healthGrade;
43819
44415
  let healthScore;
43820
44416
  const graphVersionPath = join38(unerrDir, "state", "graph_version.json");
43821
- if (existsSync31(graphVersionPath)) {
44417
+ if (existsSync32(graphVersionPath)) {
43822
44418
  try {
43823
- const gv = JSON.parse(readFileSync29(graphVersionPath, "utf-8"));
44419
+ const gv = JSON.parse(readFileSync30(graphVersionPath, "utf-8"));
43824
44420
  healthGrade = gv.health_grade;
43825
44421
  healthScore = gv.health_score;
43826
44422
  } catch {
@@ -43831,15 +44427,15 @@ function registerStatusCommand(program2) {
43831
44427
  try {
43832
44428
  const snapshotsDir = join38(localDataDir, "snapshots");
43833
44429
  let snapshotPath2 = join38(snapshotsDir, "graph.msgpack.gz");
43834
- if (!existsSync31(snapshotPath2)) {
44430
+ if (!existsSync32(snapshotPath2)) {
43835
44431
  snapshotPath2 = join38(snapshotsDir, "graph.msgpack");
43836
44432
  }
43837
- if (existsSync31(snapshotPath2)) {
44433
+ if (existsSync32(snapshotPath2)) {
43838
44434
  const { gunzipSync: gunzipSync3 } = await import("zlib");
43839
44435
  const { unpack } = await import("msgpackr");
43840
44436
  const { default: CozoDbConstructor } = await import("cozo-node");
43841
44437
  const { CozoGraphStore: CozoGraphStore2 } = await Promise.resolve().then(() => (init_local_graph(), local_graph_exports));
43842
- const raw = readFileSync29(snapshotPath2);
44438
+ const raw = readFileSync30(snapshotPath2);
43843
44439
  let buffer;
43844
44440
  try {
43845
44441
  buffer = gunzipSync3(raw);
@@ -43858,9 +44454,9 @@ function registerStatusCommand(program2) {
43858
44454
  let liveToolCalls;
43859
44455
  let latency;
43860
44456
  const statsPath = join38(unerrDir, "state", "session_stats.json");
43861
- if (existsSync31(statsPath) && proxyRunning) {
44457
+ if (existsSync32(statsPath) && proxyRunning) {
43862
44458
  try {
43863
- const liveStats = JSON.parse(readFileSync29(statsPath, "utf-8"));
44459
+ const liveStats = JSON.parse(readFileSync30(statsPath, "utf-8"));
43864
44460
  const localCalls = liveStats.toolCallsLocal ?? 0;
43865
44461
  if (localCalls > 0) {
43866
44462
  liveToolCalls = { local: localCalls };
@@ -43887,8 +44483,8 @@ function registerStatusCommand(program2) {
43887
44483
  "state",
43888
44484
  "firewall_stats.json"
43889
44485
  );
43890
- if (existsSync31(firewallStatsPath)) {
43891
- const fs5 = JSON.parse(readFileSync29(firewallStatsPath, "utf-8"));
44486
+ if (existsSync32(firewallStatsPath)) {
44487
+ const fs5 = JSON.parse(readFileSync30(firewallStatsPath, "utf-8"));
43892
44488
  firewallBlocked = fs5.blocked ?? 0;
43893
44489
  }
43894
44490
  } catch {
@@ -43925,9 +44521,9 @@ function registerStatusCommand(program2) {
43925
44521
  }
43926
44522
  let lastIndexed;
43927
44523
  const snapshotMetaPath = join38(unerrDir, "state", "snapshot_meta.json");
43928
- if (existsSync31(snapshotMetaPath)) {
44524
+ if (existsSync32(snapshotMetaPath)) {
43929
44525
  try {
43930
- const meta = JSON.parse(readFileSync29(snapshotMetaPath, "utf-8"));
44526
+ const meta = JSON.parse(readFileSync30(snapshotMetaPath, "utf-8"));
43931
44527
  if (meta.indexedAt) {
43932
44528
  const ageMs = Date.now() - new Date(meta.indexedAt).getTime();
43933
44529
  const ageHours = Math.floor(ageMs / 36e5);
@@ -43944,8 +44540,8 @@ function registerStatusCommand(program2) {
43944
44540
  let corrections;
43945
44541
  try {
43946
44542
  const corrPath = join38(unerrDir, "state", "corrections_count.json");
43947
- if (existsSync31(corrPath)) {
43948
- const cc = JSON.parse(readFileSync29(corrPath, "utf-8"));
44543
+ if (existsSync32(corrPath)) {
44544
+ const cc = JSON.parse(readFileSync30(corrPath, "utf-8"));
43949
44545
  corrections = cc.count;
43950
44546
  }
43951
44547
  } catch {
@@ -43954,8 +44550,8 @@ function registerStatusCommand(program2) {
43954
44550
  let conventionCount;
43955
44551
  let ruleCount;
43956
44552
  try {
43957
- if (existsSync31(graphVersionPath)) {
43958
- const gv = JSON.parse(readFileSync29(graphVersionPath, "utf-8"));
44553
+ if (existsSync32(graphVersionPath)) {
44554
+ const gv = JSON.parse(readFileSync30(graphVersionPath, "utf-8"));
43959
44555
  communityCount = gv.community_count;
43960
44556
  conventionCount = gv.convention_count;
43961
44557
  ruleCount = gv.rule_count;
@@ -43997,8 +44593,8 @@ function registerStatusCommand(program2) {
43997
44593
  let ledgerEntryCount;
43998
44594
  try {
43999
44595
  const ledgerPath = join38(unerrDir, "ledger", "shadow.jsonl");
44000
- if (existsSync31(ledgerPath)) {
44001
- const content = readFileSync29(ledgerPath, "utf-8");
44596
+ if (existsSync32(ledgerPath)) {
44597
+ const content = readFileSync30(ledgerPath, "utf-8");
44002
44598
  ledgerEntryCount = content.split("\n").filter((l) => l.trim().length > 0).length;
44003
44599
  }
44004
44600
  } catch {
@@ -44006,7 +44602,7 @@ function registerStatusCommand(program2) {
44006
44602
  let logPath;
44007
44603
  try {
44008
44604
  const logsDir = join38(unerrDir, "logs");
44009
- if (existsSync31(logsDir)) {
44605
+ if (existsSync32(logsDir)) {
44010
44606
  const { readdirSync: readdirSync15, statSync: statSync12 } = await import("fs");
44011
44607
  const logs = readdirSync15(logsDir).filter((f) => f.startsWith("session-") && f.endsWith(".log")).map((f) => ({
44012
44608
  name: f,
@@ -44037,8 +44633,8 @@ function registerStatusCommand(program2) {
44037
44633
  if (!proxyRunning) {
44038
44634
  try {
44039
44635
  const statsPath2 = join38(unerrDir, "state", "session_stats.json");
44040
- if (existsSync31(statsPath2)) {
44041
- const raw = JSON.parse(readFileSync29(statsPath2, "utf-8"));
44636
+ if (existsSync32(statsPath2)) {
44637
+ const raw = JSON.parse(readFileSync30(statsPath2, "utf-8"));
44042
44638
  const totalCalls = raw.toolCallsLocal ?? 0;
44043
44639
  if (totalCalls > 0) {
44044
44640
  const endTime = raw.updatedAt ? new Date(raw.updatedAt) : /* @__PURE__ */ new Date();
@@ -44073,8 +44669,8 @@ function registerStatusCommand(program2) {
44073
44669
  let preBashHookConfigured = false;
44074
44670
  try {
44075
44671
  const sp = join38(cwd, ".claude", "settings.json");
44076
- if (existsSync31(sp)) {
44077
- const raw = readFileSync29(sp, "utf8");
44672
+ if (existsSync32(sp)) {
44673
+ const raw = readFileSync30(sp, "utf8");
44078
44674
  preBashHookConfigured = raw.includes("unerr hook pre-bash") || raw.includes('"unerr hook pre-bash"');
44079
44675
  }
44080
44676
  } catch {
@@ -44362,7 +44958,7 @@ function registerTimelineCommand(program2) {
44362
44958
 
44363
44959
  // src/commands/uninstall.ts
44364
44960
  init_agent_registry();
44365
- import { existsSync as existsSync33, readFileSync as readFileSync31, unlinkSync as unlinkSync5, writeFileSync as writeFileSync17 } from "fs";
44961
+ import { existsSync as existsSync34, readFileSync as readFileSync32, unlinkSync as unlinkSync5, writeFileSync as writeFileSync18 } from "fs";
44366
44962
  import { join as join40 } from "path";
44367
44963
  init_hook_installer();
44368
44964
  init_mcp_config_writer();
@@ -44507,9 +45103,9 @@ function runUninstallAll(cwd) {
44507
45103
  }
44508
45104
  function removeCursorHooks(cwd) {
44509
45105
  const hooksPath = join40(cwd, ".cursor", "hooks.json");
44510
- if (!existsSync33(hooksPath)) return false;
45106
+ if (!existsSync34(hooksPath)) return false;
44511
45107
  try {
44512
- const config = JSON.parse(readFileSync31(hooksPath, "utf-8"));
45108
+ const config = JSON.parse(readFileSync32(hooksPath, "utf-8"));
44513
45109
  if (!Array.isArray(config.hooks)) return false;
44514
45110
  const before = config.hooks.length;
44515
45111
  config.hooks = config.hooks.filter(
@@ -44519,7 +45115,7 @@ function removeCursorHooks(cwd) {
44519
45115
  if (config.hooks.length === 0) {
44520
45116
  unlinkSync5(hooksPath);
44521
45117
  } else {
44522
- writeFileSync17(hooksPath, `${JSON.stringify(config, null, 2)}
45118
+ writeFileSync18(hooksPath, `${JSON.stringify(config, null, 2)}
44523
45119
  `);
44524
45120
  }
44525
45121
  return true;
@@ -44529,7 +45125,7 @@ function removeCursorHooks(cwd) {
44529
45125
  }
44530
45126
  function removeClineHooks(cwd) {
44531
45127
  const hookPath = join40(cwd, ".clinerules", "hooks", "unerr-pre-tool.sh");
44532
- if (!existsSync33(hookPath)) return false;
45128
+ if (!existsSync34(hookPath)) return false;
44533
45129
  try {
44534
45130
  unlinkSync5(hookPath);
44535
45131
  return true;
@@ -45084,9 +45680,9 @@ async function detectProjectRoot(cwd) {
45084
45680
  }
45085
45681
  function readLocalConfig(cwd) {
45086
45682
  const configPath = join67(cwd, ".unerr", "config.json");
45087
- if (!existsSync62(configPath)) return null;
45683
+ if (!existsSync63(configPath)) return null;
45088
45684
  try {
45089
- return JSON.parse(readFileSync56(configPath, "utf-8"));
45685
+ return JSON.parse(readFileSync57(configPath, "utf-8"));
45090
45686
  } catch {
45091
45687
  return null;
45092
45688
  }
@@ -45161,7 +45757,7 @@ async function mcpBoot(cwd) {
45161
45757
  }
45162
45758
  const stateDir = join67(cwd, ".unerr", "state");
45163
45759
  const sockPath = join67(stateDir, "proxy.sock");
45164
- if (existsSync62(sockPath)) {
45760
+ if (existsSync63(sockPath)) {
45165
45761
  const { PidLock: PidLock2 } = await Promise.resolve().then(() => (init_pid_lock(), pid_lock_exports));
45166
45762
  const pidLock = new PidLock2(stateDir);
45167
45763
  const probeResult = await pidLock.probe();
@@ -45182,12 +45778,12 @@ async function mcpBoot(cwd) {
45182
45778
  }
45183
45779
  }
45184
45780
  if (!readLocalConfig(cwd)) {
45185
- const { mkdirSync: mkdirSync39, writeFileSync: writeFileSync36 } = await import("fs");
45781
+ const { mkdirSync: mkdirSync39, writeFileSync: writeFileSync37 } = await import("fs");
45186
45782
  const { createHash: createHash5 } = await import("crypto");
45187
45783
  const repoId = createHash5("sha256").update(cwd).digest("hex").slice(0, 12);
45188
45784
  const unerrDir = join67(cwd, ".unerr");
45189
45785
  mkdirSync39(unerrDir, { recursive: true });
45190
- writeFileSync36(
45786
+ writeFileSync37(
45191
45787
  join67(unerrDir, "config.json"),
45192
45788
  JSON.stringify(
45193
45789
  { repoId, mode: "local", createdAt: (/* @__PURE__ */ new Date()).toISOString() },