wave-agent-sdk 1.0.5 → 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 (193) 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 +13 -6
  7. package/dist/managers/slashCommandManager.js +28 -0
  8. package/dist/managers/subagentManager.d.ts +0 -2
  9. package/dist/telemetry/instrumentation.d.ts +1 -1
  10. package/dist/telemetry/instrumentation.js +57 -28
  11. package/dist/tools/bashTool.js +16 -6
  12. package/dist/tools/grepTool.js +11 -3
  13. package/dist/types/index.d.ts +3 -0
  14. package/dist/types/messaging.d.ts +2 -0
  15. package/dist/utils/containerSetup.js +0 -3
  16. package/dist/utils/messageOperations.d.ts +2 -1
  17. package/dist/utils/messageOperations.js +2 -1
  18. package/dist/utils/notificationXml.js +5 -0
  19. package/dist/utils/ripgrep.d.ts +4 -4
  20. package/dist/utils/ripgrep.js +4 -27
  21. package/package.json +20 -8
  22. package/bin/rg +0 -79
  23. package/dist/managers/forkedAgentManager.d.ts +0 -50
  24. package/dist/managers/forkedAgentManager.js +0 -130
  25. package/scripts/install_ripgrep.js +0 -111
  26. package/scripts/postinstall.js +0 -38
  27. package/src/agent.ts +0 -1271
  28. package/src/constants/subagents.ts +0 -4
  29. package/src/constants/toolLimits.ts +0 -15
  30. package/src/constants/tools.ts +0 -24
  31. package/src/core/plugin.ts +0 -237
  32. package/src/core/session.ts +0 -9
  33. package/src/index.ts +0 -38
  34. package/src/managers/MemoryRuleManager.ts +0 -198
  35. package/src/managers/aiManager.ts +0 -2429
  36. package/src/managers/backgroundTaskManager.ts +0 -480
  37. package/src/managers/bangManager.ts +0 -119
  38. package/src/managers/cronManager.ts +0 -401
  39. package/src/managers/foregroundTaskManager.ts +0 -30
  40. package/src/managers/forkedAgentManager.ts +0 -216
  41. package/src/managers/hookManager.ts +0 -1089
  42. package/src/managers/liveConfigManager.ts +0 -445
  43. package/src/managers/lspManager.ts +0 -470
  44. package/src/managers/mcpManager.ts +0 -1010
  45. package/src/managers/messageManager.ts +0 -1184
  46. package/src/managers/messageQueue.ts +0 -157
  47. package/src/managers/permissionManager.ts +0 -1140
  48. package/src/managers/planManager.ts +0 -112
  49. package/src/managers/pluginManager.ts +0 -330
  50. package/src/managers/pluginScopeManager.ts +0 -124
  51. package/src/managers/reversionManager.ts +0 -178
  52. package/src/managers/skillManager.ts +0 -578
  53. package/src/managers/slashCommandManager.ts +0 -564
  54. package/src/managers/subagentManager.ts +0 -915
  55. package/src/managers/toolManager.ts +0 -489
  56. package/src/managers/workflowManager.ts +0 -493
  57. package/src/prompts/autoMemory.ts +0 -33
  58. package/src/prompts/autoMemoryExtraction.ts +0 -146
  59. package/src/prompts/index.ts +0 -506
  60. package/src/prompts/planModeReminders.ts +0 -126
  61. package/src/services/GitService.ts +0 -131
  62. package/src/services/MarketplaceService.ts +0 -1052
  63. package/src/services/MemoryRuleService.ts +0 -71
  64. package/src/services/aiService.ts +0 -919
  65. package/src/services/authService.ts +0 -565
  66. package/src/services/autoMemoryService.ts +0 -280
  67. package/src/services/configurationService.ts +0 -1520
  68. package/src/services/fileWatcher.ts +0 -319
  69. package/src/services/hook.ts +0 -348
  70. package/src/services/initializationService.ts +0 -357
  71. package/src/services/interactionService.ts +0 -229
  72. package/src/services/jsonlHandler.ts +0 -295
  73. package/src/services/memory.ts +0 -234
  74. package/src/services/pluginLoader.ts +0 -252
  75. package/src/services/remoteSettingsService.ts +0 -366
  76. package/src/services/reversionService.ts +0 -122
  77. package/src/services/session.ts +0 -917
  78. package/src/services/taskManager.ts +0 -321
  79. package/src/telemetry/events.ts +0 -65
  80. package/src/telemetry/instrumentation.ts +0 -499
  81. package/src/telemetry/sessionTracing.ts +0 -348
  82. package/src/tools/agentTool.ts +0 -322
  83. package/src/tools/askUserQuestion.ts +0 -160
  84. package/src/tools/bashTool.ts +0 -621
  85. package/src/tools/buildTool.ts +0 -61
  86. package/src/tools/cronCreateTool.ts +0 -161
  87. package/src/tools/cronDeleteTool.ts +0 -51
  88. package/src/tools/cronListTool.ts +0 -47
  89. package/src/tools/editTool.ts +0 -313
  90. package/src/tools/enterPlanMode.ts +0 -124
  91. package/src/tools/enterWorktreeTool.ts +0 -190
  92. package/src/tools/exitPlanMode.ts +0 -139
  93. package/src/tools/exitWorktreeTool.ts +0 -236
  94. package/src/tools/globTool.ts +0 -167
  95. package/src/tools/grepTool.ts +0 -399
  96. package/src/tools/lspTool.ts +0 -883
  97. package/src/tools/readTool.ts +0 -426
  98. package/src/tools/skillTool.ts +0 -248
  99. package/src/tools/taskManagementTools.ts +0 -574
  100. package/src/tools/taskStopTool.ts +0 -78
  101. package/src/tools/types.ts +0 -137
  102. package/src/tools/webFetchTool.ts +0 -373
  103. package/src/tools/workflowTool.ts +0 -205
  104. package/src/tools/writeTool.ts +0 -267
  105. package/src/types/agent.ts +0 -128
  106. package/src/types/auth.ts +0 -19
  107. package/src/types/commands.ts +0 -30
  108. package/src/types/config.ts +0 -43
  109. package/src/types/configuration.ts +0 -168
  110. package/src/types/core.ts +0 -105
  111. package/src/types/cron.ts +0 -13
  112. package/src/types/environment.ts +0 -106
  113. package/src/types/fileSearch.ts +0 -4
  114. package/src/types/history.ts +0 -7
  115. package/src/types/hooks.ts +0 -256
  116. package/src/types/index.ts +0 -41
  117. package/src/types/lsp.ts +0 -98
  118. package/src/types/marketplace.ts +0 -71
  119. package/src/types/mcp.ts +0 -43
  120. package/src/types/memoryRule.ts +0 -31
  121. package/src/types/messaging.ts +0 -114
  122. package/src/types/permissions.ts +0 -80
  123. package/src/types/plugins.ts +0 -39
  124. package/src/types/processes.ts +0 -99
  125. package/src/types/reversion.ts +0 -29
  126. package/src/types/session.ts +0 -12
  127. package/src/types/skills.ts +0 -108
  128. package/src/types/tasks.ts +0 -13
  129. package/src/types/telemetry.ts +0 -98
  130. package/src/types/tools.ts +0 -44
  131. package/src/types/workflow.ts +0 -6
  132. package/src/utils/abortUtils.ts +0 -118
  133. package/src/utils/atomicWrite.ts +0 -61
  134. package/src/utils/bashParser.ts +0 -899
  135. package/src/utils/cacheControlUtils.ts +0 -426
  136. package/src/utils/commandArgumentParser.ts +0 -161
  137. package/src/utils/commandPathResolver.ts +0 -88
  138. package/src/utils/configPaths.ts +0 -220
  139. package/src/utils/configValidator.ts +0 -100
  140. package/src/utils/constants.ts +0 -39
  141. package/src/utils/container.ts +0 -92
  142. package/src/utils/containerSetup.ts +0 -371
  143. package/src/utils/convertMessagesForAPI.ts +0 -326
  144. package/src/utils/cronTasks.ts +0 -128
  145. package/src/utils/cronTasksLock.ts +0 -182
  146. package/src/utils/cronToHuman.ts +0 -99
  147. package/src/utils/customCommands.ts +0 -95
  148. package/src/utils/editUtils.ts +0 -20
  149. package/src/utils/fileFormat.ts +0 -40
  150. package/src/utils/fileSearch.ts +0 -151
  151. package/src/utils/fileUtils.ts +0 -223
  152. package/src/utils/gitUtils.ts +0 -315
  153. package/src/utils/globalLogger.ts +0 -128
  154. package/src/utils/groupMessagesByApiRound.ts +0 -121
  155. package/src/utils/hookMatcher.ts +0 -176
  156. package/src/utils/markdownParser.ts +0 -317
  157. package/src/utils/mcpUtils.ts +0 -138
  158. package/src/utils/messageOperations.ts +0 -659
  159. package/src/utils/modelCapabilities.ts +0 -30
  160. package/src/utils/nameGenerator.ts +0 -99
  161. package/src/utils/notificationXml.ts +0 -57
  162. package/src/utils/openaiClient.ts +0 -251
  163. package/src/utils/parseCronExpression.ts +0 -78
  164. package/src/utils/path.ts +0 -72
  165. package/src/utils/pathEncoder.ts +0 -400
  166. package/src/utils/pathSafety.ts +0 -40
  167. package/src/utils/promptHistory.ts +0 -168
  168. package/src/utils/ripgrep.ts +0 -35
  169. package/src/utils/shellResolver.ts +0 -190
  170. package/src/utils/skillParser.ts +0 -246
  171. package/src/utils/stringUtils.ts +0 -156
  172. package/src/utils/subagentParser.ts +0 -279
  173. package/src/utils/taskReminder.ts +0 -96
  174. package/src/utils/tokenCalculation.ts +0 -43
  175. package/src/utils/tokenEstimate.ts +0 -34
  176. package/src/utils/toolResultStorage.ts +0 -117
  177. package/src/utils/worktreeSession.ts +0 -26
  178. package/src/utils/worktreeUtils.ts +0 -786
  179. package/src/workflow/budgetTracker.ts +0 -34
  180. package/src/workflow/concurrencyLimiter.ts +0 -47
  181. package/src/workflow/journal.ts +0 -95
  182. package/src/workflow/progressReporter.ts +0 -141
  183. package/src/workflow/runState.ts +0 -65
  184. package/src/workflow/scriptRuntime.ts +0 -274
  185. package/src/workflow/structuredOutput.ts +0 -123
  186. package/src/workflow/types.ts +0 -101
  187. package/src/workflow/workflowApis.ts +0 -410
  188. package/vendor/ripgrep/linux-aarch64/rg +0 -0
  189. package/vendor/ripgrep/linux-x86_64/rg +0 -0
  190. package/vendor/ripgrep/macos-aarch64/rg +0 -0
  191. package/vendor/ripgrep/macos-x86_64/rg +0 -0
  192. package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
  193. 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
- };