@adhisang/minecraft-modding-mcp 1.2.1 → 2.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +184 -64
  3. package/dist/cli.js +31 -4
  4. package/dist/compat-stdio-transport.d.ts +2 -7
  5. package/dist/compat-stdio-transport.js +12 -154
  6. package/dist/index.js +537 -202
  7. package/dist/json-rpc-framing.d.ts +22 -0
  8. package/dist/json-rpc-framing.js +168 -0
  9. package/dist/mapping-pipeline-service.d.ts +1 -1
  10. package/dist/mapping-pipeline-service.js +13 -5
  11. package/dist/mapping-service.d.ts +12 -4
  12. package/dist/mapping-service.js +222 -105
  13. package/dist/mcp-helpers.d.ts +10 -2
  14. package/dist/mcp-helpers.js +59 -5
  15. package/dist/minecraft-explorer-service.d.ts +1 -2
  16. package/dist/minecraft-explorer-service.js +120 -24
  17. package/dist/mixin-validator.d.ts +24 -2
  18. package/dist/mixin-validator.js +228 -103
  19. package/dist/mod-decompile-service.d.ts +5 -0
  20. package/dist/mod-decompile-service.js +40 -5
  21. package/dist/mod-remap-service.js +142 -30
  22. package/dist/mojang-tiny-mapping-service.js +26 -26
  23. package/dist/path-resolver.js +41 -4
  24. package/dist/registry-service.d.ts +10 -1
  25. package/dist/registry-service.js +154 -22
  26. package/dist/resources.js +7 -7
  27. package/dist/search-hit-accumulator.d.ts +0 -3
  28. package/dist/search-hit-accumulator.js +27 -6
  29. package/dist/source-jar-reader.js +16 -2
  30. package/dist/source-resolver.d.ts +1 -0
  31. package/dist/source-resolver.js +93 -2
  32. package/dist/source-service.d.ts +76 -47
  33. package/dist/source-service.js +1344 -763
  34. package/dist/stdio-supervisor.d.ts +46 -0
  35. package/dist/stdio-supervisor.js +349 -0
  36. package/dist/storage/files-repo.d.ts +3 -0
  37. package/dist/storage/files-repo.js +66 -1
  38. package/dist/storage/migrations.d.ts +1 -1
  39. package/dist/storage/migrations.js +6 -2
  40. package/dist/storage/schema.d.ts +1 -0
  41. package/dist/storage/schema.js +7 -0
  42. package/dist/symbols/symbol-extractor.js +6 -4
  43. package/dist/tool-execution-gate.d.ts +15 -0
  44. package/dist/tool-execution-gate.js +58 -0
  45. package/dist/tool-input.d.ts +6 -0
  46. package/dist/tool-input.js +64 -0
  47. package/dist/types.d.ts +1 -1
  48. package/dist/version-diff-service.js +10 -5
  49. package/dist/version-service.js +7 -2
  50. package/dist/workspace-mapping-service.js +12 -0
  51. package/package.json +4 -1
