wave-agent-sdk 1.0.4 → 1.0.6

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 (194) hide show
  1. package/dist/agent.d.ts +23 -1
  2. package/dist/agent.js +34 -4
  3. package/dist/managers/aiManager.d.ts +36 -0
  4. package/dist/managers/aiManager.js +240 -8
  5. package/dist/managers/messageManager.d.ts +1 -3
  6. package/dist/managers/messageManager.js +19 -9
  7. package/dist/managers/slashCommandManager.js +28 -0
  8. package/dist/managers/subagentManager.d.ts +0 -2
  9. package/dist/services/session.js +2 -9
  10. package/dist/telemetry/instrumentation.d.ts +1 -1
  11. package/dist/telemetry/instrumentation.js +57 -28
  12. package/dist/tools/bashTool.js +16 -6
  13. package/dist/tools/grepTool.js +11 -3
  14. package/dist/types/index.d.ts +3 -0
  15. package/dist/types/messaging.d.ts +2 -0
  16. package/dist/utils/containerSetup.js +0 -3
  17. package/dist/utils/messageOperations.d.ts +10 -1
  18. package/dist/utils/messageOperations.js +19 -1
  19. package/dist/utils/notificationXml.js +5 -0
  20. package/dist/utils/ripgrep.d.ts +4 -4
  21. package/dist/utils/ripgrep.js +4 -27
  22. package/package.json +20 -8
  23. package/bin/rg +0 -79
  24. package/dist/managers/forkedAgentManager.d.ts +0 -50
  25. package/dist/managers/forkedAgentManager.js +0 -130
  26. package/scripts/install_ripgrep.js +0 -111
  27. package/scripts/postinstall.js +0 -38
  28. package/src/agent.ts +0 -1271
  29. package/src/constants/subagents.ts +0 -4
  30. package/src/constants/toolLimits.ts +0 -15
  31. package/src/constants/tools.ts +0 -24
  32. package/src/core/plugin.ts +0 -237
  33. package/src/core/session.ts +0 -9
  34. package/src/index.ts +0 -38
  35. package/src/managers/MemoryRuleManager.ts +0 -198
  36. package/src/managers/aiManager.ts +0 -2429
  37. package/src/managers/backgroundTaskManager.ts +0 -480
  38. package/src/managers/bangManager.ts +0 -119
  39. package/src/managers/cronManager.ts +0 -401
  40. package/src/managers/foregroundTaskManager.ts +0 -30
  41. package/src/managers/forkedAgentManager.ts +0 -216
  42. package/src/managers/hookManager.ts +0 -1089
  43. package/src/managers/liveConfigManager.ts +0 -445
  44. package/src/managers/lspManager.ts +0 -470
  45. package/src/managers/mcpManager.ts +0 -1010
  46. package/src/managers/messageManager.ts +0 -1180
  47. package/src/managers/messageQueue.ts +0 -157
  48. package/src/managers/permissionManager.ts +0 -1140
  49. package/src/managers/planManager.ts +0 -112
  50. package/src/managers/pluginManager.ts +0 -330
  51. package/src/managers/pluginScopeManager.ts +0 -124
  52. package/src/managers/reversionManager.ts +0 -178
  53. package/src/managers/skillManager.ts +0 -578
  54. package/src/managers/slashCommandManager.ts +0 -564
  55. package/src/managers/subagentManager.ts +0 -915
  56. package/src/managers/toolManager.ts +0 -489
  57. package/src/managers/workflowManager.ts +0 -493
  58. package/src/prompts/autoMemory.ts +0 -33
  59. package/src/prompts/autoMemoryExtraction.ts +0 -146
  60. package/src/prompts/index.ts +0 -506
  61. package/src/prompts/planModeReminders.ts +0 -126
  62. package/src/services/GitService.ts +0 -131
  63. package/src/services/MarketplaceService.ts +0 -1052
  64. package/src/services/MemoryRuleService.ts +0 -71
  65. package/src/services/aiService.ts +0 -919
  66. package/src/services/authService.ts +0 -565
  67. package/src/services/autoMemoryService.ts +0 -280
  68. package/src/services/configurationService.ts +0 -1520
  69. package/src/services/fileWatcher.ts +0 -319
  70. package/src/services/hook.ts +0 -348
  71. package/src/services/initializationService.ts +0 -357
  72. package/src/services/interactionService.ts +0 -229
  73. package/src/services/jsonlHandler.ts +0 -295
  74. package/src/services/memory.ts +0 -234
  75. package/src/services/pluginLoader.ts +0 -252
  76. package/src/services/remoteSettingsService.ts +0 -366
  77. package/src/services/reversionService.ts +0 -122
  78. package/src/services/session.ts +0 -922
  79. package/src/services/taskManager.ts +0 -321
  80. package/src/telemetry/events.ts +0 -65
  81. package/src/telemetry/instrumentation.ts +0 -499
  82. package/src/telemetry/sessionTracing.ts +0 -348
  83. package/src/tools/agentTool.ts +0 -322
  84. package/src/tools/askUserQuestion.ts +0 -160
  85. package/src/tools/bashTool.ts +0 -621
  86. package/src/tools/buildTool.ts +0 -61
  87. package/src/tools/cronCreateTool.ts +0 -161
  88. package/src/tools/cronDeleteTool.ts +0 -51
  89. package/src/tools/cronListTool.ts +0 -47
  90. package/src/tools/editTool.ts +0 -313
  91. package/src/tools/enterPlanMode.ts +0 -124
  92. package/src/tools/enterWorktreeTool.ts +0 -190
  93. package/src/tools/exitPlanMode.ts +0 -139
  94. package/src/tools/exitWorktreeTool.ts +0 -236
  95. package/src/tools/globTool.ts +0 -167
  96. package/src/tools/grepTool.ts +0 -399
  97. package/src/tools/lspTool.ts +0 -883
  98. package/src/tools/readTool.ts +0 -426
  99. package/src/tools/skillTool.ts +0 -248
  100. package/src/tools/taskManagementTools.ts +0 -574
  101. package/src/tools/taskStopTool.ts +0 -78
  102. package/src/tools/types.ts +0 -137
  103. package/src/tools/webFetchTool.ts +0 -373
  104. package/src/tools/workflowTool.ts +0 -205
  105. package/src/tools/writeTool.ts +0 -267
  106. package/src/types/agent.ts +0 -128
  107. package/src/types/auth.ts +0 -19
  108. package/src/types/commands.ts +0 -30
  109. package/src/types/config.ts +0 -43
  110. package/src/types/configuration.ts +0 -168
  111. package/src/types/core.ts +0 -105
  112. package/src/types/cron.ts +0 -13
  113. package/src/types/environment.ts +0 -106
  114. package/src/types/fileSearch.ts +0 -4
  115. package/src/types/history.ts +0 -7
  116. package/src/types/hooks.ts +0 -256
  117. package/src/types/index.ts +0 -41
  118. package/src/types/lsp.ts +0 -98
  119. package/src/types/marketplace.ts +0 -71
  120. package/src/types/mcp.ts +0 -43
  121. package/src/types/memoryRule.ts +0 -31
  122. package/src/types/messaging.ts +0 -114
  123. package/src/types/permissions.ts +0 -80
  124. package/src/types/plugins.ts +0 -39
  125. package/src/types/processes.ts +0 -99
  126. package/src/types/reversion.ts +0 -29
  127. package/src/types/session.ts +0 -12
  128. package/src/types/skills.ts +0 -108
  129. package/src/types/tasks.ts +0 -13
  130. package/src/types/telemetry.ts +0 -98
  131. package/src/types/tools.ts +0 -44
  132. package/src/types/workflow.ts +0 -6
  133. package/src/utils/abortUtils.ts +0 -118
  134. package/src/utils/atomicWrite.ts +0 -61
  135. package/src/utils/bashParser.ts +0 -899
  136. package/src/utils/cacheControlUtils.ts +0 -426
  137. package/src/utils/commandArgumentParser.ts +0 -161
  138. package/src/utils/commandPathResolver.ts +0 -88
  139. package/src/utils/configPaths.ts +0 -220
  140. package/src/utils/configValidator.ts +0 -100
  141. package/src/utils/constants.ts +0 -39
  142. package/src/utils/container.ts +0 -92
  143. package/src/utils/containerSetup.ts +0 -371
  144. package/src/utils/convertMessagesForAPI.ts +0 -326
  145. package/src/utils/cronTasks.ts +0 -128
  146. package/src/utils/cronTasksLock.ts +0 -182
  147. package/src/utils/cronToHuman.ts +0 -99
  148. package/src/utils/customCommands.ts +0 -95
  149. package/src/utils/editUtils.ts +0 -20
  150. package/src/utils/fileFormat.ts +0 -40
  151. package/src/utils/fileSearch.ts +0 -151
  152. package/src/utils/fileUtils.ts +0 -223
  153. package/src/utils/gitUtils.ts +0 -315
  154. package/src/utils/globalLogger.ts +0 -128
  155. package/src/utils/groupMessagesByApiRound.ts +0 -121
  156. package/src/utils/hookMatcher.ts +0 -176
  157. package/src/utils/markdownParser.ts +0 -317
  158. package/src/utils/mcpUtils.ts +0 -138
  159. package/src/utils/messageOperations.ts +0 -641
  160. package/src/utils/modelCapabilities.ts +0 -30
  161. package/src/utils/nameGenerator.ts +0 -99
  162. package/src/utils/notificationXml.ts +0 -57
  163. package/src/utils/openaiClient.ts +0 -251
  164. package/src/utils/parseCronExpression.ts +0 -78
  165. package/src/utils/path.ts +0 -72
  166. package/src/utils/pathEncoder.ts +0 -400
  167. package/src/utils/pathSafety.ts +0 -40
  168. package/src/utils/promptHistory.ts +0 -168
  169. package/src/utils/ripgrep.ts +0 -35
  170. package/src/utils/shellResolver.ts +0 -190
  171. package/src/utils/skillParser.ts +0 -246
  172. package/src/utils/stringUtils.ts +0 -156
  173. package/src/utils/subagentParser.ts +0 -279
  174. package/src/utils/taskReminder.ts +0 -96
  175. package/src/utils/tokenCalculation.ts +0 -43
  176. package/src/utils/tokenEstimate.ts +0 -34
  177. package/src/utils/toolResultStorage.ts +0 -117
  178. package/src/utils/worktreeSession.ts +0 -26
  179. package/src/utils/worktreeUtils.ts +0 -786
  180. package/src/workflow/budgetTracker.ts +0 -34
  181. package/src/workflow/concurrencyLimiter.ts +0 -47
  182. package/src/workflow/journal.ts +0 -95
  183. package/src/workflow/progressReporter.ts +0 -141
  184. package/src/workflow/runState.ts +0 -65
  185. package/src/workflow/scriptRuntime.ts +0 -274
  186. package/src/workflow/structuredOutput.ts +0 -123
  187. package/src/workflow/types.ts +0 -101
  188. package/src/workflow/workflowApis.ts +0 -410
  189. package/vendor/ripgrep/linux-aarch64/rg +0 -0
  190. package/vendor/ripgrep/linux-x86_64/rg +0 -0
  191. package/vendor/ripgrep/macos-aarch64/rg +0 -0
  192. package/vendor/ripgrep/macos-x86_64/rg +0 -0
  193. package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
  194. package/vendor/ripgrep/windows-x86_64/rg.exe +0 -0
