@triedotdev/mcp 1.0.149 → 1.0.154
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/LICENSE +21 -0
- package/README.md +150 -36
- package/dist/{autonomy-config-ZCOSTMPD.js → autonomy-config-RKLZW4XL.js} +4 -4
- package/dist/{chat-store-OJLJCJFI.js → chat-store-O3IJ5PMN.js} +4 -4
- package/dist/{chunk-IXO4G4D3.js → chunk-2LAJKFWU.js} +2 -2
- package/dist/{chunk-23RJT5WT.js → chunk-3CYMLM35.js} +2 -2
- package/dist/{chunk-QH77RQB3.js → chunk-4ZAFQEP6.js} +5 -6
- package/dist/chunk-4ZAFQEP6.js.map +1 -0
- package/dist/{chunk-SH7H3WRU.js → chunk-7F2R2ITA.js} +4 -4
- package/dist/{chunk-SH7H3WRU.js.map → chunk-7F2R2ITA.js.map} +1 -1
- package/dist/{chunk-FG467PDD.js → chunk-ALSCZ7WR.js} +2 -2
- package/dist/{chunk-YOJGSRZK.js → chunk-CBAMZERA.js} +2 -2
- package/dist/{chunk-53KUI7RQ.js → chunk-EMJ7RVWB.js} +38 -13
- package/dist/{chunk-53KUI7RQ.js.map → chunk-EMJ7RVWB.js.map} +1 -1
- package/dist/{chunk-NKHO34UZ.js → chunk-FTOF3FHT.js} +3 -3
- package/dist/{chunk-LD7ZEFNY.js → chunk-FXZAABXO.js} +2 -2
- package/dist/{chunk-ZUEAHFSY.js → chunk-HD5H7YSW.js} +313 -61
- package/dist/chunk-HD5H7YSW.js.map +1 -0
- package/dist/{chunk-APMV77PU.js → chunk-JKEEQAG2.js} +1 -1
- package/dist/chunk-JKEEQAG2.js.map +1 -0
- package/dist/{chunk-FH335WL5.js → chunk-JYWGYUKX.js} +2 -2
- package/dist/{chunk-TIMIKBY2.js → chunk-KLMJKM63.js} +2 -2
- package/dist/{chunk-4C67GV3O.js → chunk-KYKADM7P.js} +2 -2
- package/dist/{chunk-CU5VDH6F.js → chunk-L4FODDDB.js} +2 -2
- package/dist/{chunk-ILGMFND2.js → chunk-LFNH3CSN.js} +4 -4
- package/dist/{chunk-GAL7OIYU.js → chunk-LJISDV3A.js} +15 -15
- package/dist/{chunk-72KSLD7A.js → chunk-NVZZUUEU.js} +5 -5
- package/dist/{chunk-7OJ6JIPL.js → chunk-OVSYTWUU.js} +7 -150
- package/dist/chunk-OVSYTWUU.js.map +1 -0
- package/dist/{chunk-B2AHQ2IR.js → chunk-OWSGJUUR.js} +12 -12
- package/dist/{chunk-HYNDXZAU.js → chunk-QAM5X5HM.js} +1535 -90
- package/dist/chunk-QAM5X5HM.js.map +1 -0
- package/dist/{chunk-OTQEFXHU.js → chunk-T7UAH7GE.js} +2 -2
- package/dist/{chunk-FPEMP54L.js → chunk-UL337UDQ.js} +2 -2
- package/dist/{chunk-F4NJ4CBP.js → chunk-WO7CC5FH.js} +2 -2
- package/dist/{chunk-5KJ4UJOY.js → chunk-XD2HKZVB.js} +2 -2
- package/dist/{chunk-V7AY2EJO.js → chunk-YEQXKKZQ.js} +2 -2
- package/dist/{chunk-ZDDE442Q.js → chunk-Z2E7X4WI.js} +8 -8
- package/dist/{chunk-ZDDE442Q.js.map → chunk-Z2E7X4WI.js.map} +1 -1
- package/dist/cli/create-agent.js +9 -41
- package/dist/cli/create-agent.js.map +1 -1
- package/dist/cli/main.js +58 -96
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/yolo-daemon.js +65 -28
- package/dist/cli/yolo-daemon.js.map +1 -1
- package/dist/{client-INNE2GGZ.js → client-ZHOLZTRW.js} +4 -4
- package/dist/{codebase-index-FMIULFZQ.js → codebase-index-N37NDF2A.js} +4 -4
- package/dist/{fast-analyzer-CTT3MCPE.js → fast-analyzer-U6URGNQT.js} +6 -6
- package/dist/github-ingester-AR5A4RAC.js +11 -0
- package/dist/{goal-manager-IGUMDGCA.js → goal-manager-5QDITJKE.js} +8 -8
- package/dist/{goal-validator-DV6DRSGF.js → goal-validator-FU5QWDQT.js} +7 -7
- package/dist/graph-JO7GG65P.js +10 -0
- package/dist/{hypothesis-O72ZLVOW.js → hypothesis-JURDWVDC.js} +8 -8
- package/dist/incident-index-7CAXUNTL.js +11 -0
- package/dist/index.js +136 -1740
- package/dist/index.js.map +1 -1
- package/dist/{insight-store-Q62UGMTF.js → insight-store-AMEP5PPF.js} +4 -4
- package/dist/{issue-store-4FPABLC6.js → issue-store-RM3XLLKG.js} +5 -5
- package/dist/{ledger-43SIVE7X.js → ledger-PLE3C3X4.js} +14 -6
- 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-3TUUR3L2.js +12 -0
- package/dist/trie-agent-QHPS4C5Z.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 +4 -6
- package/dist/chunk-7OJ6JIPL.js.map +0 -1
- package/dist/chunk-APMV77PU.js.map +0 -1
- package/dist/chunk-G76DYVGX.js +0 -136
- package/dist/chunk-G76DYVGX.js.map +0 -1
- package/dist/chunk-HYNDXZAU.js.map +0 -1
- package/dist/chunk-QH77RQB3.js.map +0 -1
- package/dist/chunk-ZUEAHFSY.js.map +0 -1
- package/dist/comprehension-46F7ZNKL.js +0 -821
- package/dist/comprehension-46F7ZNKL.js.map +0 -1
- package/dist/github-ingester-J2ZFYXVE.js +0 -11
- package/dist/graph-J4OGTYCO.js +0 -10
- package/dist/incident-index-BWW2UEY7.js +0 -11
- package/dist/linear-ingester-JRDQAIAA.js +0 -11
- package/dist/tiered-storage-VZL7KK64.js +0 -12
- package/dist/trie-agent-ET3DAP5Y.js +0 -27
- package/dist/workers/agent-worker.d.ts +0 -2
- package/dist/workers/agent-worker.js +0 -28
- package/dist/workers/agent-worker.js.map +0 -1
- /package/dist/{autonomy-config-ZCOSTMPD.js.map → autonomy-config-RKLZW4XL.js.map} +0 -0
- /package/dist/{chat-store-OJLJCJFI.js.map → chat-store-O3IJ5PMN.js.map} +0 -0
- /package/dist/{chunk-IXO4G4D3.js.map → chunk-2LAJKFWU.js.map} +0 -0
- /package/dist/{chunk-23RJT5WT.js.map → chunk-3CYMLM35.js.map} +0 -0
- /package/dist/{chunk-FG467PDD.js.map → chunk-ALSCZ7WR.js.map} +0 -0
- /package/dist/{chunk-YOJGSRZK.js.map → chunk-CBAMZERA.js.map} +0 -0
- /package/dist/{chunk-NKHO34UZ.js.map → chunk-FTOF3FHT.js.map} +0 -0
- /package/dist/{chunk-LD7ZEFNY.js.map → chunk-FXZAABXO.js.map} +0 -0
- /package/dist/{chunk-FH335WL5.js.map → chunk-JYWGYUKX.js.map} +0 -0
- /package/dist/{chunk-TIMIKBY2.js.map → chunk-KLMJKM63.js.map} +0 -0
- /package/dist/{chunk-4C67GV3O.js.map → chunk-KYKADM7P.js.map} +0 -0
- /package/dist/{chunk-CU5VDH6F.js.map → chunk-L4FODDDB.js.map} +0 -0
- /package/dist/{chunk-ILGMFND2.js.map → chunk-LFNH3CSN.js.map} +0 -0
- /package/dist/{chunk-GAL7OIYU.js.map → chunk-LJISDV3A.js.map} +0 -0
- /package/dist/{chunk-72KSLD7A.js.map → chunk-NVZZUUEU.js.map} +0 -0
- /package/dist/{chunk-B2AHQ2IR.js.map → chunk-OWSGJUUR.js.map} +0 -0
- /package/dist/{chunk-OTQEFXHU.js.map → chunk-T7UAH7GE.js.map} +0 -0
- /package/dist/{chunk-FPEMP54L.js.map → chunk-UL337UDQ.js.map} +0 -0
- /package/dist/{chunk-F4NJ4CBP.js.map → chunk-WO7CC5FH.js.map} +0 -0
- /package/dist/{chunk-5KJ4UJOY.js.map → chunk-XD2HKZVB.js.map} +0 -0
- /package/dist/{chunk-V7AY2EJO.js.map → chunk-YEQXKKZQ.js.map} +0 -0
- /package/dist/{client-INNE2GGZ.js.map → client-ZHOLZTRW.js.map} +0 -0
- /package/dist/{codebase-index-FMIULFZQ.js.map → codebase-index-N37NDF2A.js.map} +0 -0
- /package/dist/{fast-analyzer-CTT3MCPE.js.map → fast-analyzer-U6URGNQT.js.map} +0 -0
- /package/dist/{github-ingester-J2ZFYXVE.js.map → github-ingester-AR5A4RAC.js.map} +0 -0
- /package/dist/{goal-manager-IGUMDGCA.js.map → goal-manager-5QDITJKE.js.map} +0 -0
- /package/dist/{goal-validator-DV6DRSGF.js.map → goal-validator-FU5QWDQT.js.map} +0 -0
- /package/dist/{graph-J4OGTYCO.js.map → graph-JO7GG65P.js.map} +0 -0
- /package/dist/{hypothesis-O72ZLVOW.js.map → hypothesis-JURDWVDC.js.map} +0 -0
- /package/dist/{incident-index-BWW2UEY7.js.map → incident-index-7CAXUNTL.js.map} +0 -0
- /package/dist/{insight-store-Q62UGMTF.js.map → insight-store-AMEP5PPF.js.map} +0 -0
- /package/dist/{issue-store-4FPABLC6.js.map → issue-store-RM3XLLKG.js.map} +0 -0
- /package/dist/{ledger-43SIVE7X.js.map → ledger-PLE3C3X4.js.map} +0 -0
- /package/dist/{linear-ingester-JRDQAIAA.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-VZL7KK64.js.map → tiered-storage-3TUUR3L2.js.map} +0 -0
- /package/dist/{trie-agent-ET3DAP5Y.js.map → trie-agent-QHPS4C5Z.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-V7AY2EJO.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,79 +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-QAM5X5HM.js";
|
|
46
|
+
import "./chunk-3CYMLM35.js";
|
|
47
|
+
import "./chunk-LJISDV3A.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-
|
|
76
|
-
import {
|
|
77
|
-
getStorage
|
|
78
|
-
} from "./chunk-FG467PDD.js";
|
|
71
|
+
} from "./chunk-OWSGJUUR.js";
|
|
79
72
|
import {
|
|
80
73
|
ContextGraph
|
|
81
|
-
} from "./chunk-
|
|
82
|
-
import "./chunk-
|
|
83
|
-
import
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
import "./chunk-
|
|
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";
|
|
80
|
+
import "./chunk-6NLHFIYA.js";
|
|
81
|
+
import "./chunk-ALSCZ7WR.js";
|
|
82
|
+
import "./chunk-XD2HKZVB.js";
|
|
88
83
|
import {
|
|
89
84
|
findSimilarIssues,
|
|
90
85
|
getMemoryStats,
|
|
91
86
|
getRecentIssues,
|
|
92
87
|
markIssueResolved,
|
|
93
88
|
purgeIssues,
|
|
94
|
-
searchIssues
|
|
95
|
-
|
|
96
|
-
} from "./chunk-QH77RQB3.js";
|
|
89
|
+
searchIssues
|
|
90
|
+
} from "./chunk-4ZAFQEP6.js";
|
|
97
91
|
import "./chunk-EFWVF6TI.js";
|
|
98
|
-
import "./chunk-F4NJ4CBP.js";
|
|
99
|
-
import "./chunk-IXO4G4D3.js";
|
|
100
|
-
import "./chunk-6NLHFIYA.js";
|
|
101
92
|
import {
|
|
102
|
-
getSkillRegistry
|
|
103
|
-
} from "./chunk-G76DYVGX.js";
|
|
104
|
-
import {
|
|
105
|
-
getAutonomyConfig
|
|
106
|
-
} from "./chunk-5KJ4UJOY.js";
|
|
107
|
-
import {
|
|
108
|
-
getChangedFilesSinceTimestamp,
|
|
109
|
-
getGitChangedFiles,
|
|
110
93
|
runShellCommandSync
|
|
111
|
-
} from "./chunk-
|
|
94
|
+
} from "./chunk-HD5H7YSW.js";
|
|
112
95
|
import "./chunk-43X6JBEM.js";
|
|
113
96
|
import {
|
|
114
97
|
getTrieDirectory,
|
|
115
98
|
getWorkingDirectory
|
|
116
|
-
} from "./chunk-
|
|
99
|
+
} from "./chunk-7F2R2ITA.js";
|
|
117
100
|
import {
|
|
118
101
|
isInteractiveMode
|
|
119
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-JKEEQAG2.js";
|
|
120
103
|
import "./chunk-DGUM43GV.js";
|
|
121
104
|
|
|
122
105
|
// src/server/mcp-server.ts
|
|
@@ -795,1436 +778,29 @@ trie_test action:"run" files:["src/utils.test.ts"]
|
|
|
795
778
|
}
|
|
796
779
|
};
|
|
797
780
|
|
|
798
|
-
// src/tools/watch.ts
|
|
799
|
-
import { watch, existsSync as existsSync2, readFileSync } from "fs";
|
|
800
|
-
import { stat, readFile as readFile2 } from "fs/promises";
|
|
801
|
-
import { join as join2, extname as extname2, basename as basename2 } from "path";
|
|
802
|
-
import { createHash } from "crypto";
|
|
803
|
-
var WATCH_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
804
|
-
".ts",
|
|
805
|
-
".tsx",
|
|
806
|
-
".js",
|
|
807
|
-
".jsx",
|
|
808
|
-
".mjs",
|
|
809
|
-
".vue",
|
|
810
|
-
".svelte",
|
|
811
|
-
".astro",
|
|
812
|
-
".py",
|
|
813
|
-
".go",
|
|
814
|
-
".rs"
|
|
815
|
-
]);
|
|
816
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
817
|
-
"node_modules",
|
|
818
|
-
".git",
|
|
819
|
-
"dist",
|
|
820
|
-
"build",
|
|
821
|
-
".next",
|
|
822
|
-
".nuxt",
|
|
823
|
-
"coverage",
|
|
824
|
-
".turbo",
|
|
825
|
-
".cache"
|
|
826
|
-
]);
|
|
827
|
-
var TrieWatchTool = class _TrieWatchTool {
|
|
828
|
-
extractionPipeline = null;
|
|
829
|
-
watchedDirectory = "";
|
|
830
|
-
codebaseIndex = null;
|
|
831
|
-
state = {
|
|
832
|
-
isRunning: false,
|
|
833
|
-
lastScan: /* @__PURE__ */ new Map(),
|
|
834
|
-
pendingFiles: /* @__PURE__ */ new Set(),
|
|
835
|
-
scanDebounceTimer: null,
|
|
836
|
-
issueCache: /* @__PURE__ */ new Map(),
|
|
837
|
-
totalIssuesFound: 0,
|
|
838
|
-
filesScanned: 0,
|
|
839
|
-
nudgedFiles: /* @__PURE__ */ new Set(),
|
|
840
|
-
nudges: [],
|
|
841
|
-
lastAutoScan: 0,
|
|
842
|
-
autoScanInProgress: false,
|
|
843
|
-
tokenBudget: {
|
|
844
|
-
used: 0,
|
|
845
|
-
windowStart: Date.now(),
|
|
846
|
-
hourlyLimit: 5e4,
|
|
847
|
-
scansSaved: 0
|
|
848
|
-
},
|
|
849
|
-
cleanFiles: /* @__PURE__ */ new Map()
|
|
850
|
-
};
|
|
851
|
-
watchers = /* @__PURE__ */ new Map();
|
|
852
|
-
streamingManager = void 0;
|
|
853
|
-
dashboard = void 0;
|
|
854
|
-
lastHypothesisCheck = 0;
|
|
855
|
-
pipelineSyncTimer = null;
|
|
856
|
-
static HYPOTHESIS_CHECK_INTERVAL_MS = 3e5;
|
|
857
|
-
// Check every 5 minutes
|
|
858
|
-
static PIPELINE_SYNC_INTERVAL_MS = 30 * 60 * 1e3;
|
|
859
|
-
// 30 minutes
|
|
860
|
-
async execute(args) {
|
|
861
|
-
const { action, directory, debounceMs = 1e3 } = args;
|
|
862
|
-
switch (action) {
|
|
863
|
-
case "start":
|
|
864
|
-
return this.startWatching(getWorkingDirectory(directory), debounceMs);
|
|
865
|
-
case "stop":
|
|
866
|
-
return await this.stopWatching();
|
|
867
|
-
case "status":
|
|
868
|
-
return await this.getStatus();
|
|
869
|
-
case "issues":
|
|
870
|
-
return this.getCurrentIssues();
|
|
871
|
-
case "nudges":
|
|
872
|
-
return this.getNudges();
|
|
873
|
-
default:
|
|
874
|
-
return {
|
|
875
|
-
content: [{
|
|
876
|
-
type: "text",
|
|
877
|
-
text: `Unknown action: ${action}. Use 'start', 'stop', 'status', or 'issues'.`
|
|
878
|
-
}]
|
|
879
|
-
};
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
async startWatching(directory, debounceMs) {
|
|
883
|
-
if (this.state.isRunning) {
|
|
884
|
-
return {
|
|
885
|
-
content: [{
|
|
886
|
-
type: "text",
|
|
887
|
-
text: "[!] Watch mode is already running. Use `trie_watch stop` to stop it first."
|
|
888
|
-
}]
|
|
889
|
-
};
|
|
890
|
-
}
|
|
891
|
-
if (!isTrieInitialized(directory)) {
|
|
892
|
-
return {
|
|
893
|
-
content: [{
|
|
894
|
-
type: "text",
|
|
895
|
-
text: "Trie is not initialized for this project. Run `trie init` first."
|
|
896
|
-
}]
|
|
897
|
-
};
|
|
898
|
-
}
|
|
899
|
-
const anthropicApiKey = process.env.ANTHROPIC_API_KEY;
|
|
900
|
-
if (anthropicApiKey) {
|
|
901
|
-
this.extractionPipeline = new ExtractionPipeline({
|
|
902
|
-
workingDirectory: directory,
|
|
903
|
-
anthropicApiKey
|
|
904
|
-
});
|
|
905
|
-
await this.extractionPipeline.initialize();
|
|
906
|
-
}
|
|
907
|
-
this.codebaseIndex = new CodebaseIndex(directory);
|
|
908
|
-
this.state.isRunning = true;
|
|
909
|
-
this.watchedDirectory = directory;
|
|
910
|
-
this.state.issueCache.clear();
|
|
911
|
-
this.state.totalIssuesFound = 0;
|
|
912
|
-
this.state.filesScanned = 0;
|
|
913
|
-
this.state.nudgedFiles.clear();
|
|
914
|
-
this.state.nudges = [];
|
|
915
|
-
try {
|
|
916
|
-
const storage = getStorage(directory);
|
|
917
|
-
await storage.initialize();
|
|
918
|
-
const unresolvedNudges = await storage.queryNudges({ resolved: false });
|
|
919
|
-
console.debug(`[Watch] Found ${unresolvedNudges.length} unresolved nudges in storage`);
|
|
920
|
-
for (const nudge of unresolvedNudges) {
|
|
921
|
-
let mappedSeverity = "high";
|
|
922
|
-
if (nudge.severity === "critical") {
|
|
923
|
-
mappedSeverity = "critical";
|
|
924
|
-
} else if (nudge.severity === "high") {
|
|
925
|
-
mappedSeverity = "high";
|
|
926
|
-
}
|
|
927
|
-
this.state.nudges.push({
|
|
928
|
-
file: nudge.file || "unknown",
|
|
929
|
-
// Keep full path, don't use basename
|
|
930
|
-
message: nudge.message,
|
|
931
|
-
severity: mappedSeverity,
|
|
932
|
-
timestamp: new Date(nudge.timestamp).toLocaleTimeString("en-US", { hour12: false })
|
|
933
|
-
});
|
|
934
|
-
console.debug(`[Watch] Loaded nudge: ${nudge.message.slice(0, 60)}... (${nudge.severity} -> ${mappedSeverity})`);
|
|
935
|
-
}
|
|
936
|
-
if (unresolvedNudges.length > 0) {
|
|
937
|
-
console.log(`[Watch] \u2713 Loaded ${unresolvedNudges.length} unresolved nudges from storage`);
|
|
938
|
-
} else {
|
|
939
|
-
console.debug(`[Watch] No unresolved nudges found in storage`);
|
|
940
|
-
}
|
|
941
|
-
} catch (error) {
|
|
942
|
-
console.error("[Watch] Failed to load nudges from storage:", error);
|
|
943
|
-
}
|
|
944
|
-
try {
|
|
945
|
-
const graph = new ContextGraph(directory);
|
|
946
|
-
const allNodes = await graph.listNodes();
|
|
947
|
-
const unresolvedIncidents = allNodes.filter(
|
|
948
|
-
(n) => n.type === "incident" && !n.data.resolved
|
|
949
|
-
);
|
|
950
|
-
console.debug(`[Watch] Found ${unresolvedIncidents.length} unresolved incidents in context graph`);
|
|
951
|
-
for (const incident of unresolvedIncidents) {
|
|
952
|
-
const incidentData = incident.data;
|
|
953
|
-
let mappedSeverity = "high";
|
|
954
|
-
if (incidentData.severity === "critical") {
|
|
955
|
-
mappedSeverity = "critical";
|
|
956
|
-
} else if (incidentData.severity === "major") {
|
|
957
|
-
mappedSeverity = "critical";
|
|
958
|
-
} else if (incidentData.severity === "minor") {
|
|
959
|
-
mappedSeverity = "high";
|
|
960
|
-
}
|
|
961
|
-
const edges = await graph.getEdges(incident.id);
|
|
962
|
-
const linkedFiles = edges.filter((e) => e.type === "causedBy" || e.type === "affects").map((e) => {
|
|
963
|
-
const node = allNodes.find((n) => n.id === e.to_id || n.id === e.from_id);
|
|
964
|
-
if (node?.type === "file") {
|
|
965
|
-
return node.data.path;
|
|
966
|
-
}
|
|
967
|
-
return null;
|
|
968
|
-
}).filter((f) => f !== null);
|
|
969
|
-
const file = linkedFiles[0] || "unknown";
|
|
970
|
-
const message = `INCIDENT: ${incidentData.description}`;
|
|
971
|
-
this.state.nudges.push({
|
|
972
|
-
file,
|
|
973
|
-
message,
|
|
974
|
-
severity: mappedSeverity,
|
|
975
|
-
timestamp: new Date(incidentData.timestamp).toLocaleTimeString("en-US", { hour12: false })
|
|
976
|
-
});
|
|
977
|
-
console.debug(`[Watch] Loaded incident: ${incidentData.description.slice(0, 60)}... (${incidentData.severity} -> ${mappedSeverity})`);
|
|
978
|
-
}
|
|
979
|
-
if (unresolvedIncidents.length > 0) {
|
|
980
|
-
console.log(`[Watch] \u2713 Loaded ${unresolvedIncidents.length} unresolved incidents from context graph`);
|
|
981
|
-
} else {
|
|
982
|
-
console.debug(`[Watch] No unresolved incidents found in context graph`);
|
|
983
|
-
}
|
|
984
|
-
} catch (error) {
|
|
985
|
-
console.error("[Watch] Failed to load incidents from context graph:", error);
|
|
986
|
-
}
|
|
987
|
-
if (!isInteractiveMode()) {
|
|
988
|
-
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");
|
|
989
|
-
console.error("TRIE AGENT - NOW WATCHING");
|
|
990
|
-
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");
|
|
991
|
-
console.error("Your Trie agent is now watching over your codebase.");
|
|
992
|
-
console.error("Signal extraction: ENABLED (building governance ledger)");
|
|
993
|
-
console.error(`Watching: ${directory}`);
|
|
994
|
-
console.error(`Debounce: ${debounceMs}ms`);
|
|
995
|
-
console.error("");
|
|
996
|
-
}
|
|
997
|
-
if (isInteractiveMode()) {
|
|
998
|
-
this.streamingManager = new StreamingManager();
|
|
999
|
-
this.dashboard = new InteractiveDashboard();
|
|
1000
|
-
this.streamingManager.subscribe((update) => this.dashboard?.handleStreamUpdate(update));
|
|
1001
|
-
const outputManager = getOutputManager();
|
|
1002
|
-
outputManager.setMode("tui");
|
|
1003
|
-
outputManager.setStreamingManager(this.streamingManager);
|
|
1004
|
-
await this.dashboard.start();
|
|
1005
|
-
this.streamingManager.reportWatchStatus({ watching: true, directories: 0, debounceMs });
|
|
1006
|
-
} else {
|
|
1007
|
-
getOutputManager().setMode("console");
|
|
1008
|
-
}
|
|
1009
|
-
await this.watchDirectory(directory, debounceMs);
|
|
1010
|
-
await this.watchContextGraph(directory);
|
|
1011
|
-
if (this.streamingManager) {
|
|
1012
|
-
this.streamingManager.reportWatchStatus({
|
|
1013
|
-
watching: true,
|
|
1014
|
-
directories: this.watchers.size,
|
|
1015
|
-
debounceMs
|
|
1016
|
-
});
|
|
1017
|
-
}
|
|
1018
|
-
setTimeout(() => {
|
|
1019
|
-
void this.initialGoalComplianceScan();
|
|
1020
|
-
void this.initialHypothesisGeneration();
|
|
1021
|
-
void this.syncPipelineIntegrations();
|
|
1022
|
-
}, 1e3);
|
|
1023
|
-
this.pipelineSyncTimer = setInterval(() => {
|
|
1024
|
-
void this.syncPipelineIntegrations();
|
|
1025
|
-
}, _TrieWatchTool.PIPELINE_SYNC_INTERVAL_MS);
|
|
1026
|
-
const indexStatus = this.codebaseIndex?.isEmpty() ? "BUILDING (one-time, speeds up goal checks)" : "READY";
|
|
1027
|
-
return {
|
|
1028
|
-
content: [{
|
|
1029
|
-
type: "text",
|
|
1030
|
-
text: `**TRIE AGENT ACTIVATED**
|
|
1031
|
-
|
|
1032
|
-
Your Trie agent is now autonomously watching and learning from your codebase.
|
|
1033
|
-
|
|
1034
|
-
**Watching:** \`${directory}\`
|
|
1035
|
-
**Debounce:** ${debounceMs}ms (waits for you to stop typing)
|
|
1036
|
-
**Signal Extraction:** ${process.env.ANTHROPIC_API_KEY ? "ENABLED" : "LIMITED (set ANTHROPIC_API_KEY for full extraction)"}
|
|
1037
|
-
**Codebase Index:** ${indexStatus}
|
|
1038
|
-
|
|
1039
|
-
### How the agent works:
|
|
1040
|
-
1. You write/edit code
|
|
1041
|
-
2. Agent detects the change
|
|
1042
|
-
3. Extracts governance, facts, blockers -> stores in ledger
|
|
1043
|
-
4. Predicts risks based on historical patterns
|
|
1044
|
-
5. Nudges you if something looks risky
|
|
1045
|
-
|
|
1046
|
-
### The agent learns:
|
|
1047
|
-
- Every commit builds the governance ledger
|
|
1048
|
-
- \`trie gotcha\` queries the ledger for predictions
|
|
1049
|
-
- \`trie ok\` / \`trie bad\` teach the agent what matters
|
|
1050
|
-
|
|
1051
|
-
### Commands:
|
|
1052
|
-
- \`trie_watch status\` - See agent status
|
|
1053
|
-
- \`trie_watch stop\` - Stop the agent
|
|
1054
|
-
- \`trie_index status\` - Check codebase index`
|
|
1055
|
-
}]
|
|
1056
|
-
};
|
|
1057
|
-
}
|
|
1058
|
-
shouldSkipPath(filePath) {
|
|
1059
|
-
const parts = filePath.split("/");
|
|
1060
|
-
return parts.some((p) => SKIP_DIRS.has(p) || p.startsWith(".") && p !== ".");
|
|
1061
|
-
}
|
|
1062
|
-
async watchDirectory(dir, debounceMs) {
|
|
1063
|
-
if (!existsSync2(dir)) return;
|
|
1064
|
-
try {
|
|
1065
|
-
const dirStat = await stat(dir);
|
|
1066
|
-
if (!dirStat.isDirectory()) return;
|
|
1067
|
-
const watcher = watch(dir, { persistent: true, recursive: true }, (_eventType, filename) => {
|
|
1068
|
-
if (!filename) return;
|
|
1069
|
-
if (this.shouldSkipPath(filename)) return;
|
|
1070
|
-
const ext = extname2(filename).toLowerCase();
|
|
1071
|
-
if (!WATCH_EXTENSIONS.has(ext)) return;
|
|
1072
|
-
const fullPath = join2(dir, filename);
|
|
1073
|
-
if (!existsSync2(fullPath)) return;
|
|
1074
|
-
this.state.pendingFiles.add(fullPath);
|
|
1075
|
-
if (this.state.scanDebounceTimer) {
|
|
1076
|
-
clearTimeout(this.state.scanDebounceTimer);
|
|
1077
|
-
}
|
|
1078
|
-
this.state.scanDebounceTimer = setTimeout(() => {
|
|
1079
|
-
this.processPendingFiles();
|
|
1080
|
-
}, debounceMs);
|
|
1081
|
-
});
|
|
1082
|
-
watcher.on("error", (err) => {
|
|
1083
|
-
if (!isInteractiveMode()) {
|
|
1084
|
-
console.error(`[!] Watcher error: ${err.message}`);
|
|
1085
|
-
}
|
|
1086
|
-
watcher.close();
|
|
1087
|
-
this.watchers.delete(dir);
|
|
1088
|
-
});
|
|
1089
|
-
this.watchers.set(dir, watcher);
|
|
1090
|
-
if (this.streamingManager) {
|
|
1091
|
-
this.streamingManager.reportWatchStatus({
|
|
1092
|
-
watching: true,
|
|
1093
|
-
directories: 1,
|
|
1094
|
-
debounceMs
|
|
1095
|
-
});
|
|
1096
|
-
}
|
|
1097
|
-
} catch {
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
/**
|
|
1101
|
-
* Watch .trie/context.json for incident changes
|
|
1102
|
-
*/
|
|
1103
|
-
async watchContextGraph(directory) {
|
|
1104
|
-
const contextPath = join2(getTrieDirectory(directory), "context.json");
|
|
1105
|
-
if (!existsSync2(contextPath)) {
|
|
1106
|
-
console.debug("[Watch] No context.json found, skipping incident watcher");
|
|
1107
|
-
return;
|
|
1108
|
-
}
|
|
1109
|
-
try {
|
|
1110
|
-
const watcher = watch(contextPath, { persistent: true }, async (_eventType, _filename) => {
|
|
1111
|
-
console.debug("[Watch] context.json changed, reloading incidents...");
|
|
1112
|
-
await this.reloadIncidents(directory);
|
|
1113
|
-
});
|
|
1114
|
-
watcher.on("error", (err) => {
|
|
1115
|
-
console.error(`[Watch] Context watcher error: ${err.message}`);
|
|
1116
|
-
watcher.close();
|
|
1117
|
-
});
|
|
1118
|
-
this.watchers.set("context.json", watcher);
|
|
1119
|
-
console.debug("[Watch] \u2713 Watching context.json for incident changes");
|
|
1120
|
-
} catch (error) {
|
|
1121
|
-
console.error("[Watch] Failed to set up context.json watcher:", error);
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
/**
|
|
1125
|
-
* Reload incidents from context graph
|
|
1126
|
-
*/
|
|
1127
|
-
async reloadIncidents(directory) {
|
|
1128
|
-
try {
|
|
1129
|
-
const graph = new ContextGraph(directory);
|
|
1130
|
-
const allNodes = await graph.listNodes();
|
|
1131
|
-
const unresolvedIncidents = allNodes.filter(
|
|
1132
|
-
(n) => n.type === "incident" && !n.data.resolved
|
|
1133
|
-
);
|
|
1134
|
-
this.state.nudges = this.state.nudges.filter((n) => !n.message.startsWith("INCIDENT:"));
|
|
1135
|
-
for (const incident of unresolvedIncidents) {
|
|
1136
|
-
const incidentData = incident.data;
|
|
1137
|
-
let mappedSeverity = "high";
|
|
1138
|
-
if (incidentData.severity === "critical") {
|
|
1139
|
-
mappedSeverity = "critical";
|
|
1140
|
-
} else if (incidentData.severity === "major") {
|
|
1141
|
-
mappedSeverity = "critical";
|
|
1142
|
-
} else if (incidentData.severity === "minor") {
|
|
1143
|
-
mappedSeverity = "high";
|
|
1144
|
-
}
|
|
1145
|
-
const edges = await graph.getEdges(incident.id);
|
|
1146
|
-
const linkedFiles = edges.filter((e) => e.type === "causedBy" || e.type === "affects").map((e) => {
|
|
1147
|
-
const node = allNodes.find((n) => n.id === e.to_id || n.id === e.from_id);
|
|
1148
|
-
if (node?.type === "file") {
|
|
1149
|
-
return node.data.path;
|
|
1150
|
-
}
|
|
1151
|
-
return null;
|
|
1152
|
-
}).filter((f) => f !== null);
|
|
1153
|
-
const file = linkedFiles[0] || "unknown";
|
|
1154
|
-
const message = `INCIDENT: ${incidentData.description}`;
|
|
1155
|
-
this.state.nudges.push({
|
|
1156
|
-
file,
|
|
1157
|
-
message,
|
|
1158
|
-
severity: mappedSeverity,
|
|
1159
|
-
timestamp: new Date(incidentData.timestamp).toLocaleTimeString("en-US", { hour12: false })
|
|
1160
|
-
});
|
|
1161
|
-
}
|
|
1162
|
-
console.log(`[Watch] \u2713 Reloaded ${unresolvedIncidents.length} unresolved incidents`);
|
|
1163
|
-
} catch (error) {
|
|
1164
|
-
console.error("[Watch] Failed to reload incidents:", error);
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
async processPendingFiles() {
|
|
1168
|
-
if (this.state.pendingFiles.size === 0) return;
|
|
1169
|
-
const files = Array.from(this.state.pendingFiles);
|
|
1170
|
-
this.state.pendingFiles.clear();
|
|
1171
|
-
if (!isInteractiveMode()) {
|
|
1172
|
-
console.error(`
|
|
1173
|
-
Detected changes in ${files.length} file(s):`);
|
|
1174
|
-
for (const file of files) {
|
|
1175
|
-
console.error(` - ${basename2(file)}`);
|
|
1176
|
-
}
|
|
1177
|
-
console.error("");
|
|
1178
|
-
}
|
|
1179
|
-
try {
|
|
1180
|
-
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
1181
|
-
if (this.extractionPipeline) {
|
|
1182
|
-
try {
|
|
1183
|
-
const fileContents = await Promise.all(
|
|
1184
|
-
files.map(async (file) => {
|
|
1185
|
-
try {
|
|
1186
|
-
const content = await readFile2(file, "utf-8");
|
|
1187
|
-
return { file, content };
|
|
1188
|
-
} catch {
|
|
1189
|
-
return null;
|
|
1190
|
-
}
|
|
1191
|
-
})
|
|
1192
|
-
);
|
|
1193
|
-
const validFiles = fileContents.filter((f) => f !== null);
|
|
1194
|
-
if (validFiles.length > 0) {
|
|
1195
|
-
const combinedContent = validFiles.map(
|
|
1196
|
-
(f) => `File: ${basename2(f.file)}
|
|
1197
|
-
${f.content.slice(0, 1e3)}`
|
|
1198
|
-
// First 1KB of each file
|
|
1199
|
-
).join("\n\n---\n\n");
|
|
1200
|
-
if (!isInteractiveMode()) {
|
|
1201
|
-
console.error("[*] Extracting signals from changes...");
|
|
1202
|
-
}
|
|
1203
|
-
const signal = await this.extractionPipeline.process(combinedContent, {
|
|
1204
|
-
sourceType: "file",
|
|
1205
|
-
sourceId: `watch-${Date.now()}`
|
|
1206
|
-
});
|
|
1207
|
-
if (signal.governance.length > 0 || signal.facts.length > 0 || signal.blockers.length > 0) {
|
|
1208
|
-
const govCount = signal.governance.length;
|
|
1209
|
-
if (!isInteractiveMode()) {
|
|
1210
|
-
console.error(` [+] Extracted: ${govCount} governance, ${signal.facts.length} facts, ${signal.blockers.length} blockers`);
|
|
1211
|
-
}
|
|
1212
|
-
if (this.streamingManager) {
|
|
1213
|
-
this.streamingManager.reportSignalExtraction({
|
|
1214
|
-
governance: govCount,
|
|
1215
|
-
facts: signal.facts.length,
|
|
1216
|
-
blockers: signal.blockers.length,
|
|
1217
|
-
questions: signal.questions.length
|
|
1218
|
-
});
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
} catch (error) {
|
|
1223
|
-
if (!isInteractiveMode()) {
|
|
1224
|
-
console.error(` [!] Signal extraction failed: ${error}`);
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
void this.autoScanFiles(files).catch((err) => {
|
|
1229
|
-
getOutputManager().log("warn", `Auto scan failed: ${err}`);
|
|
1230
|
-
});
|
|
1231
|
-
await this.discoverPatternsFromIssues(projectPath);
|
|
1232
|
-
const now = Date.now();
|
|
1233
|
-
if (now - this.lastHypothesisCheck > _TrieWatchTool.HYPOTHESIS_CHECK_INTERVAL_MS) {
|
|
1234
|
-
this.checkAndGenerateHypotheses(projectPath);
|
|
1235
|
-
this.lastHypothesisCheck = now;
|
|
1236
|
-
}
|
|
1237
|
-
if (this.streamingManager) {
|
|
1238
|
-
const now2 = Date.now();
|
|
1239
|
-
for (const file of files) {
|
|
1240
|
-
const lastReport = this.state.lastScan.get(file) || 0;
|
|
1241
|
-
if (now2 - lastReport > 2e3) {
|
|
1242
|
-
this.streamingManager.reportWatchChange(file);
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
this.state.filesScanned += files.length;
|
|
1247
|
-
for (const file of files) {
|
|
1248
|
-
this.state.lastScan.set(file, Date.now());
|
|
1249
|
-
if (this.codebaseIndex) {
|
|
1250
|
-
const relativePath = file.replace(projectPath + "/", "");
|
|
1251
|
-
void this.codebaseIndex.indexFile(relativePath).then(() => {
|
|
1252
|
-
void this.codebaseIndex?.save();
|
|
1253
|
-
});
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
} catch (error) {
|
|
1257
|
-
if (!isInteractiveMode()) {
|
|
1258
|
-
console.error(`Scan error: ${error}`);
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
isQuiet() {
|
|
1263
|
-
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
1264
|
-
const quietPath = join2(getTrieDirectory(projectPath), "quiet.json");
|
|
1265
|
-
try {
|
|
1266
|
-
const raw = readFileSync(quietPath, "utf-8");
|
|
1267
|
-
const data = JSON.parse(raw);
|
|
1268
|
-
const until = new Date(data.until).getTime();
|
|
1269
|
-
return Date.now() < until;
|
|
1270
|
-
} catch {
|
|
1271
|
-
return false;
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1274
|
-
/**
|
|
1275
|
-
* Check and generate hypotheses autonomously
|
|
1276
|
-
* Claude observes patterns and creates new hypotheses to test
|
|
1277
|
-
*/
|
|
1278
|
-
async checkAndGenerateHypotheses(projectPath) {
|
|
1279
|
-
if (!isAIAvailable()) return;
|
|
1280
|
-
try {
|
|
1281
|
-
const { getHypothesisEngine } = await import("./hypothesis-O72ZLVOW.js");
|
|
1282
|
-
const { getOutputManager: getOutputManager2 } = await import("./output-manager-DZO5LGSG.js");
|
|
1283
|
-
const hypothesisEngine = getHypothesisEngine(projectPath);
|
|
1284
|
-
const recentIssues = Array.from(this.state.issueCache.values()).flat();
|
|
1285
|
-
const patterns = [];
|
|
1286
|
-
const observations = [];
|
|
1287
|
-
if (this.state.nudges.length > 0) {
|
|
1288
|
-
const nudgesByFile = {};
|
|
1289
|
-
for (const nudge of this.state.nudges) {
|
|
1290
|
-
nudgesByFile[nudge.file] = (nudgesByFile[nudge.file] || 0) + 1;
|
|
1291
|
-
}
|
|
1292
|
-
const topFiles = Object.entries(nudgesByFile).sort(([, a], [, b]) => b - a).slice(0, 3);
|
|
1293
|
-
if (topFiles[0] && topFiles[0][1] > 2) {
|
|
1294
|
-
observations.push(`File ${topFiles[0][0]} has ${topFiles[0][1]} repeated issues this session`);
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
if (this.state.nudges.length > 5) {
|
|
1298
|
-
observations.push(`High issue detection rate: ${this.state.nudges.length} violations detected in watch session`);
|
|
1299
|
-
}
|
|
1300
|
-
let generated = await hypothesisEngine.generateHypothesesWithAI({
|
|
1301
|
-
recentIssues,
|
|
1302
|
-
patterns,
|
|
1303
|
-
observations
|
|
1304
|
-
});
|
|
1305
|
-
if (generated.length === 0) {
|
|
1306
|
-
generated = await hypothesisEngine.autoGenerateHypotheses();
|
|
1307
|
-
}
|
|
1308
|
-
for (const hypothesis of generated) {
|
|
1309
|
-
const message = `[New Hypothesis] "${hypothesis.statement}" (${Math.round(hypothesis.confidence * 100)}% confidence)`;
|
|
1310
|
-
getOutputManager2().nudge(
|
|
1311
|
-
message,
|
|
1312
|
-
"info",
|
|
1313
|
-
void 0,
|
|
1314
|
-
1e4
|
|
1315
|
-
);
|
|
1316
|
-
if (!isInteractiveMode()) {
|
|
1317
|
-
console.error(`
|
|
1318
|
-
[?] ${message}`);
|
|
1319
|
-
console.error(` Test: ${hypothesis.testCriteria || "Collecting evidence..."}`);
|
|
1320
|
-
}
|
|
1321
|
-
if (this.streamingManager) {
|
|
1322
|
-
this.streamingManager.reportSignalExtraction({
|
|
1323
|
-
governance: 0,
|
|
1324
|
-
facts: 0,
|
|
1325
|
-
blockers: 0,
|
|
1326
|
-
questions: 1
|
|
1327
|
-
// Hypotheses are questions to answer
|
|
1328
|
-
});
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
if (recentIssues.length > 10) {
|
|
1332
|
-
await hypothesisEngine.updateConfidenceFromOutcomes();
|
|
1333
|
-
}
|
|
1334
|
-
} catch (error) {
|
|
1335
|
-
if (!isInteractiveMode()) {
|
|
1336
|
-
console.error(` [!] Hypothesis check failed: ${error}`);
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
/**
|
|
1341
|
-
* Discover patterns from accumulated issues
|
|
1342
|
-
* Patterns emerge naturally from your coding workflow
|
|
1343
|
-
*/
|
|
1344
|
-
async discoverPatternsFromIssues(projectPath) {
|
|
1345
|
-
const totalIssues = Array.from(this.state.issueCache.values()).flat().length;
|
|
1346
|
-
if (totalIssues < 5) return;
|
|
1347
|
-
try {
|
|
1348
|
-
const { ContextGraph: ContextGraph2 } = await import("./graph-J4OGTYCO.js");
|
|
1349
|
-
const { IncidentIndex } = await import("./incident-index-BWW2UEY7.js");
|
|
1350
|
-
const { TriePatternDiscovery } = await import("./pattern-discovery-F7LU5K6E.js");
|
|
1351
|
-
const graph = new ContextGraph2(projectPath);
|
|
1352
|
-
const incidentIndex = await IncidentIndex.build(graph, projectPath);
|
|
1353
|
-
const discovery = new TriePatternDiscovery(graph, incidentIndex);
|
|
1354
|
-
const hotPatterns = discovery.discoverHotPatterns(2);
|
|
1355
|
-
for (const hot of hotPatterns) {
|
|
1356
|
-
const existingPatterns = await graph.listNodes();
|
|
1357
|
-
const alreadyExists = existingPatterns.some(
|
|
1358
|
-
(n) => n.type === "pattern" && n.data.description?.includes(hot.path)
|
|
1359
|
-
);
|
|
1360
|
-
if (!alreadyExists) {
|
|
1361
|
-
await graph.addNode("pattern", {
|
|
1362
|
-
description: `${hot.type === "directory" ? "Directory" : "File"} hot zone: ${hot.path}`,
|
|
1363
|
-
appliesTo: [hot.path],
|
|
1364
|
-
confidence: Math.min(0.95, hot.confidence),
|
|
1365
|
-
occurrences: hot.incidentCount,
|
|
1366
|
-
firstSeen: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1367
|
-
lastSeen: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1368
|
-
isAntiPattern: hot.incidentCount >= 3,
|
|
1369
|
-
// 3+ incidents = anti-pattern
|
|
1370
|
-
source: "local"
|
|
1371
|
-
});
|
|
1372
|
-
if (!isInteractiveMode()) {
|
|
1373
|
-
console.error(` [+] Pattern discovered: ${hot.path} (${hot.incidentCount} issues)`);
|
|
1374
|
-
}
|
|
1375
|
-
if (this.streamingManager) {
|
|
1376
|
-
this.streamingManager.reportSignalExtraction({
|
|
1377
|
-
governance: 0,
|
|
1378
|
-
facts: 1,
|
|
1379
|
-
// Patterns are facts about the codebase
|
|
1380
|
-
blockers: 0,
|
|
1381
|
-
questions: 0
|
|
1382
|
-
});
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
}
|
|
1386
|
-
if (totalIssues >= 10) {
|
|
1387
|
-
const coOccurrences = await discovery.discoverCoOccurrences(2);
|
|
1388
|
-
for (const coOcc of coOccurrences.slice(0, 3)) {
|
|
1389
|
-
const desc = `Files break together: ${coOcc.files[0]} + ${coOcc.files[1]}`;
|
|
1390
|
-
const existingPatterns = await graph.listNodes();
|
|
1391
|
-
const alreadyExists = existingPatterns.some(
|
|
1392
|
-
(n) => n.type === "pattern" && n.data.description === desc
|
|
1393
|
-
);
|
|
1394
|
-
if (!alreadyExists) {
|
|
1395
|
-
await graph.addNode("pattern", {
|
|
1396
|
-
description: desc,
|
|
1397
|
-
appliesTo: [...coOcc.files],
|
|
1398
|
-
confidence: Math.min(0.95, coOcc.confidence),
|
|
1399
|
-
occurrences: coOcc.coOccurrences,
|
|
1400
|
-
firstSeen: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1401
|
-
lastSeen: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1402
|
-
isAntiPattern: coOcc.confidence > 0.7,
|
|
1403
|
-
source: "local"
|
|
1404
|
-
});
|
|
1405
|
-
if (!isInteractiveMode()) {
|
|
1406
|
-
console.error(` [+] Co-occurrence pattern: ${coOcc.files[0]} + ${coOcc.files[1]}`);
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
} catch (error) {
|
|
1412
|
-
if (!isInteractiveMode()) {
|
|
1413
|
-
console.error(` [!] Pattern discovery failed: ${error}`);
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
}
|
|
1417
|
-
// Defaults -- overridden by config when loaded
|
|
1418
|
-
aiWatcherCooldownMs = 3e4;
|
|
1419
|
-
cleanFileCooldownMs = 3e5;
|
|
1420
|
-
maxFilesPerScan = 5;
|
|
1421
|
-
maxCharsPerFile = 4e3;
|
|
1422
|
-
/**
|
|
1423
|
-
* Use the trie (context graph) to score how urgently a file needs scanning.
|
|
1424
|
-
* Higher score = more worth spending tokens on.
|
|
1425
|
-
*/
|
|
1426
|
-
async scoreScanPriority(file, graph, projectPath) {
|
|
1427
|
-
let score = 1;
|
|
1428
|
-
const lastClean = this.state.cleanFiles.get(file);
|
|
1429
|
-
if (lastClean && Date.now() - lastClean < this.cleanFileCooldownMs) {
|
|
1430
|
-
return 0;
|
|
1431
|
-
}
|
|
1432
|
-
const fileNode = await graph.getNode("file", join2(projectPath, file));
|
|
1433
|
-
if (!fileNode) return score;
|
|
1434
|
-
const data = fileNode.data;
|
|
1435
|
-
const riskScores = { critical: 10, high: 6, medium: 2, low: 1 };
|
|
1436
|
-
score += riskScores[data.riskLevel] ?? 1;
|
|
1437
|
-
score += Math.min(data.incidentCount * 3, 12);
|
|
1438
|
-
if (data.changeCount > 10) score += 2;
|
|
1439
|
-
return score;
|
|
1440
|
-
}
|
|
1441
|
-
/**
|
|
1442
|
-
* Roll the token budget window if an hour has passed.
|
|
1443
|
-
* Returns remaining tokens in the current window.
|
|
1444
|
-
*/
|
|
1445
|
-
getRemainingBudget() {
|
|
1446
|
-
const budget = this.state.tokenBudget;
|
|
1447
|
-
const elapsed = Date.now() - budget.windowStart;
|
|
1448
|
-
if (elapsed > 36e5) {
|
|
1449
|
-
budget.used = 0;
|
|
1450
|
-
budget.windowStart = Date.now();
|
|
1451
|
-
}
|
|
1452
|
-
return Math.max(0, budget.hourlyLimit - budget.used);
|
|
1453
|
-
}
|
|
1454
|
-
recordTokenUsage(tokens) {
|
|
1455
|
-
this.state.tokenBudget.used += tokens;
|
|
1456
|
-
}
|
|
1457
|
-
/**
|
|
1458
|
-
* AI-powered watcher -- the primary detection system.
|
|
1459
|
-
*
|
|
1460
|
-
* This is the single AI call that handles:
|
|
1461
|
-
* 1. Code review (bugs, security, logic errors)
|
|
1462
|
-
* 2. Goal violation detection (user-defined quality goals)
|
|
1463
|
-
*
|
|
1464
|
-
* When goals are active, files with goal violations bypass priority scoring
|
|
1465
|
-
* so violations are always caught. Throttled by cooldown + token budget.
|
|
1466
|
-
*/
|
|
1467
|
-
async autoScanFiles(files) {
|
|
1468
|
-
if (!isAIAvailable()) return;
|
|
1469
|
-
if (this.state.autoScanInProgress) return;
|
|
1470
|
-
const now = Date.now();
|
|
1471
|
-
if (now - this.state.lastAutoScan < this.aiWatcherCooldownMs) return;
|
|
1472
|
-
this.state.autoScanInProgress = true;
|
|
1473
|
-
this.state.lastAutoScan = now;
|
|
1474
|
-
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
1475
|
-
try {
|
|
1476
|
-
try {
|
|
1477
|
-
const config = await getAutonomyConfig(projectPath);
|
|
1478
|
-
const wc = config.aiWatcher;
|
|
1479
|
-
if (!wc.enabled) return;
|
|
1480
|
-
this.state.tokenBudget.hourlyLimit = wc.hourlyTokenLimit;
|
|
1481
|
-
this.aiWatcherCooldownMs = wc.scanCooldownSec * 1e3;
|
|
1482
|
-
this.cleanFileCooldownMs = wc.cleanFileCooldownSec * 1e3;
|
|
1483
|
-
this.maxFilesPerScan = wc.maxFilesPerScan;
|
|
1484
|
-
this.maxCharsPerFile = wc.maxCharsPerFile;
|
|
1485
|
-
} catch {
|
|
1486
|
-
}
|
|
1487
|
-
const remaining = this.getRemainingBudget();
|
|
1488
|
-
if (remaining < 500) return;
|
|
1489
|
-
try {
|
|
1490
|
-
const graph = new ContextGraph(projectPath);
|
|
1491
|
-
const { getActiveGoals, recordGoalViolationCaught } = await import("./goal-validator-DV6DRSGF.js");
|
|
1492
|
-
console.debug("[AI Watcher] Loading active goals...");
|
|
1493
|
-
const activeGoals = await getActiveGoals(projectPath);
|
|
1494
|
-
const hasGoals = activeGoals.length > 0;
|
|
1495
|
-
console.debug("[AI Watcher] Goals loaded:", {
|
|
1496
|
-
totalGoals: activeGoals.length,
|
|
1497
|
-
hasGoals,
|
|
1498
|
-
goals: activeGoals.map((g) => ({ id: g.id, description: g.description, status: g.status }))
|
|
1499
|
-
});
|
|
1500
|
-
if (this.isQuiet() && !hasGoals) return;
|
|
1501
|
-
const scored = [];
|
|
1502
|
-
for (const file of files) {
|
|
1503
|
-
const relativePath = file.replace(projectPath + "/", "");
|
|
1504
|
-
const score = await this.scoreScanPriority(relativePath, graph, projectPath);
|
|
1505
|
-
if (hasGoals || score > 0) {
|
|
1506
|
-
scored.push({ file, relativePath, score: Math.max(score, hasGoals ? 1 : 0) });
|
|
1507
|
-
} else {
|
|
1508
|
-
this.state.tokenBudget.scansSaved++;
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
if (scored.length === 0) return;
|
|
1512
|
-
scored.sort((a, b) => b.score - a.score);
|
|
1513
|
-
const budgetScale = remaining > 2e4 ? 1 : remaining > 1e4 ? 0.6 : 0.4;
|
|
1514
|
-
const maxFiles = Math.max(1, Math.round(this.maxFilesPerScan * budgetScale));
|
|
1515
|
-
const filesToScan = scored.slice(0, maxFiles);
|
|
1516
|
-
const charLimit = Math.round(this.maxCharsPerFile * (remaining > 15e3 ? 1 : 0.5));
|
|
1517
|
-
const fileContents = await Promise.all(
|
|
1518
|
-
filesToScan.map(async ({ file, relativePath }) => {
|
|
1519
|
-
try {
|
|
1520
|
-
const content = await readFile2(file, "utf-8");
|
|
1521
|
-
return { path: relativePath, content: content.slice(0, charLimit) };
|
|
1522
|
-
} catch {
|
|
1523
|
-
return null;
|
|
1524
|
-
}
|
|
1525
|
-
})
|
|
1526
|
-
);
|
|
1527
|
-
const valid = fileContents.filter(Boolean);
|
|
1528
|
-
if (valid.length === 0) return;
|
|
1529
|
-
const filesBlock = valid.map(
|
|
1530
|
-
(f) => `### ${f.path}
|
|
1531
|
-
\`\`\`
|
|
1532
|
-
${f.content}
|
|
1533
|
-
\`\`\``
|
|
1534
|
-
).join("\n\n");
|
|
1535
|
-
let goalsSection = "";
|
|
1536
|
-
if (hasGoals) {
|
|
1537
|
-
goalsSection = `
|
|
1538
|
-
USER-DEFINED GOALS (IMPORTANT - check EVERY file against ALL goals):
|
|
1539
|
-
${activeGoals.map((g, i) => ` ${i + 1}. "${g.description}"`).join("\n")}
|
|
1540
|
-
|
|
1541
|
-
Goal violations are HIGH PRIORITY. If a file violates any goal, you MUST report it.
|
|
1542
|
-
`;
|
|
1543
|
-
}
|
|
1544
|
-
console.debug("[AI Watcher] Sending files to AI analysis:", {
|
|
1545
|
-
fileCount: valid.length,
|
|
1546
|
-
hasGoals,
|
|
1547
|
-
goalsIncluded: hasGoals,
|
|
1548
|
-
filePaths: valid.map((f) => f.path),
|
|
1549
|
-
goalsSection: goalsSection.slice(0, 200) + (goalsSection.length > 200 ? "..." : "")
|
|
1550
|
-
});
|
|
1551
|
-
const result = await runAIAnalysis({
|
|
1552
|
-
systemPrompt: `You are a code quality watcher. You review code for two things:
|
|
1553
|
-
|
|
1554
|
-
1. CODE ISSUES: bugs, security vulnerabilities, logic errors, risky patterns
|
|
1555
|
-
2. GOAL VIOLATIONS: check every file against the user's quality goals
|
|
1556
|
-
${goalsSection}
|
|
1557
|
-
Reply ONLY with a JSON array. Each element must have:
|
|
1558
|
-
- "file": relative file path
|
|
1559
|
-
- "severity": "critical" | "major" | "minor"
|
|
1560
|
-
- "description": 1-sentence description of what you found
|
|
1561
|
-
- "confidence": number 0-100, how confident you are this is a real issue
|
|
1562
|
-
- "suggestedFix": 1-sentence description of what should change to fix it
|
|
1563
|
-
- "isGoalViolation": true if this violates a user goal, false otherwise
|
|
1564
|
-
- "goalIndex": 0-based index of the violated goal (only if isGoalViolation is true)
|
|
1565
|
-
|
|
1566
|
-
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.
|
|
1567
|
-
|
|
1568
|
-
If no issues or violations found, reply with: []
|
|
1569
|
-
Output ONLY the JSON array, no markdown fences, no commentary.`,
|
|
1570
|
-
userPrompt: `Review these changed files:
|
|
1571
|
-
|
|
1572
|
-
${filesBlock}`,
|
|
1573
|
-
maxTokens: 2048,
|
|
1574
|
-
temperature: 0.1
|
|
1575
|
-
});
|
|
1576
|
-
if (result.tokensUsed) {
|
|
1577
|
-
this.recordTokenUsage(result.tokensUsed.input + result.tokensUsed.output);
|
|
1578
|
-
}
|
|
1579
|
-
console.debug("[AI Watcher] AI analysis result:", {
|
|
1580
|
-
success: result.success,
|
|
1581
|
-
contentLength: result.content ? result.content.length : 0,
|
|
1582
|
-
tokensUsed: result.tokensUsed,
|
|
1583
|
-
contentPreview: result.content ? result.content.slice(0, 500) : "null"
|
|
1584
|
-
});
|
|
1585
|
-
if (!result.success || !result.content.trim()) {
|
|
1586
|
-
console.debug("[AI Watcher] AI analysis failed or returned empty content");
|
|
1587
|
-
return;
|
|
1588
|
-
}
|
|
1589
|
-
let issues = [];
|
|
1590
|
-
try {
|
|
1591
|
-
const cleaned = result.content.replace(/```json?\n?|\n?```/g, "").trim();
|
|
1592
|
-
console.debug("[AI Watcher] Parsing AI response:", { cleanedContent: cleaned.slice(0, 300) });
|
|
1593
|
-
issues = JSON.parse(cleaned);
|
|
1594
|
-
if (!Array.isArray(issues)) issues = [];
|
|
1595
|
-
console.debug("[AI Watcher] Parsed issues:", {
|
|
1596
|
-
totalIssues: issues.length,
|
|
1597
|
-
goalViolations: issues.filter((i) => i.isGoalViolation).length,
|
|
1598
|
-
issueTypes: issues.map((i) => ({ file: i.file, isGoalViolation: i.isGoalViolation, goalIndex: i.goalIndex }))
|
|
1599
|
-
});
|
|
1600
|
-
} catch (error) {
|
|
1601
|
-
console.debug("[AI Watcher] Failed to parse AI response:", error);
|
|
1602
|
-
return;
|
|
1603
|
-
}
|
|
1604
|
-
const issuedFiles = new Set(issues.map((i) => i.file));
|
|
1605
|
-
for (const { relativePath } of filesToScan) {
|
|
1606
|
-
if (!issuedFiles.has(relativePath)) {
|
|
1607
|
-
this.state.cleanFiles.set(relativePath, Date.now());
|
|
1608
|
-
}
|
|
1609
|
-
}
|
|
1610
|
-
if (issues.length === 0) return;
|
|
1611
|
-
for (const issue of issues.slice(0, 10)) {
|
|
1612
|
-
const severity = issue.severity === "critical" ? "critical" : issue.severity === "major" ? "major" : "minor";
|
|
1613
|
-
if (issue.isGoalViolation && issue.goalIndex != null && issue.goalIndex >= 0 && issue.goalIndex < activeGoals.length) {
|
|
1614
|
-
const goal = activeGoals[issue.goalIndex];
|
|
1615
|
-
if (!goal) continue;
|
|
1616
|
-
const confidence = Math.min(100, Math.max(0, issue.confidence ?? 80));
|
|
1617
|
-
const fixId = `fix-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1618
|
-
console.debug("[AI Watcher] Goal violation detected:", {
|
|
1619
|
-
goalDescription: goal.description,
|
|
1620
|
-
file: issue.file,
|
|
1621
|
-
violation: issue.description,
|
|
1622
|
-
confidence,
|
|
1623
|
-
goalIndex: issue.goalIndex,
|
|
1624
|
-
totalActiveGoals: activeGoals.length
|
|
1625
|
-
});
|
|
1626
|
-
const goalViolationIssue = {
|
|
1627
|
-
id: fixId,
|
|
1628
|
-
file: issue.file,
|
|
1629
|
-
agent: "goal-violation",
|
|
1630
|
-
severity: severity === "critical" ? "critical" : severity === "major" ? "serious" : "moderate",
|
|
1631
|
-
issue: `Goal "${goal.description}" violated: ${issue.description}`,
|
|
1632
|
-
fix: issue.suggestedFix || "Review and fix",
|
|
1633
|
-
category: "goal-violation",
|
|
1634
|
-
confidence,
|
|
1635
|
-
autoFixable: true
|
|
1636
|
-
};
|
|
1637
|
-
await storeIssues([goalViolationIssue], basename2(projectPath), projectPath);
|
|
1638
|
-
await recordGoalViolationCaught(goal, issue.file, projectPath);
|
|
1639
|
-
const confidenceStr = `${confidence}%`;
|
|
1640
|
-
const nudgeMsg = `Goal "${goal.description}" violated in ${issue.file}: ${issue.description} [${confidenceStr} confidence]`;
|
|
1641
|
-
console.debug("[AI Watcher] Sending nudge:", {
|
|
1642
|
-
message: nudgeMsg,
|
|
1643
|
-
file: issue.file,
|
|
1644
|
-
severity: "warning"
|
|
1645
|
-
});
|
|
1646
|
-
getOutputManager().nudge(nudgeMsg, "warning", issue.file);
|
|
1647
|
-
await this.persistNudge({
|
|
1648
|
-
message: nudgeMsg,
|
|
1649
|
-
severity: "warning",
|
|
1650
|
-
file: issue.file,
|
|
1651
|
-
category: "quality",
|
|
1652
|
-
goalId: goal.id,
|
|
1653
|
-
priority: 7,
|
|
1654
|
-
suggestedAction: issue.suggestedFix || "Review and fix manually",
|
|
1655
|
-
relatedIssues: [fixId]
|
|
1656
|
-
});
|
|
1657
|
-
if (this.streamingManager) {
|
|
1658
|
-
this.streamingManager.reportPendingFix({
|
|
1659
|
-
id: fixId,
|
|
1660
|
-
file: issue.file,
|
|
1661
|
-
description: issue.description,
|
|
1662
|
-
goalDescription: goal.description,
|
|
1663
|
-
confidence,
|
|
1664
|
-
severity: issue.severity,
|
|
1665
|
-
suggestedFix: issue.suggestedFix || "Remove the violating code"
|
|
1666
|
-
});
|
|
1667
|
-
}
|
|
1668
|
-
if (!isInteractiveMode()) {
|
|
1669
|
-
console.error(` [!] Goal violation (${confidenceStr}): ${issue.description}`);
|
|
1670
|
-
console.error(` Fix: ${issue.suggestedFix || "Review and fix manually"}`);
|
|
1671
|
-
}
|
|
1672
|
-
continue;
|
|
1673
|
-
}
|
|
1674
|
-
const incident = await graph.addNode("incident", {
|
|
1675
|
-
description: issue.description,
|
|
1676
|
-
severity,
|
|
1677
|
-
affectedUsers: null,
|
|
1678
|
-
duration: null,
|
|
1679
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1680
|
-
resolved: false,
|
|
1681
|
-
resolution: null,
|
|
1682
|
-
fixChangeId: null,
|
|
1683
|
-
reportedVia: "detected"
|
|
1684
|
-
});
|
|
1685
|
-
const filePath = join2(projectPath, issue.file);
|
|
1686
|
-
const fileNode = await graph.getNode("file", filePath);
|
|
1687
|
-
if (fileNode) {
|
|
1688
|
-
await graph.addEdge(fileNode.id, incident.id, "affects");
|
|
1689
|
-
const data = fileNode.data;
|
|
1690
|
-
const newRisk = severity === "critical" ? "critical" : severity === "major" ? "high" : data.riskLevel === "low" ? "medium" : data.riskLevel;
|
|
1691
|
-
await graph.updateNode("file", fileNode.id, {
|
|
1692
|
-
incidentCount: (data.incidentCount ?? 0) + 1,
|
|
1693
|
-
riskLevel: newRisk
|
|
1694
|
-
});
|
|
1695
|
-
}
|
|
1696
|
-
this.state.totalIssuesFound++;
|
|
1697
|
-
if (severity !== "minor") {
|
|
1698
|
-
getOutputManager().nudge(
|
|
1699
|
-
`${issue.description}`,
|
|
1700
|
-
severity === "critical" ? "critical" : "warning",
|
|
1701
|
-
issue.file,
|
|
1702
|
-
severity === "critical" ? void 0 : 15e3
|
|
1703
|
-
);
|
|
1704
|
-
await this.persistNudge({
|
|
1705
|
-
message: issue.description,
|
|
1706
|
-
severity: severity === "critical" ? "critical" : "warning",
|
|
1707
|
-
file: issue.file,
|
|
1708
|
-
category: "quality",
|
|
1709
|
-
priority: severity === "critical" ? 9 : 6,
|
|
1710
|
-
...issue.suggestedFix && { suggestedAction: issue.suggestedFix }
|
|
1711
|
-
});
|
|
1712
|
-
}
|
|
1713
|
-
}
|
|
1714
|
-
if (this.streamingManager && issues.length > 0) {
|
|
1715
|
-
this.streamingManager.reportSignalExtraction({
|
|
1716
|
-
governance: 0,
|
|
1717
|
-
facts: 0,
|
|
1718
|
-
blockers: issues.length,
|
|
1719
|
-
questions: 0
|
|
1720
|
-
});
|
|
1721
|
-
}
|
|
1722
|
-
} catch (error) {
|
|
1723
|
-
getOutputManager().log("warn", `AI watcher error: ${error}`);
|
|
1724
|
-
}
|
|
1725
|
-
} finally {
|
|
1726
|
-
this.state.autoScanInProgress = false;
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
/**
|
|
1730
|
-
* Initial hypothesis generation when watch starts.
|
|
1731
|
-
*
|
|
1732
|
-
* This generates hypotheses from existing patterns and issues to give
|
|
1733
|
-
* users immediate insights about their codebase.
|
|
1734
|
-
*/
|
|
1735
|
-
async initialHypothesisGeneration() {
|
|
1736
|
-
if (!isAIAvailable()) {
|
|
1737
|
-
console.debug("[Initial Hypothesis] AI not available, skipping initial hypothesis generation");
|
|
1738
|
-
return;
|
|
1739
|
-
}
|
|
1740
|
-
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
1741
|
-
console.debug("[Initial Hypothesis] Starting initial hypothesis generation", { projectPath });
|
|
1742
|
-
try {
|
|
1743
|
-
const { getHypothesisEngine } = await import("./hypothesis-O72ZLVOW.js");
|
|
1744
|
-
const hypothesisEngine = getHypothesisEngine(projectPath);
|
|
1745
|
-
console.debug("[Initial Hypothesis] Running AI-powered hypothesis generation...");
|
|
1746
|
-
const generated = await hypothesisEngine.generateHypothesesWithAI({
|
|
1747
|
-
recentIssues: [],
|
|
1748
|
-
// No recent issues yet on startup
|
|
1749
|
-
patterns: [],
|
|
1750
|
-
observations: ["Initial watch session started", "Analyzing codebase for potential patterns"]
|
|
1751
|
-
});
|
|
1752
|
-
console.debug("[Initial Hypothesis] Generated hypotheses on startup:", {
|
|
1753
|
-
count: generated.length,
|
|
1754
|
-
hypotheses: generated.map((h) => ({ statement: h.statement, confidence: h.confidence }))
|
|
1755
|
-
});
|
|
1756
|
-
if (generated.length > 0) {
|
|
1757
|
-
const { getOutputManager: getOutputManager2 } = await import("./output-manager-DZO5LGSG.js");
|
|
1758
|
-
const outputManager = getOutputManager2();
|
|
1759
|
-
for (const hypothesis of generated.slice(0, 2)) {
|
|
1760
|
-
const message = `[Initial Hypothesis] "${hypothesis.statement}" (${Math.round(hypothesis.confidence * 100)}% confidence)`;
|
|
1761
|
-
outputManager.nudge(message, "info", void 0, 1e4);
|
|
1762
|
-
if (!isInteractiveMode()) {
|
|
1763
|
-
console.error(` [?] ${message}`);
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
if (this.streamingManager) {
|
|
1767
|
-
this.streamingManager.reportSignalExtraction({
|
|
1768
|
-
governance: 0,
|
|
1769
|
-
facts: 0,
|
|
1770
|
-
blockers: 0,
|
|
1771
|
-
questions: generated.length
|
|
1772
|
-
});
|
|
1773
|
-
}
|
|
1774
|
-
}
|
|
1775
|
-
} catch (error) {
|
|
1776
|
-
console.debug("[Initial Hypothesis] Failed to generate initial hypotheses:", error);
|
|
1777
|
-
}
|
|
1778
|
-
}
|
|
1779
|
-
/**
|
|
1780
|
-
* Initial goal compliance scan when watch starts.
|
|
1781
|
-
*
|
|
1782
|
-
* This checks recently modified files against active goals so the user
|
|
1783
|
-
* gets immediate feedback about goal violations without waiting for files
|
|
1784
|
-
* to be changed.
|
|
1785
|
-
*
|
|
1786
|
-
* Strategy:
|
|
1787
|
-
* 1. Check if there are any active goals - if not, skip
|
|
1788
|
-
* 2. Find recently modified files (last 24 hours OR uncommitted changes)
|
|
1789
|
-
* 3. Filter to watched file types
|
|
1790
|
-
* 4. Run a quick AI scan focused only on goal violations
|
|
1791
|
-
* 5. Nudge the user about any violations found
|
|
1792
|
-
*/
|
|
1793
|
-
async initialGoalComplianceScan() {
|
|
1794
|
-
if (!isAIAvailable()) {
|
|
1795
|
-
console.debug("[Initial Scan] AI not available, skipping initial goal compliance scan");
|
|
1796
|
-
return;
|
|
1797
|
-
}
|
|
1798
|
-
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
1799
|
-
console.debug("[Initial Scan] Starting initial goal compliance scan", { projectPath });
|
|
1800
|
-
try {
|
|
1801
|
-
const { getActiveGoals, recordGoalViolationCaught } = await import("./goal-validator-DV6DRSGF.js");
|
|
1802
|
-
const activeGoals = await getActiveGoals(projectPath);
|
|
1803
|
-
console.debug("[Initial Scan] Loaded goals for initial scan:", {
|
|
1804
|
-
goalCount: activeGoals.length,
|
|
1805
|
-
goals: activeGoals.map((g) => ({ id: g.id, description: g.description }))
|
|
1806
|
-
});
|
|
1807
|
-
if (activeGoals.length === 0) {
|
|
1808
|
-
console.debug("[Initial Scan] No active goals found, skipping initial scan");
|
|
1809
|
-
return;
|
|
1810
|
-
}
|
|
1811
|
-
if (!isInteractiveMode()) {
|
|
1812
|
-
console.error("[*] Checking recent files against active goals...");
|
|
1813
|
-
}
|
|
1814
|
-
const recentFiles = /* @__PURE__ */ new Set();
|
|
1815
|
-
const uncommittedFiles = await getGitChangedFiles(projectPath);
|
|
1816
|
-
if (uncommittedFiles) {
|
|
1817
|
-
uncommittedFiles.forEach((f) => recentFiles.add(join2(projectPath, f)));
|
|
1818
|
-
}
|
|
1819
|
-
const oneDayAgo = Date.now() - 24 * 60 * 60 * 1e3;
|
|
1820
|
-
const recentChanges = await getChangedFilesSinceTimestamp(projectPath, oneDayAgo);
|
|
1821
|
-
if (recentChanges) {
|
|
1822
|
-
recentChanges.forEach((f) => recentFiles.add(f));
|
|
1823
|
-
}
|
|
1824
|
-
const filesToCheck = Array.from(recentFiles).filter((file) => {
|
|
1825
|
-
const ext = extname2(file).toLowerCase();
|
|
1826
|
-
return WATCH_EXTENSIONS.has(ext) && existsSync2(file);
|
|
1827
|
-
});
|
|
1828
|
-
console.debug("[Initial Scan] Files discovered for initial scan:", {
|
|
1829
|
-
totalRecentFiles: recentFiles.size,
|
|
1830
|
-
filteredFiles: filesToCheck.length,
|
|
1831
|
-
filePaths: filesToCheck.slice(0, 5).map((f) => f.replace(projectPath + "/", "")),
|
|
1832
|
-
// Show first 5
|
|
1833
|
-
watchedExtensions: Array.from(WATCH_EXTENSIONS)
|
|
1834
|
-
});
|
|
1835
|
-
if (filesToCheck.length === 0) {
|
|
1836
|
-
console.debug("[Initial Scan] No recent files found for initial scan");
|
|
1837
|
-
return;
|
|
1838
|
-
}
|
|
1839
|
-
const maxInitialFiles = 10;
|
|
1840
|
-
const filesToScan = filesToCheck.slice(0, maxInitialFiles);
|
|
1841
|
-
if (!isInteractiveMode()) {
|
|
1842
|
-
console.error(` Scanning ${filesToScan.length} recent file(s) against ${activeGoals.length} goal(s)...`);
|
|
1843
|
-
}
|
|
1844
|
-
const maxCharsPerFile = 3e3;
|
|
1845
|
-
const fileContents = await Promise.all(
|
|
1846
|
-
filesToScan.map(async (file) => {
|
|
1847
|
-
try {
|
|
1848
|
-
const content = await readFile2(file, "utf-8");
|
|
1849
|
-
const relativePath = file.replace(projectPath + "/", "");
|
|
1850
|
-
return { path: relativePath, content: content.slice(0, maxCharsPerFile) };
|
|
1851
|
-
} catch {
|
|
1852
|
-
return null;
|
|
1853
|
-
}
|
|
1854
|
-
})
|
|
1855
|
-
);
|
|
1856
|
-
const valid = fileContents.filter(Boolean);
|
|
1857
|
-
if (valid.length === 0) return;
|
|
1858
|
-
const filesBlock = valid.map(
|
|
1859
|
-
(f) => `### ${f.path}
|
|
1860
|
-
\`\`\`
|
|
1861
|
-
${f.content}
|
|
1862
|
-
\`\`\``
|
|
1863
|
-
).join("\n\n");
|
|
1864
|
-
const goalsSection = `
|
|
1865
|
-
USER-DEFINED GOALS (check EVERY file against ALL goals):
|
|
1866
|
-
${activeGoals.map((g, i) => ` ${i + 1}. "${g.description}"`).join("\n")}
|
|
1867
|
-
|
|
1868
|
-
This is an INITIAL SCAN at watch startup. Report ALL goal violations you find.
|
|
1869
|
-
`;
|
|
1870
|
-
const result = await runAIAnalysis({
|
|
1871
|
-
systemPrompt: `You are checking code for GOAL VIOLATIONS ONLY.
|
|
1872
|
-
${goalsSection}
|
|
1873
|
-
Reply ONLY with a JSON array. Each element must have:
|
|
1874
|
-
- "file": relative file path
|
|
1875
|
-
- "severity": "critical" | "major" | "minor"
|
|
1876
|
-
- "description": 1-sentence description of the goal violation
|
|
1877
|
-
- "confidence": number 0-100, how confident you are this is a violation
|
|
1878
|
-
- "suggestedFix": 1-sentence description of what should change
|
|
1879
|
-
- "isGoalViolation": true (always true for this scan)
|
|
1880
|
-
- "goalIndex": 0-based index of the violated goal
|
|
1881
|
-
|
|
1882
|
-
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.
|
|
1883
|
-
|
|
1884
|
-
If no violations found, reply with: []
|
|
1885
|
-
Output ONLY the JSON array, no markdown fences, no commentary.`,
|
|
1886
|
-
userPrompt: `Check these files for goal violations:
|
|
1887
|
-
|
|
1888
|
-
${filesBlock}`,
|
|
1889
|
-
maxTokens: 2048,
|
|
1890
|
-
temperature: 0.1
|
|
1891
|
-
});
|
|
1892
|
-
if (result.tokensUsed) {
|
|
1893
|
-
this.recordTokenUsage(result.tokensUsed.input + result.tokensUsed.output);
|
|
1894
|
-
}
|
|
1895
|
-
if (!result.success || !result.content.trim()) return;
|
|
1896
|
-
let issues = [];
|
|
1897
|
-
try {
|
|
1898
|
-
const parsed = JSON.parse(result.content.trim());
|
|
1899
|
-
issues = Array.isArray(parsed) ? parsed : [];
|
|
1900
|
-
} catch {
|
|
1901
|
-
return;
|
|
1902
|
-
}
|
|
1903
|
-
const issuesToStore = [];
|
|
1904
|
-
let violationsFound = 0;
|
|
1905
|
-
for (const issue of issues) {
|
|
1906
|
-
if (!issue.isGoalViolation || issue.confidence < 40) continue;
|
|
1907
|
-
violationsFound++;
|
|
1908
|
-
if (issue.goalIndex != null && issue.goalIndex >= 0 && issue.goalIndex < activeGoals.length) {
|
|
1909
|
-
const goal = activeGoals[issue.goalIndex];
|
|
1910
|
-
if (!goal) continue;
|
|
1911
|
-
const fixId = `fix-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1912
|
-
issuesToStore.push({
|
|
1913
|
-
id: fixId,
|
|
1914
|
-
file: issue.file,
|
|
1915
|
-
agent: "goal-violation",
|
|
1916
|
-
severity: issue.severity === "critical" ? "critical" : issue.severity === "major" ? "serious" : "moderate",
|
|
1917
|
-
issue: `Goal "${goal.description}" violated: ${issue.description}`,
|
|
1918
|
-
fix: issue.suggestedFix || "Review and fix",
|
|
1919
|
-
category: "goal-violation",
|
|
1920
|
-
confidence: issue.confidence,
|
|
1921
|
-
autoFixable: true
|
|
1922
|
-
});
|
|
1923
|
-
await recordGoalViolationCaught(goal, issue.file, projectPath);
|
|
1924
|
-
const confidenceStr = issue.confidence >= 80 ? "high" : issue.confidence >= 60 ? "medium" : "low";
|
|
1925
|
-
const nudgeMsg = `Goal "${goal.description}" violated in ${issue.file}: ${issue.description} [${confidenceStr} confidence]`;
|
|
1926
|
-
getOutputManager().nudge(nudgeMsg, "warning", issue.file);
|
|
1927
|
-
await this.persistNudge({
|
|
1928
|
-
message: nudgeMsg,
|
|
1929
|
-
severity: "warning",
|
|
1930
|
-
file: issue.file,
|
|
1931
|
-
category: "quality",
|
|
1932
|
-
goalId: goal.id,
|
|
1933
|
-
priority: issue.confidence >= 80 ? 8 : issue.confidence >= 60 ? 6 : 4,
|
|
1934
|
-
...issue.suggestedFix && { suggestedAction: issue.suggestedFix }
|
|
1935
|
-
});
|
|
1936
|
-
}
|
|
1937
|
-
}
|
|
1938
|
-
if (issuesToStore.length > 0) {
|
|
1939
|
-
await storeIssues(issuesToStore, basename2(projectPath), projectPath);
|
|
1940
|
-
}
|
|
1941
|
-
if (!isInteractiveMode()) {
|
|
1942
|
-
if (violationsFound > 0) {
|
|
1943
|
-
console.error(` [!] Found ${violationsFound} goal violation(s) in recent files`);
|
|
1944
|
-
} else {
|
|
1945
|
-
console.error(` [\u2713] No goal violations found in recent files`);
|
|
1946
|
-
}
|
|
1947
|
-
}
|
|
1948
|
-
} catch (error) {
|
|
1949
|
-
if (!isInteractiveMode()) {
|
|
1950
|
-
console.error(` [!] Initial goal scan error: ${error}`);
|
|
1951
|
-
}
|
|
1952
|
-
}
|
|
1953
|
-
}
|
|
1954
|
-
async syncPipelineIntegrations() {
|
|
1955
|
-
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
1956
|
-
try {
|
|
1957
|
-
const config = await loadConfig();
|
|
1958
|
-
const hasLinear = !!(config.apiKeys?.linear ?? process.env.LINEAR_API_KEY);
|
|
1959
|
-
const hasGithub = !!(config.apiKeys?.github ?? process.env.GITHUB_TOKEN);
|
|
1960
|
-
if (!hasLinear && !hasGithub) return;
|
|
1961
|
-
const graph = new ContextGraph(projectPath);
|
|
1962
|
-
if (hasLinear) {
|
|
1963
|
-
try {
|
|
1964
|
-
const { LinearIngester: LinearIngester2 } = await import("./linear-ingester-JRDQAIAA.js");
|
|
1965
|
-
const ingester = new LinearIngester2(projectPath, graph);
|
|
1966
|
-
await ingester.syncTickets();
|
|
1967
|
-
if (!isInteractiveMode()) {
|
|
1968
|
-
console.error("[Pipeline] Linear tickets synced");
|
|
1969
|
-
}
|
|
1970
|
-
} catch (error) {
|
|
1971
|
-
if (!isInteractiveMode()) {
|
|
1972
|
-
console.error(`[Pipeline] Linear sync failed: ${error}`);
|
|
1973
|
-
}
|
|
1974
|
-
}
|
|
1975
|
-
}
|
|
1976
|
-
if (hasGithub) {
|
|
1977
|
-
try {
|
|
1978
|
-
const { GitHubIngester: GitHubIngester2 } = await import("./github-ingester-J2ZFYXVE.js");
|
|
1979
|
-
const ingester = new GitHubIngester2(graph);
|
|
1980
|
-
const token = await ingester.getApiToken();
|
|
1981
|
-
const repoInfo = ingester.getRepoInfo(projectPath);
|
|
1982
|
-
if (repoInfo) {
|
|
1983
|
-
await ingester.syncPullRequests(repoInfo.owner, repoInfo.name, token);
|
|
1984
|
-
await ingester.syncIssues(repoInfo.owner, repoInfo.name, token);
|
|
1985
|
-
if (!isInteractiveMode()) {
|
|
1986
|
-
console.error("[Pipeline] GitHub PRs/issues synced");
|
|
1987
|
-
}
|
|
1988
|
-
}
|
|
1989
|
-
} catch (error) {
|
|
1990
|
-
if (!isInteractiveMode()) {
|
|
1991
|
-
console.error(`[Pipeline] GitHub sync failed: ${error}`);
|
|
1992
|
-
}
|
|
1993
|
-
}
|
|
1994
|
-
}
|
|
1995
|
-
} catch {
|
|
1996
|
-
}
|
|
1997
|
-
}
|
|
1998
|
-
async stopWatching() {
|
|
1999
|
-
if (!this.state.isRunning) {
|
|
2000
|
-
return {
|
|
2001
|
-
content: [{
|
|
2002
|
-
type: "text",
|
|
2003
|
-
text: "[!] Watch mode is not running."
|
|
2004
|
-
}]
|
|
2005
|
-
};
|
|
2006
|
-
}
|
|
2007
|
-
for (const watcher of this.watchers.values()) {
|
|
2008
|
-
watcher.close();
|
|
2009
|
-
}
|
|
2010
|
-
this.watchers.clear();
|
|
2011
|
-
if (this.extractionPipeline) {
|
|
2012
|
-
this.extractionPipeline.close();
|
|
2013
|
-
this.extractionPipeline = null;
|
|
2014
|
-
}
|
|
2015
|
-
if (this.pipelineSyncTimer) {
|
|
2016
|
-
clearInterval(this.pipelineSyncTimer);
|
|
2017
|
-
this.pipelineSyncTimer = null;
|
|
2018
|
-
}
|
|
2019
|
-
if (this.codebaseIndex) {
|
|
2020
|
-
await this.codebaseIndex.save();
|
|
2021
|
-
this.codebaseIndex = null;
|
|
2022
|
-
}
|
|
2023
|
-
if (this.state.scanDebounceTimer) {
|
|
2024
|
-
clearTimeout(this.state.scanDebounceTimer);
|
|
2025
|
-
}
|
|
2026
|
-
this.state.isRunning = false;
|
|
2027
|
-
if (!isInteractiveMode()) {
|
|
2028
|
-
console.error("\n[*] Watch mode stopped.\n");
|
|
2029
|
-
}
|
|
2030
|
-
if (this.streamingManager) {
|
|
2031
|
-
this.streamingManager.reportWatchStatus({ watching: false, directories: 0 });
|
|
2032
|
-
}
|
|
2033
|
-
const outputManager = getOutputManager();
|
|
2034
|
-
outputManager.clearTUICallbacks();
|
|
2035
|
-
outputManager.setMode("console");
|
|
2036
|
-
if (this.dashboard) {
|
|
2037
|
-
this.dashboard.stop();
|
|
2038
|
-
this.dashboard = void 0;
|
|
2039
|
-
}
|
|
2040
|
-
const budget = this.state.tokenBudget;
|
|
2041
|
-
const tokensK = (budget.used / 1e3).toFixed(1);
|
|
2042
|
-
return {
|
|
2043
|
-
content: [{
|
|
2044
|
-
type: "text",
|
|
2045
|
-
text: `[*] **TRIE AGENT STOPPED**
|
|
2046
|
-
|
|
2047
|
-
### Session Summary:
|
|
2048
|
-
- Files scanned: ${this.state.filesScanned}
|
|
2049
|
-
- Issues found: ${this.state.totalIssuesFound}
|
|
2050
|
-
- Tokens used: ${tokensK}k / ${(budget.hourlyLimit / 1e3).toFixed(0)}k hourly limit
|
|
2051
|
-
- Scans skipped (trie-throttled): ${budget.scansSaved}
|
|
2052
|
-
- Governance ledger: Updated continuously
|
|
2053
|
-
|
|
2054
|
-
Use \`trie_watch start\` to resume.`
|
|
2055
|
-
}]
|
|
2056
|
-
};
|
|
2057
|
-
}
|
|
2058
|
-
async getStatus() {
|
|
2059
|
-
if (!this.state.isRunning) {
|
|
2060
|
-
return {
|
|
2061
|
-
content: [{
|
|
2062
|
-
type: "text",
|
|
2063
|
-
text: `[*] **Watch Mode Status: STOPPED**
|
|
2064
|
-
|
|
2065
|
-
Use \`trie_watch start\` to begin autonomous scanning.`
|
|
2066
|
-
}]
|
|
2067
|
-
};
|
|
2068
|
-
}
|
|
2069
|
-
const recentScans = Array.from(this.state.lastScan.entries()).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([file, time]) => {
|
|
2070
|
-
const ago = Math.round((Date.now() - time) / 1e3);
|
|
2071
|
-
return `- \`${basename2(file)}\` (${ago}s ago)`;
|
|
2072
|
-
}).join("\n");
|
|
2073
|
-
const recentNudges = this.state.nudges.slice(-3).map(
|
|
2074
|
-
(n) => `- ${basename2(n.file)} [${n.severity}] @ ${n.timestamp}`
|
|
2075
|
-
).join("\n");
|
|
2076
|
-
let agencyStatus = "";
|
|
2077
|
-
try {
|
|
2078
|
-
const { getTrieAgent } = await import("./trie-agent-ET3DAP5Y.js");
|
|
2079
|
-
const trieAgent = getTrieAgent(this.watchedDirectory || getWorkingDirectory(void 0, true));
|
|
2080
|
-
await trieAgent.initialize();
|
|
2081
|
-
const status = await trieAgent.getAgencyStatus();
|
|
2082
|
-
agencyStatus = `
|
|
2083
|
-
### [AI] Trie Agent:
|
|
2084
|
-
- **Goals:** ${status.goals.active} active, ${status.goals.completed} completed${status.goals.topGoal ? ` (${status.goals.topGoal.slice(0, 40)}...)` : ""}
|
|
2085
|
-
- **Hypotheses:** ${status.hypotheses.testing} testing, ${status.hypotheses.validated} validated
|
|
2086
|
-
- **Risk Level:** ${status.riskLevel.toUpperCase()}
|
|
2087
|
-
- **Effectiveness:** ${status.effectiveness}%
|
|
2088
|
-
- **Scan Frequency:** ${Math.round(status.scanFrequency / 1e3)}s${status.isQuietHours ? " (quiet hours)" : ""}`;
|
|
2089
|
-
} catch {
|
|
2090
|
-
}
|
|
2091
|
-
const budget = this.state.tokenBudget;
|
|
2092
|
-
const remaining = this.getRemainingBudget();
|
|
2093
|
-
const tokensK = (budget.used / 1e3).toFixed(1);
|
|
2094
|
-
const remainingK = (remaining / 1e3).toFixed(1);
|
|
2095
|
-
return {
|
|
2096
|
-
content: [{
|
|
2097
|
-
type: "text",
|
|
2098
|
-
text: `[*] **WATCH MODE Status: RUNNING**
|
|
2099
|
-
|
|
2100
|
-
### Stats:
|
|
2101
|
-
- Directories watched: ${this.watchers.size}
|
|
2102
|
-
- Files scanned: ${this.state.filesScanned}
|
|
2103
|
-
- Issues found: ${this.state.totalIssuesFound}
|
|
2104
|
-
- Pending: ${this.state.pendingFiles.size}
|
|
2105
|
-
|
|
2106
|
-
### Token Budget:
|
|
2107
|
-
- Used: ${tokensK}k / ${(budget.hourlyLimit / 1e3).toFixed(0)}k hourly
|
|
2108
|
-
- Remaining: ${remainingK}k
|
|
2109
|
-
- Scans skipped (trie-throttled): ${budget.scansSaved}
|
|
2110
|
-
${agencyStatus}
|
|
2111
|
-
|
|
2112
|
-
### Recently Scanned:
|
|
2113
|
-
${recentScans || "(none yet)"}
|
|
2114
|
-
|
|
2115
|
-
### Recent Nudges:
|
|
2116
|
-
${recentNudges || "(none)"}
|
|
2117
|
-
|
|
2118
|
-
### Commands:
|
|
2119
|
-
- \`trie_watch issues\` - Get all issues found
|
|
2120
|
-
- \`trie_watch stop\` - Stop watching`
|
|
2121
|
-
}]
|
|
2122
|
-
};
|
|
2123
|
-
}
|
|
2124
|
-
/**
|
|
2125
|
-
* Helper to persist a nudge to both in-memory state and SQL storage
|
|
2126
|
-
*/
|
|
2127
|
-
async persistNudge(params) {
|
|
2128
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2129
|
-
const nudgeId = createHash("sha256").update(`${params.message}|${params.file || ""}|${timestamp}`).digest("hex").slice(0, 16);
|
|
2130
|
-
const nudge = {
|
|
2131
|
-
id: nudgeId,
|
|
2132
|
-
message: params.message,
|
|
2133
|
-
severity: params.severity,
|
|
2134
|
-
timestamp,
|
|
2135
|
-
resolved: false,
|
|
2136
|
-
dismissed: false,
|
|
2137
|
-
priority: params.priority ?? 5,
|
|
2138
|
-
relatedIssues: params.relatedIssues ?? [],
|
|
2139
|
-
metadata: {},
|
|
2140
|
-
...params.file && { file: params.file },
|
|
2141
|
-
...params.category && { category: params.category },
|
|
2142
|
-
...params.goalId && { goalId: params.goalId },
|
|
2143
|
-
...params.suggestedAction && { suggestedAction: params.suggestedAction }
|
|
2144
|
-
};
|
|
2145
|
-
this.state.nudges.push({
|
|
2146
|
-
file: params.file || "unknown",
|
|
2147
|
-
// Keep full path for consistency with loaded nudges
|
|
2148
|
-
message: params.message,
|
|
2149
|
-
severity: params.severity === "warning" || params.severity === "info" ? "high" : params.severity,
|
|
2150
|
-
timestamp: (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false })
|
|
2151
|
-
});
|
|
2152
|
-
try {
|
|
2153
|
-
const storage = getStorage(this.watchedDirectory);
|
|
2154
|
-
await storage.storeNudge(nudge);
|
|
2155
|
-
console.debug(`[Watch] \u2713 Persisted nudge to storage: ${nudge.message.slice(0, 60)}...`);
|
|
2156
|
-
} catch (error) {
|
|
2157
|
-
console.error("[Watch] Failed to persist nudge to storage:", error);
|
|
2158
|
-
}
|
|
2159
|
-
}
|
|
2160
|
-
getNudges() {
|
|
2161
|
-
return {
|
|
2162
|
-
content: [
|
|
2163
|
-
{
|
|
2164
|
-
type: "text",
|
|
2165
|
-
text: `[#] Recent nudges (${this.state.nudges.length} this session)
|
|
2166
|
-
` + (this.state.nudges.length ? this.state.nudges.map(
|
|
2167
|
-
(n) => `- ${basename2(n.file)} [${n.severity}] @ ${n.timestamp}
|
|
2168
|
-
${n.message}`
|
|
2169
|
-
).join("\n") : "(none)")
|
|
2170
|
-
},
|
|
2171
|
-
{
|
|
2172
|
-
type: "json",
|
|
2173
|
-
json: this.state.nudges
|
|
2174
|
-
}
|
|
2175
|
-
]
|
|
2176
|
-
};
|
|
2177
|
-
}
|
|
2178
|
-
getCurrentIssues() {
|
|
2179
|
-
return {
|
|
2180
|
-
content: [{
|
|
2181
|
-
type: "text",
|
|
2182
|
-
text: `[L] **Issues Found This Session**
|
|
2183
|
-
|
|
2184
|
-
Total issues: ${this.state.totalIssuesFound}
|
|
2185
|
-
Files scanned: ${this.state.filesScanned}
|
|
2186
|
-
|
|
2187
|
-
To get a full report, run \`trie_scan\` on your codebase.`
|
|
2188
|
-
}]
|
|
2189
|
-
};
|
|
2190
|
-
}
|
|
2191
|
-
};
|
|
2192
|
-
|
|
2193
|
-
// src/tools/pr-review.ts
|
|
2194
|
-
import { readFile as readFile3 } from "fs/promises";
|
|
2195
|
-
import { existsSync as existsSync3 } from "fs";
|
|
2196
|
-
import { join as join3, basename as basename3, resolve as resolve2, isAbsolute as isAbsolute2 } from "path";
|
|
2197
|
-
|
|
2198
|
-
// src/skills/built-in/super-reviewer.ts
|
|
2199
|
-
var CRITICAL_REVIEW_CHECKLIST = {
|
|
2200
|
-
stateAndLifecycle: [
|
|
2201
|
-
"Uninitialized state accessed before setup",
|
|
2202
|
-
"Missing cleanup on unmount/dispose",
|
|
2203
|
-
"State mutations in wrong lifecycle phase"
|
|
2204
|
-
],
|
|
2205
|
-
edgeCasesAndRaces: [
|
|
2206
|
-
"Race conditions in async operations",
|
|
2207
|
-
"Missing error handling for edge cases",
|
|
2208
|
-
"Unhandled promise rejections"
|
|
2209
|
-
],
|
|
2210
|
-
missingPieces: [
|
|
2211
|
-
"Missing input validation",
|
|
2212
|
-
"Missing error handling",
|
|
2213
|
-
"Missing logging/monitoring"
|
|
2214
|
-
]
|
|
2215
|
-
};
|
|
2216
|
-
var SuperReviewerAgent = class {
|
|
2217
|
-
async review(_files) {
|
|
2218
|
-
return { issues: [] };
|
|
2219
|
-
}
|
|
2220
|
-
async buildReviewWorkflow(files, context) {
|
|
2221
|
-
return { files, context };
|
|
2222
|
-
}
|
|
2223
|
-
};
|
|
2224
|
-
|
|
2225
781
|
// src/tools/pr-review.ts
|
|
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";
|
|
2226
785
|
var TriePRReviewTool = class {
|
|
2227
|
-
|
|
786
|
+
// Review workflow is now ledger-driven; keep a lightweight built-in checklist.
|
|
787
|
+
CRITICAL_REVIEW_CHECKLIST = {
|
|
788
|
+
stateAndLifecycle: [
|
|
789
|
+
"Uninitialized state accessed before setup",
|
|
790
|
+
"Missing cleanup on unmount/dispose",
|
|
791
|
+
"State mutations in wrong lifecycle phase"
|
|
792
|
+
],
|
|
793
|
+
edgeCasesAndRaces: [
|
|
794
|
+
"Race conditions in async operations",
|
|
795
|
+
"Missing error handling for edge cases",
|
|
796
|
+
"Unhandled promise rejections"
|
|
797
|
+
],
|
|
798
|
+
missingPieces: [
|
|
799
|
+
"Missing input validation",
|
|
800
|
+
"Missing error handling",
|
|
801
|
+
"Missing logging/monitoring"
|
|
802
|
+
]
|
|
803
|
+
};
|
|
2228
804
|
exec(command, cwd, maxBuffer) {
|
|
2229
805
|
const opts = {
|
|
2230
806
|
captureOutput: false,
|
|
@@ -2272,18 +848,15 @@ Usage:
|
|
|
2272
848
|
};
|
|
2273
849
|
}
|
|
2274
850
|
const designDocs = await this.findDesignDocs(changes.files, prInfo);
|
|
2275
|
-
const workflow =
|
|
2276
|
-
|
|
2277
|
-
{
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
designDocs
|
|
2285
|
-
}
|
|
2286
|
-
);
|
|
851
|
+
const workflow = this.buildReviewWorkflow(changes.files, {
|
|
852
|
+
...prInfo.number ? { prNumber: prInfo.number } : {},
|
|
853
|
+
...prInfo.title ? { prTitle: prInfo.title } : {},
|
|
854
|
+
...prInfo.author ? { prAuthor: prInfo.author } : {},
|
|
855
|
+
...prInfo.baseBranch ? { baseBranch: prInfo.baseBranch } : {},
|
|
856
|
+
...prInfo.headBranch ? { headBranch: prInfo.headBranch } : {},
|
|
857
|
+
mode: mode || "own",
|
|
858
|
+
designDocs
|
|
859
|
+
});
|
|
2287
860
|
const fileContents = await this.preloadFiles(changes.files.map((f) => f.path));
|
|
2288
861
|
const reviewPrompt = this.generateReviewPrompt(workflow, changes, fileContents);
|
|
2289
862
|
return {
|
|
@@ -2307,14 +880,14 @@ Usage:
|
|
|
2307
880
|
async getPRInfo(pr, worktree) {
|
|
2308
881
|
if (worktree) {
|
|
2309
882
|
const worktreePath = isAbsolute2(worktree) ? worktree : resolve2(getWorkingDirectory(void 0, true), worktree);
|
|
2310
|
-
if (!
|
|
883
|
+
if (!existsSync2(worktreePath)) {
|
|
2311
884
|
return { success: false, error: `Worktree not found: ${worktreePath}` };
|
|
2312
885
|
}
|
|
2313
886
|
return {
|
|
2314
887
|
success: true,
|
|
2315
888
|
type: "worktree",
|
|
2316
889
|
path: worktreePath,
|
|
2317
|
-
title: `Local changes in ${
|
|
890
|
+
title: `Local changes in ${basename2(worktreePath)}`,
|
|
2318
891
|
author: this.getGitUser(),
|
|
2319
892
|
baseBranch: "HEAD~1",
|
|
2320
893
|
headBranch: "HEAD"
|
|
@@ -2422,6 +995,36 @@ Usage:
|
|
|
2422
995
|
}
|
|
2423
996
|
return files;
|
|
2424
997
|
}
|
|
998
|
+
buildReviewWorkflow(files, context) {
|
|
999
|
+
const totalAdditions = files.reduce((sum, f) => sum + (f.additions || 0), 0);
|
|
1000
|
+
const totalDeletions = files.reduce((sum, f) => sum + (f.deletions || 0), 0);
|
|
1001
|
+
const reviewMode = context.mode === "others" ? "others" : "own";
|
|
1002
|
+
const reviewInstructions = {
|
|
1003
|
+
mode: reviewMode,
|
|
1004
|
+
description: reviewMode === "own" ? "Your PR \u2014 focus on footguns, missing tests, and production risks." : "Someone else's PR \u2014 focus on correctness, clarity, and maintainability."
|
|
1005
|
+
};
|
|
1006
|
+
const sorted = [...files].sort((a, b) => a.path.localeCompare(b.path));
|
|
1007
|
+
const fileOrder = sorted.map((f, i) => ({
|
|
1008
|
+
index: i + 1,
|
|
1009
|
+
path: f.path,
|
|
1010
|
+
reason: i === 0 ? "Start here to establish baseline context" : "Proceed in filename order"
|
|
1011
|
+
}));
|
|
1012
|
+
return {
|
|
1013
|
+
metadata: {
|
|
1014
|
+
...context.prNumber ? { prNumber: context.prNumber } : {},
|
|
1015
|
+
prTitle: context.prTitle || "PR Review",
|
|
1016
|
+
prAuthor: context.prAuthor || "unknown",
|
|
1017
|
+
baseBranch: context.baseBranch || "unknown",
|
|
1018
|
+
headBranch: context.headBranch || "unknown",
|
|
1019
|
+
totalFiles: files.length,
|
|
1020
|
+
totalAdditions,
|
|
1021
|
+
totalDeletions,
|
|
1022
|
+
designDocs: context.designDocs
|
|
1023
|
+
},
|
|
1024
|
+
fileOrder,
|
|
1025
|
+
reviewInstructions
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
2425
1028
|
/**
|
|
2426
1029
|
* Find related design docs
|
|
2427
1030
|
*/
|
|
@@ -2440,8 +1043,8 @@ Usage:
|
|
|
2440
1043
|
"rfcs"
|
|
2441
1044
|
];
|
|
2442
1045
|
for (const docPath of designDocPaths) {
|
|
2443
|
-
const fullPath =
|
|
2444
|
-
if (
|
|
1046
|
+
const fullPath = join2(cwd, docPath);
|
|
1047
|
+
if (existsSync2(fullPath)) {
|
|
2445
1048
|
}
|
|
2446
1049
|
}
|
|
2447
1050
|
return designDocs;
|
|
@@ -2454,9 +1057,9 @@ Usage:
|
|
|
2454
1057
|
const cwd = getWorkingDirectory(void 0, true);
|
|
2455
1058
|
await Promise.all(filePaths.map(async (filePath) => {
|
|
2456
1059
|
try {
|
|
2457
|
-
const fullPath = isAbsolute2(filePath) ? filePath :
|
|
2458
|
-
if (
|
|
2459
|
-
const content = await
|
|
1060
|
+
const fullPath = isAbsolute2(filePath) ? filePath : join2(cwd, filePath);
|
|
1061
|
+
if (existsSync2(fullPath)) {
|
|
1062
|
+
const content = await readFile2(fullPath, "utf-8");
|
|
2460
1063
|
contents.set(filePath, content);
|
|
2461
1064
|
}
|
|
2462
1065
|
} catch {
|
|
@@ -2537,21 +1140,21 @@ Usage:
|
|
|
2537
1140
|
`;
|
|
2538
1141
|
prompt += `**State & Lifecycle:**
|
|
2539
1142
|
`;
|
|
2540
|
-
for (const check of CRITICAL_REVIEW_CHECKLIST.stateAndLifecycle) {
|
|
1143
|
+
for (const check of this.CRITICAL_REVIEW_CHECKLIST.stateAndLifecycle) {
|
|
2541
1144
|
prompt += `- ${check}
|
|
2542
1145
|
`;
|
|
2543
1146
|
}
|
|
2544
1147
|
prompt += `
|
|
2545
1148
|
**Edge Cases & Races:**
|
|
2546
1149
|
`;
|
|
2547
|
-
for (const check of CRITICAL_REVIEW_CHECKLIST.edgeCasesAndRaces) {
|
|
1150
|
+
for (const check of this.CRITICAL_REVIEW_CHECKLIST.edgeCasesAndRaces) {
|
|
2548
1151
|
prompt += `- ${check}
|
|
2549
1152
|
`;
|
|
2550
1153
|
}
|
|
2551
1154
|
prompt += `
|
|
2552
1155
|
**Missing Pieces:**
|
|
2553
1156
|
`;
|
|
2554
|
-
for (const check of CRITICAL_REVIEW_CHECKLIST.missingPieces) {
|
|
1157
|
+
for (const check of this.CRITICAL_REVIEW_CHECKLIST.missingPieces) {
|
|
2555
1158
|
prompt += `- ${check}
|
|
2556
1159
|
`;
|
|
2557
1160
|
}
|
|
@@ -2899,7 +1502,7 @@ var TrieInitTool = class {
|
|
|
2899
1502
|
"",
|
|
2900
1503
|
"1. Edit `.trie/PROJECT.md` with your project description",
|
|
2901
1504
|
"2. Define coding standards in `.trie/RULES.md`",
|
|
2902
|
-
"3. Run `
|
|
1505
|
+
"3. Run `trie watch` to analyze your codebase",
|
|
2903
1506
|
'4. Run `trie_init` with action="complete" when setup is done'
|
|
2904
1507
|
);
|
|
2905
1508
|
return lines.join("\n");
|
|
@@ -3568,11 +2171,7 @@ var ToolRegistry = class {
|
|
|
3568
2171
|
properties: {
|
|
3569
2172
|
action: {
|
|
3570
2173
|
type: "string",
|
|
3571
|
-
description: "Optional quick action:
|
|
3572
|
-
},
|
|
3573
|
-
agent: {
|
|
3574
|
-
type: "string",
|
|
3575
|
-
description: "Agent key when action=agent or when using a custom skill name"
|
|
2174
|
+
description: "Optional quick action: pr_review, watch, fix, explain, test, visual_qa_browser"
|
|
3576
2175
|
},
|
|
3577
2176
|
files: {
|
|
3578
2177
|
type: "array",
|
|
@@ -3623,7 +2222,7 @@ var ToolRegistry = class {
|
|
|
3623
2222
|
issueIds: {
|
|
3624
2223
|
type: "array",
|
|
3625
2224
|
items: { type: "string" },
|
|
3626
|
-
description: "Issue IDs to dispatch (from
|
|
2225
|
+
description: "Issue IDs to dispatch (from trie_fix/watch). If omitted, dispatches all cloud-eligible pending issues. IGNORED if file+issue+fix are provided (ad-hoc mode)."
|
|
3627
2226
|
},
|
|
3628
2227
|
forceCloud: {
|
|
3629
2228
|
type: "boolean",
|
|
@@ -3817,7 +2416,7 @@ var ToolRegistry = class {
|
|
|
3817
2416
|
},
|
|
3818
2417
|
{
|
|
3819
2418
|
name: "trie_init",
|
|
3820
|
-
description: "Initialize bootstrap files (.trie/RULES.md, .trie/TEAM.md, .trie/BOOTSTRAP.md). Detects stack
|
|
2419
|
+
description: "Initialize bootstrap files (.trie/RULES.md, .trie/TEAM.md, .trie/BOOTSTRAP.md). Detects stack.",
|
|
3821
2420
|
inputSchema: {
|
|
3822
2421
|
type: "object",
|
|
3823
2422
|
properties: {
|
|
@@ -4149,17 +2748,10 @@ var ToolRegistry = class {
|
|
|
4149
2748
|
};
|
|
4150
2749
|
|
|
4151
2750
|
// src/server/resource-manager.ts
|
|
4152
|
-
import { readdir, readFile as
|
|
4153
|
-
import { existsSync as
|
|
4154
|
-
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";
|
|
4155
2754
|
import { fileURLToPath } from "url";
|
|
4156
|
-
|
|
4157
|
-
// src/skills/installer.ts
|
|
4158
|
-
async function listInstalledSkills() {
|
|
4159
|
-
return [];
|
|
4160
|
-
}
|
|
4161
|
-
|
|
4162
|
-
// src/server/resource-manager.ts
|
|
4163
2755
|
var UI_APPS = {
|
|
4164
2756
|
"ledger": {
|
|
4165
2757
|
name: "Decision Ledger",
|
|
@@ -4183,7 +2775,6 @@ var UI_APPS = {
|
|
|
4183
2775
|
}
|
|
4184
2776
|
};
|
|
4185
2777
|
var ResourceManager = class {
|
|
4186
|
-
skillRegistry = getSkillRegistry();
|
|
4187
2778
|
/**
|
|
4188
2779
|
* Get all available resources dynamically
|
|
4189
2780
|
*/
|
|
@@ -4208,12 +2799,6 @@ var ResourceManager = class {
|
|
|
4208
2799
|
description: "Detailed context state with scan history and priorities",
|
|
4209
2800
|
mimeType: "application/json"
|
|
4210
2801
|
},
|
|
4211
|
-
{
|
|
4212
|
-
uri: "trie://skills",
|
|
4213
|
-
name: "Available Skills",
|
|
4214
|
-
description: "List of all available Trie skills (built-in and custom)",
|
|
4215
|
-
mimeType: "application/json"
|
|
4216
|
-
},
|
|
4217
2802
|
{
|
|
4218
2803
|
uri: "trie://config",
|
|
4219
2804
|
name: "Trie Configuration",
|
|
@@ -4232,12 +2817,6 @@ var ResourceManager = class {
|
|
|
4232
2817
|
description: "Summary of loaded vulnerability detection signatures",
|
|
4233
2818
|
mimeType: "application/json"
|
|
4234
2819
|
},
|
|
4235
|
-
{
|
|
4236
|
-
uri: "trie://skills/installed",
|
|
4237
|
-
name: "Installed Skills",
|
|
4238
|
-
description: "External skills installed from skills.sh that Trie can apply",
|
|
4239
|
-
mimeType: "application/json"
|
|
4240
|
-
},
|
|
4241
2820
|
{
|
|
4242
2821
|
uri: "trie://bootstrap",
|
|
4243
2822
|
name: "Bootstrap Status",
|
|
@@ -4270,13 +2849,12 @@ var ResourceManager = class {
|
|
|
4270
2849
|
}
|
|
4271
2850
|
);
|
|
4272
2851
|
resources.push(...await this.getScanReportResources());
|
|
4273
|
-
resources.push(...await this.getCustomSkillResources());
|
|
4274
2852
|
resources.push(...this.getUIAppResources());
|
|
4275
2853
|
return resources;
|
|
4276
2854
|
}
|
|
4277
2855
|
async getScanReportResources() {
|
|
4278
2856
|
try {
|
|
4279
|
-
const reportsDir =
|
|
2857
|
+
const reportsDir = join3(getWorkingDirectory(void 0, true), "trie-reports");
|
|
4280
2858
|
const files = await readdir(reportsDir);
|
|
4281
2859
|
const reportFiles = files.filter((f) => f.endsWith(".txt") || f.endsWith(".json"));
|
|
4282
2860
|
return reportFiles.slice(0, 10).map((file) => ({
|
|
@@ -4289,20 +2867,6 @@ var ResourceManager = class {
|
|
|
4289
2867
|
return [];
|
|
4290
2868
|
}
|
|
4291
2869
|
}
|
|
4292
|
-
async getCustomSkillResources() {
|
|
4293
|
-
try {
|
|
4294
|
-
await this.skillRegistry.loadCustomSkills();
|
|
4295
|
-
const customSkills = this.skillRegistry.getCustomSkills();
|
|
4296
|
-
return customSkills.map((skill) => ({
|
|
4297
|
-
uri: `trie://skills/custom/${skill.name}`,
|
|
4298
|
-
name: `Custom Skill: ${skill.name}`,
|
|
4299
|
-
description: skill.description,
|
|
4300
|
-
mimeType: "application/json"
|
|
4301
|
-
}));
|
|
4302
|
-
} catch {
|
|
4303
|
-
return [];
|
|
4304
|
-
}
|
|
4305
|
-
}
|
|
4306
2870
|
/**
|
|
4307
2871
|
* Read content for a specific resource
|
|
4308
2872
|
*/
|
|
@@ -4321,12 +2885,6 @@ var ResourceManager = class {
|
|
|
4321
2885
|
if (parsedUri === "context/state") {
|
|
4322
2886
|
return await this.getContextStateResource(uri);
|
|
4323
2887
|
}
|
|
4324
|
-
if (parsedUri === "skills") {
|
|
4325
|
-
return await this.getBuiltInSkillsResource(uri);
|
|
4326
|
-
}
|
|
4327
|
-
if (parsedUri.startsWith("skills/custom/")) {
|
|
4328
|
-
return await this.getCustomSkillResource(uri, parsedUri);
|
|
4329
|
-
}
|
|
4330
2888
|
if (parsedUri === "config") {
|
|
4331
2889
|
return await this.getConfigResource(uri);
|
|
4332
2890
|
}
|
|
@@ -4336,9 +2894,6 @@ var ResourceManager = class {
|
|
|
4336
2894
|
if (parsedUri === "signatures") {
|
|
4337
2895
|
return await this.getSignaturesResource(uri);
|
|
4338
2896
|
}
|
|
4339
|
-
if (parsedUri === "skills/installed") {
|
|
4340
|
-
return await this.getInstalledSkillsResource(uri);
|
|
4341
|
-
}
|
|
4342
2897
|
if (parsedUri === "bootstrap") {
|
|
4343
2898
|
return await this.getBootstrapResource(uri);
|
|
4344
2899
|
}
|
|
@@ -4376,10 +2931,10 @@ var ResourceManager = class {
|
|
|
4376
2931
|
async getUIAppResource(uri, appId) {
|
|
4377
2932
|
const currentFile = fileURLToPath(import.meta.url);
|
|
4378
2933
|
const distDir = dirname2(dirname2(currentFile));
|
|
4379
|
-
const uiDir =
|
|
4380
|
-
const htmlPath =
|
|
2934
|
+
const uiDir = join3(distDir, "ui");
|
|
2935
|
+
const htmlPath = join3(uiDir, `${appId}.html`);
|
|
4381
2936
|
try {
|
|
4382
|
-
if (!
|
|
2937
|
+
if (!existsSync3(htmlPath)) {
|
|
4383
2938
|
return {
|
|
4384
2939
|
contents: [{
|
|
4385
2940
|
uri,
|
|
@@ -4388,7 +2943,7 @@ var ResourceManager = class {
|
|
|
4388
2943
|
}]
|
|
4389
2944
|
};
|
|
4390
2945
|
}
|
|
4391
|
-
const content = await
|
|
2946
|
+
const content = await readFile3(htmlPath, "utf-8");
|
|
4392
2947
|
return {
|
|
4393
2948
|
contents: [{
|
|
4394
2949
|
uri,
|
|
@@ -4554,24 +3109,16 @@ var ResourceManager = class {
|
|
|
4554
3109
|
summary.push("## Available Tools", "");
|
|
4555
3110
|
summary.push("| Tool | Purpose |");
|
|
4556
3111
|
summary.push("|------|---------|");
|
|
4557
|
-
summary.push("| `
|
|
3112
|
+
summary.push("| `trie_fix` | Apply high-confidence fixes |");
|
|
4558
3113
|
summary.push("| `trie_fix` | Generate fix recommendations |");
|
|
4559
3114
|
summary.push("| `trie_memory` | Search issue history |");
|
|
4560
3115
|
summary.push("| `trie_init` | Initialize bootstrap files |");
|
|
4561
3116
|
summary.push("");
|
|
4562
|
-
try {
|
|
4563
|
-
const skills = await listInstalledSkills();
|
|
4564
|
-
if (skills.length > 0) {
|
|
4565
|
-
summary.push(`**Skills:** ${skills.length} installed (${skills.slice(0, 3).map((s) => s.name).join(", ")}${skills.length > 3 ? "..." : ""})`);
|
|
4566
|
-
summary.push("");
|
|
4567
|
-
}
|
|
4568
|
-
} catch {
|
|
4569
|
-
}
|
|
4570
3117
|
summary.push("---", "", "# Detailed Context", "");
|
|
4571
|
-
const agentsMdPath =
|
|
3118
|
+
const agentsMdPath = join3(getTrieDirectory(workDir), "AGENTS.md");
|
|
4572
3119
|
try {
|
|
4573
|
-
if (
|
|
4574
|
-
const agentsContent = await
|
|
3120
|
+
if (existsSync3(agentsMdPath)) {
|
|
3121
|
+
const agentsContent = await readFile3(agentsMdPath, "utf-8");
|
|
4575
3122
|
summary.push(agentsContent);
|
|
4576
3123
|
} else {
|
|
4577
3124
|
const contextSummary = await getContextForAI();
|
|
@@ -4649,41 +3196,6 @@ This information is automatically available to Claude Code, Cursor, and other AI
|
|
|
4649
3196
|
}]
|
|
4650
3197
|
};
|
|
4651
3198
|
}
|
|
4652
|
-
async getBuiltInSkillsResource(uri) {
|
|
4653
|
-
await this.skillRegistry.loadCustomSkills();
|
|
4654
|
-
const skills = this.skillRegistry.getSkillDescriptions();
|
|
4655
|
-
return {
|
|
4656
|
-
contents: [{
|
|
4657
|
-
uri,
|
|
4658
|
-
mimeType: "application/json",
|
|
4659
|
-
text: JSON.stringify({
|
|
4660
|
-
totalSkills: skills.length,
|
|
4661
|
-
builtinCount: skills.filter((a) => !a.isCustom).length,
|
|
4662
|
-
customCount: skills.filter((a) => a.isCustom).length,
|
|
4663
|
-
skills: skills.map((a) => ({
|
|
4664
|
-
name: a.name,
|
|
4665
|
-
description: a.description,
|
|
4666
|
-
type: a.isCustom ? "custom" : "builtin"
|
|
4667
|
-
}))
|
|
4668
|
-
}, null, 2)
|
|
4669
|
-
}]
|
|
4670
|
-
};
|
|
4671
|
-
}
|
|
4672
|
-
async getCustomSkillResource(uri, parsedUri) {
|
|
4673
|
-
const skillName = parsedUri.replace("skills/custom/", "");
|
|
4674
|
-
await this.skillRegistry.loadCustomSkills();
|
|
4675
|
-
const metadata = this.skillRegistry.getCustomSkillMetadata(skillName);
|
|
4676
|
-
if (!metadata) {
|
|
4677
|
-
throw new Error(`Custom skill not found: ${skillName}`);
|
|
4678
|
-
}
|
|
4679
|
-
return {
|
|
4680
|
-
contents: [{
|
|
4681
|
-
uri,
|
|
4682
|
-
mimeType: "application/json",
|
|
4683
|
-
text: JSON.stringify(metadata, null, 2)
|
|
4684
|
-
}]
|
|
4685
|
-
};
|
|
4686
|
-
}
|
|
4687
3199
|
async getConfigResource(uri) {
|
|
4688
3200
|
const config = await loadConfig();
|
|
4689
3201
|
return {
|
|
@@ -4696,8 +3208,8 @@ This information is automatically available to Claude Code, Cursor, and other AI
|
|
|
4696
3208
|
}
|
|
4697
3209
|
async getCacheStatsResource(uri) {
|
|
4698
3210
|
try {
|
|
4699
|
-
const cachePath =
|
|
4700
|
-
const cacheContent = await
|
|
3211
|
+
const cachePath = join3(getTrieDirectory(getWorkingDirectory(void 0, true)), ".trie-cache.json");
|
|
3212
|
+
const cacheContent = await readFile3(cachePath, "utf-8");
|
|
4701
3213
|
const cache = JSON.parse(cacheContent);
|
|
4702
3214
|
const fileCount = Object.keys(cache.files || {}).length;
|
|
4703
3215
|
const totalVulns = Object.values(cache.files || {}).reduce((acc, file) => {
|
|
@@ -4724,15 +3236,15 @@ This information is automatically available to Claude Code, Cursor, and other AI
|
|
|
4724
3236
|
mimeType: "application/json",
|
|
4725
3237
|
text: JSON.stringify({
|
|
4726
3238
|
error: "No cache found. Run a scan first to build the cache.",
|
|
4727
|
-
hint: "
|
|
3239
|
+
hint: "Run trie watch to analyze your codebase"
|
|
4728
3240
|
}, null, 2)
|
|
4729
3241
|
}]
|
|
4730
3242
|
};
|
|
4731
3243
|
}
|
|
4732
3244
|
}
|
|
4733
3245
|
async getSignaturesResource(uri) {
|
|
4734
|
-
const { getVulnerabilityStats } = await import("./vulnerability-signatures-
|
|
4735
|
-
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");
|
|
4736
3248
|
const vulnStats = getVulnerabilityStats();
|
|
4737
3249
|
const vibeStats = getVibeCodeStats();
|
|
4738
3250
|
return {
|
|
@@ -4747,38 +3259,11 @@ This information is automatically available to Claude Code, Cursor, and other AI
|
|
|
4747
3259
|
}]
|
|
4748
3260
|
};
|
|
4749
3261
|
}
|
|
4750
|
-
async getInstalledSkillsResource(uri) {
|
|
4751
|
-
const skills = await listInstalledSkills();
|
|
4752
|
-
const state = await loadContextState();
|
|
4753
|
-
const skillsWithUsage = skills.map((skill) => {
|
|
4754
|
-
const record = state.skills?.[skill.name];
|
|
4755
|
-
return {
|
|
4756
|
-
name: skill.name,
|
|
4757
|
-
description: skill.description,
|
|
4758
|
-
source: skill.installedFrom,
|
|
4759
|
-
installedAt: skill.installedAt,
|
|
4760
|
-
timesApplied: record?.timesApplied || 0,
|
|
4761
|
-
appliedBy: record?.appliedBy || [],
|
|
4762
|
-
lastApplied: record?.lastApplied
|
|
4763
|
-
};
|
|
4764
|
-
});
|
|
4765
|
-
return {
|
|
4766
|
-
contents: [{
|
|
4767
|
-
uri,
|
|
4768
|
-
mimeType: "application/json",
|
|
4769
|
-
text: JSON.stringify({
|
|
4770
|
-
totalSkills: skills.length,
|
|
4771
|
-
skills: skillsWithUsage,
|
|
4772
|
-
note: "Skills are capabilities applied by agents, not autonomous agents themselves."
|
|
4773
|
-
}, null, 2)
|
|
4774
|
-
}]
|
|
4775
|
-
};
|
|
4776
|
-
}
|
|
4777
3262
|
async getScanReportResource(uri, parsedUri) {
|
|
4778
3263
|
const fileName = parsedUri.replace("reports/", "");
|
|
4779
|
-
const reportPath =
|
|
3264
|
+
const reportPath = join3(getWorkingDirectory(void 0, true), "trie-reports", fileName);
|
|
4780
3265
|
try {
|
|
4781
|
-
const content = await
|
|
3266
|
+
const content = await readFile3(reportPath, "utf-8");
|
|
4782
3267
|
return {
|
|
4783
3268
|
contents: [{
|
|
4784
3269
|
uri,
|
|
@@ -5254,38 +3739,6 @@ var RequestHandlers = class {
|
|
|
5254
3739
|
return await this.toolRegistry.getTool("register_agent").execute(args);
|
|
5255
3740
|
case "watch":
|
|
5256
3741
|
return await this.toolRegistry.getTool("watch").execute(args);
|
|
5257
|
-
// Individual agent commands
|
|
5258
|
-
case "security":
|
|
5259
|
-
return await this.toolRegistry.getTool("agent").execute({ ...args, agent: "security" });
|
|
5260
|
-
case "legal":
|
|
5261
|
-
return await this.toolRegistry.getTool("agent").execute({ ...args, agent: "legal" });
|
|
5262
|
-
case "accessibility":
|
|
5263
|
-
return await this.toolRegistry.getTool("agent").execute({ ...args, agent: "accessibility" });
|
|
5264
|
-
case "design":
|
|
5265
|
-
return await this.toolRegistry.getTool("agent").execute({ ...args, agent: "design-engineer" });
|
|
5266
|
-
case "architecture":
|
|
5267
|
-
return await this.toolRegistry.getTool("agent").execute({ ...args, agent: "software-architect" });
|
|
5268
|
-
case "bugs":
|
|
5269
|
-
return await this.toolRegistry.getTool("agent").execute({ ...args, agent: "bug-finding" });
|
|
5270
|
-
case "ux":
|
|
5271
|
-
return await this.toolRegistry.getTool("agent").execute({ ...args, agent: "user-testing" });
|
|
5272
|
-
case "types":
|
|
5273
|
-
return await this.toolRegistry.getTool("agent").execute({ ...args, agent: "typecheck" });
|
|
5274
|
-
case "devops":
|
|
5275
|
-
return await this.toolRegistry.getTool("agent").execute({ ...args, agent: "devops" });
|
|
5276
|
-
case "clean":
|
|
5277
|
-
return await this.toolRegistry.getTool("agent").execute({ ...args, agent: "trie_clean" });
|
|
5278
|
-
case "soc2":
|
|
5279
|
-
return await this.toolRegistry.getTool("agent").execute({ ...args, agent: "soc2" });
|
|
5280
|
-
// New agents
|
|
5281
|
-
case "performance":
|
|
5282
|
-
case "perf":
|
|
5283
|
-
return await this.toolRegistry.getTool("agent").execute({ ...args, agent: "performance" });
|
|
5284
|
-
case "e2e":
|
|
5285
|
-
return await this.toolRegistry.getTool("agent").execute({ ...args, agent: "e2e" });
|
|
5286
|
-
case "visual_qa":
|
|
5287
|
-
case "visual":
|
|
5288
|
-
return await this.toolRegistry.getTool("agent").execute({ ...args, agent: "visual-qa" });
|
|
5289
3742
|
case "visual_qa_browser": {
|
|
5290
3743
|
try {
|
|
5291
3744
|
const result = await runVisualQA(args);
|
|
@@ -5328,31 +3781,6 @@ npx playwright install chromium
|
|
|
5328
3781
|
};
|
|
5329
3782
|
}
|
|
5330
3783
|
}
|
|
5331
|
-
case "data_flow":
|
|
5332
|
-
case "dataflow":
|
|
5333
|
-
return await this.toolRegistry.getTool("agent").execute({ ...args, agent: "data-flow" });
|
|
5334
|
-
// Special agents
|
|
5335
|
-
case "agent_smith":
|
|
5336
|
-
case "agentsmith":
|
|
5337
|
-
case "smith":
|
|
5338
|
-
if (args?.clear_memory || args?.show_stats) {
|
|
5339
|
-
return await this.handleAgentSmith(args);
|
|
5340
|
-
}
|
|
5341
|
-
return await this.toolRegistry.getTool("agent").execute({ ...args, agent: "agent-smith" });
|
|
5342
|
-
case "super_reviewer":
|
|
5343
|
-
case "superreviewer":
|
|
5344
|
-
case "reviewer":
|
|
5345
|
-
return await this.toolRegistry.getTool("agent").execute({ ...args, agent: "super-reviewer" });
|
|
5346
|
-
// Custom skill tools (with backward-compatible agent aliases)
|
|
5347
|
-
case "create_skill":
|
|
5348
|
-
case "create_agent":
|
|
5349
|
-
return await this.toolRegistry.getTool("create_skill").execute(args);
|
|
5350
|
-
case "save_skill":
|
|
5351
|
-
case "save_agent":
|
|
5352
|
-
return await this.toolRegistry.getTool("save_skill").execute(args);
|
|
5353
|
-
case "list_skills":
|
|
5354
|
-
case "list_agents":
|
|
5355
|
-
return await this.toolRegistry.getTool("list_skills").execute(args);
|
|
5356
3784
|
case "pr_review":
|
|
5357
3785
|
return await this.toolRegistry.getTool("pr_review").execute(args);
|
|
5358
3786
|
case "project":
|
|
@@ -5454,45 +3882,16 @@ npx playwright install chromium
|
|
|
5454
3882
|
"",
|
|
5455
3883
|
"**Most common:**",
|
|
5456
3884
|
"- `trie` (no args) \u2014 show this menu",
|
|
5457
|
-
'- `trie` with `{ action: "scan", files?: [], directory?: "" }` \u2014 auto triage & scan',
|
|
5458
|
-
'- `trie` with `{ action: "security", files?: [] }` \u2014 single-skill run (any skill name works)',
|
|
5459
|
-
'- `trie` with `{ action: "agent_smith" }` \u2014 aggressive AI-pattern hunter',
|
|
5460
|
-
"",
|
|
5461
|
-
"**Other actions:**",
|
|
5462
|
-
'- `action: "pr_review"` \u2014 interactive PR review',
|
|
5463
3885
|
'- `action: "fix"` \u2014 high-confidence fixes',
|
|
5464
3886
|
'- `action: "watch"` \u2014 start/stop/status autonomous watch',
|
|
3887
|
+
'- `action: "pr_review"` \u2014 interactive PR review',
|
|
3888
|
+
"",
|
|
3889
|
+
"**Other actions:**",
|
|
5465
3890
|
'- `action: "test"` \u2014 generate/coverage/suggest/run tests (requires `files` + `action`)',
|
|
5466
3891
|
'- `action: "explain"` \u2014 explain code/issues/risks',
|
|
5467
|
-
'- `action: "list_skills"` \u2014 list all skills (external + custom)',
|
|
5468
3892
|
'- `action: "visual_qa_browser"` \u2014 screenshots for visual QA',
|
|
5469
3893
|
"",
|
|
5470
|
-
"
|
|
5471
|
-
"`security`, `legal`, `accessibility`, `design`, `architecture`, `bugs`, `ux`, `types`, `devops`, `clean`, `soc2`, `performance`, `e2e`, `visual_qa`, `data_flow`",
|
|
5472
|
-
"",
|
|
5473
|
-
"**Special skills:**",
|
|
5474
|
-
"`agent_smith` \u2014 35 vibe code hunters with cross-file detection",
|
|
5475
|
-
"`super_reviewer` \u2014 Interactive PR review with cross-examination",
|
|
5476
|
-
"",
|
|
5477
|
-
"All commands accept `trie_` prefix (e.g., `trie_scan`, `trie_security`). Short names still work for compatibility."
|
|
5478
|
-
].join("\n")
|
|
5479
|
-
}]
|
|
5480
|
-
};
|
|
5481
|
-
}
|
|
5482
|
-
async handleAgentSmith(_smithArgs) {
|
|
5483
|
-
return {
|
|
5484
|
-
content: [{
|
|
5485
|
-
type: "text",
|
|
5486
|
-
text: [
|
|
5487
|
-
"\u{1F916} Agent Smith functionality has been integrated into the decision ledger.",
|
|
5488
|
-
"",
|
|
5489
|
-
"The autonomous agent now:",
|
|
5490
|
-
"- Extracts patterns automatically via `trie watch`",
|
|
5491
|
-
"- Builds decision ledger from every change",
|
|
5492
|
-
"- Predicts problems via `trie gotcha`",
|
|
5493
|
-
"",
|
|
5494
|
-
"Start the agent: `trie_watch start`",
|
|
5495
|
-
"Query the ledger: `trie_gotcha`"
|
|
3894
|
+
"All commands accept `trie_` prefix (e.g., `trie_fix`, `trie_watch`)."
|
|
5496
3895
|
].join("\n")
|
|
5497
3896
|
}]
|
|
5498
3897
|
};
|
|
@@ -5550,7 +3949,7 @@ var MCPServer = class {
|
|
|
5550
3949
|
/**
|
|
5551
3950
|
* Show startup banner
|
|
5552
3951
|
*/
|
|
5553
|
-
showStartupBanner(
|
|
3952
|
+
showStartupBanner(toolCount, aiTool) {
|
|
5554
3953
|
console.error(`
|
|
5555
3954
|
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
5556
3955
|
\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D
|
|
@@ -5558,21 +3957,19 @@ var MCPServer = class {
|
|
|
5558
3957
|
\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D
|
|
5559
3958
|
\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
5560
3959
|
\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
5561
|
-
Your central registry for Trie
|
|
3960
|
+
Your central registry for Trie tools
|
|
5562
3961
|
|
|
5563
3962
|
by Louis Kishfy
|
|
5564
3963
|
|
|
5565
3964
|
Download the Trie workspace: https://www.trie.dev
|
|
5566
3965
|
Follow me on X: https://x.com/louiskishfy
|
|
5567
3966
|
|
|
5568
|
-
${
|
|
3967
|
+
${toolCount} tools ready | ${aiTool}
|
|
5569
3968
|
|
|
5570
3969
|
Quick Start:
|
|
5571
|
-
\u2022 "Scan this code" - Run
|
|
5572
|
-
\u2022 "
|
|
5573
|
-
\u2022 "
|
|
5574
|
-
\u2022 "Use trie_list_skills" - See all skills
|
|
5575
|
-
\u2022 "Use trie_create_skill" - Make custom skill
|
|
3970
|
+
\u2022 "Scan this code" - Run Trie analysis
|
|
3971
|
+
\u2022 "Use trie" - Open the Trie menu
|
|
3972
|
+
\u2022 "Use trie_fix" - Apply high-confidence fixes
|
|
5576
3973
|
|
|
5577
3974
|
Ready.
|
|
5578
3975
|
`);
|
|
@@ -5584,9 +3981,8 @@ var MCPServer = class {
|
|
|
5584
3981
|
try {
|
|
5585
3982
|
const aiTool = detectAITool();
|
|
5586
3983
|
await loadConfig();
|
|
5587
|
-
const
|
|
5588
|
-
|
|
5589
|
-
this.showStartupBanner(skillCount, aiTool.name);
|
|
3984
|
+
const toolCount = this.toolRegistry.getAllTools().length;
|
|
3985
|
+
this.showStartupBanner(toolCount, aiTool.name);
|
|
5590
3986
|
const transport = new StdioServerTransport();
|
|
5591
3987
|
await this.server.connect(transport);
|
|
5592
3988
|
} catch (error) {
|