@aiready/ast-mcp-server 0.1.5 → 0.2.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.
@@ -1,3 +1,5 @@
1
+ #!/usr/bin/env node
2
+
1
3
  // src/tools/search-code.ts
2
4
  import { execFile } from "child_process";
3
5
  import { promisify } from "util";
@@ -25,15 +27,9 @@ function validateWorkspacePath(inputPath) {
25
27
 
26
28
  // src/tools/search-code.ts
27
29
  var execFileAsync = promisify(execFile);
28
- async function searchCode(pattern, searchPath, filePattern, limit = 50, regex = true) {
30
+ async function searchCode(pattern, searchPath, filePattern, limit = 50, regex = true, offset = 0) {
29
31
  const safePath = validateWorkspacePath(searchPath);
30
- const args = [
31
- "--json",
32
- "--max-count",
33
- limit.toString(),
34
- "--max-columns",
35
- "500"
36
- ];
32
+ const args = ["--json", "--max-columns", "500"];
37
33
  if (!regex) {
38
34
  args.push("--fixed-strings");
39
35
  }
@@ -64,7 +60,7 @@ async function searchCode(pattern, searchPath, filePattern, limit = 50, regex =
64
60
  }
65
61
  }
66
62
  }
67
- return results.slice(0, limit);
63
+ return results.slice(offset, offset + limit);
68
64
  } catch (error) {
69
65
  if (error.code === 1) {
70
66
  return [];
@@ -77,4 +73,4 @@ export {
77
73
  validateWorkspacePath,
78
74
  searchCode
79
75
  };
80
- //# sourceMappingURL=chunk-PRWMQQYW.js.map
76
+ //# sourceMappingURL=chunk-FQP7SHLM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tools/search-code.ts","../src/security.ts"],"sourcesContent":["import { execFile } from 'child_process';\nimport { promisify } from 'util';\nimport { rgPath } from '@vscode/ripgrep';\nimport { validateWorkspacePath } from '../security.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport interface SearchResult {\n file: string;\n line: number;\n column: number;\n text: string;\n}\n\nexport async function searchCode(\n pattern: string,\n searchPath: string,\n filePattern?: string,\n limit: number = 50,\n regex: boolean = true,\n offset: number = 0\n): Promise<SearchResult[]> {\n const safePath = validateWorkspacePath(searchPath);\n\n const args = ['--json', '--max-columns', '500'];\n\n if (!regex) {\n args.push('--fixed-strings');\n }\n\n args.push(pattern, safePath);\n\n if (filePattern) {\n args.push('--glob', filePattern);\n }\n\n // Common exclusions\n args.push('--glob', '!**/node_modules/**');\n args.push('--glob', '!**/dist/**');\n args.push('--glob', '!**/.git/**');\n\n try {\n const { stdout } = await execFileAsync(rgPath, args);\n const lines = stdout.split('\\n').filter(Boolean);\n const results: SearchResult[] = [];\n\n for (const line of lines) {\n const data = JSON.parse(line);\n if (data.type === 'match') {\n const file = data.data.path.text;\n const lineNumber = data.data.line_number;\n const submatches = data.data.submatches;\n\n for (const submatch of submatches) {\n results.push({\n file,\n line: lineNumber,\n column: submatch.start,\n text: data.data.lines.text.trim(),\n });\n }\n }\n }\n\n return results.slice(offset, offset + limit);\n } catch (error: any) {\n if (error.code === 1) {\n // rg returns 1 if no matches found\n return [];\n }\n throw error;\n }\n}\n","import path from 'path';\nimport fs from 'fs';\n\nexport function resolveWorkspaceRoot(): string {\n return process.env.AST_WORKSPACE_ROOT || process.cwd();\n}\n\nexport function validateWorkspacePath(inputPath: string): string {\n const root = resolveWorkspaceRoot();\n const resolved = path.resolve(root, inputPath);\n const normalized = path.normalize(resolved);\n\n // Reject path traversal\n if (!normalized.startsWith(root)) {\n throw new Error(\n `Path traversal detected: ${inputPath} escapes workspace root`\n );\n }\n\n // Reject null bytes\n if (normalized.includes('\\0')) {\n throw new Error('Path contains null bytes');\n }\n\n return normalized;\n}\n\nexport function validateFileExists(filePath: string): string {\n const safe = validateWorkspacePath(filePath);\n if (!fs.existsSync(safe)) {\n throw new Error(`File not found: ${filePath}`);\n }\n if (!fs.statSync(safe).isFile()) {\n throw new Error(`Not a file: ${filePath}`);\n }\n return safe;\n}\n"],"mappings":";;;AAAA,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,cAAc;;;ACFvB,OAAO,UAAU;AAGV,SAAS,uBAA+B;AAC7C,SAAO,QAAQ,IAAI,sBAAsB,QAAQ,IAAI;AACvD;AAEO,SAAS,sBAAsB,WAA2B;AAC/D,QAAM,OAAO,qBAAqB;AAClC,QAAM,WAAW,KAAK,QAAQ,MAAM,SAAS;AAC7C,QAAM,aAAa,KAAK,UAAU,QAAQ;AAG1C,MAAI,CAAC,WAAW,WAAW,IAAI,GAAG;AAChC,UAAM,IAAI;AAAA,MACR,4BAA4B,SAAS;AAAA,IACvC;AAAA,EACF;AAGA,MAAI,WAAW,SAAS,IAAI,GAAG;AAC7B,UAAM,IAAI,MAAM,0BAA0B;AAAA,EAC5C;AAEA,SAAO;AACT;;;ADpBA,IAAM,gBAAgB,UAAU,QAAQ;AASxC,eAAsB,WACpB,SACA,YACA,aACA,QAAgB,IAChB,QAAiB,MACjB,SAAiB,GACQ;AACzB,QAAM,WAAW,sBAAsB,UAAU;AAEjD,QAAM,OAAO,CAAC,UAAU,iBAAiB,KAAK;AAE9C,MAAI,CAAC,OAAO;AACV,SAAK,KAAK,iBAAiB;AAAA,EAC7B;AAEA,OAAK,KAAK,SAAS,QAAQ;AAE3B,MAAI,aAAa;AACf,SAAK,KAAK,UAAU,WAAW;AAAA,EACjC;AAGA,OAAK,KAAK,UAAU,qBAAqB;AACzC,OAAK,KAAK,UAAU,aAAa;AACjC,OAAK,KAAK,UAAU,aAAa;AAEjC,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,cAAc,QAAQ,IAAI;AACnD,UAAM,QAAQ,OAAO,MAAM,IAAI,EAAE,OAAO,OAAO;AAC/C,UAAM,UAA0B,CAAC;AAEjC,eAAW,QAAQ,OAAO;AACxB,YAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,UAAI,KAAK,SAAS,SAAS;AACzB,cAAM,OAAO,KAAK,KAAK,KAAK;AAC5B,cAAM,aAAa,KAAK,KAAK;AAC7B,cAAM,aAAa,KAAK,KAAK;AAE7B,mBAAW,YAAY,YAAY;AACjC,kBAAQ,KAAK;AAAA,YACX;AAAA,YACA,MAAM;AAAA,YACN,QAAQ,SAAS;AAAA,YACjB,MAAM,KAAK,KAAK,MAAM,KAAK,KAAK;AAAA,UAClC,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,WAAO,QAAQ,MAAM,QAAQ,SAAS,KAAK;AAAA,EAC7C,SAAS,OAAY;AACnB,QAAI,MAAM,SAAS,GAAG;AAEpB,aAAO,CAAC;AAAA,IACV;AACA,UAAM;AAAA,EACR;AACF;","names":[]}
package/dist/index.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  "use strict";
2
3
  var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
@@ -72,15 +73,9 @@ var search_code_exports = {};
72
73
  __export(search_code_exports, {
73
74
  searchCode: () => searchCode
74
75
  });
75
- async function searchCode(pattern, searchPath, filePattern, limit = 50, regex = true) {
76
+ async function searchCode(pattern, searchPath, filePattern, limit = 50, regex = true, offset = 0) {
76
77
  const safePath = validateWorkspacePath(searchPath);
77
- const args = [
78
- "--json",
79
- "--max-count",
80
- limit.toString(),
81
- "--max-columns",
82
- "500"
83
- ];
78
+ const args = ["--json", "--max-columns", "500"];
84
79
  if (!regex) {
85
80
  args.push("--fixed-strings");
86
81
  }
@@ -111,7 +106,7 @@ async function searchCode(pattern, searchPath, filePattern, limit = 50, regex =
111
106
  }
112
107
  }
113
108
  }
114
- return results.slice(0, limit);
109
+ return results.slice(offset, offset + limit);
115
110
  } catch (error) {
116
111
  if (error.code === 1) {
117
112
  return [];
@@ -171,7 +166,8 @@ var SearchCodeSchema = import_zod.z.object({
171
166
  pattern: import_zod.z.string().describe("Search pattern (regex by default)"),
172
167
  path: import_zod.z.string().describe("Directory to search in"),
173
168
  filePattern: import_zod.z.string().optional().describe('Glob filter (e.g., "*.ts")'),
174
- limit: import_zod.z.number().optional().default(50).describe("Max matches to return"),
169
+ limit: import_zod.z.number().optional().default(50).describe("Max matches to return (default 50)"),
170
+ offset: import_zod.z.number().optional().default(0).describe("Pagination offset (default 0)"),
175
171
  regex: import_zod.z.boolean().optional().default(true).describe("Use regex mode (default true)")
176
172
  });
177
173
  var GetSymbolDocsSchema = import_zod.z.object({
@@ -189,6 +185,7 @@ init_cjs_shims();
189
185
  init_cjs_shims();
190
186
  var import_ts_morph3 = require("ts-morph");
191
187
  var import_fs3 = __toESM(require("fs"), 1);
188
+ var import_path5 = __toESM(require("path"), 1);
192
189
 
193
190
  // src/project-manager.ts
194
191
  init_cjs_shims();
@@ -461,6 +458,9 @@ var SymbolIndex = class {
461
458
  const memoryUsage = process.memoryUsage().heapUsed / 1024 / 1024;
462
459
  return this.computeStats(cache, duration, Math.round(memoryUsage));
463
460
  }
461
+ isInitialized() {
462
+ return Object.keys(this.index).length > 0;
463
+ }
464
464
  lookup(name) {
465
465
  return this.index[name] || [];
466
466
  }
@@ -551,8 +551,23 @@ var TypeScriptAdapter = class {
551
551
  const poolSize = parseInt(process.env.AST_WORKER_POOL_SIZE || "2");
552
552
  this.pool = new WorkerPool(poolSize);
553
553
  }
554
- async resolveDefinition(symbolName, path5) {
555
- validateWorkspacePath(path5);
554
+ async ensureIndex(p) {
555
+ if (!symbolIndex.isInitialized()) {
556
+ const absolutePath = import_path5.default.resolve(p);
557
+ let searchPath = absolutePath;
558
+ if (!import_fs3.default.statSync(absolutePath).isDirectory()) {
559
+ const tsconfig = await projectManager.findNearestTsConfig(absolutePath);
560
+ searchPath = tsconfig ? import_path5.default.dirname(tsconfig) : import_path5.default.dirname(absolutePath);
561
+ }
562
+ console.error(
563
+ `[AST] Index not initialized. Building for ${searchPath}...`
564
+ );
565
+ await symbolIndex.buildIndex(searchPath);
566
+ }
567
+ }
568
+ async resolveDefinition(symbolName, path6) {
569
+ validateWorkspacePath(path6);
570
+ await this.ensureIndex(path6);
556
571
  const indexHits = symbolIndex.lookup(symbolName);
557
572
  if (indexHits.length > 0) {
558
573
  const results = [];
@@ -580,32 +595,33 @@ var TypeScriptAdapter = class {
580
595
  }
581
596
  return results;
582
597
  }
583
- if (import_fs3.default.statSync(path5).isDirectory()) {
598
+ if (import_fs3.default.statSync(path6).isDirectory()) {
584
599
  return [];
585
600
  }
586
- const tsconfig = await projectManager.findNearestTsConfig(path5);
601
+ const tsconfig = await projectManager.findNearestTsConfig(path6);
587
602
  if (!tsconfig) return [];
588
603
  try {
589
604
  const result = await this.pool.execute(
590
605
  "resolve_definition",
591
606
  {
592
607
  tsconfig,
593
- file: path5,
608
+ file: path6,
594
609
  symbol: symbolName
595
610
  }
596
611
  );
597
612
  return result;
598
613
  } catch {
599
614
  const project = projectManager.ensureProject(tsconfig);
600
- const sourceFile = project.addSourceFileAtPathIfExists(path5);
615
+ const sourceFile = project.addSourceFileAtPathIfExists(path6);
601
616
  if (!sourceFile) return [];
602
617
  const exported = sourceFile.getExportedDeclarations().get(symbolName);
603
618
  if (!exported) return [];
604
619
  return exported.map((decl) => this.mapToDefinitionLocation(decl));
605
620
  }
606
621
  }
607
- async findReferences(symbolName, path5, limit = 50, offset = 0) {
608
- validateWorkspacePath(path5);
622
+ async findReferences(symbolName, path6, limit = 50, offset = 0) {
623
+ validateWorkspacePath(path6);
624
+ await this.ensureIndex(path6);
609
625
  const hits = symbolIndex.lookup(symbolName);
610
626
  if (hits.length === 0) return { references: [], total_count: 0 };
611
627
  const hit = hits[0];
@@ -622,7 +638,7 @@ var TypeScriptAdapter = class {
622
638
  const { searchCode: searchCode2 } = await Promise.resolve().then(() => (init_search_code(), search_code_exports));
623
639
  const searchResults = await searchCode2(
624
640
  symbolName,
625
- path5,
641
+ path6,
626
642
  "*.{ts,tsx,js,jsx}",
627
643
  1e3,
628
644
  false
@@ -633,7 +649,7 @@ var TypeScriptAdapter = class {
633
649
  }
634
650
  } catch (_e) {
635
651
  }
636
- const refSymbols = targetNode.findReferences?.();
652
+ const refSymbols = "findReferences" in targetNode && typeof targetNode.findReferences === "function" ? targetNode.findReferences() : void 0;
637
653
  if (!refSymbols) return { references: [], total_count: 0 };
638
654
  const results = [];
639
655
  for (const refSymbol of refSymbols) {
@@ -654,8 +670,9 @@ var TypeScriptAdapter = class {
654
670
  total_count: unique.length
655
671
  };
656
672
  }
657
- async findImplementations(symbolName, path5, limit = 50, offset = 0) {
658
- validateWorkspacePath(path5);
673
+ async findImplementations(symbolName, path6, limit = 50, offset = 0) {
674
+ validateWorkspacePath(path6);
675
+ await this.ensureIndex(path6);
659
676
  const hits = symbolIndex.lookup(symbolName);
660
677
  if (hits.length === 0) return { implementations: [], total_count: 0 };
661
678
  const hit = hits[0];
@@ -686,7 +703,7 @@ var TypeScriptAdapter = class {
686
703
  const { searchCode: searchCode2 } = await Promise.resolve().then(() => (init_search_code(), search_code_exports));
687
704
  const searchResults = await searchCode2(
688
705
  symbolName,
689
- path5,
706
+ path6,
690
707
  "*.{ts,tsx,js,jsx}",
691
708
  1e3,
692
709
  false
@@ -698,7 +715,7 @@ var TypeScriptAdapter = class {
698
715
  } catch (_e) {
699
716
  }
700
717
  const results = [];
701
- const implementations = targetNode.getImplementations?.();
718
+ const implementations = "getImplementations" in targetNode && typeof targetNode.getImplementations === "function" ? targetNode.getImplementations() : void 0;
702
719
  if (implementations) {
703
720
  for (const impl of implementations) {
704
721
  const sf = impl.getSourceFile();
@@ -810,42 +827,46 @@ var TypeScriptAdapter = class {
810
827
  }
811
828
  mapToFunctionInfo(fn) {
812
829
  return {
813
- name: fn.getName() || "anonymous",
830
+ name: fn?.getName?.() || "anonymous",
814
831
  ...this.getSymbolDocs(fn),
815
- params: fn.getParameters().map((p) => ({
832
+ params: (fn?.getParameters?.() || []).map((p) => ({
816
833
  name: p.getName(),
817
834
  type: p.getType().getText()
818
835
  })),
819
- returnType: fn.getReturnType().getText()
836
+ returnType: fn?.getReturnType?.()?.getText?.() || "unknown"
820
837
  };
821
838
  }
822
839
  mapToPropertyInfo(p) {
823
840
  return {
824
- name: p.getName(),
825
- type: p.getType().getText(),
841
+ name: p?.getName?.() || "unknown",
842
+ type: p?.getType?.()?.getText?.() || "unknown",
826
843
  ...this.getSymbolDocs(p)
827
844
  };
828
845
  }
829
846
  mapToInterfaceInfo(itf) {
830
847
  return {
831
- name: itf.getName(),
848
+ name: itf?.getName?.() || "unknown",
832
849
  ...this.getSymbolDocs(itf),
833
- properties: itf.getProperties().map((p) => this.mapToPropertyInfo(p)),
834
- methods: itf.getMethods().map((m) => this.mapToFunctionInfo(m))
850
+ properties: (itf.getProperties?.() || []).map(
851
+ (p) => this.mapToPropertyInfo(p)
852
+ ) || [],
853
+ methods: (itf?.getMethods?.() || []).map(
854
+ (m) => this.mapToFunctionInfo(m)
855
+ ) || []
835
856
  };
836
857
  }
837
858
  mapToTypeAliasInfo(ta) {
838
859
  return {
839
- name: ta.getName(),
840
- type: ta.getType().getText(),
860
+ name: ta?.getName?.() || "unknown",
861
+ type: ta?.getType?.()?.getText?.() || "unknown",
841
862
  ...this.getSymbolDocs(ta)
842
863
  };
843
864
  }
844
865
  mapToEnumInfo(enm) {
845
866
  return {
846
- name: enm.getName(),
867
+ name: enm?.getName?.() || "unknown",
847
868
  ...this.getSymbolDocs(enm),
848
- members: enm.getMembers().map((m) => m.getName())
869
+ members: (enm?.getMembers?.() || []).map((m) => m.getName()) || []
849
870
  };
850
871
  }
851
872
  deduplicateLocations(locations) {
@@ -861,22 +882,22 @@ var TypeScriptAdapter = class {
861
882
  var typescriptAdapter = new TypeScriptAdapter();
862
883
 
863
884
  // src/tools/resolve-definition.ts
864
- async function resolveDefinition(symbol, path5) {
865
- return await typescriptAdapter.resolveDefinition(symbol, path5);
885
+ async function resolveDefinition(symbol, path6) {
886
+ return await typescriptAdapter.resolveDefinition(symbol, path6);
866
887
  }
867
888
 
868
889
  // src/tools/find-references.ts
869
890
  init_cjs_shims();
870
- async function findReferences(symbol, path5, limit = 50, offset = 0) {
871
- return await typescriptAdapter.findReferences(symbol, path5, limit, offset);
891
+ async function findReferences(symbol, path6, limit = 50, offset = 0) {
892
+ return await typescriptAdapter.findReferences(symbol, path6, limit, offset);
872
893
  }
873
894
 
874
895
  // src/tools/find-implementations.ts
875
896
  init_cjs_shims();
876
- async function findImplementations(symbol, path5, limit = 50, offset = 0) {
897
+ async function findImplementations(symbol, path6, limit = 50, offset = 0) {
877
898
  return await typescriptAdapter.findImplementations(
878
899
  symbol,
879
- path5,
900
+ path6,
880
901
  limit,
881
902
  offset
882
903
  );
@@ -923,8 +944,8 @@ async function getSymbolDocs(symbol, filePath) {
923
944
 
924
945
  // src/tools/build-symbol-index.ts
925
946
  init_cjs_shims();
926
- async function buildSymbolIndex(path5) {
927
- return await symbolIndex.buildIndex(path5);
947
+ async function buildSymbolIndex(path6) {
948
+ return await symbolIndex.buildIndex(path6);
928
949
  }
929
950
 
930
951
  // src/index.ts
@@ -1019,6 +1040,7 @@ var ASTExplorerServer = class {
1019
1040
  path: { type: "string", description: "Directory to search" },
1020
1041
  filePattern: { type: "string", description: "Glob filter" },
1021
1042
  limit: { type: "number", default: 50 },
1043
+ offset: { type: "number", default: 0 },
1022
1044
  regex: { type: "boolean", default: true }
1023
1045
  },
1024
1046
  required: ["pattern", "path"]
@@ -1055,8 +1077,8 @@ var ASTExplorerServer = class {
1055
1077
  try {
1056
1078
  switch (name) {
1057
1079
  case "resolve_definition": {
1058
- const { symbol, path: path5 } = ResolveDefinitionSchema.parse(args);
1059
- const results = await resolveDefinition(symbol, path5);
1080
+ const { symbol, path: path6 } = ResolveDefinitionSchema.parse(args);
1081
+ const results = await resolveDefinition(symbol, path6);
1060
1082
  return {
1061
1083
  content: [
1062
1084
  { type: "text", text: JSON.stringify(results, null, 2) }
@@ -1064,8 +1086,8 @@ var ASTExplorerServer = class {
1064
1086
  };
1065
1087
  }
1066
1088
  case "find_references": {
1067
- const { symbol, path: path5, limit, offset } = FindReferencesSchema.parse(args);
1068
- const results = await findReferences(symbol, path5, limit, offset);
1089
+ const { symbol, path: path6, limit, offset } = FindReferencesSchema.parse(args);
1090
+ const results = await findReferences(symbol, path6, limit, offset);
1069
1091
  return {
1070
1092
  content: [
1071
1093
  { type: "text", text: JSON.stringify(results, null, 2) }
@@ -1073,10 +1095,10 @@ var ASTExplorerServer = class {
1073
1095
  };
1074
1096
  }
1075
1097
  case "find_implementations": {
1076
- const { symbol, path: path5, limit, offset } = FindImplementationsSchema.parse(args);
1098
+ const { symbol, path: path6, limit, offset } = FindImplementationsSchema.parse(args);
1077
1099
  const results = await findImplementations(
1078
1100
  symbol,
1079
- path5,
1101
+ path6,
1080
1102
  limit,
1081
1103
  offset
1082
1104
  );
@@ -1096,13 +1118,14 @@ var ASTExplorerServer = class {
1096
1118
  };
1097
1119
  }
1098
1120
  case "search_code": {
1099
- const { pattern, path: path5, filePattern, limit, regex } = SearchCodeSchema.parse(args);
1121
+ const { pattern, path: path6, filePattern, limit, offset, regex } = SearchCodeSchema.parse(args);
1100
1122
  const results = await searchCode(
1101
1123
  pattern,
1102
- path5,
1124
+ path6,
1103
1125
  filePattern,
1104
1126
  limit,
1105
- regex
1127
+ regex,
1128
+ offset
1106
1129
  );
1107
1130
  return {
1108
1131
  content: [
@@ -1111,15 +1134,15 @@ var ASTExplorerServer = class {
1111
1134
  };
1112
1135
  }
1113
1136
  case "get_symbol_docs": {
1114
- const { symbol, path: path5 } = GetSymbolDocsSchema.parse(args);
1115
- const docs = await getSymbolDocs(symbol, path5);
1137
+ const { symbol, path: path6 } = GetSymbolDocsSchema.parse(args);
1138
+ const docs = await getSymbolDocs(symbol, path6);
1116
1139
  return {
1117
1140
  content: [{ type: "text", text: JSON.stringify(docs, null, 2) }]
1118
1141
  };
1119
1142
  }
1120
1143
  case "build_symbol_index": {
1121
- const { path: path5 } = BuildSymbolIndexSchema.parse(args);
1122
- const stats = await buildSymbolIndex(path5);
1144
+ const { path: path6 } = BuildSymbolIndexSchema.parse(args);
1145
+ const stats = await buildSymbolIndex(path6);
1123
1146
  return {
1124
1147
  content: [{ type: "text", text: JSON.stringify(stats, null, 2) }]
1125
1148
  };