ast-mcp-server 0.5.1 → 0.7.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/CHANGELOG.md +36 -0
- package/README.md +63 -14
- package/SECURITY.md +31 -0
- package/dist/cli.js +0 -0
- package/dist/index.js +129 -10
- package/dist/server.js +25 -2
- package/dist/services/agent-setup.js +36 -117
- package/dist/services/agent-targets.js +122 -0
- package/dist/services/context-builder.js +262 -0
- package/dist/services/diagnostics.js +10 -5
- package/dist/services/file-fingerprints.js +104 -0
- package/dist/services/file-snapshot.js +103 -0
- package/dist/services/impact.js +334 -0
- package/dist/services/operations.js +343 -254
- package/dist/services/outline.js +94 -21
- package/dist/services/pagination.js +10 -4
- package/dist/services/project-operation-scheduler.js +475 -0
- package/dist/services/project-status.js +134 -52
- package/dist/services/project-watcher.js +230 -0
- package/dist/services/project.js +646 -66
- package/dist/services/public-errors.js +255 -0
- package/dist/services/references.js +54 -0
- package/dist/services/relationships.js +1198 -0
- package/dist/services/request-context.js +42 -0
- package/dist/services/runtime-activity.js +65 -0
- package/dist/services/runtime-logger.js +50 -0
- package/dist/services/runtime-policy.js +47 -0
- package/dist/services/setup-wizard.js +4 -7
- package/dist/services/shutdown.js +93 -0
- package/dist/services/symbol-index-limits.js +5 -0
- package/dist/services/symbol-index-order.js +13 -0
- package/dist/services/symbol-index-policy.js +91 -0
- package/dist/services/symbol-index-sqlite.js +850 -0
- package/dist/services/symbol-index.js +447 -0
- package/dist/services/symbols.js +430 -47
- package/dist/services/test-candidates.js +209 -0
- package/dist/services/workspace.js +18 -7
- package/dist/tools/apply_operation.js +8 -4
- package/dist/tools/explore.js +176 -0
- package/dist/tools/find_references.js +17 -55
- package/dist/tools/get_diagnostics.js +10 -7
- package/dist/tools/get_file.js +107 -0
- package/dist/tools/get_impact.js +134 -0
- package/dist/tools/get_operation_preview.js +7 -4
- package/dist/tools/get_outline.js +8 -6
- package/dist/tools/get_project_status.js +77 -8
- package/dist/tools/get_symbol_source.js +8 -6
- package/dist/tools/list_files.js +11 -6
- package/dist/tools/rename_symbol.js +6 -4
- package/dist/tools/replace_symbol_body.js +6 -4
- package/dist/tools/result.js +27 -17
- package/dist/tools/scaffold_class.js +6 -4
- package/dist/tools/search_symbols.js +15 -63
- package/docs/support.md +80 -0
- package/package.json +18 -6
- package/skills/structural-code-editing/SKILL.md +42 -8
- package/benchmark/README.md +0 -129
package/dist/services/outline.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { Node, } from "ts-morph";
|
|
3
|
+
import { NO_REQUEST_CONTEXT } from "./request-context.js";
|
|
3
4
|
function joinPresent(parts) {
|
|
4
5
|
return parts.filter((part) => Boolean(part)).join(" ");
|
|
5
6
|
}
|
|
@@ -80,20 +81,34 @@ function symbolOf(node, symbolPath, name, signature) {
|
|
|
80
81
|
endLine: node.getEndLineNumber(),
|
|
81
82
|
};
|
|
82
83
|
}
|
|
83
|
-
function
|
|
84
|
+
function classDeclarationSignature(statement) {
|
|
84
85
|
const name = statement.getName() ?? "<anonymous>";
|
|
85
|
-
const symbolPath = prefix ? `${prefix}.${name}` : name;
|
|
86
86
|
const heritage = statement.getExtends()?.getText();
|
|
87
87
|
const implementations = statement.getImplements().map((item) => item.getText());
|
|
88
|
-
|
|
88
|
+
return joinPresent([
|
|
89
89
|
modifiersOf(statement),
|
|
90
90
|
`class ${name}${typeParametersOf(statement)}`,
|
|
91
91
|
heritage ? `extends ${heritage}` : undefined,
|
|
92
92
|
implementations.length > 0 ? `implements ${implementations.join(", ")}` : undefined,
|
|
93
93
|
]);
|
|
94
|
+
}
|
|
95
|
+
function interfaceDeclarationSignature(statement) {
|
|
96
|
+
const name = statement.getName();
|
|
97
|
+
const extensions = statement.getExtends().map((item) => item.getText());
|
|
98
|
+
return joinPresent([
|
|
99
|
+
modifiersOf(statement),
|
|
100
|
+
`interface ${name}${typeParametersOf(statement)}`,
|
|
101
|
+
extensions.length > 0 ? `extends ${extensions.join(", ")}` : undefined,
|
|
102
|
+
]);
|
|
103
|
+
}
|
|
104
|
+
function formatClass(statement, prefix = "", requestContext = NO_REQUEST_CONTEXT) {
|
|
105
|
+
const name = statement.getName() ?? "<anonymous>";
|
|
106
|
+
const symbolPath = prefix ? `${prefix}.${name}` : name;
|
|
107
|
+
const declaration = classDeclarationSignature(statement);
|
|
94
108
|
const lines = [`${declaration} {`];
|
|
95
109
|
const symbols = [symbolOf(statement, symbolPath, name, declaration)];
|
|
96
110
|
for (const member of statement.getMembers()) {
|
|
111
|
+
requestContext.checkpoint();
|
|
97
112
|
const signature = memberSignature(member);
|
|
98
113
|
if (!signature)
|
|
99
114
|
continue;
|
|
@@ -104,18 +119,14 @@ function formatClass(statement, prefix = "") {
|
|
|
104
119
|
lines.push("}");
|
|
105
120
|
return { lines, symbols };
|
|
106
121
|
}
|
|
107
|
-
function formatInterface(statement, prefix = "") {
|
|
122
|
+
function formatInterface(statement, prefix = "", requestContext = NO_REQUEST_CONTEXT) {
|
|
108
123
|
const name = statement.getName();
|
|
109
124
|
const symbolPath = prefix ? `${prefix}.${name}` : name;
|
|
110
|
-
const
|
|
111
|
-
const declaration = joinPresent([
|
|
112
|
-
modifiersOf(statement),
|
|
113
|
-
`interface ${name}${typeParametersOf(statement)}`,
|
|
114
|
-
extensions.length > 0 ? `extends ${extensions.join(", ")}` : undefined,
|
|
115
|
-
]);
|
|
125
|
+
const declaration = interfaceDeclarationSignature(statement);
|
|
116
126
|
const lines = [`${declaration} {`];
|
|
117
127
|
const symbols = [symbolOf(statement, symbolPath, name, declaration)];
|
|
118
128
|
for (const member of statement.getMembers()) {
|
|
129
|
+
requestContext.checkpoint();
|
|
119
130
|
const signature = member.getText().trim();
|
|
120
131
|
lines.push(` ${signature}`);
|
|
121
132
|
const memberName = "getName" in member ? member.getName() : member.getKindName();
|
|
@@ -124,12 +135,13 @@ function formatInterface(statement, prefix = "") {
|
|
|
124
135
|
lines.push("}");
|
|
125
136
|
return { lines, symbols };
|
|
126
137
|
}
|
|
127
|
-
function formatVariable(statement) {
|
|
138
|
+
function formatVariable(statement, requestContext = NO_REQUEST_CONTEXT) {
|
|
128
139
|
const lines = [];
|
|
129
140
|
const symbols = [];
|
|
130
141
|
const keyword = statement.getDeclarationKind();
|
|
131
142
|
const modifiers = modifiersOf(statement);
|
|
132
143
|
for (const declaration of statement.getDeclarations()) {
|
|
144
|
+
requestContext.checkpoint();
|
|
133
145
|
const name = declaration.getName();
|
|
134
146
|
const explicitType = declaration.getTypeNode()?.getText();
|
|
135
147
|
const initializer = declaration.getInitializer();
|
|
@@ -155,11 +167,13 @@ function moduleStatements(module) {
|
|
|
155
167
|
return [body];
|
|
156
168
|
return [];
|
|
157
169
|
}
|
|
158
|
-
function formatStatement(statement, prefix = "") {
|
|
170
|
+
function formatStatement(statement, prefix = "", requestContext = NO_REQUEST_CONTEXT) {
|
|
171
|
+
requestContext.checkpoint();
|
|
159
172
|
if (Node.isClassDeclaration(statement))
|
|
160
|
-
return formatClass(statement, prefix);
|
|
161
|
-
if (Node.isInterfaceDeclaration(statement))
|
|
162
|
-
return formatInterface(statement, prefix);
|
|
173
|
+
return formatClass(statement, prefix, requestContext);
|
|
174
|
+
if (Node.isInterfaceDeclaration(statement)) {
|
|
175
|
+
return formatInterface(statement, prefix, requestContext);
|
|
176
|
+
}
|
|
163
177
|
if (Node.isFunctionDeclaration(statement)) {
|
|
164
178
|
const name = statement.getName() ?? "<anonymous>";
|
|
165
179
|
const symbolPath = prefix ? `${prefix}.${name}` : name;
|
|
@@ -180,13 +194,16 @@ function formatStatement(statement, prefix = "") {
|
|
|
180
194
|
const symbolPath = prefix ? `${prefix}.${name}` : name;
|
|
181
195
|
const members = statement
|
|
182
196
|
.getMembers()
|
|
183
|
-
.map((member) =>
|
|
197
|
+
.map((member) => {
|
|
198
|
+
requestContext.checkpoint();
|
|
199
|
+
return member.getName();
|
|
200
|
+
})
|
|
184
201
|
.join(", ");
|
|
185
202
|
const signature = joinPresent([modifiersOf(statement), `enum ${name} { ${members} }`]);
|
|
186
203
|
return { lines: [signature], symbols: [symbolOf(statement, symbolPath, name, signature)] };
|
|
187
204
|
}
|
|
188
205
|
if (Node.isVariableStatement(statement))
|
|
189
|
-
return formatVariable(statement);
|
|
206
|
+
return formatVariable(statement, requestContext);
|
|
190
207
|
if (Node.isModuleDeclaration(statement)) {
|
|
191
208
|
const name = statement.getName();
|
|
192
209
|
const symbolPath = prefix ? `${prefix}.${name}` : name;
|
|
@@ -194,7 +211,8 @@ function formatStatement(statement, prefix = "") {
|
|
|
194
211
|
const lines = [`${declaration} {`];
|
|
195
212
|
const symbols = [symbolOf(statement, symbolPath, name, declaration)];
|
|
196
213
|
for (const child of moduleStatements(statement)) {
|
|
197
|
-
|
|
214
|
+
requestContext.checkpoint();
|
|
215
|
+
const formatted = formatStatement(child, symbolPath, requestContext);
|
|
198
216
|
if (!formatted)
|
|
199
217
|
continue;
|
|
200
218
|
lines.push(...formatted.lines.map((line) => ` ${line}`));
|
|
@@ -205,11 +223,65 @@ function formatStatement(statement, prefix = "") {
|
|
|
205
223
|
}
|
|
206
224
|
return undefined;
|
|
207
225
|
}
|
|
208
|
-
export function
|
|
226
|
+
export function declarationSignature(node, requestContext = NO_REQUEST_CONTEXT) {
|
|
227
|
+
requestContext.checkpoint();
|
|
228
|
+
if (Node.isFunctionDeclaration(node))
|
|
229
|
+
return functionSignature(node);
|
|
230
|
+
if (Node.isMethodDeclaration(node))
|
|
231
|
+
return methodSignature(node);
|
|
232
|
+
if (Node.isConstructorDeclaration(node) ||
|
|
233
|
+
Node.isGetAccessorDeclaration(node) ||
|
|
234
|
+
Node.isSetAccessorDeclaration(node) ||
|
|
235
|
+
Node.isPropertyDeclaration(node)) {
|
|
236
|
+
return memberSignature(node) ?? node.getText().trim();
|
|
237
|
+
}
|
|
238
|
+
if (Node.isMethodSignature(node) || Node.isPropertySignature(node)) {
|
|
239
|
+
return node.getText().trim();
|
|
240
|
+
}
|
|
241
|
+
if (Node.isClassDeclaration(node))
|
|
242
|
+
return classDeclarationSignature(node);
|
|
243
|
+
if (Node.isInterfaceDeclaration(node))
|
|
244
|
+
return interfaceDeclarationSignature(node);
|
|
245
|
+
if (Node.isTypeAliasDeclaration(node)) {
|
|
246
|
+
return joinPresent([
|
|
247
|
+
modifiersOf(node),
|
|
248
|
+
`type ${node.getName()}${typeParametersOf(node)} = ${node.getTypeNode()?.getText() ?? "unknown"};`,
|
|
249
|
+
]);
|
|
250
|
+
}
|
|
251
|
+
if (Node.isEnumDeclaration(node)) {
|
|
252
|
+
const members = node.getMembers().map((member) => {
|
|
253
|
+
requestContext.checkpoint();
|
|
254
|
+
return member.getName();
|
|
255
|
+
});
|
|
256
|
+
return joinPresent([modifiersOf(node), `enum ${node.getName()} { ${members.join(", ")} }`]);
|
|
257
|
+
}
|
|
258
|
+
if (Node.isModuleDeclaration(node)) {
|
|
259
|
+
return joinPresent([modifiersOf(node), `namespace ${node.getName()}`]);
|
|
260
|
+
}
|
|
261
|
+
if (Node.isVariableDeclaration(node)) {
|
|
262
|
+
let statement = node.getParent();
|
|
263
|
+
while (statement && !Node.isVariableStatement(statement))
|
|
264
|
+
statement = statement.getParent();
|
|
265
|
+
if (statement && Node.isVariableStatement(statement)) {
|
|
266
|
+
const explicitType = node.getTypeNode()?.getText();
|
|
267
|
+
const initializer = node.getInitializer();
|
|
268
|
+
const callableType = initializer && (Node.isArrowFunction(initializer) || Node.isFunctionExpression(initializer))
|
|
269
|
+
? callableExpressionSignature(initializer)
|
|
270
|
+
: undefined;
|
|
271
|
+
return joinPresent([
|
|
272
|
+
modifiersOf(statement),
|
|
273
|
+
`${statement.getDeclarationKind()} ${node.getName()}${explicitType || callableType ? `: ${explicitType ?? callableType}` : ""};`,
|
|
274
|
+
]);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return node.getText().trim();
|
|
278
|
+
}
|
|
279
|
+
export function buildFileOutline(sourceFile, requestContext = NO_REQUEST_CONTEXT) {
|
|
209
280
|
const lines = [];
|
|
210
281
|
const symbols = [];
|
|
211
282
|
for (const statement of sourceFile.getStatements()) {
|
|
212
|
-
|
|
283
|
+
requestContext.checkpoint();
|
|
284
|
+
const formatted = formatStatement(statement, "", requestContext);
|
|
213
285
|
if (!formatted)
|
|
214
286
|
continue;
|
|
215
287
|
lines.push(...formatted.lines);
|
|
@@ -220,7 +292,8 @@ export function buildFileOutline(sourceFile) {
|
|
|
220
292
|
export function fileOutline(sourceFile) {
|
|
221
293
|
return buildFileOutline(sourceFile).text;
|
|
222
294
|
}
|
|
223
|
-
export function nodeSourceWithLocation(node, projectRoot) {
|
|
295
|
+
export function nodeSourceWithLocation(node, projectRoot, requestContext = NO_REQUEST_CONTEXT) {
|
|
296
|
+
requestContext.checkpoint();
|
|
224
297
|
const sourceFile = node.getSourceFile();
|
|
225
298
|
return {
|
|
226
299
|
file: projectRoot
|
|
@@ -2,11 +2,17 @@ import { z } from "zod";
|
|
|
2
2
|
export const DEFAULT_PAGE_LIMIT = 100;
|
|
3
3
|
export const MAX_PAGE_LIMIT = 500;
|
|
4
4
|
export function createPaginationInputSchema(defaultLimit = DEFAULT_PAGE_LIMIT) {
|
|
5
|
-
if (!Number.
|
|
5
|
+
if (!Number.isSafeInteger(defaultLimit) || defaultLimit < 1 || defaultLimit > MAX_PAGE_LIMIT) {
|
|
6
6
|
throw new Error(`Default page limit must be an integer from 1 to ${MAX_PAGE_LIMIT}.`);
|
|
7
7
|
}
|
|
8
8
|
return {
|
|
9
|
-
offset: z
|
|
9
|
+
offset: z
|
|
10
|
+
.number()
|
|
11
|
+
.int()
|
|
12
|
+
.min(0)
|
|
13
|
+
.max(Number.MAX_SAFE_INTEGER)
|
|
14
|
+
.default(0)
|
|
15
|
+
.describe("Zero-based result offset."),
|
|
10
16
|
limit: z
|
|
11
17
|
.number()
|
|
12
18
|
.int()
|
|
@@ -18,11 +24,11 @@ export function createPaginationInputSchema(defaultLimit = DEFAULT_PAGE_LIMIT) {
|
|
|
18
24
|
}
|
|
19
25
|
export const PaginationInputSchema = createPaginationInputSchema();
|
|
20
26
|
export const PaginationOutputSchema = {
|
|
21
|
-
offset: z.number().int().min(0),
|
|
27
|
+
offset: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),
|
|
22
28
|
limit: z.number().int().min(1),
|
|
23
29
|
total: z.number().int().min(0),
|
|
24
30
|
has_more: z.boolean(),
|
|
25
|
-
next_offset: z.number().int().min(0).nullable(),
|
|
31
|
+
next_offset: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER).nullable(),
|
|
26
32
|
};
|
|
27
33
|
export function paginate(items, offset, limit) {
|
|
28
34
|
const pageItems = items.slice(offset, offset + limit);
|