@unerr-ai/unerr 0.0.0-beta.6 → 0.0.0-beta.7
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 +22 -10
- package/dist/cli.js +2280 -462
- package/dist/ui/assets/index-CJ6H4e3-.css +1 -0
- package/dist/ui/assets/index-CRB4z0c9.js +10 -0
- package/dist/ui/index.html +2 -2
- package/dist/ui/screenshots/code-base-intelligence.png +0 -0
- package/dist/ui/screenshots/reasoning-quality.png +0 -0
- package/dist/ui/screenshots/reasoning-session.png +0 -0
- package/package.json +1 -1
- package/dist/ui/assets/index-Bi-binEM.js +0 -10
- package/dist/ui/assets/index-DvCs_BqT.css +0 -1
package/dist/cli.js
CHANGED
|
@@ -749,20 +749,20 @@ async function getParser(language) {
|
|
|
749
749
|
const parser = new TreeSitter();
|
|
750
750
|
const langFile = `tree-sitter-${language}.wasm`;
|
|
751
751
|
try {
|
|
752
|
-
const { join:
|
|
753
|
-
const { existsSync:
|
|
752
|
+
const { join: join68 } = await import("path");
|
|
753
|
+
const { existsSync: existsSync63 } = await import("fs");
|
|
754
754
|
const possiblePaths = [
|
|
755
|
-
|
|
755
|
+
join68(
|
|
756
756
|
process.cwd(),
|
|
757
757
|
"node_modules",
|
|
758
758
|
`tree-sitter-${language}`,
|
|
759
759
|
langFile
|
|
760
760
|
),
|
|
761
|
-
|
|
761
|
+
join68(process.cwd(), "node_modules", "web-tree-sitter", langFile)
|
|
762
762
|
];
|
|
763
763
|
let wasmPath = null;
|
|
764
764
|
for (const p of possiblePaths) {
|
|
765
|
-
if (
|
|
765
|
+
if (existsSync63(p)) {
|
|
766
766
|
wasmPath = p;
|
|
767
767
|
break;
|
|
768
768
|
}
|
|
@@ -1670,6 +1670,7 @@ async function initSchema(db) {
|
|
|
1670
1670
|
name: String,
|
|
1671
1671
|
file_path: String,
|
|
1672
1672
|
start_line: Int default 0,
|
|
1673
|
+
end_line: Int default 0,
|
|
1673
1674
|
signature: String default "",
|
|
1674
1675
|
body: String default "",
|
|
1675
1676
|
fan_in: Int default 0,
|
|
@@ -1680,6 +1681,32 @@ async function initSchema(db) {
|
|
|
1680
1681
|
}
|
|
1681
1682
|
`
|
|
1682
1683
|
);
|
|
1684
|
+
if (existing.has("entities")) {
|
|
1685
|
+
try {
|
|
1686
|
+
await db.run(
|
|
1687
|
+
`?[key, kind, name, file_path, start_line, end_line, signature, body, fan_in, fan_out, risk_level, community, is_test] :=
|
|
1688
|
+
*entities{key, kind, name, file_path, start_line, signature, body, fan_in, fan_out, risk_level, community, is_test},
|
|
1689
|
+
end_line = 0
|
|
1690
|
+
:replace entities {
|
|
1691
|
+
key: String
|
|
1692
|
+
=>
|
|
1693
|
+
kind: String,
|
|
1694
|
+
name: String,
|
|
1695
|
+
file_path: String,
|
|
1696
|
+
start_line: Int default 0,
|
|
1697
|
+
end_line: Int default 0,
|
|
1698
|
+
signature: String default "",
|
|
1699
|
+
body: String default "",
|
|
1700
|
+
fan_in: Int default 0,
|
|
1701
|
+
fan_out: Int default 0,
|
|
1702
|
+
risk_level: String default "normal",
|
|
1703
|
+
community: Int default -1,
|
|
1704
|
+
is_test: Bool default false
|
|
1705
|
+
}`
|
|
1706
|
+
);
|
|
1707
|
+
} catch {
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1683
1710
|
await createIfMissing(
|
|
1684
1711
|
db,
|
|
1685
1712
|
existing,
|
|
@@ -2339,14 +2366,15 @@ var init_local_graph = __esm({
|
|
|
2339
2366
|
async loadSnapshot(envelope) {
|
|
2340
2367
|
for (const entity of envelope.entities) {
|
|
2341
2368
|
await this.write(
|
|
2342
|
-
`?[key, kind, name, file_path, start_line, signature, body, fan_in, fan_out, risk_level] <- [[$key, $kind, $name, $fp, $sl, $sig, $body, $fi, $fo, $rl]]
|
|
2343
|
-
:put entities { key => kind, name, file_path, start_line, signature, body, fan_in, fan_out, risk_level }`,
|
|
2369
|
+
`?[key, kind, name, file_path, start_line, end_line, signature, body, fan_in, fan_out, risk_level] <- [[$key, $kind, $name, $fp, $sl, $el, $sig, $body, $fi, $fo, $rl]]
|
|
2370
|
+
:put entities { key => kind, name, file_path, start_line, end_line, signature, body, fan_in, fan_out, risk_level }`,
|
|
2344
2371
|
{
|
|
2345
2372
|
key: entity.key,
|
|
2346
2373
|
kind: entity.kind,
|
|
2347
2374
|
name: entity.name,
|
|
2348
2375
|
fp: entity.file_path,
|
|
2349
2376
|
sl: entity.start_line ?? 0,
|
|
2377
|
+
el: entity.end_line ?? 0,
|
|
2350
2378
|
sig: entity.signature ?? "",
|
|
2351
2379
|
body: entity.body ?? "",
|
|
2352
2380
|
fi: entity.fan_in ?? 0,
|
|
@@ -2454,17 +2482,18 @@ var init_local_graph = __esm({
|
|
|
2454
2482
|
*/
|
|
2455
2483
|
async getEntity(key) {
|
|
2456
2484
|
const result = await this.query(
|
|
2457
|
-
"?[key, kind, name, fp, sl, sig, body, fi, fo, rl, community] := *entities{key, kind, name, file_path: fp, start_line: sl, signature: sig, body, fan_in: fi, fan_out: fo, risk_level: rl, community}, key = $key",
|
|
2485
|
+
"?[key, kind, name, fp, sl, el, sig, body, fi, fo, rl, community] := *entities{key, kind, name, file_path: fp, start_line: sl, end_line: el, signature: sig, body, fan_in: fi, fan_out: fo, risk_level: rl, community}, key = $key",
|
|
2458
2486
|
{ key }
|
|
2459
2487
|
);
|
|
2460
2488
|
if (result.rows.length === 0) return null;
|
|
2461
|
-
const [k, kind, name, fp, sl, sig, body, fi, fo, rl, community] = result.rows[0];
|
|
2489
|
+
const [k, kind, name, fp, sl, el, sig, body, fi, fo, rl, community] = result.rows[0];
|
|
2462
2490
|
return {
|
|
2463
2491
|
key: k,
|
|
2464
2492
|
kind,
|
|
2465
2493
|
name,
|
|
2466
2494
|
file_path: fp,
|
|
2467
2495
|
start_line: sl,
|
|
2496
|
+
end_line: el,
|
|
2468
2497
|
signature: sig,
|
|
2469
2498
|
body,
|
|
2470
2499
|
fan_in: fi,
|
|
@@ -2507,6 +2536,7 @@ var init_local_graph = __esm({
|
|
|
2507
2536
|
name,
|
|
2508
2537
|
file_path: fp,
|
|
2509
2538
|
start_line: sl,
|
|
2539
|
+
end_line: 0,
|
|
2510
2540
|
signature: sig,
|
|
2511
2541
|
body,
|
|
2512
2542
|
fan_in: fi,
|
|
@@ -2550,6 +2580,7 @@ var init_local_graph = __esm({
|
|
|
2550
2580
|
name,
|
|
2551
2581
|
file_path: fp,
|
|
2552
2582
|
start_line: sl,
|
|
2583
|
+
end_line: 0,
|
|
2553
2584
|
signature: sig,
|
|
2554
2585
|
body,
|
|
2555
2586
|
fan_in: fi,
|
|
@@ -2904,14 +2935,14 @@ var init_local_graph = __esm({
|
|
|
2904
2935
|
* Degree ranking excluding kind=="file" and kind=="module".
|
|
2905
2936
|
*/
|
|
2906
2937
|
async getCriticalNodes(topN = 10, communityId) {
|
|
2907
|
-
const query = communityId !== void 0 ? `?[key, name, fp, fi, fo, degree, community, label, rl] :=
|
|
2938
|
+
const query = communityId !== void 0 ? `?[key, name, fp, fi, fo, degree, community, label, rl, kind] :=
|
|
2908
2939
|
*entities{key, name, file_path: fp, fan_in: fi, fan_out: fo, community, risk_level: rl, kind},
|
|
2909
2940
|
kind != "file", kind != "module",
|
|
2910
2941
|
community = $cid, community >= 0,
|
|
2911
2942
|
*communities{id: community, label},
|
|
2912
2943
|
degree = fi + fo
|
|
2913
2944
|
:order -degree
|
|
2914
|
-
:limit $top_n` : `?[key, name, fp, fi, fo, degree, community, label, rl] :=
|
|
2945
|
+
:limit $top_n` : `?[key, name, fp, fi, fo, degree, community, label, rl, kind] :=
|
|
2915
2946
|
*entities{key, name, file_path: fp, fan_in: fi, fan_out: fo, community, risk_level: rl, kind},
|
|
2916
2947
|
kind != "file", kind != "module",
|
|
2917
2948
|
community >= 0,
|
|
@@ -2923,11 +2954,12 @@ var init_local_graph = __esm({
|
|
|
2923
2954
|
if (communityId !== void 0) params.cid = communityId;
|
|
2924
2955
|
const result = await this.query(query, params);
|
|
2925
2956
|
return result.rows.map((row) => {
|
|
2926
|
-
const [key, name, fp, fi, fo, degree, community, label, rl] = row;
|
|
2957
|
+
const [key, name, fp, fi, fo, degree, community, label, rl, kind] = row;
|
|
2927
2958
|
return {
|
|
2928
2959
|
key,
|
|
2929
2960
|
name,
|
|
2930
2961
|
file_path: fp,
|
|
2962
|
+
kind,
|
|
2931
2963
|
fan_in: fi,
|
|
2932
2964
|
fan_out: fo,
|
|
2933
2965
|
degree,
|
|
@@ -2954,19 +2986,20 @@ var init_local_graph = __esm({
|
|
|
2954
2986
|
*/
|
|
2955
2987
|
async getEntitiesByFile(filePath) {
|
|
2956
2988
|
const result = await this.query(
|
|
2957
|
-
`?[k, kind, name, fp, sl, sig, body, fi, fo, rl, community] := *file_index[$fp, ek],
|
|
2958
|
-
*entities{key: ek, kind, name, file_path: fp, start_line: sl, signature: sig, body, fan_in: fi, fan_out: fo, risk_level: rl, community},
|
|
2989
|
+
`?[k, kind, name, fp, sl, el, sig, body, fi, fo, rl, community] := *file_index[$fp, ek],
|
|
2990
|
+
*entities{key: ek, kind, name, file_path: fp, start_line: sl, end_line: el, signature: sig, body, fan_in: fi, fan_out: fo, risk_level: rl, community},
|
|
2959
2991
|
k = ek`,
|
|
2960
2992
|
{ fp: filePath }
|
|
2961
2993
|
);
|
|
2962
2994
|
return result.rows.map((row) => {
|
|
2963
|
-
const [k, kind, name, fp, sl, sig, body, fi, fo, rl, community] = row;
|
|
2995
|
+
const [k, kind, name, fp, sl, el, sig, body, fi, fo, rl, community] = row;
|
|
2964
2996
|
return {
|
|
2965
2997
|
key: k,
|
|
2966
2998
|
kind,
|
|
2967
2999
|
name,
|
|
2968
3000
|
file_path: fp,
|
|
2969
3001
|
start_line: sl,
|
|
3002
|
+
end_line: el,
|
|
2970
3003
|
signature: sig,
|
|
2971
3004
|
body,
|
|
2972
3005
|
fan_in: fi,
|
|
@@ -2982,17 +3015,18 @@ var init_local_graph = __esm({
|
|
|
2982
3015
|
*/
|
|
2983
3016
|
async findEntityByName(name) {
|
|
2984
3017
|
const result = await this.query(
|
|
2985
|
-
"?[k, kind, name, fp, sl, sig, body, fi, fo, rl, community] := *entities{key: k, kind, name, file_path: fp, start_line: sl, signature: sig, body, fan_in: fi, fan_out: fo, risk_level: rl, community}, name = $name :limit 1",
|
|
3018
|
+
"?[k, kind, name, fp, sl, el, sig, body, fi, fo, rl, community] := *entities{key: k, kind, name, file_path: fp, start_line: sl, end_line: el, signature: sig, body, fan_in: fi, fan_out: fo, risk_level: rl, community}, name = $name :limit 1",
|
|
2986
3019
|
{ name }
|
|
2987
3020
|
);
|
|
2988
3021
|
if (result.rows.length === 0) return null;
|
|
2989
|
-
const [k, kind, n, fp, sl, sig, body, fi, fo, rl, community] = result.rows[0];
|
|
3022
|
+
const [k, kind, n, fp, sl, el, sig, body, fi, fo, rl, community] = result.rows[0];
|
|
2990
3023
|
return {
|
|
2991
3024
|
key: k,
|
|
2992
3025
|
kind,
|
|
2993
3026
|
name: n,
|
|
2994
3027
|
file_path: fp,
|
|
2995
3028
|
start_line: sl,
|
|
3029
|
+
end_line: el,
|
|
2996
3030
|
signature: sig,
|
|
2997
3031
|
body,
|
|
2998
3032
|
fan_in: fi,
|
|
@@ -3704,14 +3738,15 @@ var init_local_graph = __esm({
|
|
|
3704
3738
|
const allEntities = [...delta.entities.added, ...delta.entities.updated];
|
|
3705
3739
|
for (const entity of allEntities) {
|
|
3706
3740
|
await this.write(
|
|
3707
|
-
`?[key, kind, name, file_path, start_line, signature, body, fan_in, fan_out, risk_level] <- [[$key, $kind, $name, $fp, $sl, $sig, $body, $fi, $fo, $rl]]
|
|
3708
|
-
:put entities { key => kind, name, file_path, start_line, signature, body, fan_in, fan_out, risk_level }`,
|
|
3741
|
+
`?[key, kind, name, file_path, start_line, end_line, signature, body, fan_in, fan_out, risk_level] <- [[$key, $kind, $name, $fp, $sl, $el, $sig, $body, $fi, $fo, $rl]]
|
|
3742
|
+
:put entities { key => kind, name, file_path, start_line, end_line, signature, body, fan_in, fan_out, risk_level }`,
|
|
3709
3743
|
{
|
|
3710
3744
|
key: entity.key,
|
|
3711
3745
|
kind: entity.kind,
|
|
3712
3746
|
name: entity.name,
|
|
3713
3747
|
fp: entity.file_path,
|
|
3714
3748
|
sl: entity.start_line ?? 0,
|
|
3749
|
+
el: entity.end_line ?? 0,
|
|
3715
3750
|
sig: entity.signature ?? "",
|
|
3716
3751
|
body: entity.body ?? "",
|
|
3717
3752
|
fi: entity.fan_in ?? 0,
|
|
@@ -4578,7 +4613,9 @@ function normalizeAgentName(input) {
|
|
|
4578
4613
|
code: "vscode",
|
|
4579
4614
|
vs: "vscode",
|
|
4580
4615
|
google: "antigravity",
|
|
4581
|
-
"google-antigravity": "antigravity"
|
|
4616
|
+
"google-antigravity": "antigravity",
|
|
4617
|
+
windsurf: "windsurf",
|
|
4618
|
+
cascade: "windsurf"
|
|
4582
4619
|
};
|
|
4583
4620
|
return aliases[input.toLowerCase()] ?? input.toLowerCase();
|
|
4584
4621
|
}
|
|
@@ -4626,14 +4663,15 @@ var init_agent_registry = __esm({
|
|
|
4626
4663
|
{
|
|
4627
4664
|
id: "windsurf",
|
|
4628
4665
|
name: "Windsurf",
|
|
4629
|
-
projectConfigPath: ".windsurf/
|
|
4666
|
+
projectConfigPath: ".codeium/windsurf/mcp_config.json",
|
|
4667
|
+
configScope: "global",
|
|
4630
4668
|
configFormat: "mcp-json",
|
|
4631
4669
|
dirMarkers: [".windsurf"],
|
|
4632
|
-
envVars: [],
|
|
4633
|
-
hookSupport:
|
|
4634
|
-
description: "Codeium's AI IDE",
|
|
4635
|
-
instructionFilePath:
|
|
4636
|
-
instructionFormat:
|
|
4670
|
+
envVars: ["WINDSURF_EDITOR"],
|
|
4671
|
+
hookSupport: true,
|
|
4672
|
+
description: "Codeium's AI IDE (Cascade)",
|
|
4673
|
+
instructionFilePath: ".windsurf/rules/unerr-instructions.md",
|
|
4674
|
+
instructionFormat: "windsurf-rule"
|
|
4637
4675
|
},
|
|
4638
4676
|
{
|
|
4639
4677
|
id: "zed",
|
|
@@ -4677,8 +4715,8 @@ var init_agent_registry = __esm({
|
|
|
4677
4715
|
projectConfigPath: ".gemini/settings.json",
|
|
4678
4716
|
configFormat: "settings-json",
|
|
4679
4717
|
dirMarkers: [".gemini"],
|
|
4680
|
-
envVars: ["GEMINI_API_KEY"],
|
|
4681
|
-
hookSupport:
|
|
4718
|
+
envVars: ["GEMINI_API_KEY", "GEMINI_CLI"],
|
|
4719
|
+
hookSupport: true,
|
|
4682
4720
|
description: "Google's CLI coding agent",
|
|
4683
4721
|
instructionFilePath: "GEMINI.md",
|
|
4684
4722
|
instructionFormat: "markdown"
|
|
@@ -4746,11 +4784,11 @@ var init_agent_registry = __esm({
|
|
|
4746
4784
|
{
|
|
4747
4785
|
id: "github-copilot-cli",
|
|
4748
4786
|
name: "GitHub Copilot CLI",
|
|
4749
|
-
projectConfigPath: ".
|
|
4750
|
-
configFormat: "
|
|
4751
|
-
dirMarkers: [".github"],
|
|
4787
|
+
projectConfigPath: ".copilot/mcp-config.json",
|
|
4788
|
+
configFormat: "copilot-json",
|
|
4789
|
+
dirMarkers: [".github", ".copilot"],
|
|
4752
4790
|
envVars: ["GITHUB_COPILOT_TOKEN"],
|
|
4753
|
-
hookSupport:
|
|
4791
|
+
hookSupport: true,
|
|
4754
4792
|
description: "GitHub's CLI AI assistant",
|
|
4755
4793
|
instructionFilePath: ".github/copilot-instructions.md",
|
|
4756
4794
|
instructionFormat: "markdown"
|
|
@@ -4794,6 +4832,7 @@ __export(mcp_config_writer_exports, {
|
|
|
4794
4832
|
writeMcpConfig: () => writeMcpConfig
|
|
4795
4833
|
});
|
|
4796
4834
|
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
|
|
4835
|
+
import { homedir } from "os";
|
|
4797
4836
|
import { dirname as dirname2, join as join7 } from "path";
|
|
4798
4837
|
function createUnerrServerEntry() {
|
|
4799
4838
|
return {
|
|
@@ -4802,12 +4841,19 @@ function createUnerrServerEntry() {
|
|
|
4802
4841
|
args: ["--mcp"]
|
|
4803
4842
|
};
|
|
4804
4843
|
}
|
|
4844
|
+
function createCopilotServerEntry() {
|
|
4845
|
+
return {
|
|
4846
|
+
type: "local",
|
|
4847
|
+
command: "unerr",
|
|
4848
|
+
args: ["--mcp"]
|
|
4849
|
+
};
|
|
4850
|
+
}
|
|
4805
4851
|
function writeMcpConfig(cwd, ide) {
|
|
4806
4852
|
const agent = getAgent(ide);
|
|
4807
4853
|
if (!agent) {
|
|
4808
4854
|
return { path: "", action: "skipped" };
|
|
4809
4855
|
}
|
|
4810
|
-
const configPath = join7(cwd, agent.projectConfigPath);
|
|
4856
|
+
const configPath = agent.configScope === "global" ? join7(homedir(), agent.projectConfigPath) : join7(cwd, agent.projectConfigPath);
|
|
4811
4857
|
const dir = dirname2(configPath);
|
|
4812
4858
|
if (!existsSync5(dir)) {
|
|
4813
4859
|
mkdirSync3(dir, { recursive: true });
|
|
@@ -4817,6 +4863,8 @@ function writeMcpConfig(cwd, ide) {
|
|
|
4817
4863
|
return writeMcpJsonFormat(configPath);
|
|
4818
4864
|
case "settings-json":
|
|
4819
4865
|
return writeSettingsJsonFormat(configPath);
|
|
4866
|
+
case "copilot-json":
|
|
4867
|
+
return writeCopilotJsonFormat(configPath);
|
|
4820
4868
|
case "continue-config":
|
|
4821
4869
|
return writeContinueFormat(configPath);
|
|
4822
4870
|
default:
|
|
@@ -4832,7 +4880,7 @@ function writeAllMcpConfigs(cwd, agents) {
|
|
|
4832
4880
|
function removeMcpConfig(cwd, ide) {
|
|
4833
4881
|
const agent = getAgent(ide);
|
|
4834
4882
|
if (!agent) return false;
|
|
4835
|
-
const configPath = join7(cwd, agent.projectConfigPath);
|
|
4883
|
+
const configPath = agent.configScope === "global" ? join7(homedir(), agent.projectConfigPath) : join7(cwd, agent.projectConfigPath);
|
|
4836
4884
|
if (!existsSync5(configPath)) return false;
|
|
4837
4885
|
try {
|
|
4838
4886
|
const existing = JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
@@ -4844,6 +4892,9 @@ function removeMcpConfig(cwd, ide) {
|
|
|
4844
4892
|
} else if (agent.configFormat === "settings-json") {
|
|
4845
4893
|
if (!existing.mcp?.servers?.[UNERR_SERVER_KEY]) return false;
|
|
4846
4894
|
delete existing.mcp.servers[UNERR_SERVER_KEY];
|
|
4895
|
+
} else if (agent.configFormat === "copilot-json") {
|
|
4896
|
+
if (!existing.mcpServers?.[UNERR_SERVER_KEY]) return false;
|
|
4897
|
+
delete existing.mcpServers[UNERR_SERVER_KEY];
|
|
4847
4898
|
} else {
|
|
4848
4899
|
if (!existing.mcpServers?.[UNERR_SERVER_KEY]) return false;
|
|
4849
4900
|
delete existing.mcpServers[UNERR_SERVER_KEY];
|
|
@@ -4857,7 +4908,7 @@ function removeMcpConfig(cwd, ide) {
|
|
|
4857
4908
|
function isConfigured(cwd, ide) {
|
|
4858
4909
|
const agent = getAgent(ide);
|
|
4859
4910
|
if (!agent) return false;
|
|
4860
|
-
const configPath = join7(cwd, agent.projectConfigPath);
|
|
4911
|
+
const configPath = agent.configScope === "global" ? join7(homedir(), agent.projectConfigPath) : join7(cwd, agent.projectConfigPath);
|
|
4861
4912
|
if (!existsSync5(configPath)) return false;
|
|
4862
4913
|
try {
|
|
4863
4914
|
const existing = JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
@@ -4869,6 +4920,9 @@ function isConfigured(cwd, ide) {
|
|
|
4869
4920
|
if (agent.configFormat === "settings-json") {
|
|
4870
4921
|
return !!existing.mcp?.servers?.[UNERR_SERVER_KEY];
|
|
4871
4922
|
}
|
|
4923
|
+
if (agent.configFormat === "copilot-json") {
|
|
4924
|
+
return !!existing.mcpServers?.[UNERR_SERVER_KEY];
|
|
4925
|
+
}
|
|
4872
4926
|
return !!existing.mcpServers?.[UNERR_SERVER_KEY];
|
|
4873
4927
|
} catch {
|
|
4874
4928
|
return false;
|
|
@@ -4885,6 +4939,14 @@ function generateConfigSnippet(ide) {
|
|
|
4885
4939
|
null,
|
|
4886
4940
|
2
|
|
4887
4941
|
);
|
|
4942
|
+
case "copilot-json": {
|
|
4943
|
+
const copilotEntry = createCopilotServerEntry();
|
|
4944
|
+
return JSON.stringify(
|
|
4945
|
+
{ mcpServers: { [UNERR_SERVER_KEY]: copilotEntry } },
|
|
4946
|
+
null,
|
|
4947
|
+
2
|
|
4948
|
+
);
|
|
4949
|
+
}
|
|
4888
4950
|
case "continue-config":
|
|
4889
4951
|
return JSON.stringify(
|
|
4890
4952
|
{ mcpServers: [{ name: UNERR_SERVER_KEY, ...entry }] },
|
|
@@ -4975,6 +5037,29 @@ function writeContinueFormat(configPath) {
|
|
|
4975
5037
|
writeFileSync2(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
4976
5038
|
return { path: configPath, action: "created" };
|
|
4977
5039
|
}
|
|
5040
|
+
function writeCopilotJsonFormat(configPath) {
|
|
5041
|
+
if (existsSync5(configPath)) {
|
|
5042
|
+
try {
|
|
5043
|
+
const existing = JSON.parse(
|
|
5044
|
+
readFileSync6(configPath, "utf-8")
|
|
5045
|
+
);
|
|
5046
|
+
if (existing.mcpServers?.[UNERR_SERVER_KEY]) {
|
|
5047
|
+
return { path: configPath, action: "skipped" };
|
|
5048
|
+
}
|
|
5049
|
+
existing.mcpServers = existing.mcpServers ?? {};
|
|
5050
|
+
existing.mcpServers[UNERR_SERVER_KEY] = createCopilotServerEntry();
|
|
5051
|
+
writeFileSync2(configPath, JSON.stringify(existing, null, 2), "utf-8");
|
|
5052
|
+
return { path: configPath, action: "updated" };
|
|
5053
|
+
} catch {
|
|
5054
|
+
return { path: configPath, action: "skipped" };
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5057
|
+
const config = {
|
|
5058
|
+
mcpServers: { [UNERR_SERVER_KEY]: createCopilotServerEntry() }
|
|
5059
|
+
};
|
|
5060
|
+
writeFileSync2(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
5061
|
+
return { path: configPath, action: "created" };
|
|
5062
|
+
}
|
|
4978
5063
|
var UNERR_SERVER_KEY;
|
|
4979
5064
|
var init_mcp_config_writer = __esm({
|
|
4980
5065
|
"src/config/mcp-config-writer.ts"() {
|
|
@@ -5375,7 +5460,7 @@ __export(settings_exports, {
|
|
|
5375
5460
|
resolveInferenceEndpoint: () => resolveInferenceEndpoint
|
|
5376
5461
|
});
|
|
5377
5462
|
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
5378
|
-
import { homedir as
|
|
5463
|
+
import { homedir as homedir4 } from "os";
|
|
5379
5464
|
import { join as join12 } from "path";
|
|
5380
5465
|
import { z } from "zod";
|
|
5381
5466
|
function resolveEmbeddingEndpoint(config) {
|
|
@@ -5406,7 +5491,7 @@ function loadJsonFile(filePath) {
|
|
|
5406
5491
|
}
|
|
5407
5492
|
function loadSettings(cwd) {
|
|
5408
5493
|
const projectDir = cwd ?? process.cwd();
|
|
5409
|
-
const userSettings = loadJsonFile(join12(
|
|
5494
|
+
const userSettings = loadJsonFile(join12(homedir4(), ".unerr", "settings.json"));
|
|
5410
5495
|
const projectSettings = loadJsonFile(
|
|
5411
5496
|
join12(projectDir, ".unerr", "settings.json")
|
|
5412
5497
|
);
|
|
@@ -6562,6 +6647,7 @@ __export(ast_extractor_exports, {
|
|
|
6562
6647
|
import { createHash } from "crypto";
|
|
6563
6648
|
import { existsSync as existsSync14 } from "fs";
|
|
6564
6649
|
import { join as join19 } from "path";
|
|
6650
|
+
import { fileURLToPath } from "url";
|
|
6565
6651
|
function detectLanguage2(filePath) {
|
|
6566
6652
|
const dot = filePath.lastIndexOf(".");
|
|
6567
6653
|
if (dot < 0) return null;
|
|
@@ -6574,13 +6660,18 @@ function extractEntities(content, filePath) {
|
|
|
6574
6660
|
const lines = content.split("\n");
|
|
6575
6661
|
const entities = [];
|
|
6576
6662
|
const patterns = getPatterns(language);
|
|
6663
|
+
let currentClassName = null;
|
|
6577
6664
|
for (let i = 0; i < lines.length; i++) {
|
|
6578
6665
|
const line = lines[i] ?? "";
|
|
6579
6666
|
for (const pattern of patterns) {
|
|
6580
6667
|
const match = pattern.regex.exec(line);
|
|
6581
6668
|
if (match) {
|
|
6582
|
-
const
|
|
6583
|
-
if (!
|
|
6669
|
+
const rawName = match[pattern.nameGroup] ?? "";
|
|
6670
|
+
if (!rawName || rawName.length === 0) continue;
|
|
6671
|
+
if (pattern.kind === "class") {
|
|
6672
|
+
currentClassName = rawName;
|
|
6673
|
+
}
|
|
6674
|
+
const name = pattern.kind === "method" && currentClassName ? `${currentClassName}.${rawName}` : rawName;
|
|
6584
6675
|
const signature = match[pattern.sigGroup ?? 0] ?? "";
|
|
6585
6676
|
const lineStart = i + 1;
|
|
6586
6677
|
const lineEnd = findBlockEnd(lines, i, language);
|
|
@@ -6593,7 +6684,8 @@ function extractEntities(content, filePath) {
|
|
|
6593
6684
|
signature: signature.trim(),
|
|
6594
6685
|
line_start: lineStart,
|
|
6595
6686
|
line_end: lineEnd,
|
|
6596
|
-
content_hash: contentHash
|
|
6687
|
+
content_hash: contentHash,
|
|
6688
|
+
parent_class: pattern.kind === "method" ? currentClassName ?? void 0 : void 0
|
|
6597
6689
|
});
|
|
6598
6690
|
break;
|
|
6599
6691
|
}
|
|
@@ -6625,9 +6717,9 @@ function getPatterns(language) {
|
|
|
6625
6717
|
kind: "interface",
|
|
6626
6718
|
nameGroup: 1
|
|
6627
6719
|
},
|
|
6628
|
-
// method(params) { — inside class
|
|
6720
|
+
// method(params) { — inside class (with optional access modifiers)
|
|
6629
6721
|
{
|
|
6630
|
-
regex: /^\s+(?:async\s+)?(?:static\s+)?(?:get\s+|set\s+)?(\w+)\s*(\([^)]*\))\s*(?::\s*\S+\s*)?[{]/,
|
|
6722
|
+
regex: /^\s+(?:(?:private|protected|public|override|abstract|readonly)\s+)*(?:async\s+)?(?:static\s+)?(?:get\s+|set\s+)?(\w+)\s*(\([^)]*\))\s*(?::\s*\S+\s*)?[{]/,
|
|
6631
6723
|
kind: "method",
|
|
6632
6724
|
nameGroup: 1,
|
|
6633
6725
|
sigGroup: 2
|
|
@@ -6949,10 +7041,16 @@ async function getTSParser(grammar) {
|
|
|
6949
7041
|
}
|
|
6950
7042
|
const parser = new TreeSitter();
|
|
6951
7043
|
const wasmFile = `tree-sitter-${grammar}.wasm`;
|
|
7044
|
+
const pkgDir = import.meta.dirname ?? join19(fileURLToPath(import.meta.url), "..");
|
|
7045
|
+
const pkgRoot = join19(pkgDir, "..");
|
|
6952
7046
|
const possiblePaths = [
|
|
6953
7047
|
join19(process.cwd(), "node_modules", "tree-sitter-wasms", "out", wasmFile),
|
|
6954
7048
|
join19(process.cwd(), "node_modules", `tree-sitter-${grammar}`, wasmFile),
|
|
6955
|
-
join19(process.cwd(), "node_modules", "web-tree-sitter", wasmFile)
|
|
7049
|
+
join19(process.cwd(), "node_modules", "web-tree-sitter", wasmFile),
|
|
7050
|
+
// Package-relative paths (when unerr is installed globally or in another project)
|
|
7051
|
+
join19(pkgRoot, "node_modules", "tree-sitter-wasms", "out", wasmFile),
|
|
7052
|
+
join19(pkgRoot, "node_modules", `tree-sitter-${grammar}`, wasmFile),
|
|
7053
|
+
join19(pkgRoot, "node_modules", "web-tree-sitter", wasmFile)
|
|
6956
7054
|
];
|
|
6957
7055
|
let wasmPath = null;
|
|
6958
7056
|
for (const p of possiblePaths) {
|
|
@@ -9009,12 +9107,12 @@ var init_decoder = __esm({
|
|
|
9009
9107
|
// src/intelligence/indexer/scip/downloader.ts
|
|
9010
9108
|
import { chmodSync as chmodSync2, createWriteStream, existsSync as existsSync15, mkdirSync as mkdirSync11 } from "fs";
|
|
9011
9109
|
import { rename, rm, stat } from "fs/promises";
|
|
9012
|
-
import { homedir as
|
|
9110
|
+
import { homedir as homedir5 } from "os";
|
|
9013
9111
|
import { join as join20 } from "path";
|
|
9014
9112
|
import { pipeline } from "stream/promises";
|
|
9015
9113
|
import { createGunzip } from "zlib";
|
|
9016
9114
|
function getScipBinDir() {
|
|
9017
|
-
return join20(
|
|
9115
|
+
return join20(homedir5(), ".unerr", "bin");
|
|
9018
9116
|
}
|
|
9019
9117
|
function getCachedBinaryPath(language) {
|
|
9020
9118
|
const spec = DOWNLOAD_SPECS[language];
|
|
@@ -9720,6 +9818,10 @@ var init_orchestrator = __esm({
|
|
|
9720
9818
|
});
|
|
9721
9819
|
|
|
9722
9820
|
// src/intelligence/local-convention-detector.ts
|
|
9821
|
+
var local_convention_detector_exports = {};
|
|
9822
|
+
__export(local_convention_detector_exports, {
|
|
9823
|
+
detectLocalConventions: () => detectLocalConventions
|
|
9824
|
+
});
|
|
9723
9825
|
import { dirname as dirname4 } from "path";
|
|
9724
9826
|
async function detectLocalConventions(db) {
|
|
9725
9827
|
const start = Date.now();
|
|
@@ -10281,6 +10383,7 @@ async function indexLocalProject(projectRoot, graphStore, repoId, opts) {
|
|
|
10281
10383
|
name: entity.name,
|
|
10282
10384
|
file_path: relPath,
|
|
10283
10385
|
start_line: entity.line_start,
|
|
10386
|
+
end_line: entity.line_end,
|
|
10284
10387
|
signature: entity.signature,
|
|
10285
10388
|
body: "",
|
|
10286
10389
|
// Skip body storage for indexing performance
|
|
@@ -10444,6 +10547,7 @@ async function reindexFile(projectRoot, filePath, graphStore, repoId) {
|
|
|
10444
10547
|
name: e.name,
|
|
10445
10548
|
file_path: relPath,
|
|
10446
10549
|
start_line: e.line_start,
|
|
10550
|
+
end_line: e.line_end,
|
|
10447
10551
|
signature: e.signature,
|
|
10448
10552
|
body: "",
|
|
10449
10553
|
fan_in: 0,
|
|
@@ -10860,8 +10964,8 @@ async function populateCozoDB(graphStore, entities, edges) {
|
|
|
10860
10964
|
for (const fp of filePaths) {
|
|
10861
10965
|
try {
|
|
10862
10966
|
await graphStore.db.run(
|
|
10863
|
-
`?[key, kind, name, file_path, start_line, signature, body, fan_in, fan_out, risk_level, is_test] <- [[$key, "module", $name, $fp, 0, "", "", 0, 0, "normal", $is_test]]
|
|
10864
|
-
:put entities { key => kind, name, file_path, start_line, signature, body, fan_in, fan_out, risk_level, is_test }`,
|
|
10967
|
+
`?[key, kind, name, file_path, start_line, end_line, signature, body, fan_in, fan_out, risk_level, is_test] <- [[$key, "module", $name, $fp, 0, 0, "", "", 0, 0, "normal", $is_test]]
|
|
10968
|
+
:put entities { key => kind, name, file_path, start_line, end_line, signature, body, fan_in, fan_out, risk_level, is_test }`,
|
|
10865
10969
|
{ key: `file:${fp}`, name: basename2(fp), fp, is_test: isTestFile(fp) }
|
|
10866
10970
|
);
|
|
10867
10971
|
} catch (err) {
|
|
@@ -10909,14 +11013,15 @@ async function populateCozoDB(graphStore, entities, edges) {
|
|
|
10909
11013
|
async function insertEntity(graphStore, entity) {
|
|
10910
11014
|
try {
|
|
10911
11015
|
await graphStore.db.run(
|
|
10912
|
-
`?[key, kind, name, file_path, start_line, signature, body, fan_in, fan_out, risk_level, is_test] <- [[$key, $kind, $name, $fp, $sl, $sig, $body, $fi, $fo, $rl, $is_test]]
|
|
10913
|
-
:put entities { key => kind, name, file_path, start_line, signature, body, fan_in, fan_out, risk_level, is_test }`,
|
|
11016
|
+
`?[key, kind, name, file_path, start_line, end_line, signature, body, fan_in, fan_out, risk_level, is_test] <- [[$key, $kind, $name, $fp, $sl, $el, $sig, $body, $fi, $fo, $rl, $is_test]]
|
|
11017
|
+
:put entities { key => kind, name, file_path, start_line, end_line, signature, body, fan_in, fan_out, risk_level, is_test }`,
|
|
10914
11018
|
{
|
|
10915
11019
|
key: entity.key,
|
|
10916
11020
|
kind: entity.kind,
|
|
10917
11021
|
name: entity.name,
|
|
10918
11022
|
fp: entity.file_path,
|
|
10919
11023
|
sl: entity.start_line ?? 0,
|
|
11024
|
+
el: entity.end_line ?? 0,
|
|
10920
11025
|
sig: entity.signature ?? "",
|
|
10921
11026
|
body: entity.body ?? "",
|
|
10922
11027
|
fi: entity.fan_in ?? 0,
|
|
@@ -11737,7 +11842,7 @@ import {
|
|
|
11737
11842
|
unlinkSync as unlinkSync4,
|
|
11738
11843
|
writeFileSync as writeFileSync12
|
|
11739
11844
|
} from "fs";
|
|
11740
|
-
import { homedir as
|
|
11845
|
+
import { homedir as homedir6 } from "os";
|
|
11741
11846
|
import { dirname as dirname6, join as join29 } from "path";
|
|
11742
11847
|
function getSkillDir(ide, cwd) {
|
|
11743
11848
|
switch (ide) {
|
|
@@ -11761,9 +11866,9 @@ function getSkillDir(ide, cwd) {
|
|
|
11761
11866
|
};
|
|
11762
11867
|
case "windsurf":
|
|
11763
11868
|
return {
|
|
11764
|
-
dir: join29(cwd, ".windsurf", "
|
|
11869
|
+
dir: join29(cwd, ".windsurf", "skills"),
|
|
11765
11870
|
ext: ".md",
|
|
11766
|
-
dirPerSkill:
|
|
11871
|
+
dirPerSkill: true
|
|
11767
11872
|
};
|
|
11768
11873
|
case "zed":
|
|
11769
11874
|
return {
|
|
@@ -11771,6 +11876,18 @@ function getSkillDir(ide, cwd) {
|
|
|
11771
11876
|
ext: ".md",
|
|
11772
11877
|
dirPerSkill: false
|
|
11773
11878
|
};
|
|
11879
|
+
case "gemini-cli":
|
|
11880
|
+
return {
|
|
11881
|
+
dir: join29(cwd, ".gemini", "skills"),
|
|
11882
|
+
ext: ".md",
|
|
11883
|
+
dirPerSkill: true
|
|
11884
|
+
};
|
|
11885
|
+
case "github-copilot-cli":
|
|
11886
|
+
return {
|
|
11887
|
+
dir: join29(cwd, ".github", "skills"),
|
|
11888
|
+
ext: ".md",
|
|
11889
|
+
dirPerSkill: true
|
|
11890
|
+
};
|
|
11774
11891
|
case "antigravity":
|
|
11775
11892
|
return {
|
|
11776
11893
|
dir: join29(cwd, ".agents", "skills"),
|
|
@@ -11791,7 +11908,22 @@ function writeSkillFile(skill, ide, skillDir, ext, dirPerSkill) {
|
|
|
11791
11908
|
const skillName = `unerr-${baseName}`;
|
|
11792
11909
|
let filePath;
|
|
11793
11910
|
let content;
|
|
11794
|
-
if (dirPerSkill && ide === "
|
|
11911
|
+
if (dirPerSkill && ide === "windsurf") {
|
|
11912
|
+
const skillSubDir = join29(skillDir, skillName);
|
|
11913
|
+
mkdirSync16(skillSubDir, { recursive: true });
|
|
11914
|
+
filePath = join29(skillSubDir, "SKILL.md");
|
|
11915
|
+
content = formatWindsurfSkillMd(skill);
|
|
11916
|
+
} else if (dirPerSkill && ide === "gemini-cli") {
|
|
11917
|
+
const skillSubDir = join29(skillDir, skillName);
|
|
11918
|
+
mkdirSync16(skillSubDir, { recursive: true });
|
|
11919
|
+
filePath = join29(skillSubDir, "SKILL.md");
|
|
11920
|
+
content = formatGeminiSkill(skill);
|
|
11921
|
+
} else if (dirPerSkill && ide === "github-copilot-cli") {
|
|
11922
|
+
const skillSubDir = join29(skillDir, skillName);
|
|
11923
|
+
mkdirSync16(skillSubDir, { recursive: true });
|
|
11924
|
+
filePath = join29(skillSubDir, "SKILL.md");
|
|
11925
|
+
content = formatCopilotSkill(skill);
|
|
11926
|
+
} else if (dirPerSkill && ide === "antigravity") {
|
|
11795
11927
|
const skillSubDir = join29(skillDir, skillName);
|
|
11796
11928
|
mkdirSync16(skillSubDir, { recursive: true });
|
|
11797
11929
|
filePath = join29(skillSubDir, "SKILL.md");
|
|
@@ -11804,9 +11936,6 @@ function writeSkillFile(skill, ide, skillDir, ext, dirPerSkill) {
|
|
|
11804
11936
|
} else if (ide === "cursor" && ext === ".mdc") {
|
|
11805
11937
|
filePath = join29(skillDir, `${skillName}${ext}`);
|
|
11806
11938
|
content = formatCursorSkill(skill);
|
|
11807
|
-
} else if (ide === "windsurf") {
|
|
11808
|
-
filePath = join29(skillDir, `${skillName}${ext}`);
|
|
11809
|
-
content = formatWindsurfSkill(skill);
|
|
11810
11939
|
} else {
|
|
11811
11940
|
filePath = join29(skillDir, `${skillName}${ext}`);
|
|
11812
11941
|
content = formatMarkdownSkill(skill);
|
|
@@ -11888,53 +12017,45 @@ alwaysApply: ${alwaysApply}
|
|
|
11888
12017
|
${skill.content}
|
|
11889
12018
|
`;
|
|
11890
12019
|
}
|
|
11891
|
-
function
|
|
12020
|
+
function formatMarkdownSkill(skill) {
|
|
11892
12021
|
const trigger = skill.trigger;
|
|
11893
12022
|
const triggerType = trigger?.type ?? "always";
|
|
11894
|
-
let wsType;
|
|
11895
|
-
switch (triggerType) {
|
|
11896
|
-
case "always":
|
|
11897
|
-
wsType = "always_on";
|
|
11898
|
-
break;
|
|
11899
|
-
case "auto":
|
|
11900
|
-
wsType = "glob";
|
|
11901
|
-
break;
|
|
11902
|
-
case "agent-requested":
|
|
11903
|
-
wsType = "model_decision";
|
|
11904
|
-
break;
|
|
11905
|
-
case "manual":
|
|
11906
|
-
wsType = "manual";
|
|
11907
|
-
break;
|
|
11908
|
-
default:
|
|
11909
|
-
wsType = "always_on";
|
|
11910
|
-
}
|
|
11911
12023
|
const globLine = trigger?.globs && trigger.globs.length > 0 ? `
|
|
11912
12024
|
globs: ${JSON.stringify(trigger.globs)}` : "";
|
|
11913
|
-
const truncatedContent = skill.content.length > 5800 ? `${skill.content.slice(0, 5800)}
|
|
11914
|
-
|
|
11915
|
-
[Truncated \u2014 full content exceeds Windsurf 6K limit]` : skill.content;
|
|
11916
12025
|
return `---
|
|
11917
|
-
trigger: ${wsType}${globLine}
|
|
11918
12026
|
description: "${skill.description}"
|
|
12027
|
+
trigger: ${triggerType}${globLine}
|
|
12028
|
+
version: "${skill.version ?? "1.0.0"}"
|
|
11919
12029
|
---
|
|
11920
12030
|
|
|
11921
12031
|
# ${skill.name}
|
|
11922
12032
|
|
|
11923
|
-
${
|
|
12033
|
+
${skill.content}
|
|
11924
12034
|
`;
|
|
11925
12035
|
}
|
|
11926
|
-
function
|
|
11927
|
-
const trigger = skill.trigger;
|
|
11928
|
-
const triggerType = trigger?.type ?? "always";
|
|
11929
|
-
const globLine = trigger?.globs && trigger.globs.length > 0 ? `
|
|
11930
|
-
globs: ${JSON.stringify(trigger.globs)}` : "";
|
|
12036
|
+
function formatWindsurfSkillMd(skill) {
|
|
11931
12037
|
return `---
|
|
11932
|
-
|
|
11933
|
-
|
|
11934
|
-
version: "${skill.version ?? "1.0.0"}"
|
|
12038
|
+
name: ${skill.name}
|
|
12039
|
+
description: ${skill.description}
|
|
11935
12040
|
---
|
|
11936
12041
|
|
|
11937
|
-
|
|
12042
|
+
${skill.content}
|
|
12043
|
+
`;
|
|
12044
|
+
}
|
|
12045
|
+
function formatGeminiSkill(skill) {
|
|
12046
|
+
return `---
|
|
12047
|
+
name: ${skill.name}
|
|
12048
|
+
description: ${skill.description}
|
|
12049
|
+
---
|
|
12050
|
+
|
|
12051
|
+
${skill.content}
|
|
12052
|
+
`;
|
|
12053
|
+
}
|
|
12054
|
+
function formatCopilotSkill(skill) {
|
|
12055
|
+
return `---
|
|
12056
|
+
name: ${skill.name}
|
|
12057
|
+
description: ${skill.description}
|
|
12058
|
+
---
|
|
11938
12059
|
|
|
11939
12060
|
${skill.content}
|
|
11940
12061
|
`;
|
|
@@ -12020,7 +12141,7 @@ function scanSkillDirectory(dir) {
|
|
|
12020
12141
|
return skills;
|
|
12021
12142
|
}
|
|
12022
12143
|
function loadLocalDirectorySkills(cwd) {
|
|
12023
|
-
const userDir = join29(
|
|
12144
|
+
const userDir = join29(homedir6(), ".unerr", "skills");
|
|
12024
12145
|
const projectDir = join29(cwd, ".unerr", "skills");
|
|
12025
12146
|
const userSkills = scanSkillDirectory(userDir);
|
|
12026
12147
|
const projectSkills = scanSkillDirectory(projectDir);
|
|
@@ -13085,9 +13206,9 @@ function createEmptyAllTime() {
|
|
|
13085
13206
|
function loadStats() {
|
|
13086
13207
|
const currentWeek = getWeekStart();
|
|
13087
13208
|
try {
|
|
13088
|
-
const { readFileSync:
|
|
13209
|
+
const { readFileSync: readFileSync57 } = __require("fs");
|
|
13089
13210
|
const raw = JSON.parse(
|
|
13090
|
-
|
|
13211
|
+
readFileSync57(getStatsPath(), "utf-8")
|
|
13091
13212
|
);
|
|
13092
13213
|
if (raw.version !== 1) {
|
|
13093
13214
|
return {
|
|
@@ -13800,9 +13921,9 @@ function getCumulativePath() {
|
|
|
13800
13921
|
function loadCumulativeStats() {
|
|
13801
13922
|
const currentWeek = getWeekStart2();
|
|
13802
13923
|
try {
|
|
13803
|
-
const { readFileSync:
|
|
13924
|
+
const { readFileSync: readFileSync57 } = __require("fs");
|
|
13804
13925
|
const raw = JSON.parse(
|
|
13805
|
-
|
|
13926
|
+
readFileSync57(getCumulativePath(), "utf-8")
|
|
13806
13927
|
);
|
|
13807
13928
|
if (raw.weekStart !== currentWeek) {
|
|
13808
13929
|
return {
|
|
@@ -13863,9 +13984,9 @@ function createEmptyCumulativeLocal(weekStart) {
|
|
|
13863
13984
|
function loadCumulativeLocalStats() {
|
|
13864
13985
|
const currentWeek = getWeekStart2();
|
|
13865
13986
|
try {
|
|
13866
|
-
const { readFileSync:
|
|
13987
|
+
const { readFileSync: readFileSync57 } = __require("fs");
|
|
13867
13988
|
const raw = JSON.parse(
|
|
13868
|
-
|
|
13989
|
+
readFileSync57(getCumulativeLocalPath(), "utf-8")
|
|
13869
13990
|
);
|
|
13870
13991
|
if (raw.weekStartDate !== currentWeek) {
|
|
13871
13992
|
return createEmptyCumulativeLocal(currentWeek);
|
|
@@ -16521,6 +16642,250 @@ var init_record_fact = __esm({
|
|
|
16521
16642
|
}
|
|
16522
16643
|
});
|
|
16523
16644
|
|
|
16645
|
+
// src/intelligence/fact-generator.ts
|
|
16646
|
+
var fact_generator_exports = {};
|
|
16647
|
+
__export(fact_generator_exports, {
|
|
16648
|
+
generateFromCausalBridge: () => generateFromCausalBridge,
|
|
16649
|
+
generateFromConventions: () => generateFromConventions,
|
|
16650
|
+
generateFromNegativeKnowledge: () => generateFromNegativeKnowledge,
|
|
16651
|
+
generateFromSessionAnalysis: () => generateFromSessionAnalysis,
|
|
16652
|
+
runFactGenerationPipeline: () => runFactGenerationPipeline
|
|
16653
|
+
});
|
|
16654
|
+
import { existsSync as existsSync37, readdirSync as readdirSync9, readFileSync as readFileSync34 } from "fs";
|
|
16655
|
+
import { join as join44 } from "path";
|
|
16656
|
+
async function runFactGenerationPipeline(factStore2, unerrDir) {
|
|
16657
|
+
const results = [];
|
|
16658
|
+
try {
|
|
16659
|
+
const sessionResult = await generateFromSessionAnalysis(factStore2, unerrDir);
|
|
16660
|
+
if (sessionResult) results.push(sessionResult);
|
|
16661
|
+
} catch {
|
|
16662
|
+
}
|
|
16663
|
+
return results;
|
|
16664
|
+
}
|
|
16665
|
+
async function generateFromConventions(factStore2, conventions) {
|
|
16666
|
+
let created = 0;
|
|
16667
|
+
let reinforced = 0;
|
|
16668
|
+
const details = [];
|
|
16669
|
+
const MIN_CONFIDENCE = 0.7;
|
|
16670
|
+
const MIN_FREQUENCY = 5;
|
|
16671
|
+
for (const conv of conventions) {
|
|
16672
|
+
if (conv.confidence < MIN_CONFIDENCE) continue;
|
|
16673
|
+
if (conv.frequency < MIN_FREQUENCY) continue;
|
|
16674
|
+
const content = formatConventionFact(conv);
|
|
16675
|
+
if (content.length > 280) continue;
|
|
16676
|
+
const input = {
|
|
16677
|
+
fact_type: "semantic",
|
|
16678
|
+
scope: inferScopeFromConvention(conv),
|
|
16679
|
+
subject: conv.name,
|
|
16680
|
+
content,
|
|
16681
|
+
source: "convention_detector",
|
|
16682
|
+
base_confidence: Math.min(0.9, conv.confidence)
|
|
16683
|
+
};
|
|
16684
|
+
const factId = await factStore2.createFact(input);
|
|
16685
|
+
if (factId) {
|
|
16686
|
+
const isNew = !details.some((d) => d.includes(conv.name));
|
|
16687
|
+
if (isNew) {
|
|
16688
|
+
created++;
|
|
16689
|
+
details.push(`[convention] ${conv.name}: ${content.slice(0, 80)}`);
|
|
16690
|
+
} else {
|
|
16691
|
+
reinforced++;
|
|
16692
|
+
}
|
|
16693
|
+
}
|
|
16694
|
+
}
|
|
16695
|
+
return { created, reinforced, source: "convention_detector", details };
|
|
16696
|
+
}
|
|
16697
|
+
async function generateFromNegativeKnowledge(factStore2, corrections) {
|
|
16698
|
+
let created = 0;
|
|
16699
|
+
let reinforced = 0;
|
|
16700
|
+
const details = [];
|
|
16701
|
+
for (const correction of corrections) {
|
|
16702
|
+
const content = correction.reason.slice(0, 280);
|
|
16703
|
+
const input = {
|
|
16704
|
+
fact_type: "negative",
|
|
16705
|
+
scope: correction.entityKey,
|
|
16706
|
+
subject: correction.entityKey,
|
|
16707
|
+
content,
|
|
16708
|
+
source: "negative_knowledge",
|
|
16709
|
+
base_confidence: 0.9
|
|
16710
|
+
};
|
|
16711
|
+
const factId = await factStore2.createFact(input);
|
|
16712
|
+
if (factId) {
|
|
16713
|
+
created++;
|
|
16714
|
+
details.push(`[negative] ${correction.entityKey}: ${content.slice(0, 60)}`);
|
|
16715
|
+
}
|
|
16716
|
+
}
|
|
16717
|
+
return { created, reinforced, source: "negative_knowledge", details };
|
|
16718
|
+
}
|
|
16719
|
+
async function generateFromCausalBridge(factStore2, events) {
|
|
16720
|
+
let created = 0;
|
|
16721
|
+
let reinforced = 0;
|
|
16722
|
+
const details = [];
|
|
16723
|
+
for (const event of events) {
|
|
16724
|
+
if (event.action === "survived") {
|
|
16725
|
+
const content = `Change to ${event.entity_key} on ${new Date(event.timestamp).toISOString().split("T")[0]} survived \u2014 shipped to ${event.branch}`;
|
|
16726
|
+
const input = {
|
|
16727
|
+
fact_type: "episodic",
|
|
16728
|
+
scope: event.entity_key,
|
|
16729
|
+
subject: event.entity_key,
|
|
16730
|
+
content: content.slice(0, 280),
|
|
16731
|
+
source: "causal_bridge",
|
|
16732
|
+
base_confidence: 1
|
|
16733
|
+
};
|
|
16734
|
+
const factId = await factStore2.createFact(input);
|
|
16735
|
+
if (factId) {
|
|
16736
|
+
created++;
|
|
16737
|
+
details.push(`[episodic:survived] ${event.entity_key}`);
|
|
16738
|
+
}
|
|
16739
|
+
} else if (event.action === "reverted") {
|
|
16740
|
+
const content = `Change to ${event.entity_key} was reverted within 24h \u2014 approach was incorrect`;
|
|
16741
|
+
const input = {
|
|
16742
|
+
fact_type: "negative",
|
|
16743
|
+
scope: event.entity_key,
|
|
16744
|
+
subject: event.entity_key,
|
|
16745
|
+
content: content.slice(0, 280),
|
|
16746
|
+
source: "causal_bridge",
|
|
16747
|
+
base_confidence: 0.85
|
|
16748
|
+
};
|
|
16749
|
+
const factId = await factStore2.createFact(input);
|
|
16750
|
+
if (factId) {
|
|
16751
|
+
created++;
|
|
16752
|
+
details.push(`[negative:reverted] ${event.entity_key}`);
|
|
16753
|
+
}
|
|
16754
|
+
}
|
|
16755
|
+
}
|
|
16756
|
+
return { created, reinforced, source: "causal_bridge", details };
|
|
16757
|
+
}
|
|
16758
|
+
async function generateFromSessionAnalysis(factStore2, unerrDir) {
|
|
16759
|
+
let created = 0;
|
|
16760
|
+
let reinforced = 0;
|
|
16761
|
+
const details = [];
|
|
16762
|
+
const summaries = loadRecentSessions(unerrDir, 20);
|
|
16763
|
+
if (summaries.length < 3) {
|
|
16764
|
+
return { created: 0, reinforced: 0, source: "session_analysis", details: [] };
|
|
16765
|
+
}
|
|
16766
|
+
const hotFiles = detectHotFiles(summaries);
|
|
16767
|
+
for (const [file, frequency] of hotFiles) {
|
|
16768
|
+
const pct = Math.round(frequency * 100);
|
|
16769
|
+
const content = `${file} is accessed in ${pct}% of sessions (hot file)`;
|
|
16770
|
+
const confidence = Math.min(0.85, 0.4 + frequency * 0.5);
|
|
16771
|
+
const input = {
|
|
16772
|
+
fact_type: "procedural",
|
|
16773
|
+
scope: file,
|
|
16774
|
+
subject: file,
|
|
16775
|
+
content,
|
|
16776
|
+
source: "session_analysis",
|
|
16777
|
+
base_confidence: confidence
|
|
16778
|
+
};
|
|
16779
|
+
const factId = await factStore2.createFact(input);
|
|
16780
|
+
if (factId) {
|
|
16781
|
+
created++;
|
|
16782
|
+
details.push(`[procedural:hot] ${file} (${pct}%)`);
|
|
16783
|
+
}
|
|
16784
|
+
}
|
|
16785
|
+
const highRevertFiles = detectHighRevertFiles(summaries);
|
|
16786
|
+
for (const [file, revertRate] of highRevertFiles) {
|
|
16787
|
+
const pct = Math.round(revertRate * 100);
|
|
16788
|
+
const content = `${file} has a ${pct}% revert rate across sessions \u2014 changes here are fragile`;
|
|
16789
|
+
const input = {
|
|
16790
|
+
fact_type: "negative",
|
|
16791
|
+
scope: file,
|
|
16792
|
+
subject: file,
|
|
16793
|
+
content: content.slice(0, 280),
|
|
16794
|
+
source: "session_analysis",
|
|
16795
|
+
base_confidence: Math.min(0.85, 0.5 + revertRate * 0.4)
|
|
16796
|
+
};
|
|
16797
|
+
const factId = await factStore2.createFact(input);
|
|
16798
|
+
if (factId) {
|
|
16799
|
+
created++;
|
|
16800
|
+
details.push(`[negative:fragile] ${file} (${pct}% revert rate)`);
|
|
16801
|
+
}
|
|
16802
|
+
}
|
|
16803
|
+
return { created, reinforced, source: "session_analysis", details };
|
|
16804
|
+
}
|
|
16805
|
+
function loadRecentSessions(unerrDir, limit) {
|
|
16806
|
+
const sessionsDir = join44(unerrDir, "sessions");
|
|
16807
|
+
if (!existsSync37(sessionsDir)) return [];
|
|
16808
|
+
try {
|
|
16809
|
+
const files = readdirSync9(sessionsDir).filter((f) => f.endsWith(".jsonl")).sort().slice(-limit);
|
|
16810
|
+
const summaries = [];
|
|
16811
|
+
for (const file of files) {
|
|
16812
|
+
try {
|
|
16813
|
+
const content = readFileSync34(join44(sessionsDir, file), "utf-8");
|
|
16814
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
16815
|
+
const lastLine = lines[lines.length - 1];
|
|
16816
|
+
if (lastLine) {
|
|
16817
|
+
summaries.push(JSON.parse(lastLine));
|
|
16818
|
+
}
|
|
16819
|
+
} catch {
|
|
16820
|
+
}
|
|
16821
|
+
}
|
|
16822
|
+
return summaries;
|
|
16823
|
+
} catch {
|
|
16824
|
+
return [];
|
|
16825
|
+
}
|
|
16826
|
+
}
|
|
16827
|
+
function detectHotFiles(summaries) {
|
|
16828
|
+
const fileCounts = /* @__PURE__ */ new Map();
|
|
16829
|
+
const total = summaries.length;
|
|
16830
|
+
for (const session of summaries) {
|
|
16831
|
+
const seen = /* @__PURE__ */ new Set();
|
|
16832
|
+
for (const file of session.files_modified) {
|
|
16833
|
+
if (!seen.has(file)) {
|
|
16834
|
+
seen.add(file);
|
|
16835
|
+
fileCounts.set(file, (fileCounts.get(file) ?? 0) + 1);
|
|
16836
|
+
}
|
|
16837
|
+
}
|
|
16838
|
+
}
|
|
16839
|
+
const HOT_THRESHOLD = 0.5;
|
|
16840
|
+
return [...fileCounts.entries()].map(([file, count]) => [file, count / total]).filter(([_, freq]) => freq >= HOT_THRESHOLD).sort((a, b) => b[1] - a[1]).slice(0, 10);
|
|
16841
|
+
}
|
|
16842
|
+
function detectHighRevertFiles(summaries) {
|
|
16843
|
+
const fileModifiedCount = /* @__PURE__ */ new Map();
|
|
16844
|
+
const fileRevertCount = /* @__PURE__ */ new Map();
|
|
16845
|
+
for (const session of summaries) {
|
|
16846
|
+
for (const file of session.files_modified) {
|
|
16847
|
+
fileModifiedCount.set(file, (fileModifiedCount.get(file) ?? 0) + 1);
|
|
16848
|
+
if (session.revert_count > 0) {
|
|
16849
|
+
fileRevertCount.set(file, (fileRevertCount.get(file) ?? 0) + 1);
|
|
16850
|
+
}
|
|
16851
|
+
}
|
|
16852
|
+
}
|
|
16853
|
+
const HIGH_REVERT_THRESHOLD = 0.4;
|
|
16854
|
+
const MIN_MODIFICATIONS = 3;
|
|
16855
|
+
return [...fileModifiedCount.entries()].filter(([_, count]) => count >= MIN_MODIFICATIONS).map(([file, count]) => {
|
|
16856
|
+
const reverts = fileRevertCount.get(file) ?? 0;
|
|
16857
|
+
return [file, reverts / count];
|
|
16858
|
+
}).filter(([_, rate]) => rate >= HIGH_REVERT_THRESHOLD).sort((a, b) => b[1] - a[1]).slice(0, 5);
|
|
16859
|
+
}
|
|
16860
|
+
function formatConventionFact(conv) {
|
|
16861
|
+
const pct = Math.round(conv.confidence * 100);
|
|
16862
|
+
switch (conv.kind) {
|
|
16863
|
+
case "naming":
|
|
16864
|
+
return `Naming convention: ${conv.name} (${pct}% confidence, ${conv.frequency} entities)`;
|
|
16865
|
+
case "structure":
|
|
16866
|
+
return `Structure pattern: ${conv.name} (${pct}% confidence)`;
|
|
16867
|
+
case "import_direction":
|
|
16868
|
+
return `Import convention: ${conv.name} (${pct}% confidence)`;
|
|
16869
|
+
default:
|
|
16870
|
+
return `Convention: ${conv.name} (${pct}% confidence)`;
|
|
16871
|
+
}
|
|
16872
|
+
}
|
|
16873
|
+
function inferScopeFromConvention(conv) {
|
|
16874
|
+
if (conv.exemplarKeys.length > 0) {
|
|
16875
|
+
const first = conv.exemplarKeys[0];
|
|
16876
|
+
const parts = first.split("/");
|
|
16877
|
+
if (parts.length >= 2) {
|
|
16878
|
+
return parts.slice(0, 2).join("/");
|
|
16879
|
+
}
|
|
16880
|
+
}
|
|
16881
|
+
return "project";
|
|
16882
|
+
}
|
|
16883
|
+
var init_fact_generator = __esm({
|
|
16884
|
+
"src/intelligence/fact-generator.ts"() {
|
|
16885
|
+
"use strict";
|
|
16886
|
+
}
|
|
16887
|
+
});
|
|
16888
|
+
|
|
16524
16889
|
// src/proxy/auto-bootstrap.ts
|
|
16525
16890
|
var auto_bootstrap_exports = {};
|
|
16526
16891
|
__export(auto_bootstrap_exports, {
|
|
@@ -16969,8 +17334,8 @@ var init_model_pricing = __esm({
|
|
|
16969
17334
|
});
|
|
16970
17335
|
|
|
16971
17336
|
// src/tracking/entity-rewind.ts
|
|
16972
|
-
import { readFileSync as
|
|
16973
|
-
import { join as
|
|
17337
|
+
import { readFileSync as readFileSync35, writeFileSync as writeFileSync20 } from "fs";
|
|
17338
|
+
import { join as join45 } from "path";
|
|
16974
17339
|
async function revertEntity(entityName, localGraph, projectRoot, filePath) {
|
|
16975
17340
|
const driftEntity = await findDriftEntity(localGraph, entityName, filePath);
|
|
16976
17341
|
if (!driftEntity) {
|
|
@@ -16982,7 +17347,7 @@ async function revertEntity(entityName, localGraph, projectRoot, filePath) {
|
|
|
16982
17347
|
error: `No drifted entity found: "${entityName}"${filePath ? ` in ${filePath}` : ""}`
|
|
16983
17348
|
};
|
|
16984
17349
|
}
|
|
16985
|
-
const absPath =
|
|
17350
|
+
const absPath = join45(projectRoot, driftEntity.file_path);
|
|
16986
17351
|
if (driftEntity.drift_status === "added") {
|
|
16987
17352
|
return removeAddedEntity(absPath, driftEntity, localGraph);
|
|
16988
17353
|
}
|
|
@@ -17047,7 +17412,7 @@ async function findDriftEntity(localGraph, entityName, filePath) {
|
|
|
17047
17412
|
}
|
|
17048
17413
|
async function removeAddedEntity(absPath, entity, localGraph) {
|
|
17049
17414
|
try {
|
|
17050
|
-
const content =
|
|
17415
|
+
const content = readFileSync35(absPath, "utf-8");
|
|
17051
17416
|
const lines = content.split("\n");
|
|
17052
17417
|
const startIdx = entity.line_start - 1;
|
|
17053
17418
|
const endIdx = entity.line_end;
|
|
@@ -17082,7 +17447,7 @@ async function restoreDeletedEntity(absPath, entity, localGraph) {
|
|
|
17082
17447
|
};
|
|
17083
17448
|
}
|
|
17084
17449
|
try {
|
|
17085
|
-
const content =
|
|
17450
|
+
const content = readFileSync35(absPath, "utf-8");
|
|
17086
17451
|
const lines = content.split("\n");
|
|
17087
17452
|
const previousLines = entity.previous_body.split("\n");
|
|
17088
17453
|
const insertIdx = Math.min(entity.line_start - 1, lines.length);
|
|
@@ -17118,7 +17483,7 @@ async function restoreModifiedEntity(absPath, entity, localGraph) {
|
|
|
17118
17483
|
};
|
|
17119
17484
|
}
|
|
17120
17485
|
try {
|
|
17121
|
-
const content =
|
|
17486
|
+
const content = readFileSync35(absPath, "utf-8");
|
|
17122
17487
|
const lines = content.split("\n");
|
|
17123
17488
|
const previousLines = entity.previous_body.split("\n");
|
|
17124
17489
|
const startIdx = entity.line_start - 1;
|
|
@@ -17530,7 +17895,7 @@ __export(file_outline_exports, {
|
|
|
17530
17895
|
buildFileOutline: () => buildFileOutline,
|
|
17531
17896
|
fileOutlineTool: () => fileOutlineTool
|
|
17532
17897
|
});
|
|
17533
|
-
import { existsSync as
|
|
17898
|
+
import { existsSync as existsSync38, readFileSync as readFileSync36 } from "fs";
|
|
17534
17899
|
import { relative as relative2, resolve } from "path";
|
|
17535
17900
|
function normRisk(rl) {
|
|
17536
17901
|
const x2 = (rl ?? "low").toLowerCase();
|
|
@@ -17589,10 +17954,10 @@ function detectExportedNames(lines, scanLimit) {
|
|
|
17589
17954
|
async function buildFileOutline(params) {
|
|
17590
17955
|
const abs = resolve(params.cwd, params.filePathArg);
|
|
17591
17956
|
const rel = relative2(params.cwd, abs).replace(/\\/g, "/") || params.filePathArg;
|
|
17592
|
-
if (!
|
|
17957
|
+
if (!existsSync38(abs)) {
|
|
17593
17958
|
throw new Error(`File not found: ${abs}`);
|
|
17594
17959
|
}
|
|
17595
|
-
const raw =
|
|
17960
|
+
const raw = readFileSync36(abs);
|
|
17596
17961
|
const sampleEnd = Math.min(raw.length, 8192);
|
|
17597
17962
|
for (let i = 0; i < sampleEnd; i++) {
|
|
17598
17963
|
if (raw[i] === 0) {
|
|
@@ -17740,7 +18105,7 @@ __export(file_read_protocol_exports, {
|
|
|
17740
18105
|
runFileReadForRouter: () => runFileReadForRouter,
|
|
17741
18106
|
runFileReadTool: () => runFileReadTool
|
|
17742
18107
|
});
|
|
17743
|
-
import { existsSync as
|
|
18108
|
+
import { existsSync as existsSync39, readFileSync as readFileSync37 } from "fs";
|
|
17744
18109
|
import { relative as relative3, resolve as resolve2 } from "path";
|
|
17745
18110
|
function isGeneratedPath(rel) {
|
|
17746
18111
|
return /(?:^|\/)node_modules\/|(?:^|\/)dist\/|\/\.next\/|\.generated\./.test(
|
|
@@ -17815,10 +18180,10 @@ async function runFileReadForRouter(args, ctx) {
|
|
|
17815
18180
|
const abs = resolve2(ctx.cwd, filePathArg);
|
|
17816
18181
|
const rel = relative3(ctx.cwd, abs).replace(/\\/g, "/") || filePathArg;
|
|
17817
18182
|
const isOutOfProject = rel.startsWith("..");
|
|
17818
|
-
if (!
|
|
18183
|
+
if (!existsSync39(abs)) {
|
|
17819
18184
|
return { content: { error: `File not found: ${abs}` } };
|
|
17820
18185
|
}
|
|
17821
|
-
const raw =
|
|
18186
|
+
const raw = readFileSync37(abs);
|
|
17822
18187
|
const sampleEnd = Math.min(raw.length, 8192);
|
|
17823
18188
|
for (let i = 0; i < sampleEnd; i++) {
|
|
17824
18189
|
if (raw[i] === 0) {
|
|
@@ -17845,9 +18210,16 @@ async function runFileReadForRouter(args, ctx) {
|
|
|
17845
18210
|
content: {
|
|
17846
18211
|
...outline,
|
|
17847
18212
|
gated: true,
|
|
17848
|
-
_gate_reason: `File has ${totalLines} lines (> ${effectiveGate}). Structure shown \u2014 call file_read again with entity or offset/limit, or force_full:true.`
|
|
18213
|
+
_gate_reason: `File has ${totalLines} lines (> ${effectiveGate}). Structure shown \u2014 call file_read again with entity or offset/limit, or force_full:true. NOTE: If you plan to Edit this file, you MUST call built-in Read (not file_read) on the target lines first.`
|
|
17849
18214
|
},
|
|
17850
|
-
_layer6_meta: {
|
|
18215
|
+
_layer6_meta: {
|
|
18216
|
+
format: "outline",
|
|
18217
|
+
gated: true,
|
|
18218
|
+
total_lines: totalLines,
|
|
18219
|
+
total_chars: text2.length,
|
|
18220
|
+
tokens_estimate: Math.ceil(outline.token_estimate ?? 0),
|
|
18221
|
+
optimization: `file_read gated \u2192 outline (${totalLines} lines)`
|
|
18222
|
+
}
|
|
17851
18223
|
};
|
|
17852
18224
|
}
|
|
17853
18225
|
if (entityName) {
|
|
@@ -17869,7 +18241,7 @@ async function runFileReadForRouter(args, ctx) {
|
|
|
17869
18241
|
const match = ranked[0].entity;
|
|
17870
18242
|
if (match.start_line >= 1 && match.start_line <= totalLines) {
|
|
17871
18243
|
const start = Math.max(1, match.start_line - ENTITY_CONTEXT);
|
|
17872
|
-
const endLine = match.start_line + match.body.split("\n").length - 1;
|
|
18244
|
+
const endLine = (match.end_line ?? 0) > match.start_line ? match.end_line : match.start_line + match.body.split("\n").length - 1;
|
|
17873
18245
|
const end = Math.min(totalLines, endLine + ENTITY_CONTEXT);
|
|
17874
18246
|
offset = start;
|
|
17875
18247
|
limit = Math.min(MAX_READ_LINES, end - start + 1);
|
|
@@ -17958,9 +18330,16 @@ async function runFileReadForRouter(args, ctx) {
|
|
|
17958
18330
|
query: entityName,
|
|
17959
18331
|
suggestions: []
|
|
17960
18332
|
},
|
|
17961
|
-
_gate_reason: `Entity "${entityName}" not found with high confidence. Use one of the suggestions or specify offset/limit.`
|
|
18333
|
+
_gate_reason: `Entity "${entityName}" not found with high confidence. Use one of the suggestions or specify offset/limit. NOTE: If you plan to Edit this file, you MUST call built-in Read (not file_read) first.`
|
|
17962
18334
|
},
|
|
17963
|
-
_layer6_meta: {
|
|
18335
|
+
_layer6_meta: {
|
|
18336
|
+
format: "outline",
|
|
18337
|
+
gated: true,
|
|
18338
|
+
total_lines: totalLines,
|
|
18339
|
+
total_chars: text2.length,
|
|
18340
|
+
tokens_estimate: Math.ceil(outline.token_estimate ?? 0),
|
|
18341
|
+
optimization: `file_read gated \u2192 outline (${totalLines} lines)`
|
|
18342
|
+
}
|
|
17964
18343
|
};
|
|
17965
18344
|
}
|
|
17966
18345
|
}
|
|
@@ -17996,10 +18375,12 @@ ${body}`;
|
|
|
17996
18375
|
const meta = {
|
|
17997
18376
|
format: "json",
|
|
17998
18377
|
tokens_estimate: Math.ceil(sliced.join("\n").length / CHARS_PER_TOKEN4),
|
|
17999
|
-
total_lines: totalLines
|
|
18378
|
+
total_lines: totalLines,
|
|
18379
|
+
total_chars: text2.length
|
|
18000
18380
|
};
|
|
18001
18381
|
if (isFullRead && sliced.length === totalLines) {
|
|
18002
18382
|
meta.optimization = `file_read full (purpose:${purpose})`;
|
|
18383
|
+
meta._edit_note = "file_read does NOT satisfy Edit's Read prerequisite. You must still call built-in Read before using Edit on this file.";
|
|
18003
18384
|
} else if (effOffset > 1 || sliced.length < totalLines || entityWindowApplied) {
|
|
18004
18385
|
meta.optimization = `file_read window lines ${effOffset}-${effOffset + sliced.length - 1}`;
|
|
18005
18386
|
}
|
|
@@ -18182,9 +18563,13 @@ var init_query_router = __esm({
|
|
|
18182
18563
|
LOCAL_TOOLS = /* @__PURE__ */ new Set([
|
|
18183
18564
|
"get_function",
|
|
18184
18565
|
"get_class",
|
|
18566
|
+
"get_entity",
|
|
18567
|
+
// consolidated: replaces get_function + get_class
|
|
18185
18568
|
"get_file",
|
|
18186
18569
|
"get_callers",
|
|
18187
18570
|
"get_callees",
|
|
18571
|
+
"get_references",
|
|
18572
|
+
// consolidated: replaces get_callers + get_callees
|
|
18188
18573
|
"get_imports",
|
|
18189
18574
|
"search_code",
|
|
18190
18575
|
"get_rules",
|
|
@@ -18610,7 +18995,7 @@ var init_query_router = __esm({
|
|
|
18610
18995
|
Object.assign(meta, fr._layer6_meta);
|
|
18611
18996
|
if (this.tokenFlow && fr._layer6_meta.optimization && fr._layer6_meta.total_lines) {
|
|
18612
18997
|
const deliveredTokens = fr._layer6_meta.tokens_estimate ?? 0;
|
|
18613
|
-
const fullFileTokens = Math.ceil(fr._layer6_meta.total_lines * 80 / 4);
|
|
18998
|
+
const fullFileTokens = fr._layer6_meta.total_chars ? Math.ceil(fr._layer6_meta.total_chars / 4) : Math.ceil(fr._layer6_meta.total_lines * 80 / 4);
|
|
18614
18999
|
const fileReadSaved = fullFileTokens - deliveredTokens;
|
|
18615
19000
|
if (fileReadSaved > 50) {
|
|
18616
19001
|
this.tokenFlow.record({
|
|
@@ -19558,7 +19943,21 @@ var init_query_router = __esm({
|
|
|
19558
19943
|
case "get_file": {
|
|
19559
19944
|
const raw = args.key ?? args.name;
|
|
19560
19945
|
const key = await this.resolveKeyArg(raw);
|
|
19561
|
-
|
|
19946
|
+
const entity = await this.resolveEntityWithOverlay(key);
|
|
19947
|
+
if (entity && entity.file_path && entity.start_line > 0) {
|
|
19948
|
+
try {
|
|
19949
|
+
const { readFileSync: readFileSync57 } = await import("fs");
|
|
19950
|
+
const { resolve: resolve3 } = await import("path");
|
|
19951
|
+
const cwd = this.projectRoot ?? process.cwd();
|
|
19952
|
+
const abs = resolve3(cwd, entity.file_path);
|
|
19953
|
+
const lines = readFileSync57(abs, "utf-8").split("\n");
|
|
19954
|
+
const start = entity.start_line - 1;
|
|
19955
|
+
const end = entity.end_line ?? lines.length;
|
|
19956
|
+
entity.body = lines.slice(start, end).join("\n");
|
|
19957
|
+
} catch {
|
|
19958
|
+
}
|
|
19959
|
+
}
|
|
19960
|
+
return entity;
|
|
19562
19961
|
}
|
|
19563
19962
|
case "get_references": {
|
|
19564
19963
|
const key = await this.resolveKeyArg(args.key);
|
|
@@ -19931,11 +20330,14 @@ var init_query_router = __esm({
|
|
|
19931
20330
|
ENTITY_ARRAY_TOOLS = /* @__PURE__ */ new Set([
|
|
19932
20331
|
"get_callers",
|
|
19933
20332
|
"get_callees",
|
|
20333
|
+
"get_references",
|
|
19934
20334
|
"search_code"
|
|
19935
20335
|
]);
|
|
19936
20336
|
SINGLE_ENTITY_TOOLS = /* @__PURE__ */ new Set([
|
|
20337
|
+
"get_entity",
|
|
19937
20338
|
"get_function",
|
|
19938
|
-
"get_class"
|
|
20339
|
+
"get_class",
|
|
20340
|
+
"get_file"
|
|
19939
20341
|
]);
|
|
19940
20342
|
}
|
|
19941
20343
|
});
|
|
@@ -21074,8 +21476,8 @@ __export(causal_bridge_exports, {
|
|
|
21074
21476
|
assembleCausalChain: () => assembleCausalChain,
|
|
21075
21477
|
computeDurability: () => computeDurability
|
|
21076
21478
|
});
|
|
21077
|
-
import { existsSync as
|
|
21078
|
-
import { join as
|
|
21479
|
+
import { existsSync as existsSync40, readFileSync as readFileSync38 } from "fs";
|
|
21480
|
+
import { join as join46 } from "path";
|
|
21079
21481
|
function computeAggregateDurability(interactions) {
|
|
21080
21482
|
if (interactions.length === 0) return 1;
|
|
21081
21483
|
const survivedCount = interactions.filter((i) => i.survived).length;
|
|
@@ -21232,9 +21634,9 @@ var init_causal_bridge = __esm({
|
|
|
21232
21634
|
return chains;
|
|
21233
21635
|
}
|
|
21234
21636
|
loadEntityLedgerEntries(entityKey2) {
|
|
21235
|
-
const ledgerPath =
|
|
21236
|
-
if (!
|
|
21237
|
-
const content =
|
|
21637
|
+
const ledgerPath = join46(this.unerrDir, "ledger", "shadow.jsonl");
|
|
21638
|
+
if (!existsSync40(ledgerPath)) return [];
|
|
21639
|
+
const content = readFileSync38(ledgerPath, "utf-8");
|
|
21238
21640
|
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
21239
21641
|
const entries = [];
|
|
21240
21642
|
for (const line of lines) {
|
|
@@ -21720,15 +22122,15 @@ var context_ledger_exports = {};
|
|
|
21720
22122
|
__export(context_ledger_exports, {
|
|
21721
22123
|
createContextLedger: () => createContextLedger
|
|
21722
22124
|
});
|
|
21723
|
-
import { existsSync as
|
|
21724
|
-
import { join as
|
|
22125
|
+
import { existsSync as existsSync41, mkdirSync as mkdirSync23, readFileSync as readFileSync39, writeFileSync as writeFileSync21 } from "fs";
|
|
22126
|
+
import { join as join47 } from "path";
|
|
21725
22127
|
function createContextLedger(unerrDir) {
|
|
21726
|
-
const stateDir =
|
|
21727
|
-
const filePath =
|
|
22128
|
+
const stateDir = join47(unerrDir, "state");
|
|
22129
|
+
const filePath = join47(stateDir, "context-ledger.json");
|
|
21728
22130
|
let records = [];
|
|
21729
22131
|
let index = /* @__PURE__ */ new Map();
|
|
21730
22132
|
function ensureDir() {
|
|
21731
|
-
if (!
|
|
22133
|
+
if (!existsSync41(stateDir)) {
|
|
21732
22134
|
mkdirSync23(stateDir, { recursive: true });
|
|
21733
22135
|
}
|
|
21734
22136
|
}
|
|
@@ -21737,13 +22139,13 @@ function createContextLedger(unerrDir) {
|
|
|
21737
22139
|
}
|
|
21738
22140
|
function load() {
|
|
21739
22141
|
ensureDir();
|
|
21740
|
-
if (!
|
|
22142
|
+
if (!existsSync41(filePath)) {
|
|
21741
22143
|
records = [];
|
|
21742
22144
|
index = /* @__PURE__ */ new Map();
|
|
21743
22145
|
return /* @__PURE__ */ new Map();
|
|
21744
22146
|
}
|
|
21745
22147
|
try {
|
|
21746
|
-
const raw =
|
|
22148
|
+
const raw = readFileSync39(filePath, "utf-8");
|
|
21747
22149
|
const parsed = JSON.parse(raw);
|
|
21748
22150
|
records = Array.isArray(parsed) ? parsed : [];
|
|
21749
22151
|
} catch {
|
|
@@ -22650,13 +23052,13 @@ __export(intent_correlator_exports, {
|
|
|
22650
23052
|
IntentCorrelator: () => IntentCorrelator
|
|
22651
23053
|
});
|
|
22652
23054
|
import {
|
|
22653
|
-
existsSync as
|
|
23055
|
+
existsSync as existsSync42,
|
|
22654
23056
|
mkdirSync as mkdirSync24,
|
|
22655
|
-
readFileSync as
|
|
23057
|
+
readFileSync as readFileSync40,
|
|
22656
23058
|
renameSync,
|
|
22657
23059
|
writeFileSync as writeFileSync22
|
|
22658
23060
|
} from "fs";
|
|
22659
|
-
import { join as
|
|
23061
|
+
import { join as join48 } from "path";
|
|
22660
23062
|
function extractFiles4(args) {
|
|
22661
23063
|
const files = args.files;
|
|
22662
23064
|
if (files && Array.isArray(files)) {
|
|
@@ -22704,9 +23106,9 @@ var init_intent_correlator = __esm({
|
|
|
22704
23106
|
pendingPath;
|
|
22705
23107
|
pending = [];
|
|
22706
23108
|
constructor(unerrDir) {
|
|
22707
|
-
this.ledgerDir =
|
|
22708
|
-
this.pendingPath =
|
|
22709
|
-
if (!
|
|
23109
|
+
this.ledgerDir = join48(unerrDir, "ledger");
|
|
23110
|
+
this.pendingPath = join48(this.ledgerDir, "pending_correlations.json");
|
|
23111
|
+
if (!existsSync42(this.ledgerDir)) {
|
|
22710
23112
|
mkdirSync24(this.ledgerDir, { recursive: true });
|
|
22711
23113
|
}
|
|
22712
23114
|
this.load();
|
|
@@ -22798,9 +23200,9 @@ var init_intent_correlator = __esm({
|
|
|
22798
23200
|
}
|
|
22799
23201
|
// ── Persistence ─────────────────────────────────────────────────
|
|
22800
23202
|
load() {
|
|
22801
|
-
if (!
|
|
23203
|
+
if (!existsSync42(this.pendingPath)) return;
|
|
22802
23204
|
try {
|
|
22803
|
-
const raw =
|
|
23205
|
+
const raw = readFileSync40(this.pendingPath, "utf-8");
|
|
22804
23206
|
const parsed = JSON.parse(raw);
|
|
22805
23207
|
if (Array.isArray(parsed)) {
|
|
22806
23208
|
this.pending = parsed;
|
|
@@ -22832,14 +23234,14 @@ __export(working_snapshots_exports, {
|
|
|
22832
23234
|
});
|
|
22833
23235
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
22834
23236
|
import {
|
|
22835
|
-
existsSync as
|
|
23237
|
+
existsSync as existsSync43,
|
|
22836
23238
|
mkdirSync as mkdirSync25,
|
|
22837
|
-
readFileSync as
|
|
22838
|
-
readdirSync as
|
|
23239
|
+
readFileSync as readFileSync41,
|
|
23240
|
+
readdirSync as readdirSync10,
|
|
22839
23241
|
rmSync as rmSync2,
|
|
22840
23242
|
writeFileSync as writeFileSync23
|
|
22841
23243
|
} from "fs";
|
|
22842
|
-
import { join as
|
|
23244
|
+
import { join as join49 } from "path";
|
|
22843
23245
|
var _log2, MAX_SNAPSHOTS, AUTO_SNAPSHOT_COOLDOWN_MS, WorkingSnapshotStore;
|
|
22844
23246
|
var init_working_snapshots = __esm({
|
|
22845
23247
|
"src/tracking/working-snapshots.ts"() {
|
|
@@ -22857,8 +23259,8 @@ var init_working_snapshots = __esm({
|
|
|
22857
23259
|
unerrDir;
|
|
22858
23260
|
constructor(unerrDir) {
|
|
22859
23261
|
this.unerrDir = unerrDir;
|
|
22860
|
-
this.snapshotDir =
|
|
22861
|
-
if (!
|
|
23262
|
+
this.snapshotDir = join49(unerrDir, "snapshots");
|
|
23263
|
+
if (!existsSync43(this.snapshotDir)) {
|
|
22862
23264
|
mkdirSync25(this.snapshotDir, { recursive: true });
|
|
22863
23265
|
}
|
|
22864
23266
|
}
|
|
@@ -22878,7 +23280,7 @@ var init_working_snapshots = __esm({
|
|
|
22878
23280
|
processed: false
|
|
22879
23281
|
};
|
|
22880
23282
|
writeFileSync23(
|
|
22881
|
-
|
|
23283
|
+
join49(this.snapshotDir, `${id}.json`),
|
|
22882
23284
|
JSON.stringify(snapshot, null, 2),
|
|
22883
23285
|
"utf-8"
|
|
22884
23286
|
);
|
|
@@ -22892,10 +23294,10 @@ var init_working_snapshots = __esm({
|
|
|
22892
23294
|
* Get a snapshot by ID.
|
|
22893
23295
|
*/
|
|
22894
23296
|
get(snapshotId) {
|
|
22895
|
-
const filePath =
|
|
22896
|
-
if (!
|
|
23297
|
+
const filePath = join49(this.snapshotDir, `${snapshotId}.json`);
|
|
23298
|
+
if (!existsSync43(filePath)) return null;
|
|
22897
23299
|
try {
|
|
22898
|
-
return JSON.parse(
|
|
23300
|
+
return JSON.parse(readFileSync41(filePath, "utf-8"));
|
|
22899
23301
|
} catch {
|
|
22900
23302
|
return null;
|
|
22901
23303
|
}
|
|
@@ -22904,14 +23306,14 @@ var init_working_snapshots = __esm({
|
|
|
22904
23306
|
* List all snapshots, most recent first.
|
|
22905
23307
|
*/
|
|
22906
23308
|
list() {
|
|
22907
|
-
if (!
|
|
22908
|
-
const files =
|
|
23309
|
+
if (!existsSync43(this.snapshotDir)) return [];
|
|
23310
|
+
const files = readdirSync10(this.snapshotDir).filter(
|
|
22909
23311
|
(f) => f.endsWith(".json")
|
|
22910
23312
|
);
|
|
22911
23313
|
const snapshots = [];
|
|
22912
23314
|
for (const file of files) {
|
|
22913
23315
|
try {
|
|
22914
|
-
const raw =
|
|
23316
|
+
const raw = readFileSync41(join49(this.snapshotDir, file), "utf-8");
|
|
22915
23317
|
snapshots.push(JSON.parse(raw));
|
|
22916
23318
|
} catch {
|
|
22917
23319
|
}
|
|
@@ -22952,7 +23354,7 @@ var init_working_snapshots = __esm({
|
|
|
22952
23354
|
if (!snapshot) return;
|
|
22953
23355
|
snapshot.processed = true;
|
|
22954
23356
|
writeFileSync23(
|
|
22955
|
-
|
|
23357
|
+
join49(this.snapshotDir, `${snapshotId}.json`),
|
|
22956
23358
|
JSON.stringify(snapshot, null, 2),
|
|
22957
23359
|
"utf-8"
|
|
22958
23360
|
);
|
|
@@ -22967,8 +23369,8 @@ var init_working_snapshots = __esm({
|
|
|
22967
23369
|
* Delete a snapshot.
|
|
22968
23370
|
*/
|
|
22969
23371
|
delete(snapshotId) {
|
|
22970
|
-
const filePath =
|
|
22971
|
-
if (!
|
|
23372
|
+
const filePath = join49(this.snapshotDir, `${snapshotId}.json`);
|
|
23373
|
+
if (!existsSync43(filePath)) return false;
|
|
22972
23374
|
rmSync2(filePath);
|
|
22973
23375
|
return true;
|
|
22974
23376
|
}
|
|
@@ -22976,10 +23378,10 @@ var init_working_snapshots = __esm({
|
|
|
22976
23378
|
* Get the timeline branch counter from branch_context.json.
|
|
22977
23379
|
*/
|
|
22978
23380
|
getTimelineBranch() {
|
|
22979
|
-
const contextPath =
|
|
22980
|
-
if (!
|
|
23381
|
+
const contextPath = join49(this.unerrDir, "branch_context.json");
|
|
23382
|
+
if (!existsSync43(contextPath)) return 0;
|
|
22981
23383
|
try {
|
|
22982
|
-
const ctx = JSON.parse(
|
|
23384
|
+
const ctx = JSON.parse(readFileSync41(contextPath, "utf-8"));
|
|
22983
23385
|
return ctx.timelineBranch ?? 0;
|
|
22984
23386
|
} catch {
|
|
22985
23387
|
return 0;
|
|
@@ -22989,11 +23391,11 @@ var init_working_snapshots = __esm({
|
|
|
22989
23391
|
* Increment the timeline branch counter. Returns the new value.
|
|
22990
23392
|
*/
|
|
22991
23393
|
incrementTimelineBranch() {
|
|
22992
|
-
const contextPath =
|
|
23394
|
+
const contextPath = join49(this.unerrDir, "branch_context.json");
|
|
22993
23395
|
let ctx = {};
|
|
22994
|
-
if (
|
|
23396
|
+
if (existsSync43(contextPath)) {
|
|
22995
23397
|
try {
|
|
22996
|
-
ctx = JSON.parse(
|
|
23398
|
+
ctx = JSON.parse(readFileSync41(contextPath, "utf-8"));
|
|
22997
23399
|
} catch {
|
|
22998
23400
|
ctx = {};
|
|
22999
23401
|
}
|
|
@@ -23166,8 +23568,8 @@ var quality_signals_exports = {};
|
|
|
23166
23568
|
__export(quality_signals_exports, {
|
|
23167
23569
|
QualitySignalTracker: () => QualitySignalTracker
|
|
23168
23570
|
});
|
|
23169
|
-
import { existsSync as
|
|
23170
|
-
import { join as
|
|
23571
|
+
import { existsSync as existsSync44, readFileSync as readFileSync42, writeFileSync as writeFileSync24 } from "fs";
|
|
23572
|
+
import { join as join50 } from "path";
|
|
23171
23573
|
function computeDurabilityFromAge(survivalMs) {
|
|
23172
23574
|
if (survivalMs < FRAGILE_THRESHOLD_MS) {
|
|
23173
23575
|
return 0.1 + survivalMs / FRAGILE_THRESHOLD_MS * 0.2;
|
|
@@ -23194,7 +23596,7 @@ var init_quality_signals = __esm({
|
|
|
23194
23596
|
/** Maximum corrections to retain in memory/disk. */
|
|
23195
23597
|
static MAX_CORRECTIONS = 200;
|
|
23196
23598
|
constructor(unerrDir) {
|
|
23197
|
-
this.signalsPath =
|
|
23599
|
+
this.signalsPath = join50(unerrDir, "state", "quality_signals.json");
|
|
23198
23600
|
this.signals = this.load();
|
|
23199
23601
|
}
|
|
23200
23602
|
/**
|
|
@@ -23286,8 +23688,8 @@ var init_quality_signals = __esm({
|
|
|
23286
23688
|
*/
|
|
23287
23689
|
save() {
|
|
23288
23690
|
try {
|
|
23289
|
-
const dir =
|
|
23290
|
-
if (!
|
|
23691
|
+
const dir = join50(this.signalsPath, "..");
|
|
23692
|
+
if (!existsSync44(dir)) {
|
|
23291
23693
|
const { mkdirSync: mkdirSync39 } = __require("fs");
|
|
23292
23694
|
mkdirSync39(dir, { recursive: true });
|
|
23293
23695
|
}
|
|
@@ -23333,7 +23735,7 @@ var init_quality_signals = __esm({
|
|
|
23333
23735
|
* Load signals from disk.
|
|
23334
23736
|
*/
|
|
23335
23737
|
load() {
|
|
23336
|
-
if (!
|
|
23738
|
+
if (!existsSync44(this.signalsPath)) {
|
|
23337
23739
|
return {
|
|
23338
23740
|
durabilityScores: {},
|
|
23339
23741
|
corrections: [],
|
|
@@ -23342,7 +23744,7 @@ var init_quality_signals = __esm({
|
|
|
23342
23744
|
}
|
|
23343
23745
|
try {
|
|
23344
23746
|
return JSON.parse(
|
|
23345
|
-
|
|
23747
|
+
readFileSync42(this.signalsPath, "utf-8")
|
|
23346
23748
|
);
|
|
23347
23749
|
} catch {
|
|
23348
23750
|
return {
|
|
@@ -24359,8 +24761,8 @@ var incomplete_work_exports = {};
|
|
|
24359
24761
|
__export(incomplete_work_exports, {
|
|
24360
24762
|
IncompleteWorkDetector: () => IncompleteWorkDetector
|
|
24361
24763
|
});
|
|
24362
|
-
import { existsSync as
|
|
24363
|
-
import { join as
|
|
24764
|
+
import { existsSync as existsSync45, mkdirSync as mkdirSync26, readFileSync as readFileSync43, writeFileSync as writeFileSync25 } from "fs";
|
|
24765
|
+
import { join as join51 } from "path";
|
|
24364
24766
|
function severityRank(severity) {
|
|
24365
24767
|
switch (severity) {
|
|
24366
24768
|
case "high":
|
|
@@ -24553,9 +24955,9 @@ var init_incomplete_work = __esm({
|
|
|
24553
24955
|
persistItems(items) {
|
|
24554
24956
|
if (!this.unerrDir) return false;
|
|
24555
24957
|
try {
|
|
24556
|
-
const stateDir =
|
|
24557
|
-
if (!
|
|
24558
|
-
const filePath =
|
|
24958
|
+
const stateDir = join51(this.unerrDir, "state");
|
|
24959
|
+
if (!existsSync45(stateDir)) mkdirSync26(stateDir, { recursive: true });
|
|
24960
|
+
const filePath = join51(stateDir, PERSISTENCE_FILE);
|
|
24559
24961
|
writeFileSync25(
|
|
24560
24962
|
filePath,
|
|
24561
24963
|
JSON.stringify({
|
|
@@ -24575,9 +24977,9 @@ var init_incomplete_work = __esm({
|
|
|
24575
24977
|
*/
|
|
24576
24978
|
static readPersistedItems(unerrDir) {
|
|
24577
24979
|
try {
|
|
24578
|
-
const filePath =
|
|
24579
|
-
if (!
|
|
24580
|
-
const data = JSON.parse(
|
|
24980
|
+
const filePath = join51(unerrDir, "state", PERSISTENCE_FILE);
|
|
24981
|
+
if (!existsSync45(filePath)) return [];
|
|
24982
|
+
const data = JSON.parse(readFileSync43(filePath, "utf-8"));
|
|
24581
24983
|
return data.items ?? [];
|
|
24582
24984
|
} catch {
|
|
24583
24985
|
return [];
|
|
@@ -26048,7 +26450,7 @@ var transport_mux_exports = {};
|
|
|
26048
26450
|
__export(transport_mux_exports, {
|
|
26049
26451
|
TransportMux: () => TransportMux
|
|
26050
26452
|
});
|
|
26051
|
-
import { existsSync as
|
|
26453
|
+
import { existsSync as existsSync46, unlinkSync as unlinkSync7 } from "fs";
|
|
26052
26454
|
import { createServer as createServer2 } from "net";
|
|
26053
26455
|
var _log4, TransportMux;
|
|
26054
26456
|
var init_transport_mux = __esm({
|
|
@@ -26088,7 +26490,7 @@ var init_transport_mux = __esm({
|
|
|
26088
26490
|
* Start listening for secondary clients on the Unix domain socket.
|
|
26089
26491
|
*/
|
|
26090
26492
|
start() {
|
|
26091
|
-
if (
|
|
26493
|
+
if (existsSync46(this.sockPath)) {
|
|
26092
26494
|
try {
|
|
26093
26495
|
unlinkSync7(this.sockPath);
|
|
26094
26496
|
} catch {
|
|
@@ -26121,7 +26523,7 @@ var init_transport_mux = __esm({
|
|
|
26121
26523
|
this.server.close();
|
|
26122
26524
|
this.server = null;
|
|
26123
26525
|
}
|
|
26124
|
-
if (
|
|
26526
|
+
if (existsSync46(this.sockPath)) {
|
|
26125
26527
|
try {
|
|
26126
26528
|
unlinkSync7(this.sockPath);
|
|
26127
26529
|
} catch {
|
|
@@ -26269,8 +26671,8 @@ __export(git_trailers_exports, {
|
|
|
26269
26671
|
parseTrailersFromMessage: () => parseTrailersFromMessage,
|
|
26270
26672
|
uninstallPrepareCommitMsgHook: () => uninstallPrepareCommitMsgHook
|
|
26271
26673
|
});
|
|
26272
|
-
import { existsSync as
|
|
26273
|
-
import { join as
|
|
26674
|
+
import { existsSync as existsSync47, readFileSync as readFileSync44, writeFileSync as writeFileSync26 } from "fs";
|
|
26675
|
+
import { join as join52 } from "path";
|
|
26274
26676
|
function getCommitTrailers(ledger, timelineBranch, branch) {
|
|
26275
26677
|
const recent = ledger.getRecentEntries(1);
|
|
26276
26678
|
if (recent.length === 0) return null;
|
|
@@ -26290,15 +26692,15 @@ function formatTrailers(trailers) {
|
|
|
26290
26692
|
].join("\n");
|
|
26291
26693
|
}
|
|
26292
26694
|
function installPrepareCommitMsgHook(projectRoot) {
|
|
26293
|
-
const hooksDir =
|
|
26294
|
-
if (!
|
|
26695
|
+
const hooksDir = join52(projectRoot, ".git", "hooks");
|
|
26696
|
+
if (!existsSync47(hooksDir)) {
|
|
26295
26697
|
return false;
|
|
26296
26698
|
}
|
|
26297
|
-
const hookPath =
|
|
26699
|
+
const hookPath = join52(hooksDir, "prepare-commit-msg");
|
|
26298
26700
|
const marker = "# unerr-trailer-injection";
|
|
26299
|
-
if (
|
|
26701
|
+
if (existsSync47(hookPath)) {
|
|
26300
26702
|
try {
|
|
26301
|
-
const existing =
|
|
26703
|
+
const existing = readFileSync44(hookPath, "utf-8");
|
|
26302
26704
|
if (existing.includes(marker)) {
|
|
26303
26705
|
return true;
|
|
26304
26706
|
}
|
|
@@ -26327,10 +26729,10 @@ ${generateHookScript()}`;
|
|
|
26327
26729
|
}
|
|
26328
26730
|
}
|
|
26329
26731
|
function uninstallPrepareCommitMsgHook(projectRoot) {
|
|
26330
|
-
const hookPath =
|
|
26331
|
-
if (!
|
|
26732
|
+
const hookPath = join52(projectRoot, ".git", "hooks", "prepare-commit-msg");
|
|
26733
|
+
if (!existsSync47(hookPath)) return true;
|
|
26332
26734
|
try {
|
|
26333
|
-
const content =
|
|
26735
|
+
const content = readFileSync44(hookPath, "utf-8");
|
|
26334
26736
|
const marker = "# unerr-trailer-injection";
|
|
26335
26737
|
if (!content.includes(marker)) return true;
|
|
26336
26738
|
const lines = content.split("\n");
|
|
@@ -26495,15 +26897,15 @@ var init_http_transport = __esm({
|
|
|
26495
26897
|
|
|
26496
26898
|
// src/tracking/branch-snapshot.ts
|
|
26497
26899
|
import {
|
|
26498
|
-
existsSync as
|
|
26900
|
+
existsSync as existsSync48,
|
|
26499
26901
|
mkdirSync as mkdirSync28,
|
|
26500
|
-
readFileSync as
|
|
26501
|
-
readdirSync as
|
|
26902
|
+
readFileSync as readFileSync45,
|
|
26903
|
+
readdirSync as readdirSync11,
|
|
26502
26904
|
rmSync as rmSync3,
|
|
26503
26905
|
statSync as statSync7,
|
|
26504
26906
|
writeFileSync as writeFileSync27
|
|
26505
26907
|
} from "fs";
|
|
26506
|
-
import { join as
|
|
26908
|
+
import { join as join53 } from "path";
|
|
26507
26909
|
function sanitizeBranchName(branch) {
|
|
26508
26910
|
return branch.replace(/\//g, "__").replace(/[^a-zA-Z0-9_.\-]/g, "_");
|
|
26509
26911
|
}
|
|
@@ -26521,7 +26923,7 @@ var init_branch_snapshot = __esm({
|
|
|
26521
26923
|
branchDir;
|
|
26522
26924
|
projectRoot;
|
|
26523
26925
|
constructor(unerrDir, projectRoot) {
|
|
26524
|
-
this.branchDir =
|
|
26926
|
+
this.branchDir = join53(unerrDir, "drift", "branches");
|
|
26525
26927
|
this.projectRoot = projectRoot;
|
|
26526
26928
|
}
|
|
26527
26929
|
/**
|
|
@@ -26535,8 +26937,8 @@ var init_branch_snapshot = __esm({
|
|
|
26535
26937
|
log16.info(`No drift entities/edges to snapshot for branch ${branch}`);
|
|
26536
26938
|
}
|
|
26537
26939
|
const dirName = sanitizeBranchName(branch);
|
|
26538
|
-
const snapshotDir =
|
|
26539
|
-
if (!
|
|
26940
|
+
const snapshotDir = join53(this.branchDir, dirName);
|
|
26941
|
+
if (!existsSync48(snapshotDir)) {
|
|
26540
26942
|
mkdirSync28(snapshotDir, { recursive: true });
|
|
26541
26943
|
}
|
|
26542
26944
|
const snapshot = {
|
|
@@ -26547,12 +26949,12 @@ var init_branch_snapshot = __esm({
|
|
|
26547
26949
|
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
26548
26950
|
};
|
|
26549
26951
|
writeFileSync27(
|
|
26550
|
-
|
|
26952
|
+
join53(snapshotDir, OVERLAY_FILE),
|
|
26551
26953
|
JSON.stringify(snapshot, null, 2),
|
|
26552
26954
|
"utf-8"
|
|
26553
26955
|
);
|
|
26554
26956
|
writeFileSync27(
|
|
26555
|
-
|
|
26957
|
+
join53(snapshotDir, HASHES_FILE),
|
|
26556
26958
|
JSON.stringify(fileHashState, null, 2),
|
|
26557
26959
|
"utf-8"
|
|
26558
26960
|
);
|
|
@@ -26568,14 +26970,14 @@ var init_branch_snapshot = __esm({
|
|
|
26568
26970
|
*/
|
|
26569
26971
|
async restoreSnapshot(branch, localGraph) {
|
|
26570
26972
|
const dirName = sanitizeBranchName(branch);
|
|
26571
|
-
const snapshotDir =
|
|
26572
|
-
const overlayPath =
|
|
26573
|
-
if (!
|
|
26973
|
+
const snapshotDir = join53(this.branchDir, dirName);
|
|
26974
|
+
const overlayPath = join53(snapshotDir, OVERLAY_FILE);
|
|
26975
|
+
if (!existsSync48(overlayPath)) {
|
|
26574
26976
|
log16.info(`No snapshot for branch ${branch} \u2014 first visit`);
|
|
26575
26977
|
return null;
|
|
26576
26978
|
}
|
|
26577
26979
|
try {
|
|
26578
|
-
const raw =
|
|
26980
|
+
const raw = readFileSync45(overlayPath, "utf-8");
|
|
26579
26981
|
const snapshot = JSON.parse(raw);
|
|
26580
26982
|
for (const entity of snapshot.entities) {
|
|
26581
26983
|
await localGraph.upsertDriftEntity(entity);
|
|
@@ -26587,7 +26989,7 @@ var init_branch_snapshot = __esm({
|
|
|
26587
26989
|
}
|
|
26588
26990
|
const now = /* @__PURE__ */ new Date();
|
|
26589
26991
|
writeFileSync27(
|
|
26590
|
-
|
|
26992
|
+
join53(snapshotDir, ".last_access"),
|
|
26591
26993
|
now.toISOString(),
|
|
26592
26994
|
"utf-8"
|
|
26593
26995
|
);
|
|
@@ -26607,17 +27009,17 @@ var init_branch_snapshot = __esm({
|
|
|
26607
27009
|
*/
|
|
26608
27010
|
hasSnapshot(branch) {
|
|
26609
27011
|
const dirName = sanitizeBranchName(branch);
|
|
26610
|
-
return
|
|
27012
|
+
return existsSync48(join53(this.branchDir, dirName, OVERLAY_FILE));
|
|
26611
27013
|
}
|
|
26612
27014
|
/**
|
|
26613
27015
|
* Get the file hash state from a branch snapshot.
|
|
26614
27016
|
*/
|
|
26615
27017
|
getSnapshotFileHashes(branch) {
|
|
26616
27018
|
const dirName = sanitizeBranchName(branch);
|
|
26617
|
-
const hashesPath =
|
|
26618
|
-
if (!
|
|
27019
|
+
const hashesPath = join53(this.branchDir, dirName, HASHES_FILE);
|
|
27020
|
+
if (!existsSync48(hashesPath)) return null;
|
|
26619
27021
|
try {
|
|
26620
|
-
const raw =
|
|
27022
|
+
const raw = readFileSync45(hashesPath, "utf-8");
|
|
26621
27023
|
return JSON.parse(raw);
|
|
26622
27024
|
} catch {
|
|
26623
27025
|
return null;
|
|
@@ -26628,8 +27030,8 @@ var init_branch_snapshot = __esm({
|
|
|
26628
27030
|
*/
|
|
26629
27031
|
deleteSnapshot(branch) {
|
|
26630
27032
|
const dirName = sanitizeBranchName(branch);
|
|
26631
|
-
const snapshotDir =
|
|
26632
|
-
if (!
|
|
27033
|
+
const snapshotDir = join53(this.branchDir, dirName);
|
|
27034
|
+
if (!existsSync48(snapshotDir)) return false;
|
|
26633
27035
|
rmSync3(snapshotDir, { recursive: true, force: true });
|
|
26634
27036
|
log16.info(`Deleted branch snapshot: ${branch}`);
|
|
26635
27037
|
return true;
|
|
@@ -26638,14 +27040,14 @@ var init_branch_snapshot = __esm({
|
|
|
26638
27040
|
* Garbage-collect snapshots for branches that no longer exist in git.
|
|
26639
27041
|
*/
|
|
26640
27042
|
async garbageCollect() {
|
|
26641
|
-
if (!
|
|
27043
|
+
if (!existsSync48(this.branchDir)) return 0;
|
|
26642
27044
|
const gitBranches = new Set(await listBranches(this.projectRoot));
|
|
26643
27045
|
if (gitBranches.size === 0) return 0;
|
|
26644
27046
|
const snapshots = this.listSnapshots();
|
|
26645
27047
|
let removed = 0;
|
|
26646
27048
|
for (const snapshot of snapshots) {
|
|
26647
27049
|
if (!gitBranches.has(snapshot.branch)) {
|
|
26648
|
-
const snapshotDir =
|
|
27050
|
+
const snapshotDir = join53(this.branchDir, snapshot.id);
|
|
26649
27051
|
rmSync3(snapshotDir, { recursive: true, force: true });
|
|
26650
27052
|
log16.info(`GC removed snapshot for deleted branch: ${snapshot.branch}`);
|
|
26651
27053
|
removed++;
|
|
@@ -26657,20 +27059,20 @@ var init_branch_snapshot = __esm({
|
|
|
26657
27059
|
* List all branch snapshots, sorted by most recently accessed first.
|
|
26658
27060
|
*/
|
|
26659
27061
|
listSnapshots() {
|
|
26660
|
-
if (!
|
|
27062
|
+
if (!existsSync48(this.branchDir)) return [];
|
|
26661
27063
|
try {
|
|
26662
|
-
const entries =
|
|
27064
|
+
const entries = readdirSync11(this.branchDir, { withFileTypes: true });
|
|
26663
27065
|
const snapshots = [];
|
|
26664
27066
|
for (const entry of entries) {
|
|
26665
27067
|
if (!entry.isDirectory()) continue;
|
|
26666
|
-
const overlayPath =
|
|
26667
|
-
if (!
|
|
27068
|
+
const overlayPath = join53(this.branchDir, entry.name, OVERLAY_FILE);
|
|
27069
|
+
if (!existsSync48(overlayPath)) continue;
|
|
26668
27070
|
try {
|
|
26669
|
-
const raw =
|
|
27071
|
+
const raw = readFileSync45(overlayPath, "utf-8");
|
|
26670
27072
|
const snapshot = JSON.parse(raw);
|
|
26671
|
-
const accessPath =
|
|
27073
|
+
const accessPath = join53(this.branchDir, entry.name, ".last_access");
|
|
26672
27074
|
let accessedAt;
|
|
26673
|
-
if (
|
|
27075
|
+
if (existsSync48(accessPath)) {
|
|
26674
27076
|
accessedAt = statSync7(accessPath).mtime;
|
|
26675
27077
|
} else {
|
|
26676
27078
|
accessedAt = statSync7(overlayPath).mtime;
|
|
@@ -26697,7 +27099,7 @@ var init_branch_snapshot = __esm({
|
|
|
26697
27099
|
if (snapshots.length <= MAX_BRANCH_SNAPSHOTS) return;
|
|
26698
27100
|
const toRemove = snapshots.slice(MAX_BRANCH_SNAPSHOTS);
|
|
26699
27101
|
for (const snapshot of toRemove) {
|
|
26700
|
-
const dir =
|
|
27102
|
+
const dir = join53(this.branchDir, snapshot.id);
|
|
26701
27103
|
rmSync3(dir, { recursive: true, force: true });
|
|
26702
27104
|
log16.info(`LRU evicted branch snapshot: ${snapshot.branch}`);
|
|
26703
27105
|
}
|
|
@@ -26714,13 +27116,13 @@ __export(file_hash_state_exports, {
|
|
|
26714
27116
|
});
|
|
26715
27117
|
import { createHash as createHash3 } from "crypto";
|
|
26716
27118
|
import {
|
|
26717
|
-
existsSync as
|
|
27119
|
+
existsSync as existsSync49,
|
|
26718
27120
|
mkdirSync as mkdirSync29,
|
|
26719
|
-
readFileSync as
|
|
27121
|
+
readFileSync as readFileSync46,
|
|
26720
27122
|
renameSync as renameSync2,
|
|
26721
27123
|
writeFileSync as writeFileSync28
|
|
26722
27124
|
} from "fs";
|
|
26723
|
-
import { join as
|
|
27125
|
+
import { join as join54 } from "path";
|
|
26724
27126
|
function contentSha256(content) {
|
|
26725
27127
|
return createHash3("sha256").update(content).digest("hex");
|
|
26726
27128
|
}
|
|
@@ -26734,8 +27136,8 @@ var init_file_hash_state = __esm({
|
|
|
26734
27136
|
statePath;
|
|
26735
27137
|
state;
|
|
26736
27138
|
constructor(unerrDir) {
|
|
26737
|
-
this.stateDir =
|
|
26738
|
-
this.statePath =
|
|
27139
|
+
this.stateDir = join54(unerrDir, "state");
|
|
27140
|
+
this.statePath = join54(this.stateDir, STATE_FILE);
|
|
26739
27141
|
this.state = this.load();
|
|
26740
27142
|
}
|
|
26741
27143
|
/**
|
|
@@ -26780,7 +27182,7 @@ var init_file_hash_state = __esm({
|
|
|
26780
27182
|
* Persist state to disk atomically (write .tmp → rename).
|
|
26781
27183
|
*/
|
|
26782
27184
|
save() {
|
|
26783
|
-
if (!
|
|
27185
|
+
if (!existsSync49(this.stateDir)) {
|
|
26784
27186
|
mkdirSync29(this.stateDir, { recursive: true });
|
|
26785
27187
|
}
|
|
26786
27188
|
const tmpPath = `${this.statePath}.tmp`;
|
|
@@ -26807,11 +27209,11 @@ var init_file_hash_state = __esm({
|
|
|
26807
27209
|
this.state = { files: { ...snapshot.files } };
|
|
26808
27210
|
}
|
|
26809
27211
|
load() {
|
|
26810
|
-
if (!
|
|
27212
|
+
if (!existsSync49(this.statePath)) {
|
|
26811
27213
|
return { files: {} };
|
|
26812
27214
|
}
|
|
26813
27215
|
try {
|
|
26814
|
-
const raw =
|
|
27216
|
+
const raw = readFileSync46(this.statePath, "utf-8");
|
|
26815
27217
|
return JSON.parse(raw);
|
|
26816
27218
|
} catch {
|
|
26817
27219
|
return { files: {} };
|
|
@@ -26823,15 +27225,15 @@ var init_file_hash_state = __esm({
|
|
|
26823
27225
|
|
|
26824
27226
|
// src/tracking/stash-manager.ts
|
|
26825
27227
|
import {
|
|
26826
|
-
existsSync as
|
|
27228
|
+
existsSync as existsSync50,
|
|
26827
27229
|
mkdirSync as mkdirSync30,
|
|
26828
|
-
readFileSync as
|
|
26829
|
-
readdirSync as
|
|
27230
|
+
readFileSync as readFileSync47,
|
|
27231
|
+
readdirSync as readdirSync12,
|
|
26830
27232
|
rmSync as rmSync4,
|
|
26831
27233
|
statSync as statSync8,
|
|
26832
27234
|
writeFileSync as writeFileSync29
|
|
26833
27235
|
} from "fs";
|
|
26834
|
-
import { join as
|
|
27236
|
+
import { join as join55 } from "path";
|
|
26835
27237
|
var MAX_STASH_SNAPSHOTS, OVERLAY_FILE2, HASHES_FILE2, _log6, StashManager;
|
|
26836
27238
|
var init_stash_manager = __esm({
|
|
26837
27239
|
"src/tracking/stash-manager.ts"() {
|
|
@@ -26849,8 +27251,8 @@ var init_stash_manager = __esm({
|
|
|
26849
27251
|
constructor(unerrDir, projectRoot) {
|
|
26850
27252
|
this.unerrDir = unerrDir;
|
|
26851
27253
|
this.projectRoot = projectRoot;
|
|
26852
|
-
this.stashDir =
|
|
26853
|
-
this.gitDir =
|
|
27254
|
+
this.stashDir = join55(unerrDir, "drift", "stash");
|
|
27255
|
+
this.gitDir = join55(projectRoot, ".git");
|
|
26854
27256
|
this.previousStashRef = this.readStashRef();
|
|
26855
27257
|
this.previousStashCount = this.getStashCount();
|
|
26856
27258
|
}
|
|
@@ -26895,8 +27297,8 @@ var init_stash_manager = __esm({
|
|
|
26895
27297
|
return null;
|
|
26896
27298
|
}
|
|
26897
27299
|
const snapshotId = stashRef.slice(0, 12);
|
|
26898
|
-
const snapshotDir =
|
|
26899
|
-
if (!
|
|
27300
|
+
const snapshotDir = join55(this.stashDir, snapshotId);
|
|
27301
|
+
if (!existsSync50(snapshotDir)) {
|
|
26900
27302
|
mkdirSync30(snapshotDir, { recursive: true });
|
|
26901
27303
|
}
|
|
26902
27304
|
const snapshot = {
|
|
@@ -26907,12 +27309,12 @@ var init_stash_manager = __esm({
|
|
|
26907
27309
|
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
26908
27310
|
};
|
|
26909
27311
|
writeFileSync29(
|
|
26910
|
-
|
|
27312
|
+
join55(snapshotDir, OVERLAY_FILE2),
|
|
26911
27313
|
JSON.stringify(snapshot, null, 2),
|
|
26912
27314
|
"utf-8"
|
|
26913
27315
|
);
|
|
26914
27316
|
writeFileSync29(
|
|
26915
|
-
|
|
27317
|
+
join55(snapshotDir, HASHES_FILE2),
|
|
26916
27318
|
JSON.stringify(fileHashState, null, 2),
|
|
26917
27319
|
"utf-8"
|
|
26918
27320
|
);
|
|
@@ -26933,14 +27335,14 @@ var init_stash_manager = __esm({
|
|
|
26933
27335
|
return 0;
|
|
26934
27336
|
}
|
|
26935
27337
|
const latest = snapshots[0];
|
|
26936
|
-
const snapshotDir =
|
|
26937
|
-
const overlayPath =
|
|
26938
|
-
if (!
|
|
27338
|
+
const snapshotDir = join55(this.stashDir, latest.id);
|
|
27339
|
+
const overlayPath = join55(snapshotDir, OVERLAY_FILE2);
|
|
27340
|
+
if (!existsSync50(overlayPath)) {
|
|
26939
27341
|
_log6.warn(`Snapshot ${latest.id} missing overlay file`);
|
|
26940
27342
|
return 0;
|
|
26941
27343
|
}
|
|
26942
27344
|
try {
|
|
26943
|
-
const raw =
|
|
27345
|
+
const raw = readFileSync47(overlayPath, "utf-8");
|
|
26944
27346
|
const snapshot = JSON.parse(raw);
|
|
26945
27347
|
for (const entity of snapshot.entities) {
|
|
26946
27348
|
await localGraph.upsertDriftEntity(entity);
|
|
@@ -26970,10 +27372,10 @@ var init_stash_manager = __esm({
|
|
|
26970
27372
|
const snapshots = this.listSnapshots();
|
|
26971
27373
|
if (snapshots.length === 0) return null;
|
|
26972
27374
|
const latest = snapshots[0];
|
|
26973
|
-
const hashesPath =
|
|
26974
|
-
if (!
|
|
27375
|
+
const hashesPath = join55(this.stashDir, latest.id, HASHES_FILE2);
|
|
27376
|
+
if (!existsSync50(hashesPath)) return null;
|
|
26975
27377
|
try {
|
|
26976
|
-
const raw =
|
|
27378
|
+
const raw = readFileSync47(hashesPath, "utf-8");
|
|
26977
27379
|
return JSON.parse(raw);
|
|
26978
27380
|
} catch {
|
|
26979
27381
|
return null;
|
|
@@ -26984,8 +27386,8 @@ var init_stash_manager = __esm({
|
|
|
26984
27386
|
*/
|
|
26985
27387
|
dropSnapshot(stashRef) {
|
|
26986
27388
|
const snapshotId = stashRef.slice(0, 12);
|
|
26987
|
-
const snapshotDir =
|
|
26988
|
-
if (!
|
|
27389
|
+
const snapshotDir = join55(this.stashDir, snapshotId);
|
|
27390
|
+
if (!existsSync50(snapshotDir)) return false;
|
|
26989
27391
|
rmSync4(snapshotDir, { recursive: true, force: true });
|
|
26990
27392
|
_log6.info(`Dropped stash snapshot: ${snapshotId}`);
|
|
26991
27393
|
return true;
|
|
@@ -26994,14 +27396,14 @@ var init_stash_manager = __esm({
|
|
|
26994
27396
|
* List all stash snapshots, sorted by most recent first.
|
|
26995
27397
|
*/
|
|
26996
27398
|
listSnapshots() {
|
|
26997
|
-
if (!
|
|
27399
|
+
if (!existsSync50(this.stashDir)) return [];
|
|
26998
27400
|
try {
|
|
26999
|
-
const entries =
|
|
27401
|
+
const entries = readdirSync12(this.stashDir, { withFileTypes: true });
|
|
27000
27402
|
const snapshots = [];
|
|
27001
27403
|
for (const entry of entries) {
|
|
27002
27404
|
if (!entry.isDirectory()) continue;
|
|
27003
|
-
const overlayPath =
|
|
27004
|
-
if (!
|
|
27405
|
+
const overlayPath = join55(this.stashDir, entry.name, OVERLAY_FILE2);
|
|
27406
|
+
if (!existsSync50(overlayPath)) continue;
|
|
27005
27407
|
try {
|
|
27006
27408
|
const stat2 = statSync8(overlayPath);
|
|
27007
27409
|
snapshots.push({ id: entry.name, savedAt: stat2.mtime });
|
|
@@ -27018,10 +27420,10 @@ var init_stash_manager = __esm({
|
|
|
27018
27420
|
* Read the current stash ref SHA from `.git/refs/stash`.
|
|
27019
27421
|
*/
|
|
27020
27422
|
readStashRef() {
|
|
27021
|
-
const stashPath =
|
|
27022
|
-
if (!
|
|
27423
|
+
const stashPath = join55(this.gitDir, "refs", "stash");
|
|
27424
|
+
if (!existsSync50(stashPath)) return null;
|
|
27023
27425
|
try {
|
|
27024
|
-
return
|
|
27426
|
+
return readFileSync47(stashPath, "utf-8").trim() || null;
|
|
27025
27427
|
} catch {
|
|
27026
27428
|
return null;
|
|
27027
27429
|
}
|
|
@@ -27030,10 +27432,10 @@ var init_stash_manager = __esm({
|
|
|
27030
27432
|
* Count current stash entries via `.git/logs/refs/stash`.
|
|
27031
27433
|
*/
|
|
27032
27434
|
getStashCount() {
|
|
27033
|
-
const logPath =
|
|
27034
|
-
if (!
|
|
27435
|
+
const logPath = join55(this.gitDir, "logs", "refs", "stash");
|
|
27436
|
+
if (!existsSync50(logPath)) return 0;
|
|
27035
27437
|
try {
|
|
27036
|
-
const content =
|
|
27438
|
+
const content = readFileSync47(logPath, "utf-8");
|
|
27037
27439
|
return content.split("\n").filter((line) => line.trim().length > 0).length;
|
|
27038
27440
|
} catch {
|
|
27039
27441
|
return 0;
|
|
@@ -27047,7 +27449,7 @@ var init_stash_manager = __esm({
|
|
|
27047
27449
|
if (snapshots.length <= MAX_STASH_SNAPSHOTS) return;
|
|
27048
27450
|
const toRemove = snapshots.slice(MAX_STASH_SNAPSHOTS);
|
|
27049
27451
|
for (const snapshot of toRemove) {
|
|
27050
|
-
const dir =
|
|
27452
|
+
const dir = join55(this.stashDir, snapshot.id);
|
|
27051
27453
|
rmSync4(dir, { recursive: true, force: true });
|
|
27052
27454
|
_log6.info(`LRU evicted stash snapshot: ${snapshot.id}`);
|
|
27053
27455
|
}
|
|
@@ -27065,13 +27467,13 @@ __export(drift_tracker_exports, {
|
|
|
27065
27467
|
determineOrigin: () => determineOrigin
|
|
27066
27468
|
});
|
|
27067
27469
|
import {
|
|
27068
|
-
existsSync as
|
|
27470
|
+
existsSync as existsSync51,
|
|
27069
27471
|
mkdirSync as mkdirSync31,
|
|
27070
|
-
readFileSync as
|
|
27472
|
+
readFileSync as readFileSync48,
|
|
27071
27473
|
statSync as statSync9,
|
|
27072
27474
|
writeFileSync as writeFileSync30
|
|
27073
27475
|
} from "fs";
|
|
27074
|
-
import { join as
|
|
27476
|
+
import { join as join56 } from "path";
|
|
27075
27477
|
function determineOrigin(lastSyncTimestamp) {
|
|
27076
27478
|
if (lastSyncTimestamp === 0) return "human";
|
|
27077
27479
|
const elapsed = Date.now() - lastSyncTimestamp;
|
|
@@ -27282,15 +27684,15 @@ var init_drift_tracker = __esm({
|
|
|
27282
27684
|
crossFileInvalidated: 0,
|
|
27283
27685
|
edgesExtracted: 0
|
|
27284
27686
|
};
|
|
27285
|
-
const absPath = filePath.startsWith("/") ? filePath :
|
|
27687
|
+
const absPath = filePath.startsWith("/") ? filePath : join56(this.config.projectRoot, filePath);
|
|
27286
27688
|
const relPath = filePath.startsWith("/") ? filePath.slice(this.config.projectRoot.length + 1) : filePath;
|
|
27287
27689
|
const language = detectLanguage2(relPath);
|
|
27288
27690
|
if (!language) return result;
|
|
27289
|
-
if (
|
|
27691
|
+
if (existsSync51(absPath) && !this.mtimeCache.check(absPath)) {
|
|
27290
27692
|
result.filesSkipped = 1;
|
|
27291
27693
|
return result;
|
|
27292
27694
|
}
|
|
27293
|
-
if (!
|
|
27695
|
+
if (!existsSync51(absPath)) {
|
|
27294
27696
|
const baseEntities2 = await this.localGraph.getEntitiesByFile(relPath);
|
|
27295
27697
|
this.markFileDeleted(relPath, intentId);
|
|
27296
27698
|
result.filesProcessed = 1;
|
|
@@ -27328,7 +27730,7 @@ var init_drift_tracker = __esm({
|
|
|
27328
27730
|
this.maybeEmitDrift(relPath, result);
|
|
27329
27731
|
return result;
|
|
27330
27732
|
}
|
|
27331
|
-
const content =
|
|
27733
|
+
const content = readFileSync48(absPath, "utf-8");
|
|
27332
27734
|
const sha = contentSha256(content);
|
|
27333
27735
|
const decision = this.fileHashManager.shouldProcess(relPath, sha, headSha);
|
|
27334
27736
|
if (decision === "skip") {
|
|
@@ -27562,7 +27964,7 @@ var init_drift_tracker = __esm({
|
|
|
27562
27964
|
return await this.stashManager.restoreSnapshot(this.localGraph);
|
|
27563
27965
|
}
|
|
27564
27966
|
async markFileDeleted(filePath, intentId) {
|
|
27565
|
-
const absPath = filePath.startsWith("/") ? filePath :
|
|
27967
|
+
const absPath = filePath.startsWith("/") ? filePath : join56(this.config.projectRoot, filePath);
|
|
27566
27968
|
this.mtimeCache.evict(absPath);
|
|
27567
27969
|
const baseEntities = await this.localGraph.getEntitiesByFile(filePath);
|
|
27568
27970
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -27647,13 +28049,13 @@ var init_drift_tracker = __esm({
|
|
|
27647
28049
|
return invalidated;
|
|
27648
28050
|
}
|
|
27649
28051
|
async saveDriftSummary() {
|
|
27650
|
-
const driftDir =
|
|
27651
|
-
if (!
|
|
28052
|
+
const driftDir = join56(this.config.unerrDir, "drift");
|
|
28053
|
+
if (!existsSync51(driftDir)) {
|
|
27652
28054
|
mkdirSync31(driftDir, { recursive: true });
|
|
27653
28055
|
}
|
|
27654
28056
|
const summary = await this.getDriftSummary();
|
|
27655
28057
|
writeFileSync30(
|
|
27656
|
-
|
|
28058
|
+
join56(driftDir, "drift_summary.json"),
|
|
27657
28059
|
JSON.stringify(summary, null, 2),
|
|
27658
28060
|
"utf-8"
|
|
27659
28061
|
);
|
|
@@ -28062,8 +28464,8 @@ var incremental_indexer_exports = {};
|
|
|
28062
28464
|
__export(incremental_indexer_exports, {
|
|
28063
28465
|
indexFilesIncremental: () => indexFilesIncremental
|
|
28064
28466
|
});
|
|
28065
|
-
import { existsSync as
|
|
28066
|
-
import { join as
|
|
28467
|
+
import { existsSync as existsSync52, readFileSync as readFileSync49 } from "fs";
|
|
28468
|
+
import { join as join57, relative as relative4 } from "path";
|
|
28067
28469
|
async function indexFilesIncremental(projectRoot, changedFiles, graphStore, repoId) {
|
|
28068
28470
|
const startMs = Date.now();
|
|
28069
28471
|
const db = graphStore.db;
|
|
@@ -28078,9 +28480,9 @@ async function indexFilesIncremental(projectRoot, changedFiles, graphStore, repo
|
|
|
28078
28480
|
const changedEntityKeys = /* @__PURE__ */ new Set();
|
|
28079
28481
|
const deletedEntityKeys = /* @__PURE__ */ new Set();
|
|
28080
28482
|
for (const filePath of changedFiles) {
|
|
28081
|
-
const absPath = filePath.startsWith("/") ? filePath :
|
|
28483
|
+
const absPath = filePath.startsWith("/") ? filePath : join57(projectRoot, filePath);
|
|
28082
28484
|
const relPath = filePath.startsWith("/") ? relative4(projectRoot, filePath) : filePath;
|
|
28083
|
-
if (!
|
|
28485
|
+
if (!existsSync52(absPath)) {
|
|
28084
28486
|
const deleted2 = await deleteFileFromGraph(db, relPath);
|
|
28085
28487
|
filesDeleted++;
|
|
28086
28488
|
totalEntitiesDeleted += deleted2.entitiesDeleted;
|
|
@@ -28093,7 +28495,7 @@ async function indexFilesIncremental(projectRoot, changedFiles, graphStore, repo
|
|
|
28093
28495
|
}
|
|
28094
28496
|
let content;
|
|
28095
28497
|
try {
|
|
28096
|
-
content =
|
|
28498
|
+
content = readFileSync49(absPath, "utf-8");
|
|
28097
28499
|
} catch {
|
|
28098
28500
|
continue;
|
|
28099
28501
|
}
|
|
@@ -28381,6 +28783,7 @@ async function upsertEntitiesBatched(db, entities) {
|
|
|
28381
28783
|
e.name,
|
|
28382
28784
|
e.file_path,
|
|
28383
28785
|
e.start_line ?? 0,
|
|
28786
|
+
e.end_line ?? 0,
|
|
28384
28787
|
e.signature ?? "",
|
|
28385
28788
|
e.body ?? "",
|
|
28386
28789
|
e.fan_in ?? 0,
|
|
@@ -28398,21 +28801,22 @@ async function upsertEntitiesBatched(db, entities) {
|
|
|
28398
28801
|
});
|
|
28399
28802
|
try {
|
|
28400
28803
|
await db.run(
|
|
28401
|
-
`?[key, kind, name, file_path, start_line, signature, body, fan_in, fan_out, risk_level, is_test] <- [${rowStrs.join(", ")}]
|
|
28402
|
-
:put entities { key => kind, name, file_path, start_line, signature, body, fan_in, fan_out, risk_level, is_test }`
|
|
28804
|
+
`?[key, kind, name, file_path, start_line, end_line, signature, body, fan_in, fan_out, risk_level, is_test] <- [${rowStrs.join(", ")}]
|
|
28805
|
+
:put entities { key => kind, name, file_path, start_line, end_line, signature, body, fan_in, fan_out, risk_level, is_test }`
|
|
28403
28806
|
);
|
|
28404
28807
|
} catch {
|
|
28405
28808
|
for (const entity of entities) {
|
|
28406
28809
|
try {
|
|
28407
28810
|
await db.run(
|
|
28408
|
-
`?[key, kind, name, file_path, start_line, signature, body, fan_in, fan_out, risk_level, is_test] <- [[$key, $kind, $name, $fp, $sl, $sig, $body, $fi, $fo, $rl, $is_test]]
|
|
28409
|
-
:put entities { key => kind, name, file_path, start_line, signature, body, fan_in, fan_out, risk_level, is_test }`,
|
|
28811
|
+
`?[key, kind, name, file_path, start_line, end_line, signature, body, fan_in, fan_out, risk_level, is_test] <- [[$key, $kind, $name, $fp, $sl, $el, $sig, $body, $fi, $fo, $rl, $is_test]]
|
|
28812
|
+
:put entities { key => kind, name, file_path, start_line, end_line, signature, body, fan_in, fan_out, risk_level, is_test }`,
|
|
28410
28813
|
{
|
|
28411
28814
|
key: entity.key,
|
|
28412
28815
|
kind: entity.kind,
|
|
28413
28816
|
name: entity.name,
|
|
28414
28817
|
fp: entity.file_path,
|
|
28415
28818
|
sl: entity.start_line ?? 0,
|
|
28819
|
+
el: entity.end_line ?? 0,
|
|
28416
28820
|
sig: entity.signature ?? "",
|
|
28417
28821
|
body: entity.body ?? "",
|
|
28418
28822
|
fi: entity.fan_in ?? 0,
|
|
@@ -29258,8 +29662,8 @@ var workspace_manifest_exports = {};
|
|
|
29258
29662
|
__export(workspace_manifest_exports, {
|
|
29259
29663
|
WorkspaceManifest: () => WorkspaceManifest
|
|
29260
29664
|
});
|
|
29261
|
-
import { existsSync as
|
|
29262
|
-
import { join as
|
|
29665
|
+
import { existsSync as existsSync53, mkdirSync as mkdirSync32, readFileSync as readFileSync50, writeFileSync as writeFileSync31 } from "fs";
|
|
29666
|
+
import { join as join58 } from "path";
|
|
29263
29667
|
var WorkspaceManifest;
|
|
29264
29668
|
var init_workspace_manifest = __esm({
|
|
29265
29669
|
"src/tracking/workspace-manifest.ts"() {
|
|
@@ -29269,7 +29673,7 @@ var init_workspace_manifest = __esm({
|
|
|
29269
29673
|
this.unerrDir = unerrDir;
|
|
29270
29674
|
this.repoId = repoId;
|
|
29271
29675
|
this.sessionId = sessionId;
|
|
29272
|
-
this.manifestPath =
|
|
29676
|
+
this.manifestPath = join58(unerrDir, "manifest.json");
|
|
29273
29677
|
this.data = this.load();
|
|
29274
29678
|
}
|
|
29275
29679
|
unerrDir;
|
|
@@ -29364,7 +29768,7 @@ var init_workspace_manifest = __esm({
|
|
|
29364
29768
|
}
|
|
29365
29769
|
// ── Internal ─────────────────────────────────────────────────────
|
|
29366
29770
|
load() {
|
|
29367
|
-
if (!
|
|
29771
|
+
if (!existsSync53(this.manifestPath)) {
|
|
29368
29772
|
return {
|
|
29369
29773
|
version: 1,
|
|
29370
29774
|
repoId: this.repoId,
|
|
@@ -29374,7 +29778,7 @@ var init_workspace_manifest = __esm({
|
|
|
29374
29778
|
};
|
|
29375
29779
|
}
|
|
29376
29780
|
try {
|
|
29377
|
-
const raw =
|
|
29781
|
+
const raw = readFileSync50(this.manifestPath, "utf-8");
|
|
29378
29782
|
const parsed = JSON.parse(raw);
|
|
29379
29783
|
if (parsed.repoId !== this.repoId) {
|
|
29380
29784
|
return {
|
|
@@ -29397,7 +29801,7 @@ var init_workspace_manifest = __esm({
|
|
|
29397
29801
|
}
|
|
29398
29802
|
}
|
|
29399
29803
|
save() {
|
|
29400
|
-
if (!
|
|
29804
|
+
if (!existsSync53(this.unerrDir)) {
|
|
29401
29805
|
mkdirSync32(this.unerrDir, { recursive: true });
|
|
29402
29806
|
}
|
|
29403
29807
|
writeFileSync31(
|
|
@@ -29591,8 +29995,8 @@ var log_tailer_exports = {};
|
|
|
29591
29995
|
__export(log_tailer_exports, {
|
|
29592
29996
|
startLogTailer: () => startLogTailer
|
|
29593
29997
|
});
|
|
29594
|
-
import { existsSync as
|
|
29595
|
-
import { join as
|
|
29998
|
+
import { existsSync as existsSync54, statSync as statSync10, openSync, readSync, closeSync, watch } from "fs";
|
|
29999
|
+
import { join as join59 } from "path";
|
|
29596
30000
|
function formatSize2(bytes) {
|
|
29597
30001
|
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
29598
30002
|
if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
@@ -29699,29 +30103,29 @@ function tailFile(state, handler) {
|
|
|
29699
30103
|
}
|
|
29700
30104
|
}
|
|
29701
30105
|
function startLogTailer(cwd, options) {
|
|
29702
|
-
const logsDir =
|
|
29703
|
-
const compressionPath =
|
|
29704
|
-
const generalPath =
|
|
29705
|
-
const tokenFlowPath =
|
|
29706
|
-
const fileReadsPath =
|
|
30106
|
+
const logsDir = join59(cwd, ".unerr", "logs");
|
|
30107
|
+
const compressionPath = join59(logsDir, "compression.jsonl");
|
|
30108
|
+
const generalPath = join59(logsDir, "unerr.jsonl");
|
|
30109
|
+
const tokenFlowPath = join59(logsDir, "token-flow.jsonl");
|
|
30110
|
+
const fileReadsPath = join59(logsDir, "file-reads.jsonl");
|
|
29707
30111
|
const compressionState = {
|
|
29708
30112
|
path: compressionPath,
|
|
29709
|
-
offset:
|
|
30113
|
+
offset: existsSync54(compressionPath) ? statSync10(compressionPath).size : 0,
|
|
29710
30114
|
watcher: null
|
|
29711
30115
|
};
|
|
29712
30116
|
const generalState = {
|
|
29713
30117
|
path: generalPath,
|
|
29714
|
-
offset:
|
|
30118
|
+
offset: existsSync54(generalPath) ? statSync10(generalPath).size : 0,
|
|
29715
30119
|
watcher: null
|
|
29716
30120
|
};
|
|
29717
30121
|
const tokenFlowState = {
|
|
29718
30122
|
path: tokenFlowPath,
|
|
29719
|
-
offset:
|
|
30123
|
+
offset: existsSync54(tokenFlowPath) ? statSync10(tokenFlowPath).size : 0,
|
|
29720
30124
|
watcher: null
|
|
29721
30125
|
};
|
|
29722
30126
|
const fileReadsState = {
|
|
29723
30127
|
path: fileReadsPath,
|
|
29724
|
-
offset:
|
|
30128
|
+
offset: existsSync54(fileReadsPath) ? statSync10(fileReadsPath).size : 0,
|
|
29725
30129
|
watcher: null
|
|
29726
30130
|
};
|
|
29727
30131
|
function setupWatcher(state, handler) {
|
|
@@ -29749,7 +30153,7 @@ function startLogTailer(cwd, options) {
|
|
|
29749
30153
|
];
|
|
29750
30154
|
const pollInterval = setInterval(() => {
|
|
29751
30155
|
for (const { state, handler } of allStates) {
|
|
29752
|
-
if (!state.watcher &&
|
|
30156
|
+
if (!state.watcher && existsSync54(state.path)) {
|
|
29753
30157
|
try {
|
|
29754
30158
|
state.offset = 0;
|
|
29755
30159
|
state.watcher = watch(state.path, () => {
|
|
@@ -30467,6 +30871,336 @@ function createIntelligenceRoutes(deps) {
|
|
|
30467
30871
|
}
|
|
30468
30872
|
});
|
|
30469
30873
|
});
|
|
30874
|
+
app.get("/risk-hotspots", async (c) => {
|
|
30875
|
+
const start = performance.now();
|
|
30876
|
+
const limit = parseLimit(c.req.query("limit"), 20, 50);
|
|
30877
|
+
if (!deps.localGraph) {
|
|
30878
|
+
return c.json(
|
|
30879
|
+
{
|
|
30880
|
+
data: [],
|
|
30881
|
+
_meta: {
|
|
30882
|
+
source: "local",
|
|
30883
|
+
graph: "unavailable",
|
|
30884
|
+
latency_ms: Math.round((performance.now() - start) * 100) / 100
|
|
30885
|
+
}
|
|
30886
|
+
},
|
|
30887
|
+
503
|
|
30888
|
+
);
|
|
30889
|
+
}
|
|
30890
|
+
const topNodes = await deps.localGraph.getCriticalNodes(limit);
|
|
30891
|
+
const keys = topNodes.map((n) => n.key);
|
|
30892
|
+
const testCountMap = /* @__PURE__ */ new Map();
|
|
30893
|
+
if (keys.length > 0) {
|
|
30894
|
+
try {
|
|
30895
|
+
const directResult = await deps.localGraph.db.run(
|
|
30896
|
+
`?[target, count(tk)] := *edges{from_key: tk, to_key: target, type: "tests"},
|
|
30897
|
+
target in $keys`,
|
|
30898
|
+
{ keys }
|
|
30899
|
+
);
|
|
30900
|
+
for (const row of directResult.rows) {
|
|
30901
|
+
testCountMap.set(row[0], row[1]);
|
|
30902
|
+
}
|
|
30903
|
+
const transitiveResult = await deps.localGraph.db.run(
|
|
30904
|
+
`?[target, count(tk)] := *edges{from_key: mid, to_key: target, type: "calls"},
|
|
30905
|
+
*edges{from_key: tk, to_key: mid, type: "tests"},
|
|
30906
|
+
target in $keys`,
|
|
30907
|
+
{ keys }
|
|
30908
|
+
);
|
|
30909
|
+
for (const row of transitiveResult.rows) {
|
|
30910
|
+
const key = row[0];
|
|
30911
|
+
const existing = testCountMap.get(key) ?? 0;
|
|
30912
|
+
testCountMap.set(key, existing + row[1]);
|
|
30913
|
+
}
|
|
30914
|
+
} catch {
|
|
30915
|
+
}
|
|
30916
|
+
}
|
|
30917
|
+
const callerCountMap = /* @__PURE__ */ new Map();
|
|
30918
|
+
if (keys.length > 0) {
|
|
30919
|
+
try {
|
|
30920
|
+
const callerResult = await deps.localGraph.db.run(
|
|
30921
|
+
`?[target, count(caller)] := *edges{from_key: caller, to_key: target, type: "calls"},
|
|
30922
|
+
target in $keys`,
|
|
30923
|
+
{ keys }
|
|
30924
|
+
);
|
|
30925
|
+
for (const row of callerResult.rows) {
|
|
30926
|
+
callerCountMap.set(row[0], row[1]);
|
|
30927
|
+
}
|
|
30928
|
+
} catch {
|
|
30929
|
+
}
|
|
30930
|
+
}
|
|
30931
|
+
const hotspots = topNodes.map((n) => ({
|
|
30932
|
+
key: n.key,
|
|
30933
|
+
name: n.name,
|
|
30934
|
+
file_path: n.file_path,
|
|
30935
|
+
kind: n.kind,
|
|
30936
|
+
fan_in: n.fan_in,
|
|
30937
|
+
fan_out: n.fan_out,
|
|
30938
|
+
degree: n.degree,
|
|
30939
|
+
risk_level: n.risk_level,
|
|
30940
|
+
community_label: n.community_label,
|
|
30941
|
+
test_count: testCountMap.get(n.key) ?? 0,
|
|
30942
|
+
caller_count: callerCountMap.get(n.key) ?? 0
|
|
30943
|
+
}));
|
|
30944
|
+
return c.json({
|
|
30945
|
+
data: hotspots,
|
|
30946
|
+
_meta: {
|
|
30947
|
+
source: "local",
|
|
30948
|
+
latency_ms: Math.round((performance.now() - start) * 100) / 100
|
|
30949
|
+
}
|
|
30950
|
+
});
|
|
30951
|
+
});
|
|
30952
|
+
app.get("/insights", async (c) => {
|
|
30953
|
+
const start = performance.now();
|
|
30954
|
+
if (!deps.localGraph) {
|
|
30955
|
+
return c.json(
|
|
30956
|
+
{
|
|
30957
|
+
data: null,
|
|
30958
|
+
_meta: {
|
|
30959
|
+
source: "local",
|
|
30960
|
+
graph: "unavailable",
|
|
30961
|
+
latency_ms: Math.round((performance.now() - start) * 100) / 100
|
|
30962
|
+
}
|
|
30963
|
+
},
|
|
30964
|
+
503
|
|
30965
|
+
);
|
|
30966
|
+
}
|
|
30967
|
+
const topN = 50;
|
|
30968
|
+
const topNodes = await deps.localGraph.getCriticalNodes(topN);
|
|
30969
|
+
const keys = topNodes.map((n) => n.key);
|
|
30970
|
+
const testCountMap = /* @__PURE__ */ new Map();
|
|
30971
|
+
if (keys.length > 0) {
|
|
30972
|
+
try {
|
|
30973
|
+
const directResult = await deps.localGraph.db.run(
|
|
30974
|
+
`?[target, count(tk)] := *edges{from_key: tk, to_key: target, type: "tests"},
|
|
30975
|
+
target in $keys`,
|
|
30976
|
+
{ keys }
|
|
30977
|
+
);
|
|
30978
|
+
for (const row of directResult.rows) {
|
|
30979
|
+
testCountMap.set(row[0], row[1]);
|
|
30980
|
+
}
|
|
30981
|
+
const transitiveResult = await deps.localGraph.db.run(
|
|
30982
|
+
`?[target, count(tk)] := *edges{from_key: mid, to_key: target, type: "calls"},
|
|
30983
|
+
*edges{from_key: tk, to_key: mid, type: "tests"},
|
|
30984
|
+
target in $keys`,
|
|
30985
|
+
{ keys }
|
|
30986
|
+
);
|
|
30987
|
+
for (const row of transitiveResult.rows) {
|
|
30988
|
+
const k = row[0];
|
|
30989
|
+
testCountMap.set(k, (testCountMap.get(k) ?? 0) + row[1]);
|
|
30990
|
+
}
|
|
30991
|
+
} catch {
|
|
30992
|
+
}
|
|
30993
|
+
}
|
|
30994
|
+
let totalBlastRadius = 0;
|
|
30995
|
+
let testedBlastRadius = 0;
|
|
30996
|
+
let untestedBlastRadius = 0;
|
|
30997
|
+
for (const n of topNodes) {
|
|
30998
|
+
const weight = Math.max(n.fan_in, 1);
|
|
30999
|
+
totalBlastRadius += weight;
|
|
31000
|
+
if ((testCountMap.get(n.key) ?? 0) > 0) {
|
|
31001
|
+
testedBlastRadius += weight;
|
|
31002
|
+
} else {
|
|
31003
|
+
untestedBlastRadius += weight;
|
|
31004
|
+
}
|
|
31005
|
+
}
|
|
31006
|
+
const blastRadiusCoverage = totalBlastRadius > 0 ? Math.round(testedBlastRadius / totalBlastRadius * 100) : 0;
|
|
31007
|
+
const bottlenecks = topNodes.filter(
|
|
31008
|
+
(n) => n.fan_in >= 3 && n.fan_out >= 2 && (testCountMap.get(n.key) ?? 0) === 0
|
|
31009
|
+
).map((n) => ({
|
|
31010
|
+
key: n.key,
|
|
31011
|
+
name: n.name,
|
|
31012
|
+
file_path: n.file_path,
|
|
31013
|
+
kind: n.kind,
|
|
31014
|
+
fan_in: n.fan_in,
|
|
31015
|
+
fan_out: n.fan_out,
|
|
31016
|
+
degree: n.degree,
|
|
31017
|
+
risk_level: n.risk_level
|
|
31018
|
+
}));
|
|
31019
|
+
const riskDistribution = { high: 0, medium: 0, low: 0 };
|
|
31020
|
+
for (const n of topNodes) {
|
|
31021
|
+
const level = n.risk_level;
|
|
31022
|
+
if (level in riskDistribution) riskDistribution[level]++;
|
|
31023
|
+
}
|
|
31024
|
+
const sortedByDegree = [...topNodes].sort((a, b) => b.degree - a.degree);
|
|
31025
|
+
const totalDegree = topNodes.reduce((s, n) => s + n.degree, 0);
|
|
31026
|
+
const top5Degree = sortedByDegree.slice(0, 5).reduce((s, n) => s + n.degree, 0);
|
|
31027
|
+
const riskConcentration = totalDegree > 0 ? Math.round(top5Degree / totalDegree * 100) : 0;
|
|
31028
|
+
const communityMap = /* @__PURE__ */ new Map();
|
|
31029
|
+
for (const n of topNodes) {
|
|
31030
|
+
const label = n.community_label || `cluster-${n.community}`;
|
|
31031
|
+
let comm = communityMap.get(label);
|
|
31032
|
+
if (!comm) {
|
|
31033
|
+
comm = {
|
|
31034
|
+
label,
|
|
31035
|
+
entities: 0,
|
|
31036
|
+
totalDegree: 0,
|
|
31037
|
+
riskHigh: 0,
|
|
31038
|
+
riskMedium: 0,
|
|
31039
|
+
riskLow: 0,
|
|
31040
|
+
untested: 0,
|
|
31041
|
+
tested: 0,
|
|
31042
|
+
totalFanIn: 0,
|
|
31043
|
+
untestedFanIn: 0
|
|
31044
|
+
};
|
|
31045
|
+
communityMap.set(label, comm);
|
|
31046
|
+
}
|
|
31047
|
+
comm.entities++;
|
|
31048
|
+
comm.totalDegree += n.degree;
|
|
31049
|
+
comm.totalFanIn += n.fan_in;
|
|
31050
|
+
const hasCoverage = (testCountMap.get(n.key) ?? 0) > 0;
|
|
31051
|
+
if (hasCoverage) comm.tested++;
|
|
31052
|
+
else {
|
|
31053
|
+
comm.untested++;
|
|
31054
|
+
comm.untestedFanIn += n.fan_in;
|
|
31055
|
+
}
|
|
31056
|
+
if (n.risk_level === "high") comm.riskHigh++;
|
|
31057
|
+
else if (n.risk_level === "medium") comm.riskMedium++;
|
|
31058
|
+
else comm.riskLow++;
|
|
31059
|
+
}
|
|
31060
|
+
const communityHealth = [];
|
|
31061
|
+
for (const [, comm] of communityMap) {
|
|
31062
|
+
const total = comm.tested + comm.untested;
|
|
31063
|
+
communityHealth.push({
|
|
31064
|
+
label: comm.label,
|
|
31065
|
+
entities: comm.entities,
|
|
31066
|
+
tested: comm.tested,
|
|
31067
|
+
untested: comm.untested,
|
|
31068
|
+
coveragePct: total > 0 ? Math.round(comm.tested / total * 100) : 0,
|
|
31069
|
+
blastRadiusCoveragePct: comm.totalFanIn > 0 ? Math.round(
|
|
31070
|
+
(comm.totalFanIn - comm.untestedFanIn) / comm.totalFanIn * 100
|
|
31071
|
+
) : 0,
|
|
31072
|
+
riskHigh: comm.riskHigh,
|
|
31073
|
+
riskMedium: comm.riskMedium,
|
|
31074
|
+
riskLow: comm.riskLow,
|
|
31075
|
+
totalDegree: comm.totalDegree
|
|
31076
|
+
});
|
|
31077
|
+
}
|
|
31078
|
+
communityHealth.sort(
|
|
31079
|
+
(a, b) => a.blastRadiusCoveragePct - b.blastRadiusCoveragePct
|
|
31080
|
+
);
|
|
31081
|
+
let mostCoupledPair = null;
|
|
31082
|
+
try {
|
|
31083
|
+
const fcResult = await deps.localGraph.db.run(
|
|
31084
|
+
`?[from_label, to_label, w] :=
|
|
31085
|
+
*file_edges{from_file, to_file, weight: w},
|
|
31086
|
+
*file_communities{file_path: from_file, label: from_label},
|
|
31087
|
+
*file_communities{file_path: to_file, label: to_label},
|
|
31088
|
+
from_label != to_label
|
|
31089
|
+
:order -w
|
|
31090
|
+
:limit 1`
|
|
31091
|
+
);
|
|
31092
|
+
if (fcResult.rows.length > 0) {
|
|
31093
|
+
const [from, to, weight] = fcResult.rows[0];
|
|
31094
|
+
mostCoupledPair = { from, to, weight };
|
|
31095
|
+
}
|
|
31096
|
+
} catch {
|
|
31097
|
+
}
|
|
31098
|
+
const insights = [];
|
|
31099
|
+
if (blastRadiusCoverage < 50) {
|
|
31100
|
+
insights.push({
|
|
31101
|
+
id: "blast-radius-coverage",
|
|
31102
|
+
severity: "critical",
|
|
31103
|
+
title: `Blast-radius coverage is only ${blastRadiusCoverage}%`,
|
|
31104
|
+
description: `${untestedBlastRadius} dependency-weighted risk sits in untested code. Your entity-count coverage looks better but hides that your most-depended-on code is unprotected.`,
|
|
31105
|
+
metric: blastRadiusCoverage,
|
|
31106
|
+
metricLabel: "blast-radius coverage"
|
|
31107
|
+
});
|
|
31108
|
+
} else if (blastRadiusCoverage < 75) {
|
|
31109
|
+
insights.push({
|
|
31110
|
+
id: "blast-radius-coverage",
|
|
31111
|
+
severity: "warning",
|
|
31112
|
+
title: `Blast-radius coverage at ${blastRadiusCoverage}%`,
|
|
31113
|
+
description: `Your most critical code paths are partially covered, but ${untestedBlastRadius} dependency-weight remains untested.`,
|
|
31114
|
+
metric: blastRadiusCoverage,
|
|
31115
|
+
metricLabel: "blast-radius coverage"
|
|
31116
|
+
});
|
|
31117
|
+
} else {
|
|
31118
|
+
insights.push({
|
|
31119
|
+
id: "blast-radius-coverage",
|
|
31120
|
+
severity: "positive",
|
|
31121
|
+
title: `Strong blast-radius coverage: ${blastRadiusCoverage}%`,
|
|
31122
|
+
description: "Your highest-impact code paths are well tested. Regressions are unlikely to cascade silently.",
|
|
31123
|
+
metric: blastRadiusCoverage,
|
|
31124
|
+
metricLabel: "blast-radius coverage"
|
|
31125
|
+
});
|
|
31126
|
+
}
|
|
31127
|
+
if (bottlenecks.length > 0) {
|
|
31128
|
+
const totalBottleneckFanIn = bottlenecks.reduce(
|
|
31129
|
+
(s, b) => s + b.fan_in,
|
|
31130
|
+
0
|
|
31131
|
+
);
|
|
31132
|
+
insights.push({
|
|
31133
|
+
id: "bottlenecks",
|
|
31134
|
+
severity: bottlenecks.length >= 5 ? "critical" : "warning",
|
|
31135
|
+
title: `${bottlenecks.length} structural bottleneck${bottlenecks.length !== 1 ? "s" : ""} with zero tests`,
|
|
31136
|
+
description: `These functions have high fan-in AND fan-out \u2014 all dependency traffic flows through them. Combined, ${totalBottleneckFanIn} dependents are exposed. A failure in any one cascades in both directions.`,
|
|
31137
|
+
metric: bottlenecks.length,
|
|
31138
|
+
metricLabel: "bottlenecks"
|
|
31139
|
+
});
|
|
31140
|
+
}
|
|
31141
|
+
if (riskConcentration > 40) {
|
|
31142
|
+
insights.push({
|
|
31143
|
+
id: "risk-concentration",
|
|
31144
|
+
severity: riskConcentration > 60 ? "warning" : "info",
|
|
31145
|
+
title: `${riskConcentration}% of structural risk concentrated in top 5 entities`,
|
|
31146
|
+
description: `Your risk isn't spread evenly \u2014 a small number of entities carry most of the dependency weight. Focus testing and review here for maximum impact.`,
|
|
31147
|
+
metric: riskConcentration,
|
|
31148
|
+
metricLabel: "risk in top 5"
|
|
31149
|
+
});
|
|
31150
|
+
}
|
|
31151
|
+
if (mostCoupledPair && mostCoupledPair.weight >= 3) {
|
|
31152
|
+
insights.push({
|
|
31153
|
+
id: "coupling-hotspot",
|
|
31154
|
+
severity: mostCoupledPair.weight >= 10 ? "warning" : "info",
|
|
31155
|
+
title: `"${mostCoupledPair.from}" and "${mostCoupledPair.to}" are tightly coupled`,
|
|
31156
|
+
description: `${mostCoupledPair.weight} cross-boundary calls between these modules. Changes in one are likely to require changes in the other.`,
|
|
31157
|
+
metric: mostCoupledPair.weight,
|
|
31158
|
+
metricLabel: "cross-boundary calls"
|
|
31159
|
+
});
|
|
31160
|
+
}
|
|
31161
|
+
if (riskDistribution.high === 0 && topNodes.length > 0) {
|
|
31162
|
+
insights.push({
|
|
31163
|
+
id: "no-high-risk",
|
|
31164
|
+
severity: "positive",
|
|
31165
|
+
title: "No high-risk entities detected",
|
|
31166
|
+
description: "Your top entities are well-balanced with moderate dependency counts. The codebase structure is healthy."
|
|
31167
|
+
});
|
|
31168
|
+
}
|
|
31169
|
+
const bottleneckPenalty = Math.min(bottlenecks.length * 5, 25);
|
|
31170
|
+
const highRiskPenalty = Math.min(riskDistribution.high * 3, 15);
|
|
31171
|
+
const concentrationPenalty = riskConcentration > 50 ? (riskConcentration - 50) * 0.4 : 0;
|
|
31172
|
+
const healthScore = Math.max(
|
|
31173
|
+
0,
|
|
31174
|
+
Math.min(
|
|
31175
|
+
100,
|
|
31176
|
+
Math.round(
|
|
31177
|
+
blastRadiusCoverage * 0.4 + (100 - bottleneckPenalty) * 0.25 + (100 - concentrationPenalty) * 0.2 + (100 - highRiskPenalty) * 0.15
|
|
31178
|
+
)
|
|
31179
|
+
)
|
|
31180
|
+
);
|
|
31181
|
+
const healthGrade = healthScore >= 90 ? "A" : healthScore >= 80 ? "B" : healthScore >= 65 ? "C" : healthScore >= 50 ? "D" : "F";
|
|
31182
|
+
return c.json({
|
|
31183
|
+
data: {
|
|
31184
|
+
healthScore,
|
|
31185
|
+
healthGrade,
|
|
31186
|
+
blastRadiusCoverage,
|
|
31187
|
+
untestedBlastRadius,
|
|
31188
|
+
testedBlastRadius,
|
|
31189
|
+
totalBlastRadius,
|
|
31190
|
+
bottlenecks,
|
|
31191
|
+
riskDistribution,
|
|
31192
|
+
riskConcentration,
|
|
31193
|
+
communityHealth,
|
|
31194
|
+
mostCoupledPair,
|
|
31195
|
+
insights
|
|
31196
|
+
},
|
|
31197
|
+
_meta: {
|
|
31198
|
+
source: "local",
|
|
31199
|
+
entities_analyzed: topNodes.length,
|
|
31200
|
+
latency_ms: Math.round((performance.now() - start) * 100) / 100
|
|
31201
|
+
}
|
|
31202
|
+
});
|
|
31203
|
+
});
|
|
30470
31204
|
app.get("/durability", async (c) => {
|
|
30471
31205
|
const start = performance.now();
|
|
30472
31206
|
const limit = parseLimit(c.req.query("ledger_limit"), 800, 5e3);
|
|
@@ -30753,13 +31487,13 @@ function createSystemRoutes(deps) {
|
|
|
30753
31487
|
});
|
|
30754
31488
|
app.get("/config", async (c) => {
|
|
30755
31489
|
const start = performance.now();
|
|
30756
|
-
const { existsSync:
|
|
30757
|
-
const { join:
|
|
31490
|
+
const { existsSync: existsSync63, readFileSync: readFileSync57 } = await import("fs");
|
|
31491
|
+
const { join: join68 } = await import("path");
|
|
30758
31492
|
let config = {};
|
|
30759
|
-
const configPath =
|
|
30760
|
-
if (
|
|
31493
|
+
const configPath = join68(deps.cwd, ".unerr", "config.json");
|
|
31494
|
+
if (existsSync63(configPath)) {
|
|
30761
31495
|
try {
|
|
30762
|
-
config = JSON.parse(
|
|
31496
|
+
config = JSON.parse(readFileSync57(configPath, "utf-8"));
|
|
30763
31497
|
} catch {
|
|
30764
31498
|
config = { error: "unreadable" };
|
|
30765
31499
|
}
|
|
@@ -30960,20 +31694,20 @@ __export(session_history_exports, {
|
|
|
30960
31694
|
getWeeklyStats: () => getWeeklyStats,
|
|
30961
31695
|
readSessionHistory: () => readSessionHistory
|
|
30962
31696
|
});
|
|
30963
|
-
import { appendFileSync as appendFileSync8, existsSync as
|
|
30964
|
-
import { join as
|
|
31697
|
+
import { appendFileSync as appendFileSync8, existsSync as existsSync55, mkdirSync as mkdirSync33, readFileSync as readFileSync51 } from "fs";
|
|
31698
|
+
import { join as join60 } from "path";
|
|
30965
31699
|
function appendSessionHistory(unerrDir, entry) {
|
|
30966
|
-
const stateDir =
|
|
30967
|
-
if (!
|
|
30968
|
-
const historyPath =
|
|
31700
|
+
const stateDir = join60(unerrDir, "state");
|
|
31701
|
+
if (!existsSync55(stateDir)) mkdirSync33(stateDir, { recursive: true });
|
|
31702
|
+
const historyPath = join60(stateDir, "session-history.jsonl");
|
|
30969
31703
|
appendFileSync8(historyPath, `${JSON.stringify(entry)}
|
|
30970
31704
|
`, "utf-8");
|
|
30971
31705
|
}
|
|
30972
31706
|
function readSessionHistory(unerrDir) {
|
|
30973
|
-
const historyPath =
|
|
30974
|
-
if (!
|
|
31707
|
+
const historyPath = join60(unerrDir, "state", "session-history.jsonl");
|
|
31708
|
+
if (!existsSync55(historyPath)) return [];
|
|
30975
31709
|
try {
|
|
30976
|
-
const content =
|
|
31710
|
+
const content = readFileSync51(historyPath, "utf-8");
|
|
30977
31711
|
return content.split("\n").filter(Boolean).map((line) => {
|
|
30978
31712
|
try {
|
|
30979
31713
|
return JSON.parse(line);
|
|
@@ -31211,7 +31945,7 @@ function createTokenFlowRoutes(deps) {
|
|
|
31211
31945
|
first_ts: data.first_ts,
|
|
31212
31946
|
last_ts: data.last_ts,
|
|
31213
31947
|
mechanisms: [...data.mechanisms],
|
|
31214
|
-
agent_name: agentBySession.get(id) ?? null
|
|
31948
|
+
agent_name: agentBySession.get(id) ?? deps.getAgentName?.(id) ?? null
|
|
31215
31949
|
};
|
|
31216
31950
|
}).sort((a, b) => b.last_ts.localeCompare(a.last_ts));
|
|
31217
31951
|
const paginated = allSessions.slice(offset, offset + limit);
|
|
@@ -31350,18 +32084,297 @@ var init_token_flow2 = __esm({
|
|
|
31350
32084
|
}
|
|
31351
32085
|
});
|
|
31352
32086
|
|
|
32087
|
+
// src/server/routes/reasoning-quality.ts
|
|
32088
|
+
import { Hono as Hono7 } from "hono";
|
|
32089
|
+
function computeQualityMetrics(events) {
|
|
32090
|
+
if (events.length === 0) {
|
|
32091
|
+
return {
|
|
32092
|
+
signal_to_noise_ratio: 0,
|
|
32093
|
+
noise_removed_pct: 0,
|
|
32094
|
+
context_density: 0,
|
|
32095
|
+
entities_resolved: 0,
|
|
32096
|
+
graph_tokens_delivered: 0,
|
|
32097
|
+
attention_multiplier: 1,
|
|
32098
|
+
first_call_resolution_rate: 0,
|
|
32099
|
+
graph_calls: 0,
|
|
32100
|
+
total_tool_calls: 0,
|
|
32101
|
+
turns_saved: 0,
|
|
32102
|
+
exploration_loops_prevented: 0,
|
|
32103
|
+
blast_radius_warnings: 0,
|
|
32104
|
+
circuit_breaker_activations: 0,
|
|
32105
|
+
convention_injections: 0,
|
|
32106
|
+
prevention_score: 0,
|
|
32107
|
+
reasoning_quality_multiplier: 0,
|
|
32108
|
+
total_sessions: 0,
|
|
32109
|
+
total_turns: 0,
|
|
32110
|
+
total_events: 0
|
|
32111
|
+
};
|
|
32112
|
+
}
|
|
32113
|
+
let totalWithout = 0;
|
|
32114
|
+
let totalWith = 0;
|
|
32115
|
+
let graphCalls = 0;
|
|
32116
|
+
let graphTokensDelivered = 0;
|
|
32117
|
+
let shellCompressionEvents = 0;
|
|
32118
|
+
let behaviorEvents = 0;
|
|
32119
|
+
let blastRadiusWarnings = 0;
|
|
32120
|
+
let circuitBreakerActivations = 0;
|
|
32121
|
+
let conventionInjections = 0;
|
|
32122
|
+
let explorationLoopsPrevented = 0;
|
|
32123
|
+
let dedupEvents = 0;
|
|
32124
|
+
const sessions = /* @__PURE__ */ new Set();
|
|
32125
|
+
const turns = /* @__PURE__ */ new Set();
|
|
32126
|
+
for (const e of events) {
|
|
32127
|
+
totalWithout += e.tokens_without;
|
|
32128
|
+
totalWith += e.tokens_with;
|
|
32129
|
+
sessions.add(e.session_id);
|
|
32130
|
+
turns.add(`${e.session_id}:${e.turn}`);
|
|
32131
|
+
if (GRAPH_MECHANISMS.has(e.mechanism)) {
|
|
32132
|
+
graphCalls++;
|
|
32133
|
+
graphTokensDelivered += e.tokens_with;
|
|
32134
|
+
}
|
|
32135
|
+
if (SHELL_MECHANISMS.has(e.mechanism)) {
|
|
32136
|
+
shellCompressionEvents++;
|
|
32137
|
+
}
|
|
32138
|
+
if (SAFETY_MECHANISMS.has(e.mechanism)) {
|
|
32139
|
+
behaviorEvents++;
|
|
32140
|
+
}
|
|
32141
|
+
const d = e.detail;
|
|
32142
|
+
if (d) {
|
|
32143
|
+
if (d.counterfactual === "blast_radius" || d.blast_radius) {
|
|
32144
|
+
blastRadiusWarnings++;
|
|
32145
|
+
}
|
|
32146
|
+
if (d.circuit_breaker || d.counterfactual === "circuit_breaker") {
|
|
32147
|
+
circuitBreakerActivations++;
|
|
32148
|
+
}
|
|
32149
|
+
if (d.conventions_injected || d.counterfactual === "convention_injection") {
|
|
32150
|
+
conventionInjections++;
|
|
32151
|
+
}
|
|
32152
|
+
if (d.hook_redirect || d.counterfactual === "hook_redirect") {
|
|
32153
|
+
explorationLoopsPrevented++;
|
|
32154
|
+
}
|
|
32155
|
+
}
|
|
32156
|
+
if (e.mechanism === "session_dedup") {
|
|
32157
|
+
dedupEvents++;
|
|
32158
|
+
}
|
|
32159
|
+
}
|
|
32160
|
+
const totalSaved = totalWithout - totalWith;
|
|
32161
|
+
const totalToolCalls = events.length;
|
|
32162
|
+
const snr = totalWithout > 0 ? totalWith / totalWithout : 0;
|
|
32163
|
+
const noiseRemovedPct = totalWithout > 0 ? Math.round((totalWithout - totalWith) / totalWithout * 100) : 0;
|
|
32164
|
+
const contextDensity = graphTokensDelivered > 0 ? Math.round(graphCalls / (graphTokensDelivered / 1e3) * 10) / 10 : 0;
|
|
32165
|
+
const compressionRatio = totalWith > 0 ? totalWithout / totalWith : 1;
|
|
32166
|
+
const attentionMultiplier = Math.round(Math.sqrt(compressionRatio) * 100) / 100;
|
|
32167
|
+
const firstCallRate = totalToolCalls > 0 ? Math.round(graphCalls / totalToolCalls * 100) : 0;
|
|
32168
|
+
const turnsSaved = Math.round(graphCalls * 2.5);
|
|
32169
|
+
const totalConventionInjections = conventionInjections + behaviorEvents;
|
|
32170
|
+
const preventionScore = blastRadiusWarnings + circuitBreakerActivations * 10 + totalConventionInjections;
|
|
32171
|
+
const firstCallRateDecimal = firstCallRate / 100;
|
|
32172
|
+
const reasoningMultiplier = compressionRatio > 0 && firstCallRateDecimal > 0 ? Math.round(compressionRatio * (1 + firstCallRateDecimal) * 100) / 100 : 0;
|
|
32173
|
+
return {
|
|
32174
|
+
signal_to_noise_ratio: Math.round(snr * 1e3) / 1e3,
|
|
32175
|
+
noise_removed_pct: noiseRemovedPct,
|
|
32176
|
+
context_density: contextDensity,
|
|
32177
|
+
entities_resolved: graphCalls,
|
|
32178
|
+
graph_tokens_delivered: graphTokensDelivered,
|
|
32179
|
+
attention_multiplier: attentionMultiplier,
|
|
32180
|
+
first_call_resolution_rate: firstCallRate,
|
|
32181
|
+
graph_calls: graphCalls,
|
|
32182
|
+
total_tool_calls: totalToolCalls,
|
|
32183
|
+
turns_saved: turnsSaved,
|
|
32184
|
+
exploration_loops_prevented: explorationLoopsPrevented,
|
|
32185
|
+
blast_radius_warnings: blastRadiusWarnings,
|
|
32186
|
+
circuit_breaker_activations: circuitBreakerActivations,
|
|
32187
|
+
convention_injections: totalConventionInjections,
|
|
32188
|
+
prevention_score: preventionScore,
|
|
32189
|
+
reasoning_quality_multiplier: reasoningMultiplier,
|
|
32190
|
+
total_sessions: sessions.size,
|
|
32191
|
+
total_turns: turns.size,
|
|
32192
|
+
total_events: events.length
|
|
32193
|
+
};
|
|
32194
|
+
}
|
|
32195
|
+
function createReasoningQualityRoutes(deps) {
|
|
32196
|
+
const app = new Hono7();
|
|
32197
|
+
app.get("/global", (c) => {
|
|
32198
|
+
const start = performance.now();
|
|
32199
|
+
const fromTs = c.req.query("from_ts");
|
|
32200
|
+
const toTs = c.req.query("to_ts");
|
|
32201
|
+
const events = readTokenFlowEvents(deps.unerrDir, {
|
|
32202
|
+
from_ts: fromTs || void 0,
|
|
32203
|
+
to_ts: toTs || void 0
|
|
32204
|
+
});
|
|
32205
|
+
const metrics = computeQualityMetrics(events);
|
|
32206
|
+
return c.json({
|
|
32207
|
+
data: metrics,
|
|
32208
|
+
_meta: { latency_ms: Math.round((performance.now() - start) * 100) / 100 }
|
|
32209
|
+
});
|
|
32210
|
+
});
|
|
32211
|
+
app.get("/session", (c) => {
|
|
32212
|
+
const start = performance.now();
|
|
32213
|
+
const querySessionId = c.req.query("session_id");
|
|
32214
|
+
const writer = deps.getTokenFlowWriter();
|
|
32215
|
+
const allEvents = readTokenFlowEvents(deps.unerrDir);
|
|
32216
|
+
if (allEvents.length === 0) {
|
|
32217
|
+
return c.json({ data: null, _meta: { latency_ms: 0 } });
|
|
32218
|
+
}
|
|
32219
|
+
let sessionId = querySessionId || writer?.sessionId;
|
|
32220
|
+
if (!sessionId) {
|
|
32221
|
+
sessionId = allEvents[allEvents.length - 1]?.session_id;
|
|
32222
|
+
}
|
|
32223
|
+
if (!sessionId) {
|
|
32224
|
+
return c.json({ data: null, _meta: { latency_ms: 0 } });
|
|
32225
|
+
}
|
|
32226
|
+
const sessionEvents = querySessionId ? allEvents.filter((e) => e.session_id === sessionId) : allEvents.filter((e) => e.session_id === sessionId || e.session_id === "unknown");
|
|
32227
|
+
const metrics = computeQualityMetrics(sessionEvents);
|
|
32228
|
+
const turnGroups = /* @__PURE__ */ new Map();
|
|
32229
|
+
for (const e of sessionEvents) {
|
|
32230
|
+
const group = turnGroups.get(e.turn) ?? [];
|
|
32231
|
+
group.push(e);
|
|
32232
|
+
turnGroups.set(e.turn, group);
|
|
32233
|
+
}
|
|
32234
|
+
const trajectory = [];
|
|
32235
|
+
let cumWithout = 0;
|
|
32236
|
+
let cumWith = 0;
|
|
32237
|
+
for (const [turn, turnEvents] of [...turnGroups.entries()].sort(([a], [b]) => a - b)) {
|
|
32238
|
+
let turnGraphCalls = 0;
|
|
32239
|
+
let turnGraphDelivered = 0;
|
|
32240
|
+
for (const e of turnEvents) {
|
|
32241
|
+
cumWithout += e.tokens_without;
|
|
32242
|
+
cumWith += e.tokens_with;
|
|
32243
|
+
if (GRAPH_MECHANISMS.has(e.mechanism)) {
|
|
32244
|
+
turnGraphCalls++;
|
|
32245
|
+
turnGraphDelivered += e.tokens_with;
|
|
32246
|
+
}
|
|
32247
|
+
}
|
|
32248
|
+
trajectory.push({
|
|
32249
|
+
turn,
|
|
32250
|
+
snr: cumWithout > 0 ? Math.round(cumWith / cumWithout * 1e3) / 1e3 : 0,
|
|
32251
|
+
cumulative_noise_removed_pct: cumWithout > 0 ? Math.round((cumWithout - cumWith) / cumWithout * 100) : 0,
|
|
32252
|
+
graph_calls_this_turn: turnGraphCalls,
|
|
32253
|
+
context_density: turnGraphDelivered > 0 ? Math.round(turnGraphCalls / (turnGraphDelivered / 1e3) * 10) / 10 : 0
|
|
32254
|
+
});
|
|
32255
|
+
}
|
|
32256
|
+
return c.json({
|
|
32257
|
+
data: {
|
|
32258
|
+
...metrics,
|
|
32259
|
+
session_id: sessionId,
|
|
32260
|
+
trajectory
|
|
32261
|
+
},
|
|
32262
|
+
_meta: { latency_ms: Math.round((performance.now() - start) * 100) / 100 }
|
|
32263
|
+
});
|
|
32264
|
+
});
|
|
32265
|
+
app.get("/sessions", (c) => {
|
|
32266
|
+
const start = performance.now();
|
|
32267
|
+
const fromTs = c.req.query("from_ts");
|
|
32268
|
+
const toTs = c.req.query("to_ts");
|
|
32269
|
+
const limit = Math.min(Number(c.req.query("limit") ?? 50), 200);
|
|
32270
|
+
const offset = Math.max(Number(c.req.query("offset") ?? 0), 0);
|
|
32271
|
+
const allEvents = readTokenFlowEvents(deps.unerrDir, {
|
|
32272
|
+
from_ts: fromTs || void 0,
|
|
32273
|
+
to_ts: toTs || void 0
|
|
32274
|
+
});
|
|
32275
|
+
const historyEntries = readSessionHistory(deps.unerrDir);
|
|
32276
|
+
const agentBySession = /* @__PURE__ */ new Map();
|
|
32277
|
+
for (const h of historyEntries) {
|
|
32278
|
+
if (h.agentName) agentBySession.set(h.sessionId, h.agentName);
|
|
32279
|
+
}
|
|
32280
|
+
const sessionMap = /* @__PURE__ */ new Map();
|
|
32281
|
+
for (const e of allEvents) {
|
|
32282
|
+
const group = sessionMap.get(e.session_id) ?? [];
|
|
32283
|
+
group.push(e);
|
|
32284
|
+
sessionMap.set(e.session_id, group);
|
|
32285
|
+
}
|
|
32286
|
+
const allSessions = [...sessionMap.entries()].map(([sessionId, events]) => {
|
|
32287
|
+
const m = computeQualityMetrics(events);
|
|
32288
|
+
const lastTs = events.reduce((max, e) => e.ts > max ? e.ts : max, events[0].ts);
|
|
32289
|
+
const firstTs = events.reduce((min, e) => e.ts < min ? e.ts : min, events[0].ts);
|
|
32290
|
+
return {
|
|
32291
|
+
session_id: sessionId,
|
|
32292
|
+
first_ts: firstTs,
|
|
32293
|
+
last_ts: lastTs,
|
|
32294
|
+
agent_name: agentBySession.get(sessionId) ?? deps.getAgentName?.(sessionId) ?? null,
|
|
32295
|
+
noise_removed_pct: m.noise_removed_pct,
|
|
32296
|
+
first_call_resolution_rate: m.first_call_resolution_rate,
|
|
32297
|
+
prevention_score: m.prevention_score,
|
|
32298
|
+
reasoning_quality_multiplier: m.reasoning_quality_multiplier,
|
|
32299
|
+
context_density: m.context_density,
|
|
32300
|
+
turns_saved: m.turns_saved,
|
|
32301
|
+
total_events: m.total_events,
|
|
32302
|
+
total_turns: m.total_turns
|
|
32303
|
+
};
|
|
32304
|
+
}).sort((a, b) => b.last_ts.localeCompare(a.last_ts));
|
|
32305
|
+
const paginated = allSessions.slice(offset, offset + limit);
|
|
32306
|
+
return c.json({
|
|
32307
|
+
data: paginated,
|
|
32308
|
+
total: allSessions.length,
|
|
32309
|
+
limit,
|
|
32310
|
+
offset,
|
|
32311
|
+
_meta: { latency_ms: Math.round((performance.now() - start) * 100) / 100 }
|
|
32312
|
+
});
|
|
32313
|
+
});
|
|
32314
|
+
app.get("/trend", (c) => {
|
|
32315
|
+
const start = performance.now();
|
|
32316
|
+
const fromTs = c.req.query("from_ts");
|
|
32317
|
+
const toTs = c.req.query("to_ts");
|
|
32318
|
+
const allEvents = readTokenFlowEvents(deps.unerrDir, {
|
|
32319
|
+
from_ts: fromTs || void 0,
|
|
32320
|
+
to_ts: toTs || void 0
|
|
32321
|
+
});
|
|
32322
|
+
const sessionMap = /* @__PURE__ */ new Map();
|
|
32323
|
+
for (const e of allEvents) {
|
|
32324
|
+
const group = sessionMap.get(e.session_id) ?? [];
|
|
32325
|
+
group.push(e);
|
|
32326
|
+
sessionMap.set(e.session_id, group);
|
|
32327
|
+
}
|
|
32328
|
+
const trend = [...sessionMap.entries()].map(([sessionId, events]) => {
|
|
32329
|
+
const m = computeQualityMetrics(events);
|
|
32330
|
+
const firstTs = events.reduce((min, e) => e.ts < min ? e.ts : min, events[0].ts);
|
|
32331
|
+
const lastTs = events.reduce((max, e) => e.ts > max ? e.ts : max, events[0].ts);
|
|
32332
|
+
return {
|
|
32333
|
+
session_id: sessionId,
|
|
32334
|
+
first_ts: firstTs,
|
|
32335
|
+
last_ts: lastTs,
|
|
32336
|
+
noise_removed_pct: m.noise_removed_pct,
|
|
32337
|
+
first_call_resolution_rate: m.first_call_resolution_rate,
|
|
32338
|
+
prevention_score: m.prevention_score,
|
|
32339
|
+
reasoning_quality_multiplier: m.reasoning_quality_multiplier,
|
|
32340
|
+
context_density: m.context_density,
|
|
32341
|
+
attention_multiplier: m.attention_multiplier,
|
|
32342
|
+
turns_saved: m.turns_saved,
|
|
32343
|
+
total_events: m.total_events
|
|
32344
|
+
};
|
|
32345
|
+
}).sort((a, b) => a.first_ts.localeCompare(b.first_ts));
|
|
32346
|
+
return c.json({
|
|
32347
|
+
data: trend,
|
|
32348
|
+
total: trend.length,
|
|
32349
|
+
_meta: { latency_ms: Math.round((performance.now() - start) * 100) / 100 }
|
|
32350
|
+
});
|
|
32351
|
+
});
|
|
32352
|
+
return app;
|
|
32353
|
+
}
|
|
32354
|
+
var GRAPH_MECHANISMS, SHELL_MECHANISMS, SAFETY_MECHANISMS;
|
|
32355
|
+
var init_reasoning_quality = __esm({
|
|
32356
|
+
"src/server/routes/reasoning-quality.ts"() {
|
|
32357
|
+
"use strict";
|
|
32358
|
+
init_token_flow();
|
|
32359
|
+
init_session_history();
|
|
32360
|
+
GRAPH_MECHANISMS = /* @__PURE__ */ new Set(["graph_query", "file_read"]);
|
|
32361
|
+
SHELL_MECHANISMS = /* @__PURE__ */ new Set(["shell_compression"]);
|
|
32362
|
+
SAFETY_MECHANISMS = /* @__PURE__ */ new Set(["behavior_automation"]);
|
|
32363
|
+
}
|
|
32364
|
+
});
|
|
32365
|
+
|
|
31353
32366
|
// src/server/http.ts
|
|
31354
32367
|
var http_exports = {};
|
|
31355
32368
|
__export(http_exports, {
|
|
31356
32369
|
startDashboardServer: () => startDashboardServer
|
|
31357
32370
|
});
|
|
31358
|
-
import { existsSync as
|
|
32371
|
+
import { existsSync as existsSync56, readFileSync as readFileSync52, unlinkSync as unlinkSync8, writeFileSync as writeFileSync32 } from "fs";
|
|
31359
32372
|
import { createServer as createServer3 } from "net";
|
|
31360
|
-
import { dirname as dirname7, join as
|
|
31361
|
-
import { fileURLToPath } from "url";
|
|
32373
|
+
import { dirname as dirname7, join as join61 } from "path";
|
|
32374
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
31362
32375
|
import { serve } from "@hono/node-server";
|
|
31363
32376
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
31364
|
-
import { Hono as
|
|
32377
|
+
import { Hono as Hono8 } from "hono";
|
|
31365
32378
|
async function findAvailablePort() {
|
|
31366
32379
|
for (let port = PORT_START; port <= PORT_END; port++) {
|
|
31367
32380
|
const available = await new Promise((resolve3) => {
|
|
@@ -31385,7 +32398,7 @@ async function startDashboardServer(opts) {
|
|
|
31385
32398
|
return null;
|
|
31386
32399
|
}
|
|
31387
32400
|
opts.system.dashboardPort = port;
|
|
31388
|
-
const app = new
|
|
32401
|
+
const app = new Hono8();
|
|
31389
32402
|
app.use("*", corsMiddleware);
|
|
31390
32403
|
app.use("*", cacheMiddleware);
|
|
31391
32404
|
app.use("*", timingMiddleware);
|
|
@@ -31400,10 +32413,13 @@ async function startDashboardServer(opts) {
|
|
|
31400
32413
|
if (opts.tokenFlow) {
|
|
31401
32414
|
app.route("/api/token-flow", createTokenFlowRoutes(opts.tokenFlow));
|
|
31402
32415
|
}
|
|
31403
|
-
|
|
31404
|
-
|
|
31405
|
-
|
|
31406
|
-
|
|
32416
|
+
if (opts.reasoningQuality) {
|
|
32417
|
+
app.route("/api/reasoning-quality", createReasoningQualityRoutes(opts.reasoningQuality));
|
|
32418
|
+
}
|
|
32419
|
+
const distDir = join61(dirname7(fileURLToPath2(import.meta.url)), "ui");
|
|
32420
|
+
const spaIndex = join61(distDir, "index.html");
|
|
32421
|
+
if (existsSync56(spaIndex)) {
|
|
32422
|
+
const spaHtml = readFileSync52(spaIndex, "utf-8");
|
|
31407
32423
|
app.use("*", serveStatic({ root: distDir }));
|
|
31408
32424
|
app.get("*", (c) => {
|
|
31409
32425
|
const path7 = c.req.path;
|
|
@@ -31432,7 +32448,7 @@ async function startDashboardServer(opts) {
|
|
|
31432
32448
|
port,
|
|
31433
32449
|
hostname: "127.0.0.1"
|
|
31434
32450
|
});
|
|
31435
|
-
const serverJsonPath =
|
|
32451
|
+
const serverJsonPath = join61(opts.stateDir, "server.json");
|
|
31436
32452
|
const serverInfo = {
|
|
31437
32453
|
port,
|
|
31438
32454
|
pid: process.pid,
|
|
@@ -31466,6 +32482,7 @@ var init_http = __esm({
|
|
|
31466
32482
|
init_system();
|
|
31467
32483
|
init_temporal();
|
|
31468
32484
|
init_token_flow2();
|
|
32485
|
+
init_reasoning_quality();
|
|
31469
32486
|
PORT_START = 7600;
|
|
31470
32487
|
PORT_END = 7700;
|
|
31471
32488
|
}
|
|
@@ -31833,13 +32850,13 @@ var proxy_exports = {};
|
|
|
31833
32850
|
__export(proxy_exports, {
|
|
31834
32851
|
startProxy: () => startProxy
|
|
31835
32852
|
});
|
|
31836
|
-
import { existsSync as
|
|
31837
|
-
import { join as
|
|
32853
|
+
import { existsSync as existsSync57, mkdirSync as mkdirSync34, readFileSync as readFileSync53, readdirSync as readdirSync13, writeFileSync as fsWriteFileSync } from "fs";
|
|
32854
|
+
import { join as join62 } from "path";
|
|
31838
32855
|
async function getProxyFactStore(unerrDir) {
|
|
31839
32856
|
if (proxyFactStore !== void 0) return proxyFactStore;
|
|
31840
32857
|
try {
|
|
31841
32858
|
const { TemporalFactStore: TemporalFactStore2 } = await Promise.resolve().then(() => (init_temporal_facts(), temporal_facts_exports));
|
|
31842
|
-
const cwd =
|
|
32859
|
+
const cwd = join62(unerrDir, "..");
|
|
31843
32860
|
proxyFactStore = await TemporalFactStore2.create(cwd);
|
|
31844
32861
|
return proxyFactStore;
|
|
31845
32862
|
} catch {
|
|
@@ -31917,15 +32934,32 @@ async function handleRecallFactsProxy(args, unerrDir) {
|
|
|
31917
32934
|
};
|
|
31918
32935
|
}
|
|
31919
32936
|
}
|
|
32937
|
+
function migrateAgentPermissions(cwd) {
|
|
32938
|
+
try {
|
|
32939
|
+
const settingsPath = join62(cwd, ".claude", "settings.json");
|
|
32940
|
+
if (!existsSync57(settingsPath)) return;
|
|
32941
|
+
const raw = readFileSync53(settingsPath, "utf-8");
|
|
32942
|
+
const settings = JSON.parse(raw);
|
|
32943
|
+
const deny = settings?.permissions?.deny;
|
|
32944
|
+
if (!Array.isArray(deny)) return;
|
|
32945
|
+
const readIdx = deny.indexOf("Read");
|
|
32946
|
+
if (readIdx < 0) return;
|
|
32947
|
+
deny.splice(readIdx, 1);
|
|
32948
|
+
fsWriteFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
32949
|
+
process.stderr.write("[unerr] Migrated permissions: removed Read from deny list (required for Edit workflow)\n");
|
|
32950
|
+
} catch {
|
|
32951
|
+
}
|
|
32952
|
+
}
|
|
31920
32953
|
async function startProxy(opts = {}) {
|
|
31921
32954
|
const stats = createSessionStats(true);
|
|
31922
32955
|
const startup = new StartupRenderer();
|
|
31923
32956
|
startup.mount();
|
|
32957
|
+
migrateAgentPermissions(process.cwd());
|
|
31924
32958
|
const lifecycle = createLifecycleActor(process.cwd());
|
|
31925
32959
|
lifecycle.send({ type: "START_DETECT" });
|
|
31926
32960
|
startup.setLocalMode(true);
|
|
31927
|
-
const stateDir =
|
|
31928
|
-
if (!
|
|
32961
|
+
const stateDir = join62(process.cwd(), ".unerr", "state");
|
|
32962
|
+
if (!existsSync57(stateDir)) {
|
|
31929
32963
|
mkdirSync34(stateDir, { recursive: true });
|
|
31930
32964
|
}
|
|
31931
32965
|
const pidLock = new PidLock(stateDir);
|
|
@@ -31943,7 +32977,7 @@ async function startProxy(opts = {}) {
|
|
|
31943
32977
|
startupLog.step(
|
|
31944
32978
|
`PID ${process.pid} ${startupLog.fmt.muted(`\xB7 health localhost:${lockResult.healthPort}`)}`
|
|
31945
32979
|
);
|
|
31946
|
-
const ledgerDir =
|
|
32980
|
+
const ledgerDir = join62(process.cwd(), ".unerr", "ledger");
|
|
31947
32981
|
const previousSession = detectSessionResume(stateDir, ledgerDir);
|
|
31948
32982
|
if (previousSession) {
|
|
31949
32983
|
stats.isResumedSession = true;
|
|
@@ -31958,17 +32992,17 @@ async function startProxy(opts = {}) {
|
|
|
31958
32992
|
if (opts.repoId) {
|
|
31959
32993
|
repoIds = [opts.repoId];
|
|
31960
32994
|
} else {
|
|
31961
|
-
const configPath =
|
|
31962
|
-
if (
|
|
32995
|
+
const configPath = join62(process.cwd(), ".unerr", "config.json");
|
|
32996
|
+
if (existsSync57(configPath)) {
|
|
31963
32997
|
try {
|
|
31964
|
-
const config = JSON.parse(
|
|
32998
|
+
const config = JSON.parse(readFileSync53(configPath, "utf-8"));
|
|
31965
32999
|
if (config.repoId) repoIds = [config.repoId];
|
|
31966
33000
|
} catch {
|
|
31967
33001
|
}
|
|
31968
33002
|
}
|
|
31969
|
-
const manifestsDir =
|
|
31970
|
-
if (repoIds.length === 0 &&
|
|
31971
|
-
repoIds =
|
|
33003
|
+
const manifestsDir = join62(process.cwd(), ".unerr", "manifests");
|
|
33004
|
+
if (repoIds.length === 0 && existsSync57(manifestsDir)) {
|
|
33005
|
+
repoIds = readdirSync13(manifestsDir).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
|
|
31972
33006
|
}
|
|
31973
33007
|
}
|
|
31974
33008
|
if (repoIds.length === 0) {
|
|
@@ -32048,13 +33082,10 @@ async function startProxy(opts = {}) {
|
|
|
32048
33082
|
startupLog.perf(
|
|
32049
33083
|
`${startupLog.fmt.cyan("Persistent graph")} ${startupLog.fmt.muted("\u2014 zero recomputation, all intelligence preserved")}`
|
|
32050
33084
|
);
|
|
32051
|
-
|
|
32052
|
-
|
|
32053
|
-
|
|
32054
|
-
|
|
32055
|
-
`${startupLog.fmt.muted("Source changes detected \u2014 incremental update after MCP ready")}`
|
|
32056
|
-
);
|
|
32057
|
-
}
|
|
33085
|
+
needsBackgroundIndex = true;
|
|
33086
|
+
startupLog.step(
|
|
33087
|
+
`${startupLog.fmt.muted("Background reindex will refresh graph data after MCP ready")}`
|
|
33088
|
+
);
|
|
32058
33089
|
} else {
|
|
32059
33090
|
const { loadLocalSnapshot: loadLocalSnapshot2 } = await Promise.resolve().then(() => (init_local_snapshot(), local_snapshot_exports));
|
|
32060
33091
|
const snapshotStart = Date.now();
|
|
@@ -32094,6 +33125,32 @@ async function startProxy(opts = {}) {
|
|
|
32094
33125
|
startupLog.done(
|
|
32095
33126
|
`Migrated snapshot to persistent graph ${startupLog.fmt.muted(`\u2192 ${dbPath}`)}`
|
|
32096
33127
|
);
|
|
33128
|
+
try {
|
|
33129
|
+
const migrationUnerrDir = join62(process.cwd(), ".unerr");
|
|
33130
|
+
const factStoreForMigration = await getProxyFactStore(migrationUnerrDir);
|
|
33131
|
+
if (factStoreForMigration) {
|
|
33132
|
+
const { detectLocalConventions: detectLocalConventions2 } = await Promise.resolve().then(() => (init_local_convention_detector(), local_convention_detector_exports));
|
|
33133
|
+
const { generateFromConventions: generateFromConventions2, runFactGenerationPipeline: runFactGenerationPipeline2 } = await Promise.resolve().then(() => (init_fact_generator(), fact_generator_exports));
|
|
33134
|
+
const detection = await detectLocalConventions2(localGraph.db);
|
|
33135
|
+
if (detection.conventions.length > 0) {
|
|
33136
|
+
const convResult = await generateFromConventions2(factStoreForMigration, detection.conventions);
|
|
33137
|
+
if (convResult.created > 0 || convResult.reinforced > 0) {
|
|
33138
|
+
log21.info(
|
|
33139
|
+
`Fact generator: ${convResult.created} convention facts created, ${convResult.reinforced} reinforced`
|
|
33140
|
+
);
|
|
33141
|
+
}
|
|
33142
|
+
}
|
|
33143
|
+
const pipelineResults = await runFactGenerationPipeline2(factStoreForMigration, migrationUnerrDir);
|
|
33144
|
+
for (const r of pipelineResults) {
|
|
33145
|
+
if (r.created > 0 || r.reinforced > 0) {
|
|
33146
|
+
log21.info(
|
|
33147
|
+
`Fact generator [${r.source}]: ${r.created} created, ${r.reinforced} reinforced`
|
|
33148
|
+
);
|
|
33149
|
+
}
|
|
33150
|
+
}
|
|
33151
|
+
}
|
|
33152
|
+
} catch {
|
|
33153
|
+
}
|
|
32097
33154
|
} else {
|
|
32098
33155
|
needsBackgroundIndex = true;
|
|
32099
33156
|
startupLog.step(
|
|
@@ -32161,7 +33218,7 @@ async function startProxy(opts = {}) {
|
|
|
32161
33218
|
try {
|
|
32162
33219
|
const { generateSessionResume: generateSessionResume2 } = await Promise.resolve().then(() => (init_session_resume(), session_resume_exports));
|
|
32163
33220
|
const { ShadowLedger: ResumeLedger } = await Promise.resolve().then(() => (init_shadow_ledger(), shadow_ledger_exports));
|
|
32164
|
-
const resumeLedger = new ResumeLedger(
|
|
33221
|
+
const resumeLedger = new ResumeLedger(join62(process.cwd(), ".unerr"));
|
|
32165
33222
|
const ledgerEntries = resumeLedger.getRecentEntries(50);
|
|
32166
33223
|
const resumeCtx = generateSessionResume2(ledgerEntries);
|
|
32167
33224
|
if (resumeCtx) {
|
|
@@ -32181,7 +33238,7 @@ async function startProxy(opts = {}) {
|
|
|
32181
33238
|
const { createDurabilityScorer: createDurabilityScorer2 } = await Promise.resolve().then(() => (init_durability_scorer(), durability_scorer_exports));
|
|
32182
33239
|
const durabilityScorer = createDurabilityScorer2();
|
|
32183
33240
|
const { ShadowLedger: DurLedger } = await Promise.resolve().then(() => (init_shadow_ledger(), shadow_ledger_exports));
|
|
32184
|
-
const durLedger = new DurLedger(
|
|
33241
|
+
const durLedger = new DurLedger(join62(process.cwd(), ".unerr"));
|
|
32185
33242
|
const durEntries = durLedger.getRecentEntries(200);
|
|
32186
33243
|
if (durEntries.length > 0) {
|
|
32187
33244
|
durabilityScorer.computeScores(durEntries);
|
|
@@ -32201,7 +33258,7 @@ async function startProxy(opts = {}) {
|
|
|
32201
33258
|
try {
|
|
32202
33259
|
const { detectInstableEntities: detectInstableEntities2 } = await Promise.resolve().then(() => (init_negative_knowledge(), negative_knowledge_exports));
|
|
32203
33260
|
const { ShadowLedger: NkLedger } = await Promise.resolve().then(() => (init_shadow_ledger(), shadow_ledger_exports));
|
|
32204
|
-
const nkLedger = new NkLedger(
|
|
33261
|
+
const nkLedger = new NkLedger(join62(process.cwd(), ".unerr"));
|
|
32205
33262
|
const nkEntries = nkLedger.getRecentEntries(200);
|
|
32206
33263
|
if (nkEntries.length > 0) {
|
|
32207
33264
|
const antiPatterns = detectInstableEntities2(nkEntries);
|
|
@@ -32223,7 +33280,7 @@ async function startProxy(opts = {}) {
|
|
|
32223
33280
|
try {
|
|
32224
33281
|
const { CausalBridge: CausalBridge2 } = await Promise.resolve().then(() => (init_causal_bridge(), causal_bridge_exports));
|
|
32225
33282
|
const causalBridge = new CausalBridge2(
|
|
32226
|
-
|
|
33283
|
+
join62(process.cwd(), ".unerr"),
|
|
32227
33284
|
process.cwd()
|
|
32228
33285
|
);
|
|
32229
33286
|
router2.setCausalBridge(causalBridge);
|
|
@@ -32233,7 +33290,7 @@ async function startProxy(opts = {}) {
|
|
|
32233
33290
|
try {
|
|
32234
33291
|
const { learnConventions: learnConventions2 } = await Promise.resolve().then(() => (init_convention_learner(), convention_learner_exports));
|
|
32235
33292
|
const { ShadowLedger: ConvLedger } = await Promise.resolve().then(() => (init_shadow_ledger(), shadow_ledger_exports));
|
|
32236
|
-
const convLedger = new ConvLedger(
|
|
33293
|
+
const convLedger = new ConvLedger(join62(process.cwd(), ".unerr"));
|
|
32237
33294
|
const convEntries = convLedger.getRecentEntries(100);
|
|
32238
33295
|
if (convEntries.length > 0) {
|
|
32239
33296
|
const learned = learnConventions2(convEntries);
|
|
@@ -32256,7 +33313,7 @@ async function startProxy(opts = {}) {
|
|
|
32256
33313
|
try {
|
|
32257
33314
|
const { computePromptDurabilityProfiles: computePromptDurabilityProfiles2 } = await Promise.resolve().then(() => (init_prompt_durability(), prompt_durability_exports));
|
|
32258
33315
|
const { ShadowLedger: DurProfLedger } = await Promise.resolve().then(() => (init_shadow_ledger(), shadow_ledger_exports));
|
|
32259
|
-
const durProfLedger = new DurProfLedger(
|
|
33316
|
+
const durProfLedger = new DurProfLedger(join62(process.cwd(), ".unerr"));
|
|
32260
33317
|
const durProfEntries = durProfLedger.getRecentEntries(200);
|
|
32261
33318
|
if (durProfEntries.length > 0) {
|
|
32262
33319
|
const profiles = computePromptDurabilityProfiles2(durProfEntries);
|
|
@@ -32276,7 +33333,7 @@ async function startProxy(opts = {}) {
|
|
|
32276
33333
|
}
|
|
32277
33334
|
try {
|
|
32278
33335
|
const { createContextLedger: createContextLedger2 } = await Promise.resolve().then(() => (init_context_ledger(), context_ledger_exports));
|
|
32279
|
-
const contextLedger = createContextLedger2(
|
|
33336
|
+
const contextLedger = createContextLedger2(join62(process.cwd(), ".unerr"));
|
|
32280
33337
|
contextLedger.load();
|
|
32281
33338
|
contextLedger.prune();
|
|
32282
33339
|
router2.setContextLedger(contextLedger);
|
|
@@ -32365,7 +33422,7 @@ async function startProxy(opts = {}) {
|
|
|
32365
33422
|
}));
|
|
32366
33423
|
const { ShadowLedger: ShadowLedger2 } = await Promise.resolve().then(() => (init_shadow_ledger(), shadow_ledger_exports));
|
|
32367
33424
|
const { IntentCorrelator: IntentCorrelator2 } = await Promise.resolve().then(() => (init_intent_correlator(), intent_correlator_exports));
|
|
32368
|
-
const unerrDirForLedger =
|
|
33425
|
+
const unerrDirForLedger = join62(process.cwd(), ".unerr");
|
|
32369
33426
|
const shadowLedger2 = new ShadowLedger2(unerrDirForLedger);
|
|
32370
33427
|
const intentCorrelator = new IntentCorrelator2(unerrDirForLedger);
|
|
32371
33428
|
log21.info(
|
|
@@ -32379,7 +33436,7 @@ async function startProxy(opts = {}) {
|
|
|
32379
33436
|
process.env.UNERR_SESSION_ID = shadowLedger2.getSessionId();
|
|
32380
33437
|
try {
|
|
32381
33438
|
const { writeFileSync: writeFileSync36 } = await import("fs");
|
|
32382
|
-
writeFileSync36(
|
|
33439
|
+
writeFileSync36(join62(unerrDirForLedger, "state", "session.id"), shadowLedger2.getSessionId(), "utf-8");
|
|
32383
33440
|
} catch {
|
|
32384
33441
|
}
|
|
32385
33442
|
router2.setTokenFlow(tokenFlowWriter2);
|
|
@@ -32718,8 +33775,9 @@ async function startProxy(opts = {}) {
|
|
|
32718
33775
|
lifecycle.send({ type: "INDEX_COMPLETE" });
|
|
32719
33776
|
lifecycle.send({ type: "MCP_READY" });
|
|
32720
33777
|
const { TransportMux: TransportMux2 } = await Promise.resolve().then(() => (init_transport_mux(), transport_mux_exports));
|
|
32721
|
-
const sockPath =
|
|
33778
|
+
const sockPath = join62(stateDir, "proxy.sock");
|
|
32722
33779
|
const transportMux = new TransportMux2(sockPath);
|
|
33780
|
+
const agentNameByClient = /* @__PURE__ */ new Map();
|
|
32723
33781
|
transportMux.setCustomHttpHandler("/commit-context", (_url) => {
|
|
32724
33782
|
const { getCommitTrailers: getCommitTrailers2 } = (init_git_trailers(), __toCommonJS(git_trailers_exports));
|
|
32725
33783
|
const branch = branchContext?.currentBranch ?? "unknown";
|
|
@@ -32729,6 +33787,10 @@ async function startProxy(opts = {}) {
|
|
|
32729
33787
|
});
|
|
32730
33788
|
transportMux.setHandler(async (clientId, message) => {
|
|
32731
33789
|
if (message.method === "initialize") {
|
|
33790
|
+
const clientName = message.params?.clientInfo?.name;
|
|
33791
|
+
if (clientName) {
|
|
33792
|
+
agentNameByClient.set(clientId, clientName);
|
|
33793
|
+
}
|
|
32732
33794
|
return {
|
|
32733
33795
|
jsonrpc: "2.0",
|
|
32734
33796
|
id: message.id,
|
|
@@ -32757,6 +33819,88 @@ async function startProxy(opts = {}) {
|
|
|
32757
33819
|
};
|
|
32758
33820
|
}
|
|
32759
33821
|
const { name, arguments: toolArgs = {} } = params;
|
|
33822
|
+
toolUsageTracker2.record(name);
|
|
33823
|
+
if (name === "record_fact") {
|
|
33824
|
+
const factResult = await handleRecordFactProxy(toolArgs, unerrDirForLedger, shadowLedger2);
|
|
33825
|
+
return { jsonrpc: "2.0", result: factResult };
|
|
33826
|
+
}
|
|
33827
|
+
if (name === "recall_facts") {
|
|
33828
|
+
const factResult = await handleRecallFactsProxy(toolArgs, unerrDirForLedger);
|
|
33829
|
+
return { jsonrpc: "2.0", result: factResult };
|
|
33830
|
+
}
|
|
33831
|
+
if (name === "unerr_mark_working") {
|
|
33832
|
+
const branch2 = branchContext?.currentBranch ?? "unknown";
|
|
33833
|
+
const headSha2 = branchContext?.headSha ?? "";
|
|
33834
|
+
const snapshot = workingSnapshotStore.create({
|
|
33835
|
+
commitSha: headSha2,
|
|
33836
|
+
reason: toolArgs.reason ?? "manual mark",
|
|
33837
|
+
branch: branch2,
|
|
33838
|
+
timelineBranch: workingSnapshotStore.getTimelineBranch(),
|
|
33839
|
+
sessionId: shadowLedger2.getSessionId()
|
|
33840
|
+
});
|
|
33841
|
+
shadowLedger2.record(name, toolArgs, { snapshot_id: snapshot.id, commit: headSha2 }, branch2, headSha2);
|
|
33842
|
+
return {
|
|
33843
|
+
jsonrpc: "2.0",
|
|
33844
|
+
result: {
|
|
33845
|
+
content: [{ type: "text", text: stringifyMcpToolJson({ snapshot_id: snapshot.id, commit_sha: snapshot.commitSha, reason: snapshot.reason, timestamp: snapshot.timestamp, branch: snapshot.branch }) }],
|
|
33846
|
+
_meta: { source: "local", latency_ms: 0, format: "json" }
|
|
33847
|
+
}
|
|
33848
|
+
};
|
|
33849
|
+
}
|
|
33850
|
+
if (name === "unerr_revert_to_working_state") {
|
|
33851
|
+
const { revertToWorkingState: revertToWorkingState2 } = await Promise.resolve().then(() => (init_rewind_engine(), rewind_engine_exports));
|
|
33852
|
+
const revertResult = await revertToWorkingState2({
|
|
33853
|
+
snapshotId: toolArgs.snapshot_id ?? "latest",
|
|
33854
|
+
cwd: process.cwd(),
|
|
33855
|
+
unerrDir: unerrDirForLedger,
|
|
33856
|
+
graph: graphForRouter,
|
|
33857
|
+
ledger: shadowLedger2,
|
|
33858
|
+
snapshotStore: workingSnapshotStore,
|
|
33859
|
+
dryRun: toolArgs.dry_run ?? false
|
|
33860
|
+
});
|
|
33861
|
+
const branch2 = branchContext?.currentBranch ?? "unknown";
|
|
33862
|
+
const headSha2 = branchContext?.headSha ?? "";
|
|
33863
|
+
shadowLedger2.record(name, toolArgs, { success: revertResult.success, snapshot_id: revertResult.snapshot?.id, files_restored: revertResult.rewindResult?.filesRestored.length ?? 0 }, branch2, headSha2);
|
|
33864
|
+
return {
|
|
33865
|
+
jsonrpc: "2.0",
|
|
33866
|
+
result: {
|
|
33867
|
+
content: [{ type: "text", text: stringifyMcpToolJson(revertResult) }],
|
|
33868
|
+
_meta: { source: "local", latency_ms: 0, format: "json" }
|
|
33869
|
+
}
|
|
33870
|
+
};
|
|
33871
|
+
}
|
|
33872
|
+
if (name === "unerr_get_timeline") {
|
|
33873
|
+
const { getTimeline: getTimeline2 } = await Promise.resolve().then(() => (init_timeline(), timeline_exports));
|
|
33874
|
+
const branch2 = branchContext?.currentBranch ?? "unknown";
|
|
33875
|
+
const timelineBranch = workingSnapshotStore.getTimelineBranch();
|
|
33876
|
+
const timeline = getTimeline2(shadowLedger2, branch2, timelineBranch, {
|
|
33877
|
+
branch: toolArgs.branch,
|
|
33878
|
+
limit: toolArgs.limit,
|
|
33879
|
+
tool: toolArgs.tool,
|
|
33880
|
+
rootsOnly: toolArgs.roots_only
|
|
33881
|
+
});
|
|
33882
|
+
return {
|
|
33883
|
+
jsonrpc: "2.0",
|
|
33884
|
+
result: {
|
|
33885
|
+
content: [{ type: "text", text: stringifyMcpToolJson(timeline) }],
|
|
33886
|
+
_meta: { source: "local", latency_ms: 0, format: "json" }
|
|
33887
|
+
}
|
|
33888
|
+
};
|
|
33889
|
+
}
|
|
33890
|
+
if (localGraph) {
|
|
33891
|
+
const { handleDeepDiveTool: handleDeepDiveTool2 } = await Promise.resolve().then(() => (init_deep_dive_tools(), deep_dive_tools_exports));
|
|
33892
|
+
const deepDiveResult = await handleDeepDiveTool2(name, toolArgs, localGraph);
|
|
33893
|
+
if (deepDiveResult) {
|
|
33894
|
+
recordToolCall(stats);
|
|
33895
|
+
recordLatency(stats.latency, 0);
|
|
33896
|
+
pidLock.recordToolCall();
|
|
33897
|
+
if (stats.localMode) recordGraphQuery(stats.localMode, name);
|
|
33898
|
+
const branch2 = branchContext?.currentBranch ?? "unknown";
|
|
33899
|
+
const headSha2 = branchContext?.headSha ?? "";
|
|
33900
|
+
shadowLedger2.record(name, toolArgs, { tool: name, source: "local", client: clientId }, branch2, headSha2);
|
|
33901
|
+
return { jsonrpc: "2.0", result: deepDiveResult };
|
|
33902
|
+
}
|
|
33903
|
+
}
|
|
32760
33904
|
const result = await router2.execute(name, toolArgs);
|
|
32761
33905
|
recordToolCall(stats);
|
|
32762
33906
|
recordLatency(stats.latency, result._meta.latency_ms);
|
|
@@ -32829,7 +33973,7 @@ async function startProxy(opts = {}) {
|
|
|
32829
33973
|
try {
|
|
32830
33974
|
const { DriftTracker: DriftTracker2 } = await Promise.resolve().then(() => (init_drift_tracker(), drift_tracker_exports));
|
|
32831
33975
|
const { FileHashManager: FileHashManager2 } = await Promise.resolve().then(() => (init_file_hash_state(), file_hash_state_exports));
|
|
32832
|
-
const unerrDir =
|
|
33976
|
+
const unerrDir = join62(process.cwd(), ".unerr");
|
|
32833
33977
|
const fileHashManager = new FileHashManager2(unerrDir);
|
|
32834
33978
|
_driftTracker = new DriftTracker2(
|
|
32835
33979
|
{ projectRoot: process.cwd(), repoId: repoIds[0], unerrDir },
|
|
@@ -32974,6 +34118,32 @@ async function startProxy(opts = {}) {
|
|
|
32974
34118
|
`Post-index DriftTracker init failed: ${err instanceof Error ? err.message : String(err)}`
|
|
32975
34119
|
);
|
|
32976
34120
|
});
|
|
34121
|
+
try {
|
|
34122
|
+
const factStoreForGen = await getProxyFactStore(unerrDirForLedger);
|
|
34123
|
+
if (factStoreForGen) {
|
|
34124
|
+
const { detectLocalConventions: detectLocalConventions2 } = await Promise.resolve().then(() => (init_local_convention_detector(), local_convention_detector_exports));
|
|
34125
|
+
const { generateFromConventions: generateFromConventions2 } = await Promise.resolve().then(() => (init_fact_generator(), fact_generator_exports));
|
|
34126
|
+
const detection = await detectLocalConventions2(localGraph.db);
|
|
34127
|
+
if (detection.conventions.length > 0) {
|
|
34128
|
+
const convResult = await generateFromConventions2(factStoreForGen, detection.conventions);
|
|
34129
|
+
if (convResult.created > 0 || convResult.reinforced > 0) {
|
|
34130
|
+
log21.info(
|
|
34131
|
+
`Fact generator: ${convResult.created} convention facts created, ${convResult.reinforced} reinforced`
|
|
34132
|
+
);
|
|
34133
|
+
}
|
|
34134
|
+
}
|
|
34135
|
+
const { runFactGenerationPipeline: runFactGenerationPipeline2 } = await Promise.resolve().then(() => (init_fact_generator(), fact_generator_exports));
|
|
34136
|
+
const pipelineResults = await runFactGenerationPipeline2(factStoreForGen, unerrDirForLedger);
|
|
34137
|
+
for (const r of pipelineResults) {
|
|
34138
|
+
if (r.created > 0 || r.reinforced > 0) {
|
|
34139
|
+
log21.info(
|
|
34140
|
+
`Fact generator [${r.source}]: ${r.created} created, ${r.reinforced} reinforced`
|
|
34141
|
+
);
|
|
34142
|
+
}
|
|
34143
|
+
}
|
|
34144
|
+
}
|
|
34145
|
+
} catch {
|
|
34146
|
+
}
|
|
32977
34147
|
},
|
|
32978
34148
|
// onError
|
|
32979
34149
|
(err) => {
|
|
@@ -32997,7 +34167,7 @@ async function startProxy(opts = {}) {
|
|
|
32997
34167
|
}
|
|
32998
34168
|
const { WorkspaceManifest: WorkspaceManifest2 } = await Promise.resolve().then(() => (init_workspace_manifest(), workspace_manifest_exports));
|
|
32999
34169
|
const workspaceManifest = repoIds[0] ? new WorkspaceManifest2(
|
|
33000
|
-
|
|
34170
|
+
join62(process.cwd(), ".unerr"),
|
|
33001
34171
|
repoIds[0],
|
|
33002
34172
|
shadowLedger2.getSessionId()
|
|
33003
34173
|
) : null;
|
|
@@ -33082,7 +34252,7 @@ async function startProxy(opts = {}) {
|
|
|
33082
34252
|
if (proxyMode === "parse" && parseIndex) {
|
|
33083
34253
|
try {
|
|
33084
34254
|
const { extractEntitiesFromSource: extractEntitiesFromSource2 } = await Promise.resolve().then(() => (init_auto_bootstrap(), auto_bootstrap_exports));
|
|
33085
|
-
const allFiles =
|
|
34255
|
+
const allFiles = readdirSync13(process.cwd(), {
|
|
33086
34256
|
recursive: true,
|
|
33087
34257
|
encoding: "utf-8"
|
|
33088
34258
|
});
|
|
@@ -33096,7 +34266,7 @@ async function startProxy(opts = {}) {
|
|
|
33096
34266
|
});
|
|
33097
34267
|
for (const file of sourceFiles.slice(0, 500)) {
|
|
33098
34268
|
try {
|
|
33099
|
-
const content =
|
|
34269
|
+
const content = readFileSync53(join62(process.cwd(), file), "utf-8");
|
|
33100
34270
|
const entities = extractEntitiesFromSource2(file, content);
|
|
33101
34271
|
parseIndex.addEntities(entities);
|
|
33102
34272
|
} catch {
|
|
@@ -33158,7 +34328,7 @@ async function startProxy(opts = {}) {
|
|
|
33158
34328
|
}
|
|
33159
34329
|
const { writeFileSync: writeStatsFile } = await import("fs");
|
|
33160
34330
|
const { computePercentiles: computePercentiles2 } = await Promise.resolve().then(() => (init_session_stats(), session_stats_exports));
|
|
33161
|
-
const statsSnapshotPath =
|
|
34331
|
+
const statsSnapshotPath = join62(stateDir, "session_stats.json");
|
|
33162
34332
|
const statsSnapshotInterval = setInterval(() => {
|
|
33163
34333
|
try {
|
|
33164
34334
|
const total = stats.toolCallsLocal;
|
|
@@ -33196,7 +34366,7 @@ async function startProxy(opts = {}) {
|
|
|
33196
34366
|
const { startDashboardServer: startDashboardServer2 } = await Promise.resolve().then(() => (init_http(), http_exports));
|
|
33197
34367
|
const { detectIde: detectIdeDashboard } = await Promise.resolve().then(() => (init_detect(), detect_exports));
|
|
33198
34368
|
const ideType = await detectIdeDashboard(process.cwd());
|
|
33199
|
-
const unerrDirForApi =
|
|
34369
|
+
const unerrDirForApi = join62(process.cwd(), ".unerr");
|
|
33200
34370
|
dashboardHandle = await startDashboardServer2({
|
|
33201
34371
|
system: {
|
|
33202
34372
|
stats,
|
|
@@ -33241,12 +34411,24 @@ async function startProxy(opts = {}) {
|
|
|
33241
34411
|
stateDir,
|
|
33242
34412
|
tokenFlow: {
|
|
33243
34413
|
unerrDir: unerrDirForApi,
|
|
33244
|
-
getTokenFlowWriter: () => tokenFlowWriter2
|
|
34414
|
+
getTokenFlowWriter: () => tokenFlowWriter2,
|
|
34415
|
+
getAgentName: (_sessionId) => {
|
|
34416
|
+
const last = [...agentNameByClient.values()].pop();
|
|
34417
|
+
return last ?? server.getClientVersion?.()?.name ?? void 0;
|
|
34418
|
+
}
|
|
34419
|
+
},
|
|
34420
|
+
reasoningQuality: {
|
|
34421
|
+
unerrDir: unerrDirForApi,
|
|
34422
|
+
getTokenFlowWriter: () => tokenFlowWriter2,
|
|
34423
|
+
getAgentName: (_sessionId) => {
|
|
34424
|
+
const last = [...agentNameByClient.values()].pop();
|
|
34425
|
+
return last ?? server.getClientVersion?.()?.name ?? void 0;
|
|
34426
|
+
}
|
|
33245
34427
|
},
|
|
33246
34428
|
temporal: await (async () => {
|
|
33247
34429
|
try {
|
|
33248
34430
|
const { TemporalFactStore: TemporalFactStore2 } = await Promise.resolve().then(() => (init_temporal_facts(), temporal_facts_exports));
|
|
33249
|
-
const { readdirSync:
|
|
34431
|
+
const { readdirSync: readdirSync15, readFileSync: readFileSync57 } = await import("fs");
|
|
33250
34432
|
const factStore2 = await TemporalFactStore2.create(
|
|
33251
34433
|
process.cwd()
|
|
33252
34434
|
);
|
|
@@ -33254,10 +34436,10 @@ async function startProxy(opts = {}) {
|
|
|
33254
34436
|
factStore: factStore2,
|
|
33255
34437
|
loadRecentSessions: (limit) => {
|
|
33256
34438
|
try {
|
|
33257
|
-
const sessDir =
|
|
33258
|
-
const files =
|
|
34439
|
+
const sessDir = join62(unerrDirForApi, "sessions");
|
|
34440
|
+
const files = readdirSync15(sessDir).filter((f) => f.endsWith(".jsonl")).sort().slice(-limit);
|
|
33259
34441
|
return files.map((f) => {
|
|
33260
|
-
const content =
|
|
34442
|
+
const content = readFileSync57(join62(sessDir, f), "utf-8").trim().split("\n").pop();
|
|
33261
34443
|
return JSON.parse(content);
|
|
33262
34444
|
});
|
|
33263
34445
|
} catch {
|
|
@@ -33330,7 +34512,7 @@ async function startProxy(opts = {}) {
|
|
|
33330
34512
|
try {
|
|
33331
34513
|
const { appendSessionHistory: appendSessionHistory2 } = (init_session_history(), __toCommonJS(session_history_exports));
|
|
33332
34514
|
const topMech = Object.entries(tokenFlowSummary.by_mechanism).sort(([, a], [, b]) => b.tokens_saved - a.tokens_saved)[0];
|
|
33333
|
-
appendSessionHistory2(
|
|
34515
|
+
appendSessionHistory2(join62(process.cwd(), ".unerr"), {
|
|
33334
34516
|
sessionId: shadowLedger2.getSessionId(),
|
|
33335
34517
|
startedAt: new Date(stats.sessionStartedAt).toISOString(),
|
|
33336
34518
|
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -33342,6 +34524,7 @@ async function startProxy(opts = {}) {
|
|
|
33342
34524
|
dollarsSaved: router2.getSessionDollarsSaved(),
|
|
33343
34525
|
modelId: "unknown",
|
|
33344
34526
|
entityCount: 0,
|
|
34527
|
+
agentName: agentNameByClient.values().next().value ?? server.getClientVersion?.()?.name ?? void 0,
|
|
33345
34528
|
tokenFlowSummary: {
|
|
33346
34529
|
by_mechanism: Object.fromEntries(
|
|
33347
34530
|
Object.entries(tokenFlowSummary.by_mechanism).map(([k, v]) => [
|
|
@@ -33455,7 +34638,7 @@ async function startProxy(opts = {}) {
|
|
|
33455
34638
|
try {
|
|
33456
34639
|
const correctionModule = (init_correction_detector(), __toCommonJS(correction_detector_exports));
|
|
33457
34640
|
const detectCorrections2 = correctionModule.detectCorrections;
|
|
33458
|
-
const ledgerPath =
|
|
34641
|
+
const ledgerPath = join62(
|
|
33459
34642
|
process.cwd(),
|
|
33460
34643
|
".unerr",
|
|
33461
34644
|
"ledger",
|
|
@@ -33473,10 +34656,49 @@ async function startProxy(opts = {}) {
|
|
|
33473
34656
|
`[unerr] Learned ${patterns.length} correction pattern${patterns.length !== 1 ? "s" : ""} from this session
|
|
33474
34657
|
`
|
|
33475
34658
|
);
|
|
34659
|
+
try {
|
|
34660
|
+
const factStoreForShutdown = await getProxyFactStore(unerrDirForLedger);
|
|
34661
|
+
if (factStoreForShutdown) {
|
|
34662
|
+
const { generateFromNegativeKnowledge: generateFromNegativeKnowledge2 } = await Promise.resolve().then(() => (init_fact_generator(), fact_generator_exports));
|
|
34663
|
+
const corrections = patterns.map((p, i) => ({
|
|
34664
|
+
id: `correction-${shadowLedger2.getSessionId()}-${i}`,
|
|
34665
|
+
entityKey: p.entity_key,
|
|
34666
|
+
pattern: p.error_type,
|
|
34667
|
+
reason: p.correction_summary,
|
|
34668
|
+
detectedAt: p.last_seen,
|
|
34669
|
+
rewindEntryId: shadowLedger2.getSessionId(),
|
|
34670
|
+
confidence: p.confidence
|
|
34671
|
+
}));
|
|
34672
|
+
const negResult = await generateFromNegativeKnowledge2(factStoreForShutdown, corrections);
|
|
34673
|
+
if (negResult.created > 0) {
|
|
34674
|
+
process.stderr.write(
|
|
34675
|
+
`[unerr] Fact generator: ${negResult.created} negative knowledge facts created
|
|
34676
|
+
`
|
|
34677
|
+
);
|
|
34678
|
+
}
|
|
34679
|
+
}
|
|
34680
|
+
} catch {
|
|
34681
|
+
}
|
|
33476
34682
|
}
|
|
33477
34683
|
} catch {
|
|
33478
34684
|
}
|
|
33479
34685
|
}
|
|
34686
|
+
try {
|
|
34687
|
+
const factStoreForSession = await getProxyFactStore(unerrDirForLedger);
|
|
34688
|
+
if (factStoreForSession) {
|
|
34689
|
+
const { runFactGenerationPipeline: runFactGenerationPipeline2 } = await Promise.resolve().then(() => (init_fact_generator(), fact_generator_exports));
|
|
34690
|
+
const pipelineResults = await runFactGenerationPipeline2(factStoreForSession, unerrDirForLedger);
|
|
34691
|
+
for (const r of pipelineResults) {
|
|
34692
|
+
if (r.created > 0 || r.reinforced > 0) {
|
|
34693
|
+
process.stderr.write(
|
|
34694
|
+
`[unerr] Fact generator [${r.source}]: ${r.created} created, ${r.reinforced} reinforced
|
|
34695
|
+
`
|
|
34696
|
+
);
|
|
34697
|
+
}
|
|
34698
|
+
}
|
|
34699
|
+
}
|
|
34700
|
+
} catch {
|
|
34701
|
+
}
|
|
33480
34702
|
if (workspaceManifest) {
|
|
33481
34703
|
const orphans = intentCorrelator.getPending();
|
|
33482
34704
|
if (orphans.length > 0) {
|
|
@@ -33639,13 +34861,13 @@ __export(session_logger_exports, {
|
|
|
33639
34861
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
33640
34862
|
import {
|
|
33641
34863
|
appendFileSync as appendFileSync9,
|
|
33642
|
-
existsSync as
|
|
34864
|
+
existsSync as existsSync58,
|
|
33643
34865
|
mkdirSync as mkdirSync35,
|
|
33644
|
-
readdirSync as
|
|
34866
|
+
readdirSync as readdirSync14,
|
|
33645
34867
|
statSync as statSync11,
|
|
33646
34868
|
unlinkSync as unlinkSync9
|
|
33647
34869
|
} from "fs";
|
|
33648
|
-
import { join as
|
|
34870
|
+
import { join as join63 } from "path";
|
|
33649
34871
|
import { createConsola as createConsola2 } from "consola";
|
|
33650
34872
|
function formatTimestamp() {
|
|
33651
34873
|
const d = /* @__PURE__ */ new Date();
|
|
@@ -33653,10 +34875,10 @@ function formatTimestamp() {
|
|
|
33653
34875
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
|
|
33654
34876
|
}
|
|
33655
34877
|
function cleanupOldLogs(logsDir) {
|
|
33656
|
-
if (!
|
|
34878
|
+
if (!existsSync58(logsDir)) return;
|
|
33657
34879
|
try {
|
|
33658
|
-
const files =
|
|
33659
|
-
const fullPath =
|
|
34880
|
+
const files = readdirSync14(logsDir).filter((f) => f.startsWith("session-") && f.endsWith(".log")).map((f) => {
|
|
34881
|
+
const fullPath = join63(logsDir, f);
|
|
33660
34882
|
const stat2 = statSync11(fullPath);
|
|
33661
34883
|
return {
|
|
33662
34884
|
name: f,
|
|
@@ -33673,7 +34895,7 @@ function cleanupOldLogs(logsDir) {
|
|
|
33673
34895
|
}
|
|
33674
34896
|
}
|
|
33675
34897
|
}
|
|
33676
|
-
const remaining = files.filter((f) =>
|
|
34898
|
+
const remaining = files.filter((f) => existsSync58(f.path));
|
|
33677
34899
|
if (remaining.length > MAX_FILES) {
|
|
33678
34900
|
for (const file of remaining.slice(MAX_FILES)) {
|
|
33679
34901
|
try {
|
|
@@ -33688,11 +34910,11 @@ function cleanupOldLogs(logsDir) {
|
|
|
33688
34910
|
function initSessionLogger(opts = {}) {
|
|
33689
34911
|
if (_logger) return _logger;
|
|
33690
34912
|
const cwd = opts.cwd ?? process.cwd();
|
|
33691
|
-
const logsDir =
|
|
34913
|
+
const logsDir = join63(cwd, ".unerr", "logs");
|
|
33692
34914
|
mkdirSync35(logsDir, { recursive: true });
|
|
33693
34915
|
cleanupOldLogs(logsDir);
|
|
33694
34916
|
const timestamp2 = formatTimestamp();
|
|
33695
|
-
_logFilePath =
|
|
34917
|
+
_logFilePath = join63(logsDir, `session-${timestamp2}.log`);
|
|
33696
34918
|
const levelNum = opts.level !== void 0 ? Number(opts.level) : void 0;
|
|
33697
34919
|
const envLevel = process.env.UNERR_LOG_LEVEL;
|
|
33698
34920
|
const resolvedLevel = levelNum ?? (envLevel ? Number(envLevel) : 3);
|
|
@@ -33766,22 +34988,22 @@ __export(setup_wizard_exports, {
|
|
|
33766
34988
|
runSetup: () => runSetup
|
|
33767
34989
|
});
|
|
33768
34990
|
import { createHash as createHash4 } from "crypto";
|
|
33769
|
-
import { existsSync as
|
|
33770
|
-
import { join as
|
|
34991
|
+
import { existsSync as existsSync59, mkdirSync as mkdirSync36, writeFileSync as writeFileSync33 } from "fs";
|
|
34992
|
+
import { join as join64 } from "path";
|
|
33771
34993
|
import * as clack from "@clack/prompts";
|
|
33772
34994
|
async function runSetup(cwd) {
|
|
33773
34995
|
const projectDir = cwd ?? process.cwd();
|
|
33774
34996
|
clack.intro("unerr");
|
|
33775
34997
|
clack.log.step("Project Setup");
|
|
33776
34998
|
const repoId = await generateRepoId(projectDir);
|
|
33777
|
-
const configDir =
|
|
34999
|
+
const configDir = join64(projectDir, ".unerr");
|
|
33778
35000
|
mkdirSync36(configDir, { recursive: true });
|
|
33779
|
-
const configPath =
|
|
33780
|
-
const settingsPath =
|
|
35001
|
+
const configPath = join64(configDir, "config.json");
|
|
35002
|
+
const settingsPath = join64(configDir, "settings.json");
|
|
33781
35003
|
writeFileSync33(configPath, `${JSON.stringify({ repoId }, null, 2)}
|
|
33782
35004
|
`);
|
|
33783
35005
|
let existingSettings = {};
|
|
33784
|
-
if (
|
|
35006
|
+
if (existsSync59(settingsPath)) {
|
|
33785
35007
|
try {
|
|
33786
35008
|
existingSettings = JSON.parse(
|
|
33787
35009
|
__require("fs").readFileSync(settingsPath, "utf-8")
|
|
@@ -34390,13 +35612,13 @@ __export(session_summary_writer_exports, {
|
|
|
34390
35612
|
readLastSession: () => readLastSession,
|
|
34391
35613
|
writeSessionSummary: () => writeSessionSummary
|
|
34392
35614
|
});
|
|
34393
|
-
import { existsSync as
|
|
34394
|
-
import { join as
|
|
35615
|
+
import { existsSync as existsSync60, mkdirSync as mkdirSync37, appendFileSync as appendFileSync10, writeFileSync as writeFileSync34, readFileSync as readFileSync54 } from "fs";
|
|
35616
|
+
import { join as join65 } from "path";
|
|
34395
35617
|
function writeSessionSummary(unerrDir, ctx) {
|
|
34396
35618
|
if (ctx.entries.length === 0) return null;
|
|
34397
35619
|
try {
|
|
34398
|
-
const sessionsDir =
|
|
34399
|
-
if (!
|
|
35620
|
+
const sessionsDir = join65(unerrDir, "sessions");
|
|
35621
|
+
if (!existsSync60(sessionsDir)) {
|
|
34400
35622
|
mkdirSync37(sessionsDir, { recursive: true });
|
|
34401
35623
|
}
|
|
34402
35624
|
const sorted = [...ctx.entries].sort(
|
|
@@ -34440,7 +35662,7 @@ function writeSessionSummary(unerrDir, ctx) {
|
|
|
34440
35662
|
token_estimate: ctx.tokenEstimate,
|
|
34441
35663
|
branch: last.branch ?? "unknown"
|
|
34442
35664
|
};
|
|
34443
|
-
const filePath =
|
|
35665
|
+
const filePath = join65(sessionsDir, `${ctx.sessionId}.jsonl`);
|
|
34444
35666
|
appendFileSync10(filePath, `${JSON.stringify(record)}
|
|
34445
35667
|
`, "utf-8");
|
|
34446
35668
|
writeLastSessionPointer(unerrDir, ctx.sessionId, record);
|
|
@@ -34455,9 +35677,9 @@ function writeSessionSummary(unerrDir, ctx) {
|
|
|
34455
35677
|
}
|
|
34456
35678
|
function readLastSession(unerrDir) {
|
|
34457
35679
|
try {
|
|
34458
|
-
const pointerPath =
|
|
34459
|
-
if (!
|
|
34460
|
-
const content =
|
|
35680
|
+
const pointerPath = join65(unerrDir, "state", "last_session.json");
|
|
35681
|
+
if (!existsSync60(pointerPath)) return null;
|
|
35682
|
+
const content = readFileSync54(pointerPath, "utf-8");
|
|
34461
35683
|
return JSON.parse(content);
|
|
34462
35684
|
} catch {
|
|
34463
35685
|
return null;
|
|
@@ -34465,11 +35687,11 @@ function readLastSession(unerrDir) {
|
|
|
34465
35687
|
}
|
|
34466
35688
|
function writeLastSessionPointer(unerrDir, sessionId, record) {
|
|
34467
35689
|
try {
|
|
34468
|
-
const stateDir =
|
|
34469
|
-
if (!
|
|
35690
|
+
const stateDir = join65(unerrDir, "state");
|
|
35691
|
+
if (!existsSync60(stateDir)) {
|
|
34470
35692
|
mkdirSync37(stateDir, { recursive: true });
|
|
34471
35693
|
}
|
|
34472
|
-
const pointerPath =
|
|
35694
|
+
const pointerPath = join65(stateDir, "last_session.json");
|
|
34473
35695
|
writeFileSync34(pointerPath, JSON.stringify(record, null, 2), "utf-8");
|
|
34474
35696
|
} catch {
|
|
34475
35697
|
}
|
|
@@ -34667,16 +35889,33 @@ var mcp_server_exports = {};
|
|
|
34667
35889
|
__export(mcp_server_exports, {
|
|
34668
35890
|
startMcpServer: () => startMcpServer
|
|
34669
35891
|
});
|
|
34670
|
-
import { existsSync as
|
|
34671
|
-
import { join as
|
|
35892
|
+
import { existsSync as existsSync61, mkdirSync as mkdirSync38, readFileSync as readFileSync55, writeFileSync as writeFileSync35 } from "fs";
|
|
35893
|
+
import { join as join66 } from "path";
|
|
35894
|
+
function migrateAgentPermissions2(cwd) {
|
|
35895
|
+
try {
|
|
35896
|
+
const settingsPath = join66(cwd, ".claude", "settings.json");
|
|
35897
|
+
if (!existsSync61(settingsPath)) return;
|
|
35898
|
+
const raw = readFileSync55(settingsPath, "utf-8");
|
|
35899
|
+
const settings = JSON.parse(raw);
|
|
35900
|
+
const deny = settings?.permissions?.deny;
|
|
35901
|
+
if (!Array.isArray(deny)) return;
|
|
35902
|
+
const readIdx = deny.indexOf("Read");
|
|
35903
|
+
if (readIdx < 0) return;
|
|
35904
|
+
deny.splice(readIdx, 1);
|
|
35905
|
+
writeFileSync35(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
35906
|
+
process.stderr.write("[unerr] Migrated permissions: removed Read from deny list (required for Edit workflow)\n");
|
|
35907
|
+
} catch {
|
|
35908
|
+
}
|
|
35909
|
+
}
|
|
34672
35910
|
async function startMcpServer(cwd) {
|
|
34673
35911
|
const { Server } = await import("@modelcontextprotocol/sdk/server/index.js");
|
|
34674
35912
|
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
34675
35913
|
const { ListToolsRequestSchema, CallToolRequestSchema } = await import("@modelcontextprotocol/sdk/types.js");
|
|
34676
|
-
const unerrDir =
|
|
34677
|
-
if (!
|
|
35914
|
+
const unerrDir = join66(cwd, ".unerr");
|
|
35915
|
+
if (!existsSync61(unerrDir)) {
|
|
34678
35916
|
mkdirSync38(unerrDir, { recursive: true });
|
|
34679
35917
|
}
|
|
35918
|
+
migrateAgentPermissions2(cwd);
|
|
34680
35919
|
await initTier2Modules(unerrDir);
|
|
34681
35920
|
const server = new Server(
|
|
34682
35921
|
{ name: "unerr-local", version: "0.1.0" },
|
|
@@ -34694,6 +35933,25 @@ async function startMcpServer(cwd) {
|
|
|
34694
35933
|
if (name === "recall_facts") {
|
|
34695
35934
|
return handleRecallFacts(args);
|
|
34696
35935
|
}
|
|
35936
|
+
const PROXY_ONLY_TOOLS = [
|
|
35937
|
+
"unerr_get_timeline",
|
|
35938
|
+
"unerr_revert_to_working_state",
|
|
35939
|
+
"unerr_mark_working"
|
|
35940
|
+
];
|
|
35941
|
+
if (PROXY_ONLY_TOOLS.includes(name)) {
|
|
35942
|
+
return {
|
|
35943
|
+
content: [
|
|
35944
|
+
{
|
|
35945
|
+
type: "text",
|
|
35946
|
+
text: JSON.stringify({
|
|
35947
|
+
tool: name,
|
|
35948
|
+
available: false,
|
|
35949
|
+
message: `${name} requires the full unerr proxy (run 'unerr' without --mcp). The timeline/revert subsystem is not available in lightweight MCP mode.`
|
|
35950
|
+
})
|
|
35951
|
+
}
|
|
35952
|
+
]
|
|
35953
|
+
};
|
|
35954
|
+
}
|
|
34697
35955
|
if (!graphReady || !router) {
|
|
34698
35956
|
if (name === "file_read" || name === "file_outline") {
|
|
34699
35957
|
try {
|
|
@@ -34773,25 +36031,6 @@ async function startMcpServer(cwd) {
|
|
|
34773
36031
|
]
|
|
34774
36032
|
};
|
|
34775
36033
|
}
|
|
34776
|
-
const PROXY_ONLY_TOOLS = [
|
|
34777
|
-
"unerr_get_timeline",
|
|
34778
|
-
"unerr_revert_to_working_state",
|
|
34779
|
-
"unerr_mark_working"
|
|
34780
|
-
];
|
|
34781
|
-
if (PROXY_ONLY_TOOLS.includes(name)) {
|
|
34782
|
-
return {
|
|
34783
|
-
content: [
|
|
34784
|
-
{
|
|
34785
|
-
type: "text",
|
|
34786
|
-
text: JSON.stringify({
|
|
34787
|
-
tool: name,
|
|
34788
|
-
available: false,
|
|
34789
|
-
message: `${name} requires the full unerr proxy (run 'unerr' without --mcp). The timeline/revert subsystem is not available in lightweight MCP mode.`
|
|
34790
|
-
})
|
|
34791
|
-
}
|
|
34792
|
-
]
|
|
34793
|
-
};
|
|
34794
|
-
}
|
|
34795
36034
|
try {
|
|
34796
36035
|
const result = await router.execute(name, args);
|
|
34797
36036
|
let text2 = stringifyMcpToolJson(result.content);
|
|
@@ -35079,9 +36318,9 @@ async function initTier2Modules(unerrDir) {
|
|
|
35079
36318
|
tokenFlowWriter = new TokenFlowWriter2(unerrDir, sessionId);
|
|
35080
36319
|
process.env.UNERR_SESSION_ID = sessionId;
|
|
35081
36320
|
try {
|
|
35082
|
-
const stateDir =
|
|
36321
|
+
const stateDir = join66(unerrDir, "state");
|
|
35083
36322
|
mkdirSync38(stateDir, { recursive: true });
|
|
35084
|
-
writeFileSync35(
|
|
36323
|
+
writeFileSync35(join66(stateDir, "session.id"), sessionId, "utf-8");
|
|
35085
36324
|
} catch {
|
|
35086
36325
|
}
|
|
35087
36326
|
log24.info(`Token flow active (session ${sessionId.slice(0, 8)})`);
|
|
@@ -35112,7 +36351,7 @@ async function initTier2Modules(unerrDir) {
|
|
|
35112
36351
|
}
|
|
35113
36352
|
try {
|
|
35114
36353
|
const { TemporalFactStore: TemporalFactStore2 } = await Promise.resolve().then(() => (init_temporal_facts(), temporal_facts_exports));
|
|
35115
|
-
const projectRoot =
|
|
36354
|
+
const projectRoot = join66(unerrDir, "..");
|
|
35116
36355
|
factStore = await TemporalFactStore2.create(projectRoot);
|
|
35117
36356
|
log24.info("Temporal fact store active (facts.db)");
|
|
35118
36357
|
} catch (err) {
|
|
@@ -35222,8 +36461,8 @@ function wireTier2IntoRouter(r) {
|
|
|
35222
36461
|
async function loadIntelligence(cwd) {
|
|
35223
36462
|
const projectRoot = cwd;
|
|
35224
36463
|
try {
|
|
35225
|
-
const unerrDir =
|
|
35226
|
-
if (!
|
|
36464
|
+
const unerrDir = join66(projectRoot, ".unerr");
|
|
36465
|
+
if (!existsSync61(unerrDir)) {
|
|
35227
36466
|
mkdirSync38(unerrDir, { recursive: true });
|
|
35228
36467
|
}
|
|
35229
36468
|
const { openPersistentDb: openPersistentDb2 } = await Promise.resolve().then(() => (init_persistent_db(), persistent_db_exports));
|
|
@@ -35232,11 +36471,8 @@ async function loadIntelligence(cwd) {
|
|
|
35232
36471
|
const localGraph = await CozoGraphStore2.create(db, projectRoot);
|
|
35233
36472
|
let needsIndex = false;
|
|
35234
36473
|
if (!isNew && await localGraph.isPopulated()) {
|
|
35235
|
-
log24.info("Persistent graph loaded");
|
|
35236
|
-
|
|
35237
|
-
if (shouldReindex2(projectRoot)) {
|
|
35238
|
-
needsIndex = true;
|
|
35239
|
-
}
|
|
36474
|
+
log24.info("Persistent graph loaded \u2014 background reindex will refresh entity data");
|
|
36475
|
+
needsIndex = true;
|
|
35240
36476
|
} else {
|
|
35241
36477
|
const { loadLocalSnapshot: loadLocalSnapshot2 } = await Promise.resolve().then(() => (init_local_snapshot(), local_snapshot_exports));
|
|
35242
36478
|
const migrated = await loadLocalSnapshot2(projectRoot, localGraph);
|
|
@@ -35245,11 +36481,11 @@ async function loadIntelligence(cwd) {
|
|
|
35245
36481
|
await buildSearchIndex2(localGraph.db);
|
|
35246
36482
|
const { runCommunityDetection: runCommunityDetection2, runConventionDetection: runConventionDetection2 } = await Promise.resolve().then(() => (init_local_indexer(), local_indexer_exports));
|
|
35247
36483
|
await runCommunityDetection2(localGraph);
|
|
35248
|
-
const configPath =
|
|
36484
|
+
const configPath = join66(projectRoot, ".unerr", "config.json");
|
|
35249
36485
|
let repoId = "unknown";
|
|
35250
|
-
if (
|
|
36486
|
+
if (existsSync61(configPath)) {
|
|
35251
36487
|
try {
|
|
35252
|
-
const config = JSON.parse(
|
|
36488
|
+
const config = JSON.parse(readFileSync55(configPath, "utf-8"));
|
|
35253
36489
|
repoId = config.repoId ?? repoId;
|
|
35254
36490
|
} catch {
|
|
35255
36491
|
}
|
|
@@ -35276,11 +36512,11 @@ async function loadIntelligence(cwd) {
|
|
|
35276
36512
|
log24.info("Intelligence graph ready \u2014 tools fully operational");
|
|
35277
36513
|
if (needsIndex) {
|
|
35278
36514
|
try {
|
|
35279
|
-
const configPath =
|
|
36515
|
+
const configPath = join66(projectRoot, ".unerr", "config.json");
|
|
35280
36516
|
let indexRepoId = "unknown";
|
|
35281
|
-
if (
|
|
36517
|
+
if (existsSync61(configPath)) {
|
|
35282
36518
|
try {
|
|
35283
|
-
const cfg = JSON.parse(
|
|
36519
|
+
const cfg = JSON.parse(readFileSync55(configPath, "utf-8"));
|
|
35284
36520
|
indexRepoId = cfg.repoId ?? indexRepoId;
|
|
35285
36521
|
} catch {
|
|
35286
36522
|
}
|
|
@@ -35390,8 +36626,8 @@ var init_mcp_server = __esm({
|
|
|
35390
36626
|
|
|
35391
36627
|
// src/entrypoints/cli.ts
|
|
35392
36628
|
init_startup_log();
|
|
35393
|
-
import { existsSync as
|
|
35394
|
-
import { join as
|
|
36629
|
+
import { existsSync as existsSync62, readFileSync as readFileSync56 } from "fs";
|
|
36630
|
+
import { join as join67 } from "path";
|
|
35395
36631
|
import { Command } from "commander";
|
|
35396
36632
|
|
|
35397
36633
|
// src/commands/branches.ts
|
|
@@ -35832,9 +37068,9 @@ function registerDashboardCommand(program2) {
|
|
|
35832
37068
|
// src/commands/debug.ts
|
|
35833
37069
|
init_git();
|
|
35834
37070
|
import { existsSync as existsSync9, readFileSync as readFileSync9, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
|
|
35835
|
-
import { arch, homedir as
|
|
37071
|
+
import { arch, homedir as homedir3, platform, release } from "os";
|
|
35836
37072
|
import { join as join11 } from "path";
|
|
35837
|
-
var UNERR_DIR = join11(
|
|
37073
|
+
var UNERR_DIR = join11(homedir3(), ".unerr");
|
|
35838
37074
|
function registerDebugCommand(program2) {
|
|
35839
37075
|
program2.command("debug").description("Diagnostics dump for support").action(async () => {
|
|
35840
37076
|
const sections = [];
|
|
@@ -35939,9 +37175,9 @@ function registerDebugCommand(program2) {
|
|
|
35939
37175
|
const mcpLocations = [
|
|
35940
37176
|
join11(process.cwd(), ".cursor", "mcp.json"),
|
|
35941
37177
|
join11(process.cwd(), ".vscode", "mcp.json"),
|
|
35942
|
-
join11(
|
|
37178
|
+
join11(homedir3(), ".claude", "claude_desktop_config.json"),
|
|
35943
37179
|
join11(
|
|
35944
|
-
|
|
37180
|
+
homedir3(),
|
|
35945
37181
|
"Library",
|
|
35946
37182
|
"Application Support",
|
|
35947
37183
|
"Cursor",
|
|
@@ -35954,7 +37190,7 @@ function registerDebugCommand(program2) {
|
|
|
35954
37190
|
for (const loc of mcpLocations) {
|
|
35955
37191
|
const exists = existsSync9(loc);
|
|
35956
37192
|
sections.push(
|
|
35957
|
-
` ${exists ? "[x]" : "[ ]"} ${loc.replace(
|
|
37193
|
+
` ${exists ? "[x]" : "[ ]"} ${loc.replace(homedir3(), "~")}`
|
|
35958
37194
|
);
|
|
35959
37195
|
}
|
|
35960
37196
|
sections.push("");
|
|
@@ -37395,19 +38631,19 @@ async function buildShellDiffRiskMap(graph, stdout) {
|
|
|
37395
38631
|
}
|
|
37396
38632
|
async function tryLoadGraphForShellBoost(cwd) {
|
|
37397
38633
|
try {
|
|
37398
|
-
const { existsSync:
|
|
37399
|
-
const { join:
|
|
38634
|
+
const { existsSync: existsSync63, readFileSync: readFileSync57 } = await import("fs");
|
|
38635
|
+
const { join: join68 } = await import("path");
|
|
37400
38636
|
const { gunzipSync: gunzipSync3 } = await import("zlib");
|
|
37401
38637
|
const { unpack } = await import("msgpackr");
|
|
37402
|
-
const snapshotsDir =
|
|
37403
|
-
let snapshotPath2 =
|
|
37404
|
-
if (!
|
|
37405
|
-
snapshotPath2 =
|
|
38638
|
+
const snapshotsDir = join68(cwd, ".unerr", "snapshots");
|
|
38639
|
+
let snapshotPath2 = join68(snapshotsDir, "graph.msgpack.gz");
|
|
38640
|
+
if (!existsSync63(snapshotPath2)) {
|
|
38641
|
+
snapshotPath2 = join68(snapshotsDir, "graph.msgpack");
|
|
37406
38642
|
}
|
|
37407
|
-
if (!
|
|
38643
|
+
if (!existsSync63(snapshotPath2)) return null;
|
|
37408
38644
|
const { default: CozoDbConstructor } = await import("cozo-node");
|
|
37409
38645
|
const { CozoGraphStore: CozoGraphStore2 } = await Promise.resolve().then(() => (init_local_graph(), local_graph_exports));
|
|
37410
|
-
const raw =
|
|
38646
|
+
const raw = readFileSync57(snapshotPath2);
|
|
37411
38647
|
let buffer;
|
|
37412
38648
|
try {
|
|
37413
38649
|
buffer = gunzipSync3(raw);
|
|
@@ -40643,12 +41879,17 @@ var preReadHandler = (normalized) => {
|
|
|
40643
41879
|
const input = normalized.toolInput;
|
|
40644
41880
|
const filePath = extractFilePath2(input);
|
|
40645
41881
|
if (!filePath) return passthrough();
|
|
41882
|
+
const hasOffset = input.offset !== void 0;
|
|
41883
|
+
const hasLimit = input.limit !== void 0;
|
|
41884
|
+
const isTargeted = hasOffset || hasLimit;
|
|
41885
|
+
const targetedReadGuidance = isTargeted ? "" : `
|
|
41886
|
+
IMPORTANT: Use offset/limit to read only the lines you plan to edit \u2014 do NOT read the entire file. You already know the target lines from file_read. Example: Read with offset=50, limit=30 to read lines 50-79.`;
|
|
40646
41887
|
return nudge(
|
|
40647
41888
|
`READ ROUTING: Built-in Read is ONLY for the Edit workflow (Read \u2192 Edit). For all other reading, use unerr tools instead:
|
|
40648
41889
|
- Reading to understand: \`file_read({ file_path: "${filePath}", purpose: "explore" })\`
|
|
40649
41890
|
- Reading before Edit: built-in Read is correct (proceed)
|
|
40650
41891
|
- File structure: \`file_outline("${filePath}")\`
|
|
40651
|
-
- Specific function: \`get_entity\` or \`file_read\` with \`entity\` param`
|
|
41892
|
+
- Specific function: \`get_entity\` or \`file_read\` with \`entity\` param` + targetedReadGuidance
|
|
40652
41893
|
);
|
|
40653
41894
|
};
|
|
40654
41895
|
var preGrepHandler = (normalized) => {
|
|
@@ -40700,17 +41941,20 @@ var preEditHandler = (normalized) => {
|
|
|
40700
41941
|
const input = normalized.toolInput;
|
|
40701
41942
|
const filePath = extractFilePath2(input);
|
|
40702
41943
|
if (!filePath || !isCodeFile(filePath)) return passthrough();
|
|
41944
|
+
const readPrereq = `CRITICAL: Edit REQUIRES built-in Read to have been called on "${filePath}" first. file_read (MCP) does NOT satisfy this \u2014 the Edit tool will fail with "File has not been read yet". If you haven't called built-in Read on this file, do so now before attempting Edit. Use offset/limit to read only the lines you plan to edit \u2014 do NOT read the entire file.
|
|
41945
|
+
|
|
41946
|
+
`;
|
|
40703
41947
|
const oldStr = input.old_string;
|
|
40704
41948
|
const hasSignatureChange = oldStr && /^(export\s+)?(async\s+)?function\s+\w+|^(export\s+)?class\s+\w+/.test(oldStr.trim());
|
|
40705
41949
|
if (hasSignatureChange) {
|
|
40706
41950
|
return nudge(
|
|
40707
|
-
`You're editing a function/class signature in "${filePath}". This may break callers.
|
|
41951
|
+
readPrereq + `You're editing a function/class signature in "${filePath}". This may break callers.
|
|
40708
41952
|
- \`get_references\` on the entity you're modifying \u2014 all callers must be updated to match
|
|
40709
41953
|
- \`get_rules "${filePath}"\` with content after editing \u2014 validate the change follows project conventions`
|
|
40710
41954
|
);
|
|
40711
41955
|
}
|
|
40712
41956
|
return nudge(
|
|
40713
|
-
`Before editing "${filePath}":
|
|
41957
|
+
readPrereq + `Before editing "${filePath}":
|
|
40714
41958
|
- \`get_references\` on any entity you're changing \u2014 ensure callers won't break
|
|
40715
41959
|
- \`get_rules "${filePath}"\` with content after editing \u2014 validate against project conventions`
|
|
40716
41960
|
);
|
|
@@ -41168,7 +42412,7 @@ function mergePreToolUseBashHook(cwd) {
|
|
|
41168
42412
|
return { ok: false, path: settingsPath, action: "failed" };
|
|
41169
42413
|
}
|
|
41170
42414
|
}
|
|
41171
|
-
var DISALLOWED_TOOLS = ["
|
|
42415
|
+
var DISALLOWED_TOOLS = ["Grep", "Glob"];
|
|
41172
42416
|
function addDisallowedTools(cwd) {
|
|
41173
42417
|
const dir = join27(cwd, ".claude");
|
|
41174
42418
|
const settingsPath = join27(dir, "settings.json");
|
|
@@ -41186,6 +42430,10 @@ function addDisallowedTools(cwd) {
|
|
|
41186
42430
|
}
|
|
41187
42431
|
const permissions = settings.permissions ?? {};
|
|
41188
42432
|
const deny = Array.isArray(permissions.deny) ? [...permissions.deny] : [];
|
|
42433
|
+
const readIdx = deny.indexOf("Read");
|
|
42434
|
+
if (readIdx >= 0) {
|
|
42435
|
+
deny.splice(readIdx, 1);
|
|
42436
|
+
}
|
|
41189
42437
|
let added = 0;
|
|
41190
42438
|
for (const tool of DISALLOWED_TOOLS) {
|
|
41191
42439
|
if (!deny.includes(tool)) {
|
|
@@ -41306,10 +42554,12 @@ This project has unerr MCP tools installed. You MUST use these instead of built-
|
|
|
41306
42554
|
| Intent | Tool | Why |
|
|
41307
42555
|
|--------|------|-----|
|
|
41308
42556
|
| Reading to understand code | \`file_read\` (unerr MCP) | Auto-injects conventions, facts, drift status |
|
|
41309
|
-
| Reading immediately before Edit | Built-in \`Read\` | Required by Edit tool \u2014 \`file_read\` does NOT satisfy this |
|
|
42557
|
+
| Reading immediately before Edit | Built-in \`Read\` with offset/limit | Required by Edit tool \u2014 \`file_read\` does NOT satisfy this. Use targeted reads (offset/limit) for only the lines you plan to edit. |
|
|
41310
42558
|
|
|
41311
42559
|
When your next action is Edit, use built-in Read. For everything else, use \`file_read\`.
|
|
41312
42560
|
|
|
42561
|
+
**Common failure mode:** Using \`file_read\` to understand a file, then attempting Edit without calling built-in Read first. The Edit tool WILL reject with "File has not been read yet" \u2014 \`file_read\` (MCP) cannot satisfy this requirement. Always call built-in Read immediately before Edit.
|
|
42562
|
+
|
|
41313
42563
|
### Tool Reference
|
|
41314
42564
|
|
|
41315
42565
|
#### Graph Navigation (8 tools)
|
|
@@ -41376,7 +42626,7 @@ ONLY use built-in Read/Grep/Glob when:
|
|
|
41376
42626
|
### Summary (CRITICAL \u2014 read this even if you skimmed above)
|
|
41377
42627
|
|
|
41378
42628
|
ALWAYS use unerr MCP tools: \`search_code\`, \`get_references\`, \`file_read\`, \`file_outline\`, \`get_entity\`.
|
|
41379
|
-
NEVER use built-in Read/Grep/Glob for code navigation. EXCEPTION: built-in Read is REQUIRED before Edit (file_read cannot substitute).
|
|
42629
|
+
NEVER use built-in Read/Grep/Glob for code navigation. EXCEPTION: built-in Read is REQUIRED before Edit (file_read cannot substitute \u2014 Edit will fail without it).
|
|
41380
42630
|
unerr skills available: /audit, /blame, /test, /lint, /commit, /review-pr.
|
|
41381
42631
|
Before writing code: \`get_conventions\`, \`get_rules\`. After writing: \`get_rules\` with content to validate.
|
|
41382
42632
|
Before risky changes: \`unerr_mark_working\`. If things break: \`unerr_revert_to_working_state\`.`;
|
|
@@ -41399,6 +42649,29 @@ function writeInstructionFile(cwd, ide) {
|
|
|
41399
42649
|
if (agentDef.instructionFormat === "mdc") {
|
|
41400
42650
|
return writeMdcInstructionFile(filePath);
|
|
41401
42651
|
}
|
|
42652
|
+
if (agentDef.instructionFormat === "windsurf-rule") {
|
|
42653
|
+
mkdirSync15(dirname5(filePath), { recursive: true });
|
|
42654
|
+
const content = getInstructionContent();
|
|
42655
|
+
const truncatedContent = content.length > 5800 ? `${content.slice(0, 5800)}
|
|
42656
|
+
|
|
42657
|
+
[Truncated \u2014 full content exceeds Windsurf 6K limit]` : content;
|
|
42658
|
+
const windsurfContent = `---
|
|
42659
|
+
trigger: always_on
|
|
42660
|
+
description: "Tool routing instructions for unerr MCP integration"
|
|
42661
|
+
---
|
|
42662
|
+
|
|
42663
|
+
${truncatedContent}
|
|
42664
|
+
`;
|
|
42665
|
+
const existed = existsSync21(filePath);
|
|
42666
|
+
if (existed) {
|
|
42667
|
+
const existing = readFileSync20(filePath, "utf-8");
|
|
42668
|
+
if (existing === windsurfContent) {
|
|
42669
|
+
return { path: filePath, action: "skipped" };
|
|
42670
|
+
}
|
|
42671
|
+
}
|
|
42672
|
+
writeFileSync11(filePath, windsurfContent, "utf-8");
|
|
42673
|
+
return { path: filePath, action: existed ? "updated" : "created" };
|
|
42674
|
+
}
|
|
41402
42675
|
if (agentDef.instructionFormat === "antigravity-rule") {
|
|
41403
42676
|
mkdirSync15(dirname5(filePath), { recursive: true });
|
|
41404
42677
|
const content = getInstructionContent();
|
|
@@ -41477,6 +42750,13 @@ function removeInstructionSection(cwd, ide) {
|
|
|
41477
42750
|
unlinkSync3(filePath);
|
|
41478
42751
|
return true;
|
|
41479
42752
|
}
|
|
42753
|
+
if (agentDef.instructionFormat === "windsurf-rule") {
|
|
42754
|
+
if (existsSync21(filePath)) {
|
|
42755
|
+
unlinkSync3(filePath);
|
|
42756
|
+
return true;
|
|
42757
|
+
}
|
|
42758
|
+
return false;
|
|
42759
|
+
}
|
|
41480
42760
|
if (agentDef.instructionFormat === "antigravity-rule") {
|
|
41481
42761
|
if (existsSync21(filePath)) {
|
|
41482
42762
|
unlinkSync3(filePath);
|
|
@@ -42041,8 +43321,8 @@ async function persistPatterns(patterns, _unerrDir) {
|
|
|
42041
43321
|
);
|
|
42042
43322
|
return;
|
|
42043
43323
|
}
|
|
42044
|
-
const { readFileSync:
|
|
42045
|
-
const config = JSON.parse(
|
|
43324
|
+
const { readFileSync: readFileSync57 } = await import("fs");
|
|
43325
|
+
const config = JSON.parse(readFileSync57(configPath, "utf-8"));
|
|
42046
43326
|
if (!config.repoId || !config.orgId) {
|
|
42047
43327
|
warn("Repo not configured \u2014 cannot persist to CozoDB.");
|
|
42048
43328
|
return;
|
|
@@ -42060,7 +43340,7 @@ async function persistPatterns(patterns, _unerrDir) {
|
|
|
42060
43340
|
const { CozoGraphStore: CozoGraphStore2 } = await Promise.resolve().then(() => (init_local_graph(), local_graph_exports));
|
|
42061
43341
|
const store = await CozoGraphStore2.create(db);
|
|
42062
43342
|
const { unpack } = await import("msgpackr");
|
|
42063
|
-
const raw =
|
|
43343
|
+
const raw = readFileSync57(snapshotPath2);
|
|
42064
43344
|
const buffer = gunzipSync2(raw);
|
|
42065
43345
|
const envelope = unpack(buffer);
|
|
42066
43346
|
await store.loadSnapshot(envelope);
|
|
@@ -42727,8 +44007,8 @@ function registerStatusCommand(program2) {
|
|
|
42727
44007
|
try {
|
|
42728
44008
|
const logsDir = join38(unerrDir, "logs");
|
|
42729
44009
|
if (existsSync31(logsDir)) {
|
|
42730
|
-
const { readdirSync:
|
|
42731
|
-
const logs =
|
|
44010
|
+
const { readdirSync: readdirSync15, statSync: statSync12 } = await import("fs");
|
|
44011
|
+
const logs = readdirSync15(logsDir).filter((f) => f.startsWith("session-") && f.endsWith(".log")).map((f) => ({
|
|
42732
44012
|
name: f,
|
|
42733
44013
|
mtime: statSync12(join38(logsDir, f)).mtimeMs
|
|
42734
44014
|
})).sort((a, b) => b.mtime - a.mtime);
|
|
@@ -43293,17 +44573,538 @@ async function isInsideGitRepo2() {
|
|
|
43293
44573
|
const { isGitRepo: isGitRepo3 } = await Promise.resolve().then(() => (init_git(), git_exports));
|
|
43294
44574
|
return isGitRepo3(process.cwd());
|
|
43295
44575
|
}
|
|
44576
|
+
var DEFINITIVE_MARKERS = [
|
|
44577
|
+
// JavaScript / TypeScript
|
|
44578
|
+
"package.json",
|
|
44579
|
+
"tsconfig.json",
|
|
44580
|
+
"jsconfig.json",
|
|
44581
|
+
"deno.json",
|
|
44582
|
+
"deno.jsonc",
|
|
44583
|
+
"bun.lockb",
|
|
44584
|
+
"bunfig.toml",
|
|
44585
|
+
// Python
|
|
44586
|
+
"pyproject.toml",
|
|
44587
|
+
"setup.py",
|
|
44588
|
+
"setup.cfg",
|
|
44589
|
+
"Pipfile",
|
|
44590
|
+
"hatch.toml",
|
|
44591
|
+
// Rust
|
|
44592
|
+
"Cargo.toml",
|
|
44593
|
+
// Go
|
|
44594
|
+
"go.mod",
|
|
44595
|
+
// Java / Kotlin / Gradle
|
|
44596
|
+
"pom.xml",
|
|
44597
|
+
"build.gradle",
|
|
44598
|
+
"build.gradle.kts",
|
|
44599
|
+
"settings.gradle",
|
|
44600
|
+
"settings.gradle.kts",
|
|
44601
|
+
// C# / .NET
|
|
44602
|
+
"Directory.Build.props",
|
|
44603
|
+
"global.json",
|
|
44604
|
+
"nuget.config",
|
|
44605
|
+
// Ruby
|
|
44606
|
+
"Gemfile",
|
|
44607
|
+
"Rakefile",
|
|
44608
|
+
// PHP
|
|
44609
|
+
"composer.json",
|
|
44610
|
+
// Swift / Obj-C / Apple
|
|
44611
|
+
"Package.swift",
|
|
44612
|
+
"Podfile",
|
|
44613
|
+
// C / C++
|
|
44614
|
+
"CMakeLists.txt",
|
|
44615
|
+
"Makefile",
|
|
44616
|
+
"configure.ac",
|
|
44617
|
+
"meson.build",
|
|
44618
|
+
"conanfile.txt",
|
|
44619
|
+
"conanfile.py",
|
|
44620
|
+
"vcpkg.json",
|
|
44621
|
+
// Dart / Flutter
|
|
44622
|
+
"pubspec.yaml",
|
|
44623
|
+
// Elixir
|
|
44624
|
+
"mix.exs",
|
|
44625
|
+
// Scala
|
|
44626
|
+
"build.sbt",
|
|
44627
|
+
"build.sc",
|
|
44628
|
+
// Haskell
|
|
44629
|
+
"stack.yaml",
|
|
44630
|
+
"cabal.project",
|
|
44631
|
+
// Clojure
|
|
44632
|
+
"project.clj",
|
|
44633
|
+
"deps.edn",
|
|
44634
|
+
"shadow-cljs.edn",
|
|
44635
|
+
// Zig
|
|
44636
|
+
"build.zig",
|
|
44637
|
+
"build.zig.zon",
|
|
44638
|
+
// Julia
|
|
44639
|
+
"Project.toml",
|
|
44640
|
+
// Terraform / IaC
|
|
44641
|
+
"main.tf",
|
|
44642
|
+
"terraform.tf",
|
|
44643
|
+
"pulumi.yaml",
|
|
44644
|
+
"serverless.yml",
|
|
44645
|
+
"cdk.json",
|
|
44646
|
+
"sam.yaml",
|
|
44647
|
+
// Containers
|
|
44648
|
+
"Dockerfile",
|
|
44649
|
+
"docker-compose.yml",
|
|
44650
|
+
"docker-compose.yaml",
|
|
44651
|
+
// Monorepo
|
|
44652
|
+
"lerna.json",
|
|
44653
|
+
"nx.json",
|
|
44654
|
+
"turbo.json",
|
|
44655
|
+
"pnpm-workspace.yaml",
|
|
44656
|
+
"rush.json",
|
|
44657
|
+
"pants.toml",
|
|
44658
|
+
// Bazel
|
|
44659
|
+
"BUILD.bazel",
|
|
44660
|
+
"WORKSPACE",
|
|
44661
|
+
"WORKSPACE.bazel",
|
|
44662
|
+
// General build / task
|
|
44663
|
+
"Justfile",
|
|
44664
|
+
"Taskfile.yml",
|
|
44665
|
+
// Nix
|
|
44666
|
+
"flake.nix",
|
|
44667
|
+
"shell.nix",
|
|
44668
|
+
"default.nix",
|
|
44669
|
+
// Android
|
|
44670
|
+
"AndroidManifest.xml"
|
|
44671
|
+
];
|
|
44672
|
+
var STRONG_SIGNAL_FILES = [
|
|
44673
|
+
// Lock files (imply a package manager was run here)
|
|
44674
|
+
"package-lock.json",
|
|
44675
|
+
"yarn.lock",
|
|
44676
|
+
"pnpm-lock.yaml",
|
|
44677
|
+
"Pipfile.lock",
|
|
44678
|
+
"poetry.lock",
|
|
44679
|
+
"Gemfile.lock",
|
|
44680
|
+
"composer.lock",
|
|
44681
|
+
"Cargo.lock",
|
|
44682
|
+
"flake.lock",
|
|
44683
|
+
"pubspec.lock",
|
|
44684
|
+
"mix.lock",
|
|
44685
|
+
"go.sum",
|
|
44686
|
+
"uv.lock",
|
|
44687
|
+
"pdm.lock",
|
|
44688
|
+
// CI/CD (a CI config strongly implies project root)
|
|
44689
|
+
".gitlab-ci.yml",
|
|
44690
|
+
"Jenkinsfile",
|
|
44691
|
+
".travis.yml",
|
|
44692
|
+
"azure-pipelines.yml",
|
|
44693
|
+
"bitbucket-pipelines.yml",
|
|
44694
|
+
// Editor / LSP configs (placed at project root)
|
|
44695
|
+
".editorconfig",
|
|
44696
|
+
".prettierrc",
|
|
44697
|
+
".prettierrc.json",
|
|
44698
|
+
".eslintrc",
|
|
44699
|
+
".eslintrc.json",
|
|
44700
|
+
".eslintrc.js",
|
|
44701
|
+
"biome.json",
|
|
44702
|
+
"biome.jsonc",
|
|
44703
|
+
"pyrightconfig.json",
|
|
44704
|
+
"rust-toolchain.toml",
|
|
44705
|
+
".clang-format",
|
|
44706
|
+
".clangd",
|
|
44707
|
+
"compile_commands.json",
|
|
44708
|
+
".luarc.json",
|
|
44709
|
+
"stylua.toml",
|
|
44710
|
+
".rubocop.yml",
|
|
44711
|
+
".php-cs-fixer.php",
|
|
44712
|
+
// Environment
|
|
44713
|
+
".env",
|
|
44714
|
+
".envrc",
|
|
44715
|
+
// Git-specific (at root level these are strong signals)
|
|
44716
|
+
".gitignore",
|
|
44717
|
+
".gitattributes"
|
|
44718
|
+
];
|
|
44719
|
+
var STRONG_SIGNAL_DIRS = [
|
|
44720
|
+
".git",
|
|
44721
|
+
".svn",
|
|
44722
|
+
".hg",
|
|
44723
|
+
".fossil",
|
|
44724
|
+
".bzr",
|
|
44725
|
+
"_darcs",
|
|
44726
|
+
// VCS
|
|
44727
|
+
".github",
|
|
44728
|
+
".circleci",
|
|
44729
|
+
".buildkite",
|
|
44730
|
+
// CI/CD
|
|
44731
|
+
".idea",
|
|
44732
|
+
".vscode",
|
|
44733
|
+
".vs",
|
|
44734
|
+
".fleet",
|
|
44735
|
+
".zed"
|
|
44736
|
+
// IDE
|
|
44737
|
+
];
|
|
44738
|
+
var EXTENSION_MARKERS = [
|
|
44739
|
+
".sln",
|
|
44740
|
+
".csproj",
|
|
44741
|
+
".fsproj",
|
|
44742
|
+
".vbproj",
|
|
44743
|
+
// .NET
|
|
44744
|
+
".xcodeproj",
|
|
44745
|
+
".xcworkspace",
|
|
44746
|
+
// Xcode (dirs)
|
|
44747
|
+
".cabal",
|
|
44748
|
+
// Haskell
|
|
44749
|
+
".nimble",
|
|
44750
|
+
// Nim
|
|
44751
|
+
".gemspec",
|
|
44752
|
+
// Ruby
|
|
44753
|
+
".rockspec"
|
|
44754
|
+
// Lua
|
|
44755
|
+
];
|
|
44756
|
+
var CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
44757
|
+
// Web / JS ecosystem
|
|
44758
|
+
".ts",
|
|
44759
|
+
".tsx",
|
|
44760
|
+
".js",
|
|
44761
|
+
".jsx",
|
|
44762
|
+
".mjs",
|
|
44763
|
+
".cjs",
|
|
44764
|
+
".vue",
|
|
44765
|
+
".svelte",
|
|
44766
|
+
".astro",
|
|
44767
|
+
// Systems
|
|
44768
|
+
".rs",
|
|
44769
|
+
".go",
|
|
44770
|
+
".c",
|
|
44771
|
+
".h",
|
|
44772
|
+
".cpp",
|
|
44773
|
+
".cc",
|
|
44774
|
+
".cxx",
|
|
44775
|
+
".hpp",
|
|
44776
|
+
".hxx",
|
|
44777
|
+
".zig",
|
|
44778
|
+
// JVM
|
|
44779
|
+
".java",
|
|
44780
|
+
".kt",
|
|
44781
|
+
".kts",
|
|
44782
|
+
".scala",
|
|
44783
|
+
".sc",
|
|
44784
|
+
".clj",
|
|
44785
|
+
".cljs",
|
|
44786
|
+
".cljc",
|
|
44787
|
+
// .NET
|
|
44788
|
+
".cs",
|
|
44789
|
+
".fs",
|
|
44790
|
+
".fsx",
|
|
44791
|
+
".vb",
|
|
44792
|
+
// Scripting
|
|
44793
|
+
".py",
|
|
44794
|
+
".pyi",
|
|
44795
|
+
".rb",
|
|
44796
|
+
".php",
|
|
44797
|
+
".lua",
|
|
44798
|
+
".pl",
|
|
44799
|
+
".pm",
|
|
44800
|
+
".raku",
|
|
44801
|
+
// Apple / Mobile
|
|
44802
|
+
".swift",
|
|
44803
|
+
".m",
|
|
44804
|
+
".mm",
|
|
44805
|
+
".dart",
|
|
44806
|
+
// Functional
|
|
44807
|
+
".hs",
|
|
44808
|
+
".lhs",
|
|
44809
|
+
".ml",
|
|
44810
|
+
".mli",
|
|
44811
|
+
".re",
|
|
44812
|
+
".rei",
|
|
44813
|
+
".elm",
|
|
44814
|
+
".ex",
|
|
44815
|
+
".exs",
|
|
44816
|
+
".purs",
|
|
44817
|
+
".idr",
|
|
44818
|
+
".agda",
|
|
44819
|
+
".lean",
|
|
44820
|
+
// Data / Science
|
|
44821
|
+
".r",
|
|
44822
|
+
".R",
|
|
44823
|
+
".jl",
|
|
44824
|
+
// Infrastructure
|
|
44825
|
+
".tf",
|
|
44826
|
+
".hcl",
|
|
44827
|
+
// Emerging / Niche
|
|
44828
|
+
".nim",
|
|
44829
|
+
".cr",
|
|
44830
|
+
".d",
|
|
44831
|
+
".v",
|
|
44832
|
+
".sol",
|
|
44833
|
+
".move",
|
|
44834
|
+
".cairo",
|
|
44835
|
+
".nr",
|
|
44836
|
+
// Shell / Config as code
|
|
44837
|
+
".sh",
|
|
44838
|
+
".bash",
|
|
44839
|
+
".zsh",
|
|
44840
|
+
".fish",
|
|
44841
|
+
".ps1"
|
|
44842
|
+
]);
|
|
44843
|
+
var ANTI_SIGNAL_FILES = [
|
|
44844
|
+
".bashrc",
|
|
44845
|
+
".zshrc",
|
|
44846
|
+
".bash_profile",
|
|
44847
|
+
".profile",
|
|
44848
|
+
".zprofile",
|
|
44849
|
+
".zshenv",
|
|
44850
|
+
".bash_history",
|
|
44851
|
+
".zsh_history"
|
|
44852
|
+
];
|
|
44853
|
+
var ANTI_SIGNAL_DIRS = [
|
|
44854
|
+
".config",
|
|
44855
|
+
".local",
|
|
44856
|
+
".cache",
|
|
44857
|
+
".ssh",
|
|
44858
|
+
".gnupg",
|
|
44859
|
+
".npm",
|
|
44860
|
+
".cargo",
|
|
44861
|
+
"Desktop",
|
|
44862
|
+
"Downloads",
|
|
44863
|
+
"Documents",
|
|
44864
|
+
"Pictures",
|
|
44865
|
+
"Music",
|
|
44866
|
+
"Movies",
|
|
44867
|
+
"Library",
|
|
44868
|
+
"Applications"
|
|
44869
|
+
];
|
|
44870
|
+
var ANTI_SIGNAL_PATHS = [
|
|
44871
|
+
"/",
|
|
44872
|
+
"/usr",
|
|
44873
|
+
"/tmp",
|
|
44874
|
+
"/var",
|
|
44875
|
+
"/etc",
|
|
44876
|
+
"/opt",
|
|
44877
|
+
"/home",
|
|
44878
|
+
"/Users",
|
|
44879
|
+
"/root"
|
|
44880
|
+
];
|
|
44881
|
+
var SOURCE_DIRS = /* @__PURE__ */ new Set([
|
|
44882
|
+
"src",
|
|
44883
|
+
"lib",
|
|
44884
|
+
"app",
|
|
44885
|
+
"apps",
|
|
44886
|
+
"cmd",
|
|
44887
|
+
"pkg",
|
|
44888
|
+
"packages",
|
|
44889
|
+
"internal",
|
|
44890
|
+
"modules",
|
|
44891
|
+
"components",
|
|
44892
|
+
"pages",
|
|
44893
|
+
"routes",
|
|
44894
|
+
"api",
|
|
44895
|
+
"server",
|
|
44896
|
+
"client",
|
|
44897
|
+
"web",
|
|
44898
|
+
"core",
|
|
44899
|
+
"common",
|
|
44900
|
+
"shared",
|
|
44901
|
+
"utils",
|
|
44902
|
+
"helpers",
|
|
44903
|
+
"services",
|
|
44904
|
+
"models",
|
|
44905
|
+
"views",
|
|
44906
|
+
"controllers",
|
|
44907
|
+
"handlers",
|
|
44908
|
+
"middleware",
|
|
44909
|
+
"plugins",
|
|
44910
|
+
"test",
|
|
44911
|
+
"tests",
|
|
44912
|
+
"spec",
|
|
44913
|
+
"__tests__",
|
|
44914
|
+
"e2e",
|
|
44915
|
+
"integration",
|
|
44916
|
+
"scripts",
|
|
44917
|
+
"tools",
|
|
44918
|
+
"bin",
|
|
44919
|
+
"examples",
|
|
44920
|
+
"samples",
|
|
44921
|
+
"proto",
|
|
44922
|
+
"schemas",
|
|
44923
|
+
"migrations",
|
|
44924
|
+
"seeds"
|
|
44925
|
+
]);
|
|
44926
|
+
var DETECTION_THRESHOLD = 5;
|
|
44927
|
+
async function detectProjectRoot(cwd) {
|
|
44928
|
+
const { readdirSync: readdirSync15 } = await import("fs");
|
|
44929
|
+
let score = 0;
|
|
44930
|
+
const signals = [];
|
|
44931
|
+
let hasGit = false;
|
|
44932
|
+
const normalizedCwd = cwd.replace(/\\/g, "/");
|
|
44933
|
+
for (const ap of ANTI_SIGNAL_PATHS) {
|
|
44934
|
+
if (normalizedCwd === ap || normalizedCwd === ap + "/") {
|
|
44935
|
+
return {
|
|
44936
|
+
isProject: false,
|
|
44937
|
+
hasGit: false,
|
|
44938
|
+
reason: `System/root path: ${cwd}`,
|
|
44939
|
+
score: -15,
|
|
44940
|
+
signals: [`anti-path: ${ap}`]
|
|
44941
|
+
};
|
|
44942
|
+
}
|
|
44943
|
+
}
|
|
44944
|
+
let rootEntries = [];
|
|
44945
|
+
try {
|
|
44946
|
+
rootEntries = readdirSync15(cwd, { withFileTypes: true }).map((e) => ({
|
|
44947
|
+
name: e.name,
|
|
44948
|
+
isFile: e.isFile(),
|
|
44949
|
+
isDir: e.isDirectory()
|
|
44950
|
+
}));
|
|
44951
|
+
} catch {
|
|
44952
|
+
return {
|
|
44953
|
+
isProject: false,
|
|
44954
|
+
hasGit: false,
|
|
44955
|
+
reason: "Cannot read directory",
|
|
44956
|
+
score: -1,
|
|
44957
|
+
signals: ["unreadable"]
|
|
44958
|
+
};
|
|
44959
|
+
}
|
|
44960
|
+
const rootFileNames = new Set(rootEntries.filter((e) => e.isFile).map((e) => e.name));
|
|
44961
|
+
const rootDirNames = new Set(rootEntries.filter((e) => e.isDir).map((e) => e.name));
|
|
44962
|
+
for (const af of ANTI_SIGNAL_FILES) {
|
|
44963
|
+
if (rootFileNames.has(af)) {
|
|
44964
|
+
score -= 3;
|
|
44965
|
+
signals.push(`anti-file: ${af}`);
|
|
44966
|
+
}
|
|
44967
|
+
}
|
|
44968
|
+
for (const ad of ANTI_SIGNAL_DIRS) {
|
|
44969
|
+
if (rootDirNames.has(ad)) {
|
|
44970
|
+
score -= 2;
|
|
44971
|
+
signals.push(`anti-dir: ${ad}/`);
|
|
44972
|
+
}
|
|
44973
|
+
}
|
|
44974
|
+
for (const marker of DEFINITIVE_MARKERS) {
|
|
44975
|
+
if (rootFileNames.has(marker)) {
|
|
44976
|
+
score += 10;
|
|
44977
|
+
signals.push(`marker: ${marker}`);
|
|
44978
|
+
break;
|
|
44979
|
+
}
|
|
44980
|
+
}
|
|
44981
|
+
if (score < DETECTION_THRESHOLD) {
|
|
44982
|
+
for (const entry of rootEntries) {
|
|
44983
|
+
if (!entry.isFile && !entry.isDir) continue;
|
|
44984
|
+
const name = entry.name.toLowerCase();
|
|
44985
|
+
for (const ext of EXTENSION_MARKERS) {
|
|
44986
|
+
if (name.endsWith(ext)) {
|
|
44987
|
+
score += 10;
|
|
44988
|
+
signals.push(`ext-marker: ${entry.name}`);
|
|
44989
|
+
break;
|
|
44990
|
+
}
|
|
44991
|
+
}
|
|
44992
|
+
if (score >= DETECTION_THRESHOLD) break;
|
|
44993
|
+
}
|
|
44994
|
+
}
|
|
44995
|
+
for (const vcs of STRONG_SIGNAL_DIRS.slice(0, 6)) {
|
|
44996
|
+
if (rootDirNames.has(vcs)) {
|
|
44997
|
+
if (vcs === ".git") hasGit = true;
|
|
44998
|
+
score += 8;
|
|
44999
|
+
signals.push(`vcs: ${vcs}/`);
|
|
45000
|
+
break;
|
|
45001
|
+
}
|
|
45002
|
+
}
|
|
45003
|
+
for (const sf of STRONG_SIGNAL_FILES) {
|
|
45004
|
+
if (rootFileNames.has(sf)) {
|
|
45005
|
+
score += 4;
|
|
45006
|
+
signals.push(`strong: ${sf}`);
|
|
45007
|
+
if (score >= DETECTION_THRESHOLD) break;
|
|
45008
|
+
}
|
|
45009
|
+
}
|
|
45010
|
+
for (const sd of STRONG_SIGNAL_DIRS.slice(6)) {
|
|
45011
|
+
if (rootDirNames.has(sd)) {
|
|
45012
|
+
score += 4;
|
|
45013
|
+
signals.push(`strong: ${sd}/`);
|
|
45014
|
+
if (score >= DETECTION_THRESHOLD) break;
|
|
45015
|
+
}
|
|
45016
|
+
}
|
|
45017
|
+
if (!hasGit) {
|
|
45018
|
+
try {
|
|
45019
|
+
hasGit = await isInsideGitRepo2();
|
|
45020
|
+
if (hasGit) {
|
|
45021
|
+
score += 6;
|
|
45022
|
+
signals.push("git: inside work tree");
|
|
45023
|
+
}
|
|
45024
|
+
} catch {
|
|
45025
|
+
}
|
|
45026
|
+
}
|
|
45027
|
+
if (score >= DETECTION_THRESHOLD) {
|
|
45028
|
+
const topSignal2 = signals.find((s) => !s.startsWith("anti-")) ?? signals[0] ?? "unknown";
|
|
45029
|
+
return { isProject: true, hasGit, reason: topSignal2, score, signals };
|
|
45030
|
+
}
|
|
45031
|
+
function dirHasCodeFile(dir) {
|
|
45032
|
+
try {
|
|
45033
|
+
const entries = readdirSync15(dir, { withFileTypes: true });
|
|
45034
|
+
for (const entry of entries) {
|
|
45035
|
+
if (!entry.isFile()) continue;
|
|
45036
|
+
const dot = entry.name.lastIndexOf(".");
|
|
45037
|
+
if (dot >= 0 && CODE_EXTENSIONS.has(entry.name.slice(dot).toLowerCase())) {
|
|
45038
|
+
return entry.name;
|
|
45039
|
+
}
|
|
45040
|
+
}
|
|
45041
|
+
} catch {
|
|
45042
|
+
}
|
|
45043
|
+
return null;
|
|
45044
|
+
}
|
|
45045
|
+
const rootCodeFile = dirHasCodeFile(cwd);
|
|
45046
|
+
if (rootCodeFile) {
|
|
45047
|
+
score += 5;
|
|
45048
|
+
signals.push(`code-root: ${rootCodeFile}`);
|
|
45049
|
+
}
|
|
45050
|
+
if (score >= DETECTION_THRESHOLD) {
|
|
45051
|
+
const topSignal2 = signals.find((s) => !s.startsWith("anti-")) ?? signals[0] ?? "unknown";
|
|
45052
|
+
return { isProject: true, hasGit, reason: topSignal2, score, signals };
|
|
45053
|
+
}
|
|
45054
|
+
for (const dirEntry of rootEntries) {
|
|
45055
|
+
if (!dirEntry.isDir || dirEntry.name.startsWith(".")) continue;
|
|
45056
|
+
if (!SOURCE_DIRS.has(dirEntry.name.toLowerCase())) continue;
|
|
45057
|
+
const sourceDir = join67(cwd, dirEntry.name);
|
|
45058
|
+
const srcCodeFile = dirHasCodeFile(sourceDir);
|
|
45059
|
+
if (srcCodeFile) {
|
|
45060
|
+
score += 6;
|
|
45061
|
+
signals.push(`code-src: ${dirEntry.name}/${srcCodeFile}`);
|
|
45062
|
+
break;
|
|
45063
|
+
}
|
|
45064
|
+
try {
|
|
45065
|
+
const subEntries = readdirSync15(sourceDir, { withFileTypes: true });
|
|
45066
|
+
let found = false;
|
|
45067
|
+
for (const sub of subEntries) {
|
|
45068
|
+
if (!sub.isDirectory() || sub.name.startsWith(".")) continue;
|
|
45069
|
+
const deepCode = dirHasCodeFile(join67(sourceDir, sub.name));
|
|
45070
|
+
if (deepCode) {
|
|
45071
|
+
score += 6;
|
|
45072
|
+
signals.push(`code-src: ${dirEntry.name}/${sub.name}/${deepCode}`);
|
|
45073
|
+
found = true;
|
|
45074
|
+
break;
|
|
45075
|
+
}
|
|
45076
|
+
}
|
|
45077
|
+
if (found) break;
|
|
45078
|
+
} catch {
|
|
45079
|
+
}
|
|
45080
|
+
}
|
|
45081
|
+
const isProject = score >= DETECTION_THRESHOLD;
|
|
45082
|
+
const topSignal = signals.find((s) => !s.startsWith("anti-")) ?? signals[0] ?? "No signals";
|
|
45083
|
+
return { isProject, hasGit, reason: isProject ? topSignal : "No code files or project markers found", score, signals };
|
|
45084
|
+
}
|
|
43296
45085
|
function readLocalConfig(cwd) {
|
|
43297
|
-
const configPath =
|
|
43298
|
-
if (!
|
|
45086
|
+
const configPath = join67(cwd, ".unerr", "config.json");
|
|
45087
|
+
if (!existsSync62(configPath)) return null;
|
|
43299
45088
|
try {
|
|
43300
|
-
return JSON.parse(
|
|
45089
|
+
return JSON.parse(readFileSync56(configPath, "utf-8"));
|
|
43301
45090
|
} catch {
|
|
43302
45091
|
return null;
|
|
43303
45092
|
}
|
|
43304
45093
|
}
|
|
43305
45094
|
async function resumeBoot(config) {
|
|
43306
45095
|
initFileLog(process.cwd());
|
|
45096
|
+
const detection = await detectProjectRoot(process.cwd());
|
|
45097
|
+
if (!detection.isProject) {
|
|
45098
|
+
const antiSignals = detection.signals.filter((s) => s.startsWith("anti-"));
|
|
45099
|
+
process.stderr.write(
|
|
45100
|
+
`\x1B[38;2;248;113;113m\u2717\x1B[0m No code project detected in this directory.
|
|
45101
|
+
Found .unerr/config.json but the directory doesn't look like a project root.
|
|
45102
|
+
Detection score: ${detection.score} (need ${DETECTION_THRESHOLD})
|
|
45103
|
+
` + (antiSignals.length > 0 ? ` Negative signals: ${antiSignals.map((s) => s.replace("anti-", "")).join(", ")}
|
|
45104
|
+
` : "") + "\n unerr checks for: project files (package.json, Cargo.toml, go.mod, ...),\n VCS directories (.git, .hg), IDE configs, CI/CD files, lock files,\n and source code across 50+ languages.\n\n \u25B8 Run \x1B[1munerr\x1B[0m from a project root directory.\n"
|
|
45105
|
+
);
|
|
45106
|
+
process.exit(1);
|
|
45107
|
+
}
|
|
43307
45108
|
const { initSessionLogger: initSessionLogger2, createSessionModuleLogger: createSessionModuleLogger2 } = await Promise.resolve().then(() => (init_session_logger(), session_logger_exports));
|
|
43308
45109
|
initSessionLogger2();
|
|
43309
45110
|
const log25 = createSessionModuleLogger2("boot");
|
|
@@ -43317,13 +45118,24 @@ async function firstRunBoot() {
|
|
|
43317
45118
|
const { initSessionLogger: initSessionLogger2, createSessionModuleLogger: createSessionModuleLogger2 } = await Promise.resolve().then(() => (init_session_logger(), session_logger_exports));
|
|
43318
45119
|
initSessionLogger2();
|
|
43319
45120
|
const log25 = createSessionModuleLogger2("boot");
|
|
43320
|
-
|
|
45121
|
+
const detection = await detectProjectRoot(process.cwd());
|
|
45122
|
+
if (!detection.isProject) {
|
|
45123
|
+
const antiSignals = detection.signals.filter((s) => s.startsWith("anti-"));
|
|
43321
45124
|
process.stderr.write(
|
|
43322
|
-
|
|
45125
|
+
`\x1B[38;2;248;113;113m\u2717\x1B[0m No code project detected in this directory.
|
|
45126
|
+
unerr needs a folder containing source code to index.
|
|
45127
|
+
Detection score: ${detection.score} (need ${DETECTION_THRESHOLD})
|
|
45128
|
+
` + (antiSignals.length > 0 ? ` Negative signals: ${antiSignals.map((s) => s.replace("anti-", "")).join(", ")}
|
|
45129
|
+
` : "") + "\n Checked for: project files (package.json, Cargo.toml, go.mod, pyproject.toml, ...),\n VCS directories (.git, .hg, .svn), IDE configs (.vscode, .idea),\n CI/CD files (.github, Jenkinsfile), lock files, and source code\n across 50+ languages in root + standard source directories.\n\n \u25B8 Run \x1B[1munerr\x1B[0m from a project root directory.\n"
|
|
43323
45130
|
);
|
|
43324
45131
|
process.exit(1);
|
|
43325
45132
|
}
|
|
43326
|
-
|
|
45133
|
+
if (!detection.hasGit) {
|
|
45134
|
+
process.stderr.write(
|
|
45135
|
+
"\x1B[38;2;251;191;36m\u26A0\x1B[0m Project detected (" + detection.reason + ") but no git repository found.\n unerr works best with git. Consider running \x1B[1mgit init\x1B[0m first.\n Continuing anyway...\n\n"
|
|
45136
|
+
);
|
|
45137
|
+
}
|
|
45138
|
+
log25.info({ msg: "First-run boot, entering local setup", detection: detection.reason, score: detection.score, hasGit: detection.hasGit });
|
|
43327
45139
|
const { runSetup: runSetup2 } = await Promise.resolve().then(() => (init_setup_wizard(), setup_wizard_exports));
|
|
43328
45140
|
const result = await runSetup2();
|
|
43329
45141
|
if (result.action === "setup") {
|
|
@@ -43335,15 +45147,21 @@ async function firstRunBoot() {
|
|
|
43335
45147
|
}
|
|
43336
45148
|
async function mcpBoot(cwd) {
|
|
43337
45149
|
initFileLog(cwd);
|
|
43338
|
-
|
|
45150
|
+
const detection = await detectProjectRoot(cwd);
|
|
45151
|
+
if (!detection.isProject) {
|
|
45152
|
+
const antiSignals = detection.signals.filter((s) => s.startsWith("anti-"));
|
|
43339
45153
|
process.stderr.write(
|
|
43340
|
-
|
|
45154
|
+
`\x1B[38;2;248;113;113m\u2717\x1B[0m No code project detected in this directory.
|
|
45155
|
+
unerr needs a folder containing source code to index.
|
|
45156
|
+
Detection score: ${detection.score} (need ${DETECTION_THRESHOLD})
|
|
45157
|
+
` + (antiSignals.length > 0 ? ` Negative signals: ${antiSignals.map((s) => s.replace("anti-", "")).join(", ")}
|
|
45158
|
+
` : "") + "\n \u25B8 Run \x1B[1munerr --mcp\x1B[0m from a project root directory.\n"
|
|
43341
45159
|
);
|
|
43342
45160
|
process.exit(1);
|
|
43343
45161
|
}
|
|
43344
|
-
const stateDir =
|
|
43345
|
-
const sockPath =
|
|
43346
|
-
if (
|
|
45162
|
+
const stateDir = join67(cwd, ".unerr", "state");
|
|
45163
|
+
const sockPath = join67(stateDir, "proxy.sock");
|
|
45164
|
+
if (existsSync62(sockPath)) {
|
|
43347
45165
|
const { PidLock: PidLock2 } = await Promise.resolve().then(() => (init_pid_lock(), pid_lock_exports));
|
|
43348
45166
|
const pidLock = new PidLock2(stateDir);
|
|
43349
45167
|
const probeResult = await pidLock.probe();
|
|
@@ -43367,10 +45185,10 @@ async function mcpBoot(cwd) {
|
|
|
43367
45185
|
const { mkdirSync: mkdirSync39, writeFileSync: writeFileSync36 } = await import("fs");
|
|
43368
45186
|
const { createHash: createHash5 } = await import("crypto");
|
|
43369
45187
|
const repoId = createHash5("sha256").update(cwd).digest("hex").slice(0, 12);
|
|
43370
|
-
const unerrDir =
|
|
45188
|
+
const unerrDir = join67(cwd, ".unerr");
|
|
43371
45189
|
mkdirSync39(unerrDir, { recursive: true });
|
|
43372
45190
|
writeFileSync36(
|
|
43373
|
-
|
|
45191
|
+
join67(unerrDir, "config.json"),
|
|
43374
45192
|
JSON.stringify(
|
|
43375
45193
|
{ repoId, mode: "local", createdAt: (/* @__PURE__ */ new Date()).toISOString() },
|
|
43376
45194
|
null,
|