@unerr-ai/unerr 0.0.0-beta.8 → 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 +1173 -574
- 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
|
}
|
|
@@ -7227,9 +7227,9 @@ function extractTSEntities(node, lines, entities) {
|
|
|
7227
7227
|
const nameNode = child.childForFieldName("name");
|
|
7228
7228
|
const valueNode = child.childForFieldName("value");
|
|
7229
7229
|
if (!nameNode || !valueNode) continue;
|
|
7230
|
+
const startLine = n.startPosition.row + 1;
|
|
7231
|
+
const endLine = n.endPosition.row + 1;
|
|
7230
7232
|
if (valueNode.type === "arrow_function" || valueNode.type === "function_expression" || valueNode.type === "function") {
|
|
7231
|
-
const startLine = n.startPosition.row + 1;
|
|
7232
|
-
const endLine = n.endPosition.row + 1;
|
|
7233
7233
|
entities.push({
|
|
7234
7234
|
name: nameNode.text,
|
|
7235
7235
|
kind: "function",
|
|
@@ -7238,11 +7238,53 @@ function extractTSEntities(node, lines, entities) {
|
|
|
7238
7238
|
line_end: endLine,
|
|
7239
7239
|
content_hash: hashLines(lines, startLine, endLine)
|
|
7240
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
|
+
}
|
|
7241
7253
|
}
|
|
7242
7254
|
}
|
|
7243
7255
|
}
|
|
7244
7256
|
break;
|
|
7245
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
|
+
}
|
|
7246
7288
|
}
|
|
7247
7289
|
});
|
|
7248
7290
|
}
|
|
@@ -9370,6 +9412,42 @@ var init_downloader = __esm({
|
|
|
9370
9412
|
archiveType: "gz",
|
|
9371
9413
|
archiveBinaryPath: null,
|
|
9372
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)"
|
|
9373
9451
|
}
|
|
9374
9452
|
};
|
|
9375
9453
|
}
|
|
@@ -9483,7 +9561,10 @@ var init_detector = __esm({
|
|
|
9483
9561
|
EXTERNAL_SCIP = {
|
|
9484
9562
|
go: ["scip-go"],
|
|
9485
9563
|
java: ["scip-java"],
|
|
9486
|
-
rust: ["rust-analyzer"]
|
|
9564
|
+
rust: ["rust-analyzer"],
|
|
9565
|
+
ruby: ["scip-ruby"],
|
|
9566
|
+
cpp: ["scip-clang"],
|
|
9567
|
+
csharp: ["scip-dotnet"]
|
|
9487
9568
|
};
|
|
9488
9569
|
EXT_TO_SCIP_LANG = {
|
|
9489
9570
|
// TypeScript / JavaScript (scip-typescript handles all)
|
|
@@ -9508,7 +9589,19 @@ var init_detector = __esm({
|
|
|
9508
9589
|
".scala": "java",
|
|
9509
9590
|
".sc": "java",
|
|
9510
9591
|
// Rust
|
|
9511
|
-
".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"
|
|
9512
9605
|
};
|
|
9513
9606
|
}
|
|
9514
9607
|
});
|
|
@@ -9615,6 +9708,9 @@ async function runScipIndexer(options) {
|
|
|
9615
9708
|
}
|
|
9616
9709
|
const outputPath = join22(outputDir, "index.scip");
|
|
9617
9710
|
const args = buildScipArgs(language, binaryPath, projectRoot, outputPath);
|
|
9711
|
+
if (options.extraArgs?.length) {
|
|
9712
|
+
args.push(...options.extraArgs);
|
|
9713
|
+
}
|
|
9618
9714
|
try {
|
|
9619
9715
|
log4.info(`Running SCIP indexer: ${args[0]} for ${language}`);
|
|
9620
9716
|
const result = await exec(args[0], args.slice(1), {
|
|
@@ -9623,6 +9719,12 @@ async function runScipIndexer(options) {
|
|
|
9623
9719
|
});
|
|
9624
9720
|
const durationMs = performance.now() - start;
|
|
9625
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
|
+
}
|
|
9626
9728
|
log4.warn(
|
|
9627
9729
|
`SCIP indexer failed (exit ${result.exitCode}): ${result.stderr.slice(0, 500)}`
|
|
9628
9730
|
);
|
|
@@ -9669,6 +9771,12 @@ function buildScipArgs(language, binaryPath, projectRoot, outputPath) {
|
|
|
9669
9771
|
return [binaryPath, "index", "--output", outputPath];
|
|
9670
9772
|
case "rust":
|
|
9671
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];
|
|
9672
9780
|
default:
|
|
9673
9781
|
return [binaryPath, "--output", outputPath];
|
|
9674
9782
|
}
|
|
@@ -9684,8 +9792,9 @@ var init_runner = __esm({
|
|
|
9684
9792
|
});
|
|
9685
9793
|
|
|
9686
9794
|
// src/intelligence/indexer/scip/orchestrator.ts
|
|
9795
|
+
import { existsSync as existsSync18, readFileSync as readFileSync18, writeFileSync as writeFileSync9 } from "fs";
|
|
9687
9796
|
import { join as join23 } from "path";
|
|
9688
|
-
async function enrichWithScip(files, projectRoot, existingEdges, entities) {
|
|
9797
|
+
async function enrichWithScip(files, projectRoot, existingEdges, entities, options) {
|
|
9689
9798
|
const languages = detectProjectLanguages(files);
|
|
9690
9799
|
if (languages.length === 0) {
|
|
9691
9800
|
return skipResult(existingEdges, "No supported language detected");
|
|
@@ -9699,20 +9808,29 @@ async function enrichWithScip(files, projectRoot, existingEdges, entities) {
|
|
|
9699
9808
|
for (const { language, fileCount } of languages) {
|
|
9700
9809
|
log5.info(`SCIP: processing ${language} (${fileCount} files)`);
|
|
9701
9810
|
let binaryInfo = await detectScipBinary(language);
|
|
9702
|
-
if (!binaryInfo.available
|
|
9703
|
-
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
9711
|
-
log5.
|
|
9712
|
-
|
|
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 ? `
|
|
9713
9830
|
Manual install: ${manualInstr}` : ""}`
|
|
9714
|
-
|
|
9715
|
-
|
|
9831
|
+
);
|
|
9832
|
+
continue;
|
|
9833
|
+
}
|
|
9716
9834
|
}
|
|
9717
9835
|
}
|
|
9718
9836
|
if (!binaryInfo.available) {
|
|
@@ -9726,12 +9844,38 @@ async function enrichWithScip(files, projectRoot, existingEdges, entities) {
|
|
|
9726
9844
|
);
|
|
9727
9845
|
const outputDir = join23(projectRoot, ".unerr", "scip");
|
|
9728
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
|
+
}
|
|
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
|
+
}
|
|
9729
9872
|
const runResult = await runScipIndexer({
|
|
9730
9873
|
language,
|
|
9731
9874
|
binaryPath,
|
|
9732
9875
|
projectRoot,
|
|
9733
9876
|
outputDir,
|
|
9734
|
-
timeoutMs: 3e4
|
|
9877
|
+
timeoutMs: 3e4,
|
|
9878
|
+
extraArgs
|
|
9735
9879
|
});
|
|
9736
9880
|
lastRunResult = runResult;
|
|
9737
9881
|
if (!runResult.success || !runResult.outputPath) {
|
|
@@ -9778,6 +9922,136 @@ async function enrichWithScip(files, projectRoot, existingEdges, entities) {
|
|
|
9778
9922
|
skipReason: null
|
|
9779
9923
|
};
|
|
9780
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
|
+
}
|
|
9781
10055
|
function skipResult(existingEdges, reason) {
|
|
9782
10056
|
log5.info(`SCIP skipped: ${reason}`);
|
|
9783
10057
|
return {
|
|
@@ -9799,10 +10073,11 @@ function markAsStructural(edges) {
|
|
|
9799
10073
|
scipVerified: false
|
|
9800
10074
|
}));
|
|
9801
10075
|
}
|
|
9802
|
-
var log5;
|
|
10076
|
+
var log5, BUILD_TOOL_FILES, COMPDB_SEARCH_PATHS;
|
|
9803
10077
|
var init_orchestrator = __esm({
|
|
9804
10078
|
"src/intelligence/indexer/scip/orchestrator.ts"() {
|
|
9805
10079
|
"use strict";
|
|
10080
|
+
init_exec();
|
|
9806
10081
|
init_logger();
|
|
9807
10082
|
init_decoder();
|
|
9808
10083
|
init_detector();
|
|
@@ -9810,6 +10085,19 @@ var init_orchestrator = __esm({
|
|
|
9810
10085
|
init_merger();
|
|
9811
10086
|
init_runner();
|
|
9812
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
|
+
];
|
|
9813
10101
|
}
|
|
9814
10102
|
});
|
|
9815
10103
|
|
|
@@ -10329,7 +10617,7 @@ __export(local_indexer_exports, {
|
|
|
10329
10617
|
runCommunityDetection: () => runCommunityDetection,
|
|
10330
10618
|
runConventionDetection: () => runConventionDetection
|
|
10331
10619
|
});
|
|
10332
|
-
import { readFileSync as
|
|
10620
|
+
import { readFileSync as readFileSync19, readdirSync as readdirSync6, statSync as statSync6 } from "fs";
|
|
10333
10621
|
import { basename as basename2, extname as extname2, join as join24, relative } from "path";
|
|
10334
10622
|
async function indexLocalProject(projectRoot, graphStore, repoId, opts) {
|
|
10335
10623
|
const startTime = Date.now();
|
|
@@ -10357,7 +10645,7 @@ async function indexLocalProject(projectRoot, graphStore, repoId, opts) {
|
|
|
10357
10645
|
});
|
|
10358
10646
|
let content;
|
|
10359
10647
|
try {
|
|
10360
|
-
content =
|
|
10648
|
+
content = readFileSync19(absPath, "utf-8");
|
|
10361
10649
|
} catch {
|
|
10362
10650
|
filesProcessed++;
|
|
10363
10651
|
continue;
|
|
@@ -10468,10 +10756,18 @@ async function indexLocalProject(projectRoot, graphStore, repoId, opts) {
|
|
|
10468
10756
|
...coChangeCompactEdges
|
|
10469
10757
|
];
|
|
10470
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);
|
|
10471
10766
|
for (const e of allEntities) indexedEntityKeys.add(e.key);
|
|
10472
10767
|
for (const e of allEntities) {
|
|
10473
10768
|
if (e.file_path) indexedEntityKeys.add(`file:${e.file_path}`);
|
|
10474
10769
|
}
|
|
10770
|
+
for (const dk of docKeys) indexedEntityKeys.add(dk);
|
|
10475
10771
|
await removeOrphanedEntities(graphStore, indexedEntityKeys);
|
|
10476
10772
|
await materializeL1Edges(graphStore);
|
|
10477
10773
|
progress?.({
|
|
@@ -10507,7 +10803,7 @@ async function indexLocalProject(projectRoot, graphStore, repoId, opts) {
|
|
|
10507
10803
|
await persistLocalSnapshot(projectRoot, repoId, allEntities, resolvedEdges);
|
|
10508
10804
|
const elapsedMs = Date.now() - startTime;
|
|
10509
10805
|
log6.info(
|
|
10510
|
-
`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`
|
|
10511
10807
|
);
|
|
10512
10808
|
return {
|
|
10513
10809
|
fileCount: files.length,
|
|
@@ -10516,6 +10812,7 @@ async function indexLocalProject(projectRoot, graphStore, repoId, opts) {
|
|
|
10516
10812
|
communityCount,
|
|
10517
10813
|
patternCount,
|
|
10518
10814
|
ruleCount,
|
|
10815
|
+
docCount: docKeys.length,
|
|
10519
10816
|
elapsedMs,
|
|
10520
10817
|
scip: scipResult.mergeResult ? {
|
|
10521
10818
|
language: scipResult.language,
|
|
@@ -10531,7 +10828,7 @@ async function reindexFile(projectRoot, filePath, graphStore, repoId) {
|
|
|
10531
10828
|
await removeFileEntities(graphStore, relPath);
|
|
10532
10829
|
let content;
|
|
10533
10830
|
try {
|
|
10534
|
-
content =
|
|
10831
|
+
content = readFileSync19(absPath, "utf-8");
|
|
10535
10832
|
} catch {
|
|
10536
10833
|
return { entities: 0, edges: 0 };
|
|
10537
10834
|
}
|
|
@@ -11338,7 +11635,113 @@ async function resolveEntityNameGlobal(name, graphStore) {
|
|
|
11338
11635
|
}
|
|
11339
11636
|
return null;
|
|
11340
11637
|
}
|
|
11341
|
-
|
|
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;
|
|
11342
11745
|
var init_local_indexer = __esm({
|
|
11343
11746
|
"src/intelligence/local-indexer.ts"() {
|
|
11344
11747
|
"use strict";
|
|
@@ -11392,6 +11795,87 @@ var init_local_indexer = __esm({
|
|
|
11392
11795
|
".parcel-cache"
|
|
11393
11796
|
]);
|
|
11394
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;
|
|
11395
11879
|
log6 = {
|
|
11396
11880
|
info: (msg) => process.stderr.write(`[unerr] ${msg}
|
|
11397
11881
|
`),
|
|
@@ -11400,6 +11884,31 @@ var init_local_indexer = __esm({
|
|
|
11400
11884
|
`);
|
|
11401
11885
|
}
|
|
11402
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
|
+
};
|
|
11403
11912
|
}
|
|
11404
11913
|
});
|
|
11405
11914
|
|
|
@@ -11412,23 +11921,23 @@ __export(hook_installer_exports, {
|
|
|
11412
11921
|
});
|
|
11413
11922
|
import {
|
|
11414
11923
|
chmodSync as chmodSync3,
|
|
11415
|
-
existsSync as
|
|
11924
|
+
existsSync as existsSync19,
|
|
11416
11925
|
mkdirSync as mkdirSync13,
|
|
11417
11926
|
unlinkSync as unlinkSync2,
|
|
11418
|
-
writeFileSync as
|
|
11927
|
+
writeFileSync as writeFileSync10
|
|
11419
11928
|
} from "fs";
|
|
11420
11929
|
import { join as join25 } from "path";
|
|
11421
11930
|
function installClaudeHook(cwd) {
|
|
11422
11931
|
const hooksDir = join25(cwd, ".claude", "hooks");
|
|
11423
11932
|
const hookPath = join25(hooksDir, "PostToolUse.sh");
|
|
11424
|
-
if (
|
|
11933
|
+
if (existsSync19(hookPath)) {
|
|
11425
11934
|
return { path: hookPath, action: "already_exists" };
|
|
11426
11935
|
}
|
|
11427
11936
|
try {
|
|
11428
|
-
if (!
|
|
11937
|
+
if (!existsSync19(hooksDir)) {
|
|
11429
11938
|
mkdirSync13(hooksDir, { recursive: true });
|
|
11430
11939
|
}
|
|
11431
|
-
|
|
11940
|
+
writeFileSync10(hookPath, HOOK_CONTENT, "utf-8");
|
|
11432
11941
|
chmodSync3(hookPath, 493);
|
|
11433
11942
|
return { path: hookPath, action: "installed" };
|
|
11434
11943
|
} catch {
|
|
@@ -11437,7 +11946,7 @@ function installClaudeHook(cwd) {
|
|
|
11437
11946
|
}
|
|
11438
11947
|
function removeClaudeHook(cwd) {
|
|
11439
11948
|
const hookPath = join25(cwd, ".claude", "hooks", "PostToolUse.sh");
|
|
11440
|
-
if (!
|
|
11949
|
+
if (!existsSync19(hookPath)) return false;
|
|
11441
11950
|
try {
|
|
11442
11951
|
unlinkSync2(hookPath);
|
|
11443
11952
|
return true;
|
|
@@ -11446,7 +11955,7 @@ function removeClaudeHook(cwd) {
|
|
|
11446
11955
|
}
|
|
11447
11956
|
}
|
|
11448
11957
|
function isClaudeHookInstalled(cwd) {
|
|
11449
|
-
return
|
|
11958
|
+
return existsSync19(join25(cwd, ".claude", "hooks", "PostToolUse.sh"));
|
|
11450
11959
|
}
|
|
11451
11960
|
var HOOK_CONTENT;
|
|
11452
11961
|
var init_hook_installer = __esm({
|
|
@@ -11473,13 +11982,13 @@ __export(tool_detector_exports, {
|
|
|
11473
11982
|
detectTools: () => detectTools,
|
|
11474
11983
|
formatDetectedTools: () => formatDetectedTools
|
|
11475
11984
|
});
|
|
11476
|
-
import { existsSync as
|
|
11985
|
+
import { existsSync as existsSync20 } from "fs";
|
|
11477
11986
|
import { join as join26 } from "path";
|
|
11478
11987
|
function detectTools(cwd) {
|
|
11479
11988
|
const detected = [];
|
|
11480
11989
|
for (const agent of AGENT_REGISTRY) {
|
|
11481
11990
|
const hasDirMarker = agent.dirMarkers.some(
|
|
11482
|
-
(dir) =>
|
|
11991
|
+
(dir) => existsSync20(join26(cwd, dir))
|
|
11483
11992
|
);
|
|
11484
11993
|
const hasEnvVar = agent.envVars.some((envVar) => !!process.env[envVar]);
|
|
11485
11994
|
if (hasDirMarker || hasEnvVar) {
|
|
@@ -11487,7 +11996,7 @@ function detectTools(cwd) {
|
|
|
11487
11996
|
detected.push({
|
|
11488
11997
|
ide: agent.id,
|
|
11489
11998
|
configDir: join26(cwd, agent.dirMarkers[0] ?? ""),
|
|
11490
|
-
hasExistingConfig:
|
|
11999
|
+
hasExistingConfig: existsSync20(configPath),
|
|
11491
12000
|
agent
|
|
11492
12001
|
});
|
|
11493
12002
|
}
|
|
@@ -11831,13 +12340,13 @@ __export(resolver_exports, {
|
|
|
11831
12340
|
scanSkillDirectory: () => scanSkillDirectory
|
|
11832
12341
|
});
|
|
11833
12342
|
import {
|
|
11834
|
-
existsSync as
|
|
12343
|
+
existsSync as existsSync23,
|
|
11835
12344
|
mkdirSync as mkdirSync16,
|
|
11836
|
-
readFileSync as
|
|
12345
|
+
readFileSync as readFileSync22,
|
|
11837
12346
|
readdirSync as readdirSync7,
|
|
11838
12347
|
rmSync,
|
|
11839
12348
|
unlinkSync as unlinkSync4,
|
|
11840
|
-
writeFileSync as
|
|
12349
|
+
writeFileSync as writeFileSync13
|
|
11841
12350
|
} from "fs";
|
|
11842
12351
|
import { homedir as homedir6 } from "os";
|
|
11843
12352
|
import { dirname as dirname6, join as join29 } from "path";
|
|
@@ -11937,7 +12446,7 @@ function writeSkillFile(skill, ide, skillDir, ext, dirPerSkill) {
|
|
|
11937
12446
|
filePath = join29(skillDir, `${skillName}${ext}`);
|
|
11938
12447
|
content = formatMarkdownSkill(skill);
|
|
11939
12448
|
}
|
|
11940
|
-
|
|
12449
|
+
writeFileSync13(filePath, content);
|
|
11941
12450
|
return filePath;
|
|
11942
12451
|
}
|
|
11943
12452
|
function formatClaudeCodeSkill(skill) {
|
|
@@ -12114,13 +12623,13 @@ function parseFrontmatter(raw) {
|
|
|
12114
12623
|
return { meta, content };
|
|
12115
12624
|
}
|
|
12116
12625
|
function scanSkillDirectory(dir) {
|
|
12117
|
-
if (!
|
|
12626
|
+
if (!existsSync23(dir)) return [];
|
|
12118
12627
|
const skills = [];
|
|
12119
12628
|
try {
|
|
12120
12629
|
const files = readdirSync7(dir).filter((f) => f.endsWith(".md"));
|
|
12121
12630
|
for (const file of files) {
|
|
12122
12631
|
try {
|
|
12123
|
-
const raw =
|
|
12632
|
+
const raw = readFileSync22(join29(dir, file), "utf-8");
|
|
12124
12633
|
const { meta, content } = parseFrontmatter(raw);
|
|
12125
12634
|
const name = meta.name ?? file.replace(/\.md$/, "");
|
|
12126
12635
|
skills.push({
|
|
@@ -12167,13 +12676,13 @@ async function resolveAndInstallSkills(opts) {
|
|
|
12167
12676
|
}
|
|
12168
12677
|
async function ensureSkillsPresent(opts) {
|
|
12169
12678
|
const { dir, ext, dirPerSkill } = getSkillDir(opts.ide, opts.cwd);
|
|
12170
|
-
if (
|
|
12679
|
+
if (existsSync23(dir)) {
|
|
12171
12680
|
try {
|
|
12172
12681
|
const entries = readdirSync7(dir);
|
|
12173
12682
|
let hasSkills;
|
|
12174
12683
|
if (dirPerSkill) {
|
|
12175
12684
|
hasSkills = entries.some(
|
|
12176
|
-
(f) => f.startsWith("unerr-") &&
|
|
12685
|
+
(f) => f.startsWith("unerr-") && existsSync23(join29(dir, f, "SKILL.md"))
|
|
12177
12686
|
);
|
|
12178
12687
|
} else {
|
|
12179
12688
|
hasSkills = entries.some(
|
|
@@ -12189,11 +12698,11 @@ async function ensureSkillsPresent(opts) {
|
|
|
12189
12698
|
}
|
|
12190
12699
|
function listInstalledSkills(ide, cwd) {
|
|
12191
12700
|
const { dir, ext, dirPerSkill } = getSkillDir(ide, cwd);
|
|
12192
|
-
if (!
|
|
12701
|
+
if (!existsSync23(dir)) return [];
|
|
12193
12702
|
try {
|
|
12194
12703
|
if (dirPerSkill) {
|
|
12195
12704
|
return readdirSync7(dir).filter(
|
|
12196
|
-
(f) => f.startsWith("unerr-") &&
|
|
12705
|
+
(f) => f.startsWith("unerr-") && existsSync23(join29(dir, f, "SKILL.md"))
|
|
12197
12706
|
).map((f) => ({
|
|
12198
12707
|
name: f.replace("unerr-", ""),
|
|
12199
12708
|
path: join29(dir, f, "SKILL.md")
|
|
@@ -12236,7 +12745,7 @@ var correction_detector_exports = {};
|
|
|
12236
12745
|
__export(correction_detector_exports, {
|
|
12237
12746
|
detectCorrections: () => detectCorrections
|
|
12238
12747
|
});
|
|
12239
|
-
import { existsSync as
|
|
12748
|
+
import { existsSync as existsSync25, readFileSync as readFileSync24 } from "fs";
|
|
12240
12749
|
function detectCorrections(ledgerPath, options) {
|
|
12241
12750
|
const MIN_CONFIDENCE = options?.min_confidence ?? 0.6;
|
|
12242
12751
|
const CORRECTION_WINDOW = options?.correction_window_ms ?? 6e4;
|
|
@@ -12259,11 +12768,11 @@ function detectCorrections(ledgerPath, options) {
|
|
|
12259
12768
|
return deduplicatePatterns(scored);
|
|
12260
12769
|
}
|
|
12261
12770
|
function readLedgerEntries(ledgerPath, sinceDays) {
|
|
12262
|
-
if (!
|
|
12771
|
+
if (!existsSync25(ledgerPath)) return [];
|
|
12263
12772
|
const cutoff = Date.now() - sinceDays * 24 * 60 * 60 * 1e3;
|
|
12264
12773
|
const entries = [];
|
|
12265
12774
|
try {
|
|
12266
|
-
const content =
|
|
12775
|
+
const content = readFileSync24(ledgerPath, "utf-8");
|
|
12267
12776
|
const lines = content.split("\n");
|
|
12268
12777
|
for (const line of lines) {
|
|
12269
12778
|
if (line.trim().length === 0) continue;
|
|
@@ -12468,10 +12977,10 @@ __export(offline_rewind_exports, {
|
|
|
12468
12977
|
import { randomBytes } from "crypto";
|
|
12469
12978
|
import {
|
|
12470
12979
|
appendFileSync as appendFileSync6,
|
|
12471
|
-
existsSync as
|
|
12980
|
+
existsSync as existsSync28,
|
|
12472
12981
|
mkdirSync as mkdirSync18,
|
|
12473
|
-
readFileSync as
|
|
12474
|
-
writeFileSync as
|
|
12982
|
+
readFileSync as readFileSync26,
|
|
12983
|
+
writeFileSync as writeFileSync15
|
|
12475
12984
|
} from "fs";
|
|
12476
12985
|
import { join as join33 } from "path";
|
|
12477
12986
|
async function offlineRewind(input) {
|
|
@@ -12609,9 +13118,9 @@ async function computeLocalBlastRadius(graph, revertFiles, targetFiles, startTim
|
|
|
12609
13118
|
}
|
|
12610
13119
|
function readBranchCounter(unerrDir) {
|
|
12611
13120
|
const contextPath = join33(unerrDir, "ledger", "branch_context.json");
|
|
12612
|
-
if (!
|
|
13121
|
+
if (!existsSync28(contextPath)) return 1;
|
|
12613
13122
|
try {
|
|
12614
|
-
const data = JSON.parse(
|
|
13123
|
+
const data = JSON.parse(readFileSync26(contextPath, "utf-8"));
|
|
12615
13124
|
return data.timeline_branch ?? 1;
|
|
12616
13125
|
} catch {
|
|
12617
13126
|
return 1;
|
|
@@ -12620,13 +13129,13 @@ function readBranchCounter(unerrDir) {
|
|
|
12620
13129
|
function incrementBranchCounter(unerrDir) {
|
|
12621
13130
|
const contextPath = join33(unerrDir, "ledger", "branch_context.json");
|
|
12622
13131
|
const ledgerDir = join33(unerrDir, "ledger");
|
|
12623
|
-
if (!
|
|
13132
|
+
if (!existsSync28(ledgerDir)) {
|
|
12624
13133
|
mkdirSync18(ledgerDir, { recursive: true });
|
|
12625
13134
|
}
|
|
12626
13135
|
let data = {};
|
|
12627
|
-
if (
|
|
13136
|
+
if (existsSync28(contextPath)) {
|
|
12628
13137
|
try {
|
|
12629
|
-
data = JSON.parse(
|
|
13138
|
+
data = JSON.parse(readFileSync26(contextPath, "utf-8"));
|
|
12630
13139
|
} catch {
|
|
12631
13140
|
}
|
|
12632
13141
|
}
|
|
@@ -12634,25 +13143,25 @@ function incrementBranchCounter(unerrDir) {
|
|
|
12634
13143
|
const next = current + 1;
|
|
12635
13144
|
data.timeline_branch = next;
|
|
12636
13145
|
data.last_rewind_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
12637
|
-
|
|
13146
|
+
writeFileSync15(contextPath, JSON.stringify(data, null, 2), "utf-8");
|
|
12638
13147
|
return next;
|
|
12639
13148
|
}
|
|
12640
13149
|
function markEntriesReverted(unerrDir, entryIds) {
|
|
12641
13150
|
if (entryIds.length === 0) return;
|
|
12642
13151
|
const revertedPath = join33(unerrDir, "ledger", "reverted_entries.json");
|
|
12643
13152
|
const ledgerDir = join33(unerrDir, "ledger");
|
|
12644
|
-
if (!
|
|
13153
|
+
if (!existsSync28(ledgerDir)) {
|
|
12645
13154
|
mkdirSync18(ledgerDir, { recursive: true });
|
|
12646
13155
|
}
|
|
12647
13156
|
let existing = [];
|
|
12648
|
-
if (
|
|
13157
|
+
if (existsSync28(revertedPath)) {
|
|
12649
13158
|
try {
|
|
12650
|
-
existing = JSON.parse(
|
|
13159
|
+
existing = JSON.parse(readFileSync26(revertedPath, "utf-8"));
|
|
12651
13160
|
} catch {
|
|
12652
13161
|
}
|
|
12653
13162
|
}
|
|
12654
13163
|
const merged = Array.from(/* @__PURE__ */ new Set([...existing, ...entryIds]));
|
|
12655
|
-
|
|
13164
|
+
writeFileSync15(revertedPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
12656
13165
|
}
|
|
12657
13166
|
function extractFilesFromEntry(entry) {
|
|
12658
13167
|
const files = [];
|
|
@@ -12712,10 +13221,10 @@ __export(shadow_ledger_exports, {
|
|
|
12712
13221
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
12713
13222
|
import {
|
|
12714
13223
|
appendFileSync as appendFileSync7,
|
|
12715
|
-
existsSync as
|
|
13224
|
+
existsSync as existsSync29,
|
|
12716
13225
|
mkdirSync as mkdirSync19,
|
|
12717
|
-
readFileSync as
|
|
12718
|
-
writeFileSync as
|
|
13226
|
+
readFileSync as readFileSync27,
|
|
13227
|
+
writeFileSync as writeFileSync16
|
|
12719
13228
|
} from "fs";
|
|
12720
13229
|
import { join as join34 } from "path";
|
|
12721
13230
|
function generateId2() {
|
|
@@ -12750,7 +13259,7 @@ var init_shadow_ledger = __esm({
|
|
|
12750
13259
|
this.ledgerDir = join34(unerrDir, "ledger");
|
|
12751
13260
|
this.filePath = join34(this.ledgerDir, "shadow.jsonl");
|
|
12752
13261
|
this.sessionId = generateId2();
|
|
12753
|
-
if (!
|
|
13262
|
+
if (!existsSync29(this.ledgerDir)) {
|
|
12754
13263
|
mkdirSync19(this.ledgerDir, { recursive: true });
|
|
12755
13264
|
}
|
|
12756
13265
|
this.recoverFile();
|
|
@@ -12857,9 +13366,9 @@ var init_shadow_ledger = __esm({
|
|
|
12857
13366
|
* Get all entries (read from file). Used sparingly — prefer buffer.
|
|
12858
13367
|
*/
|
|
12859
13368
|
readAllEntries() {
|
|
12860
|
-
if (!
|
|
13369
|
+
if (!existsSync29(this.filePath)) return [];
|
|
12861
13370
|
try {
|
|
12862
|
-
const content =
|
|
13371
|
+
const content = readFileSync27(this.filePath, "utf-8");
|
|
12863
13372
|
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
12864
13373
|
return lines.map((line) => JSON.parse(line));
|
|
12865
13374
|
} catch {
|
|
@@ -12896,9 +13405,9 @@ var init_shadow_ledger = __esm({
|
|
|
12896
13405
|
* Recover from file corruption: truncate to last valid JSONL line.
|
|
12897
13406
|
*/
|
|
12898
13407
|
recoverFile() {
|
|
12899
|
-
if (!
|
|
13408
|
+
if (!existsSync29(this.filePath)) return;
|
|
12900
13409
|
try {
|
|
12901
|
-
const content =
|
|
13410
|
+
const content = readFileSync27(this.filePath, "utf-8");
|
|
12902
13411
|
const lines = content.split("\n");
|
|
12903
13412
|
const validLines = [];
|
|
12904
13413
|
for (const line of lines) {
|
|
@@ -12915,23 +13424,23 @@ var init_shadow_ledger = __esm({
|
|
|
12915
13424
|
}
|
|
12916
13425
|
}
|
|
12917
13426
|
if (validLines.length < lines.filter((l) => l.trim().length > 0).length) {
|
|
12918
|
-
|
|
13427
|
+
writeFileSync16(
|
|
12919
13428
|
this.filePath,
|
|
12920
13429
|
validLines.join("\n") + (validLines.length > 0 ? "\n" : ""),
|
|
12921
13430
|
"utf-8"
|
|
12922
13431
|
);
|
|
12923
13432
|
}
|
|
12924
13433
|
} catch {
|
|
12925
|
-
|
|
13434
|
+
writeFileSync16(this.filePath, "", "utf-8");
|
|
12926
13435
|
}
|
|
12927
13436
|
}
|
|
12928
13437
|
/**
|
|
12929
13438
|
* Load the most recent entries from the file into the buffer.
|
|
12930
13439
|
*/
|
|
12931
13440
|
loadRecentEntries() {
|
|
12932
|
-
if (!
|
|
13441
|
+
if (!existsSync29(this.filePath)) return;
|
|
12933
13442
|
try {
|
|
12934
|
-
const content =
|
|
13443
|
+
const content = readFileSync27(this.filePath, "utf-8");
|
|
12935
13444
|
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
12936
13445
|
const start = Math.max(0, lines.length - MAX_BUFFER_SIZE);
|
|
12937
13446
|
for (let i = start; i < lines.length; i++) {
|
|
@@ -12959,9 +13468,9 @@ var init_shadow_ledger = __esm({
|
|
|
12959
13468
|
* Count total entries in the file.
|
|
12960
13469
|
*/
|
|
12961
13470
|
countEntries() {
|
|
12962
|
-
if (!
|
|
13471
|
+
if (!existsSync29(this.filePath)) return 0;
|
|
12963
13472
|
try {
|
|
12964
|
-
const content =
|
|
13473
|
+
const content = readFileSync27(this.filePath, "utf-8");
|
|
12965
13474
|
return content.split("\n").filter((l) => l.trim().length > 0).length;
|
|
12966
13475
|
} catch {
|
|
12967
13476
|
return 0;
|
|
@@ -12986,7 +13495,7 @@ __export(timeline_fork_exports, {
|
|
|
12986
13495
|
resetTimelineForks: () => resetTimelineForks,
|
|
12987
13496
|
wasForkPoint: () => wasForkPoint
|
|
12988
13497
|
});
|
|
12989
|
-
import { existsSync as
|
|
13498
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync20, readFileSync as readFileSync28, writeFileSync as writeFileSync17 } from "fs";
|
|
12990
13499
|
import { join as join35 } from "path";
|
|
12991
13500
|
function createTimelineFork(snapshotId, abandonedEntities, prompts, reason, unerrDir) {
|
|
12992
13501
|
const state = unerrDir ? loadState(unerrDir) : { activeTimeline: inMemoryTimeline, forks: [] };
|
|
@@ -13117,17 +13626,17 @@ function getStatePath(unerrDir) {
|
|
|
13117
13626
|
}
|
|
13118
13627
|
function ensureStateDir(unerrDir) {
|
|
13119
13628
|
const stateDir = join35(unerrDir, "state");
|
|
13120
|
-
if (!
|
|
13629
|
+
if (!existsSync30(stateDir)) {
|
|
13121
13630
|
mkdirSync20(stateDir, { recursive: true });
|
|
13122
13631
|
}
|
|
13123
13632
|
}
|
|
13124
13633
|
function loadState(unerrDir) {
|
|
13125
13634
|
const path7 = getStatePath(unerrDir);
|
|
13126
|
-
if (!
|
|
13635
|
+
if (!existsSync30(path7)) {
|
|
13127
13636
|
return { activeTimeline: 0, forks: [] };
|
|
13128
13637
|
}
|
|
13129
13638
|
try {
|
|
13130
|
-
const raw =
|
|
13639
|
+
const raw = readFileSync28(path7, "utf-8");
|
|
13131
13640
|
const parsed = JSON.parse(raw);
|
|
13132
13641
|
return {
|
|
13133
13642
|
activeTimeline: typeof parsed.activeTimeline === "number" ? parsed.activeTimeline : 0,
|
|
@@ -13141,7 +13650,7 @@ function loadState(unerrDir) {
|
|
|
13141
13650
|
function saveState(unerrDir, state) {
|
|
13142
13651
|
ensureStateDir(unerrDir);
|
|
13143
13652
|
const path7 = getStatePath(unerrDir);
|
|
13144
|
-
|
|
13653
|
+
writeFileSync17(path7, JSON.stringify(state, null, 2), "utf-8");
|
|
13145
13654
|
}
|
|
13146
13655
|
function deduplicateStrings(arr) {
|
|
13147
13656
|
return Array.from(new Set(arr));
|
|
@@ -13203,9 +13712,9 @@ function createEmptyAllTime() {
|
|
|
13203
13712
|
function loadStats() {
|
|
13204
13713
|
const currentWeek = getWeekStart();
|
|
13205
13714
|
try {
|
|
13206
|
-
const { readFileSync:
|
|
13715
|
+
const { readFileSync: readFileSync58 } = __require("fs");
|
|
13207
13716
|
const raw = JSON.parse(
|
|
13208
|
-
|
|
13717
|
+
readFileSync58(getStatsPath(), "utf-8")
|
|
13209
13718
|
);
|
|
13210
13719
|
if (raw.version !== 1) {
|
|
13211
13720
|
return {
|
|
@@ -13328,7 +13837,7 @@ __export(branch_context_exports, {
|
|
|
13328
13837
|
getHeadSha: () => getHeadSha2,
|
|
13329
13838
|
startBranchPoller: () => startBranchPoller
|
|
13330
13839
|
});
|
|
13331
|
-
import { existsSync as
|
|
13840
|
+
import { existsSync as existsSync31, readFileSync as readFileSync29 } from "fs";
|
|
13332
13841
|
import { join as join37 } from "path";
|
|
13333
13842
|
async function computeBranchContext(cwd) {
|
|
13334
13843
|
const dir = cwd ?? process.cwd();
|
|
@@ -13393,9 +13902,9 @@ function detectBranchSwitch(previousBranch, cwd) {
|
|
|
13393
13902
|
function getCurrentBranch2(cwd) {
|
|
13394
13903
|
const gitDir = cwd ? join37(cwd, ".git") : join37(process.cwd(), ".git");
|
|
13395
13904
|
const headPath = join37(gitDir, "HEAD");
|
|
13396
|
-
if (!
|
|
13905
|
+
if (!existsSync31(headPath)) return null;
|
|
13397
13906
|
try {
|
|
13398
|
-
const content =
|
|
13907
|
+
const content = readFileSync29(headPath, "utf-8").trim();
|
|
13399
13908
|
if (content.startsWith("ref: refs/heads/")) {
|
|
13400
13909
|
return content.slice("ref: refs/heads/".length);
|
|
13401
13910
|
}
|
|
@@ -13918,9 +14427,9 @@ function getCumulativePath() {
|
|
|
13918
14427
|
function loadCumulativeStats() {
|
|
13919
14428
|
const currentWeek = getWeekStart2();
|
|
13920
14429
|
try {
|
|
13921
|
-
const { readFileSync:
|
|
14430
|
+
const { readFileSync: readFileSync58 } = __require("fs");
|
|
13922
14431
|
const raw = JSON.parse(
|
|
13923
|
-
|
|
14432
|
+
readFileSync58(getCumulativePath(), "utf-8")
|
|
13924
14433
|
);
|
|
13925
14434
|
if (raw.weekStart !== currentWeek) {
|
|
13926
14435
|
return {
|
|
@@ -13981,9 +14490,9 @@ function createEmptyCumulativeLocal(weekStart) {
|
|
|
13981
14490
|
function loadCumulativeLocalStats() {
|
|
13982
14491
|
const currentWeek = getWeekStart2();
|
|
13983
14492
|
try {
|
|
13984
|
-
const { readFileSync:
|
|
14493
|
+
const { readFileSync: readFileSync58 } = __require("fs");
|
|
13985
14494
|
const raw = JSON.parse(
|
|
13986
|
-
|
|
14495
|
+
readFileSync58(getCumulativeLocalPath(), "utf-8")
|
|
13987
14496
|
);
|
|
13988
14497
|
if (raw.weekStartDate !== currentWeek) {
|
|
13989
14498
|
return createEmptyCumulativeLocal(currentWeek);
|
|
@@ -14587,11 +15096,11 @@ __export(pid_lock_exports, {
|
|
|
14587
15096
|
PidLock: () => PidLock
|
|
14588
15097
|
});
|
|
14589
15098
|
import {
|
|
14590
|
-
existsSync as
|
|
15099
|
+
existsSync as existsSync35,
|
|
14591
15100
|
mkdirSync as mkdirSync21,
|
|
14592
|
-
readFileSync as
|
|
15101
|
+
readFileSync as readFileSync33,
|
|
14593
15102
|
unlinkSync as unlinkSync6,
|
|
14594
|
-
writeFileSync as
|
|
15103
|
+
writeFileSync as writeFileSync19
|
|
14595
15104
|
} from "fs";
|
|
14596
15105
|
import { createServer } from "http";
|
|
14597
15106
|
import { join as join41 } from "path";
|
|
@@ -14671,12 +15180,12 @@ var init_pid_lock = __esm({
|
|
|
14671
15180
|
*/
|
|
14672
15181
|
async acquire() {
|
|
14673
15182
|
const stateDir = join41(this.pidPath, "..");
|
|
14674
|
-
if (!
|
|
15183
|
+
if (!existsSync35(stateDir)) {
|
|
14675
15184
|
mkdirSync21(stateDir, { recursive: true });
|
|
14676
15185
|
}
|
|
14677
|
-
if (
|
|
15186
|
+
if (existsSync35(this.pidPath)) {
|
|
14678
15187
|
try {
|
|
14679
|
-
const raw =
|
|
15188
|
+
const raw = readFileSync33(this.pidPath, "utf-8").trim();
|
|
14680
15189
|
const pidData = parsePidFile(raw);
|
|
14681
15190
|
if (pidData && isProcessAlive(pidData.pid)) {
|
|
14682
15191
|
if (pidData.healthPort) {
|
|
@@ -14736,7 +15245,7 @@ var init_pid_lock = __esm({
|
|
|
14736
15245
|
startedAt: this.startedAt,
|
|
14737
15246
|
healthPort: this.healthPort
|
|
14738
15247
|
};
|
|
14739
|
-
|
|
15248
|
+
writeFileSync19(this.pidPath, JSON.stringify(data), "utf-8");
|
|
14740
15249
|
}
|
|
14741
15250
|
async startHealthServer() {
|
|
14742
15251
|
return new Promise((resolve3) => {
|
|
@@ -14781,8 +15290,8 @@ var init_pid_lock = __esm({
|
|
|
14781
15290
|
this.healthServer = null;
|
|
14782
15291
|
}
|
|
14783
15292
|
try {
|
|
14784
|
-
if (
|
|
14785
|
-
const raw =
|
|
15293
|
+
if (existsSync35(this.pidPath)) {
|
|
15294
|
+
const raw = readFileSync33(this.pidPath, "utf-8").trim();
|
|
14786
15295
|
const data = parsePidFile(raw);
|
|
14787
15296
|
if (data && data.pid === process.pid) {
|
|
14788
15297
|
unlinkSync6(this.pidPath);
|
|
@@ -14795,9 +15304,9 @@ var init_pid_lock = __esm({
|
|
|
14795
15304
|
* Check if a proxy is currently running (for external callers).
|
|
14796
15305
|
*/
|
|
14797
15306
|
isLocked() {
|
|
14798
|
-
if (!
|
|
15307
|
+
if (!existsSync35(this.pidPath)) return { locked: false };
|
|
14799
15308
|
try {
|
|
14800
|
-
const raw =
|
|
15309
|
+
const raw = readFileSync33(this.pidPath, "utf-8").trim();
|
|
14801
15310
|
const data = parsePidFile(raw);
|
|
14802
15311
|
if (data && isProcessAlive(data.pid)) {
|
|
14803
15312
|
return { locked: true, pid: data.pid, healthPort: data.healthPort };
|
|
@@ -14812,9 +15321,9 @@ var init_pid_lock = __esm({
|
|
|
14812
15321
|
* Uses health check for full verification (unlike isLocked() which is sync-only).
|
|
14813
15322
|
*/
|
|
14814
15323
|
async probe() {
|
|
14815
|
-
if (!
|
|
15324
|
+
if (!existsSync35(this.pidPath)) return { alive: false };
|
|
14816
15325
|
try {
|
|
14817
|
-
const raw =
|
|
15326
|
+
const raw = readFileSync33(this.pidPath, "utf-8").trim();
|
|
14818
15327
|
const pidData = parsePidFile(raw);
|
|
14819
15328
|
if (!pidData || !isProcessAlive(pidData.pid)) return { alive: false };
|
|
14820
15329
|
if (pidData.healthPort) {
|
|
@@ -14831,9 +15340,9 @@ var init_pid_lock = __esm({
|
|
|
14831
15340
|
*/
|
|
14832
15341
|
static readPidFile(stateDir) {
|
|
14833
15342
|
const pidPath = join41(stateDir, PID_FILENAME);
|
|
14834
|
-
if (!
|
|
15343
|
+
if (!existsSync35(pidPath)) return null;
|
|
14835
15344
|
try {
|
|
14836
|
-
const raw =
|
|
15345
|
+
const raw = readFileSync33(pidPath, "utf-8").trim();
|
|
14837
15346
|
const data = parsePidFile(raw);
|
|
14838
15347
|
if (data && isProcessAlive(data.pid)) return data;
|
|
14839
15348
|
return null;
|
|
@@ -15124,6 +15633,11 @@ var init_tool_definitions = __esm({
|
|
|
15124
15633
|
description: "Direction of references: 'callers' (who calls this entity, default) or 'callees' (what this entity calls)",
|
|
15125
15634
|
default: "callers"
|
|
15126
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
|
+
},
|
|
15127
15641
|
token_budget: TOKEN_BUDGET_PROP
|
|
15128
15642
|
},
|
|
15129
15643
|
required: ["key"]
|
|
@@ -15282,12 +15796,8 @@ var init_tool_definitions = __esm({
|
|
|
15282
15796
|
},
|
|
15283
15797
|
purpose: {
|
|
15284
15798
|
type: "string",
|
|
15285
|
-
enum: ["explore", "
|
|
15286
|
-
description: "Read intent: 'explore' (default, budget-capped for browsing), '
|
|
15287
|
-
},
|
|
15288
|
-
force_full: {
|
|
15289
|
-
type: "boolean",
|
|
15290
|
-
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."
|
|
15291
15801
|
},
|
|
15292
15802
|
token_budget: TOKEN_BUDGET_PROP
|
|
15293
15803
|
},
|
|
@@ -15640,7 +16150,7 @@ var init_deep_link = __esm({
|
|
|
15640
16150
|
});
|
|
15641
16151
|
|
|
15642
16152
|
// src/proxy/startup-renderer.ts
|
|
15643
|
-
import { existsSync as
|
|
16153
|
+
import { existsSync as existsSync36, readFileSync as readFileSync34, writeFileSync as writeFileSync20 } from "fs";
|
|
15644
16154
|
import { join as join42 } from "path";
|
|
15645
16155
|
import React2 from "react";
|
|
15646
16156
|
var StartupRenderer;
|
|
@@ -15753,9 +16263,9 @@ var init_startup_renderer = __esm({
|
|
|
15753
16263
|
"state",
|
|
15754
16264
|
"graph_version.json"
|
|
15755
16265
|
);
|
|
15756
|
-
if (!
|
|
16266
|
+
if (!existsSync36(versionPath)) return true;
|
|
15757
16267
|
try {
|
|
15758
|
-
const data = JSON.parse(
|
|
16268
|
+
const data = JSON.parse(readFileSync34(versionPath, "utf-8"));
|
|
15759
16269
|
return !data.first_boot_shown;
|
|
15760
16270
|
} catch {
|
|
15761
16271
|
return true;
|
|
@@ -15766,11 +16276,11 @@ var init_startup_renderer = __esm({
|
|
|
15766
16276
|
const versionPath = join42(stateDir, "graph_version.json");
|
|
15767
16277
|
try {
|
|
15768
16278
|
let data = {};
|
|
15769
|
-
if (
|
|
15770
|
-
data = JSON.parse(
|
|
16279
|
+
if (existsSync36(versionPath)) {
|
|
16280
|
+
data = JSON.parse(readFileSync34(versionPath, "utf-8"));
|
|
15771
16281
|
}
|
|
15772
16282
|
data.first_boot_shown = true;
|
|
15773
|
-
|
|
16283
|
+
writeFileSync20(versionPath, JSON.stringify(data, null, 2));
|
|
15774
16284
|
} catch {
|
|
15775
16285
|
}
|
|
15776
16286
|
}
|
|
@@ -16041,13 +16551,13 @@ var init_lifecycle_actor = __esm({
|
|
|
16041
16551
|
});
|
|
16042
16552
|
|
|
16043
16553
|
// src/intelligence/facts-schema.ts
|
|
16044
|
-
import { existsSync as
|
|
16554
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync22 } from "fs";
|
|
16045
16555
|
import { join as join43 } from "path";
|
|
16046
16556
|
async function openFactsDb(projectRoot) {
|
|
16047
16557
|
const unerrDir = join43(projectRoot, ".unerr");
|
|
16048
16558
|
mkdirSync22(unerrDir, { recursive: true });
|
|
16049
16559
|
const dbPath = join43(unerrDir, FACTS_DB_FILENAME);
|
|
16050
|
-
const isNew = !
|
|
16560
|
+
const isNew = !existsSync37(dbPath);
|
|
16051
16561
|
const cozoModule = await import("cozo-node");
|
|
16052
16562
|
const CozoDbConstructor = cozoModule.default ? cozoModule.default.CozoDb : cozoModule.CozoDb;
|
|
16053
16563
|
const db = new CozoDbConstructor("sqlite", dbPath);
|
|
@@ -16648,7 +17158,7 @@ __export(fact_generator_exports, {
|
|
|
16648
17158
|
generateFromSessionAnalysis: () => generateFromSessionAnalysis,
|
|
16649
17159
|
runFactGenerationPipeline: () => runFactGenerationPipeline
|
|
16650
17160
|
});
|
|
16651
|
-
import { existsSync as
|
|
17161
|
+
import { existsSync as existsSync38, readdirSync as readdirSync9, readFileSync as readFileSync35 } from "fs";
|
|
16652
17162
|
import { join as join44 } from "path";
|
|
16653
17163
|
async function runFactGenerationPipeline(factStore2, unerrDir) {
|
|
16654
17164
|
const results = [];
|
|
@@ -16801,13 +17311,13 @@ async function generateFromSessionAnalysis(factStore2, unerrDir) {
|
|
|
16801
17311
|
}
|
|
16802
17312
|
function loadRecentSessions(unerrDir, limit) {
|
|
16803
17313
|
const sessionsDir = join44(unerrDir, "sessions");
|
|
16804
|
-
if (!
|
|
17314
|
+
if (!existsSync38(sessionsDir)) return [];
|
|
16805
17315
|
try {
|
|
16806
17316
|
const files = readdirSync9(sessionsDir).filter((f) => f.endsWith(".jsonl")).sort().slice(-limit);
|
|
16807
17317
|
const summaries = [];
|
|
16808
17318
|
for (const file of files) {
|
|
16809
17319
|
try {
|
|
16810
|
-
const content =
|
|
17320
|
+
const content = readFileSync35(join44(sessionsDir, file), "utf-8");
|
|
16811
17321
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
16812
17322
|
const lastLine = lines[lines.length - 1];
|
|
16813
17323
|
if (lastLine) {
|
|
@@ -17331,7 +17841,7 @@ var init_model_pricing = __esm({
|
|
|
17331
17841
|
});
|
|
17332
17842
|
|
|
17333
17843
|
// src/tracking/entity-rewind.ts
|
|
17334
|
-
import { readFileSync as
|
|
17844
|
+
import { readFileSync as readFileSync36, writeFileSync as writeFileSync21 } from "fs";
|
|
17335
17845
|
import { join as join45 } from "path";
|
|
17336
17846
|
async function revertEntity(entityName, localGraph, projectRoot, filePath) {
|
|
17337
17847
|
const driftEntity = await findDriftEntity(localGraph, entityName, filePath);
|
|
@@ -17409,12 +17919,12 @@ async function findDriftEntity(localGraph, entityName, filePath) {
|
|
|
17409
17919
|
}
|
|
17410
17920
|
async function removeAddedEntity(absPath, entity, localGraph) {
|
|
17411
17921
|
try {
|
|
17412
|
-
const content =
|
|
17922
|
+
const content = readFileSync36(absPath, "utf-8");
|
|
17413
17923
|
const lines = content.split("\n");
|
|
17414
17924
|
const startIdx = entity.line_start - 1;
|
|
17415
17925
|
const endIdx = entity.line_end;
|
|
17416
17926
|
lines.splice(startIdx, endIdx - startIdx);
|
|
17417
|
-
|
|
17927
|
+
writeFileSync21(absPath, lines.join("\n"), "utf-8");
|
|
17418
17928
|
await localGraph.removeDriftEntity(entity.key);
|
|
17419
17929
|
return {
|
|
17420
17930
|
reverted: true,
|
|
@@ -17444,12 +17954,12 @@ async function restoreDeletedEntity(absPath, entity, localGraph) {
|
|
|
17444
17954
|
};
|
|
17445
17955
|
}
|
|
17446
17956
|
try {
|
|
17447
|
-
const content =
|
|
17957
|
+
const content = readFileSync36(absPath, "utf-8");
|
|
17448
17958
|
const lines = content.split("\n");
|
|
17449
17959
|
const previousLines = entity.previous_body.split("\n");
|
|
17450
17960
|
const insertIdx = Math.min(entity.line_start - 1, lines.length);
|
|
17451
17961
|
lines.splice(insertIdx, 0, ...previousLines);
|
|
17452
|
-
|
|
17962
|
+
writeFileSync21(absPath, lines.join("\n"), "utf-8");
|
|
17453
17963
|
await localGraph.removeDriftEntity(entity.key);
|
|
17454
17964
|
const endLine = insertIdx + previousLines.length;
|
|
17455
17965
|
return {
|
|
@@ -17480,13 +17990,13 @@ async function restoreModifiedEntity(absPath, entity, localGraph) {
|
|
|
17480
17990
|
};
|
|
17481
17991
|
}
|
|
17482
17992
|
try {
|
|
17483
|
-
const content =
|
|
17993
|
+
const content = readFileSync36(absPath, "utf-8");
|
|
17484
17994
|
const lines = content.split("\n");
|
|
17485
17995
|
const previousLines = entity.previous_body.split("\n");
|
|
17486
17996
|
const startIdx = entity.line_start - 1;
|
|
17487
17997
|
const deleteCount = entity.line_end - entity.line_start + 1;
|
|
17488
17998
|
lines.splice(startIdx, deleteCount, ...previousLines);
|
|
17489
|
-
|
|
17999
|
+
writeFileSync21(absPath, lines.join("\n"), "utf-8");
|
|
17490
18000
|
await localGraph.removeDriftEntity(entity.key);
|
|
17491
18001
|
const endLine = startIdx + previousLines.length;
|
|
17492
18002
|
return {
|
|
@@ -17892,7 +18402,7 @@ __export(file_outline_exports, {
|
|
|
17892
18402
|
buildFileOutline: () => buildFileOutline,
|
|
17893
18403
|
fileOutlineTool: () => fileOutlineTool
|
|
17894
18404
|
});
|
|
17895
|
-
import { existsSync as
|
|
18405
|
+
import { existsSync as existsSync39, readFileSync as readFileSync37 } from "fs";
|
|
17896
18406
|
import { relative as relative2, resolve } from "path";
|
|
17897
18407
|
function normRisk(rl) {
|
|
17898
18408
|
const x2 = (rl ?? "low").toLowerCase();
|
|
@@ -17951,10 +18461,10 @@ function detectExportedNames(lines, scanLimit) {
|
|
|
17951
18461
|
async function buildFileOutline(params) {
|
|
17952
18462
|
const abs = resolve(params.cwd, params.filePathArg);
|
|
17953
18463
|
const rel = relative2(params.cwd, abs).replace(/\\/g, "/") || params.filePathArg;
|
|
17954
|
-
if (!
|
|
18464
|
+
if (!existsSync39(abs)) {
|
|
17955
18465
|
throw new Error(`File not found: ${abs}`);
|
|
17956
18466
|
}
|
|
17957
|
-
const raw =
|
|
18467
|
+
const raw = readFileSync37(abs);
|
|
17958
18468
|
const sampleEnd = Math.min(raw.length, 8192);
|
|
17959
18469
|
for (let i = 0; i < sampleEnd; i++) {
|
|
17960
18470
|
if (raw[i] === 0) {
|
|
@@ -18102,7 +18612,7 @@ __export(file_read_protocol_exports, {
|
|
|
18102
18612
|
runFileReadForRouter: () => runFileReadForRouter,
|
|
18103
18613
|
runFileReadTool: () => runFileReadTool
|
|
18104
18614
|
});
|
|
18105
|
-
import { existsSync as
|
|
18615
|
+
import { existsSync as existsSync40, readFileSync as readFileSync38 } from "fs";
|
|
18106
18616
|
import { relative as relative3, resolve as resolve2 } from "path";
|
|
18107
18617
|
function isGeneratedPath(rel) {
|
|
18108
18618
|
return /(?:^|\/)node_modules\/|(?:^|\/)dist\/|\/\.next\/|\.generated\./.test(
|
|
@@ -18163,9 +18673,8 @@ function logFileRead(cwd, file, mode, totalLines, returnedLines, entity, tokenEs
|
|
|
18163
18673
|
async function runFileReadForRouter(args, ctx) {
|
|
18164
18674
|
const filePathArg = args.file_path;
|
|
18165
18675
|
if (!filePathArg) throw new Error("file_read requires file_path");
|
|
18166
|
-
const
|
|
18167
|
-
const purpose =
|
|
18168
|
-
const isFullRead = forceFull || purpose === "edit";
|
|
18676
|
+
const rawPurpose = args.purpose?.trim() || "explore";
|
|
18677
|
+
const purpose = rawPurpose === "edit" ? "explore" : rawPurpose;
|
|
18169
18678
|
const entityName = args.entity?.trim();
|
|
18170
18679
|
let entityWindowApplied = false;
|
|
18171
18680
|
let entityMatchInfo;
|
|
@@ -18173,14 +18682,14 @@ async function runFileReadForRouter(args, ctx) {
|
|
|
18173
18682
|
let limit = args.limit != null ? Math.min(MAX_READ_LINES, Math.max(1, Number(args.limit))) : void 0;
|
|
18174
18683
|
const defaultBudget = purpose === "reference" ? 1e3 : 2e3;
|
|
18175
18684
|
const tokenBudget = typeof args.token_budget === "number" && args.token_budget >= 100 ? args.token_budget : defaultBudget;
|
|
18176
|
-
const budgetLines =
|
|
18685
|
+
const budgetLines = Math.floor(tokenBudget * CHARS_PER_TOKEN4 / AVG_CHARS_PER_LINE);
|
|
18177
18686
|
const abs = resolve2(ctx.cwd, filePathArg);
|
|
18178
18687
|
const rel = relative3(ctx.cwd, abs).replace(/\\/g, "/") || filePathArg;
|
|
18179
18688
|
const isOutOfProject = rel.startsWith("..");
|
|
18180
|
-
if (!
|
|
18689
|
+
if (!existsSync40(abs)) {
|
|
18181
18690
|
return { content: { error: `File not found: ${abs}` } };
|
|
18182
18691
|
}
|
|
18183
|
-
const raw =
|
|
18692
|
+
const raw = readFileSync38(abs);
|
|
18184
18693
|
const sampleEnd = Math.min(raw.length, 8192);
|
|
18185
18694
|
for (let i = 0; i < sampleEnd; i++) {
|
|
18186
18695
|
if (raw[i] === 0) {
|
|
@@ -18196,7 +18705,7 @@ async function runFileReadForRouter(args, ctx) {
|
|
|
18196
18705
|
const lines = text2.split("\n");
|
|
18197
18706
|
const totalLines = lines.length;
|
|
18198
18707
|
const effectiveGate = Math.max(LINE_GATE, Math.min(HARD_GATE_CEILING, budgetLines));
|
|
18199
|
-
if (totalLines > effectiveGate && totalLines > LINE_GATE && offset === void 0 && !entityName && !
|
|
18708
|
+
if (totalLines > effectiveGate && totalLines > LINE_GATE && offset === void 0 && !entityName && !entityName) {
|
|
18200
18709
|
const outline = await buildFileOutline({
|
|
18201
18710
|
cwd: ctx.cwd,
|
|
18202
18711
|
filePathArg,
|
|
@@ -18207,7 +18716,7 @@ async function runFileReadForRouter(args, ctx) {
|
|
|
18207
18716
|
content: {
|
|
18208
18717
|
...outline,
|
|
18209
18718
|
gated: true,
|
|
18210
|
-
_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.`
|
|
18211
18720
|
},
|
|
18212
18721
|
_layer6_meta: {
|
|
18213
18722
|
format: "outline",
|
|
@@ -18345,7 +18854,7 @@ async function runFileReadForRouter(args, ctx) {
|
|
|
18345
18854
|
limit = LOG_TAIL_LINES;
|
|
18346
18855
|
}
|
|
18347
18856
|
const effOffset = offset ?? 1;
|
|
18348
|
-
const effLimit = limit ??
|
|
18857
|
+
const effLimit = limit ?? Math.min(budgetLines, totalLines);
|
|
18349
18858
|
const sliced = lines.slice(effOffset - 1, effOffset - 1 + effLimit);
|
|
18350
18859
|
const numbered = sliced.map((line, i) => `${effOffset + i} ${line}`).join("\n");
|
|
18351
18860
|
let body = effOffset > 1 || sliced.length < totalLines || entityWindowApplied ? `${numbered}
|
|
@@ -18375,10 +18884,7 @@ ${body}`;
|
|
|
18375
18884
|
total_lines: totalLines,
|
|
18376
18885
|
total_chars: text2.length
|
|
18377
18886
|
};
|
|
18378
|
-
if (
|
|
18379
|
-
meta.optimization = `file_read full (purpose:${purpose})`;
|
|
18380
|
-
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.";
|
|
18381
|
-
} else if (effOffset > 1 || sliced.length < totalLines || entityWindowApplied) {
|
|
18887
|
+
if (effOffset > 1 || sliced.length < totalLines || entityWindowApplied) {
|
|
18382
18888
|
meta.optimization = `file_read window lines ${effOffset}-${effOffset + sliced.length - 1}`;
|
|
18383
18889
|
}
|
|
18384
18890
|
if (entityWindowApplied) {
|
|
@@ -18390,6 +18896,10 @@ ${body}`;
|
|
|
18390
18896
|
if (isProbableLogPath(rel) && offset !== void 0 && totalLines > LINE_GATE) {
|
|
18391
18897
|
meta.optimization = `${meta.optimization ?? "file_read"} \xB7 log_tail`;
|
|
18392
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
|
+
}
|
|
18393
18903
|
const returnedLineCount = sliced.length;
|
|
18394
18904
|
const readMode = entityWindowApplied ? "entity" : isProbableLogPath(rel) && offset !== void 0 && totalLines > LINE_GATE ? "log_tail" : effOffset > 1 || returnedLineCount < totalLines ? "slice" : "full";
|
|
18395
18905
|
logFileRead(
|
|
@@ -19943,11 +20453,11 @@ var init_query_router = __esm({
|
|
|
19943
20453
|
const entity = await this.resolveEntityWithOverlay(key);
|
|
19944
20454
|
if (entity && entity.file_path && entity.start_line > 0) {
|
|
19945
20455
|
try {
|
|
19946
|
-
const { readFileSync:
|
|
20456
|
+
const { readFileSync: readFileSync58 } = await import("fs");
|
|
19947
20457
|
const { resolve: resolve3 } = await import("path");
|
|
19948
20458
|
const cwd = this.projectRoot ?? process.cwd();
|
|
19949
20459
|
const abs = resolve3(cwd, entity.file_path);
|
|
19950
|
-
const lines =
|
|
20460
|
+
const lines = readFileSync58(abs, "utf-8").split("\n");
|
|
19951
20461
|
const start = entity.start_line - 1;
|
|
19952
20462
|
const end = entity.end_line ?? lines.length;
|
|
19953
20463
|
entity.body = lines.slice(start, end).join("\n");
|
|
@@ -19959,15 +20469,29 @@ var init_query_router = __esm({
|
|
|
19959
20469
|
case "get_references": {
|
|
19960
20470
|
const key = await this.resolveKeyArg(args.key);
|
|
19961
20471
|
const direction = args.direction ?? "callers";
|
|
19962
|
-
|
|
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
|
+
};
|
|
19963
20485
|
}
|
|
19964
20486
|
case "get_callers": {
|
|
19965
20487
|
const key = await this.resolveKeyArg(args.key);
|
|
19966
|
-
|
|
20488
|
+
const rawCallers = await this.localGraph.getCallersOf(key);
|
|
20489
|
+
return rawCallers.slice(0, 25).map(({ body: _body, ...rest }) => rest);
|
|
19967
20490
|
}
|
|
19968
20491
|
case "get_callees": {
|
|
19969
20492
|
const key = await this.resolveKeyArg(args.key);
|
|
19970
|
-
|
|
20493
|
+
const rawCallees = await this.localGraph.getCalleesOf(key);
|
|
20494
|
+
return rawCallees.slice(0, 25).map(({ body: _body, ...rest }) => rest);
|
|
19971
20495
|
}
|
|
19972
20496
|
case "get_imports": {
|
|
19973
20497
|
const filePath = args.file_path;
|
|
@@ -20000,6 +20524,14 @@ var init_query_router = __esm({
|
|
|
20000
20524
|
{ entityKey: entityKey2, exceptions }
|
|
20001
20525
|
);
|
|
20002
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
|
+
}
|
|
20003
20535
|
return await this.localGraph.getRules(filePath);
|
|
20004
20536
|
}
|
|
20005
20537
|
case "check_rules": {
|
|
@@ -20035,7 +20567,37 @@ var init_query_router = __esm({
|
|
|
20035
20567
|
if (!raw)
|
|
20036
20568
|
throw new Error("get_business_context requires key or entity_key");
|
|
20037
20569
|
const key = await this.resolveKeyArg(raw);
|
|
20038
|
-
|
|
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
|
+
};
|
|
20039
20601
|
}
|
|
20040
20602
|
case "get_conventions": {
|
|
20041
20603
|
return await this.localGraph.getConventions();
|
|
@@ -20164,6 +20726,18 @@ var init_query_router = __esm({
|
|
|
20164
20726
|
throw new Error(`Unknown local tool: ${toolName}`);
|
|
20165
20727
|
}
|
|
20166
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
|
+
}
|
|
20167
20741
|
/**
|
|
20168
20742
|
* Resolve a key argument: if it looks like a hex hash (entity key), use as-is.
|
|
20169
20743
|
* Otherwise, search by name and return the best match's key.
|
|
@@ -21473,7 +22047,7 @@ __export(causal_bridge_exports, {
|
|
|
21473
22047
|
assembleCausalChain: () => assembleCausalChain,
|
|
21474
22048
|
computeDurability: () => computeDurability
|
|
21475
22049
|
});
|
|
21476
|
-
import { existsSync as
|
|
22050
|
+
import { existsSync as existsSync41, readFileSync as readFileSync39 } from "fs";
|
|
21477
22051
|
import { join as join46 } from "path";
|
|
21478
22052
|
function computeAggregateDurability(interactions) {
|
|
21479
22053
|
if (interactions.length === 0) return 1;
|
|
@@ -21632,8 +22206,8 @@ var init_causal_bridge = __esm({
|
|
|
21632
22206
|
}
|
|
21633
22207
|
loadEntityLedgerEntries(entityKey2) {
|
|
21634
22208
|
const ledgerPath = join46(this.unerrDir, "ledger", "shadow.jsonl");
|
|
21635
|
-
if (!
|
|
21636
|
-
const content =
|
|
22209
|
+
if (!existsSync41(ledgerPath)) return [];
|
|
22210
|
+
const content = readFileSync39(ledgerPath, "utf-8");
|
|
21637
22211
|
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
21638
22212
|
const entries = [];
|
|
21639
22213
|
for (const line of lines) {
|
|
@@ -22119,7 +22693,7 @@ var context_ledger_exports = {};
|
|
|
22119
22693
|
__export(context_ledger_exports, {
|
|
22120
22694
|
createContextLedger: () => createContextLedger
|
|
22121
22695
|
});
|
|
22122
|
-
import { existsSync as
|
|
22696
|
+
import { existsSync as existsSync42, mkdirSync as mkdirSync23, readFileSync as readFileSync40, writeFileSync as writeFileSync22 } from "fs";
|
|
22123
22697
|
import { join as join47 } from "path";
|
|
22124
22698
|
function createContextLedger(unerrDir) {
|
|
22125
22699
|
const stateDir = join47(unerrDir, "state");
|
|
@@ -22127,7 +22701,7 @@ function createContextLedger(unerrDir) {
|
|
|
22127
22701
|
let records = [];
|
|
22128
22702
|
let index = /* @__PURE__ */ new Map();
|
|
22129
22703
|
function ensureDir() {
|
|
22130
|
-
if (!
|
|
22704
|
+
if (!existsSync42(stateDir)) {
|
|
22131
22705
|
mkdirSync23(stateDir, { recursive: true });
|
|
22132
22706
|
}
|
|
22133
22707
|
}
|
|
@@ -22136,13 +22710,13 @@ function createContextLedger(unerrDir) {
|
|
|
22136
22710
|
}
|
|
22137
22711
|
function load() {
|
|
22138
22712
|
ensureDir();
|
|
22139
|
-
if (!
|
|
22713
|
+
if (!existsSync42(filePath)) {
|
|
22140
22714
|
records = [];
|
|
22141
22715
|
index = /* @__PURE__ */ new Map();
|
|
22142
22716
|
return /* @__PURE__ */ new Map();
|
|
22143
22717
|
}
|
|
22144
22718
|
try {
|
|
22145
|
-
const raw =
|
|
22719
|
+
const raw = readFileSync40(filePath, "utf-8");
|
|
22146
22720
|
const parsed = JSON.parse(raw);
|
|
22147
22721
|
records = Array.isArray(parsed) ? parsed : [];
|
|
22148
22722
|
} catch {
|
|
@@ -22178,7 +22752,7 @@ function createContextLedger(unerrDir) {
|
|
|
22178
22752
|
records = newRecords;
|
|
22179
22753
|
index = delivered;
|
|
22180
22754
|
try {
|
|
22181
|
-
|
|
22755
|
+
writeFileSync22(filePath, JSON.stringify(records, null, 2), "utf-8");
|
|
22182
22756
|
} catch {
|
|
22183
22757
|
}
|
|
22184
22758
|
}
|
|
@@ -23049,11 +23623,11 @@ __export(intent_correlator_exports, {
|
|
|
23049
23623
|
IntentCorrelator: () => IntentCorrelator
|
|
23050
23624
|
});
|
|
23051
23625
|
import {
|
|
23052
|
-
existsSync as
|
|
23626
|
+
existsSync as existsSync43,
|
|
23053
23627
|
mkdirSync as mkdirSync24,
|
|
23054
|
-
readFileSync as
|
|
23628
|
+
readFileSync as readFileSync41,
|
|
23055
23629
|
renameSync,
|
|
23056
|
-
writeFileSync as
|
|
23630
|
+
writeFileSync as writeFileSync23
|
|
23057
23631
|
} from "fs";
|
|
23058
23632
|
import { join as join48 } from "path";
|
|
23059
23633
|
function extractFiles4(args) {
|
|
@@ -23105,7 +23679,7 @@ var init_intent_correlator = __esm({
|
|
|
23105
23679
|
constructor(unerrDir) {
|
|
23106
23680
|
this.ledgerDir = join48(unerrDir, "ledger");
|
|
23107
23681
|
this.pendingPath = join48(this.ledgerDir, "pending_correlations.json");
|
|
23108
|
-
if (!
|
|
23682
|
+
if (!existsSync43(this.ledgerDir)) {
|
|
23109
23683
|
mkdirSync24(this.ledgerDir, { recursive: true });
|
|
23110
23684
|
}
|
|
23111
23685
|
this.load();
|
|
@@ -23197,9 +23771,9 @@ var init_intent_correlator = __esm({
|
|
|
23197
23771
|
}
|
|
23198
23772
|
// ── Persistence ─────────────────────────────────────────────────
|
|
23199
23773
|
load() {
|
|
23200
|
-
if (!
|
|
23774
|
+
if (!existsSync43(this.pendingPath)) return;
|
|
23201
23775
|
try {
|
|
23202
|
-
const raw =
|
|
23776
|
+
const raw = readFileSync41(this.pendingPath, "utf-8");
|
|
23203
23777
|
const parsed = JSON.parse(raw);
|
|
23204
23778
|
if (Array.isArray(parsed)) {
|
|
23205
23779
|
this.pending = parsed;
|
|
@@ -23211,7 +23785,7 @@ var init_intent_correlator = __esm({
|
|
|
23211
23785
|
save() {
|
|
23212
23786
|
try {
|
|
23213
23787
|
const tmpPath = `${this.pendingPath}.tmp`;
|
|
23214
|
-
|
|
23788
|
+
writeFileSync23(tmpPath, JSON.stringify(this.pending, null, 2), "utf-8");
|
|
23215
23789
|
renameSync(tmpPath, this.pendingPath);
|
|
23216
23790
|
} catch (err) {
|
|
23217
23791
|
process.stderr.write(
|
|
@@ -23231,12 +23805,12 @@ __export(working_snapshots_exports, {
|
|
|
23231
23805
|
});
|
|
23232
23806
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
23233
23807
|
import {
|
|
23234
|
-
existsSync as
|
|
23808
|
+
existsSync as existsSync44,
|
|
23235
23809
|
mkdirSync as mkdirSync25,
|
|
23236
|
-
readFileSync as
|
|
23810
|
+
readFileSync as readFileSync42,
|
|
23237
23811
|
readdirSync as readdirSync10,
|
|
23238
23812
|
rmSync as rmSync2,
|
|
23239
|
-
writeFileSync as
|
|
23813
|
+
writeFileSync as writeFileSync24
|
|
23240
23814
|
} from "fs";
|
|
23241
23815
|
import { join as join49 } from "path";
|
|
23242
23816
|
var _log2, MAX_SNAPSHOTS, AUTO_SNAPSHOT_COOLDOWN_MS, WorkingSnapshotStore;
|
|
@@ -23257,7 +23831,7 @@ var init_working_snapshots = __esm({
|
|
|
23257
23831
|
constructor(unerrDir) {
|
|
23258
23832
|
this.unerrDir = unerrDir;
|
|
23259
23833
|
this.snapshotDir = join49(unerrDir, "snapshots");
|
|
23260
|
-
if (!
|
|
23834
|
+
if (!existsSync44(this.snapshotDir)) {
|
|
23261
23835
|
mkdirSync25(this.snapshotDir, { recursive: true });
|
|
23262
23836
|
}
|
|
23263
23837
|
}
|
|
@@ -23276,7 +23850,7 @@ var init_working_snapshots = __esm({
|
|
|
23276
23850
|
sessionId: opts.sessionId,
|
|
23277
23851
|
processed: false
|
|
23278
23852
|
};
|
|
23279
|
-
|
|
23853
|
+
writeFileSync24(
|
|
23280
23854
|
join49(this.snapshotDir, `${id}.json`),
|
|
23281
23855
|
JSON.stringify(snapshot, null, 2),
|
|
23282
23856
|
"utf-8"
|
|
@@ -23292,9 +23866,9 @@ var init_working_snapshots = __esm({
|
|
|
23292
23866
|
*/
|
|
23293
23867
|
get(snapshotId) {
|
|
23294
23868
|
const filePath = join49(this.snapshotDir, `${snapshotId}.json`);
|
|
23295
|
-
if (!
|
|
23869
|
+
if (!existsSync44(filePath)) return null;
|
|
23296
23870
|
try {
|
|
23297
|
-
return JSON.parse(
|
|
23871
|
+
return JSON.parse(readFileSync42(filePath, "utf-8"));
|
|
23298
23872
|
} catch {
|
|
23299
23873
|
return null;
|
|
23300
23874
|
}
|
|
@@ -23303,14 +23877,14 @@ var init_working_snapshots = __esm({
|
|
|
23303
23877
|
* List all snapshots, most recent first.
|
|
23304
23878
|
*/
|
|
23305
23879
|
list() {
|
|
23306
|
-
if (!
|
|
23880
|
+
if (!existsSync44(this.snapshotDir)) return [];
|
|
23307
23881
|
const files = readdirSync10(this.snapshotDir).filter(
|
|
23308
23882
|
(f) => f.endsWith(".json")
|
|
23309
23883
|
);
|
|
23310
23884
|
const snapshots = [];
|
|
23311
23885
|
for (const file of files) {
|
|
23312
23886
|
try {
|
|
23313
|
-
const raw =
|
|
23887
|
+
const raw = readFileSync42(join49(this.snapshotDir, file), "utf-8");
|
|
23314
23888
|
snapshots.push(JSON.parse(raw));
|
|
23315
23889
|
} catch {
|
|
23316
23890
|
}
|
|
@@ -23350,7 +23924,7 @@ var init_working_snapshots = __esm({
|
|
|
23350
23924
|
const snapshot = this.get(snapshotId);
|
|
23351
23925
|
if (!snapshot) return;
|
|
23352
23926
|
snapshot.processed = true;
|
|
23353
|
-
|
|
23927
|
+
writeFileSync24(
|
|
23354
23928
|
join49(this.snapshotDir, `${snapshotId}.json`),
|
|
23355
23929
|
JSON.stringify(snapshot, null, 2),
|
|
23356
23930
|
"utf-8"
|
|
@@ -23367,7 +23941,7 @@ var init_working_snapshots = __esm({
|
|
|
23367
23941
|
*/
|
|
23368
23942
|
delete(snapshotId) {
|
|
23369
23943
|
const filePath = join49(this.snapshotDir, `${snapshotId}.json`);
|
|
23370
|
-
if (!
|
|
23944
|
+
if (!existsSync44(filePath)) return false;
|
|
23371
23945
|
rmSync2(filePath);
|
|
23372
23946
|
return true;
|
|
23373
23947
|
}
|
|
@@ -23376,9 +23950,9 @@ var init_working_snapshots = __esm({
|
|
|
23376
23950
|
*/
|
|
23377
23951
|
getTimelineBranch() {
|
|
23378
23952
|
const contextPath = join49(this.unerrDir, "branch_context.json");
|
|
23379
|
-
if (!
|
|
23953
|
+
if (!existsSync44(contextPath)) return 0;
|
|
23380
23954
|
try {
|
|
23381
|
-
const ctx = JSON.parse(
|
|
23955
|
+
const ctx = JSON.parse(readFileSync42(contextPath, "utf-8"));
|
|
23382
23956
|
return ctx.timelineBranch ?? 0;
|
|
23383
23957
|
} catch {
|
|
23384
23958
|
return 0;
|
|
@@ -23390,16 +23964,16 @@ var init_working_snapshots = __esm({
|
|
|
23390
23964
|
incrementTimelineBranch() {
|
|
23391
23965
|
const contextPath = join49(this.unerrDir, "branch_context.json");
|
|
23392
23966
|
let ctx = {};
|
|
23393
|
-
if (
|
|
23967
|
+
if (existsSync44(contextPath)) {
|
|
23394
23968
|
try {
|
|
23395
|
-
ctx = JSON.parse(
|
|
23969
|
+
ctx = JSON.parse(readFileSync42(contextPath, "utf-8"));
|
|
23396
23970
|
} catch {
|
|
23397
23971
|
ctx = {};
|
|
23398
23972
|
}
|
|
23399
23973
|
}
|
|
23400
23974
|
const current = ctx.timelineBranch ?? 0;
|
|
23401
23975
|
ctx.timelineBranch = current + 1;
|
|
23402
|
-
|
|
23976
|
+
writeFileSync24(contextPath, JSON.stringify(ctx, null, 2), "utf-8");
|
|
23403
23977
|
return current + 1;
|
|
23404
23978
|
}
|
|
23405
23979
|
/**
|
|
@@ -23565,7 +24139,7 @@ var quality_signals_exports = {};
|
|
|
23565
24139
|
__export(quality_signals_exports, {
|
|
23566
24140
|
QualitySignalTracker: () => QualitySignalTracker
|
|
23567
24141
|
});
|
|
23568
|
-
import { existsSync as
|
|
24142
|
+
import { existsSync as existsSync45, readFileSync as readFileSync43, writeFileSync as writeFileSync25 } from "fs";
|
|
23569
24143
|
import { join as join50 } from "path";
|
|
23570
24144
|
function computeDurabilityFromAge(survivalMs) {
|
|
23571
24145
|
if (survivalMs < FRAGILE_THRESHOLD_MS) {
|
|
@@ -23686,11 +24260,11 @@ var init_quality_signals = __esm({
|
|
|
23686
24260
|
save() {
|
|
23687
24261
|
try {
|
|
23688
24262
|
const dir = join50(this.signalsPath, "..");
|
|
23689
|
-
if (!
|
|
24263
|
+
if (!existsSync45(dir)) {
|
|
23690
24264
|
const { mkdirSync: mkdirSync39 } = __require("fs");
|
|
23691
24265
|
mkdirSync39(dir, { recursive: true });
|
|
23692
24266
|
}
|
|
23693
|
-
|
|
24267
|
+
writeFileSync25(
|
|
23694
24268
|
this.signalsPath,
|
|
23695
24269
|
JSON.stringify(this.signals, null, 2),
|
|
23696
24270
|
"utf-8"
|
|
@@ -23732,7 +24306,7 @@ var init_quality_signals = __esm({
|
|
|
23732
24306
|
* Load signals from disk.
|
|
23733
24307
|
*/
|
|
23734
24308
|
load() {
|
|
23735
|
-
if (!
|
|
24309
|
+
if (!existsSync45(this.signalsPath)) {
|
|
23736
24310
|
return {
|
|
23737
24311
|
durabilityScores: {},
|
|
23738
24312
|
corrections: [],
|
|
@@ -23741,7 +24315,7 @@ var init_quality_signals = __esm({
|
|
|
23741
24315
|
}
|
|
23742
24316
|
try {
|
|
23743
24317
|
return JSON.parse(
|
|
23744
|
-
|
|
24318
|
+
readFileSync43(this.signalsPath, "utf-8")
|
|
23745
24319
|
);
|
|
23746
24320
|
} catch {
|
|
23747
24321
|
return {
|
|
@@ -24758,7 +25332,7 @@ var incomplete_work_exports = {};
|
|
|
24758
25332
|
__export(incomplete_work_exports, {
|
|
24759
25333
|
IncompleteWorkDetector: () => IncompleteWorkDetector
|
|
24760
25334
|
});
|
|
24761
|
-
import { existsSync as
|
|
25335
|
+
import { existsSync as existsSync46, mkdirSync as mkdirSync26, readFileSync as readFileSync44, writeFileSync as writeFileSync26 } from "fs";
|
|
24762
25336
|
import { join as join51 } from "path";
|
|
24763
25337
|
function severityRank(severity) {
|
|
24764
25338
|
switch (severity) {
|
|
@@ -24953,9 +25527,9 @@ var init_incomplete_work = __esm({
|
|
|
24953
25527
|
if (!this.unerrDir) return false;
|
|
24954
25528
|
try {
|
|
24955
25529
|
const stateDir = join51(this.unerrDir, "state");
|
|
24956
|
-
if (!
|
|
25530
|
+
if (!existsSync46(stateDir)) mkdirSync26(stateDir, { recursive: true });
|
|
24957
25531
|
const filePath = join51(stateDir, PERSISTENCE_FILE);
|
|
24958
|
-
|
|
25532
|
+
writeFileSync26(
|
|
24959
25533
|
filePath,
|
|
24960
25534
|
JSON.stringify({
|
|
24961
25535
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -24975,8 +25549,8 @@ var init_incomplete_work = __esm({
|
|
|
24975
25549
|
static readPersistedItems(unerrDir) {
|
|
24976
25550
|
try {
|
|
24977
25551
|
const filePath = join51(unerrDir, "state", PERSISTENCE_FILE);
|
|
24978
|
-
if (!
|
|
24979
|
-
const data = JSON.parse(
|
|
25552
|
+
if (!existsSync46(filePath)) return [];
|
|
25553
|
+
const data = JSON.parse(readFileSync44(filePath, "utf-8"));
|
|
24980
25554
|
return data.items ?? [];
|
|
24981
25555
|
} catch {
|
|
24982
25556
|
return [];
|
|
@@ -26447,7 +27021,7 @@ var transport_mux_exports = {};
|
|
|
26447
27021
|
__export(transport_mux_exports, {
|
|
26448
27022
|
TransportMux: () => TransportMux
|
|
26449
27023
|
});
|
|
26450
|
-
import { existsSync as
|
|
27024
|
+
import { existsSync as existsSync47, unlinkSync as unlinkSync7 } from "fs";
|
|
26451
27025
|
import { createServer as createServer2 } from "net";
|
|
26452
27026
|
var _log4, TransportMux;
|
|
26453
27027
|
var init_transport_mux = __esm({
|
|
@@ -26487,7 +27061,7 @@ var init_transport_mux = __esm({
|
|
|
26487
27061
|
* Start listening for secondary clients on the Unix domain socket.
|
|
26488
27062
|
*/
|
|
26489
27063
|
start() {
|
|
26490
|
-
if (
|
|
27064
|
+
if (existsSync47(this.sockPath)) {
|
|
26491
27065
|
try {
|
|
26492
27066
|
unlinkSync7(this.sockPath);
|
|
26493
27067
|
} catch {
|
|
@@ -26520,7 +27094,7 @@ var init_transport_mux = __esm({
|
|
|
26520
27094
|
this.server.close();
|
|
26521
27095
|
this.server = null;
|
|
26522
27096
|
}
|
|
26523
|
-
if (
|
|
27097
|
+
if (existsSync47(this.sockPath)) {
|
|
26524
27098
|
try {
|
|
26525
27099
|
unlinkSync7(this.sockPath);
|
|
26526
27100
|
} catch {
|
|
@@ -26668,7 +27242,7 @@ __export(git_trailers_exports, {
|
|
|
26668
27242
|
parseTrailersFromMessage: () => parseTrailersFromMessage,
|
|
26669
27243
|
uninstallPrepareCommitMsgHook: () => uninstallPrepareCommitMsgHook
|
|
26670
27244
|
});
|
|
26671
|
-
import { existsSync as
|
|
27245
|
+
import { existsSync as existsSync48, readFileSync as readFileSync45, writeFileSync as writeFileSync27 } from "fs";
|
|
26672
27246
|
import { join as join52 } from "path";
|
|
26673
27247
|
function getCommitTrailers(ledger, timelineBranch, branch) {
|
|
26674
27248
|
const recent = ledger.getRecentEntries(1);
|
|
@@ -26690,14 +27264,14 @@ function formatTrailers(trailers) {
|
|
|
26690
27264
|
}
|
|
26691
27265
|
function installPrepareCommitMsgHook(projectRoot) {
|
|
26692
27266
|
const hooksDir = join52(projectRoot, ".git", "hooks");
|
|
26693
|
-
if (!
|
|
27267
|
+
if (!existsSync48(hooksDir)) {
|
|
26694
27268
|
return false;
|
|
26695
27269
|
}
|
|
26696
27270
|
const hookPath = join52(hooksDir, "prepare-commit-msg");
|
|
26697
27271
|
const marker = "# unerr-trailer-injection";
|
|
26698
|
-
if (
|
|
27272
|
+
if (existsSync48(hookPath)) {
|
|
26699
27273
|
try {
|
|
26700
|
-
const existing =
|
|
27274
|
+
const existing = readFileSync45(hookPath, "utf-8");
|
|
26701
27275
|
if (existing.includes(marker)) {
|
|
26702
27276
|
return true;
|
|
26703
27277
|
}
|
|
@@ -26705,7 +27279,7 @@ function installPrepareCommitMsgHook(projectRoot) {
|
|
|
26705
27279
|
|
|
26706
27280
|
${marker}
|
|
26707
27281
|
${generateHookScript()}`;
|
|
26708
|
-
|
|
27282
|
+
writeFileSync27(hookPath, existing + appendSection, { mode: 493 });
|
|
26709
27283
|
_log5.info(
|
|
26710
27284
|
"Appended trailer injection to existing prepare-commit-msg hook"
|
|
26711
27285
|
);
|
|
@@ -26718,7 +27292,7 @@ ${generateHookScript()}`;
|
|
|
26718
27292
|
${marker}
|
|
26719
27293
|
${generateHookScript()}`;
|
|
26720
27294
|
try {
|
|
26721
|
-
|
|
27295
|
+
writeFileSync27(hookPath, hookContent, { mode: 493 });
|
|
26722
27296
|
_log5.info("Installed prepare-commit-msg hook for trailer injection");
|
|
26723
27297
|
return true;
|
|
26724
27298
|
} catch {
|
|
@@ -26727,9 +27301,9 @@ ${generateHookScript()}`;
|
|
|
26727
27301
|
}
|
|
26728
27302
|
function uninstallPrepareCommitMsgHook(projectRoot) {
|
|
26729
27303
|
const hookPath = join52(projectRoot, ".git", "hooks", "prepare-commit-msg");
|
|
26730
|
-
if (!
|
|
27304
|
+
if (!existsSync48(hookPath)) return true;
|
|
26731
27305
|
try {
|
|
26732
|
-
const content =
|
|
27306
|
+
const content = readFileSync45(hookPath, "utf-8");
|
|
26733
27307
|
const marker = "# unerr-trailer-injection";
|
|
26734
27308
|
if (!content.includes(marker)) return true;
|
|
26735
27309
|
const lines = content.split("\n");
|
|
@@ -26742,7 +27316,7 @@ function uninstallPrepareCommitMsgHook(projectRoot) {
|
|
|
26742
27316
|
} else {
|
|
26743
27317
|
const markerIdx = content.indexOf(marker);
|
|
26744
27318
|
if (markerIdx > 0) {
|
|
26745
|
-
|
|
27319
|
+
writeFileSync27(hookPath, `${content.slice(0, markerIdx).trimEnd()}
|
|
26746
27320
|
`, {
|
|
26747
27321
|
mode: 493
|
|
26748
27322
|
});
|
|
@@ -26894,13 +27468,13 @@ var init_http_transport = __esm({
|
|
|
26894
27468
|
|
|
26895
27469
|
// src/tracking/branch-snapshot.ts
|
|
26896
27470
|
import {
|
|
26897
|
-
existsSync as
|
|
27471
|
+
existsSync as existsSync49,
|
|
26898
27472
|
mkdirSync as mkdirSync28,
|
|
26899
|
-
readFileSync as
|
|
27473
|
+
readFileSync as readFileSync46,
|
|
26900
27474
|
readdirSync as readdirSync11,
|
|
26901
27475
|
rmSync as rmSync3,
|
|
26902
27476
|
statSync as statSync7,
|
|
26903
|
-
writeFileSync as
|
|
27477
|
+
writeFileSync as writeFileSync28
|
|
26904
27478
|
} from "fs";
|
|
26905
27479
|
import { join as join53 } from "path";
|
|
26906
27480
|
function sanitizeBranchName(branch) {
|
|
@@ -26935,7 +27509,7 @@ var init_branch_snapshot = __esm({
|
|
|
26935
27509
|
}
|
|
26936
27510
|
const dirName = sanitizeBranchName(branch);
|
|
26937
27511
|
const snapshotDir = join53(this.branchDir, dirName);
|
|
26938
|
-
if (!
|
|
27512
|
+
if (!existsSync49(snapshotDir)) {
|
|
26939
27513
|
mkdirSync28(snapshotDir, { recursive: true });
|
|
26940
27514
|
}
|
|
26941
27515
|
const snapshot = {
|
|
@@ -26945,12 +27519,12 @@ var init_branch_snapshot = __esm({
|
|
|
26945
27519
|
fileHashes: fileHashState,
|
|
26946
27520
|
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
26947
27521
|
};
|
|
26948
|
-
|
|
27522
|
+
writeFileSync28(
|
|
26949
27523
|
join53(snapshotDir, OVERLAY_FILE),
|
|
26950
27524
|
JSON.stringify(snapshot, null, 2),
|
|
26951
27525
|
"utf-8"
|
|
26952
27526
|
);
|
|
26953
|
-
|
|
27527
|
+
writeFileSync28(
|
|
26954
27528
|
join53(snapshotDir, HASHES_FILE),
|
|
26955
27529
|
JSON.stringify(fileHashState, null, 2),
|
|
26956
27530
|
"utf-8"
|
|
@@ -26969,12 +27543,12 @@ var init_branch_snapshot = __esm({
|
|
|
26969
27543
|
const dirName = sanitizeBranchName(branch);
|
|
26970
27544
|
const snapshotDir = join53(this.branchDir, dirName);
|
|
26971
27545
|
const overlayPath = join53(snapshotDir, OVERLAY_FILE);
|
|
26972
|
-
if (!
|
|
27546
|
+
if (!existsSync49(overlayPath)) {
|
|
26973
27547
|
log16.info(`No snapshot for branch ${branch} \u2014 first visit`);
|
|
26974
27548
|
return null;
|
|
26975
27549
|
}
|
|
26976
27550
|
try {
|
|
26977
|
-
const raw =
|
|
27551
|
+
const raw = readFileSync46(overlayPath, "utf-8");
|
|
26978
27552
|
const snapshot = JSON.parse(raw);
|
|
26979
27553
|
for (const entity of snapshot.entities) {
|
|
26980
27554
|
await localGraph.upsertDriftEntity(entity);
|
|
@@ -26985,7 +27559,7 @@ var init_branch_snapshot = __esm({
|
|
|
26985
27559
|
}
|
|
26986
27560
|
}
|
|
26987
27561
|
const now = /* @__PURE__ */ new Date();
|
|
26988
|
-
|
|
27562
|
+
writeFileSync28(
|
|
26989
27563
|
join53(snapshotDir, ".last_access"),
|
|
26990
27564
|
now.toISOString(),
|
|
26991
27565
|
"utf-8"
|
|
@@ -27006,7 +27580,7 @@ var init_branch_snapshot = __esm({
|
|
|
27006
27580
|
*/
|
|
27007
27581
|
hasSnapshot(branch) {
|
|
27008
27582
|
const dirName = sanitizeBranchName(branch);
|
|
27009
|
-
return
|
|
27583
|
+
return existsSync49(join53(this.branchDir, dirName, OVERLAY_FILE));
|
|
27010
27584
|
}
|
|
27011
27585
|
/**
|
|
27012
27586
|
* Get the file hash state from a branch snapshot.
|
|
@@ -27014,9 +27588,9 @@ var init_branch_snapshot = __esm({
|
|
|
27014
27588
|
getSnapshotFileHashes(branch) {
|
|
27015
27589
|
const dirName = sanitizeBranchName(branch);
|
|
27016
27590
|
const hashesPath = join53(this.branchDir, dirName, HASHES_FILE);
|
|
27017
|
-
if (!
|
|
27591
|
+
if (!existsSync49(hashesPath)) return null;
|
|
27018
27592
|
try {
|
|
27019
|
-
const raw =
|
|
27593
|
+
const raw = readFileSync46(hashesPath, "utf-8");
|
|
27020
27594
|
return JSON.parse(raw);
|
|
27021
27595
|
} catch {
|
|
27022
27596
|
return null;
|
|
@@ -27028,7 +27602,7 @@ var init_branch_snapshot = __esm({
|
|
|
27028
27602
|
deleteSnapshot(branch) {
|
|
27029
27603
|
const dirName = sanitizeBranchName(branch);
|
|
27030
27604
|
const snapshotDir = join53(this.branchDir, dirName);
|
|
27031
|
-
if (!
|
|
27605
|
+
if (!existsSync49(snapshotDir)) return false;
|
|
27032
27606
|
rmSync3(snapshotDir, { recursive: true, force: true });
|
|
27033
27607
|
log16.info(`Deleted branch snapshot: ${branch}`);
|
|
27034
27608
|
return true;
|
|
@@ -27037,7 +27611,7 @@ var init_branch_snapshot = __esm({
|
|
|
27037
27611
|
* Garbage-collect snapshots for branches that no longer exist in git.
|
|
27038
27612
|
*/
|
|
27039
27613
|
async garbageCollect() {
|
|
27040
|
-
if (!
|
|
27614
|
+
if (!existsSync49(this.branchDir)) return 0;
|
|
27041
27615
|
const gitBranches = new Set(await listBranches(this.projectRoot));
|
|
27042
27616
|
if (gitBranches.size === 0) return 0;
|
|
27043
27617
|
const snapshots = this.listSnapshots();
|
|
@@ -27056,20 +27630,20 @@ var init_branch_snapshot = __esm({
|
|
|
27056
27630
|
* List all branch snapshots, sorted by most recently accessed first.
|
|
27057
27631
|
*/
|
|
27058
27632
|
listSnapshots() {
|
|
27059
|
-
if (!
|
|
27633
|
+
if (!existsSync49(this.branchDir)) return [];
|
|
27060
27634
|
try {
|
|
27061
27635
|
const entries = readdirSync11(this.branchDir, { withFileTypes: true });
|
|
27062
27636
|
const snapshots = [];
|
|
27063
27637
|
for (const entry of entries) {
|
|
27064
27638
|
if (!entry.isDirectory()) continue;
|
|
27065
27639
|
const overlayPath = join53(this.branchDir, entry.name, OVERLAY_FILE);
|
|
27066
|
-
if (!
|
|
27640
|
+
if (!existsSync49(overlayPath)) continue;
|
|
27067
27641
|
try {
|
|
27068
|
-
const raw =
|
|
27642
|
+
const raw = readFileSync46(overlayPath, "utf-8");
|
|
27069
27643
|
const snapshot = JSON.parse(raw);
|
|
27070
27644
|
const accessPath = join53(this.branchDir, entry.name, ".last_access");
|
|
27071
27645
|
let accessedAt;
|
|
27072
|
-
if (
|
|
27646
|
+
if (existsSync49(accessPath)) {
|
|
27073
27647
|
accessedAt = statSync7(accessPath).mtime;
|
|
27074
27648
|
} else {
|
|
27075
27649
|
accessedAt = statSync7(overlayPath).mtime;
|
|
@@ -27113,11 +27687,11 @@ __export(file_hash_state_exports, {
|
|
|
27113
27687
|
});
|
|
27114
27688
|
import { createHash as createHash3 } from "crypto";
|
|
27115
27689
|
import {
|
|
27116
|
-
existsSync as
|
|
27690
|
+
existsSync as existsSync50,
|
|
27117
27691
|
mkdirSync as mkdirSync29,
|
|
27118
|
-
readFileSync as
|
|
27692
|
+
readFileSync as readFileSync47,
|
|
27119
27693
|
renameSync as renameSync2,
|
|
27120
|
-
writeFileSync as
|
|
27694
|
+
writeFileSync as writeFileSync29
|
|
27121
27695
|
} from "fs";
|
|
27122
27696
|
import { join as join54 } from "path";
|
|
27123
27697
|
function contentSha256(content) {
|
|
@@ -27179,11 +27753,11 @@ var init_file_hash_state = __esm({
|
|
|
27179
27753
|
* Persist state to disk atomically (write .tmp → rename).
|
|
27180
27754
|
*/
|
|
27181
27755
|
save() {
|
|
27182
|
-
if (!
|
|
27756
|
+
if (!existsSync50(this.stateDir)) {
|
|
27183
27757
|
mkdirSync29(this.stateDir, { recursive: true });
|
|
27184
27758
|
}
|
|
27185
27759
|
const tmpPath = `${this.statePath}.tmp`;
|
|
27186
|
-
|
|
27760
|
+
writeFileSync29(tmpPath, JSON.stringify(this.state, null, 2), "utf-8");
|
|
27187
27761
|
renameSync2(tmpPath, this.statePath);
|
|
27188
27762
|
}
|
|
27189
27763
|
/**
|
|
@@ -27206,11 +27780,11 @@ var init_file_hash_state = __esm({
|
|
|
27206
27780
|
this.state = { files: { ...snapshot.files } };
|
|
27207
27781
|
}
|
|
27208
27782
|
load() {
|
|
27209
|
-
if (!
|
|
27783
|
+
if (!existsSync50(this.statePath)) {
|
|
27210
27784
|
return { files: {} };
|
|
27211
27785
|
}
|
|
27212
27786
|
try {
|
|
27213
|
-
const raw =
|
|
27787
|
+
const raw = readFileSync47(this.statePath, "utf-8");
|
|
27214
27788
|
return JSON.parse(raw);
|
|
27215
27789
|
} catch {
|
|
27216
27790
|
return { files: {} };
|
|
@@ -27222,13 +27796,13 @@ var init_file_hash_state = __esm({
|
|
|
27222
27796
|
|
|
27223
27797
|
// src/tracking/stash-manager.ts
|
|
27224
27798
|
import {
|
|
27225
|
-
existsSync as
|
|
27799
|
+
existsSync as existsSync51,
|
|
27226
27800
|
mkdirSync as mkdirSync30,
|
|
27227
|
-
readFileSync as
|
|
27801
|
+
readFileSync as readFileSync48,
|
|
27228
27802
|
readdirSync as readdirSync12,
|
|
27229
27803
|
rmSync as rmSync4,
|
|
27230
27804
|
statSync as statSync8,
|
|
27231
|
-
writeFileSync as
|
|
27805
|
+
writeFileSync as writeFileSync30
|
|
27232
27806
|
} from "fs";
|
|
27233
27807
|
import { join as join55 } from "path";
|
|
27234
27808
|
var MAX_STASH_SNAPSHOTS, OVERLAY_FILE2, HASHES_FILE2, _log6, StashManager;
|
|
@@ -27295,7 +27869,7 @@ var init_stash_manager = __esm({
|
|
|
27295
27869
|
}
|
|
27296
27870
|
const snapshotId = stashRef.slice(0, 12);
|
|
27297
27871
|
const snapshotDir = join55(this.stashDir, snapshotId);
|
|
27298
|
-
if (!
|
|
27872
|
+
if (!existsSync51(snapshotDir)) {
|
|
27299
27873
|
mkdirSync30(snapshotDir, { recursive: true });
|
|
27300
27874
|
}
|
|
27301
27875
|
const snapshot = {
|
|
@@ -27305,12 +27879,12 @@ var init_stash_manager = __esm({
|
|
|
27305
27879
|
fileHashes: fileHashState,
|
|
27306
27880
|
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
27307
27881
|
};
|
|
27308
|
-
|
|
27882
|
+
writeFileSync30(
|
|
27309
27883
|
join55(snapshotDir, OVERLAY_FILE2),
|
|
27310
27884
|
JSON.stringify(snapshot, null, 2),
|
|
27311
27885
|
"utf-8"
|
|
27312
27886
|
);
|
|
27313
|
-
|
|
27887
|
+
writeFileSync30(
|
|
27314
27888
|
join55(snapshotDir, HASHES_FILE2),
|
|
27315
27889
|
JSON.stringify(fileHashState, null, 2),
|
|
27316
27890
|
"utf-8"
|
|
@@ -27334,12 +27908,12 @@ var init_stash_manager = __esm({
|
|
|
27334
27908
|
const latest = snapshots[0];
|
|
27335
27909
|
const snapshotDir = join55(this.stashDir, latest.id);
|
|
27336
27910
|
const overlayPath = join55(snapshotDir, OVERLAY_FILE2);
|
|
27337
|
-
if (!
|
|
27911
|
+
if (!existsSync51(overlayPath)) {
|
|
27338
27912
|
_log6.warn(`Snapshot ${latest.id} missing overlay file`);
|
|
27339
27913
|
return 0;
|
|
27340
27914
|
}
|
|
27341
27915
|
try {
|
|
27342
|
-
const raw =
|
|
27916
|
+
const raw = readFileSync48(overlayPath, "utf-8");
|
|
27343
27917
|
const snapshot = JSON.parse(raw);
|
|
27344
27918
|
for (const entity of snapshot.entities) {
|
|
27345
27919
|
await localGraph.upsertDriftEntity(entity);
|
|
@@ -27370,9 +27944,9 @@ var init_stash_manager = __esm({
|
|
|
27370
27944
|
if (snapshots.length === 0) return null;
|
|
27371
27945
|
const latest = snapshots[0];
|
|
27372
27946
|
const hashesPath = join55(this.stashDir, latest.id, HASHES_FILE2);
|
|
27373
|
-
if (!
|
|
27947
|
+
if (!existsSync51(hashesPath)) return null;
|
|
27374
27948
|
try {
|
|
27375
|
-
const raw =
|
|
27949
|
+
const raw = readFileSync48(hashesPath, "utf-8");
|
|
27376
27950
|
return JSON.parse(raw);
|
|
27377
27951
|
} catch {
|
|
27378
27952
|
return null;
|
|
@@ -27384,7 +27958,7 @@ var init_stash_manager = __esm({
|
|
|
27384
27958
|
dropSnapshot(stashRef) {
|
|
27385
27959
|
const snapshotId = stashRef.slice(0, 12);
|
|
27386
27960
|
const snapshotDir = join55(this.stashDir, snapshotId);
|
|
27387
|
-
if (!
|
|
27961
|
+
if (!existsSync51(snapshotDir)) return false;
|
|
27388
27962
|
rmSync4(snapshotDir, { recursive: true, force: true });
|
|
27389
27963
|
_log6.info(`Dropped stash snapshot: ${snapshotId}`);
|
|
27390
27964
|
return true;
|
|
@@ -27393,14 +27967,14 @@ var init_stash_manager = __esm({
|
|
|
27393
27967
|
* List all stash snapshots, sorted by most recent first.
|
|
27394
27968
|
*/
|
|
27395
27969
|
listSnapshots() {
|
|
27396
|
-
if (!
|
|
27970
|
+
if (!existsSync51(this.stashDir)) return [];
|
|
27397
27971
|
try {
|
|
27398
27972
|
const entries = readdirSync12(this.stashDir, { withFileTypes: true });
|
|
27399
27973
|
const snapshots = [];
|
|
27400
27974
|
for (const entry of entries) {
|
|
27401
27975
|
if (!entry.isDirectory()) continue;
|
|
27402
27976
|
const overlayPath = join55(this.stashDir, entry.name, OVERLAY_FILE2);
|
|
27403
|
-
if (!
|
|
27977
|
+
if (!existsSync51(overlayPath)) continue;
|
|
27404
27978
|
try {
|
|
27405
27979
|
const stat2 = statSync8(overlayPath);
|
|
27406
27980
|
snapshots.push({ id: entry.name, savedAt: stat2.mtime });
|
|
@@ -27418,9 +27992,9 @@ var init_stash_manager = __esm({
|
|
|
27418
27992
|
*/
|
|
27419
27993
|
readStashRef() {
|
|
27420
27994
|
const stashPath = join55(this.gitDir, "refs", "stash");
|
|
27421
|
-
if (!
|
|
27995
|
+
if (!existsSync51(stashPath)) return null;
|
|
27422
27996
|
try {
|
|
27423
|
-
return
|
|
27997
|
+
return readFileSync48(stashPath, "utf-8").trim() || null;
|
|
27424
27998
|
} catch {
|
|
27425
27999
|
return null;
|
|
27426
28000
|
}
|
|
@@ -27430,9 +28004,9 @@ var init_stash_manager = __esm({
|
|
|
27430
28004
|
*/
|
|
27431
28005
|
getStashCount() {
|
|
27432
28006
|
const logPath = join55(this.gitDir, "logs", "refs", "stash");
|
|
27433
|
-
if (!
|
|
28007
|
+
if (!existsSync51(logPath)) return 0;
|
|
27434
28008
|
try {
|
|
27435
|
-
const content =
|
|
28009
|
+
const content = readFileSync48(logPath, "utf-8");
|
|
27436
28010
|
return content.split("\n").filter((line) => line.trim().length > 0).length;
|
|
27437
28011
|
} catch {
|
|
27438
28012
|
return 0;
|
|
@@ -27464,11 +28038,11 @@ __export(drift_tracker_exports, {
|
|
|
27464
28038
|
determineOrigin: () => determineOrigin
|
|
27465
28039
|
});
|
|
27466
28040
|
import {
|
|
27467
|
-
existsSync as
|
|
28041
|
+
existsSync as existsSync52,
|
|
27468
28042
|
mkdirSync as mkdirSync31,
|
|
27469
|
-
readFileSync as
|
|
28043
|
+
readFileSync as readFileSync49,
|
|
27470
28044
|
statSync as statSync9,
|
|
27471
|
-
writeFileSync as
|
|
28045
|
+
writeFileSync as writeFileSync31
|
|
27472
28046
|
} from "fs";
|
|
27473
28047
|
import { join as join56 } from "path";
|
|
27474
28048
|
function determineOrigin(lastSyncTimestamp) {
|
|
@@ -27685,11 +28259,11 @@ var init_drift_tracker = __esm({
|
|
|
27685
28259
|
const relPath = filePath.startsWith("/") ? filePath.slice(this.config.projectRoot.length + 1) : filePath;
|
|
27686
28260
|
const language = detectLanguage2(relPath);
|
|
27687
28261
|
if (!language) return result;
|
|
27688
|
-
if (
|
|
28262
|
+
if (existsSync52(absPath) && !this.mtimeCache.check(absPath)) {
|
|
27689
28263
|
result.filesSkipped = 1;
|
|
27690
28264
|
return result;
|
|
27691
28265
|
}
|
|
27692
|
-
if (!
|
|
28266
|
+
if (!existsSync52(absPath)) {
|
|
27693
28267
|
const baseEntities2 = await this.localGraph.getEntitiesByFile(relPath);
|
|
27694
28268
|
this.markFileDeleted(relPath, intentId);
|
|
27695
28269
|
result.filesProcessed = 1;
|
|
@@ -27727,7 +28301,7 @@ var init_drift_tracker = __esm({
|
|
|
27727
28301
|
this.maybeEmitDrift(relPath, result);
|
|
27728
28302
|
return result;
|
|
27729
28303
|
}
|
|
27730
|
-
const content =
|
|
28304
|
+
const content = readFileSync49(absPath, "utf-8");
|
|
27731
28305
|
const sha = contentSha256(content);
|
|
27732
28306
|
const decision = this.fileHashManager.shouldProcess(relPath, sha, headSha);
|
|
27733
28307
|
if (decision === "skip") {
|
|
@@ -28047,11 +28621,11 @@ var init_drift_tracker = __esm({
|
|
|
28047
28621
|
}
|
|
28048
28622
|
async saveDriftSummary() {
|
|
28049
28623
|
const driftDir = join56(this.config.unerrDir, "drift");
|
|
28050
|
-
if (!
|
|
28624
|
+
if (!existsSync52(driftDir)) {
|
|
28051
28625
|
mkdirSync31(driftDir, { recursive: true });
|
|
28052
28626
|
}
|
|
28053
28627
|
const summary = await this.getDriftSummary();
|
|
28054
|
-
|
|
28628
|
+
writeFileSync31(
|
|
28055
28629
|
join56(driftDir, "drift_summary.json"),
|
|
28056
28630
|
JSON.stringify(summary, null, 2),
|
|
28057
28631
|
"utf-8"
|
|
@@ -28461,7 +29035,7 @@ var incremental_indexer_exports = {};
|
|
|
28461
29035
|
__export(incremental_indexer_exports, {
|
|
28462
29036
|
indexFilesIncremental: () => indexFilesIncremental
|
|
28463
29037
|
});
|
|
28464
|
-
import { existsSync as
|
|
29038
|
+
import { existsSync as existsSync53, readFileSync as readFileSync50 } from "fs";
|
|
28465
29039
|
import { join as join57, relative as relative4 } from "path";
|
|
28466
29040
|
async function indexFilesIncremental(projectRoot, changedFiles, graphStore, repoId) {
|
|
28467
29041
|
const startMs = Date.now();
|
|
@@ -28479,7 +29053,7 @@ async function indexFilesIncremental(projectRoot, changedFiles, graphStore, repo
|
|
|
28479
29053
|
for (const filePath of changedFiles) {
|
|
28480
29054
|
const absPath = filePath.startsWith("/") ? filePath : join57(projectRoot, filePath);
|
|
28481
29055
|
const relPath = filePath.startsWith("/") ? relative4(projectRoot, filePath) : filePath;
|
|
28482
|
-
if (!
|
|
29056
|
+
if (!existsSync53(absPath)) {
|
|
28483
29057
|
const deleted2 = await deleteFileFromGraph(db, relPath);
|
|
28484
29058
|
filesDeleted++;
|
|
28485
29059
|
totalEntitiesDeleted += deleted2.entitiesDeleted;
|
|
@@ -28492,7 +29066,7 @@ async function indexFilesIncremental(projectRoot, changedFiles, graphStore, repo
|
|
|
28492
29066
|
}
|
|
28493
29067
|
let content;
|
|
28494
29068
|
try {
|
|
28495
|
-
content =
|
|
29069
|
+
content = readFileSync50(absPath, "utf-8");
|
|
28496
29070
|
} catch {
|
|
28497
29071
|
continue;
|
|
28498
29072
|
}
|
|
@@ -29659,7 +30233,7 @@ var workspace_manifest_exports = {};
|
|
|
29659
30233
|
__export(workspace_manifest_exports, {
|
|
29660
30234
|
WorkspaceManifest: () => WorkspaceManifest
|
|
29661
30235
|
});
|
|
29662
|
-
import { existsSync as
|
|
30236
|
+
import { existsSync as existsSync54, mkdirSync as mkdirSync32, readFileSync as readFileSync51, writeFileSync as writeFileSync32 } from "fs";
|
|
29663
30237
|
import { join as join58 } from "path";
|
|
29664
30238
|
var WorkspaceManifest;
|
|
29665
30239
|
var init_workspace_manifest = __esm({
|
|
@@ -29765,7 +30339,7 @@ var init_workspace_manifest = __esm({
|
|
|
29765
30339
|
}
|
|
29766
30340
|
// ── Internal ─────────────────────────────────────────────────────
|
|
29767
30341
|
load() {
|
|
29768
|
-
if (!
|
|
30342
|
+
if (!existsSync54(this.manifestPath)) {
|
|
29769
30343
|
return {
|
|
29770
30344
|
version: 1,
|
|
29771
30345
|
repoId: this.repoId,
|
|
@@ -29775,7 +30349,7 @@ var init_workspace_manifest = __esm({
|
|
|
29775
30349
|
};
|
|
29776
30350
|
}
|
|
29777
30351
|
try {
|
|
29778
|
-
const raw =
|
|
30352
|
+
const raw = readFileSync51(this.manifestPath, "utf-8");
|
|
29779
30353
|
const parsed = JSON.parse(raw);
|
|
29780
30354
|
if (parsed.repoId !== this.repoId) {
|
|
29781
30355
|
return {
|
|
@@ -29798,10 +30372,10 @@ var init_workspace_manifest = __esm({
|
|
|
29798
30372
|
}
|
|
29799
30373
|
}
|
|
29800
30374
|
save() {
|
|
29801
|
-
if (!
|
|
30375
|
+
if (!existsSync54(this.unerrDir)) {
|
|
29802
30376
|
mkdirSync32(this.unerrDir, { recursive: true });
|
|
29803
30377
|
}
|
|
29804
|
-
|
|
30378
|
+
writeFileSync32(
|
|
29805
30379
|
this.manifestPath,
|
|
29806
30380
|
JSON.stringify(this.data, null, 2),
|
|
29807
30381
|
"utf-8"
|
|
@@ -29992,7 +30566,7 @@ var log_tailer_exports = {};
|
|
|
29992
30566
|
__export(log_tailer_exports, {
|
|
29993
30567
|
startLogTailer: () => startLogTailer
|
|
29994
30568
|
});
|
|
29995
|
-
import { existsSync as
|
|
30569
|
+
import { existsSync as existsSync55, statSync as statSync10, openSync, readSync, closeSync, watch } from "fs";
|
|
29996
30570
|
import { join as join59 } from "path";
|
|
29997
30571
|
function formatSize2(bytes) {
|
|
29998
30572
|
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
@@ -30107,22 +30681,22 @@ function startLogTailer(cwd, options) {
|
|
|
30107
30681
|
const fileReadsPath = join59(logsDir, "file-reads.jsonl");
|
|
30108
30682
|
const compressionState = {
|
|
30109
30683
|
path: compressionPath,
|
|
30110
|
-
offset:
|
|
30684
|
+
offset: existsSync55(compressionPath) ? statSync10(compressionPath).size : 0,
|
|
30111
30685
|
watcher: null
|
|
30112
30686
|
};
|
|
30113
30687
|
const generalState = {
|
|
30114
30688
|
path: generalPath,
|
|
30115
|
-
offset:
|
|
30689
|
+
offset: existsSync55(generalPath) ? statSync10(generalPath).size : 0,
|
|
30116
30690
|
watcher: null
|
|
30117
30691
|
};
|
|
30118
30692
|
const tokenFlowState = {
|
|
30119
30693
|
path: tokenFlowPath,
|
|
30120
|
-
offset:
|
|
30694
|
+
offset: existsSync55(tokenFlowPath) ? statSync10(tokenFlowPath).size : 0,
|
|
30121
30695
|
watcher: null
|
|
30122
30696
|
};
|
|
30123
30697
|
const fileReadsState = {
|
|
30124
30698
|
path: fileReadsPath,
|
|
30125
|
-
offset:
|
|
30699
|
+
offset: existsSync55(fileReadsPath) ? statSync10(fileReadsPath).size : 0,
|
|
30126
30700
|
watcher: null
|
|
30127
30701
|
};
|
|
30128
30702
|
function setupWatcher(state, handler) {
|
|
@@ -30150,7 +30724,7 @@ function startLogTailer(cwd, options) {
|
|
|
30150
30724
|
];
|
|
30151
30725
|
const pollInterval = setInterval(() => {
|
|
30152
30726
|
for (const { state, handler } of allStates) {
|
|
30153
|
-
if (!state.watcher &&
|
|
30727
|
+
if (!state.watcher && existsSync55(state.path)) {
|
|
30154
30728
|
try {
|
|
30155
30729
|
state.offset = 0;
|
|
30156
30730
|
state.watcher = watch(state.path, () => {
|
|
@@ -31484,13 +32058,13 @@ function createSystemRoutes(deps) {
|
|
|
31484
32058
|
});
|
|
31485
32059
|
app.get("/config", async (c) => {
|
|
31486
32060
|
const start = performance.now();
|
|
31487
|
-
const { existsSync:
|
|
32061
|
+
const { existsSync: existsSync64, readFileSync: readFileSync58 } = await import("fs");
|
|
31488
32062
|
const { join: join68 } = await import("path");
|
|
31489
32063
|
let config = {};
|
|
31490
32064
|
const configPath = join68(deps.cwd, ".unerr", "config.json");
|
|
31491
|
-
if (
|
|
32065
|
+
if (existsSync64(configPath)) {
|
|
31492
32066
|
try {
|
|
31493
|
-
config = JSON.parse(
|
|
32067
|
+
config = JSON.parse(readFileSync58(configPath, "utf-8"));
|
|
31494
32068
|
} catch {
|
|
31495
32069
|
config = { error: "unreadable" };
|
|
31496
32070
|
}
|
|
@@ -31691,20 +32265,20 @@ __export(session_history_exports, {
|
|
|
31691
32265
|
getWeeklyStats: () => getWeeklyStats,
|
|
31692
32266
|
readSessionHistory: () => readSessionHistory
|
|
31693
32267
|
});
|
|
31694
|
-
import { appendFileSync as appendFileSync8, existsSync as
|
|
32268
|
+
import { appendFileSync as appendFileSync8, existsSync as existsSync56, mkdirSync as mkdirSync33, readFileSync as readFileSync52 } from "fs";
|
|
31695
32269
|
import { join as join60 } from "path";
|
|
31696
32270
|
function appendSessionHistory(unerrDir, entry) {
|
|
31697
32271
|
const stateDir = join60(unerrDir, "state");
|
|
31698
|
-
if (!
|
|
32272
|
+
if (!existsSync56(stateDir)) mkdirSync33(stateDir, { recursive: true });
|
|
31699
32273
|
const historyPath = join60(stateDir, "session-history.jsonl");
|
|
31700
32274
|
appendFileSync8(historyPath, `${JSON.stringify(entry)}
|
|
31701
32275
|
`, "utf-8");
|
|
31702
32276
|
}
|
|
31703
32277
|
function readSessionHistory(unerrDir) {
|
|
31704
32278
|
const historyPath = join60(unerrDir, "state", "session-history.jsonl");
|
|
31705
|
-
if (!
|
|
32279
|
+
if (!existsSync56(historyPath)) return [];
|
|
31706
32280
|
try {
|
|
31707
|
-
const content =
|
|
32281
|
+
const content = readFileSync52(historyPath, "utf-8");
|
|
31708
32282
|
return content.split("\n").filter(Boolean).map((line) => {
|
|
31709
32283
|
try {
|
|
31710
32284
|
return JSON.parse(line);
|
|
@@ -32365,7 +32939,7 @@ var http_exports = {};
|
|
|
32365
32939
|
__export(http_exports, {
|
|
32366
32940
|
startDashboardServer: () => startDashboardServer
|
|
32367
32941
|
});
|
|
32368
|
-
import { existsSync as
|
|
32942
|
+
import { existsSync as existsSync57, readFileSync as readFileSync53, unlinkSync as unlinkSync8, writeFileSync as writeFileSync33 } from "fs";
|
|
32369
32943
|
import { createServer as createServer3 } from "net";
|
|
32370
32944
|
import { dirname as dirname7, join as join61 } from "path";
|
|
32371
32945
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -32415,8 +32989,8 @@ async function startDashboardServer(opts) {
|
|
|
32415
32989
|
}
|
|
32416
32990
|
const distDir = join61(dirname7(fileURLToPath2(import.meta.url)), "ui");
|
|
32417
32991
|
const spaIndex = join61(distDir, "index.html");
|
|
32418
|
-
if (
|
|
32419
|
-
const spaHtml =
|
|
32992
|
+
if (existsSync57(spaIndex)) {
|
|
32993
|
+
const spaHtml = readFileSync53(spaIndex, "utf-8");
|
|
32420
32994
|
app.use("*", serveStatic({ root: distDir }));
|
|
32421
32995
|
app.get("*", (c) => {
|
|
32422
32996
|
const path7 = c.req.path;
|
|
@@ -32453,7 +33027,7 @@ async function startDashboardServer(opts) {
|
|
|
32453
33027
|
url: `http://localhost:${port}`
|
|
32454
33028
|
};
|
|
32455
33029
|
const tmpPath = `${serverJsonPath}.tmp.${process.pid}`;
|
|
32456
|
-
|
|
33030
|
+
writeFileSync33(tmpPath, JSON.stringify(serverInfo, null, 2));
|
|
32457
33031
|
const { renameSync: renameSync3 } = await import("fs");
|
|
32458
33032
|
renameSync3(tmpPath, serverJsonPath);
|
|
32459
33033
|
const close = () => {
|
|
@@ -32847,7 +33421,7 @@ var proxy_exports = {};
|
|
|
32847
33421
|
__export(proxy_exports, {
|
|
32848
33422
|
startProxy: () => startProxy
|
|
32849
33423
|
});
|
|
32850
|
-
import { existsSync as
|
|
33424
|
+
import { existsSync as existsSync58, mkdirSync as mkdirSync34, readFileSync as readFileSync54, readdirSync as readdirSync13, writeFileSync as fsWriteFileSync } from "fs";
|
|
32851
33425
|
import { join as join62 } from "path";
|
|
32852
33426
|
async function getProxyFactStore(unerrDir) {
|
|
32853
33427
|
if (proxyFactStore !== void 0) return proxyFactStore;
|
|
@@ -32934,8 +33508,8 @@ async function handleRecallFactsProxy(args, unerrDir) {
|
|
|
32934
33508
|
function migrateAgentPermissions(cwd) {
|
|
32935
33509
|
try {
|
|
32936
33510
|
const settingsPath = join62(cwd, ".claude", "settings.json");
|
|
32937
|
-
if (!
|
|
32938
|
-
const raw =
|
|
33511
|
+
if (!existsSync58(settingsPath)) return;
|
|
33512
|
+
const raw = readFileSync54(settingsPath, "utf-8");
|
|
32939
33513
|
const settings = JSON.parse(raw);
|
|
32940
33514
|
const deny = settings?.permissions?.deny;
|
|
32941
33515
|
if (!Array.isArray(deny)) return;
|
|
@@ -32956,7 +33530,7 @@ async function startProxy(opts = {}) {
|
|
|
32956
33530
|
lifecycle.send({ type: "START_DETECT" });
|
|
32957
33531
|
startup.setLocalMode(true);
|
|
32958
33532
|
const stateDir = join62(process.cwd(), ".unerr", "state");
|
|
32959
|
-
if (!
|
|
33533
|
+
if (!existsSync58(stateDir)) {
|
|
32960
33534
|
mkdirSync34(stateDir, { recursive: true });
|
|
32961
33535
|
}
|
|
32962
33536
|
const pidLock = new PidLock(stateDir);
|
|
@@ -32990,15 +33564,15 @@ async function startProxy(opts = {}) {
|
|
|
32990
33564
|
repoIds = [opts.repoId];
|
|
32991
33565
|
} else {
|
|
32992
33566
|
const configPath = join62(process.cwd(), ".unerr", "config.json");
|
|
32993
|
-
if (
|
|
33567
|
+
if (existsSync58(configPath)) {
|
|
32994
33568
|
try {
|
|
32995
|
-
const config = JSON.parse(
|
|
33569
|
+
const config = JSON.parse(readFileSync54(configPath, "utf-8"));
|
|
32996
33570
|
if (config.repoId) repoIds = [config.repoId];
|
|
32997
33571
|
} catch {
|
|
32998
33572
|
}
|
|
32999
33573
|
}
|
|
33000
33574
|
const manifestsDir = join62(process.cwd(), ".unerr", "manifests");
|
|
33001
|
-
if (repoIds.length === 0 &&
|
|
33575
|
+
if (repoIds.length === 0 && existsSync58(manifestsDir)) {
|
|
33002
33576
|
repoIds = readdirSync13(manifestsDir).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
|
|
33003
33577
|
}
|
|
33004
33578
|
}
|
|
@@ -33432,8 +34006,8 @@ async function startProxy(opts = {}) {
|
|
|
33432
34006
|
);
|
|
33433
34007
|
process.env.UNERR_SESSION_ID = shadowLedger2.getSessionId();
|
|
33434
34008
|
try {
|
|
33435
|
-
const { writeFileSync:
|
|
33436
|
-
|
|
34009
|
+
const { writeFileSync: writeFileSync37 } = await import("fs");
|
|
34010
|
+
writeFileSync37(join62(unerrDirForLedger, "state", "session.id"), shadowLedger2.getSessionId(), "utf-8");
|
|
33437
34011
|
} catch {
|
|
33438
34012
|
}
|
|
33439
34013
|
router2.setTokenFlow(tokenFlowWriter2);
|
|
@@ -34263,7 +34837,7 @@ async function startProxy(opts = {}) {
|
|
|
34263
34837
|
});
|
|
34264
34838
|
for (const file of sourceFiles.slice(0, 500)) {
|
|
34265
34839
|
try {
|
|
34266
|
-
const content =
|
|
34840
|
+
const content = readFileSync54(join62(process.cwd(), file), "utf-8");
|
|
34267
34841
|
const entities = extractEntitiesFromSource2(file, content);
|
|
34268
34842
|
parseIndex.addEntities(entities);
|
|
34269
34843
|
} catch {
|
|
@@ -34425,7 +34999,7 @@ async function startProxy(opts = {}) {
|
|
|
34425
34999
|
temporal: await (async () => {
|
|
34426
35000
|
try {
|
|
34427
35001
|
const { TemporalFactStore: TemporalFactStore2 } = await Promise.resolve().then(() => (init_temporal_facts(), temporal_facts_exports));
|
|
34428
|
-
const { readdirSync: readdirSync15, readFileSync:
|
|
35002
|
+
const { readdirSync: readdirSync15, readFileSync: readFileSync58 } = await import("fs");
|
|
34429
35003
|
const factStore2 = await TemporalFactStore2.create(
|
|
34430
35004
|
process.cwd()
|
|
34431
35005
|
);
|
|
@@ -34436,7 +35010,7 @@ async function startProxy(opts = {}) {
|
|
|
34436
35010
|
const sessDir = join62(unerrDirForApi, "sessions");
|
|
34437
35011
|
const files = readdirSync15(sessDir).filter((f) => f.endsWith(".jsonl")).sort().slice(-limit);
|
|
34438
35012
|
return files.map((f) => {
|
|
34439
|
-
const content =
|
|
35013
|
+
const content = readFileSync58(join62(sessDir, f), "utf-8").trim().split("\n").pop();
|
|
34440
35014
|
return JSON.parse(content);
|
|
34441
35015
|
});
|
|
34442
35016
|
} catch {
|
|
@@ -34858,7 +35432,7 @@ __export(session_logger_exports, {
|
|
|
34858
35432
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
34859
35433
|
import {
|
|
34860
35434
|
appendFileSync as appendFileSync9,
|
|
34861
|
-
existsSync as
|
|
35435
|
+
existsSync as existsSync59,
|
|
34862
35436
|
mkdirSync as mkdirSync35,
|
|
34863
35437
|
readdirSync as readdirSync14,
|
|
34864
35438
|
statSync as statSync11,
|
|
@@ -34872,7 +35446,7 @@ function formatTimestamp() {
|
|
|
34872
35446
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
|
|
34873
35447
|
}
|
|
34874
35448
|
function cleanupOldLogs(logsDir) {
|
|
34875
|
-
if (!
|
|
35449
|
+
if (!existsSync59(logsDir)) return;
|
|
34876
35450
|
try {
|
|
34877
35451
|
const files = readdirSync14(logsDir).filter((f) => f.startsWith("session-") && f.endsWith(".log")).map((f) => {
|
|
34878
35452
|
const fullPath = join63(logsDir, f);
|
|
@@ -34892,7 +35466,7 @@ function cleanupOldLogs(logsDir) {
|
|
|
34892
35466
|
}
|
|
34893
35467
|
}
|
|
34894
35468
|
}
|
|
34895
|
-
const remaining = files.filter((f) =>
|
|
35469
|
+
const remaining = files.filter((f) => existsSync59(f.path));
|
|
34896
35470
|
if (remaining.length > MAX_FILES) {
|
|
34897
35471
|
for (const file of remaining.slice(MAX_FILES)) {
|
|
34898
35472
|
try {
|
|
@@ -34985,7 +35559,7 @@ __export(setup_wizard_exports, {
|
|
|
34985
35559
|
runSetup: () => runSetup
|
|
34986
35560
|
});
|
|
34987
35561
|
import { createHash as createHash4 } from "crypto";
|
|
34988
|
-
import { existsSync as
|
|
35562
|
+
import { existsSync as existsSync60, mkdirSync as mkdirSync36, writeFileSync as writeFileSync34 } from "fs";
|
|
34989
35563
|
import { join as join64 } from "path";
|
|
34990
35564
|
import * as clack from "@clack/prompts";
|
|
34991
35565
|
async function runSetup(cwd) {
|
|
@@ -34997,10 +35571,10 @@ async function runSetup(cwd) {
|
|
|
34997
35571
|
mkdirSync36(configDir, { recursive: true });
|
|
34998
35572
|
const configPath = join64(configDir, "config.json");
|
|
34999
35573
|
const settingsPath = join64(configDir, "settings.json");
|
|
35000
|
-
|
|
35574
|
+
writeFileSync34(configPath, `${JSON.stringify({ repoId }, null, 2)}
|
|
35001
35575
|
`);
|
|
35002
35576
|
let existingSettings = {};
|
|
35003
|
-
if (
|
|
35577
|
+
if (existsSync60(settingsPath)) {
|
|
35004
35578
|
try {
|
|
35005
35579
|
existingSettings = JSON.parse(
|
|
35006
35580
|
__require("fs").readFileSync(settingsPath, "utf-8")
|
|
@@ -35008,7 +35582,7 @@ async function runSetup(cwd) {
|
|
|
35008
35582
|
} catch {
|
|
35009
35583
|
}
|
|
35010
35584
|
}
|
|
35011
|
-
|
|
35585
|
+
writeFileSync34(
|
|
35012
35586
|
settingsPath,
|
|
35013
35587
|
`${JSON.stringify({ ...existingSettings }, null, 2)}
|
|
35014
35588
|
`
|
|
@@ -35609,13 +36183,13 @@ __export(session_summary_writer_exports, {
|
|
|
35609
36183
|
readLastSession: () => readLastSession,
|
|
35610
36184
|
writeSessionSummary: () => writeSessionSummary
|
|
35611
36185
|
});
|
|
35612
|
-
import { existsSync as
|
|
36186
|
+
import { existsSync as existsSync61, mkdirSync as mkdirSync37, appendFileSync as appendFileSync10, writeFileSync as writeFileSync35, readFileSync as readFileSync55 } from "fs";
|
|
35613
36187
|
import { join as join65 } from "path";
|
|
35614
36188
|
function writeSessionSummary(unerrDir, ctx) {
|
|
35615
36189
|
if (ctx.entries.length === 0) return null;
|
|
35616
36190
|
try {
|
|
35617
36191
|
const sessionsDir = join65(unerrDir, "sessions");
|
|
35618
|
-
if (!
|
|
36192
|
+
if (!existsSync61(sessionsDir)) {
|
|
35619
36193
|
mkdirSync37(sessionsDir, { recursive: true });
|
|
35620
36194
|
}
|
|
35621
36195
|
const sorted = [...ctx.entries].sort(
|
|
@@ -35675,8 +36249,8 @@ function writeSessionSummary(unerrDir, ctx) {
|
|
|
35675
36249
|
function readLastSession(unerrDir) {
|
|
35676
36250
|
try {
|
|
35677
36251
|
const pointerPath = join65(unerrDir, "state", "last_session.json");
|
|
35678
|
-
if (!
|
|
35679
|
-
const content =
|
|
36252
|
+
if (!existsSync61(pointerPath)) return null;
|
|
36253
|
+
const content = readFileSync55(pointerPath, "utf-8");
|
|
35680
36254
|
return JSON.parse(content);
|
|
35681
36255
|
} catch {
|
|
35682
36256
|
return null;
|
|
@@ -35685,11 +36259,11 @@ function readLastSession(unerrDir) {
|
|
|
35685
36259
|
function writeLastSessionPointer(unerrDir, sessionId, record) {
|
|
35686
36260
|
try {
|
|
35687
36261
|
const stateDir = join65(unerrDir, "state");
|
|
35688
|
-
if (!
|
|
36262
|
+
if (!existsSync61(stateDir)) {
|
|
35689
36263
|
mkdirSync37(stateDir, { recursive: true });
|
|
35690
36264
|
}
|
|
35691
36265
|
const pointerPath = join65(stateDir, "last_session.json");
|
|
35692
|
-
|
|
36266
|
+
writeFileSync35(pointerPath, JSON.stringify(record, null, 2), "utf-8");
|
|
35693
36267
|
} catch {
|
|
35694
36268
|
}
|
|
35695
36269
|
}
|
|
@@ -35886,20 +36460,20 @@ var mcp_server_exports = {};
|
|
|
35886
36460
|
__export(mcp_server_exports, {
|
|
35887
36461
|
startMcpServer: () => startMcpServer
|
|
35888
36462
|
});
|
|
35889
|
-
import { existsSync as
|
|
36463
|
+
import { existsSync as existsSync62, mkdirSync as mkdirSync38, readFileSync as readFileSync56, writeFileSync as writeFileSync36 } from "fs";
|
|
35890
36464
|
import { join as join66 } from "path";
|
|
35891
36465
|
function migrateAgentPermissions2(cwd) {
|
|
35892
36466
|
try {
|
|
35893
36467
|
const settingsPath = join66(cwd, ".claude", "settings.json");
|
|
35894
|
-
if (!
|
|
35895
|
-
const raw =
|
|
36468
|
+
if (!existsSync62(settingsPath)) return;
|
|
36469
|
+
const raw = readFileSync56(settingsPath, "utf-8");
|
|
35896
36470
|
const settings = JSON.parse(raw);
|
|
35897
36471
|
const deny = settings?.permissions?.deny;
|
|
35898
36472
|
if (!Array.isArray(deny)) return;
|
|
35899
36473
|
const readIdx = deny.indexOf("Read");
|
|
35900
36474
|
if (readIdx < 0) return;
|
|
35901
36475
|
deny.splice(readIdx, 1);
|
|
35902
|
-
|
|
36476
|
+
writeFileSync36(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
35903
36477
|
process.stderr.write("[unerr] Migrated permissions: removed Read from deny list (required for Edit workflow)\n");
|
|
35904
36478
|
} catch {
|
|
35905
36479
|
}
|
|
@@ -35909,7 +36483,7 @@ async function startMcpServer(cwd) {
|
|
|
35909
36483
|
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
35910
36484
|
const { ListToolsRequestSchema, CallToolRequestSchema } = await import("@modelcontextprotocol/sdk/types.js");
|
|
35911
36485
|
const unerrDir = join66(cwd, ".unerr");
|
|
35912
|
-
if (!
|
|
36486
|
+
if (!existsSync62(unerrDir)) {
|
|
35913
36487
|
mkdirSync38(unerrDir, { recursive: true });
|
|
35914
36488
|
}
|
|
35915
36489
|
migrateAgentPermissions2(cwd);
|
|
@@ -36075,22 +36649,28 @@ async function startMcpServer(cwd) {
|
|
|
36075
36649
|
const filePath = args.file_path ?? args.path ?? args.key?.split("::")[0] ?? null;
|
|
36076
36650
|
if (filePath) {
|
|
36077
36651
|
try {
|
|
36078
|
-
|
|
36079
|
-
|
|
36080
|
-
|
|
36081
|
-
|
|
36082
|
-
|
|
36083
|
-
|
|
36084
|
-
|
|
36085
|
-
|
|
36086
|
-
|
|
36087
|
-
|
|
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
|
+
}
|
|
36088
36668
|
}
|
|
36089
|
-
|
|
36090
|
-
|
|
36091
|
-
|
|
36092
|
-
|
|
36093
|
-
|
|
36669
|
+
for (const f of negativeFacts) {
|
|
36670
|
+
if (!seen.has(f.fact_id)) {
|
|
36671
|
+
seen.add(f.fact_id);
|
|
36672
|
+
merged.push(f);
|
|
36673
|
+
}
|
|
36094
36674
|
}
|
|
36095
36675
|
}
|
|
36096
36676
|
if (merged.length > 0) {
|
|
@@ -36317,7 +36897,7 @@ async function initTier2Modules(unerrDir) {
|
|
|
36317
36897
|
try {
|
|
36318
36898
|
const stateDir = join66(unerrDir, "state");
|
|
36319
36899
|
mkdirSync38(stateDir, { recursive: true });
|
|
36320
|
-
|
|
36900
|
+
writeFileSync36(join66(stateDir, "session.id"), sessionId, "utf-8");
|
|
36321
36901
|
} catch {
|
|
36322
36902
|
}
|
|
36323
36903
|
log24.info(`Token flow active (session ${sessionId.slice(0, 8)})`);
|
|
@@ -36459,7 +37039,7 @@ async function loadIntelligence(cwd) {
|
|
|
36459
37039
|
const projectRoot = cwd;
|
|
36460
37040
|
try {
|
|
36461
37041
|
const unerrDir = join66(projectRoot, ".unerr");
|
|
36462
|
-
if (!
|
|
37042
|
+
if (!existsSync62(unerrDir)) {
|
|
36463
37043
|
mkdirSync38(unerrDir, { recursive: true });
|
|
36464
37044
|
}
|
|
36465
37045
|
const { openPersistentDb: openPersistentDb2 } = await Promise.resolve().then(() => (init_persistent_db(), persistent_db_exports));
|
|
@@ -36480,9 +37060,9 @@ async function loadIntelligence(cwd) {
|
|
|
36480
37060
|
await runCommunityDetection2(localGraph);
|
|
36481
37061
|
const configPath = join66(projectRoot, ".unerr", "config.json");
|
|
36482
37062
|
let repoId = "unknown";
|
|
36483
|
-
if (
|
|
37063
|
+
if (existsSync62(configPath)) {
|
|
36484
37064
|
try {
|
|
36485
|
-
const config = JSON.parse(
|
|
37065
|
+
const config = JSON.parse(readFileSync56(configPath, "utf-8"));
|
|
36486
37066
|
repoId = config.repoId ?? repoId;
|
|
36487
37067
|
} catch {
|
|
36488
37068
|
}
|
|
@@ -36511,9 +37091,9 @@ async function loadIntelligence(cwd) {
|
|
|
36511
37091
|
try {
|
|
36512
37092
|
const configPath = join66(projectRoot, ".unerr", "config.json");
|
|
36513
37093
|
let indexRepoId = "unknown";
|
|
36514
|
-
if (
|
|
37094
|
+
if (existsSync62(configPath)) {
|
|
36515
37095
|
try {
|
|
36516
|
-
const cfg = JSON.parse(
|
|
37096
|
+
const cfg = JSON.parse(readFileSync56(configPath, "utf-8"));
|
|
36517
37097
|
indexRepoId = cfg.repoId ?? indexRepoId;
|
|
36518
37098
|
} catch {
|
|
36519
37099
|
}
|
|
@@ -36623,7 +37203,7 @@ var init_mcp_server = __esm({
|
|
|
36623
37203
|
|
|
36624
37204
|
// src/entrypoints/cli.ts
|
|
36625
37205
|
init_startup_log();
|
|
36626
|
-
import { existsSync as
|
|
37206
|
+
import { existsSync as existsSync63, readFileSync as readFileSync57 } from "fs";
|
|
36627
37207
|
import { join as join67 } from "path";
|
|
36628
37208
|
import { Command } from "commander";
|
|
36629
37209
|
|
|
@@ -38628,19 +39208,19 @@ async function buildShellDiffRiskMap(graph, stdout) {
|
|
|
38628
39208
|
}
|
|
38629
39209
|
async function tryLoadGraphForShellBoost(cwd) {
|
|
38630
39210
|
try {
|
|
38631
|
-
const { existsSync:
|
|
39211
|
+
const { existsSync: existsSync64, readFileSync: readFileSync58 } = await import("fs");
|
|
38632
39212
|
const { join: join68 } = await import("path");
|
|
38633
39213
|
const { gunzipSync: gunzipSync3 } = await import("zlib");
|
|
38634
39214
|
const { unpack } = await import("msgpackr");
|
|
38635
39215
|
const snapshotsDir = join68(cwd, ".unerr", "snapshots");
|
|
38636
39216
|
let snapshotPath2 = join68(snapshotsDir, "graph.msgpack.gz");
|
|
38637
|
-
if (!
|
|
39217
|
+
if (!existsSync64(snapshotPath2)) {
|
|
38638
39218
|
snapshotPath2 = join68(snapshotsDir, "graph.msgpack");
|
|
38639
39219
|
}
|
|
38640
|
-
if (!
|
|
39220
|
+
if (!existsSync64(snapshotPath2)) return null;
|
|
38641
39221
|
const { default: CozoDbConstructor } = await import("cozo-node");
|
|
38642
39222
|
const { CozoGraphStore: CozoGraphStore2 } = await Promise.resolve().then(() => (init_local_graph(), local_graph_exports));
|
|
38643
|
-
const raw =
|
|
39223
|
+
const raw = readFileSync58(snapshotPath2);
|
|
38644
39224
|
let buffer;
|
|
38645
39225
|
try {
|
|
38646
39226
|
buffer = gunzipSync3(raw);
|
|
@@ -41832,6 +42412,7 @@ function runPreToolUseHook(stdinJson, handler) {
|
|
|
41832
42412
|
if (!payload) return "{}";
|
|
41833
42413
|
const adapter = detectAdapter(payload);
|
|
41834
42414
|
const normalized = adapter.normalize(payload);
|
|
42415
|
+
normalized.agentName = adapter.name;
|
|
41835
42416
|
const result = handler(normalized);
|
|
41836
42417
|
return adapter.formatPreToolUse(result);
|
|
41837
42418
|
}
|
|
@@ -41840,6 +42421,7 @@ function runPostToolUseHook(stdinJson, handler) {
|
|
|
41840
42421
|
if (!payload) return "{}";
|
|
41841
42422
|
const adapter = detectAdapter(payload);
|
|
41842
42423
|
const normalized = adapter.normalize(payload);
|
|
42424
|
+
normalized.agentName = adapter.name;
|
|
41843
42425
|
const result = handler(normalized);
|
|
41844
42426
|
return adapter.formatPostToolUse(result);
|
|
41845
42427
|
}
|
|
@@ -41848,6 +42430,7 @@ function runPromptSubmitHook(stdinJson, handler) {
|
|
|
41848
42430
|
if (!payload) return "{}";
|
|
41849
42431
|
const adapter = detectAdapter(payload);
|
|
41850
42432
|
const normalized = adapter.normalize(payload);
|
|
42433
|
+
normalized.agentName = adapter.name;
|
|
41851
42434
|
const result = handler(normalized);
|
|
41852
42435
|
return adapter.formatPromptSubmit(result);
|
|
41853
42436
|
}
|
|
@@ -41876,17 +42459,27 @@ var preReadHandler = (normalized) => {
|
|
|
41876
42459
|
const input = normalized.toolInput;
|
|
41877
42460
|
const filePath = extractFilePath2(input);
|
|
41878
42461
|
if (!filePath) return passthrough();
|
|
42462
|
+
const isClaudeCode = normalized.agentName === "claude-code";
|
|
41879
42463
|
const hasOffset = input.offset !== void 0;
|
|
41880
42464
|
const hasLimit = input.limit !== void 0;
|
|
41881
42465
|
const isTargeted = hasOffset || hasLimit;
|
|
41882
|
-
|
|
41883
|
-
|
|
41884
|
-
|
|
41885
|
-
|
|
41886
|
-
|
|
41887
|
-
|
|
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}" })\`
|
|
41888
42474
|
- File structure: \`file_outline("${filePath}")\`
|
|
41889
|
-
- 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`
|
|
41890
42483
|
);
|
|
41891
42484
|
};
|
|
41892
42485
|
var preGrepHandler = (normalized) => {
|
|
@@ -41938,9 +42531,10 @@ var preEditHandler = (normalized) => {
|
|
|
41938
42531
|
const input = normalized.toolInput;
|
|
41939
42532
|
const filePath = extractFilePath2(input);
|
|
41940
42533
|
if (!filePath || !isCodeFile(filePath)) return passthrough();
|
|
41941
|
-
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.
|
|
41942
42536
|
|
|
41943
|
-
|
|
42537
|
+
` : "";
|
|
41944
42538
|
const oldStr = input.old_string;
|
|
41945
42539
|
const hasSignatureChange = oldStr && /^(export\s+)?(async\s+)?function\s+\w+|^(export\s+)?class\s+\w+/.test(oldStr.trim());
|
|
41946
42540
|
if (hasSignatureChange) {
|
|
@@ -41960,9 +42554,16 @@ var postReadHandler = (normalized) => {
|
|
|
41960
42554
|
const input = normalized.toolInput;
|
|
41961
42555
|
const filePath = extractFilePath2(input);
|
|
41962
42556
|
if (!filePath || !isCodeFile(filePath)) return passthrough();
|
|
41963
|
-
|
|
41964
|
-
|
|
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).
|
|
41965
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.
|
|
41966
42567
|
Next steps: \`get_references\` to check callers before modifying, \`get_rules\` to validate conventions.`
|
|
41967
42568
|
);
|
|
41968
42569
|
};
|
|
@@ -42085,56 +42686,32 @@ function runPreBashHook(stdinJson) {
|
|
|
42085
42686
|
}
|
|
42086
42687
|
|
|
42087
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
|
+
}
|
|
42088
42701
|
function registerHookCommand(program2) {
|
|
42089
42702
|
const hook = program2.command("hook").description("IDE hook handlers (stdin/stdout JSON)");
|
|
42090
|
-
hook.command("pre-bash").description("Rewrite Bash tool input to pipe through unerr exec").action(()
|
|
42091
|
-
|
|
42092
|
-
|
|
42093
|
-
|
|
42094
|
-
hook.command("pre-
|
|
42095
|
-
|
|
42096
|
-
|
|
42097
|
-
|
|
42098
|
-
hook.command("
|
|
42099
|
-
|
|
42100
|
-
|
|
42101
|
-
|
|
42102
|
-
hook.command("pre-glob").description("Nudge agent to use search_code instead of Glob").action(() => {
|
|
42103
|
-
const stdin = readFileSync15(0, "utf-8");
|
|
42104
|
-
process.stdout.write(runPreGlobHook(stdin));
|
|
42105
|
-
});
|
|
42106
|
-
hook.command("pre-write").description("Blast radius + convention check before Write").action(() => {
|
|
42107
|
-
const stdin = readFileSync15(0, "utf-8");
|
|
42108
|
-
process.stdout.write(runPreWriteHook(stdin));
|
|
42109
|
-
});
|
|
42110
|
-
hook.command("pre-edit").description("Blast radius + convention check before Edit").action(() => {
|
|
42111
|
-
const stdin = readFileSync15(0, "utf-8");
|
|
42112
|
-
process.stdout.write(runPreEditHook(stdin));
|
|
42113
|
-
});
|
|
42114
|
-
hook.command("post-read").description("Enrich Read output with graph navigation suggestions").action(() => {
|
|
42115
|
-
const stdin = readFileSync15(0, "utf-8");
|
|
42116
|
-
process.stdout.write(runPostReadHook(stdin));
|
|
42117
|
-
});
|
|
42118
|
-
hook.command("post-grep").description("Enrich Grep output with graph navigation suggestions").action(() => {
|
|
42119
|
-
const stdin = readFileSync15(0, "utf-8");
|
|
42120
|
-
process.stdout.write(runPostGrepHook(stdin));
|
|
42121
|
-
});
|
|
42122
|
-
hook.command("post-glob").description("Enrich Glob output with graph navigation suggestions").action(() => {
|
|
42123
|
-
const stdin = readFileSync15(0, "utf-8");
|
|
42124
|
-
process.stdout.write(runPostGlobHook(stdin));
|
|
42125
|
-
});
|
|
42126
|
-
hook.command("post-write").description("Post-write convention check + caller verification reminder").action(() => {
|
|
42127
|
-
const stdin = readFileSync15(0, "utf-8");
|
|
42128
|
-
process.stdout.write(runPostWriteHook(stdin));
|
|
42129
|
-
});
|
|
42130
|
-
hook.command("post-edit").description("Post-edit convention check + caller verification reminder").action(() => {
|
|
42131
|
-
const stdin = readFileSync15(0, "utf-8");
|
|
42132
|
-
process.stdout.write(runPostEditHook(stdin));
|
|
42133
|
-
});
|
|
42134
|
-
hook.command("prompt-submit").description("Inject unerr tool reminder on each user prompt").action(() => {
|
|
42135
|
-
const stdin = readFileSync15(0, "utf-8");
|
|
42136
|
-
process.stdout.write(runUserPromptSubmitHook(stdin));
|
|
42137
|
-
});
|
|
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));
|
|
42138
42715
|
}
|
|
42139
42716
|
|
|
42140
42717
|
// src/commands/index.ts
|
|
@@ -42304,89 +42881,108 @@ function runInit(cwd) {
|
|
|
42304
42881
|
|
|
42305
42882
|
// src/commands/install.ts
|
|
42306
42883
|
init_agent_registry();
|
|
42307
|
-
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";
|
|
42308
42885
|
import { join as join30 } from "path";
|
|
42309
42886
|
|
|
42310
42887
|
// src/config/claude-settings-hooks.ts
|
|
42311
|
-
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";
|
|
42312
42890
|
import { join as join27 } from "path";
|
|
42313
|
-
|
|
42314
|
-
|
|
42315
|
-
|
|
42316
|
-
|
|
42317
|
-
|
|
42318
|
-
{
|
|
42319
|
-
|
|
42320
|
-
|
|
42321
|
-
|
|
42322
|
-
|
|
42323
|
-
|
|
42324
|
-
|
|
42325
|
-
|
|
42326
|
-
|
|
42327
|
-
|
|
42328
|
-
|
|
42329
|
-
|
|
42330
|
-
|
|
42331
|
-
|
|
42332
|
-
|
|
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) {
|
|
42333
42938
|
if (!entry || typeof entry !== "object") return false;
|
|
42334
42939
|
const e = entry;
|
|
42335
|
-
if (matcherName !== void 0 && e.matcher !== matcherName) return false;
|
|
42336
42940
|
const hs = e.hooks;
|
|
42337
42941
|
if (!Array.isArray(hs)) return false;
|
|
42338
42942
|
return hs.some((h) => {
|
|
42339
42943
|
if (!h || typeof h !== "object") return false;
|
|
42340
|
-
const
|
|
42341
|
-
return
|
|
42944
|
+
const cmd = h.command;
|
|
42945
|
+
return typeof cmd === "string" && /(?:^|\/)unerr\s+hook\s+/.test(cmd);
|
|
42342
42946
|
});
|
|
42343
42947
|
}
|
|
42344
|
-
function isAnyUnerrHook(entry) {
|
|
42345
|
-
for (const hookDef of UNERR_MATCHER_HOOKS) {
|
|
42346
|
-
if (isUnerrHookEntry(entry, hookDef.command, hookDef.matcher)) return true;
|
|
42347
|
-
}
|
|
42348
|
-
for (const hookDef of UNERR_GLOBAL_HOOKS) {
|
|
42349
|
-
if (isUnerrHookEntry(entry, hookDef.command)) return true;
|
|
42350
|
-
}
|
|
42351
|
-
return false;
|
|
42352
|
-
}
|
|
42353
42948
|
function mergePreToolUseBashHook(cwd) {
|
|
42354
42949
|
const dir = join27(cwd, ".claude");
|
|
42355
42950
|
const settingsPath = join27(dir, "settings.json");
|
|
42356
42951
|
try {
|
|
42357
|
-
if (!
|
|
42952
|
+
if (!existsSync21(dir)) {
|
|
42358
42953
|
mkdirSync14(dir, { recursive: true });
|
|
42359
42954
|
}
|
|
42360
42955
|
let settings = {};
|
|
42361
|
-
if (
|
|
42956
|
+
if (existsSync21(settingsPath)) {
|
|
42362
42957
|
try {
|
|
42363
|
-
settings = JSON.parse(
|
|
42958
|
+
settings = JSON.parse(readFileSync20(settingsPath, "utf-8"));
|
|
42364
42959
|
} catch {
|
|
42365
42960
|
settings = {};
|
|
42366
42961
|
}
|
|
42367
42962
|
}
|
|
42368
42963
|
const hooks = settings.hooks ?? {};
|
|
42369
42964
|
let added = 0;
|
|
42370
|
-
|
|
42371
|
-
|
|
42372
|
-
|
|
42373
|
-
|
|
42374
|
-
|
|
42375
|
-
|
|
42376
|
-
|
|
42377
|
-
matcher: hookDef.matcher,
|
|
42378
|
-
hooks: [{ type: "command", command: hookDef.command }]
|
|
42379
|
-
});
|
|
42380
|
-
hooks[hookDef.event] = eventArray;
|
|
42381
|
-
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
|
+
);
|
|
42382
42972
|
}
|
|
42383
42973
|
}
|
|
42384
|
-
for (const hookDef of
|
|
42974
|
+
for (const hookDef of matcherHooks) {
|
|
42385
42975
|
const eventArray = Array.isArray(hooks[hookDef.event]) ? [...hooks[hookDef.event]] : [];
|
|
42386
|
-
|
|
42387
|
-
|
|
42388
|
-
|
|
42389
|
-
|
|
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))) {
|
|
42390
42986
|
eventArray.push({
|
|
42391
42987
|
hooks: [{ type: "command", command: hookDef.command }]
|
|
42392
42988
|
});
|
|
@@ -42398,7 +42994,7 @@ function mergePreToolUseBashHook(cwd) {
|
|
|
42398
42994
|
return { ok: true, path: settingsPath, action: "already_present" };
|
|
42399
42995
|
}
|
|
42400
42996
|
settings.hooks = hooks;
|
|
42401
|
-
|
|
42997
|
+
writeFileSync11(
|
|
42402
42998
|
settingsPath,
|
|
42403
42999
|
`${JSON.stringify(settings, null, 2)}
|
|
42404
43000
|
`,
|
|
@@ -42414,13 +43010,13 @@ function addDisallowedTools(cwd) {
|
|
|
42414
43010
|
const dir = join27(cwd, ".claude");
|
|
42415
43011
|
const settingsPath = join27(dir, "settings.json");
|
|
42416
43012
|
try {
|
|
42417
|
-
if (!
|
|
43013
|
+
if (!existsSync21(dir)) {
|
|
42418
43014
|
mkdirSync14(dir, { recursive: true });
|
|
42419
43015
|
}
|
|
42420
43016
|
let settings = {};
|
|
42421
|
-
if (
|
|
43017
|
+
if (existsSync21(settingsPath)) {
|
|
42422
43018
|
try {
|
|
42423
|
-
settings = JSON.parse(
|
|
43019
|
+
settings = JSON.parse(readFileSync20(settingsPath, "utf-8"));
|
|
42424
43020
|
} catch {
|
|
42425
43021
|
settings = {};
|
|
42426
43022
|
}
|
|
@@ -42443,7 +43039,7 @@ function addDisallowedTools(cwd) {
|
|
|
42443
43039
|
}
|
|
42444
43040
|
permissions.deny = deny;
|
|
42445
43041
|
settings.permissions = permissions;
|
|
42446
|
-
|
|
43042
|
+
writeFileSync11(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
42447
43043
|
`, "utf-8");
|
|
42448
43044
|
return { added, path: settingsPath };
|
|
42449
43045
|
} catch {
|
|
@@ -42452,9 +43048,9 @@ function addDisallowedTools(cwd) {
|
|
|
42452
43048
|
}
|
|
42453
43049
|
function removeDisallowedTools(cwd) {
|
|
42454
43050
|
const settingsPath = join27(cwd, ".claude", "settings.json");
|
|
42455
|
-
if (!
|
|
43051
|
+
if (!existsSync21(settingsPath)) return false;
|
|
42456
43052
|
try {
|
|
42457
|
-
const settings = JSON.parse(
|
|
43053
|
+
const settings = JSON.parse(readFileSync20(settingsPath, "utf-8"));
|
|
42458
43054
|
const permissions = settings.permissions;
|
|
42459
43055
|
if (!permissions || !Array.isArray(permissions.deny)) return false;
|
|
42460
43056
|
const before = permissions.deny.length;
|
|
@@ -42465,7 +43061,7 @@ function removeDisallowedTools(cwd) {
|
|
|
42465
43061
|
if (removed === 0) return false;
|
|
42466
43062
|
if (permissions.deny.length === 0) delete permissions.deny;
|
|
42467
43063
|
if (Object.keys(permissions).length === 0) delete settings.permissions;
|
|
42468
|
-
|
|
43064
|
+
writeFileSync11(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
42469
43065
|
`, "utf-8");
|
|
42470
43066
|
return true;
|
|
42471
43067
|
} catch {
|
|
@@ -42474,9 +43070,9 @@ function removeDisallowedTools(cwd) {
|
|
|
42474
43070
|
}
|
|
42475
43071
|
function removePreToolUseBashHook(cwd) {
|
|
42476
43072
|
const settingsPath = join27(cwd, ".claude", "settings.json");
|
|
42477
|
-
if (!
|
|
43073
|
+
if (!existsSync21(settingsPath)) return false;
|
|
42478
43074
|
try {
|
|
42479
|
-
const settings = JSON.parse(
|
|
43075
|
+
const settings = JSON.parse(readFileSync20(settingsPath, "utf-8"));
|
|
42480
43076
|
const hooks = settings.hooks;
|
|
42481
43077
|
if (!hooks) return false;
|
|
42482
43078
|
let totalRemoved = 0;
|
|
@@ -42493,7 +43089,7 @@ function removePreToolUseBashHook(cwd) {
|
|
|
42493
43089
|
}
|
|
42494
43090
|
if (totalRemoved === 0) return false;
|
|
42495
43091
|
if (Object.keys(hooks).length === 0) delete settings.hooks;
|
|
42496
|
-
|
|
43092
|
+
writeFileSync11(
|
|
42497
43093
|
settingsPath,
|
|
42498
43094
|
`${JSON.stringify(settings, null, 2)}
|
|
42499
43095
|
`,
|
|
@@ -42511,11 +43107,31 @@ init_mcp_config_writer();
|
|
|
42511
43107
|
|
|
42512
43108
|
// src/config/instruction-writer.ts
|
|
42513
43109
|
init_agent_registry();
|
|
42514
|
-
import { existsSync as
|
|
43110
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync15, readFileSync as readFileSync21, unlinkSync as unlinkSync3, writeFileSync as writeFileSync12 } from "fs";
|
|
42515
43111
|
import { dirname as dirname5, join as join28 } from "path";
|
|
42516
43112
|
var SENTINEL_START = "<!-- unerr:start -->";
|
|
42517
43113
|
var SENTINEL_END = "<!-- unerr:end -->";
|
|
42518
|
-
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.`;
|
|
42519
43135
|
return `## REQUIRED: Use unerr Graph Intelligence Tools (18 MCP tools)
|
|
42520
43136
|
|
|
42521
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.
|
|
@@ -42526,8 +43142,8 @@ This project has unerr MCP tools installed. You MUST use these instead of built-
|
|
|
42526
43142
|
|---|---|---|
|
|
42527
43143
|
| Find a function, class, or type | \`search_code\` | Grep, Glob |
|
|
42528
43144
|
| Find callers or callees | \`get_references\` (direction: callers/callees) | Grep for function name |
|
|
42529
|
-
| Read a file for understanding | \`file_read\` with \`purpose:'explore'\` (default, auto-injects conventions/facts) | Built-in Read
|
|
42530
|
-
|
|
43145
|
+
| Read a file for understanding | \`file_read\` with \`purpose:'explore'\` (default, auto-injects conventions/facts) | Built-in Read/Grep/Glob |
|
|
43146
|
+
${readForEditRow}
|
|
42531
43147
|
| Get file structure overview | \`file_outline\` | Reading the whole file |
|
|
42532
43148
|
| Get a specific function or class | \`get_entity\` or \`file_read\` with \`entity\` param | Reading entire file |
|
|
42533
43149
|
| Trace imports/dependencies | \`get_imports\` or \`get_references\` (direction: callees) | Manual import scanning |
|
|
@@ -42535,27 +43151,12 @@ This project has unerr MCP tools installed. You MUST use these instead of built-
|
|
|
42535
43151
|
|
|
42536
43152
|
### FORBIDDEN Patterns (these waste tokens and miss context)
|
|
42537
43153
|
|
|
42538
|
-
- Calling file_read multiple times with offset/limit to read a whole file -> use \`file_read\` with \`purpose:'edit'\` (one call, full content)
|
|
42539
43154
|
- Reading an entire file to find one function -> use \`get_entity\` or \`file_read\` with \`entity\` param
|
|
42540
43155
|
- Grep for a function name to find callers -> use \`get_references\` (finds indirect refs too)
|
|
42541
43156
|
- Glob + Grep to search for code -> use \`search_code\` (indexes ALL entities, <5ms)
|
|
42542
43157
|
- Reading multiple files to understand conventions -> use \`get_conventions\`
|
|
42543
43158
|
- Guessing code style for new code -> use \`get_rules\` (with \`content\` param to validate)
|
|
42544
|
-
|
|
42545
|
-
### IMPORTANT: Two-step Read Routing
|
|
42546
|
-
|
|
42547
|
-
**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.
|
|
42548
|
-
|
|
42549
|
-
**The rule \u2014 two paths, choose by intent:**
|
|
42550
|
-
|
|
42551
|
-
| Intent | Tool | Why |
|
|
42552
|
-
|--------|------|-----|
|
|
42553
|
-
| Reading to understand code | \`file_read\` (unerr MCP) | Auto-injects conventions, facts, drift status |
|
|
42554
|
-
| 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. |
|
|
42555
|
-
|
|
42556
|
-
When your next action is Edit, use built-in Read. For everything else, use \`file_read\`.
|
|
42557
|
-
|
|
42558
|
-
**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}
|
|
42559
43160
|
|
|
42560
43161
|
### Tool Reference
|
|
42561
43162
|
|
|
@@ -42581,9 +43182,8 @@ When your next action is Edit, use built-in Read. For everything else, use \`fil
|
|
|
42581
43182
|
|
|
42582
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.
|
|
42583
43184
|
|
|
42584
|
-
**\`purpose\` parameter
|
|
42585
|
-
- \`purpose:'explore'\` (default) \u2014 budget-capped, returns outline for large files. Use for browsing
|
|
42586
|
-
- \`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.
|
|
42587
43187
|
- \`purpose:'reference'\` \u2014 tight budget, entity/offset reads only. Use for quick lookups.
|
|
42588
43188
|
|
|
42589
43189
|
#### Shell Compression (automatic)
|
|
@@ -42622,8 +43222,7 @@ ONLY use built-in Read/Grep/Glob when:
|
|
|
42622
43222
|
|
|
42623
43223
|
### Summary (CRITICAL \u2014 read this even if you skimmed above)
|
|
42624
43224
|
|
|
42625
|
-
ALWAYS use unerr MCP tools: \`search_code\`, \`get_references\`, \`file_read\`, \`file_outline\`, \`get_entity
|
|
42626
|
-
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}
|
|
42627
43226
|
unerr skills available: /audit, /blame, /test, /lint, /commit, /review-pr.
|
|
42628
43227
|
Before writing code: \`get_conventions\`, \`get_rules\`. After writing: \`get_rules\` with content to validate.
|
|
42629
43228
|
Before risky changes: \`unerr_mark_working\`. If things break: \`unerr_revert_to_working_state\`.`;
|
|
@@ -42634,7 +43233,7 @@ description: REQUIRED \u2014 use unerr graph intelligence tools instead of built
|
|
|
42634
43233
|
alwaysApply: true
|
|
42635
43234
|
---
|
|
42636
43235
|
|
|
42637
|
-
${getInstructionContent()}
|
|
43236
|
+
${getInstructionContent("cursor")}
|
|
42638
43237
|
`;
|
|
42639
43238
|
}
|
|
42640
43239
|
function writeInstructionFile(cwd, ide) {
|
|
@@ -42648,7 +43247,7 @@ function writeInstructionFile(cwd, ide) {
|
|
|
42648
43247
|
}
|
|
42649
43248
|
if (agentDef.instructionFormat === "windsurf-rule") {
|
|
42650
43249
|
mkdirSync15(dirname5(filePath), { recursive: true });
|
|
42651
|
-
const content = getInstructionContent();
|
|
43250
|
+
const content = getInstructionContent(ide);
|
|
42652
43251
|
const truncatedContent = content.length > 5800 ? `${content.slice(0, 5800)}
|
|
42653
43252
|
|
|
42654
43253
|
[Truncated \u2014 full content exceeds Windsurf 6K limit]` : content;
|
|
@@ -42659,19 +43258,19 @@ description: "Tool routing instructions for unerr MCP integration"
|
|
|
42659
43258
|
|
|
42660
43259
|
${truncatedContent}
|
|
42661
43260
|
`;
|
|
42662
|
-
const existed =
|
|
43261
|
+
const existed = existsSync22(filePath);
|
|
42663
43262
|
if (existed) {
|
|
42664
|
-
const existing =
|
|
43263
|
+
const existing = readFileSync21(filePath, "utf-8");
|
|
42665
43264
|
if (existing === windsurfContent) {
|
|
42666
43265
|
return { path: filePath, action: "skipped" };
|
|
42667
43266
|
}
|
|
42668
43267
|
}
|
|
42669
|
-
|
|
43268
|
+
writeFileSync12(filePath, windsurfContent, "utf-8");
|
|
42670
43269
|
return { path: filePath, action: existed ? "updated" : "created" };
|
|
42671
43270
|
}
|
|
42672
43271
|
if (agentDef.instructionFormat === "antigravity-rule") {
|
|
42673
43272
|
mkdirSync15(dirname5(filePath), { recursive: true });
|
|
42674
|
-
const content = getInstructionContent();
|
|
43273
|
+
const content = getInstructionContent(ide);
|
|
42675
43274
|
const antigravityContent = `---
|
|
42676
43275
|
name: unerr-instructions
|
|
42677
43276
|
description: Tool routing instructions for unerr MCP integration
|
|
@@ -42680,34 +43279,34 @@ type: manual
|
|
|
42680
43279
|
|
|
42681
43280
|
${content}
|
|
42682
43281
|
`;
|
|
42683
|
-
const existed =
|
|
43282
|
+
const existed = existsSync22(filePath);
|
|
42684
43283
|
if (existed) {
|
|
42685
|
-
const existing =
|
|
43284
|
+
const existing = readFileSync21(filePath, "utf-8");
|
|
42686
43285
|
if (existing === antigravityContent) {
|
|
42687
43286
|
return { path: filePath, action: "skipped" };
|
|
42688
43287
|
}
|
|
42689
43288
|
}
|
|
42690
|
-
|
|
43289
|
+
writeFileSync12(filePath, antigravityContent, "utf-8");
|
|
42691
43290
|
return { path: filePath, action: existed ? "updated" : "created" };
|
|
42692
43291
|
}
|
|
42693
|
-
return mergeMarkdownSection(filePath, getInstructionContent());
|
|
43292
|
+
return mergeMarkdownSection(filePath, getInstructionContent(ide));
|
|
42694
43293
|
}
|
|
42695
43294
|
function mergeMarkdownSection(filePath, content) {
|
|
42696
43295
|
const wrappedContent = `${SENTINEL_START}
|
|
42697
43296
|
${content}
|
|
42698
43297
|
${SENTINEL_END}`;
|
|
42699
|
-
if (!
|
|
43298
|
+
if (!existsSync22(filePath)) {
|
|
42700
43299
|
const dir = dirname5(filePath);
|
|
42701
|
-
if (!
|
|
42702
|
-
|
|
43300
|
+
if (!existsSync22(dir)) mkdirSync15(dir, { recursive: true });
|
|
43301
|
+
writeFileSync12(filePath, `${wrappedContent}
|
|
42703
43302
|
`);
|
|
42704
43303
|
return { path: filePath, action: "created" };
|
|
42705
43304
|
}
|
|
42706
|
-
const existing =
|
|
43305
|
+
const existing = readFileSync21(filePath, "utf-8");
|
|
42707
43306
|
const startIdx = existing.indexOf(SENTINEL_START);
|
|
42708
43307
|
const endIdx = existing.indexOf(SENTINEL_END);
|
|
42709
43308
|
if (startIdx === -1 || endIdx === -1) {
|
|
42710
|
-
|
|
43309
|
+
writeFileSync12(filePath, `${wrappedContent}
|
|
42711
43310
|
|
|
42712
43311
|
${existing}`);
|
|
42713
43312
|
return { path: filePath, action: "updated" };
|
|
@@ -42718,21 +43317,21 @@ ${existing}`);
|
|
|
42718
43317
|
}
|
|
42719
43318
|
const before = existing.slice(0, startIdx);
|
|
42720
43319
|
const after = existing.slice(endIdx + SENTINEL_END.length);
|
|
42721
|
-
|
|
43320
|
+
writeFileSync12(filePath, `${before}${wrappedContent}${after}`);
|
|
42722
43321
|
return { path: filePath, action: "updated" };
|
|
42723
43322
|
}
|
|
42724
43323
|
function writeMdcInstructionFile(filePath) {
|
|
42725
43324
|
const content = getMdcContent();
|
|
42726
|
-
const alreadyExists =
|
|
43325
|
+
const alreadyExists = existsSync22(filePath);
|
|
42727
43326
|
if (alreadyExists) {
|
|
42728
|
-
const existing =
|
|
43327
|
+
const existing = readFileSync21(filePath, "utf-8");
|
|
42729
43328
|
if (existing === content) {
|
|
42730
43329
|
return { path: filePath, action: "skipped" };
|
|
42731
43330
|
}
|
|
42732
43331
|
}
|
|
42733
43332
|
const dir = dirname5(filePath);
|
|
42734
|
-
if (!
|
|
42735
|
-
|
|
43333
|
+
if (!existsSync22(dir)) mkdirSync15(dir, { recursive: true });
|
|
43334
|
+
writeFileSync12(filePath, content);
|
|
42736
43335
|
return {
|
|
42737
43336
|
path: filePath,
|
|
42738
43337
|
action: alreadyExists ? "updated" : "created"
|
|
@@ -42742,26 +43341,26 @@ function removeInstructionSection(cwd, ide) {
|
|
|
42742
43341
|
const agentDef = getAgent(ide);
|
|
42743
43342
|
if (!agentDef?.instructionFilePath) return false;
|
|
42744
43343
|
const filePath = join28(cwd, agentDef.instructionFilePath);
|
|
42745
|
-
if (!
|
|
43344
|
+
if (!existsSync22(filePath)) return false;
|
|
42746
43345
|
if (agentDef.instructionFormat === "mdc") {
|
|
42747
43346
|
unlinkSync3(filePath);
|
|
42748
43347
|
return true;
|
|
42749
43348
|
}
|
|
42750
43349
|
if (agentDef.instructionFormat === "windsurf-rule") {
|
|
42751
|
-
if (
|
|
43350
|
+
if (existsSync22(filePath)) {
|
|
42752
43351
|
unlinkSync3(filePath);
|
|
42753
43352
|
return true;
|
|
42754
43353
|
}
|
|
42755
43354
|
return false;
|
|
42756
43355
|
}
|
|
42757
43356
|
if (agentDef.instructionFormat === "antigravity-rule") {
|
|
42758
|
-
if (
|
|
43357
|
+
if (existsSync22(filePath)) {
|
|
42759
43358
|
unlinkSync3(filePath);
|
|
42760
43359
|
return true;
|
|
42761
43360
|
}
|
|
42762
43361
|
return false;
|
|
42763
43362
|
}
|
|
42764
|
-
const content =
|
|
43363
|
+
const content = readFileSync21(filePath, "utf-8");
|
|
42765
43364
|
const startIdx = content.indexOf(SENTINEL_START);
|
|
42766
43365
|
const endIdx = content.indexOf(SENTINEL_END);
|
|
42767
43366
|
if (startIdx === -1 || endIdx === -1) return false;
|
|
@@ -42772,12 +43371,12 @@ function removeInstructionSection(cwd, ide) {
|
|
|
42772
43371
|
if (trimmed.length === 0) {
|
|
42773
43372
|
unlinkSync3(filePath);
|
|
42774
43373
|
} else {
|
|
42775
|
-
|
|
43374
|
+
writeFileSync12(filePath, cleaned);
|
|
42776
43375
|
}
|
|
42777
43376
|
return true;
|
|
42778
43377
|
}
|
|
42779
43378
|
function generateCustomInstructions(ide) {
|
|
42780
|
-
const content = getInstructionContent();
|
|
43379
|
+
const content = getInstructionContent(ide);
|
|
42781
43380
|
if (ide && ide !== "other") {
|
|
42782
43381
|
const agentDef = getAgent(ide);
|
|
42783
43382
|
if (agentDef?.instructionFilePath) {
|
|
@@ -43149,16 +43748,16 @@ function installCursorHooks(cwd) {
|
|
|
43149
43748
|
]
|
|
43150
43749
|
};
|
|
43151
43750
|
try {
|
|
43152
|
-
if (!
|
|
43751
|
+
if (!existsSync24(hooksDir)) {
|
|
43153
43752
|
mkdirSync17(hooksDir, { recursive: true });
|
|
43154
43753
|
}
|
|
43155
43754
|
let config = {
|
|
43156
43755
|
version: 1,
|
|
43157
43756
|
hooks: {}
|
|
43158
43757
|
};
|
|
43159
|
-
if (
|
|
43758
|
+
if (existsSync24(hooksJsonPath)) {
|
|
43160
43759
|
try {
|
|
43161
|
-
const existing = JSON.parse(
|
|
43760
|
+
const existing = JSON.parse(readFileSync23(hooksJsonPath, "utf-8"));
|
|
43162
43761
|
if (existing.version && existing.hooks) {
|
|
43163
43762
|
config = existing;
|
|
43164
43763
|
}
|
|
@@ -43175,7 +43774,7 @@ function installCursorHooks(cwd) {
|
|
|
43175
43774
|
}
|
|
43176
43775
|
config.hooks[event] = existing;
|
|
43177
43776
|
}
|
|
43178
|
-
|
|
43777
|
+
writeFileSync14(hooksJsonPath, `${JSON.stringify(config, null, 2)}
|
|
43179
43778
|
`);
|
|
43180
43779
|
writeCursorHookScript(hooksDir, "unerr-pre-tool.sh", CURSOR_PRE_TOOL_SCRIPT);
|
|
43181
43780
|
writeCursorHookScript(hooksDir, "unerr-post-tool.sh", CURSOR_POST_TOOL_SCRIPT);
|
|
@@ -43188,15 +43787,15 @@ function installCursorHooks(cwd) {
|
|
|
43188
43787
|
}
|
|
43189
43788
|
function writeCursorHookScript(dir, filename, content) {
|
|
43190
43789
|
const scriptPath = join30(dir, filename);
|
|
43191
|
-
|
|
43790
|
+
writeFileSync14(scriptPath, content, "utf-8");
|
|
43192
43791
|
chmodSync4(scriptPath, 493);
|
|
43193
43792
|
}
|
|
43194
43793
|
function installClineHooks(cwd) {
|
|
43195
43794
|
const hooksDir = join30(cwd, ".clinerules", "hooks");
|
|
43196
43795
|
const hookPath = join30(hooksDir, "unerr-pre-tool.sh");
|
|
43197
|
-
if (
|
|
43796
|
+
if (existsSync24(hookPath)) return true;
|
|
43198
43797
|
try {
|
|
43199
|
-
if (!
|
|
43798
|
+
if (!existsSync24(hooksDir)) {
|
|
43200
43799
|
const { mkdirSync: mkdirSync39 } = __require("fs");
|
|
43201
43800
|
mkdirSync39(hooksDir, { recursive: true });
|
|
43202
43801
|
}
|
|
@@ -43206,7 +43805,7 @@ function installClineHooks(cwd) {
|
|
|
43206
43805
|
# Removed by: unerr uninstall cline
|
|
43207
43806
|
cat /dev/stdin | unerr hook pre-read
|
|
43208
43807
|
`;
|
|
43209
|
-
|
|
43808
|
+
writeFileSync14(hookPath, hookContent, "utf-8");
|
|
43210
43809
|
chmodSync4(hookPath, 493);
|
|
43211
43810
|
return true;
|
|
43212
43811
|
} catch {
|
|
@@ -43215,15 +43814,15 @@ cat /dev/stdin | unerr hook pre-read
|
|
|
43215
43814
|
}
|
|
43216
43815
|
function ensureGitignore(cwd) {
|
|
43217
43816
|
const gitignorePath = join30(cwd, ".gitignore");
|
|
43218
|
-
if (!
|
|
43817
|
+
if (!existsSync24(gitignorePath)) return false;
|
|
43219
43818
|
try {
|
|
43220
|
-
const content =
|
|
43819
|
+
const content = readFileSync23(gitignorePath, "utf-8");
|
|
43221
43820
|
const lines = content.split("\n");
|
|
43222
43821
|
if (lines.some((l) => l.trim() === ".unerr" || l.trim() === ".unerr/")) {
|
|
43223
43822
|
return false;
|
|
43224
43823
|
}
|
|
43225
43824
|
const newline = content.endsWith("\n") ? "" : "\n";
|
|
43226
|
-
|
|
43825
|
+
writeFileSync14(
|
|
43227
43826
|
gitignorePath,
|
|
43228
43827
|
`${content}${newline}
|
|
43229
43828
|
# unerr local artifacts
|
|
@@ -43238,7 +43837,7 @@ function ensureGitignore(cwd) {
|
|
|
43238
43837
|
|
|
43239
43838
|
// src/commands/learn.ts
|
|
43240
43839
|
init_correction_detector();
|
|
43241
|
-
import { existsSync as
|
|
43840
|
+
import { existsSync as existsSync26 } from "fs";
|
|
43242
43841
|
import { join as join31 } from "path";
|
|
43243
43842
|
import { gunzipSync as gunzipSync2 } from "zlib";
|
|
43244
43843
|
import pc3 from "picocolors";
|
|
@@ -43255,7 +43854,7 @@ function registerLearnCommand(program2) {
|
|
|
43255
43854
|
const cwd = process.cwd();
|
|
43256
43855
|
const unerrDir = join31(cwd, ".unerr");
|
|
43257
43856
|
const ledgerPath = join31(unerrDir, "ledger", "shadow.jsonl");
|
|
43258
|
-
if (!
|
|
43857
|
+
if (!existsSync26(ledgerPath)) {
|
|
43259
43858
|
fail("No shadow ledger found at .unerr/ledger/shadow.jsonl");
|
|
43260
43859
|
info(
|
|
43261
43860
|
"The shadow ledger is created when the unerr proxy runs. Start the proxy first."
|
|
@@ -43312,20 +43911,20 @@ async function persistPatterns(patterns, _unerrDir) {
|
|
|
43312
43911
|
try {
|
|
43313
43912
|
const cwd = process.cwd();
|
|
43314
43913
|
const configPath = join31(cwd, ".unerr", "config.json");
|
|
43315
|
-
if (!
|
|
43914
|
+
if (!existsSync26(configPath)) {
|
|
43316
43915
|
warn(
|
|
43317
43916
|
"No repo config found \u2014 cannot persist to CozoDB. Run 'unerr' to configure."
|
|
43318
43917
|
);
|
|
43319
43918
|
return;
|
|
43320
43919
|
}
|
|
43321
|
-
const { readFileSync:
|
|
43322
|
-
const config = JSON.parse(
|
|
43920
|
+
const { readFileSync: readFileSync58 } = await import("fs");
|
|
43921
|
+
const config = JSON.parse(readFileSync58(configPath, "utf-8"));
|
|
43323
43922
|
if (!config.repoId || !config.orgId) {
|
|
43324
43923
|
warn("Repo not configured \u2014 cannot persist to CozoDB.");
|
|
43325
43924
|
return;
|
|
43326
43925
|
}
|
|
43327
43926
|
const snapshotPath2 = join31(cwd, ".unerr", "snapshots", "graph.msgpack.gz");
|
|
43328
|
-
if (!
|
|
43927
|
+
if (!existsSync26(snapshotPath2)) {
|
|
43329
43928
|
warn("No graph snapshot found \u2014 cannot persist to CozoDB.");
|
|
43330
43929
|
return;
|
|
43331
43930
|
}
|
|
@@ -43337,7 +43936,7 @@ async function persistPatterns(patterns, _unerrDir) {
|
|
|
43337
43936
|
const { CozoGraphStore: CozoGraphStore2 } = await Promise.resolve().then(() => (init_local_graph(), local_graph_exports));
|
|
43338
43937
|
const store = await CozoGraphStore2.create(db);
|
|
43339
43938
|
const { unpack } = await import("msgpackr");
|
|
43340
|
-
const raw =
|
|
43939
|
+
const raw = readFileSync58(snapshotPath2);
|
|
43341
43940
|
const buffer = gunzipSync2(raw);
|
|
43342
43941
|
const envelope = unpack(buffer);
|
|
43343
43942
|
await store.loadSnapshot(envelope);
|
|
@@ -43354,16 +43953,16 @@ async function persistPatterns(patterns, _unerrDir) {
|
|
|
43354
43953
|
|
|
43355
43954
|
// src/commands/manifest.ts
|
|
43356
43955
|
init_exec();
|
|
43357
|
-
import { existsSync as
|
|
43956
|
+
import { existsSync as existsSync27, readFileSync as readFileSync25 } from "fs";
|
|
43358
43957
|
import { join as join32 } from "path";
|
|
43359
43958
|
async function findRepoRoot() {
|
|
43360
43959
|
return gitQuery(["rev-parse", "--show-toplevel"]);
|
|
43361
43960
|
}
|
|
43362
43961
|
function loadManifest(repoRoot) {
|
|
43363
43962
|
const manifestPath = join32(repoRoot, ".unerr", "manifest.json");
|
|
43364
|
-
if (!
|
|
43963
|
+
if (!existsSync27(manifestPath)) return null;
|
|
43365
43964
|
try {
|
|
43366
|
-
const raw =
|
|
43965
|
+
const raw = readFileSync25(manifestPath, "utf-8");
|
|
43367
43966
|
return JSON.parse(raw);
|
|
43368
43967
|
} catch {
|
|
43369
43968
|
return null;
|
|
@@ -43684,7 +44283,7 @@ function registerStatsCommand(program2) {
|
|
|
43684
44283
|
|
|
43685
44284
|
// src/commands/status.ts
|
|
43686
44285
|
init_git();
|
|
43687
|
-
import { existsSync as
|
|
44286
|
+
import { existsSync as existsSync32, readFileSync as readFileSync30 } from "fs";
|
|
43688
44287
|
import { join as join38 } from "path";
|
|
43689
44288
|
import React from "react";
|
|
43690
44289
|
function buildSuggestions(data) {
|
|
@@ -43733,9 +44332,9 @@ function registerStatusCommand(program2) {
|
|
|
43733
44332
|
const localDataDir = join38(cwd, ".unerr");
|
|
43734
44333
|
let repoId = "";
|
|
43735
44334
|
const configPath = join38(unerrDir, "config.json");
|
|
43736
|
-
if (
|
|
44335
|
+
if (existsSync32(configPath)) {
|
|
43737
44336
|
try {
|
|
43738
|
-
const config = JSON.parse(
|
|
44337
|
+
const config = JSON.parse(readFileSync30(configPath, "utf-8"));
|
|
43739
44338
|
repoId = config.repoId ?? "";
|
|
43740
44339
|
} catch {
|
|
43741
44340
|
}
|
|
@@ -43764,9 +44363,9 @@ function registerStatusCommand(program2) {
|
|
|
43764
44363
|
let proxyStatus = "Not running";
|
|
43765
44364
|
let proxyRunning = false;
|
|
43766
44365
|
const pidPath = join38(unerrDir, "state", "proxy.pid");
|
|
43767
|
-
if (
|
|
44366
|
+
if (existsSync32(pidPath)) {
|
|
43768
44367
|
try {
|
|
43769
|
-
const pidStr =
|
|
44368
|
+
const pidStr = readFileSync30(pidPath, "utf-8").trim();
|
|
43770
44369
|
const pid = Number.parseInt(pidStr, 10);
|
|
43771
44370
|
process.kill(pid, 0);
|
|
43772
44371
|
proxyStatus = `Running (PID ${pid})`;
|
|
@@ -43777,10 +44376,10 @@ function registerStatusCommand(program2) {
|
|
|
43777
44376
|
}
|
|
43778
44377
|
let graphInfo = "No local graph";
|
|
43779
44378
|
const manifestsDir = join38(localDataDir, "manifests");
|
|
43780
|
-
if (repoId &&
|
|
44379
|
+
if (repoId && existsSync32(join38(manifestsDir, `${repoId}.json`))) {
|
|
43781
44380
|
try {
|
|
43782
44381
|
const manifest = JSON.parse(
|
|
43783
|
-
|
|
44382
|
+
readFileSync30(join38(manifestsDir, `${repoId}.json`), "utf-8")
|
|
43784
44383
|
);
|
|
43785
44384
|
const entityCount = manifest.entityCount ?? 0;
|
|
43786
44385
|
const edgeCount = manifest.edgeCount ?? 0;
|
|
@@ -43799,10 +44398,10 @@ function registerStatusCommand(program2) {
|
|
|
43799
44398
|
}
|
|
43800
44399
|
let drift;
|
|
43801
44400
|
const driftSummaryPath = join38(unerrDir, "drift", "drift_summary.json");
|
|
43802
|
-
if (
|
|
44401
|
+
if (existsSync32(driftSummaryPath)) {
|
|
43803
44402
|
try {
|
|
43804
44403
|
const summary = JSON.parse(
|
|
43805
|
-
|
|
44404
|
+
readFileSync30(driftSummaryPath, "utf-8")
|
|
43806
44405
|
);
|
|
43807
44406
|
drift = {
|
|
43808
44407
|
modified: summary.modified ?? 0,
|
|
@@ -43815,9 +44414,9 @@ function registerStatusCommand(program2) {
|
|
|
43815
44414
|
let healthGrade;
|
|
43816
44415
|
let healthScore;
|
|
43817
44416
|
const graphVersionPath = join38(unerrDir, "state", "graph_version.json");
|
|
43818
|
-
if (
|
|
44417
|
+
if (existsSync32(graphVersionPath)) {
|
|
43819
44418
|
try {
|
|
43820
|
-
const gv = JSON.parse(
|
|
44419
|
+
const gv = JSON.parse(readFileSync30(graphVersionPath, "utf-8"));
|
|
43821
44420
|
healthGrade = gv.health_grade;
|
|
43822
44421
|
healthScore = gv.health_score;
|
|
43823
44422
|
} catch {
|
|
@@ -43828,15 +44427,15 @@ function registerStatusCommand(program2) {
|
|
|
43828
44427
|
try {
|
|
43829
44428
|
const snapshotsDir = join38(localDataDir, "snapshots");
|
|
43830
44429
|
let snapshotPath2 = join38(snapshotsDir, "graph.msgpack.gz");
|
|
43831
|
-
if (!
|
|
44430
|
+
if (!existsSync32(snapshotPath2)) {
|
|
43832
44431
|
snapshotPath2 = join38(snapshotsDir, "graph.msgpack");
|
|
43833
44432
|
}
|
|
43834
|
-
if (
|
|
44433
|
+
if (existsSync32(snapshotPath2)) {
|
|
43835
44434
|
const { gunzipSync: gunzipSync3 } = await import("zlib");
|
|
43836
44435
|
const { unpack } = await import("msgpackr");
|
|
43837
44436
|
const { default: CozoDbConstructor } = await import("cozo-node");
|
|
43838
44437
|
const { CozoGraphStore: CozoGraphStore2 } = await Promise.resolve().then(() => (init_local_graph(), local_graph_exports));
|
|
43839
|
-
const raw =
|
|
44438
|
+
const raw = readFileSync30(snapshotPath2);
|
|
43840
44439
|
let buffer;
|
|
43841
44440
|
try {
|
|
43842
44441
|
buffer = gunzipSync3(raw);
|
|
@@ -43855,9 +44454,9 @@ function registerStatusCommand(program2) {
|
|
|
43855
44454
|
let liveToolCalls;
|
|
43856
44455
|
let latency;
|
|
43857
44456
|
const statsPath = join38(unerrDir, "state", "session_stats.json");
|
|
43858
|
-
if (
|
|
44457
|
+
if (existsSync32(statsPath) && proxyRunning) {
|
|
43859
44458
|
try {
|
|
43860
|
-
const liveStats = JSON.parse(
|
|
44459
|
+
const liveStats = JSON.parse(readFileSync30(statsPath, "utf-8"));
|
|
43861
44460
|
const localCalls = liveStats.toolCallsLocal ?? 0;
|
|
43862
44461
|
if (localCalls > 0) {
|
|
43863
44462
|
liveToolCalls = { local: localCalls };
|
|
@@ -43884,8 +44483,8 @@ function registerStatusCommand(program2) {
|
|
|
43884
44483
|
"state",
|
|
43885
44484
|
"firewall_stats.json"
|
|
43886
44485
|
);
|
|
43887
|
-
if (
|
|
43888
|
-
const fs5 = JSON.parse(
|
|
44486
|
+
if (existsSync32(firewallStatsPath)) {
|
|
44487
|
+
const fs5 = JSON.parse(readFileSync30(firewallStatsPath, "utf-8"));
|
|
43889
44488
|
firewallBlocked = fs5.blocked ?? 0;
|
|
43890
44489
|
}
|
|
43891
44490
|
} catch {
|
|
@@ -43922,9 +44521,9 @@ function registerStatusCommand(program2) {
|
|
|
43922
44521
|
}
|
|
43923
44522
|
let lastIndexed;
|
|
43924
44523
|
const snapshotMetaPath = join38(unerrDir, "state", "snapshot_meta.json");
|
|
43925
|
-
if (
|
|
44524
|
+
if (existsSync32(snapshotMetaPath)) {
|
|
43926
44525
|
try {
|
|
43927
|
-
const meta = JSON.parse(
|
|
44526
|
+
const meta = JSON.parse(readFileSync30(snapshotMetaPath, "utf-8"));
|
|
43928
44527
|
if (meta.indexedAt) {
|
|
43929
44528
|
const ageMs = Date.now() - new Date(meta.indexedAt).getTime();
|
|
43930
44529
|
const ageHours = Math.floor(ageMs / 36e5);
|
|
@@ -43941,8 +44540,8 @@ function registerStatusCommand(program2) {
|
|
|
43941
44540
|
let corrections;
|
|
43942
44541
|
try {
|
|
43943
44542
|
const corrPath = join38(unerrDir, "state", "corrections_count.json");
|
|
43944
|
-
if (
|
|
43945
|
-
const cc = JSON.parse(
|
|
44543
|
+
if (existsSync32(corrPath)) {
|
|
44544
|
+
const cc = JSON.parse(readFileSync30(corrPath, "utf-8"));
|
|
43946
44545
|
corrections = cc.count;
|
|
43947
44546
|
}
|
|
43948
44547
|
} catch {
|
|
@@ -43951,8 +44550,8 @@ function registerStatusCommand(program2) {
|
|
|
43951
44550
|
let conventionCount;
|
|
43952
44551
|
let ruleCount;
|
|
43953
44552
|
try {
|
|
43954
|
-
if (
|
|
43955
|
-
const gv = JSON.parse(
|
|
44553
|
+
if (existsSync32(graphVersionPath)) {
|
|
44554
|
+
const gv = JSON.parse(readFileSync30(graphVersionPath, "utf-8"));
|
|
43956
44555
|
communityCount = gv.community_count;
|
|
43957
44556
|
conventionCount = gv.convention_count;
|
|
43958
44557
|
ruleCount = gv.rule_count;
|
|
@@ -43994,8 +44593,8 @@ function registerStatusCommand(program2) {
|
|
|
43994
44593
|
let ledgerEntryCount;
|
|
43995
44594
|
try {
|
|
43996
44595
|
const ledgerPath = join38(unerrDir, "ledger", "shadow.jsonl");
|
|
43997
|
-
if (
|
|
43998
|
-
const content =
|
|
44596
|
+
if (existsSync32(ledgerPath)) {
|
|
44597
|
+
const content = readFileSync30(ledgerPath, "utf-8");
|
|
43999
44598
|
ledgerEntryCount = content.split("\n").filter((l) => l.trim().length > 0).length;
|
|
44000
44599
|
}
|
|
44001
44600
|
} catch {
|
|
@@ -44003,7 +44602,7 @@ function registerStatusCommand(program2) {
|
|
|
44003
44602
|
let logPath;
|
|
44004
44603
|
try {
|
|
44005
44604
|
const logsDir = join38(unerrDir, "logs");
|
|
44006
|
-
if (
|
|
44605
|
+
if (existsSync32(logsDir)) {
|
|
44007
44606
|
const { readdirSync: readdirSync15, statSync: statSync12 } = await import("fs");
|
|
44008
44607
|
const logs = readdirSync15(logsDir).filter((f) => f.startsWith("session-") && f.endsWith(".log")).map((f) => ({
|
|
44009
44608
|
name: f,
|
|
@@ -44034,8 +44633,8 @@ function registerStatusCommand(program2) {
|
|
|
44034
44633
|
if (!proxyRunning) {
|
|
44035
44634
|
try {
|
|
44036
44635
|
const statsPath2 = join38(unerrDir, "state", "session_stats.json");
|
|
44037
|
-
if (
|
|
44038
|
-
const raw = JSON.parse(
|
|
44636
|
+
if (existsSync32(statsPath2)) {
|
|
44637
|
+
const raw = JSON.parse(readFileSync30(statsPath2, "utf-8"));
|
|
44039
44638
|
const totalCalls = raw.toolCallsLocal ?? 0;
|
|
44040
44639
|
if (totalCalls > 0) {
|
|
44041
44640
|
const endTime = raw.updatedAt ? new Date(raw.updatedAt) : /* @__PURE__ */ new Date();
|
|
@@ -44070,8 +44669,8 @@ function registerStatusCommand(program2) {
|
|
|
44070
44669
|
let preBashHookConfigured = false;
|
|
44071
44670
|
try {
|
|
44072
44671
|
const sp = join38(cwd, ".claude", "settings.json");
|
|
44073
|
-
if (
|
|
44074
|
-
const raw =
|
|
44672
|
+
if (existsSync32(sp)) {
|
|
44673
|
+
const raw = readFileSync30(sp, "utf8");
|
|
44075
44674
|
preBashHookConfigured = raw.includes("unerr hook pre-bash") || raw.includes('"unerr hook pre-bash"');
|
|
44076
44675
|
}
|
|
44077
44676
|
} catch {
|
|
@@ -44359,7 +44958,7 @@ function registerTimelineCommand(program2) {
|
|
|
44359
44958
|
|
|
44360
44959
|
// src/commands/uninstall.ts
|
|
44361
44960
|
init_agent_registry();
|
|
44362
|
-
import { existsSync as
|
|
44961
|
+
import { existsSync as existsSync34, readFileSync as readFileSync32, unlinkSync as unlinkSync5, writeFileSync as writeFileSync18 } from "fs";
|
|
44363
44962
|
import { join as join40 } from "path";
|
|
44364
44963
|
init_hook_installer();
|
|
44365
44964
|
init_mcp_config_writer();
|
|
@@ -44504,9 +45103,9 @@ function runUninstallAll(cwd) {
|
|
|
44504
45103
|
}
|
|
44505
45104
|
function removeCursorHooks(cwd) {
|
|
44506
45105
|
const hooksPath = join40(cwd, ".cursor", "hooks.json");
|
|
44507
|
-
if (!
|
|
45106
|
+
if (!existsSync34(hooksPath)) return false;
|
|
44508
45107
|
try {
|
|
44509
|
-
const config = JSON.parse(
|
|
45108
|
+
const config = JSON.parse(readFileSync32(hooksPath, "utf-8"));
|
|
44510
45109
|
if (!Array.isArray(config.hooks)) return false;
|
|
44511
45110
|
const before = config.hooks.length;
|
|
44512
45111
|
config.hooks = config.hooks.filter(
|
|
@@ -44516,7 +45115,7 @@ function removeCursorHooks(cwd) {
|
|
|
44516
45115
|
if (config.hooks.length === 0) {
|
|
44517
45116
|
unlinkSync5(hooksPath);
|
|
44518
45117
|
} else {
|
|
44519
|
-
|
|
45118
|
+
writeFileSync18(hooksPath, `${JSON.stringify(config, null, 2)}
|
|
44520
45119
|
`);
|
|
44521
45120
|
}
|
|
44522
45121
|
return true;
|
|
@@ -44526,7 +45125,7 @@ function removeCursorHooks(cwd) {
|
|
|
44526
45125
|
}
|
|
44527
45126
|
function removeClineHooks(cwd) {
|
|
44528
45127
|
const hookPath = join40(cwd, ".clinerules", "hooks", "unerr-pre-tool.sh");
|
|
44529
|
-
if (!
|
|
45128
|
+
if (!existsSync34(hookPath)) return false;
|
|
44530
45129
|
try {
|
|
44531
45130
|
unlinkSync5(hookPath);
|
|
44532
45131
|
return true;
|
|
@@ -45081,9 +45680,9 @@ async function detectProjectRoot(cwd) {
|
|
|
45081
45680
|
}
|
|
45082
45681
|
function readLocalConfig(cwd) {
|
|
45083
45682
|
const configPath = join67(cwd, ".unerr", "config.json");
|
|
45084
|
-
if (!
|
|
45683
|
+
if (!existsSync63(configPath)) return null;
|
|
45085
45684
|
try {
|
|
45086
|
-
return JSON.parse(
|
|
45685
|
+
return JSON.parse(readFileSync57(configPath, "utf-8"));
|
|
45087
45686
|
} catch {
|
|
45088
45687
|
return null;
|
|
45089
45688
|
}
|
|
@@ -45158,7 +45757,7 @@ async function mcpBoot(cwd) {
|
|
|
45158
45757
|
}
|
|
45159
45758
|
const stateDir = join67(cwd, ".unerr", "state");
|
|
45160
45759
|
const sockPath = join67(stateDir, "proxy.sock");
|
|
45161
|
-
if (
|
|
45760
|
+
if (existsSync63(sockPath)) {
|
|
45162
45761
|
const { PidLock: PidLock2 } = await Promise.resolve().then(() => (init_pid_lock(), pid_lock_exports));
|
|
45163
45762
|
const pidLock = new PidLock2(stateDir);
|
|
45164
45763
|
const probeResult = await pidLock.probe();
|
|
@@ -45179,12 +45778,12 @@ async function mcpBoot(cwd) {
|
|
|
45179
45778
|
}
|
|
45180
45779
|
}
|
|
45181
45780
|
if (!readLocalConfig(cwd)) {
|
|
45182
|
-
const { mkdirSync: mkdirSync39, writeFileSync:
|
|
45781
|
+
const { mkdirSync: mkdirSync39, writeFileSync: writeFileSync37 } = await import("fs");
|
|
45183
45782
|
const { createHash: createHash5 } = await import("crypto");
|
|
45184
45783
|
const repoId = createHash5("sha256").update(cwd).digest("hex").slice(0, 12);
|
|
45185
45784
|
const unerrDir = join67(cwd, ".unerr");
|
|
45186
45785
|
mkdirSync39(unerrDir, { recursive: true });
|
|
45187
|
-
|
|
45786
|
+
writeFileSync37(
|
|
45188
45787
|
join67(unerrDir, "config.json"),
|
|
45189
45788
|
JSON.stringify(
|
|
45190
45789
|
{ repoId, mode: "local", createdAt: (/* @__PURE__ */ new Date()).toISOString() },
|