@@ -0,0 +1,46 @@
1
+ type SupervisorOptions = {
2
+ entryFile: string;
3
+ };
4
+ export declare class StdioSupervisor {
5
+ private readonly entryFile;
6
+ private readonly clientReader;
7
+ private readonly workerReader;
8
+ private readonly queuedMessages;
9
+ private readonly pendingRequests;
10
+ private child;
11
+ private childReady;
12
+ private shuttingDown;
13
+ private restartTimer;
14
+ private workerStderrBuffer;
15
+ private clientMode;
16
+ private initializeRequest;
17
+ private initializedNotification;
18
+ private clientInitialized;
19
+ private replayingInitialization;
20
+ private initializeSentToWorker;
21
+ constructor(options: SupervisorOptions);
22
+ start(): Promise<void>;
23
+ private readonly handleClientData;
24
+ private readonly handleClientError;
25
+ private readonly handleClientClosed;
26
+ private readonly handleTerminateSignal;
27
+ private handleClientMessage;
28
+ private forwardToWorker;
29
+ private spawnWorker;
30
+ private readonly handleWorkerData;
31
+ private readonly handleWorkerStdinError;
32
+ private readonly handleWorkerStderr;
33
+ private readonly handleWorkerProcessError;
34
+ private readonly handleWorkerExit;
35
+ private handleWorkerMessage;
36
+ private handleWorkerReady;
37
+ private isInitializationResponse;
38
+ private flushQueue;
39
+ private failPendingRequestsOnWorkerExit;
40
+ private writeToClient;
41
+ private scheduleRestart;
42
+ private detachChild;
43
+ private shutdown;
44
+ }
45
+ export declare const STDIO_WORKER_MODE_ENV = "MCP_STDIO_WORKER_MODE";
46
+ export {};
@@ -0,0 +1,349 @@
1
+ import { spawn } from "node:child_process";
2
+ import process from "node:process";
3
+ import { encodeJsonRpcMessage, JsonRpcFrameReader } from "./json-rpc-framing.js";
4
+ import { log } from "./logger.js";
5
+ const DEFAULT_CLIENT_MODE = "line";
6
+ const WORKER_MODE_ENV = "MCP_STDIO_WORKER_MODE";
7
+ const WORKER_READY_MARKER = "__MCP_STDIO_WORKER_READY__";
8
+ const SUPERVISOR_DEBUG_ENABLED = process.env.MCP_SUPERVISOR_DEBUG === "1";
9
+ function isRequest(message) {
10
+ return "method" in message && "id" in message;
11
+ }
12
+ function isNotification(message) {
13
+ return "method" in message && !("id" in message);
14
+ }
15
+ function isResponse(message) {
16
+ return !("method" in message) && "id" in message;
17
+ }
18
+ function getTrackedRequestId(message) {
19
+ return typeof message.id === "string" || typeof message.id === "number"
20
+ ? message.id
21
+ : undefined;
22
+ }
23
+ function requestKey(id) {
24
+ return `${typeof id}:${String(id)}`;
25
+ }
26
+ function buildWorkerRestartError(id) {
27
+ return {
28
+ jsonrpc: "2.0",
29
+ id,
30
+ error: {
31
+ code: -32603,
32
+ message: "MCP worker restarted while handling the request. Retry the request."
33
+ }
34
+ };
35
+ }
36
+ function debugSupervisor(event, details) {
37
+ if (!SUPERVISOR_DEBUG_ENABLED) {
38
+ return;
39
+ }
40
+ log("info", `supervisor.debug.${event}`, details);
41
+ }
42
+ export class StdioSupervisor {
43
+ entryFile;
44
+ clientReader = new JsonRpcFrameReader();
45
+ workerReader = new JsonRpcFrameReader();
46
+ queuedMessages = [];
47
+ pendingRequests = new Map();
48
+ child;
49
+ childReady = false;
50
+ shuttingDown = false;
51
+ restartTimer;
52
+ workerStderrBuffer = "";
53
+ clientMode = DEFAULT_CLIENT_MODE;
54
+ initializeRequest;
55
+ initializedNotification;
56
+ clientInitialized = false;
57
+ replayingInitialization = false;
58
+ initializeSentToWorker = false;
59
+ constructor(options) {
60
+ this.entryFile = options.entryFile;
61
+ }
62
+ async start() {
63
+ process.stdin.on("data", this.handleClientData);
64
+ process.stdin.on("error", this.handleClientError);
65
+ process.stdin.on("end", this.handleClientClosed);
66
+ process.stdin.on("close", this.handleClientClosed);
67
+ process.stdin.resume();
68
+ process.on("SIGINT", this.handleTerminateSignal);
69
+ process.on("SIGTERM", this.handleTerminateSignal);
70
+ this.spawnWorker();
71
+ }
72
+ handleClientData = (chunk) => {
73
+ this.clientReader.processChunk(chunk, {
74
+ onFrame: ({ message, mode }) => {
75
+ this.clientMode = mode;
76
+ this.handleClientMessage(message);
77
+ },
78
+ onError: (error) => {
79
+ log("warn", "supervisor.client_parse_error", { message: error.message });
80
+ }
81
+ });
82
+ };
83
+ handleClientError = (error) => {
84
+ log("warn", "supervisor.client_stream_error", { message: error.message });
85
+ };
86
+ handleClientClosed = () => {
87
+ void this.shutdown();
88
+ };
89
+ handleTerminateSignal = () => {
90
+ void this.shutdown();
91
+ };
92
+ handleClientMessage(message) {
93
+ debugSupervisor("client_message", {
94
+ hasMethod: "method" in message,
95
+ method: "method" in message ? message.method : undefined,
96
+ id: "id" in message ? message.id : undefined,
97
+ childReady: this.childReady
98
+ });
99
+ if (isRequest(message) && message.method === "initialize") {
100
+ this.initializeRequest = message;
101
+ this.clientInitialized = false;
102
+ }
103
+ else if (isNotification(message) && message.method === "notifications/initialized") {
104
+ this.initializedNotification = message;
105
+ }
106
+ if (!this.childReady) {
107
+ this.queuedMessages.push(message);
108
+ return;
109
+ }
110
+ this.forwardToWorker(message);
111
+ }
112
+ forwardToWorker(message) {
113
+ const child = this.child;
114
+ if (!child || child.stdin.destroyed) {
115
+ this.queuedMessages.push(message);
116
+ if (!this.shuttingDown) {
117
+ this.scheduleRestart();
118
+ }
119
+ return;
120
+ }
121
+ if (isRequest(message)) {
122
+ const id = getTrackedRequestId(message);
123
+ if (id !== undefined) {
124
+ this.pendingRequests.set(requestKey(id), {
125
+ id,
126
+ method: message.method
127
+ });
128
+ }
129
+ if (message.method === "initialize") {
130
+ this.initializeSentToWorker = true;
131
+ }
132
+ }
133
+ debugSupervisor("forward_to_worker", {
134
+ method: "method" in message ? message.method : undefined,
135
+ id: "id" in message ? message.id : undefined
136
+ });
137
+ child.stdin.write(encodeJsonRpcMessage(message, "content-length"));
138
+ }
139
+ spawnWorker() {
140
+ if (this.shuttingDown) {
141
+ return;
142
+ }
143
+ const child = spawn(process.execPath, [...process.execArgv, this.entryFile], {
144
+ env: {
145
+ ...process.env,
146
+ [WORKER_MODE_ENV]: "1"
147
+ },
148
+ stdio: ["pipe", "pipe", "pipe"]
149
+ });
150
+ this.child = child;
151
+ this.childReady = false;
152
+ this.initializeSentToWorker = false;
153
+ this.workerReader.clear();
154
+ this.workerStderrBuffer = "";
155
+ child.stdout.on("data", this.handleWorkerData);
156
+ child.stderr.on("data", this.handleWorkerStderr);
157
+ child.stdin.on("error", this.handleWorkerStdinError);
158
+ child.once("error", this.handleWorkerProcessError);
159
+ child.once("exit", this.handleWorkerExit);
160
+ log("info", "supervisor.worker_spawn", { pid: child.pid });
161
+ }
162
+ handleWorkerData = (chunk) => {
163
+ this.workerReader.processChunk(chunk, {
164
+ onFrame: ({ message }) => {
165
+ this.handleWorkerMessage(message);
166
+ },
167
+ onError: (error) => {
168
+ log("warn", "supervisor.worker_parse_error", { message: error.message });
169
+ }
170
+ });
171
+ };
172
+ handleWorkerStdinError = (error) => {
173
+ if (error.code === "EPIPE") {
174
+ return;
175
+ }
176
+ log("warn", "supervisor.worker_stdin_error", { message: error.message });
177
+ };
178
+ handleWorkerStderr = (chunk) => {
179
+ this.workerStderrBuffer += chunk.toString();
180
+ const lines = this.workerStderrBuffer.split(/\r?\n/);
181
+ this.workerStderrBuffer = lines.pop() ?? "";
182
+ for (const line of lines) {
183
+ if (line === WORKER_READY_MARKER) {
184
+ this.handleWorkerReady();
185
+ continue;
186
+ }
187
+ process.stderr.write(`${line}\n`);
188
+ }
189
+ };
190
+ handleWorkerProcessError = (error) => {
191
+ log("error", "supervisor.worker_process_error", { message: error.message });
192
+ };
193
+ handleWorkerExit = (code, signal) => {
194
+ const childPid = this.child?.pid;
195
+ this.detachChild();
196
+ if (this.shuttingDown) {
197
+ return;
198
+ }
199
+ log("warn", "supervisor.worker_exit", {
200
+ pid: childPid,
201
+ code,
202
+ signal,
203
+ pendingRequests: this.pendingRequests.size
204
+ });
205
+ this.failPendingRequestsOnWorkerExit();
206
+ this.scheduleRestart();
207
+ };
208
+ handleWorkerMessage(message) {
209
+ debugSupervisor("worker_message", {
210
+ hasMethod: "method" in message,
211
+ method: "method" in message ? message.method : undefined,
212
+ id: "id" in message ? message.id : undefined,
213
+ replayingInitialization: this.replayingInitialization
214
+ });
215
+ if (this.isInitializationResponse(message)) {
216
+ const id = getTrackedRequestId(message);
217
+ if (id !== undefined) {
218
+ this.pendingRequests.delete(requestKey(id));
219
+ }
220
+ if (this.replayingInitialization) {
221
+ this.replayingInitialization = false;
222
+ if (this.initializedNotification) {
223
+ this.forwardToWorker(this.initializedNotification);
224
+ }
225
+ this.childReady = true;
226
+ this.flushQueue();
227
+ return;
228
+ }
229
+ this.clientInitialized = true;
230
+ this.childReady = true;
231
+ this.writeToClient(message);
232
+ this.flushQueue();
233
+ return;
234
+ }
235
+ if (isResponse(message)) {
236
+ const id = getTrackedRequestId(message);
237
+ if (id !== undefined) {
238
+ this.pendingRequests.delete(requestKey(id));
239
+ }
240
+ }
241
+ this.writeToClient(message);
242
+ }
243
+ handleWorkerReady() {
244
+ debugSupervisor("worker_ready", {
245
+ hasInitializeRequest: this.initializeRequest !== undefined,
246
+ clientInitialized: this.clientInitialized
247
+ });
248
+ if (!this.initializeRequest) {
249
+ this.childReady = true;
250
+ this.flushQueue();
251
+ return;
252
+ }
253
+ this.replayingInitialization = this.clientInitialized;
254
+ this.forwardToWorker(this.initializeRequest);
255
+ }
256
+ isInitializationResponse(message) {
257
+ const id = isResponse(message) ? getTrackedRequestId(message) : undefined;
258
+ const initializeId = this.initializeRequest
259
+ ? getTrackedRequestId(this.initializeRequest)
260
+ : undefined;
261
+ return (id !== undefined &&
262
+ initializeId !== undefined &&
263
+ requestKey(id) === requestKey(initializeId));
264
+ }
265
+ flushQueue() {
266
+ if (!this.childReady || this.queuedMessages.length === 0) {
267
+ return;
268
+ }
269
+ const pending = this.queuedMessages.splice(0, this.queuedMessages.length);
270
+ for (const message of pending) {
271
+ if (this.initializeSentToWorker &&
272
+ isRequest(message) &&
273
+ message.method === "initialize" &&
274
+ this.initializeRequest !== undefined &&
275
+ getTrackedRequestId(message) === getTrackedRequestId(this.initializeRequest)) {
276
+ continue;
277
+ }
278
+ this.forwardToWorker(message);
279
+ }
280
+ }
281
+ failPendingRequestsOnWorkerExit() {
282
+ const preservedInitializeKey = this.initializeRequest && !this.clientInitialized
283
+ ? requestKey(this.initializeRequest.id)
284
+ : undefined;
285
+ for (const [key, pending] of [...this.pendingRequests.entries()]) {
286
+ if (key === preservedInitializeKey) {
287
+ continue;
288
+ }
289
+ this.pendingRequests.delete(key);
290
+ this.writeToClient(buildWorkerRestartError(pending.id));
291
+ }
292
+ }
293
+ writeToClient(message) {
294
+ debugSupervisor("write_to_client", {
295
+ hasMethod: "method" in message,
296
+ method: "method" in message ? message.method : undefined,
297
+ id: "id" in message ? message.id : undefined,
298
+ clientMode: this.clientMode
299
+ });
300
+ const frame = encodeJsonRpcMessage(message, this.clientMode);
301
+ process.stdout.write(frame);
302
+ }
303
+ scheduleRestart() {
304
+ if (this.restartTimer || this.shuttingDown) {
305
+ return;
306
+ }
307
+ this.restartTimer = setTimeout(() => {
308
+ this.restartTimer = undefined;
309
+ this.spawnWorker();
310
+ }, 100);
311
+ }
312
+ detachChild() {
313
+ const child = this.child;
314
+ if (!child) {
315
+ this.childReady = false;
316
+ return;
317
+ }
318
+ child.stdout.off("data", this.handleWorkerData);
319
+ child.stderr.off("data", this.handleWorkerStderr);
320
+ child.stdin.off("error", this.handleWorkerStdinError);
321
+ child.off("error", this.handleWorkerProcessError);
322
+ child.off("exit", this.handleWorkerExit);
323
+ this.child = undefined;
324
+ this.childReady = false;
325
+ }
326
+ async shutdown() {
327
+ if (this.shuttingDown) {
328
+ return;
329
+ }
330
+ this.shuttingDown = true;
331
+ if (this.restartTimer) {
332
+ clearTimeout(this.restartTimer);
333
+ this.restartTimer = undefined;
334
+ }
335
+ process.stdin.off("data", this.handleClientData);
336
+ process.stdin.off("error", this.handleClientError);
337
+ process.stdin.off("end", this.handleClientClosed);
338
+ process.stdin.off("close", this.handleClientClosed);
339
+ process.off("SIGINT", this.handleTerminateSignal);
340
+ process.off("SIGTERM", this.handleTerminateSignal);
341
+ const child = this.child;
342
+ this.detachChild();
343
+ if (child && child.exitCode === null) {
344
+ child.kill("SIGTERM");
345
+ }
346
+ }
347
+ }
348
+ export const STDIO_WORKER_MODE_ENV = WORKER_MODE_ENV;
349
+ //# sourceMappingURL=stdio-supervisor.js.map
@@ -50,6 +50,7 @@ export declare class FilesRepo {
50
50
  private readonly searchPathStmt;
51
51
  private readonly searchFtsStmt;
52
52
  private readonly getByPathsStmtCache;
53
+ private readonly classLookupStmtCache;
53
54
  constructor(db: SqliteDatabase);
54
55
  clearFilesForArtifact(artifactId: string): void;
55
56
  insertFilesForArtifact(artifactId: string, files: IndexedFile[]): void;
@@ -70,6 +71,8 @@ export declare class FilesRepo {
70
71
  countTextCandidates(artifactId: string, query: string): number;
71
72
  countPathCandidates(artifactId: string, query: string): number;
72
73
  findFirstFilePathByName(artifactId: string, fileName: string): string | undefined;
74
+ private getClassLookupStmt;
75
+ findBestClassLookupPath(artifactId: string, exactFilePaths: string[], qualifiedClassName: string, simpleName: string, expectedPrefix: string): string | undefined;
73
76
  searchFiles(artifactId: string, options: SearchFilesOptions): PagedResult<SearchFilesResult>;
74
77
  totalContentBytes(): number;
75
78
  contentBytesForArtifact(artifactId: string): number;
@@ -114,6 +114,7 @@ export class FilesRepo {
114
114
  searchPathStmt;
115
115
  searchFtsStmt;
116
116
  getByPathsStmtCache = new Map();
117
+ classLookupStmtCache = new Map();
117
118
  constructor(db) {
118
119
  this.db = db;
119
120
  this.deleteStmt = this.db.prepare(`
@@ -416,6 +417,65 @@ export class FilesRepo {
416
417
  .get(artifactId, normalized, `%/${normalized}`);
417
418
  return row?.file_path;
418
419
  }
420
+ getClassLookupStmt(exactPathCount) {
421
+ const cached = this.classLookupStmtCache.get(exactPathCount);
422
+ if (cached) {
423
+ return cached;
424
+ }
425
+ const exactPlaceholders = Array.from({ length: exactPathCount }, () => "?").join(", ");
426
+ const statement = this.db.prepare(`
427
+ WITH ranked_candidates AS (
428
+ SELECT file_path, 0 AS rank
429
+ FROM files
430
+ WHERE artifact_id = ?
431
+ AND file_path IN (${exactPlaceholders})
432
+
433
+ UNION ALL
434
+
435
+ SELECT file_path,
436
+ CASE
437
+ WHEN qualified_name = ? THEN 1
438
+ WHEN symbol_name = ? THEN 2
439
+ ELSE 3
440
+ END AS rank
441
+ FROM symbols
442
+ WHERE artifact_id = ?
443
+ AND symbol_kind = 'class'
444
+ AND (
445
+ qualified_name = ?
446
+ OR symbol_name = ?
447
+ OR qualified_name LIKE ?
448
+ )
449
+ AND (? = '' OR instr(file_path, ?) = 1)
450
+
451
+ UNION ALL
452
+
453
+ SELECT file_path, 4 AS rank
454
+ FROM files
455
+ WHERE artifact_id = ?
456
+ AND (file_path = ? OR file_path LIKE ? ESCAPE '\\')
457
+ AND (? = '' OR instr(file_path, ?) = 1)
458
+ )
459
+ SELECT file_path
460
+ FROM ranked_candidates
461
+ ORDER BY rank ASC, file_path ASC
462
+ LIMIT 1
463
+ `);
464
+ this.classLookupStmtCache.set(exactPathCount, statement);
465
+ return statement;
466
+ }
467
+ findBestClassLookupPath(artifactId, exactFilePaths, qualifiedClassName, simpleName, expectedPrefix) {
468
+ const normalizedExactFilePaths = [...new Set(exactFilePaths.map((entry) => entry.trim()).filter(Boolean))];
469
+ const normalizedClassName = qualifiedClassName.trim();
470
+ const normalizedSimpleName = simpleName.trim();
471
+ if (normalizedExactFilePaths.length === 0 || !normalizedClassName || !normalizedSimpleName) {
472
+ return undefined;
473
+ }
474
+ const stmt = this.getClassLookupStmt(normalizedExactFilePaths.length);
475
+ const fileName = `${normalizedSimpleName}.java`;
476
+ const row = stmt.get(artifactId, ...normalizedExactFilePaths, normalizedClassName, normalizedSimpleName, artifactId, normalizedClassName, normalizedSimpleName, `%.${normalizedSimpleName}`, expectedPrefix, expectedPrefix, artifactId, fileName, `%/${fileName}`, expectedPrefix, expectedPrefix);
477
+ return row?.file_path;
478
+ }
419
479
  searchFiles(artifactId, options) {
420
480
  const page = this.searchFilesWithContent(artifactId, options);
421
481
  return {
@@ -446,10 +506,15 @@ export class FilesRepo {
446
506
  const normalizedCount = Math.max(1, Math.trunc(pathCount));
447
507
  const cached = this.getByPathsStmtCache.get(normalizedCount);
448
508
  if (cached) {
509
+ this.getByPathsStmtCache.delete(normalizedCount);
510
+ this.getByPathsStmtCache.set(normalizedCount, cached);
449
511
  return cached;
450
512
  }
451
513
  if (this.getByPathsStmtCache.size >= 64) {
452
- this.getByPathsStmtCache.clear();
514
+ const oldestKey = this.getByPathsStmtCache.keys().next().value;
515
+ if (oldestKey !== undefined) {
516
+ this.getByPathsStmtCache.delete(oldestKey);
517
+ }
453
518
  }
454
519
  const placeholders = Array.from({ length: normalizedCount }, () => "?").join(", ");
455
520
  const stmt = this.db.prepare(`
@@ -6,6 +6,6 @@ type MigrationRunner = {
6
6
  };
7
7
  transaction<T>(fn: () => T): () => T;
8
8
  };
9
- export declare const LATEST_SCHEMA_VERSION = 1;
9
+ export declare const LATEST_SCHEMA_VERSION = 2;
10
10
  export declare function runMigrations(db: MigrationRunner): number;
11
11
  export {};
@@ -1,10 +1,14 @@
1
1
  import { createError, ERROR_CODES } from "../errors.js";
2
- import { SCHEMA_V1_STATEMENTS } from "./schema.js";
3
- export const LATEST_SCHEMA_VERSION = 1;
2
+ import { SCHEMA_V1_STATEMENTS, SCHEMA_V2_STATEMENTS } from "./schema.js";
3
+ export const LATEST_SCHEMA_VERSION = 2;
4
4
  const migrations = [
5
5
  {
6
6
  version: 1,
7
7
  statements: SCHEMA_V1_STATEMENTS
8
+ },
9
+ {
10
+ version: 2,
11
+ statements: SCHEMA_V2_STATEMENTS
8
12
  }
9
13
  ];
10
14
  function selectSchemaVersion(tx) {
@@ -1 +1,2 @@
1
1
  export declare const SCHEMA_V1_STATEMENTS: string[];
2
+ export declare const SCHEMA_V2_STATEMENTS: string[];
@@ -157,4 +157,11 @@ export const SCHEMA_V1_STATEMENTS = [
157
157
  );
158
158
  END`
159
159
  ];
160
+ export const SCHEMA_V2_STATEMENTS = [
161
+ `DELETE FROM symbols`,
162
+ `DELETE FROM files`,
163
+ `DELETE FROM artifact_content_bytes`,
164
+ `DELETE FROM artifact_index_meta`,
165
+ `DELETE FROM artifacts`
166
+ ];
160
167
  //# sourceMappingURL=schema.js.map
@@ -1,11 +1,12 @@
1
1
  const CLASS_DECLARATION = /^(?:\s*@[\w.]+\s+)*(?:\s*(?:public|private|protected|abstract|final|sealed|non-sealed|static)\s+)*\s*(class|interface|enum|record)\s+([A-Za-z_$][\w$]*)/;
2
2
  const METHOD_DECLARATION = /^(?:\s*@[\w.]+\s+)*[^{;]*?\b([A-Za-z_$][\w$]*)\s*\([^)]*\)\s*(?:\{|;)/;
3
3
  const FIELD_DECLARATION = /^(?:\s*@[\w.]+\s+)*[^\s][\w<>\[\],.?]+\s+([A-Za-z_$][\w$]*)\s*(?:=|;|,)/;
4
+ const NOISE_TOKENS = new Set(["if", "for", "while", "switch", "catch", "return", "new", "throw"]);
4
5
  function normalizeLine(line) {
5
6
  return line.replace(/\s+/g, " ").trim();
6
7
  }
7
8
  function isNoiseToken(token) {
8
- return new Set(["if", "for", "while", "switch", "catch", "return", "new", "throw"]).has(token);
9
+ return NOISE_TOKENS.has(token);
9
10
  }
10
11
  function lineIndexToLine(lineNo) {
11
12
  return lineNo + 1;
@@ -13,6 +14,7 @@ function lineIndexToLine(lineNo) {
13
14
  export function extractSymbolsFromSource(filePath, content) {
14
15
  const lines = content.split(/\r?\n/);
15
16
  const symbols = [];
17
+ const qualifiedName = filePath.replace(/\.java$/, "").replaceAll("/", ".");
16
18
  for (let index = 0; index < lines.length; index += 1) {
17
19
  const rawLine = lines[index] ?? "";
18
20
  const line = normalizeLine(rawLine);
@@ -27,7 +29,7 @@ export function extractSymbolsFromSource(filePath, content) {
27
29
  symbols.push({
28
30
  symbolKind,
29
31
  symbolName,
30
- qualifiedName: filePath.replace(/\.java$/, "").replaceAll("/", "."),
32
+ qualifiedName,
31
33
  line: lineIndexToLine(index)
32
34
  });
33
35
  }
@@ -40,7 +42,7 @@ export function extractSymbolsFromSource(filePath, content) {
40
42
  symbols.push({
41
43
  symbolKind: "method",
42
44
  symbolName,
43
- qualifiedName: filePath.replace(/\.java$/, "").replaceAll("/", "."),
45
+ qualifiedName,
44
46
  line: lineIndexToLine(index)
45
47
  });
46
48
  }
@@ -53,7 +55,7 @@ export function extractSymbolsFromSource(filePath, content) {
53
55
  symbols.push({
54
56
  symbolKind: "field",
55
57
  symbolName,
56
- qualifiedName: filePath.replace(/\.java$/, "").replaceAll("/", "."),
58
+ qualifiedName,
57
59
  line: lineIndexToLine(index)
58
60
  });
59
61
  }
@@ -0,0 +1,15 @@
1
+ type GateOptions = {
2
+ maxConcurrent: number;
3
+ maxQueue: number;
4
+ };
5
+ export declare class ToolExecutionGate {
6
+ private readonly maxConcurrent;
7
+ private readonly maxQueue;
8
+ private activeCount;
9
+ private readonly queue;
10
+ constructor(options?: Partial<GateOptions>);
11
+ run<T>(tool: string, task: () => Promise<T>): Promise<T>;
12
+ private execute;
13
+ private drain;
14
+ }
15
+ export {};
@@ -0,0 +1,58 @@
1
+ import { createError, ERROR_CODES } from "./errors.js";
2
+ const DEFAULT_OPTIONS = {
3
+ maxConcurrent: 1,
4
+ maxQueue: 2
5
+ };
6
+ export class ToolExecutionGate {
7
+ maxConcurrent;
8
+ maxQueue;
9
+ activeCount = 0;
10
+ queue = [];
11
+ constructor(options = {}) {
12
+ this.maxConcurrent = Math.max(1, Math.trunc(options.maxConcurrent ?? DEFAULT_OPTIONS.maxConcurrent));
13
+ this.maxQueue = Math.max(0, Math.trunc(options.maxQueue ?? DEFAULT_OPTIONS.maxQueue));
14
+ }
15
+ run(tool, task) {
16
+ if (this.activeCount < this.maxConcurrent) {
17
+ return this.execute({ tool, task: task });
18
+ }
19
+ if (this.queue.length >= this.maxQueue) {
20
+ return Promise.reject(createError({
21
+ code: ERROR_CODES.LIMIT_EXCEEDED,
22
+ message: `Heavy tool queue is full; "${tool}" was not started.`,
23
+ details: {
24
+ tool,
25
+ activeCount: this.activeCount,
26
+ queuedCount: this.queue.length,
27
+ maxConcurrent: this.maxConcurrent,
28
+ maxQueue: this.maxQueue,
29
+ nextAction: "Retry after the current heavy analysis request completes. Avoid sending multiple heavy mapping/version analysis tools in parallel."
30
+ }
31
+ }));
32
+ }
33
+ return new Promise((resolve, reject) => {
34
+ this.queue.push({
35
+ tool,
36
+ task: task,
37
+ resolve: resolve,
38
+ reject
39
+ });
40
+ });
41
+ }
42
+ execute(entry) {
43
+ this.activeCount += 1;
44
+ return Promise.resolve()
45
+ .then(entry.task)
46
+ .finally(() => {
47
+ this.activeCount = Math.max(0, this.activeCount - 1);
48
+ this.drain();
49
+ });
50
+ }
51
+ drain() {
52
+ while (this.activeCount < this.maxConcurrent && this.queue.length > 0) {
53
+ const next = this.queue.shift();
54
+ this.execute(next).then(next.resolve, next.reject);
55
+ }
56
+ }
57
+ }
58
+ //# sourceMappingURL=tool-execution-gate.js.map
@@ -0,0 +1,6 @@
1
+ export type PreparedToolInput = {
2
+ normalizedInput: unknown;
3
+ removedOfficialPaths: string[];
4
+ suggestedReplacementInput?: Record<string, unknown>;
5
+ };
6
+ export declare function prepareToolInput(rawInput: unknown): PreparedToolInput;