@navigation-agent/mcp-server 0.1.0
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/app/createMcpServer.d.ts +18 -0
- package/dist/app/createMcpServer.js +152 -0
- package/dist/bin/navigation-mcp.d.ts +2 -0
- package/dist/bin/navigation-mcp.js +36 -0
- package/dist/contracts/public/code.d.ts +1106 -0
- package/dist/contracts/public/code.js +523 -0
- package/dist/contracts/public/common.d.ts +28 -0
- package/dist/contracts/public/common.js +23 -0
- package/dist/engine/protocol.d.ts +290 -0
- package/dist/engine/protocol.js +13 -0
- package/dist/engine/rustEngineClient.d.ts +16 -0
- package/dist/engine/rustEngineClient.js +138 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/services/findSymbolService.d.ts +11 -0
- package/dist/services/findSymbolService.js +224 -0
- package/dist/services/inspectTreeService.d.ts +11 -0
- package/dist/services/inspectTreeService.js +191 -0
- package/dist/services/listEndpointsService.d.ts +11 -0
- package/dist/services/listEndpointsService.js +233 -0
- package/dist/services/searchTextService.d.ts +11 -0
- package/dist/services/searchTextService.js +233 -0
- package/dist/services/traceCallersService.d.ts +11 -0
- package/dist/services/traceCallersService.js +256 -0
- package/dist/services/traceSymbolService.d.ts +11 -0
- package/dist/services/traceSymbolService.js +231 -0
- package/dist/tools/registerCodeTools.d.ts +20 -0
- package/dist/tools/registerCodeTools.js +188 -0
- package/package.json +51 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { normalizeSearchTextInput, } from "../contracts/public/code.js";
|
|
2
|
+
import { createResponseMeta, } from "../contracts/public/common.js";
|
|
3
|
+
import { nextRequestId, } from "../engine/protocol.js";
|
|
4
|
+
const TOOL_NAME = "code.search_text";
|
|
5
|
+
export function createSearchTextService(options) {
|
|
6
|
+
return {
|
|
7
|
+
async execute(input) {
|
|
8
|
+
let response;
|
|
9
|
+
try {
|
|
10
|
+
response = await options.engineClient.request({
|
|
11
|
+
id: nextRequestId(),
|
|
12
|
+
capability: "workspace.search_text",
|
|
13
|
+
workspaceRoot: options.workspaceRoot,
|
|
14
|
+
payload: {
|
|
15
|
+
query: input.query,
|
|
16
|
+
path: input.path ?? null,
|
|
17
|
+
publicLanguageFilter: resolveEffectiveLanguage(input.language, input.framework),
|
|
18
|
+
include: input.include ?? null,
|
|
19
|
+
regex: input.regex,
|
|
20
|
+
context: input.context,
|
|
21
|
+
limit: input.limit,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
return buildEngineFailureResponse(input, error);
|
|
27
|
+
}
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
return buildMappedErrorResponse(input, response.error.code, response.error.message, response.error.details, response.error.retryable);
|
|
30
|
+
}
|
|
31
|
+
return buildSuccessResponse(input, response.result);
|
|
32
|
+
},
|
|
33
|
+
async validateAndExecute(payload) {
|
|
34
|
+
const normalized = normalizeSearchTextInput(payload);
|
|
35
|
+
if (!normalized.ok) {
|
|
36
|
+
return buildValidationErrorResponse(normalized.issues);
|
|
37
|
+
}
|
|
38
|
+
return this.execute(normalized.value);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function buildSuccessResponse(input, result) {
|
|
43
|
+
const returnedFileCount = result.items.length;
|
|
44
|
+
const returnedMatchCount = result.items.reduce((total, item) => total + item.matchCount, 0);
|
|
45
|
+
const effectiveLanguage = resolveEffectiveLanguage(input.language, input.framework);
|
|
46
|
+
return {
|
|
47
|
+
tool: TOOL_NAME,
|
|
48
|
+
status: result.truncated ? "partial" : "ok",
|
|
49
|
+
summary: buildSummary(input.query, result.totalFileCount, result.totalMatchCount, result.truncated),
|
|
50
|
+
data: {
|
|
51
|
+
fileCount: result.truncated ? returnedFileCount : result.totalFileCount,
|
|
52
|
+
matchCount: result.truncated ? returnedMatchCount : result.totalMatchCount,
|
|
53
|
+
totalFileCount: result.totalFileCount,
|
|
54
|
+
totalMatchCount: result.totalMatchCount,
|
|
55
|
+
items: result.items.map((item) => ({
|
|
56
|
+
path: item.path,
|
|
57
|
+
language: item.language,
|
|
58
|
+
matchCount: item.matchCount,
|
|
59
|
+
matches: item.matches.map((match) => ({
|
|
60
|
+
line: match.line,
|
|
61
|
+
text: match.text,
|
|
62
|
+
submatches: match.submatches.map((submatch) => ({ ...submatch })),
|
|
63
|
+
before: match.before.map((contextLine) => ({ ...contextLine })),
|
|
64
|
+
after: match.after.map((contextLine) => ({ ...contextLine })),
|
|
65
|
+
})),
|
|
66
|
+
})),
|
|
67
|
+
},
|
|
68
|
+
errors: result.truncated
|
|
69
|
+
? [
|
|
70
|
+
{
|
|
71
|
+
code: "RESULT_TRUNCATED",
|
|
72
|
+
message: `Result set exceeded the requested limit of ${input.limit} files.`,
|
|
73
|
+
retryable: false,
|
|
74
|
+
suggestion: "Increase limit or narrow the path/include/language filters.",
|
|
75
|
+
details: {
|
|
76
|
+
returnedFiles: returnedFileCount,
|
|
77
|
+
totalFiles: result.totalFileCount,
|
|
78
|
+
returnedMatches: returnedMatchCount,
|
|
79
|
+
totalMatches: result.totalMatchCount,
|
|
80
|
+
limit: input.limit,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
]
|
|
84
|
+
: [],
|
|
85
|
+
meta: createResponseMeta({
|
|
86
|
+
query: { ...input },
|
|
87
|
+
resolvedPath: result.resolvedPath,
|
|
88
|
+
truncated: result.truncated,
|
|
89
|
+
counts: {
|
|
90
|
+
returnedFileCount,
|
|
91
|
+
totalFileCount: result.totalFileCount,
|
|
92
|
+
returnedMatchCount,
|
|
93
|
+
totalMatchCount: result.totalMatchCount,
|
|
94
|
+
},
|
|
95
|
+
detection: {
|
|
96
|
+
effectiveLanguage,
|
|
97
|
+
framework: input.framework ?? null,
|
|
98
|
+
},
|
|
99
|
+
}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function buildValidationErrorResponse(issues) {
|
|
103
|
+
return {
|
|
104
|
+
tool: TOOL_NAME,
|
|
105
|
+
status: "error",
|
|
106
|
+
summary: "Request validation failed.",
|
|
107
|
+
data: {
|
|
108
|
+
fileCount: 0,
|
|
109
|
+
matchCount: 0,
|
|
110
|
+
totalFileCount: 0,
|
|
111
|
+
totalMatchCount: 0,
|
|
112
|
+
items: [],
|
|
113
|
+
},
|
|
114
|
+
errors: [
|
|
115
|
+
{
|
|
116
|
+
code: "INVALID_INPUT",
|
|
117
|
+
message: "One or more input fields are invalid.",
|
|
118
|
+
retryable: false,
|
|
119
|
+
suggestion: "Correct the invalid fields and try again.",
|
|
120
|
+
details: { issues },
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
meta: createResponseMeta({ query: {} }),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function buildMappedErrorResponse(input, code, message, details, retryable) {
|
|
127
|
+
const query = { ...input };
|
|
128
|
+
if (code === "FILE_NOT_FOUND") {
|
|
129
|
+
return {
|
|
130
|
+
tool: TOOL_NAME,
|
|
131
|
+
status: "error",
|
|
132
|
+
summary: "Path not found.",
|
|
133
|
+
data: emptyData(),
|
|
134
|
+
errors: [
|
|
135
|
+
{
|
|
136
|
+
code,
|
|
137
|
+
message,
|
|
138
|
+
retryable,
|
|
139
|
+
suggestion: "Provide an existing file or directory path inside the workspace root.",
|
|
140
|
+
details,
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
meta: createResponseMeta({ query }),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if (code === "PATH_OUTSIDE_WORKSPACE") {
|
|
147
|
+
return {
|
|
148
|
+
tool: TOOL_NAME,
|
|
149
|
+
status: "error",
|
|
150
|
+
summary: "Path validation failed.",
|
|
151
|
+
data: emptyData(),
|
|
152
|
+
errors: [
|
|
153
|
+
{
|
|
154
|
+
code,
|
|
155
|
+
message,
|
|
156
|
+
retryable,
|
|
157
|
+
suggestion: "Use a path inside the workspace root or omit the path filter.",
|
|
158
|
+
details,
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
meta: createResponseMeta({ query }),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (code === "UNSUPPORTED_CAPABILITY") {
|
|
165
|
+
return {
|
|
166
|
+
tool: TOOL_NAME,
|
|
167
|
+
status: "error",
|
|
168
|
+
summary: "Text search failed.",
|
|
169
|
+
data: emptyData(),
|
|
170
|
+
errors: [
|
|
171
|
+
{
|
|
172
|
+
code: "BACKEND_EXECUTION_FAILED",
|
|
173
|
+
message,
|
|
174
|
+
retryable,
|
|
175
|
+
suggestion: "Verify the engine supports workspace.search_text and retry.",
|
|
176
|
+
details,
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
meta: createResponseMeta({ query }),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
tool: TOOL_NAME,
|
|
184
|
+
status: "error",
|
|
185
|
+
summary: "Text search failed.",
|
|
186
|
+
data: emptyData(),
|
|
187
|
+
errors: [
|
|
188
|
+
{
|
|
189
|
+
code,
|
|
190
|
+
message,
|
|
191
|
+
retryable,
|
|
192
|
+
details,
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
meta: createResponseMeta({ query }),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function buildEngineFailureResponse(input, error) {
|
|
199
|
+
return buildMappedErrorResponse(input, "BACKEND_EXECUTION_FAILED", error instanceof Error ? error.message : String(error), {}, false);
|
|
200
|
+
}
|
|
201
|
+
function emptyData() {
|
|
202
|
+
return {
|
|
203
|
+
fileCount: 0,
|
|
204
|
+
matchCount: 0,
|
|
205
|
+
totalFileCount: 0,
|
|
206
|
+
totalMatchCount: 0,
|
|
207
|
+
items: [],
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function buildSummary(query, fileCount, matchCount, truncated) {
|
|
211
|
+
if (matchCount === 0) {
|
|
212
|
+
return `No text matches found for '${query}'.`;
|
|
213
|
+
}
|
|
214
|
+
if (truncated) {
|
|
215
|
+
return `Found ${matchCount} text matches across ${fileCount} files for '${query}' and returned a truncated subset.`;
|
|
216
|
+
}
|
|
217
|
+
if (matchCount === 1) {
|
|
218
|
+
return `Found 1 text match in 1 file for '${query}'.`;
|
|
219
|
+
}
|
|
220
|
+
return `Found ${matchCount} text matches across ${fileCount} files for '${query}'.`;
|
|
221
|
+
}
|
|
222
|
+
function resolveEffectiveLanguage(language, framework) {
|
|
223
|
+
if (language) {
|
|
224
|
+
return language;
|
|
225
|
+
}
|
|
226
|
+
if (framework === "react-router") {
|
|
227
|
+
return "typescript";
|
|
228
|
+
}
|
|
229
|
+
if (framework === "spring") {
|
|
230
|
+
return "java";
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type TraceCallersData, type TraceCallersInput } from "../contracts/public/code.ts";
|
|
2
|
+
import { type ResponseEnvelope } from "../contracts/public/common.ts";
|
|
3
|
+
import type { EngineClient } from "../engine/rustEngineClient.ts";
|
|
4
|
+
export interface TraceCallersService {
|
|
5
|
+
execute(input: TraceCallersInput): Promise<ResponseEnvelope<TraceCallersData>>;
|
|
6
|
+
validateAndExecute(payload: Record<string, unknown>): Promise<ResponseEnvelope<TraceCallersData>>;
|
|
7
|
+
}
|
|
8
|
+
export declare function createTraceCallersService(options: {
|
|
9
|
+
workspaceRoot: string;
|
|
10
|
+
engineClient: EngineClient;
|
|
11
|
+
}): TraceCallersService;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { normalizeTraceCallersInput, } from "../contracts/public/code.js";
|
|
2
|
+
import { createResponseMeta, } from "../contracts/public/common.js";
|
|
3
|
+
import { nextRequestId, } from "../engine/protocol.js";
|
|
4
|
+
const TOOL_NAME = "code.trace_callers";
|
|
5
|
+
const DEFAULT_MAX_DEPTH = 3;
|
|
6
|
+
const MAX_MAX_DEPTH = 8;
|
|
7
|
+
export function createTraceCallersService(options) {
|
|
8
|
+
return {
|
|
9
|
+
async execute(input) {
|
|
10
|
+
let response;
|
|
11
|
+
try {
|
|
12
|
+
response = await options.engineClient.request({
|
|
13
|
+
id: nextRequestId(),
|
|
14
|
+
capability: "workspace.trace_callers",
|
|
15
|
+
workspaceRoot: options.workspaceRoot,
|
|
16
|
+
payload: {
|
|
17
|
+
path: input.path,
|
|
18
|
+
symbol: input.symbol,
|
|
19
|
+
analyzerLanguage: resolveAnalyzerLanguage(input.language, input.framework),
|
|
20
|
+
publicLanguageFilter: resolveEffectiveLanguage(input.language, input.framework),
|
|
21
|
+
recursive: input.recursive,
|
|
22
|
+
maxDepth: input.recursive ? resolveMaxDepth(input.max_depth) : null,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
return buildEngineFailureResponse(input, error);
|
|
28
|
+
}
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
return buildMappedErrorResponse(input, response.error.code, response.error.message, response.error.details, response.error.retryable);
|
|
31
|
+
}
|
|
32
|
+
return buildSuccessResponse(input, response.result);
|
|
33
|
+
},
|
|
34
|
+
async validateAndExecute(payload) {
|
|
35
|
+
const normalized = normalizeTraceCallersInput(payload);
|
|
36
|
+
if (!normalized.ok) {
|
|
37
|
+
return buildValidationErrorResponse(normalized.issues);
|
|
38
|
+
}
|
|
39
|
+
return this.execute(normalized.value);
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function buildSuccessResponse(input, result) {
|
|
44
|
+
const effectiveLanguage = resolveEffectiveLanguage(input.language, input.framework);
|
|
45
|
+
const count = result.totalMatched;
|
|
46
|
+
return {
|
|
47
|
+
tool: TOOL_NAME,
|
|
48
|
+
status: result.truncated ? "partial" : "ok",
|
|
49
|
+
summary: buildSummary(input.symbol, input.path, count, input.recursive),
|
|
50
|
+
data: {
|
|
51
|
+
target: {
|
|
52
|
+
path: input.path,
|
|
53
|
+
symbol: input.symbol,
|
|
54
|
+
language: inferLanguageFromPath(input.path),
|
|
55
|
+
},
|
|
56
|
+
count,
|
|
57
|
+
returnedCount: result.items.length,
|
|
58
|
+
items: result.items,
|
|
59
|
+
recursive: result.recursive,
|
|
60
|
+
},
|
|
61
|
+
errors: result.truncated
|
|
62
|
+
? [
|
|
63
|
+
{
|
|
64
|
+
code: "RESULT_TRUNCATED",
|
|
65
|
+
message: "Recursive reverse-trace payload exceeded the response safety caps.",
|
|
66
|
+
retryable: false,
|
|
67
|
+
suggestion: "Retry with a lower max_depth or disable recursive mode.",
|
|
68
|
+
details: {
|
|
69
|
+
maxDepth: input.recursive ? resolveMaxDepth(input.max_depth) : null,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
]
|
|
73
|
+
: [],
|
|
74
|
+
meta: createResponseMeta({
|
|
75
|
+
query: { ...input },
|
|
76
|
+
resolvedPath: result.resolvedPath,
|
|
77
|
+
truncated: result.truncated,
|
|
78
|
+
counts: {
|
|
79
|
+
returnedCount: result.items.length,
|
|
80
|
+
totalMatched: count,
|
|
81
|
+
},
|
|
82
|
+
detection: {
|
|
83
|
+
effectiveLanguage,
|
|
84
|
+
framework: input.framework ?? null,
|
|
85
|
+
},
|
|
86
|
+
}),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function buildValidationErrorResponse(issues) {
|
|
90
|
+
return {
|
|
91
|
+
tool: TOOL_NAME,
|
|
92
|
+
status: "error",
|
|
93
|
+
summary: "Request validation failed.",
|
|
94
|
+
data: emptyData(),
|
|
95
|
+
errors: [
|
|
96
|
+
{
|
|
97
|
+
code: "INVALID_INPUT",
|
|
98
|
+
message: "One or more input fields are invalid.",
|
|
99
|
+
retryable: false,
|
|
100
|
+
suggestion: "Correct the invalid fields and try again.",
|
|
101
|
+
details: { issues },
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
meta: createResponseMeta({ query: {} }),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function buildMappedErrorResponse(input, code, message, details, retryable) {
|
|
108
|
+
const query = { ...input };
|
|
109
|
+
if (code === "FILE_NOT_FOUND") {
|
|
110
|
+
return {
|
|
111
|
+
tool: TOOL_NAME,
|
|
112
|
+
status: "error",
|
|
113
|
+
summary: "Path not found.",
|
|
114
|
+
data: emptyData(input.path, input.symbol),
|
|
115
|
+
errors: [
|
|
116
|
+
{
|
|
117
|
+
code,
|
|
118
|
+
message,
|
|
119
|
+
retryable,
|
|
120
|
+
suggestion: "Provide an existing file path inside the workspace root.",
|
|
121
|
+
details,
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
meta: createResponseMeta({ query }),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
if (code === "PATH_OUTSIDE_WORKSPACE") {
|
|
128
|
+
return {
|
|
129
|
+
tool: TOOL_NAME,
|
|
130
|
+
status: "error",
|
|
131
|
+
summary: "Path validation failed.",
|
|
132
|
+
data: emptyData(input.path, input.symbol),
|
|
133
|
+
errors: [
|
|
134
|
+
{
|
|
135
|
+
code,
|
|
136
|
+
message,
|
|
137
|
+
retryable,
|
|
138
|
+
suggestion: "Use a file path inside the workspace root.",
|
|
139
|
+
details,
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
meta: createResponseMeta({ query }),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
if (code === "UNSUPPORTED_CAPABILITY") {
|
|
146
|
+
return {
|
|
147
|
+
tool: TOOL_NAME,
|
|
148
|
+
status: "error",
|
|
149
|
+
summary: "Caller trace failed.",
|
|
150
|
+
data: emptyData(input.path, input.symbol),
|
|
151
|
+
errors: [
|
|
152
|
+
{
|
|
153
|
+
code: "BACKEND_EXECUTION_FAILED",
|
|
154
|
+
message,
|
|
155
|
+
retryable,
|
|
156
|
+
suggestion: "Verify the engine supports workspace.trace_callers and retry.",
|
|
157
|
+
details,
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
meta: createResponseMeta({ query }),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
tool: TOOL_NAME,
|
|
165
|
+
status: "error",
|
|
166
|
+
summary: "Caller trace failed.",
|
|
167
|
+
data: emptyData(input.path, input.symbol),
|
|
168
|
+
errors: [
|
|
169
|
+
{
|
|
170
|
+
code: code === "BACKEND_EXECUTION_FAILED" ? code : "BACKEND_EXECUTION_FAILED",
|
|
171
|
+
message,
|
|
172
|
+
retryable,
|
|
173
|
+
details,
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
meta: createResponseMeta({ query }),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function buildEngineFailureResponse(input, error) {
|
|
180
|
+
return buildMappedErrorResponse(input, "BACKEND_EXECUTION_FAILED", error instanceof Error ? error.message : String(error), {}, false);
|
|
181
|
+
}
|
|
182
|
+
function emptyData(path = "", symbol = "") {
|
|
183
|
+
return {
|
|
184
|
+
target: {
|
|
185
|
+
path,
|
|
186
|
+
symbol,
|
|
187
|
+
language: inferLanguageFromPath(path),
|
|
188
|
+
},
|
|
189
|
+
count: 0,
|
|
190
|
+
returnedCount: 0,
|
|
191
|
+
items: [],
|
|
192
|
+
recursive: null,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function buildSummary(symbol, path, count, recursive) {
|
|
196
|
+
if (count === 0) {
|
|
197
|
+
return `Trace completed for incoming callers of '${symbol}' from '${path}' with no callers found.`;
|
|
198
|
+
}
|
|
199
|
+
if (recursive) {
|
|
200
|
+
if (count === 1) {
|
|
201
|
+
return `Found 1 incoming caller for '${symbol}' from '${path}' with recursive reverse trace.`;
|
|
202
|
+
}
|
|
203
|
+
return `Found ${count} incoming callers for '${symbol}' from '${path}' with recursive reverse trace.`;
|
|
204
|
+
}
|
|
205
|
+
if (count === 1) {
|
|
206
|
+
return `Found 1 incoming caller for '${symbol}' from '${path}'.`;
|
|
207
|
+
}
|
|
208
|
+
return `Found ${count} incoming callers for '${symbol}' from '${path}'.`;
|
|
209
|
+
}
|
|
210
|
+
function resolveMaxDepth(maxDepth) {
|
|
211
|
+
return Math.min(maxDepth ?? DEFAULT_MAX_DEPTH, MAX_MAX_DEPTH);
|
|
212
|
+
}
|
|
213
|
+
function resolveEffectiveLanguage(language, framework) {
|
|
214
|
+
if (language) {
|
|
215
|
+
return language;
|
|
216
|
+
}
|
|
217
|
+
if (framework === "react-router") {
|
|
218
|
+
return "typescript";
|
|
219
|
+
}
|
|
220
|
+
if (framework === "spring") {
|
|
221
|
+
return "java";
|
|
222
|
+
}
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
function resolveAnalyzerLanguage(language, framework) {
|
|
226
|
+
const effective = resolveEffectiveLanguage(language, framework);
|
|
227
|
+
if (effective === "java") {
|
|
228
|
+
return "java";
|
|
229
|
+
}
|
|
230
|
+
if (effective === "python") {
|
|
231
|
+
return "python";
|
|
232
|
+
}
|
|
233
|
+
if (effective === "rust") {
|
|
234
|
+
return "rust";
|
|
235
|
+
}
|
|
236
|
+
return "typescript";
|
|
237
|
+
}
|
|
238
|
+
function inferLanguageFromPath(path) {
|
|
239
|
+
const normalized = path.toLowerCase();
|
|
240
|
+
if (normalized.endsWith(".ts") || normalized.endsWith(".tsx")) {
|
|
241
|
+
return "typescript";
|
|
242
|
+
}
|
|
243
|
+
if (normalized.endsWith(".js") || normalized.endsWith(".jsx")) {
|
|
244
|
+
return "javascript";
|
|
245
|
+
}
|
|
246
|
+
if (normalized.endsWith(".java")) {
|
|
247
|
+
return "java";
|
|
248
|
+
}
|
|
249
|
+
if (normalized.endsWith(".py")) {
|
|
250
|
+
return "python";
|
|
251
|
+
}
|
|
252
|
+
if (normalized.endsWith(".rs")) {
|
|
253
|
+
return "rust";
|
|
254
|
+
}
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type TraceSymbolData, type TraceSymbolInput } from "../contracts/public/code.ts";
|
|
2
|
+
import { type ResponseEnvelope } from "../contracts/public/common.ts";
|
|
3
|
+
import type { EngineClient } from "../engine/rustEngineClient.ts";
|
|
4
|
+
export interface TraceSymbolService {
|
|
5
|
+
execute(input: TraceSymbolInput): Promise<ResponseEnvelope<TraceSymbolData>>;
|
|
6
|
+
validateAndExecute(payload: Record<string, unknown>): Promise<ResponseEnvelope<TraceSymbolData>>;
|
|
7
|
+
}
|
|
8
|
+
export declare function createTraceSymbolService(options: {
|
|
9
|
+
workspaceRoot: string;
|
|
10
|
+
engineClient: EngineClient;
|
|
11
|
+
}): TraceSymbolService;
|