@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.
@@ -977,9 +977,9 @@ function useDashboard() {
977
977
  }
978
978
 
979
979
  // src/cli/dashboard/App.tsx
980
- import { existsSync as existsSync3 } from "fs";
981
- import { readFile as readFile2, writeFile, mkdir } from "fs/promises";
982
- import { join as join2 } from "path";
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 = pendingFixes.filter((f) => f.status === "pending" || f.status === "applying");
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
- ` ${padEnd(t.data.ticketId, 10)}${padEnd(truncate2(t.data.title, 35), 37)}${padEnd(t.data.priority, 9)}${prInfo}`
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
- ` #${padEnd(String(pr.data.prNumber), 5)}${padEnd(truncate2(pr.data.title, 30) + ticketRef, 42)}${padEnd(stateTag, 10)}CI: ${padEnd(pr.data.ciStatus, 10)}Review: ${pr.data.reviewStatus}`
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
- ` #${padEnd(String(issue.data.issueNumber), 5)}${padEnd(truncate2(issue.data.title, 35), 37)}${padEnd(labels, 18)}${linked}`
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 = `${shortPath(issue.data.file)}:${issue.data.line ?? "?"}`;
5994
- lines.push(` ${padEnd(loc, 32)}${padEnd(issue.data.severity, 10)}${truncate2(issue.data.issueText, 40)}`);
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 = `${shortPath(issue.data.file)}:${issue.data.line ?? "?"}`;
6121
- lines.push(` ${padEnd(loc, 32)}${truncate2(issue.data.issueText, 45)}`);
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 shortPath(file) {
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 padEnd(str, len) {
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 = execSync("git remote get-url origin", {
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 pendingFixes = [];
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
- pendingFixes.push(fixData);
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 (!pendingFixes.some((f) => f.file === fixData.file)) {
7527
- pendingFixes.push(fixData);
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 (pendingFixes.length > 0) {
8737
+ if (pendingFixes2.length > 0) {
7536
8738
  action.content = result.content.replace(/\[PENDING_FIX:.+?\]/g, "").trim();
7537
- const firstFix = pendingFixes[0];
7538
- if (pendingFixes.length === 1 && firstFix) {
8739
+ const firstFix = pendingFixes2[0];
8740
+ if (pendingFixes2.length === 1 && firstFix) {
7539
8741
  action.pendingFix = firstFix;
7540
8742
  } else {
7541
- action.pendingFixes = 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 = join2(projectPath, fix.file);
7990
- const content = await readFile2(fullPath, "utf-8");
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 writeFile(fullPath, fixedContent, "utf-8");
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 = join2(getTrieDirectory(getWorkingDirectory(void 0, true)), "agent.json");
9242
+ const configPath = join3(getTrieDirectory(getWorkingDirectory(void 0, true)), "agent.json");
8041
9243
  const loadConfig2 = useCallback7(async () => {
8042
- if (!existsSync3(configPath)) return;
9244
+ if (!existsSync5(configPath)) return;
8043
9245
  try {
8044
- const raw = await readFile2(configPath, "utf-8");
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 mkdir(getTrieDirectory(workDir), { recursive: true });
8054
- await writeFile(configPath, JSON.stringify(stateRef.current.agentConfig, null, 2), "utf-8");
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-Y2LYDCJD.js.map
9606
+ //# sourceMappingURL=chunk-AIJF67CF.js.map