@aiready/ast-mcp-server 0.1.6 → 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.
@@ -27,15 +27,9 @@ function validateWorkspacePath(inputPath) {
27
27
 
28
28
  // src/tools/search-code.ts
29
29
  var execFileAsync = promisify(execFile);
30
- async function searchCode(pattern, searchPath, filePattern, limit = 50, regex = true) {
30
+ async function searchCode(pattern, searchPath, filePattern, limit = 50, regex = true, offset = 0) {
31
31
  const safePath = validateWorkspacePath(searchPath);
32
- const args = [
33
- "--json",
34
- "--max-count",
35
- limit.toString(),
36
- "--max-columns",
37
- "500"
38
- ];
32
+ const args = ["--json", "--max-columns", "500"];
39
33
  if (!regex) {
40
34
  args.push("--fixed-strings");
41
35
  }
@@ -66,7 +60,7 @@ async function searchCode(pattern, searchPath, filePattern, limit = 50, regex =
66
60
  }
67
61
  }
68
62
  }
69
- return results.slice(0, limit);
63
+ return results.slice(offset, offset + limit);
70
64
  } catch (error) {
71
65
  if (error.code === 1) {
72
66
  return [];
@@ -79,4 +73,4 @@ export {
79
73
  validateWorkspacePath,
80
74
  searchCode
81
75
  };
82
- //# sourceMappingURL=chunk-KTDNJP4B.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
@@ -73,15 +73,9 @@ var search_code_exports = {};
73
73
  __export(search_code_exports, {
74
74
  searchCode: () => searchCode
75
75
  });
76
- async function searchCode(pattern, searchPath, filePattern, limit = 50, regex = true) {
76
+ async function searchCode(pattern, searchPath, filePattern, limit = 50, regex = true, offset = 0) {
77
77
  const safePath = validateWorkspacePath(searchPath);
78
- const args = [
79
- "--json",
80
- "--max-count",
81
- limit.toString(),
82
- "--max-columns",
83
- "500"
84
- ];
78
+ const args = ["--json", "--max-columns", "500"];
85
79
  if (!regex) {
86
80
  args.push("--fixed-strings");
87
81
  }
@@ -112,7 +106,7 @@ async function searchCode(pattern, searchPath, filePattern, limit = 50, regex =
112
106
  }
113
107
  }
114
108
  }
115
- return results.slice(0, limit);
109
+ return results.slice(offset, offset + limit);
116
110
  } catch (error) {
117
111
  if (error.code === 1) {
118
112
  return [];
@@ -172,7 +166,8 @@ var SearchCodeSchema = import_zod.z.object({
172
166
  pattern: import_zod.z.string().describe("Search pattern (regex by default)"),
173
167
  path: import_zod.z.string().describe("Directory to search in"),
174
168
  filePattern: import_zod.z.string().optional().describe('Glob filter (e.g., "*.ts")'),
175
- 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)"),
176
171
  regex: import_zod.z.boolean().optional().default(true).describe("Use regex mode (default true)")
177
172
  });
178
173
  var GetSymbolDocsSchema = import_zod.z.object({
@@ -190,6 +185,7 @@ init_cjs_shims();
190
185
  init_cjs_shims();
191
186
  var import_ts_morph3 = require("ts-morph");
192
187
  var import_fs3 = __toESM(require("fs"), 1);
188
+ var import_path5 = __toESM(require("path"), 1);
193
189
 
194
190
  // src/project-manager.ts
195
191
  init_cjs_shims();
@@ -462,6 +458,9 @@ var SymbolIndex = class {
462
458
  const memoryUsage = process.memoryUsage().heapUsed / 1024 / 1024;
463
459
  return this.computeStats(cache, duration, Math.round(memoryUsage));
464
460
  }
461
+ isInitialized() {
462
+ return Object.keys(this.index).length > 0;
463
+ }
465
464
  lookup(name) {
466
465
  return this.index[name] || [];
467
466
  }
@@ -552,8 +551,23 @@ var TypeScriptAdapter = class {
552
551
  const poolSize = parseInt(process.env.AST_WORKER_POOL_SIZE || "2");
553
552
  this.pool = new WorkerPool(poolSize);
554
553
  }
555
- async resolveDefinition(symbolName, path5) {
556
- 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);
557
571
  const indexHits = symbolIndex.lookup(symbolName);
558
572
  if (indexHits.length > 0) {
559
573
  const results = [];
@@ -581,32 +595,33 @@ var TypeScriptAdapter = class {
581
595
  }
582
596
  return results;
583
597
  }
584
- if (import_fs3.default.statSync(path5).isDirectory()) {
598
+ if (import_fs3.default.statSync(path6).isDirectory()) {
585
599
  return [];
586
600
  }
587
- const tsconfig = await projectManager.findNearestTsConfig(path5);
601
+ const tsconfig = await projectManager.findNearestTsConfig(path6);
588
602
  if (!tsconfig) return [];
589
603
  try {
590
604
  const result = await this.pool.execute(
591
605
  "resolve_definition",
592
606
  {
593
607
  tsconfig,
594
- file: path5,
608
+ file: path6,
595
609
  symbol: symbolName
596
610
  }
597
611
  );
598
612
  return result;
599
613
  } catch {
600
614
  const project = projectManager.ensureProject(tsconfig);
601
- const sourceFile = project.addSourceFileAtPathIfExists(path5);
615
+ const sourceFile = project.addSourceFileAtPathIfExists(path6);
602
616
  if (!sourceFile) return [];
603
617
  const exported = sourceFile.getExportedDeclarations().get(symbolName);
604
618
  if (!exported) return [];
605
619
  return exported.map((decl) => this.mapToDefinitionLocation(decl));
606
620
  }
607
621
  }
608
- async findReferences(symbolName, path5, limit = 50, offset = 0) {
609
- validateWorkspacePath(path5);
622
+ async findReferences(symbolName, path6, limit = 50, offset = 0) {
623
+ validateWorkspacePath(path6);
624
+ await this.ensureIndex(path6);
610
625
  const hits = symbolIndex.lookup(symbolName);
611
626
  if (hits.length === 0) return { references: [], total_count: 0 };
612
627
  const hit = hits[0];
@@ -623,7 +638,7 @@ var TypeScriptAdapter = class {
623
638
  const { searchCode: searchCode2 } = await Promise.resolve().then(() => (init_search_code(), search_code_exports));
624
639
  const searchResults = await searchCode2(
625
640
  symbolName,
626
- path5,
641
+ path6,
627
642
  "*.{ts,tsx,js,jsx}",
628
643
  1e3,
629
644
  false
@@ -655,8 +670,9 @@ var TypeScriptAdapter = class {
655
670
  total_count: unique.length
656
671
  };
657
672
  }
658
- async findImplementations(symbolName, path5, limit = 50, offset = 0) {
659
- validateWorkspacePath(path5);
673
+ async findImplementations(symbolName, path6, limit = 50, offset = 0) {
674
+ validateWorkspacePath(path6);
675
+ await this.ensureIndex(path6);
660
676
  const hits = symbolIndex.lookup(symbolName);
661
677
  if (hits.length === 0) return { implementations: [], total_count: 0 };
662
678
  const hit = hits[0];
@@ -687,7 +703,7 @@ var TypeScriptAdapter = class {
687
703
  const { searchCode: searchCode2 } = await Promise.resolve().then(() => (init_search_code(), search_code_exports));
688
704
  const searchResults = await searchCode2(
689
705
  symbolName,
690
- path5,
706
+ path6,
691
707
  "*.{ts,tsx,js,jsx}",
692
708
  1e3,
693
709
  false
@@ -866,22 +882,22 @@ var TypeScriptAdapter = class {
866
882
  var typescriptAdapter = new TypeScriptAdapter();
867
883
 
868
884
  // src/tools/resolve-definition.ts
869
- async function resolveDefinition(symbol, path5) {
870
- return await typescriptAdapter.resolveDefinition(symbol, path5);
885
+ async function resolveDefinition(symbol, path6) {
886
+ return await typescriptAdapter.resolveDefinition(symbol, path6);
871
887
  }
872
888
 
873
889
  // src/tools/find-references.ts
874
890
  init_cjs_shims();
875
- async function findReferences(symbol, path5, limit = 50, offset = 0) {
876
- 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);
877
893
  }
878
894
 
879
895
  // src/tools/find-implementations.ts
880
896
  init_cjs_shims();
881
- async function findImplementations(symbol, path5, limit = 50, offset = 0) {
897
+ async function findImplementations(symbol, path6, limit = 50, offset = 0) {
882
898
  return await typescriptAdapter.findImplementations(
883
899
  symbol,
884
- path5,
900
+ path6,
885
901
  limit,
886
902
  offset
887
903
  );
@@ -928,8 +944,8 @@ async function getSymbolDocs(symbol, filePath) {
928
944
 
929
945
  // src/tools/build-symbol-index.ts
930
946
  init_cjs_shims();
931
- async function buildSymbolIndex(path5) {
932
- return await symbolIndex.buildIndex(path5);
947
+ async function buildSymbolIndex(path6) {
948
+ return await symbolIndex.buildIndex(path6);
933
949
  }
934
950
 
935
951
  // src/index.ts
@@ -1024,6 +1040,7 @@ var ASTExplorerServer = class {
1024
1040
  path: { type: "string", description: "Directory to search" },
1025
1041
  filePattern: { type: "string", description: "Glob filter" },
1026
1042
  limit: { type: "number", default: 50 },
1043
+ offset: { type: "number", default: 0 },
1027
1044
  regex: { type: "boolean", default: true }
1028
1045
  },
1029
1046
  required: ["pattern", "path"]
@@ -1060,8 +1077,8 @@ var ASTExplorerServer = class {
1060
1077
  try {
1061
1078
  switch (name) {
1062
1079
  case "resolve_definition": {
1063
- const { symbol, path: path5 } = ResolveDefinitionSchema.parse(args);
1064
- const results = await resolveDefinition(symbol, path5);
1080
+ const { symbol, path: path6 } = ResolveDefinitionSchema.parse(args);
1081
+ const results = await resolveDefinition(symbol, path6);
1065
1082
  return {
1066
1083
  content: [
1067
1084
  { type: "text", text: JSON.stringify(results, null, 2) }
@@ -1069,8 +1086,8 @@ var ASTExplorerServer = class {
1069
1086
  };
1070
1087
  }
1071
1088
  case "find_references": {
1072
- const { symbol, path: path5, limit, offset } = FindReferencesSchema.parse(args);
1073
- 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);
1074
1091
  return {
1075
1092
  content: [
1076
1093
  { type: "text", text: JSON.stringify(results, null, 2) }
@@ -1078,10 +1095,10 @@ var ASTExplorerServer = class {
1078
1095
  };
1079
1096
  }
1080
1097
  case "find_implementations": {
1081
- const { symbol, path: path5, limit, offset } = FindImplementationsSchema.parse(args);
1098
+ const { symbol, path: path6, limit, offset } = FindImplementationsSchema.parse(args);
1082
1099
  const results = await findImplementations(
1083
1100
  symbol,
1084
- path5,
1101
+ path6,
1085
1102
  limit,
1086
1103
  offset
1087
1104
  );
@@ -1101,13 +1118,14 @@ var ASTExplorerServer = class {
1101
1118
  };
1102
1119
  }
1103
1120
  case "search_code": {
1104
- const { pattern, path: path5, filePattern, limit, regex } = SearchCodeSchema.parse(args);
1121
+ const { pattern, path: path6, filePattern, limit, offset, regex } = SearchCodeSchema.parse(args);
1105
1122
  const results = await searchCode(
1106
1123
  pattern,
1107
- path5,
1124
+ path6,
1108
1125
  filePattern,
1109
1126
  limit,
1110
- regex
1127
+ regex,
1128
+ offset
1111
1129
  );
1112
1130
  return {
1113
1131
  content: [
@@ -1116,15 +1134,15 @@ var ASTExplorerServer = class {
1116
1134
  };
1117
1135
  }
1118
1136
  case "get_symbol_docs": {
1119
- const { symbol, path: path5 } = GetSymbolDocsSchema.parse(args);
1120
- const docs = await getSymbolDocs(symbol, path5);
1137
+ const { symbol, path: path6 } = GetSymbolDocsSchema.parse(args);
1138
+ const docs = await getSymbolDocs(symbol, path6);
1121
1139
  return {
1122
1140
  content: [{ type: "text", text: JSON.stringify(docs, null, 2) }]
1123
1141
  };
1124
1142
  }
1125
1143
  case "build_symbol_index": {
1126
- const { path: path5 } = BuildSymbolIndexSchema.parse(args);
1127
- const stats = await buildSymbolIndex(path5);
1144
+ const { path: path6 } = BuildSymbolIndexSchema.parse(args);
1145
+ const stats = await buildSymbolIndex(path6);
1128
1146
  return {
1129
1147
  content: [{ type: "text", text: JSON.stringify(stats, null, 2) }]
1130
1148
  };