@triedotdev/mcp 1.0.168 → 1.0.170

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.
Files changed (149) hide show
  1. package/README.md +54 -500
  2. package/dist/chunk-2YXOBNKW.js +619 -0
  3. package/dist/chunk-2YXOBNKW.js.map +1 -0
  4. package/dist/chunk-QR64Y5TI.js +363 -0
  5. package/dist/chunk-QR64Y5TI.js.map +1 -0
  6. package/dist/cli/main.d.ts +0 -15
  7. package/dist/cli/main.js +356 -3100
  8. package/dist/cli/main.js.map +1 -1
  9. package/dist/index.js +2 -36
  10. package/dist/index.js.map +1 -1
  11. package/dist/server/mcp-server.js +2 -36
  12. package/package.json +8 -31
  13. package/dist/autonomy-config-FSERX3O3.js +0 -30
  14. package/dist/autonomy-config-FSERX3O3.js.map +0 -1
  15. package/dist/chat-store-JNGNTDSN.js +0 -15
  16. package/dist/chat-store-JNGNTDSN.js.map +0 -1
  17. package/dist/chunk-2HF65EHQ.js +0 -311
  18. package/dist/chunk-2HF65EHQ.js.map +0 -1
  19. package/dist/chunk-43X6JBEM.js +0 -36
  20. package/dist/chunk-43X6JBEM.js.map +0 -1
  21. package/dist/chunk-4MXH2ZPT.js +0 -1827
  22. package/dist/chunk-4MXH2ZPT.js.map +0 -1
  23. package/dist/chunk-575YT2SD.js +0 -737
  24. package/dist/chunk-575YT2SD.js.map +0 -1
  25. package/dist/chunk-5BRRRTN6.js +0 -354
  26. package/dist/chunk-5BRRRTN6.js.map +0 -1
  27. package/dist/chunk-6NLHFIYA.js +0 -344
  28. package/dist/chunk-6NLHFIYA.js.map +0 -1
  29. package/dist/chunk-7WITSO22.js +0 -824
  30. package/dist/chunk-7WITSO22.js.map +0 -1
  31. package/dist/chunk-DGUM43GV.js +0 -11
  32. package/dist/chunk-DGUM43GV.js.map +0 -1
  33. package/dist/chunk-EFWVF6TI.js +0 -267
  34. package/dist/chunk-EFWVF6TI.js.map +0 -1
  35. package/dist/chunk-F6WFNUAY.js +0 -216
  36. package/dist/chunk-F6WFNUAY.js.map +0 -1
  37. package/dist/chunk-FQ45QP5A.js +0 -361
  38. package/dist/chunk-FQ45QP5A.js.map +0 -1
  39. package/dist/chunk-G2TGF6TR.js +0 -573
  40. package/dist/chunk-G2TGF6TR.js.map +0 -1
  41. package/dist/chunk-GTKYBOXL.js +0 -700
  42. package/dist/chunk-GTKYBOXL.js.map +0 -1
  43. package/dist/chunk-HVCDY3AK.js +0 -850
  44. package/dist/chunk-HVCDY3AK.js.map +0 -1
  45. package/dist/chunk-JVMBCWKS.js +0 -348
  46. package/dist/chunk-JVMBCWKS.js.map +0 -1
  47. package/dist/chunk-KDHN2ZQE.js +0 -313
  48. package/dist/chunk-KDHN2ZQE.js.map +0 -1
  49. package/dist/chunk-LQIMKE3P.js +0 -12524
  50. package/dist/chunk-LQIMKE3P.js.map +0 -1
  51. package/dist/chunk-ME2OERF5.js +0 -345
  52. package/dist/chunk-ME2OERF5.js.map +0 -1
  53. package/dist/chunk-MRHKX5M5.js +0 -662
  54. package/dist/chunk-MRHKX5M5.js.map +0 -1
  55. package/dist/chunk-OBQ74FOU.js +0 -27
  56. package/dist/chunk-OBQ74FOU.js.map +0 -1
  57. package/dist/chunk-OMR4YCBS.js +0 -987
  58. package/dist/chunk-OMR4YCBS.js.map +0 -1
  59. package/dist/chunk-Q5EKA5YA.js +0 -254
  60. package/dist/chunk-Q5EKA5YA.js.map +0 -1
  61. package/dist/chunk-Q63FFI6D.js +0 -132
  62. package/dist/chunk-Q63FFI6D.js.map +0 -1
  63. package/dist/chunk-SY6KQG44.js +0 -983
  64. package/dist/chunk-SY6KQG44.js.map +0 -1
  65. package/dist/chunk-T63OHG4Q.js +0 -440
  66. package/dist/chunk-T63OHG4Q.js.map +0 -1
  67. package/dist/chunk-TN5WEKWI.js +0 -173
  68. package/dist/chunk-TN5WEKWI.js.map +0 -1
  69. package/dist/chunk-VUL52BQL.js +0 -402
  70. package/dist/chunk-VUL52BQL.js.map +0 -1
  71. package/dist/chunk-VVITXIHN.js +0 -189
  72. package/dist/chunk-VVITXIHN.js.map +0 -1
  73. package/dist/chunk-WCN7S3EI.js +0 -14
  74. package/dist/chunk-WCN7S3EI.js.map +0 -1
  75. package/dist/chunk-XE6KQRKZ.js +0 -816
  76. package/dist/chunk-XE6KQRKZ.js.map +0 -1
  77. package/dist/chunk-XPZZFPBZ.js +0 -491
  78. package/dist/chunk-XPZZFPBZ.js.map +0 -1
  79. package/dist/chunk-XTFWT2XM.js +0 -727
  80. package/dist/chunk-XTFWT2XM.js.map +0 -1
  81. package/dist/chunk-YDHUCDHM.js +0 -4011
  82. package/dist/chunk-YDHUCDHM.js.map +0 -1
  83. package/dist/chunk-YZ6Y2H3P.js +0 -1289
  84. package/dist/chunk-YZ6Y2H3P.js.map +0 -1
  85. package/dist/chunk-ZJF5FTBX.js +0 -1396
  86. package/dist/chunk-ZJF5FTBX.js.map +0 -1
  87. package/dist/chunk-ZV2K6M7T.js +0 -74
  88. package/dist/chunk-ZV2K6M7T.js.map +0 -1
  89. package/dist/cli/create-agent.d.ts +0 -1
  90. package/dist/cli/create-agent.js +0 -1050
  91. package/dist/cli/create-agent.js.map +0 -1
  92. package/dist/cli/yolo-daemon.d.ts +0 -1
  93. package/dist/cli/yolo-daemon.js +0 -423
  94. package/dist/cli/yolo-daemon.js.map +0 -1
  95. package/dist/client-NJPZE5JT.js +0 -28
  96. package/dist/client-NJPZE5JT.js.map +0 -1
  97. package/dist/codebase-index-VAPF32XX.js +0 -12
  98. package/dist/codebase-index-VAPF32XX.js.map +0 -1
  99. package/dist/fast-analyzer-XXYMOXRK.js +0 -216
  100. package/dist/fast-analyzer-XXYMOXRK.js.map +0 -1
  101. package/dist/git-EO5SRFMN.js +0 -28
  102. package/dist/git-EO5SRFMN.js.map +0 -1
  103. package/dist/github-ingester-ZOKK6GRS.js +0 -11
  104. package/dist/github-ingester-ZOKK6GRS.js.map +0 -1
  105. package/dist/goal-manager-YOB7VWK7.js +0 -25
  106. package/dist/goal-manager-YOB7VWK7.js.map +0 -1
  107. package/dist/goal-validator-ULKIBDPX.js +0 -24
  108. package/dist/goal-validator-ULKIBDPX.js.map +0 -1
  109. package/dist/graph-B3NA4S7I.js +0 -10
  110. package/dist/graph-B3NA4S7I.js.map +0 -1
  111. package/dist/hypothesis-7BFFT5JY.js +0 -23
  112. package/dist/hypothesis-7BFFT5JY.js.map +0 -1
  113. package/dist/incident-index-EFNUSGWL.js +0 -11
  114. package/dist/incident-index-EFNUSGWL.js.map +0 -1
  115. package/dist/insight-store-EC4PLSAW.js +0 -22
  116. package/dist/insight-store-EC4PLSAW.js.map +0 -1
  117. package/dist/issue-store-ZIRP23EP.js +0 -36
  118. package/dist/issue-store-ZIRP23EP.js.map +0 -1
  119. package/dist/ledger-TWZTGDFA.js +0 -58
  120. package/dist/ledger-TWZTGDFA.js.map +0 -1
  121. package/dist/linear-ingester-XXPAZZRW.js +0 -11
  122. package/dist/linear-ingester-XXPAZZRW.js.map +0 -1
  123. package/dist/output-manager-RVJ37XKA.js +0 -13
  124. package/dist/output-manager-RVJ37XKA.js.map +0 -1
  125. package/dist/parse-goal-violation-SACGFG3C.js +0 -8
  126. package/dist/parse-goal-violation-SACGFG3C.js.map +0 -1
  127. package/dist/pattern-discovery-F7LU5K6E.js +0 -8
  128. package/dist/pattern-discovery-F7LU5K6E.js.map +0 -1
  129. package/dist/progress-SRQ2V3BP.js +0 -18
  130. package/dist/progress-SRQ2V3BP.js.map +0 -1
  131. package/dist/project-state-AHPA77SM.js +0 -28
  132. package/dist/project-state-AHPA77SM.js.map +0 -1
  133. package/dist/sync-M2FSWPBC.js +0 -12
  134. package/dist/sync-M2FSWPBC.js.map +0 -1
  135. package/dist/terminal-spawn-5YXDMUCF.js +0 -157
  136. package/dist/terminal-spawn-5YXDMUCF.js.map +0 -1
  137. package/dist/tiered-storage-Z3YCR465.js +0 -12
  138. package/dist/tiered-storage-Z3YCR465.js.map +0 -1
  139. package/dist/trie-agent-3YDPEGHJ.js +0 -28
  140. package/dist/trie-agent-3YDPEGHJ.js.map +0 -1
  141. package/dist/ui/chat.html +0 -1014
  142. package/dist/ui/goals.html +0 -967
  143. package/dist/ui/hypotheses.html +0 -1011
  144. package/dist/ui/ledger.html +0 -954
  145. package/dist/ui/nudges.html +0 -995
  146. package/dist/vibe-code-signatures-F6URTBW3.js +0 -16
  147. package/dist/vibe-code-signatures-F6URTBW3.js.map +0 -1
  148. package/dist/vulnerability-signatures-T7SKHORW.js +0 -18
  149. package/dist/vulnerability-signatures-T7SKHORW.js.map +0 -1