@@ -1,883 +0,0 @@
1
- import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
2
- import { relative, join, isAbsolute } from "path";
3
- import * as fs from "fs";
4
- import { logger } from "../utils/globalLogger.js";
5
- import { getDisplayPath, toPosixPath } from "../utils/path.js";
6
- import { LSP_TOOL_NAME } from "../constants/tools.js";
7
- import type {
8
- LspLocation as Location,
9
- LspLocationLink as LocationLink,
10
- LspHover as Hover,
11
- LspSymbolInformation as SymbolInformation,
12
- LspDocumentSymbol as DocumentSymbol,
13
- LspCallHierarchyItem as CallHierarchyItem,
14
- LspCallHierarchyIncomingCall as CallHierarchyIncomingCall,
15
- LspCallHierarchyOutgoingCall as CallHierarchyOutgoingCall,
16
- } from "../types/lsp.js";
17
-
18
- export const MAX_RESULTS = 1000;
19
- export const MAX_FILES = 100;
20
-
21
- /**
22
- * Formats an LSP URI into a readable file path
23
- */
24
- function formatUri(uri: string, workdir?: string): string {
25
- if (!uri) {
26
- logger.warn(
27
- "formatUri called with undefined URI - indicates malformed LSP server response",
28
- );
29
- return "<unknown location>";
30
- }
31
-
32
- let path = uri.replace(/^file:\/\//, "");
33
- try {
34
- path = decodeURIComponent(path);
35
- } catch (e) {
36
- const message = e instanceof Error ? e.message : String(e);
37
- logger.warn(
38
- `Failed to decode LSP URI '${uri}': ${message}. Using un-decoded path: ${path}`,
39
- );
40
- }
41
-
42
- if (workdir) {
43
- const relativePath = relative(workdir, path);
44
- if (
45
- relativePath.length < path.length &&
46
- !relativePath.startsWith("../../")
47
- ) {
48
- return toPosixPath(relativePath);
49
- }
50
- }
51
- return toPosixPath(path);
52
- }
53
-
54
- /**
55
- * Formats an error message when no results are found, including context from the file
56
- */
57
- function formatNoResultError(
58
- message: string,
59
- filePath: string,
60
- line: number,
61
- character: number,
62
- workdir?: string,
63
- ): string {
64
- let absolutePath = filePath;
65
- if (!isAbsolute(filePath) && workdir) {
66
- absolutePath = join(workdir, filePath);
67
- }
68
-
69
- let lineContent = "";
70
- try {
71
- const content = fs.readFileSync(absolutePath, "utf-8");
72
- const lines = content.split("\n");
73
- if (line > 0 && line <= lines.length) {
74
- lineContent = lines[line - 1];
75
- }
76
- } catch {
77
- // Ignore file read errors
78
- }
79
-
80
- const displayPath = getDisplayPath(filePath, workdir || "");
81
- const baseMessage = `${message}\n\nPlease check if the character offset is correct and points to a valid symbol.`;
82
-
83
- if (lineContent) {
84
- const pointer = " ".repeat(Math.max(0, character - 1)) + "^";
85
- return `${message}\n\nContext at ${displayPath}:${line}:${character}:\n${lineContent}\n${pointer}\n\nPlease check if the character offset is correct and points to a valid symbol.`;
86
- }
87
-
88
- return baseMessage;
89
- }
90
-
91
- /**
92
- * Groups items by their URI
93
- */
94
- function groupItemsByUri<
95
- T extends { uri: string } | { location: Location } | LocationLink,
96
- >(items: T[], workdir?: string): Map<string, T[]> {
97
- const map = new Map<string, T[]>();
98
- for (const item of items) {
99
- let uri: string;
100
- if ("uri" in item) {
101
- uri = item.uri;
102
- } else if ("location" in item) {
103
- uri = item.location.uri;
104
- } else {
105
- uri = item.targetUri;
106
- }
107
- const path = formatUri(uri, workdir);
108
- const existing = map.get(path);
109
- if (existing) {
110
- existing.push(item);
111
- } else {
112
- map.set(path, [item]);
113
- }
114
- }
115
- return map;
116
- }
117
-
118
- /**
119
- * Formats a single location as path:line:character
120
- */
121
- function formatLocation(loc: Location, workdir?: string): string {
122
- const path = formatUri(loc.uri, workdir);
123
- const line = loc.range.start.line + 1;
124
- const character = loc.range.start.character + 1;
125
- return `${path}:${line}:${character}`;
126
- }
127
-
128
- /**
129
- * Converts a LocationLink to a Location
130
- */
131
- function locationLinkToLocation(link: LocationLink): Location {
132
- return {
133
- uri: link.targetUri,
134
- range: link.targetSelectionRange || link.targetRange,
135
- };
136
- }
137
-
138
- /**
139
- * Checks if an object is a LocationLink
140
- */
141
- function isLocationLink(loc: Location | LocationLink): loc is LocationLink {
142
- return "targetUri" in loc;
143
- }
144
-
145
- /**
146
- * Formats the result of a goToDefinition operation
147
- */
148
- function formatGoToDefinitionResult(
149
- result: Location | Location[] | LocationLink | LocationLink[] | null,
150
- filePath: string,
151
- line: number,
152
- character: number,
153
- operation: string,
154
- workdir?: string,
155
- ): string {
156
- const message =
157
- operation === "goToImplementation"
158
- ? "No implementation found. This may occur if the cursor is not on a symbol, or if the implementation is in an external library not indexed by the LSP server."
159
- : "No definition found. This may occur if the cursor is not on a symbol, or if the definition is in an external library not indexed by the LSP server.";
160
-
161
- if (!result) {
162
- return formatNoResultError(message, filePath, line, character, workdir);
163
- }
164
-
165
- if (Array.isArray(result)) {
166
- const locations = result.map((loc) =>
167
- isLocationLink(loc) ? locationLinkToLocation(loc) : loc,
168
- );
169
- const validLocations = locations.filter((loc) => loc && loc.uri);
170
-
171
- if (validLocations.length === 0) {
172
- return formatNoResultError(message, filePath, line, character, workdir);
173
- }
174
-
175
- if (validLocations.length === 1) {
176
- return `Defined in ${formatLocation(validLocations[0], workdir)}`;
177
- }
178
-
179
- const shownLocations = validLocations.slice(0, MAX_RESULTS);
180
- const formatted = shownLocations
181
- .map((loc) => ` ${formatLocation(loc, workdir)}`)
182
- .join("\n");
183
-
184
- let header = `Found ${validLocations.length} definitions:`;
185
- if (validLocations.length > MAX_RESULTS) {
186
- header += ` (showing first ${MAX_RESULTS})`;
187
- }
188
- return `${header}\n${formatted}`;
189
- }
190
-
191
- const loc = isLocationLink(result) ? locationLinkToLocation(result) : result;
192
- return `Defined in ${formatLocation(loc, workdir)}`;
193
- }
194
-
195
- /**
196
- * Formats the result of a findReferences operation
197
- */
198
- function formatFindReferencesResult(
199
- result: Location[] | null,
200
- filePath: string,
201
- line: number,
202
- character: number,
203
- workdir?: string,
204
- ): string {
205
- const message =
206
- "No references found. This may occur if the symbol has no usages, or if the LSP server has not fully indexed the workspace.";
207
-
208
- if (!result || result.length === 0) {
209
- return formatNoResultError(message, filePath, line, character, workdir);
210
- }
211
-
212
- const validLocations = result.filter((loc) => loc && loc.uri);
213
- if (validLocations.length === 0) {
214
- return formatNoResultError(message, filePath, line, character, workdir);
215
- }
216
-
217
- if (validLocations.length === 1) {
218
- return `Found 1 reference:\n ${formatLocation(validLocations[0], workdir)}`;
219
- }
220
-
221
- const grouped = groupItemsByUri(validLocations, workdir);
222
- const totalResults = validLocations.length;
223
- const totalFiles = grouped.size;
224
-
225
- let resultsShown = 0;
226
- let filesShown = 0;
227
- const lines: string[] = [];
228
-
229
- for (const [path, locs] of grouped) {
230
- if (resultsShown >= MAX_RESULTS || filesShown >= MAX_FILES) break;
231
-
232
- filesShown++;
233
- const remainingResults = MAX_RESULTS - resultsShown;
234
- const locsToShow = locs.slice(0, remainingResults);
235
- const isTruncated = locsToShow.length < locs.length;
236
-
237
- lines.push(`\n${path}:`);
238
- for (const loc of locsToShow) {
239
- const line = loc.range.start.line + 1;
240
- const character = loc.range.start.character + 1;
241
- lines.push(` Line ${line}:${character}`);
242
- }
243
-
244
- if (isTruncated) {
245
- lines.push(
246
- ` ... and ${locs.length - locsToShow.length} more in this file`,
247
- );
248
- }
249
-
250
- resultsShown += locsToShow.length;
251
- }
252
-
253
- let header = `Found ${totalResults} references across ${totalFiles} files:`;
254
- if (totalResults > resultsShown || totalFiles > filesShown) {
255
- header += ` (showing first ${resultsShown} results and ${filesShown} files)`;
256
- }
257
- lines.unshift(header);
258
-
259
- return lines.join("\n");
260
- }
261
-
262
- /**
263
- * Formats hover contents
264
- */
265
- function formatHoverContents(contents: Hover["contents"]): string {
266
- if (Array.isArray(contents)) {
267
- return contents
268
- .map((c) => (typeof c === "string" ? c : c.value))
269
- .join("\n\n");
270
- }
271
- if (typeof contents === "string") {
272
- return contents;
273
- }
274
- return contents.value;
275
- }
276
-
277
- /**
278
- * Formats the result of a hover operation
279
- */
280
- function formatHoverResult(
281
- result: Hover | null,
282
- filePath: string,
283
- line: number,
284
- character: number,
285
- workdir?: string,
286
- ): string {
287
- if (!result) {
288
- return formatNoResultError(
289
- "No hover information available. This may occur if the cursor is not on a symbol, or if the LSP server has not fully indexed the file.",
290
- filePath,
291
- line,
292
- character,
293
- workdir,
294
- );
295
- }
296
-
297
- const contents = formatHoverContents(result.contents);
298
- if (result.range) {
299
- const line = result.range.start.line + 1;
300
- const character = result.range.start.character + 1;
301
- return `Hover info at ${line}:${character}:\n\n${contents}`;
302
- }
303
- return contents;
304
- }
305
-
306
- /**
307
- * Gets the name of a symbol kind
308
- */
309
- function getSymbolKindName(kind: number): string {
310
- const kinds: Record<number, string> = {
311
- 1: "File",
312
- 2: "Module",
313
- 3: "Namespace",
314
- 4: "Package",
315
- 5: "Class",
316
- 6: "Method",
317
- 7: "Property",
318
- 8: "Field",
319
- 9: "Constructor",
320
- 10: "Enum",
321
- 11: "Interface",
322
- 12: "Function",
323
- 13: "Variable",
324
- 14: "Constant",
325
- 15: "String",
326
- 16: "Number",
327
- 17: "Boolean",
328
- 18: "Array",
329
- 19: "Object",
330
- 20: "Key",
331
- 21: "Null",
332
- 22: "EnumMember",
333
- 23: "Struct",
334
- 24: "Event",
335
- 25: "Operator",
336
- 26: "TypeParameter",
337
- };
338
- return kinds[kind] || "Unknown";
339
- }
340
-
341
- /**
342
- * Formats a single document symbol recursively
343
- */
344
- function formatDocumentSymbol(
345
- symbol: DocumentSymbol,
346
- state: { count: number; total: number },
347
- depth = 0,
348
- ): string[] {
349
- state.total++;
350
- const results: string[] = [];
351
-
352
- if (state.count < MAX_RESULTS) {
353
- const indent = " ".repeat(depth);
354
- const kindName = getSymbolKindName(symbol.kind);
355
- let line = `${indent}${symbol.name} (${kindName})`;
356
-
357
- if (symbol.detail) {
358
- line += ` ${symbol.detail}`;
359
- }
360
-
361
- const startLine = symbol.range.start.line + 1;
362
- line += ` - Line ${startLine}`;
363
- results.push(line);
364
- state.count++;
365
- }
366
-
367
- if (symbol.children && symbol.children.length > 0) {
368
- for (const child of symbol.children) {
369
- results.push(...formatDocumentSymbol(child, state, depth + 1));
370
- }
371
- }
372
-
373
- return results;
374
- }
375
-
376
- /**
377
- * Formats the result of a documentSymbol operation
378
- */
379
- function formatDocumentSymbolResult(
380
- result: DocumentSymbol[] | SymbolInformation[] | null,
381
- workdir?: string,
382
- ): string {
383
- if (!result || result.length === 0) {
384
- return "No symbols found in document. This may occur if the file is empty, not supported by the LSP server, or if the server has not fully indexed the file.";
385
- }
386
-
387
- const first = result[0];
388
- if (first && "location" in first) {
389
- return formatWorkspaceSymbolResult(result as SymbolInformation[], workdir);
390
- }
391
-
392
- const state = { count: 0, total: 0 };
393
- const lines: string[] = [];
394
- for (const symbol of result as DocumentSymbol[]) {
395
- lines.push(...formatDocumentSymbol(symbol, state));
396
- }
397
-
398
- let header = "Document symbols:";
399
- if (state.total > MAX_RESULTS) {
400
- header += ` (showing first ${MAX_RESULTS} of ${state.total})`;
401
- }
402
- lines.unshift(header);
403
-
404
- return lines.join("\n");
405
- }
406
-
407
- /**
408
- * Formats the result of a workspaceSymbol operation
409
- */
410
- function formatWorkspaceSymbolResult(
411
- result: SymbolInformation[] | null,
412
- workdir?: string,
413
- ): string {
414
- if (!result || result.length === 0) {
415
- return "No symbols found in workspace. This may occur if the workspace is empty, or if the LSP server has not finished indexing the project.";
416
- }
417
-
418
- const validSymbols = result.filter((s) => s && s.location && s.location.uri);
419
- if (validSymbols.length === 0) {
420
- return "No symbols found in workspace. This may occur if the workspace is empty, or if the LSP server has not finished indexing the project.";
421
- }
422
-
423
- const grouped = groupItemsByUri(validSymbols, workdir);
424
- const totalResults = validSymbols.length;
425
- const totalFiles = grouped.size;
426
-
427
- let resultsShown = 0;
428
- let filesShown = 0;
429
- const lines: string[] = [];
430
-
431
- for (const [path, symbols] of grouped) {
432
- if (resultsShown >= MAX_RESULTS || filesShown >= MAX_FILES) break;
433
-
434
- filesShown++;
435
- const remainingResults = MAX_RESULTS - resultsShown;
436
- const symbolsToShow = symbols.slice(0, remainingResults);
437
- const isTruncated = symbolsToShow.length < symbols.length;
438
-
439
- lines.push(`\n${path}:`);
440
- for (const s of symbolsToShow) {
441
- const kindName = getSymbolKindName(s.kind);
442
- const startLine = s.location.range.start.line + 1;
443
- let line = ` ${s.name} (${kindName}) - Line ${startLine}`;
444
- if (s.containerName) {
445
- line += ` in ${s.containerName}`;
446
- }
447
- lines.push(line);
448
- }
449
-
450
- if (isTruncated) {
451
- lines.push(
452
- ` ... and ${symbols.length - symbolsToShow.length} more in this file`,
453
- );
454
- }
455
-
456
- resultsShown += symbolsToShow.length;
457
- }
458
-
459
- let header = `Found ${totalResults} symbol${totalResults === 1 ? "" : "s"} in workspace:`;
460
- if (totalResults > resultsShown || totalFiles > filesShown) {
461
- header += ` (showing first ${resultsShown} results and ${filesShown} files)`;
462
- }
463
- lines.unshift(header);
464
-
465
- return lines.join("\n");
466
- }
467
-
468
- /**
469
- * Formats a call hierarchy item
470
- */
471
- function formatCallHierarchyItem(
472
- item: CallHierarchyItem,
473
- workdir?: string,
474
- ): string {
475
- if (!item.uri) {
476
- logger.warn("formatCallHierarchyItem: CallHierarchyItem has undefined URI");
477
- return `${item.name} (${getSymbolKindName(item.kind)}) - <unknown location>`;
478
- }
479
-
480
- const path = formatUri(item.uri, workdir);
481
- const startLine = item.range.start.line + 1;
482
- const kindName = getSymbolKindName(item.kind);
483
- let line = `${item.name} (${kindName}) - ${path}:${startLine}`;
484
-
485
- if (item.detail) {
486
- line += ` [${item.detail}]`;
487
- }
488
- return line;
489
- }
490
-
491
- /**
492
- * Formats the result of a prepareCallHierarchy operation
493
- */
494
- function formatPrepareCallHierarchyResult(
495
- result: CallHierarchyItem[] | null,
496
- filePath: string,
497
- line: number,
498
- character: number,
499
- workdir?: string,
500
- ): string {
501
- if (!result || result.length === 0) {
502
- return formatNoResultError(
503
- "No call hierarchy item found at this position",
504
- filePath,
505
- line,
506
- character,
507
- workdir,
508
- );
509
- }
510
-
511
- if (result.length === 1) {
512
- return `Call hierarchy item: ${formatCallHierarchyItem(result[0], workdir)}`;
513
- }
514
-
515
- const shownItems = result.slice(0, MAX_RESULTS);
516
- const lines: string[] = [];
517
- for (const item of shownItems) {
518
- lines.push(` ${formatCallHierarchyItem(item, workdir)}`);
519
- }
520
-
521
- let header = `Found ${result.length} call hierarchy items:`;
522
- if (result.length > MAX_RESULTS) {
523
- header += ` (showing first ${MAX_RESULTS})`;
524
- }
525
- lines.unshift(header);
526
-
527
- return lines.join("\n");
528
- }
529
-
530
- /**
531
- * Formats the result of an incomingCalls operation
532
- */
533
- function formatIncomingCallsResult(
534
- result: CallHierarchyIncomingCall[] | null,
535
- workdir?: string,
536
- ): string {
537
- if (!result || result.length === 0) {
538
- return "No incoming calls found (nothing calls this function)";
539
- }
540
-
541
- const grouped = new Map<string, CallHierarchyIncomingCall[]>();
542
- const totalResults = result.length;
543
-
544
- for (const call of result) {
545
- if (!call.from) {
546
- logger.warn(
547
- "formatIncomingCallsResult: CallHierarchyIncomingCall has undefined from field",
548
- );
549
- continue;
550
- }
551
- const path = formatUri(call.from.uri, workdir);
552
- const existing = grouped.get(path);
553
- if (existing) {
554
- existing.push(call);
555
- } else {
556
- grouped.set(path, [call]);
557
- }
558
- }
559
-
560
- const totalFiles = grouped.size;
561
- let resultsShown = 0;
562
- let filesShown = 0;
563
- const lines: string[] = [];
564
-
565
- for (const [path, calls] of grouped) {
566
- if (resultsShown >= MAX_RESULTS || filesShown >= MAX_FILES) break;
567
-
568
- filesShown++;
569
- const remainingResults = MAX_RESULTS - resultsShown;
570
- const callsToShow = calls.slice(0, remainingResults);
571
- const isTruncated = callsToShow.length < calls.length;
572
-
573
- lines.push(`\n${path}:`);
574
- for (const call of callsToShow) {
575
- if (!call.from) continue;
576
- const kindName = getSymbolKindName(call.from.kind);
577
- const startLine = call.from.range.start.line + 1;
578
- let line = ` ${call.from.name} (${kindName}) - Line ${startLine}`;
579
-
580
- if (call.fromRanges && call.fromRanges.length > 0) {
581
- const ranges = call.fromRanges
582
- .map((r) => `${r.start.line + 1}:${r.start.character + 1}`)
583
- .join(", ");
584
- line += ` [calls at: ${ranges}]`;
585
- }
586
- lines.push(line);
587
- }
588
-
589
- if (isTruncated) {
590
- lines.push(
591
- ` ... and ${calls.length - callsToShow.length} more in this file`,
592
- );
593
- }
594
-
595
- resultsShown += callsToShow.length;
596
- }
597
-
598
- let header = `Found ${totalResults} incoming call${totalResults === 1 ? "" : "s"}:`;
599
- if (totalResults > resultsShown || totalFiles > filesShown) {
600
- header += ` (showing first ${resultsShown} results and ${filesShown} files)`;
601
- }
602
- lines.unshift(header);
603
-
604
- return lines.join("\n");
605
- }
606
-
607
- /**
608
- * Formats the result of an outgoingCalls operation
609
- */
610
- function formatOutgoingCallsResult(
611
- result: CallHierarchyOutgoingCall[] | null,
612
- workdir?: string,
613
- ): string {
614
- if (!result || result.length === 0) {
615
- return "No outgoing calls found (this function calls nothing)";
616
- }
617
-
618
- const grouped = new Map<string, CallHierarchyOutgoingCall[]>();
619
- const totalResults = result.length;
620
-
621
- for (const call of result) {
622
- if (!call.to) {
623
- logger.warn(
624
- "formatOutgoingCallsResult: CallHierarchyOutgoingCall has undefined to field",
625
- );
626
- continue;
627
- }
628
- const path = formatUri(call.to.uri, workdir);
629
- const existing = grouped.get(path);
630
- if (existing) {
631
- existing.push(call);
632
- } else {
633
- grouped.set(path, [call]);
634
- }
635
- }
636
-
637
- const totalFiles = grouped.size;
638
- let resultsShown = 0;
639
- let filesShown = 0;
640
- const lines: string[] = [];
641
-
642
- for (const [path, calls] of grouped) {
643
- if (resultsShown >= MAX_RESULTS || filesShown >= MAX_FILES) break;
644
-
645
- filesShown++;
646
- const remainingResults = MAX_RESULTS - resultsShown;
647
- const callsToShow = calls.slice(0, remainingResults);
648
- const isTruncated = callsToShow.length < calls.length;
649
-
650
- lines.push(`\n${path}:`);
651
- for (const call of callsToShow) {
652
- if (!call.to) continue;
653
- const kindName = getSymbolKindName(call.to.kind);
654
- const startLine = call.to.range.start.line + 1;
655
- let line = ` ${call.to.name} (${kindName}) - Line ${startLine}`;
656
-
657
- if (call.fromRanges && call.fromRanges.length > 0) {
658
- const ranges = call.fromRanges
659
- .map((r) => `${r.start.line + 1}:${r.start.character + 1}`)
660
- .join(", ");
661
- line += ` [called from: ${ranges}]`;
662
- }
663
- lines.push(line);
664
- }
665
-
666
- if (isTruncated) {
667
- lines.push(
668
- ` ... and ${calls.length - callsToShow.length} more in this file`,
669
- );
670
- }
671
-
672
- resultsShown += callsToShow.length;
673
- }
674
-
675
- let header = `Found ${totalResults} outgoing call${totalResults === 1 ? "" : "s"}:`;
676
- if (totalResults > resultsShown || totalFiles > filesShown) {
677
- header += ` (showing first ${resultsShown} results and ${filesShown} files)`;
678
- }
679
- lines.unshift(header);
680
-
681
- return lines.join("\n");
682
- }
683
-
684
- /**
685
- * LSP tool plugin - interact with LSP servers for code intelligence
686
- */
687
- export const lspTool: ToolPlugin = {
688
- name: LSP_TOOL_NAME,
689
- config: {
690
- type: "function",
691
- function: {
692
- name: LSP_TOOL_NAME,
693
- description: `Interact with Language Server Protocol (LSP) servers to get code intelligence features.`,
694
- parameters: {
695
- type: "object",
696
- properties: {
697
- operation: {
698
- type: "string",
699
- enum: [
700
- "goToDefinition",
701
- "findReferences",
702
- "hover",
703
- "documentSymbol",
704
- "workspaceSymbol",
705
- "goToImplementation",
706
- "prepareCallHierarchy",
707
- "incomingCalls",
708
- "outgoingCalls",
709
- ],
710
- description: "The LSP operation to perform",
711
- },
712
- filePath: {
713
- type: "string",
714
- description: "The absolute or relative path to the file",
715
- },
716
- line: {
717
- type: "number",
718
- description: "The line number (1-based, as shown in editors)",
719
- },
720
- character: {
721
- type: "number",
722
- description: "The character offset (1-based, as shown in editors)",
723
- },
724
- },
725
- required: ["operation", "filePath", "line", "character"],
726
- },
727
- },
728
- },
729
- prompt:
730
- () => `Interact with Language Server Protocol (LSP) servers to get code intelligence features.
731
-
732
- Supported operations:
733
- - goToDefinition: Find where a symbol is defined
734
- - findReferences: Find all references to a symbol
735
- - hover: Get hover information (documentation, type info) for a symbol
736
- - documentSymbol: Get all symbols (functions, classes, variables) in a document
737
- - workspaceSymbol: Search for symbols across the entire workspace
738
- - goToImplementation: Find implementations of an interface or abstract method
739
- - prepareCallHierarchy: Get call hierarchy item at a position (functions/methods)
740
- - incomingCalls: Find all functions/methods that call the function at a position
741
- - outgoingCalls: Find all functions/methods called by the function at a position
742
-
743
- All operations require:
744
- - filePath: The file to operate on
745
- - line: The line number (1-based, as shown in editors)
746
- - character: The character offset (1-based, as shown in editors)
747
-
748
- Note: LSP servers must be configured for the file type. If no server is available, an error will be returned.`,
749
- execute: async (
750
- args: Record<string, unknown>,
751
- context: ToolContext,
752
- ): Promise<ToolResult> => {
753
- const { operation, filePath, line, character } = args as {
754
- operation: string;
755
- filePath: string;
756
- line: number;
757
- character: number;
758
- };
759
-
760
- if (!context.lspManager) {
761
- return {
762
- success: false,
763
- content: "",
764
- error: "LSP manager not available in tool context",
765
- };
766
- }
767
-
768
- try {
769
- const result = await context.lspManager.execute({
770
- operation,
771
- filePath,
772
- line,
773
- character,
774
- });
775
-
776
- if (!result.success) {
777
- return {
778
- success: false,
779
- content: "",
780
- error: result.content,
781
- };
782
- }
783
-
784
- const rawResult = JSON.parse(result.content);
785
- let formattedContent: string;
786
- switch (operation) {
787
- case "goToDefinition":
788
- case "goToImplementation":
789
- formattedContent = formatGoToDefinitionResult(
790
- rawResult as
791
- | Location
792
- | Location[]
793
- | LocationLink
794
- | LocationLink[]
795
- | null,
796
- filePath,
797
- line,
798
- character,
799
- operation,
800
- context.workdir,
801
- );
802
- break;
803
- case "findReferences":
804
- formattedContent = formatFindReferencesResult(
805
- rawResult as Location[] | null,
806
- filePath,
807
- line,
808
- character,
809
- context.workdir,
810
- );
811
- break;
812
- case "hover":
813
- formattedContent = formatHoverResult(
814
- rawResult as Hover | null,
815
- filePath,
816
- line,
817
- character,
818
- context.workdir,
819
- );
820
- break;
821
- case "documentSymbol":
822
- formattedContent = formatDocumentSymbolResult(
823
- rawResult as DocumentSymbol[] | SymbolInformation[] | null,
824
- context.workdir,
825
- );
826
- break;
827
- case "workspaceSymbol":
828
- formattedContent = formatWorkspaceSymbolResult(
829
- rawResult as SymbolInformation[] | null,
830
- context.workdir,
831
- );
832
- break;
833
- case "prepareCallHierarchy":
834
- formattedContent = formatPrepareCallHierarchyResult(
835
- rawResult as CallHierarchyItem[] | null,
836
- filePath,
837
- line,
838
- character,
839
- context.workdir,
840
- );
841
- break;
842
- case "incomingCalls":
843
- formattedContent = formatIncomingCallsResult(
844
- rawResult as CallHierarchyIncomingCall[] | null,
845
- context.workdir,
846
- );
847
- break;
848
- case "outgoingCalls":
849
- formattedContent = formatOutgoingCallsResult(
850
- rawResult as CallHierarchyOutgoingCall[] | null,
851
- context.workdir,
852
- );
853
- break;
854
- default:
855
- formattedContent = JSON.stringify(rawResult, null, 2);
856
- }
857
-
858
- return {
859
- success: true,
860
- content: formattedContent,
861
- };
862
- } catch (error) {
863
- return {
864
- success: false,
865
- content: "",
866
- error: `LSP operation failed: ${error instanceof Error ? error.message : String(error)}`,
867
- };
868
- }
869
- },
870
- formatCompactParams: (
871
- params: Record<string, unknown>,
872
- context: ToolContext,
873
- ): string => {
874
- const { operation, filePath, line, character } = params as {
875
- operation: string;
876
- filePath: string;
877
- line: number;
878
- character: number;
879
- };
880
- const displayPath = getDisplayPath(filePath, context.workdir);
881
- return `${operation} ${displayPath}:${line}:${character}`;
882
- },
883
- };