@triedotdev/mcp 1.0.142 → 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-Y2LYDCJD.js → chunk-AIJF67CF.js} +1237 -33
- package/dist/chunk-AIJF67CF.js.map +1 -0
- package/dist/cli/yolo-daemon.js +1 -1
- package/dist/index.js +84 -1225
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/chunk-Y2LYDCJD.js.map +0 -1
|
@@ -977,9 +977,9 @@ function useDashboard() {
|
|
|
977
977
|
}
|
|
978
978
|
|
|
979
979
|
// src/cli/dashboard/App.tsx
|
|
980
|
-
import { existsSync as
|
|
981
|
-
import { readFile as
|
|
982
|
-
import { join as
|
|
980
|
+
import { existsSync as existsSync5 } from "fs";
|
|
981
|
+
import { readFile as readFile4, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
982
|
+
import { join as join3 } from "path";
|
|
983
983
|
|
|
984
984
|
// src/cli/dashboard/components/Header.tsx
|
|
985
985
|
import { Box, Text, useStdout } from "ink";
|
|
@@ -1739,7 +1739,7 @@ function truncate(str, max) {
|
|
|
1739
1739
|
}
|
|
1740
1740
|
function OverviewView() {
|
|
1741
1741
|
const { state, dispatch } = useDashboard();
|
|
1742
|
-
const { progress, signalExtraction, watch, issues, activityLog, activityPage, pendingFixes, selectedFixIndex } = state;
|
|
1742
|
+
const { progress, signalExtraction, watch, issues, activityLog, activityPage, pendingFixes: pendingFixes2, selectedFixIndex } = state;
|
|
1743
1743
|
const { totalIssues } = progress;
|
|
1744
1744
|
const { stdout } = useStdout4();
|
|
1745
1745
|
const cols = stdout?.columns || 80;
|
|
@@ -1748,7 +1748,7 @@ function OverviewView() {
|
|
|
1748
1748
|
const endTime = state.scanComplete && state.scanEndTime ? state.scanEndTime : Date.now();
|
|
1749
1749
|
const elapsed = ((endTime - state.scanStartTime) / 1e3).toFixed(1);
|
|
1750
1750
|
const criticalIssues = issues.filter((i) => i.severity === "critical").slice(0, 3);
|
|
1751
|
-
const activeFixes =
|
|
1751
|
+
const activeFixes = pendingFixes2.filter((f) => f.status === "pending" || f.status === "applying");
|
|
1752
1752
|
const activityRows = activeFixes.length > 0 ? 5 : 8;
|
|
1753
1753
|
const startIdx = activityPage * activityRows;
|
|
1754
1754
|
const pageActivities = activityLog.slice(startIdx, startIdx + activityRows);
|
|
@@ -5513,6 +5513,1163 @@ trie_explain type:"risk" target:"Adding Stripe integration for payments"
|
|
|
5513
5513
|
}
|
|
5514
5514
|
};
|
|
5515
5515
|
|
|
5516
|
+
// src/tools/cloud-fix.ts
|
|
5517
|
+
import { readFile as readFile3, writeFile, mkdir } from "fs/promises";
|
|
5518
|
+
import { existsSync as existsSync4, readFileSync } from "fs";
|
|
5519
|
+
import { join as join2 } from "path";
|
|
5520
|
+
import { execSync } from "child_process";
|
|
5521
|
+
|
|
5522
|
+
// src/integrations/cursor-cloud-agent.ts
|
|
5523
|
+
var BASE_URL = "https://api.cursor.com/v1";
|
|
5524
|
+
var CursorCloudAgentClient = class {
|
|
5525
|
+
apiKey;
|
|
5526
|
+
constructor(apiKey) {
|
|
5527
|
+
this.apiKey = apiKey;
|
|
5528
|
+
}
|
|
5529
|
+
/**
|
|
5530
|
+
* Dispatch an issue to a cloud agent for fixing.
|
|
5531
|
+
*/
|
|
5532
|
+
async dispatch(issue, triageResult, repoUrl, branch) {
|
|
5533
|
+
const prompt = this.buildPrompt(issue, triageResult);
|
|
5534
|
+
const body = {
|
|
5535
|
+
prompt,
|
|
5536
|
+
repo: repoUrl,
|
|
5537
|
+
branch,
|
|
5538
|
+
metadata: {
|
|
5539
|
+
issueId: issue.id,
|
|
5540
|
+
file: issue.file,
|
|
5541
|
+
line: issue.line,
|
|
5542
|
+
severity: issue.severity,
|
|
5543
|
+
agent: issue.agent
|
|
5544
|
+
}
|
|
5545
|
+
};
|
|
5546
|
+
const res = await this.request("POST", "/agents/tasks", body);
|
|
5547
|
+
return {
|
|
5548
|
+
jobId: res.id ?? res.taskId ?? res.jobId,
|
|
5549
|
+
status: "dispatched",
|
|
5550
|
+
artifactUrls: [],
|
|
5551
|
+
dispatchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5552
|
+
};
|
|
5553
|
+
}
|
|
5554
|
+
/**
|
|
5555
|
+
* Poll job status.
|
|
5556
|
+
*/
|
|
5557
|
+
async poll(jobId) {
|
|
5558
|
+
const res = await this.request("GET", `/agents/tasks/${jobId}`);
|
|
5559
|
+
const status = mapStatus(res.status);
|
|
5560
|
+
const prUrl = extractPrUrl(res);
|
|
5561
|
+
const artifactUrls = this.extractArtifacts(res);
|
|
5562
|
+
return { status, prUrl, artifactUrls };
|
|
5563
|
+
}
|
|
5564
|
+
/**
|
|
5565
|
+
* Get artifact URLs for a completed job.
|
|
5566
|
+
*/
|
|
5567
|
+
async getArtifacts(jobId) {
|
|
5568
|
+
const res = await this.request("GET", `/agents/tasks/${jobId}`);
|
|
5569
|
+
return this.extractArtifacts(res);
|
|
5570
|
+
}
|
|
5571
|
+
/**
|
|
5572
|
+
* Cancel a running job.
|
|
5573
|
+
*/
|
|
5574
|
+
async cancelJob(jobId) {
|
|
5575
|
+
await this.request("DELETE", `/agents/tasks/${jobId}`);
|
|
5576
|
+
}
|
|
5577
|
+
// --------------------------------------------------------------------------
|
|
5578
|
+
// Internal
|
|
5579
|
+
// --------------------------------------------------------------------------
|
|
5580
|
+
buildPrompt(issue, triageResult) {
|
|
5581
|
+
const parts = [
|
|
5582
|
+
"You are fixing a verified issue in the codebase.",
|
|
5583
|
+
"",
|
|
5584
|
+
`Issue: ${issue.issue}`,
|
|
5585
|
+
`File: ${issue.file}${issue.line ? `:${issue.line}` : ""}`,
|
|
5586
|
+
`Severity: ${issue.severity} | Effort: ${issue.effort ?? "medium"}`,
|
|
5587
|
+
`Agent that found it: ${issue.agent}`,
|
|
5588
|
+
`Suggested fix: ${issue.fix}`
|
|
5589
|
+
];
|
|
5590
|
+
if (issue.cwe) parts.push(`CWE: ${issue.cwe}`);
|
|
5591
|
+
if (issue.owasp) parts.push(`OWASP: ${issue.owasp}`);
|
|
5592
|
+
parts.push(`Triage confidence: ${triageResult.confidence.toFixed(2)}`);
|
|
5593
|
+
parts.push(`Why cloud agent: ${triageResult.reasons.join(", ")}`);
|
|
5594
|
+
parts.push("");
|
|
5595
|
+
parts.push("Steps:");
|
|
5596
|
+
parts.push(`1. Apply the minimal fix described above to ${issue.file}`);
|
|
5597
|
+
parts.push("2. Run the existing test suite (detect test runner from package.json scripts)");
|
|
5598
|
+
parts.push("3. Screenshot the passing test output \u2014 this is the verification artifact");
|
|
5599
|
+
parts.push("4. If tests fail, iterate on the fix until they pass (max 3 attempts)");
|
|
5600
|
+
parts.push("5. Open a PR with only this single change \u2014 do not bundle other fixes");
|
|
5601
|
+
parts.push("6. Include the screenshot in the PR description as evidence");
|
|
5602
|
+
return parts.join("\n");
|
|
5603
|
+
}
|
|
5604
|
+
async request(method, path2, body) {
|
|
5605
|
+
const url = `${BASE_URL}${path2}`;
|
|
5606
|
+
const headers = {
|
|
5607
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
5608
|
+
"Content-Type": "application/json"
|
|
5609
|
+
};
|
|
5610
|
+
const init = { method, headers };
|
|
5611
|
+
if (body) init.body = JSON.stringify(body);
|
|
5612
|
+
const res = await fetch(url, init);
|
|
5613
|
+
if (res.status === 401) {
|
|
5614
|
+
throw new Error(
|
|
5615
|
+
'Cursor API key is invalid or expired.\nUpdate it: trie_cloud_fix action:configure apiKey:"key-..."'
|
|
5616
|
+
);
|
|
5617
|
+
}
|
|
5618
|
+
if (res.status === 404) {
|
|
5619
|
+
throw new Error(`Job not found: ${path2}`);
|
|
5620
|
+
}
|
|
5621
|
+
if (res.status >= 500) {
|
|
5622
|
+
throw new Error(
|
|
5623
|
+
`Cursor API returned ${res.status}. The service may be temporarily unavailable \u2014 retry in a few minutes.`
|
|
5624
|
+
);
|
|
5625
|
+
}
|
|
5626
|
+
if (!res.ok) {
|
|
5627
|
+
const text = await res.text().catch(() => "");
|
|
5628
|
+
throw new Error(`Cursor API error ${res.status}: ${text}`);
|
|
5629
|
+
}
|
|
5630
|
+
return res.json();
|
|
5631
|
+
}
|
|
5632
|
+
extractArtifacts(res) {
|
|
5633
|
+
const urls = [];
|
|
5634
|
+
if (Array.isArray(res.messages)) {
|
|
5635
|
+
for (const msg of res.messages) {
|
|
5636
|
+
if (typeof msg.content === "string") {
|
|
5637
|
+
const matches = msg.content.match(/https:\/\/[^\s)]+\.(png|jpg|mp4|webm)/g);
|
|
5638
|
+
if (matches) urls.push(...matches);
|
|
5639
|
+
}
|
|
5640
|
+
}
|
|
5641
|
+
}
|
|
5642
|
+
if (Array.isArray(res.artifacts)) {
|
|
5643
|
+
for (const a of res.artifacts) {
|
|
5644
|
+
if (a.url) urls.push(a.url);
|
|
5645
|
+
}
|
|
5646
|
+
}
|
|
5647
|
+
return [...new Set(urls)];
|
|
5648
|
+
}
|
|
5649
|
+
};
|
|
5650
|
+
function mapStatus(raw) {
|
|
5651
|
+
switch (raw) {
|
|
5652
|
+
case "pending":
|
|
5653
|
+
case "queued":
|
|
5654
|
+
return "pending";
|
|
5655
|
+
case "running":
|
|
5656
|
+
case "in_progress":
|
|
5657
|
+
return "running";
|
|
5658
|
+
case "completed":
|
|
5659
|
+
case "succeeded":
|
|
5660
|
+
case "verified":
|
|
5661
|
+
return "completed";
|
|
5662
|
+
default:
|
|
5663
|
+
return "failed";
|
|
5664
|
+
}
|
|
5665
|
+
}
|
|
5666
|
+
function extractPrUrl(res) {
|
|
5667
|
+
if (typeof res.prUrl === "string") return res.prUrl;
|
|
5668
|
+
if (typeof res.pr_url === "string") return res.pr_url;
|
|
5669
|
+
if (typeof res.pullRequestUrl === "string") return res.pullRequestUrl;
|
|
5670
|
+
if (Array.isArray(res.messages)) {
|
|
5671
|
+
for (const msg of res.messages) {
|
|
5672
|
+
if (typeof msg.content === "string") {
|
|
5673
|
+
const match = msg.content.match(/https:\/\/github\.com\/[^\s)]+\/pull\/\d+/);
|
|
5674
|
+
if (match) return match[0];
|
|
5675
|
+
}
|
|
5676
|
+
}
|
|
5677
|
+
}
|
|
5678
|
+
return void 0;
|
|
5679
|
+
}
|
|
5680
|
+
|
|
5681
|
+
// src/tools/fix-triage.ts
|
|
5682
|
+
var EFFORT_SCORES = {
|
|
5683
|
+
trivial: -6,
|
|
5684
|
+
easy: -3,
|
|
5685
|
+
medium: 0,
|
|
5686
|
+
hard: 4
|
|
5687
|
+
};
|
|
5688
|
+
var SEVERITY_SCORES = {
|
|
5689
|
+
critical: 4,
|
|
5690
|
+
serious: 2,
|
|
5691
|
+
moderate: -1,
|
|
5692
|
+
low: -3
|
|
5693
|
+
};
|
|
5694
|
+
function triageIssue(issue, context, occurrence, config, pipeline) {
|
|
5695
|
+
const reasons = [];
|
|
5696
|
+
if (config?.level === "passive") {
|
|
5697
|
+
return {
|
|
5698
|
+
strategy: "local-ai",
|
|
5699
|
+
score: 0,
|
|
5700
|
+
confidence: 1,
|
|
5701
|
+
reasons: ["Autonomy level is passive \u2014 cloud dispatch disabled"],
|
|
5702
|
+
fallback: "local-ai"
|
|
5703
|
+
};
|
|
5704
|
+
}
|
|
5705
|
+
if (config?.cloudAgentEnabled === false) {
|
|
5706
|
+
return {
|
|
5707
|
+
strategy: "local-ai",
|
|
5708
|
+
score: 0,
|
|
5709
|
+
confidence: 1,
|
|
5710
|
+
reasons: ["Cloud agent not enabled (run trie_cloud_fix action:configure)"],
|
|
5711
|
+
fallback: "local-ai"
|
|
5712
|
+
};
|
|
5713
|
+
}
|
|
5714
|
+
let score = 0;
|
|
5715
|
+
const effort = issue.effort ?? "medium";
|
|
5716
|
+
const effortScore = EFFORT_SCORES[effort] ?? 0;
|
|
5717
|
+
if (effortScore !== 0) {
|
|
5718
|
+
score += effortScore;
|
|
5719
|
+
reasons.push(`effort:${effort}`);
|
|
5720
|
+
}
|
|
5721
|
+
const severityScore = SEVERITY_SCORES[issue.severity] ?? 0;
|
|
5722
|
+
if (severityScore !== 0) {
|
|
5723
|
+
score += severityScore;
|
|
5724
|
+
reasons.push(`severity:${issue.severity}`);
|
|
5725
|
+
}
|
|
5726
|
+
if (issue.autoFixable) {
|
|
5727
|
+
score -= 2;
|
|
5728
|
+
reasons.push("autoFixable");
|
|
5729
|
+
}
|
|
5730
|
+
if (issue.confidence < 0.7) {
|
|
5731
|
+
score -= 2;
|
|
5732
|
+
reasons.push(`low confidence (${(issue.confidence * 100).toFixed(0)}%)`);
|
|
5733
|
+
}
|
|
5734
|
+
if (issue.cwe) {
|
|
5735
|
+
score += 3;
|
|
5736
|
+
reasons.push(`cwe:${issue.cwe}`);
|
|
5737
|
+
}
|
|
5738
|
+
if (issue.owasp) {
|
|
5739
|
+
score += 2;
|
|
5740
|
+
reasons.push(`owasp:${issue.owasp}`);
|
|
5741
|
+
}
|
|
5742
|
+
if (issue.category === "security") {
|
|
5743
|
+
score += 2;
|
|
5744
|
+
reasons.push("category:security");
|
|
5745
|
+
}
|
|
5746
|
+
if (context) {
|
|
5747
|
+
if (context.hasTests) {
|
|
5748
|
+
score += 1;
|
|
5749
|
+
reasons.push("has tests");
|
|
5750
|
+
} else {
|
|
5751
|
+
score -= 2;
|
|
5752
|
+
reasons.push("no tests");
|
|
5753
|
+
}
|
|
5754
|
+
if (context.complexity === "high") {
|
|
5755
|
+
score += 1;
|
|
5756
|
+
reasons.push("high complexity");
|
|
5757
|
+
}
|
|
5758
|
+
if (context.touchesAuth || context.touchesCrypto || context.touchesPayments) {
|
|
5759
|
+
score += 2;
|
|
5760
|
+
reasons.push("touches auth/crypto/payments");
|
|
5761
|
+
}
|
|
5762
|
+
if (context.touchesDatabase) {
|
|
5763
|
+
score += 1;
|
|
5764
|
+
reasons.push("touches database");
|
|
5765
|
+
}
|
|
5766
|
+
}
|
|
5767
|
+
if (occurrence) {
|
|
5768
|
+
if (occurrence.count >= 5) {
|
|
5769
|
+
score += 3;
|
|
5770
|
+
reasons.push(`${occurrence.count}\xD7 seen`);
|
|
5771
|
+
} else if (occurrence.count >= 3) {
|
|
5772
|
+
score += 1;
|
|
5773
|
+
reasons.push(`${occurrence.count}\xD7 seen`);
|
|
5774
|
+
}
|
|
5775
|
+
if (occurrence.escalationLevel === "block") {
|
|
5776
|
+
score += 4;
|
|
5777
|
+
reasons.push("escalation:block");
|
|
5778
|
+
} else if (occurrence.escalationLevel === "escalate") {
|
|
5779
|
+
score += 2;
|
|
5780
|
+
reasons.push("escalation:escalate");
|
|
5781
|
+
}
|
|
5782
|
+
}
|
|
5783
|
+
if (config?.level === "aggressive") {
|
|
5784
|
+
score -= 1;
|
|
5785
|
+
reasons.push("aggressive mode (\u22121 threshold)");
|
|
5786
|
+
}
|
|
5787
|
+
if (pipeline) {
|
|
5788
|
+
if (pipeline.hasLinkedPR && pipeline.prState === "open") {
|
|
5789
|
+
score -= 2;
|
|
5790
|
+
reasons.push("has open PR");
|
|
5791
|
+
}
|
|
5792
|
+
if (pipeline.hasLinkedTicket && pipeline.ticketStatus?.toLowerCase().includes("started")) {
|
|
5793
|
+
score -= 1;
|
|
5794
|
+
reasons.push("ticket in active sprint");
|
|
5795
|
+
}
|
|
5796
|
+
if (pipeline.hasLinkedTicket && pipeline.ticketPriority === "urgent") {
|
|
5797
|
+
score += 1;
|
|
5798
|
+
reasons.push("ticket:urgent");
|
|
5799
|
+
}
|
|
5800
|
+
if (!pipeline.hasLinkedTicket && !pipeline.hasLinkedPR && issue.severity === "critical") {
|
|
5801
|
+
score += 2;
|
|
5802
|
+
reasons.push("critical, no ticket/PR (falling through cracks)");
|
|
5803
|
+
}
|
|
5804
|
+
}
|
|
5805
|
+
let strategy;
|
|
5806
|
+
if (score < 0 && issue.autoFixable) {
|
|
5807
|
+
strategy = "inline-auto";
|
|
5808
|
+
} else if (score < 4) {
|
|
5809
|
+
strategy = "local-ai";
|
|
5810
|
+
} else {
|
|
5811
|
+
strategy = "cloud-agent";
|
|
5812
|
+
}
|
|
5813
|
+
const confidence = Math.min(1, Math.abs(score) / 8);
|
|
5814
|
+
return { strategy, score, confidence, reasons, fallback: "local-ai" };
|
|
5815
|
+
}
|
|
5816
|
+
function triageIssues(issues, context, occurrences, config, pipelineContexts) {
|
|
5817
|
+
const results = /* @__PURE__ */ new Map();
|
|
5818
|
+
const summary = {
|
|
5819
|
+
inlineAuto: [],
|
|
5820
|
+
localAi: [],
|
|
5821
|
+
cloudAgent: [],
|
|
5822
|
+
cloudAgentScore: 0
|
|
5823
|
+
};
|
|
5824
|
+
for (const issue of issues) {
|
|
5825
|
+
const occurrence = occurrences?.get(issue.id);
|
|
5826
|
+
const pipeline = pipelineContexts?.get(issue.id);
|
|
5827
|
+
const result = triageIssue(issue, context, occurrence, config, pipeline);
|
|
5828
|
+
results.set(issue.id, result);
|
|
5829
|
+
switch (result.strategy) {
|
|
5830
|
+
case "inline-auto":
|
|
5831
|
+
summary.inlineAuto.push(issue);
|
|
5832
|
+
break;
|
|
5833
|
+
case "local-ai":
|
|
5834
|
+
summary.localAi.push(issue);
|
|
5835
|
+
break;
|
|
5836
|
+
case "cloud-agent":
|
|
5837
|
+
summary.cloudAgent.push(issue);
|
|
5838
|
+
summary.cloudAgentScore += result.score;
|
|
5839
|
+
break;
|
|
5840
|
+
}
|
|
5841
|
+
}
|
|
5842
|
+
return { results, summary };
|
|
5843
|
+
}
|
|
5844
|
+
function formatTriageTable(results, issues) {
|
|
5845
|
+
const LINE = "\u2500".repeat(68);
|
|
5846
|
+
const lines = [];
|
|
5847
|
+
lines.push("FIX ROUTING PLAN");
|
|
5848
|
+
lines.push(LINE);
|
|
5849
|
+
lines.push(
|
|
5850
|
+
padEnd("Issue", 32) + padEnd("Strategy", 14) + padEnd("Score", 7) + "Reason"
|
|
5851
|
+
);
|
|
5852
|
+
for (const issue of issues) {
|
|
5853
|
+
const r = results.get(issue.id);
|
|
5854
|
+
if (!r) continue;
|
|
5855
|
+
const loc = `${shortPath(issue.file)}:${issue.line ?? "?"}`;
|
|
5856
|
+
const scoreStr = r.score >= 0 ? `+${r.score}` : String(r.score);
|
|
5857
|
+
lines.push(
|
|
5858
|
+
padEnd(loc, 32) + padEnd(r.strategy, 14) + padEnd(scoreStr, 7) + r.reasons.join(", ")
|
|
5859
|
+
);
|
|
5860
|
+
}
|
|
5861
|
+
lines.push(LINE);
|
|
5862
|
+
const counts = [];
|
|
5863
|
+
const cloud = issues.filter((i) => results.get(i.id)?.strategy === "cloud-agent");
|
|
5864
|
+
const local = issues.filter((i) => results.get(i.id)?.strategy === "local-ai");
|
|
5865
|
+
const auto = issues.filter((i) => results.get(i.id)?.strategy === "inline-auto");
|
|
5866
|
+
if (cloud.length) counts.push(`${cloud.length} for cloud agent`);
|
|
5867
|
+
if (local.length) counts.push(`${local.length} for local AI`);
|
|
5868
|
+
if (auto.length) counts.push(`${auto.length} auto-fixable`);
|
|
5869
|
+
lines.push(counts.join(", "));
|
|
5870
|
+
if (cloud.length > 0) {
|
|
5871
|
+
const ids = cloud.map((i) => `"${i.id}"`).join(",");
|
|
5872
|
+
lines.push("");
|
|
5873
|
+
lines.push(`To dispatch cloud issues: trie_cloud_fix action:dispatch issueIds:[${ids}]`);
|
|
5874
|
+
}
|
|
5875
|
+
if (local.length > 0) {
|
|
5876
|
+
const ids = local.map((i) => `"${i.id}"`).join(",");
|
|
5877
|
+
lines.push(`To fix local issues: trie_fix issueIds:[${ids}]`);
|
|
5878
|
+
}
|
|
5879
|
+
return lines.join("\n");
|
|
5880
|
+
}
|
|
5881
|
+
function formatCloudRecommendation(results, issues) {
|
|
5882
|
+
const cloud = issues.filter((i) => {
|
|
5883
|
+
const r = results.get(i.id);
|
|
5884
|
+
return r && r.score >= 4;
|
|
5885
|
+
});
|
|
5886
|
+
if (cloud.length === 0) return null;
|
|
5887
|
+
const LINE = "\u2500".repeat(65);
|
|
5888
|
+
const lines = [];
|
|
5889
|
+
lines.push(LINE);
|
|
5890
|
+
lines.push(
|
|
5891
|
+
`${cloud.length} issue${cloud.length > 1 ? "s" : ""} qualify for cloud agent verification (test-verified fix + PR):`
|
|
5892
|
+
);
|
|
5893
|
+
for (const issue of cloud) {
|
|
5894
|
+
const r = results.get(issue.id);
|
|
5895
|
+
const loc = `${shortPath(issue.file)}:${issue.line ?? "?"}`;
|
|
5896
|
+
const scoreStr = r.score >= 0 ? `+${r.score}` : String(r.score);
|
|
5897
|
+
lines.push(` \u2022 ${padEnd(loc, 30)} \u2014 score ${scoreStr} (${r.reasons.join(", ")})`);
|
|
5898
|
+
}
|
|
5899
|
+
const ids = cloud.map((i) => `"${i.id}"`).join(",");
|
|
5900
|
+
lines.push("");
|
|
5901
|
+
lines.push(`Run: trie_cloud_fix action:dispatch issueIds:[${ids}]`);
|
|
5902
|
+
lines.push(LINE);
|
|
5903
|
+
return lines.join("\n");
|
|
5904
|
+
}
|
|
5905
|
+
function shortPath(file) {
|
|
5906
|
+
const parts = file.split("/");
|
|
5907
|
+
return parts.length > 2 ? parts.slice(-2).join("/") : file;
|
|
5908
|
+
}
|
|
5909
|
+
function padEnd(str, len) {
|
|
5910
|
+
if (str.length >= len) return str.slice(0, len);
|
|
5911
|
+
return str + " ".repeat(len - str.length);
|
|
5912
|
+
}
|
|
5913
|
+
|
|
5914
|
+
// src/tools/fix.ts
|
|
5915
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
5916
|
+
import { existsSync as existsSync3 } from "fs";
|
|
5917
|
+
import { extname as extname2, relative as relative2, resolve as resolve2, isAbsolute as isAbsolute2 } from "path";
|
|
5918
|
+
var pendingFixes = /* @__PURE__ */ new Map();
|
|
5919
|
+
var TrieFixTool = class {
|
|
5920
|
+
async execute(args) {
|
|
5921
|
+
const { issueIds, file, line, issue, fix, autoApprove = false, dryRun = false, action } = args || {};
|
|
5922
|
+
if (action === "route") {
|
|
5923
|
+
return this.routeIssues(issueIds);
|
|
5924
|
+
}
|
|
5925
|
+
if (issueIds && issueIds.length > 0) {
|
|
5926
|
+
return this.fixByIds(issueIds, autoApprove, dryRun);
|
|
5927
|
+
}
|
|
5928
|
+
if (file && fix) {
|
|
5929
|
+
return this.applyFix(file, line || 1, issue || "User-specified fix", fix, dryRun);
|
|
5930
|
+
}
|
|
5931
|
+
if (pendingFixes.size > 0) {
|
|
5932
|
+
return this.showPendingFixes();
|
|
5933
|
+
}
|
|
5934
|
+
if (file && issue) {
|
|
5935
|
+
return this.generateFixPrompt(file, line || 1, issue);
|
|
5936
|
+
}
|
|
5937
|
+
return {
|
|
5938
|
+
content: [{
|
|
5939
|
+
type: "text",
|
|
5940
|
+
text: this.getHelpText()
|
|
5941
|
+
}]
|
|
5942
|
+
};
|
|
5943
|
+
}
|
|
5944
|
+
async routeIssues(issueIds) {
|
|
5945
|
+
await loadPendingFixesFromMemory();
|
|
5946
|
+
const pending = getPendingFixes();
|
|
5947
|
+
if (pending.length === 0) {
|
|
5948
|
+
return {
|
|
5949
|
+
content: [{ type: "text", text: "No pending issues. Run trie_scan to detect new issues, or check memory with trie_memory action:recent." }]
|
|
5950
|
+
};
|
|
5951
|
+
}
|
|
5952
|
+
const issues = pending.map((p) => ({
|
|
5953
|
+
id: p.id,
|
|
5954
|
+
severity: p.severity ?? "moderate",
|
|
5955
|
+
effort: p.effort,
|
|
5956
|
+
issue: p.issue,
|
|
5957
|
+
fix: p.suggestedFix,
|
|
5958
|
+
file: p.file,
|
|
5959
|
+
line: p.line,
|
|
5960
|
+
confidence: p.confidence,
|
|
5961
|
+
autoFixable: p.autoFixable ?? false,
|
|
5962
|
+
agent: "trie_scan",
|
|
5963
|
+
cwe: p.cwe,
|
|
5964
|
+
owasp: p.owasp,
|
|
5965
|
+
category: p.category
|
|
5966
|
+
}));
|
|
5967
|
+
const filtered = issueIds && issueIds.length > 0 ? issues.filter((i) => issueIds.includes(i.id)) : issues;
|
|
5968
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
5969
|
+
const config = await loadAutonomyConfig(workDir);
|
|
5970
|
+
const { results } = triageIssues(filtered, void 0, void 0, config);
|
|
5971
|
+
const table = formatTriageTable(results, filtered);
|
|
5972
|
+
return { content: [{ type: "text", text: `
|
|
5973
|
+
${table}
|
|
5974
|
+
` }] };
|
|
5975
|
+
}
|
|
5976
|
+
async fixByIds(issueIds, autoApprove, dryRun) {
|
|
5977
|
+
const results = [];
|
|
5978
|
+
let fixed = 0;
|
|
5979
|
+
let failed = 0;
|
|
5980
|
+
for (const id of issueIds) {
|
|
5981
|
+
const pendingFix = pendingFixes.get(id);
|
|
5982
|
+
if (!pendingFix) {
|
|
5983
|
+
results.push(`\u274C Issue ${id}: Not found in pending fixes`);
|
|
5984
|
+
failed++;
|
|
5985
|
+
continue;
|
|
5986
|
+
}
|
|
5987
|
+
if (pendingFix.confidence < 0.8 && !autoApprove) {
|
|
5988
|
+
results.push(`[!] Issue ${id}: Confidence too low (${(pendingFix.confidence * 100).toFixed(0)}%) - use autoApprove:true to override`);
|
|
5989
|
+
continue;
|
|
5990
|
+
}
|
|
5991
|
+
if (dryRun) {
|
|
5992
|
+
results.push(`\u{1F50D} Issue ${id}: Would fix "${pendingFix.issue}" in ${pendingFix.file}:${pendingFix.line}`);
|
|
5993
|
+
continue;
|
|
5994
|
+
}
|
|
5995
|
+
try {
|
|
5996
|
+
results.push(`\u2705 Issue ${id}: Fix prepared for ${pendingFix.file}:${pendingFix.line}`);
|
|
5997
|
+
results.push(` Issue: ${pendingFix.issue}`);
|
|
5998
|
+
results.push(` Fix: ${pendingFix.suggestedFix}`);
|
|
5999
|
+
pendingFix.status = "applied";
|
|
6000
|
+
fixed++;
|
|
6001
|
+
} catch (error) {
|
|
6002
|
+
results.push(`\u274C Issue ${id}: Failed to apply - ${error}`);
|
|
6003
|
+
failed++;
|
|
6004
|
+
}
|
|
6005
|
+
}
|
|
6006
|
+
let output = `
|
|
6007
|
+
${"\u2501".repeat(60)}
|
|
6008
|
+
`;
|
|
6009
|
+
output += `\u{1F527} FIX RESULTS
|
|
6010
|
+
`;
|
|
6011
|
+
output += `${"\u2501".repeat(60)}
|
|
6012
|
+
|
|
6013
|
+
`;
|
|
6014
|
+
output += results.join("\n");
|
|
6015
|
+
output += `
|
|
6016
|
+
|
|
6017
|
+
**Summary:** ${fixed} fixed, ${failed} failed, ${issueIds.length - fixed - failed} skipped
|
|
6018
|
+
`;
|
|
6019
|
+
output += await this.appendCloudRecommendation(issueIds);
|
|
6020
|
+
return { content: [{ type: "text", text: output }] };
|
|
6021
|
+
}
|
|
6022
|
+
async applyFix(file, line, issue, fix, dryRun) {
|
|
6023
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
6024
|
+
const filePath = isAbsolute2(file) ? file : resolve2(workDir, file);
|
|
6025
|
+
if (!existsSync3(filePath)) {
|
|
6026
|
+
return {
|
|
6027
|
+
content: [{
|
|
6028
|
+
type: "text",
|
|
6029
|
+
text: `\u274C File not found: ${filePath}`
|
|
6030
|
+
}]
|
|
6031
|
+
};
|
|
6032
|
+
}
|
|
6033
|
+
const content = await readFile2(filePath, "utf-8");
|
|
6034
|
+
const lines = content.split("\n");
|
|
6035
|
+
const language = this.detectLanguage(filePath);
|
|
6036
|
+
const contextStart = Math.max(0, line - 10);
|
|
6037
|
+
const contextEnd = Math.min(lines.length, line + 10);
|
|
6038
|
+
const contextLines = lines.slice(contextStart, contextEnd);
|
|
6039
|
+
const prompt = getPrompt("fix", "apply", {
|
|
6040
|
+
issue,
|
|
6041
|
+
fix,
|
|
6042
|
+
language,
|
|
6043
|
+
code: contextLines.join("\n"),
|
|
6044
|
+
filePath: relative2(workDir, filePath),
|
|
6045
|
+
line: String(line)
|
|
6046
|
+
});
|
|
6047
|
+
const systemPrompt = getSystemPrompt("fix");
|
|
6048
|
+
let output = `
|
|
6049
|
+
${"\u2501".repeat(60)}
|
|
6050
|
+
`;
|
|
6051
|
+
output += `\u{1F527} FIX APPLICATION REQUEST
|
|
6052
|
+
`;
|
|
6053
|
+
output += `${"\u2501".repeat(60)}
|
|
6054
|
+
|
|
6055
|
+
`;
|
|
6056
|
+
output += `## \u{1F4CD} Target
|
|
6057
|
+
|
|
6058
|
+
`;
|
|
6059
|
+
output += `- **File:** \`${relative2(workDir, filePath)}\`
|
|
6060
|
+
`;
|
|
6061
|
+
output += `- **Line:** ${line}
|
|
6062
|
+
`;
|
|
6063
|
+
output += `- **Issue:** ${issue}
|
|
6064
|
+
`;
|
|
6065
|
+
output += `- **Requested Fix:** ${fix}
|
|
6066
|
+
|
|
6067
|
+
`;
|
|
6068
|
+
output += `## \u{1F4C4} Current Code Context
|
|
6069
|
+
|
|
6070
|
+
`;
|
|
6071
|
+
output += `\`\`\`${language}
|
|
6072
|
+
`;
|
|
6073
|
+
for (let i = 0; i < contextLines.length; i++) {
|
|
6074
|
+
const lineNum = contextStart + i + 1;
|
|
6075
|
+
const marker = lineNum === line ? "\u2192 " : " ";
|
|
6076
|
+
output += `${marker}${lineNum.toString().padStart(4)} | ${contextLines[i]}
|
|
6077
|
+
`;
|
|
6078
|
+
}
|
|
6079
|
+
output += `\`\`\`
|
|
6080
|
+
|
|
6081
|
+
`;
|
|
6082
|
+
if (dryRun) {
|
|
6083
|
+
output += `## \u{1F50D} Dry Run Mode
|
|
6084
|
+
|
|
6085
|
+
`;
|
|
6086
|
+
output += `No changes will be made. Review the fix below:
|
|
6087
|
+
|
|
6088
|
+
`;
|
|
6089
|
+
}
|
|
6090
|
+
output += `${"\u2500".repeat(60)}
|
|
6091
|
+
`;
|
|
6092
|
+
output += `## \u{1F9E0} Fix Request for AI
|
|
6093
|
+
|
|
6094
|
+
`;
|
|
6095
|
+
output += `**Role:** ${systemPrompt.split("\n")[0]}
|
|
6096
|
+
|
|
6097
|
+
`;
|
|
6098
|
+
output += prompt;
|
|
6099
|
+
output += `
|
|
6100
|
+
${"\u2500".repeat(60)}
|
|
6101
|
+
`;
|
|
6102
|
+
output += `
|
|
6103
|
+
### After generating the fix, apply it with:
|
|
6104
|
+
|
|
6105
|
+
`;
|
|
6106
|
+
output += `\`\`\`
|
|
6107
|
+
`;
|
|
6108
|
+
output += `Use the edit_file tool to apply the generated code changes
|
|
6109
|
+
`;
|
|
6110
|
+
output += `\`\`\`
|
|
6111
|
+
`;
|
|
6112
|
+
return { content: [{ type: "text", text: output }] };
|
|
6113
|
+
}
|
|
6114
|
+
async generateFixPrompt(file, line, issue) {
|
|
6115
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
6116
|
+
const filePath = isAbsolute2(file) ? file : resolve2(workDir, file);
|
|
6117
|
+
if (!existsSync3(filePath)) {
|
|
6118
|
+
return {
|
|
6119
|
+
content: [{
|
|
6120
|
+
type: "text",
|
|
6121
|
+
text: `\u274C File not found: ${filePath}`
|
|
6122
|
+
}]
|
|
6123
|
+
};
|
|
6124
|
+
}
|
|
6125
|
+
const content = await readFile2(filePath, "utf-8");
|
|
6126
|
+
const lines = content.split("\n");
|
|
6127
|
+
const language = this.detectLanguage(filePath);
|
|
6128
|
+
const contextStart = Math.max(0, line - 20);
|
|
6129
|
+
const contextEnd = Math.min(lines.length, line + 20);
|
|
6130
|
+
const contextLines = lines.slice(contextStart, contextEnd);
|
|
6131
|
+
let output = `
|
|
6132
|
+
${"\u2501".repeat(60)}
|
|
6133
|
+
`;
|
|
6134
|
+
output += `\u{1F527} FIX GENERATION REQUEST
|
|
6135
|
+
`;
|
|
6136
|
+
output += `${"\u2501".repeat(60)}
|
|
6137
|
+
|
|
6138
|
+
`;
|
|
6139
|
+
output += `## \u{1F4CD} Issue Details
|
|
6140
|
+
|
|
6141
|
+
`;
|
|
6142
|
+
output += `- **File:** \`${relative2(workDir, filePath)}\`
|
|
6143
|
+
`;
|
|
6144
|
+
output += `- **Line:** ${line}
|
|
6145
|
+
`;
|
|
6146
|
+
output += `- **Issue:** ${issue}
|
|
6147
|
+
|
|
6148
|
+
`;
|
|
6149
|
+
output += `## \u{1F4C4} Code Context
|
|
6150
|
+
|
|
6151
|
+
`;
|
|
6152
|
+
output += `\`\`\`${language}
|
|
6153
|
+
`;
|
|
6154
|
+
for (let i = 0; i < contextLines.length; i++) {
|
|
6155
|
+
const lineNum = contextStart + i + 1;
|
|
6156
|
+
const marker = lineNum === line ? "\u2192 " : " ";
|
|
6157
|
+
output += `${marker}${lineNum.toString().padStart(4)} | ${contextLines[i]}
|
|
6158
|
+
`;
|
|
6159
|
+
}
|
|
6160
|
+
output += `\`\`\`
|
|
6161
|
+
|
|
6162
|
+
`;
|
|
6163
|
+
output += `## \u{1F9E0} Analysis Request
|
|
6164
|
+
|
|
6165
|
+
`;
|
|
6166
|
+
output += `Please analyze this issue and provide:
|
|
6167
|
+
|
|
6168
|
+
`;
|
|
6169
|
+
output += `1. **Root cause** - Why does this issue occur?
|
|
6170
|
+
`;
|
|
6171
|
+
output += `2. **Impact** - What could go wrong if unfixed?
|
|
6172
|
+
`;
|
|
6173
|
+
output += `3. **Fix** - The exact code change needed
|
|
6174
|
+
`;
|
|
6175
|
+
output += `4. **Verification** - How to test the fix works
|
|
6176
|
+
|
|
6177
|
+
`;
|
|
6178
|
+
output += `After analysis, you can apply the fix using the edit_file tool.
|
|
6179
|
+
`;
|
|
6180
|
+
return { content: [{ type: "text", text: output }] };
|
|
6181
|
+
}
|
|
6182
|
+
showPendingFixes() {
|
|
6183
|
+
let output = `
|
|
6184
|
+
${"\u2501".repeat(60)}
|
|
6185
|
+
`;
|
|
6186
|
+
output += `\u{1F527} PENDING FIXES
|
|
6187
|
+
`;
|
|
6188
|
+
output += `${"\u2501".repeat(60)}
|
|
6189
|
+
|
|
6190
|
+
`;
|
|
6191
|
+
if (pendingFixes.size === 0) {
|
|
6192
|
+
output += `No pending fixes. Run \`trie_scan\` first to detect issues.
|
|
6193
|
+
`;
|
|
6194
|
+
return { content: [{ type: "text", text: output }] };
|
|
6195
|
+
}
|
|
6196
|
+
const fixes = Array.from(pendingFixes.values());
|
|
6197
|
+
const byStatus = {
|
|
6198
|
+
pending: fixes.filter((f) => f.status === "pending"),
|
|
6199
|
+
applied: fixes.filter((f) => f.status === "applied"),
|
|
6200
|
+
rejected: fixes.filter((f) => f.status === "rejected")
|
|
6201
|
+
};
|
|
6202
|
+
if (byStatus.pending.length > 0) {
|
|
6203
|
+
output += `## \u23F3 Pending (${byStatus.pending.length})
|
|
6204
|
+
|
|
6205
|
+
`;
|
|
6206
|
+
output += `| ID | File | Line | Issue | Confidence |
|
|
6207
|
+
`;
|
|
6208
|
+
output += `|----|------|------|-------|------------|
|
|
6209
|
+
`;
|
|
6210
|
+
for (const fix of byStatus.pending) {
|
|
6211
|
+
const conf = `${(fix.confidence * 100).toFixed(0)}%`;
|
|
6212
|
+
const shortFile = fix.file.split("/").slice(-2).join("/");
|
|
6213
|
+
output += `| ${fix.id} | ${shortFile} | ${fix.line} | ${fix.issue.slice(0, 40)}... | ${conf} |
|
|
6214
|
+
`;
|
|
6215
|
+
}
|
|
6216
|
+
output += "\n";
|
|
6217
|
+
}
|
|
6218
|
+
output += `### Commands
|
|
6219
|
+
|
|
6220
|
+
`;
|
|
6221
|
+
output += `- Fix all high-confidence: \`trie_fix autoApprove:true\`
|
|
6222
|
+
`;
|
|
6223
|
+
output += `- Fix specific: \`trie_fix issueIds:["id1", "id2"]\`
|
|
6224
|
+
`;
|
|
6225
|
+
output += `- Preview: \`trie_fix dryRun:true\`
|
|
6226
|
+
`;
|
|
6227
|
+
return { content: [{ type: "text", text: output }] };
|
|
6228
|
+
}
|
|
6229
|
+
getHelpText() {
|
|
6230
|
+
return `
|
|
6231
|
+
${"\u2501".repeat(60)}
|
|
6232
|
+
\u{1F527} TRIE FIX - AI-POWERED CODE FIXING
|
|
6233
|
+
${"\u2501".repeat(60)}
|
|
6234
|
+
|
|
6235
|
+
## Usage
|
|
6236
|
+
|
|
6237
|
+
### Fix issues from a scan:
|
|
6238
|
+
\`\`\`
|
|
6239
|
+
trie_fix issueIds:["issue-1", "issue-2"]
|
|
6240
|
+
\`\`\`
|
|
6241
|
+
|
|
6242
|
+
### Auto-fix all high-confidence issues:
|
|
6243
|
+
\`\`\`
|
|
6244
|
+
trie_fix autoApprove:true
|
|
6245
|
+
\`\`\`
|
|
6246
|
+
|
|
6247
|
+
### Fix specific file and line:
|
|
6248
|
+
\`\`\`
|
|
6249
|
+
trie_fix file:"src/app.ts" line:42 issue:"SQL injection" fix:"Use parameterized query"
|
|
6250
|
+
\`\`\`
|
|
6251
|
+
|
|
6252
|
+
### Preview fixes without applying:
|
|
6253
|
+
\`\`\`
|
|
6254
|
+
trie_fix dryRun:true
|
|
6255
|
+
\`\`\`
|
|
6256
|
+
|
|
6257
|
+
### View pending fixes:
|
|
6258
|
+
\`\`\`
|
|
6259
|
+
trie_fix
|
|
6260
|
+
\`\`\`
|
|
6261
|
+
|
|
6262
|
+
## Workflow
|
|
6263
|
+
|
|
6264
|
+
1. Run \`trie_scan\` to detect issues
|
|
6265
|
+
2. Review the issues found
|
|
6266
|
+
3. Run \`trie_fix\` to apply fixes
|
|
6267
|
+
|
|
6268
|
+
The AI will analyze each issue, generate the fix, and you can review before applying.
|
|
6269
|
+
`;
|
|
6270
|
+
}
|
|
6271
|
+
async appendCloudRecommendation(handledIds) {
|
|
6272
|
+
try {
|
|
6273
|
+
const pending = getPendingFixes();
|
|
6274
|
+
const remaining = pending.filter((p) => !handledIds.includes(p.id) || pendingFixes.get(p.id)?.status === "pending");
|
|
6275
|
+
if (remaining.length === 0) return "";
|
|
6276
|
+
const issues = remaining.map((p) => ({
|
|
6277
|
+
id: p.id,
|
|
6278
|
+
severity: "moderate",
|
|
6279
|
+
issue: p.issue,
|
|
6280
|
+
fix: p.suggestedFix,
|
|
6281
|
+
file: p.file,
|
|
6282
|
+
line: p.line,
|
|
6283
|
+
confidence: p.confidence,
|
|
6284
|
+
autoFixable: false,
|
|
6285
|
+
agent: "trie_scan"
|
|
6286
|
+
}));
|
|
6287
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
6288
|
+
const config = await loadAutonomyConfig(workDir);
|
|
6289
|
+
const { results } = triageIssues(issues, void 0, void 0, config);
|
|
6290
|
+
const footer = formatCloudRecommendation(results, issues);
|
|
6291
|
+
return footer ? `
|
|
6292
|
+
${footer}
|
|
6293
|
+
` : "";
|
|
6294
|
+
} catch {
|
|
6295
|
+
return "";
|
|
6296
|
+
}
|
|
6297
|
+
}
|
|
6298
|
+
detectLanguage(filePath) {
|
|
6299
|
+
const ext = extname2(filePath).toLowerCase();
|
|
6300
|
+
const langMap = {
|
|
6301
|
+
".ts": "typescript",
|
|
6302
|
+
".tsx": "tsx",
|
|
6303
|
+
".js": "javascript",
|
|
6304
|
+
".jsx": "jsx",
|
|
6305
|
+
".py": "python",
|
|
6306
|
+
".go": "go",
|
|
6307
|
+
".rs": "rust"
|
|
6308
|
+
};
|
|
6309
|
+
return langMap[ext] || "plaintext";
|
|
6310
|
+
}
|
|
6311
|
+
};
|
|
6312
|
+
function getPendingFixes() {
|
|
6313
|
+
return Array.from(pendingFixes.values());
|
|
6314
|
+
}
|
|
6315
|
+
async function loadPendingFixesFromMemory() {
|
|
6316
|
+
try {
|
|
6317
|
+
const { getRecentIssues } = await import("./issue-store-LZWZIGM7.js");
|
|
6318
|
+
pendingFixes.clear();
|
|
6319
|
+
const recentIssues = await getRecentIssues({ limit: 50, includeResolved: false });
|
|
6320
|
+
for (const storedIssue of recentIssues) {
|
|
6321
|
+
const fix = {
|
|
6322
|
+
id: storedIssue.id,
|
|
6323
|
+
file: storedIssue.file,
|
|
6324
|
+
line: storedIssue.line || 0,
|
|
6325
|
+
issue: storedIssue.issue,
|
|
6326
|
+
suggestedFix: storedIssue.fix,
|
|
6327
|
+
confidence: 0.8,
|
|
6328
|
+
// Default confidence for memory issues
|
|
6329
|
+
status: "pending",
|
|
6330
|
+
severity: storedIssue.severity,
|
|
6331
|
+
autoFixable: true,
|
|
6332
|
+
// Memory issues are generally auto-fixable
|
|
6333
|
+
category: storedIssue.category
|
|
6334
|
+
};
|
|
6335
|
+
pendingFixes.set(fix.id, fix);
|
|
6336
|
+
}
|
|
6337
|
+
} catch (error) {
|
|
6338
|
+
console.warn("Failed to load pending fixes from memory:", error);
|
|
6339
|
+
}
|
|
6340
|
+
}
|
|
6341
|
+
|
|
6342
|
+
// src/tools/cloud-fix.ts
|
|
6343
|
+
var TrieCloudFixTool = class {
|
|
6344
|
+
async execute(args) {
|
|
6345
|
+
const { action = "status" } = args || {};
|
|
6346
|
+
switch (action) {
|
|
6347
|
+
case "configure":
|
|
6348
|
+
return this.configure(args);
|
|
6349
|
+
case "dispatch":
|
|
6350
|
+
return this.dispatch(args);
|
|
6351
|
+
case "status":
|
|
6352
|
+
return this.status();
|
|
6353
|
+
case "artifacts":
|
|
6354
|
+
return this.artifacts(args);
|
|
6355
|
+
case "cancel":
|
|
6356
|
+
return this.cancel(args);
|
|
6357
|
+
default:
|
|
6358
|
+
return this.text(`Unknown action "${action}". Use: configure | dispatch | status | artifacts | cancel`);
|
|
6359
|
+
}
|
|
6360
|
+
}
|
|
6361
|
+
// --------------------------------------------------------------------------
|
|
6362
|
+
// configure
|
|
6363
|
+
// --------------------------------------------------------------------------
|
|
6364
|
+
async configure(args) {
|
|
6365
|
+
const apiKey = args?.apiKey || process.env.CURSOR_API_KEY;
|
|
6366
|
+
if (!apiKey) {
|
|
6367
|
+
return this.text(
|
|
6368
|
+
'Missing API key.\n\nUsage: trie_cloud_fix action:configure apiKey:"key-..."\nOr set: export CURSOR_API_KEY="key-..."'
|
|
6369
|
+
);
|
|
6370
|
+
}
|
|
6371
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
6372
|
+
await saveAutonomyConfig(workDir, { cloudAgentEnabled: true, cursorApiKey: apiKey });
|
|
6373
|
+
return this.text("Cursor API key saved. Cloud agent dispatch enabled.");
|
|
6374
|
+
}
|
|
6375
|
+
// --------------------------------------------------------------------------
|
|
6376
|
+
// dispatch
|
|
6377
|
+
// --------------------------------------------------------------------------
|
|
6378
|
+
async dispatch(args) {
|
|
6379
|
+
console.log("Cloud dispatch starting...");
|
|
6380
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
6381
|
+
const apiKey = await this.resolveApiKey(workDir);
|
|
6382
|
+
if (!apiKey) return this.setupGuard();
|
|
6383
|
+
const config = await loadAutonomyConfig(workDir);
|
|
6384
|
+
console.log("About to resolve issues...");
|
|
6385
|
+
const allIssues = await this.resolveIssues(args?.issueIds);
|
|
6386
|
+
console.log(`Resolved ${allIssues.length} issues`);
|
|
6387
|
+
if (allIssues.length === 0) {
|
|
6388
|
+
return this.text("No issues to dispatch. Run trie_scan to detect new issues, or check memory with trie_memory action:recent.");
|
|
6389
|
+
}
|
|
6390
|
+
const { results, summary } = triageIssues(allIssues, void 0, void 0, config);
|
|
6391
|
+
const lines = [];
|
|
6392
|
+
lines.push(formatTriageTable(results, allIssues));
|
|
6393
|
+
lines.push("");
|
|
6394
|
+
for (const issue of allIssues) {
|
|
6395
|
+
const r = results.get(issue.id);
|
|
6396
|
+
if (r && r.strategy !== "cloud-agent") {
|
|
6397
|
+
lines.push(`Skipped ${issue.id}: routed to ${r.strategy} (score ${r.score >= 0 ? "+" : ""}${r.score} \u2014 ${r.reasons.join(", ")})`);
|
|
6398
|
+
}
|
|
6399
|
+
}
|
|
6400
|
+
if (summary.cloudAgent.length === 0) {
|
|
6401
|
+
lines.push("\nNo issues qualify for cloud dispatch. Use trie_fix for local fixes.");
|
|
6402
|
+
return this.text(lines.join("\n"));
|
|
6403
|
+
}
|
|
6404
|
+
const client = new CursorCloudAgentClient(apiKey);
|
|
6405
|
+
const repoUrl = this.getRepoUrl(workDir);
|
|
6406
|
+
const branch = this.getBranch(workDir);
|
|
6407
|
+
const store = await this.loadJobs(workDir);
|
|
6408
|
+
lines.push("\nDISPATCHED");
|
|
6409
|
+
for (const issue of summary.cloudAgent) {
|
|
6410
|
+
const triageResult = results.get(issue.id);
|
|
6411
|
+
try {
|
|
6412
|
+
const job = await client.dispatch(issue, triageResult, repoUrl, branch);
|
|
6413
|
+
store.jobs[issue.id] = {
|
|
6414
|
+
issueId: issue.id,
|
|
6415
|
+
jobId: job.jobId,
|
|
6416
|
+
status: "dispatched",
|
|
6417
|
+
score: triageResult.score,
|
|
6418
|
+
strategy: "cloud-agent",
|
|
6419
|
+
dispatchedAt: job.dispatchedAt,
|
|
6420
|
+
artifactUrls: [],
|
|
6421
|
+
prUrl: null
|
|
6422
|
+
};
|
|
6423
|
+
lines.push(` ${issue.id} ${shortPath2(issue.file)}:${issue.line ?? "?"} job:${job.jobId} (score +${triageResult.score})`);
|
|
6424
|
+
} catch (err) {
|
|
6425
|
+
lines.push(` ${issue.id} FAILED: ${err.message}`);
|
|
6426
|
+
}
|
|
6427
|
+
}
|
|
6428
|
+
await this.saveJobs(workDir, store);
|
|
6429
|
+
lines.push("");
|
|
6430
|
+
lines.push("Cloud agents are running in isolated VMs. Check back with:");
|
|
6431
|
+
lines.push("trie_cloud_fix action:status");
|
|
6432
|
+
return this.text(lines.join("\n"));
|
|
6433
|
+
}
|
|
6434
|
+
// --------------------------------------------------------------------------
|
|
6435
|
+
// status
|
|
6436
|
+
// --------------------------------------------------------------------------
|
|
6437
|
+
async status() {
|
|
6438
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
6439
|
+
const apiKey = await this.resolveApiKey(workDir);
|
|
6440
|
+
if (!apiKey) return this.setupGuard();
|
|
6441
|
+
const store = await this.loadJobs(workDir);
|
|
6442
|
+
const entries = Object.values(store.jobs);
|
|
6443
|
+
if (entries.length === 0) {
|
|
6444
|
+
return this.text("No cloud jobs. Dispatch with: trie_cloud_fix action:dispatch");
|
|
6445
|
+
}
|
|
6446
|
+
const client = new CursorCloudAgentClient(apiKey);
|
|
6447
|
+
const LINE = "\u2500".repeat(68);
|
|
6448
|
+
const lines = ["JOB STATUS", LINE];
|
|
6449
|
+
lines.push(padEnd2("Issue", 32) + padEnd2("Status", 12) + padEnd2("PR", 28) + "Age");
|
|
6450
|
+
for (const job of entries) {
|
|
6451
|
+
if (job.status === "dispatched" || job.status === "running") {
|
|
6452
|
+
try {
|
|
6453
|
+
const poll = await client.poll(job.jobId);
|
|
6454
|
+
if (poll.status === "completed" && poll.prUrl) {
|
|
6455
|
+
job.status = "verified";
|
|
6456
|
+
job.prUrl = poll.prUrl;
|
|
6457
|
+
} else if (poll.status === "running") {
|
|
6458
|
+
job.status = "running";
|
|
6459
|
+
} else if (poll.status === "failed") {
|
|
6460
|
+
job.status = "failed";
|
|
6461
|
+
}
|
|
6462
|
+
if (poll.artifactUrls.length) {
|
|
6463
|
+
job.artifactUrls = poll.artifactUrls;
|
|
6464
|
+
}
|
|
6465
|
+
} catch {
|
|
6466
|
+
}
|
|
6467
|
+
}
|
|
6468
|
+
const age = formatAge(job.dispatchedAt);
|
|
6469
|
+
const pr = job.prUrl ?? "\u2014";
|
|
6470
|
+
lines.push(padEnd2(job.issueId, 32) + padEnd2(job.status, 12) + padEnd2(pr, 28) + age);
|
|
6471
|
+
}
|
|
6472
|
+
lines.push(LINE);
|
|
6473
|
+
await this.saveJobs(workDir, store);
|
|
6474
|
+
return this.text(lines.join("\n"));
|
|
6475
|
+
}
|
|
6476
|
+
// --------------------------------------------------------------------------
|
|
6477
|
+
// artifacts
|
|
6478
|
+
// --------------------------------------------------------------------------
|
|
6479
|
+
async artifacts(args) {
|
|
6480
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
6481
|
+
const apiKey = await this.resolveApiKey(workDir);
|
|
6482
|
+
if (!apiKey) return this.setupGuard();
|
|
6483
|
+
const store = await this.loadJobs(workDir);
|
|
6484
|
+
const jobId = args?.jobId;
|
|
6485
|
+
const targets = jobId ? Object.values(store.jobs).filter((j) => j.jobId === jobId) : Object.values(store.jobs).filter((j) => j.status === "verified");
|
|
6486
|
+
if (targets.length === 0) {
|
|
6487
|
+
return this.text(jobId ? `No job found with id ${jobId}` : "No completed jobs with artifacts.");
|
|
6488
|
+
}
|
|
6489
|
+
const client = new CursorCloudAgentClient(apiKey);
|
|
6490
|
+
const lines = [];
|
|
6491
|
+
for (const job of targets) {
|
|
6492
|
+
try {
|
|
6493
|
+
const artifacts = await client.getArtifacts(job.jobId);
|
|
6494
|
+
job.artifactUrls = artifacts;
|
|
6495
|
+
} catch {
|
|
6496
|
+
}
|
|
6497
|
+
lines.push(`Issue: ${job.issueId}`);
|
|
6498
|
+
lines.push(`PR: ${job.prUrl ?? "N/A"}`);
|
|
6499
|
+
for (const url of job.artifactUrls) {
|
|
6500
|
+
const ext = url.split(".").pop();
|
|
6501
|
+
const label = ext === "mp4" || ext === "webm" ? "Video" : "Screenshot";
|
|
6502
|
+
lines.push(`${label}: ${url}`);
|
|
6503
|
+
}
|
|
6504
|
+
if (job.status === "verified") {
|
|
6505
|
+
lines.push("Agent verified: all tests pass with this fix applied.");
|
|
6506
|
+
}
|
|
6507
|
+
lines.push("");
|
|
6508
|
+
}
|
|
6509
|
+
await this.saveJobs(workDir, store);
|
|
6510
|
+
return this.text(lines.join("\n"));
|
|
6511
|
+
}
|
|
6512
|
+
// --------------------------------------------------------------------------
|
|
6513
|
+
// cancel
|
|
6514
|
+
// --------------------------------------------------------------------------
|
|
6515
|
+
async cancel(args) {
|
|
6516
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
6517
|
+
const apiKey = await this.resolveApiKey(workDir);
|
|
6518
|
+
if (!apiKey) return this.setupGuard();
|
|
6519
|
+
const jobId = args?.jobId;
|
|
6520
|
+
if (!jobId) {
|
|
6521
|
+
return this.text('Provide jobId to cancel. Example: trie_cloud_fix action:cancel jobId:"cursor-task-xyz"');
|
|
6522
|
+
}
|
|
6523
|
+
const store = await this.loadJobs(workDir);
|
|
6524
|
+
const entry = Object.values(store.jobs).find((j) => j.jobId === jobId);
|
|
6525
|
+
if (!entry) {
|
|
6526
|
+
return this.text(`Job ${jobId} not found in cloud-jobs.json.`);
|
|
6527
|
+
}
|
|
6528
|
+
const client = new CursorCloudAgentClient(apiKey);
|
|
6529
|
+
try {
|
|
6530
|
+
await client.cancelJob(jobId);
|
|
6531
|
+
} catch (err) {
|
|
6532
|
+
return this.text(`Cancel failed: ${err.message}`);
|
|
6533
|
+
}
|
|
6534
|
+
delete store.jobs[entry.issueId];
|
|
6535
|
+
await this.saveJobs(workDir, store);
|
|
6536
|
+
return this.text(`Job ${jobId} cancelled and removed.`);
|
|
6537
|
+
}
|
|
6538
|
+
// --------------------------------------------------------------------------
|
|
6539
|
+
// Helpers
|
|
6540
|
+
// --------------------------------------------------------------------------
|
|
6541
|
+
async resolveApiKey(workDir) {
|
|
6542
|
+
if (process.env.CURSOR_API_KEY) return process.env.CURSOR_API_KEY;
|
|
6543
|
+
const fromEnv = this.loadCursorKeyFromEnv(workDir);
|
|
6544
|
+
if (fromEnv) return fromEnv;
|
|
6545
|
+
const config = await loadAutonomyConfig(workDir);
|
|
6546
|
+
return config.cursorApiKey ?? null;
|
|
6547
|
+
}
|
|
6548
|
+
loadCursorKeyFromEnv(workDir) {
|
|
6549
|
+
for (const envFile of [".env", ".env.local", ".env.production"]) {
|
|
6550
|
+
const envPath = join2(workDir, envFile);
|
|
6551
|
+
if (!existsSync4(envPath)) continue;
|
|
6552
|
+
try {
|
|
6553
|
+
const content = readFileSync(envPath, "utf-8");
|
|
6554
|
+
const match = content.match(/^\s*CURSOR_API_KEY\s*=\s*(.+)$/m);
|
|
6555
|
+
if (match?.[1]) {
|
|
6556
|
+
const key = match[1].trim().replace(/^["']|["']$/g, "");
|
|
6557
|
+
if (key.length > 10) return key;
|
|
6558
|
+
}
|
|
6559
|
+
} catch {
|
|
6560
|
+
}
|
|
6561
|
+
}
|
|
6562
|
+
return null;
|
|
6563
|
+
}
|
|
6564
|
+
setupGuard() {
|
|
6565
|
+
return this.text(
|
|
6566
|
+
'Cloud Agent dispatch requires a Cursor API key.\n\nGet your key at: cursor.com/settings \u2192 API Keys\n\nThen either:\n \u2022 trie_cloud_fix action:configure apiKey:"key-..."\n \u2022 Or add to Config \u2192 API Keys \u2192 Cursor (press s in watch mode)\n \u2022 Or set: export CURSOR_API_KEY="key-..."\n \u2022 Or add CURSOR_API_KEY=... to your .env file'
|
|
6567
|
+
);
|
|
6568
|
+
}
|
|
6569
|
+
async resolveIssues(issueIds) {
|
|
6570
|
+
let pending = getPendingFixes();
|
|
6571
|
+
if (pending.length === 0) {
|
|
6572
|
+
try {
|
|
6573
|
+
console.log("Loading issues from memory...");
|
|
6574
|
+
const { getRecentIssues } = await import("./issue-store-LZWZIGM7.js");
|
|
6575
|
+
const recentIssues = await getRecentIssues({ limit: 50, includeResolved: false });
|
|
6576
|
+
console.log(`Found ${recentIssues.length} recent issues in memory`);
|
|
6577
|
+
const memoryIssues = recentIssues.map((storedIssue) => ({
|
|
6578
|
+
id: storedIssue.id,
|
|
6579
|
+
severity: storedIssue.severity || "moderate",
|
|
6580
|
+
issue: storedIssue.issue,
|
|
6581
|
+
fix: storedIssue.fix,
|
|
6582
|
+
file: storedIssue.file,
|
|
6583
|
+
line: storedIssue.line,
|
|
6584
|
+
confidence: 0.8,
|
|
6585
|
+
// Default confidence for memory issues
|
|
6586
|
+
autoFixable: true,
|
|
6587
|
+
// Memory issues are generally auto-fixable
|
|
6588
|
+
agent: storedIssue.agent,
|
|
6589
|
+
category: storedIssue.category
|
|
6590
|
+
}));
|
|
6591
|
+
console.log(`Converted ${memoryIssues.length} memory issues for cloud dispatch`);
|
|
6592
|
+
if (issueIds && issueIds.length > 0) {
|
|
6593
|
+
return memoryIssues.filter((i) => issueIds.includes(i.id));
|
|
6594
|
+
}
|
|
6595
|
+
return memoryIssues;
|
|
6596
|
+
} catch (error) {
|
|
6597
|
+
console.warn("Failed to load issues from memory:", error);
|
|
6598
|
+
console.warn("Error details:", error);
|
|
6599
|
+
}
|
|
6600
|
+
}
|
|
6601
|
+
const issues = pending.map((p) => ({
|
|
6602
|
+
id: p.id,
|
|
6603
|
+
severity: p.severity ?? "moderate",
|
|
6604
|
+
effort: p.effort,
|
|
6605
|
+
issue: p.issue,
|
|
6606
|
+
fix: p.suggestedFix,
|
|
6607
|
+
file: p.file,
|
|
6608
|
+
line: p.line,
|
|
6609
|
+
confidence: p.confidence,
|
|
6610
|
+
autoFixable: p.autoFixable ?? false,
|
|
6611
|
+
agent: "trie_scan",
|
|
6612
|
+
cwe: p.cwe,
|
|
6613
|
+
owasp: p.owasp,
|
|
6614
|
+
category: p.category
|
|
6615
|
+
}));
|
|
6616
|
+
if (issueIds && issueIds.length > 0) {
|
|
6617
|
+
return issues.filter((i) => issueIds.includes(i.id));
|
|
6618
|
+
}
|
|
6619
|
+
return issues;
|
|
6620
|
+
}
|
|
6621
|
+
getRepoUrl(workDir) {
|
|
6622
|
+
try {
|
|
6623
|
+
return execSync("git remote get-url origin", { cwd: workDir, encoding: "utf-8" }).trim();
|
|
6624
|
+
} catch {
|
|
6625
|
+
return "unknown";
|
|
6626
|
+
}
|
|
6627
|
+
}
|
|
6628
|
+
getBranch(workDir) {
|
|
6629
|
+
try {
|
|
6630
|
+
return execSync("git rev-parse --abbrev-ref HEAD", { cwd: workDir, encoding: "utf-8" }).trim();
|
|
6631
|
+
} catch {
|
|
6632
|
+
return "main";
|
|
6633
|
+
}
|
|
6634
|
+
}
|
|
6635
|
+
async loadJobs(workDir) {
|
|
6636
|
+
const path2 = join2(getTrieDirectory(workDir), "cloud-jobs.json");
|
|
6637
|
+
try {
|
|
6638
|
+
if (existsSync4(path2)) {
|
|
6639
|
+
const raw = await readFile3(path2, "utf-8");
|
|
6640
|
+
return JSON.parse(raw);
|
|
6641
|
+
}
|
|
6642
|
+
} catch {
|
|
6643
|
+
}
|
|
6644
|
+
return { jobs: {} };
|
|
6645
|
+
}
|
|
6646
|
+
async saveJobs(workDir, store) {
|
|
6647
|
+
const trieDir = getTrieDirectory(workDir);
|
|
6648
|
+
if (!existsSync4(trieDir)) await mkdir(trieDir, { recursive: true });
|
|
6649
|
+
const path2 = join2(trieDir, "cloud-jobs.json");
|
|
6650
|
+
await writeFile(path2, JSON.stringify(store, null, 2));
|
|
6651
|
+
}
|
|
6652
|
+
text(msg) {
|
|
6653
|
+
return { content: [{ type: "text", text: msg }] };
|
|
6654
|
+
}
|
|
6655
|
+
};
|
|
6656
|
+
function formatAge(isoDate) {
|
|
6657
|
+
const ms = Date.now() - new Date(isoDate).getTime();
|
|
6658
|
+
const mins = Math.floor(ms / 6e4);
|
|
6659
|
+
if (mins < 60) return `${mins}m`;
|
|
6660
|
+
const hrs = Math.floor(mins / 60);
|
|
6661
|
+
if (hrs < 24) return `${hrs}h ${mins % 60}m`;
|
|
6662
|
+
return `${Math.floor(hrs / 24)}d`;
|
|
6663
|
+
}
|
|
6664
|
+
function shortPath2(file) {
|
|
6665
|
+
const parts = file.split("/");
|
|
6666
|
+
return parts.length > 2 ? parts.slice(-2).join("/") : file;
|
|
6667
|
+
}
|
|
6668
|
+
function padEnd2(str, len) {
|
|
6669
|
+
if (str.length >= len) return str.slice(0, len);
|
|
6670
|
+
return str + " ".repeat(len - str.length);
|
|
6671
|
+
}
|
|
6672
|
+
|
|
5516
6673
|
// src/tools/query-tools.ts
|
|
5517
6674
|
var TrieGetGovernanceTool = class {
|
|
5518
6675
|
async execute(input) {
|
|
@@ -5946,7 +7103,7 @@ var TriePipelineTool = class {
|
|
|
5946
7103
|
);
|
|
5947
7104
|
const prInfo = linkedPR ? `PR #${linkedPR.data.prNumber} (CI: ${linkedPR.data.ciStatus})` : "No PR yet";
|
|
5948
7105
|
lines.push(
|
|
5949
|
-
` ${
|
|
7106
|
+
` ${padEnd3(t.data.ticketId, 10)}${padEnd3(truncate2(t.data.title, 35), 37)}${padEnd3(t.data.priority, 9)}${prInfo}`
|
|
5950
7107
|
);
|
|
5951
7108
|
}
|
|
5952
7109
|
lines.push("");
|
|
@@ -5963,7 +7120,7 @@ var TriePipelineTool = class {
|
|
|
5963
7120
|
const stateTag = pr.data.isDraft ? "draft" : pr.data.state;
|
|
5964
7121
|
const ticketRef = pr.data.linkedTicketIds.length > 0 ? ` (${pr.data.linkedTicketIds.join(", ")})` : "";
|
|
5965
7122
|
lines.push(
|
|
5966
|
-
` #${
|
|
7123
|
+
` #${padEnd3(String(pr.data.prNumber), 5)}${padEnd3(truncate2(pr.data.title, 30) + ticketRef, 42)}${padEnd3(stateTag, 10)}CI: ${padEnd3(pr.data.ciStatus, 10)}Review: ${pr.data.reviewStatus}`
|
|
5967
7124
|
);
|
|
5968
7125
|
}
|
|
5969
7126
|
lines.push("");
|
|
@@ -5977,7 +7134,7 @@ var TriePipelineTool = class {
|
|
|
5977
7134
|
const labels = issue.data.labels.length > 0 ? `[${issue.data.labels.join(", ")}]` : "";
|
|
5978
7135
|
const linked = issue.data.linkedTicketIds.length > 0 ? `linked: ${issue.data.linkedTicketIds.join(", ")}` : "no ticket";
|
|
5979
7136
|
lines.push(
|
|
5980
|
-
` #${
|
|
7137
|
+
` #${padEnd3(String(issue.data.issueNumber), 5)}${padEnd3(truncate2(issue.data.title, 35), 37)}${padEnd3(labels, 18)}${linked}`
|
|
5981
7138
|
);
|
|
5982
7139
|
}
|
|
5983
7140
|
if (ghIssues.length > 10) {
|
|
@@ -5990,8 +7147,8 @@ var TriePipelineTool = class {
|
|
|
5990
7147
|
if (uncoveredIssues.length > 0) {
|
|
5991
7148
|
lines.push(`COVERAGE GAPS (${uncoveredIssues.length} Trie issue${uncoveredIssues.length !== 1 ? "s" : ""} with no ticket or PR)`);
|
|
5992
7149
|
for (const issue of uncoveredIssues.slice(0, 10)) {
|
|
5993
|
-
const loc = `${
|
|
5994
|
-
lines.push(` ${
|
|
7150
|
+
const loc = `${shortPath3(issue.data.file)}:${issue.data.line ?? "?"}`;
|
|
7151
|
+
lines.push(` ${padEnd3(loc, 32)}${padEnd3(issue.data.severity, 10)}${truncate2(issue.data.issueText, 40)}`);
|
|
5995
7152
|
}
|
|
5996
7153
|
if (uncoveredIssues.length > 10) {
|
|
5997
7154
|
lines.push(` ... and ${uncoveredIssues.length - 10} more`);
|
|
@@ -6117,8 +7274,8 @@ var TriePipelineTool = class {
|
|
|
6117
7274
|
if (group.items.length === 0) continue;
|
|
6118
7275
|
lines.push(`${group.label} (${group.items.length}):`);
|
|
6119
7276
|
for (const issue of group.items.slice(0, 10)) {
|
|
6120
|
-
const loc = `${
|
|
6121
|
-
lines.push(` ${
|
|
7277
|
+
const loc = `${shortPath3(issue.data.file)}:${issue.data.line ?? "?"}`;
|
|
7278
|
+
lines.push(` ${padEnd3(loc, 32)}${truncate2(issue.data.issueText, 45)}`);
|
|
6122
7279
|
}
|
|
6123
7280
|
if (group.items.length > 10) {
|
|
6124
7281
|
lines.push(` ... and ${group.items.length - 10} more`);
|
|
@@ -6291,11 +7448,11 @@ var TriePipelineTool = class {
|
|
|
6291
7448
|
return parts.join("\n");
|
|
6292
7449
|
}
|
|
6293
7450
|
};
|
|
6294
|
-
function
|
|
7451
|
+
function shortPath3(file) {
|
|
6295
7452
|
const parts = file.split("/");
|
|
6296
7453
|
return parts.length > 2 ? parts.slice(-2).join("/") : file;
|
|
6297
7454
|
}
|
|
6298
|
-
function
|
|
7455
|
+
function padEnd3(str, len) {
|
|
6299
7456
|
if (str.length >= len) return str.slice(0, len);
|
|
6300
7457
|
return str + " ".repeat(len - str.length);
|
|
6301
7458
|
}
|
|
@@ -6305,7 +7462,7 @@ function truncate2(str, maxLen) {
|
|
|
6305
7462
|
}
|
|
6306
7463
|
|
|
6307
7464
|
// src/tools/github-branches.ts
|
|
6308
|
-
import { execSync } from "child_process";
|
|
7465
|
+
import { execSync as execSync2 } from "child_process";
|
|
6309
7466
|
var GitHubBranchesTool = class {
|
|
6310
7467
|
async execute(input) {
|
|
6311
7468
|
const workDir = input.directory || getWorkingDirectory(void 0, true);
|
|
@@ -6375,7 +7532,7 @@ Check token validity and repo access. Token needs \`repo\` or \`public_repo\` sc
|
|
|
6375
7532
|
}
|
|
6376
7533
|
getRepoInfo(projectPath) {
|
|
6377
7534
|
try {
|
|
6378
|
-
const url =
|
|
7535
|
+
const url = execSync2("git remote get-url origin", {
|
|
6379
7536
|
cwd: projectPath,
|
|
6380
7537
|
encoding: "utf-8",
|
|
6381
7538
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -6719,6 +7876,33 @@ var CHAT_TOOLS = [
|
|
|
6719
7876
|
},
|
|
6720
7877
|
required: ["hypothesisId"]
|
|
6721
7878
|
}
|
|
7879
|
+
},
|
|
7880
|
+
{
|
|
7881
|
+
name: "trie_cloud_fix",
|
|
7882
|
+
description: 'Dispatch issues to Cursor Cloud agents for verified, test-passing fixes. Use when the user asks to "spin up Cursor Cloud agent", "dispatch to cloud", or "run cloud fix". Requires CURSOR_API_KEY (set in Config \u2192 API Keys \u2192 Cursor, or export CURSOR_API_KEY). Actions: configure (save API key), dispatch (send issues to cloud), status (check job status), artifacts (get PR/screenshots).',
|
|
7883
|
+
input_schema: {
|
|
7884
|
+
type: "object",
|
|
7885
|
+
properties: {
|
|
7886
|
+
action: {
|
|
7887
|
+
type: "string",
|
|
7888
|
+
enum: ["configure", "dispatch", "status", "artifacts", "cancel"],
|
|
7889
|
+
description: "Action: configure (save apiKey), dispatch (send issues), status (check jobs), artifacts (get results), cancel (abort job)"
|
|
7890
|
+
},
|
|
7891
|
+
apiKey: {
|
|
7892
|
+
type: "string",
|
|
7893
|
+
description: "Cursor API key (only for action:configure). Get at cursor.com/settings \u2192 API Keys."
|
|
7894
|
+
},
|
|
7895
|
+
issueIds: {
|
|
7896
|
+
type: "array",
|
|
7897
|
+
items: { type: "string" },
|
|
7898
|
+
description: "Issue IDs to dispatch (for action:dispatch). Omit to dispatch all cloud-eligible issues."
|
|
7899
|
+
},
|
|
7900
|
+
jobId: {
|
|
7901
|
+
type: "string",
|
|
7902
|
+
description: "Job ID for action:artifacts or action:cancel"
|
|
7903
|
+
}
|
|
7904
|
+
}
|
|
7905
|
+
}
|
|
6722
7906
|
}
|
|
6723
7907
|
];
|
|
6724
7908
|
async function executeTool(name, input, onProgress) {
|
|
@@ -7076,6 +8260,17 @@ ${truncated}`;
|
|
|
7076
8260
|
return `Scan failed: ${error.message}`;
|
|
7077
8261
|
}
|
|
7078
8262
|
}
|
|
8263
|
+
case "trie_cloud_fix": {
|
|
8264
|
+
const cloudFixTool = new TrieCloudFixTool();
|
|
8265
|
+
const cloudFixArgs = {
|
|
8266
|
+
action: input.action || "status"
|
|
8267
|
+
};
|
|
8268
|
+
if (input.apiKey) cloudFixArgs.apiKey = String(input.apiKey);
|
|
8269
|
+
if (Array.isArray(input.issueIds)) cloudFixArgs.issueIds = input.issueIds;
|
|
8270
|
+
if (input.jobId) cloudFixArgs.jobId = String(input.jobId);
|
|
8271
|
+
const result = await cloudFixTool.execute(cloudFixArgs);
|
|
8272
|
+
return textFromResult(result);
|
|
8273
|
+
}
|
|
7079
8274
|
case "trie_test_hypothesis": {
|
|
7080
8275
|
const hypothesisId = String(input.hypothesisId || "").trim();
|
|
7081
8276
|
if (!hypothesisId) {
|
|
@@ -7313,12 +8508,19 @@ var SYSTEM_PROMPT = `You are Trie, a code assistant embedded in a terminal TUI.
|
|
|
7313
8508
|
5. AFTER the tool call completes, the system will ask for user confirmation - do NOT add your own confirmation message
|
|
7314
8509
|
6. When the user says "yes", "yes to all", or "no", the system handles spawning Claude Code automatically
|
|
7315
8510
|
|
|
8511
|
+
**When user asks to "spin up Cursor Cloud agent", "dispatch to cloud", or "run cloud fix":**
|
|
8512
|
+
- You HAVE trie_cloud_fix \u2014 use it! Call trie_cloud_fix with action:dispatch to send issues to Cursor Cloud agents.
|
|
8513
|
+
- If the tool returns "Cloud Agent dispatch requires a Cursor API key", tell the user: "Your Cursor API key isn't configured. Press 's' to open Config \u2192 API Keys \u2192 Cursor and add it, or run: trie_cloud_fix action:configure apiKey:"your-key""
|
|
8514
|
+
- For status: trie_cloud_fix action:status
|
|
8515
|
+
- The key must be in Config (API Keys \u2192 Cursor) or set as CURSOR_API_KEY env var
|
|
8516
|
+
|
|
7316
8517
|
Examples:
|
|
7317
8518
|
- User: "do we have emojis?" \u2192 Check nudges first. If none or unclear: Call trie_scan_for_goal_violations to scan the codebase.
|
|
7318
8519
|
- User: "run a full scan for emojis" \u2192 Call trie_scan_for_goal_violations directly.
|
|
7319
8520
|
- User: "fix the emoji violations" \u2192 Find ALL emoji violations in nudges, call trie_propose_fixes_batch ONCE with all fixes, then STOP
|
|
7320
8521
|
- User responds "yes to all" after proposal \u2192 Just say "Spawning Claude Code to fix all files..." The system handles it.
|
|
7321
8522
|
- User: "search for TODO comments" \u2192 If there's a goal about TODOs, use trie_scan_for_goal_violations. Otherwise explain no such goal exists.
|
|
8523
|
+
- User: "spin up Cursor Cloud agent" or "check our hypotheses with cloud" \u2192 Call trie_cloud_fix action:dispatch (or status first to see existing jobs).
|
|
7322
8524
|
|
|
7323
8525
|
Answer concisely. Reference specific files, decisions, and patterns when relevant.`;
|
|
7324
8526
|
function ChatView() {
|
|
@@ -7502,14 +8704,14 @@ ${contextBlock}`;
|
|
|
7502
8704
|
content: result.content
|
|
7503
8705
|
};
|
|
7504
8706
|
const pendingFixRegex = /\[PENDING_FIX:(.+?)\]/g;
|
|
7505
|
-
const
|
|
8707
|
+
const pendingFixes2 = [];
|
|
7506
8708
|
let match;
|
|
7507
8709
|
while ((match = pendingFixRegex.exec(result.content)) !== null) {
|
|
7508
8710
|
try {
|
|
7509
8711
|
const jsonStr = match[1];
|
|
7510
8712
|
if (jsonStr) {
|
|
7511
8713
|
const fixData = JSON.parse(jsonStr);
|
|
7512
|
-
|
|
8714
|
+
pendingFixes2.push(fixData);
|
|
7513
8715
|
}
|
|
7514
8716
|
} catch {
|
|
7515
8717
|
}
|
|
@@ -7523,8 +8725,8 @@ ${contextBlock}`;
|
|
|
7523
8725
|
const jsonStr = toolMatch[1];
|
|
7524
8726
|
if (jsonStr) {
|
|
7525
8727
|
const fixData = JSON.parse(jsonStr);
|
|
7526
|
-
if (!
|
|
7527
|
-
|
|
8728
|
+
if (!pendingFixes2.some((f) => f.file === fixData.file)) {
|
|
8729
|
+
pendingFixes2.push(fixData);
|
|
7528
8730
|
}
|
|
7529
8731
|
}
|
|
7530
8732
|
} catch {
|
|
@@ -7532,13 +8734,13 @@ ${contextBlock}`;
|
|
|
7532
8734
|
}
|
|
7533
8735
|
}
|
|
7534
8736
|
}
|
|
7535
|
-
if (
|
|
8737
|
+
if (pendingFixes2.length > 0) {
|
|
7536
8738
|
action.content = result.content.replace(/\[PENDING_FIX:.+?\]/g, "").trim();
|
|
7537
|
-
const firstFix =
|
|
7538
|
-
if (
|
|
8739
|
+
const firstFix = pendingFixes2[0];
|
|
8740
|
+
if (pendingFixes2.length === 1 && firstFix) {
|
|
7539
8741
|
action.pendingFix = firstFix;
|
|
7540
8742
|
} else {
|
|
7541
|
-
action.pendingFixes =
|
|
8743
|
+
action.pendingFixes = pendingFixes2;
|
|
7542
8744
|
}
|
|
7543
8745
|
}
|
|
7544
8746
|
if (result.toolCalls && result.toolCalls.length > 0) {
|
|
@@ -7986,8 +9188,8 @@ async function applyGoalFix(fix, dispatch) {
|
|
|
7986
9188
|
return;
|
|
7987
9189
|
}
|
|
7988
9190
|
const projectPath = getWorkingDirectory(void 0, true);
|
|
7989
|
-
const fullPath =
|
|
7990
|
-
const content = await
|
|
9191
|
+
const fullPath = join3(projectPath, fix.file);
|
|
9192
|
+
const content = await readFile4(fullPath, "utf-8");
|
|
7991
9193
|
const result = await runAIAnalysis({
|
|
7992
9194
|
systemPrompt: `You are a precise code fixer. The user has a quality goal: "${fix.goalDescription}".
|
|
7993
9195
|
A violation was detected: "${fix.description}"
|
|
@@ -8013,7 +9215,7 @@ ${content}
|
|
|
8013
9215
|
if (fixedContent.startsWith("```")) {
|
|
8014
9216
|
fixedContent = fixedContent.replace(/^```\w*\n?/, "").replace(/\n?```$/, "");
|
|
8015
9217
|
}
|
|
8016
|
-
await
|
|
9218
|
+
await writeFile2(fullPath, fixedContent, "utf-8");
|
|
8017
9219
|
const { recordGoalViolationFixed, getActiveGoals } = await import("./goal-validator-DA3JQ6JN.js");
|
|
8018
9220
|
const goals = await getActiveGoals(projectPath);
|
|
8019
9221
|
const matchedGoal = goals.find((g) => g.description === fix.goalDescription);
|
|
@@ -8037,11 +9239,11 @@ function DashboardApp({ onReady }) {
|
|
|
8037
9239
|
dispatchRef.current = dispatch;
|
|
8038
9240
|
const stateRef = useRef4(state);
|
|
8039
9241
|
stateRef.current = state;
|
|
8040
|
-
const configPath =
|
|
9242
|
+
const configPath = join3(getTrieDirectory(getWorkingDirectory(void 0, true)), "agent.json");
|
|
8041
9243
|
const loadConfig2 = useCallback7(async () => {
|
|
8042
|
-
if (!
|
|
9244
|
+
if (!existsSync5(configPath)) return;
|
|
8043
9245
|
try {
|
|
8044
|
-
const raw = await
|
|
9246
|
+
const raw = await readFile4(configPath, "utf-8");
|
|
8045
9247
|
const parsed = JSON.parse(raw);
|
|
8046
9248
|
dispatchRef.current({ type: "SET_AGENT_CONFIG", config: parsed });
|
|
8047
9249
|
} catch {
|
|
@@ -8050,8 +9252,8 @@ function DashboardApp({ onReady }) {
|
|
|
8050
9252
|
const persistConfig = useCallback7(async () => {
|
|
8051
9253
|
try {
|
|
8052
9254
|
const workDir = getWorkingDirectory(void 0, true);
|
|
8053
|
-
await
|
|
8054
|
-
await
|
|
9255
|
+
await mkdir2(getTrieDirectory(workDir), { recursive: true });
|
|
9256
|
+
await writeFile2(configPath, JSON.stringify(stateRef.current.agentConfig, null, 2), "utf-8");
|
|
8055
9257
|
const { saveAutonomyConfig: saveAutonomyConfig2, loadAutonomyConfig: loadAutonomyConfig2 } = await import("./autonomy-config-ZCOSTMPD.js");
|
|
8056
9258
|
const autonomy = await loadAutonomyConfig2(workDir);
|
|
8057
9259
|
autonomy.aiWatcher = stateRef.current.agentConfig.aiWatcher;
|
|
@@ -8382,6 +9584,8 @@ export {
|
|
|
8382
9584
|
TrieScanTool,
|
|
8383
9585
|
getPrompt,
|
|
8384
9586
|
getSystemPrompt,
|
|
9587
|
+
TrieFixTool,
|
|
9588
|
+
TrieCloudFixTool,
|
|
8385
9589
|
TrieExplainTool,
|
|
8386
9590
|
StreamingManager,
|
|
8387
9591
|
ExtractionPipeline,
|
|
@@ -8399,4 +9603,4 @@ export {
|
|
|
8399
9603
|
GitHubBranchesTool,
|
|
8400
9604
|
InteractiveDashboard
|
|
8401
9605
|
};
|
|
8402
|
-
//# sourceMappingURL=chunk-
|
|
9606
|
+
//# sourceMappingURL=chunk-AIJF67CF.js.map
|