@aiready/ast-mcp-server 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -329,22 +329,101 @@ var SymbolIndex = class {
329
329
  };
330
330
  var symbolIndex = new SymbolIndex();
331
331
 
332
+ // src/worker/pool.ts
333
+ import { Worker } from "worker_threads";
334
+ import path3 from "path";
335
+ import { fileURLToPath } from "url";
336
+ var WorkerPool = class {
337
+ constructor(poolSize) {
338
+ this.poolSize = poolSize;
339
+ }
340
+ workers = [];
341
+ available = [];
342
+ queue = [];
343
+ activeJobs = /* @__PURE__ */ new Map();
344
+ taskId = 0;
345
+ async init() {
346
+ const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
347
+ const workerPath = path3.join(__dirname2, "ast-worker.js");
348
+ for (let i = 0; i < this.poolSize; i++) {
349
+ const worker = new Worker(workerPath);
350
+ worker.on("message", (msg) => this.handleResult(msg));
351
+ worker.on("error", (err) => this.handleWorkerError(worker, err));
352
+ this.workers.push(worker);
353
+ this.available.push(worker);
354
+ }
355
+ }
356
+ async execute(type, payload) {
357
+ return new Promise((resolve, reject) => {
358
+ const id = String(++this.taskId);
359
+ const task = { id, type, payload, resolve, reject };
360
+ const worker = this.available.pop();
361
+ if (worker) {
362
+ this.dispatch(worker, task);
363
+ } else {
364
+ this.queue.push(task);
365
+ }
366
+ });
367
+ }
368
+ dispatch(worker, task) {
369
+ this.activeJobs.set(task.id, task);
370
+ worker.postMessage({ id: task.id, type: task.type, payload: task.payload });
371
+ }
372
+ handleResult(msg) {
373
+ const task = this.activeJobs.get(msg.id);
374
+ if (!task) return;
375
+ this.activeJobs.delete(msg.id);
376
+ if (msg.error) {
377
+ task.reject(new Error(msg.error));
378
+ } else {
379
+ task.resolve(msg.result);
380
+ }
381
+ const worker = this.workers.find(
382
+ (w) => !this.available.includes(w) && ![...this.activeJobs.values()].some((t) => t.id === msg.id)
383
+ // simplistic
384
+ );
385
+ }
386
+ handleWorkerError(worker, err) {
387
+ const idx = this.workers.indexOf(worker);
388
+ if (idx !== -1) {
389
+ worker.terminate();
390
+ const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
391
+ const workerPath = path3.join(__dirname2, "ast-worker.js");
392
+ const newWorker = new Worker(workerPath);
393
+ newWorker.on("message", (msg) => this.handleResult(msg));
394
+ newWorker.on("error", (e) => this.handleWorkerError(newWorker, e));
395
+ this.workers[idx] = newWorker;
396
+ this.available.push(newWorker);
397
+ }
398
+ }
399
+ async terminate() {
400
+ await Promise.all(this.workers.map((w) => w.terminate()));
401
+ this.workers = [];
402
+ this.available = [];
403
+ }
404
+ };
405
+
332
406
  // src/adapters/typescript-adapter.ts
