ast-mcp-server 0.9.2 → 0.10.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 +16 -0
- package/README.md +21 -14
- package/SECURITY.md +1 -1
- package/dist/batch/schema.js +2 -0
- package/dist/server.js +2 -0
- package/dist/services/agent-targets.js +1 -0
- package/dist/services/call-spines.js +101 -0
- package/dist/services/context-builder.js +83 -24
- package/dist/services/explore-presentation.js +160 -0
- package/dist/services/project.js +5 -2
- package/dist/services/public-errors.js +7 -1
- package/dist/services/relationships.js +104 -0
- package/dist/services/test-candidates.js +15 -8
- package/dist/tools/explore.js +73 -2
- package/dist/tools/find_test_candidates.js +124 -0
- package/dist/tools/get_impact.js +3 -30
- package/dist/tools/relationship-schema.js +31 -0
- package/docs/support.md +13 -13
- package/package.json +2 -2
- package/skills/structural-code-editing/SKILL.md +6 -4
- package/skills/structural-code-editing/releases.json +7 -2
|
@@ -1159,6 +1159,110 @@ export function createCompilerRelationshipResolver(project, projectRoot, freshne
|
|
|
1159
1159
|
},
|
|
1160
1160
|
};
|
|
1161
1161
|
}
|
|
1162
|
+
function callLikeExpression(node) {
|
|
1163
|
+
if (Node.isCallExpression(node) || Node.isNewExpression(node))
|
|
1164
|
+
return node.getExpression();
|
|
1165
|
+
if (Node.isTaggedTemplateExpression(node))
|
|
1166
|
+
return node.getTag();
|
|
1167
|
+
return undefined;
|
|
1168
|
+
}
|
|
1169
|
+
function unwrapInvocationExpression(node) {
|
|
1170
|
+
let current = node;
|
|
1171
|
+
while (Node.isParenthesizedExpression(current) ||
|
|
1172
|
+
Node.isNonNullExpression(current) ||
|
|
1173
|
+
Node.isAsExpression(current) ||
|
|
1174
|
+
Node.isTypeAssertion(current) ||
|
|
1175
|
+
Node.isSatisfiesExpression(current)) {
|
|
1176
|
+
current = current.getExpression();
|
|
1177
|
+
}
|
|
1178
|
+
return current;
|
|
1179
|
+
}
|
|
1180
|
+
function locatedCallTarget(projectRoot, declaration) {
|
|
1181
|
+
if (Node.isParameterDeclaration(declaration) || Node.isMethodSignature(declaration))
|
|
1182
|
+
return undefined;
|
|
1183
|
+
const sourceFile = declaration.getSourceFile();
|
|
1184
|
+
if (!isProjectScopedFile(projectRoot, sourceFile.getFilePath()))
|
|
1185
|
+
return undefined;
|
|
1186
|
+
const targetNode = Node.isConstructorDeclaration(declaration)
|
|
1187
|
+
? declaration.getFirstAncestorByKind(SyntaxKind.ClassDeclaration)
|
|
1188
|
+
: declaration;
|
|
1189
|
+
if (!targetNode)
|
|
1190
|
+
return undefined;
|
|
1191
|
+
return (collectSymbols(sourceFile).find((symbol) => symbol.node === targetNode) ??
|
|
1192
|
+
containingSymbol(sourceFile, targetNode));
|
|
1193
|
+
}
|
|
1194
|
+
export function collectCompilerCallRelationships(project, projectRoot, freshness, options = {}, requestContext = NO_REQUEST_CONTEXT) {
|
|
1195
|
+
const maxEdges = options.max_edges ?? 5_000;
|
|
1196
|
+
const maxWorkItems = options.max_work_items ?? 100_000;
|
|
1197
|
+
if (!Number.isSafeInteger(maxEdges) || maxEdges < 1) {
|
|
1198
|
+
throw new Error("Compiler call max_edges must be a positive safe integer.");
|
|
1199
|
+
}
|
|
1200
|
+
if (!Number.isSafeInteger(maxWorkItems) || maxWorkItems < 1) {
|
|
1201
|
+
throw new Error("Compiler call max_work_items must be a positive safe integer.");
|
|
1202
|
+
}
|
|
1203
|
+
const normalizedFreshness = normalizeFreshness(freshness);
|
|
1204
|
+
const checker = project.getTypeChecker();
|
|
1205
|
+
const edges = new Map();
|
|
1206
|
+
let workItems = 0;
|
|
1207
|
+
let workLimitReached = false;
|
|
1208
|
+
for (const sourceFile of project
|
|
1209
|
+
.getSourceFiles()
|
|
1210
|
+
.sort((left, right) => left.getFilePath().localeCompare(right.getFilePath()))) {
|
|
1211
|
+
sourceFile.forEachDescendant((node, traversal) => {
|
|
1212
|
+
requestContext.checkpoint();
|
|
1213
|
+
if (++workItems > maxWorkItems) {
|
|
1214
|
+
workLimitReached = true;
|
|
1215
|
+
traversal.stop();
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
const expression = callLikeExpression(node);
|
|
1219
|
+
if (!expression)
|
|
1220
|
+
return;
|
|
1221
|
+
const invoked = unwrapInvocationExpression(expression);
|
|
1222
|
+
const caller = containingSymbol(sourceFile, node);
|
|
1223
|
+
if (!caller)
|
|
1224
|
+
return;
|
|
1225
|
+
const signature = checker.getResolvedSignature(node);
|
|
1226
|
+
const implicitConstructSignatures = Node.isNewExpression(node)
|
|
1227
|
+
? invoked.getType().getConstructSignatures()
|
|
1228
|
+
: [];
|
|
1229
|
+
if (!signature && implicitConstructSignatures.length !== 1)
|
|
1230
|
+
return;
|
|
1231
|
+
const signatureDeclaration = signature?.getDeclaration();
|
|
1232
|
+
const declarations = declarationsForSymbol(invoked.getSymbol());
|
|
1233
|
+
const targets = new Map();
|
|
1234
|
+
for (const declaration of declarations.length > 0
|
|
1235
|
+
? declarations
|
|
1236
|
+
: signatureDeclaration
|
|
1237
|
+
? [signatureDeclaration]
|
|
1238
|
+
: []) {
|
|
1239
|
+
const target = locatedCallTarget(projectRoot, declaration);
|
|
1240
|
+
if (target)
|
|
1241
|
+
targets.set(symbolEndpoint(target, projectRoot).selector, target);
|
|
1242
|
+
}
|
|
1243
|
+
if (targets.size !== 1)
|
|
1244
|
+
return;
|
|
1245
|
+
const target = [...targets.values()][0];
|
|
1246
|
+
const edge = createRelationshipEdge({
|
|
1247
|
+
source: symbolEndpoint(caller, projectRoot),
|
|
1248
|
+
target: symbolEndpoint(target, projectRoot),
|
|
1249
|
+
kind: "call",
|
|
1250
|
+
provenance: "compiler",
|
|
1251
|
+
confidence: "exact",
|
|
1252
|
+
resolution: "resolved",
|
|
1253
|
+
freshness: normalizedFreshness,
|
|
1254
|
+
});
|
|
1255
|
+
edges.set(edge.relationship_id, edge);
|
|
1256
|
+
});
|
|
1257
|
+
if (workLimitReached)
|
|
1258
|
+
break;
|
|
1259
|
+
}
|
|
1260
|
+
const ordered = [...edges.values()].sort((left, right) => left.relationship_id.localeCompare(right.relationship_id));
|
|
1261
|
+
return Object.freeze({
|
|
1262
|
+
edges: Object.freeze(ordered.slice(0, maxEdges)),
|
|
1263
|
+
incomplete: workLimitReached || ordered.length > maxEdges,
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1162
1266
|
export function collectCompilerRelationships(project, projectRoot, freshness, requestContext = NO_REQUEST_CONTEXT) {
|
|
1163
1267
|
requestContext.checkpoint();
|
|
1164
1268
|
const sourceFiles = project
|
|
@@ -4,21 +4,22 @@ export const DEFAULT_TEST_DIRECTORIES = Object.freeze(["test", "tests", "__tests
|
|
|
4
4
|
export const TEST_CANDIDATE_REASONS = Object.freeze([
|
|
5
5
|
"direct_compiler_reference",
|
|
6
6
|
"transitive_compiler_reference",
|
|
7
|
+
"convention_match",
|
|
7
8
|
]);
|
|
8
9
|
export const TEST_CANDIDATE_CONFIDENCES = Object.freeze(["exact", "high"]);
|
|
9
|
-
const
|
|
10
|
-
const
|
|
10
|
+
export const MAX_TEST_CANDIDATE_CONVENTION_ITEMS = 32;
|
|
11
|
+
export const MAX_TEST_CANDIDATE_CONVENTION_LENGTH = 256;
|
|
11
12
|
function normalizeConventionValues(values, defaults, label) {
|
|
12
13
|
const selected = values ?? defaults;
|
|
13
|
-
if (!Array.isArray(selected) || selected.length >
|
|
14
|
-
throw new Error(`${label} must contain at most ${
|
|
14
|
+
if (!Array.isArray(selected) || selected.length > MAX_TEST_CANDIDATE_CONVENTION_ITEMS) {
|
|
15
|
+
throw new Error(`${label} must contain at most ${MAX_TEST_CANDIDATE_CONVENTION_ITEMS} entries.`);
|
|
15
16
|
}
|
|
16
17
|
const normalized = selected.map((value) => {
|
|
17
18
|
if (typeof value !== "string" || value.trim().length === 0) {
|
|
18
19
|
throw new Error(`${label} entries must be non-empty strings.`);
|
|
19
20
|
}
|
|
20
|
-
if (value.length >
|
|
21
|
-
throw new Error(`${label} entries must not exceed ${
|
|
21
|
+
if (value.length > MAX_TEST_CANDIDATE_CONVENTION_LENGTH) {
|
|
22
|
+
throw new Error(`${label} entries must not exceed ${MAX_TEST_CANDIDATE_CONVENTION_LENGTH} characters.`);
|
|
22
23
|
}
|
|
23
24
|
return value.trim().replaceAll("\\", "/");
|
|
24
25
|
});
|
|
@@ -112,7 +113,13 @@ function edgeOrder(left, right) {
|
|
|
112
113
|
left.target.file.localeCompare(right.target.file) ||
|
|
113
114
|
left.target.selector.localeCompare(right.target.selector));
|
|
114
115
|
}
|
|
115
|
-
function candidateReason(depth) {
|
|
116
|
+
function candidateReason(file, depth) {
|
|
117
|
+
if (!isTestFile(file, {
|
|
118
|
+
patterns: DEFAULT_TEST_FILE_PATTERNS,
|
|
119
|
+
directories: DEFAULT_TEST_DIRECTORIES,
|
|
120
|
+
})) {
|
|
121
|
+
return "convention_match";
|
|
122
|
+
}
|
|
116
123
|
return depth === 1 ? "direct_compiler_reference" : "transitive_compiler_reference";
|
|
117
124
|
}
|
|
118
125
|
function candidateConfidence(depth) {
|
|
@@ -194,7 +201,7 @@ export function findTestCandidates(impact, conventions = {}) {
|
|
|
194
201
|
const depth = candidate.depth;
|
|
195
202
|
return {
|
|
196
203
|
file,
|
|
197
|
-
reason: candidateReason(depth),
|
|
204
|
+
reason: candidateReason(file, depth),
|
|
198
205
|
confidence: candidateConfidence(depth),
|
|
199
206
|
evidence: {
|
|
200
207
|
depth,
|
package/dist/tools/explore.js
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { buildExploreContext, EXPLORE_DEFAULT_LIMIT, EXPLORE_DEFAULT_MAX_BYTES, EXPLORE_DEFAULT_REFERENCE_LIMIT, EXPLORE_MAX_BYTES, } from "../services/context-builder.js";
|
|
2
|
+
import { buildExploreContext, EXPLORE_DEFAULT_LIMIT, EXPLORE_DEFAULT_MAX_BYTES, EXPLORE_DEFAULT_OMISSION_DETAIL_LIMIT, EXPLORE_DEFAULT_REFERENCE_LIMIT, EXPLORE_MAX_BYTES, EXPLORE_MAX_OMISSION_DETAIL_LIMIT, } from "../services/context-builder.js";
|
|
3
|
+
import { DEFAULT_IMPACT_MAX_DEPTH, DEFAULT_IMPACT_MAX_EDGES, DEFAULT_IMPACT_MAX_NODES, MAX_IMPACT_DEPTH, MAX_IMPACT_EDGES, MAX_IMPACT_NODES, } from "../services/impact.js";
|
|
4
|
+
import { EXPLORE_OMISSION_REASONS } from "../services/explore-presentation.js";
|
|
3
5
|
import { createPaginationInputSchema } from "../services/pagination.js";
|
|
4
6
|
import { withProject } from "../services/project.js";
|
|
5
7
|
import { createRequestContext } from "../services/request-context.js";
|
|
6
8
|
import { FRESHNESS_CAUSES, SNAPSHOT_STATES, TRUNCATION_REASONS, } from "../services/read-contracts.js";
|
|
7
9
|
import { createToolErrorContext, errorResult, structuredResult } from "./result.js";
|
|
10
|
+
import { RelationshipEndpointSchema } from "./relationship-schema.js";
|
|
8
11
|
const ExploreDetailSchema = z.enum(["selectors", "summary", "context", "full"]).default("summary");
|
|
9
12
|
const ReferenceDetailSchema = z.enum(["locations", "context"]).default("locations");
|
|
13
|
+
const CallSpinesInputSchema = z.object({
|
|
14
|
+
direction: z.enum(["incoming", "outgoing"]).default("outgoing"),
|
|
15
|
+
max_depth: z.number().int().min(0).max(MAX_IMPACT_DEPTH).default(DEFAULT_IMPACT_MAX_DEPTH),
|
|
16
|
+
max_nodes: z.number().int().min(1).max(MAX_IMPACT_NODES).default(DEFAULT_IMPACT_MAX_NODES),
|
|
17
|
+
max_edges: z.number().int().min(1).max(MAX_IMPACT_EDGES).default(DEFAULT_IMPACT_MAX_EDGES),
|
|
18
|
+
});
|
|
10
19
|
const ExploreInputSchema = z
|
|
11
20
|
.object({
|
|
12
21
|
project_root: z
|
|
@@ -46,6 +55,14 @@ const ExploreInputSchema = z
|
|
|
46
55
|
.max(EXPLORE_MAX_BYTES)
|
|
47
56
|
.default(EXPLORE_DEFAULT_MAX_BYTES)
|
|
48
57
|
.describe("Maximum serialized logical result size in UTF-8 bytes."),
|
|
58
|
+
call_spines: CallSpinesInputSchema.optional().describe("Optional compiler-authoritative static call paths for an exact file_path and symbol_path."),
|
|
59
|
+
omission_detail_limit: z
|
|
60
|
+
.number()
|
|
61
|
+
.int()
|
|
62
|
+
.min(0)
|
|
63
|
+
.max(EXPLORE_MAX_OMISSION_DETAIL_LIMIT)
|
|
64
|
+
.default(EXPLORE_DEFAULT_OMISSION_DETAIL_LIMIT)
|
|
65
|
+
.describe("Maximum deterministic omission detail records; exact counts are always retained."),
|
|
49
66
|
})
|
|
50
67
|
.refine((input) => Boolean(input.query || input.file_path), {
|
|
51
68
|
message: "Provide query, file_path, or both file_path and symbol_path.",
|
|
@@ -54,6 +71,10 @@ const ExploreInputSchema = z
|
|
|
54
71
|
.refine((input) => !input.symbol_path || Boolean(input.file_path), {
|
|
55
72
|
message: "symbol_path requires file_path so the exact declaration can be resolved.",
|
|
56
73
|
path: ["symbol_path"],
|
|
74
|
+
})
|
|
75
|
+
.refine((input) => !input.call_spines || Boolean(input.file_path && input.symbol_path), {
|
|
76
|
+
message: "call_spines requires exact file_path and symbol_path inputs.",
|
|
77
|
+
path: ["call_spines"],
|
|
57
78
|
});
|
|
58
79
|
const ExploreSymbolSchema = z.object({
|
|
59
80
|
file: z.string(),
|
|
@@ -97,6 +118,35 @@ const ExploreEvidenceSchema = z.object({
|
|
|
97
118
|
})
|
|
98
119
|
.optional(),
|
|
99
120
|
});
|
|
121
|
+
const ExploreOmissionSchema = z.object({
|
|
122
|
+
subject: z.string(),
|
|
123
|
+
category: z.enum(["budget", "incomplete", "untrusted"]),
|
|
124
|
+
component: z.enum(["signature", "source", "references", "call_spine"]),
|
|
125
|
+
reason: z.enum(EXPLORE_OMISSION_REASONS),
|
|
126
|
+
});
|
|
127
|
+
const CallSpinesOutputSchema = z.object({
|
|
128
|
+
root: RelationshipEndpointSchema,
|
|
129
|
+
direction: z.enum(["incoming", "outgoing"]),
|
|
130
|
+
paths: z.array(z.object({
|
|
131
|
+
endpoint: RelationshipEndpointSchema,
|
|
132
|
+
endpoints: z.array(RelationshipEndpointSchema),
|
|
133
|
+
relationship_ids: z.array(z.string()),
|
|
134
|
+
})),
|
|
135
|
+
visited: z.object({
|
|
136
|
+
nodes: z.number().int().min(0),
|
|
137
|
+
edges: z.number().int().min(0),
|
|
138
|
+
max_depth: z.number().int().min(0),
|
|
139
|
+
}),
|
|
140
|
+
budget: z.object({
|
|
141
|
+
max_depth: z.number().int().min(0),
|
|
142
|
+
max_nodes: z.number().int().positive(),
|
|
143
|
+
max_edges: z.number().int().positive(),
|
|
144
|
+
}),
|
|
145
|
+
incomplete: z.boolean(),
|
|
146
|
+
truncation_reasons: z.array(z.enum(["depth_limit", "node_limit", "edge_limit"])),
|
|
147
|
+
authority_state: z.enum(["authoritative", "incomplete", "untrusted"]),
|
|
148
|
+
empty_proven: z.boolean(),
|
|
149
|
+
});
|
|
100
150
|
const ExploreOutputSchema = z.object({
|
|
101
151
|
route: z.enum(["query", "file", "symbol"]),
|
|
102
152
|
query: z.string().nullable(),
|
|
@@ -110,6 +160,17 @@ const ExploreOutputSchema = z.object({
|
|
|
110
160
|
total: z.number().int().min(0),
|
|
111
161
|
has_more: z.boolean(),
|
|
112
162
|
next_offset: z.number().int().min(0).nullable(),
|
|
163
|
+
omissions: z.object({
|
|
164
|
+
counts: z.array(z.object({
|
|
165
|
+
category: z.enum(["budget", "incomplete", "untrusted"]),
|
|
166
|
+
component: z.enum(["signature", "source", "references", "call_spine"]),
|
|
167
|
+
count: z.number().int().positive(),
|
|
168
|
+
})),
|
|
169
|
+
details: z.array(ExploreOmissionSchema),
|
|
170
|
+
total: z.number().int().min(0),
|
|
171
|
+
has_more: z.boolean(),
|
|
172
|
+
}),
|
|
173
|
+
call_spines: CallSpinesOutputSchema.optional(),
|
|
113
174
|
freshness: z.object({
|
|
114
175
|
state: z.enum(SNAPSHOT_STATES),
|
|
115
176
|
causes: z.array(z.enum(FRESHNESS_CAUSES)),
|
|
@@ -119,6 +180,7 @@ const ExploreOutputSchema = z.object({
|
|
|
119
180
|
complete: z.boolean(),
|
|
120
181
|
symbols_complete: z.boolean(),
|
|
121
182
|
evidence_complete: z.boolean(),
|
|
183
|
+
spines_complete: z.boolean().optional(),
|
|
122
184
|
unresolved: z.array(z.object({
|
|
123
185
|
selector: z.string(),
|
|
124
186
|
reason: z.enum(["source_unresolved", "references_unresolved"]),
|
|
@@ -149,7 +211,7 @@ export function registerExplore(server) {
|
|
|
149
211
|
idempotentHint: true,
|
|
150
212
|
openWorldHint: false,
|
|
151
213
|
},
|
|
152
|
-
}, async ({ project_root, query, file_path, symbol_path, kinds, file_filter, detail, include_source, include_references, reference_detail, reference_limit, offset, limit, max_bytes, }, extra) => {
|
|
214
|
+
}, async ({ project_root, query, file_path, symbol_path, kinds, file_filter, detail, include_source, include_references, reference_detail, reference_limit, offset, limit, max_bytes, call_spines, omission_detail_limit, }, extra) => {
|
|
153
215
|
const requestContext = createRequestContext(extra.signal);
|
|
154
216
|
try {
|
|
155
217
|
const output = await withProject(project_root, (context, operationContext) => buildExploreContext(context, {
|
|
@@ -166,6 +228,15 @@ export function registerExplore(server) {
|
|
|
166
228
|
limit,
|
|
167
229
|
referenceLimit: reference_limit,
|
|
168
230
|
maxBytes: max_bytes,
|
|
231
|
+
callSpines: call_spines
|
|
232
|
+
? {
|
|
233
|
+
direction: call_spines.direction,
|
|
234
|
+
maxDepth: call_spines.max_depth,
|
|
235
|
+
maxNodes: call_spines.max_nodes,
|
|
236
|
+
maxEdges: call_spines.max_edges,
|
|
237
|
+
}
|
|
238
|
+
: undefined,
|
|
239
|
+
omissionDetailLimit: omission_detail_limit,
|
|
169
240
|
}, operationContext), requestContext);
|
|
170
241
|
return structuredResult(ExploreOutputSchema.parse(output));
|
|
171
242
|
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { DEFAULT_IMPACT_MAX_DEPTH, DEFAULT_IMPACT_MAX_EDGES, DEFAULT_IMPACT_MAX_NODES, MAX_IMPACT_DEPTH, MAX_IMPACT_EDGES, MAX_IMPACT_NODES, resolveImpactRoot, traverseCompilerImpact, } from "../services/impact.js";
|
|
3
|
+
import { paginate, PaginationInputSchema, PaginationOutputSchema } from "../services/pagination.js";
|
|
4
|
+
import { withProject } from "../services/project.js";
|
|
5
|
+
import { PublicOperationalError } from "../services/public-errors.js";
|
|
6
|
+
import { createRequestContext } from "../services/request-context.js";
|
|
7
|
+
import { findTestCandidates, MAX_TEST_CANDIDATE_CONVENTION_ITEMS, MAX_TEST_CANDIDATE_CONVENTION_LENGTH, TEST_CANDIDATE_CONFIDENCES, TEST_CANDIDATE_REASONS, } from "../services/test-candidates.js";
|
|
8
|
+
import { FreshnessSchema, RelationshipEdgeSchema, RelationshipEndpointSchema, } from "./relationship-schema.js";
|
|
9
|
+
import { createToolErrorContext, errorResult, structuredResult } from "./result.js";
|
|
10
|
+
const ConventionSchema = z
|
|
11
|
+
.array(z.string().min(1).max(MAX_TEST_CANDIDATE_CONVENTION_LENGTH))
|
|
12
|
+
.max(MAX_TEST_CANDIDATE_CONVENTION_ITEMS)
|
|
13
|
+
.optional();
|
|
14
|
+
const AstFindTestCandidatesInputSchema = z.object({
|
|
15
|
+
project_root: z.string(),
|
|
16
|
+
file_path: z.string(),
|
|
17
|
+
symbol_path: z.string(),
|
|
18
|
+
max_depth: z.number().int().min(0).max(MAX_IMPACT_DEPTH).default(DEFAULT_IMPACT_MAX_DEPTH),
|
|
19
|
+
max_nodes: z.number().int().min(1).max(MAX_IMPACT_NODES).default(DEFAULT_IMPACT_MAX_NODES),
|
|
20
|
+
max_edges: z.number().int().min(1).max(MAX_IMPACT_EDGES).default(DEFAULT_IMPACT_MAX_EDGES),
|
|
21
|
+
test_file_patterns: ConventionSchema,
|
|
22
|
+
test_directories: ConventionSchema,
|
|
23
|
+
...PaginationInputSchema,
|
|
24
|
+
});
|
|
25
|
+
const TestCandidateSchema = z.object({
|
|
26
|
+
file: z.string(),
|
|
27
|
+
reason: z.enum(TEST_CANDIDATE_REASONS),
|
|
28
|
+
confidence: z.enum(TEST_CANDIDATE_CONFIDENCES),
|
|
29
|
+
evidence: z.object({
|
|
30
|
+
depth: z.number().int().min(0),
|
|
31
|
+
direct: z.boolean(),
|
|
32
|
+
compiler_authoritative: z.literal(true),
|
|
33
|
+
relationship_ids: z.array(z.string()),
|
|
34
|
+
relationships: z.array(RelationshipEdgeSchema),
|
|
35
|
+
}),
|
|
36
|
+
});
|
|
37
|
+
const FindTestCandidatesOutputSchema = z.object({
|
|
38
|
+
backend: z.literal("typescript_compiler"),
|
|
39
|
+
compiler_authoritative: z.literal(true),
|
|
40
|
+
root: RelationshipEndpointSchema,
|
|
41
|
+
direction: z.literal("incoming"),
|
|
42
|
+
candidates: z.array(TestCandidateSchema),
|
|
43
|
+
...PaginationOutputSchema,
|
|
44
|
+
visited_nodes: z.number().int().min(0),
|
|
45
|
+
visited_edges: z.number().int().min(0),
|
|
46
|
+
max_depth_reached: z.number().int().min(0),
|
|
47
|
+
max_depth: z.number().int().min(0),
|
|
48
|
+
max_nodes: z.number().int().positive(),
|
|
49
|
+
max_edges: z.number().int().positive(),
|
|
50
|
+
incomplete: z.literal(false),
|
|
51
|
+
truncation: z.object({
|
|
52
|
+
truncated: z.literal(false),
|
|
53
|
+
reason: z.null(),
|
|
54
|
+
}),
|
|
55
|
+
freshness: FreshnessSchema,
|
|
56
|
+
completeness: z.object({
|
|
57
|
+
complete: z.literal(true),
|
|
58
|
+
proven_empty: z.boolean(),
|
|
59
|
+
}),
|
|
60
|
+
});
|
|
61
|
+
export function registerFindTestCandidates(server) {
|
|
62
|
+
server.registerTool("ast_find_test_candidates", {
|
|
63
|
+
title: "Find compiler-backed affected test candidates",
|
|
64
|
+
description: "Finds deterministic affected test candidates from fresh, complete incoming compiler relationships. Returns bounded whole-candidate proofs and never executes tests or prepares mutations.",
|
|
65
|
+
inputSchema: AstFindTestCandidatesInputSchema,
|
|
66
|
+
outputSchema: FindTestCandidatesOutputSchema,
|
|
67
|
+
annotations: {
|
|
68
|
+
readOnlyHint: true,
|
|
69
|
+
destructiveHint: false,
|
|
70
|
+
idempotentHint: true,
|
|
71
|
+
openWorldHint: false,
|
|
72
|
+
},
|
|
73
|
+
}, async ({ project_root, file_path, symbol_path, max_depth, max_nodes, max_edges, test_file_patterns, test_directories, offset, limit, }, extra) => {
|
|
74
|
+
const requestContext = createRequestContext(extra.signal);
|
|
75
|
+
try {
|
|
76
|
+
const result = await withProject(project_root, (context, operationContext) => {
|
|
77
|
+
if (context.status.state !== "fresh") {
|
|
78
|
+
throw new PublicOperationalError("STALE_WORKSPACE", "Retry the workspace read.");
|
|
79
|
+
}
|
|
80
|
+
const freshness = {
|
|
81
|
+
state: context.status.state,
|
|
82
|
+
causes: context.status.causes,
|
|
83
|
+
checked_at: context.status.lastSuccessfulSyncAt,
|
|
84
|
+
};
|
|
85
|
+
const root = resolveImpactRoot(context.project, context.projectRoot, { file_path, symbol_path }, operationContext);
|
|
86
|
+
const impact = traverseCompilerImpact(context.project, context.projectRoot, root, freshness, { direction: "incoming", max_depth, max_nodes, max_edges }, operationContext);
|
|
87
|
+
if (impact.incomplete || impact.truncation.truncated) {
|
|
88
|
+
throw new PublicOperationalError("INCOMPLETE_EVIDENCE", "Evidence is incomplete.");
|
|
89
|
+
}
|
|
90
|
+
const candidates = findTestCandidates(impact, {
|
|
91
|
+
test_file_patterns,
|
|
92
|
+
test_directories,
|
|
93
|
+
});
|
|
94
|
+
const page = paginate(candidates, offset, limit);
|
|
95
|
+
return {
|
|
96
|
+
backend: "typescript_compiler",
|
|
97
|
+
compiler_authoritative: true,
|
|
98
|
+
root: impact.root,
|
|
99
|
+
direction: "incoming",
|
|
100
|
+
candidates: page.items,
|
|
101
|
+
offset: page.offset,
|
|
102
|
+
limit: page.limit,
|
|
103
|
+
total: page.total,
|
|
104
|
+
has_more: page.has_more,
|
|
105
|
+
next_offset: page.next_offset,
|
|
106
|
+
visited_nodes: impact.visited_nodes,
|
|
107
|
+
visited_edges: impact.visited_edges,
|
|
108
|
+
max_depth_reached: impact.max_depth_reached,
|
|
109
|
+
max_depth: impact.max_depth,
|
|
110
|
+
max_nodes: impact.max_nodes,
|
|
111
|
+
max_edges: impact.max_edges,
|
|
112
|
+
incomplete: false,
|
|
113
|
+
truncation: { truncated: false, reason: null },
|
|
114
|
+
freshness,
|
|
115
|
+
completeness: { complete: true, proven_empty: candidates.length === 0 },
|
|
116
|
+
};
|
|
117
|
+
}, requestContext);
|
|
118
|
+
return structuredResult(FindTestCandidatesOutputSchema.parse(result));
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
return errorResult(error, createToolErrorContext("ast_find_test_candidates", project_root));
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
package/dist/tools/get_impact.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { DEFAULT_IMPACT_MAX_DEPTH, DEFAULT_IMPACT_MAX_EDGES, DEFAULT_IMPACT_MAX_NODES, IMPACT_DIRECTIONS, MAX_IMPACT_DEPTH, MAX_IMPACT_EDGES, MAX_IMPACT_NODES, resolveImpactRoot, traverseCompilerImpact, } from "../services/impact.js";
|
|
3
|
-
import {
|
|
3
|
+
import { TRUNCATION_REASONS } from "../services/read-contracts.js";
|
|
4
4
|
import { withProject } from "../services/project.js";
|
|
5
5
|
import { createRequestContext } from "../services/request-context.js";
|
|
6
|
-
import {
|
|
6
|
+
import { RELATIONSHIP_EDGE_KINDS } from "../services/relationships.js";
|
|
7
|
+
import { FreshnessSchema, RelationshipEdgeSchema, RelationshipEndpointSchema, } from "./relationship-schema.js";
|
|
7
8
|
import { createToolErrorContext, errorResult, formattedResult, ToolOutputFormatInputSchema, } from "./result.js";
|
|
8
9
|
const AstGetImpactInputSchema = z.object({
|
|
9
10
|
project_root: z
|
|
@@ -42,34 +43,6 @@ const AstGetImpactInputSchema = z.object({
|
|
|
42
43
|
.describe("Optional relationship kinds to traverse; defaults to all normalized kinds."),
|
|
43
44
|
...ToolOutputFormatInputSchema,
|
|
44
45
|
});
|
|
45
|
-
const SourceRangeSchema = z.object({
|
|
46
|
-
start_line: z.number().int().positive(),
|
|
47
|
-
start_column: z.number().int().positive().optional(),
|
|
48
|
-
end_line: z.number().int().positive().optional(),
|
|
49
|
-
end_column: z.number().int().positive().optional(),
|
|
50
|
-
});
|
|
51
|
-
const RelationshipEndpointSchema = z.object({
|
|
52
|
-
file: z.string(),
|
|
53
|
-
symbol_path: z.string(),
|
|
54
|
-
selector: z.string(),
|
|
55
|
-
range: SourceRangeSchema.optional(),
|
|
56
|
-
});
|
|
57
|
-
const FreshnessSchema = z.object({
|
|
58
|
-
state: z.enum(SNAPSHOT_STATES),
|
|
59
|
-
causes: z.array(z.enum(FRESHNESS_CAUSES)),
|
|
60
|
-
checked_at: z.string().nullable(),
|
|
61
|
-
});
|
|
62
|
-
const RelationshipEdgeSchema = z.object({
|
|
63
|
-
relationship_id: z.string(),
|
|
64
|
-
source: RelationshipEndpointSchema,
|
|
65
|
-
target: RelationshipEndpointSchema,
|
|
66
|
-
kind: z.enum(RELATIONSHIP_EDGE_KINDS),
|
|
67
|
-
provenance: z.enum(RELATIONSHIP_PROVENANCES),
|
|
68
|
-
confidence: z.enum(RELATIONSHIP_CONFIDENCES),
|
|
69
|
-
resolution: z.enum(RELATIONSHIP_RESOLUTIONS),
|
|
70
|
-
compiler_authoritative: z.boolean(),
|
|
71
|
-
freshness: FreshnessSchema,
|
|
72
|
-
});
|
|
73
46
|
const ImpactOutputSchema = z.object({
|
|
74
47
|
root: RelationshipEndpointSchema,
|
|
75
48
|
direction: z.enum(IMPACT_DIRECTIONS),
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { FRESHNESS_CAUSES, SNAPSHOT_STATES } from "../services/read-contracts.js";
|
|
3
|
+
import { RELATIONSHIP_CONFIDENCES, RELATIONSHIP_EDGE_KINDS, RELATIONSHIP_PROVENANCES, RELATIONSHIP_RESOLUTIONS, } from "../services/relationships.js";
|
|
4
|
+
export const SourceRangeSchema = z.object({
|
|
5
|
+
start_line: z.number().int().positive(),
|
|
6
|
+
start_column: z.number().int().positive().optional(),
|
|
7
|
+
end_line: z.number().int().positive().optional(),
|
|
8
|
+
end_column: z.number().int().positive().optional(),
|
|
9
|
+
});
|
|
10
|
+
export const RelationshipEndpointSchema = z.object({
|
|
11
|
+
file: z.string(),
|
|
12
|
+
symbol_path: z.string(),
|
|
13
|
+
selector: z.string(),
|
|
14
|
+
range: SourceRangeSchema.optional(),
|
|
15
|
+
});
|
|
16
|
+
export const FreshnessSchema = z.object({
|
|
17
|
+
state: z.enum(SNAPSHOT_STATES),
|
|
18
|
+
causes: z.array(z.enum(FRESHNESS_CAUSES)),
|
|
19
|
+
checked_at: z.string().nullable(),
|
|
20
|
+
});
|
|
21
|
+
export const RelationshipEdgeSchema = z.object({
|
|
22
|
+
relationship_id: z.string(),
|
|
23
|
+
source: RelationshipEndpointSchema,
|
|
24
|
+
target: RelationshipEndpointSchema,
|
|
25
|
+
kind: z.enum(RELATIONSHIP_EDGE_KINDS),
|
|
26
|
+
provenance: z.enum(RELATIONSHIP_PROVENANCES),
|
|
27
|
+
confidence: z.enum(RELATIONSHIP_CONFIDENCES),
|
|
28
|
+
resolution: z.enum(RELATIONSHIP_RESOLUTIONS),
|
|
29
|
+
compiler_authoritative: z.boolean(),
|
|
30
|
+
freshness: FreshnessSchema,
|
|
31
|
+
});
|
package/docs/support.md
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
# Support policy
|
|
2
2
|
|
|
3
|
-
This document defines the supported platform, runtime, persistence, and operational boundary for published `ast-mcp-server` v0.8.1 and the local `0.
|
|
3
|
+
This document defines the supported platform, runtime, persistence, and operational boundary for published `ast-mcp-server` v0.8.1 and the local `0.10.0` release candidate.
|
|
4
4
|
|
|
5
5
|
## Supported release target
|
|
6
6
|
|
|
7
|
-
| Environment | Status | Contract
|
|
8
|
-
| ----------------------------------------------------------------------------- | ----------- |
|
|
9
|
-
| Linux x64 with required GNU coreutils and procfs, Node.js 22.5.0 | Published | Immutable v0.8.1 release matrix; not the runtime floor of the current development tree.
|
|
10
|
-
| Linux x64 with required GNU coreutils and procfs, exact Node.js 22.13.0 | Supported | Local 0.
|
|
11
|
-
| Linux x64 with required GNU coreutils and procfs, current Node.js 24 line | Supported | Published v0.8.1 and local 0.
|
|
12
|
-
| Other Linux architectures or systems without the required filesystem features | Unverified | Not supported until equivalent architecture, mutation, and filesystem-publication gates pass.
|
|
13
|
-
| macOS | Unverified | Not supported until equivalent filesystem, process, package, mutation, and canary gates pass.
|
|
14
|
-
| Windows | Unverified | Not supported until equivalent filesystem, process, package, mutation, and canary gates pass.
|
|
15
|
-
| Remote, untrusted, or multi-tenant service use | Unsupported | No network authentication, sandbox, or tenant-isolation boundary is provided.
|
|
7
|
+
| Environment | Status | Contract |
|
|
8
|
+
| ----------------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------- |
|
|
9
|
+
| Linux x64 with required GNU coreutils and procfs, Node.js 22.5.0 | Published | Immutable v0.8.1 release matrix; not the runtime floor of the current development tree. |
|
|
10
|
+
| Linux x64 with required GNU coreutils and procfs, exact Node.js 22.13.0 | Supported | Local 0.10.0 candidate package, persistence, MCP/CLI/package, lifecycle and mutation matrix. |
|
|
11
|
+
| Linux x64 with required GNU coreutils and procfs, current Node.js 24 line | Supported | Published v0.8.1 and local 0.10.0 candidate matrices, each bound to its own package/tree bytes. |
|
|
12
|
+
| Other Linux architectures or systems without the required filesystem features | Unverified | Not supported until equivalent architecture, mutation, and filesystem-publication gates pass. |
|
|
13
|
+
| macOS | Unverified | Not supported until equivalent filesystem, process, package, mutation, and canary gates pass. |
|
|
14
|
+
| Windows | Unverified | Not supported until equivalent filesystem, process, package, mutation, and canary gates pass. |
|
|
15
|
+
| Remote, untrusted, or multi-tenant service use | Unsupported | No network authentication, sandbox, or tenant-isolation boundary is provided. |
|
|
16
16
|
|
|
17
|
-
The local `0.
|
|
17
|
+
The local `0.10.0` candidate requires Node.js `>=22.13.0`; its matrix targets exact Node.js 22.13.0 and the governed Node.js 24 major. Published v0.8.1 retains its immutable `>=22.5.0` metadata and exact Node.js 22.5.0/24 release evidence. A future runtime satisfying an engine range is not automatically a verified release target.
|
|
18
18
|
|
|
19
19
|
The checked-evidence freezer requires GNU coreutils 9.7 `mv` with `--update=none-fail`, `--no-copy`, and `--no-target-directory`. Managed setup-file mutation additionally requires that same `mv` with `--exchange`, GNU coreutils `ln -L -T`, procfs descriptor paths at `/proc/self/fd`, `O_DIRECTORY`, and `O_NOFOLLOW`. It fails closed when any verified Linux primitive is unavailable. Equivalent publication, exchange, descriptor-link, and descriptor-relative mutation semantics have not been verified on other operating systems or architectures.
|
|
20
20
|
|
|
@@ -34,7 +34,7 @@ Operation locks coordinate cooperating same-user processes. They do not stop edi
|
|
|
34
34
|
|
|
35
35
|
## Symbol-index persistence
|
|
36
36
|
|
|
37
|
-
Published v0.8.1 remains memory-default. In the local `0.
|
|
37
|
+
Published v0.8.1 remains memory-default. In the local `0.10.0` candidate, absence and explicit `enabled` select native SQLite; operators requiring no persistent index state must set `disabled` explicitly:
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
40
|
AST_SYMBOL_INDEX_PERSISTENCE=disabled \
|
|
@@ -64,7 +64,7 @@ Both commands are bounded and path-free. Clear preflights the tree, refuses unsa
|
|
|
64
64
|
|
|
65
65
|
## Production-readiness acceptance
|
|
66
66
|
|
|
67
|
-
The four retained Linux x64 production-readiness reports cover `ast-mcp-server` and `x-scraper` under Node.js 22.5.0 and Node.js 24 for package version 0.6.0, commit `2d0b21bbb80fae1acfca6a85d5891d87e68b59c1`, and tree `af931d49769fabdf06f623965a6cfe1f9afb8a81`. They are historical integration evidence only: they are not reattributed to v0.8.1, published-next v0.9.0 or v0.9.1, or the local 0.9.2 candidate. The exact 0.9.0 and 0.9.1 release matrices remain historical evidence for their immutable published-next bytes. A separate exact 0.
|
|
67
|
+
The four retained Linux x64 production-readiness reports cover `ast-mcp-server` and `x-scraper` under Node.js 22.5.0 and Node.js 24 for package version 0.6.0, commit `2d0b21bbb80fae1acfca6a85d5891d87e68b59c1`, and tree `af931d49769fabdf06f623965a6cfe1f9afb8a81`. They are historical integration evidence only: they are not reattributed to v0.8.1, published-next v0.9.0 or v0.9.1, or the local 0.9.2 candidate. The exact 0.9.0 and 0.9.1 release matrices remain historical evidence for their immutable published-next bytes, and the exact 0.9.2 release matrix remains historical local-candidate evidence for its own bytes. A separate exact 0.10.0 release matrix is the candidate-specific gate for current repository bytes under both supported runtime lines, including managed setup; none of these matrices rebinds the older retained reports. MCP-PROD-404 remains normative for admission:
|
|
68
68
|
|
|
69
69
|
- zero semantic mismatches across disabled, cold, warm, restart, and rollback reads;
|
|
70
70
|
- byte-identical real-repository status and worktree trees;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ast-mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Structural code MCP server and batch CLI for compact TypeScript/JavaScript reads and hash-bound AST edits.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -97,5 +97,5 @@
|
|
|
97
97
|
"typescript-eslint": "^8.65.0",
|
|
98
98
|
"vitest": "^4.1.10"
|
|
99
99
|
},
|
|
100
|
-
"gitHead": "
|
|
100
|
+
"gitHead": "7f97830d82d086e1a7ceafb84a8bc795be57039a"
|
|
101
101
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: structural-code-editing
|
|
3
3
|
description: Leer, navegar y editar proyectos TypeScript/JavaScript mediante el resolver real del compilador y operaciones AST preparadas, revisadas y vinculadas por hash.
|
|
4
|
-
version: "4.
|
|
4
|
+
version: "4.4.0"
|
|
5
5
|
author: "yail"
|
|
6
6
|
license: "ISC"
|
|
7
7
|
metadata:
|
|
@@ -46,7 +46,7 @@ El setup falla cerrado ante un registro `ast` conflictivo. No eliminar ni reempl
|
|
|
46
46
|
|
|
47
47
|
## Runtime, soporte y errores
|
|
48
48
|
|
|
49
|
-
El candidato local 0.
|
|
49
|
+
El candidato local 0.10.0 requiere Node.js `>=22.13.0` y se verifica con Node.js exacto 22.13.0 y la línea actual de Node.js 24. La versión publicada v0.8.1 conserva su matriz inmutable 22.5.0/24. La publicación de archivos gestionados por setup requiere además GNU coreutils 9.7 `mv` con `--update=none-fail`, `--exchange`, `--no-copy` y `--no-target-directory`, GNU coreutils `ln -L -T`, y procfs en `/proc/self/fd` con `O_DIRECTORY`/`O_NOFOLLOW`. Otras arquitecturas Linux, sistemas sin esos primitivos, macOS y Windows están sin verificar.
|
|
50
50
|
|
|
51
51
|
El transporte soportado es stdio local bajo los permisos filesystem del usuario que invoca el proceso. No asumir autenticación HTTP, sandbox, aislamiento entre tenants ni una frontera segura para clientes remotos o no confiables.
|
|
52
52
|
|
|
@@ -73,7 +73,7 @@ Los errores públicos de las tools MCP usan un vocabulario cerrado dentro de `{
|
|
|
73
73
|
|
|
74
74
|
`ast_get_file` es read-only y solo acepta archivos incluidos por el tsconfig activo. Su modo normal devuelve líneas exactas, paginadas y numeradas desde 1, junto con el hash SHA-256 de los bytes actuales. `symbols_only: true` devuelve selectors y signatures sin cuerpos. `snapshot_state: "fresh"` describe sincronización con el snapshot del compilador, no ausencia de diagnostics; consultar `ast_get_diagnostics` por separado.
|
|
75
75
|
|
|
76
|
-
`ast_explore` es read-only y no prepara ni aplica operaciones. Sus selectors son directamente reutilizables por las tools primitivas y de mutación. La respuesta
|
|
76
|
+
`ast_explore` es read-only y no prepara ni aplica operaciones. Sus selectors son directamente reutilizables por las tools primitivas y de mutación. La respuesta admite clusters completos dentro de `max_bytes`, nunca corta source, referencias ni paths, y clasifica lo omitido como `budget`, `incomplete` o `untrusted`. Para un `file_path + symbol_path` exacto, `call_spines` puede pedir paths estáticos de invocación compiler-resolved; no representa stacks de runtime ni relabela referencias genéricas. Ausencia de la opción no ejecuta ese recorrido.
|
|
77
77
|
|
|
78
78
|
## Modelo de confianza y freshness
|
|
79
79
|
|
|
@@ -86,7 +86,7 @@ No tratar toda salida como evidencia equivalente. Cuando una relación expone me
|
|
|
86
86
|
|
|
87
87
|
`fresh` significa que la evidencia coincide con el snapshot sincronizado. `pending`, `rebuilding`, `stale` y `degraded` no deben presentarse como evidencia actual. Las respuestas preservan `causes` (`source_change`, `config_change`, `index_failure`, `watcher_failure`, `compiler_rebuild`) y `checked_at`. `ast_get_impact` rechaza relaciones que no estén fresh; `ast_explore` conserva `completeness`, `unresolved`, `budget` y `truncation` para que una lectura parcial no parezca un negativo.
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
`ast_find_test_candidates` resuelve un símbolo exacto, fuerza impacto entrante y solo acepta evidencia fresh, exacta, resuelta y compiler-authoritative. Devuelve candidatos directos, transitivos o por convención con IDs y paths de relación completos; no ejecuta tests. Evidencia stale, truncada, incompleta, ambigua, heurística o no autoritativa falla cerrada. Solo un recorrido completo puede devolver una página vacía marcada `proven_empty`.
|
|
90
90
|
|
|
91
91
|
Todos los reads son bounded. Respetar `limit`, `reference_limit`, `max_bytes`, y en impacto `max_depth`, `max_nodes` y `max_edges`; revisar siempre `budget` y `truncation` antes de razonar sobre ausencia. Los límites de batch son independientes y también deben mantenerse explícitos.
|
|
92
92
|
|
|
@@ -115,6 +115,8 @@ Cuando un pipeline conocido requiere varias llamadas MCP dependientes y el clien
|
|
|
115
115
|
- Definir `emit` para que los resultados intermedios no entren al contexto.
|
|
116
116
|
- Mantener paginación y filtros: batch elimina roundtrips, no vuelve razonable leer un monorepo entero.
|
|
117
117
|
- No generar JavaScript/eval dentro del documento; el contrato es declarativo y limitado.
|
|
118
|
+
- `ast_find_test_candidates` está admitida como lectura: el batch inyecta el `project_root` del pipeline, rechaza conflictos y conserva candidatos completos al paginar. JSON y el TOON final representan el mismo resultado lógico del handler MCP registrado.
|
|
119
|
+
- `ast_explore` también está admitida como lectura mediante el handler MCP registrado. Mantener `max_bytes` y los límites de spines explícitos; los intermediates son JSON y solo el resultado final puede serializarse como TOON.
|
|
118
120
|
|
|
119
121
|
`ast-tool validate pipeline.json` valida schema, orden de referencias y política sin cargar el proyecto. Límites por defecto: 50 steps, 500 invocaciones, 200 items por foreach, concurrencia 4 (máximo 16), input 1 MiB, 10 MiB por resultado retenido/output y 50 MiB de contexto intermedio acumulado. Los errores del CLI permanecen como JSON en stderr aunque el output exitoso solicitado sea TOON.
|
|
120
122
|
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"schema_version": 1,
|
|
3
3
|
"algorithm": "sha256",
|
|
4
4
|
"current": {
|
|
5
|
-
"version": "4.
|
|
6
|
-
"sha256": "
|
|
5
|
+
"version": "4.4.0",
|
|
6
|
+
"sha256": "253b588c56d44c8f5565db3f33ce227d80c7c29aa6604d362844f44b1278385a"
|
|
7
7
|
},
|
|
8
8
|
"predecessors": [
|
|
9
9
|
{
|
|
@@ -60,6 +60,11 @@
|
|
|
60
60
|
"version": "4.3.0",
|
|
61
61
|
"sha256": "a8dc2ba8eecbf3a21d7a86092323a2674b0353f5ec1f2bb36ba7ed0cd2890c20",
|
|
62
62
|
"npm_versions": ["0.9.1"]
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"version": "4.3.0",
|
|
66
|
+
"sha256": "bf0814b539cd8638aeed108a4e118fe1f2ab418e2e968a77ad75f2d0dbde93b9",
|
|
67
|
+
"npm_versions": ["0.9.2"]
|
|
63
68
|
}
|
|
64
69
|
]
|
|
65
70
|
}
|