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.
- package/dist/agent.d.ts +23 -1
- package/dist/agent.js +34 -4
- package/dist/managers/aiManager.d.ts +36 -0
- package/dist/managers/aiManager.js +240 -8
- package/dist/managers/messageManager.d.ts +1 -3
- package/dist/managers/messageManager.js +19 -9
- package/dist/managers/slashCommandManager.js +28 -0
- package/dist/managers/subagentManager.d.ts +0 -2
- package/dist/services/session.js +2 -9
- package/dist/telemetry/instrumentation.d.ts +1 -1
- package/dist/telemetry/instrumentation.js +57 -28
- package/dist/tools/bashTool.js +16 -6
- package/dist/tools/grepTool.js +11 -3
- package/dist/types/index.d.ts +3 -0
- package/dist/types/messaging.d.ts +2 -0
- package/dist/utils/containerSetup.js +0 -3
- package/dist/utils/messageOperations.d.ts +10 -1
- package/dist/utils/messageOperations.js +19 -1
- package/dist/utils/notificationXml.js +5 -0
- package/dist/utils/ripgrep.d.ts +4 -4
- package/dist/utils/ripgrep.js +4 -27
- package/package.json +20 -8
- package/bin/rg +0 -79
- package/dist/managers/forkedAgentManager.d.ts +0 -50
- package/dist/managers/forkedAgentManager.js +0 -130
- package/scripts/install_ripgrep.js +0 -111
- package/scripts/postinstall.js +0 -38
- package/src/agent.ts +0 -1271
- package/src/constants/subagents.ts +0 -4
- package/src/constants/toolLimits.ts +0 -15
- package/src/constants/tools.ts +0 -24
- package/src/core/plugin.ts +0 -237
- package/src/core/session.ts +0 -9
- package/src/index.ts +0 -38
- package/src/managers/MemoryRuleManager.ts +0 -198
- package/src/managers/aiManager.ts +0 -2429
- package/src/managers/backgroundTaskManager.ts +0 -480
- package/src/managers/bangManager.ts +0 -119
- package/src/managers/cronManager.ts +0 -401
- package/src/managers/foregroundTaskManager.ts +0 -30
- package/src/managers/forkedAgentManager.ts +0 -216
- package/src/managers/hookManager.ts +0 -1089
- package/src/managers/liveConfigManager.ts +0 -445
- package/src/managers/lspManager.ts +0 -470
- package/src/managers/mcpManager.ts +0 -1010
- package/src/managers/messageManager.ts +0 -1180
- package/src/managers/messageQueue.ts +0 -157
- package/src/managers/permissionManager.ts +0 -1140
- package/src/managers/planManager.ts +0 -112
- package/src/managers/pluginManager.ts +0 -330
- package/src/managers/pluginScopeManager.ts +0 -124
- package/src/managers/reversionManager.ts +0 -178
- package/src/managers/skillManager.ts +0 -578
- package/src/managers/slashCommandManager.ts +0 -564
- package/src/managers/subagentManager.ts +0 -915
- package/src/managers/toolManager.ts +0 -489
- package/src/managers/workflowManager.ts +0 -493
- package/src/prompts/autoMemory.ts +0 -33
- package/src/prompts/autoMemoryExtraction.ts +0 -146
- package/src/prompts/index.ts +0 -506
- package/src/prompts/planModeReminders.ts +0 -126
- package/src/services/GitService.ts +0 -131
- package/src/services/MarketplaceService.ts +0 -1052
- package/src/services/MemoryRuleService.ts +0 -71
- package/src/services/aiService.ts +0 -919
- package/src/services/authService.ts +0 -565
- package/src/services/autoMemoryService.ts +0 -280
- package/src/services/configurationService.ts +0 -1520
- package/src/services/fileWatcher.ts +0 -319
- package/src/services/hook.ts +0 -348
- package/src/services/initializationService.ts +0 -357
- package/src/services/interactionService.ts +0 -229
- package/src/services/jsonlHandler.ts +0 -295
- package/src/services/memory.ts +0 -234
- package/src/services/pluginLoader.ts +0 -252
- package/src/services/remoteSettingsService.ts +0 -366
- package/src/services/reversionService.ts +0 -122
- package/src/services/session.ts +0 -922
- package/src/services/taskManager.ts +0 -321
- package/src/telemetry/events.ts +0 -65
- package/src/telemetry/instrumentation.ts +0 -499
- package/src/telemetry/sessionTracing.ts +0 -348
- package/src/tools/agentTool.ts +0 -322
- package/src/tools/askUserQuestion.ts +0 -160
- package/src/tools/bashTool.ts +0 -621
- package/src/tools/buildTool.ts +0 -61
- package/src/tools/cronCreateTool.ts +0 -161
- package/src/tools/cronDeleteTool.ts +0 -51
- package/src/tools/cronListTool.ts +0 -47
- package/src/tools/editTool.ts +0 -313
- package/src/tools/enterPlanMode.ts +0 -124
- package/src/tools/enterWorktreeTool.ts +0 -190
- package/src/tools/exitPlanMode.ts +0 -139
- package/src/tools/exitWorktreeTool.ts +0 -236
- package/src/tools/globTool.ts +0 -167
- package/src/tools/grepTool.ts +0 -399
- package/src/tools/lspTool.ts +0 -883
- package/src/tools/readTool.ts +0 -426
- package/src/tools/skillTool.ts +0 -248
- package/src/tools/taskManagementTools.ts +0 -574
- package/src/tools/taskStopTool.ts +0 -78
- package/src/tools/types.ts +0 -137
- package/src/tools/webFetchTool.ts +0 -373
- package/src/tools/workflowTool.ts +0 -205
- package/src/tools/writeTool.ts +0 -267
- package/src/types/agent.ts +0 -128
- package/src/types/auth.ts +0 -19
- package/src/types/commands.ts +0 -30
- package/src/types/config.ts +0 -43
- package/src/types/configuration.ts +0 -168
- package/src/types/core.ts +0 -105
- package/src/types/cron.ts +0 -13
- package/src/types/environment.ts +0 -106
- package/src/types/fileSearch.ts +0 -4
- package/src/types/history.ts +0 -7
- package/src/types/hooks.ts +0 -256
- package/src/types/index.ts +0 -41
- package/src/types/lsp.ts +0 -98
- package/src/types/marketplace.ts +0 -71
- package/src/types/mcp.ts +0 -43
- package/src/types/memoryRule.ts +0 -31
- package/src/types/messaging.ts +0 -114
- package/src/types/permissions.ts +0 -80
- package/src/types/plugins.ts +0 -39
- package/src/types/processes.ts +0 -99
- package/src/types/reversion.ts +0 -29
- package/src/types/session.ts +0 -12
- package/src/types/skills.ts +0 -108
- package/src/types/tasks.ts +0 -13
- package/src/types/telemetry.ts +0 -98
- package/src/types/tools.ts +0 -44
- package/src/types/workflow.ts +0 -6
- package/src/utils/abortUtils.ts +0 -118
- package/src/utils/atomicWrite.ts +0 -61
- package/src/utils/bashParser.ts +0 -899
- package/src/utils/cacheControlUtils.ts +0 -426
- package/src/utils/commandArgumentParser.ts +0 -161
- package/src/utils/commandPathResolver.ts +0 -88
- package/src/utils/configPaths.ts +0 -220
- package/src/utils/configValidator.ts +0 -100
- package/src/utils/constants.ts +0 -39
- package/src/utils/container.ts +0 -92
- package/src/utils/containerSetup.ts +0 -371
- package/src/utils/convertMessagesForAPI.ts +0 -326
- package/src/utils/cronTasks.ts +0 -128
- package/src/utils/cronTasksLock.ts +0 -182
- package/src/utils/cronToHuman.ts +0 -99
- package/src/utils/customCommands.ts +0 -95
- package/src/utils/editUtils.ts +0 -20
- package/src/utils/fileFormat.ts +0 -40
- package/src/utils/fileSearch.ts +0 -151
- package/src/utils/fileUtils.ts +0 -223
- package/src/utils/gitUtils.ts +0 -315
- package/src/utils/globalLogger.ts +0 -128
- package/src/utils/groupMessagesByApiRound.ts +0 -121
- package/src/utils/hookMatcher.ts +0 -176
- package/src/utils/markdownParser.ts +0 -317
- package/src/utils/mcpUtils.ts +0 -138
- package/src/utils/messageOperations.ts +0 -641
- package/src/utils/modelCapabilities.ts +0 -30
- package/src/utils/nameGenerator.ts +0 -99
- package/src/utils/notificationXml.ts +0 -57
- package/src/utils/openaiClient.ts +0 -251
- package/src/utils/parseCronExpression.ts +0 -78
- package/src/utils/path.ts +0 -72
- package/src/utils/pathEncoder.ts +0 -400
- package/src/utils/pathSafety.ts +0 -40
- package/src/utils/promptHistory.ts +0 -168
- package/src/utils/ripgrep.ts +0 -35
- package/src/utils/shellResolver.ts +0 -190
- package/src/utils/skillParser.ts +0 -246
- package/src/utils/stringUtils.ts +0 -156
- package/src/utils/subagentParser.ts +0 -279
- package/src/utils/taskReminder.ts +0 -96
- package/src/utils/tokenCalculation.ts +0 -43
- package/src/utils/tokenEstimate.ts +0 -34
- package/src/utils/toolResultStorage.ts +0 -117
- package/src/utils/worktreeSession.ts +0 -26
- package/src/utils/worktreeUtils.ts +0 -786
- package/src/workflow/budgetTracker.ts +0 -34
- package/src/workflow/concurrencyLimiter.ts +0 -47
- package/src/workflow/journal.ts +0 -95
- package/src/workflow/progressReporter.ts +0 -141
- package/src/workflow/runState.ts +0 -65
- package/src/workflow/scriptRuntime.ts +0 -274
- package/src/workflow/structuredOutput.ts +0 -123
- package/src/workflow/types.ts +0 -101
- package/src/workflow/workflowApis.ts +0 -410
- package/vendor/ripgrep/linux-aarch64/rg +0 -0
- package/vendor/ripgrep/linux-x86_64/rg +0 -0
- package/vendor/ripgrep/macos-aarch64/rg +0 -0
- package/vendor/ripgrep/macos-x86_64/rg +0 -0
- package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
- package/vendor/ripgrep/windows-x86_64/rg.exe +0 -0
package/src/tools/lspTool.ts
DELETED
|
@@ -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
|
-
};
|