@@ -1,216 +0,0 @@
1
- import {
2
- loadConfig
3
- } from "./chunk-XPZZFPBZ.js";
4
-
5
- // src/ingest/github-ingester.ts
6
- import path from "path";
7
- import { execSync } from "child_process";
8
- var TICKET_PATTERN = /\b([A-Z]{2,10}-\d+)\b/g;
9
- var GitHubIngester = class {
10
- graph;
11
- constructor(graph) {
12
- this.graph = graph;
13
- }
14
- async getApiToken() {
15
- const config = await loadConfig();
16
- return config.apiKeys?.github ?? process.env.GITHUB_TOKEN ?? null;
17
- }
18
- getRepoInfo(projectPath) {
19
- try {
20
- const url = execSync("git remote get-url origin", {
21
- cwd: projectPath,
22
- encoding: "utf-8",
23
- stdio: ["pipe", "pipe", "pipe"]
24
- }).trim();
25
- const sshMatch = url.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
26
- if (sshMatch) {
27
- return { owner: sshMatch[1], name: sshMatch[2] };
28
- }
29
- const httpsMatch = url.match(/github\.com\/([^/]+)\/([^/.]+)/);
30
- if (httpsMatch) {
31
- return { owner: httpsMatch[1], name: httpsMatch[2] };
32
- }
33
- return null;
34
- } catch {
35
- return null;
36
- }
37
- }
38
- async syncPullRequests(owner, repo, token) {
39
- const prs = await this.fetchJson(
40
- `https://api.github.com/repos/${owner}/${repo}/pulls?state=open&per_page=50`,
41
- token
42
- );
43
- let linkedTickets = 0;
44
- let linkedFiles = 0;
45
- for (const pr of prs) {
46
- const filesChanged = await this.fetchPRFiles(owner, repo, pr.number, token);
47
- const reviewStatus = await this.fetchReviewStatus(owner, repo, pr.number, token);
48
- const ciStatus = await this.fetchCIStatus(owner, repo, pr.head.ref, token);
49
- const linkedTicketIds = this.extractTicketIds(pr.title, pr.body);
50
- const state = pr.draft ? "draft" : pr.state === "open" ? "open" : "closed";
51
- const data = {
52
- prNumber: pr.number,
53
- title: pr.title,
54
- url: pr.html_url,
55
- state,
56
- author: pr.user.login,
57
- branch: pr.head.ref,
58
- baseBranch: pr.base.ref,
59
- isDraft: pr.draft,
60
- filesChanged,
61
- linkedTicketIds,
62
- ciStatus,
63
- reviewStatus,
64
- createdAt: pr.created_at,
65
- updatedAt: pr.updated_at,
66
- ...pr.merged_at != null && { mergedAt: pr.merged_at }
67
- };
68
- const prNodeId = `pr:${pr.number}`;
69
- const existingNode = await this.graph.getNode("pull-request", prNodeId);
70
- if (existingNode) {
71
- await this.graph.updateNode("pull-request", prNodeId, data);
72
- } else {
73
- await this.graph.addNode("pull-request", data);
74
- }
75
- for (const file of filesChanged) {
76
- const resolvedPath = path.resolve(this.graph.projectRoot, file);
77
- const fileNode = await this.graph.getNode("file", resolvedPath);
78
- if (fileNode) {
79
- linkedFiles++;
80
- await this.graph.addEdge(prNodeId, fileNode.id, "affects");
81
- }
82
- }
83
- for (const ticketId of linkedTicketIds) {
84
- const ticketNodeId = `linear:${ticketId}`;
85
- const ticketNode = await this.graph.getNode("linear-ticket", ticketNodeId);
86
- if (ticketNode) {
87
- linkedTickets++;
88
- await this.graph.addEdge(ticketNodeId, prNodeId, "implements");
89
- }
90
- }
91
- }
92
- return { prs: prs.length, linkedTickets, linkedFiles };
93
- }
94
- async syncIssues(owner, repo, token) {
95
- const allItems = await this.fetchJson(
96
- `https://api.github.com/repos/${owner}/${repo}/issues?state=open&per_page=50`,
97
- token
98
- );
99
- const issues = allItems.filter((item) => !item.pull_request);
100
- for (const issue of issues) {
101
- const linkedTicketIds = this.extractTicketIds(issue.title, issue.body);
102
- const data = {
103
- issueNumber: issue.number,
104
- title: issue.title,
105
- url: issue.html_url,
106
- state: issue.state,
107
- labels: issue.labels.map((l) => l.name),
108
- assignees: issue.assignees.map((a) => a.login),
109
- body: issue.body ?? "",
110
- linkedTicketIds,
111
- createdAt: issue.created_at,
112
- updatedAt: issue.updated_at,
113
- ...issue.closed_at != null && { closedAt: issue.closed_at }
114
- };
115
- const issueNodeId = `gh-issue:${issue.number}`;
116
- const existingNode = await this.graph.getNode("github-issue", issueNodeId);
117
- if (existingNode) {
118
- await this.graph.updateNode("github-issue", issueNodeId, data);
119
- } else {
120
- await this.graph.addNode("github-issue", data);
121
- }
122
- for (const ticketId of linkedTicketIds) {
123
- const ticketNodeId = `linear:${ticketId}`;
124
- const ticketNode = await this.graph.getNode("linear-ticket", ticketNodeId);
125
- if (ticketNode) {
126
- await this.graph.addEdge(issueNodeId, ticketNodeId, "relatedTo");
127
- }
128
- }
129
- }
130
- return { issues: issues.length };
131
- }
132
- async fetchPRFiles(owner, repo, prNumber, token) {
133
- try {
134
- const files = await this.fetchJson(
135
- `https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/files?per_page=100`,
136
- token
137
- );
138
- return files.map((f) => f.filename);
139
- } catch {
140
- return [];
141
- }
142
- }
143
- async fetchReviewStatus(owner, repo, prNumber, token) {
144
- try {
145
- const reviews = await this.fetchJson(
146
- `https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/reviews`,
147
- token
148
- );
149
- if (reviews.length === 0) return "none";
150
- const states = reviews.map((r) => r.state);
151
- if (states.includes("CHANGES_REQUESTED")) return "changes_requested";
152
- if (states.includes("APPROVED")) return "approved";
153
- return "pending";
154
- } catch {
155
- return "none";
156
- }
157
- }
158
- async fetchCIStatus(owner, repo, ref, token) {
159
- try {
160
- const status = await this.fetchJson(
161
- `https://api.github.com/repos/${owner}/${repo}/commits/${ref}/status`,
162
- token
163
- );
164
- switch (status.state) {
165
- case "success":
166
- return "passing";
167
- case "failure":
168
- case "error":
169
- return "failing";
170
- case "pending":
171
- return "pending";
172
- default:
173
- return "unknown";
174
- }
175
- } catch {
176
- return "unknown";
177
- }
178
- }
179
- extractTicketIds(title, body) {
180
- const text = `${title} ${body ?? ""}`;
181
- const matches = text.match(TICKET_PATTERN);
182
- return matches ? [...new Set(matches)] : [];
183
- }
184
- async fetchJson(url, token) {
185
- const response = await fetch(url, {
186
- headers: {
187
- "Accept": "application/vnd.github.v3+json",
188
- "Authorization": `Bearer ${token}`,
189
- "User-Agent": "trie-agents",
190
- "X-GitHub-Api-Version": "2022-11-28"
191
- }
192
- });
193
- if (response.status === 401) {
194
- throw new Error(
195
- "GitHub token is invalid or expired. Set a valid token:\n \u2022 Environment: GITHUB_TOKEN=ghp_...\n \u2022 Config: trie config \u2192 API Keys \u2192 GitHub"
196
- );
197
- }
198
- if (response.status === 403) {
199
- const resetHeader = response.headers.get("X-RateLimit-Reset");
200
- const resetTime = resetHeader ? new Date(parseInt(resetHeader, 10) * 1e3).toLocaleTimeString() : "unknown";
201
- throw new Error(`GitHub API rate limit exceeded. Resets at ${resetTime}.`);
202
- }
203
- if (response.status === 404) {
204
- throw new Error("Repository not found or token lacks access. Ensure the token has `repo` scope.");
205
- }
206
- if (!response.ok) {
207
- throw new Error(`GitHub API error ${response.status}: ${response.statusText}`);
208
- }
209
- return await response.json();
210
- }
211
- };
212
-
213
- export {
214
- GitHubIngester
215
- };
216
- //# sourceMappingURL=chunk-F6WFNUAY.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/ingest/github-ingester.ts"],"sourcesContent":["import path from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { loadConfig } from '../config/loader.js';\nimport { ContextGraph } from '../context/graph.js';\nimport type { PullRequestNodeData, GitHubIssueNodeData } from '../context/nodes.js';\n\nconst TICKET_PATTERN = /\\b([A-Z]{2,10}-\\d+)\\b/g;\n\ninterface GitHubPR {\n number: number;\n title: string;\n html_url: string;\n state: string;\n draft: boolean;\n user: { login: string };\n head: { ref: string };\n base: { ref: string };\n body: string | null;\n created_at: string;\n updated_at: string;\n merged_at: string | null;\n}\n\ninterface GitHubFile {\n filename: string;\n}\n\ninterface GitHubReview {\n state: string;\n}\n\ninterface GitHubIssue {\n number: number;\n title: string;\n html_url: string;\n state: string;\n labels: Array<{ name: string }>;\n assignees: Array<{ login: string }>;\n body: string | null;\n pull_request?: unknown;\n created_at: string;\n updated_at: string;\n closed_at: string | null;\n}\n\ninterface GitHubCommitStatus {\n state: string;\n}\n\nexport interface GitHubSyncResult {\n prs: number;\n issues: number;\n linkedTickets: number;\n linkedFiles: number;\n}\n\nexport class GitHubIngester {\n private readonly graph: ContextGraph;\n\n constructor(graph: ContextGraph) {\n this.graph = graph;\n }\n\n async getApiToken(): Promise<string | null> {\n const config = await loadConfig();\n return config.apiKeys?.github ?? process.env.GITHUB_TOKEN ?? null;\n }\n\n getRepoInfo(projectPath: string): { owner: string; name: string } | null {\n try {\n const url = execSync('git remote get-url origin', {\n cwd: projectPath,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n // SSH: git@github.com:owner/repo.git\n const sshMatch = url.match(/github\\.com[:/]([^/]+)\\/([^/.]+)/);\n if (sshMatch) {\n return { owner: sshMatch[1]!, name: sshMatch[2]! };\n }\n\n // HTTPS: https://github.com/owner/repo.git\n const httpsMatch = url.match(/github\\.com\\/([^/]+)\\/([^/.]+)/);\n if (httpsMatch) {\n return { owner: httpsMatch[1]!, name: httpsMatch[2]! };\n }\n\n return null;\n } catch {\n return null;\n }\n }\n\n async syncPullRequests(owner: string, repo: string, token: string): Promise<{ prs: number; linkedTickets: number; linkedFiles: number }> {\n const prs = await this.fetchJson<GitHubPR[]>(\n `https://api.github.com/repos/${owner}/${repo}/pulls?state=open&per_page=50`,\n token,\n );\n\n let linkedTickets = 0;\n let linkedFiles = 0;\n\n for (const pr of prs) {\n const filesChanged = await this.fetchPRFiles(owner, repo, pr.number, token);\n const reviewStatus = await this.fetchReviewStatus(owner, repo, pr.number, token);\n const ciStatus = await this.fetchCIStatus(owner, repo, pr.head.ref, token);\n const linkedTicketIds = this.extractTicketIds(pr.title, pr.body);\n\n const state: PullRequestNodeData['state'] = pr.draft ? 'draft' : pr.state === 'open' ? 'open' : 'closed';\n\n const data: PullRequestNodeData = {\n prNumber: pr.number,\n title: pr.title,\n url: pr.html_url,\n state,\n author: pr.user.login,\n branch: pr.head.ref,\n baseBranch: pr.base.ref,\n isDraft: pr.draft,\n filesChanged,\n linkedTicketIds,\n ciStatus,\n reviewStatus,\n createdAt: pr.created_at,\n updatedAt: pr.updated_at,\n ...(pr.merged_at != null && { mergedAt: pr.merged_at }),\n };\n\n const prNodeId = `pr:${pr.number}`;\n\n const existingNode = await this.graph.getNode('pull-request', prNodeId);\n if (existingNode) {\n await this.graph.updateNode('pull-request', prNodeId, data);\n } else {\n await this.graph.addNode('pull-request', data);\n }\n\n // Link PR to files it touches (file node IDs are normalized absolute paths)\n for (const file of filesChanged) {\n const resolvedPath = path.resolve(this.graph.projectRoot, file);\n const fileNode = await this.graph.getNode('file', resolvedPath);\n if (fileNode) {\n linkedFiles++;\n await this.graph.addEdge(prNodeId, fileNode.id, 'affects');\n }\n }\n\n // Link Linear tickets to this PR\n for (const ticketId of linkedTicketIds) {\n const ticketNodeId = `linear:${ticketId}`;\n const ticketNode = await this.graph.getNode('linear-ticket', ticketNodeId);\n if (ticketNode) {\n linkedTickets++;\n await this.graph.addEdge(ticketNodeId, prNodeId, 'implements');\n }\n }\n }\n\n return { prs: prs.length, linkedTickets, linkedFiles };\n }\n\n async syncIssues(owner: string, repo: string, token: string): Promise<{ issues: number }> {\n const allItems = await this.fetchJson<GitHubIssue[]>(\n `https://api.github.com/repos/${owner}/${repo}/issues?state=open&per_page=50`,\n token,\n );\n\n // GitHub returns PRs in the issues endpoint — filter them out\n const issues = allItems.filter(item => !item.pull_request);\n\n for (const issue of issues) {\n const linkedTicketIds = this.extractTicketIds(issue.title, issue.body);\n\n const data: GitHubIssueNodeData = {\n issueNumber: issue.number,\n title: issue.title,\n url: issue.html_url,\n state: issue.state as 'open' | 'closed',\n labels: issue.labels.map(l => l.name),\n assignees: issue.assignees.map(a => a.login),\n body: issue.body ?? '',\n linkedTicketIds,\n createdAt: issue.created_at,\n updatedAt: issue.updated_at,\n ...(issue.closed_at != null && { closedAt: issue.closed_at }),\n };\n\n const issueNodeId = `gh-issue:${issue.number}`;\n\n const existingNode = await this.graph.getNode('github-issue', issueNodeId);\n if (existingNode) {\n await this.graph.updateNode('github-issue', issueNodeId, data);\n } else {\n await this.graph.addNode('github-issue', data);\n }\n\n // Link to related Linear tickets\n for (const ticketId of linkedTicketIds) {\n const ticketNodeId = `linear:${ticketId}`;\n const ticketNode = await this.graph.getNode('linear-ticket', ticketNodeId);\n if (ticketNode) {\n await this.graph.addEdge(issueNodeId, ticketNodeId, 'relatedTo');\n }\n }\n }\n\n return { issues: issues.length };\n }\n\n private async fetchPRFiles(owner: string, repo: string, prNumber: number, token: string): Promise<string[]> {\n try {\n const files = await this.fetchJson<GitHubFile[]>(\n `https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/files?per_page=100`,\n token,\n );\n return files.map(f => f.filename);\n } catch {\n return [];\n }\n }\n\n private async fetchReviewStatus(\n owner: string,\n repo: string,\n prNumber: number,\n token: string,\n ): Promise<PullRequestNodeData['reviewStatus']> {\n try {\n const reviews = await this.fetchJson<GitHubReview[]>(\n `https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/reviews`,\n token,\n );\n if (reviews.length === 0) return 'none';\n\n const states = reviews.map(r => r.state);\n if (states.includes('CHANGES_REQUESTED')) return 'changes_requested';\n if (states.includes('APPROVED')) return 'approved';\n return 'pending';\n } catch {\n return 'none';\n }\n }\n\n private async fetchCIStatus(\n owner: string,\n repo: string,\n ref: string,\n token: string,\n ): Promise<PullRequestNodeData['ciStatus']> {\n try {\n const status = await this.fetchJson<GitHubCommitStatus>(\n `https://api.github.com/repos/${owner}/${repo}/commits/${ref}/status`,\n token,\n );\n switch (status.state) {\n case 'success': return 'passing';\n case 'failure': case 'error': return 'failing';\n case 'pending': return 'pending';\n default: return 'unknown';\n }\n } catch {\n return 'unknown';\n }\n }\n\n private extractTicketIds(title: string, body: string | null): string[] {\n const text = `${title} ${body ?? ''}`;\n const matches = text.match(TICKET_PATTERN);\n return matches ? [...new Set(matches)] : [];\n }\n\n private async fetchJson<T>(url: string, token: string): Promise<T> {\n const response = await fetch(url, {\n headers: {\n 'Accept': 'application/vnd.github.v3+json',\n 'Authorization': `Bearer ${token}`,\n 'User-Agent': 'trie-agents',\n 'X-GitHub-Api-Version': '2022-11-28',\n },\n });\n\n if (response.status === 401) {\n throw new Error(\n 'GitHub token is invalid or expired. Set a valid token:\\n' +\n ' • Environment: GITHUB_TOKEN=ghp_...\\n' +\n ' • Config: trie config → API Keys → GitHub',\n );\n }\n\n if (response.status === 403) {\n const resetHeader = response.headers.get('X-RateLimit-Reset');\n const resetTime = resetHeader\n ? new Date(parseInt(resetHeader, 10) * 1000).toLocaleTimeString()\n : 'unknown';\n throw new Error(`GitHub API rate limit exceeded. Resets at ${resetTime}.`);\n }\n\n if (response.status === 404) {\n throw new Error('Repository not found or token lacks access. Ensure the token has `repo` scope.');\n }\n\n if (!response.ok) {\n throw new Error(`GitHub API error ${response.status}: ${response.statusText}`);\n }\n\n return (await response.json()) as T;\n }\n}\n"],"mappings":";;;;;AAAA,OAAO,UAAU;AACjB,SAAS,gBAAgB;AAKzB,IAAM,iBAAiB;AAkDhB,IAAM,iBAAN,MAAqB;AAAA,EACT;AAAA,EAEjB,YAAY,OAAqB;AAC/B,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAM,cAAsC;AAC1C,UAAM,SAAS,MAAM,WAAW;AAChC,WAAO,OAAO,SAAS,UAAU,QAAQ,IAAI,gBAAgB;AAAA,EAC/D;AAAA,EAEA,YAAY,aAA6D;AACvE,QAAI;AACF,YAAM,MAAM,SAAS,6BAA6B;AAAA,QAChD,KAAK;AAAA,QACL,UAAU;AAAA,QACV,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,MAChC,CAAC,EAAE,KAAK;AAGR,YAAM,WAAW,IAAI,MAAM,kCAAkC;AAC7D,UAAI,UAAU;AACZ,eAAO,EAAE,OAAO,SAAS,CAAC,GAAI,MAAM,SAAS,CAAC,EAAG;AAAA,MACnD;AAGA,YAAM,aAAa,IAAI,MAAM,gCAAgC;AAC7D,UAAI,YAAY;AACd,eAAO,EAAE,OAAO,WAAW,CAAC,GAAI,MAAM,WAAW,CAAC,EAAG;AAAA,MACvD;AAEA,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,iBAAiB,OAAe,MAAc,OAAqF;AACvI,UAAM,MAAM,MAAM,KAAK;AAAA,MACrB,gCAAgC,KAAK,IAAI,IAAI;AAAA,MAC7C;AAAA,IACF;AAEA,QAAI,gBAAgB;AACpB,QAAI,cAAc;AAElB,eAAW,MAAM,KAAK;AACpB,YAAM,eAAe,MAAM,KAAK,aAAa,OAAO,MAAM,GAAG,QAAQ,KAAK;AAC1E,YAAM,eAAe,MAAM,KAAK,kBAAkB,OAAO,MAAM,GAAG,QAAQ,KAAK;AAC/E,YAAM,WAAW,MAAM,KAAK,cAAc,OAAO,MAAM,GAAG,KAAK,KAAK,KAAK;AACzE,YAAM,kBAAkB,KAAK,iBAAiB,GAAG,OAAO,GAAG,IAAI;AAE/D,YAAM,QAAsC,GAAG,QAAQ,UAAU,GAAG,UAAU,SAAS,SAAS;AAEhG,YAAM,OAA4B;AAAA,QAChC,UAAU,GAAG;AAAA,QACb,OAAO,GAAG;AAAA,QACV,KAAK,GAAG;AAAA,QACR;AAAA,QACA,QAAQ,GAAG,KAAK;AAAA,QAChB,QAAQ,GAAG,KAAK;AAAA,QAChB,YAAY,GAAG,KAAK;AAAA,QACpB,SAAS,GAAG;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW,GAAG;AAAA,QACd,WAAW,GAAG;AAAA,QACd,GAAI,GAAG,aAAa,QAAQ,EAAE,UAAU,GAAG,UAAU;AAAA,MACvD;AAEA,YAAM,WAAW,MAAM,GAAG,MAAM;AAEhC,YAAM,eAAe,MAAM,KAAK,MAAM,QAAQ,gBAAgB,QAAQ;AACtE,UAAI,cAAc;AAChB,cAAM,KAAK,MAAM,WAAW,gBAAgB,UAAU,IAAI;AAAA,MAC5D,OAAO;AACL,cAAM,KAAK,MAAM,QAAQ,gBAAgB,IAAI;AAAA,MAC/C;AAGA,iBAAW,QAAQ,cAAc;AAC/B,cAAM,eAAe,KAAK,QAAQ,KAAK,MAAM,aAAa,IAAI;AAC9D,cAAM,WAAW,MAAM,KAAK,MAAM,QAAQ,QAAQ,YAAY;AAC9D,YAAI,UAAU;AACZ;AACA,gBAAM,KAAK,MAAM,QAAQ,UAAU,SAAS,IAAI,SAAS;AAAA,QAC3D;AAAA,MACF;AAGA,iBAAW,YAAY,iBAAiB;AACtC,cAAM,eAAe,UAAU,QAAQ;AACvC,cAAM,aAAa,MAAM,KAAK,MAAM,QAAQ,iBAAiB,YAAY;AACzE,YAAI,YAAY;AACd;AACA,gBAAM,KAAK,MAAM,QAAQ,cAAc,UAAU,YAAY;AAAA,QAC/D;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,KAAK,IAAI,QAAQ,eAAe,YAAY;AAAA,EACvD;AAAA,EAEA,MAAM,WAAW,OAAe,MAAc,OAA4C;AACxF,UAAM,WAAW,MAAM,KAAK;AAAA,MAC1B,gCAAgC,KAAK,IAAI,IAAI;AAAA,MAC7C;AAAA,IACF;AAGA,UAAM,SAAS,SAAS,OAAO,UAAQ,CAAC,KAAK,YAAY;AAEzD,eAAW,SAAS,QAAQ;AAC1B,YAAM,kBAAkB,KAAK,iBAAiB,MAAM,OAAO,MAAM,IAAI;AAErE,YAAM,OAA4B;AAAA,QAChC,aAAa,MAAM;AAAA,QACnB,OAAO,MAAM;AAAA,QACb,KAAK,MAAM;AAAA,QACX,OAAO,MAAM;AAAA,QACb,QAAQ,MAAM,OAAO,IAAI,OAAK,EAAE,IAAI;AAAA,QACpC,WAAW,MAAM,UAAU,IAAI,OAAK,EAAE,KAAK;AAAA,QAC3C,MAAM,MAAM,QAAQ;AAAA,QACpB;AAAA,QACA,WAAW,MAAM;AAAA,QACjB,WAAW,MAAM;AAAA,QACjB,GAAI,MAAM,aAAa,QAAQ,EAAE,UAAU,MAAM,UAAU;AAAA,MAC7D;AAEA,YAAM,cAAc,YAAY,MAAM,MAAM;AAE5C,YAAM,eAAe,MAAM,KAAK,MAAM,QAAQ,gBAAgB,WAAW;AACzE,UAAI,cAAc;AAChB,cAAM,KAAK,MAAM,WAAW,gBAAgB,aAAa,IAAI;AAAA,MAC/D,OAAO;AACL,cAAM,KAAK,MAAM,QAAQ,gBAAgB,IAAI;AAAA,MAC/C;AAGA,iBAAW,YAAY,iBAAiB;AACtC,cAAM,eAAe,UAAU,QAAQ;AACvC,cAAM,aAAa,MAAM,KAAK,MAAM,QAAQ,iBAAiB,YAAY;AACzE,YAAI,YAAY;AACd,gBAAM,KAAK,MAAM,QAAQ,aAAa,cAAc,WAAW;AAAA,QACjE;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,QAAQ,OAAO,OAAO;AAAA,EACjC;AAAA,EAEA,MAAc,aAAa,OAAe,MAAc,UAAkB,OAAkC;AAC1G,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK;AAAA,QACvB,gCAAgC,KAAK,IAAI,IAAI,UAAU,QAAQ;AAAA,QAC/D;AAAA,MACF;AACA,aAAO,MAAM,IAAI,OAAK,EAAE,QAAQ;AAAA,IAClC,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EAEA,MAAc,kBACZ,OACA,MACA,UACA,OAC8C;AAC9C,QAAI;AACF,YAAM,UAAU,MAAM,KAAK;AAAA,QACzB,gCAAgC,KAAK,IAAI,IAAI,UAAU,QAAQ;AAAA,QAC/D;AAAA,MACF;AACA,UAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,YAAM,SAAS,QAAQ,IAAI,OAAK,EAAE,KAAK;AACvC,UAAI,OAAO,SAAS,mBAAmB,EAAG,QAAO;AACjD,UAAI,OAAO,SAAS,UAAU,EAAG,QAAO;AACxC,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAc,cACZ,OACA,MACA,KACA,OAC0C;AAC1C,QAAI;AACF,YAAM,SAAS,MAAM,KAAK;AAAA,QACxB,gCAAgC,KAAK,IAAI,IAAI,YAAY,GAAG;AAAA,QAC5D;AAAA,MACF;AACA,cAAQ,OAAO,OAAO;AAAA,QACpB,KAAK;AAAW,iBAAO;AAAA,QACvB,KAAK;AAAA,QAAW,KAAK;AAAS,iBAAO;AAAA,QACrC,KAAK;AAAW,iBAAO;AAAA,QACvB;AAAS,iBAAO;AAAA,MAClB;AAAA,IACF,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,iBAAiB,OAAe,MAA+B;AACrE,UAAM,OAAO,GAAG,KAAK,IAAI,QAAQ,EAAE;AACnC,UAAM,UAAU,KAAK,MAAM,cAAc;AACzC,WAAO,UAAU,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;AAAA,EAC5C;AAAA,EAEA,MAAc,UAAa,KAAa,OAA2B;AACjE,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,SAAS;AAAA,QACP,UAAU;AAAA,QACV,iBAAiB,UAAU,KAAK;AAAA,QAChC,cAAc;AAAA,QACd,wBAAwB;AAAA,MAC1B;AAAA,IACF,CAAC;AAED,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI;AAAA,QACR;AAAA,MAGF;AAAA,IACF;AAEA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,cAAc,SAAS,QAAQ,IAAI,mBAAmB;AAC5D,YAAM,YAAY,cACd,IAAI,KAAK,SAAS,aAAa,EAAE,IAAI,GAAI,EAAE,mBAAmB,IAC9D;AACJ,YAAM,IAAI,MAAM,6CAA6C,SAAS,GAAG;AAAA,IAC3E;AAEA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,gFAAgF;AAAA,IAClG;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,oBAAoB,SAAS,MAAM,KAAK,SAAS,UAAU,EAAE;AAAA,IAC/E;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AACF;","names":[]}
@@ -1,361 +0,0 @@
1
- import {
2
- getTrieDirectory,
3
- getWorkingDirectory
4
- } from "./chunk-VVITXIHN.js";
5
-
6
- // src/ai/client.ts
7
- import Anthropic from "@anthropic-ai/sdk";
8
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
9
- import { execSync } from "child_process";
10
- import { join } from "path";
11
- var clientInstance = null;
12
- var apiKeyChecked = false;
13
- var apiKeyAvailable = false;
14
- function isAIAvailable() {
15
- if (!apiKeyChecked) {
16
- checkAPIKey();
17
- }
18
- return apiKeyAvailable;
19
- }
20
- function getKeyFromKeychain() {
21
- if (process.platform !== "darwin") return null;
22
- try {
23
- const result = execSync(
24
- 'security find-generic-password -a "trie" -s "anthropic-api-key" -w 2>/dev/null',
25
- { encoding: "utf-8" }
26
- ).trim();
27
- return result.length > 10 ? result : null;
28
- } catch {
29
- return null;
30
- }
31
- }
32
- function saveKeyToKeychain(key) {
33
- if (process.platform !== "darwin") return false;
34
- try {
35
- try {
36
- execSync('security delete-generic-password -a "trie" -s "anthropic-api-key" 2>/dev/null');
37
- } catch {
38
- }
39
- execSync(`security add-generic-password -a "trie" -s "anthropic-api-key" -w "${key.replace(/"/g, '\\"')}"`);
40
- return true;
41
- } catch {
42
- return false;
43
- }
44
- }
45
- function setAPIKey(key) {
46
- process.env.ANTHROPIC_API_KEY = key;
47
- clientInstance = null;
48
- apiKeyChecked = true;
49
- apiKeyAvailable = true;
50
- if (saveKeyToKeychain(key)) {
51
- return { saved: true, method: "keychain" };
52
- }
53
- try {
54
- const workDir = getWorkingDirectory(void 0, true);
55
- const trieDir = getTrieDirectory(workDir);
56
- const configPath = join(trieDir, "config.json");
57
- let config = {};
58
- if (existsSync(configPath)) {
59
- config = JSON.parse(readFileSync(configPath, "utf-8"));
60
- }
61
- if (!config.apiKeys || typeof config.apiKeys !== "object") config.apiKeys = {};
62
- config.apiKeys.anthropic = key;
63
- mkdirSync(trieDir, { recursive: true });
64
- writeFileSync(configPath, JSON.stringify(config, null, 2));
65
- } catch {
66
- }
67
- return { saved: true, method: "config" };
68
- }
69
- function checkAPIKey() {
70
- apiKeyChecked = true;
71
- const envApiKey = process.env.ANTHROPIC_API_KEY;
72
- if (envApiKey && envApiKey.length > 10) {
73
- apiKeyAvailable = true;
74
- return;
75
- }
76
- const keychainKey = getKeyFromKeychain();
77
- if (keychainKey) {
78
- process.env.ANTHROPIC_API_KEY = keychainKey;
79
- apiKeyAvailable = true;
80
- return;
81
- }
82
- try {
83
- const workDir = getWorkingDirectory(void 0, true);
84
- const configPath = join(getTrieDirectory(workDir), "config.json");
85
- if (existsSync(configPath)) {
86
- const configContent = readFileSync(configPath, "utf-8");
87
- const config = JSON.parse(configContent);
88
- if (config.apiKeys?.anthropic && config.apiKeys.anthropic.length > 10) {
89
- process.env.ANTHROPIC_API_KEY = config.apiKeys.anthropic;
90
- apiKeyAvailable = true;
91
- return;
92
- }
93
- }
94
- } catch {
95
- }
96
- try {
97
- const workDir = getWorkingDirectory(void 0, true);
98
- const envFiles = [".env", ".env.local", ".env.production"];
99
- for (const envFile of envFiles) {
100
- const envPath = join(workDir, envFile);
101
- if (existsSync(envPath)) {
102
- const envContent = readFileSync(envPath, "utf-8");
103
- const lines = envContent.split("\n");
104
- for (const line of lines) {
105
- const match = line.match(/^\s*ANTHROPIC_API_KEY\s*=\s*(.+)$/);
106
- if (match && match[1]) {
107
- const key = match[1].trim().replace(/^["']|["']$/g, "");
108
- if (key.length > 10) {
109
- process.env.ANTHROPIC_API_KEY = key;
110
- apiKeyAvailable = true;
111
- return;
112
- }
113
- }
114
- }
115
- }
116
- }
117
- } catch {
118
- }
119
- apiKeyAvailable = false;
120
- }
121
- function getClient() {
122
- if (!isAIAvailable()) {
123
- throw new Error(
124
- "ANTHROPIC_API_KEY not found. Set it in your environment to enable AI-powered analysis.\nExample: export ANTHROPIC_API_KEY=sk-ant-..."
125
- );
126
- }
127
- if (!clientInstance) {
128
- clientInstance = new Anthropic();
129
- }
130
- return clientInstance;
131
- }
132
- function tryGetClient() {
133
- if (!isAIAvailable()) {
134
- return null;
135
- }
136
- if (!clientInstance) {
137
- clientInstance = new Anthropic();
138
- }
139
- return clientInstance;
140
- }
141
- async function runAIAnalysis(request) {
142
- const client = tryGetClient();
143
- if (!client) {
144
- return {
145
- success: false,
146
- content: "",
147
- error: "AI not available - ANTHROPIC_API_KEY not set"
148
- };
149
- }
150
- try {
151
- const response = await client.messages.create(
152
- {
153
- model: "claude-sonnet-4-20250514",
154
- // Increase default token limit to prevent truncation in Trie Watch
155
- max_tokens: request.maxTokens || 8192,
156
- temperature: request.temperature ?? 0.3,
157
- system: request.systemPrompt,
158
- messages: [
159
- {
160
- role: "user",
161
- content: request.userPrompt
162
- }
163
- ]
164
- },
165
- request.signal ? { signal: request.signal } : void 0
166
- );
167
- const textContent = response.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
168
- return {
169
- success: true,
170
- content: textContent,
171
- tokensUsed: {
172
- input: response.usage.input_tokens,
173
- output: response.usage.output_tokens
174
- }
175
- };
176
- } catch (error) {
177
- const errorMessage = error instanceof Error ? error.message : String(error);
178
- if (errorMessage.includes("authentication") || errorMessage.includes("API key")) {
179
- return {
180
- success: false,
181
- content: "",
182
- error: "Invalid API key. Check your ANTHROPIC_API_KEY."
183
- };
184
- }
185
- if (errorMessage.includes("rate limit")) {
186
- return {
187
- success: false,
188
- content: "",
189
- error: "Rate limited. Try again in a moment."
190
- };
191
- }
192
- return {
193
- success: false,
194
- content: "",
195
- error: `AI analysis failed: ${errorMessage}`
196
- };
197
- }
198
- }
199
- async function analyzeCodeIssues(request) {
200
- const systemPrompts = {
201
- validate: `You are a senior code reviewer. Analyze the following issues detected by static analysis.
202
- For each issue:
203
- 1. Determine if it's a TRUE POSITIVE (real problem) or FALSE POSITIVE (not actually an issue)
204
- 2. Provide a confidence score (0-100)
205
- 3. Explain your reasoning briefly
206
-
207
- Output format:
208
- ### Issue 1: [file:line]
209
- **Verdict:** TRUE_POSITIVE / FALSE_POSITIVE
210
- **Confidence:** [0-100]
211
- **Reason:** [one line explanation]
212
- **Fix:** [specific fix if true positive]`,
213
- expand: `You are a security expert and senior architect. Given these detected code issues, look deeper:
214
- 1. Are there related problems in the same code that were missed?
215
- 2. What's the root cause of these patterns?
216
- 3. What's the blast radius if these issues cause problems?
217
- 4. What architectural changes would prevent these patterns?
218
-
219
- Be specific. Reference line numbers. Provide code examples.`,
220
- fix: `You are an expert programmer. For each issue, provide a specific fix:
221
- 1. Show the exact code change needed
222
- 2. Explain why this fix works
223
- 3. Note any edge cases to consider
224
-
225
- Use markdown code blocks with the language specified.`,
226
- explain: `You are a patient teacher explaining code issues to a junior developer.
227
- For each issue:
228
- 1. Explain what the problem is in simple terms
229
- 2. Explain why it's a problem (what could go wrong)
230
- 3. Explain the fix and why it works
231
- 4. Provide a learning resource if relevant`
232
- };
233
- const issuesText = request.issues.map((issue, i) => {
234
- let text = `### Issue ${i + 1}: ${issue.file}${issue.line ? ":" + issue.line : ""}
235
- `;
236
- text += `**Problem:** ${issue.issue}
237
- `;
238
- if (issue.code) {
239
- text += `**Code:**
240
- \`\`\`
241
- ${issue.code}
242
- \`\`\`
243
- `;
244
- }
245
- return text;
246
- }).join("\n");
247
- const userPrompt = `${request.context ? `Context: ${request.context}
248
-
249
- ` : ""}${issuesText}`;
250
- return runAIAnalysis({
251
- systemPrompt: systemPrompts[request.analysisType],
252
- userPrompt,
253
- // Increase token limit to prevent response truncation
254
- maxTokens: 8192,
255
- temperature: 0.2
256
- });
257
- }
258
- function getAIStatusMessage() {
259
- if (isAIAvailable()) {
260
- return "[AI] AI-powered analysis enabled";
261
- }
262
- return "[!] AI not available (ANTHROPIC_API_KEY not set) - using pattern-only mode";
263
- }
264
- async function runAIWithTools(request) {
265
- const client = tryGetClient();
266
- if (!client) {
267
- return { success: false, content: "", error: "AI not available - ANTHROPIC_API_KEY not set" };
268
- }
269
- const maxRounds = request.maxToolRounds ?? 5;
270
- const messages = [...request.messages];
271
- const allToolCalls = [];
272
- const allToolResults = [];
273
- const { onProgress } = request;
274
- try {
275
- for (let round = 0; round < maxRounds; round++) {
276
- onProgress?.(round === 0 ? "Analyzing..." : `Processing (round ${round + 1})...`);
277
- const response = await client.messages.create({
278
- model: "claude-sonnet-4-20250514",
279
- // Increase default token limit to prevent truncation in Trie Watch
280
- max_tokens: request.maxTokens || 8192,
281
- temperature: 0.3,
282
- system: request.systemPrompt,
283
- messages,
284
- tools: request.tools
285
- });
286
- const toolUseBlocks = response.content.filter(
287
- (b) => b.type === "tool_use"
288
- );
289
- const textBlocks = response.content.filter(
290
- (b) => b.type === "text"
291
- );
292
- if (toolUseBlocks.length === 0) {
293
- const result2 = {
294
- success: true,
295
- content: textBlocks.map((b) => b.text).join("\n")
296
- };
297
- if (allToolCalls.length > 0) {
298
- result2.toolCalls = allToolCalls;
299
- result2.toolResults = allToolResults;
300
- }
301
- return result2;
302
- }
303
- messages.push({ role: "assistant", content: response.content });
304
- const toolResults = [];
305
- for (const block of toolUseBlocks) {
306
- const input = block.input ?? {};
307
- const toolLabel = block.name.replace(/^trie_/, "").replace(/_/g, " ");
308
- onProgress?.(`Running ${toolLabel}...`);
309
- let resultText;
310
- let isError = false;
311
- try {
312
- resultText = await request.executeTool(block.name, input, onProgress);
313
- } catch (err) {
314
- resultText = `Tool error: ${err instanceof Error ? err.message : String(err)}`;
315
- isError = true;
316
- }
317
- allToolCalls.push({ name: block.name, input, result: resultText });
318
- allToolResults.push(resultText);
319
- toolResults.push({
320
- type: "tool_result",
321
- tool_use_id: block.id,
322
- content: resultText,
323
- is_error: isError
324
- });
325
- }
326
- messages.push({ role: "user", content: toolResults });
327
- if (response.stop_reason === "end_turn") {
328
- const text = textBlocks.map((b) => b.text).join("\n");
329
- const result2 = { success: true, content: text || "Done." };
330
- if (allToolCalls.length > 0) {
331
- result2.toolCalls = allToolCalls;
332
- result2.toolResults = allToolResults;
333
- }
334
- return result2;
335
- }
336
- }
337
- const result = { success: true, content: "Reached maximum tool rounds." };
338
- if (allToolCalls.length > 0) {
339
- result.toolCalls = allToolCalls;
340
- result.toolResults = allToolResults;
341
- }
342
- return result;
343
- } catch (error) {
344
- const errorMessage = error instanceof Error ? error.message : String(error);
345
- return { success: false, content: "", error: `AI tool-use failed: ${errorMessage}` };
346
- }
347
- }
348
-
349
- export {
350
- isAIAvailable,
351
- getKeyFromKeychain,
352
- saveKeyToKeychain,
353
- setAPIKey,
354
- getClient,
355
- tryGetClient,
356
- runAIAnalysis,
357
- analyzeCodeIssues,
358
- getAIStatusMessage,
359
- runAIWithTools
360
- };
361
- //# sourceMappingURL=chunk-FQ45QP5A.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/ai/client.ts"],"sourcesContent":["/**\n * Centralized AI Client\n * \n * Handles API key detection, graceful fallbacks, and AI-powered analysis.\n * All agents use this for AI-enhanced analysis.\n * \n * API Key Setup:\n * 1. Set ANTHROPIC_API_KEY in your environment\n * 2. Or add it to your MCP server config (mcp.json)\n * 3. Or create .env file in your project root\n * \n * If no API key is found, agents fall back to pattern-only mode.\n */\n\nimport Anthropic from '@anthropic-ai/sdk';\nimport { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';\nimport { execSync } from 'node:child_process';\nimport { join } from 'path';\nimport { getWorkingDirectory, getTrieDirectory } from '../utils/workspace.js';\n\n// Cached client instance\nlet clientInstance: Anthropic | null = null;\nlet apiKeyChecked = false;\nlet apiKeyAvailable = false;\n\n/**\n * Check if AI is available (API key is set)\n */\nexport function isAIAvailable(): boolean {\n if (!apiKeyChecked) {\n checkAPIKey();\n }\n return apiKeyAvailable;\n}\n\n/**\n * Read API key from Apple Keychain (macOS only)\n */\nexport function getKeyFromKeychain(): string | null {\n if (process.platform !== 'darwin') return null;\n try {\n const result = execSync(\n 'security find-generic-password -a \"trie\" -s \"anthropic-api-key\" -w 2>/dev/null',\n { encoding: 'utf-8' }\n ).trim();\n return result.length > 10 ? result : null;\n } catch { return null; }\n}\n\n/**\n * Save API key to Apple Keychain (macOS only)\n */\nexport function saveKeyToKeychain(key: string): boolean {\n if (process.platform !== 'darwin') return false;\n try {\n try { execSync('security delete-generic-password -a \"trie\" -s \"anthropic-api-key\" 2>/dev/null'); } catch { /* not found */ }\n execSync(`security add-generic-password -a \"trie\" -s \"anthropic-api-key\" -w \"${key.replace(/\"/g, '\\\\\"')}\"`);\n return true;\n } catch { return false; }\n}\n\n/**\n * Save API key and refresh the cached state\n */\nexport function setAPIKey(key: string): { saved: boolean; method: 'keychain' | 'config' } {\n process.env.ANTHROPIC_API_KEY = key;\n clientInstance = null;\n apiKeyChecked = true;\n apiKeyAvailable = true;\n\n if (saveKeyToKeychain(key)) {\n return { saved: true, method: 'keychain' };\n }\n\n try {\n const workDir = getWorkingDirectory(undefined, true);\n const trieDir = getTrieDirectory(workDir);\n const configPath = join(trieDir, 'config.json');\n let config: Record<string, unknown> = {};\n if (existsSync(configPath)) {\n config = JSON.parse(readFileSync(configPath, 'utf-8'));\n }\n if (!config.apiKeys || typeof config.apiKeys !== 'object') config.apiKeys = {};\n (config.apiKeys as Record<string, string>).anthropic = key;\n mkdirSync(trieDir, { recursive: true });\n writeFileSync(configPath, JSON.stringify(config, null, 2));\n } catch { /* best effort */ }\n\n return { saved: true, method: 'config' };\n}\n\n/**\n * Check for API key in environment, keychain, config, and .env files\n */\nfunction checkAPIKey(): void {\n apiKeyChecked = true;\n \n // 1. Check standard env var first\n const envApiKey = process.env.ANTHROPIC_API_KEY;\n if (envApiKey && envApiKey.length > 10) {\n apiKeyAvailable = true;\n return;\n }\n\n // 2. Check Apple Keychain (macOS)\n const keychainKey = getKeyFromKeychain();\n if (keychainKey) {\n process.env.ANTHROPIC_API_KEY = keychainKey;\n apiKeyAvailable = true;\n return;\n }\n \n // 3. Check config file\n try {\n const workDir = getWorkingDirectory(undefined, true);\n const configPath = join(getTrieDirectory(workDir), 'config.json');\n \n if (existsSync(configPath)) {\n const configContent = readFileSync(configPath, 'utf-8');\n const config = JSON.parse(configContent);\n \n if (config.apiKeys?.anthropic && config.apiKeys.anthropic.length > 10) {\n process.env.ANTHROPIC_API_KEY = config.apiKeys.anthropic;\n apiKeyAvailable = true;\n return;\n }\n }\n } catch {\n // Config file doesn't exist or couldn't be read\n }\n \n // 4. Check .env files\n try {\n const workDir = getWorkingDirectory(undefined, true);\n const envFiles = ['.env', '.env.local', '.env.production'];\n \n for (const envFile of envFiles) {\n const envPath = join(workDir, envFile);\n if (existsSync(envPath)) {\n const envContent = readFileSync(envPath, 'utf-8');\n const lines = envContent.split('\\n');\n \n for (const line of lines) {\n const match = line.match(/^\\s*ANTHROPIC_API_KEY\\s*=\\s*(.+)$/);\n if (match && match[1]) {\n const key = match[1].trim().replace(/^[\"']|[\"']$/g, '');\n if (key.length > 10) {\n process.env.ANTHROPIC_API_KEY = key;\n apiKeyAvailable = true;\n return;\n }\n }\n }\n }\n }\n } catch {\n // .env file doesn't exist or couldn't be read\n }\n \n apiKeyAvailable = false;\n}\n\n/**\n * Get the Anthropic client (lazy initialized)\n * Throws if API key is not available\n */\nexport function getClient(): Anthropic {\n if (!isAIAvailable()) {\n throw new Error(\n 'ANTHROPIC_API_KEY not found. Set it in your environment to enable AI-powered analysis.\\n' +\n 'Example: export ANTHROPIC_API_KEY=sk-ant-...'\n );\n }\n \n if (!clientInstance) {\n clientInstance = new Anthropic();\n }\n \n return clientInstance;\n}\n\n/**\n * Try to get client, return null if not available\n */\nexport function tryGetClient(): Anthropic | null {\n if (!isAIAvailable()) {\n return null;\n }\n \n if (!clientInstance) {\n clientInstance = new Anthropic();\n }\n \n return clientInstance;\n}\n\nexport interface AIAnalysisRequest {\n systemPrompt: string;\n userPrompt: string;\n maxTokens?: number;\n temperature?: number;\n /** Optional AbortSignal to cancel the request */\n signal?: AbortSignal;\n}\n\nexport interface AIAnalysisResult {\n success: boolean;\n content: string;\n error?: string;\n tokensUsed?: {\n input: number;\n output: number;\n };\n}\n\n/**\n * Run AI analysis with the given prompts\n */\nexport async function runAIAnalysis(request: AIAnalysisRequest): Promise<AIAnalysisResult> {\n const client = tryGetClient();\n \n if (!client) {\n return {\n success: false,\n content: '',\n error: 'AI not available - ANTHROPIC_API_KEY not set'\n };\n }\n \n try {\n const response = await client.messages.create(\n {\n model: 'claude-sonnet-4-20250514',\n // Increase default token limit to prevent truncation in Trie Watch\n max_tokens: request.maxTokens || 8192,\n temperature: request.temperature ?? 0.3,\n system: request.systemPrompt,\n messages: [\n {\n role: 'user',\n content: request.userPrompt\n }\n ]\n },\n request.signal ? { signal: request.signal } : undefined\n );\n \n // Extract text content - ensure full text is preserved without truncation\n const textContent = response.content\n .filter((block): block is Anthropic.TextBlock => block.type === 'text')\n .map(block => block.text)\n .join('\\n');\n \n return {\n success: true,\n content: textContent,\n tokensUsed: {\n input: response.usage.input_tokens,\n output: response.usage.output_tokens\n }\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n \n // Check for specific error types\n if (errorMessage.includes('authentication') || errorMessage.includes('API key')) {\n return {\n success: false,\n content: '',\n error: 'Invalid API key. Check your ANTHROPIC_API_KEY.'\n };\n }\n \n if (errorMessage.includes('rate limit')) {\n return {\n success: false,\n content: '',\n error: 'Rate limited. Try again in a moment.'\n };\n }\n \n return {\n success: false,\n content: '',\n error: `AI analysis failed: ${errorMessage}`\n };\n }\n}\n\n/**\n * AI analysis for code issues\n */\nexport interface CodeIssueAnalysisRequest {\n issues: Array<{\n file: string;\n line?: number;\n issue: string;\n code?: string;\n }>;\n analysisType: 'validate' | 'expand' | 'fix' | 'explain';\n context?: string;\n}\n\n/**\n * Run AI analysis on code issues\n * Used by agents to validate, expand, or fix detected issues\n */\nexport async function analyzeCodeIssues(request: CodeIssueAnalysisRequest): Promise<AIAnalysisResult> {\n const systemPrompts: Record<CodeIssueAnalysisRequest['analysisType'], string> = {\n validate: `You are a senior code reviewer. Analyze the following issues detected by static analysis.\nFor each issue:\n1. Determine if it's a TRUE POSITIVE (real problem) or FALSE POSITIVE (not actually an issue)\n2. Provide a confidence score (0-100)\n3. Explain your reasoning briefly\n\nOutput format:\n### Issue 1: [file:line]\n**Verdict:** TRUE_POSITIVE / FALSE_POSITIVE\n**Confidence:** [0-100]\n**Reason:** [one line explanation]\n**Fix:** [specific fix if true positive]`,\n\n expand: `You are a security expert and senior architect. Given these detected code issues, look deeper:\n1. Are there related problems in the same code that were missed?\n2. What's the root cause of these patterns?\n3. What's the blast radius if these issues cause problems?\n4. What architectural changes would prevent these patterns?\n\nBe specific. Reference line numbers. Provide code examples.`,\n\n fix: `You are an expert programmer. For each issue, provide a specific fix:\n1. Show the exact code change needed\n2. Explain why this fix works\n3. Note any edge cases to consider\n\nUse markdown code blocks with the language specified.`,\n\n explain: `You are a patient teacher explaining code issues to a junior developer.\nFor each issue:\n1. Explain what the problem is in simple terms\n2. Explain why it's a problem (what could go wrong)\n3. Explain the fix and why it works\n4. Provide a learning resource if relevant`\n };\n\n const issuesText = request.issues.map((issue, i) => {\n let text = `### Issue ${i + 1}: ${issue.file}${issue.line ? ':' + issue.line : ''}\\n`;\n text += `**Problem:** ${issue.issue}\\n`;\n if (issue.code) {\n // Preserve complete code without truncation for analysis\n text += `**Code:**\\n\\`\\`\\`\\n${issue.code}\\n\\`\\`\\`\\n`;\n }\n return text;\n }).join('\\n');\n\n const userPrompt = `${request.context ? `Context: ${request.context}\\n\\n` : ''}${issuesText}`;\n\n return runAIAnalysis({\n systemPrompt: systemPrompts[request.analysisType],\n userPrompt,\n // Increase token limit to prevent response truncation\n maxTokens: 8192,\n temperature: 0.2\n });\n}\n\n/**\n * Get status message about AI availability\n */\nexport function getAIStatusMessage(): string {\n if (isAIAvailable()) {\n return '[AI] AI-powered analysis enabled';\n }\n return '[!] AI not available (ANTHROPIC_API_KEY not set) - using pattern-only mode';\n}\n\nexport interface AIToolCallRecord {\n name: string;\n input: Record<string, unknown>;\n result?: string;\n}\n\nexport interface AIToolCallResult {\n success: boolean;\n content: string;\n error?: string;\n toolCalls?: AIToolCallRecord[];\n toolResults?: string[];\n}\n\n/**\n * Run AI analysis with tool-use support.\n * Loops until Claude returns a final text response or hits maxToolRounds.\n */\nexport async function runAIWithTools(request: {\n systemPrompt: string;\n messages: Anthropic.MessageParam[];\n tools: Anthropic.Tool[];\n executeTool: (name: string, input: Record<string, unknown>, onProgress?: (msg: string) => void) => Promise<string>;\n maxTokens?: number;\n maxToolRounds?: number;\n onProgress?: (message: string) => void;\n}): Promise<AIToolCallResult> {\n const client = tryGetClient();\n if (!client) {\n return { success: false, content: '', error: 'AI not available - ANTHROPIC_API_KEY not set' };\n }\n\n const maxRounds = request.maxToolRounds ?? 5;\n const messages: Anthropic.MessageParam[] = [...request.messages];\n const allToolCalls: AIToolCallRecord[] = [];\n const allToolResults: string[] = [];\n\n const { onProgress } = request;\n\n try {\n for (let round = 0; round < maxRounds; round++) {\n onProgress?.(round === 0 ? 'Analyzing...' : `Processing (round ${round + 1})...`);\n const response = await client.messages.create({\n model: 'claude-sonnet-4-20250514',\n // Increase default token limit to prevent truncation in Trie Watch\n max_tokens: request.maxTokens || 8192,\n temperature: 0.3,\n system: request.systemPrompt,\n messages,\n tools: request.tools,\n });\n\n const toolUseBlocks = response.content.filter(\n (b): b is Anthropic.ToolUseBlock => b.type === 'tool_use'\n );\n const textBlocks = response.content.filter(\n (b): b is Anthropic.TextBlock => b.type === 'text'\n );\n\n if (toolUseBlocks.length === 0) {\n // Preserve complete text content without truncation\n const result: AIToolCallResult = {\n success: true,\n content: textBlocks.map(b => b.text).join('\\n'),\n };\n if (allToolCalls.length > 0) {\n result.toolCalls = allToolCalls;\n result.toolResults = allToolResults;\n }\n return result;\n }\n\n // Claude wants to use tools -- execute them\n messages.push({ role: 'assistant', content: response.content });\n\n const toolResults: Anthropic.ToolResultBlockParam[] = [];\n for (const block of toolUseBlocks) {\n const input = (block.input ?? {}) as Record<string, unknown>;\n const toolLabel = block.name.replace(/^trie_/, '').replace(/_/g, ' ');\n onProgress?.(`Running ${toolLabel}...`);\n let resultText: string;\n let isError = false;\n try {\n resultText = await request.executeTool(block.name, input, onProgress);\n } catch (err) {\n resultText = `Tool error: ${err instanceof Error ? err.message : String(err)}`;\n isError = true;\n }\n allToolCalls.push({ name: block.name, input, result: resultText });\n allToolResults.push(resultText);\n toolResults.push({\n type: 'tool_result',\n tool_use_id: block.id,\n content: resultText,\n is_error: isError,\n });\n }\n\n messages.push({ role: 'user', content: toolResults });\n\n if (response.stop_reason === 'end_turn') {\n // Preserve complete text without truncation\n const text = textBlocks.map(b => b.text).join('\\n');\n const result: AIToolCallResult = { success: true, content: text || 'Done.' };\n if (allToolCalls.length > 0) {\n result.toolCalls = allToolCalls;\n result.toolResults = allToolResults;\n }\n return result;\n }\n }\n\n const result: AIToolCallResult = { success: true, content: 'Reached maximum tool rounds.' };\n if (allToolCalls.length > 0) {\n result.toolCalls = allToolCalls;\n result.toolResults = allToolResults;\n }\n return result;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return { success: false, content: '', error: `AI tool-use failed: ${errorMessage}` };\n }\n}\n"],"mappings":"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","names":["result"]}