@triedotdev/mcp 1.0.142 → 1.0.144
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/dist/{chunk-Y2LYDCJD.js → chunk-5FQTNPBP.js} +1296 -37
- package/dist/chunk-5FQTNPBP.js.map +1 -0
- package/dist/cli/yolo-daemon.js +1 -1
- package/dist/index.js +103 -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,1191 @@ 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
|
+
const forceCloud = args?.forceCloud === true;
|
|
6385
|
+
console.log("About to resolve issues...");
|
|
6386
|
+
let allIssues = await this.resolveIssues(args?.issueIds);
|
|
6387
|
+
if (allIssues.length === 0 && args?.file && args?.issue && args?.fix) {
|
|
6388
|
+
const adHocId = `ad-hoc-${Date.now()}`;
|
|
6389
|
+
allIssues = [{
|
|
6390
|
+
id: adHocId,
|
|
6391
|
+
severity: args.severity ?? "critical",
|
|
6392
|
+
effort: args.effort ?? "hard",
|
|
6393
|
+
issue: args.issue,
|
|
6394
|
+
fix: args.fix,
|
|
6395
|
+
file: args.file,
|
|
6396
|
+
line: args.line ?? 1,
|
|
6397
|
+
confidence: 0.9,
|
|
6398
|
+
autoFixable: false,
|
|
6399
|
+
agent: "user",
|
|
6400
|
+
category: args.category
|
|
6401
|
+
}];
|
|
6402
|
+
console.log(`Created ad-hoc issue ${adHocId} for cloud dispatch`);
|
|
6403
|
+
}
|
|
6404
|
+
if (allIssues.length === 0) {
|
|
6405
|
+
return this.text("No issues to dispatch. Run trie_scan to detect new issues, or check memory with trie_memory action:recent. For ad-hoc fixes, pass file, issue, and fix.");
|
|
6406
|
+
}
|
|
6407
|
+
const { results, summary } = triageIssues(allIssues, void 0, void 0, config);
|
|
6408
|
+
const lines = [];
|
|
6409
|
+
const toDispatch = forceCloud ? allIssues : summary.cloudAgent;
|
|
6410
|
+
if (forceCloud) {
|
|
6411
|
+
lines.push("FIX ROUTING PLAN (forceCloud: bypassing triage)");
|
|
6412
|
+
lines.push("\u2500".repeat(68));
|
|
6413
|
+
for (const issue of allIssues) {
|
|
6414
|
+
lines.push(` ${shortPath2(issue.file)}:${issue.line ?? "?"} \u2192 cloud-agent (user requested)`);
|
|
6415
|
+
}
|
|
6416
|
+
lines.push("");
|
|
6417
|
+
} else {
|
|
6418
|
+
lines.push(formatTriageTable(results, allIssues));
|
|
6419
|
+
lines.push("");
|
|
6420
|
+
for (const issue of allIssues) {
|
|
6421
|
+
const r = results.get(issue.id);
|
|
6422
|
+
if (r && r.strategy !== "cloud-agent") {
|
|
6423
|
+
lines.push(`Skipped ${issue.id}: routed to ${r.strategy} (score ${r.score >= 0 ? "+" : ""}${r.score} \u2014 ${r.reasons.join(", ")})`);
|
|
6424
|
+
}
|
|
6425
|
+
}
|
|
6426
|
+
if (summary.cloudAgent.length === 0) {
|
|
6427
|
+
lines.push("\nNo issues qualify for cloud dispatch. Use trie_fix for local fixes.");
|
|
6428
|
+
lines.push("To force cloud dispatch: trie_cloud_fix action:dispatch forceCloud:true");
|
|
6429
|
+
return this.text(lines.join("\n"));
|
|
6430
|
+
}
|
|
6431
|
+
}
|
|
6432
|
+
const client = new CursorCloudAgentClient(apiKey);
|
|
6433
|
+
const repoUrl = this.getRepoUrl(workDir);
|
|
6434
|
+
const branch = this.getBranch(workDir);
|
|
6435
|
+
const store = await this.loadJobs(workDir);
|
|
6436
|
+
lines.push("\nDISPATCHED");
|
|
6437
|
+
for (const issue of toDispatch) {
|
|
6438
|
+
const triageResult = forceCloud ? { strategy: "cloud-agent", score: 6, confidence: 1, reasons: ["forceCloud: user requested cloud dispatch"], fallback: "local-ai" } : results.get(issue.id);
|
|
6439
|
+
try {
|
|
6440
|
+
const job = await client.dispatch(issue, triageResult, repoUrl, branch);
|
|
6441
|
+
store.jobs[issue.id] = {
|
|
6442
|
+
issueId: issue.id,
|
|
6443
|
+
jobId: job.jobId,
|
|
6444
|
+
status: "dispatched",
|
|
6445
|
+
score: triageResult.score,
|
|
6446
|
+
strategy: "cloud-agent",
|
|
6447
|
+
dispatchedAt: job.dispatchedAt,
|
|
6448
|
+
artifactUrls: [],
|
|
6449
|
+
prUrl: null
|
|
6450
|
+
};
|
|
6451
|
+
lines.push(` ${issue.id} ${shortPath2(issue.file)}:${issue.line ?? "?"} job:${job.jobId} (score +${triageResult.score})`);
|
|
6452
|
+
} catch (err) {
|
|
6453
|
+
lines.push(` ${issue.id} FAILED: ${err.message}`);
|
|
6454
|
+
}
|
|
6455
|
+
}
|
|
6456
|
+
await this.saveJobs(workDir, store);
|
|
6457
|
+
lines.push("");
|
|
6458
|
+
lines.push("Cloud agents are running in isolated VMs. Check back with:");
|
|
6459
|
+
lines.push("trie_cloud_fix action:status");
|
|
6460
|
+
return this.text(lines.join("\n"));
|
|
6461
|
+
}
|
|
6462
|
+
// --------------------------------------------------------------------------
|
|
6463
|
+
// status
|
|
6464
|
+
// --------------------------------------------------------------------------
|
|
6465
|
+
async status() {
|
|
6466
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
6467
|
+
const apiKey = await this.resolveApiKey(workDir);
|
|
6468
|
+
if (!apiKey) return this.setupGuard();
|
|
6469
|
+
const store = await this.loadJobs(workDir);
|
|
6470
|
+
const entries = Object.values(store.jobs);
|
|
6471
|
+
if (entries.length === 0) {
|
|
6472
|
+
return this.text("No cloud jobs. Dispatch with: trie_cloud_fix action:dispatch");
|
|
6473
|
+
}
|
|
6474
|
+
const client = new CursorCloudAgentClient(apiKey);
|
|
6475
|
+
const LINE = "\u2500".repeat(68);
|
|
6476
|
+
const lines = ["JOB STATUS", LINE];
|
|
6477
|
+
lines.push(padEnd2("Issue", 32) + padEnd2("Status", 12) + padEnd2("PR", 28) + "Age");
|
|
6478
|
+
for (const job of entries) {
|
|
6479
|
+
if (job.status === "dispatched" || job.status === "running") {
|
|
6480
|
+
try {
|
|
6481
|
+
const poll = await client.poll(job.jobId);
|
|
6482
|
+
if (poll.status === "completed" && poll.prUrl) {
|
|
6483
|
+
job.status = "verified";
|
|
6484
|
+
job.prUrl = poll.prUrl;
|
|
6485
|
+
} else if (poll.status === "running") {
|
|
6486
|
+
job.status = "running";
|
|
6487
|
+
} else if (poll.status === "failed") {
|
|
6488
|
+
job.status = "failed";
|
|
6489
|
+
}
|
|
6490
|
+
if (poll.artifactUrls.length) {
|
|
6491
|
+
job.artifactUrls = poll.artifactUrls;
|
|
6492
|
+
}
|
|
6493
|
+
} catch {
|
|
6494
|
+
}
|
|
6495
|
+
}
|
|
6496
|
+
const age = formatAge(job.dispatchedAt);
|
|
6497
|
+
const pr = job.prUrl ?? "\u2014";
|
|
6498
|
+
lines.push(padEnd2(job.issueId, 32) + padEnd2(job.status, 12) + padEnd2(pr, 28) + age);
|
|
6499
|
+
}
|
|
6500
|
+
lines.push(LINE);
|
|
6501
|
+
await this.saveJobs(workDir, store);
|
|
6502
|
+
return this.text(lines.join("\n"));
|
|
6503
|
+
}
|
|
6504
|
+
// --------------------------------------------------------------------------
|
|
6505
|
+
// artifacts
|
|
6506
|
+
// --------------------------------------------------------------------------
|
|
6507
|
+
async artifacts(args) {
|
|
6508
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
6509
|
+
const apiKey = await this.resolveApiKey(workDir);
|
|
6510
|
+
if (!apiKey) return this.setupGuard();
|
|
6511
|
+
const store = await this.loadJobs(workDir);
|
|
6512
|
+
const jobId = args?.jobId;
|
|
6513
|
+
const targets = jobId ? Object.values(store.jobs).filter((j) => j.jobId === jobId) : Object.values(store.jobs).filter((j) => j.status === "verified");
|
|
6514
|
+
if (targets.length === 0) {
|
|
6515
|
+
return this.text(jobId ? `No job found with id ${jobId}` : "No completed jobs with artifacts.");
|
|
6516
|
+
}
|
|
6517
|
+
const client = new CursorCloudAgentClient(apiKey);
|
|
6518
|
+
const lines = [];
|
|
6519
|
+
for (const job of targets) {
|
|
6520
|
+
try {
|
|
6521
|
+
const artifacts = await client.getArtifacts(job.jobId);
|
|
6522
|
+
job.artifactUrls = artifacts;
|
|
6523
|
+
} catch {
|
|
6524
|
+
}
|
|
6525
|
+
lines.push(`Issue: ${job.issueId}`);
|
|
6526
|
+
lines.push(`PR: ${job.prUrl ?? "N/A"}`);
|
|
6527
|
+
for (const url of job.artifactUrls) {
|
|
6528
|
+
const ext = url.split(".").pop();
|
|
6529
|
+
const label = ext === "mp4" || ext === "webm" ? "Video" : "Screenshot";
|
|
6530
|
+
lines.push(`${label}: ${url}`);
|
|
6531
|
+
}
|
|
6532
|
+
if (job.status === "verified") {
|
|
6533
|
+
lines.push("Agent verified: all tests pass with this fix applied.");
|
|
6534
|
+
}
|
|
6535
|
+
lines.push("");
|
|
6536
|
+
}
|
|
6537
|
+
await this.saveJobs(workDir, store);
|
|
6538
|
+
return this.text(lines.join("\n"));
|
|
6539
|
+
}
|
|
6540
|
+
// --------------------------------------------------------------------------
|
|
6541
|
+
// cancel
|
|
6542
|
+
// --------------------------------------------------------------------------
|
|
6543
|
+
async cancel(args) {
|
|
6544
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
6545
|
+
const apiKey = await this.resolveApiKey(workDir);
|
|
6546
|
+
if (!apiKey) return this.setupGuard();
|
|
6547
|
+
const jobId = args?.jobId;
|
|
6548
|
+
if (!jobId) {
|
|
6549
|
+
return this.text('Provide jobId to cancel. Example: trie_cloud_fix action:cancel jobId:"cursor-task-xyz"');
|
|
6550
|
+
}
|
|
6551
|
+
const store = await this.loadJobs(workDir);
|
|
6552
|
+
const entry = Object.values(store.jobs).find((j) => j.jobId === jobId);
|
|
6553
|
+
if (!entry) {
|
|
6554
|
+
return this.text(`Job ${jobId} not found in cloud-jobs.json.`);
|
|
6555
|
+
}
|
|
6556
|
+
const client = new CursorCloudAgentClient(apiKey);
|
|
6557
|
+
try {
|
|
6558
|
+
await client.cancelJob(jobId);
|
|
6559
|
+
} catch (err) {
|
|
6560
|
+
return this.text(`Cancel failed: ${err.message}`);
|
|
6561
|
+
}
|
|
6562
|
+
delete store.jobs[entry.issueId];
|
|
6563
|
+
await this.saveJobs(workDir, store);
|
|
6564
|
+
return this.text(`Job ${jobId} cancelled and removed.`);
|
|
6565
|
+
}
|
|
6566
|
+
// --------------------------------------------------------------------------
|
|
6567
|
+
// Helpers
|
|
6568
|
+
// --------------------------------------------------------------------------
|
|
6569
|
+
async resolveApiKey(workDir) {
|
|
6570
|
+
if (process.env.CURSOR_API_KEY) return process.env.CURSOR_API_KEY;
|
|
6571
|
+
const fromEnv = this.loadCursorKeyFromEnv(workDir);
|
|
6572
|
+
if (fromEnv) return fromEnv;
|
|
6573
|
+
const config = await loadAutonomyConfig(workDir);
|
|
6574
|
+
return config.cursorApiKey ?? null;
|
|
6575
|
+
}
|
|
6576
|
+
loadCursorKeyFromEnv(workDir) {
|
|
6577
|
+
for (const envFile of [".env", ".env.local", ".env.production"]) {
|
|
6578
|
+
const envPath = join2(workDir, envFile);
|
|
6579
|
+
if (!existsSync4(envPath)) continue;
|
|
6580
|
+
try {
|
|
6581
|
+
const content = readFileSync(envPath, "utf-8");
|
|
6582
|
+
const match = content.match(/^\s*CURSOR_API_KEY\s*=\s*(.+)$/m);
|
|
6583
|
+
if (match?.[1]) {
|
|
6584
|
+
const key = match[1].trim().replace(/^["']|["']$/g, "");
|
|
6585
|
+
if (key.length > 10) return key;
|
|
6586
|
+
}
|
|
6587
|
+
} catch {
|
|
6588
|
+
}
|
|
6589
|
+
}
|
|
6590
|
+
return null;
|
|
6591
|
+
}
|
|
6592
|
+
setupGuard() {
|
|
6593
|
+
return this.text(
|
|
6594
|
+
'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'
|
|
6595
|
+
);
|
|
6596
|
+
}
|
|
6597
|
+
async resolveIssues(issueIds) {
|
|
6598
|
+
let pending = getPendingFixes();
|
|
6599
|
+
if (pending.length === 0) {
|
|
6600
|
+
try {
|
|
6601
|
+
console.log("Loading issues from memory...");
|
|
6602
|
+
const { getRecentIssues } = await import("./issue-store-LZWZIGM7.js");
|
|
6603
|
+
const recentIssues = await getRecentIssues({ limit: 50, includeResolved: false });
|
|
6604
|
+
console.log(`Found ${recentIssues.length} recent issues in memory`);
|
|
6605
|
+
const memoryIssues = recentIssues.map((storedIssue) => ({
|
|
6606
|
+
id: storedIssue.id,
|
|
6607
|
+
severity: storedIssue.severity || "moderate",
|
|
6608
|
+
issue: storedIssue.issue,
|
|
6609
|
+
fix: storedIssue.fix,
|
|
6610
|
+
file: storedIssue.file,
|
|
6611
|
+
line: storedIssue.line,
|
|
6612
|
+
confidence: 0.8,
|
|
6613
|
+
// Default confidence for memory issues
|
|
6614
|
+
autoFixable: true,
|
|
6615
|
+
// Memory issues are generally auto-fixable
|
|
6616
|
+
agent: storedIssue.agent,
|
|
6617
|
+
category: storedIssue.category
|
|
6618
|
+
}));
|
|
6619
|
+
console.log(`Converted ${memoryIssues.length} memory issues for cloud dispatch`);
|
|
6620
|
+
if (issueIds && issueIds.length > 0) {
|
|
6621
|
+
return memoryIssues.filter((i) => issueIds.includes(i.id));
|
|
6622
|
+
}
|
|
6623
|
+
return memoryIssues;
|
|
6624
|
+
} catch (error) {
|
|
6625
|
+
console.warn("Failed to load issues from memory:", error);
|
|
6626
|
+
console.warn("Error details:", error);
|
|
6627
|
+
}
|
|
6628
|
+
}
|
|
6629
|
+
const issues = pending.map((p) => ({
|
|
6630
|
+
id: p.id,
|
|
6631
|
+
severity: p.severity ?? "moderate",
|
|
6632
|
+
effort: p.effort,
|
|
6633
|
+
issue: p.issue,
|
|
6634
|
+
fix: p.suggestedFix,
|
|
6635
|
+
file: p.file,
|
|
6636
|
+
line: p.line,
|
|
6637
|
+
confidence: p.confidence,
|
|
6638
|
+
autoFixable: p.autoFixable ?? false,
|
|
6639
|
+
agent: "trie_scan",
|
|
6640
|
+
cwe: p.cwe,
|
|
6641
|
+
owasp: p.owasp,
|
|
6642
|
+
category: p.category
|
|
6643
|
+
}));
|
|
6644
|
+
if (issueIds && issueIds.length > 0) {
|
|
6645
|
+
return issues.filter((i) => issueIds.includes(i.id));
|
|
6646
|
+
}
|
|
6647
|
+
return issues;
|
|
6648
|
+
}
|
|
6649
|
+
getRepoUrl(workDir) {
|
|
6650
|
+
try {
|
|
6651
|
+
return execSync("git remote get-url origin", { cwd: workDir, encoding: "utf-8" }).trim();
|
|
6652
|
+
} catch {
|
|
6653
|
+
return "unknown";
|
|
6654
|
+
}
|
|
6655
|
+
}
|
|
6656
|
+
getBranch(workDir) {
|
|
6657
|
+
try {
|
|
6658
|
+
return execSync("git rev-parse --abbrev-ref HEAD", { cwd: workDir, encoding: "utf-8" }).trim();
|
|
6659
|
+
} catch {
|
|
6660
|
+
return "main";
|
|
6661
|
+
}
|
|
6662
|
+
}
|
|
6663
|
+
async loadJobs(workDir) {
|
|
6664
|
+
const path2 = join2(getTrieDirectory(workDir), "cloud-jobs.json");
|
|
6665
|
+
try {
|
|
6666
|
+
if (existsSync4(path2)) {
|
|
6667
|
+
const raw = await readFile3(path2, "utf-8");
|
|
6668
|
+
return JSON.parse(raw);
|
|
6669
|
+
}
|
|
6670
|
+
} catch {
|
|
6671
|
+
}
|
|
6672
|
+
return { jobs: {} };
|
|
6673
|
+
}
|
|
6674
|
+
async saveJobs(workDir, store) {
|
|
6675
|
+
const trieDir = getTrieDirectory(workDir);
|
|
6676
|
+
if (!existsSync4(trieDir)) await mkdir(trieDir, { recursive: true });
|
|
6677
|
+
const path2 = join2(trieDir, "cloud-jobs.json");
|
|
6678
|
+
await writeFile(path2, JSON.stringify(store, null, 2));
|
|
6679
|
+
}
|
|
6680
|
+
text(msg) {
|
|
6681
|
+
return { content: [{ type: "text", text: msg }] };
|
|
6682
|
+
}
|
|
6683
|
+
};
|
|
6684
|
+
function formatAge(isoDate) {
|
|
6685
|
+
const ms = Date.now() - new Date(isoDate).getTime();
|
|
6686
|
+
const mins = Math.floor(ms / 6e4);
|
|
6687
|
+
if (mins < 60) return `${mins}m`;
|
|
6688
|
+
const hrs = Math.floor(mins / 60);
|
|
6689
|
+
if (hrs < 24) return `${hrs}h ${mins % 60}m`;
|
|
6690
|
+
return `${Math.floor(hrs / 24)}d`;
|
|
6691
|
+
}
|
|
6692
|
+
function shortPath2(file) {
|
|
6693
|
+
const parts = file.split("/");
|
|
6694
|
+
return parts.length > 2 ? parts.slice(-2).join("/") : file;
|
|
6695
|
+
}
|
|
6696
|
+
function padEnd2(str, len) {
|
|
6697
|
+
if (str.length >= len) return str.slice(0, len);
|
|
6698
|
+
return str + " ".repeat(len - str.length);
|
|
6699
|
+
}
|
|
6700
|
+
|
|
5516
6701
|
// src/tools/query-tools.ts
|
|
5517
6702
|
var TrieGetGovernanceTool = class {
|
|
5518
6703
|
async execute(input) {
|
|
@@ -5946,7 +7131,7 @@ var TriePipelineTool = class {
|
|
|
5946
7131
|
);
|
|
5947
7132
|
const prInfo = linkedPR ? `PR #${linkedPR.data.prNumber} (CI: ${linkedPR.data.ciStatus})` : "No PR yet";
|
|
5948
7133
|
lines.push(
|
|
5949
|
-
` ${
|
|
7134
|
+
` ${padEnd3(t.data.ticketId, 10)}${padEnd3(truncate2(t.data.title, 35), 37)}${padEnd3(t.data.priority, 9)}${prInfo}`
|
|
5950
7135
|
);
|
|
5951
7136
|
}
|
|
5952
7137
|
lines.push("");
|
|
@@ -5963,7 +7148,7 @@ var TriePipelineTool = class {
|
|
|
5963
7148
|
const stateTag = pr.data.isDraft ? "draft" : pr.data.state;
|
|
5964
7149
|
const ticketRef = pr.data.linkedTicketIds.length > 0 ? ` (${pr.data.linkedTicketIds.join(", ")})` : "";
|
|
5965
7150
|
lines.push(
|
|
5966
|
-
` #${
|
|
7151
|
+
` #${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
7152
|
);
|
|
5968
7153
|
}
|
|
5969
7154
|
lines.push("");
|
|
@@ -5977,7 +7162,7 @@ var TriePipelineTool = class {
|
|
|
5977
7162
|
const labels = issue.data.labels.length > 0 ? `[${issue.data.labels.join(", ")}]` : "";
|
|
5978
7163
|
const linked = issue.data.linkedTicketIds.length > 0 ? `linked: ${issue.data.linkedTicketIds.join(", ")}` : "no ticket";
|
|
5979
7164
|
lines.push(
|
|
5980
|
-
` #${
|
|
7165
|
+
` #${padEnd3(String(issue.data.issueNumber), 5)}${padEnd3(truncate2(issue.data.title, 35), 37)}${padEnd3(labels, 18)}${linked}`
|
|
5981
7166
|
);
|
|
5982
7167
|
}
|
|
5983
7168
|
if (ghIssues.length > 10) {
|
|
@@ -5990,8 +7175,8 @@ var TriePipelineTool = class {
|
|
|
5990
7175
|
if (uncoveredIssues.length > 0) {
|
|
5991
7176
|
lines.push(`COVERAGE GAPS (${uncoveredIssues.length} Trie issue${uncoveredIssues.length !== 1 ? "s" : ""} with no ticket or PR)`);
|
|
5992
7177
|
for (const issue of uncoveredIssues.slice(0, 10)) {
|
|
5993
|
-
const loc = `${
|
|
5994
|
-
lines.push(` ${
|
|
7178
|
+
const loc = `${shortPath3(issue.data.file)}:${issue.data.line ?? "?"}`;
|
|
7179
|
+
lines.push(` ${padEnd3(loc, 32)}${padEnd3(issue.data.severity, 10)}${truncate2(issue.data.issueText, 40)}`);
|
|
5995
7180
|
}
|
|
5996
7181
|
if (uncoveredIssues.length > 10) {
|
|
5997
7182
|
lines.push(` ... and ${uncoveredIssues.length - 10} more`);
|
|
@@ -6117,8 +7302,8 @@ var TriePipelineTool = class {
|
|
|
6117
7302
|
if (group.items.length === 0) continue;
|
|
6118
7303
|
lines.push(`${group.label} (${group.items.length}):`);
|
|
6119
7304
|
for (const issue of group.items.slice(0, 10)) {
|
|
6120
|
-
const loc = `${
|
|
6121
|
-
lines.push(` ${
|
|
7305
|
+
const loc = `${shortPath3(issue.data.file)}:${issue.data.line ?? "?"}`;
|
|
7306
|
+
lines.push(` ${padEnd3(loc, 32)}${truncate2(issue.data.issueText, 45)}`);
|
|
6122
7307
|
}
|
|
6123
7308
|
if (group.items.length > 10) {
|
|
6124
7309
|
lines.push(` ... and ${group.items.length - 10} more`);
|
|
@@ -6291,11 +7476,11 @@ var TriePipelineTool = class {
|
|
|
6291
7476
|
return parts.join("\n");
|
|
6292
7477
|
}
|
|
6293
7478
|
};
|
|
6294
|
-
function
|
|
7479
|
+
function shortPath3(file) {
|
|
6295
7480
|
const parts = file.split("/");
|
|
6296
7481
|
return parts.length > 2 ? parts.slice(-2).join("/") : file;
|
|
6297
7482
|
}
|
|
6298
|
-
function
|
|
7483
|
+
function padEnd3(str, len) {
|
|
6299
7484
|
if (str.length >= len) return str.slice(0, len);
|
|
6300
7485
|
return str + " ".repeat(len - str.length);
|
|
6301
7486
|
}
|
|
@@ -6305,7 +7490,7 @@ function truncate2(str, maxLen) {
|
|
|
6305
7490
|
}
|
|
6306
7491
|
|
|
6307
7492
|
// src/tools/github-branches.ts
|
|
6308
|
-
import { execSync } from "child_process";
|
|
7493
|
+
import { execSync as execSync2 } from "child_process";
|
|
6309
7494
|
var GitHubBranchesTool = class {
|
|
6310
7495
|
async execute(input) {
|
|
6311
7496
|
const workDir = input.directory || getWorkingDirectory(void 0, true);
|
|
@@ -6375,7 +7560,7 @@ Check token validity and repo access. Token needs \`repo\` or \`public_repo\` sc
|
|
|
6375
7560
|
}
|
|
6376
7561
|
getRepoInfo(projectPath) {
|
|
6377
7562
|
try {
|
|
6378
|
-
const url =
|
|
7563
|
+
const url = execSync2("git remote get-url origin", {
|
|
6379
7564
|
cwd: projectPath,
|
|
6380
7565
|
encoding: "utf-8",
|
|
6381
7566
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -6719,6 +7904,44 @@ var CHAT_TOOLS = [
|
|
|
6719
7904
|
},
|
|
6720
7905
|
required: ["hypothesisId"]
|
|
6721
7906
|
}
|
|
7907
|
+
},
|
|
7908
|
+
{
|
|
7909
|
+
name: "trie_cloud_fix",
|
|
7910
|
+
description: 'Dispatch to Cursor Cloud agents. Default: triage routes issues. When user explicitly says they want cloud (e.g. "fix with cloud agent") \u2014 pass forceCloud:true to honor their choice, do not reject. For ad-hoc (no scan issues): pass file, issue, fix from prior trie_propose_fix. Actions: configure, dispatch, status, artifacts, cancel.',
|
|
7911
|
+
input_schema: {
|
|
7912
|
+
type: "object",
|
|
7913
|
+
properties: {
|
|
7914
|
+
action: {
|
|
7915
|
+
type: "string",
|
|
7916
|
+
enum: ["configure", "dispatch", "status", "artifacts", "cancel"],
|
|
7917
|
+
description: "Action: configure (save apiKey), dispatch (send issues), status (check jobs), artifacts (get results), cancel (abort job)"
|
|
7918
|
+
},
|
|
7919
|
+
apiKey: {
|
|
7920
|
+
type: "string",
|
|
7921
|
+
description: "Cursor API key (only for action:configure). Get at cursor.com/settings \u2192 API Keys."
|
|
7922
|
+
},
|
|
7923
|
+
issueIds: {
|
|
7924
|
+
type: "array",
|
|
7925
|
+
items: { type: "string" },
|
|
7926
|
+
description: "Issue IDs to dispatch (for action:dispatch). Omit to dispatch all cloud-eligible issues."
|
|
7927
|
+
},
|
|
7928
|
+
forceCloud: {
|
|
7929
|
+
type: "boolean",
|
|
7930
|
+
description: "When true, bypass triage and dispatch ALL issues to cloud. Use when user explicitly requests cloud fix."
|
|
7931
|
+
},
|
|
7932
|
+
file: {
|
|
7933
|
+
type: "string",
|
|
7934
|
+
description: "For ad-hoc dispatch: file path (use with issue and fix when no scan issues)"
|
|
7935
|
+
},
|
|
7936
|
+
issue: { type: "string", description: "For ad-hoc dispatch: issue description" },
|
|
7937
|
+
fix: { type: "string", description: "For ad-hoc dispatch: suggested fix" },
|
|
7938
|
+
line: { type: "number", description: "For ad-hoc dispatch: line number" },
|
|
7939
|
+
jobId: {
|
|
7940
|
+
type: "string",
|
|
7941
|
+
description: "Job ID for action:artifacts or action:cancel"
|
|
7942
|
+
}
|
|
7943
|
+
}
|
|
7944
|
+
}
|
|
6722
7945
|
}
|
|
6723
7946
|
];
|
|
6724
7947
|
async function executeTool(name, input, onProgress) {
|
|
@@ -7076,6 +8299,24 @@ ${truncated}`;
|
|
|
7076
8299
|
return `Scan failed: ${error.message}`;
|
|
7077
8300
|
}
|
|
7078
8301
|
}
|
|
8302
|
+
case "trie_cloud_fix": {
|
|
8303
|
+
const cloudFixTool = new TrieCloudFixTool();
|
|
8304
|
+
const cloudFixArgs = {
|
|
8305
|
+
action: input.action || "status"
|
|
8306
|
+
};
|
|
8307
|
+
if (input.apiKey) cloudFixArgs.apiKey = String(input.apiKey);
|
|
8308
|
+
if (Array.isArray(input.issueIds)) cloudFixArgs.issueIds = input.issueIds;
|
|
8309
|
+
if (input.jobId) cloudFixArgs.jobId = String(input.jobId);
|
|
8310
|
+
if (input.forceCloud === true) cloudFixArgs.forceCloud = true;
|
|
8311
|
+
if (input.file) cloudFixArgs.file = String(input.file);
|
|
8312
|
+
if (input.issue) cloudFixArgs.issue = String(input.issue);
|
|
8313
|
+
if (input.fix) cloudFixArgs.fix = String(input.fix);
|
|
8314
|
+
if (typeof input.line === "number") cloudFixArgs.line = input.line;
|
|
8315
|
+
if (input.severity) cloudFixArgs.severity = String(input.severity);
|
|
8316
|
+
if (input.effort) cloudFixArgs.effort = String(input.effort);
|
|
8317
|
+
const result = await cloudFixTool.execute(cloudFixArgs);
|
|
8318
|
+
return textFromResult(result);
|
|
8319
|
+
}
|
|
7079
8320
|
case "trie_test_hypothesis": {
|
|
7080
8321
|
const hypothesisId = String(input.hypothesisId || "").trim();
|
|
7081
8322
|
if (!hypothesisId) {
|
|
@@ -7257,10 +8498,17 @@ async function buildContext(workDir, dashboardState) {
|
|
|
7257
8498
|
return parts.length > 0 ? parts.join("\n\n") : "No context data available yet.";
|
|
7258
8499
|
}
|
|
7259
8500
|
function chatHistoryToMessages(history) {
|
|
7260
|
-
return history.map((m) =>
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
8501
|
+
return history.map((m) => {
|
|
8502
|
+
let content = m.content;
|
|
8503
|
+
const fixes = m.pendingFixes ?? (m.pendingFix ? [m.pendingFix] : []);
|
|
8504
|
+
if (fixes.length > 0) {
|
|
8505
|
+
const fixCtx = fixes.map((f) => `file: ${f.file}, goal: ${f.goal}, violation: ${f.violation}${f.suggestedFix ? `, suggestedFix: ${f.suggestedFix}` : ""}`).join("; ");
|
|
8506
|
+
content = content ? `${content}
|
|
8507
|
+
|
|
8508
|
+
[Proposed fix context: ${fixCtx}]` : `[Proposed fix context: ${fixCtx}]`;
|
|
8509
|
+
}
|
|
8510
|
+
return { role: m.role, content };
|
|
8511
|
+
});
|
|
7264
8512
|
}
|
|
7265
8513
|
var SYSTEM_PROMPT = `You are Trie, a code assistant embedded in a terminal TUI.
|
|
7266
8514
|
|
|
@@ -7313,12 +8561,21 @@ var SYSTEM_PROMPT = `You are Trie, a code assistant embedded in a terminal TUI.
|
|
|
7313
8561
|
5. AFTER the tool call completes, the system will ask for user confirmation - do NOT add your own confirmation message
|
|
7314
8562
|
6. When the user says "yes", "yes to all", or "no", the system handles spawning Claude Code automatically
|
|
7315
8563
|
|
|
8564
|
+
**When user asks to dispatch or fix with Cursor Cloud agent:**
|
|
8565
|
+
- Default: Call trie_cloud_fix action:dispatch \u2014 triage runs and routes issues as usual.
|
|
8566
|
+
- If user EXPLICITLY says they want cloud (e.g. "fix it with cloud agent", "I want cloud", "use cursor cloud"): add forceCloud:true so triage does not override their choice. Do NOT suggest local fix instead \u2014 honor their request.
|
|
8567
|
+
- If there are no scan issues but the conversation has a recent trie_propose_fix: use ad-hoc dispatch with file, issue, fix from that context.
|
|
8568
|
+
- If the tool returns "Cloud Agent dispatch requires a Cursor API key", tell the user to configure it.
|
|
8569
|
+
- For status: trie_cloud_fix action:status
|
|
8570
|
+
|
|
7316
8571
|
Examples:
|
|
7317
8572
|
- User: "do we have emojis?" \u2192 Check nudges first. If none or unclear: Call trie_scan_for_goal_violations to scan the codebase.
|
|
7318
8573
|
- User: "run a full scan for emojis" \u2192 Call trie_scan_for_goal_violations directly.
|
|
7319
8574
|
- User: "fix the emoji violations" \u2192 Find ALL emoji violations in nudges, call trie_propose_fixes_batch ONCE with all fixes, then STOP
|
|
7320
8575
|
- User responds "yes to all" after proposal \u2192 Just say "Spawning Claude Code to fix all files..." The system handles it.
|
|
7321
8576
|
- 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.
|
|
8577
|
+
- User: "fix it with cloud agent" (explicit) \u2192 Call trie_cloud_fix action:dispatch forceCloud:true \u2014 honor their choice, don't suggest local.
|
|
8578
|
+
- User: "dispatch to cloud" (generic) \u2192 Call trie_cloud_fix action:dispatch \u2014 triage as usual.
|
|
7322
8579
|
|
|
7323
8580
|
Answer concisely. Reference specific files, decisions, and patterns when relevant.`;
|
|
7324
8581
|
function ChatView() {
|
|
@@ -7502,14 +8759,14 @@ ${contextBlock}`;
|
|
|
7502
8759
|
content: result.content
|
|
7503
8760
|
};
|
|
7504
8761
|
const pendingFixRegex = /\[PENDING_FIX:(.+?)\]/g;
|
|
7505
|
-
const
|
|
8762
|
+
const pendingFixes2 = [];
|
|
7506
8763
|
let match;
|
|
7507
8764
|
while ((match = pendingFixRegex.exec(result.content)) !== null) {
|
|
7508
8765
|
try {
|
|
7509
8766
|
const jsonStr = match[1];
|
|
7510
8767
|
if (jsonStr) {
|
|
7511
8768
|
const fixData = JSON.parse(jsonStr);
|
|
7512
|
-
|
|
8769
|
+
pendingFixes2.push(fixData);
|
|
7513
8770
|
}
|
|
7514
8771
|
} catch {
|
|
7515
8772
|
}
|
|
@@ -7523,8 +8780,8 @@ ${contextBlock}`;
|
|
|
7523
8780
|
const jsonStr = toolMatch[1];
|
|
7524
8781
|
if (jsonStr) {
|
|
7525
8782
|
const fixData = JSON.parse(jsonStr);
|
|
7526
|
-
if (!
|
|
7527
|
-
|
|
8783
|
+
if (!pendingFixes2.some((f) => f.file === fixData.file)) {
|
|
8784
|
+
pendingFixes2.push(fixData);
|
|
7528
8785
|
}
|
|
7529
8786
|
}
|
|
7530
8787
|
} catch {
|
|
@@ -7532,13 +8789,13 @@ ${contextBlock}`;
|
|
|
7532
8789
|
}
|
|
7533
8790
|
}
|
|
7534
8791
|
}
|
|
7535
|
-
if (
|
|
8792
|
+
if (pendingFixes2.length > 0) {
|
|
7536
8793
|
action.content = result.content.replace(/\[PENDING_FIX:.+?\]/g, "").trim();
|
|
7537
|
-
const firstFix =
|
|
7538
|
-
if (
|
|
8794
|
+
const firstFix = pendingFixes2[0];
|
|
8795
|
+
if (pendingFixes2.length === 1 && firstFix) {
|
|
7539
8796
|
action.pendingFix = firstFix;
|
|
7540
8797
|
} else {
|
|
7541
|
-
action.pendingFixes =
|
|
8798
|
+
action.pendingFixes = pendingFixes2;
|
|
7542
8799
|
}
|
|
7543
8800
|
}
|
|
7544
8801
|
if (result.toolCalls && result.toolCalls.length > 0) {
|
|
@@ -7986,8 +9243,8 @@ async function applyGoalFix(fix, dispatch) {
|
|
|
7986
9243
|
return;
|
|
7987
9244
|
}
|
|
7988
9245
|
const projectPath = getWorkingDirectory(void 0, true);
|
|
7989
|
-
const fullPath =
|
|
7990
|
-
const content = await
|
|
9246
|
+
const fullPath = join3(projectPath, fix.file);
|
|
9247
|
+
const content = await readFile4(fullPath, "utf-8");
|
|
7991
9248
|
const result = await runAIAnalysis({
|
|
7992
9249
|
systemPrompt: `You are a precise code fixer. The user has a quality goal: "${fix.goalDescription}".
|
|
7993
9250
|
A violation was detected: "${fix.description}"
|
|
@@ -8013,7 +9270,7 @@ ${content}
|
|
|
8013
9270
|
if (fixedContent.startsWith("```")) {
|
|
8014
9271
|
fixedContent = fixedContent.replace(/^```\w*\n?/, "").replace(/\n?```$/, "");
|
|
8015
9272
|
}
|
|
8016
|
-
await
|
|
9273
|
+
await writeFile2(fullPath, fixedContent, "utf-8");
|
|
8017
9274
|
const { recordGoalViolationFixed, getActiveGoals } = await import("./goal-validator-DA3JQ6JN.js");
|
|
8018
9275
|
const goals = await getActiveGoals(projectPath);
|
|
8019
9276
|
const matchedGoal = goals.find((g) => g.description === fix.goalDescription);
|
|
@@ -8037,11 +9294,11 @@ function DashboardApp({ onReady }) {
|
|
|
8037
9294
|
dispatchRef.current = dispatch;
|
|
8038
9295
|
const stateRef = useRef4(state);
|
|
8039
9296
|
stateRef.current = state;
|
|
8040
|
-
const configPath =
|
|
9297
|
+
const configPath = join3(getTrieDirectory(getWorkingDirectory(void 0, true)), "agent.json");
|
|
8041
9298
|
const loadConfig2 = useCallback7(async () => {
|
|
8042
|
-
if (!
|
|
9299
|
+
if (!existsSync5(configPath)) return;
|
|
8043
9300
|
try {
|
|
8044
|
-
const raw = await
|
|
9301
|
+
const raw = await readFile4(configPath, "utf-8");
|
|
8045
9302
|
const parsed = JSON.parse(raw);
|
|
8046
9303
|
dispatchRef.current({ type: "SET_AGENT_CONFIG", config: parsed });
|
|
8047
9304
|
} catch {
|
|
@@ -8050,8 +9307,8 @@ function DashboardApp({ onReady }) {
|
|
|
8050
9307
|
const persistConfig = useCallback7(async () => {
|
|
8051
9308
|
try {
|
|
8052
9309
|
const workDir = getWorkingDirectory(void 0, true);
|
|
8053
|
-
await
|
|
8054
|
-
await
|
|
9310
|
+
await mkdir2(getTrieDirectory(workDir), { recursive: true });
|
|
9311
|
+
await writeFile2(configPath, JSON.stringify(stateRef.current.agentConfig, null, 2), "utf-8");
|
|
8055
9312
|
const { saveAutonomyConfig: saveAutonomyConfig2, loadAutonomyConfig: loadAutonomyConfig2 } = await import("./autonomy-config-ZCOSTMPD.js");
|
|
8056
9313
|
const autonomy = await loadAutonomyConfig2(workDir);
|
|
8057
9314
|
autonomy.aiWatcher = stateRef.current.agentConfig.aiWatcher;
|
|
@@ -8382,6 +9639,8 @@ export {
|
|
|
8382
9639
|
TrieScanTool,
|
|
8383
9640
|
getPrompt,
|
|
8384
9641
|
getSystemPrompt,
|
|
9642
|
+
TrieFixTool,
|
|
9643
|
+
TrieCloudFixTool,
|
|
8385
9644
|
TrieExplainTool,
|
|
8386
9645
|
StreamingManager,
|
|
8387
9646
|
ExtractionPipeline,
|
|
@@ -8399,4 +9658,4 @@ export {
|
|
|
8399
9658
|
GitHubBranchesTool,
|
|
8400
9659
|
InteractiveDashboard
|
|
8401
9660
|
};
|
|
8402
|
-
//# sourceMappingURL=chunk-
|
|
9661
|
+
//# sourceMappingURL=chunk-5FQTNPBP.js.map
|