@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/README.md +24 -8
- package/dist/cli.js +1252 -656
- package/dist/ui/assets/index-B_QbOkZx.js +10 -0
- package/dist/ui/assets/index-C2BBqscH.css +1 -0
- package/dist/ui/assets/rolldown-runtime-S-ySWqyJ.js +1 -0
- package/dist/ui/index.html +3 -3
- package/package.json +2 -1
- package/dist/ui/assets/index-CJ6H4e3-.css +0 -1
- package/dist/ui/assets/index-CRB4z0c9.js +0 -10
- package/dist/ui/assets/rolldown-runtime-jpDsebLB.js +0 -1
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:
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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
|
|
9711
|
-
if (
|
|
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
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
|
|
9725
|
-
|
|
9726
|
-
|
|
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 (!
|
|
9907
|
+
if (!anySucceeded) {
|
|
9732
9908
|
return skipResult(
|
|
9733
9909
|
existingEdges,
|
|
9734
|
-
`SCIP binary
|
|
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:
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
11924
|
+
existsSync as existsSync19,
|
|
11419
11925
|
mkdirSync as mkdirSync13,
|
|
11420
11926
|
unlinkSync as unlinkSync2,
|
|
11421
|
-
writeFileSync as
|
|
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 (
|
|
11933
|
+
if (existsSync19(hookPath)) {
|
|
11428
11934
|
return { path: hookPath, action: "already_exists" };
|
|
11429
11935
|
}
|
|
11430
11936
|
try {
|
|
11431
|
-
if (!
|
|
11937
|
+
if (!existsSync19(hooksDir)) {
|
|
11432
11938
|
mkdirSync13(hooksDir, { recursive: true });
|
|
11433
11939
|
}
|
|
11434
|
-
|
|
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 (!
|
|
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
|
|
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
|
|
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) =>
|
|
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:
|
|
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
|
|
12343
|
+
existsSync as existsSync23,
|
|
11838
12344
|
mkdirSync as mkdirSync16,
|
|
11839
|
-
readFileSync as
|
|
12345
|
+
readFileSync as readFileSync22,
|
|
11840
12346
|
readdirSync as readdirSync7,
|
|
11841
12347
|
rmSync,
|
|
11842
12348
|
unlinkSync as unlinkSync4,
|
|
11843
|
-
writeFileSync as
|
|
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
|
-
|
|
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 (!
|
|
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 =
|
|
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 (
|
|
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-") &&
|
|
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 (!
|
|
12701
|
+
if (!existsSync23(dir)) return [];
|
|
12196
12702
|
try {
|
|
12197
12703
|
if (dirPerSkill) {
|
|
12198
12704
|
return readdirSync7(dir).filter(
|
|
12199
|
-
(f) => f.startsWith("unerr-") &&
|
|
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
|
|
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 (!
|
|
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 =
|
|
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
|
|
12980
|
+
existsSync as existsSync28,
|
|
12475
12981
|
mkdirSync as mkdirSync18,
|
|
12476
|
-
readFileSync as
|
|
12477
|
-
writeFileSync as
|
|
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 (!
|
|
13121
|
+
if (!existsSync28(contextPath)) return 1;
|
|
12616
13122
|
try {
|
|
12617
|
-
const data = JSON.parse(
|
|
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 (!
|
|
13132
|
+
if (!existsSync28(ledgerDir)) {
|
|
12627
13133
|
mkdirSync18(ledgerDir, { recursive: true });
|
|
12628
13134
|
}
|
|
12629
13135
|
let data = {};
|
|
12630
|
-
if (
|
|
13136
|
+
if (existsSync28(contextPath)) {
|
|
12631
13137
|
try {
|
|
12632
|
-
data = JSON.parse(
|
|
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
|
-
|
|
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 (!
|
|
13153
|
+
if (!existsSync28(ledgerDir)) {
|
|
12648
13154
|
mkdirSync18(ledgerDir, { recursive: true });
|
|
12649
13155
|
}
|
|
12650
13156
|
let existing = [];
|
|
12651
|
-
if (
|
|
13157
|
+
if (existsSync28(revertedPath)) {
|
|
12652
13158
|
try {
|
|
12653
|
-
existing = JSON.parse(
|
|
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
|
-
|
|
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
|
|
13224
|
+
existsSync as existsSync29,
|
|
12719
13225
|
mkdirSync as mkdirSync19,
|
|
12720
|
-
readFileSync as
|
|
12721
|
-
writeFileSync as
|
|
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 (!
|
|
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 (!
|
|
13369
|
+
if (!existsSync29(this.filePath)) return [];
|
|
12864
13370
|
try {
|
|
12865
|
-
const content =
|
|
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 (!
|
|
13408
|
+
if (!existsSync29(this.filePath)) return;
|
|
12903
13409
|
try {
|
|
12904
|
-
const content =
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
13441
|
+
if (!existsSync29(this.filePath)) return;
|
|
12936
13442
|
try {
|
|
12937
|
-
const content =
|
|
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 (!
|
|
13471
|
+
if (!existsSync29(this.filePath)) return 0;
|
|
12966
13472
|
try {
|
|
12967
|
-
const content =
|
|
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
|
|
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 (!
|
|
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 (!
|
|
13635
|
+
if (!existsSync30(path7)) {
|
|
13130
13636
|
return { activeTimeline: 0, forks: [] };
|
|
13131
13637
|
}
|
|
13132
13638
|
try {
|
|
13133
|
-
const raw =
|
|
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
|
-
|
|
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:
|
|
13715
|
+
const { readFileSync: readFileSync58 } = __require("fs");
|
|
13210
13716
|
const raw = JSON.parse(
|
|
13211
|
-
|
|
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
|
|
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 (!
|
|
13905
|
+
if (!existsSync31(headPath)) return null;
|
|
13400
13906
|
try {
|
|
13401
|
-
const content =
|
|
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:
|
|
14430
|
+
const { readFileSync: readFileSync58 } = __require("fs");
|
|
13925
14431
|
const raw = JSON.parse(
|
|
13926
|
-
|
|
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:
|
|
14493
|
+
const { readFileSync: readFileSync58 } = __require("fs");
|
|
13988
14494
|
const raw = JSON.parse(
|
|
13989
|
-
|
|
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
|
|
15099
|
+
existsSync as existsSync35,
|
|
14594
15100
|
mkdirSync as mkdirSync21,
|
|
14595
|
-
readFileSync as
|
|
15101
|
+
readFileSync as readFileSync33,
|
|
14596
15102
|
unlinkSync as unlinkSync6,
|
|
14597
|
-
writeFileSync as
|
|
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 (!
|
|
15183
|
+
if (!existsSync35(stateDir)) {
|
|
14678
15184
|
mkdirSync21(stateDir, { recursive: true });
|
|
14679
15185
|
}
|
|
14680
|
-
if (
|
|
15186
|
+
if (existsSync35(this.pidPath)) {
|
|
14681
15187
|
try {
|
|
14682
|
-
const raw =
|
|
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
|
-
|
|
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 (
|
|
14788
|
-
const raw =
|
|
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 (!
|
|
15307
|
+
if (!existsSync35(this.pidPath)) return { locked: false };
|
|
14802
15308
|
try {
|
|
14803
|
-
const raw =
|
|
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 (!
|
|
15324
|
+
if (!existsSync35(this.pidPath)) return { alive: false };
|
|
14819
15325
|
try {
|
|
14820
|
-
const raw =
|
|
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 (!
|
|
15343
|
+
if (!existsSync35(pidPath)) return null;
|
|
14838
15344
|
try {
|
|
14839
|
-
const raw =
|
|
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", "
|
|
15289
|
-
description: "Read intent: 'explore' (default, budget-capped for browsing), '
|
|
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
|
|
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 (!
|
|
16266
|
+
if (!existsSync36(versionPath)) return true;
|
|
15760
16267
|
try {
|
|
15761
|
-
const data = JSON.parse(
|
|
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 (
|
|
15773
|
-
data = JSON.parse(
|
|
16279
|
+
if (existsSync36(versionPath)) {
|
|
16280
|
+
data = JSON.parse(readFileSync34(versionPath, "utf-8"));
|
|
15774
16281
|
}
|
|
15775
16282
|
data.first_boot_shown = true;
|
|
15776
|
-
|
|
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
|
|
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 = !
|
|
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
|
|
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 (!
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
18464
|
+
if (!existsSync39(abs)) {
|
|
17958
18465
|
throw new Error(`File not found: ${abs}`);
|
|
17959
18466
|
}
|
|
17960
|
-
const raw =
|
|
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
|
|
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
|
|
18170
|
-
const purpose =
|
|
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 =
|
|
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 (!
|
|
18689
|
+
if (!existsSync40(abs)) {
|
|
18184
18690
|
return { content: { error: `File not found: ${abs}` } };
|
|
18185
18691
|
}
|
|
18186
|
-
const raw =
|
|
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 && !
|
|
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
|
|
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 ??
|
|
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 (
|
|
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:
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
21639
|
-
const content =
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 =
|
|
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
|
-
|
|
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
|
|
23626
|
+
existsSync as existsSync43,
|
|
23056
23627
|
mkdirSync as mkdirSync24,
|
|
23057
|
-
readFileSync as
|
|
23628
|
+
readFileSync as readFileSync41,
|
|
23058
23629
|
renameSync,
|
|
23059
|
-
writeFileSync as
|
|
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 (!
|
|
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 (!
|
|
23774
|
+
if (!existsSync43(this.pendingPath)) return;
|
|
23204
23775
|
try {
|
|
23205
|
-
const raw =
|
|
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
|
-
|
|
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
|
|
23808
|
+
existsSync as existsSync44,
|
|
23238
23809
|
mkdirSync as mkdirSync25,
|
|
23239
|
-
readFileSync as
|
|
23810
|
+
readFileSync as readFileSync42,
|
|
23240
23811
|
readdirSync as readdirSync10,
|
|
23241
23812
|
rmSync as rmSync2,
|
|
23242
|
-
writeFileSync as
|
|
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 (!
|
|
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
|
-
|
|
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 (!
|
|
23869
|
+
if (!existsSync44(filePath)) return null;
|
|
23299
23870
|
try {
|
|
23300
|
-
return JSON.parse(
|
|
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 (!
|
|
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 =
|
|
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
|
-
|
|
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 (!
|
|
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 (!
|
|
23953
|
+
if (!existsSync44(contextPath)) return 0;
|
|
23383
23954
|
try {
|
|
23384
|
-
const ctx = JSON.parse(
|
|
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 (
|
|
23967
|
+
if (existsSync44(contextPath)) {
|
|
23397
23968
|
try {
|
|
23398
|
-
ctx = JSON.parse(
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
24263
|
+
if (!existsSync45(dir)) {
|
|
23693
24264
|
const { mkdirSync: mkdirSync39 } = __require("fs");
|
|
23694
24265
|
mkdirSync39(dir, { recursive: true });
|
|
23695
24266
|
}
|
|
23696
|
-
|
|
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 (!
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
25530
|
+
if (!existsSync46(stateDir)) mkdirSync26(stateDir, { recursive: true });
|
|
24960
25531
|
const filePath = join51(stateDir, PERSISTENCE_FILE);
|
|
24961
|
-
|
|
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 (!
|
|
24982
|
-
const data = JSON.parse(
|
|
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
|
|
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 (
|
|
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 (
|
|
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
|
|
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 (!
|
|
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 (
|
|
27272
|
+
if (existsSync48(hookPath)) {
|
|
26702
27273
|
try {
|
|
26703
|
-
const existing =
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
27304
|
+
if (!existsSync48(hookPath)) return true;
|
|
26734
27305
|
try {
|
|
26735
|
-
const content =
|
|
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
|
-
|
|
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
|
|
27471
|
+
existsSync as existsSync49,
|
|
26901
27472
|
mkdirSync as mkdirSync28,
|
|
26902
|
-
readFileSync as
|
|
27473
|
+
readFileSync as readFileSync46,
|
|
26903
27474
|
readdirSync as readdirSync11,
|
|
26904
27475
|
rmSync as rmSync3,
|
|
26905
27476
|
statSync as statSync7,
|
|
26906
|
-
writeFileSync as
|
|
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 (!
|
|
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
|
-
|
|
27522
|
+
writeFileSync28(
|
|
26952
27523
|
join53(snapshotDir, OVERLAY_FILE),
|
|
26953
27524
|
JSON.stringify(snapshot, null, 2),
|
|
26954
27525
|
"utf-8"
|
|
26955
27526
|
);
|
|
26956
|
-
|
|
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 (!
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
27591
|
+
if (!existsSync49(hashesPath)) return null;
|
|
27021
27592
|
try {
|
|
27022
|
-
const raw =
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
27640
|
+
if (!existsSync49(overlayPath)) continue;
|
|
27070
27641
|
try {
|
|
27071
|
-
const raw =
|
|
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 (
|
|
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
|
|
27690
|
+
existsSync as existsSync50,
|
|
27120
27691
|
mkdirSync as mkdirSync29,
|
|
27121
|
-
readFileSync as
|
|
27692
|
+
readFileSync as readFileSync47,
|
|
27122
27693
|
renameSync as renameSync2,
|
|
27123
|
-
writeFileSync as
|
|
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 (!
|
|
27756
|
+
if (!existsSync50(this.stateDir)) {
|
|
27186
27757
|
mkdirSync29(this.stateDir, { recursive: true });
|
|
27187
27758
|
}
|
|
27188
27759
|
const tmpPath = `${this.statePath}.tmp`;
|
|
27189
|
-
|
|
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 (!
|
|
27783
|
+
if (!existsSync50(this.statePath)) {
|
|
27213
27784
|
return { files: {} };
|
|
27214
27785
|
}
|
|
27215
27786
|
try {
|
|
27216
|
-
const raw =
|
|
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
|
|
27799
|
+
existsSync as existsSync51,
|
|
27229
27800
|
mkdirSync as mkdirSync30,
|
|
27230
|
-
readFileSync as
|
|
27801
|
+
readFileSync as readFileSync48,
|
|
27231
27802
|
readdirSync as readdirSync12,
|
|
27232
27803
|
rmSync as rmSync4,
|
|
27233
27804
|
statSync as statSync8,
|
|
27234
|
-
writeFileSync as
|
|
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 (!
|
|
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
|
-
|
|
27882
|
+
writeFileSync30(
|
|
27312
27883
|
join55(snapshotDir, OVERLAY_FILE2),
|
|
27313
27884
|
JSON.stringify(snapshot, null, 2),
|
|
27314
27885
|
"utf-8"
|
|
27315
27886
|
);
|
|
27316
|
-
|
|
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 (!
|
|
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 =
|
|
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 (!
|
|
27947
|
+
if (!existsSync51(hashesPath)) return null;
|
|
27377
27948
|
try {
|
|
27378
|
-
const raw =
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
27995
|
+
if (!existsSync51(stashPath)) return null;
|
|
27425
27996
|
try {
|
|
27426
|
-
return
|
|
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 (!
|
|
28007
|
+
if (!existsSync51(logPath)) return 0;
|
|
27437
28008
|
try {
|
|
27438
|
-
const content =
|
|
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
|
|
28041
|
+
existsSync as existsSync52,
|
|
27471
28042
|
mkdirSync as mkdirSync31,
|
|
27472
|
-
readFileSync as
|
|
28043
|
+
readFileSync as readFileSync49,
|
|
27473
28044
|
statSync as statSync9,
|
|
27474
|
-
writeFileSync as
|
|
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 (
|
|
28262
|
+
if (existsSync52(absPath) && !this.mtimeCache.check(absPath)) {
|
|
27692
28263
|
result.filesSkipped = 1;
|
|
27693
28264
|
return result;
|
|
27694
28265
|
}
|
|
27695
|
-
if (!
|
|
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 =
|
|
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 (!
|
|
28624
|
+
if (!existsSync52(driftDir)) {
|
|
28054
28625
|
mkdirSync31(driftDir, { recursive: true });
|
|
28055
28626
|
}
|
|
28056
28627
|
const summary = await this.getDriftSummary();
|
|
28057
|
-
|
|
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
|
|
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 (!
|
|
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 =
|
|
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
|
|
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 (!
|
|
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 =
|
|
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 (!
|
|
30375
|
+
if (!existsSync54(this.unerrDir)) {
|
|
29805
30376
|
mkdirSync32(this.unerrDir, { recursive: true });
|
|
29806
30377
|
}
|
|
29807
|
-
|
|
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
|
|
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:
|
|
30684
|
+
offset: existsSync55(compressionPath) ? statSync10(compressionPath).size : 0,
|
|
30114
30685
|
watcher: null
|
|
30115
30686
|
};
|
|
30116
30687
|
const generalState = {
|
|
30117
30688
|
path: generalPath,
|
|
30118
|
-
offset:
|
|
30689
|
+
offset: existsSync55(generalPath) ? statSync10(generalPath).size : 0,
|
|
30119
30690
|
watcher: null
|
|
30120
30691
|
};
|
|
30121
30692
|
const tokenFlowState = {
|
|
30122
30693
|
path: tokenFlowPath,
|
|
30123
|
-
offset:
|
|
30694
|
+
offset: existsSync55(tokenFlowPath) ? statSync10(tokenFlowPath).size : 0,
|
|
30124
30695
|
watcher: null
|
|
30125
30696
|
};
|
|
30126
30697
|
const fileReadsState = {
|
|
30127
30698
|
path: fileReadsPath,
|
|
30128
|
-
offset:
|
|
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 &&
|
|
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:
|
|
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 (
|
|
32065
|
+
if (existsSync64(configPath)) {
|
|
31495
32066
|
try {
|
|
31496
|
-
config = JSON.parse(
|
|
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
|
|
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 (!
|
|
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 (!
|
|
32279
|
+
if (!existsSync56(historyPath)) return [];
|
|
31709
32280
|
try {
|
|
31710
|
-
const content =
|
|
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
|
|
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 (
|
|
32422
|
-
const spaHtml =
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
32941
|
-
const raw =
|
|
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 (!
|
|
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 (
|
|
33567
|
+
if (existsSync58(configPath)) {
|
|
32997
33568
|
try {
|
|
32998
|
-
const config = JSON.parse(
|
|
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 &&
|
|
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:
|
|
33439
|
-
|
|
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 =
|
|
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:
|
|
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 =
|
|
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
|
|
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 (!
|
|
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) =>
|
|
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
|
|
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
|
-
|
|
35574
|
+
writeFileSync34(configPath, `${JSON.stringify({ repoId }, null, 2)}
|
|
35004
35575
|
`);
|
|
35005
35576
|
let existingSettings = {};
|
|
35006
|
-
if (
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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 (!
|
|
35682
|
-
const content =
|
|
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 (!
|
|
36262
|
+
if (!existsSync61(stateDir)) {
|
|
35692
36263
|
mkdirSync37(stateDir, { recursive: true });
|
|
35693
36264
|
}
|
|
35694
36265
|
const pointerPath = join65(stateDir, "last_session.json");
|
|
35695
|
-
|
|
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
|
|
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 (!
|
|
35898
|
-
const raw =
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
36082
|
-
|
|
36083
|
-
|
|
36084
|
-
|
|
36085
|
-
|
|
36086
|
-
|
|
36087
|
-
|
|
36088
|
-
|
|
36089
|
-
|
|
36090
|
-
|
|
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
|
-
|
|
36094
|
-
|
|
36095
|
-
|
|
36096
|
-
|
|
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
|
-
|
|
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 (!
|
|
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 (
|
|
37063
|
+
if (existsSync62(configPath)) {
|
|
36487
37064
|
try {
|
|
36488
|
-
const config = JSON.parse(
|
|
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 (
|
|
37094
|
+
if (existsSync62(configPath)) {
|
|
36518
37095
|
try {
|
|
36519
|
-
const cfg = JSON.parse(
|
|
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
|
|
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:
|
|
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 (!
|
|
39217
|
+
if (!existsSync64(snapshotPath2)) {
|
|
38641
39218
|
snapshotPath2 = join68(snapshotsDir, "graph.msgpack");
|
|
38642
39219
|
}
|
|
38643
|
-
if (!
|
|
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 =
|
|
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
|
-
|
|
41886
|
-
|
|
41887
|
-
|
|
41888
|
-
|
|
41889
|
-
|
|
41890
|
-
|
|
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`
|
|
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
|
|
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
|
-
|
|
41967
|
-
|
|
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
|
-
|
|
42095
|
-
|
|
42096
|
-
|
|
42097
|
-
hook.command("pre-
|
|
42098
|
-
|
|
42099
|
-
|
|
42100
|
-
|
|
42101
|
-
hook.command("
|
|
42102
|
-
|
|
42103
|
-
|
|
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
|
|
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 {
|
|
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
|
-
|
|
42317
|
-
|
|
42318
|
-
|
|
42319
|
-
|
|
42320
|
-
|
|
42321
|
-
{
|
|
42322
|
-
|
|
42323
|
-
|
|
42324
|
-
|
|
42325
|
-
|
|
42326
|
-
|
|
42327
|
-
|
|
42328
|
-
|
|
42329
|
-
|
|
42330
|
-
|
|
42331
|
-
|
|
42332
|
-
|
|
42333
|
-
|
|
42334
|
-
|
|
42335
|
-
|
|
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
|
|
42344
|
-
return
|
|
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 (!
|
|
42952
|
+
if (!existsSync21(dir)) {
|
|
42361
42953
|
mkdirSync14(dir, { recursive: true });
|
|
42362
42954
|
}
|
|
42363
42955
|
let settings = {};
|
|
42364
|
-
if (
|
|
42956
|
+
if (existsSync21(settingsPath)) {
|
|
42365
42957
|
try {
|
|
42366
|
-
settings = JSON.parse(
|
|
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
|
-
|
|
42374
|
-
|
|
42375
|
-
|
|
42376
|
-
|
|
42377
|
-
|
|
42378
|
-
|
|
42379
|
-
|
|
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
|
|
42974
|
+
for (const hookDef of matcherHooks) {
|
|
42388
42975
|
const eventArray = Array.isArray(hooks[hookDef.event]) ? [...hooks[hookDef.event]] : [];
|
|
42389
|
-
|
|
42390
|
-
|
|
42391
|
-
|
|
42392
|
-
|
|
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
|
-
|
|
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 (!
|
|
43013
|
+
if (!existsSync21(dir)) {
|
|
42421
43014
|
mkdirSync14(dir, { recursive: true });
|
|
42422
43015
|
}
|
|
42423
43016
|
let settings = {};
|
|
42424
|
-
if (
|
|
43017
|
+
if (existsSync21(settingsPath)) {
|
|
42425
43018
|
try {
|
|
42426
|
-
settings = JSON.parse(
|
|
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
|
-
|
|
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 (!
|
|
43051
|
+
if (!existsSync21(settingsPath)) return false;
|
|
42459
43052
|
try {
|
|
42460
|
-
const settings = JSON.parse(
|
|
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
|
-
|
|
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 (!
|
|
43073
|
+
if (!existsSync21(settingsPath)) return false;
|
|
42481
43074
|
try {
|
|
42482
|
-
const settings = JSON.parse(
|
|
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
|
-
|
|
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
|
|
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
|
|
42533
|
-
|
|
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
|
|
42588
|
-
- \`purpose:'explore'\` (default) \u2014 budget-capped, returns outline for large files. Use for browsing
|
|
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 =
|
|
43261
|
+
const existed = existsSync22(filePath);
|
|
42666
43262
|
if (existed) {
|
|
42667
|
-
const existing =
|
|
43263
|
+
const existing = readFileSync21(filePath, "utf-8");
|
|
42668
43264
|
if (existing === windsurfContent) {
|
|
42669
43265
|
return { path: filePath, action: "skipped" };
|
|
42670
43266
|
}
|
|
42671
43267
|
}
|
|
42672
|
-
|
|
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 =
|
|
43282
|
+
const existed = existsSync22(filePath);
|
|
42687
43283
|
if (existed) {
|
|
42688
|
-
const existing =
|
|
43284
|
+
const existing = readFileSync21(filePath, "utf-8");
|
|
42689
43285
|
if (existing === antigravityContent) {
|
|
42690
43286
|
return { path: filePath, action: "skipped" };
|
|
42691
43287
|
}
|
|
42692
43288
|
}
|
|
42693
|
-
|
|
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 (!
|
|
43298
|
+
if (!existsSync22(filePath)) {
|
|
42703
43299
|
const dir = dirname5(filePath);
|
|
42704
|
-
if (!
|
|
42705
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
43325
|
+
const alreadyExists = existsSync22(filePath);
|
|
42730
43326
|
if (alreadyExists) {
|
|
42731
|
-
const existing =
|
|
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 (!
|
|
42738
|
-
|
|
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 (!
|
|
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 (
|
|
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 (
|
|
43357
|
+
if (existsSync22(filePath)) {
|
|
42762
43358
|
unlinkSync3(filePath);
|
|
42763
43359
|
return true;
|
|
42764
43360
|
}
|
|
42765
43361
|
return false;
|
|
42766
43362
|
}
|
|
42767
|
-
const content =
|
|
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
|
-
|
|
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 (!
|
|
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 (
|
|
43758
|
+
if (existsSync24(hooksJsonPath)) {
|
|
43163
43759
|
try {
|
|
43164
|
-
const existing = JSON.parse(
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
43796
|
+
if (existsSync24(hookPath)) return true;
|
|
43201
43797
|
try {
|
|
43202
|
-
if (!
|
|
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
|
-
|
|
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 (!
|
|
43817
|
+
if (!existsSync24(gitignorePath)) return false;
|
|
43222
43818
|
try {
|
|
43223
|
-
const content =
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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:
|
|
43325
|
-
const config = JSON.parse(
|
|
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 (!
|
|
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 =
|
|
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
|
|
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 (!
|
|
43963
|
+
if (!existsSync27(manifestPath)) return null;
|
|
43368
43964
|
try {
|
|
43369
|
-
const raw =
|
|
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
|
|
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 (
|
|
44335
|
+
if (existsSync32(configPath)) {
|
|
43740
44336
|
try {
|
|
43741
|
-
const config = JSON.parse(
|
|
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 (
|
|
44366
|
+
if (existsSync32(pidPath)) {
|
|
43771
44367
|
try {
|
|
43772
|
-
const pidStr =
|
|
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 &&
|
|
44379
|
+
if (repoId && existsSync32(join38(manifestsDir, `${repoId}.json`))) {
|
|
43784
44380
|
try {
|
|
43785
44381
|
const manifest = JSON.parse(
|
|
43786
|
-
|
|
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 (
|
|
44401
|
+
if (existsSync32(driftSummaryPath)) {
|
|
43806
44402
|
try {
|
|
43807
44403
|
const summary = JSON.parse(
|
|
43808
|
-
|
|
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 (
|
|
44417
|
+
if (existsSync32(graphVersionPath)) {
|
|
43822
44418
|
try {
|
|
43823
|
-
const gv = JSON.parse(
|
|
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 (!
|
|
44430
|
+
if (!existsSync32(snapshotPath2)) {
|
|
43835
44431
|
snapshotPath2 = join38(snapshotsDir, "graph.msgpack");
|
|
43836
44432
|
}
|
|
43837
|
-
if (
|
|
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 =
|
|
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 (
|
|
44457
|
+
if (existsSync32(statsPath) && proxyRunning) {
|
|
43862
44458
|
try {
|
|
43863
|
-
const liveStats = JSON.parse(
|
|
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 (
|
|
43891
|
-
const fs5 = JSON.parse(
|
|
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 (
|
|
44524
|
+
if (existsSync32(snapshotMetaPath)) {
|
|
43929
44525
|
try {
|
|
43930
|
-
const meta = JSON.parse(
|
|
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 (
|
|
43948
|
-
const cc = JSON.parse(
|
|
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 (
|
|
43958
|
-
const gv = JSON.parse(
|
|
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 (
|
|
44001
|
-
const content =
|
|
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 (
|
|
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 (
|
|
44041
|
-
const raw = JSON.parse(
|
|
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 (
|
|
44077
|
-
const raw =
|
|
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
|
|
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 (!
|
|
45106
|
+
if (!existsSync34(hooksPath)) return false;
|
|
44511
45107
|
try {
|
|
44512
|
-
const config = JSON.parse(
|
|
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
|
-
|
|
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 (!
|
|
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 (!
|
|
45683
|
+
if (!existsSync63(configPath)) return null;
|
|
45088
45684
|
try {
|
|
45089
|
-
return JSON.parse(
|
|
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 (
|
|
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:
|
|
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
|
-
|
|
45786
|
+
writeFileSync37(
|
|
45191
45787
|
join67(unerrDir, "config.json"),
|
|
45192
45788
|
JSON.stringify(
|
|
45193
45789
|
{ repoId, mode: "local", createdAt: (/* @__PURE__ */ new Date()).toISOString() },
|