wave-agent-sdk 0.0.8 → 0.0.10
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 +92 -23
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +340 -137
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/managers/aiManager.d.ts +14 -36
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +74 -77
- package/dist/managers/backgroundBashManager.d.ts.map +1 -1
- package/dist/managers/backgroundBashManager.js +4 -3
- package/dist/managers/hookManager.d.ts +3 -8
- package/dist/managers/hookManager.d.ts.map +1 -1
- package/dist/managers/hookManager.js +39 -29
- package/dist/managers/liveConfigManager.d.ts +55 -18
- package/dist/managers/liveConfigManager.d.ts.map +1 -1
- package/dist/managers/liveConfigManager.js +372 -90
- package/dist/managers/lspManager.d.ts +43 -0
- package/dist/managers/lspManager.d.ts.map +1 -0
- package/dist/managers/lspManager.js +326 -0
- package/dist/managers/messageManager.d.ts +8 -16
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +52 -74
- package/dist/managers/permissionManager.d.ts +66 -0
- package/dist/managers/permissionManager.d.ts.map +1 -0
- package/dist/managers/permissionManager.js +208 -0
- package/dist/managers/skillManager.d.ts +1 -0
- package/dist/managers/skillManager.d.ts.map +1 -1
- package/dist/managers/skillManager.js +2 -1
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +0 -1
- package/dist/managers/subagentManager.d.ts +8 -23
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +97 -117
- package/dist/managers/toolManager.d.ts +38 -1
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +66 -2
- package/dist/services/aiService.d.ts +3 -1
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +123 -30
- package/dist/services/configurationService.d.ts +116 -0
- package/dist/services/configurationService.d.ts.map +1 -0
- package/dist/services/configurationService.js +585 -0
- package/dist/services/fileWatcher.d.ts.map +1 -1
- package/dist/services/fileWatcher.js +5 -6
- package/dist/services/hook.d.ts +7 -124
- package/dist/services/hook.d.ts.map +1 -1
- package/dist/services/hook.js +46 -458
- package/dist/services/jsonlHandler.d.ts +24 -15
- package/dist/services/jsonlHandler.d.ts.map +1 -1
- package/dist/services/jsonlHandler.js +67 -88
- package/dist/services/memory.d.ts +0 -9
- package/dist/services/memory.d.ts.map +1 -1
- package/dist/services/memory.js +2 -49
- package/dist/services/session.d.ts +82 -33
- package/dist/services/session.d.ts.map +1 -1
- package/dist/services/session.js +275 -181
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +72 -13
- package/dist/tools/deleteFileTool.d.ts.map +1 -1
- package/dist/tools/deleteFileTool.js +25 -0
- package/dist/tools/editTool.d.ts.map +1 -1
- package/dist/tools/editTool.js +30 -6
- package/dist/tools/lspTool.d.ts +6 -0
- package/dist/tools/lspTool.d.ts.map +1 -0
- package/dist/tools/lspTool.js +589 -0
- package/dist/tools/multiEditTool.d.ts.map +1 -1
- package/dist/tools/multiEditTool.js +26 -7
- package/dist/tools/readTool.d.ts.map +1 -1
- package/dist/tools/readTool.js +111 -2
- package/dist/tools/skillTool.js +2 -2
- package/dist/tools/todoWriteTool.d.ts.map +1 -1
- package/dist/tools/todoWriteTool.js +23 -0
- package/dist/tools/types.d.ts +11 -8
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/writeTool.d.ts.map +1 -1
- package/dist/tools/writeTool.js +25 -9
- package/dist/types/commands.d.ts +0 -1
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/types/config.d.ts +4 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/configuration.d.ts +69 -0
- package/dist/types/configuration.d.ts.map +1 -0
- package/dist/types/configuration.js +8 -0
- package/dist/types/core.d.ts +10 -0
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/environment.d.ts +41 -0
- package/dist/types/environment.d.ts.map +1 -1
- package/dist/types/fileSearch.d.ts +5 -0
- package/dist/types/fileSearch.d.ts.map +1 -0
- package/dist/types/fileSearch.js +1 -0
- package/dist/types/hooks.d.ts +11 -2
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/hooks.js +1 -7
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +5 -0
- package/dist/types/lsp.d.ts +90 -0
- package/dist/types/lsp.d.ts.map +1 -0
- package/dist/types/lsp.js +4 -0
- package/dist/types/messaging.d.ts +6 -11
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/types/permissions.d.ts +35 -0
- package/dist/types/permissions.d.ts.map +1 -0
- package/dist/types/permissions.js +12 -0
- package/dist/types/session.d.ts +1 -6
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/skills.d.ts +1 -0
- package/dist/types/skills.d.ts.map +1 -1
- package/dist/types/tools.d.ts +35 -0
- package/dist/types/tools.d.ts.map +1 -0
- package/dist/types/tools.js +4 -0
- package/dist/utils/abortUtils.d.ts +34 -0
- package/dist/utils/abortUtils.d.ts.map +1 -0
- package/dist/utils/abortUtils.js +92 -0
- package/dist/utils/bashHistory.d.ts +4 -0
- package/dist/utils/bashHistory.d.ts.map +1 -1
- package/dist/utils/bashHistory.js +21 -4
- package/dist/utils/builtinSubagents.d.ts +7 -0
- package/dist/utils/builtinSubagents.d.ts.map +1 -0
- package/dist/utils/builtinSubagents.js +65 -0
- package/dist/utils/cacheControlUtils.d.ts +8 -33
- package/dist/utils/cacheControlUtils.d.ts.map +1 -1
- package/dist/utils/cacheControlUtils.js +83 -126
- package/dist/utils/constants.d.ts +0 -12
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +1 -13
- package/dist/utils/convertMessagesForAPI.d.ts +2 -1
- package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
- package/dist/utils/convertMessagesForAPI.js +33 -14
- package/dist/utils/fileSearch.d.ts +14 -0
- package/dist/utils/fileSearch.d.ts.map +1 -0
- package/dist/utils/fileSearch.js +88 -0
- package/dist/utils/fileUtils.d.ts +14 -2
- package/dist/utils/fileUtils.d.ts.map +1 -1
- package/dist/utils/fileUtils.js +101 -17
- package/dist/utils/globalLogger.d.ts +0 -14
- package/dist/utils/globalLogger.d.ts.map +1 -1
- package/dist/utils/globalLogger.js +0 -16
- package/dist/utils/largeOutputHandler.d.ts +15 -0
- package/dist/utils/largeOutputHandler.d.ts.map +1 -0
- package/dist/utils/largeOutputHandler.js +40 -0
- package/dist/utils/markdownParser.d.ts.map +1 -1
- package/dist/utils/markdownParser.js +1 -17
- package/dist/utils/messageOperations.d.ts +1 -11
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +7 -24
- package/dist/utils/pathEncoder.d.ts +4 -0
- package/dist/utils/pathEncoder.d.ts.map +1 -1
- package/dist/utils/pathEncoder.js +16 -9
- package/dist/utils/subagentParser.d.ts +2 -2
- package/dist/utils/subagentParser.d.ts.map +1 -1
- package/dist/utils/subagentParser.js +10 -7
- package/dist/utils/tokenEstimator.d.ts +39 -0
- package/dist/utils/tokenEstimator.d.ts.map +1 -0
- package/dist/utils/tokenEstimator.js +55 -0
- package/package.json +5 -8
- package/src/agent.ts +460 -216
- package/src/index.ts +2 -0
- package/src/managers/aiManager.ts +107 -111
- package/src/managers/backgroundBashManager.ts +4 -3
- package/src/managers/hookManager.ts +44 -39
- package/src/managers/liveConfigManager.ts +524 -138
- package/src/managers/lspManager.ts +434 -0
- package/src/managers/messageManager.ts +73 -103
- package/src/managers/permissionManager.ts +276 -0
- package/src/managers/skillManager.ts +3 -1
- package/src/managers/slashCommandManager.ts +1 -2
- package/src/managers/subagentManager.ts +116 -159
- package/src/managers/toolManager.ts +95 -3
- package/src/services/aiService.ts +207 -26
- package/src/services/configurationService.ts +762 -0
- package/src/services/fileWatcher.ts +5 -6
- package/src/services/hook.ts +50 -631
- package/src/services/jsonlHandler.ts +84 -100
- package/src/services/memory.ts +2 -59
- package/src/services/session.ts +338 -213
- package/src/tools/bashTool.ts +89 -16
- package/src/tools/deleteFileTool.ts +36 -0
- package/src/tools/editTool.ts +41 -7
- package/src/tools/lspTool.ts +760 -0
- package/src/tools/multiEditTool.ts +37 -8
- package/src/tools/readTool.ts +125 -2
- package/src/tools/skillTool.ts +2 -2
- package/src/tools/todoWriteTool.ts +33 -1
- package/src/tools/types.ts +15 -9
- package/src/tools/writeTool.ts +36 -10
- package/src/types/commands.ts +0 -1
- package/src/types/config.ts +5 -0
- package/src/types/configuration.ts +73 -0
- package/src/types/core.ts +11 -0
- package/src/types/environment.ts +44 -0
- package/src/types/fileSearch.ts +4 -0
- package/src/types/hooks.ts +14 -11
- package/src/types/index.ts +5 -0
- package/src/types/lsp.ts +96 -0
- package/src/types/messaging.ts +8 -13
- package/src/types/permissions.ts +48 -0
- package/src/types/session.ts +3 -8
- package/src/types/skills.ts +1 -0
- package/src/types/tools.ts +38 -0
- package/src/utils/abortUtils.ts +118 -0
- package/src/utils/bashHistory.ts +28 -4
- package/src/utils/builtinSubagents.ts +71 -0
- package/src/utils/cacheControlUtils.ts +106 -171
- package/src/utils/constants.ts +1 -16
- package/src/utils/convertMessagesForAPI.ts +38 -14
- package/src/utils/fileSearch.ts +107 -0
- package/src/utils/fileUtils.ts +114 -19
- package/src/utils/globalLogger.ts +0 -17
- package/src/utils/largeOutputHandler.ts +55 -0
- package/src/utils/markdownParser.ts +1 -19
- package/src/utils/messageOperations.ts +7 -35
- package/src/utils/pathEncoder.ts +24 -9
- package/src/utils/subagentParser.ts +11 -8
- package/src/utils/tokenEstimator.ts +68 -0
- package/dist/constants/events.d.ts +0 -28
- package/dist/constants/events.d.ts.map +0 -1
- package/dist/constants/events.js +0 -27
- package/dist/services/configurationWatcher.d.ts +0 -120
- package/dist/services/configurationWatcher.d.ts.map +0 -1
- package/dist/services/configurationWatcher.js +0 -439
- package/dist/services/memoryStore.d.ts +0 -81
- package/dist/services/memoryStore.d.ts.map +0 -1
- package/dist/services/memoryStore.js +0 -200
- package/dist/types/memoryStore.d.ts +0 -82
- package/dist/types/memoryStore.d.ts.map +0 -1
- package/dist/types/memoryStore.js +0 -7
- package/dist/utils/configResolver.d.ts +0 -65
- package/dist/utils/configResolver.d.ts.map +0 -1
- package/dist/utils/configResolver.js +0 -210
- package/src/constants/events.ts +0 -38
- package/src/services/configurationWatcher.ts +0 -622
- package/src/services/memoryStore.ts +0 -279
- package/src/types/memoryStore.ts +0 -94
- package/src/utils/configResolver.ts +0 -302
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
|
|
2
|
+
import { relative } from "path";
|
|
3
|
+
import { logger } from "../utils/globalLogger.js";
|
|
4
|
+
import { getDisplayPath } from "../utils/path.js";
|
|
5
|
+
import type {
|
|
6
|
+
LspLocation as Location,
|
|
7
|
+
LspLocationLink as LocationLink,
|
|
8
|
+
LspHover as Hover,
|
|
9
|
+
LspSymbolInformation as SymbolInformation,
|
|
10
|
+
LspDocumentSymbol as DocumentSymbol,
|
|
11
|
+
LspCallHierarchyItem as CallHierarchyItem,
|
|
12
|
+
LspCallHierarchyIncomingCall as CallHierarchyIncomingCall,
|
|
13
|
+
LspCallHierarchyOutgoingCall as CallHierarchyOutgoingCall,
|
|
14
|
+
} from "../types/lsp.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Formats an LSP URI into a readable file path
|
|
18
|
+
*/
|
|
19
|
+
function formatUri(uri: string, workdir?: string): string {
|
|
20
|
+
if (!uri) {
|
|
21
|
+
logger.warn(
|
|
22
|
+
"formatUri called with undefined URI - indicates malformed LSP server response",
|
|
23
|
+
);
|
|
24
|
+
return "<unknown location>";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let path = uri.replace(/^file:\/\//, "");
|
|
28
|
+
try {
|
|
29
|
+
path = decodeURIComponent(path);
|
|
30
|
+
} catch (e) {
|
|
31
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
32
|
+
logger.warn(
|
|
33
|
+
`Failed to decode LSP URI '${uri}': ${message}. Using un-decoded path: ${path}`,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (workdir) {
|
|
38
|
+
const relativePath = relative(workdir, path);
|
|
39
|
+
if (
|
|
40
|
+
relativePath.length < path.length &&
|
|
41
|
+
!relativePath.startsWith("../../")
|
|
42
|
+
) {
|
|
43
|
+
return relativePath;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return path;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Groups items by their URI
|
|
51
|
+
*/
|
|
52
|
+
function groupItemsByUri<
|
|
53
|
+
T extends { uri: string } | { location: Location } | LocationLink,
|
|
54
|
+
>(items: T[], workdir?: string): Map<string, T[]> {
|
|
55
|
+
const map = new Map<string, T[]>();
|
|
56
|
+
for (const item of items) {
|
|
57
|
+
let uri: string;
|
|
58
|
+
if ("uri" in item) {
|
|
59
|
+
uri = item.uri;
|
|
60
|
+
} else if ("location" in item) {
|
|
61
|
+
uri = item.location.uri;
|
|
62
|
+
} else {
|
|
63
|
+
uri = item.targetUri;
|
|
64
|
+
}
|
|
65
|
+
const path = formatUri(uri, workdir);
|
|
66
|
+
const existing = map.get(path);
|
|
67
|
+
if (existing) {
|
|
68
|
+
existing.push(item);
|
|
69
|
+
} else {
|
|
70
|
+
map.set(path, [item]);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return map;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Formats a single location as path:line:character
|
|
78
|
+
*/
|
|
79
|
+
function formatLocation(loc: Location, workdir?: string): string {
|
|
80
|
+
const path = formatUri(loc.uri, workdir);
|
|
81
|
+
const line = loc.range.start.line + 1;
|
|
82
|
+
const character = loc.range.start.character + 1;
|
|
83
|
+
return `${path}:${line}:${character}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Converts a LocationLink to a Location
|
|
88
|
+
*/
|
|
89
|
+
function locationLinkToLocation(link: LocationLink): Location {
|
|
90
|
+
return {
|
|
91
|
+
uri: link.targetUri,
|
|
92
|
+
range: link.targetSelectionRange || link.targetRange,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Checks if an object is a LocationLink
|
|
98
|
+
*/
|
|
99
|
+
function isLocationLink(loc: Location | LocationLink): loc is LocationLink {
|
|
100
|
+
return "targetUri" in loc;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Formats the result of a goToDefinition operation
|
|
105
|
+
*/
|
|
106
|
+
function formatGoToDefinitionResult(
|
|
107
|
+
result: Location | Location[] | LocationLink | LocationLink[] | null,
|
|
108
|
+
workdir?: string,
|
|
109
|
+
): string {
|
|
110
|
+
if (!result) {
|
|
111
|
+
return "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.";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (Array.isArray(result)) {
|
|
115
|
+
const locations = result.map((loc) =>
|
|
116
|
+
isLocationLink(loc) ? locationLinkToLocation(loc) : loc,
|
|
117
|
+
);
|
|
118
|
+
const validLocations = locations.filter((loc) => loc && loc.uri);
|
|
119
|
+
|
|
120
|
+
if (validLocations.length === 0) {
|
|
121
|
+
return "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.";
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (validLocations.length === 1) {
|
|
125
|
+
return `Defined in ${formatLocation(validLocations[0], workdir)}`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const formatted = validLocations
|
|
129
|
+
.map((loc) => ` ${formatLocation(loc, workdir)}`)
|
|
130
|
+
.join("\n");
|
|
131
|
+
return `Found ${validLocations.length} definitions:\n${formatted}`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const loc = isLocationLink(result) ? locationLinkToLocation(result) : result;
|
|
135
|
+
return `Defined in ${formatLocation(loc, workdir)}`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Formats the result of a findReferences operation
|
|
140
|
+
*/
|
|
141
|
+
function formatFindReferencesResult(
|
|
142
|
+
result: Location[] | null,
|
|
143
|
+
workdir?: string,
|
|
144
|
+
): string {
|
|
145
|
+
if (!result || result.length === 0) {
|
|
146
|
+
return "No references found. This may occur if the symbol has no usages, or if the LSP server has not fully indexed the workspace.";
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const validLocations = result.filter((loc) => loc && loc.uri);
|
|
150
|
+
if (validLocations.length === 0) {
|
|
151
|
+
return "No references found. This may occur if the symbol has no usages, or if the LSP server has not fully indexed the workspace.";
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (validLocations.length === 1) {
|
|
155
|
+
return `Found 1 reference:\n ${formatLocation(validLocations[0], workdir)}`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const grouped = groupItemsByUri(validLocations, workdir);
|
|
159
|
+
const lines = [
|
|
160
|
+
`Found ${validLocations.length} references across ${grouped.size} files:`,
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
for (const [path, locs] of grouped) {
|
|
164
|
+
lines.push(`\n${path}:`);
|
|
165
|
+
for (const loc of locs) {
|
|
166
|
+
const line = loc.range.start.line + 1;
|
|
167
|
+
const character = loc.range.start.character + 1;
|
|
168
|
+
lines.push(` Line ${line}:${character}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return lines.join("\n");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Formats hover contents
|
|
177
|
+
*/
|
|
178
|
+
function formatHoverContents(contents: Hover["contents"]): string {
|
|
179
|
+
if (Array.isArray(contents)) {
|
|
180
|
+
return contents
|
|
181
|
+
.map((c) => (typeof c === "string" ? c : c.value))
|
|
182
|
+
.join("\n\n");
|
|
183
|
+
}
|
|
184
|
+
if (typeof contents === "string") {
|
|
185
|
+
return contents;
|
|
186
|
+
}
|
|
187
|
+
return contents.value;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Formats the result of a hover operation
|
|
192
|
+
*/
|
|
193
|
+
function formatHoverResult(result: Hover | null): string {
|
|
194
|
+
if (!result) {
|
|
195
|
+
return "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.";
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const contents = formatHoverContents(result.contents);
|
|
199
|
+
if (result.range) {
|
|
200
|
+
const line = result.range.start.line + 1;
|
|
201
|
+
const character = result.range.start.character + 1;
|
|
202
|
+
return `Hover info at ${line}:${character}:\n\n${contents}`;
|
|
203
|
+
}
|
|
204
|
+
return contents;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Gets the name of a symbol kind
|
|
209
|
+
*/
|
|
210
|
+
function getSymbolKindName(kind: number): string {
|
|
211
|
+
const kinds: Record<number, string> = {
|
|
212
|
+
1: "File",
|
|
213
|
+
2: "Module",
|
|
214
|
+
3: "Namespace",
|
|
215
|
+
4: "Package",
|
|
216
|
+
5: "Class",
|
|
217
|
+
6: "Method",
|
|
218
|
+
7: "Property",
|
|
219
|
+
8: "Field",
|
|
220
|
+
9: "Constructor",
|
|
221
|
+
10: "Enum",
|
|
222
|
+
11: "Interface",
|
|
223
|
+
12: "Function",
|
|
224
|
+
13: "Variable",
|
|
225
|
+
14: "Constant",
|
|
226
|
+
15: "String",
|
|
227
|
+
16: "Number",
|
|
228
|
+
17: "Boolean",
|
|
229
|
+
18: "Array",
|
|
230
|
+
19: "Object",
|
|
231
|
+
20: "Key",
|
|
232
|
+
21: "Null",
|
|
233
|
+
22: "EnumMember",
|
|
234
|
+
23: "Struct",
|
|
235
|
+
24: "Event",
|
|
236
|
+
25: "Operator",
|
|
237
|
+
26: "TypeParameter",
|
|
238
|
+
};
|
|
239
|
+
return kinds[kind] || "Unknown";
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Formats a single document symbol recursively
|
|
244
|
+
*/
|
|
245
|
+
function formatDocumentSymbol(symbol: DocumentSymbol, depth = 0): string[] {
|
|
246
|
+
const results: string[] = [];
|
|
247
|
+
const indent = " ".repeat(depth);
|
|
248
|
+
const kindName = getSymbolKindName(symbol.kind);
|
|
249
|
+
let line = `${indent}${symbol.name} (${kindName})`;
|
|
250
|
+
|
|
251
|
+
if (symbol.detail) {
|
|
252
|
+
line += ` ${symbol.detail}`;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const startLine = symbol.range.start.line + 1;
|
|
256
|
+
line += ` - Line ${startLine}`;
|
|
257
|
+
results.push(line);
|
|
258
|
+
|
|
259
|
+
if (symbol.children && symbol.children.length > 0) {
|
|
260
|
+
for (const child of symbol.children) {
|
|
261
|
+
results.push(...formatDocumentSymbol(child, depth + 1));
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return results;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Formats the result of a documentSymbol operation
|
|
270
|
+
*/
|
|
271
|
+
function formatDocumentSymbolResult(
|
|
272
|
+
result: DocumentSymbol[] | SymbolInformation[] | null,
|
|
273
|
+
workdir?: string,
|
|
274
|
+
): string {
|
|
275
|
+
if (!result || result.length === 0) {
|
|
276
|
+
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.";
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const first = result[0];
|
|
280
|
+
if (first && "location" in first) {
|
|
281
|
+
return formatWorkspaceSymbolResult(result as SymbolInformation[], workdir);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const lines = ["Document symbols:"];
|
|
285
|
+
for (const symbol of result as DocumentSymbol[]) {
|
|
286
|
+
lines.push(...formatDocumentSymbol(symbol));
|
|
287
|
+
}
|
|
288
|
+
return lines.join("\n");
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Formats the result of a workspaceSymbol operation
|
|
293
|
+
*/
|
|
294
|
+
function formatWorkspaceSymbolResult(
|
|
295
|
+
result: SymbolInformation[] | null,
|
|
296
|
+
workdir?: string,
|
|
297
|
+
): string {
|
|
298
|
+
if (!result || result.length === 0) {
|
|
299
|
+
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.";
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const validSymbols = result.filter((s) => s && s.location && s.location.uri);
|
|
303
|
+
if (validSymbols.length === 0) {
|
|
304
|
+
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.";
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const lines = [
|
|
308
|
+
`Found ${validSymbols.length} symbol${validSymbols.length === 1 ? "" : "s"} in workspace:`,
|
|
309
|
+
];
|
|
310
|
+
const grouped = groupItemsByUri(validSymbols, workdir);
|
|
311
|
+
|
|
312
|
+
for (const [path, symbols] of grouped) {
|
|
313
|
+
lines.push(`\n${path}:`);
|
|
314
|
+
for (const s of symbols) {
|
|
315
|
+
const kindName = getSymbolKindName(s.kind);
|
|
316
|
+
const startLine = s.location.range.start.line + 1;
|
|
317
|
+
let line = ` ${s.name} (${kindName}) - Line ${startLine}`;
|
|
318
|
+
if (s.containerName) {
|
|
319
|
+
line += ` in ${s.containerName}`;
|
|
320
|
+
}
|
|
321
|
+
lines.push(line);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return lines.join("\n");
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Formats a call hierarchy item
|
|
330
|
+
*/
|
|
331
|
+
function formatCallHierarchyItem(
|
|
332
|
+
item: CallHierarchyItem,
|
|
333
|
+
workdir?: string,
|
|
334
|
+
): string {
|
|
335
|
+
if (!item.uri) {
|
|
336
|
+
logger.warn("formatCallHierarchyItem: CallHierarchyItem has undefined URI");
|
|
337
|
+
return `${item.name} (${getSymbolKindName(item.kind)}) - <unknown location>`;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const path = formatUri(item.uri, workdir);
|
|
341
|
+
const startLine = item.range.start.line + 1;
|
|
342
|
+
const kindName = getSymbolKindName(item.kind);
|
|
343
|
+
let line = `${item.name} (${kindName}) - ${path}:${startLine}`;
|
|
344
|
+
|
|
345
|
+
if (item.detail) {
|
|
346
|
+
line += ` [${item.detail}]`;
|
|
347
|
+
}
|
|
348
|
+
return line;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Formats the result of a prepareCallHierarchy operation
|
|
353
|
+
*/
|
|
354
|
+
function formatPrepareCallHierarchyResult(
|
|
355
|
+
result: CallHierarchyItem[] | null,
|
|
356
|
+
workdir?: string,
|
|
357
|
+
): string {
|
|
358
|
+
if (!result || result.length === 0) {
|
|
359
|
+
return "No call hierarchy item found at this position";
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (result.length === 1) {
|
|
363
|
+
return `Call hierarchy item: ${formatCallHierarchyItem(result[0], workdir)}`;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const lines = [`Found ${result.length} call hierarchy items:`];
|
|
367
|
+
for (const item of result) {
|
|
368
|
+
lines.push(` ${formatCallHierarchyItem(item, workdir)}`);
|
|
369
|
+
}
|
|
370
|
+
return lines.join("\n");
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Formats the result of an incomingCalls operation
|
|
375
|
+
*/
|
|
376
|
+
function formatIncomingCallsResult(
|
|
377
|
+
result: CallHierarchyIncomingCall[] | null,
|
|
378
|
+
workdir?: string,
|
|
379
|
+
): string {
|
|
380
|
+
if (!result || result.length === 0) {
|
|
381
|
+
return "No incoming calls found (nothing calls this function)";
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const lines = [
|
|
385
|
+
`Found ${result.length} incoming call${result.length === 1 ? "" : "s"}:`,
|
|
386
|
+
];
|
|
387
|
+
const grouped = new Map<string, CallHierarchyIncomingCall[]>();
|
|
388
|
+
|
|
389
|
+
for (const call of result) {
|
|
390
|
+
if (!call.from) {
|
|
391
|
+
logger.warn(
|
|
392
|
+
"formatIncomingCallsResult: CallHierarchyIncomingCall has undefined from field",
|
|
393
|
+
);
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
const path = formatUri(call.from.uri, workdir);
|
|
397
|
+
const existing = grouped.get(path);
|
|
398
|
+
if (existing) {
|
|
399
|
+
existing.push(call);
|
|
400
|
+
} else {
|
|
401
|
+
grouped.set(path, [call]);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
for (const [path, calls] of grouped) {
|
|
406
|
+
lines.push(`\n${path}:`);
|
|
407
|
+
for (const call of calls) {
|
|
408
|
+
if (!call.from) continue;
|
|
409
|
+
const kindName = getSymbolKindName(call.from.kind);
|
|
410
|
+
const startLine = call.from.range.start.line + 1;
|
|
411
|
+
let line = ` ${call.from.name} (${kindName}) - Line ${startLine}`;
|
|
412
|
+
|
|
413
|
+
if (call.fromRanges && call.fromRanges.length > 0) {
|
|
414
|
+
const ranges = call.fromRanges
|
|
415
|
+
.map((r) => `${r.start.line + 1}:${r.start.character + 1}`)
|
|
416
|
+
.join(", ");
|
|
417
|
+
line += ` [calls at: ${ranges}]`;
|
|
418
|
+
}
|
|
419
|
+
lines.push(line);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return lines.join("\n");
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Formats the result of an outgoingCalls operation
|
|
428
|
+
*/
|
|
429
|
+
function formatOutgoingCallsResult(
|
|
430
|
+
result: CallHierarchyOutgoingCall[] | null,
|
|
431
|
+
workdir?: string,
|
|
432
|
+
): string {
|
|
433
|
+
if (!result || result.length === 0) {
|
|
434
|
+
return "No outgoing calls found (this function calls nothing)";
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const lines = [
|
|
438
|
+
`Found ${result.length} outgoing call${result.length === 1 ? "" : "s"}:`,
|
|
439
|
+
];
|
|
440
|
+
const grouped = new Map<string, CallHierarchyOutgoingCall[]>();
|
|
441
|
+
|
|
442
|
+
for (const call of result) {
|
|
443
|
+
if (!call.to) {
|
|
444
|
+
logger.warn(
|
|
445
|
+
"formatOutgoingCallsResult: CallHierarchyOutgoingCall has undefined to field",
|
|
446
|
+
);
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
const path = formatUri(call.to.uri, workdir);
|
|
450
|
+
const existing = grouped.get(path);
|
|
451
|
+
if (existing) {
|
|
452
|
+
existing.push(call);
|
|
453
|
+
} else {
|
|
454
|
+
grouped.set(path, [call]);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
for (const [path, calls] of grouped) {
|
|
459
|
+
lines.push(`\n${path}:`);
|
|
460
|
+
for (const call of calls) {
|
|
461
|
+
if (!call.to) continue;
|
|
462
|
+
const kindName = getSymbolKindName(call.to.kind);
|
|
463
|
+
const startLine = call.to.range.start.line + 1;
|
|
464
|
+
let line = ` ${call.to.name} (${kindName}) - Line ${startLine}`;
|
|
465
|
+
|
|
466
|
+
if (call.fromRanges && call.fromRanges.length > 0) {
|
|
467
|
+
const ranges = call.fromRanges
|
|
468
|
+
.map((r) => `${r.start.line + 1}:${r.start.character + 1}`)
|
|
469
|
+
.join(", ");
|
|
470
|
+
line += ` [called from: ${ranges}]`;
|
|
471
|
+
}
|
|
472
|
+
lines.push(line);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return lines.join("\n");
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* LSP tool plugin - interact with LSP servers for code intelligence
|
|
481
|
+
*/
|
|
482
|
+
export const lspTool: ToolPlugin = {
|
|
483
|
+
name: "LSP",
|
|
484
|
+
config: {
|
|
485
|
+
type: "function",
|
|
486
|
+
function: {
|
|
487
|
+
name: "LSP",
|
|
488
|
+
description: `Interact with Language Server Protocol (LSP) servers to get code intelligence features.
|
|
489
|
+
|
|
490
|
+
Supported operations:
|
|
491
|
+
- goToDefinition: Find where a symbol is defined
|
|
492
|
+
- findReferences: Find all references to a symbol
|
|
493
|
+
- hover: Get hover information (documentation, type info) for a symbol
|
|
494
|
+
- documentSymbol: Get all symbols (functions, classes, variables) in a document
|
|
495
|
+
- workspaceSymbol: Search for symbols across the entire workspace
|
|
496
|
+
- goToImplementation: Find implementations of an interface or abstract method
|
|
497
|
+
- prepareCallHierarchy: Get call hierarchy item at a position (functions/methods)
|
|
498
|
+
- incomingCalls: Find all functions/methods that call the function at a position
|
|
499
|
+
- outgoingCalls: Find all functions/methods called by the function at a position
|
|
500
|
+
|
|
501
|
+
All operations require:
|
|
502
|
+
- filePath: The file to operate on
|
|
503
|
+
- line: The line number (1-based, as shown in editors)
|
|
504
|
+
- character: The character offset (1-based, as shown in editors)
|
|
505
|
+
|
|
506
|
+
Note: LSP servers must be configured for the file type. If no server is available, an error will be returned.`,
|
|
507
|
+
parameters: {
|
|
508
|
+
type: "object",
|
|
509
|
+
properties: {
|
|
510
|
+
operation: {
|
|
511
|
+
type: "string",
|
|
512
|
+
enum: [
|
|
513
|
+
"goToDefinition",
|
|
514
|
+
"findReferences",
|
|
515
|
+
"hover",
|
|
516
|
+
"documentSymbol",
|
|
517
|
+
"workspaceSymbol",
|
|
518
|
+
"goToImplementation",
|
|
519
|
+
"prepareCallHierarchy",
|
|
520
|
+
"incomingCalls",
|
|
521
|
+
"outgoingCalls",
|
|
522
|
+
],
|
|
523
|
+
description: "The LSP operation to perform",
|
|
524
|
+
},
|
|
525
|
+
filePath: {
|
|
526
|
+
type: "string",
|
|
527
|
+
description: "The absolute or relative path to the file",
|
|
528
|
+
},
|
|
529
|
+
line: {
|
|
530
|
+
type: "number",
|
|
531
|
+
description: "The line number (1-based, as shown in editors)",
|
|
532
|
+
},
|
|
533
|
+
character: {
|
|
534
|
+
type: "number",
|
|
535
|
+
description: "The character offset (1-based, as shown in editors)",
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
required: ["operation", "filePath", "line", "character"],
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
execute: async (
|
|
543
|
+
args: Record<string, unknown>,
|
|
544
|
+
context: ToolContext,
|
|
545
|
+
): Promise<ToolResult> => {
|
|
546
|
+
const { operation, filePath, line, character } = args as {
|
|
547
|
+
operation: string;
|
|
548
|
+
filePath: string;
|
|
549
|
+
line: number;
|
|
550
|
+
character: number;
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
if (context.lspManager) {
|
|
554
|
+
try {
|
|
555
|
+
const result = await context.lspManager.execute({
|
|
556
|
+
operation,
|
|
557
|
+
filePath,
|
|
558
|
+
line,
|
|
559
|
+
character,
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
if (!result.success) {
|
|
563
|
+
return {
|
|
564
|
+
success: false,
|
|
565
|
+
content: "",
|
|
566
|
+
error: result.content,
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const rawResult = JSON.parse(result.content);
|
|
571
|
+
let formattedContent: string;
|
|
572
|
+
switch (operation) {
|
|
573
|
+
case "goToDefinition":
|
|
574
|
+
case "goToImplementation":
|
|
575
|
+
formattedContent = formatGoToDefinitionResult(
|
|
576
|
+
rawResult as
|
|
577
|
+
| Location
|
|
578
|
+
| Location[]
|
|
579
|
+
| LocationLink
|
|
580
|
+
| LocationLink[]
|
|
581
|
+
| null,
|
|
582
|
+
context.workdir,
|
|
583
|
+
);
|
|
584
|
+
break;
|
|
585
|
+
case "findReferences":
|
|
586
|
+
formattedContent = formatFindReferencesResult(
|
|
587
|
+
rawResult as Location[] | null,
|
|
588
|
+
context.workdir,
|
|
589
|
+
);
|
|
590
|
+
break;
|
|
591
|
+
case "hover":
|
|
592
|
+
formattedContent = formatHoverResult(rawResult as Hover | null);
|
|
593
|
+
break;
|
|
594
|
+
case "documentSymbol":
|
|
595
|
+
formattedContent = formatDocumentSymbolResult(
|
|
596
|
+
rawResult as DocumentSymbol[] | SymbolInformation[] | null,
|
|
597
|
+
context.workdir,
|
|
598
|
+
);
|
|
599
|
+
break;
|
|
600
|
+
case "workspaceSymbol":
|
|
601
|
+
formattedContent = formatWorkspaceSymbolResult(
|
|
602
|
+
rawResult as SymbolInformation[] | null,
|
|
603
|
+
context.workdir,
|
|
604
|
+
);
|
|
605
|
+
break;
|
|
606
|
+
case "prepareCallHierarchy":
|
|
607
|
+
formattedContent = formatPrepareCallHierarchyResult(
|
|
608
|
+
rawResult as CallHierarchyItem[] | null,
|
|
609
|
+
context.workdir,
|
|
610
|
+
);
|
|
611
|
+
break;
|
|
612
|
+
case "incomingCalls":
|
|
613
|
+
formattedContent = formatIncomingCallsResult(
|
|
614
|
+
rawResult as CallHierarchyIncomingCall[] | null,
|
|
615
|
+
context.workdir,
|
|
616
|
+
);
|
|
617
|
+
break;
|
|
618
|
+
case "outgoingCalls":
|
|
619
|
+
formattedContent = formatOutgoingCallsResult(
|
|
620
|
+
rawResult as CallHierarchyOutgoingCall[] | null,
|
|
621
|
+
context.workdir,
|
|
622
|
+
);
|
|
623
|
+
break;
|
|
624
|
+
default:
|
|
625
|
+
formattedContent = JSON.stringify(rawResult, null, 2);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
return {
|
|
629
|
+
success: true,
|
|
630
|
+
content: formattedContent,
|
|
631
|
+
};
|
|
632
|
+
} catch (error) {
|
|
633
|
+
return {
|
|
634
|
+
success: false,
|
|
635
|
+
content: "",
|
|
636
|
+
error: `LSP operation failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
if (!context.mcpManager) {
|
|
642
|
+
return {
|
|
643
|
+
success: false,
|
|
644
|
+
content: "",
|
|
645
|
+
error: "MCP manager not available in tool context",
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
try {
|
|
650
|
+
// Call the MCP tool named "LSP"
|
|
651
|
+
// We assume there is an MCP server that provides this tool
|
|
652
|
+
const result = await context.mcpManager.executeMcpTool("LSP", {
|
|
653
|
+
operation,
|
|
654
|
+
filePath,
|
|
655
|
+
line,
|
|
656
|
+
character,
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
if (!result.success) {
|
|
660
|
+
return {
|
|
661
|
+
success: false,
|
|
662
|
+
content: "",
|
|
663
|
+
error: result.content,
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
let rawResult: unknown;
|
|
668
|
+
try {
|
|
669
|
+
rawResult = JSON.parse(result.content);
|
|
670
|
+
} catch {
|
|
671
|
+
// If it's not JSON, it might already be formatted or an error message
|
|
672
|
+
return {
|
|
673
|
+
success: true,
|
|
674
|
+
content: result.content,
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
let formattedContent: string;
|
|
679
|
+
switch (operation) {
|
|
680
|
+
case "goToDefinition":
|
|
681
|
+
case "goToImplementation":
|
|
682
|
+
formattedContent = formatGoToDefinitionResult(
|
|
683
|
+
rawResult as
|
|
684
|
+
| Location
|
|
685
|
+
| Location[]
|
|
686
|
+
| LocationLink
|
|
687
|
+
| LocationLink[]
|
|
688
|
+
| null,
|
|
689
|
+
context.workdir,
|
|
690
|
+
);
|
|
691
|
+
break;
|
|
692
|
+
case "findReferences":
|
|
693
|
+
formattedContent = formatFindReferencesResult(
|
|
694
|
+
rawResult as Location[] | null,
|
|
695
|
+
context.workdir,
|
|
696
|
+
);
|
|
697
|
+
break;
|
|
698
|
+
case "hover":
|
|
699
|
+
formattedContent = formatHoverResult(rawResult as Hover | null);
|
|
700
|
+
break;
|
|
701
|
+
case "documentSymbol":
|
|
702
|
+
formattedContent = formatDocumentSymbolResult(
|
|
703
|
+
rawResult as DocumentSymbol[] | SymbolInformation[] | null,
|
|
704
|
+
context.workdir,
|
|
705
|
+
);
|
|
706
|
+
break;
|
|
707
|
+
case "workspaceSymbol":
|
|
708
|
+
formattedContent = formatWorkspaceSymbolResult(
|
|
709
|
+
rawResult as SymbolInformation[] | null,
|
|
710
|
+
context.workdir,
|
|
711
|
+
);
|
|
712
|
+
break;
|
|
713
|
+
case "prepareCallHierarchy":
|
|
714
|
+
formattedContent = formatPrepareCallHierarchyResult(
|
|
715
|
+
rawResult as CallHierarchyItem[] | null,
|
|
716
|
+
context.workdir,
|
|
717
|
+
);
|
|
718
|
+
break;
|
|
719
|
+
case "incomingCalls":
|
|
720
|
+
formattedContent = formatIncomingCallsResult(
|
|
721
|
+
rawResult as CallHierarchyIncomingCall[] | null,
|
|
722
|
+
context.workdir,
|
|
723
|
+
);
|
|
724
|
+
break;
|
|
725
|
+
case "outgoingCalls":
|
|
726
|
+
formattedContent = formatOutgoingCallsResult(
|
|
727
|
+
rawResult as CallHierarchyOutgoingCall[] | null,
|
|
728
|
+
context.workdir,
|
|
729
|
+
);
|
|
730
|
+
break;
|
|
731
|
+
default:
|
|
732
|
+
formattedContent = JSON.stringify(rawResult, null, 2);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
return {
|
|
736
|
+
success: true,
|
|
737
|
+
content: formattedContent,
|
|
738
|
+
};
|
|
739
|
+
} catch (error) {
|
|
740
|
+
return {
|
|
741
|
+
success: false,
|
|
742
|
+
content: "",
|
|
743
|
+
error: `LSP operation failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
},
|
|
747
|
+
formatCompactParams: (
|
|
748
|
+
params: Record<string, unknown>,
|
|
749
|
+
context: ToolContext,
|
|
750
|
+
): string => {
|
|
751
|
+
const { operation, filePath, line, character } = params as {
|
|
752
|
+
operation: string;
|
|
753
|
+
filePath: string;
|
|
754
|
+
line: number;
|
|
755
|
+
character: number;
|
|
756
|
+
};
|
|
757
|
+
const displayPath = getDisplayPath(filePath, context.workdir);
|
|
758
|
+
return `${operation} ${displayPath}:${line}:${character}`;
|
|
759
|
+
},
|
|
760
|
+
};
|