ast-mcp-server 0.13.0 → 0.14.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 +25 -1
- package/README.md +56 -25
- package/cordis.patch.yml +8 -1
- package/dist/batch/schema.js +1 -1
- package/dist/server.js +15 -1
- package/dist/services/authenticated-publication.js +181 -0
- package/dist/services/call-spines.js +10 -5
- package/dist/services/compiler-worker-host.js +36 -16
- package/dist/services/compiler-worker-protocol.js +70 -2
- package/dist/services/context-builder.js +2 -1
- package/dist/services/diagnostics.js +293 -9
- package/dist/services/h03-timeout-fixture.js +335 -0
- package/dist/services/impact.js +46 -14
- package/dist/services/managed-file.js +3 -50
- package/dist/services/operation-plan-file.js +17 -18
- package/dist/services/operations.js +231 -87
- package/dist/services/project.js +29 -16
- package/dist/services/public-errors.js +7 -5
- package/dist/services/relationships.js +403 -4
- package/dist/services/runtime-policy.js +68 -2
- package/dist/services/test-candidates.js +35 -2
- package/dist/tools/explore.js +5 -3
- package/dist/tools/find_test_candidates.js +24 -3
- package/dist/tools/get_impact.js +12 -3
- package/dist/tools/relationship-schema.js +12 -1
- package/dist/tools/result.js +48 -0
- package/dist/worker-runtime.js +6 -1
- package/docs/support.md +4 -2
- package/package.json +16 -8
- package/skills/structural-code-editing/SKILL.md +8 -0
- package/skills/structural-code-editing/releases.json +1 -1
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assertCompleteExactImpactEvidence } from "./impact.js";
|
|
2
|
+
import { isRelationshipCoverageComplete, } from "./relationships.js";
|
|
3
|
+
export const AFFECTED_TEST_RELATIONSHIP_KINDS = Object.freeze([
|
|
4
|
+
"reference",
|
|
5
|
+
"import",
|
|
6
|
+
"export",
|
|
7
|
+
"extends",
|
|
8
|
+
"implements",
|
|
9
|
+
"call",
|
|
10
|
+
]);
|
|
11
|
+
const AFFECTED_TEST_RELATIONSHIP_KIND_SET = new Set(AFFECTED_TEST_RELATIONSHIP_KINDS);
|
|
2
12
|
export const DEFAULT_TEST_FILE_PATTERNS = Object.freeze(["**/*.test.*", "**/*.spec.*"]);
|
|
3
13
|
export const DEFAULT_TEST_DIRECTORIES = Object.freeze(["test", "tests", "__tests__"]);
|
|
4
14
|
export const TEST_CANDIDATE_REASONS = Object.freeze([
|
|
@@ -156,8 +166,31 @@ function findPathToRoot(startKeys, rootKey, edges) {
|
|
|
156
166
|
}
|
|
157
167
|
return null;
|
|
158
168
|
}
|
|
169
|
+
function assertCandidateImpactAuthority(impact) {
|
|
170
|
+
assertCompleteExactImpactEvidence(impact);
|
|
171
|
+
if (impact.direction !== "incoming" ||
|
|
172
|
+
impact.relationship_kinds.length !== AFFECTED_TEST_RELATIONSHIP_KINDS.length ||
|
|
173
|
+
!impact.relationship_kinds.every((kind, index) => kind === AFFECTED_TEST_RELATIONSHIP_KINDS[index])) {
|
|
174
|
+
throw new Error("Test candidates require the six-kind incoming impact scope.");
|
|
175
|
+
}
|
|
176
|
+
const coveredKinds = new Set(impact.coverage?.map((entry) => entry.kind));
|
|
177
|
+
if (!Array.isArray(impact.coverage) ||
|
|
178
|
+
coveredKinds.size !== AFFECTED_TEST_RELATIONSHIP_KINDS.length ||
|
|
179
|
+
!AFFECTED_TEST_RELATIONSHIP_KINDS.every((kind) => coveredKinds.has(kind)) ||
|
|
180
|
+
!impact.coverage.every((entry) => entry.direction === "incoming" &&
|
|
181
|
+
AFFECTED_TEST_RELATIONSHIP_KIND_SET.has(entry.kind) &&
|
|
182
|
+
(entry.status === "completed" || entry.status === "not_applicable")) ||
|
|
183
|
+
!isRelationshipCoverageComplete(impact.coverage)) {
|
|
184
|
+
throw new Error("Test candidates require complete six-kind incoming coverage.");
|
|
185
|
+
}
|
|
186
|
+
if (impact.work === undefined ||
|
|
187
|
+
impact.work.work_limit_reached ||
|
|
188
|
+
impact.work.work_items > impact.work.max_work_items) {
|
|
189
|
+
throw new Error("Test candidates require bounded impact work.");
|
|
190
|
+
}
|
|
191
|
+
}
|
|
159
192
|
export function findTestCandidates(impact, conventions = {}) {
|
|
160
|
-
|
|
193
|
+
assertCandidateImpactAuthority(impact);
|
|
161
194
|
if (typeof impact.root !== "object" ||
|
|
162
195
|
impact.root === null ||
|
|
163
196
|
!Array.isArray(impact.nodes) ||
|
package/dist/tools/explore.js
CHANGED
|
@@ -17,7 +17,7 @@ const CallSpinesInputSchema = z.object({
|
|
|
17
17
|
max_nodes: z.number().int().min(1).max(MAX_IMPACT_NODES).default(DEFAULT_IMPACT_MAX_NODES),
|
|
18
18
|
max_edges: z.number().int().min(1).max(MAX_IMPACT_EDGES).default(DEFAULT_IMPACT_MAX_EDGES),
|
|
19
19
|
});
|
|
20
|
-
const
|
|
20
|
+
const ExploreInputObjectSchema = z
|
|
21
21
|
.object({
|
|
22
22
|
project_root: z
|
|
23
23
|
.string()
|
|
@@ -65,7 +65,8 @@ const ExploreInputSchema = z
|
|
|
65
65
|
.default(EXPLORE_DEFAULT_OMISSION_DETAIL_LIMIT)
|
|
66
66
|
.describe("Maximum deterministic omission detail records; exact counts are always retained."),
|
|
67
67
|
})
|
|
68
|
-
.
|
|
68
|
+
.strip();
|
|
69
|
+
const ExploreInputSchema = ExploreInputObjectSchema.refine((input) => Boolean(input.query || input.file_path), {
|
|
69
70
|
message: "Provide query, file_path, or both file_path and symbol_path.",
|
|
70
71
|
path: ["query"],
|
|
71
72
|
})
|
|
@@ -204,7 +205,7 @@ export function registerExplore(server) {
|
|
|
204
205
|
server.registerTool(TOOL_NAME, {
|
|
205
206
|
title: "Explore project context",
|
|
206
207
|
description: "Composes bounded structural search, exact selectors, source evidence and compiler references without weakening the primitive AST tools. Returns bounded session freshness metadata for fresh, pending, stale, rebuilding or degraded state.",
|
|
207
|
-
inputSchema:
|
|
208
|
+
inputSchema: ExploreInputObjectSchema,
|
|
208
209
|
outputSchema: ExploreOutputSchema,
|
|
209
210
|
annotations: {
|
|
210
211
|
readOnlyHint: true,
|
|
@@ -215,6 +216,7 @@ export function registerExplore(server) {
|
|
|
215
216
|
}, 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) => {
|
|
216
217
|
const requestContext = createRequestContext(extra.signal);
|
|
217
218
|
try {
|
|
219
|
+
ExploreInputSchema.parse({ project_root, query, file_path, symbol_path, call_spines });
|
|
218
220
|
const output = await withProject(project_root, (context, operationContext) => buildExploreContext(context, {
|
|
219
221
|
query,
|
|
220
222
|
filePath: file_path,
|
|
@@ -4,9 +4,10 @@ import { paginate, PaginationInputSchema, PaginationOutputSchema } from "../serv
|
|
|
4
4
|
import { withProject } from "../services/project.js";
|
|
5
5
|
import { PublicOperationalError } from "../services/public-errors.js";
|
|
6
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";
|
|
7
|
+
import { AFFECTED_TEST_RELATIONSHIP_KINDS, 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, RelationshipCoverageEntrySchema, RelationshipEdgeSchema, RelationshipEndpointSchema, RelationshipWorkSchema, } from "./relationship-schema.js";
|
|
9
9
|
import { createToolErrorContext, errorResult, structuredResult } from "./result.js";
|
|
10
|
+
export { AFFECTED_TEST_RELATIONSHIP_KINDS } from "../services/test-candidates.js";
|
|
10
11
|
const TOOL_NAME = "ast_find_test_candidates";
|
|
11
12
|
const ConventionSchema = z
|
|
12
13
|
.array(z.string().min(1).max(MAX_TEST_CANDIDATE_CONVENTION_LENGTH))
|
|
@@ -40,6 +41,16 @@ const FindTestCandidatesOutputSchema = z.object({
|
|
|
40
41
|
compiler_authoritative: z.literal(true),
|
|
41
42
|
root: RelationshipEndpointSchema,
|
|
42
43
|
direction: z.literal("incoming"),
|
|
44
|
+
relationship_kinds: z.tuple([
|
|
45
|
+
z.literal("reference"),
|
|
46
|
+
z.literal("import"),
|
|
47
|
+
z.literal("export"),
|
|
48
|
+
z.literal("extends"),
|
|
49
|
+
z.literal("implements"),
|
|
50
|
+
z.literal("call"),
|
|
51
|
+
]),
|
|
52
|
+
coverage: z.array(RelationshipCoverageEntrySchema),
|
|
53
|
+
work: RelationshipWorkSchema,
|
|
43
54
|
candidates: z.array(TestCandidateSchema),
|
|
44
55
|
...PaginationOutputSchema,
|
|
45
56
|
visited_nodes: z.number().int().min(0),
|
|
@@ -84,7 +95,14 @@ export function registerFindTestCandidates(server) {
|
|
|
84
95
|
checked_at: context.status.lastSuccessfulSyncAt,
|
|
85
96
|
};
|
|
86
97
|
const root = resolveImpactRoot(context.project, context.projectRoot, { file_path, symbol_path }, operationContext);
|
|
87
|
-
const impact = traverseCompilerImpact(context.project, context.projectRoot, root, freshness, {
|
|
98
|
+
const impact = traverseCompilerImpact(context.project, context.projectRoot, root, freshness, {
|
|
99
|
+
direction: "incoming",
|
|
100
|
+
relationship_kinds: AFFECTED_TEST_RELATIONSHIP_KINDS,
|
|
101
|
+
max_depth,
|
|
102
|
+
max_nodes,
|
|
103
|
+
max_edges,
|
|
104
|
+
authority: "semantic",
|
|
105
|
+
}, operationContext);
|
|
88
106
|
if (impact.incomplete || impact.truncation.truncated) {
|
|
89
107
|
throw new PublicOperationalError("INCOMPLETE_EVIDENCE", "Evidence is incomplete.");
|
|
90
108
|
}
|
|
@@ -98,6 +116,9 @@ export function registerFindTestCandidates(server) {
|
|
|
98
116
|
compiler_authoritative: true,
|
|
99
117
|
root: impact.root,
|
|
100
118
|
direction: "incoming",
|
|
119
|
+
relationship_kinds: AFFECTED_TEST_RELATIONSHIP_KINDS,
|
|
120
|
+
coverage: impact.coverage,
|
|
121
|
+
work: impact.work,
|
|
101
122
|
candidates: page.items,
|
|
102
123
|
offset: page.offset,
|
|
103
124
|
limit: page.limit,
|
package/dist/tools/get_impact.js
CHANGED
|
@@ -4,7 +4,7 @@ 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
6
|
import { RELATIONSHIP_EDGE_KINDS } from "../services/relationships.js";
|
|
7
|
-
import { FreshnessSchema, RelationshipEdgeSchema, RelationshipEndpointSchema, } from "./relationship-schema.js";
|
|
7
|
+
import { FreshnessSchema, RelationshipCoverageEntrySchema, RelationshipEdgeSchema, RelationshipEndpointSchema, RelationshipWorkSchema, } from "./relationship-schema.js";
|
|
8
8
|
import { createToolErrorContext, errorResult, formattedResult, ToolOutputFormatInputSchema, } from "./result.js";
|
|
9
9
|
const TOOL_NAME = "ast_get_impact";
|
|
10
10
|
const AstGetImpactInputSchema = z.object({
|
|
@@ -60,6 +60,9 @@ const ImpactOutputSchema = z.object({
|
|
|
60
60
|
max_depth: z.number().int().min(0),
|
|
61
61
|
max_nodes: z.number().int().positive(),
|
|
62
62
|
max_edges: z.number().int().positive(),
|
|
63
|
+
coverage: z.array(RelationshipCoverageEntrySchema),
|
|
64
|
+
work: RelationshipWorkSchema,
|
|
65
|
+
proven_empty: z.boolean(),
|
|
63
66
|
incomplete: z.boolean(),
|
|
64
67
|
truncation: z.object({
|
|
65
68
|
truncated: z.boolean(),
|
|
@@ -73,7 +76,6 @@ export function registerGetImpact(server) {
|
|
|
73
76
|
title: "Get bounded compiler-backed impact",
|
|
74
77
|
description: "Traverses exact compiler-backed relationships around a symbol with explicit direction, depth, node and edge budgets. Returns bounded session freshness metadata and fails closed when exact relationships are not fresh. Read-only evidence only; it never prepares or applies mutations.",
|
|
75
78
|
inputSchema: AstGetImpactInputSchema,
|
|
76
|
-
outputSchema: ImpactOutputSchema,
|
|
77
79
|
annotations: {
|
|
78
80
|
readOnlyHint: true,
|
|
79
81
|
destructiveHint: false,
|
|
@@ -93,7 +95,14 @@ export function registerGetImpact(server) {
|
|
|
93
95
|
checked_at: context.status.lastSuccessfulSyncAt,
|
|
94
96
|
};
|
|
95
97
|
const root = resolveImpactRoot(context.project, context.projectRoot, { file_path, symbol_path }, operationContext);
|
|
96
|
-
const impact = traverseCompilerImpact(context.project, context.projectRoot, root, freshness, {
|
|
98
|
+
const impact = traverseCompilerImpact(context.project, context.projectRoot, root, freshness, {
|
|
99
|
+
direction,
|
|
100
|
+
max_depth,
|
|
101
|
+
max_nodes,
|
|
102
|
+
max_edges,
|
|
103
|
+
relationship_kinds,
|
|
104
|
+
authority: "semantic",
|
|
105
|
+
}, operationContext);
|
|
97
106
|
return {
|
|
98
107
|
...impact,
|
|
99
108
|
freshness,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
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";
|
|
3
|
+
import { RELATIONSHIP_CONFIDENCES, RELATIONSHIP_COVERAGE_STATUSES, RELATIONSHIP_EDGE_KINDS, RELATIONSHIP_PROVENANCES, RELATIONSHIP_RESOLUTIONS, } from "../services/relationships.js";
|
|
4
4
|
export const SourceRangeSchema = z.object({
|
|
5
5
|
start_line: z.number().int().positive(),
|
|
6
6
|
start_column: z.number().int().positive().optional(),
|
|
@@ -18,6 +18,17 @@ export const FreshnessSchema = z.object({
|
|
|
18
18
|
causes: z.array(z.enum(FRESHNESS_CAUSES)),
|
|
19
19
|
checked_at: z.string().nullable(),
|
|
20
20
|
});
|
|
21
|
+
export const RelationshipCoverageEntrySchema = z.object({
|
|
22
|
+
kind: z.enum(RELATIONSHIP_EDGE_KINDS),
|
|
23
|
+
direction: z.enum(["incoming", "outgoing"]),
|
|
24
|
+
endpoint_class: z.enum(["module", "symbol"]),
|
|
25
|
+
status: z.enum(RELATIONSHIP_COVERAGE_STATUSES),
|
|
26
|
+
});
|
|
27
|
+
export const RelationshipWorkSchema = z.object({
|
|
28
|
+
work_items: z.number().int().min(0),
|
|
29
|
+
max_work_items: z.number().int().positive(),
|
|
30
|
+
work_limit_reached: z.boolean(),
|
|
31
|
+
});
|
|
21
32
|
export const RelationshipEdgeSchema = z.object({
|
|
22
33
|
relationship_id: z.string(),
|
|
23
34
|
source: RelationshipEndpointSchema,
|
package/dist/tools/result.js
CHANGED
|
@@ -2,10 +2,15 @@ import { Buffer } from "node:buffer";
|
|
|
2
2
|
import { isDeepStrictEqual } from "node:util";
|
|
3
3
|
import { decode, encode } from "@toon-format/toon";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { COMPILER_WORKER_MAX_RESULT_BYTES, fitsCompilerWorkerResponseResult, } from "../services/compiler-worker-protocol.js";
|
|
5
6
|
import { createProjectIdentity } from "../services/project-status.js";
|
|
6
7
|
import { renderPublicError } from "../services/public-errors.js";
|
|
7
8
|
import { emitToolFailureEvent } from "../services/runtime-logger.js";
|
|
8
9
|
export const MAX_TOON_RESULT_BYTES = 10 * 1024 * 1024;
|
|
10
|
+
// Leave room for the unchanged structured value plus JSON-RPC framing under
|
|
11
|
+
// the supervised worker's 256 KiB line limit.
|
|
12
|
+
export const MAX_TEXT_PROJECTION_BYTES = 112 * 1024;
|
|
13
|
+
export const MAX_PROJECTED_RESULT_BYTES = COMPILER_WORKER_MAX_RESULT_BYTES;
|
|
9
14
|
export const ToolOutputFormatSchema = z.enum(["json", "toon"]).default("json");
|
|
10
15
|
export const ToolOutputFormatInputSchema = {
|
|
11
16
|
output_format: ToolOutputFormatSchema.describe("Result representation. TOON wraps one compact encoded value in structuredContent."),
|
|
@@ -24,6 +29,49 @@ export function createToolErrorContext(toolName, projectRoot) {
|
|
|
24
29
|
return { toolName };
|
|
25
30
|
}
|
|
26
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Add a deterministic model-visible JSON projection without replacing the
|
|
34
|
+
* canonical structured value. Existing content wins, and oversized values get
|
|
35
|
+
* an explicit bounded marker when the complete framed result has room for it;
|
|
36
|
+
* otherwise the unchanged structured-only result remains authoritative.
|
|
37
|
+
*/
|
|
38
|
+
export function projectStructuredContentAsText(result, enabled, maxBytes = MAX_TEXT_PROJECTION_BYTES) {
|
|
39
|
+
if (!enabled ||
|
|
40
|
+
result.isError === true ||
|
|
41
|
+
result.content.length > 0 ||
|
|
42
|
+
result.structuredContent === undefined) {
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
let text;
|
|
46
|
+
try {
|
|
47
|
+
text =
|
|
48
|
+
JSON.stringify(result.structuredContent) ??
|
|
49
|
+
"(structured result is not JSON-serializable for model-visible projection)";
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
text = "(structured result is not JSON-serializable for model-visible projection)";
|
|
53
|
+
}
|
|
54
|
+
const byteLength = Buffer.byteLength(text, "utf8");
|
|
55
|
+
if (byteLength > maxBytes) {
|
|
56
|
+
text = `(structured result exceeds the ${maxBytes}-byte model-visible projection limit; canonical structuredContent remains available)`;
|
|
57
|
+
}
|
|
58
|
+
let projected = {
|
|
59
|
+
...result,
|
|
60
|
+
content: [{ type: "text", text }],
|
|
61
|
+
};
|
|
62
|
+
if (fitsCompilerWorkerResponseResult(projected))
|
|
63
|
+
return projected;
|
|
64
|
+
projected = {
|
|
65
|
+
...result,
|
|
66
|
+
content: [
|
|
67
|
+
{
|
|
68
|
+
type: "text",
|
|
69
|
+
text: `(structured result exceeds the ${MAX_PROJECTED_RESULT_BYTES}-byte complete projection limit; canonical structuredContent remains available)`,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
return fitsCompilerWorkerResponseResult(projected) ? projected : result;
|
|
74
|
+
}
|
|
27
75
|
export function structuredResult(structuredContent) {
|
|
28
76
|
return { content: [], structuredContent };
|
|
29
77
|
}
|
package/dist/worker-runtime.js
CHANGED
|
@@ -15,7 +15,12 @@ export async function runStdioServer(options = {}) {
|
|
|
15
15
|
const runtimeActivity = options.runtimeActivity ?? new RuntimeActivityTracker();
|
|
16
16
|
runtimeActivity.prepareForStartup();
|
|
17
17
|
const runtimePolicy = options.runtimePolicy ?? readRuntimePolicy();
|
|
18
|
-
const server = options.server ??
|
|
18
|
+
const server = options.server ??
|
|
19
|
+
createServer({
|
|
20
|
+
runtimeActivity,
|
|
21
|
+
denyApply: runtimePolicy.denyApply,
|
|
22
|
+
projectStructuredContentAsText: runtimePolicy.projectStructuredContentAsText,
|
|
23
|
+
});
|
|
19
24
|
const transport = options.transport ?? new StdioServerTransport();
|
|
20
25
|
const send = transport.send.bind(transport);
|
|
21
26
|
transport.send = (message, sendOptions) => runtimeActivity.trackSend(() => send(message, sendOptions));
|
package/docs/support.md
CHANGED
|
@@ -16,7 +16,7 @@ This document defines the supported platform, runtime, persistence, and operatio
|
|
|
16
16
|
|
|
17
17
|
Published v0.12.0 requires Node.js `>=22.13.0`; its immutable matrix targets exact Node.js 22.13.0 and the governed Node.js 24 major. The earlier v0.8.1 Node.js 22.5.0/24 matrix remains historical evidence for those bytes only. A future runtime satisfying an engine range is not automatically a verified release target.
|
|
18
18
|
|
|
19
|
-
The checked-evidence freezer requires GNU coreutils 9.7 `mv` with `--update=none-fail`, `--no-copy`, and `--no-target-directory`.
|
|
19
|
+
The checked-evidence freezer requires GNU coreutils 9.7 `mv` with `--update=none-fail`, `--no-copy`, and `--no-target-directory`. Structural apply and managed setup-file mutation additionally require that same `mv` with `--exchange`, GNU coreutils `ln -L -T`, procfs descriptor paths at `/proc/self/fd`, `O_DIRECTORY`/`O_NOFOLLOW`, and successful owned link/exchange identity probes on each destination parent filesystem before source effects. Missing, denied, or behaviorally incompatible primitives fail closed; there is no rename, copy/delete, or pathname-only fallback. Equivalent semantics have not been verified on other operating systems, architectures, or filesystems.
|
|
20
20
|
|
|
21
21
|
## Local stdio trust boundary
|
|
22
22
|
|
|
@@ -30,7 +30,9 @@ The server does not provide:
|
|
|
30
30
|
- tenant isolation;
|
|
31
31
|
- protection from a malicious process running with the same filesystem authority.
|
|
32
32
|
|
|
33
|
-
Operation locks coordinate cooperating same
|
|
33
|
+
Operation locks coordinate cooperating processes using the same configuration identity; they do not authorize writes against editors, overlapping configurations, network filesystems, continuously mutating writers, or hostile same-user processes. Structural replacement authenticates one exchanged pair at a time, and multi-file apply is sequential rather than globally atomic. Reverse rollback restores only exact owned pairs. Creation is no-clobber, but if a later failure follows its first publication, unlink ownership is not inferred: the destination and hidden stage are preserved and the outcome is `AMBIGUOUS_APPLY`. Deterministic promise barriers prove only the named publication and rollback interleavings, not arbitrary-writer atomicity.
|
|
34
|
+
|
|
35
|
+
Report-set freezer coordination likewise assumes cooperating same-UID processes and cannot defend checked evidence against a malicious process with the same filesystem authority. The freezer guarantees no-replace atomic visibility of the checked directory; it does not claim persistence across sudden power loss because parent-directory `fsync` durability is not established.
|
|
34
36
|
|
|
35
37
|
## Optional supervised compiler worker
|
|
36
38
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ast-mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.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",
|
|
@@ -34,7 +34,13 @@
|
|
|
34
34
|
"deepseekHarness": {
|
|
35
35
|
"revision": "cd5ef8148158c3a752a658978873241fdf8e2bbc",
|
|
36
36
|
"tag": "dsh-v0.1.2-alpha.1",
|
|
37
|
-
"mcpClientVersion": "0.1.2-alpha.1"
|
|
37
|
+
"mcpClientVersion": "0.1.2-alpha.1",
|
|
38
|
+
"timeoutBudget": {
|
|
39
|
+
"queueWaitMs": 30000,
|
|
40
|
+
"executionDeadlineMs": 120000,
|
|
41
|
+
"marginMs": 15000,
|
|
42
|
+
"outerToolCallMs": 180000
|
|
43
|
+
}
|
|
38
44
|
},
|
|
39
45
|
"main": "dist/index.js",
|
|
40
46
|
"bin": {
|
|
@@ -54,8 +60,10 @@
|
|
|
54
60
|
"node": ">=22.13.0"
|
|
55
61
|
},
|
|
56
62
|
"resolutions": {
|
|
57
|
-
"
|
|
58
|
-
"
|
|
63
|
+
"fast-uri": "3.1.9",
|
|
64
|
+
"hono": "4.13.13",
|
|
65
|
+
"nanoid": "3.3.18",
|
|
66
|
+
"qs": "6.16.0"
|
|
59
67
|
},
|
|
60
68
|
"scripts": {
|
|
61
69
|
"build": "tsc",
|
|
@@ -92,12 +100,12 @@
|
|
|
92
100
|
"dev": "tsc --watch"
|
|
93
101
|
},
|
|
94
102
|
"dependencies": {
|
|
95
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
103
|
+
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
96
104
|
"@toon-format/toon": "4.1.0",
|
|
97
105
|
"diff": "^9.0.0",
|
|
98
106
|
"jsonc-parser": "^3.3.1",
|
|
99
107
|
"ts-morph": "24.0.0",
|
|
100
|
-
"zod": "^
|
|
108
|
+
"zod": "^4.6.7"
|
|
101
109
|
},
|
|
102
110
|
"devDependencies": {
|
|
103
111
|
"@eslint/js": "^10.0.1",
|
|
@@ -107,8 +115,8 @@
|
|
|
107
115
|
"prettier": "^3.9.6",
|
|
108
116
|
"typescript": "^5.6.0",
|
|
109
117
|
"typescript-eslint": "^8.65.0",
|
|
110
|
-
"vitest": "^4.1.
|
|
118
|
+
"vitest": "^4.1.12",
|
|
111
119
|
"yaml": "^2.9.0"
|
|
112
120
|
},
|
|
113
|
-
"gitHead": "
|
|
121
|
+
"gitHead": "f6e64751398f69eb3f0f1abc808f4221f7d2bc52"
|
|
114
122
|
}
|
|
@@ -29,6 +29,14 @@ Published v0.12.0 requires Node.js `>=22.13.0` and retains immutable release evi
|
|
|
29
29
|
- Nunca reconstruir postimages desde un diff; después del apply, ejecutar el gate canónico del proyecto.
|
|
30
30
|
- Conservar `correlation_id` en errores públicos sin exponer source, paths absolutos, argumentos, environment, cache, stacks ni credenciales.
|
|
31
31
|
|
|
32
|
+
## Impacto y candidatos de test
|
|
33
|
+
|
|
34
|
+
`ast_get_impact` separa dos canales. `coverage` asigna a cada kind/direction/endpoint una de `not_applicable`, `completed`, `unsupported` o `unfinished`; `truncation` y `work` describen límites de recorrido. `incomplete` es true si falla cualquiera. `proven_empty` exige cero edges, cobertura aplicable completa y ningún límite agotado.
|
|
35
|
+
|
|
36
|
+
La selección default conserva los siete kinds. `contains` aplicable es `unsupported`, por lo que el default puede ser semánticamente incompleto sin truncamiento. Calls directas por identificador, `new` y tagged templates pueden ser exactas; property, element, dynamic, unresolved, multiple o external dispatch no generan edges adivinados y dejan esa dirección `unfinished`. No presentar esto como certificación de #219 o #220.
|
|
37
|
+
|
|
38
|
+
`ast_find_test_candidates` fija seis kinds incoming (`reference`, `import`, `export`, `extends`, `implements`, `call`) y excluye `contains`. Solo acepta evidencia fresh/exact/resolved/compiler-authoritative, cobertura completa y trabajo/recorrido sin agotar. Si no, devuelve `INCOMPLETE_EVIDENCE`, no una página vacía. Después de admitir, pagina candidatos ordenados sin partir paths ni ocultar `coverage`/`work`. MCP entrega JSON canónico; TOON de batch debe conservar el mismo significado lógico.
|
|
39
|
+
|
|
32
40
|
## Diagnostic aggregates
|
|
33
41
|
|
|
34
42
|
`ast_get_diagnostics` keeps its existing response shape unless `include_aggregates: true` is requested. Enabled summaries cover the complete normalized snapshot, not only the selected raw page, and cap both code and file groups at 20.
|