@triedotdev/mcp 1.0.141 → 1.0.143
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/dist/{chunk-GXF6JOCN.js → chunk-4PAAGLKO.js} +7 -7
- package/dist/{chunk-ROVR5OHR.js → chunk-AIJF67CF.js} +1255 -51
- package/dist/chunk-AIJF67CF.js.map +1 -0
- package/dist/{chunk-LHPWSUVT.js → chunk-N2EDZTKG.js} +5 -5
- package/dist/{chunk-75ADWWUF.js → chunk-T6PS3MXJ.js} +2 -2
- package/dist/{chunk-AF2APASP.js → chunk-V7AY2EJO.js} +4 -4
- package/dist/{chunk-M5R6DZQY.js → chunk-WMDFK7LI.js} +10 -10
- package/dist/cli/main.js +16 -16
- package/dist/cli/yolo-daemon.js +9 -9
- package/dist/{codebase-index-5SEOESWM.js → codebase-index-FMIULFZQ.js} +3 -3
- package/dist/{fast-analyzer-AYLZB5TW.js → fast-analyzer-MWKCDRGD.js} +5 -5
- package/dist/{goal-validator-HNXXUCPW.js → goal-validator-DA3JQ6JN.js} +2 -2
- package/dist/index.js +106 -1187
- package/dist/index.js.map +1 -1
- package/dist/{trie-agent-CZ5CGATT.js → trie-agent-6A7YBNTQ.js} +6 -6
- package/package.json +2 -1
- package/dist/chunk-ROVR5OHR.js.map +0 -1
- /package/dist/{chunk-GXF6JOCN.js.map → chunk-4PAAGLKO.js.map} +0 -0
- /package/dist/{chunk-LHPWSUVT.js.map → chunk-N2EDZTKG.js.map} +0 -0
- /package/dist/{chunk-75ADWWUF.js.map → chunk-T6PS3MXJ.js.map} +0 -0
- /package/dist/{chunk-AF2APASP.js.map → chunk-V7AY2EJO.js.map} +0 -0
- /package/dist/{chunk-M5R6DZQY.js.map → chunk-WMDFK7LI.js.map} +0 -0
- /package/dist/{codebase-index-5SEOESWM.js.map → codebase-index-FMIULFZQ.js.map} +0 -0
- /package/dist/{fast-analyzer-AYLZB5TW.js.map → fast-analyzer-MWKCDRGD.js.map} +0 -0
- /package/dist/{goal-validator-HNXXUCPW.js.map → goal-validator-DA3JQ6JN.js.map} +0 -0
- /package/dist/{trie-agent-CZ5CGATT.js.map → trie-agent-6A7YBNTQ.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-YOJGSRZK.js";
|
|
5
5
|
import {
|
|
6
6
|
CodebaseIndex
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-V7AY2EJO.js";
|
|
8
8
|
import {
|
|
9
9
|
appendToSection,
|
|
10
10
|
completeBootstrap,
|
|
@@ -32,8 +32,10 @@ import {
|
|
|
32
32
|
InteractiveDashboard,
|
|
33
33
|
StreamingManager,
|
|
34
34
|
TrieCheckTool,
|
|
35
|
+
TrieCloudFixTool,
|
|
35
36
|
TrieExplainTool,
|
|
36
37
|
TrieFeedbackTool,
|
|
38
|
+
TrieFixTool,
|
|
37
39
|
TrieGetBlockersTool,
|
|
38
40
|
TrieGetDecisionsTool,
|
|
39
41
|
TrieGetGovernanceTool,
|
|
@@ -46,9 +48,12 @@ import {
|
|
|
46
48
|
getPrompt,
|
|
47
49
|
getSystemPrompt,
|
|
48
50
|
handleCheckpointTool
|
|
49
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-AIJF67CF.js";
|
|
50
52
|
import "./chunk-23RJT5WT.js";
|
|
51
|
-
import "./chunk-
|
|
53
|
+
import "./chunk-N2EDZTKG.js";
|
|
54
|
+
import {
|
|
55
|
+
getOutputManager
|
|
56
|
+
} from "./chunk-TIMIKBY2.js";
|
|
52
57
|
import "./chunk-YEIJW6X6.js";
|
|
53
58
|
import "./chunk-XTTZAQWJ.js";
|
|
54
59
|
import "./chunk-3MUCUZ46.js";
|
|
@@ -57,7 +62,7 @@ import {
|
|
|
57
62
|
formatFriendlyError,
|
|
58
63
|
importFromJson,
|
|
59
64
|
isTrieInitialized
|
|
60
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-4PAAGLKO.js";
|
|
61
66
|
import {
|
|
62
67
|
loadConfig
|
|
63
68
|
} from "./chunk-NKHO34UZ.js";
|
|
@@ -68,16 +73,28 @@ import {
|
|
|
68
73
|
getGlobalMemoryStats,
|
|
69
74
|
listTrackedProjects,
|
|
70
75
|
searchGlobalPatterns
|
|
71
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-WMDFK7LI.js";
|
|
72
77
|
import {
|
|
73
78
|
ContextGraph
|
|
74
79
|
} from "./chunk-FH335WL5.js";
|
|
75
|
-
import "./chunk-
|
|
80
|
+
import "./chunk-T6PS3MXJ.js";
|
|
76
81
|
import {
|
|
77
82
|
isAIAvailable,
|
|
78
83
|
runAIAnalysis
|
|
79
84
|
} from "./chunk-FPEMP54L.js";
|
|
80
85
|
import "./chunk-LT6VUZG2.js";
|
|
86
|
+
import "./chunk-F4NJ4CBP.js";
|
|
87
|
+
import "./chunk-IXO4G4D3.js";
|
|
88
|
+
import "./chunk-6NLHFIYA.js";
|
|
89
|
+
import {
|
|
90
|
+
getStorage
|
|
91
|
+
} from "./chunk-FG467PDD.js";
|
|
92
|
+
import {
|
|
93
|
+
getSkillRegistry
|
|
94
|
+
} from "./chunk-G76DYVGX.js";
|
|
95
|
+
import {
|
|
96
|
+
getAutonomyConfig
|
|
97
|
+
} from "./chunk-5KJ4UJOY.js";
|
|
81
98
|
import {
|
|
82
99
|
findSimilarIssues,
|
|
83
100
|
getChangedFilesSinceTimestamp,
|
|
@@ -92,27 +109,10 @@ import {
|
|
|
92
109
|
} from "./chunk-62POBLFC.js";
|
|
93
110
|
import "./chunk-4MJ52WBH.js";
|
|
94
111
|
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";
|
|
109
112
|
import {
|
|
110
113
|
getTrieDirectory,
|
|
111
114
|
getWorkingDirectory
|
|
112
115
|
} from "./chunk-SH7H3WRU.js";
|
|
113
|
-
import {
|
|
114
|
-
getOutputManager
|
|
115
|
-
} from "./chunk-TIMIKBY2.js";
|
|
116
116
|
import {
|
|
117
117
|
isInteractiveMode
|
|
118
118
|
} from "./chunk-APMV77PU.js";
|
|
@@ -195,1091 +195,10 @@ function detectAITool() {
|
|
|
195
195
|
};
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
-
// src/tools/
|
|
198
|
+
// src/tools/test.ts
|
|
199
199
|
import { readFile } from "fs/promises";
|
|
200
200
|
import { existsSync } from "fs";
|
|
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
|
|
437
|
-
var pendingFixes = /* @__PURE__ */ new Map();
|
|
438
|
-
var TrieFixTool = class {
|
|
439
|
-
async execute(args) {
|
|
440
|
-
const { issueIds, file, line, issue, fix, autoApprove = false, dryRun = false, action } = args || {};
|
|
441
|
-
if (action === "route") {
|
|
442
|
-
return this.routeIssues(issueIds);
|
|
443
|
-
}
|
|
444
|
-
if (issueIds && issueIds.length > 0) {
|
|
445
|
-
return this.fixByIds(issueIds, autoApprove, dryRun);
|
|
446
|
-
}
|
|
447
|
-
if (file && fix) {
|
|
448
|
-
return this.applyFix(file, line || 1, issue || "User-specified fix", fix, dryRun);
|
|
449
|
-
}
|
|
450
|
-
if (pendingFixes.size > 0) {
|
|
451
|
-
return this.showPendingFixes();
|
|
452
|
-
}
|
|
453
|
-
if (file && issue) {
|
|
454
|
-
return this.generateFixPrompt(file, line || 1, issue);
|
|
455
|
-
}
|
|
456
|
-
return {
|
|
457
|
-
content: [{
|
|
458
|
-
type: "text",
|
|
459
|
-
text: this.getHelpText()
|
|
460
|
-
}]
|
|
461
|
-
};
|
|
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
|
-
}
|
|
494
|
-
async fixByIds(issueIds, autoApprove, dryRun) {
|
|
495
|
-
const results = [];
|
|
496
|
-
let fixed = 0;
|
|
497
|
-
let failed = 0;
|
|
498
|
-
for (const id of issueIds) {
|
|
499
|
-
const pendingFix = pendingFixes.get(id);
|
|
500
|
-
if (!pendingFix) {
|
|
501
|
-
results.push(`\u274C Issue ${id}: Not found in pending fixes`);
|
|
502
|
-
failed++;
|
|
503
|
-
continue;
|
|
504
|
-
}
|
|
505
|
-
if (pendingFix.confidence < 0.8 && !autoApprove) {
|
|
506
|
-
results.push(`[!] Issue ${id}: Confidence too low (${(pendingFix.confidence * 100).toFixed(0)}%) - use autoApprove:true to override`);
|
|
507
|
-
continue;
|
|
508
|
-
}
|
|
509
|
-
if (dryRun) {
|
|
510
|
-
results.push(`\u{1F50D} Issue ${id}: Would fix "${pendingFix.issue}" in ${pendingFix.file}:${pendingFix.line}`);
|
|
511
|
-
continue;
|
|
512
|
-
}
|
|
513
|
-
try {
|
|
514
|
-
results.push(`\u2705 Issue ${id}: Fix prepared for ${pendingFix.file}:${pendingFix.line}`);
|
|
515
|
-
results.push(` Issue: ${pendingFix.issue}`);
|
|
516
|
-
results.push(` Fix: ${pendingFix.suggestedFix}`);
|
|
517
|
-
pendingFix.status = "applied";
|
|
518
|
-
fixed++;
|
|
519
|
-
} catch (error) {
|
|
520
|
-
results.push(`\u274C Issue ${id}: Failed to apply - ${error}`);
|
|
521
|
-
failed++;
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
let output = `
|
|
525
|
-
${"\u2501".repeat(60)}
|
|
526
|
-
`;
|
|
527
|
-
output += `\u{1F527} FIX RESULTS
|
|
528
|
-
`;
|
|
529
|
-
output += `${"\u2501".repeat(60)}
|
|
530
|
-
|
|
531
|
-
`;
|
|
532
|
-
output += results.join("\n");
|
|
533
|
-
output += `
|
|
534
|
-
|
|
535
|
-
**Summary:** ${fixed} fixed, ${failed} failed, ${issueIds.length - fixed - failed} skipped
|
|
536
|
-
`;
|
|
537
|
-
output += await this.appendCloudRecommendation(issueIds);
|
|
538
|
-
return { content: [{ type: "text", text: output }] };
|
|
539
|
-
}
|
|
540
|
-
async applyFix(file, line, issue, fix, dryRun) {
|
|
541
|
-
const workDir = getWorkingDirectory(void 0, true);
|
|
542
|
-
const filePath = isAbsolute(file) ? file : resolve(workDir, file);
|
|
543
|
-
if (!existsSync(filePath)) {
|
|
544
|
-
return {
|
|
545
|
-
content: [{
|
|
546
|
-
type: "text",
|
|
547
|
-
text: `\u274C File not found: ${filePath}`
|
|
548
|
-
}]
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
|
-
const content = await readFile(filePath, "utf-8");
|
|
552
|
-
const lines = content.split("\n");
|
|
553
|
-
const language = this.detectLanguage(filePath);
|
|
554
|
-
const contextStart = Math.max(0, line - 10);
|
|
555
|
-
const contextEnd = Math.min(lines.length, line + 10);
|
|
556
|
-
const contextLines = lines.slice(contextStart, contextEnd);
|
|
557
|
-
const prompt = getPrompt("fix", "apply", {
|
|
558
|
-
issue,
|
|
559
|
-
fix,
|
|
560
|
-
language,
|
|
561
|
-
code: contextLines.join("\n"),
|
|
562
|
-
filePath: relative(workDir, filePath),
|
|
563
|
-
line: String(line)
|
|
564
|
-
});
|
|
565
|
-
const systemPrompt = getSystemPrompt("fix");
|
|
566
|
-
let output = `
|
|
567
|
-
${"\u2501".repeat(60)}
|
|
568
|
-
`;
|
|
569
|
-
output += `\u{1F527} FIX APPLICATION REQUEST
|
|
570
|
-
`;
|
|
571
|
-
output += `${"\u2501".repeat(60)}
|
|
572
|
-
|
|
573
|
-
`;
|
|
574
|
-
output += `## \u{1F4CD} Target
|
|
575
|
-
|
|
576
|
-
`;
|
|
577
|
-
output += `- **File:** \`${relative(workDir, filePath)}\`
|
|
578
|
-
`;
|
|
579
|
-
output += `- **Line:** ${line}
|
|
580
|
-
`;
|
|
581
|
-
output += `- **Issue:** ${issue}
|
|
582
|
-
`;
|
|
583
|
-
output += `- **Requested Fix:** ${fix}
|
|
584
|
-
|
|
585
|
-
`;
|
|
586
|
-
output += `## \u{1F4C4} Current Code Context
|
|
587
|
-
|
|
588
|
-
`;
|
|
589
|
-
output += `\`\`\`${language}
|
|
590
|
-
`;
|
|
591
|
-
for (let i = 0; i < contextLines.length; i++) {
|
|
592
|
-
const lineNum = contextStart + i + 1;
|
|
593
|
-
const marker = lineNum === line ? "\u2192 " : " ";
|
|
594
|
-
output += `${marker}${lineNum.toString().padStart(4)} | ${contextLines[i]}
|
|
595
|
-
`;
|
|
596
|
-
}
|
|
597
|
-
output += `\`\`\`
|
|
598
|
-
|
|
599
|
-
`;
|
|
600
|
-
if (dryRun) {
|
|
601
|
-
output += `## \u{1F50D} Dry Run Mode
|
|
602
|
-
|
|
603
|
-
`;
|
|
604
|
-
output += `No changes will be made. Review the fix below:
|
|
605
|
-
|
|
606
|
-
`;
|
|
607
|
-
}
|
|
608
|
-
output += `${"\u2500".repeat(60)}
|
|
609
|
-
`;
|
|
610
|
-
output += `## \u{1F9E0} Fix Request for AI
|
|
611
|
-
|
|
612
|
-
`;
|
|
613
|
-
output += `**Role:** ${systemPrompt.split("\n")[0]}
|
|
614
|
-
|
|
615
|
-
`;
|
|
616
|
-
output += prompt;
|
|
617
|
-
output += `
|
|
618
|
-
${"\u2500".repeat(60)}
|
|
619
|
-
`;
|
|
620
|
-
output += `
|
|
621
|
-
### After generating the fix, apply it with:
|
|
622
|
-
|
|
623
|
-
`;
|
|
624
|
-
output += `\`\`\`
|
|
625
|
-
`;
|
|
626
|
-
output += `Use the edit_file tool to apply the generated code changes
|
|
627
|
-
`;
|
|
628
|
-
output += `\`\`\`
|
|
629
|
-
`;
|
|
630
|
-
return { content: [{ type: "text", text: output }] };
|
|
631
|
-
}
|
|
632
|
-
async generateFixPrompt(file, line, issue) {
|
|
633
|
-
const workDir = getWorkingDirectory(void 0, true);
|
|
634
|
-
const filePath = isAbsolute(file) ? file : resolve(workDir, file);
|
|
635
|
-
if (!existsSync(filePath)) {
|
|
636
|
-
return {
|
|
637
|
-
content: [{
|
|
638
|
-
type: "text",
|
|
639
|
-
text: `\u274C File not found: ${filePath}`
|
|
640
|
-
}]
|
|
641
|
-
};
|
|
642
|
-
}
|
|
643
|
-
const content = await readFile(filePath, "utf-8");
|
|
644
|
-
const lines = content.split("\n");
|
|
645
|
-
const language = this.detectLanguage(filePath);
|
|
646
|
-
const contextStart = Math.max(0, line - 20);
|
|
647
|
-
const contextEnd = Math.min(lines.length, line + 20);
|
|
648
|
-
const contextLines = lines.slice(contextStart, contextEnd);
|
|
649
|
-
let output = `
|
|
650
|
-
${"\u2501".repeat(60)}
|
|
651
|
-
`;
|
|
652
|
-
output += `\u{1F527} FIX GENERATION REQUEST
|
|
653
|
-
`;
|
|
654
|
-
output += `${"\u2501".repeat(60)}
|
|
655
|
-
|
|
656
|
-
`;
|
|
657
|
-
output += `## \u{1F4CD} Issue Details
|
|
658
|
-
|
|
659
|
-
`;
|
|
660
|
-
output += `- **File:** \`${relative(workDir, filePath)}\`
|
|
661
|
-
`;
|
|
662
|
-
output += `- **Line:** ${line}
|
|
663
|
-
`;
|
|
664
|
-
output += `- **Issue:** ${issue}
|
|
665
|
-
|
|
666
|
-
`;
|
|
667
|
-
output += `## \u{1F4C4} Code Context
|
|
668
|
-
|
|
669
|
-
`;
|
|
670
|
-
output += `\`\`\`${language}
|
|
671
|
-
`;
|
|
672
|
-
for (let i = 0; i < contextLines.length; i++) {
|
|
673
|
-
const lineNum = contextStart + i + 1;
|
|
674
|
-
const marker = lineNum === line ? "\u2192 " : " ";
|
|
675
|
-
output += `${marker}${lineNum.toString().padStart(4)} | ${contextLines[i]}
|
|
676
|
-
`;
|
|
677
|
-
}
|
|
678
|
-
output += `\`\`\`
|
|
679
|
-
|
|
680
|
-
`;
|
|
681
|
-
output += `## \u{1F9E0} Analysis Request
|
|
682
|
-
|
|
683
|
-
`;
|
|
684
|
-
output += `Please analyze this issue and provide:
|
|
685
|
-
|
|
686
|
-
`;
|
|
687
|
-
output += `1. **Root cause** - Why does this issue occur?
|
|
688
|
-
`;
|
|
689
|
-
output += `2. **Impact** - What could go wrong if unfixed?
|
|
690
|
-
`;
|
|
691
|
-
output += `3. **Fix** - The exact code change needed
|
|
692
|
-
`;
|
|
693
|
-
output += `4. **Verification** - How to test the fix works
|
|
694
|
-
|
|
695
|
-
`;
|
|
696
|
-
output += `After analysis, you can apply the fix using the edit_file tool.
|
|
697
|
-
`;
|
|
698
|
-
return { content: [{ type: "text", text: output }] };
|
|
699
|
-
}
|
|
700
|
-
showPendingFixes() {
|
|
701
|
-
let output = `
|
|
702
|
-
${"\u2501".repeat(60)}
|
|
703
|
-
`;
|
|
704
|
-
output += `\u{1F527} PENDING FIXES
|
|
705
|
-
`;
|
|
706
|
-
output += `${"\u2501".repeat(60)}
|
|
707
|
-
|
|
708
|
-
`;
|
|
709
|
-
if (pendingFixes.size === 0) {
|
|
710
|
-
output += `No pending fixes. Run \`trie_scan\` first to detect issues.
|
|
711
|
-
`;
|
|
712
|
-
return { content: [{ type: "text", text: output }] };
|
|
713
|
-
}
|
|
714
|
-
const fixes = Array.from(pendingFixes.values());
|
|
715
|
-
const byStatus = {
|
|
716
|
-
pending: fixes.filter((f) => f.status === "pending"),
|
|
717
|
-
applied: fixes.filter((f) => f.status === "applied"),
|
|
718
|
-
rejected: fixes.filter((f) => f.status === "rejected")
|
|
719
|
-
};
|
|
720
|
-
if (byStatus.pending.length > 0) {
|
|
721
|
-
output += `## \u23F3 Pending (${byStatus.pending.length})
|
|
722
|
-
|
|
723
|
-
`;
|
|
724
|
-
output += `| ID | File | Line | Issue | Confidence |
|
|
725
|
-
`;
|
|
726
|
-
output += `|----|------|------|-------|------------|
|
|
727
|
-
`;
|
|
728
|
-
for (const fix of byStatus.pending) {
|
|
729
|
-
const conf = `${(fix.confidence * 100).toFixed(0)}%`;
|
|
730
|
-
const shortFile = fix.file.split("/").slice(-2).join("/");
|
|
731
|
-
output += `| ${fix.id} | ${shortFile} | ${fix.line} | ${fix.issue.slice(0, 40)}... | ${conf} |
|
|
732
|
-
`;
|
|
733
|
-
}
|
|
734
|
-
output += "\n";
|
|
735
|
-
}
|
|
736
|
-
output += `### Commands
|
|
737
|
-
|
|
738
|
-
`;
|
|
739
|
-
output += `- Fix all high-confidence: \`trie_fix autoApprove:true\`
|
|
740
|
-
`;
|
|
741
|
-
output += `- Fix specific: \`trie_fix issueIds:["id1", "id2"]\`
|
|
742
|
-
`;
|
|
743
|
-
output += `- Preview: \`trie_fix dryRun:true\`
|
|
744
|
-
`;
|
|
745
|
-
return { content: [{ type: "text", text: output }] };
|
|
746
|
-
}
|
|
747
|
-
getHelpText() {
|
|
748
|
-
return `
|
|
749
|
-
${"\u2501".repeat(60)}
|
|
750
|
-
\u{1F527} TRIE FIX - AI-POWERED CODE FIXING
|
|
751
|
-
${"\u2501".repeat(60)}
|
|
752
|
-
|
|
753
|
-
## Usage
|
|
754
|
-
|
|
755
|
-
### Fix issues from a scan:
|
|
756
|
-
\`\`\`
|
|
757
|
-
trie_fix issueIds:["issue-1", "issue-2"]
|
|
758
|
-
\`\`\`
|
|
759
|
-
|
|
760
|
-
### Auto-fix all high-confidence issues:
|
|
761
|
-
\`\`\`
|
|
762
|
-
trie_fix autoApprove:true
|
|
763
|
-
\`\`\`
|
|
764
|
-
|
|
765
|
-
### Fix specific file and line:
|
|
766
|
-
\`\`\`
|
|
767
|
-
trie_fix file:"src/app.ts" line:42 issue:"SQL injection" fix:"Use parameterized query"
|
|
768
|
-
\`\`\`
|
|
769
|
-
|
|
770
|
-
### Preview fixes without applying:
|
|
771
|
-
\`\`\`
|
|
772
|
-
trie_fix dryRun:true
|
|
773
|
-
\`\`\`
|
|
774
|
-
|
|
775
|
-
### View pending fixes:
|
|
776
|
-
\`\`\`
|
|
777
|
-
trie_fix
|
|
778
|
-
\`\`\`
|
|
779
|
-
|
|
780
|
-
## Workflow
|
|
781
|
-
|
|
782
|
-
1. Run \`trie_scan\` to detect issues
|
|
783
|
-
2. Review the issues found
|
|
784
|
-
3. Run \`trie_fix\` to apply fixes
|
|
785
|
-
|
|
786
|
-
The AI will analyze each issue, generate the fix, and you can review before applying.
|
|
787
|
-
`;
|
|
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
|
-
}
|
|
816
|
-
detectLanguage(filePath) {
|
|
817
|
-
const ext = extname(filePath).toLowerCase();
|
|
818
|
-
const langMap = {
|
|
819
|
-
".ts": "typescript",
|
|
820
|
-
".tsx": "tsx",
|
|
821
|
-
".js": "javascript",
|
|
822
|
-
".jsx": "jsx",
|
|
823
|
-
".py": "python",
|
|
824
|
-
".go": "go",
|
|
825
|
-
".rs": "rust"
|
|
826
|
-
};
|
|
827
|
-
return langMap[ext] || "plaintext";
|
|
828
|
-
}
|
|
829
|
-
};
|
|
830
|
-
function getPendingFixes() {
|
|
831
|
-
return Array.from(pendingFixes.values());
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
// src/tools/cloud-fix.ts
|
|
835
|
-
import { readFile as readFile2, writeFile, mkdir } from "fs/promises";
|
|
836
|
-
import { existsSync as existsSync2 } from "fs";
|
|
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";
|
|
201
|
+
import { extname, relative, resolve, isAbsolute, dirname, basename, join } from "path";
|
|
1283
202
|
var TrieTestTool = class {
|
|
1284
203
|
async execute(args) {
|
|
1285
204
|
const { action, files, framework, style = "unit" } = args || {};
|
|
@@ -1332,15 +251,15 @@ ${"\u2501".repeat(60)}
|
|
|
1332
251
|
const workDir = getWorkingDirectory(void 0, true);
|
|
1333
252
|
const allUnits = [];
|
|
1334
253
|
for (const file of files) {
|
|
1335
|
-
const resolvedPath =
|
|
1336
|
-
if (!
|
|
254
|
+
const resolvedPath = isAbsolute(file) ? file : resolve(workDir, file);
|
|
255
|
+
if (!existsSync(resolvedPath)) {
|
|
1337
256
|
output += `[!] File not found: ${file}
|
|
1338
257
|
`;
|
|
1339
258
|
continue;
|
|
1340
259
|
}
|
|
1341
|
-
const code = await
|
|
260
|
+
const code = await readFile(resolvedPath, "utf-8");
|
|
1342
261
|
const language = this.detectLanguage(resolvedPath);
|
|
1343
|
-
const relativePath =
|
|
262
|
+
const relativePath = relative(workDir, resolvedPath);
|
|
1344
263
|
const units = this.extractTestableUnits(code, language);
|
|
1345
264
|
allUnits.push({ file: relativePath, units });
|
|
1346
265
|
output += `### \u{1F4C4} ${relativePath}
|
|
@@ -1371,7 +290,7 @@ ${"\u2501".repeat(60)}
|
|
|
1371
290
|
`;
|
|
1372
291
|
for (const { file, units } of allUnits) {
|
|
1373
292
|
if (units.length === 0) continue;
|
|
1374
|
-
const code = await
|
|
293
|
+
const code = await readFile(resolve(workDir, file), "utf-8");
|
|
1375
294
|
const language = this.detectLanguage(file);
|
|
1376
295
|
const prompt = getPrompt("test", "generate", {
|
|
1377
296
|
code,
|
|
@@ -1416,21 +335,21 @@ ${"\u2501".repeat(60)}
|
|
|
1416
335
|
`;
|
|
1417
336
|
const workDir = getWorkingDirectory(void 0, true);
|
|
1418
337
|
for (const file of files) {
|
|
1419
|
-
const resolvedPath =
|
|
1420
|
-
if (!
|
|
338
|
+
const resolvedPath = isAbsolute(file) ? file : resolve(workDir, file);
|
|
339
|
+
if (!existsSync(resolvedPath)) {
|
|
1421
340
|
output += `[!] File not found: ${file}
|
|
1422
341
|
`;
|
|
1423
342
|
continue;
|
|
1424
343
|
}
|
|
1425
|
-
const code = await
|
|
344
|
+
const code = await readFile(resolvedPath, "utf-8");
|
|
1426
345
|
const language = this.detectLanguage(resolvedPath);
|
|
1427
|
-
const relativePath =
|
|
346
|
+
const relativePath = relative(workDir, resolvedPath);
|
|
1428
347
|
const units = this.extractTestableUnits(code, language);
|
|
1429
348
|
const testFile = await this.findTestFile(resolvedPath);
|
|
1430
349
|
let testCode = "";
|
|
1431
350
|
let testedUnits = [];
|
|
1432
351
|
if (testFile) {
|
|
1433
|
-
testCode = await
|
|
352
|
+
testCode = await readFile(testFile, "utf-8");
|
|
1434
353
|
testedUnits = this.findTestedUnits(testCode, units.map((u) => u.name));
|
|
1435
354
|
}
|
|
1436
355
|
const coverage = units.length > 0 ? Math.round(testedUnits.length / units.length * 100) : 0;
|
|
@@ -1441,7 +360,7 @@ ${"\u2501".repeat(60)}
|
|
|
1441
360
|
output += `**Coverage:** ${coverageIcon} ${coverage}% (${testedUnits.length}/${units.length} units)
|
|
1442
361
|
`;
|
|
1443
362
|
if (testFile) {
|
|
1444
|
-
output += `**Test file:** \`${
|
|
363
|
+
output += `**Test file:** \`${relative(workDir, testFile)}\`
|
|
1445
364
|
`;
|
|
1446
365
|
} else {
|
|
1447
366
|
output += `**Test file:** \u274C Not found
|
|
@@ -1498,14 +417,14 @@ ${"\u2501".repeat(60)}
|
|
|
1498
417
|
`;
|
|
1499
418
|
const workDir = getWorkingDirectory(void 0, true);
|
|
1500
419
|
for (const file of files) {
|
|
1501
|
-
const resolvedPath =
|
|
1502
|
-
if (!
|
|
420
|
+
const resolvedPath = isAbsolute(file) ? file : resolve(workDir, file);
|
|
421
|
+
if (!existsSync(resolvedPath)) {
|
|
1503
422
|
output += `[!] File not found: ${file}
|
|
1504
423
|
`;
|
|
1505
424
|
continue;
|
|
1506
425
|
}
|
|
1507
|
-
const code = await
|
|
1508
|
-
const relativePath =
|
|
426
|
+
const code = await readFile(resolvedPath, "utf-8");
|
|
427
|
+
const relativePath = relative(workDir, resolvedPath);
|
|
1509
428
|
const patterns = this.detectTestablePatterns(code);
|
|
1510
429
|
output += `### \u{1F4C4} ${relativePath}
|
|
1511
430
|
|
|
@@ -1741,8 +660,8 @@ ${"\u2501".repeat(60)}
|
|
|
1741
660
|
}
|
|
1742
661
|
async findTestFile(sourcePath) {
|
|
1743
662
|
const dir = dirname(sourcePath);
|
|
1744
|
-
const base = basename(sourcePath,
|
|
1745
|
-
const ext =
|
|
663
|
+
const base = basename(sourcePath, extname(sourcePath));
|
|
664
|
+
const ext = extname(sourcePath);
|
|
1746
665
|
const patterns = [
|
|
1747
666
|
`${base}.test${ext}`,
|
|
1748
667
|
`${base}.spec${ext}`,
|
|
@@ -1750,16 +669,16 @@ ${"\u2501".repeat(60)}
|
|
|
1750
669
|
`test_${base}${ext}`
|
|
1751
670
|
];
|
|
1752
671
|
for (const pattern of patterns) {
|
|
1753
|
-
const testPath =
|
|
1754
|
-
if (
|
|
672
|
+
const testPath = join(dir, pattern);
|
|
673
|
+
if (existsSync(testPath)) {
|
|
1755
674
|
return testPath;
|
|
1756
675
|
}
|
|
1757
676
|
}
|
|
1758
|
-
const testsDir =
|
|
1759
|
-
if (
|
|
677
|
+
const testsDir = join(dir, "__tests__");
|
|
678
|
+
if (existsSync(testsDir)) {
|
|
1760
679
|
for (const pattern of patterns) {
|
|
1761
|
-
const testPath =
|
|
1762
|
-
if (
|
|
680
|
+
const testPath = join(testsDir, pattern);
|
|
681
|
+
if (existsSync(testPath)) {
|
|
1763
682
|
return testPath;
|
|
1764
683
|
}
|
|
1765
684
|
}
|
|
@@ -1806,10 +725,10 @@ ${"\u2501".repeat(60)}
|
|
|
1806
725
|
}
|
|
1807
726
|
async detectTestFramework() {
|
|
1808
727
|
const workDir = getWorkingDirectory(void 0, true);
|
|
1809
|
-
const packagePath =
|
|
1810
|
-
if (
|
|
728
|
+
const packagePath = resolve(workDir, "package.json");
|
|
729
|
+
if (existsSync(packagePath)) {
|
|
1811
730
|
try {
|
|
1812
|
-
const pkg = JSON.parse(await
|
|
731
|
+
const pkg = JSON.parse(await readFile(packagePath, "utf-8"));
|
|
1813
732
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
1814
733
|
if (deps.vitest) return "vitest";
|
|
1815
734
|
if (deps.jest) return "jest";
|
|
@@ -1817,13 +736,13 @@ ${"\u2501".repeat(60)}
|
|
|
1817
736
|
} catch {
|
|
1818
737
|
}
|
|
1819
738
|
}
|
|
1820
|
-
if (
|
|
739
|
+
if (existsSync(resolve(workDir, "pytest.ini")) || existsSync(resolve(workDir, "pyproject.toml"))) {
|
|
1821
740
|
return "pytest";
|
|
1822
741
|
}
|
|
1823
742
|
return "jest";
|
|
1824
743
|
}
|
|
1825
744
|
detectLanguage(filePath) {
|
|
1826
|
-
const ext =
|
|
745
|
+
const ext = extname(filePath).toLowerCase();
|
|
1827
746
|
const langMap = {
|
|
1828
747
|
".ts": "typescript",
|
|
1829
748
|
".tsx": "tsx",
|
|
@@ -1876,9 +795,9 @@ trie_test action:"run" files:["src/utils.test.ts"]
|
|
|
1876
795
|
};
|
|
1877
796
|
|
|
1878
797
|
// src/tools/watch.ts
|
|
1879
|
-
import { watch, existsSync as
|
|
1880
|
-
import { stat, readFile as
|
|
1881
|
-
import { join as
|
|
798
|
+
import { watch, existsSync as existsSync2, readFileSync } from "fs";
|
|
799
|
+
import { stat, readFile as readFile2 } from "fs/promises";
|
|
800
|
+
import { join as join2, extname as extname2, basename as basename2 } from "path";
|
|
1882
801
|
import { createHash } from "crypto";
|
|
1883
802
|
var WATCH_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1884
803
|
".ts",
|
|
@@ -2096,17 +1015,17 @@ Your Trie agent is now autonomously watching and learning from your codebase.
|
|
|
2096
1015
|
return parts.some((p) => SKIP_DIRS.has(p) || p.startsWith(".") && p !== ".");
|
|
2097
1016
|
}
|
|
2098
1017
|
async watchDirectory(dir, debounceMs) {
|
|
2099
|
-
if (!
|
|
1018
|
+
if (!existsSync2(dir)) return;
|
|
2100
1019
|
try {
|
|
2101
1020
|
const dirStat = await stat(dir);
|
|
2102
1021
|
if (!dirStat.isDirectory()) return;
|
|
2103
1022
|
const watcher = watch(dir, { persistent: true, recursive: true }, (_eventType, filename) => {
|
|
2104
1023
|
if (!filename) return;
|
|
2105
1024
|
if (this.shouldSkipPath(filename)) return;
|
|
2106
|
-
const ext =
|
|
1025
|
+
const ext = extname2(filename).toLowerCase();
|
|
2107
1026
|
if (!WATCH_EXTENSIONS.has(ext)) return;
|
|
2108
|
-
const fullPath =
|
|
2109
|
-
if (!
|
|
1027
|
+
const fullPath = join2(dir, filename);
|
|
1028
|
+
if (!existsSync2(fullPath)) return;
|
|
2110
1029
|
this.state.pendingFiles.add(fullPath);
|
|
2111
1030
|
if (this.state.scanDebounceTimer) {
|
|
2112
1031
|
clearTimeout(this.state.scanDebounceTimer);
|
|
@@ -2152,7 +1071,7 @@ Detected changes in ${files.length} file(s):`);
|
|
|
2152
1071
|
const fileContents = await Promise.all(
|
|
2153
1072
|
files.map(async (file) => {
|
|
2154
1073
|
try {
|
|
2155
|
-
const content = await
|
|
1074
|
+
const content = await readFile2(file, "utf-8");
|
|
2156
1075
|
return { file, content };
|
|
2157
1076
|
} catch {
|
|
2158
1077
|
return null;
|
|
@@ -2230,7 +1149,7 @@ ${f.content.slice(0, 1e3)}`
|
|
|
2230
1149
|
}
|
|
2231
1150
|
isQuiet() {
|
|
2232
1151
|
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
2233
|
-
const quietPath =
|
|
1152
|
+
const quietPath = join2(getTrieDirectory(projectPath), "quiet.json");
|
|
2234
1153
|
try {
|
|
2235
1154
|
const raw = readFileSync(quietPath, "utf-8");
|
|
2236
1155
|
const data = JSON.parse(raw);
|
|
@@ -2398,7 +1317,7 @@ ${f.content.slice(0, 1e3)}`
|
|
|
2398
1317
|
if (lastClean && Date.now() - lastClean < this.cleanFileCooldownMs) {
|
|
2399
1318
|
return 0;
|
|
2400
1319
|
}
|
|
2401
|
-
const fileNode = await graph.getNode("file",
|
|
1320
|
+
const fileNode = await graph.getNode("file", join2(projectPath, file));
|
|
2402
1321
|
if (!fileNode) return score;
|
|
2403
1322
|
const data = fileNode.data;
|
|
2404
1323
|
const riskScores = { critical: 10, high: 6, medium: 2, low: 1 };
|
|
@@ -2457,7 +1376,7 @@ ${f.content.slice(0, 1e3)}`
|
|
|
2457
1376
|
if (remaining < 500) return;
|
|
2458
1377
|
try {
|
|
2459
1378
|
const graph = new ContextGraph(projectPath);
|
|
2460
|
-
const { getActiveGoals, recordGoalViolationCaught } = await import("./goal-validator-
|
|
1379
|
+
const { getActiveGoals, recordGoalViolationCaught } = await import("./goal-validator-DA3JQ6JN.js");
|
|
2461
1380
|
console.debug("[AI Watcher] Loading active goals...");
|
|
2462
1381
|
const activeGoals = await getActiveGoals(projectPath);
|
|
2463
1382
|
const hasGoals = activeGoals.length > 0;
|
|
@@ -2486,7 +1405,7 @@ ${f.content.slice(0, 1e3)}`
|
|
|
2486
1405
|
const fileContents = await Promise.all(
|
|
2487
1406
|
filesToScan.map(async ({ file, relativePath }) => {
|
|
2488
1407
|
try {
|
|
2489
|
-
const content = await
|
|
1408
|
+
const content = await readFile2(file, "utf-8");
|
|
2490
1409
|
return { path: relativePath, content: content.slice(0, charLimit) };
|
|
2491
1410
|
} catch {
|
|
2492
1411
|
return null;
|
|
@@ -2651,7 +1570,7 @@ ${filesBlock}`,
|
|
|
2651
1570
|
fixChangeId: null,
|
|
2652
1571
|
reportedVia: "detected"
|
|
2653
1572
|
});
|
|
2654
|
-
const filePath =
|
|
1573
|
+
const filePath = join2(projectPath, issue.file);
|
|
2655
1574
|
const fileNode = await graph.getNode("file", filePath);
|
|
2656
1575
|
if (fileNode) {
|
|
2657
1576
|
await graph.addEdge(fileNode.id, incident.id, "affects");
|
|
@@ -2767,7 +1686,7 @@ ${filesBlock}`,
|
|
|
2767
1686
|
const projectPath = this.watchedDirectory || getWorkingDirectory(void 0, true);
|
|
2768
1687
|
console.debug("[Initial Scan] Starting initial goal compliance scan", { projectPath });
|
|
2769
1688
|
try {
|
|
2770
|
-
const { getActiveGoals, recordGoalViolationCaught } = await import("./goal-validator-
|
|
1689
|
+
const { getActiveGoals, recordGoalViolationCaught } = await import("./goal-validator-DA3JQ6JN.js");
|
|
2771
1690
|
const activeGoals = await getActiveGoals(projectPath);
|
|
2772
1691
|
console.debug("[Initial Scan] Loaded goals for initial scan:", {
|
|
2773
1692
|
goalCount: activeGoals.length,
|
|
@@ -2783,7 +1702,7 @@ ${filesBlock}`,
|
|
|
2783
1702
|
const recentFiles = /* @__PURE__ */ new Set();
|
|
2784
1703
|
const uncommittedFiles = await getGitChangedFiles(projectPath);
|
|
2785
1704
|
if (uncommittedFiles) {
|
|
2786
|
-
uncommittedFiles.forEach((f) => recentFiles.add(
|
|
1705
|
+
uncommittedFiles.forEach((f) => recentFiles.add(join2(projectPath, f)));
|
|
2787
1706
|
}
|
|
2788
1707
|
const oneDayAgo = Date.now() - 24 * 60 * 60 * 1e3;
|
|
2789
1708
|
const recentChanges = await getChangedFilesSinceTimestamp(projectPath, oneDayAgo);
|
|
@@ -2791,8 +1710,8 @@ ${filesBlock}`,
|
|
|
2791
1710
|
recentChanges.forEach((f) => recentFiles.add(f));
|
|
2792
1711
|
}
|
|
2793
1712
|
const filesToCheck = Array.from(recentFiles).filter((file) => {
|
|
2794
|
-
const ext =
|
|
2795
|
-
return WATCH_EXTENSIONS.has(ext) &&
|
|
1713
|
+
const ext = extname2(file).toLowerCase();
|
|
1714
|
+
return WATCH_EXTENSIONS.has(ext) && existsSync2(file);
|
|
2796
1715
|
});
|
|
2797
1716
|
console.debug("[Initial Scan] Files discovered for initial scan:", {
|
|
2798
1717
|
totalRecentFiles: recentFiles.size,
|
|
@@ -2814,7 +1733,7 @@ ${filesBlock}`,
|
|
|
2814
1733
|
const fileContents = await Promise.all(
|
|
2815
1734
|
filesToScan.map(async (file) => {
|
|
2816
1735
|
try {
|
|
2817
|
-
const content = await
|
|
1736
|
+
const content = await readFile2(file, "utf-8");
|
|
2818
1737
|
const relativePath = file.replace(projectPath + "/", "");
|
|
2819
1738
|
return { path: relativePath, content: content.slice(0, maxCharsPerFile) };
|
|
2820
1739
|
} catch {
|
|
@@ -3044,7 +1963,7 @@ Use \`trie_watch start\` to begin autonomous scanning.`
|
|
|
3044
1963
|
).join("\n");
|
|
3045
1964
|
let agencyStatus = "";
|
|
3046
1965
|
try {
|
|
3047
|
-
const { getTrieAgent } = await import("./trie-agent-
|
|
1966
|
+
const { getTrieAgent } = await import("./trie-agent-6A7YBNTQ.js");
|
|
3048
1967
|
const trieAgent = getTrieAgent(this.watchedDirectory || getWorkingDirectory(void 0, true));
|
|
3049
1968
|
await trieAgent.initialize();
|
|
3050
1969
|
const status = await trieAgent.getAgencyStatus();
|
|
@@ -3160,9 +2079,9 @@ To get a full report, run \`trie_scan\` on your codebase.`
|
|
|
3160
2079
|
};
|
|
3161
2080
|
|
|
3162
2081
|
// src/tools/pr-review.ts
|
|
3163
|
-
import { readFile as
|
|
3164
|
-
import { existsSync as
|
|
3165
|
-
import { join as
|
|
2082
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
2083
|
+
import { existsSync as existsSync3 } from "fs";
|
|
2084
|
+
import { join as join3, basename as basename3, resolve as resolve2, isAbsolute as isAbsolute2 } from "path";
|
|
3166
2085
|
|
|
3167
2086
|
// src/skills/built-in/super-reviewer.ts
|
|
3168
2087
|
var CRITICAL_REVIEW_CHECKLIST = {
|
|
@@ -3275,8 +2194,8 @@ Usage:
|
|
|
3275
2194
|
*/
|
|
3276
2195
|
async getPRInfo(pr, worktree) {
|
|
3277
2196
|
if (worktree) {
|
|
3278
|
-
const worktreePath =
|
|
3279
|
-
if (!
|
|
2197
|
+
const worktreePath = isAbsolute2(worktree) ? worktree : resolve2(getWorkingDirectory(void 0, true), worktree);
|
|
2198
|
+
if (!existsSync3(worktreePath)) {
|
|
3280
2199
|
return { success: false, error: `Worktree not found: ${worktreePath}` };
|
|
3281
2200
|
}
|
|
3282
2201
|
return {
|
|
@@ -3409,8 +2328,8 @@ Usage:
|
|
|
3409
2328
|
"rfcs"
|
|
3410
2329
|
];
|
|
3411
2330
|
for (const docPath of designDocPaths) {
|
|
3412
|
-
const fullPath =
|
|
3413
|
-
if (
|
|
2331
|
+
const fullPath = join3(cwd, docPath);
|
|
2332
|
+
if (existsSync3(fullPath)) {
|
|
3414
2333
|
}
|
|
3415
2334
|
}
|
|
3416
2335
|
return designDocs;
|
|
@@ -3423,9 +2342,9 @@ Usage:
|
|
|
3423
2342
|
const cwd = getWorkingDirectory(void 0, true);
|
|
3424
2343
|
await Promise.all(filePaths.map(async (filePath) => {
|
|
3425
2344
|
try {
|
|
3426
|
-
const fullPath =
|
|
3427
|
-
if (
|
|
3428
|
-
const content = await
|
|
2345
|
+
const fullPath = isAbsolute2(filePath) ? filePath : join3(cwd, filePath);
|
|
2346
|
+
if (existsSync3(fullPath)) {
|
|
2347
|
+
const content = await readFile3(fullPath, "utf-8");
|
|
3429
2348
|
contents.set(filePath, content);
|
|
3430
2349
|
}
|
|
3431
2350
|
} catch {
|
|
@@ -5186,9 +4105,9 @@ var ToolRegistry = class {
|
|
|
5186
4105
|
};
|
|
5187
4106
|
|
|
5188
4107
|
// src/server/resource-manager.ts
|
|
5189
|
-
import { readdir, readFile as
|
|
5190
|
-
import { existsSync as
|
|
5191
|
-
import { join as
|
|
4108
|
+
import { readdir, readFile as readFile4 } from "fs/promises";
|
|
4109
|
+
import { existsSync as existsSync4 } from "fs";
|
|
4110
|
+
import { join as join4, dirname as dirname2 } from "path";
|
|
5192
4111
|
import { fileURLToPath } from "url";
|
|
5193
4112
|
|
|
5194
4113
|
// src/skills/installer.ts
|
|
@@ -5313,7 +4232,7 @@ var ResourceManager = class {
|
|
|
5313
4232
|
}
|
|
5314
4233
|
async getScanReportResources() {
|
|
5315
4234
|
try {
|
|
5316
|
-
const reportsDir =
|
|
4235
|
+
const reportsDir = join4(getWorkingDirectory(void 0, true), "trie-reports");
|
|
5317
4236
|
const files = await readdir(reportsDir);
|
|
5318
4237
|
const reportFiles = files.filter((f) => f.endsWith(".txt") || f.endsWith(".json"));
|
|
5319
4238
|
return reportFiles.slice(0, 10).map((file) => ({
|
|
@@ -5413,10 +4332,10 @@ var ResourceManager = class {
|
|
|
5413
4332
|
async getUIAppResource(uri, appId) {
|
|
5414
4333
|
const currentFile = fileURLToPath(import.meta.url);
|
|
5415
4334
|
const distDir = dirname2(dirname2(currentFile));
|
|
5416
|
-
const uiDir =
|
|
5417
|
-
const htmlPath =
|
|
4335
|
+
const uiDir = join4(distDir, "ui");
|
|
4336
|
+
const htmlPath = join4(uiDir, `${appId}.html`);
|
|
5418
4337
|
try {
|
|
5419
|
-
if (!
|
|
4338
|
+
if (!existsSync4(htmlPath)) {
|
|
5420
4339
|
return {
|
|
5421
4340
|
contents: [{
|
|
5422
4341
|
uri,
|
|
@@ -5425,7 +4344,7 @@ var ResourceManager = class {
|
|
|
5425
4344
|
}]
|
|
5426
4345
|
};
|
|
5427
4346
|
}
|
|
5428
|
-
const content = await
|
|
4347
|
+
const content = await readFile4(htmlPath, "utf-8");
|
|
5429
4348
|
return {
|
|
5430
4349
|
contents: [{
|
|
5431
4350
|
uri,
|
|
@@ -5605,10 +4524,10 @@ var ResourceManager = class {
|
|
|
5605
4524
|
} catch {
|
|
5606
4525
|
}
|
|
5607
4526
|
summary.push("---", "", "# Detailed Context", "");
|
|
5608
|
-
const agentsMdPath =
|
|
4527
|
+
const agentsMdPath = join4(getTrieDirectory(workDir), "AGENTS.md");
|
|
5609
4528
|
try {
|
|
5610
|
-
if (
|
|
5611
|
-
const agentsContent = await
|
|
4529
|
+
if (existsSync4(agentsMdPath)) {
|
|
4530
|
+
const agentsContent = await readFile4(agentsMdPath, "utf-8");
|
|
5612
4531
|
summary.push(agentsContent);
|
|
5613
4532
|
} else {
|
|
5614
4533
|
const contextSummary = await getContextForAI();
|
|
@@ -5733,8 +4652,8 @@ This information is automatically available to Claude Code, Cursor, and other AI
|
|
|
5733
4652
|
}
|
|
5734
4653
|
async getCacheStatsResource(uri) {
|
|
5735
4654
|
try {
|
|
5736
|
-
const cachePath =
|
|
5737
|
-
const cacheContent = await
|
|
4655
|
+
const cachePath = join4(getTrieDirectory(getWorkingDirectory(void 0, true)), ".trie-cache.json");
|
|
4656
|
+
const cacheContent = await readFile4(cachePath, "utf-8");
|
|
5738
4657
|
const cache = JSON.parse(cacheContent);
|
|
5739
4658
|
const fileCount = Object.keys(cache.files || {}).length;
|
|
5740
4659
|
const totalVulns = Object.values(cache.files || {}).reduce((acc, file) => {
|
|
@@ -5813,9 +4732,9 @@ This information is automatically available to Claude Code, Cursor, and other AI
|
|
|
5813
4732
|
}
|
|
5814
4733
|
async getScanReportResource(uri, parsedUri) {
|
|
5815
4734
|
const fileName = parsedUri.replace("reports/", "");
|
|
5816
|
-
const reportPath =
|
|
4735
|
+
const reportPath = join4(getWorkingDirectory(void 0, true), "trie-reports", fileName);
|
|
5817
4736
|
try {
|
|
5818
|
-
const content = await
|
|
4737
|
+
const content = await readFile4(reportPath, "utf-8");
|
|
5819
4738
|
return {
|
|
5820
4739
|
contents: [{
|
|
5821
4740
|
uri,
|
|
@@ -5962,32 +4881,32 @@ async function findOpenPort() {
|
|
|
5962
4881
|
return portChecks.find((port) => port !== null) || null;
|
|
5963
4882
|
}
|
|
5964
4883
|
async function checkPort(port) {
|
|
5965
|
-
return new Promise((
|
|
4884
|
+
return new Promise((resolve3) => {
|
|
5966
4885
|
const testServer = createServer();
|
|
5967
4886
|
let portInUse = false;
|
|
5968
4887
|
testServer.once("error", (err) => {
|
|
5969
4888
|
if (err.code === "EADDRINUSE") {
|
|
5970
4889
|
portInUse = true;
|
|
5971
|
-
testHttpPort(port).then(
|
|
4890
|
+
testHttpPort(port).then(resolve3).catch(() => resolve3(false));
|
|
5972
4891
|
} else {
|
|
5973
|
-
|
|
4892
|
+
resolve3(false);
|
|
5974
4893
|
}
|
|
5975
4894
|
});
|
|
5976
4895
|
testServer.once("listening", () => {
|
|
5977
4896
|
testServer.close();
|
|
5978
|
-
|
|
4897
|
+
resolve3(false);
|
|
5979
4898
|
});
|
|
5980
4899
|
setTimeout(() => {
|
|
5981
4900
|
if (!portInUse) {
|
|
5982
4901
|
testServer.close();
|
|
5983
|
-
|
|
4902
|
+
resolve3(false);
|
|
5984
4903
|
}
|
|
5985
4904
|
}, 1e3);
|
|
5986
4905
|
testServer.listen(port, "127.0.0.1");
|
|
5987
4906
|
});
|
|
5988
4907
|
}
|
|
5989
4908
|
async function testHttpPort(port) {
|
|
5990
|
-
return new Promise((
|
|
4909
|
+
return new Promise((resolve3) => {
|
|
5991
4910
|
const req = request({
|
|
5992
4911
|
hostname: "localhost",
|
|
5993
4912
|
port,
|
|
@@ -5995,18 +4914,18 @@ async function testHttpPort(port) {
|
|
|
5995
4914
|
method: "GET",
|
|
5996
4915
|
timeout: 2e3
|
|
5997
4916
|
}, (res) => {
|
|
5998
|
-
|
|
4917
|
+
resolve3(res.statusCode !== void 0);
|
|
5999
4918
|
res.on("data", () => {
|
|
6000
4919
|
});
|
|
6001
4920
|
res.on("end", () => {
|
|
6002
4921
|
});
|
|
6003
4922
|
});
|
|
6004
4923
|
req.on("error", () => {
|
|
6005
|
-
|
|
4924
|
+
resolve3(false);
|
|
6006
4925
|
});
|
|
6007
4926
|
req.on("timeout", () => {
|
|
6008
4927
|
req.destroy();
|
|
6009
|
-
|
|
4928
|
+
resolve3(false);
|
|
6010
4929
|
});
|
|
6011
4930
|
req.end();
|
|
6012
4931
|
});
|