archtracker-mcp 0.2.0 → 0.3.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/dist/mcp/index.js CHANGED
@@ -1,10 +1,3 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
1
  // src/mcp/index.ts
9
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -173,6 +166,514 @@ function detectCycles(edges) {
173
166
  return cycles;
174
167
  }
175
168
 
169
+ // src/analyzer/engines/strip-comments.ts
170
+ function stripComments(content, style) {
171
+ switch (style) {
172
+ case "c-style":
173
+ return stripCStyle(content);
174
+ case "hash":
175
+ return stripHash(content);
176
+ case "python":
177
+ return stripPython(content);
178
+ case "ruby":
179
+ return stripRuby(content);
180
+ case "php":
181
+ return stripPhp(content);
182
+ default: {
183
+ const _exhaustive = style;
184
+ throw new Error(`Unknown comment style: ${_exhaustive}`);
185
+ }
186
+ }
187
+ }
188
+ function stripCStyle(content) {
189
+ let result = "";
190
+ let i = 0;
191
+ while (i < content.length) {
192
+ if (content[i] === "r" && i + 1 < content.length) {
193
+ let hashes = 0;
194
+ let j = i + 1;
195
+ while (j < content.length && content[j] === "#") {
196
+ hashes++;
197
+ j++;
198
+ }
199
+ if (j < content.length && content[j] === '"') {
200
+ for (let k = i; k <= j; k++) {
201
+ result += " ";
202
+ }
203
+ i = j + 1;
204
+ while (i < content.length) {
205
+ if (content[i] === '"') {
206
+ let matchHashes = 0;
207
+ let m = i + 1;
208
+ while (m < content.length && content[m] === "#" && matchHashes < hashes) {
209
+ matchHashes++;
210
+ m++;
211
+ }
212
+ if (matchHashes === hashes) {
213
+ for (let k = i; k < m; k++) {
214
+ result += " ";
215
+ }
216
+ i = m;
217
+ break;
218
+ }
219
+ }
220
+ result += content[i] === "\n" ? "\n" : " ";
221
+ i++;
222
+ }
223
+ continue;
224
+ }
225
+ }
226
+ if (content[i] === "/" && content[i + 1] === "/") {
227
+ while (i < content.length && content[i] !== "\n") {
228
+ result += " ";
229
+ i++;
230
+ }
231
+ } else if (content[i] === "/" && content[i + 1] === "*") {
232
+ result += " ";
233
+ i++;
234
+ result += " ";
235
+ i++;
236
+ while (i < content.length) {
237
+ if (content[i] === "*" && content[i + 1] === "/") {
238
+ result += " ";
239
+ i++;
240
+ result += " ";
241
+ i++;
242
+ break;
243
+ }
244
+ result += content[i] === "\n" ? "\n" : " ";
245
+ i++;
246
+ }
247
+ } else if (content[i] === '"') {
248
+ result += content[i];
249
+ i++;
250
+ while (i < content.length && content[i] !== '"') {
251
+ if (content[i] === "\\" && i + 1 < content.length) {
252
+ result += content[i];
253
+ i++;
254
+ result += content[i];
255
+ i++;
256
+ } else if (content[i] === "\n") {
257
+ result += "\n";
258
+ i++;
259
+ } else {
260
+ result += content[i];
261
+ i++;
262
+ }
263
+ }
264
+ if (i < content.length) {
265
+ result += content[i];
266
+ i++;
267
+ }
268
+ } else if (content[i] === "'") {
269
+ result += content[i];
270
+ i++;
271
+ while (i < content.length && content[i] !== "'") {
272
+ if (content[i] === "\\" && i + 1 < content.length) {
273
+ result += content[i];
274
+ i++;
275
+ result += content[i];
276
+ i++;
277
+ } else {
278
+ result += content[i];
279
+ i++;
280
+ }
281
+ }
282
+ if (i < content.length) {
283
+ result += content[i];
284
+ i++;
285
+ }
286
+ } else if (content[i] === "`") {
287
+ result += " ";
288
+ i++;
289
+ while (i < content.length && content[i] !== "`") {
290
+ result += content[i] === "\n" ? "\n" : " ";
291
+ i++;
292
+ }
293
+ if (i < content.length) {
294
+ result += " ";
295
+ i++;
296
+ }
297
+ } else {
298
+ result += content[i];
299
+ i++;
300
+ }
301
+ }
302
+ return result;
303
+ }
304
+ function stripHash(content) {
305
+ let result = "";
306
+ let i = 0;
307
+ while (i < content.length) {
308
+ if (content[i] === "#") {
309
+ while (i < content.length && content[i] !== "\n") {
310
+ result += " ";
311
+ i++;
312
+ }
313
+ } else if (content[i] === '"') {
314
+ result += content[i];
315
+ i++;
316
+ while (i < content.length && content[i] !== '"') {
317
+ if (content[i] === "\\" && i + 1 < content.length) {
318
+ result += content[i++];
319
+ result += content[i++];
320
+ } else {
321
+ result += content[i++];
322
+ }
323
+ }
324
+ if (i < content.length) {
325
+ result += content[i];
326
+ i++;
327
+ }
328
+ } else if (content[i] === "'") {
329
+ result += content[i];
330
+ i++;
331
+ while (i < content.length && content[i] !== "'") {
332
+ if (content[i] === "\\" && i + 1 < content.length) {
333
+ result += content[i++];
334
+ result += content[i++];
335
+ } else {
336
+ result += content[i++];
337
+ }
338
+ }
339
+ if (i < content.length) {
340
+ result += content[i];
341
+ i++;
342
+ }
343
+ } else {
344
+ result += content[i];
345
+ i++;
346
+ }
347
+ }
348
+ return result;
349
+ }
350
+ function stripPython(content) {
351
+ let result = "";
352
+ let i = 0;
353
+ while (i < content.length) {
354
+ let prefixLen = 0;
355
+ let isRaw = false;
356
+ if (i < content.length) {
357
+ const c0 = content[i];
358
+ const c1 = i + 1 < content.length ? content[i + 1] : "";
359
+ const c2 = i + 2 < content.length ? content[i + 2] : "";
360
+ if ((c0 === "r" || c0 === "R" || c0 === "b" || c0 === "B" || c0 === "f" || c0 === "F") && (c1 === "r" || c1 === "R" || c1 === "b" || c1 === "B" || c1 === "f" || c1 === "F") && (c2 === '"' || c2 === "'")) {
361
+ const pair = (c0 + c1).toLowerCase();
362
+ if (pair === "rb" || pair === "br" || pair === "fr" || pair === "rf") {
363
+ prefixLen = 2;
364
+ isRaw = pair.includes("r");
365
+ }
366
+ }
367
+ if (prefixLen === 0 && (c0 === "r" || c0 === "R" || c0 === "b" || c0 === "B" || c0 === "f" || c0 === "F") && (c1 === '"' || c1 === "'")) {
368
+ prefixLen = 1;
369
+ isRaw = c0 === "r" || c0 === "R";
370
+ }
371
+ }
372
+ const quoteStart = i + prefixLen;
373
+ if (quoteStart + 2 < content.length && (content[quoteStart] === '"' || content[quoteStart] === "'") && content[quoteStart + 1] === content[quoteStart] && content[quoteStart + 2] === content[quoteStart]) {
374
+ const quote = content[quoteStart];
375
+ for (let k = 0; k < prefixLen; k++) {
376
+ result += " ";
377
+ }
378
+ result += " ";
379
+ i = quoteStart + 3;
380
+ while (i < content.length) {
381
+ if (content[i] === quote && content[i + 1] === quote && content[i + 2] === quote) {
382
+ result += " ";
383
+ i += 3;
384
+ break;
385
+ }
386
+ if (!isRaw && content[i] === "\\" && i + 1 < content.length) {
387
+ result += " ";
388
+ i++;
389
+ result += content[i] === "\n" ? "\n" : " ";
390
+ i++;
391
+ } else {
392
+ result += content[i] === "\n" ? "\n" : " ";
393
+ i++;
394
+ }
395
+ }
396
+ } else if (prefixLen === 0 && content[i] === "#") {
397
+ while (i < content.length && content[i] !== "\n") {
398
+ result += " ";
399
+ i++;
400
+ }
401
+ } else if (quoteStart < content.length && (content[quoteStart] === '"' || content[quoteStart] === "'") && (prefixLen > 0 || content[i] === '"' || content[i] === "'")) {
402
+ const quote = content[quoteStart];
403
+ for (let k = i; k < quoteStart; k++) {
404
+ result += content[k];
405
+ }
406
+ result += content[quoteStart];
407
+ i = quoteStart + 1;
408
+ while (i < content.length && content[i] !== quote) {
409
+ if (!isRaw && content[i] === "\\" && i + 1 < content.length) {
410
+ result += content[i++];
411
+ result += content[i++];
412
+ } else if (content[i] === "\n") {
413
+ result += "\n";
414
+ i++;
415
+ break;
416
+ } else {
417
+ result += content[i++];
418
+ }
419
+ }
420
+ if (i < content.length && content[i] === quote) {
421
+ result += content[i];
422
+ i++;
423
+ }
424
+ } else {
425
+ result += content[i];
426
+ i++;
427
+ }
428
+ }
429
+ return result;
430
+ }
431
+ function stripRuby(content) {
432
+ const lines = content.split("\n");
433
+ const result = [];
434
+ let inBlock = false;
435
+ for (const line of lines) {
436
+ if (!inBlock && /^=begin(\s|$)/.test(line)) {
437
+ inBlock = true;
438
+ result.push(" ".repeat(line.length));
439
+ continue;
440
+ }
441
+ if (inBlock) {
442
+ if (/^=end(\s|$)/.test(line)) {
443
+ inBlock = false;
444
+ }
445
+ result.push(" ".repeat(line.length));
446
+ continue;
447
+ }
448
+ let processed = "";
449
+ let i = 0;
450
+ while (i < line.length) {
451
+ if (line[i] === "#") {
452
+ processed += " ".repeat(line.length - i);
453
+ break;
454
+ } else if (line[i] === '"') {
455
+ processed += line[i];
456
+ i++;
457
+ processed = scanRubyDoubleQuotedString(line, i, processed);
458
+ i = scanRubyDoubleQuotedStringIndex(line, i);
459
+ } else if (line[i] === "'") {
460
+ processed += line[i];
461
+ i++;
462
+ while (i < line.length && line[i] !== "'") {
463
+ if (line[i] === "\\" && i + 1 < line.length) {
464
+ processed += line[i++];
465
+ processed += line[i++];
466
+ } else {
467
+ processed += line[i++];
468
+ }
469
+ }
470
+ if (i < line.length) {
471
+ processed += line[i];
472
+ i++;
473
+ }
474
+ } else {
475
+ processed += line[i];
476
+ i++;
477
+ }
478
+ }
479
+ result.push(processed);
480
+ }
481
+ return result.join("\n");
482
+ }
483
+ function scanRubyDoubleQuotedString(line, startI, processed) {
484
+ let i = startI;
485
+ while (i < line.length && line[i] !== '"') {
486
+ if (line[i] === "\\" && i + 1 < line.length) {
487
+ processed += line[i++];
488
+ processed += line[i++];
489
+ } else if (line[i] === "#" && i + 1 < line.length && line[i + 1] === "{") {
490
+ processed += line[i++];
491
+ processed += line[i++];
492
+ let depth = 1;
493
+ while (i < line.length && depth > 0) {
494
+ if (line[i] === "{") {
495
+ depth++;
496
+ processed += line[i++];
497
+ } else if (line[i] === "}") {
498
+ depth--;
499
+ processed += line[i++];
500
+ } else if (line[i] === "\\" && i + 1 < line.length) {
501
+ processed += line[i++];
502
+ processed += line[i++];
503
+ } else {
504
+ processed += line[i++];
505
+ }
506
+ }
507
+ } else {
508
+ processed += line[i++];
509
+ }
510
+ }
511
+ if (i < line.length) {
512
+ processed += line[i];
513
+ }
514
+ return processed;
515
+ }
516
+ function scanRubyDoubleQuotedStringIndex(line, startI) {
517
+ let i = startI;
518
+ while (i < line.length && line[i] !== '"') {
519
+ if (line[i] === "\\" && i + 1 < line.length) {
520
+ i += 2;
521
+ } else if (line[i] === "#" && i + 1 < line.length && line[i + 1] === "{") {
522
+ i += 2;
523
+ let depth = 1;
524
+ while (i < line.length && depth > 0) {
525
+ if (line[i] === "{") {
526
+ depth++;
527
+ i++;
528
+ } else if (line[i] === "}") {
529
+ depth--;
530
+ i++;
531
+ } else if (line[i] === "\\" && i + 1 < line.length) {
532
+ i += 2;
533
+ } else {
534
+ i++;
535
+ }
536
+ }
537
+ } else {
538
+ i++;
539
+ }
540
+ }
541
+ if (i < line.length) {
542
+ i++;
543
+ }
544
+ return i;
545
+ }
546
+ function stripPhp(content) {
547
+ let result = "";
548
+ let i = 0;
549
+ while (i < content.length) {
550
+ if (content[i] === "<" && content[i + 1] === "<" && content[i + 2] === "<") {
551
+ const heredocStart = i;
552
+ let j = i + 3;
553
+ let isNowdoc = false;
554
+ if (j < content.length && content[j] === "'") {
555
+ isNowdoc = true;
556
+ j++;
557
+ }
558
+ const identStart = j;
559
+ while (j < content.length && /[A-Za-z0-9_]/.test(content[j])) {
560
+ j++;
561
+ }
562
+ const identifier = content.slice(identStart, j);
563
+ if (identifier.length > 0) {
564
+ if (isNowdoc && j < content.length && content[j] === "'") {
565
+ j++;
566
+ }
567
+ let validHeredoc = false;
568
+ let lineEnd = j;
569
+ if (lineEnd < content.length && content[lineEnd] === "\n") {
570
+ validHeredoc = true;
571
+ }
572
+ if (validHeredoc) {
573
+ for (let k = heredocStart; k <= lineEnd; k++) {
574
+ if (content[k] === "\n") {
575
+ result += "\n";
576
+ } else {
577
+ result += content[k];
578
+ }
579
+ }
580
+ i = lineEnd + 1;
581
+ let found = false;
582
+ while (i < content.length && !found) {
583
+ const lineStart = i;
584
+ let lineEndIdx = i;
585
+ while (lineEndIdx < content.length && content[lineEndIdx] !== "\n") {
586
+ lineEndIdx++;
587
+ }
588
+ const currentLine = content.slice(lineStart, lineEndIdx);
589
+ const trimmedLine = currentLine.trimStart();
590
+ if (trimmedLine === identifier || trimmedLine === identifier + ";" || trimmedLine === identifier + "," || trimmedLine === identifier + ");" || trimmedLine === identifier + ")" || trimmedLine.startsWith(identifier + ";") || trimmedLine === identifier) {
591
+ for (let k = lineStart; k < lineEndIdx; k++) {
592
+ result += content[k];
593
+ }
594
+ i = lineEndIdx;
595
+ if (i < content.length && content[i] === "\n") {
596
+ result += "\n";
597
+ i++;
598
+ }
599
+ found = true;
600
+ } else {
601
+ for (let k = lineStart; k < lineEndIdx; k++) {
602
+ result += " ";
603
+ }
604
+ i = lineEndIdx;
605
+ if (i < content.length && content[i] === "\n") {
606
+ result += "\n";
607
+ i++;
608
+ }
609
+ }
610
+ }
611
+ continue;
612
+ }
613
+ }
614
+ result += content[i];
615
+ i++;
616
+ continue;
617
+ }
618
+ if (content[i] === "/" && content[i + 1] === "/" || content[i] === "#") {
619
+ while (i < content.length && content[i] !== "\n") {
620
+ result += " ";
621
+ i++;
622
+ }
623
+ } else if (content[i] === "/" && content[i + 1] === "*") {
624
+ result += " ";
625
+ i++;
626
+ result += " ";
627
+ i++;
628
+ while (i < content.length) {
629
+ if (content[i] === "*" && content[i + 1] === "/") {
630
+ result += " ";
631
+ i++;
632
+ result += " ";
633
+ i++;
634
+ break;
635
+ }
636
+ result += content[i] === "\n" ? "\n" : " ";
637
+ i++;
638
+ }
639
+ } else if (content[i] === '"') {
640
+ result += content[i];
641
+ i++;
642
+ while (i < content.length && content[i] !== '"') {
643
+ if (content[i] === "\\" && i + 1 < content.length) {
644
+ result += content[i++];
645
+ result += content[i++];
646
+ } else {
647
+ result += content[i++];
648
+ }
649
+ }
650
+ if (i < content.length) {
651
+ result += content[i];
652
+ i++;
653
+ }
654
+ } else if (content[i] === "'") {
655
+ result += content[i];
656
+ i++;
657
+ while (i < content.length && content[i] !== "'") {
658
+ if (content[i] === "\\" && i + 1 < content.length) {
659
+ result += content[i++];
660
+ result += content[i++];
661
+ } else {
662
+ result += content[i++];
663
+ }
664
+ }
665
+ if (i < content.length) {
666
+ result += content[i];
667
+ i++;
668
+ }
669
+ } else {
670
+ result += content[i];
671
+ i++;
672
+ }
673
+ }
674
+ return result;
675
+ }
676
+
176
677
  // src/analyzer/engines/regex-engine.ts
