archtracker-mcp 0.2.1 → 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/cli/index.js +474 -51
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +474 -51
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.js +480 -57
- package/dist/mcp/index.js.map +1 -1
- package/package.json +5 -2
package/dist/mcp/index.js
CHANGED
|
@@ -179,12 +179,50 @@ function stripComments(content, style) {
|
|
|
179
179
|
return stripRuby(content);
|
|
180
180
|
case "php":
|
|
181
181
|
return stripPhp(content);
|
|
182
|
+
default: {
|
|
183
|
+
const _exhaustive = style;
|
|
184
|
+
throw new Error(`Unknown comment style: ${_exhaustive}`);
|
|
185
|
+
}
|
|
182
186
|
}
|
|
183
187
|
}
|
|
184
188
|
function stripCStyle(content) {
|
|
185
189
|
let result = "";
|
|
186
190
|
let i = 0;
|
|
187
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
|
+
}
|
|
188
226
|
if (content[i] === "/" && content[i + 1] === "/") {
|
|
189
227
|
while (i < content.length && content[i] !== "\n") {
|
|
190
228
|
result += " ";
|
|
@@ -227,6 +265,24 @@ function stripCStyle(content) {
|
|
|
227
265
|
result += content[i];
|
|
228
266
|
i++;
|
|
229
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
|
+
}
|
|
230
286
|
} else if (content[i] === "`") {
|
|
231
287
|
result += " ";
|
|
232
288
|
i++;
|
|
@@ -295,31 +351,62 @@ function stripPython(content) {
|
|
|
295
351
|
let result = "";
|
|
296
352
|
let i = 0;
|
|
297
353
|
while (i < content.length) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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
|
+
}
|
|
301
378
|
result += " ";
|
|
302
|
-
i
|
|
379
|
+
i = quoteStart + 3;
|
|
303
380
|
while (i < content.length) {
|
|
304
381
|
if (content[i] === quote && content[i + 1] === quote && content[i + 2] === quote) {
|
|
305
382
|
result += " ";
|
|
306
383
|
i += 3;
|
|
307
384
|
break;
|
|
308
385
|
}
|
|
309
|
-
|
|
310
|
-
|
|
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
|
+
}
|
|
311
395
|
}
|
|
312
|
-
} else if (content[i] === "#") {
|
|
396
|
+
} else if (prefixLen === 0 && content[i] === "#") {
|
|
313
397
|
while (i < content.length && content[i] !== "\n") {
|
|
314
398
|
result += " ";
|
|
315
399
|
i++;
|
|
316
400
|
}
|
|
317
|
-
} else if (content[i] === '"' || content[i] === "'") {
|
|
318
|
-
const quote = content[
|
|
319
|
-
|
|
320
|
-
|
|
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;
|
|
321
408
|
while (i < content.length && content[i] !== quote) {
|
|
322
|
-
if (content[i] === "\\" && i + 1 < content.length) {
|
|
409
|
+
if (!isRaw && content[i] === "\\" && i + 1 < content.length) {
|
|
323
410
|
result += content[i++];
|
|
324
411
|
result += content[i++];
|
|
325
412
|
} else if (content[i] === "\n") {
|
|
@@ -346,13 +433,13 @@ function stripRuby(content) {
|
|
|
346
433
|
const result = [];
|
|
347
434
|
let inBlock = false;
|
|
348
435
|
for (const line of lines) {
|
|
349
|
-
if (!inBlock &&
|
|
436
|
+
if (!inBlock && /^=begin(\s|$)/.test(line)) {
|
|
350
437
|
inBlock = true;
|
|
351
438
|
result.push(" ".repeat(line.length));
|
|
352
439
|
continue;
|
|
353
440
|
}
|
|
354
441
|
if (inBlock) {
|
|
355
|
-
if (
|
|
442
|
+
if (/^=end(\s|$)/.test(line)) {
|
|
356
443
|
inBlock = false;
|
|
357
444
|
}
|
|
358
445
|
result.push(" ".repeat(line.length));
|
|
@@ -367,18 +454,8 @@ function stripRuby(content) {
|
|
|
367
454
|
} else if (line[i] === '"') {
|
|
368
455
|
processed += line[i];
|
|
369
456
|
i++;
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
processed += line[i++];
|
|
373
|
-
processed += line[i++];
|
|
374
|
-
} else {
|
|
375
|
-
processed += line[i++];
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
if (i < line.length) {
|
|
379
|
-
processed += line[i];
|
|
380
|
-
i++;
|
|
381
|
-
}
|
|
457
|
+
processed = scanRubyDoubleQuotedString(line, i, processed);
|
|
458
|
+
i = scanRubyDoubleQuotedStringIndex(line, i);
|
|
382
459
|
} else if (line[i] === "'") {
|
|
383
460
|
processed += line[i];
|
|
384
461
|
i++;
|
|
@@ -403,10 +480,141 @@ function stripRuby(content) {
|
|
|
403
480
|
}
|
|
404
481
|
return result.join("\n");
|
|
405
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
|
+
}
|
|
406
546
|
function stripPhp(content) {
|
|
407
547
|
let result = "";
|
|
408
548
|
let i = 0;
|
|
409
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
|
+
}
|
|
410
618
|
if (content[i] === "/" && content[i + 1] === "/" || content[i] === "#") {
|
|
411
619
|
while (i < content.length && content[i] !== "\n") {
|
|
412
620
|
result += " ";
|
|
@@ -479,6 +687,7 @@ var RegexEngine = class {
|
|
|
479
687
|
"\\.archtracker"
|
|
480
688
|
].map((p) => new RegExp(p));
|
|
481
689
|
const projectFiles = await this.collectFiles(
|
|
690
|
+
absRootDir,
|
|
482
691
|
absRootDir,
|
|
483
692
|
excludePatterns,
|
|
484
693
|
options.maxDepth ?? 0
|
|
@@ -502,6 +711,7 @@ var RegexEngine = class {
|
|
|
502
711
|
try {
|
|
503
712
|
content = await readFile(filePath, "utf-8");
|
|
504
713
|
} catch {
|
|
714
|
+
if (files[relSource]) files[relSource].exists = false;
|
|
505
715
|
continue;
|
|
506
716
|
}
|
|
507
717
|
const stripped = stripComments(content, this.config.commentStyle);
|
|
@@ -545,7 +755,8 @@ var RegexEngine = class {
|
|
|
545
755
|
}
|
|
546
756
|
const imports = [];
|
|
547
757
|
for (const pattern of this.config.importPatterns) {
|
|
548
|
-
const
|
|
758
|
+
const flags = pattern.regex.flags.includes("g") ? pattern.regex.flags : pattern.regex.flags + "g";
|
|
759
|
+
const regex = new RegExp(pattern.regex.source, flags);
|
|
549
760
|
let match;
|
|
550
761
|
while ((match = regex.exec(content)) !== null) {
|
|
551
762
|
if (match[1]) {
|
|
@@ -555,7 +766,7 @@ var RegexEngine = class {
|
|
|
555
766
|
}
|
|
556
767
|
return imports;
|
|
557
768
|
}
|
|
558
|
-
async collectFiles(dir, excludePatterns, maxDepth, currentDepth = 0) {
|
|
769
|
+
async collectFiles(dir, absRootDir, excludePatterns, maxDepth, currentDepth = 0) {
|
|
559
770
|
if (maxDepth > 0 && currentDepth >= maxDepth) return [];
|
|
560
771
|
const results = [];
|
|
561
772
|
let entries;
|
|
@@ -566,7 +777,7 @@ var RegexEngine = class {
|
|
|
566
777
|
}
|
|
567
778
|
for (const entry of entries) {
|
|
568
779
|
const fullPath = join(dir, entry.name);
|
|
569
|
-
const relPath = relative(
|
|
780
|
+
const relPath = relative(absRootDir, fullPath);
|
|
570
781
|
if (excludePatterns.some(
|
|
571
782
|
(p) => p.test(entry.name) || p.test(relPath) || p.test(fullPath)
|
|
572
783
|
)) {
|
|
@@ -576,6 +787,7 @@ var RegexEngine = class {
|
|
|
576
787
|
if (entry.name.startsWith(".")) continue;
|
|
577
788
|
const sub = await this.collectFiles(
|
|
578
789
|
fullPath,
|
|
790
|
+
absRootDir,
|
|
579
791
|
excludePatterns,
|
|
580
792
|
maxDepth,
|
|
581
793
|
currentDepth + 1
|
|
@@ -608,14 +820,21 @@ var MARKERS = [
|
|
|
608
820
|
{ file: "pom.xml", language: "java" },
|
|
609
821
|
{ file: "build.gradle", language: "java" },
|
|
610
822
|
{ file: "build.gradle.kts", language: "kotlin" },
|
|
823
|
+
{ file: "build.sbt", language: "scala" },
|
|
824
|
+
{ file: "build.sc", language: "scala" },
|
|
611
825
|
{ file: "Package.swift", language: "swift" },
|
|
612
826
|
{ file: "Gemfile", language: "ruby" },
|
|
613
827
|
{ file: "composer.json", language: "php" },
|
|
828
|
+
{ file: "pubspec.yaml", language: "dart" },
|
|
614
829
|
{ file: "CMakeLists.txt", language: "c-cpp" },
|
|
615
830
|
{ file: "Makefile", language: "c-cpp" },
|
|
616
831
|
{ file: "package.json", language: "javascript" },
|
|
617
832
|
{ file: "tsconfig.json", language: "javascript" }
|
|
618
833
|
];
|
|
834
|
+
var EXT_MARKERS = [
|
|
835
|
+
[".sln", "c-sharp"],
|
|
836
|
+
[".csproj", "c-sharp"]
|
|
837
|
+
];
|
|
619
838
|
var EXT_MAP = {
|
|
620
839
|
".ts": "javascript",
|
|
621
840
|
".tsx": "javascript",
|
|
@@ -637,7 +856,11 @@ var EXT_MAP = {
|
|
|
637
856
|
".php": "php",
|
|
638
857
|
".swift": "swift",
|
|
639
858
|
".kt": "kotlin",
|
|
640
|
-
".kts": "kotlin"
|
|
859
|
+
".kts": "kotlin",
|
|
860
|
+
".cs": "c-sharp",
|
|
861
|
+
".dart": "dart",
|
|
862
|
+
".scala": "scala",
|
|
863
|
+
".sc": "scala"
|
|
641
864
|
};
|
|
642
865
|
async function detectLanguage(rootDir) {
|
|
643
866
|
for (const marker of MARKERS) {
|
|
@@ -649,6 +872,16 @@ async function detectLanguage(rootDir) {
|
|
|
649
872
|
} catch {
|
|
650
873
|
}
|
|
651
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
|
+
}
|
|
652
885
|
const counts = /* @__PURE__ */ new Map();
|
|
653
886
|
try {
|
|
654
887
|
await scanExtensions(rootDir, counts, 2, 0);
|
|
@@ -701,10 +934,27 @@ var python = {
|
|
|
701
934
|
commentStyle: "python",
|
|
702
935
|
importPatterns: [
|
|
703
936
|
// from package.module import something
|
|
704
|
-
{ regex: /^from\s+(\.[\w.]*|\w[\w.]*)\s+import\b/gm }
|
|
705
|
-
// import package.module
|
|
706
|
-
{ 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)
|
|
707
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
|
+
},
|
|
708
958
|
resolveImport(importPath, sourceFile, rootDir, projectFiles) {
|
|
709
959
|
if (importPath.startsWith(".")) {
|
|
710
960
|
const dots = importPath.match(/^\.+/)?.[0].length ?? 1;
|
|
@@ -741,6 +991,16 @@ var rust = {
|
|
|
741
991
|
const body = match[1].trim();
|
|
742
992
|
extractRustUsePaths(body, "", imports);
|
|
743
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
|
+
}
|
|
744
1004
|
return imports;
|
|
745
1005
|
},
|
|
746
1006
|
resolveImport(importPath, sourceFile, rootDir, projectFiles) {
|
|
@@ -753,6 +1013,30 @@ var rust = {
|
|
|
753
1013
|
if (projectFiles.has(asDir)) return asDir;
|
|
754
1014
|
return null;
|
|
755
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
|
+
}
|
|
756
1040
|
const segments = importPath.split("::");
|
|
757
1041
|
for (let i = segments.length; i > 0; i--) {
|
|
758
1042
|
const path = segments.slice(0, i).join("/");
|
|
@@ -765,11 +1049,14 @@ var rust = {
|
|
|
765
1049
|
},
|
|
766
1050
|
defaultExclude: ["target"]
|
|
767
1051
|
};
|
|
768
|
-
function extractRustUsePaths(body, prefix, results) {
|
|
1052
|
+
function extractRustUsePaths(body, prefix, results, rootPrefix) {
|
|
769
1053
|
const trimmed = body.trim();
|
|
770
1054
|
const braceStart = trimmed.indexOf("{");
|
|
771
1055
|
if (braceStart === -1) {
|
|
772
|
-
|
|
1056
|
+
let path = prefix ? `${prefix}::${trimmed}` : trimmed;
|
|
1057
|
+
if (rootPrefix) {
|
|
1058
|
+
path = `${rootPrefix}::${path}`;
|
|
1059
|
+
}
|
|
773
1060
|
if (path && !path.includes("{")) {
|
|
774
1061
|
results.push(path);
|
|
775
1062
|
}
|
|
@@ -798,9 +1085,10 @@ function extractRustUsePaths(body, prefix, results) {
|
|
|
798
1085
|
for (const item of items) {
|
|
799
1086
|
const cleaned = item.trim();
|
|
800
1087
|
if (cleaned === "self") {
|
|
801
|
-
|
|
1088
|
+
const selfPath = rootPrefix ? `${rootPrefix}::${fullPrefix}` : fullPrefix;
|
|
1089
|
+
results.push(selfPath);
|
|
802
1090
|
} else if (cleaned) {
|
|
803
|
-
extractRustUsePaths(cleaned, fullPrefix, results);
|
|
1091
|
+
extractRustUsePaths(cleaned, fullPrefix, results, rootPrefix);
|
|
804
1092
|
}
|
|
805
1093
|
}
|
|
806
1094
|
}
|
|
@@ -874,14 +1162,21 @@ var java = {
|
|
|
874
1162
|
extensions: [".java"],
|
|
875
1163
|
commentStyle: "c-style",
|
|
876
1164
|
importPatterns: [
|
|
877
|
-
// import com.example.ClassName;
|
|
878
|
-
|
|
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 }
|
|
879
1168
|
],
|
|
880
1169
|
resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
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
|
+
}
|
|
885
1180
|
}
|
|
886
1181
|
return null;
|
|
887
1182
|
},
|
|
@@ -937,8 +1232,8 @@ var php = {
|
|
|
937
1232
|
importPatterns: [
|
|
938
1233
|
// require/include/require_once/include_once 'path'
|
|
939
1234
|
{ regex: /\b(?:require|include)(?:_once)?\s+['"]([^'"]+)['"]/gm },
|
|
940
|
-
// use Namespace\Class
|
|
941
|
-
{ 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 }
|
|
942
1237
|
],
|
|
943
1238
|
resolveImport(importPath, sourceFile, rootDir, projectFiles) {
|
|
944
1239
|
if (importPath.includes("/") || importPath.endsWith(".php")) {
|
|
@@ -963,8 +1258,8 @@ var swift = {
|
|
|
963
1258
|
extensions: [".swift"],
|
|
964
1259
|
commentStyle: "c-style",
|
|
965
1260
|
importPatterns: [
|
|
966
|
-
// import ModuleName
|
|
967
|
-
{ regex: /^import\s+(?:class\s+|struct\s+|enum\s+|protocol\s+|func\s+|var\s+|let\s+|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 }
|
|
968
1263
|
],
|
|
969
1264
|
resolveImport(importPath, sourceFile, rootDir, projectFiles) {
|
|
970
1265
|
const spmDir = join3(rootDir, "Sources", importPath);
|
|
@@ -980,11 +1275,18 @@ var kotlin = {
|
|
|
980
1275
|
extensions: [".kt", ".kts"],
|
|
981
1276
|
commentStyle: "c-style",
|
|
982
1277
|
importPatterns: [
|
|
983
|
-
// import com.example.ClassName
|
|
984
|
-
{ regex: /^import\s+([\w
|
|
1278
|
+
// Bug #7/#8 fix: import com.example.ClassName and import com.example.*
|
|
1279
|
+
{ regex: /^import\s+([\w.*]+)/gm }
|
|
985
1280
|
],
|
|
986
1281
|
resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
|
|
987
|
-
|
|
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, "/");
|
|
988
1290
|
for (const ext of [".kt", ".kts"]) {
|
|
989
1291
|
for (const srcRoot of [
|
|
990
1292
|
"",
|
|
@@ -1002,6 +1304,124 @@ var kotlin = {
|
|
|
1002
1304
|
},
|
|
1003
1305
|
defaultExclude: ["build", "\\.gradle", "\\.idea"]
|
|
1004
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
|
+
};
|
|
1005
1425
|
var LANGUAGE_CONFIGS = {
|
|
1006
1426
|
javascript: null,
|
|
1007
1427
|
// handled by DependencyCruiserEngine
|
|
@@ -1010,10 +1430,13 @@ var LANGUAGE_CONFIGS = {
|
|
|
1010
1430
|
go,
|
|
1011
1431
|
java,
|
|
1012
1432
|
"c-cpp": cCpp,
|
|
1433
|
+
"c-sharp": cSharp,
|
|
1013
1434
|
ruby,
|
|
1014
1435
|
php,
|
|
1015
1436
|
swift,
|
|
1016
|
-
kotlin
|
|
1437
|
+
kotlin,
|
|
1438
|
+
dart,
|
|
1439
|
+
scala
|
|
1017
1440
|
};
|
|
1018
1441
|
function getLanguageConfig(id) {
|
|
1019
1442
|
return LANGUAGE_CONFIGS[id] ?? null;
|
|
@@ -1571,7 +1994,7 @@ server.tool(
|
|
|
1571
1994
|
targetDir: z2.string().default("src").describe("Target directory path (default: src)"),
|
|
1572
1995
|
exclude: z2.array(z2.string()).optional().describe("Array of regex patterns to exclude (e.g. ['test', 'mock'])"),
|
|
1573
1996
|
maxDepth: z2.number().int().min(0).optional().describe("Max analysis depth (0 = unlimited)"),
|
|
1574
|
-
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)")
|
|
1575
1998
|
},
|
|
1576
1999
|
async ({ targetDir, exclude, maxDepth, language }) => {
|
|
1577
2000
|
try {
|
|
@@ -1601,7 +2024,7 @@ server.tool(
|
|
|
1601
2024
|
topN: z2.number().int().min(1).max(50).optional().describe("Number of top items to show in each section (default: 10)"),
|
|
1602
2025
|
saveSnapshot: z2.boolean().optional().describe("Also save a snapshot after analysis (default: false)"),
|
|
1603
2026
|
projectRoot: z2.string().default(".").describe("Project root (needed only when saveSnapshot is true)"),
|
|
1604
|
-
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)")
|
|
1605
2028
|
},
|
|
1606
2029
|
async ({ targetDir, exclude, topN, saveSnapshot: doSave, projectRoot, language }) => {
|
|
1607
2030
|
try {
|
|
@@ -1628,7 +2051,7 @@ server.tool(
|
|
|
1628
2051
|
{
|
|
1629
2052
|
targetDir: z2.string().default("src").describe("Target directory path"),
|
|
1630
2053
|
projectRoot: z2.string().default(".").describe("Project root (where .archtracker is placed)"),
|
|
1631
|
-
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)")
|
|
1632
2055
|
},
|
|
1633
2056
|
async ({ targetDir, projectRoot, language }) => {
|
|
1634
2057
|
try {
|
|
@@ -1658,7 +2081,7 @@ server.tool(
|
|
|
1658
2081
|
{
|
|
1659
2082
|
targetDir: z2.string().default("src").describe("Target directory path"),
|
|
1660
2083
|
projectRoot: z2.string().default(".").describe("Project root (where .archtracker is placed)"),
|
|
1661
|
-
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)")
|
|
1662
2085
|
},
|
|
1663
2086
|
async ({ targetDir, projectRoot, language }) => {
|
|
1664
2087
|
try {
|
|
@@ -1696,7 +2119,7 @@ server.tool(
|
|
|
1696
2119
|
{
|
|
1697
2120
|
targetDir: z2.string().default("src").describe("Target directory path"),
|
|
1698
2121
|
projectRoot: z2.string().default(".").describe("Project root"),
|
|
1699
|
-
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)")
|
|
1700
2123
|
},
|
|
1701
2124
|
async ({ targetDir, projectRoot, language }) => {
|
|
1702
2125
|
try {
|
|
@@ -1756,7 +2179,7 @@ server.tool(
|
|
|
1756
2179
|
targetDir: z2.string().default("src").describe("Target directory path"),
|
|
1757
2180
|
projectRoot: z2.string().default(".").describe("Project root"),
|
|
1758
2181
|
limit: z2.number().int().min(1).max(50).optional().describe("Max results (default: 10)"),
|
|
1759
|
-
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)")
|
|
1760
2183
|
},
|
|
1761
2184
|
async ({ query, mode, targetDir, projectRoot, limit, language }) => {
|
|
1762
2185
|
try {
|