@triedotdev/mcp 1.0.138 → 1.0.140
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +184 -38
- package/dist/{autonomy-config-TZ6HF4FA.js → autonomy-config-ZCOSTMPD.js} +2 -2
- package/dist/{chunk-X3F5QDER.js → chunk-4O2KRHK4.js} +934 -132
- package/dist/chunk-4O2KRHK4.js.map +1 -0
- package/dist/{chunk-J5EMP4XW.js → chunk-5KJ4UJOY.js} +9 -4
- package/dist/chunk-5KJ4UJOY.js.map +1 -0
- package/dist/chunk-62POBLFC.js +1925 -0
- package/dist/chunk-62POBLFC.js.map +1 -0
- package/dist/{chunk-GFFUDJMK.js → chunk-75ADWWUF.js} +13 -13
- package/dist/chunk-75ADWWUF.js.map +1 -0
- package/dist/{chunk-D3AS5LY7.js → chunk-7OJ6JIPL.js} +39 -604
- package/dist/chunk-7OJ6JIPL.js.map +1 -0
- package/dist/{chunk-3RRXWX3V.js → chunk-AF2APASP.js} +38 -4
- package/dist/{chunk-3RRXWX3V.js.map → chunk-AF2APASP.js.map} +1 -1
- package/dist/{chunk-QSWUPSLK.js → chunk-FH335WL5.js} +9 -1
- package/dist/chunk-FH335WL5.js.map +1 -0
- package/dist/{chunk-Y32FM3MR.js → chunk-FPEMP54L.js} +21 -15
- package/dist/chunk-FPEMP54L.js.map +1 -0
- package/dist/{chunk-EDDT4ZIH.js → chunk-GXF6JOCN.js} +21 -323
- package/dist/chunk-GXF6JOCN.js.map +1 -0
- package/dist/chunk-LD7ZEFNY.js +132 -0
- package/dist/chunk-LD7ZEFNY.js.map +1 -0
- package/dist/chunk-NKHO34UZ.js +467 -0
- package/dist/chunk-NKHO34UZ.js.map +1 -0
- package/dist/{chunk-YOKQ25IW.js → chunk-OQ4A3RDY.js} +14 -14
- package/dist/{chunk-6LLH3TBZ.js → chunk-UOSTOLU7.js} +12 -12
- package/dist/{chunk-67GSG2ST.js → chunk-XTTZAQWJ.js} +18 -15
- package/dist/chunk-XTTZAQWJ.js.map +1 -0
- package/dist/{chunk-FOCXXIXY.js → chunk-YEIJW6X6.js} +2 -2
- package/dist/chunk-YOJGSRZK.js +216 -0
- package/dist/chunk-YOJGSRZK.js.map +1 -0
- package/dist/cli/main.js +573 -59
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/yolo-daemon.js +15 -13
- package/dist/cli/yolo-daemon.js.map +1 -1
- package/dist/{client-JTU5TRLB.js → client-INNE2GGZ.js} +2 -2
- package/dist/{codebase-index-FNJ4GCBE.js → codebase-index-5SEOESWM.js} +3 -3
- package/dist/fast-analyzer-AYLZB5TW.js +216 -0
- package/dist/fast-analyzer-AYLZB5TW.js.map +1 -0
- package/dist/github-ingester-J2ZFYXVE.js +11 -0
- package/dist/{goal-manager-6BJQ36AH.js → goal-manager-ZBWKWEML.js} +3 -3
- package/dist/{goal-validator-GISXYANK.js → goal-validator-HNXXUCPW.js} +3 -3
- package/dist/{graph-X2FMRQLG.js → graph-J4OGTYCO.js} +2 -2
- package/dist/{hypothesis-K3KQJOXJ.js → hypothesis-JCUMZKTG.js} +3 -3
- package/dist/index.js +1090 -108
- package/dist/index.js.map +1 -1
- package/dist/{issue-store-BO5OWLJW.js → issue-store-LZWZIGM7.js} +2 -2
- package/dist/linear-ingester-JRDQAIAA.js +11 -0
- package/dist/linear-ingester-JRDQAIAA.js.map +1 -0
- package/dist/{trie-agent-XMSGMD7E.js → trie-agent-M6PHM6UD.js} +10 -10
- package/dist/trie-agent-M6PHM6UD.js.map +1 -0
- package/package.json +15 -8
- package/dist/chunk-67GSG2ST.js.map +0 -1
- package/dist/chunk-D3AS5LY7.js.map +0 -1
- package/dist/chunk-EDDT4ZIH.js.map +0 -1
- package/dist/chunk-GFFUDJMK.js.map +0 -1
- package/dist/chunk-J5EMP4XW.js.map +0 -1
- package/dist/chunk-QSWUPSLK.js.map +0 -1
- package/dist/chunk-X3F5QDER.js.map +0 -1
- package/dist/chunk-Y32FM3MR.js.map +0 -1
- package/dist/chunk-Z2P4WST6.js +0 -883
- package/dist/chunk-Z2P4WST6.js.map +0 -1
- /package/dist/{autonomy-config-TZ6HF4FA.js.map → autonomy-config-ZCOSTMPD.js.map} +0 -0
- /package/dist/{chunk-YOKQ25IW.js.map → chunk-OQ4A3RDY.js.map} +0 -0
- /package/dist/{chunk-6LLH3TBZ.js.map → chunk-UOSTOLU7.js.map} +0 -0
- /package/dist/{chunk-FOCXXIXY.js.map → chunk-YEIJW6X6.js.map} +0 -0
- /package/dist/{client-JTU5TRLB.js.map → client-INNE2GGZ.js.map} +0 -0
- /package/dist/{codebase-index-FNJ4GCBE.js.map → codebase-index-5SEOESWM.js.map} +0 -0
- /package/dist/{goal-manager-6BJQ36AH.js.map → github-ingester-J2ZFYXVE.js.map} +0 -0
- /package/dist/{goal-validator-GISXYANK.js.map → goal-manager-ZBWKWEML.js.map} +0 -0
- /package/dist/{graph-X2FMRQLG.js.map → goal-validator-HNXXUCPW.js.map} +0 -0
- /package/dist/{hypothesis-K3KQJOXJ.js.map → graph-J4OGTYCO.js.map} +0 -0
- /package/dist/{issue-store-BO5OWLJW.js.map → hypothesis-JCUMZKTG.js.map} +0 -0
- /package/dist/{trie-agent-XMSGMD7E.js.map → issue-store-LZWZIGM7.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
GitHubIngester
|
|
4
|
+
} from "./chunk-YOJGSRZK.js";
|
|
2
5
|
import {
|
|
3
6
|
CodebaseIndex
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-AF2APASP.js";
|
|
5
8
|
import {
|
|
6
|
-
LinearIngester,
|
|
7
9
|
appendToSection,
|
|
8
10
|
completeBootstrap,
|
|
9
11
|
getContextForAI,
|
|
@@ -13,7 +15,6 @@ import {
|
|
|
13
15
|
initProjectInfo,
|
|
14
16
|
initializeBootstrapFiles,
|
|
15
17
|
loadBootstrapContext,
|
|
16
|
-
loadConfig,
|
|
17
18
|
loadContextState,
|
|
18
19
|
loadProjectInfo,
|
|
19
20
|
loadRules,
|
|
@@ -21,12 +22,13 @@ import {
|
|
|
21
22
|
needsBootstrap,
|
|
22
23
|
projectInfoExists,
|
|
23
24
|
updateProjectSection
|
|
24
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-7OJ6JIPL.js";
|
|
25
26
|
import {
|
|
26
|
-
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
LinearIngester
|
|
28
|
+
} from "./chunk-LD7ZEFNY.js";
|
|
28
29
|
import {
|
|
29
30
|
ExtractionPipeline,
|
|
31
|
+
GitHubBranchesTool,
|
|
30
32
|
InteractiveDashboard,
|
|
31
33
|
StreamingManager,
|
|
32
34
|
TrieCheckTool,
|
|
@@ -37,27 +39,28 @@ import {
|
|
|
37
39
|
TrieGetGovernanceTool,
|
|
38
40
|
TrieGetRelatedDecisionsTool,
|
|
39
41
|
TrieGetRelatedGovernanceTool,
|
|
42
|
+
TriePipelineTool,
|
|
40
43
|
TrieQueryContextTool,
|
|
41
44
|
TrieScanTool,
|
|
42
45
|
TrieTellTool,
|
|
43
46
|
getPrompt,
|
|
44
47
|
getSystemPrompt,
|
|
45
48
|
handleCheckpointTool
|
|
46
|
-
} from "./chunk-
|
|
47
|
-
import "./chunk-YOKQ25IW.js";
|
|
49
|
+
} from "./chunk-4O2KRHK4.js";
|
|
48
50
|
import "./chunk-23RJT5WT.js";
|
|
49
|
-
import "./chunk-
|
|
50
|
-
import "./chunk-
|
|
51
|
+
import "./chunk-OQ4A3RDY.js";
|
|
52
|
+
import "./chunk-YEIJW6X6.js";
|
|
53
|
+
import "./chunk-XTTZAQWJ.js";
|
|
51
54
|
import "./chunk-3MUCUZ46.js";
|
|
52
55
|
import {
|
|
53
56
|
exportToJson,
|
|
54
57
|
formatFriendlyError,
|
|
55
|
-
getChangedFilesSinceTimestamp,
|
|
56
|
-
getGitChangedFiles,
|
|
57
58
|
importFromJson,
|
|
58
|
-
isTrieInitialized
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
isTrieInitialized
|
|
60
|
+
} from "./chunk-GXF6JOCN.js";
|
|
61
|
+
import {
|
|
62
|
+
loadConfig
|
|
63
|
+
} from "./chunk-NKHO34UZ.js";
|
|
61
64
|
import "./chunk-4C67GV3O.js";
|
|
62
65
|
import "./chunk-ZV2K6M7T.js";
|
|
63
66
|
import {
|
|
@@ -65,43 +68,51 @@ import {
|
|
|
65
68
|
getGlobalMemoryStats,
|
|
66
69
|
listTrackedProjects,
|
|
67
70
|
searchGlobalPatterns
|
|
68
|
-
} from "./chunk-
|
|
69
|
-
import
|
|
71
|
+
} from "./chunk-UOSTOLU7.js";
|
|
72
|
+
import {
|
|
73
|
+
ContextGraph
|
|
74
|
+
} from "./chunk-FH335WL5.js";
|
|
75
|
+
import "./chunk-75ADWWUF.js";
|
|
70
76
|
import {
|
|
71
77
|
isAIAvailable,
|
|
72
78
|
runAIAnalysis
|
|
73
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-FPEMP54L.js";
|
|
74
80
|
import "./chunk-LT6VUZG2.js";
|
|
75
|
-
import "./chunk-F4NJ4CBP.js";
|
|
76
|
-
import "./chunk-IXO4G4D3.js";
|
|
77
|
-
import "./chunk-6NLHFIYA.js";
|
|
78
|
-
import {
|
|
79
|
-
getStorage
|
|
80
|
-
} from "./chunk-FG467PDD.js";
|
|
81
|
-
import {
|
|
82
|
-
ContextGraph
|
|
83
|
-
} from "./chunk-QSWUPSLK.js";
|
|
84
|
-
import {
|
|
85
|
-
getSkillRegistry
|
|
86
|
-
} from "./chunk-G76DYVGX.js";
|
|
87
|
-
import {
|
|
88
|
-
getOutputManager
|
|
89
|
-
} from "./chunk-TIMIKBY2.js";
|
|
90
81
|
import {
|
|
91
82
|
findSimilarIssues,
|
|
83
|
+
getChangedFilesSinceTimestamp,
|
|
84
|
+
getGitChangedFiles,
|
|
92
85
|
getMemoryStats,
|
|
93
86
|
getRecentIssues,
|
|
94
87
|
markIssueResolved,
|
|
95
88
|
purgeIssues,
|
|
89
|
+
runShellCommandSync,
|
|
96
90
|
searchIssues,
|
|
97
91
|
storeIssues
|
|
98
|
-
} from "./chunk-
|
|
92
|
+
} from "./chunk-62POBLFC.js";
|
|
99
93
|
import "./chunk-4MJ52WBH.js";
|
|
100
94
|
import "./chunk-43X6JBEM.js";
|
|
95
|
+
import "./chunk-F4NJ4CBP.js";
|
|
96
|
+
import "./chunk-IXO4G4D3.js";
|
|
97
|
+
import "./chunk-6NLHFIYA.js";
|
|
98
|
+
import {
|
|
99
|
+
getStorage
|
|
100
|
+
} from "./chunk-FG467PDD.js";
|
|
101
|
+
import {
|
|
102
|
+
getSkillRegistry
|
|
103
|
+
} from "./chunk-G76DYVGX.js";
|
|
104
|
+
import {
|
|
105
|
+
getAutonomyConfig,
|
|
106
|
+
loadAutonomyConfig,
|
|
107
|
+
saveAutonomyConfig
|
|
108
|
+
} from "./chunk-5KJ4UJOY.js";
|
|
101
109
|
import {
|
|
102
110
|
getTrieDirectory,
|
|
103
111
|
getWorkingDirectory
|
|
104
112
|
} from "./chunk-SH7H3WRU.js";
|
|
113
|
+
import {
|
|
114
|
+
getOutputManager
|
|
115
|
+
} from "./chunk-TIMIKBY2.js";
|
|
105
116
|
import {
|
|
106
117
|
isInteractiveMode
|
|
107
118
|
} from "./chunk-APMV77PU.js";
|
|
@@ -188,10 +199,248 @@ function detectAITool() {
|
|
|
188
199
|
import { readFile } from "fs/promises";
|
|
189
200
|
import { existsSync } from "fs";
|
|
190
201
|
import { extname, relative, resolve, isAbsolute } from "path";
|
|
202
|
+
|
|
203
|
+
// src/tools/fix-triage.ts
|
|
204
|
+
var EFFORT_SCORES = {
|
|
205
|
+
trivial: -6,
|
|
206
|
+
easy: -3,
|
|
207
|
+
medium: 0,
|
|
208
|
+
hard: 4
|
|
209
|
+
};
|
|
210
|
+
var SEVERITY_SCORES = {
|
|
211
|
+
critical: 4,
|
|
212
|
+
serious: 2,
|
|
213
|
+
moderate: -1,
|
|
214
|
+
low: -3
|
|
215
|
+
};
|
|
216
|
+
function triageIssue(issue, context, occurrence, config, pipeline) {
|
|
217
|
+
const reasons = [];
|
|
218
|
+
if (config?.level === "passive") {
|
|
219
|
+
return {
|
|
220
|
+
strategy: "local-ai",
|
|
221
|
+
score: 0,
|
|
222
|
+
confidence: 1,
|
|
223
|
+
reasons: ["Autonomy level is passive \u2014 cloud dispatch disabled"],
|
|
224
|
+
fallback: "local-ai"
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
if (config?.cloudAgentEnabled === false) {
|
|
228
|
+
return {
|
|
229
|
+
strategy: "local-ai",
|
|
230
|
+
score: 0,
|
|
231
|
+
confidence: 1,
|
|
232
|
+
reasons: ["Cloud agent not enabled (run trie_cloud_fix action:configure)"],
|
|
233
|
+
fallback: "local-ai"
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
let score = 0;
|
|
237
|
+
const effort = issue.effort ?? "medium";
|
|
238
|
+
const effortScore = EFFORT_SCORES[effort] ?? 0;
|
|
239
|
+
if (effortScore !== 0) {
|
|
240
|
+
score += effortScore;
|
|
241
|
+
reasons.push(`effort:${effort}`);
|
|
242
|
+
}
|
|
243
|
+
const severityScore = SEVERITY_SCORES[issue.severity] ?? 0;
|
|
244
|
+
if (severityScore !== 0) {
|
|
245
|
+
score += severityScore;
|
|
246
|
+
reasons.push(`severity:${issue.severity}`);
|
|
247
|
+
}
|
|
248
|
+
if (issue.autoFixable) {
|
|
249
|
+
score -= 2;
|
|
250
|
+
reasons.push("autoFixable");
|
|
251
|
+
}
|
|
252
|
+
if (issue.confidence < 0.7) {
|
|
253
|
+
score -= 2;
|
|
254
|
+
reasons.push(`low confidence (${(issue.confidence * 100).toFixed(0)}%)`);
|
|
255
|
+
}
|
|
256
|
+
if (issue.cwe) {
|
|
257
|
+
score += 3;
|
|
258
|
+
reasons.push(`cwe:${issue.cwe}`);
|
|
259
|
+
}
|
|
260
|
+
if (issue.owasp) {
|
|
261
|
+
score += 2;
|
|
262
|
+
reasons.push(`owasp:${issue.owasp}`);
|
|
263
|
+
}
|
|
264
|
+
if (issue.category === "security") {
|
|
265
|
+
score += 2;
|
|
266
|
+
reasons.push("category:security");
|
|
267
|
+
}
|
|
268
|
+
if (context) {
|
|
269
|
+
if (context.hasTests) {
|
|
270
|
+
score += 1;
|
|
271
|
+
reasons.push("has tests");
|
|
272
|
+
} else {
|
|
273
|
+
score -= 2;
|
|
274
|
+
reasons.push("no tests");
|
|
275
|
+
}
|
|
276
|
+
if (context.complexity === "high") {
|
|
277
|
+
score += 1;
|
|
278
|
+
reasons.push("high complexity");
|
|
279
|
+
}
|
|
280
|
+
if (context.touchesAuth || context.touchesCrypto || context.touchesPayments) {
|
|
281
|
+
score += 2;
|
|
282
|
+
reasons.push("touches auth/crypto/payments");
|
|
283
|
+
}
|
|
284
|
+
if (context.touchesDatabase) {
|
|
285
|
+
score += 1;
|
|
286
|
+
reasons.push("touches database");
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (occurrence) {
|
|
290
|
+
if (occurrence.count >= 5) {
|
|
291
|
+
score += 3;
|
|
292
|
+
reasons.push(`${occurrence.count}\xD7 seen`);
|
|
293
|
+
} else if (occurrence.count >= 3) {
|
|
294
|
+
score += 1;
|
|
295
|
+
reasons.push(`${occurrence.count}\xD7 seen`);
|
|
296
|
+
}
|
|
297
|
+
if (occurrence.escalationLevel === "block") {
|
|
298
|
+
score += 4;
|
|
299
|
+
reasons.push("escalation:block");
|
|
300
|
+
} else if (occurrence.escalationLevel === "escalate") {
|
|
301
|
+
score += 2;
|
|
302
|
+
reasons.push("escalation:escalate");
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (config?.level === "aggressive") {
|
|
306
|
+
score -= 1;
|
|
307
|
+
reasons.push("aggressive mode (\u22121 threshold)");
|
|
308
|
+
}
|
|
309
|
+
if (pipeline) {
|
|
310
|
+
if (pipeline.hasLinkedPR && pipeline.prState === "open") {
|
|
311
|
+
score -= 2;
|
|
312
|
+
reasons.push("has open PR");
|
|
313
|
+
}
|
|
314
|
+
if (pipeline.hasLinkedTicket && pipeline.ticketStatus?.toLowerCase().includes("started")) {
|
|
315
|
+
score -= 1;
|
|
316
|
+
reasons.push("ticket in active sprint");
|
|
317
|
+
}
|
|
318
|
+
if (pipeline.hasLinkedTicket && pipeline.ticketPriority === "urgent") {
|
|
319
|
+
score += 1;
|
|
320
|
+
reasons.push("ticket:urgent");
|
|
321
|
+
}
|
|
322
|
+
if (!pipeline.hasLinkedTicket && !pipeline.hasLinkedPR && issue.severity === "critical") {
|
|
323
|
+
score += 2;
|
|
324
|
+
reasons.push("critical, no ticket/PR (falling through cracks)");
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
let strategy;
|
|
328
|
+
if (score < 0 && issue.autoFixable) {
|
|
329
|
+
strategy = "inline-auto";
|
|
330
|
+
} else if (score < 4) {
|
|
331
|
+
strategy = "local-ai";
|
|
332
|
+
} else {
|
|
333
|
+
strategy = "cloud-agent";
|
|
334
|
+
}
|
|
335
|
+
const confidence = Math.min(1, Math.abs(score) / 8);
|
|
336
|
+
return { strategy, score, confidence, reasons, fallback: "local-ai" };
|
|
337
|
+
}
|
|
338
|
+
function triageIssues(issues, context, occurrences, config, pipelineContexts) {
|
|
339
|
+
const results = /* @__PURE__ */ new Map();
|
|
340
|
+
const summary = {
|
|
341
|
+
inlineAuto: [],
|
|
342
|
+
localAi: [],
|
|
343
|
+
cloudAgent: [],
|
|
344
|
+
cloudAgentScore: 0
|
|
345
|
+
};
|
|
346
|
+
for (const issue of issues) {
|
|
347
|
+
const occurrence = occurrences?.get(issue.id);
|
|
348
|
+
const pipeline = pipelineContexts?.get(issue.id);
|
|
349
|
+
const result = triageIssue(issue, context, occurrence, config, pipeline);
|
|
350
|
+
results.set(issue.id, result);
|
|
351
|
+
switch (result.strategy) {
|
|
352
|
+
case "inline-auto":
|
|
353
|
+
summary.inlineAuto.push(issue);
|
|
354
|
+
break;
|
|
355
|
+
case "local-ai":
|
|
356
|
+
summary.localAi.push(issue);
|
|
357
|
+
break;
|
|
358
|
+
case "cloud-agent":
|
|
359
|
+
summary.cloudAgent.push(issue);
|
|
360
|
+
summary.cloudAgentScore += result.score;
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return { results, summary };
|
|
365
|
+
}
|
|
366
|
+
function formatTriageTable(results, issues) {
|
|
367
|
+
const LINE = "\u2500".repeat(68);
|
|
368
|
+
const lines = [];
|
|
369
|
+
lines.push("FIX ROUTING PLAN");
|
|
370
|
+
lines.push(LINE);
|
|
371
|
+
lines.push(
|
|
372
|
+
padEnd("Issue", 32) + padEnd("Strategy", 14) + padEnd("Score", 7) + "Reason"
|
|
373
|
+
);
|
|
374
|
+
for (const issue of issues) {
|
|
375
|
+
const r = results.get(issue.id);
|
|
376
|
+
if (!r) continue;
|
|
377
|
+
const loc = `${shortPath(issue.file)}:${issue.line ?? "?"}`;
|
|
378
|
+
const scoreStr = r.score >= 0 ? `+${r.score}` : String(r.score);
|
|
379
|
+
lines.push(
|
|
380
|
+
padEnd(loc, 32) + padEnd(r.strategy, 14) + padEnd(scoreStr, 7) + r.reasons.join(", ")
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
lines.push(LINE);
|
|
384
|
+
const counts = [];
|
|
385
|
+
const cloud = issues.filter((i) => results.get(i.id)?.strategy === "cloud-agent");
|
|
386
|
+
const local = issues.filter((i) => results.get(i.id)?.strategy === "local-ai");
|
|
387
|
+
const auto = issues.filter((i) => results.get(i.id)?.strategy === "inline-auto");
|
|
388
|
+
if (cloud.length) counts.push(`${cloud.length} for cloud agent`);
|
|
389
|
+
if (local.length) counts.push(`${local.length} for local AI`);
|
|
390
|
+
if (auto.length) counts.push(`${auto.length} auto-fixable`);
|
|
391
|
+
lines.push(counts.join(", "));
|
|
392
|
+
if (cloud.length > 0) {
|
|
393
|
+
const ids = cloud.map((i) => `"${i.id}"`).join(",");
|
|
394
|
+
lines.push("");
|
|
395
|
+
lines.push(`To dispatch cloud issues: trie_cloud_fix action:dispatch issueIds:[${ids}]`);
|
|
396
|
+
}
|
|
397
|
+
if (local.length > 0) {
|
|
398
|
+
const ids = local.map((i) => `"${i.id}"`).join(",");
|
|
399
|
+
lines.push(`To fix local issues: trie_fix issueIds:[${ids}]`);
|
|
400
|
+
}
|
|
401
|
+
return lines.join("\n");
|
|
402
|
+
}
|
|
403
|
+
function formatCloudRecommendation(results, issues) {
|
|
404
|
+
const cloud = issues.filter((i) => {
|
|
405
|
+
const r = results.get(i.id);
|
|
406
|
+
return r && r.score >= 4;
|
|
407
|
+
});
|
|
408
|
+
if (cloud.length === 0) return null;
|
|
409
|
+
const LINE = "\u2500".repeat(65);
|
|
410
|
+
const lines = [];
|
|
411
|
+
lines.push(LINE);
|
|
412
|
+
lines.push(
|
|
413
|
+
`${cloud.length} issue${cloud.length > 1 ? "s" : ""} qualify for cloud agent verification (test-verified fix + PR):`
|
|
414
|
+
);
|
|
415
|
+
for (const issue of cloud) {
|
|
416
|
+
const r = results.get(issue.id);
|
|
417
|
+
const loc = `${shortPath(issue.file)}:${issue.line ?? "?"}`;
|
|
418
|
+
const scoreStr = r.score >= 0 ? `+${r.score}` : String(r.score);
|
|
419
|
+
lines.push(` \u2022 ${padEnd(loc, 30)} \u2014 score ${scoreStr} (${r.reasons.join(", ")})`);
|
|
420
|
+
}
|
|
421
|
+
const ids = cloud.map((i) => `"${i.id}"`).join(",");
|
|
422
|
+
lines.push("");
|
|
423
|
+
lines.push(`Run: trie_cloud_fix action:dispatch issueIds:[${ids}]`);
|
|
424
|
+
lines.push(LINE);
|
|
425
|
+
return lines.join("\n");
|
|
426
|
+
}
|
|
427
|
+
function shortPath(file) {
|
|
428
|
+
const parts = file.split("/");
|
|
429
|
+
return parts.length > 2 ? parts.slice(-2).join("/") : file;
|
|
430
|
+
}
|
|
431
|
+
function padEnd(str, len) {
|
|
432
|
+
if (str.length >= len) return str.slice(0, len);
|
|
433
|
+
return str + " ".repeat(len - str.length);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// src/tools/fix.ts
|
|
191
437
|
var pendingFixes = /* @__PURE__ */ new Map();
|
|
192
438
|
var TrieFixTool = class {
|
|
193
439
|
async execute(args) {
|
|
194
|
-
const { issueIds, file, line, issue, fix, autoApprove = false, dryRun = false } = args || {};
|
|
440
|
+
const { issueIds, file, line, issue, fix, autoApprove = false, dryRun = false, action } = args || {};
|
|
441
|
+
if (action === "route") {
|
|
442
|
+
return this.routeIssues(issueIds);
|
|
443
|
+
}
|
|
195
444
|
if (issueIds && issueIds.length > 0) {
|
|
196
445
|
return this.fixByIds(issueIds, autoApprove, dryRun);
|
|
197
446
|
}
|
|
@@ -211,6 +460,37 @@ var TrieFixTool = class {
|
|
|
211
460
|
}]
|
|
212
461
|
};
|
|
213
462
|
}
|
|
463
|
+
async routeIssues(issueIds) {
|
|
464
|
+
const pending = getPendingFixes();
|
|
465
|
+
if (pending.length === 0) {
|
|
466
|
+
return {
|
|
467
|
+
content: [{ type: "text", text: "No pending issues. Run `trie_scan` first to detect issues." }]
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
const issues = pending.map((p) => ({
|
|
471
|
+
id: p.id,
|
|
472
|
+
severity: p.severity ?? "moderate",
|
|
473
|
+
effort: p.effort,
|
|
474
|
+
issue: p.issue,
|
|
475
|
+
fix: p.suggestedFix,
|
|
476
|
+
file: p.file,
|
|
477
|
+
line: p.line,
|
|
478
|
+
confidence: p.confidence,
|
|
479
|
+
autoFixable: p.autoFixable ?? false,
|
|
480
|
+
agent: "trie_scan",
|
|
481
|
+
cwe: p.cwe,
|
|
482
|
+
owasp: p.owasp,
|
|
483
|
+
category: p.category
|
|
484
|
+
}));
|
|
485
|
+
const filtered = issueIds && issueIds.length > 0 ? issues.filter((i) => issueIds.includes(i.id)) : issues;
|
|
486
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
487
|
+
const config = await loadAutonomyConfig(workDir);
|
|
488
|
+
const { results } = triageIssues(filtered, void 0, void 0, config);
|
|
489
|
+
const table = formatTriageTable(results, filtered);
|
|
490
|
+
return { content: [{ type: "text", text: `
|
|
491
|
+
${table}
|
|
492
|
+
` }] };
|
|
493
|
+
}
|
|
214
494
|
async fixByIds(issueIds, autoApprove, dryRun) {
|
|
215
495
|
const results = [];
|
|
216
496
|
let fixed = 0;
|
|
@@ -254,6 +534,7 @@ ${"\u2501".repeat(60)}
|
|
|
254
534
|
|
|
255
535
|
**Summary:** ${fixed} fixed, ${failed} failed, ${issueIds.length - fixed - failed} skipped
|
|
256
536
|
`;
|
|
537
|
+
output += await this.appendCloudRecommendation(issueIds);
|
|
257
538
|
return { content: [{ type: "text", text: output }] };
|
|
258
539
|
}
|
|
259
540
|
async applyFix(file, line, issue, fix, dryRun) {
|
|
@@ -505,6 +786,33 @@ trie_fix
|
|
|
505
786
|
The AI will analyze each issue, generate the fix, and you can review before applying.
|
|
506
787
|
`;
|
|
507
788
|
}
|
|
789
|
+
async appendCloudRecommendation(handledIds) {
|
|
790
|
+
try {
|
|
791
|
+
const pending = getPendingFixes();
|
|
792
|
+
const remaining = pending.filter((p) => !handledIds.includes(p.id) || pendingFixes.get(p.id)?.status === "pending");
|
|
793
|
+
if (remaining.length === 0) return "";
|
|
794
|
+
const issues = remaining.map((p) => ({
|
|
795
|
+
id: p.id,
|
|
796
|
+
severity: "moderate",
|
|
797
|
+
issue: p.issue,
|
|
798
|
+
fix: p.suggestedFix,
|
|
799
|
+
file: p.file,
|
|
800
|
+
line: p.line,
|
|
801
|
+
confidence: p.confidence,
|
|
802
|
+
autoFixable: false,
|
|
803
|
+
agent: "trie_scan"
|
|
804
|
+
}));
|
|
805
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
806
|
+
const config = await loadAutonomyConfig(workDir);
|
|
807
|
+
const { results } = triageIssues(issues, void 0, void 0, config);
|
|
808
|
+
const footer = formatCloudRecommendation(results, issues);
|
|
809
|
+
return footer ? `
|
|
810
|
+
${footer}
|
|
811
|
+
` : "";
|
|
812
|
+
} catch {
|
|
813
|
+
return "";
|
|
814
|
+
}
|
|
815
|
+
}
|
|
508
816
|
detectLanguage(filePath) {
|
|
509
817
|
const ext = extname(filePath).toLowerCase();
|
|
510
818
|
const langMap = {
|
|
@@ -519,11 +827,459 @@ The AI will analyze each issue, generate the fix, and you can review before appl
|
|
|
519
827
|
return langMap[ext] || "plaintext";
|
|
520
828
|
}
|
|
521
829
|
};
|
|
830
|
+
function getPendingFixes() {
|
|
831
|
+
return Array.from(pendingFixes.values());
|
|
832
|
+
}
|
|
522
833
|
|
|
523
|
-
// src/tools/
|
|
524
|
-
import { readFile as readFile2 } from "fs/promises";
|
|
834
|
+
// src/tools/cloud-fix.ts
|
|
835
|
+
import { readFile as readFile2, writeFile, mkdir } from "fs/promises";
|
|
525
836
|
import { existsSync as existsSync2 } from "fs";
|
|
526
|
-
import {
|
|
837
|
+
import { join } from "path";
|
|
838
|
+
import { execSync } from "child_process";
|
|
839
|
+
|
|
840
|
+
// src/integrations/cursor-cloud-agent.ts
|
|
841
|
+
var BASE_URL = "https://api.cursor.com/v1";
|
|
842
|
+
var CursorCloudAgentClient = class {
|
|
843
|
+
apiKey;
|
|
844
|
+
constructor(apiKey) {
|
|
845
|
+
this.apiKey = apiKey;
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Dispatch an issue to a cloud agent for fixing.
|
|
849
|
+
*/
|
|
850
|
+
async dispatch(issue, triageResult, repoUrl, branch) {
|
|
851
|
+
const prompt = this.buildPrompt(issue, triageResult);
|
|
852
|
+
const body = {
|
|
853
|
+
prompt,
|
|
854
|
+
repo: repoUrl,
|
|
855
|
+
branch,
|
|
856
|
+
metadata: {
|
|
857
|
+
issueId: issue.id,
|
|
858
|
+
file: issue.file,
|
|
859
|
+
line: issue.line,
|
|
860
|
+
severity: issue.severity,
|
|
861
|
+
agent: issue.agent
|
|
862
|
+
}
|
|
863
|
+
};
|
|
864
|
+
const res = await this.request("POST", "/agents/tasks", body);
|
|
865
|
+
return {
|
|
866
|
+
jobId: res.id ?? res.taskId ?? res.jobId,
|
|
867
|
+
status: "dispatched",
|
|
868
|
+
artifactUrls: [],
|
|
869
|
+
dispatchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Poll job status.
|
|
874
|
+
*/
|
|
875
|
+
async poll(jobId) {
|
|
876
|
+
const res = await this.request("GET", `/agents/tasks/${jobId}`);
|
|
877
|
+
const status = mapStatus(res.status);
|
|
878
|
+
const prUrl = extractPrUrl(res);
|
|
879
|
+
const artifactUrls = this.extractArtifacts(res);
|
|
880
|
+
return { status, prUrl, artifactUrls };
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Get artifact URLs for a completed job.
|
|
884
|
+
*/
|
|
885
|
+
async getArtifacts(jobId) {
|
|
886
|
+
const res = await this.request("GET", `/agents/tasks/${jobId}`);
|
|
887
|
+
return this.extractArtifacts(res);
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Cancel a running job.
|
|
891
|
+
*/
|
|
892
|
+
async cancelJob(jobId) {
|
|
893
|
+
await this.request("DELETE", `/agents/tasks/${jobId}`);
|
|
894
|
+
}
|
|
895
|
+
// --------------------------------------------------------------------------
|
|
896
|
+
// Internal
|
|
897
|
+
// --------------------------------------------------------------------------
|
|
898
|
+
buildPrompt(issue, triageResult) {
|
|
899
|
+
const parts = [
|
|
900
|
+
"You are fixing a verified issue in the codebase.",
|
|
901
|
+
"",
|
|
902
|
+
`Issue: ${issue.issue}`,
|
|
903
|
+
`File: ${issue.file}${issue.line ? `:${issue.line}` : ""}`,
|
|
904
|
+
`Severity: ${issue.severity} | Effort: ${issue.effort ?? "medium"}`,
|
|
905
|
+
`Agent that found it: ${issue.agent}`,
|
|
906
|
+
`Suggested fix: ${issue.fix}`
|
|
907
|
+
];
|
|
908
|
+
if (issue.cwe) parts.push(`CWE: ${issue.cwe}`);
|
|
909
|
+
if (issue.owasp) parts.push(`OWASP: ${issue.owasp}`);
|
|
910
|
+
parts.push(`Triage confidence: ${triageResult.confidence.toFixed(2)}`);
|
|
911
|
+
parts.push(`Why cloud agent: ${triageResult.reasons.join(", ")}`);
|
|
912
|
+
parts.push("");
|
|
913
|
+
parts.push("Steps:");
|
|
914
|
+
parts.push(`1. Apply the minimal fix described above to ${issue.file}`);
|
|
915
|
+
parts.push("2. Run the existing test suite (detect test runner from package.json scripts)");
|
|
916
|
+
parts.push("3. Screenshot the passing test output \u2014 this is the verification artifact");
|
|
917
|
+
parts.push("4. If tests fail, iterate on the fix until they pass (max 3 attempts)");
|
|
918
|
+
parts.push("5. Open a PR with only this single change \u2014 do not bundle other fixes");
|
|
919
|
+
parts.push("6. Include the screenshot in the PR description as evidence");
|
|
920
|
+
return parts.join("\n");
|
|
921
|
+
}
|
|
922
|
+
async request(method, path2, body) {
|
|
923
|
+
const url = `${BASE_URL}${path2}`;
|
|
924
|
+
const headers = {
|
|
925
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
926
|
+
"Content-Type": "application/json"
|
|
927
|
+
};
|
|
928
|
+
const init = { method, headers };
|
|
929
|
+
if (body) init.body = JSON.stringify(body);
|
|
930
|
+
const res = await fetch(url, init);
|
|
931
|
+
if (res.status === 401) {
|
|
932
|
+
throw new Error(
|
|
933
|
+
'Cursor API key is invalid or expired.\nUpdate it: trie_cloud_fix action:configure apiKey:"key-..."'
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
if (res.status === 404) {
|
|
937
|
+
throw new Error(`Job not found: ${path2}`);
|
|
938
|
+
}
|
|
939
|
+
if (res.status >= 500) {
|
|
940
|
+
throw new Error(
|
|
941
|
+
`Cursor API returned ${res.status}. The service may be temporarily unavailable \u2014 retry in a few minutes.`
|
|
942
|
+
);
|
|
943
|
+
}
|
|
944
|
+
if (!res.ok) {
|
|
945
|
+
const text = await res.text().catch(() => "");
|
|
946
|
+
throw new Error(`Cursor API error ${res.status}: ${text}`);
|
|
947
|
+
}
|
|
948
|
+
return res.json();
|
|
949
|
+
}
|
|
950
|
+
extractArtifacts(res) {
|
|
951
|
+
const urls = [];
|
|
952
|
+
if (Array.isArray(res.messages)) {
|
|
953
|
+
for (const msg of res.messages) {
|
|
954
|
+
if (typeof msg.content === "string") {
|
|
955
|
+
const matches = msg.content.match(/https:\/\/[^\s)]+\.(png|jpg|mp4|webm)/g);
|
|
956
|
+
if (matches) urls.push(...matches);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
if (Array.isArray(res.artifacts)) {
|
|
961
|
+
for (const a of res.artifacts) {
|
|
962
|
+
if (a.url) urls.push(a.url);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
return [...new Set(urls)];
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
function mapStatus(raw) {
|
|
969
|
+
switch (raw) {
|
|
970
|
+
case "pending":
|
|
971
|
+
case "queued":
|
|
972
|
+
return "pending";
|
|
973
|
+
case "running":
|
|
974
|
+
case "in_progress":
|
|
975
|
+
return "running";
|
|
976
|
+
case "completed":
|
|
977
|
+
case "succeeded":
|
|
978
|
+
case "verified":
|
|
979
|
+
return "completed";
|
|
980
|
+
default:
|
|
981
|
+
return "failed";
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
function extractPrUrl(res) {
|
|
985
|
+
if (typeof res.prUrl === "string") return res.prUrl;
|
|
986
|
+
if (typeof res.pr_url === "string") return res.pr_url;
|
|
987
|
+
if (typeof res.pullRequestUrl === "string") return res.pullRequestUrl;
|
|
988
|
+
if (Array.isArray(res.messages)) {
|
|
989
|
+
for (const msg of res.messages) {
|
|
990
|
+
if (typeof msg.content === "string") {
|
|
991
|
+
const match = msg.content.match(/https:\/\/github\.com\/[^\s)]+\/pull\/\d+/);
|
|
992
|
+
if (match) return match[0];
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return void 0;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// src/tools/cloud-fix.ts
|
|
1000
|
+
var TrieCloudFixTool = class {
|
|
1001
|
+
async execute(args) {
|
|
1002
|
+
const { action = "status" } = args || {};
|
|
1003
|
+
switch (action) {
|
|
1004
|
+
case "configure":
|
|
1005
|
+
return this.configure(args);
|
|
1006
|
+
case "dispatch":
|
|
1007
|
+
return this.dispatch(args);
|
|
1008
|
+
case "status":
|
|
1009
|
+
return this.status();
|
|
1010
|
+
case "artifacts":
|
|
1011
|
+
return this.artifacts(args);
|
|
1012
|
+
case "cancel":
|
|
1013
|
+
return this.cancel(args);
|
|
1014
|
+
default:
|
|
1015
|
+
return this.text(`Unknown action "${action}". Use: configure | dispatch | status | artifacts | cancel`);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
// --------------------------------------------------------------------------
|
|
1019
|
+
// configure
|
|
1020
|
+
// --------------------------------------------------------------------------
|
|
1021
|
+
async configure(args) {
|
|
1022
|
+
const apiKey = args?.apiKey || process.env.CURSOR_API_KEY;
|
|
1023
|
+
if (!apiKey) {
|
|
1024
|
+
return this.text(
|
|
1025
|
+
'Missing API key.\n\nUsage: trie_cloud_fix action:configure apiKey:"key-..."\nOr set: export CURSOR_API_KEY="key-..."'
|
|
1026
|
+
);
|
|
1027
|
+
}
|
|
1028
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
1029
|
+
await saveAutonomyConfig(workDir, { cloudAgentEnabled: true, cursorApiKey: apiKey });
|
|
1030
|
+
return this.text("Cursor API key saved. Cloud agent dispatch enabled.");
|
|
1031
|
+
}
|
|
1032
|
+
// --------------------------------------------------------------------------
|
|
1033
|
+
// dispatch
|
|
1034
|
+
// --------------------------------------------------------------------------
|
|
1035
|
+
async dispatch(args) {
|
|
1036
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
1037
|
+
const apiKey = await this.resolveApiKey(workDir);
|
|
1038
|
+
if (!apiKey) return this.setupGuard();
|
|
1039
|
+
const config = await loadAutonomyConfig(workDir);
|
|
1040
|
+
const allIssues = this.resolveIssues(args?.issueIds);
|
|
1041
|
+
if (allIssues.length === 0) {
|
|
1042
|
+
return this.text("No issues to dispatch. Run trie_scan first, then trie_fix action:route.");
|
|
1043
|
+
}
|
|
1044
|
+
const { results, summary } = triageIssues(allIssues, void 0, void 0, config);
|
|
1045
|
+
const lines = [];
|
|
1046
|
+
lines.push(formatTriageTable(results, allIssues));
|
|
1047
|
+
lines.push("");
|
|
1048
|
+
for (const issue of allIssues) {
|
|
1049
|
+
const r = results.get(issue.id);
|
|
1050
|
+
if (r && r.strategy !== "cloud-agent") {
|
|
1051
|
+
lines.push(`Skipped ${issue.id}: routed to ${r.strategy} (score ${r.score >= 0 ? "+" : ""}${r.score} \u2014 ${r.reasons.join(", ")})`);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
if (summary.cloudAgent.length === 0) {
|
|
1055
|
+
lines.push("\nNo issues qualify for cloud dispatch. Use trie_fix for local fixes.");
|
|
1056
|
+
return this.text(lines.join("\n"));
|
|
1057
|
+
}
|
|
1058
|
+
const client = new CursorCloudAgentClient(apiKey);
|
|
1059
|
+
const repoUrl = this.getRepoUrl(workDir);
|
|
1060
|
+
const branch = this.getBranch(workDir);
|
|
1061
|
+
const store = await this.loadJobs(workDir);
|
|
1062
|
+
lines.push("\nDISPATCHED");
|
|
1063
|
+
for (const issue of summary.cloudAgent) {
|
|
1064
|
+
const triageResult = results.get(issue.id);
|
|
1065
|
+
try {
|
|
1066
|
+
const job = await client.dispatch(issue, triageResult, repoUrl, branch);
|
|
1067
|
+
store.jobs[issue.id] = {
|
|
1068
|
+
issueId: issue.id,
|
|
1069
|
+
jobId: job.jobId,
|
|
1070
|
+
status: "dispatched",
|
|
1071
|
+
score: triageResult.score,
|
|
1072
|
+
strategy: "cloud-agent",
|
|
1073
|
+
dispatchedAt: job.dispatchedAt,
|
|
1074
|
+
artifactUrls: [],
|
|
1075
|
+
prUrl: null
|
|
1076
|
+
};
|
|
1077
|
+
lines.push(` ${issue.id} ${shortPath2(issue.file)}:${issue.line ?? "?"} job:${job.jobId} (score +${triageResult.score})`);
|
|
1078
|
+
} catch (err) {
|
|
1079
|
+
lines.push(` ${issue.id} FAILED: ${err.message}`);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
await this.saveJobs(workDir, store);
|
|
1083
|
+
lines.push("");
|
|
1084
|
+
lines.push("Cloud agents are running in isolated VMs. Check back with:");
|
|
1085
|
+
lines.push("trie_cloud_fix action:status");
|
|
1086
|
+
return this.text(lines.join("\n"));
|
|
1087
|
+
}
|
|
1088
|
+
// --------------------------------------------------------------------------
|
|
1089
|
+
// status
|
|
1090
|
+
// --------------------------------------------------------------------------
|
|
1091
|
+
async status() {
|
|
1092
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
1093
|
+
const apiKey = await this.resolveApiKey(workDir);
|
|
1094
|
+
if (!apiKey) return this.setupGuard();
|
|
1095
|
+
const store = await this.loadJobs(workDir);
|
|
1096
|
+
const entries = Object.values(store.jobs);
|
|
1097
|
+
if (entries.length === 0) {
|
|
1098
|
+
return this.text("No cloud jobs. Dispatch with: trie_cloud_fix action:dispatch");
|
|
1099
|
+
}
|
|
1100
|
+
const client = new CursorCloudAgentClient(apiKey);
|
|
1101
|
+
const LINE = "\u2500".repeat(68);
|
|
1102
|
+
const lines = ["JOB STATUS", LINE];
|
|
1103
|
+
lines.push(padEnd2("Issue", 32) + padEnd2("Status", 12) + padEnd2("PR", 28) + "Age");
|
|
1104
|
+
for (const job of entries) {
|
|
1105
|
+
if (job.status === "dispatched" || job.status === "running") {
|
|
1106
|
+
try {
|
|
1107
|
+
const poll = await client.poll(job.jobId);
|
|
1108
|
+
if (poll.status === "completed" && poll.prUrl) {
|
|
1109
|
+
job.status = "verified";
|
|
1110
|
+
job.prUrl = poll.prUrl;
|
|
1111
|
+
} else if (poll.status === "running") {
|
|
1112
|
+
job.status = "running";
|
|
1113
|
+
} else if (poll.status === "failed") {
|
|
1114
|
+
job.status = "failed";
|
|
1115
|
+
}
|
|
1116
|
+
if (poll.artifactUrls.length) {
|
|
1117
|
+
job.artifactUrls = poll.artifactUrls;
|
|
1118
|
+
}
|
|
1119
|
+
} catch {
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
const age = formatAge(job.dispatchedAt);
|
|
1123
|
+
const pr = job.prUrl ?? "\u2014";
|
|
1124
|
+
lines.push(padEnd2(job.issueId, 32) + padEnd2(job.status, 12) + padEnd2(pr, 28) + age);
|
|
1125
|
+
}
|
|
1126
|
+
lines.push(LINE);
|
|
1127
|
+
await this.saveJobs(workDir, store);
|
|
1128
|
+
return this.text(lines.join("\n"));
|
|
1129
|
+
}
|
|
1130
|
+
// --------------------------------------------------------------------------
|
|
1131
|
+
// artifacts
|
|
1132
|
+
// --------------------------------------------------------------------------
|
|
1133
|
+
async artifacts(args) {
|
|
1134
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
1135
|
+
const apiKey = await this.resolveApiKey(workDir);
|
|
1136
|
+
if (!apiKey) return this.setupGuard();
|
|
1137
|
+
const store = await this.loadJobs(workDir);
|
|
1138
|
+
const jobId = args?.jobId;
|
|
1139
|
+
const targets = jobId ? Object.values(store.jobs).filter((j) => j.jobId === jobId) : Object.values(store.jobs).filter((j) => j.status === "verified");
|
|
1140
|
+
if (targets.length === 0) {
|
|
1141
|
+
return this.text(jobId ? `No job found with id ${jobId}` : "No completed jobs with artifacts.");
|
|
1142
|
+
}
|
|
1143
|
+
const client = new CursorCloudAgentClient(apiKey);
|
|
1144
|
+
const lines = [];
|
|
1145
|
+
for (const job of targets) {
|
|
1146
|
+
try {
|
|
1147
|
+
const artifacts = await client.getArtifacts(job.jobId);
|
|
1148
|
+
job.artifactUrls = artifacts;
|
|
1149
|
+
} catch {
|
|
1150
|
+
}
|
|
1151
|
+
lines.push(`Issue: ${job.issueId}`);
|
|
1152
|
+
lines.push(`PR: ${job.prUrl ?? "N/A"}`);
|
|
1153
|
+
for (const url of job.artifactUrls) {
|
|
1154
|
+
const ext = url.split(".").pop();
|
|
1155
|
+
const label = ext === "mp4" || ext === "webm" ? "Video" : "Screenshot";
|
|
1156
|
+
lines.push(`${label}: ${url}`);
|
|
1157
|
+
}
|
|
1158
|
+
if (job.status === "verified") {
|
|
1159
|
+
lines.push("Agent verified: all tests pass with this fix applied.");
|
|
1160
|
+
}
|
|
1161
|
+
lines.push("");
|
|
1162
|
+
}
|
|
1163
|
+
await this.saveJobs(workDir, store);
|
|
1164
|
+
return this.text(lines.join("\n"));
|
|
1165
|
+
}
|
|
1166
|
+
// --------------------------------------------------------------------------
|
|
1167
|
+
// cancel
|
|
1168
|
+
// --------------------------------------------------------------------------
|
|
1169
|
+
async cancel(args) {
|
|
1170
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
1171
|
+
const apiKey = await this.resolveApiKey(workDir);
|
|
1172
|
+
if (!apiKey) return this.setupGuard();
|
|
1173
|
+
const jobId = args?.jobId;
|
|
1174
|
+
if (!jobId) {
|
|
1175
|
+
return this.text('Provide jobId to cancel. Example: trie_cloud_fix action:cancel jobId:"cursor-task-xyz"');
|
|
1176
|
+
}
|
|
1177
|
+
const store = await this.loadJobs(workDir);
|
|
1178
|
+
const entry = Object.values(store.jobs).find((j) => j.jobId === jobId);
|
|
1179
|
+
if (!entry) {
|
|
1180
|
+
return this.text(`Job ${jobId} not found in cloud-jobs.json.`);
|
|
1181
|
+
}
|
|
1182
|
+
const client = new CursorCloudAgentClient(apiKey);
|
|
1183
|
+
try {
|
|
1184
|
+
await client.cancelJob(jobId);
|
|
1185
|
+
} catch (err) {
|
|
1186
|
+
return this.text(`Cancel failed: ${err.message}`);
|
|
1187
|
+
}
|
|
1188
|
+
delete store.jobs[entry.issueId];
|
|
1189
|
+
await this.saveJobs(workDir, store);
|
|
1190
|
+
return this.text(`Job ${jobId} cancelled and removed.`);
|
|
1191
|
+
}
|
|
1192
|
+
// --------------------------------------------------------------------------
|
|
1193
|
+
// Helpers
|
|
1194
|
+
// --------------------------------------------------------------------------
|
|
1195
|
+
async resolveApiKey(workDir) {
|
|
1196
|
+
if (process.env.CURSOR_API_KEY) return process.env.CURSOR_API_KEY;
|
|
1197
|
+
const config = await loadAutonomyConfig(workDir);
|
|
1198
|
+
return config.cursorApiKey ?? null;
|
|
1199
|
+
}
|
|
1200
|
+
setupGuard() {
|
|
1201
|
+
return this.text(
|
|
1202
|
+
'Cloud Agent dispatch requires a Cursor API key.\n\nGet your key at: cursor.com/settings \u2192 API Keys\nThen run: trie_cloud_fix action:configure apiKey:"key-..."\n\nOr set the environment variable: export CURSOR_API_KEY="key-..."'
|
|
1203
|
+
);
|
|
1204
|
+
}
|
|
1205
|
+
resolveIssues(issueIds) {
|
|
1206
|
+
const pending = getPendingFixes();
|
|
1207
|
+
const issues = pending.map((p) => ({
|
|
1208
|
+
id: p.id,
|
|
1209
|
+
severity: p.severity ?? "moderate",
|
|
1210
|
+
effort: p.effort,
|
|
1211
|
+
issue: p.issue,
|
|
1212
|
+
fix: p.suggestedFix,
|
|
1213
|
+
file: p.file,
|
|
1214
|
+
line: p.line,
|
|
1215
|
+
confidence: p.confidence,
|
|
1216
|
+
autoFixable: p.autoFixable ?? false,
|
|
1217
|
+
agent: "trie_scan",
|
|
1218
|
+
cwe: p.cwe,
|
|
1219
|
+
owasp: p.owasp,
|
|
1220
|
+
category: p.category
|
|
1221
|
+
}));
|
|
1222
|
+
if (issueIds && issueIds.length > 0) {
|
|
1223
|
+
return issues.filter((i) => issueIds.includes(i.id));
|
|
1224
|
+
}
|
|
1225
|
+
return issues;
|
|
1226
|
+
}
|
|
1227
|
+
getRepoUrl(workDir) {
|
|
1228
|
+
try {
|
|
1229
|
+
return execSync("git remote get-url origin", { cwd: workDir, encoding: "utf-8" }).trim();
|
|
1230
|
+
} catch {
|
|
1231
|
+
return "unknown";
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
getBranch(workDir) {
|
|
1235
|
+
try {
|
|
1236
|
+
return execSync("git rev-parse --abbrev-ref HEAD", { cwd: workDir, encoding: "utf-8" }).trim();
|
|
1237
|
+
} catch {
|
|
1238
|
+
return "main";
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
async loadJobs(workDir) {
|
|
1242
|
+
const path2 = join(getTrieDirectory(workDir), "cloud-jobs.json");
|
|
1243
|
+
try {
|
|
1244
|
+
if (existsSync2(path2)) {
|
|
1245
|
+
const raw = await readFile2(path2, "utf-8");
|
|
1246
|
+
return JSON.parse(raw);
|
|
1247
|
+
}
|
|
1248
|
+
} catch {
|
|
1249
|
+
}
|
|
1250
|
+
return { jobs: {} };
|
|
1251
|
+
}
|
|
1252
|
+
async saveJobs(workDir, store) {
|
|
1253
|
+
const trieDir = getTrieDirectory(workDir);
|
|
1254
|
+
if (!existsSync2(trieDir)) await mkdir(trieDir, { recursive: true });
|
|
1255
|
+
const path2 = join(trieDir, "cloud-jobs.json");
|
|
1256
|
+
await writeFile(path2, JSON.stringify(store, null, 2));
|
|
1257
|
+
}
|
|
1258
|
+
text(msg) {
|
|
1259
|
+
return { content: [{ type: "text", text: msg }] };
|
|
1260
|
+
}
|
|
1261
|
+
};
|
|
1262
|
+
function formatAge(isoDate) {
|
|
1263
|
+
const ms = Date.now() - new Date(isoDate).getTime();
|
|
1264
|
+
const mins = Math.floor(ms / 6e4);
|
|
1265
|
+
if (mins < 60) return `${mins}m`;
|
|
1266
|
+
const hrs = Math.floor(mins / 60);
|
|
1267
|
+
if (hrs < 24) return `${hrs}h ${mins % 60}m`;
|
|
1268
|
+
return `${Math.floor(hrs / 24)}d`;
|
|
1269
|
+
}
|
|
1270
|
+
function shortPath2(file) {
|
|
1271
|
+
const parts = file.split("/");
|
|
1272
|
+
return parts.length > 2 ? parts.slice(-2).join("/") : file;
|
|
1273
|
+
}
|
|
1274
|
+
function padEnd2(str, len) {
|
|
1275
|
+
if (str.length >= len) return str.slice(0, len);
|
|
1276
|
+
return str + " ".repeat(len - str.length);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// src/tools/test.ts
|
|
1280
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1281
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1282
|
+
import { extname as extname2, relative as relative2, resolve as resolve2, isAbsolute as isAbsolute2, dirname, basename, join as join2 } from "path";
|
|
527
1283
|
var TrieTestTool = class {
|
|
528
1284
|
async execute(args) {
|
|
529
1285
|
const { action, files, framework, style = "unit" } = args || {};
|
|
@@ -577,12 +1333,12 @@ ${"\u2501".repeat(60)}
|
|
|
577
1333
|
const allUnits = [];
|
|
578
1334
|
for (const file of files) {
|
|
579
1335
|
const resolvedPath = isAbsolute2(file) ? file : resolve2(workDir, file);
|
|
580
|
-
if (!
|
|
1336
|
+
if (!existsSync3(resolvedPath)) {
|
|
581
1337
|
output += `[!] File not found: ${file}
|
|
582
1338
|
`;
|
|
583
1339
|
continue;
|
|
584
1340
|
}
|
|
585
|
-
const code = await
|
|
1341
|
+
const code = await readFile3(resolvedPath, "utf-8");
|
|
586
1342
|
const language = this.detectLanguage(resolvedPath);
|
|
587
1343
|
const relativePath = relative2(workDir, resolvedPath);
|
|
588
1344
|
const units = this.extractTestableUnits(code, language);
|
|
@@ -615,7 +1371,7 @@ ${"\u2501".repeat(60)}
|
|
|
615
1371
|
`;
|
|
616
1372
|
for (const { file, units } of allUnits) {
|
|
617
1373
|
if (units.length === 0) continue;
|
|
618
|
-
const code = await
|
|
1374
|
+
const code = await readFile3(resolve2(workDir, file), "utf-8");
|
|
619
1375
|
const language = this.detectLanguage(file);
|
|
620
1376
|
const prompt = getPrompt("test", "generate", {
|
|
621
1377
|
code,
|
|
@@ -661,12 +1417,12 @@ ${"\u2501".repeat(60)}
|
|
|
661
1417
|
const workDir = getWorkingDirectory(void 0, true);
|
|
662
1418
|
for (const file of files) {
|
|
663
1419
|
const resolvedPath = isAbsolute2(file) ? file : resolve2(workDir, file);
|
|
664
|
-
if (!
|
|
1420
|
+
if (!existsSync3(resolvedPath)) {
|
|
665
1421
|
output += `[!] File not found: ${file}
|
|
666
1422
|
`;
|
|
667
1423
|
continue;
|
|
668
1424
|
}
|
|
669
|
-
const code = await
|
|
1425
|
+
const code = await readFile3(resolvedPath, "utf-8");
|
|
670
1426
|
const language = this.detectLanguage(resolvedPath);
|
|
671
1427
|
const relativePath = relative2(workDir, resolvedPath);
|
|
672
1428
|
const units = this.extractTestableUnits(code, language);
|
|
@@ -674,7 +1430,7 @@ ${"\u2501".repeat(60)}
|
|
|
674
1430
|
let testCode = "";
|
|
675
1431
|
let testedUnits = [];
|
|
676
1432
|
if (testFile) {
|
|
677
|
-
testCode = await
|
|
1433
|
+
testCode = await readFile3(testFile, "utf-8");
|
|
678
1434
|
testedUnits = this.findTestedUnits(testCode, units.map((u) => u.name));
|
|
679
1435
|
}
|
|
680
1436
|
const coverage = units.length > 0 ? Math.round(testedUnits.length / units.length * 100) : 0;
|
|
@@ -743,12 +1499,12 @@ ${"\u2501".repeat(60)}
|
|
|
743
1499
|
const workDir = getWorkingDirectory(void 0, true);
|
|
744
1500
|
for (const file of files) {
|
|
745
1501
|
const resolvedPath = isAbsolute2(file) ? file : resolve2(workDir, file);
|
|
746
|
-
if (!
|
|
1502
|
+
if (!existsSync3(resolvedPath)) {
|
|
747
1503
|
output += `[!] File not found: ${file}
|
|
748
1504
|
`;
|
|
749
1505
|
continue;
|
|
750
1506
|
}
|
|
751
|
-
const code = await
|
|
1507
|
+
const code = await readFile3(resolvedPath, "utf-8");
|
|
752
1508
|
const relativePath = relative2(workDir, resolvedPath);
|
|
753
1509
|
const patterns = this.detectTestablePatterns(code);
|
|
754
1510
|
output += `### \u{1F4C4} ${relativePath}
|
|
@@ -994,16 +1750,16 @@ ${"\u2501".repeat(60)}
|
|
|
994
1750
|
`test_${base}${ext}`
|
|
995
1751
|
];
|
|
996
1752
|
for (const pattern of patterns) {
|
|
997
|
-
const testPath =
|
|
998
|
-
if (
|
|
1753
|
+
const testPath = join2(dir, pattern);
|
|
1754
|
+
if (existsSync3(testPath)) {
|
|
999
1755
|
return testPath;
|
|
1000
1756
|
}
|
|
1001
1757
|
}
|
|
1002
|
-
const testsDir =
|
|
1003
|
-
if (
|
|
1758
|
+
const testsDir = join2(dir, "__tests__");
|
|
1759
|
+
if (existsSync3(testsDir)) {
|
|
1004
1760
|
for (const pattern of patterns) {
|
|
1005
|
-
const testPath =
|
|
1006
|
-
if (
|
|
1761
|
+
const testPath = join2(testsDir, pattern);
|
|
1762
|
+
if (existsSync3(testPath)) {
|
|
1007
1763
|
return testPath;
|
|
1008
1764
|
}
|
|
1009
1765
|
}
|
|
@@ -1051,9 +1807,9 @@ ${"\u2501".repeat(60)}
|
|
|
1051
1807
|
async detectTestFramework() {
|
|
1052
1808
|
const workDir = getWorkingDirectory(void 0, true);
|
|
1053
1809
|
const packagePath = resolve2(workDir, "package.json");
|
|
1054
|
-
if (
|
|
1810
|
+
if (existsSync3(packagePath)) {
|
|
1055
1811
|
try {
|
|
1056
|
-
const pkg = JSON.parse(await
|
|
1812
|
+
const pkg = JSON.parse(await readFile3(packagePath, "utf-8"));
|
|
1057
1813
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
1058
1814
|
if (deps.vitest) return "vitest";
|
|
1059
1815
|
if (deps.jest) return "jest";
|
|
@@ -1061,7 +1817,7 @@ ${"\u2501".repeat(60)}
|
|
|
1061
1817
|
} catch {
|
|
1062
1818
|
}
|
|
1063
1819
|
}
|
|
1064
|
-
if (
|
|
1820
|
+
if (existsSync3(resolve2(workDir, "pytest.ini")) || existsSync3(resolve2(workDir, "pyproject.toml"))) {
|
|
1065
1821
|
return "pytest";
|
|
1066
1822
|
}
|
|
1067
1823
|
return "jest";
|
|
@@ -1120,9 +1876,9 @@ trie_test action:"run" files:["src/utils.test.ts"]
|
|
|
1120
1876
|
};
|
|
1121
1877
|
|
|
1122
1878
|
// src/tools/watch.ts
|
|
1123
|
-
import { watch, existsSync as
|
|
1124
|
-
import { stat, readFile as
|
|
1125
|
-
import { join as
|
|
1879
|
+
import { watch, existsSync as existsSync4, readFileSync } from "fs";
|
|
1880
|
+
import { stat, readFile as readFile4 } from "fs/promises";
|
|
1881
|
+
import { join as join3, extname as extname3, basename as basename2 } from "path";
|
|
1126
1882
|
import { createHash } from "crypto";
|
|
1127
1883
|
var WATCH_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1128
1884
|
".ts",
|
|
@@ -1176,8 +1932,11 @@ var TrieWatchTool = class _TrieWatchTool {
|
|
|
1176
1932
|
streamingManager = void 0;
|
|
1177
1933
|
dashboard = void 0;
|
|
1178
1934
|
lastHypothesisCheck = 0;
|
|
1935
|
+
pipelineSyncTimer = null;
|
|
1179
1936
|
static HYPOTHESIS_CHECK_INTERVAL_MS = 3e5;
|
|
1180
1937
|
// Check every 5 minutes
|
|
1938
|
+
static PIPELINE_SYNC_INTERVAL_MS = 30 * 60 * 1e3;
|
|
1939
|
+
// 30 minutes
|
|
1181
1940
|
async execute(args) {
|
|
1182
1941
|
const { action, directory, debounceMs = 1e3 } = args;
|
|
1183
1942
|
switch (action) {
|
|
@@ -1237,16 +1996,27 @@ var TrieWatchTool = class _TrieWatchTool {
|
|
|
1237
1996
|
const storage = getStorage(directory);
|
|
1238
1997
|
await storage.initialize();
|
|
1239
1998
|
const unresolvedNudges = await storage.queryNudges({ resolved: false });
|
|
1999
|
+
console.debug(`[Watch] Found ${unresolvedNudges.length} unresolved nudges in storage`);
|
|
1240
2000
|
for (const nudge of unresolvedNudges) {
|
|
2001
|
+
let mappedSeverity = "high";
|
|
2002
|
+
if (nudge.severity === "critical") {
|
|
2003
|
+
mappedSeverity = "critical";
|
|
2004
|
+
} else if (nudge.severity === "high") {
|
|
2005
|
+
mappedSeverity = "high";
|
|
2006
|
+
}
|
|
1241
2007
|
this.state.nudges.push({
|
|
1242
|
-
file: nudge.file
|
|
2008
|
+
file: nudge.file || "unknown",
|
|
2009
|
+
// Keep full path, don't use basename
|
|
1243
2010
|
message: nudge.message,
|
|
1244
|
-
severity:
|
|
2011
|
+
severity: mappedSeverity,
|
|
1245
2012
|
timestamp: new Date(nudge.timestamp).toLocaleTimeString("en-US", { hour12: false })
|
|
1246
2013
|
});
|
|
2014
|
+
console.debug(`[Watch] Loaded nudge: ${nudge.message.slice(0, 60)}... (${nudge.severity} -> ${mappedSeverity})`);
|
|
1247
2015
|
}
|
|
1248
2016
|
if (unresolvedNudges.length > 0) {
|
|
1249
|
-
console.
|
|
2017
|
+
console.log(`[Watch] \u2713 Loaded ${unresolvedNudges.length} unresolved nudges from storage`);
|
|
2018
|
+
} else {
|
|
2019
|
+
console.debug(`[Watch] No unresolved nudges found in storage`);
|
|
1250
2020
|
}
|
|
1251
2021
|
} catch (error) {
|
|
1252
2022
|
console.error("[Watch] Failed to load nudges from storage:", error);
|
|
@@ -1284,7 +2054,11 @@ var TrieWatchTool = class _TrieWatchTool {
|
|
|
1284
2054
|
setTimeout(() => {
|
|
1285
2055
|
void this.initialGoalComplianceScan();
|
|
1286
2056
|
void this.initialHypothesisGeneration();
|
|
2057
|
+
void this.syncPipelineIntegrations();
|
|
1287
2058
|
}, 1e3);
|
|
2059
|
+
this.pipelineSyncTimer = setInterval(() => {
|
|
2060
|
+
void this.syncPipelineIntegrations();
|
|
2061
|
+
}, _TrieWatchTool.PIPELINE_SYNC_INTERVAL_MS);
|
|
1288
2062
|
const indexStatus = this.codebaseIndex?.isEmpty() ? "BUILDING (one-time, speeds up goal checks)" : "READY";
|
|
1289
2063
|
return {
|
|
1290
2064
|
content: [{
|
|
@@ -1322,7 +2096,7 @@ Your Trie agent is now autonomously watching and learning from your codebase.
|
|
|
1322
2096
|
return parts.some((p) => SKIP_DIRS.has(p) || p.startsWith(".") && p !== ".");
|
|
1323
2097
|
}
|
|
1324
2098
|
async watchDirectory(dir, debounceMs) {
|
|
1325
|
-
if (!
|
|
2099
|
+
if (!existsSync4(dir)) return;
|
|
1326
2100
|
try {
|
|
1327
2101
|
const dirStat = await stat(dir);
|
|
1328
2102
|
if (!dirStat.isDirectory()) return;
|
|
@@ -1331,8 +2105,8 @@ Your Trie agent is now autonomously watching and learning from your codebase.
|
|
|
1331
2105
|
if (this.shouldSkipPath(filename)) return;
|
|
1332
2106
|
const ext = extname3(filename).toLowerCase();
|
|
1333
2107
|
if (!WATCH_EXTENSIONS.has(ext)) return;
|
|
1334
|
-
const fullPath =
|
|
1335
|
-
if (!
|
|
2108
|
+
const fullPath = join3(dir, filename);
|
|
2109
|
+
if (!existsSync4(fullPath)) return;
|
|
1336
2110
|
this.state.pendingFiles.add(fullPath);
|
|
1337
2111
|
if (this.state.scanDebounceTimer) {
|
|
1338
2112
|
clearTimeout(this.state.scanDebounceTimer);
|
|
@@ -1378,7 +2152,7 @@ Detected changes in ${files.length} file(s):`);
|
|
|
1378
2152
|
const fileContents = await Promise.all(
|
|
1379
2153
|
files.map(async (file) => {
|
|
1380
2154
|
try {
|
|
1381
|
-
const content = await
|
|
2155
|
+
const content = await readFile4(file, "utf-8");
|
|
1382
2156
|
return { file, content };
|
|
1383
2157
|
} catch {
|
|
1384
2158
|
return null;
|
|
@@ -1456,7 +2230,7 @@ ${f.content.slice(0, 1e3)}`
|
|
|
1456
2230
|
}
|
|
1457
2231
|
isQuiet() {
|
|
1458
2232
|
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
1459
|
-
const quietPath =
|
|
2233
|
+
const quietPath = join3(getTrieDirectory(projectPath), "quiet.json");
|
|
1460
2234
|
try {
|
|
1461
2235
|
const raw = readFileSync(quietPath, "utf-8");
|
|
1462
2236
|
const data = JSON.parse(raw);
|
|
@@ -1473,7 +2247,7 @@ ${f.content.slice(0, 1e3)}`
|
|
|
1473
2247
|
async checkAndGenerateHypotheses(projectPath) {
|
|
1474
2248
|
if (!isAIAvailable()) return;
|
|
1475
2249
|
try {
|
|
1476
|
-
const { getHypothesisEngine } = await import("./hypothesis-
|
|
2250
|
+
const { getHypothesisEngine } = await import("./hypothesis-JCUMZKTG.js");
|
|
1477
2251
|
const { getOutputManager: getOutputManager2 } = await import("./output-manager-DZO5LGSG.js");
|
|
1478
2252
|
const hypothesisEngine = getHypothesisEngine(projectPath);
|
|
1479
2253
|
const recentIssues = Array.from(this.state.issueCache.values()).flat();
|
|
@@ -1540,7 +2314,7 @@ ${f.content.slice(0, 1e3)}`
|
|
|
1540
2314
|
const totalIssues = Array.from(this.state.issueCache.values()).flat().length;
|
|
1541
2315
|
if (totalIssues < 5) return;
|
|
1542
2316
|
try {
|
|
1543
|
-
const { ContextGraph: ContextGraph2 } = await import("./graph-
|
|
2317
|
+
const { ContextGraph: ContextGraph2 } = await import("./graph-J4OGTYCO.js");
|
|
1544
2318
|
const { IncidentIndex } = await import("./incident-index-BWW2UEY7.js");
|
|
1545
2319
|
const { TriePatternDiscovery } = await import("./pattern-discovery-F7LU5K6E.js");
|
|
1546
2320
|
const graph = new ContextGraph2(projectPath);
|
|
@@ -1624,7 +2398,7 @@ ${f.content.slice(0, 1e3)}`
|
|
|
1624
2398
|
if (lastClean && Date.now() - lastClean < this.cleanFileCooldownMs) {
|
|
1625
2399
|
return 0;
|
|
1626
2400
|
}
|
|
1627
|
-
const fileNode = await graph.getNode("file",
|
|
2401
|
+
const fileNode = await graph.getNode("file", join3(projectPath, file));
|
|
1628
2402
|
if (!fileNode) return score;
|
|
1629
2403
|
const data = fileNode.data;
|
|
1630
2404
|
const riskScores = { critical: 10, high: 6, medium: 2, low: 1 };
|
|
@@ -1683,7 +2457,7 @@ ${f.content.slice(0, 1e3)}`
|
|
|
1683
2457
|
if (remaining < 500) return;
|
|
1684
2458
|
try {
|
|
1685
2459
|
const graph = new ContextGraph(projectPath);
|
|
1686
|
-
const { getActiveGoals, recordGoalViolationCaught } = await import("./goal-validator-
|
|
2460
|
+
const { getActiveGoals, recordGoalViolationCaught } = await import("./goal-validator-HNXXUCPW.js");
|
|
1687
2461
|
console.debug("[AI Watcher] Loading active goals...");
|
|
1688
2462
|
const activeGoals = await getActiveGoals(projectPath);
|
|
1689
2463
|
const hasGoals = activeGoals.length > 0;
|
|
@@ -1712,7 +2486,7 @@ ${f.content.slice(0, 1e3)}`
|
|
|
1712
2486
|
const fileContents = await Promise.all(
|
|
1713
2487
|
filesToScan.map(async ({ file, relativePath }) => {
|
|
1714
2488
|
try {
|
|
1715
|
-
const content = await
|
|
2489
|
+
const content = await readFile4(file, "utf-8");
|
|
1716
2490
|
return { path: relativePath, content: content.slice(0, charLimit) };
|
|
1717
2491
|
} catch {
|
|
1718
2492
|
return null;
|
|
@@ -1877,7 +2651,7 @@ ${filesBlock}`,
|
|
|
1877
2651
|
fixChangeId: null,
|
|
1878
2652
|
reportedVia: "detected"
|
|
1879
2653
|
});
|
|
1880
|
-
const filePath =
|
|
2654
|
+
const filePath = join3(projectPath, issue.file);
|
|
1881
2655
|
const fileNode = await graph.getNode("file", filePath);
|
|
1882
2656
|
if (fileNode) {
|
|
1883
2657
|
await graph.addEdge(fileNode.id, incident.id, "affects");
|
|
@@ -1935,7 +2709,7 @@ ${filesBlock}`,
|
|
|
1935
2709
|
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
1936
2710
|
console.debug("[Initial Hypothesis] Starting initial hypothesis generation", { projectPath });
|
|
1937
2711
|
try {
|
|
1938
|
-
const { getHypothesisEngine } = await import("./hypothesis-
|
|
2712
|
+
const { getHypothesisEngine } = await import("./hypothesis-JCUMZKTG.js");
|
|
1939
2713
|
const hypothesisEngine = getHypothesisEngine(projectPath);
|
|
1940
2714
|
console.debug("[Initial Hypothesis] Running AI-powered hypothesis generation...");
|
|
1941
2715
|
const generated = await hypothesisEngine.generateHypothesesWithAI({
|
|
@@ -1993,7 +2767,7 @@ ${filesBlock}`,
|
|
|
1993
2767
|
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
1994
2768
|
console.debug("[Initial Scan] Starting initial goal compliance scan", { projectPath });
|
|
1995
2769
|
try {
|
|
1996
|
-
const { getActiveGoals, recordGoalViolationCaught } = await import("./goal-validator-
|
|
2770
|
+
const { getActiveGoals, recordGoalViolationCaught } = await import("./goal-validator-HNXXUCPW.js");
|
|
1997
2771
|
const activeGoals = await getActiveGoals(projectPath);
|
|
1998
2772
|
console.debug("[Initial Scan] Loaded goals for initial scan:", {
|
|
1999
2773
|
goalCount: activeGoals.length,
|
|
@@ -2009,7 +2783,7 @@ ${filesBlock}`,
|
|
|
2009
2783
|
const recentFiles = /* @__PURE__ */ new Set();
|
|
2010
2784
|
const uncommittedFiles = await getGitChangedFiles(projectPath);
|
|
2011
2785
|
if (uncommittedFiles) {
|
|
2012
|
-
uncommittedFiles.forEach((f) => recentFiles.add(
|
|
2786
|
+
uncommittedFiles.forEach((f) => recentFiles.add(join3(projectPath, f)));
|
|
2013
2787
|
}
|
|
2014
2788
|
const oneDayAgo = Date.now() - 24 * 60 * 60 * 1e3;
|
|
2015
2789
|
const recentChanges = await getChangedFilesSinceTimestamp(projectPath, oneDayAgo);
|
|
@@ -2018,7 +2792,7 @@ ${filesBlock}`,
|
|
|
2018
2792
|
}
|
|
2019
2793
|
const filesToCheck = Array.from(recentFiles).filter((file) => {
|
|
2020
2794
|
const ext = extname3(file).toLowerCase();
|
|
2021
|
-
return WATCH_EXTENSIONS.has(ext) &&
|
|
2795
|
+
return WATCH_EXTENSIONS.has(ext) && existsSync4(file);
|
|
2022
2796
|
});
|
|
2023
2797
|
console.debug("[Initial Scan] Files discovered for initial scan:", {
|
|
2024
2798
|
totalRecentFiles: recentFiles.size,
|
|
@@ -2040,7 +2814,7 @@ ${filesBlock}`,
|
|
|
2040
2814
|
const fileContents = await Promise.all(
|
|
2041
2815
|
filesToScan.map(async (file) => {
|
|
2042
2816
|
try {
|
|
2043
|
-
const content = await
|
|
2817
|
+
const content = await readFile4(file, "utf-8");
|
|
2044
2818
|
const relativePath = file.replace(projectPath + "/", "");
|
|
2045
2819
|
return { path: relativePath, content: content.slice(0, maxCharsPerFile) };
|
|
2046
2820
|
} catch {
|
|
@@ -2146,6 +2920,50 @@ ${filesBlock}`,
|
|
|
2146
2920
|
}
|
|
2147
2921
|
}
|
|
2148
2922
|
}
|
|
2923
|
+
async syncPipelineIntegrations() {
|
|
2924
|
+
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
2925
|
+
try {
|
|
2926
|
+
const config = await loadConfig();
|
|
2927
|
+
const hasLinear = !!(config.apiKeys?.linear ?? process.env.LINEAR_API_KEY);
|
|
2928
|
+
const hasGithub = !!(config.apiKeys?.github ?? process.env.GITHUB_TOKEN);
|
|
2929
|
+
if (!hasLinear && !hasGithub) return;
|
|
2930
|
+
const graph = new ContextGraph(projectPath);
|
|
2931
|
+
if (hasLinear) {
|
|
2932
|
+
try {
|
|
2933
|
+
const { LinearIngester: LinearIngester2 } = await import("./linear-ingester-JRDQAIAA.js");
|
|
2934
|
+
const ingester = new LinearIngester2(projectPath, graph);
|
|
2935
|
+
await ingester.syncTickets();
|
|
2936
|
+
if (!isInteractiveMode()) {
|
|
2937
|
+
console.error("[Pipeline] Linear tickets synced");
|
|
2938
|
+
}
|
|
2939
|
+
} catch (error) {
|
|
2940
|
+
if (!isInteractiveMode()) {
|
|
2941
|
+
console.error(`[Pipeline] Linear sync failed: ${error}`);
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
if (hasGithub) {
|
|
2946
|
+
try {
|
|
2947
|
+
const { GitHubIngester: GitHubIngester2 } = await import("./github-ingester-J2ZFYXVE.js");
|
|
2948
|
+
const ingester = new GitHubIngester2(graph);
|
|
2949
|
+
const token = await ingester.getApiToken();
|
|
2950
|
+
const repoInfo = ingester.getRepoInfo(projectPath);
|
|
2951
|
+
if (repoInfo) {
|
|
2952
|
+
await ingester.syncPullRequests(repoInfo.owner, repoInfo.name, token);
|
|
2953
|
+
await ingester.syncIssues(repoInfo.owner, repoInfo.name, token);
|
|
2954
|
+
if (!isInteractiveMode()) {
|
|
2955
|
+
console.error("[Pipeline] GitHub PRs/issues synced");
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
} catch (error) {
|
|
2959
|
+
if (!isInteractiveMode()) {
|
|
2960
|
+
console.error(`[Pipeline] GitHub sync failed: ${error}`);
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2964
|
+
} catch {
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2149
2967
|
async stopWatching() {
|
|
2150
2968
|
if (!this.state.isRunning) {
|
|
2151
2969
|
return {
|
|
@@ -2163,6 +2981,10 @@ ${filesBlock}`,
|
|
|
2163
2981
|
this.extractionPipeline.close();
|
|
2164
2982
|
this.extractionPipeline = null;
|
|
2165
2983
|
}
|
|
2984
|
+
if (this.pipelineSyncTimer) {
|
|
2985
|
+
clearInterval(this.pipelineSyncTimer);
|
|
2986
|
+
this.pipelineSyncTimer = null;
|
|
2987
|
+
}
|
|
2166
2988
|
if (this.codebaseIndex) {
|
|
2167
2989
|
await this.codebaseIndex.save();
|
|
2168
2990
|
this.codebaseIndex = null;
|
|
@@ -2222,7 +3044,7 @@ Use \`trie_watch start\` to begin autonomous scanning.`
|
|
|
2222
3044
|
).join("\n");
|
|
2223
3045
|
let agencyStatus = "";
|
|
2224
3046
|
try {
|
|
2225
|
-
const { getTrieAgent } = await import("./trie-agent-
|
|
3047
|
+
const { getTrieAgent } = await import("./trie-agent-M6PHM6UD.js");
|
|
2226
3048
|
const trieAgent = getTrieAgent(this.watchedDirectory || getWorkingDirectory(void 0, true));
|
|
2227
3049
|
await trieAgent.initialize();
|
|
2228
3050
|
const status = await trieAgent.getAgencyStatus();
|
|
@@ -2290,7 +3112,8 @@ ${recentNudges || "(none)"}
|
|
|
2290
3112
|
...params.suggestedAction && { suggestedAction: params.suggestedAction }
|
|
2291
3113
|
};
|
|
2292
3114
|
this.state.nudges.push({
|
|
2293
|
-
file: params.file
|
|
3115
|
+
file: params.file || "unknown",
|
|
3116
|
+
// Keep full path for consistency with loaded nudges
|
|
2294
3117
|
message: params.message,
|
|
2295
3118
|
severity: params.severity === "warning" || params.severity === "info" ? "high" : params.severity,
|
|
2296
3119
|
timestamp: (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false })
|
|
@@ -2298,6 +3121,7 @@ ${recentNudges || "(none)"}
|
|
|
2298
3121
|
try {
|
|
2299
3122
|
const storage = getStorage(this.watchedDirectory);
|
|
2300
3123
|
await storage.storeNudge(nudge);
|
|
3124
|
+
console.debug(`[Watch] \u2713 Persisted nudge to storage: ${nudge.message.slice(0, 60)}...`);
|
|
2301
3125
|
} catch (error) {
|
|
2302
3126
|
console.error("[Watch] Failed to persist nudge to storage:", error);
|
|
2303
3127
|
}
|
|
@@ -2336,9 +3160,9 @@ To get a full report, run \`trie_scan\` on your codebase.`
|
|
|
2336
3160
|
};
|
|
2337
3161
|
|
|
2338
3162
|
// src/tools/pr-review.ts
|
|
2339
|
-
import { readFile as
|
|
2340
|
-
import { existsSync as
|
|
2341
|
-
import { join as
|
|
3163
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
3164
|
+
import { existsSync as existsSync5 } from "fs";
|
|
3165
|
+
import { join as join4, basename as basename3, resolve as resolve3, isAbsolute as isAbsolute3 } from "path";
|
|
2342
3166
|
|
|
2343
3167
|
// src/skills/built-in/super-reviewer.ts
|
|
2344
3168
|
var CRITICAL_REVIEW_CHECKLIST = {
|
|
@@ -2452,7 +3276,7 @@ Usage:
|
|
|
2452
3276
|
async getPRInfo(pr, worktree) {
|
|
2453
3277
|
if (worktree) {
|
|
2454
3278
|
const worktreePath = isAbsolute3(worktree) ? worktree : resolve3(getWorkingDirectory(void 0, true), worktree);
|
|
2455
|
-
if (!
|
|
3279
|
+
if (!existsSync5(worktreePath)) {
|
|
2456
3280
|
return { success: false, error: `Worktree not found: ${worktreePath}` };
|
|
2457
3281
|
}
|
|
2458
3282
|
return {
|
|
@@ -2585,8 +3409,8 @@ Usage:
|
|
|
2585
3409
|
"rfcs"
|
|
2586
3410
|
];
|
|
2587
3411
|
for (const docPath of designDocPaths) {
|
|
2588
|
-
const fullPath =
|
|
2589
|
-
if (
|
|
3412
|
+
const fullPath = join4(cwd, docPath);
|
|
3413
|
+
if (existsSync5(fullPath)) {
|
|
2590
3414
|
}
|
|
2591
3415
|
}
|
|
2592
3416
|
return designDocs;
|
|
@@ -2599,9 +3423,9 @@ Usage:
|
|
|
2599
3423
|
const cwd = getWorkingDirectory(void 0, true);
|
|
2600
3424
|
await Promise.all(filePaths.map(async (filePath) => {
|
|
2601
3425
|
try {
|
|
2602
|
-
const fullPath = isAbsolute3(filePath) ? filePath :
|
|
2603
|
-
if (
|
|
2604
|
-
const content = await
|
|
3426
|
+
const fullPath = isAbsolute3(filePath) ? filePath : join4(cwd, filePath);
|
|
3427
|
+
if (existsSync5(fullPath)) {
|
|
3428
|
+
const content = await readFile5(fullPath, "utf-8");
|
|
2605
3429
|
contents.set(filePath, content);
|
|
2606
3430
|
}
|
|
2607
3431
|
} catch {
|
|
@@ -3465,6 +4289,65 @@ var LinearSyncTool = class {
|
|
|
3465
4289
|
}
|
|
3466
4290
|
};
|
|
3467
4291
|
|
|
4292
|
+
// src/tools/github-sync.ts
|
|
4293
|
+
var GitHubSyncTool = class {
|
|
4294
|
+
async execute(input) {
|
|
4295
|
+
try {
|
|
4296
|
+
const workDir = input.directory || getWorkingDirectory(void 0, true);
|
|
4297
|
+
const graph = new ContextGraph(workDir);
|
|
4298
|
+
const ingester = new GitHubIngester(graph);
|
|
4299
|
+
const token = await ingester.getApiToken();
|
|
4300
|
+
if (!token) {
|
|
4301
|
+
return {
|
|
4302
|
+
isError: true,
|
|
4303
|
+
content: [{
|
|
4304
|
+
type: "text",
|
|
4305
|
+
text: 'GitHub token not configured.\n\nSet one of:\n \u2022 Environment variable: GITHUB_TOKEN=ghp_...\n \u2022 Config dialog (C key) \u2192 API Keys \u2192 GitHub\n \u2022 .trie/config.json: { "apiKeys": { "github": "ghp_..." } }\n\nToken needs `repo` scope for private repos, `public_repo` for public.'
|
|
4306
|
+
}]
|
|
4307
|
+
};
|
|
4308
|
+
}
|
|
4309
|
+
const repoInfo = ingester.getRepoInfo(workDir);
|
|
4310
|
+
if (!repoInfo) {
|
|
4311
|
+
return {
|
|
4312
|
+
isError: true,
|
|
4313
|
+
content: [{
|
|
4314
|
+
type: "text",
|
|
4315
|
+
text: "Could not detect GitHub repository.\n\nMake sure this directory is a git repo with a GitHub remote:\n git remote get-url origin"
|
|
4316
|
+
}]
|
|
4317
|
+
};
|
|
4318
|
+
}
|
|
4319
|
+
const prResult = await ingester.syncPullRequests(repoInfo.owner, repoInfo.name, token);
|
|
4320
|
+
const issueResult = await ingester.syncIssues(repoInfo.owner, repoInfo.name, token);
|
|
4321
|
+
const LINE = "\u2500".repeat(45);
|
|
4322
|
+
const lines = [
|
|
4323
|
+
"GITHUB SYNC",
|
|
4324
|
+
LINE,
|
|
4325
|
+
`Repo: ${repoInfo.owner}/${repoInfo.name}`,
|
|
4326
|
+
`Pulled ${prResult.prs} open PR${prResult.prs !== 1 ? "s" : ""}, ${issueResult.issues} open issue${issueResult.issues !== 1 ? "s" : ""}`,
|
|
4327
|
+
`Linked ${prResult.linkedTickets} PR${prResult.linkedTickets !== 1 ? "s" : ""} to Linear tickets`,
|
|
4328
|
+
`Linked ${prResult.linkedFiles} PR${prResult.linkedFiles !== 1 ? "s" : ""} to changed files in context graph`,
|
|
4329
|
+
LINE,
|
|
4330
|
+
"Next sync: trie_github_sync",
|
|
4331
|
+
"Or use trie_pipeline for consolidated view."
|
|
4332
|
+
];
|
|
4333
|
+
return {
|
|
4334
|
+
content: [{
|
|
4335
|
+
type: "text",
|
|
4336
|
+
text: lines.join("\n")
|
|
4337
|
+
}]
|
|
4338
|
+
};
|
|
4339
|
+
} catch (error) {
|
|
4340
|
+
return {
|
|
4341
|
+
isError: true,
|
|
4342
|
+
content: [{
|
|
4343
|
+
type: "text",
|
|
4344
|
+
text: `GitHub sync failed: ${error.message}`
|
|
4345
|
+
}]
|
|
4346
|
+
};
|
|
4347
|
+
}
|
|
4348
|
+
}
|
|
4349
|
+
};
|
|
4350
|
+
|
|
3468
4351
|
// src/tools/index-codebase.ts
|
|
3469
4352
|
import { glob } from "glob";
|
|
3470
4353
|
var INDEXABLE_EXTENSIONS = [
|
|
@@ -3617,6 +4500,7 @@ var ToolRegistry = class {
|
|
|
3617
4500
|
initializeTools() {
|
|
3618
4501
|
this.tools.set("scan", new TrieScanTool());
|
|
3619
4502
|
this.tools.set("fix", new TrieFixTool());
|
|
4503
|
+
this.tools.set("cloud_fix", new TrieCloudFixTool());
|
|
3620
4504
|
this.tools.set("explain", new TrieExplainTool());
|
|
3621
4505
|
this.tools.set("test", new TrieTestTool());
|
|
3622
4506
|
this.tools.set("watch", new TrieWatchTool());
|
|
@@ -3633,6 +4517,9 @@ var ToolRegistry = class {
|
|
|
3633
4517
|
this.tools.set("ok", new TrieFeedbackTool());
|
|
3634
4518
|
this.tools.set("bad", new TrieFeedbackTool());
|
|
3635
4519
|
this.tools.set("linear_sync", new LinearSyncTool());
|
|
4520
|
+
this.tools.set("github_sync", new GitHubSyncTool());
|
|
4521
|
+
this.tools.set("github_branches", new GitHubBranchesTool());
|
|
4522
|
+
this.tools.set("pipeline", new TriePipelineTool());
|
|
3636
4523
|
this.tools.set("index", new TrieIndexTool());
|
|
3637
4524
|
this.tools.set("get_governance", new TrieGetGovernanceTool());
|
|
3638
4525
|
this.tools.set("get_decisions", new TrieGetDecisionsTool());
|
|
@@ -3757,10 +4644,15 @@ var ToolRegistry = class {
|
|
|
3757
4644
|
},
|
|
3758
4645
|
{
|
|
3759
4646
|
name: "trie_fix",
|
|
3760
|
-
description: "Apply high-confidence fixes to code. Alias: fix",
|
|
4647
|
+
description: "Apply high-confidence fixes to code. Use action:route to see triage plan first. Alias: fix",
|
|
3761
4648
|
inputSchema: {
|
|
3762
4649
|
type: "object",
|
|
3763
4650
|
properties: {
|
|
4651
|
+
action: {
|
|
4652
|
+
type: "string",
|
|
4653
|
+
enum: ["route"],
|
|
4654
|
+
description: "route: show fix routing plan without applying fixes"
|
|
4655
|
+
},
|
|
3764
4656
|
issueIds: {
|
|
3765
4657
|
type: "array",
|
|
3766
4658
|
items: { type: "string" },
|
|
@@ -3773,6 +4665,34 @@ var ToolRegistry = class {
|
|
|
3773
4665
|
}
|
|
3774
4666
|
}
|
|
3775
4667
|
},
|
|
4668
|
+
{
|
|
4669
|
+
name: "trie_cloud_fix",
|
|
4670
|
+
description: "Dispatch issues to Cursor cloud agents for verified, test-passing fixes. The cloud agent runs in an isolated VM, applies the fix, runs tests, screenshots the result, and opens a PR. Use trie_fix action:route first to see which issues qualify.",
|
|
4671
|
+
inputSchema: {
|
|
4672
|
+
type: "object",
|
|
4673
|
+
properties: {
|
|
4674
|
+
action: {
|
|
4675
|
+
type: "string",
|
|
4676
|
+
enum: ["configure", "dispatch", "status", "artifacts", "cancel"],
|
|
4677
|
+
description: "configure: save API key | dispatch: send to cloud | status: poll jobs | artifacts: get screenshots + PR links | cancel: abort a job"
|
|
4678
|
+
},
|
|
4679
|
+
issueIds: {
|
|
4680
|
+
type: "array",
|
|
4681
|
+
items: { type: "string" },
|
|
4682
|
+
description: "Issue IDs to dispatch (from trie_scan). If omitted, dispatches all cloud-eligible pending issues."
|
|
4683
|
+
},
|
|
4684
|
+
apiKey: {
|
|
4685
|
+
type: "string",
|
|
4686
|
+
description: "Cursor API key (configure action only)"
|
|
4687
|
+
},
|
|
4688
|
+
jobId: {
|
|
4689
|
+
type: "string",
|
|
4690
|
+
description: "Job ID for cancel or artifacts actions"
|
|
4691
|
+
}
|
|
4692
|
+
},
|
|
4693
|
+
required: ["action"]
|
|
4694
|
+
}
|
|
4695
|
+
},
|
|
3776
4696
|
{
|
|
3777
4697
|
name: "trie_explain",
|
|
3778
4698
|
description: "Explain code, issues, or changes in plain language. Alias: explain",
|
|
@@ -4118,6 +5038,51 @@ var ToolRegistry = class {
|
|
|
4118
5038
|
}
|
|
4119
5039
|
}
|
|
4120
5040
|
},
|
|
5041
|
+
{
|
|
5042
|
+
name: "trie_github_sync",
|
|
5043
|
+
description: "Sync open PRs and issues from GitHub into the Trie context graph. Links PRs to files they touch and to Linear tickets mentioned in PR descriptions. Run once to initialize, or periodically to stay current.",
|
|
5044
|
+
inputSchema: {
|
|
5045
|
+
type: "object",
|
|
5046
|
+
properties: {
|
|
5047
|
+
directory: { type: "string", description: "Project directory (defaults to current workspace)" }
|
|
5048
|
+
}
|
|
5049
|
+
}
|
|
5050
|
+
},
|
|
5051
|
+
{
|
|
5052
|
+
name: "trie_github_branches",
|
|
5053
|
+
description: "Fetch GitHub branches with latest commit info. Use when the user asks which branch has the latest updates, what branches exist, or when branches were last updated. Requires GitHub API key.",
|
|
5054
|
+
inputSchema: {
|
|
5055
|
+
type: "object",
|
|
5056
|
+
properties: {
|
|
5057
|
+
directory: { type: "string", description: "Project directory (defaults to current workspace)" },
|
|
5058
|
+
limit: { type: "number", description: "Max branches to return (default 15, max 30)" }
|
|
5059
|
+
}
|
|
5060
|
+
}
|
|
5061
|
+
},
|
|
5062
|
+
{
|
|
5063
|
+
name: "trie_pipeline",
|
|
5064
|
+
description: "Get consolidated pipeline status: open PRs, active Linear tickets, open GitHub issues, and Trie scan issues not yet in any ticket or PR (coverage gaps). Use to understand where work stands and what is falling through the cracks.",
|
|
5065
|
+
inputSchema: {
|
|
5066
|
+
type: "object",
|
|
5067
|
+
properties: {
|
|
5068
|
+
action: {
|
|
5069
|
+
type: "string",
|
|
5070
|
+
enum: ["status", "coverage", "create_tickets"],
|
|
5071
|
+
description: "status: full pipeline view | coverage: only show Trie issues with no ticket/PR | create_tickets: open Linear tickets for uncovered issues"
|
|
5072
|
+
},
|
|
5073
|
+
focus: {
|
|
5074
|
+
type: "string",
|
|
5075
|
+
description: "Optional file path or Linear ticket ID to narrow the view"
|
|
5076
|
+
},
|
|
5077
|
+
issueIds: {
|
|
5078
|
+
type: "array",
|
|
5079
|
+
items: { type: "string" },
|
|
5080
|
+
description: "Issue IDs to create tickets for (create_tickets action only)"
|
|
5081
|
+
},
|
|
5082
|
+
directory: { type: "string", description: "Project directory" }
|
|
5083
|
+
}
|
|
5084
|
+
}
|
|
5085
|
+
},
|
|
4121
5086
|
{
|
|
4122
5087
|
name: "trie_get_governance",
|
|
4123
5088
|
description: "Query governance records (architectural decisions, standards, team agreements) from governance ledger with targeted retrieval. Prevents context pollution by returning only relevant records.",
|
|
@@ -4221,9 +5186,9 @@ var ToolRegistry = class {
|
|
|
4221
5186
|
};
|
|
4222
5187
|
|
|
4223
5188
|
// src/server/resource-manager.ts
|
|
4224
|
-
import { readdir, readFile as
|
|
4225
|
-
import { existsSync as
|
|
4226
|
-
import { join as
|
|
5189
|
+
import { readdir, readFile as readFile6 } from "fs/promises";
|
|
5190
|
+
import { existsSync as existsSync6 } from "fs";
|
|
5191
|
+
import { join as join5, dirname as dirname2 } from "path";
|
|
4227
5192
|
import { fileURLToPath } from "url";
|
|
4228
5193
|
|
|
4229
5194
|
// src/skills/installer.ts
|
|
@@ -4348,7 +5313,7 @@ var ResourceManager = class {
|
|
|
4348
5313
|
}
|
|
4349
5314
|
async getScanReportResources() {
|
|
4350
5315
|
try {
|
|
4351
|
-
const reportsDir =
|
|
5316
|
+
const reportsDir = join5(getWorkingDirectory(void 0, true), "trie-reports");
|
|
4352
5317
|
const files = await readdir(reportsDir);
|
|
4353
5318
|
const reportFiles = files.filter((f) => f.endsWith(".txt") || f.endsWith(".json"));
|
|
4354
5319
|
return reportFiles.slice(0, 10).map((file) => ({
|
|
@@ -4448,10 +5413,10 @@ var ResourceManager = class {
|
|
|
4448
5413
|
async getUIAppResource(uri, appId) {
|
|
4449
5414
|
const currentFile = fileURLToPath(import.meta.url);
|
|
4450
5415
|
const distDir = dirname2(dirname2(currentFile));
|
|
4451
|
-
const uiDir =
|
|
4452
|
-
const htmlPath =
|
|
5416
|
+
const uiDir = join5(distDir, "ui");
|
|
5417
|
+
const htmlPath = join5(uiDir, `${appId}.html`);
|
|
4453
5418
|
try {
|
|
4454
|
-
if (!
|
|
5419
|
+
if (!existsSync6(htmlPath)) {
|
|
4455
5420
|
return {
|
|
4456
5421
|
contents: [{
|
|
4457
5422
|
uri,
|
|
@@ -4460,7 +5425,7 @@ var ResourceManager = class {
|
|
|
4460
5425
|
}]
|
|
4461
5426
|
};
|
|
4462
5427
|
}
|
|
4463
|
-
const content = await
|
|
5428
|
+
const content = await readFile6(htmlPath, "utf-8");
|
|
4464
5429
|
return {
|
|
4465
5430
|
contents: [{
|
|
4466
5431
|
uri,
|
|
@@ -4640,10 +5605,10 @@ var ResourceManager = class {
|
|
|
4640
5605
|
} catch {
|
|
4641
5606
|
}
|
|
4642
5607
|
summary.push("---", "", "# Detailed Context", "");
|
|
4643
|
-
const agentsMdPath =
|
|
5608
|
+
const agentsMdPath = join5(getTrieDirectory(workDir), "AGENTS.md");
|
|
4644
5609
|
try {
|
|
4645
|
-
if (
|
|
4646
|
-
const agentsContent = await
|
|
5610
|
+
if (existsSync6(agentsMdPath)) {
|
|
5611
|
+
const agentsContent = await readFile6(agentsMdPath, "utf-8");
|
|
4647
5612
|
summary.push(agentsContent);
|
|
4648
5613
|
} else {
|
|
4649
5614
|
const contextSummary = await getContextForAI();
|
|
@@ -4768,8 +5733,8 @@ This information is automatically available to Claude Code, Cursor, and other AI
|
|
|
4768
5733
|
}
|
|
4769
5734
|
async getCacheStatsResource(uri) {
|
|
4770
5735
|
try {
|
|
4771
|
-
const cachePath =
|
|
4772
|
-
const cacheContent = await
|
|
5736
|
+
const cachePath = join5(getTrieDirectory(getWorkingDirectory(void 0, true)), ".trie-cache.json");
|
|
5737
|
+
const cacheContent = await readFile6(cachePath, "utf-8");
|
|
4773
5738
|
const cache = JSON.parse(cacheContent);
|
|
4774
5739
|
const fileCount = Object.keys(cache.files || {}).length;
|
|
4775
5740
|
const totalVulns = Object.values(cache.files || {}).reduce((acc, file) => {
|
|
@@ -4848,9 +5813,9 @@ This information is automatically available to Claude Code, Cursor, and other AI
|
|
|
4848
5813
|
}
|
|
4849
5814
|
async getScanReportResource(uri, parsedUri) {
|
|
4850
5815
|
const fileName = parsedUri.replace("reports/", "");
|
|
4851
|
-
const reportPath =
|
|
5816
|
+
const reportPath = join5(getWorkingDirectory(void 0, true), "trie-reports", fileName);
|
|
4852
5817
|
try {
|
|
4853
|
-
const content = await
|
|
5818
|
+
const content = await readFile6(reportPath, "utf-8");
|
|
4854
5819
|
return {
|
|
4855
5820
|
contents: [{
|
|
4856
5821
|
uri,
|
|
@@ -5450,8 +6415,25 @@ npx playwright install chromium
|
|
|
5450
6415
|
case "cp":
|
|
5451
6416
|
case "save":
|
|
5452
6417
|
return await this.toolRegistry.getTool("checkpoint").execute(args);
|
|
5453
|
-
|
|
6418
|
+
case "cloud_fix":
|
|
6419
|
+
return await this.toolRegistry.getTool("cloud_fix").execute(args);
|
|
6420
|
+
case "linear_sync":
|
|
6421
|
+
return await this.toolRegistry.getTool("linear_sync").execute(args);
|
|
6422
|
+
case "github_sync":
|
|
6423
|
+
return await this.toolRegistry.getTool("github_sync").execute(args);
|
|
6424
|
+
case "github_branches":
|
|
6425
|
+
return await this.toolRegistry.getTool("github_branches").execute(args);
|
|
6426
|
+
case "pipeline":
|
|
6427
|
+
return await this.toolRegistry.getTool("pipeline").execute(args);
|
|
6428
|
+
case "index":
|
|
6429
|
+
return await this.toolRegistry.getTool("index").execute(args);
|
|
6430
|
+
default: {
|
|
6431
|
+
const tool = this.toolRegistry.getTool(normalizedName);
|
|
6432
|
+
if (tool) {
|
|
6433
|
+
return await tool.execute(args);
|
|
6434
|
+
}
|
|
5454
6435
|
throw new Error(`Unknown tool: ${name}`);
|
|
6436
|
+
}
|
|
5455
6437
|
}
|
|
5456
6438
|
} catch (error) {
|
|
5457
6439
|
return {
|
|
@@ -5477,12 +6459,12 @@ npx playwright install chromium
|
|
|
5477
6459
|
}
|
|
5478
6460
|
normalizeName(name) {
|
|
5479
6461
|
const stripNamespace = (n) => {
|
|
5480
|
-
const trimmed = n.trim();
|
|
6462
|
+
const trimmed = n.trim().replace(/\s*\([^)]*\)\s*$/, "").trim();
|
|
5481
6463
|
const withoutSlash = trimmed.startsWith("/") ? trimmed.slice(1) : trimmed;
|
|
5482
6464
|
const parts = withoutSlash.split(":");
|
|
5483
|
-
const base = parts[0] || withoutSlash;
|
|
6465
|
+
const base = (parts[0] || withoutSlash).trim();
|
|
5484
6466
|
const slashParts = base.split("/");
|
|
5485
|
-
return slashParts[slashParts.length - 1] || base;
|
|
6467
|
+
return (slashParts[slashParts.length - 1] || base).trim();
|
|
5486
6468
|
};
|
|
5487
6469
|
const rawName = stripNamespace(name);
|
|
5488
6470
|
return rawName.startsWith("trie_") ? rawName.slice("trie_".length) : rawName;
|