wave-agent-sdk 1.0.5 → 1.0.7

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