333
407
  var TypeScriptAdapter = class {
334
- async resolveDefinition(symbolName, path3) {
335
- validateWorkspacePath(path3);
408
+ pool;
409
+ constructor() {
410
+ const poolSize = parseInt(process.env.AST_WORKER_POOL_SIZE || "2");
411
+ this.pool = new WorkerPool(poolSize);
412
+ }
413
+ async resolveDefinition(symbolName, path4) {
414
+ validateWorkspacePath(path4);
336
415
  const indexHits = symbolIndex.lookup(symbolName);
337
416
  if (indexHits.length > 0) {
338
417
  const results = [];
339
418
  for (const hit of indexHits) {
340
419
  const tsconfig2 = await projectManager.findNearestTsConfig(hit.file);
341
420
  if (tsconfig2) {
342
- const project2 = projectManager.ensureProject(tsconfig2);
343
- const sourceFile2 = project2.addSourceFileAtPathIfExists(hit.file);
344
- if (sourceFile2) {
345
- const exported2 = sourceFile2.getExportedDeclarations().get(symbolName);
346
- if (exported2 && exported2.length > 0) {
347
- results.push(this.mapToDefinitionLocation(exported2[0]));
421
+ const project = projectManager.ensureProject(tsconfig2);
422
+ const sourceFile = project.addSourceFileAtPathIfExists(hit.file);
423
+ if (sourceFile) {
424
+ const exported = sourceFile.getExportedDeclarations().get(symbolName);
425
+ if (exported && exported.length > 0) {
426
+ results.push(this.mapToDefinitionLocation(exported[0]));
348
427
  continue;
349
428
  }
350
429
  }
@@ -360,20 +439,32 @@ var TypeScriptAdapter = class {
360
439
  }
361
440
  return results;
362
441
  }
363
- if (fs3.statSync(path3).isDirectory()) {
442
+ if (fs3.statSync(path4).isDirectory()) {
364
443
  return [];
365
444
  }
366
- const tsconfig = await projectManager.findNearestTsConfig(path3);
445
+ const tsconfig = await projectManager.findNearestTsConfig(path4);
367
446
  if (!tsconfig) return [];
368
- const project = projectManager.ensureProject(tsconfig);
369
- const sourceFile = project.addSourceFileAtPathIfExists(path3);
370
- if (!sourceFile) return [];
371
- const exported = sourceFile.getExportedDeclarations().get(symbolName);
372
- if (!exported) return [];
373
- return exported.map((decl) => this.mapToDefinitionLocation(decl));
447
+ try {
448
+ const result = await this.pool.execute(
449
+ "resolve_definition",
450
+ {
451
+ tsconfig,
452
+ file: path4,
453
+ symbol: symbolName
454
+ }
455
+ );
456
+ return result;
457
+ } catch {
458
+ const project = projectManager.ensureProject(tsconfig);
459
+ const sourceFile = project.addSourceFileAtPathIfExists(path4);
460
+ if (!sourceFile) return [];
461
+ const exported = sourceFile.getExportedDeclarations().get(symbolName);
462
+ if (!exported) return [];
463
+ return exported.map((decl) => this.mapToDefinitionLocation(decl));
464
+ }
374
465
  }
375
- async findReferences(symbolName, path3, limit = 50, offset = 0) {
376
- validateWorkspacePath(path3);
466
+ async findReferences(symbolName, path4, limit = 50, offset = 0) {
467
+ validateWorkspacePath(path4);
377
468
  const hits = symbolIndex.lookup(symbolName);
378
469
  if (hits.length === 0) return { references: [], total_count: 0 };
379
470
  const hit = hits[0];
@@ -390,13 +481,12 @@ var TypeScriptAdapter = class {
390
481
  const { searchCode: searchCode2 } = await import("./search-code-V3LACKQ6.js");
391
482
  const searchResults = await searchCode2(
392
483
  symbolName,
393
- path3,
484
+ path4,
394
485
  "*.{ts,tsx,js,jsx}",
395
486
  1e3,
396
487
  false
397
488
  );
398
489
  const filesToLoad = [...new Set(searchResults.map((r) => r.file))];
399
- console.log("Search code files to load:", filesToLoad);
400
490
  for (const file of filesToLoad) {
401
491
  project.addSourceFileAtPathIfExists(file);
402
492
  }
@@ -423,57 +513,69 @@ var TypeScriptAdapter = class {
423
513
  total_count: unique.length
424
514
  };
425
515
  }
426
- async findImplementations(symbolName, path3, limit = 50, offset = 0) {
427
- validateWorkspacePath(path3);
516
+ async findImplementations(symbolName, path4, limit = 50, offset = 0) {
517
+ validateWorkspacePath(path4);
428
518
  const hits = symbolIndex.lookup(symbolName);
429
519
  if (hits.length === 0) return { implementations: [], total_count: 0 };
430
520
  const hit = hits[0];
431
521
  const tsconfig = await projectManager.findNearestTsConfig(hit.file);
432
522
  if (!tsconfig) return { implementations: [], total_count: 0 };
433
- const project = projectManager.ensureProject(tsconfig);
434
- const sourceFile = project.addSourceFileAtPathIfExists(hit.file);
435
- if (!sourceFile) return { implementations: [], total_count: 0 };
436
- const exported = sourceFile.getExportedDeclarations().get(symbolName);
437
- if (!exported || exported.length === 0)
438
- return { implementations: [], total_count: 0 };
439
- const targetNode = exported[0];
440
- if (!Node2.isClassDeclaration(targetNode) && !Node2.isInterfaceDeclaration(targetNode)) {
441
- return { implementations: [], total_count: 0 };
442
- }
443
523
  try {
444
- const { searchCode: searchCode2 } = await import("./search-code-V3LACKQ6.js");
445
- const searchResults = await searchCode2(
446
- symbolName,
447
- path3,
448
- "*.{ts,tsx,js,jsx}",
449
- 1e3,
450
- false
451
- );
452
- const filesToLoad = [...new Set(searchResults.map((r) => r.file))];
453
- for (const file of filesToLoad) {
454
- project.addSourceFileAtPathIfExists(file);
524
+ const result = await this.pool.execute("find_implementations", {
525
+ tsconfig,
526
+ file: hit.file,
527
+ symbol: symbolName
528
+ });
529
+ return {
530
+ implementations: result.implementations.slice(offset, offset + limit),
531
+ total_count: result.total_count
532
+ };
533
+ } catch {
534
+ const project = projectManager.ensureProject(tsconfig);
535
+ const sourceFile = project.addSourceFileAtPathIfExists(hit.file);
536
+ if (!sourceFile) return { implementations: [], total_count: 0 };
537
+ const exported = sourceFile.getExportedDeclarations().get(symbolName);
538
+ if (!exported || exported.length === 0)
539
+ return { implementations: [], total_count: 0 };
540
+ const targetNode = exported[0];
541
+ if (!Node2.isClassDeclaration(targetNode) && !Node2.isInterfaceDeclaration(targetNode)) {
542
+ return { implementations: [], total_count: 0 };
455
543
  }
456
- } catch (_e) {
457
- }
458
- const results = [];
459
- const implementations = targetNode.getImplementations?.();
460
- if (implementations) {
461
- for (const impl of implementations) {
462
- const sf = impl.getSourceFile();
463
- const lc = sf.getLineAndColumnAtPos(impl.getTextSpan().getStart());
464
- results.push({
465
- file: sf.getFilePath(),
466
- line: lc.line,
467
- column: lc.column,
468
- text: impl.getNode().getParent()?.getText() || impl.getNode().getText()
469
- });
544
+ try {
545
+ const { searchCode: searchCode2 } = await import("./search-code-V3LACKQ6.js");
546
+ const searchResults = await searchCode2(
547
+ symbolName,
548
+ path4,
549
+ "*.{ts,tsx,js,jsx}",
550
+ 1e3,
551
+ false
552
+ );
553
+ const filesToLoad = [...new Set(searchResults.map((r) => r.file))];
554
+ for (const file of filesToLoad) {
555
+ project.addSourceFileAtPathIfExists(file);
556
+ }
557
+ } catch (_e) {
558
+ }
559
+ const results = [];
560
+ const implementations = targetNode.getImplementations?.();
561
+ if (implementations) {
562
+ for (const impl of implementations) {
563
+ const sf = impl.getSourceFile();
564
+ const lc = sf.getLineAndColumnAtPos(impl.getTextSpan().getStart());
565
+ results.push({
566
+ file: sf.getFilePath(),
567
+ line: lc.line,
568
+ column: lc.column,
569
+ text: impl.getNode().getParent()?.getText() || impl.getNode().getText()
570
+ });
571
+ }
470
572
  }
573
+ const unique = this.deduplicateLocations(results);
574
+ return {
575
+ implementations: unique.slice(offset, offset + limit),
576
+ total_count: unique.length
577
+ };
471
578
  }
472
- const unique = this.deduplicateLocations(results);
473
- return {
474
- implementations: unique.slice(offset, offset + limit),
475
- total_count: unique.length
476
- };
477
579
  }
478
580
  async getFileStructure(filePath) {
479
581
  const safePath = validateWorkspacePath(filePath);
@@ -500,6 +602,9 @@ var TypeScriptAdapter = class {
500
602
  };
501
603
  return structure;
502
604
  }
605
+ async shutdown() {
606
+ await this.pool.terminate();
607
+ }
503
608
  mapToDefinitionLocation(node) {
504
609
  const sourceFile = node.getSourceFile();
505
610
  const lineAndColumn = sourceFile.getLineAndColumnAtPos(node.getStart());
@@ -615,20 +720,20 @@ var TypeScriptAdapter = class {
615
720
  var typescriptAdapter = new TypeScriptAdapter();
616
721
 
617
722
  // src/tools/resolve-definition.ts
618
- async function resolveDefinition(symbol, path3) {
619
- return await typescriptAdapter.resolveDefinition(symbol, path3);
723
+ async function resolveDefinition(symbol, path4) {
724
+ return await typescriptAdapter.resolveDefinition(symbol, path4);
620
725
  }
621
726
 
622
727
  // src/tools/find-references.ts
623
- async function findReferences(symbol, path3, limit = 50, offset = 0) {
624
- return await typescriptAdapter.findReferences(symbol, path3, limit, offset);
728
+ async function findReferences(symbol, path4, limit = 50, offset = 0) {
729
+ return await typescriptAdapter.findReferences(symbol, path4, limit, offset);
625
730
  }
626
731
 
627
732
  // src/tools/find-implementations.ts
628
- async function findImplementations(symbol, path3, limit = 50, offset = 0) {
733
+ async function findImplementations(symbol, path4, limit = 50, offset = 0) {
629
734
  return await typescriptAdapter.findImplementations(
630
735
  symbol,
631
- path3,
736
+ path4,
632
737
  limit,
633
738
  offset
634
739
  );
@@ -668,8 +773,8 @@ async function getSymbolDocs(symbol, filePath) {
668
773
  }
669
774
 
670
775
  // src/tools/build-symbol-index.ts
671
- async function buildSymbolIndex(path3) {
672
- return await symbolIndex.buildIndex(path3);
776
+ async function buildSymbolIndex(path4) {
777
+ return await symbolIndex.buildIndex(path4);
673
778
  }
674
779
 
675
780
  // src/index.ts
@@ -800,8 +905,8 @@ var ASTExplorerServer = class {
800
905
  try {
801
906
  switch (name) {
802
907
  case "resolve_definition": {
803
- const { symbol, path: path3 } = ResolveDefinitionSchema.parse(args);
804
- const results = await resolveDefinition(symbol, path3);
908
+ const { symbol, path: path4 } = ResolveDefinitionSchema.parse(args);
909
+ const results = await resolveDefinition(symbol, path4);
805
910
  return {
806
911
  content: [
807
912
  { type: "text", text: JSON.stringify(results, null, 2) }
@@ -809,8 +914,8 @@ var ASTExplorerServer = class {
809
914
  };
810
915
  }
811
916
  case "find_references": {
812
- const { symbol, path: path3, limit, offset } = FindReferencesSchema.parse(args);
813
- const results = await findReferences(symbol, path3, limit, offset);
917
+ const { symbol, path: path4, limit, offset } = FindReferencesSchema.parse(args);
918
+ const results = await findReferences(symbol, path4, limit, offset);
814
919
  return {
815
920
  content: [
816
921
  { type: "text", text: JSON.stringify(results, null, 2) }
@@ -818,10 +923,10 @@ var ASTExplorerServer = class {
818
923
  };
819
924
  }
820
925
  case "find_implementations": {
821
- const { symbol, path: path3, limit, offset } = FindImplementationsSchema.parse(args);
926
+ const { symbol, path: path4, limit, offset } = FindImplementationsSchema.parse(args);
822
927
  const results = await findImplementations(
823
928
  symbol,
824
- path3,
929
+ path4,
825
930
  limit,
826
931
  offset
827
932
  );
@@ -841,10 +946,10 @@ var ASTExplorerServer = class {
841
946
  };
842
947
  }
843
948
  case "search_code": {
844
- const { pattern, path: path3, filePattern, limit, regex } = SearchCodeSchema.parse(args);
949
+ const { pattern, path: path4, filePattern, limit, regex } = SearchCodeSchema.parse(args);
845
950
  const results = await searchCode(
846
951
  pattern,
847
- path3,
952
+ path4,
848
953
  filePattern,
849
954
  limit,
850
955
  regex
@@ -856,15 +961,15 @@ var ASTExplorerServer = class {
856
961
  };
857
962
  }
858
963
  case "get_symbol_docs": {
859
- const { symbol, path: path3 } = GetSymbolDocsSchema.parse(args);
860
- const docs = await getSymbolDocs(symbol, path3);
964
+ const { symbol, path: path4 } = GetSymbolDocsSchema.parse(args);
965
+ const docs = await getSymbolDocs(symbol, path4);
861
966
  return {
862
967
  content: [{ type: "text", text: JSON.stringify(docs, null, 2) }]
863
968
  };
864
969
  }
865
970
  case "build_symbol_index": {
866
- const { path: path3 } = BuildSymbolIndexSchema.parse(args);
867
- const stats = await buildSymbolIndex(path3);
971
+ const { path: path4 } = BuildSymbolIndexSchema.parse(args);
972
+ const stats = await buildSymbolIndex(path4);
868
973
  return {
869
974
  content: [{ type: "text", text: JSON.stringify(stats, null, 2) }]
870
975
  };