@triedotdev/mcp 1.0.151 → 1.0.155
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 +34 -6
- package/dist/{autonomy-config-3APNC6QF.js → autonomy-config-RKLZW4XL.js} +4 -4
- package/dist/{chat-store-HMTDL7I2.js → chat-store-O3IJ5PMN.js} +4 -4
- package/dist/{chunk-IXO4G4D3.js → chunk-2LAJKFWU.js} +2 -2
- package/dist/{chunk-F7BMFOZ6.js → chunk-3CYMLM35.js} +2 -2
- package/dist/{chunk-ED7PLRQA.js → chunk-4ZAFQEP6.js} +4 -4
- package/dist/{chunk-4TQQP7JD.js → chunk-7F2R2ITA.js} +2 -2
- package/dist/{chunk-SU3WCAC4.js → chunk-BUA5PQJY.js} +1487 -49
- package/dist/chunk-BUA5PQJY.js.map +1 -0
- package/dist/{chunk-V3O7C2LY.js → chunk-CBAMZERA.js} +2 -2
- package/dist/{chunk-G7Q23IGF.js → chunk-EMJ7RVWB.js} +9 -9
- package/dist/{chunk-TU7D5DEW.js → chunk-FTOF3FHT.js} +3 -3
- package/dist/{chunk-3KZBC3RJ.js → chunk-FXZAABXO.js} +2 -2
- package/dist/{chunk-TWPX6PHF.js → chunk-HD5H7YSW.js} +2 -2
- package/dist/{chunk-APMV77PU.js → chunk-JKEEQAG2.js} +1 -1
- package/dist/chunk-JKEEQAG2.js.map +1 -0
- package/dist/{chunk-TWQPOVRA.js → chunk-JYWGYUKX.js} +2 -2
- package/dist/{chunk-TIMIKBY2.js → chunk-KLMJKM63.js} +2 -2
- package/dist/{chunk-TCNCNWGV.js → chunk-KYKADM7P.js} +2 -2
- package/dist/{chunk-GLY76TSI.js → chunk-L4FODDDB.js} +2 -2
- package/dist/{chunk-JIS2OCZR.js → chunk-LFNH3CSN.js} +4 -4
- package/dist/{chunk-74R4XSFB.js → chunk-NVZZUUEU.js} +5 -5
- package/dist/{chunk-REHKDCI6.js → chunk-OVSYTWUU.js} +2 -2
- package/dist/{chunk-IFBEAOHH.js → chunk-RY57G46E.js} +6 -6
- package/dist/{chunk-ABY2R7OK.js → chunk-T7UAH7GE.js} +2 -2
- package/dist/{chunk-OJXFQRUE.js → chunk-UL337UDQ.js} +2 -2
- package/dist/{chunk-F4NJ4CBP.js → chunk-WO7CC5FH.js} +2 -2
- package/dist/{chunk-4UDBGYI3.js → chunk-X64XFVAY.js} +13 -13
- package/dist/{chunk-QQG42HCI.js → chunk-XD2HKZVB.js} +2 -2
- package/dist/{chunk-LNUMECBJ.js → chunk-XUGUKSKO.js} +10 -2
- package/dist/chunk-XUGUKSKO.js.map +1 -0
- package/dist/{chunk-WOTLY5NA.js → chunk-YEQXKKZQ.js} +2 -2
- package/dist/{chunk-7HYOJ4Q7.js → chunk-Z2E7X4WI.js} +7 -7
- package/dist/cli/create-agent.js +2 -2
- package/dist/cli/main.js +86 -40
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/yolo-daemon.js +78 -27
- package/dist/cli/yolo-daemon.js.map +1 -1
- package/dist/{client-5L64D5SQ.js → client-ZHOLZTRW.js} +4 -4
- package/dist/{codebase-index-OOE7OAHP.js → codebase-index-N37NDF2A.js} +4 -4
- package/dist/{fast-analyzer-FMU3X4AZ.js → fast-analyzer-U6URGNQT.js} +6 -6
- package/dist/github-ingester-AR5A4RAC.js +11 -0
- package/dist/{goal-manager-VTBFFYN4.js → goal-manager-5QDITJKE.js} +8 -8
- package/dist/{goal-validator-EM5XVWVC.js → goal-validator-FU5QWDQT.js} +7 -7
- package/dist/graph-JO7GG65P.js +10 -0
- package/dist/{hypothesis-4UPE7KXU.js → hypothesis-JURDWVDC.js} +8 -8
- package/dist/incident-index-7CAXUNTL.js +11 -0
- package/dist/index.js +57 -1466
- package/dist/index.js.map +1 -1
- package/dist/{insight-store-QEEUQR5L.js → insight-store-AMEP5PPF.js} +4 -4
- package/dist/{issue-store-C6XYENE5.js → issue-store-RM3XLLKG.js} +5 -5
- package/dist/{ledger-VNA4DX3Z.js → ledger-PLE3C3X4.js} +4 -4
- package/dist/linear-ingester-NHFMKJBZ.js +11 -0
- package/dist/{output-manager-DZO5LGSG.js → output-manager-FX4V7ERT.js} +3 -3
- package/dist/{progress-PQVEM7BR.js → progress-PAYTY7BF.js} +2 -2
- package/dist/tiered-storage-SQDVZM2M.js +12 -0
- package/dist/trie-agent-TLOJ2UFS.js +27 -0
- package/dist/{vibe-code-signatures-ELEWJFGZ.js → vibe-code-signatures-J4GD4JOV.js} +3 -3
- package/dist/{vulnerability-signatures-EIJQX2TS.js → vulnerability-signatures-EIKOHFPK.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-APMV77PU.js.map +0 -1
- package/dist/chunk-LNUMECBJ.js.map +0 -1
- package/dist/chunk-SU3WCAC4.js.map +0 -1
- package/dist/github-ingester-C66ZRUYC.js +0 -11
- package/dist/graph-26JPZ3DF.js +0 -10
- package/dist/incident-index-H6APJ4S3.js +0 -11
- package/dist/linear-ingester-WIUBWF55.js +0 -11
- package/dist/tiered-storage-P6Z3NV2Q.js +0 -12
- package/dist/trie-agent-GJJJCL6P.js +0 -27
- /package/dist/{autonomy-config-3APNC6QF.js.map → autonomy-config-RKLZW4XL.js.map} +0 -0
- /package/dist/{chat-store-HMTDL7I2.js.map → chat-store-O3IJ5PMN.js.map} +0 -0
- /package/dist/{chunk-IXO4G4D3.js.map → chunk-2LAJKFWU.js.map} +0 -0
- /package/dist/{chunk-F7BMFOZ6.js.map → chunk-3CYMLM35.js.map} +0 -0
- /package/dist/{chunk-ED7PLRQA.js.map → chunk-4ZAFQEP6.js.map} +0 -0
- /package/dist/{chunk-4TQQP7JD.js.map → chunk-7F2R2ITA.js.map} +0 -0
- /package/dist/{chunk-V3O7C2LY.js.map → chunk-CBAMZERA.js.map} +0 -0
- /package/dist/{chunk-G7Q23IGF.js.map → chunk-EMJ7RVWB.js.map} +0 -0
- /package/dist/{chunk-TU7D5DEW.js.map → chunk-FTOF3FHT.js.map} +0 -0
- /package/dist/{chunk-3KZBC3RJ.js.map → chunk-FXZAABXO.js.map} +0 -0
- /package/dist/{chunk-TWPX6PHF.js.map → chunk-HD5H7YSW.js.map} +0 -0
- /package/dist/{chunk-TWQPOVRA.js.map → chunk-JYWGYUKX.js.map} +0 -0
- /package/dist/{chunk-TIMIKBY2.js.map → chunk-KLMJKM63.js.map} +0 -0
- /package/dist/{chunk-TCNCNWGV.js.map → chunk-KYKADM7P.js.map} +0 -0
- /package/dist/{chunk-GLY76TSI.js.map → chunk-L4FODDDB.js.map} +0 -0
- /package/dist/{chunk-JIS2OCZR.js.map → chunk-LFNH3CSN.js.map} +0 -0
- /package/dist/{chunk-74R4XSFB.js.map → chunk-NVZZUUEU.js.map} +0 -0
- /package/dist/{chunk-REHKDCI6.js.map → chunk-OVSYTWUU.js.map} +0 -0
- /package/dist/{chunk-IFBEAOHH.js.map → chunk-RY57G46E.js.map} +0 -0
- /package/dist/{chunk-ABY2R7OK.js.map → chunk-T7UAH7GE.js.map} +0 -0
- /package/dist/{chunk-OJXFQRUE.js.map → chunk-UL337UDQ.js.map} +0 -0
- /package/dist/{chunk-F4NJ4CBP.js.map → chunk-WO7CC5FH.js.map} +0 -0
- /package/dist/{chunk-4UDBGYI3.js.map → chunk-X64XFVAY.js.map} +0 -0
- /package/dist/{chunk-QQG42HCI.js.map → chunk-XD2HKZVB.js.map} +0 -0
- /package/dist/{chunk-WOTLY5NA.js.map → chunk-YEQXKKZQ.js.map} +0 -0
- /package/dist/{chunk-7HYOJ4Q7.js.map → chunk-Z2E7X4WI.js.map} +0 -0
- /package/dist/{client-5L64D5SQ.js.map → client-ZHOLZTRW.js.map} +0 -0
- /package/dist/{codebase-index-OOE7OAHP.js.map → codebase-index-N37NDF2A.js.map} +0 -0
- /package/dist/{fast-analyzer-FMU3X4AZ.js.map → fast-analyzer-U6URGNQT.js.map} +0 -0
- /package/dist/{github-ingester-C66ZRUYC.js.map → github-ingester-AR5A4RAC.js.map} +0 -0
- /package/dist/{goal-manager-VTBFFYN4.js.map → goal-manager-5QDITJKE.js.map} +0 -0
- /package/dist/{goal-validator-EM5XVWVC.js.map → goal-validator-FU5QWDQT.js.map} +0 -0
- /package/dist/{graph-26JPZ3DF.js.map → graph-JO7GG65P.js.map} +0 -0
- /package/dist/{hypothesis-4UPE7KXU.js.map → hypothesis-JURDWVDC.js.map} +0 -0
- /package/dist/{incident-index-H6APJ4S3.js.map → incident-index-7CAXUNTL.js.map} +0 -0
- /package/dist/{insight-store-QEEUQR5L.js.map → insight-store-AMEP5PPF.js.map} +0 -0
- /package/dist/{issue-store-C6XYENE5.js.map → issue-store-RM3XLLKG.js.map} +0 -0
- /package/dist/{ledger-VNA4DX3Z.js.map → ledger-PLE3C3X4.js.map} +0 -0
- /package/dist/{linear-ingester-WIUBWF55.js.map → linear-ingester-NHFMKJBZ.js.map} +0 -0
- /package/dist/{output-manager-DZO5LGSG.js.map → output-manager-FX4V7ERT.js.map} +0 -0
- /package/dist/{progress-PQVEM7BR.js.map → progress-PAYTY7BF.js.map} +0 -0
- /package/dist/{tiered-storage-P6Z3NV2Q.js.map → tiered-storage-SQDVZM2M.js.map} +0 -0
- /package/dist/{trie-agent-GJJJCL6P.js.map → trie-agent-TLOJ2UFS.js.map} +0 -0
- /package/dist/{vibe-code-signatures-ELEWJFGZ.js.map → vibe-code-signatures-J4GD4JOV.js.map} +0 -0
- /package/dist/{vulnerability-signatures-EIJQX2TS.js.map → vulnerability-signatures-EIKOHFPK.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
GitHubIngester
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import {
|
|
6
|
-
CodebaseIndex
|
|
7
|
-
} from "./chunk-WOTLY5NA.js";
|
|
4
|
+
} from "./chunk-CBAMZERA.js";
|
|
8
5
|
import {
|
|
9
6
|
appendToSection,
|
|
10
7
|
completeBootstrap,
|
|
@@ -22,15 +19,12 @@ import {
|
|
|
22
19
|
needsBootstrap,
|
|
23
20
|
projectInfoExists,
|
|
24
21
|
updateProjectSection
|
|
25
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-OVSYTWUU.js";
|
|
26
23
|
import {
|
|
27
24
|
LinearIngester
|
|
28
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-FXZAABXO.js";
|
|
29
26
|
import {
|
|
30
|
-
ExtractionPipeline,
|
|
31
27
|
GitHubBranchesTool,
|
|
32
|
-
InteractiveDashboard,
|
|
33
|
-
StreamingManager,
|
|
34
28
|
TrieCheckTool,
|
|
35
29
|
TrieCloudFixTool,
|
|
36
30
|
TrieExplainTool,
|
|
@@ -44,76 +38,68 @@ import {
|
|
|
44
38
|
TriePipelineTool,
|
|
45
39
|
TrieQueryContextTool,
|
|
46
40
|
TrieTellTool,
|
|
41
|
+
TrieWatchTool,
|
|
47
42
|
getPrompt,
|
|
48
43
|
getSystemPrompt,
|
|
49
44
|
handleCheckpointTool
|
|
50
|
-
} from "./chunk-
|
|
51
|
-
import "./chunk-
|
|
52
|
-
import "./chunk-
|
|
45
|
+
} from "./chunk-BUA5PQJY.js";
|
|
46
|
+
import "./chunk-3CYMLM35.js";
|
|
47
|
+
import "./chunk-X64XFVAY.js";
|
|
53
48
|
import {
|
|
54
|
-
|
|
55
|
-
} from "./chunk-
|
|
56
|
-
import "./chunk-
|
|
57
|
-
import "./chunk-
|
|
58
|
-
import "./chunk-
|
|
49
|
+
CodebaseIndex
|
|
50
|
+
} from "./chunk-YEQXKKZQ.js";
|
|
51
|
+
import "./chunk-KLMJKM63.js";
|
|
52
|
+
import "./chunk-LFNH3CSN.js";
|
|
53
|
+
import "./chunk-NVZZUUEU.js";
|
|
54
|
+
import "./chunk-T7UAH7GE.js";
|
|
59
55
|
import {
|
|
60
56
|
exportToJson,
|
|
61
57
|
formatFriendlyError,
|
|
62
58
|
importFromJson,
|
|
63
59
|
isTrieInitialized
|
|
64
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-EMJ7RVWB.js";
|
|
65
61
|
import {
|
|
66
62
|
loadConfig
|
|
67
|
-
} from "./chunk-
|
|
68
|
-
import "./chunk-
|
|
63
|
+
} from "./chunk-FTOF3FHT.js";
|
|
64
|
+
import "./chunk-KYKADM7P.js";
|
|
69
65
|
import "./chunk-ZV2K6M7T.js";
|
|
70
66
|
import {
|
|
71
67
|
findCrossProjectPatterns,
|
|
72
68
|
getGlobalMemoryStats,
|
|
73
69
|
listTrackedProjects,
|
|
74
70
|
searchGlobalPatterns
|
|
75
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-RY57G46E.js";
|
|
76
72
|
import {
|
|
77
73
|
ContextGraph
|
|
78
|
-
} from "./chunk-
|
|
79
|
-
import "./chunk-
|
|
80
|
-
import
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
import "./chunk-GLY76TSI.js";
|
|
85
|
-
import "./chunk-F4NJ4CBP.js";
|
|
86
|
-
import "./chunk-IXO4G4D3.js";
|
|
74
|
+
} from "./chunk-JYWGYUKX.js";
|
|
75
|
+
import "./chunk-Z2E7X4WI.js";
|
|
76
|
+
import "./chunk-UL337UDQ.js";
|
|
77
|
+
import "./chunk-L4FODDDB.js";
|
|
78
|
+
import "./chunk-WO7CC5FH.js";
|
|
79
|
+
import "./chunk-2LAJKFWU.js";
|
|
87
80
|
import "./chunk-6NLHFIYA.js";
|
|
88
|
-
import
|
|
89
|
-
|
|
90
|
-
} from "./chunk-LNUMECBJ.js";
|
|
91
|
-
import {
|
|
92
|
-
getAutonomyConfig
|
|
93
|
-
} from "./chunk-QQG42HCI.js";
|
|
81
|
+
import "./chunk-XUGUKSKO.js";
|
|
82
|
+
import "./chunk-XD2HKZVB.js";
|
|
94
83
|
import {
|
|
95
84
|
findSimilarIssues,
|
|
96
85
|
getMemoryStats,
|
|
97
86
|
getRecentIssues,
|
|
98
87
|
markIssueResolved,
|
|
99
88
|
purgeIssues,
|
|
100
|
-
searchIssues
|
|
101
|
-
|
|
102
|
-
} from "./chunk-ED7PLRQA.js";
|
|
89
|
+
searchIssues
|
|
90
|
+
} from "./chunk-4ZAFQEP6.js";
|
|
103
91
|
import "./chunk-EFWVF6TI.js";
|
|
104
92
|
import {
|
|
105
|
-
getChangedFilesSinceTimestamp,
|
|
106
|
-
getGitChangedFiles,
|
|
107
93
|
runShellCommandSync
|
|
108
|
-
} from "./chunk-
|
|
94
|
+
} from "./chunk-HD5H7YSW.js";
|
|
109
95
|
import "./chunk-43X6JBEM.js";
|
|
110
96
|
import {
|
|
111
97
|
getTrieDirectory,
|
|
112
98
|
getWorkingDirectory
|
|
113
|
-
} from "./chunk-
|
|
99
|
+
} from "./chunk-7F2R2ITA.js";
|
|
114
100
|
import {
|
|
115
101
|
isInteractiveMode
|
|
116
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-JKEEQAG2.js";
|
|
117
103
|
import "./chunk-DGUM43GV.js";
|
|
118
104
|
|
|
119
105
|
// src/server/mcp-server.ts
|
|
@@ -792,1405 +778,10 @@ trie_test action:"run" files:["src/utils.test.ts"]
|
|
|
792
778
|
}
|
|
793
779
|
};
|
|
794
780
|
|
|
795
|
-
// src/tools/watch.ts
|
|
796
|
-
import { watch, existsSync as existsSync2, readFileSync } from "fs";
|
|
797
|
-
import { stat, readFile as readFile2 } from "fs/promises";
|
|
798
|
-
import { join as join2, extname as extname2, basename as basename2 } from "path";
|
|
799
|
-
import { createHash } from "crypto";
|
|
800
|
-
var WATCH_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
801
|
-
".ts",
|
|
802
|
-
".tsx",
|
|
803
|
-
".js",
|
|
804
|
-
".jsx",
|
|
805
|
-
".mjs",
|
|
806
|
-
".vue",
|
|
807
|
-
".svelte",
|
|
808
|
-
".astro",
|
|
809
|
-
".py",
|
|
810
|
-
".go",
|
|
811
|
-
".rs"
|
|
812
|
-
]);
|
|
813
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
814
|
-
"node_modules",
|
|
815
|
-
".git",
|
|
816
|
-
"dist",
|
|
817
|
-
"build",
|
|
818
|
-
".next",
|
|
819
|
-
".nuxt",
|
|
820
|
-
"coverage",
|
|
821
|
-
".turbo",
|
|
822
|
-
".cache"
|
|
823
|
-
]);
|
|
824
|
-
var TrieWatchTool = class _TrieWatchTool {
|
|
825
|
-
extractionPipeline = null;
|
|
826
|
-
watchedDirectory = "";
|
|
827
|
-
codebaseIndex = null;
|
|
828
|
-
state = {
|
|
829
|
-
isRunning: false,
|
|
830
|
-
lastScan: /* @__PURE__ */ new Map(),
|
|
831
|
-
pendingFiles: /* @__PURE__ */ new Set(),
|
|
832
|
-
scanDebounceTimer: null,
|
|
833
|
-
issueCache: /* @__PURE__ */ new Map(),
|
|
834
|
-
totalIssuesFound: 0,
|
|
835
|
-
filesScanned: 0,
|
|
836
|
-
nudgedFiles: /* @__PURE__ */ new Set(),
|
|
837
|
-
nudges: [],
|
|
838
|
-
lastAutoScan: 0,
|
|
839
|
-
autoScanInProgress: false,
|
|
840
|
-
tokenBudget: {
|
|
841
|
-
used: 0,
|
|
842
|
-
windowStart: Date.now(),
|
|
843
|
-
hourlyLimit: 5e4,
|
|
844
|
-
scansSaved: 0
|
|
845
|
-
},
|
|
846
|
-
cleanFiles: /* @__PURE__ */ new Map()
|
|
847
|
-
};
|
|
848
|
-
watchers = /* @__PURE__ */ new Map();
|
|
849
|
-
streamingManager = void 0;
|
|
850
|
-
dashboard = void 0;
|
|
851
|
-
lastHypothesisCheck = 0;
|
|
852
|
-
pipelineSyncTimer = null;
|
|
853
|
-
static HYPOTHESIS_CHECK_INTERVAL_MS = 3e5;
|
|
854
|
-
// Check every 5 minutes
|
|
855
|
-
static PIPELINE_SYNC_INTERVAL_MS = 30 * 60 * 1e3;
|
|
856
|
-
// 30 minutes
|
|
857
|
-
async execute(args) {
|
|
858
|
-
const { action, directory, debounceMs = 1e3 } = args;
|
|
859
|
-
switch (action) {
|
|
860
|
-
case "start":
|
|
861
|
-
return this.startWatching(getWorkingDirectory(directory), debounceMs);
|
|
862
|
-
case "stop":
|
|
863
|
-
return await this.stopWatching();
|
|
864
|
-
case "status":
|
|
865
|
-
return await this.getStatus();
|
|
866
|
-
case "issues":
|
|
867
|
-
return this.getCurrentIssues();
|
|
868
|
-
case "nudges":
|
|
869
|
-
return this.getNudges();
|
|
870
|
-
default:
|
|
871
|
-
return {
|
|
872
|
-
content: [{
|
|
873
|
-
type: "text",
|
|
874
|
-
text: `Unknown action: ${action}. Use 'start', 'stop', 'status', or 'issues'.`
|
|
875
|
-
}]
|
|
876
|
-
};
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
async startWatching(directory, debounceMs) {
|
|
880
|
-
if (this.state.isRunning) {
|
|
881
|
-
return {
|
|
882
|
-
content: [{
|
|
883
|
-
type: "text",
|
|
884
|
-
text: "[!] Watch mode is already running. Use `trie_watch stop` to stop it first."
|
|
885
|
-
}]
|
|
886
|
-
};
|
|
887
|
-
}
|
|
888
|
-
if (!isTrieInitialized(directory)) {
|
|
889
|
-
return {
|
|
890
|
-
content: [{
|
|
891
|
-
type: "text",
|
|
892
|
-
text: "Trie is not initialized for this project. Run `trie init` first."
|
|
893
|
-
}]
|
|
894
|
-
};
|
|
895
|
-
}
|
|
896
|
-
const anthropicApiKey = process.env.ANTHROPIC_API_KEY;
|
|
897
|
-
if (anthropicApiKey) {
|
|
898
|
-
this.extractionPipeline = new ExtractionPipeline({
|
|
899
|
-
workingDirectory: directory,
|
|
900
|
-
anthropicApiKey
|
|
901
|
-
});
|
|
902
|
-
await this.extractionPipeline.initialize();
|
|
903
|
-
}
|
|
904
|
-
this.codebaseIndex = new CodebaseIndex(directory);
|
|
905
|
-
this.state.isRunning = true;
|
|
906
|
-
this.watchedDirectory = directory;
|
|
907
|
-
this.state.issueCache.clear();
|
|
908
|
-
this.state.totalIssuesFound = 0;
|
|
909
|
-
this.state.filesScanned = 0;
|
|
910
|
-
this.state.nudgedFiles.clear();
|
|
911
|
-
this.state.nudges = [];
|
|
912
|
-
try {
|
|
913
|
-
const storage = getStorage(directory);
|
|
914
|
-
await storage.initialize();
|
|
915
|
-
const unresolvedNudges = await storage.queryNudges({ resolved: false });
|
|
916
|
-
console.debug(`[Watch] Found ${unresolvedNudges.length} unresolved nudges in storage`);
|
|
917
|
-
for (const nudge of unresolvedNudges) {
|
|
918
|
-
let mappedSeverity = "high";
|
|
919
|
-
if (nudge.severity === "critical") {
|
|
920
|
-
mappedSeverity = "critical";
|
|
921
|
-
} else if (nudge.severity === "high") {
|
|
922
|
-
mappedSeverity = "high";
|
|
923
|
-
}
|
|
924
|
-
this.state.nudges.push({
|
|
925
|
-
file: nudge.file || "unknown",
|
|
926
|
-
// Keep full path, don't use basename
|
|
927
|
-
message: nudge.message,
|
|
928
|
-
severity: mappedSeverity,
|
|
929
|
-
timestamp: new Date(nudge.timestamp).toLocaleTimeString("en-US", { hour12: false })
|
|
930
|
-
});
|
|
931
|
-
console.debug(`[Watch] Loaded nudge: ${nudge.message.slice(0, 60)}... (${nudge.severity} -> ${mappedSeverity})`);
|
|
932
|
-
}
|
|
933
|
-
if (unresolvedNudges.length > 0) {
|
|
934
|
-
console.log(`[Watch] \u2713 Loaded ${unresolvedNudges.length} unresolved nudges from storage`);
|
|
935
|
-
} else {
|
|
936
|
-
console.debug(`[Watch] No unresolved nudges found in storage`);
|
|
937
|
-
}
|
|
938
|
-
} catch (error) {
|
|
939
|
-
console.error("[Watch] Failed to load nudges from storage:", error);
|
|
940
|
-
}
|
|
941
|
-
try {
|
|
942
|
-
const graph = new ContextGraph(directory);
|
|
943
|
-
const allNodes = await graph.listNodes();
|
|
944
|
-
const unresolvedIncidents = allNodes.filter(
|
|
945
|
-
(n) => n.type === "incident" && !n.data.resolved
|
|
946
|
-
);
|
|
947
|
-
console.debug(`[Watch] Found ${unresolvedIncidents.length} unresolved incidents in context graph`);
|
|
948
|
-
for (const incident of unresolvedIncidents) {
|
|
949
|
-
const incidentData = incident.data;
|
|
950
|
-
let mappedSeverity = "high";
|
|
951
|
-
if (incidentData.severity === "critical") {
|
|
952
|
-
mappedSeverity = "critical";
|
|
953
|
-
} else if (incidentData.severity === "major") {
|
|
954
|
-
mappedSeverity = "critical";
|
|
955
|
-
} else if (incidentData.severity === "minor") {
|
|
956
|
-
mappedSeverity = "high";
|
|
957
|
-
}
|
|
958
|
-
const edges = await graph.getEdges(incident.id);
|
|
959
|
-
const linkedFiles = edges.filter((e) => e.type === "causedBy" || e.type === "affects").map((e) => {
|
|
960
|
-
const node = allNodes.find((n) => n.id === e.to_id || n.id === e.from_id);
|
|
961
|
-
if (node?.type === "file") {
|
|
962
|
-
return node.data.path;
|
|
963
|
-
}
|
|
964
|
-
return null;
|
|
965
|
-
}).filter((f) => f !== null);
|
|
966
|
-
const file = linkedFiles[0] || "unknown";
|
|
967
|
-
const message = `INCIDENT: ${incidentData.description}`;
|
|
968
|
-
this.state.nudges.push({
|
|
969
|
-
file,
|
|
970
|
-
message,
|
|
971
|
-
severity: mappedSeverity,
|
|
972
|
-
timestamp: new Date(incidentData.timestamp).toLocaleTimeString("en-US", { hour12: false })
|
|
973
|
-
});
|
|
974
|
-
console.debug(`[Watch] Loaded incident: ${incidentData.description.slice(0, 60)}... (${incidentData.severity} -> ${mappedSeverity})`);
|
|
975
|
-
}
|
|
976
|
-
if (unresolvedIncidents.length > 0) {
|
|
977
|
-
console.log(`[Watch] \u2713 Loaded ${unresolvedIncidents.length} unresolved incidents from context graph`);
|
|
978
|
-
} else {
|
|
979
|
-
console.debug(`[Watch] No unresolved incidents found in context graph`);
|
|
980
|
-
}
|
|
981
|
-
} catch (error) {
|
|
982
|
-
console.error("[Watch] Failed to load incidents from context graph:", error);
|
|
983
|
-
}
|
|
984
|
-
if (!isInteractiveMode()) {
|
|
985
|
-
console.error("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
986
|
-
console.error("TRIE AGENT - NOW WATCHING");
|
|
987
|
-
console.error("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
|
|
988
|
-
console.error("Your Trie agent is now watching over your codebase.");
|
|
989
|
-
console.error("Signal extraction: ENABLED (building governance ledger)");
|
|
990
|
-
console.error(`Watching: ${directory}`);
|
|
991
|
-
console.error(`Debounce: ${debounceMs}ms`);
|
|
992
|
-
console.error("");
|
|
993
|
-
}
|
|
994
|
-
if (isInteractiveMode()) {
|
|
995
|
-
this.streamingManager = new StreamingManager();
|
|
996
|
-
this.dashboard = new InteractiveDashboard();
|
|
997
|
-
this.streamingManager.subscribe((update) => this.dashboard?.handleStreamUpdate(update));
|
|
998
|
-
const outputManager = getOutputManager();
|
|
999
|
-
outputManager.setMode("tui");
|
|
1000
|
-
outputManager.setStreamingManager(this.streamingManager);
|
|
1001
|
-
await this.dashboard.start();
|
|
1002
|
-
this.streamingManager.reportWatchStatus({ watching: true, directories: 0, debounceMs });
|
|
1003
|
-
} else {
|
|
1004
|
-
getOutputManager().setMode("console");
|
|
1005
|
-
}
|
|
1006
|
-
await this.watchDirectory(directory, debounceMs);
|
|
1007
|
-
await this.watchContextGraph(directory);
|
|
1008
|
-
if (this.streamingManager) {
|
|
1009
|
-
this.streamingManager.reportWatchStatus({
|
|
1010
|
-
watching: true,
|
|
1011
|
-
directories: this.watchers.size,
|
|
1012
|
-
debounceMs
|
|
1013
|
-
});
|
|
1014
|
-
}
|
|
1015
|
-
setTimeout(() => {
|
|
1016
|
-
void this.initialGoalComplianceScan();
|
|
1017
|
-
void this.initialHypothesisGeneration();
|
|
1018
|
-
void this.syncPipelineIntegrations();
|
|
1019
|
-
}, 1e3);
|
|
1020
|
-
this.pipelineSyncTimer = setInterval(() => {
|
|
1021
|
-
void this.syncPipelineIntegrations();
|
|
1022
|
-
}, _TrieWatchTool.PIPELINE_SYNC_INTERVAL_MS);
|
|
1023
|
-
const indexStatus = this.codebaseIndex?.isEmpty() ? "BUILDING (one-time, speeds up goal checks)" : "READY";
|
|
1024
|
-
return {
|
|
1025
|
-
content: [{
|
|
1026
|
-
type: "text",
|
|
1027
|
-
text: `**TRIE AGENT ACTIVATED**
|
|
1028
|
-
|
|
1029
|
-
Your Trie agent is now autonomously watching and learning from your codebase.
|
|
1030
|
-
|
|
1031
|
-
**Watching:** \`${directory}\`
|
|
1032
|
-
**Debounce:** ${debounceMs}ms (waits for you to stop typing)
|
|
1033
|
-
**Signal Extraction:** ${process.env.ANTHROPIC_API_KEY ? "ENABLED" : "LIMITED (set ANTHROPIC_API_KEY for full extraction)"}
|
|
1034
|
-
**Codebase Index:** ${indexStatus}
|
|
1035
|
-
|
|
1036
|
-
### How the agent works:
|
|
1037
|
-
1. You write/edit code
|
|
1038
|
-
2. Agent detects the change
|
|
1039
|
-
3. Extracts governance, facts, blockers -> stores in ledger
|
|
1040
|
-
4. Predicts risks based on historical patterns
|
|
1041
|
-
5. Nudges you if something looks risky
|
|
1042
|
-
|
|
1043
|
-
### The agent learns:
|
|
1044
|
-
- Every commit builds the governance ledger
|
|
1045
|
-
- \`trie gotcha\` queries the ledger for predictions
|
|
1046
|
-
- \`trie ok\` / \`trie bad\` teach the agent what matters
|
|
1047
|
-
|
|
1048
|
-
### Commands:
|
|
1049
|
-
- \`trie_watch status\` - See agent status
|
|
1050
|
-
- \`trie_watch stop\` - Stop the agent
|
|
1051
|
-
- \`trie_index status\` - Check codebase index`
|
|
1052
|
-
}]
|
|
1053
|
-
};
|
|
1054
|
-
}
|
|
1055
|
-
shouldSkipPath(filePath) {
|
|
1056
|
-
const parts = filePath.split("/");
|
|
1057
|
-
return parts.some((p) => SKIP_DIRS.has(p) || p.startsWith(".") && p !== ".");
|
|
1058
|
-
}
|
|
1059
|
-
async watchDirectory(dir, debounceMs) {
|
|
1060
|
-
if (!existsSync2(dir)) return;
|
|
1061
|
-
try {
|
|
1062
|
-
const dirStat = await stat(dir);
|
|
1063
|
-
if (!dirStat.isDirectory()) return;
|
|
1064
|
-
const watcher = watch(dir, { persistent: true, recursive: true }, (_eventType, filename) => {
|
|
1065
|
-
if (!filename) return;
|
|
1066
|
-
if (this.shouldSkipPath(filename)) return;
|
|
1067
|
-
const ext = extname2(filename).toLowerCase();
|
|
1068
|
-
if (!WATCH_EXTENSIONS.has(ext)) return;
|
|
1069
|
-
const fullPath = join2(dir, filename);
|
|
1070
|
-
if (!existsSync2(fullPath)) return;
|
|
1071
|
-
this.state.pendingFiles.add(fullPath);
|
|
1072
|
-
if (this.state.scanDebounceTimer) {
|
|
1073
|
-
clearTimeout(this.state.scanDebounceTimer);
|
|
1074
|
-
}
|
|
1075
|
-
this.state.scanDebounceTimer = setTimeout(() => {
|
|
1076
|
-
this.processPendingFiles();
|
|
1077
|
-
}, debounceMs);
|
|
1078
|
-
});
|
|
1079
|
-
watcher.on("error", (err) => {
|
|
1080
|
-
if (!isInteractiveMode()) {
|
|
1081
|
-
console.error(`[!] Watcher error: ${err.message}`);
|
|
1082
|
-
}
|
|
1083
|
-
watcher.close();
|
|
1084
|
-
this.watchers.delete(dir);
|
|
1085
|
-
});
|
|
1086
|
-
this.watchers.set(dir, watcher);
|
|
1087
|
-
if (this.streamingManager) {
|
|
1088
|
-
this.streamingManager.reportWatchStatus({
|
|
1089
|
-
watching: true,
|
|
1090
|
-
directories: 1,
|
|
1091
|
-
debounceMs
|
|
1092
|
-
});
|
|
1093
|
-
}
|
|
1094
|
-
} catch {
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
/**
|
|
1098
|
-
* Watch .trie/context.json for incident changes
|
|
1099
|
-
*/
|
|
1100
|
-
async watchContextGraph(directory) {
|
|
1101
|
-
const contextPath = join2(getTrieDirectory(directory), "context.json");
|
|
1102
|
-
if (!existsSync2(contextPath)) {
|
|
1103
|
-
console.debug("[Watch] No context.json found, skipping incident watcher");
|
|
1104
|
-
return;
|
|
1105
|
-
}
|
|
1106
|
-
try {
|
|
1107
|
-
const watcher = watch(contextPath, { persistent: true }, async (_eventType, _filename) => {
|
|
1108
|
-
console.debug("[Watch] context.json changed, reloading incidents...");
|
|
1109
|
-
await this.reloadIncidents(directory);
|
|
1110
|
-
});
|
|
1111
|
-
watcher.on("error", (err) => {
|
|
1112
|
-
console.error(`[Watch] Context watcher error: ${err.message}`);
|
|
1113
|
-
watcher.close();
|
|
1114
|
-
});
|
|
1115
|
-
this.watchers.set("context.json", watcher);
|
|
1116
|
-
console.debug("[Watch] \u2713 Watching context.json for incident changes");
|
|
1117
|
-
} catch (error) {
|
|
1118
|
-
console.error("[Watch] Failed to set up context.json watcher:", error);
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
/**
|
|
1122
|
-
* Reload incidents from context graph
|
|
1123
|
-
*/
|
|
1124
|
-
async reloadIncidents(directory) {
|
|
1125
|
-
try {
|
|
1126
|
-
const graph = new ContextGraph(directory);
|
|
1127
|
-
const allNodes = await graph.listNodes();
|
|
1128
|
-
const unresolvedIncidents = allNodes.filter(
|
|
1129
|
-
(n) => n.type === "incident" && !n.data.resolved
|
|
1130
|
-
);
|
|
1131
|
-
this.state.nudges = this.state.nudges.filter((n) => !n.message.startsWith("INCIDENT:"));
|
|
1132
|
-
for (const incident of unresolvedIncidents) {
|
|
1133
|
-
const incidentData = incident.data;
|
|
1134
|
-
let mappedSeverity = "high";
|
|
1135
|
-
if (incidentData.severity === "critical") {
|
|
1136
|
-
mappedSeverity = "critical";
|
|
1137
|
-
} else if (incidentData.severity === "major") {
|
|
1138
|
-
mappedSeverity = "critical";
|
|
1139
|
-
} else if (incidentData.severity === "minor") {
|
|
1140
|
-
mappedSeverity = "high";
|
|
1141
|
-
}
|
|
1142
|
-
const edges = await graph.getEdges(incident.id);
|
|
1143
|
-
const linkedFiles = edges.filter((e) => e.type === "causedBy" || e.type === "affects").map((e) => {
|
|
1144
|
-
const node = allNodes.find((n) => n.id === e.to_id || n.id === e.from_id);
|
|
1145
|
-
if (node?.type === "file") {
|
|
1146
|
-
return node.data.path;
|
|
1147
|
-
}
|
|
1148
|
-
return null;
|
|
1149
|
-
}).filter((f) => f !== null);
|
|
1150
|
-
const file = linkedFiles[0] || "unknown";
|
|
1151
|
-
const message = `INCIDENT: ${incidentData.description}`;
|
|
1152
|
-
this.state.nudges.push({
|
|
1153
|
-
file,
|
|
1154
|
-
message,
|
|
1155
|
-
severity: mappedSeverity,
|
|
1156
|
-
timestamp: new Date(incidentData.timestamp).toLocaleTimeString("en-US", { hour12: false })
|
|
1157
|
-
});
|
|
1158
|
-
}
|
|
1159
|
-
console.log(`[Watch] \u2713 Reloaded ${unresolvedIncidents.length} unresolved incidents`);
|
|
1160
|
-
} catch (error) {
|
|
1161
|
-
console.error("[Watch] Failed to reload incidents:", error);
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
async processPendingFiles() {
|
|
1165
|
-
if (this.state.pendingFiles.size === 0) return;
|
|
1166
|
-
const files = Array.from(this.state.pendingFiles);
|
|
1167
|
-
this.state.pendingFiles.clear();
|
|
1168
|
-
if (!isInteractiveMode()) {
|
|
1169
|
-
console.error(`
|
|
1170
|
-
Detected changes in ${files.length} file(s):`);
|
|
1171
|
-
for (const file of files) {
|
|
1172
|
-
console.error(` - ${basename2(file)}`);
|
|
1173
|
-
}
|
|
1174
|
-
console.error("");
|
|
1175
|
-
}
|
|
1176
|
-
try {
|
|
1177
|
-
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
1178
|
-
if (this.extractionPipeline) {
|
|
1179
|
-
try {
|
|
1180
|
-
const fileContents = await Promise.all(
|
|
1181
|
-
files.map(async (file) => {
|
|
1182
|
-
try {
|
|
1183
|
-
const content = await readFile2(file, "utf-8");
|
|
1184
|
-
return { file, content };
|
|
1185
|
-
} catch {
|
|
1186
|
-
return null;
|
|
1187
|
-
}
|
|
1188
|
-
})
|
|
1189
|
-
);
|
|
1190
|
-
const validFiles = fileContents.filter((f) => f !== null);
|
|
1191
|
-
if (validFiles.length > 0) {
|
|
1192
|
-
const combinedContent = validFiles.map(
|
|
1193
|
-
(f) => `File: ${basename2(f.file)}
|
|
1194
|
-
${f.content.slice(0, 1e3)}`
|
|
1195
|
-
// First 1KB of each file
|
|
1196
|
-
).join("\n\n---\n\n");
|
|
1197
|
-
if (!isInteractiveMode()) {
|
|
1198
|
-
console.error("[*] Extracting signals from changes...");
|
|
1199
|
-
}
|
|
1200
|
-
const signal = await this.extractionPipeline.process(combinedContent, {
|
|
1201
|
-
sourceType: "file",
|
|
1202
|
-
sourceId: `watch-${Date.now()}`
|
|
1203
|
-
});
|
|
1204
|
-
if (signal.governance.length > 0 || signal.facts.length > 0 || signal.blockers.length > 0) {
|
|
1205
|
-
const govCount = signal.governance.length;
|
|
1206
|
-
if (!isInteractiveMode()) {
|
|
1207
|
-
console.error(` [+] Extracted: ${govCount} governance, ${signal.facts.length} facts, ${signal.blockers.length} blockers`);
|
|
1208
|
-
}
|
|
1209
|
-
if (this.streamingManager) {
|
|
1210
|
-
this.streamingManager.reportSignalExtraction({
|
|
1211
|
-
governance: govCount,
|
|
1212
|
-
facts: signal.facts.length,
|
|
1213
|
-
blockers: signal.blockers.length,
|
|
1214
|
-
questions: signal.questions.length
|
|
1215
|
-
});
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
} catch (error) {
|
|
1220
|
-
if (!isInteractiveMode()) {
|
|
1221
|
-
console.error(` [!] Signal extraction failed: ${error}`);
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
void this.autoScanFiles(files).catch((err) => {
|
|
1226
|
-
getOutputManager().log("warn", `Auto scan failed: ${err}`);
|
|
1227
|
-
});
|
|
1228
|
-
await this.discoverPatternsFromIssues(projectPath);
|
|
1229
|
-
const now = Date.now();
|
|
1230
|
-
if (now - this.lastHypothesisCheck > _TrieWatchTool.HYPOTHESIS_CHECK_INTERVAL_MS) {
|
|
1231
|
-
this.checkAndGenerateHypotheses(projectPath);
|
|
1232
|
-
this.lastHypothesisCheck = now;
|
|
1233
|
-
}
|
|
1234
|
-
if (this.streamingManager) {
|
|
1235
|
-
const now2 = Date.now();
|
|
1236
|
-
for (const file of files) {
|
|
1237
|
-
const lastReport = this.state.lastScan.get(file) || 0;
|
|
1238
|
-
if (now2 - lastReport > 2e3) {
|
|
1239
|
-
this.streamingManager.reportWatchChange(file);
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
this.state.filesScanned += files.length;
|
|
1244
|
-
for (const file of files) {
|
|
1245
|
-
this.state.lastScan.set(file, Date.now());
|
|
1246
|
-
if (this.codebaseIndex) {
|
|
1247
|
-
const relativePath = file.replace(projectPath + "/", "");
|
|
1248
|
-
void this.codebaseIndex.indexFile(relativePath).then(() => {
|
|
1249
|
-
void this.codebaseIndex?.save();
|
|
1250
|
-
});
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
} catch (error) {
|
|
1254
|
-
if (!isInteractiveMode()) {
|
|
1255
|
-
console.error(`Scan error: ${error}`);
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
isQuiet() {
|
|
1260
|
-
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
1261
|
-
const quietPath = join2(getTrieDirectory(projectPath), "quiet.json");
|
|
1262
|
-
try {
|
|
1263
|
-
const raw = readFileSync(quietPath, "utf-8");
|
|
1264
|
-
const data = JSON.parse(raw);
|
|
1265
|
-
const until = new Date(data.until).getTime();
|
|
1266
|
-
return Date.now() < until;
|
|
1267
|
-
} catch {
|
|
1268
|
-
return false;
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
/**
|
|
1272
|
-
* Check and generate hypotheses autonomously
|
|
1273
|
-
* Claude observes patterns and creates new hypotheses to test
|
|
1274
|
-
*/
|
|
1275
|
-
async checkAndGenerateHypotheses(projectPath) {
|
|
1276
|
-
if (!isAIAvailable()) return;
|
|
1277
|
-
try {
|
|
1278
|
-
const { getHypothesisEngine } = await import("./hypothesis-4UPE7KXU.js");
|
|
1279
|
-
const { getOutputManager: getOutputManager2 } = await import("./output-manager-DZO5LGSG.js");
|
|
1280
|
-
const hypothesisEngine = getHypothesisEngine(projectPath);
|
|
1281
|
-
const recentIssues = Array.from(this.state.issueCache.values()).flat();
|
|
1282
|
-
const patterns = [];
|
|
1283
|
-
const observations = [];
|
|
1284
|
-
if (this.state.nudges.length > 0) {
|
|
1285
|
-
const nudgesByFile = {};
|
|
1286
|
-
for (const nudge of this.state.nudges) {
|
|
1287
|
-
nudgesByFile[nudge.file] = (nudgesByFile[nudge.file] || 0) + 1;
|
|
1288
|
-
}
|
|
1289
|
-
const topFiles = Object.entries(nudgesByFile).sort(([, a], [, b]) => b - a).slice(0, 3);
|
|
1290
|
-
if (topFiles[0] && topFiles[0][1] > 2) {
|
|
1291
|
-
observations.push(`File ${topFiles[0][0]} has ${topFiles[0][1]} repeated issues this session`);
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
if (this.state.nudges.length > 5) {
|
|
1295
|
-
observations.push(`High issue detection rate: ${this.state.nudges.length} violations detected in watch session`);
|
|
1296
|
-
}
|
|
1297
|
-
let generated = await hypothesisEngine.generateHypothesesWithAI({
|
|
1298
|
-
recentIssues,
|
|
1299
|
-
patterns,
|
|
1300
|
-
observations
|
|
1301
|
-
});
|
|
1302
|
-
if (generated.length === 0) {
|
|
1303
|
-
generated = await hypothesisEngine.autoGenerateHypotheses();
|
|
1304
|
-
}
|
|
1305
|
-
for (const hypothesis of generated) {
|
|
1306
|
-
const message = `[New Hypothesis] "${hypothesis.statement}" (${Math.round(hypothesis.confidence * 100)}% confidence)`;
|
|
1307
|
-
getOutputManager2().nudge(
|
|
1308
|
-
message,
|
|
1309
|
-
"info",
|
|
1310
|
-
void 0,
|
|
1311
|
-
1e4
|
|
1312
|
-
);
|
|
1313
|
-
if (!isInteractiveMode()) {
|
|
1314
|
-
console.error(`
|
|
1315
|
-
[?] ${message}`);
|
|
1316
|
-
console.error(` Test: ${hypothesis.testCriteria || "Collecting evidence..."}`);
|
|
1317
|
-
}
|
|
1318
|
-
if (this.streamingManager) {
|
|
1319
|
-
this.streamingManager.reportSignalExtraction({
|
|
1320
|
-
governance: 0,
|
|
1321
|
-
facts: 0,
|
|
1322
|
-
blockers: 0,
|
|
1323
|
-
questions: 1
|
|
1324
|
-
// Hypotheses are questions to answer
|
|
1325
|
-
});
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
if (recentIssues.length > 10) {
|
|
1329
|
-
await hypothesisEngine.updateConfidenceFromOutcomes();
|
|
1330
|
-
}
|
|
1331
|
-
} catch (error) {
|
|
1332
|
-
if (!isInteractiveMode()) {
|
|
1333
|
-
console.error(` [!] Hypothesis check failed: ${error}`);
|
|
1334
|
-
}
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
/**
|
|
1338
|
-
* Discover patterns from accumulated issues
|
|
1339
|
-
* Patterns emerge naturally from your coding workflow
|
|
1340
|
-
*/
|
|
1341
|
-
async discoverPatternsFromIssues(projectPath) {
|
|
1342
|
-
const totalIssues = Array.from(this.state.issueCache.values()).flat().length;
|
|
1343
|
-
if (totalIssues < 5) return;
|
|
1344
|
-
try {
|
|
1345
|
-
const { ContextGraph: ContextGraph2 } = await import("./graph-26JPZ3DF.js");
|
|
1346
|
-
const { IncidentIndex } = await import("./incident-index-H6APJ4S3.js");
|
|
1347
|
-
const { TriePatternDiscovery } = await import("./pattern-discovery-F7LU5K6E.js");
|
|
1348
|
-
const graph = new ContextGraph2(projectPath);
|
|
1349
|
-
const incidentIndex = await IncidentIndex.build(graph, projectPath);
|
|
1350
|
-
const discovery = new TriePatternDiscovery(graph, incidentIndex);
|
|
1351
|
-
const hotPatterns = discovery.discoverHotPatterns(2);
|
|
1352
|
-
for (const hot of hotPatterns) {
|
|
1353
|
-
const existingPatterns = await graph.listNodes();
|
|
1354
|
-
const alreadyExists = existingPatterns.some(
|
|
1355
|
-
(n) => n.type === "pattern" && n.data.description?.includes(hot.path)
|
|
1356
|
-
);
|
|
1357
|
-
if (!alreadyExists) {
|
|
1358
|
-
await graph.addNode("pattern", {
|
|
1359
|
-
description: `${hot.type === "directory" ? "Directory" : "File"} hot zone: ${hot.path}`,
|
|
1360
|
-
appliesTo: [hot.path],
|
|
1361
|
-
confidence: Math.min(0.95, hot.confidence),
|
|
1362
|
-
occurrences: hot.incidentCount,
|
|
1363
|
-
firstSeen: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1364
|
-
lastSeen: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1365
|
-
isAntiPattern: hot.incidentCount >= 3,
|
|
1366
|
-
// 3+ incidents = anti-pattern
|
|
1367
|
-
source: "local"
|
|
1368
|
-
});
|
|
1369
|
-
if (!isInteractiveMode()) {
|
|
1370
|
-
console.error(` [+] Pattern discovered: ${hot.path} (${hot.incidentCount} issues)`);
|
|
1371
|
-
}
|
|
1372
|
-
if (this.streamingManager) {
|
|
1373
|
-
this.streamingManager.reportSignalExtraction({
|
|
1374
|
-
governance: 0,
|
|
1375
|
-
facts: 1,
|
|
1376
|
-
// Patterns are facts about the codebase
|
|
1377
|
-
blockers: 0,
|
|
1378
|
-
questions: 0
|
|
1379
|
-
});
|
|
1380
|
-
}
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
if (totalIssues >= 10) {
|
|
1384
|
-
const coOccurrences = await discovery.discoverCoOccurrences(2);
|
|
1385
|
-
for (const coOcc of coOccurrences.slice(0, 3)) {
|
|
1386
|
-
const desc = `Files break together: ${coOcc.files[0]} + ${coOcc.files[1]}`;
|
|
1387
|
-
const existingPatterns = await graph.listNodes();
|
|
1388
|
-
const alreadyExists = existingPatterns.some(
|
|
1389
|
-
(n) => n.type === "pattern" && n.data.description === desc
|
|
1390
|
-
);
|
|
1391
|
-
if (!alreadyExists) {
|
|
1392
|
-
await graph.addNode("pattern", {
|
|
1393
|
-
description: desc,
|
|
1394
|
-
appliesTo: [...coOcc.files],
|
|
1395
|
-
confidence: Math.min(0.95, coOcc.confidence),
|
|
1396
|
-
occurrences: coOcc.coOccurrences,
|
|
1397
|
-
firstSeen: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1398
|
-
lastSeen: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1399
|
-
isAntiPattern: coOcc.confidence > 0.7,
|
|
1400
|
-
source: "local"
|
|
1401
|
-
});
|
|
1402
|
-
if (!isInteractiveMode()) {
|
|
1403
|
-
console.error(` [+] Co-occurrence pattern: ${coOcc.files[0]} + ${coOcc.files[1]}`);
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
} catch (error) {
|
|
1409
|
-
if (!isInteractiveMode()) {
|
|
1410
|
-
console.error(` [!] Pattern discovery failed: ${error}`);
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
// Defaults -- overridden by config when loaded
|
|
1415
|
-
aiWatcherCooldownMs = 3e4;
|
|
1416
|
-
cleanFileCooldownMs = 3e5;
|
|
1417
|
-
maxFilesPerScan = 5;
|
|
1418
|
-
maxCharsPerFile = 4e3;
|
|
1419
|
-
/**
|
|
1420
|
-
* Use the trie (context graph) to score how urgently a file needs scanning.
|
|
1421
|
-
* Higher score = more worth spending tokens on.
|
|
1422
|
-
*/
|
|
1423
|
-
async scoreScanPriority(file, graph, projectPath) {
|
|
1424
|
-
let score = 1;
|
|
1425
|
-
const lastClean = this.state.cleanFiles.get(file);
|
|
1426
|
-
if (lastClean && Date.now() - lastClean < this.cleanFileCooldownMs) {
|
|
1427
|
-
return 0;
|
|
1428
|
-
}
|
|
1429
|
-
const fileNode = await graph.getNode("file", join2(projectPath, file));
|
|
1430
|
-
if (!fileNode) return score;
|
|
1431
|
-
const data = fileNode.data;
|
|
1432
|
-
const riskScores = { critical: 10, high: 6, medium: 2, low: 1 };
|
|
1433
|
-
score += riskScores[data.riskLevel] ?? 1;
|
|
1434
|
-
score += Math.min(data.incidentCount * 3, 12);
|
|
1435
|
-
if (data.changeCount > 10) score += 2;
|
|
1436
|
-
return score;
|
|
1437
|
-
}
|
|
1438
|
-
/**
|
|
1439
|
-
* Roll the token budget window if an hour has passed.
|
|
1440
|
-
* Returns remaining tokens in the current window.
|
|
1441
|
-
*/
|
|
1442
|
-
getRemainingBudget() {
|
|
1443
|
-
const budget = this.state.tokenBudget;
|
|
1444
|
-
const elapsed = Date.now() - budget.windowStart;
|
|
1445
|
-
if (elapsed > 36e5) {
|
|
1446
|
-
budget.used = 0;
|
|
1447
|
-
budget.windowStart = Date.now();
|
|
1448
|
-
}
|
|
1449
|
-
return Math.max(0, budget.hourlyLimit - budget.used);
|
|
1450
|
-
}
|
|
1451
|
-
recordTokenUsage(tokens) {
|
|
1452
|
-
this.state.tokenBudget.used += tokens;
|
|
1453
|
-
}
|
|
1454
|
-
/**
|
|
1455
|
-
* AI-powered watcher -- the primary detection system.
|
|
1456
|
-
*
|
|
1457
|
-
* This is the single AI call that handles:
|
|
1458
|
-
* 1. Code review (bugs, security, logic errors)
|
|
1459
|
-
* 2. Goal violation detection (user-defined quality goals)
|
|
1460
|
-
*
|
|
1461
|
-
* When goals are active, files with goal violations bypass priority scoring
|
|
1462
|
-
* so violations are always caught. Throttled by cooldown + token budget.
|
|
1463
|
-
*/
|
|
1464
|
-
async autoScanFiles(files) {
|
|
1465
|
-
if (!isAIAvailable()) return;
|
|
1466
|
-
if (this.state.autoScanInProgress) return;
|
|
1467
|
-
const now = Date.now();
|
|
1468
|
-
if (now - this.state.lastAutoScan < this.aiWatcherCooldownMs) return;
|
|
1469
|
-
this.state.autoScanInProgress = true;
|
|
1470
|
-
this.state.lastAutoScan = now;
|
|
1471
|
-
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
1472
|
-
try {
|
|
1473
|
-
try {
|
|
1474
|
-
const config = await getAutonomyConfig(projectPath);
|
|
1475
|
-
const wc = config.aiWatcher;
|
|
1476
|
-
if (!wc.enabled) return;
|
|
1477
|
-
this.state.tokenBudget.hourlyLimit = wc.hourlyTokenLimit;
|
|
1478
|
-
this.aiWatcherCooldownMs = wc.scanCooldownSec * 1e3;
|
|
1479
|
-
this.cleanFileCooldownMs = wc.cleanFileCooldownSec * 1e3;
|
|
1480
|
-
this.maxFilesPerScan = wc.maxFilesPerScan;
|
|
1481
|
-
this.maxCharsPerFile = wc.maxCharsPerFile;
|
|
1482
|
-
} catch {
|
|
1483
|
-
}
|
|
1484
|
-
const remaining = this.getRemainingBudget();
|
|
1485
|
-
if (remaining < 500) return;
|
|
1486
|
-
try {
|
|
1487
|
-
const graph = new ContextGraph(projectPath);
|
|
1488
|
-
const { getActiveGoals, recordGoalViolationCaught } = await import("./goal-validator-EM5XVWVC.js");
|
|
1489
|
-
console.debug("[AI Watcher] Loading active goals...");
|
|
1490
|
-
const activeGoals = await getActiveGoals(projectPath);
|
|
1491
|
-
const hasGoals = activeGoals.length > 0;
|
|
1492
|
-
console.debug("[AI Watcher] Goals loaded:", {
|
|
1493
|
-
totalGoals: activeGoals.length,
|
|
1494
|
-
hasGoals,
|
|
1495
|
-
goals: activeGoals.map((g) => ({ id: g.id, description: g.description, status: g.status }))
|
|
1496
|
-
});
|
|
1497
|
-
if (this.isQuiet() && !hasGoals) return;
|
|
1498
|
-
const scored = [];
|
|
1499
|
-
for (const file of files) {
|
|
1500
|
-
const relativePath = file.replace(projectPath + "/", "");
|
|
1501
|
-
const score = await this.scoreScanPriority(relativePath, graph, projectPath);
|
|
1502
|
-
if (hasGoals || score > 0) {
|
|
1503
|
-
scored.push({ file, relativePath, score: Math.max(score, hasGoals ? 1 : 0) });
|
|
1504
|
-
} else {
|
|
1505
|
-
this.state.tokenBudget.scansSaved++;
|
|
1506
|
-
}
|
|
1507
|
-
}
|
|
1508
|
-
if (scored.length === 0) return;
|
|
1509
|
-
scored.sort((a, b) => b.score - a.score);
|
|
1510
|
-
const budgetScale = remaining > 2e4 ? 1 : remaining > 1e4 ? 0.6 : 0.4;
|
|
1511
|
-
const maxFiles = Math.max(1, Math.round(this.maxFilesPerScan * budgetScale));
|
|
1512
|
-
const filesToScan = scored.slice(0, maxFiles);
|
|
1513
|
-
const charLimit = Math.round(this.maxCharsPerFile * (remaining > 15e3 ? 1 : 0.5));
|
|
1514
|
-
const fileContents = await Promise.all(
|
|
1515
|
-
filesToScan.map(async ({ file, relativePath }) => {
|
|
1516
|
-
try {
|
|
1517
|
-
const content = await readFile2(file, "utf-8");
|
|
1518
|
-
return { path: relativePath, content: content.slice(0, charLimit) };
|
|
1519
|
-
} catch {
|
|
1520
|
-
return null;
|
|
1521
|
-
}
|
|
1522
|
-
})
|
|
1523
|
-
);
|
|
1524
|
-
const valid = fileContents.filter(Boolean);
|
|
1525
|
-
if (valid.length === 0) return;
|
|
1526
|
-
const filesBlock = valid.map(
|
|
1527
|
-
(f) => `### ${f.path}
|
|
1528
|
-
\`\`\`
|
|
1529
|
-
${f.content}
|
|
1530
|
-
\`\`\``
|
|
1531
|
-
).join("\n\n");
|
|
1532
|
-
let goalsSection = "";
|
|
1533
|
-
if (hasGoals) {
|
|
1534
|
-
goalsSection = `
|
|
1535
|
-
USER-DEFINED GOALS (IMPORTANT - check EVERY file against ALL goals):
|
|
1536
|
-
${activeGoals.map((g, i) => ` ${i + 1}. "${g.description}"`).join("\n")}
|
|
1537
|
-
|
|
1538
|
-
Goal violations are HIGH PRIORITY. If a file violates any goal, you MUST report it.
|
|
1539
|
-
`;
|
|
1540
|
-
}
|
|
1541
|
-
console.debug("[AI Watcher] Sending files to AI analysis:", {
|
|
1542
|
-
fileCount: valid.length,
|
|
1543
|
-
hasGoals,
|
|
1544
|
-
goalsIncluded: hasGoals,
|
|
1545
|
-
filePaths: valid.map((f) => f.path),
|
|
1546
|
-
goalsSection: goalsSection.slice(0, 200) + (goalsSection.length > 200 ? "..." : "")
|
|
1547
|
-
});
|
|
1548
|
-
const result = await runAIAnalysis({
|
|
1549
|
-
systemPrompt: `You are a code quality watcher. You review code for two things:
|
|
1550
|
-
|
|
1551
|
-
1. CODE ISSUES: bugs, security vulnerabilities, logic errors, risky patterns
|
|
1552
|
-
2. GOAL VIOLATIONS: check every file against the user's quality goals
|
|
1553
|
-
${goalsSection}
|
|
1554
|
-
Reply ONLY with a JSON array. Each element must have:
|
|
1555
|
-
- "file": relative file path
|
|
1556
|
-
- "severity": "critical" | "major" | "minor"
|
|
1557
|
-
- "description": 1-sentence description of what you found
|
|
1558
|
-
- "confidence": number 0-100, how confident you are this is a real issue
|
|
1559
|
-
- "suggestedFix": 1-sentence description of what should change to fix it
|
|
1560
|
-
- "isGoalViolation": true if this violates a user goal, false otherwise
|
|
1561
|
-
- "goalIndex": 0-based index of the violated goal (only if isGoalViolation is true)
|
|
1562
|
-
|
|
1563
|
-
Be thorough with goal checking. If a goal says "no emojis" and you see an emoji anywhere in the file, report it with the exact location. If a goal says "no inline styles" and you see a style attribute, report it.
|
|
1564
|
-
|
|
1565
|
-
If no issues or violations found, reply with: []
|
|
1566
|
-
Output ONLY the JSON array, no markdown fences, no commentary.`,
|
|
1567
|
-
userPrompt: `Review these changed files:
|
|
1568
|
-
|
|
1569
|
-
${filesBlock}`,
|
|
1570
|
-
maxTokens: 2048,
|
|
1571
|
-
temperature: 0.1
|
|
1572
|
-
});
|
|
1573
|
-
if (result.tokensUsed) {
|
|
1574
|
-
this.recordTokenUsage(result.tokensUsed.input + result.tokensUsed.output);
|
|
1575
|
-
}
|
|
1576
|
-
console.debug("[AI Watcher] AI analysis result:", {
|
|
1577
|
-
success: result.success,
|
|
1578
|
-
contentLength: result.content ? result.content.length : 0,
|
|
1579
|
-
tokensUsed: result.tokensUsed,
|
|
1580
|
-
contentPreview: result.content ? result.content.slice(0, 500) : "null"
|
|
1581
|
-
});
|
|
1582
|
-
if (!result.success || !result.content.trim()) {
|
|
1583
|
-
console.debug("[AI Watcher] AI analysis failed or returned empty content");
|
|
1584
|
-
return;
|
|
1585
|
-
}
|
|
1586
|
-
let issues = [];
|
|
1587
|
-
try {
|
|
1588
|
-
const cleaned = result.content.replace(/```json?\n?|\n?```/g, "").trim();
|
|
1589
|
-
console.debug("[AI Watcher] Parsing AI response:", { cleanedContent: cleaned.slice(0, 300) });
|
|
1590
|
-
issues = JSON.parse(cleaned);
|
|
1591
|
-
if (!Array.isArray(issues)) issues = [];
|
|
1592
|
-
console.debug("[AI Watcher] Parsed issues:", {
|
|
1593
|
-
totalIssues: issues.length,
|
|
1594
|
-
goalViolations: issues.filter((i) => i.isGoalViolation).length,
|
|
1595
|
-
issueTypes: issues.map((i) => ({ file: i.file, isGoalViolation: i.isGoalViolation, goalIndex: i.goalIndex }))
|
|
1596
|
-
});
|
|
1597
|
-
} catch (error) {
|
|
1598
|
-
console.debug("[AI Watcher] Failed to parse AI response:", error);
|
|
1599
|
-
return;
|
|
1600
|
-
}
|
|
1601
|
-
const issuedFiles = new Set(issues.map((i) => i.file));
|
|
1602
|
-
for (const { relativePath } of filesToScan) {
|
|
1603
|
-
if (!issuedFiles.has(relativePath)) {
|
|
1604
|
-
this.state.cleanFiles.set(relativePath, Date.now());
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1607
|
-
if (issues.length === 0) return;
|
|
1608
|
-
for (const issue of issues.slice(0, 10)) {
|
|
1609
|
-
const severity = issue.severity === "critical" ? "critical" : issue.severity === "major" ? "major" : "minor";
|
|
1610
|
-
if (issue.isGoalViolation && issue.goalIndex != null && issue.goalIndex >= 0 && issue.goalIndex < activeGoals.length) {
|
|
1611
|
-
const goal = activeGoals[issue.goalIndex];
|
|
1612
|
-
if (!goal) continue;
|
|
1613
|
-
const confidence = Math.min(100, Math.max(0, issue.confidence ?? 80));
|
|
1614
|
-
const fixId = `fix-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1615
|
-
console.debug("[AI Watcher] Goal violation detected:", {
|
|
1616
|
-
goalDescription: goal.description,
|
|
1617
|
-
file: issue.file,
|
|
1618
|
-
violation: issue.description,
|
|
1619
|
-
confidence,
|
|
1620
|
-
goalIndex: issue.goalIndex,
|
|
1621
|
-
totalActiveGoals: activeGoals.length
|
|
1622
|
-
});
|
|
1623
|
-
const goalViolationIssue = {
|
|
1624
|
-
id: fixId,
|
|
1625
|
-
file: issue.file,
|
|
1626
|
-
agent: "goal-violation",
|
|
1627
|
-
severity: severity === "critical" ? "critical" : severity === "major" ? "serious" : "moderate",
|
|
1628
|
-
issue: `Goal "${goal.description}" violated: ${issue.description}`,
|
|
1629
|
-
fix: issue.suggestedFix || "Review and fix",
|
|
1630
|
-
category: "goal-violation",
|
|
1631
|
-
confidence,
|
|
1632
|
-
autoFixable: true
|
|
1633
|
-
};
|
|
1634
|
-
await storeIssues([goalViolationIssue], basename2(projectPath), projectPath);
|
|
1635
|
-
await recordGoalViolationCaught(goal, issue.file, projectPath);
|
|
1636
|
-
const confidenceStr = `${confidence}%`;
|
|
1637
|
-
const nudgeMsg = `Goal "${goal.description}" violated in ${issue.file}: ${issue.description} [${confidenceStr} confidence]`;
|
|
1638
|
-
console.debug("[AI Watcher] Sending nudge:", {
|
|
1639
|
-
message: nudgeMsg,
|
|
1640
|
-
file: issue.file,
|
|
1641
|
-
severity: "warning"
|
|
1642
|
-
});
|
|
1643
|
-
getOutputManager().nudge(nudgeMsg, "warning", issue.file);
|
|
1644
|
-
await this.persistNudge({
|
|
1645
|
-
message: nudgeMsg,
|
|
1646
|
-
severity: "warning",
|
|
1647
|
-
file: issue.file,
|
|
1648
|
-
category: "quality",
|
|
1649
|
-
goalId: goal.id,
|
|
1650
|
-
priority: 7,
|
|
1651
|
-
suggestedAction: issue.suggestedFix || "Review and fix manually",
|
|
1652
|
-
relatedIssues: [fixId]
|
|
1653
|
-
});
|
|
1654
|
-
if (this.streamingManager) {
|
|
1655
|
-
this.streamingManager.reportPendingFix({
|
|
1656
|
-
id: fixId,
|
|
1657
|
-
file: issue.file,
|
|
1658
|
-
description: issue.description,
|
|
1659
|
-
goalDescription: goal.description,
|
|
1660
|
-
confidence,
|
|
1661
|
-
severity: issue.severity,
|
|
1662
|
-
suggestedFix: issue.suggestedFix || "Remove the violating code"
|
|
1663
|
-
});
|
|
1664
|
-
}
|
|
1665
|
-
if (!isInteractiveMode()) {
|
|
1666
|
-
console.error(` [!] Goal violation (${confidenceStr}): ${issue.description}`);
|
|
1667
|
-
console.error(` Fix: ${issue.suggestedFix || "Review and fix manually"}`);
|
|
1668
|
-
}
|
|
1669
|
-
continue;
|
|
1670
|
-
}
|
|
1671
|
-
const incident = await graph.addNode("incident", {
|
|
1672
|
-
description: issue.description,
|
|
1673
|
-
severity,
|
|
1674
|
-
affectedUsers: null,
|
|
1675
|
-
duration: null,
|
|
1676
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1677
|
-
resolved: false,
|
|
1678
|
-
resolution: null,
|
|
1679
|
-
fixChangeId: null,
|
|
1680
|
-
reportedVia: "detected"
|
|
1681
|
-
});
|
|
1682
|
-
const filePath = join2(projectPath, issue.file);
|
|
1683
|
-
const fileNode = await graph.getNode("file", filePath);
|
|
1684
|
-
if (fileNode) {
|
|
1685
|
-
await graph.addEdge(fileNode.id, incident.id, "affects");
|
|
1686
|
-
const data = fileNode.data;
|
|
1687
|
-
const newRisk = severity === "critical" ? "critical" : severity === "major" ? "high" : data.riskLevel === "low" ? "medium" : data.riskLevel;
|
|
1688
|
-
await graph.updateNode("file", fileNode.id, {
|
|
1689
|
-
incidentCount: (data.incidentCount ?? 0) + 1,
|
|
1690
|
-
riskLevel: newRisk
|
|
1691
|
-
});
|
|
1692
|
-
}
|
|
1693
|
-
this.state.totalIssuesFound++;
|
|
1694
|
-
if (severity !== "minor") {
|
|
1695
|
-
getOutputManager().nudge(
|
|
1696
|
-
`${issue.description}`,
|
|
1697
|
-
severity === "critical" ? "critical" : "warning",
|
|
1698
|
-
issue.file,
|
|
1699
|
-
severity === "critical" ? void 0 : 15e3
|
|
1700
|
-
);
|
|
1701
|
-
await this.persistNudge({
|
|
1702
|
-
message: issue.description,
|
|
1703
|
-
severity: severity === "critical" ? "critical" : "warning",
|
|
1704
|
-
file: issue.file,
|
|
1705
|
-
category: "quality",
|
|
1706
|
-
priority: severity === "critical" ? 9 : 6,
|
|
1707
|
-
...issue.suggestedFix && { suggestedAction: issue.suggestedFix }
|
|
1708
|
-
});
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
if (this.streamingManager && issues.length > 0) {
|
|
1712
|
-
this.streamingManager.reportSignalExtraction({
|
|
1713
|
-
governance: 0,
|
|
1714
|
-
facts: 0,
|
|
1715
|
-
blockers: issues.length,
|
|
1716
|
-
questions: 0
|
|
1717
|
-
});
|
|
1718
|
-
}
|
|
1719
|
-
} catch (error) {
|
|
1720
|
-
getOutputManager().log("warn", `AI watcher error: ${error}`);
|
|
1721
|
-
}
|
|
1722
|
-
} finally {
|
|
1723
|
-
this.state.autoScanInProgress = false;
|
|
1724
|
-
}
|
|
1725
|
-
}
|
|
1726
|
-
/**
|
|
1727
|
-
* Initial hypothesis generation when watch starts.
|
|
1728
|
-
*
|
|
1729
|
-
* This generates hypotheses from existing patterns and issues to give
|
|
1730
|
-
* users immediate insights about their codebase.
|
|
1731
|
-
*/
|
|
1732
|
-
async initialHypothesisGeneration() {
|
|
1733
|
-
if (!isAIAvailable()) {
|
|
1734
|
-
console.debug("[Initial Hypothesis] AI not available, skipping initial hypothesis generation");
|
|
1735
|
-
return;
|
|
1736
|
-
}
|
|
1737
|
-
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
1738
|
-
console.debug("[Initial Hypothesis] Starting initial hypothesis generation", { projectPath });
|
|
1739
|
-
try {
|
|
1740
|
-
const { getHypothesisEngine } = await import("./hypothesis-4UPE7KXU.js");
|
|
1741
|
-
const hypothesisEngine = getHypothesisEngine(projectPath);
|
|
1742
|
-
console.debug("[Initial Hypothesis] Running AI-powered hypothesis generation...");
|
|
1743
|
-
const generated = await hypothesisEngine.generateHypothesesWithAI({
|
|
1744
|
-
recentIssues: [],
|
|
1745
|
-
// No recent issues yet on startup
|
|
1746
|
-
patterns: [],
|
|
1747
|
-
observations: ["Initial watch session started", "Analyzing codebase for potential patterns"]
|
|
1748
|
-
});
|
|
1749
|
-
console.debug("[Initial Hypothesis] Generated hypotheses on startup:", {
|
|
1750
|
-
count: generated.length,
|
|
1751
|
-
hypotheses: generated.map((h) => ({ statement: h.statement, confidence: h.confidence }))
|
|
1752
|
-
});
|
|
1753
|
-
if (generated.length > 0) {
|
|
1754
|
-
const { getOutputManager: getOutputManager2 } = await import("./output-manager-DZO5LGSG.js");
|
|
1755
|
-
const outputManager = getOutputManager2();
|
|
1756
|
-
for (const hypothesis of generated.slice(0, 2)) {
|
|
1757
|
-
const message = `[Initial Hypothesis] "${hypothesis.statement}" (${Math.round(hypothesis.confidence * 100)}% confidence)`;
|
|
1758
|
-
outputManager.nudge(message, "info", void 0, 1e4);
|
|
1759
|
-
if (!isInteractiveMode()) {
|
|
1760
|
-
console.error(` [?] ${message}`);
|
|
1761
|
-
}
|
|
1762
|
-
}
|
|
1763
|
-
if (this.streamingManager) {
|
|
1764
|
-
this.streamingManager.reportSignalExtraction({
|
|
1765
|
-
governance: 0,
|
|
1766
|
-
facts: 0,
|
|
1767
|
-
blockers: 0,
|
|
1768
|
-
questions: generated.length
|
|
1769
|
-
});
|
|
1770
|
-
}
|
|
1771
|
-
}
|
|
1772
|
-
} catch (error) {
|
|
1773
|
-
console.debug("[Initial Hypothesis] Failed to generate initial hypotheses:", error);
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
|
-
/**
|
|
1777
|
-
* Initial goal compliance scan when watch starts.
|
|
1778
|
-
*
|
|
1779
|
-
* This checks recently modified files against active goals so the user
|
|
1780
|
-
* gets immediate feedback about goal violations without waiting for files
|
|
1781
|
-
* to be changed.
|
|
1782
|
-
*
|
|
1783
|
-
* Strategy:
|
|
1784
|
-
* 1. Check if there are any active goals - if not, skip
|
|
1785
|
-
* 2. Find recently modified files (last 24 hours OR uncommitted changes)
|
|
1786
|
-
* 3. Filter to watched file types
|
|
1787
|
-
* 4. Run a quick AI scan focused only on goal violations
|
|
1788
|
-
* 5. Nudge the user about any violations found
|
|
1789
|
-
*/
|
|
1790
|
-
async initialGoalComplianceScan() {
|
|
1791
|
-
if (!isAIAvailable()) {
|
|
1792
|
-
console.debug("[Initial Scan] AI not available, skipping initial goal compliance scan");
|
|
1793
|
-
return;
|
|
1794
|
-
}
|
|
1795
|
-
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
1796
|
-
console.debug("[Initial Scan] Starting initial goal compliance scan", { projectPath });
|
|
1797
|
-
try {
|
|
1798
|
-
const { getActiveGoals, recordGoalViolationCaught } = await import("./goal-validator-EM5XVWVC.js");
|
|
1799
|
-
const activeGoals = await getActiveGoals(projectPath);
|
|
1800
|
-
console.debug("[Initial Scan] Loaded goals for initial scan:", {
|
|
1801
|
-
goalCount: activeGoals.length,
|
|
1802
|
-
goals: activeGoals.map((g) => ({ id: g.id, description: g.description }))
|
|
1803
|
-
});
|
|
1804
|
-
if (activeGoals.length === 0) {
|
|
1805
|
-
console.debug("[Initial Scan] No active goals found, skipping initial scan");
|
|
1806
|
-
return;
|
|
1807
|
-
}
|
|
1808
|
-
if (!isInteractiveMode()) {
|
|
1809
|
-
console.error("[*] Checking recent files against active goals...");
|
|
1810
|
-
}
|
|
1811
|
-
const recentFiles = /* @__PURE__ */ new Set();
|
|
1812
|
-
const uncommittedFiles = await getGitChangedFiles(projectPath);
|
|
1813
|
-
if (uncommittedFiles) {
|
|
1814
|
-
uncommittedFiles.forEach((f) => recentFiles.add(join2(projectPath, f)));
|
|
1815
|
-
}
|
|
1816
|
-
const oneDayAgo = Date.now() - 24 * 60 * 60 * 1e3;
|
|
1817
|
-
const recentChanges = await getChangedFilesSinceTimestamp(projectPath, oneDayAgo);
|
|
1818
|
-
if (recentChanges) {
|
|
1819
|
-
recentChanges.forEach((f) => recentFiles.add(f));
|
|
1820
|
-
}
|
|
1821
|
-
const filesToCheck = Array.from(recentFiles).filter((file) => {
|
|
1822
|
-
const ext = extname2(file).toLowerCase();
|
|
1823
|
-
return WATCH_EXTENSIONS.has(ext) && existsSync2(file);
|
|
1824
|
-
});
|
|
1825
|
-
console.debug("[Initial Scan] Files discovered for initial scan:", {
|
|
1826
|
-
totalRecentFiles: recentFiles.size,
|
|
1827
|
-
filteredFiles: filesToCheck.length,
|
|
1828
|
-
filePaths: filesToCheck.slice(0, 5).map((f) => f.replace(projectPath + "/", "")),
|
|
1829
|
-
// Show first 5
|
|
1830
|
-
watchedExtensions: Array.from(WATCH_EXTENSIONS)
|
|
1831
|
-
});
|
|
1832
|
-
if (filesToCheck.length === 0) {
|
|
1833
|
-
console.debug("[Initial Scan] No recent files found for initial scan");
|
|
1834
|
-
return;
|
|
1835
|
-
}
|
|
1836
|
-
const maxInitialFiles = 10;
|
|
1837
|
-
const filesToScan = filesToCheck.slice(0, maxInitialFiles);
|
|
1838
|
-
if (!isInteractiveMode()) {
|
|
1839
|
-
console.error(` Scanning ${filesToScan.length} recent file(s) against ${activeGoals.length} goal(s)...`);
|
|
1840
|
-
}
|
|
1841
|
-
const maxCharsPerFile = 3e3;
|
|
1842
|
-
const fileContents = await Promise.all(
|
|
1843
|
-
filesToScan.map(async (file) => {
|
|
1844
|
-
try {
|
|
1845
|
-
const content = await readFile2(file, "utf-8");
|
|
1846
|
-
const relativePath = file.replace(projectPath + "/", "");
|
|
1847
|
-
return { path: relativePath, content: content.slice(0, maxCharsPerFile) };
|
|
1848
|
-
} catch {
|
|
1849
|
-
return null;
|
|
1850
|
-
}
|
|
1851
|
-
})
|
|
1852
|
-
);
|
|
1853
|
-
const valid = fileContents.filter(Boolean);
|
|
1854
|
-
if (valid.length === 0) return;
|
|
1855
|
-
const filesBlock = valid.map(
|
|
1856
|
-
(f) => `### ${f.path}
|
|
1857
|
-
\`\`\`
|
|
1858
|
-
${f.content}
|
|
1859
|
-
\`\`\``
|
|
1860
|
-
).join("\n\n");
|
|
1861
|
-
const goalsSection = `
|
|
1862
|
-
USER-DEFINED GOALS (check EVERY file against ALL goals):
|
|
1863
|
-
${activeGoals.map((g, i) => ` ${i + 1}. "${g.description}"`).join("\n")}
|
|
1864
|
-
|
|
1865
|
-
This is an INITIAL SCAN at watch startup. Report ALL goal violations you find.
|
|
1866
|
-
`;
|
|
1867
|
-
const result = await runAIAnalysis({
|
|
1868
|
-
systemPrompt: `You are checking code for GOAL VIOLATIONS ONLY.
|
|
1869
|
-
${goalsSection}
|
|
1870
|
-
Reply ONLY with a JSON array. Each element must have:
|
|
1871
|
-
- "file": relative file path
|
|
1872
|
-
- "severity": "critical" | "major" | "minor"
|
|
1873
|
-
- "description": 1-sentence description of the goal violation
|
|
1874
|
-
- "confidence": number 0-100, how confident you are this is a violation
|
|
1875
|
-
- "suggestedFix": 1-sentence description of what should change
|
|
1876
|
-
- "isGoalViolation": true (always true for this scan)
|
|
1877
|
-
- "goalIndex": 0-based index of the violated goal
|
|
1878
|
-
|
|
1879
|
-
Be thorough. If a goal says "no emojis" and you see ANY emoji in the file, report it. If a goal says "no console.log" and you see console.log, report it.
|
|
1880
|
-
|
|
1881
|
-
If no violations found, reply with: []
|
|
1882
|
-
Output ONLY the JSON array, no markdown fences, no commentary.`,
|
|
1883
|
-
userPrompt: `Check these files for goal violations:
|
|
1884
|
-
|
|
1885
|
-
${filesBlock}`,
|
|
1886
|
-
maxTokens: 2048,
|
|
1887
|
-
temperature: 0.1
|
|
1888
|
-
});
|
|
1889
|
-
if (result.tokensUsed) {
|
|
1890
|
-
this.recordTokenUsage(result.tokensUsed.input + result.tokensUsed.output);
|
|
1891
|
-
}
|
|
1892
|
-
if (!result.success || !result.content.trim()) return;
|
|
1893
|
-
let issues = [];
|
|
1894
|
-
try {
|
|
1895
|
-
const parsed = JSON.parse(result.content.trim());
|
|
1896
|
-
issues = Array.isArray(parsed) ? parsed : [];
|
|
1897
|
-
} catch {
|
|
1898
|
-
return;
|
|
1899
|
-
}
|
|
1900
|
-
const issuesToStore = [];
|
|
1901
|
-
let violationsFound = 0;
|
|
1902
|
-
for (const issue of issues) {
|
|
1903
|
-
if (!issue.isGoalViolation || issue.confidence < 40) continue;
|
|
1904
|
-
violationsFound++;
|
|
1905
|
-
if (issue.goalIndex != null && issue.goalIndex >= 0 && issue.goalIndex < activeGoals.length) {
|
|
1906
|
-
const goal = activeGoals[issue.goalIndex];
|
|
1907
|
-
if (!goal) continue;
|
|
1908
|
-
const fixId = `fix-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1909
|
-
issuesToStore.push({
|
|
1910
|
-
id: fixId,
|
|
1911
|
-
file: issue.file,
|
|
1912
|
-
agent: "goal-violation",
|
|
1913
|
-
severity: issue.severity === "critical" ? "critical" : issue.severity === "major" ? "serious" : "moderate",
|
|
1914
|
-
issue: `Goal "${goal.description}" violated: ${issue.description}`,
|
|
1915
|
-
fix: issue.suggestedFix || "Review and fix",
|
|
1916
|
-
category: "goal-violation",
|
|
1917
|
-
confidence: issue.confidence,
|
|
1918
|
-
autoFixable: true
|
|
1919
|
-
});
|
|
1920
|
-
await recordGoalViolationCaught(goal, issue.file, projectPath);
|
|
1921
|
-
const confidenceStr = issue.confidence >= 80 ? "high" : issue.confidence >= 60 ? "medium" : "low";
|
|
1922
|
-
const nudgeMsg = `Goal "${goal.description}" violated in ${issue.file}: ${issue.description} [${confidenceStr} confidence]`;
|
|
1923
|
-
getOutputManager().nudge(nudgeMsg, "warning", issue.file);
|
|
1924
|
-
await this.persistNudge({
|
|
1925
|
-
message: nudgeMsg,
|
|
1926
|
-
severity: "warning",
|
|
1927
|
-
file: issue.file,
|
|
1928
|
-
category: "quality",
|
|
1929
|
-
goalId: goal.id,
|
|
1930
|
-
priority: issue.confidence >= 80 ? 8 : issue.confidence >= 60 ? 6 : 4,
|
|
1931
|
-
...issue.suggestedFix && { suggestedAction: issue.suggestedFix }
|
|
1932
|
-
});
|
|
1933
|
-
}
|
|
1934
|
-
}
|
|
1935
|
-
if (issuesToStore.length > 0) {
|
|
1936
|
-
await storeIssues(issuesToStore, basename2(projectPath), projectPath);
|
|
1937
|
-
}
|
|
1938
|
-
if (!isInteractiveMode()) {
|
|
1939
|
-
if (violationsFound > 0) {
|
|
1940
|
-
console.error(` [!] Found ${violationsFound} goal violation(s) in recent files`);
|
|
1941
|
-
} else {
|
|
1942
|
-
console.error(` [\u2713] No goal violations found in recent files`);
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1945
|
-
} catch (error) {
|
|
1946
|
-
if (!isInteractiveMode()) {
|
|
1947
|
-
console.error(` [!] Initial goal scan error: ${error}`);
|
|
1948
|
-
}
|
|
1949
|
-
}
|
|
1950
|
-
}
|
|
1951
|
-
async syncPipelineIntegrations() {
|
|
1952
|
-
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
1953
|
-
try {
|
|
1954
|
-
const config = await loadConfig();
|
|
1955
|
-
const hasLinear = !!(config.apiKeys?.linear ?? process.env.LINEAR_API_KEY);
|
|
1956
|
-
const hasGithub = !!(config.apiKeys?.github ?? process.env.GITHUB_TOKEN);
|
|
1957
|
-
if (!hasLinear && !hasGithub) return;
|
|
1958
|
-
const graph = new ContextGraph(projectPath);
|
|
1959
|
-
if (hasLinear) {
|
|
1960
|
-
try {
|
|
1961
|
-
const { LinearIngester: LinearIngester2 } = await import("./linear-ingester-WIUBWF55.js");
|
|
1962
|
-
const ingester = new LinearIngester2(projectPath, graph);
|
|
1963
|
-
await ingester.syncTickets();
|
|
1964
|
-
if (!isInteractiveMode()) {
|
|
1965
|
-
console.error("[Pipeline] Linear tickets synced");
|
|
1966
|
-
}
|
|
1967
|
-
} catch (error) {
|
|
1968
|
-
if (!isInteractiveMode()) {
|
|
1969
|
-
console.error(`[Pipeline] Linear sync failed: ${error}`);
|
|
1970
|
-
}
|
|
1971
|
-
}
|
|
1972
|
-
}
|
|
1973
|
-
if (hasGithub) {
|
|
1974
|
-
try {
|
|
1975
|
-
const { GitHubIngester: GitHubIngester2 } = await import("./github-ingester-C66ZRUYC.js");
|
|
1976
|
-
const ingester = new GitHubIngester2(graph);
|
|
1977
|
-
const token = await ingester.getApiToken();
|
|
1978
|
-
const repoInfo = ingester.getRepoInfo(projectPath);
|
|
1979
|
-
if (repoInfo) {
|
|
1980
|
-
await ingester.syncPullRequests(repoInfo.owner, repoInfo.name, token);
|
|
1981
|
-
await ingester.syncIssues(repoInfo.owner, repoInfo.name, token);
|
|
1982
|
-
if (!isInteractiveMode()) {
|
|
1983
|
-
console.error("[Pipeline] GitHub PRs/issues synced");
|
|
1984
|
-
}
|
|
1985
|
-
}
|
|
1986
|
-
} catch (error) {
|
|
1987
|
-
if (!isInteractiveMode()) {
|
|
1988
|
-
console.error(`[Pipeline] GitHub sync failed: ${error}`);
|
|
1989
|
-
}
|
|
1990
|
-
}
|
|
1991
|
-
}
|
|
1992
|
-
} catch {
|
|
1993
|
-
}
|
|
1994
|
-
}
|
|
1995
|
-
async stopWatching() {
|
|
1996
|
-
if (!this.state.isRunning) {
|
|
1997
|
-
return {
|
|
1998
|
-
content: [{
|
|
1999
|
-
type: "text",
|
|
2000
|
-
text: "[!] Watch mode is not running."
|
|
2001
|
-
}]
|
|
2002
|
-
};
|
|
2003
|
-
}
|
|
2004
|
-
for (const watcher of this.watchers.values()) {
|
|
2005
|
-
watcher.close();
|
|
2006
|
-
}
|
|
2007
|
-
this.watchers.clear();
|
|
2008
|
-
if (this.extractionPipeline) {
|
|
2009
|
-
this.extractionPipeline.close();
|
|
2010
|
-
this.extractionPipeline = null;
|
|
2011
|
-
}
|
|
2012
|
-
if (this.pipelineSyncTimer) {
|
|
2013
|
-
clearInterval(this.pipelineSyncTimer);
|
|
2014
|
-
this.pipelineSyncTimer = null;
|
|
2015
|
-
}
|
|
2016
|
-
if (this.codebaseIndex) {
|
|
2017
|
-
await this.codebaseIndex.save();
|
|
2018
|
-
this.codebaseIndex = null;
|
|
2019
|
-
}
|
|
2020
|
-
if (this.state.scanDebounceTimer) {
|
|
2021
|
-
clearTimeout(this.state.scanDebounceTimer);
|
|
2022
|
-
}
|
|
2023
|
-
this.state.isRunning = false;
|
|
2024
|
-
if (!isInteractiveMode()) {
|
|
2025
|
-
console.error("\n[*] Watch mode stopped.\n");
|
|
2026
|
-
}
|
|
2027
|
-
if (this.streamingManager) {
|
|
2028
|
-
this.streamingManager.reportWatchStatus({ watching: false, directories: 0 });
|
|
2029
|
-
}
|
|
2030
|
-
const outputManager = getOutputManager();
|
|
2031
|
-
outputManager.clearTUICallbacks();
|
|
2032
|
-
outputManager.setMode("console");
|
|
2033
|
-
if (this.dashboard) {
|
|
2034
|
-
this.dashboard.stop();
|
|
2035
|
-
this.dashboard = void 0;
|
|
2036
|
-
}
|
|
2037
|
-
const budget = this.state.tokenBudget;
|
|
2038
|
-
const tokensK = (budget.used / 1e3).toFixed(1);
|
|
2039
|
-
return {
|
|
2040
|
-
content: [{
|
|
2041
|
-
type: "text",
|
|
2042
|
-
text: `[*] **TRIE AGENT STOPPED**
|
|
2043
|
-
|
|
2044
|
-
### Session Summary:
|
|
2045
|
-
- Files scanned: ${this.state.filesScanned}
|
|
2046
|
-
- Issues found: ${this.state.totalIssuesFound}
|
|
2047
|
-
- Tokens used: ${tokensK}k / ${(budget.hourlyLimit / 1e3).toFixed(0)}k hourly limit
|
|
2048
|
-
- Scans skipped (trie-throttled): ${budget.scansSaved}
|
|
2049
|
-
- Governance ledger: Updated continuously
|
|
2050
|
-
|
|
2051
|
-
Use \`trie_watch start\` to resume.`
|
|
2052
|
-
}]
|
|
2053
|
-
};
|
|
2054
|
-
}
|
|
2055
|
-
async getStatus() {
|
|
2056
|
-
if (!this.state.isRunning) {
|
|
2057
|
-
return {
|
|
2058
|
-
content: [{
|
|
2059
|
-
type: "text",
|
|
2060
|
-
text: `[*] **Watch Mode Status: STOPPED**
|
|
2061
|
-
|
|
2062
|
-
Use \`trie_watch start\` to begin autonomous scanning.`
|
|
2063
|
-
}]
|
|
2064
|
-
};
|
|
2065
|
-
}
|
|
2066
|
-
const recentScans = Array.from(this.state.lastScan.entries()).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([file, time]) => {
|
|
2067
|
-
const ago = Math.round((Date.now() - time) / 1e3);
|
|
2068
|
-
return `- \`${basename2(file)}\` (${ago}s ago)`;
|
|
2069
|
-
}).join("\n");
|
|
2070
|
-
const recentNudges = this.state.nudges.slice(-3).map(
|
|
2071
|
-
(n) => `- ${basename2(n.file)} [${n.severity}] @ ${n.timestamp}`
|
|
2072
|
-
).join("\n");
|
|
2073
|
-
let agencyStatus = "";
|
|
2074
|
-
try {
|
|
2075
|
-
const { getTrieAgent } = await import("./trie-agent-GJJJCL6P.js");
|
|
2076
|
-
const trieAgent = getTrieAgent(this.watchedDirectory || getWorkingDirectory(void 0, true));
|
|
2077
|
-
await trieAgent.initialize();
|
|
2078
|
-
const status = await trieAgent.getAgencyStatus();
|
|
2079
|
-
agencyStatus = `
|
|
2080
|
-
### [AI] Trie Agent:
|
|
2081
|
-
- **Goals:** ${status.goals.active} active, ${status.goals.completed} completed${status.goals.topGoal ? ` (${status.goals.topGoal.slice(0, 40)}...)` : ""}
|
|
2082
|
-
- **Hypotheses:** ${status.hypotheses.testing} testing, ${status.hypotheses.validated} validated
|
|
2083
|
-
- **Risk Level:** ${status.riskLevel.toUpperCase()}
|
|
2084
|
-
- **Effectiveness:** ${status.effectiveness}%
|
|
2085
|
-
- **Scan Frequency:** ${Math.round(status.scanFrequency / 1e3)}s${status.isQuietHours ? " (quiet hours)" : ""}`;
|
|
2086
|
-
} catch {
|
|
2087
|
-
}
|
|
2088
|
-
const budget = this.state.tokenBudget;
|
|
2089
|
-
const remaining = this.getRemainingBudget();
|
|
2090
|
-
const tokensK = (budget.used / 1e3).toFixed(1);
|
|
2091
|
-
const remainingK = (remaining / 1e3).toFixed(1);
|
|
2092
|
-
return {
|
|
2093
|
-
content: [{
|
|
2094
|
-
type: "text",
|
|
2095
|
-
text: `[*] **WATCH MODE Status: RUNNING**
|
|
2096
|
-
|
|
2097
|
-
### Stats:
|
|
2098
|
-
- Directories watched: ${this.watchers.size}
|
|
2099
|
-
- Files scanned: ${this.state.filesScanned}
|
|
2100
|
-
- Issues found: ${this.state.totalIssuesFound}
|
|
2101
|
-
- Pending: ${this.state.pendingFiles.size}
|
|
2102
|
-
|
|
2103
|
-
### Token Budget:
|
|
2104
|
-
- Used: ${tokensK}k / ${(budget.hourlyLimit / 1e3).toFixed(0)}k hourly
|
|
2105
|
-
- Remaining: ${remainingK}k
|
|
2106
|
-
- Scans skipped (trie-throttled): ${budget.scansSaved}
|
|
2107
|
-
${agencyStatus}
|
|
2108
|
-
|
|
2109
|
-
### Recently Scanned:
|
|
2110
|
-
${recentScans || "(none yet)"}
|
|
2111
|
-
|
|
2112
|
-
### Recent Nudges:
|
|
2113
|
-
${recentNudges || "(none)"}
|
|
2114
|
-
|
|
2115
|
-
### Commands:
|
|
2116
|
-
- \`trie_watch issues\` - Get all issues found
|
|
2117
|
-
- \`trie_watch stop\` - Stop watching`
|
|
2118
|
-
}]
|
|
2119
|
-
};
|
|
2120
|
-
}
|
|
2121
|
-
/**
|
|
2122
|
-
* Helper to persist a nudge to both in-memory state and SQL storage
|
|
2123
|
-
*/
|
|
2124
|
-
async persistNudge(params) {
|
|
2125
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2126
|
-
const nudgeId = createHash("sha256").update(`${params.message}|${params.file || ""}|${timestamp}`).digest("hex").slice(0, 16);
|
|
2127
|
-
const nudge = {
|
|
2128
|
-
id: nudgeId,
|
|
2129
|
-
message: params.message,
|
|
2130
|
-
severity: params.severity,
|
|
2131
|
-
timestamp,
|
|
2132
|
-
resolved: false,
|
|
2133
|
-
dismissed: false,
|
|
2134
|
-
priority: params.priority ?? 5,
|
|
2135
|
-
relatedIssues: params.relatedIssues ?? [],
|
|
2136
|
-
metadata: {},
|
|
2137
|
-
...params.file && { file: params.file },
|
|
2138
|
-
...params.category && { category: params.category },
|
|
2139
|
-
...params.goalId && { goalId: params.goalId },
|
|
2140
|
-
...params.suggestedAction && { suggestedAction: params.suggestedAction }
|
|
2141
|
-
};
|
|
2142
|
-
this.state.nudges.push({
|
|
2143
|
-
file: params.file || "unknown",
|
|
2144
|
-
// Keep full path for consistency with loaded nudges
|
|
2145
|
-
message: params.message,
|
|
2146
|
-
severity: params.severity === "warning" || params.severity === "info" ? "high" : params.severity,
|
|
2147
|
-
timestamp: (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false })
|
|
2148
|
-
});
|
|
2149
|
-
try {
|
|
2150
|
-
const storage = getStorage(this.watchedDirectory);
|
|
2151
|
-
await storage.storeNudge(nudge);
|
|
2152
|
-
console.debug(`[Watch] \u2713 Persisted nudge to storage: ${nudge.message.slice(0, 60)}...`);
|
|
2153
|
-
} catch (error) {
|
|
2154
|
-
console.error("[Watch] Failed to persist nudge to storage:", error);
|
|
2155
|
-
}
|
|
2156
|
-
}
|
|
2157
|
-
getNudges() {
|
|
2158
|
-
return {
|
|
2159
|
-
content: [
|
|
2160
|
-
{
|
|
2161
|
-
type: "text",
|
|
2162
|
-
text: `[#] Recent nudges (${this.state.nudges.length} this session)
|
|
2163
|
-
` + (this.state.nudges.length ? this.state.nudges.map(
|
|
2164
|
-
(n) => `- ${basename2(n.file)} [${n.severity}] @ ${n.timestamp}
|
|
2165
|
-
${n.message}`
|
|
2166
|
-
).join("\n") : "(none)")
|
|
2167
|
-
},
|
|
2168
|
-
{
|
|
2169
|
-
type: "json",
|
|
2170
|
-
json: this.state.nudges
|
|
2171
|
-
}
|
|
2172
|
-
]
|
|
2173
|
-
};
|
|
2174
|
-
}
|
|
2175
|
-
getCurrentIssues() {
|
|
2176
|
-
return {
|
|
2177
|
-
content: [{
|
|
2178
|
-
type: "text",
|
|
2179
|
-
text: `[L] **Issues Found This Session**
|
|
2180
|
-
|
|
2181
|
-
Total issues: ${this.state.totalIssuesFound}
|
|
2182
|
-
Files scanned: ${this.state.filesScanned}
|
|
2183
|
-
|
|
2184
|
-
To get a full report, run \`trie watch\` on your codebase.`
|
|
2185
|
-
}]
|
|
2186
|
-
};
|
|
2187
|
-
}
|
|
2188
|
-
};
|
|
2189
|
-
|
|
2190
781
|
// src/tools/pr-review.ts
|
|
2191
|
-
import { readFile as
|
|
2192
|
-
import { existsSync as
|
|
2193
|
-
import { join as
|
|
782
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
783
|
+
import { existsSync as existsSync2 } from "fs";
|
|
784
|
+
import { join as join2, basename as basename2, resolve as resolve2, isAbsolute as isAbsolute2 } from "path";
|
|
2194
785
|
var TriePRReviewTool = class {
|
|
2195
786
|
// Review workflow is now ledger-driven; keep a lightweight built-in checklist.
|
|
2196
787
|
CRITICAL_REVIEW_CHECKLIST = {
|
|
@@ -2289,14 +880,14 @@ Usage:
|
|
|
2289
880
|
async getPRInfo(pr, worktree) {
|
|
2290
881
|
if (worktree) {
|
|
2291
882
|
const worktreePath = isAbsolute2(worktree) ? worktree : resolve2(getWorkingDirectory(void 0, true), worktree);
|
|
2292
|
-
if (!
|
|
883
|
+
if (!existsSync2(worktreePath)) {
|
|
2293
884
|
return { success: false, error: `Worktree not found: ${worktreePath}` };
|
|
2294
885
|
}
|
|
2295
886
|
return {
|
|
2296
887
|
success: true,
|
|
2297
888
|
type: "worktree",
|
|
2298
889
|
path: worktreePath,
|
|
2299
|
-
title: `Local changes in ${
|
|
890
|
+
title: `Local changes in ${basename2(worktreePath)}`,
|
|
2300
891
|
author: this.getGitUser(),
|
|
2301
892
|
baseBranch: "HEAD~1",
|
|
2302
893
|
headBranch: "HEAD"
|
|
@@ -2452,8 +1043,8 @@ Usage:
|
|
|
2452
1043
|
"rfcs"
|
|
2453
1044
|
];
|
|
2454
1045
|
for (const docPath of designDocPaths) {
|
|
2455
|
-
const fullPath =
|
|
2456
|
-
if (
|
|
1046
|
+
const fullPath = join2(cwd, docPath);
|
|
1047
|
+
if (existsSync2(fullPath)) {
|
|
2457
1048
|
}
|
|
2458
1049
|
}
|
|
2459
1050
|
return designDocs;
|
|
@@ -2466,9 +1057,9 @@ Usage:
|
|
|
2466
1057
|
const cwd = getWorkingDirectory(void 0, true);
|
|
2467
1058
|
await Promise.all(filePaths.map(async (filePath) => {
|
|
2468
1059
|
try {
|
|
2469
|
-
const fullPath = isAbsolute2(filePath) ? filePath :
|
|
2470
|
-
if (
|
|
2471
|
-
const content = await
|
|
1060
|
+
const fullPath = isAbsolute2(filePath) ? filePath : join2(cwd, filePath);
|
|
1061
|
+
if (existsSync2(fullPath)) {
|
|
1062
|
+
const content = await readFile2(fullPath, "utf-8");
|
|
2472
1063
|
contents.set(filePath, content);
|
|
2473
1064
|
}
|
|
2474
1065
|
} catch {
|
|
@@ -4157,9 +2748,9 @@ var ToolRegistry = class {
|
|
|
4157
2748
|
};
|
|
4158
2749
|
|
|
4159
2750
|
// src/server/resource-manager.ts
|
|
4160
|
-
import { readdir, readFile as
|
|
4161
|
-
import { existsSync as
|
|
4162
|
-
import { join as
|
|
2751
|
+
import { readdir, readFile as readFile3 } from "fs/promises";
|
|
2752
|
+
import { existsSync as existsSync3 } from "fs";
|
|
2753
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
4163
2754
|
import { fileURLToPath } from "url";
|
|
4164
2755
|
var UI_APPS = {
|
|
4165
2756
|
"ledger": {
|
|
@@ -4263,7 +2854,7 @@ var ResourceManager = class {
|
|
|
4263
2854
|
}
|
|
4264
2855
|
async getScanReportResources() {
|
|
4265
2856
|
try {
|
|
4266
|
-
const reportsDir =
|
|
2857
|
+
const reportsDir = join3(getWorkingDirectory(void 0, true), "trie-reports");
|
|
4267
2858
|
const files = await readdir(reportsDir);
|
|
4268
2859
|
const reportFiles = files.filter((f) => f.endsWith(".txt") || f.endsWith(".json"));
|
|
4269
2860
|
return reportFiles.slice(0, 10).map((file) => ({
|
|
@@ -4340,10 +2931,10 @@ var ResourceManager = class {
|
|
|
4340
2931
|
async getUIAppResource(uri, appId) {
|
|
4341
2932
|
const currentFile = fileURLToPath(import.meta.url);
|
|
4342
2933
|
const distDir = dirname2(dirname2(currentFile));
|
|
4343
|
-
const uiDir =
|
|
4344
|
-
const htmlPath =
|
|
2934
|
+
const uiDir = join3(distDir, "ui");
|
|
2935
|
+
const htmlPath = join3(uiDir, `${appId}.html`);
|
|
4345
2936
|
try {
|
|
4346
|
-
if (!
|
|
2937
|
+
if (!existsSync3(htmlPath)) {
|
|
4347
2938
|
return {
|
|
4348
2939
|
contents: [{
|
|
4349
2940
|
uri,
|
|
@@ -4352,7 +2943,7 @@ var ResourceManager = class {
|
|
|
4352
2943
|
}]
|
|
4353
2944
|
};
|
|
4354
2945
|
}
|
|
4355
|
-
const content = await
|
|
2946
|
+
const content = await readFile3(htmlPath, "utf-8");
|
|
4356
2947
|
return {
|
|
4357
2948
|
contents: [{
|
|
4358
2949
|
uri,
|
|
@@ -4524,10 +3115,10 @@ var ResourceManager = class {
|
|
|
4524
3115
|
summary.push("| `trie_init` | Initialize bootstrap files |");
|
|
4525
3116
|
summary.push("");
|
|
4526
3117
|
summary.push("---", "", "# Detailed Context", "");
|
|
4527
|
-
const agentsMdPath =
|
|
3118
|
+
const agentsMdPath = join3(getTrieDirectory(workDir), "AGENTS.md");
|
|
4528
3119
|
try {
|
|
4529
|
-
if (
|
|
4530
|
-
const agentsContent = await
|
|
3120
|
+
if (existsSync3(agentsMdPath)) {
|
|
3121
|
+
const agentsContent = await readFile3(agentsMdPath, "utf-8");
|
|
4531
3122
|
summary.push(agentsContent);
|
|
4532
3123
|
} else {
|
|
4533
3124
|
const contextSummary = await getContextForAI();
|
|
@@ -4617,8 +3208,8 @@ This information is automatically available to Claude Code, Cursor, and other AI
|
|
|
4617
3208
|
}
|
|
4618
3209
|
async getCacheStatsResource(uri) {
|
|
4619
3210
|
try {
|
|
4620
|
-
const cachePath =
|
|
4621
|
-
const cacheContent = await
|
|
3211
|
+
const cachePath = join3(getTrieDirectory(getWorkingDirectory(void 0, true)), ".trie-cache.json");
|
|
3212
|
+
const cacheContent = await readFile3(cachePath, "utf-8");
|
|
4622
3213
|
const cache = JSON.parse(cacheContent);
|
|
4623
3214
|
const fileCount = Object.keys(cache.files || {}).length;
|
|
4624
3215
|
const totalVulns = Object.values(cache.files || {}).reduce((acc, file) => {
|
|
@@ -4652,8 +3243,8 @@ This information is automatically available to Claude Code, Cursor, and other AI
|
|
|
4652
3243
|
}
|
|
4653
3244
|
}
|
|
4654
3245
|
async getSignaturesResource(uri) {
|
|
4655
|
-
const { getVulnerabilityStats } = await import("./vulnerability-signatures-
|
|
4656
|
-
const { getVibeCodeStats } = await import("./vibe-code-signatures-
|
|
3246
|
+
const { getVulnerabilityStats } = await import("./vulnerability-signatures-EIKOHFPK.js");
|
|
3247
|
+
const { getVibeCodeStats } = await import("./vibe-code-signatures-J4GD4JOV.js");
|
|
4657
3248
|
const vulnStats = getVulnerabilityStats();
|
|
4658
3249
|
const vibeStats = getVibeCodeStats();
|
|
4659
3250
|
return {
|
|
@@ -4670,9 +3261,9 @@ This information is automatically available to Claude Code, Cursor, and other AI
|
|
|
4670
3261
|
}
|
|
4671
3262
|
async getScanReportResource(uri, parsedUri) {
|
|
4672
3263
|
const fileName = parsedUri.replace("reports/", "");
|
|
4673
|
-
const reportPath =
|
|
3264
|
+
const reportPath = join3(getWorkingDirectory(void 0, true), "trie-reports", fileName);
|
|
4674
3265
|
try {
|
|
4675
|
-
const content = await
|
|
3266
|
+
const content = await readFile3(reportPath, "utf-8");
|
|
4676
3267
|
return {
|
|
4677
3268
|
contents: [{
|
|
4678
3269
|
uri,
|