177
678
  var RegexEngine = class {
178
679
  constructor(config) {
@@ -186,6 +687,7 @@ var RegexEngine = class {
186
687
  "\\.archtracker"
187
688
  ].map((p) => new RegExp(p));
188
689
  const projectFiles = await this.collectFiles(
690
+ absRootDir,
189
691
  absRootDir,
190
692
  excludePatterns,
191
693
  options.maxDepth ?? 0
@@ -193,6 +695,7 @@ var RegexEngine = class {
193
695
  const projectFileSet = new Set(projectFiles);
194
696
  const files = {};
195
697
  const edges = [];
698
+ const edgeSet = /* @__PURE__ */ new Set();
196
699
  for (const filePath of projectFiles) {
197
700
  const relPath = relative(absRootDir, filePath);
198
701
  files[relPath] = {
@@ -208,9 +711,11 @@ var RegexEngine = class {
208
711
  try {
209
712
  content = await readFile(filePath, "utf-8");
210
713
  } catch {
714
+ if (files[relSource]) files[relSource].exists = false;
211
715
  continue;
212
716
  }
213
- const imports = this.extractImports(content);
717
+ const stripped = stripComments(content, this.config.commentStyle);
718
+ const imports = this.extractImports(stripped);
214
719
  for (const importPath of imports) {
215
720
  const resolved = this.config.resolveImport(
216
721
  importPath,
@@ -221,6 +726,10 @@ var RegexEngine = class {
221
726
  if (!resolved) continue;
222
727
  const relTarget = relative(absRootDir, resolved);
223
728
  if (!files[relTarget]) continue;
729
+ if (relSource === relTarget) continue;
730
+ const edgeKey = `${relSource}\0${relTarget}`;
731
+ if (edgeSet.has(edgeKey)) continue;
732
+ edgeSet.add(edgeKey);
224
733
  edges.push({
225
734
  source: relSource,
226
735
  target: relTarget,
@@ -241,9 +750,13 @@ var RegexEngine = class {
241
750
  };
242
751
  }
243
752
  extractImports(content) {
753
+ if (this.config.extractImports) {
754
+ return this.config.extractImports(content);
755
+ }
244
756
  const imports = [];
245
757
  for (const pattern of this.config.importPatterns) {
246
- const regex = new RegExp(pattern.regex.source, pattern.regex.flags);
758
+ const flags = pattern.regex.flags.includes("g") ? pattern.regex.flags : pattern.regex.flags + "g";
759
+ const regex = new RegExp(pattern.regex.source, flags);
247
760
  let match;
248
761
  while ((match = regex.exec(content)) !== null) {
249
762
  if (match[1]) {
@@ -253,7 +766,7 @@ var RegexEngine = class {
253
766
  }
254
767
  return imports;
255
768
  }
256
- async collectFiles(dir, excludePatterns, maxDepth, currentDepth = 0) {
769
+ async collectFiles(dir, absRootDir, excludePatterns, maxDepth, currentDepth = 0) {
257
770
  if (maxDepth > 0 && currentDepth >= maxDepth) return [];
258
771
  const results = [];
259
772
  let entries;
@@ -264,7 +777,7 @@ var RegexEngine = class {
264
777
  }
265
778
  for (const entry of entries) {
266
779
  const fullPath = join(dir, entry.name);
267
- const relPath = relative(dir, fullPath);
780
+ const relPath = relative(absRootDir, fullPath);
268
781
  if (excludePatterns.some(
269
782
  (p) => p.test(entry.name) || p.test(relPath) || p.test(fullPath)
270
783
  )) {
@@ -274,6 +787,7 @@ var RegexEngine = class {
274
787
  if (entry.name.startsWith(".")) continue;
275
788
  const sub = await this.collectFiles(
276
789
  fullPath,
790
+ absRootDir,
277
791
  excludePatterns,
278
792
  maxDepth,
279
793
  currentDepth + 1
@@ -306,14 +820,21 @@ var MARKERS = [
306
820
  { file: "pom.xml", language: "java" },
307
821
  { file: "build.gradle", language: "java" },
308
822
  { file: "build.gradle.kts", language: "kotlin" },
823
+ { file: "build.sbt", language: "scala" },
824
+ { file: "build.sc", language: "scala" },
309
825
  { file: "Package.swift", language: "swift" },
310
826
  { file: "Gemfile", language: "ruby" },
311
827
  { file: "composer.json", language: "php" },
828
+ { file: "pubspec.yaml", language: "dart" },
312
829
  { file: "CMakeLists.txt", language: "c-cpp" },
313
830
  { file: "Makefile", language: "c-cpp" },
314
831
  { file: "package.json", language: "javascript" },
315
832
  { file: "tsconfig.json", language: "javascript" }
316
833
  ];
834
+ var EXT_MARKERS = [
835
+ [".sln", "c-sharp"],
836
+ [".csproj", "c-sharp"]
837
+ ];
317
838
  var EXT_MAP = {
318
839
  ".ts": "javascript",
319
840
  ".tsx": "javascript",
@@ -335,7 +856,11 @@ var EXT_MAP = {
335
856
  ".php": "php",
336
857
  ".swift": "swift",
337
858
  ".kt": "kotlin",
338
- ".kts": "kotlin"
859
+ ".kts": "kotlin",
860
+ ".cs": "c-sharp",
861
+ ".dart": "dart",
862
+ ".scala": "scala",
863
+ ".sc": "scala"
339
864
  };
340
865
  async function detectLanguage(rootDir) {
341
866
  for (const marker of MARKERS) {
@@ -347,6 +872,16 @@ async function detectLanguage(rootDir) {
347
872
  } catch {
348
873
  }
349
874
  }
875
+ try {
876
+ const topEntries = await readdir2(rootDir, { withFileTypes: true });
877
+ for (const entry of topEntries) {
878
+ if (!entry.isFile()) continue;
879
+ for (const [ext, lang] of EXT_MARKERS) {
880
+ if (entry.name.endsWith(ext)) return lang;
881
+ }
882
+ }
883
+ } catch {
884
+ }
350
885
  const counts = /* @__PURE__ */ new Map();
351
886
  try {
352
887
  await scanExtensions(rootDir, counts, 2, 0);
@@ -391,16 +926,35 @@ async function scanExtensions(dir, counts, maxDepth, currentDepth) {
391
926
  }
392
927
 
393
928
  // src/analyzer/engines/languages.ts
929
+ import { readFileSync } from "fs";
394
930
  import { join as join3, dirname, resolve as resolve3 } from "path";
395
931
  var python = {
396
932
  id: "python",
397
933
  extensions: [".py"],
934
+ commentStyle: "python",
398
935
  importPatterns: [
399
936
  // from package.module import something
400
- { regex: /^from\s+(\.[\w.]*|\w[\w.]*)\s+import\b/gm },
401
- // import package.module
402
- { regex: /^import\s+([\w.]+)/gm }
937
+ { regex: /^from\s+(\.[\w.]*|\w[\w.]*)\s+import\b/gm }
938
+ // import package.module (handled by extractImports for multi-module case)
403
939
  ],
940
+ // Bug #1 fix: custom extractImports to handle `import a, b, c`
941
+ extractImports(content) {
942
+ const imports = [];
943
+ const fromRegex = /^from\s+(\.[\w.]*|\w[\w.]*)\s+import\b/gm;
944
+ let match;
945
+ while ((match = fromRegex.exec(content)) !== null) {
946
+ imports.push(match[1]);
947
+ }
948
+ const importRegex = /^import\s+([\w.]+(?:\s*,\s*[\w.]+)*)/gm;
949
+ while ((match = importRegex.exec(content)) !== null) {
950
+ const modules = match[1].split(",");
951
+ for (const mod of modules) {
952
+ const trimmed = mod.trim();
953
+ if (trimmed) imports.push(trimmed);
954
+ }
955
+ }
956
+ return imports;
957
+ },
404
958
  resolveImport(importPath, sourceFile, rootDir, projectFiles) {
405
959
  if (importPath.startsWith(".")) {
406
960
  const dots = importPath.match(/^\.+/)?.[0].length ?? 1;
@@ -422,12 +976,33 @@ function tryPythonResolve(base, projectFiles) {
422
976
  var rust = {
423
977
  id: "rust",
424
978
  extensions: [".rs"],
425
- importPatterns: [
426
- // use crate::module::item;
427
- { regex: /\buse\s+crate::(\w[\w:]*)/gm },
428
- // mod child;
429
- { regex: /\bmod\s+(\w+)\s*;/gm }
430
- ],
979
+ commentStyle: "c-style",
980
+ importPatterns: [],
981
+ // handled by extractImports
982
+ extractImports(content) {
983
+ const imports = [];
984
+ const modRegex = /\bmod\s+(\w+)\s*;/gm;
985
+ let match;
986
+ while ((match = modRegex.exec(content)) !== null) {
987
+ imports.push(match[1]);
988
+ }
989
+ const useRegex = /\buse\s+crate::([\s\S]*?);/gm;
990
+ while ((match = useRegex.exec(content)) !== null) {
991
+ const body = match[1].trim();
992
+ extractRustUsePaths(body, "", imports);
993
+ }
994
+ const useSuperRegex = /\buse\s+super::([\s\S]*?);/gm;
995
+ while ((match = useSuperRegex.exec(content)) !== null) {
996
+ const body = match[1].trim();
997
+ extractRustUsePaths(body, "", imports, "super");
998
+ }
999
+ const useSelfRegex = /\buse\s+self::([\s\S]*?);/gm;
1000
+ while ((match = useSelfRegex.exec(content)) !== null) {
1001
+ const body = match[1].trim();
1002
+ extractRustUsePaths(body, "", imports, "self");
1003
+ }
1004
+ return imports;
1005
+ },
431
1006
  resolveImport(importPath, sourceFile, rootDir, projectFiles) {
432
1007
  const srcDir = join3(rootDir, "src");
433
1008
  if (!importPath.includes("::")) {
@@ -438,6 +1013,30 @@ var rust = {
438
1013
  if (projectFiles.has(asDir)) return asDir;
439
1014
  return null;
440
1015
  }
1016
+ if (importPath.startsWith("super::")) {
1017
+ const parentDir = dirname(dirname(sourceFile));
1018
+ const segments2 = importPath.slice("super::".length).split("::");
1019
+ for (let i = segments2.length; i > 0; i--) {
1020
+ const path = segments2.slice(0, i).join("/");
1021
+ const asFile = join3(parentDir, path + ".rs");
1022
+ if (projectFiles.has(asFile)) return asFile;
1023
+ const asDir = join3(parentDir, path, "mod.rs");
1024
+ if (projectFiles.has(asDir)) return asDir;
1025
+ }
1026
+ return null;
1027
+ }
1028
+ if (importPath.startsWith("self::")) {
1029
+ const selfDir = dirname(sourceFile);
1030
+ const segments2 = importPath.slice("self::".length).split("::");
1031
+ for (let i = segments2.length; i > 0; i--) {
1032
+ const path = segments2.slice(0, i).join("/");
1033
+ const asFile = join3(selfDir, path + ".rs");
1034
+ if (projectFiles.has(asFile)) return asFile;
1035
+ const asDir = join3(selfDir, path, "mod.rs");
1036
+ if (projectFiles.has(asDir)) return asDir;
1037
+ }
1038
+ return null;
1039
+ }
441
1040
  const segments = importPath.split("::");
442
1041
  for (let i = segments.length; i > 0; i--) {
443
1042
  const path = segments.slice(0, i).join("/");
@@ -450,15 +1049,88 @@ var rust = {
450
1049
  },
451
1050
  defaultExclude: ["target"]
452
1051
  };
1052
+ function extractRustUsePaths(body, prefix, results, rootPrefix) {
1053
+ const trimmed = body.trim();
1054
+ const braceStart = trimmed.indexOf("{");
1055
+ if (braceStart === -1) {
1056
+ let path = prefix ? `${prefix}::${trimmed}` : trimmed;
1057
+ if (rootPrefix) {
1058
+ path = `${rootPrefix}::${path}`;
1059
+ }
1060
+ if (path && !path.includes("{")) {
1061
+ results.push(path);
1062
+ }
1063
+ return;
1064
+ }
1065
+ let pathPrefix = trimmed.slice(0, braceStart).trim();
1066
+ if (pathPrefix.endsWith("::")) {
1067
+ pathPrefix = pathPrefix.slice(0, -2);
1068
+ }
1069
+ const fullPrefix = prefix ? `${prefix}::${pathPrefix}` : pathPrefix;
1070
+ let depth = 0;
1071
+ let braceEnd = -1;
1072
+ for (let i = braceStart; i < trimmed.length; i++) {
1073
+ if (trimmed[i] === "{") depth++;
1074
+ else if (trimmed[i] === "}") {
1075
+ depth--;
1076
+ if (depth === 0) {
1077
+ braceEnd = i;
1078
+ break;
1079
+ }
1080
+ }
1081
+ }
1082
+ if (braceEnd === -1) return;
1083
+ const inner = trimmed.slice(braceStart + 1, braceEnd).trim();
1084
+ const items = splitByTopLevelComma(inner);
1085
+ for (const item of items) {
1086
+ const cleaned = item.trim();
1087
+ if (cleaned === "self") {
1088
+ const selfPath = rootPrefix ? `${rootPrefix}::${fullPrefix}` : fullPrefix;
1089
+ results.push(selfPath);
1090
+ } else if (cleaned) {
1091
+ extractRustUsePaths(cleaned, fullPrefix, results, rootPrefix);
1092
+ }
1093
+ }
1094
+ }
1095
+ function splitByTopLevelComma(s) {
1096
+ const parts = [];
1097
+ let depth = 0;
1098
+ let start = 0;
1099
+ for (let i = 0; i < s.length; i++) {
1100
+ if (s[i] === "{") depth++;
1101
+ else if (s[i] === "}") depth--;
1102
+ else if (s[i] === "," && depth === 0) {
1103
+ parts.push(s.slice(start, i));
1104
+ start = i + 1;
1105
+ }
1106
+ }
1107
+ parts.push(s.slice(start));
1108
+ return parts;
1109
+ }
453
1110
  var go = {
454
1111
  id: "go",
455
1112
  extensions: [".go"],
456
- importPatterns: [
457
- // import "path" or import ( "path" )
458
- { regex: /\bimport\s+(?:\w+\s+)?"([^"]+)"/gm },
459
- // import block entries
460
- { regex: /^\s+(?:\w+\s+)?"([^"]+)"/gm }
461
- ],
1113
+ commentStyle: "c-style",
1114
+ importPatterns: [],
1115
+ // handled by extractImports
1116
+ extractImports(content) {
1117
+ const imports = [];
1118
+ const singleRegex = /\bimport\s+(?:\w+\s+)?"([^"]+)"/gm;
1119
+ let match;
1120
+ while ((match = singleRegex.exec(content)) !== null) {
1121
+ imports.push(match[1]);
1122
+ }
1123
+ const blockRegex = /\bimport\s*\(([^)]*)\)/gms;
1124
+ while ((match = blockRegex.exec(content)) !== null) {
1125
+ const block = match[1];
1126
+ const entryRegex = /(?:\w+\s+)?"([^"]+)"/g;
1127
+ let entry;
1128
+ while ((entry = entryRegex.exec(block)) !== null) {
1129
+ imports.push(entry[1]);
1130
+ }
1131
+ }
1132
+ return imports;
1133
+ },
462
1134
  resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
463
1135
  const modPrefix = goModulePrefix(rootDir);
464
1136
  if (!modPrefix || !importPath.startsWith(modPrefix)) return null;
@@ -475,8 +1147,7 @@ var goModCache = /* @__PURE__ */ new Map();
475
1147
  function goModulePrefix(rootDir) {
476
1148
  if (goModCache.has(rootDir)) return goModCache.get(rootDir);
477
1149
  try {
478
- const fs = __require("fs");
479
- const content = fs.readFileSync(join3(rootDir, "go.mod"), "utf-8");
1150
+ const content = readFileSync(join3(rootDir, "go.mod"), "utf-8");
480
1151
  const match = content.match(/^module\s+(.+)$/m);
481
1152
  const prefix = match ? match[1].trim() : null;
482
1153
  goModCache.set(rootDir, prefix);
@@ -489,15 +1160,23 @@ function goModulePrefix(rootDir) {
489
1160
  var java = {
490
1161
  id: "java",
491
1162
  extensions: [".java"],
1163
+ commentStyle: "c-style",
492
1164
  importPatterns: [
493
- // import com.example.ClassName;
494
- { regex: /^import\s+(?:static\s+)?([\w.]+);/gm }
1165
+ // Bug #5 fix: import com.example.ClassName; and import com.example.*; (wildcard)
1166
+ // Bug #6: static imports also captured here
1167
+ { regex: /^import\s+(?:static\s+)?([\w.*]+);/gm }
495
1168
  ],
496
1169
  resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
497
- const filePath = importPath.replace(/\./g, "/") + ".java";
498
- for (const srcRoot of ["", "src/main/java/", "src/", "app/src/main/java/"]) {
499
- const full = join3(rootDir, srcRoot, filePath);
500
- if (projectFiles.has(full)) return full;
1170
+ if (importPath.endsWith(".*")) {
1171
+ return null;
1172
+ }
1173
+ const segments = importPath.split(".");
1174
+ for (let i = segments.length; i > 0; i--) {
1175
+ const filePath = segments.slice(0, i).join("/") + ".java";
1176
+ for (const srcRoot of ["", "src/main/java/", "src/", "app/src/main/java/"]) {
1177
+ const full = join3(rootDir, srcRoot, filePath);
1178
+ if (projectFiles.has(full)) return full;
1179
+ }
501
1180
  }
502
1181
  return null;
503
1182
  },
@@ -506,6 +1185,7 @@ var java = {
506
1185
  var cCpp = {
507
1186
  id: "c-cpp",
508
1187
  extensions: [".c", ".cpp", ".cc", ".cxx", ".h", ".hpp"],
1188
+ commentStyle: "c-style",
509
1189
  importPatterns: [
510
1190
  // #include "file.h" (skip <system> includes)
511
1191
  { regex: /^#include\s+"([^"]+)"/gm }
@@ -526,6 +1206,7 @@ var cCpp = {
526
1206
  var ruby = {
527
1207
  id: "ruby",
528
1208
  extensions: [".rb"],
1209
+ commentStyle: "ruby",
529
1210
  importPatterns: [
530
1211
  // require_relative 'path'
531
1212
  { regex: /\brequire_relative\s+['"]([^'"]+)['"]/gm },
@@ -547,11 +1228,12 @@ var ruby = {
547
1228
  var php = {
548
1229
  id: "php",
549
1230
  extensions: [".php"],
1231
+ commentStyle: "php",
550
1232
  importPatterns: [
551
1233
  // require/include/require_once/include_once 'path'
552
1234
  { regex: /\b(?:require|include)(?:_once)?\s+['"]([^'"]+)['"]/gm },
553
- // use Namespace\Class (PSR-4 style)
554
- { regex: /^use\s+([\w\\]+)/gm }
1235
+ // Bug #9 fix: use Namespace\Class skip `function` and `const` keywords
1236
+ { regex: /^use\s+(?:function\s+|const\s+)?([\w\\]+)/gm }
555
1237
  ],
556
1238
  resolveImport(importPath, sourceFile, rootDir, projectFiles) {
557
1239
  if (importPath.includes("/") || importPath.endsWith(".php")) {
@@ -574,11 +1256,12 @@ var php = {
574
1256
  var swift = {
575
1257
  id: "swift",
576
1258
  extensions: [".swift"],
1259
+ commentStyle: "c-style",
577
1260
  importPatterns: [
578
- // import ModuleName
579
- { regex: /^import\s+(?:class|struct|enum|protocol|func|var|let|typealias)?\s*(\w+)/gm }
1261
+ // Bug #10 fix: import ModuleName and @testable import ModuleName
1262
+ { regex: /^(?:@testable\s+)?import\s+(?:class\s+|struct\s+|enum\s+|protocol\s+|func\s+|var\s+|let\s+|typealias\s+)?(\w+)/gm }
580
1263
  ],
581
- resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
1264
+ resolveImport(importPath, sourceFile, rootDir, projectFiles) {
582
1265
  const spmDir = join3(rootDir, "Sources", importPath);
583
1266
  for (const f of projectFiles) {
584
1267
  if (f.startsWith(spmDir + "/") && f.endsWith(".swift")) return f;
@@ -590,12 +1273,20 @@ var swift = {
590
1273
  var kotlin = {
591
1274
  id: "kotlin",
592
1275
  extensions: [".kt", ".kts"],
1276
+ commentStyle: "c-style",
593
1277
  importPatterns: [
594
- // import com.example.ClassName
595
- { regex: /^import\s+([\w.]+)/gm }
1278
+ // Bug #7/#8 fix: import com.example.ClassName and import com.example.*
1279
+ { regex: /^import\s+([\w.*]+)/gm }
596
1280
  ],
597
1281
  resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
598
- const filePath = importPath.replace(/\./g, "/");
1282
+ if (importPath.endsWith(".*")) {
1283
+ return null;
1284
+ }
1285
+ let cleanPath = importPath;
1286
+ if (cleanPath.endsWith(".")) {
1287
+ cleanPath = cleanPath.slice(0, -1);
1288
+ }
1289
+ const filePath = cleanPath.replace(/\./g, "/");
599
1290
  for (const ext of [".kt", ".kts"]) {
600
1291
  for (const srcRoot of [
601
1292
  "",
@@ -613,6 +1304,124 @@ var kotlin = {
613
1304
  },
614
1305
  defaultExclude: ["build", "\\.gradle", "\\.idea"]
615
1306
  };
1307
+ var cSharp = {
1308
+ id: "c-sharp",
1309
+ extensions: [".cs"],
1310
+ commentStyle: "c-style",
1311
+ importPatterns: [
1312
+ // using Namespace; and using Namespace.SubNamespace;
1313
+ // using static Namespace.Class;
1314
+ // Skip: using Alias = Namespace.Class; (captured but resolved same way)
1315
+ { regex: /^using\s+(?:static\s+)?([\w.]+)\s*;/gm }
1316
+ ],
1317
+ resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
1318
+ const segments = importPath.split(".");
1319
+ for (let i = segments.length; i > 0; i--) {
1320
+ const filePath = segments.slice(0, i).join("/") + ".cs";
1321
+ for (const srcRoot of ["", "src/", "lib/"]) {
1322
+ const full = join3(rootDir, srcRoot, filePath);
1323
+ if (projectFiles.has(full)) return full;
1324
+ }
1325
+ }
1326
+ for (let start = 0; start < segments.length; start++) {
1327
+ const dirPath = segments.slice(start).join("/");
1328
+ for (const srcRoot of ["", "src/", "lib/"]) {
1329
+ const prefix = join3(rootDir, srcRoot, dirPath) + "/";
1330
+ for (const f of projectFiles) {
1331
+ if (f.startsWith(prefix) && f.endsWith(".cs")) return f;
1332
+ }
1333
+ }
1334
+ }
1335
+ return null;
1336
+ },
1337
+ defaultExclude: ["bin", "obj", "\\.vs", "packages", "TestResults"]
1338
+ };
1339
+ var dart = {
1340
+ id: "dart",
1341
+ extensions: [".dart"],
1342
+ commentStyle: "c-style",
1343
+ importPatterns: [
1344
+ // import 'package:pkg/file.dart'; or import 'relative/path.dart';
1345
+ { regex: /^import\s+['"]([^'"]+)['"]/gm },
1346
+ // export 'file.dart'; (re-exports create dependencies too)
1347
+ { regex: /^export\s+['"]([^'"]+)['"]/gm }
1348
+ ],
1349
+ resolveImport(importPath, sourceFile, rootDir, projectFiles) {
1350
+ if (importPath.startsWith("dart:")) return null;
1351
+ if (importPath.startsWith("package:")) {
1352
+ const ownPackage = dartPackageName(rootDir);
1353
+ if (!ownPackage) return null;
1354
+ const prefix = `package:${ownPackage}/`;
1355
+ if (!importPath.startsWith(prefix)) return null;
1356
+ const relPath = importPath.slice(prefix.length);
1357
+ const full = join3(rootDir, "lib", relPath);
1358
+ if (projectFiles.has(full)) return full;
1359
+ return null;
1360
+ }
1361
+ const resolved = resolve3(dirname(sourceFile), importPath);
1362
+ if (projectFiles.has(resolved)) return resolved;
1363
+ return null;
1364
+ },
1365
+ defaultExclude: ["\\.dart_tool", "build", "\\.packages"]
1366
+ };
1367
+ var dartPackageCache = /* @__PURE__ */ new Map();
1368
+ function dartPackageName(rootDir) {
1369
+ if (dartPackageCache.has(rootDir)) return dartPackageCache.get(rootDir);
1370
+ try {
1371
+ const content = readFileSync(join3(rootDir, "pubspec.yaml"), "utf-8");
1372
+ const match = content.match(/^name:\s*(\S+)/m);
1373
+ const name = match ? match[1] : null;
1374
+ dartPackageCache.set(rootDir, name);
1375
+ return name;
1376
+ } catch {
1377
+ dartPackageCache.set(rootDir, null);
1378
+ return null;
1379
+ }
1380
+ }
1381
+ var scala = {
1382
+ id: "scala",
1383
+ extensions: [".scala", ".sc"],
1384
+ commentStyle: "c-style",
1385
+ importPatterns: [],
1386
+ // handled by extractImports for grouped syntax
1387
+ extractImports(content) {
1388
+ const imports = [];
1389
+ const importRegex = /\bimport\s+([\w.]+(?:\.\{[^}]+\}|\.\w+|\._))/gm;
1390
+ let match;
1391
+ while ((match = importRegex.exec(content)) !== null) {
1392
+ const full = match[1];
1393
+ const braceMatch = full.match(/^([\w.]+)\.\{([^}]+)\}$/);
1394
+ if (braceMatch) {
1395
+ const prefix = braceMatch[1];
1396
+ const items = braceMatch[2].split(",");
1397
+ for (const item of items) {
1398
+ const trimmed = item.trim().split(/\s+/)[0];
1399
+ if (trimmed === "_") continue;
1400
+ imports.push(`${prefix}.${trimmed}`);
1401
+ }
1402
+ } else if (full.endsWith("._")) {
1403
+ continue;
1404
+ } else {
1405
+ imports.push(full);
1406
+ }
1407
+ }
1408
+ return imports;
1409
+ },
1410
+ resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
1411
+ const segments = importPath.split(".");
1412
+ for (let i = segments.length; i > 0; i--) {
1413
+ const filePath = segments.slice(0, i).join("/");
1414
+ for (const ext of [".scala", ".sc"]) {
1415
+ for (const srcRoot of ["", "src/main/scala/", "src/", "app/"]) {
1416
+ const full = join3(rootDir, srcRoot, filePath + ext);
1417
+ if (projectFiles.has(full)) return full;
1418
+ }
1419
+ }
1420
+ }
1421
+ return null;
1422
+ },
1423
+ defaultExclude: ["target", "\\.bsp", "\\.metals", "\\.bloop"]
1424
+ };
616
1425
  var LANGUAGE_CONFIGS = {
617
1426
  javascript: null,
618
1427
  // handled by DependencyCruiserEngine
@@ -621,10 +1430,13 @@ var LANGUAGE_CONFIGS = {
621
1430
  go,
622
1431
  java,
623
1432
  "c-cpp": cCpp,
1433
+ "c-sharp": cSharp,
624
1434
  ruby,
625
1435
  php,
626
1436
  swift,
627
- kotlin
1437
+ kotlin,
1438
+ dart,
1439
+ scala
628
1440
  };
629
1441
  function getLanguageConfig(id) {
630
1442
  return LANGUAGE_CONFIGS[id] ?? null;
@@ -1182,7 +1994,7 @@ server.tool(
1182
1994
  targetDir: z2.string().default("src").describe("Target directory path (default: src)"),
1183
1995
  exclude: z2.array(z2.string()).optional().describe("Array of regex patterns to exclude (e.g. ['test', 'mock'])"),
1184
1996
  maxDepth: z2.number().int().min(0).optional().describe("Max analysis depth (0 = unlimited)"),
1185
- language: z2.enum(["javascript", "python", "rust", "go", "java", "c-cpp", "ruby", "php", "swift", "kotlin"]).optional().describe("Target language (auto-detected if omitted)")
1997
+ language: z2.enum(["javascript", "python", "rust", "go", "java", "c-cpp", "c-sharp", "ruby", "php", "swift", "kotlin", "dart", "scala"]).optional().describe("Target language (auto-detected if omitted)")
1186
1998
  },
1187
1999
  async ({ targetDir, exclude, maxDepth, language }) => {
1188
2000
  try {
@@ -1212,7 +2024,7 @@ server.tool(
1212
2024
  topN: z2.number().int().min(1).max(50).optional().describe("Number of top items to show in each section (default: 10)"),
1213
2025
  saveSnapshot: z2.boolean().optional().describe("Also save a snapshot after analysis (default: false)"),
1214
2026
  projectRoot: z2.string().default(".").describe("Project root (needed only when saveSnapshot is true)"),
1215
- language: z2.enum(["javascript", "python", "rust", "go", "java", "c-cpp", "ruby", "php", "swift", "kotlin"]).optional().describe("Target language (auto-detected if omitted)")
2027
+ language: z2.enum(["javascript", "python", "rust", "go", "java", "c-cpp", "c-sharp", "ruby", "php", "swift", "kotlin", "dart", "scala"]).optional().describe("Target language (auto-detected if omitted)")
1216
2028
  },
1217
2029
  async ({ targetDir, exclude, topN, saveSnapshot: doSave, projectRoot, language }) => {
1218
2030
  try {
@@ -1239,7 +2051,7 @@ server.tool(
1239
2051
  {
1240
2052
  targetDir: z2.string().default("src").describe("Target directory path"),
1241
2053
  projectRoot: z2.string().default(".").describe("Project root (where .archtracker is placed)"),
1242
- language: z2.enum(["javascript", "python", "rust", "go", "java", "c-cpp", "ruby", "php", "swift", "kotlin"]).optional().describe("Target language (auto-detected if omitted)")
2054
+ language: z2.enum(["javascript", "python", "rust", "go", "java", "c-cpp", "c-sharp", "ruby", "php", "swift", "kotlin", "dart", "scala"]).optional().describe("Target language (auto-detected if omitted)")
1243
2055
  },
1244
2056
  async ({ targetDir, projectRoot, language }) => {
1245
2057
  try {
@@ -1269,7 +2081,7 @@ server.tool(
1269
2081
  {
1270
2082
  targetDir: z2.string().default("src").describe("Target directory path"),
1271
2083
  projectRoot: z2.string().default(".").describe("Project root (where .archtracker is placed)"),
1272
- language: z2.enum(["javascript", "python", "rust", "go", "java", "c-cpp", "ruby", "php", "swift", "kotlin"]).optional().describe("Target language (auto-detected if omitted)")
2084
+ language: z2.enum(["javascript", "python", "rust", "go", "java", "c-cpp", "c-sharp", "ruby", "php", "swift", "kotlin", "dart", "scala"]).optional().describe("Target language (auto-detected if omitted)")
1273
2085
  },
1274
2086
  async ({ targetDir, projectRoot, language }) => {
1275
2087
  try {
@@ -1307,7 +2119,7 @@ server.tool(
1307
2119
  {
1308
2120
  targetDir: z2.string().default("src").describe("Target directory path"),
1309
2121
  projectRoot: z2.string().default(".").describe("Project root"),
1310
- language: z2.enum(["javascript", "python", "rust", "go", "java", "c-cpp", "ruby", "php", "swift", "kotlin"]).optional().describe("Target language (auto-detected if omitted)")
2122
+ language: z2.enum(["javascript", "python", "rust", "go", "java", "c-cpp", "c-sharp", "ruby", "php", "swift", "kotlin", "dart", "scala"]).optional().describe("Target language (auto-detected if omitted)")
1311
2123
  },
1312
2124
  async ({ targetDir, projectRoot, language }) => {
1313
2125
  try {
@@ -1367,7 +2179,7 @@ server.tool(
1367
2179
  targetDir: z2.string().default("src").describe("Target directory path"),
1368
2180
  projectRoot: z2.string().default(".").describe("Project root"),
1369
2181
  limit: z2.number().int().min(1).max(50).optional().describe("Max results (default: 10)"),
1370
- language: z2.enum(["javascript", "python", "rust", "go", "java", "c-cpp", "ruby", "php", "swift", "kotlin"]).optional().describe("Target language (auto-detected if omitted)")
2182
+ language: z2.enum(["javascript", "python", "rust", "go", "java", "c-cpp", "c-sharp", "ruby", "php", "swift", "kotlin", "dart", "scala"]).optional().describe("Target language (auto-detected if omitted)")
1371
2183
  },
1372
2184
  async ({ query, mode, targetDir, projectRoot, limit, language }) => {
1373
2185
  try {