archtracker-mcp 0.2.0 → 0.2.1

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 CHANGED
@@ -1,10 +1,4 @@
1
1
  #!/usr/bin/env node
2
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
- }) : x)(function(x) {
5
- if (typeof require !== "undefined") return require.apply(this, arguments);
6
- throw Error('Dynamic require of "' + x + '" is not supported');
7
- });
8
2
 
9
3
  // src/cli/index.ts
10
4
  import { Command } from "commander";
@@ -175,6 +169,306 @@ function detectCycles(edges) {
175
169
  return cycles;
176
170
  }
177
171
 
172
+ // src/analyzer/engines/strip-comments.ts
173
+ function stripComments(content, style) {
174
+ switch (style) {
175
+ case "c-style":
176
+ return stripCStyle(content);
177
+ case "hash":
178
+ return stripHash(content);
179
+ case "python":
180
+ return stripPython(content);
181
+ case "ruby":
182
+ return stripRuby(content);
183
+ case "php":
184
+ return stripPhp(content);
185
+ }
186
+ }
187
+ function stripCStyle(content) {
188
+ let result = "";
189
+ let i = 0;
190
+ while (i < content.length) {
191
+ if (content[i] === "/" && content[i + 1] === "/") {
192
+ while (i < content.length && content[i] !== "\n") {
193
+ result += " ";
194
+ i++;
195
+ }
196
+ } else if (content[i] === "/" && content[i + 1] === "*") {
197
+ result += " ";
198
+ i++;
199
+ result += " ";
200
+ i++;
201
+ while (i < content.length) {
202
+ if (content[i] === "*" && content[i + 1] === "/") {
203
+ result += " ";
204
+ i++;
205
+ result += " ";
206
+ i++;
207
+ break;
208
+ }
209
+ result += content[i] === "\n" ? "\n" : " ";
210
+ i++;
211
+ }
212
+ } else if (content[i] === '"') {
213
+ result += content[i];
214
+ i++;
215
+ while (i < content.length && content[i] !== '"') {
216
+ if (content[i] === "\\" && i + 1 < content.length) {
217
+ result += content[i];
218
+ i++;
219
+ result += content[i];
220
+ i++;
221
+ } else if (content[i] === "\n") {
222
+ result += "\n";
223
+ i++;
224
+ } else {
225
+ result += content[i];
226
+ i++;
227
+ }
228
+ }
229
+ if (i < content.length) {
230
+ result += content[i];
231
+ i++;
232
+ }
233
+ } else if (content[i] === "`") {
234
+ result += " ";
235
+ i++;
236
+ while (i < content.length && content[i] !== "`") {
237
+ result += content[i] === "\n" ? "\n" : " ";
238
+ i++;
239
+ }
240
+ if (i < content.length) {
241
+ result += " ";
242
+ i++;
243
+ }
244
+ } else {
245
+ result += content[i];
246
+ i++;
247
+ }
248
+ }
249
+ return result;
250
+ }
251
+ function stripHash(content) {
252
+ let result = "";
253
+ let i = 0;
254
+ while (i < content.length) {
255
+ if (content[i] === "#") {
256
+ while (i < content.length && content[i] !== "\n") {
257
+ result += " ";
258
+ i++;
259
+ }
260
+ } else if (content[i] === '"') {
261
+ result += content[i];
262
+ i++;
263
+ while (i < content.length && content[i] !== '"') {
264
+ if (content[i] === "\\" && i + 1 < content.length) {
265
+ result += content[i++];
266
+ result += content[i++];
267
+ } else {
268
+ result += content[i++];
269
+ }
270
+ }
271
+ if (i < content.length) {
272
+ result += content[i];
273
+ i++;
274
+ }
275
+ } else if (content[i] === "'") {
276
+ result += content[i];
277
+ i++;
278
+ while (i < content.length && content[i] !== "'") {
279
+ if (content[i] === "\\" && i + 1 < content.length) {
280
+ result += content[i++];
281
+ result += content[i++];
282
+ } else {
283
+ result += content[i++];
284
+ }
285
+ }
286
+ if (i < content.length) {
287
+ result += content[i];
288
+ i++;
289
+ }
290
+ } else {
291
+ result += content[i];
292
+ i++;
293
+ }
294
+ }
295
+ return result;
296
+ }
297
+ function stripPython(content) {
298
+ let result = "";
299
+ let i = 0;
300
+ while (i < content.length) {
301
+ if (content[i] === '"' && content[i + 1] === '"' && content[i + 2] === '"' || content[i] === "'" && content[i + 1] === "'" && content[i + 2] === "'") {
302
+ const quote = content[i];
303
+ const tripleQuote = quote + quote + quote;
304
+ result += " ";
305
+ i += 3;
306
+ while (i < content.length) {
307
+ if (content[i] === quote && content[i + 1] === quote && content[i + 2] === quote) {
308
+ result += " ";
309
+ i += 3;
310
+ break;
311
+ }
312
+ result += content[i] === "\n" ? "\n" : " ";
313
+ i++;
314
+ }
315
+ } else if (content[i] === "#") {
316
+ while (i < content.length && content[i] !== "\n") {
317
+ result += " ";
318
+ i++;
319
+ }
320
+ } else if (content[i] === '"' || content[i] === "'") {
321
+ const quote = content[i];
322
+ result += content[i];
323
+ i++;
324
+ while (i < content.length && content[i] !== quote) {
325
+ if (content[i] === "\\" && i + 1 < content.length) {
326
+ result += content[i++];
327
+ result += content[i++];
328
+ } else if (content[i] === "\n") {
329
+ result += "\n";
330
+ i++;
331
+ break;
332
+ } else {
333
+ result += content[i++];
334
+ }
335
+ }
336
+ if (i < content.length && content[i] === quote) {
337
+ result += content[i];
338
+ i++;
339
+ }
340
+ } else {
341
+ result += content[i];
342
+ i++;
343
+ }
344
+ }
345
+ return result;
346
+ }
347
+ function stripRuby(content) {
348
+ const lines = content.split("\n");
349
+ const result = [];
350
+ let inBlock = false;
351
+ for (const line of lines) {
352
+ if (!inBlock && line.startsWith("=begin")) {
353
+ inBlock = true;
354
+ result.push(" ".repeat(line.length));
355
+ continue;
356
+ }
357
+ if (inBlock) {
358
+ if (line.startsWith("=end")) {
359
+ inBlock = false;
360
+ }
361
+ result.push(" ".repeat(line.length));
362
+ continue;
363
+ }
364
+ let processed = "";
365
+ let i = 0;
366
+ while (i < line.length) {
367
+ if (line[i] === "#") {
368
+ processed += " ".repeat(line.length - i);
369
+ break;
370
+ } else if (line[i] === '"') {
371
+ processed += line[i];
372
+ i++;
373
+ while (i < line.length && line[i] !== '"') {
374
+ if (line[i] === "\\" && i + 1 < line.length) {
375
+ processed += line[i++];
376
+ processed += line[i++];
377
+ } else {
378
+ processed += line[i++];
379
+ }
380
+ }
381
+ if (i < line.length) {
382
+ processed += line[i];
383
+ i++;
384
+ }
385
+ } else if (line[i] === "'") {
386
+ processed += line[i];
387
+ i++;
388
+ while (i < line.length && line[i] !== "'") {
389
+ if (line[i] === "\\" && i + 1 < line.length) {
390
+ processed += line[i++];
391
+ processed += line[i++];
392
+ } else {
393
+ processed += line[i++];
394
+ }
395
+ }
396
+ if (i < line.length) {
397
+ processed += line[i];
398
+ i++;
399
+ }
400
+ } else {
401
+ processed += line[i];
402
+ i++;
403
+ }
404
+ }
405
+ result.push(processed);
406
+ }
407
+ return result.join("\n");
408
+ }
409
+ function stripPhp(content) {
410
+ let result = "";
411
+ let i = 0;
412
+ while (i < content.length) {
413
+ if (content[i] === "/" && content[i + 1] === "/" || content[i] === "#") {
414
+ while (i < content.length && content[i] !== "\n") {
415
+ result += " ";
416
+ i++;
417
+ }
418
+ } else if (content[i] === "/" && content[i + 1] === "*") {
419
+ result += " ";
420
+ i++;
421
+ result += " ";
422
+ i++;
423
+ while (i < content.length) {
424
+ if (content[i] === "*" && content[i + 1] === "/") {
425
+ result += " ";
426
+ i++;
427
+ result += " ";
428
+ i++;
429
+ break;
430
+ }
431
+ result += content[i] === "\n" ? "\n" : " ";
432
+ i++;
433
+ }
434
+ } else if (content[i] === '"') {
435
+ result += content[i];
436
+ i++;
437
+ while (i < content.length && content[i] !== '"') {
438
+ if (content[i] === "\\" && i + 1 < content.length) {
439
+ result += content[i++];
440
+ result += content[i++];
441
+ } else {
442
+ result += content[i++];
443
+ }
444
+ }
445
+ if (i < content.length) {
446
+ result += content[i];
447
+ i++;
448
+ }
449
+ } else if (content[i] === "'") {
450
+ result += content[i];
451
+ i++;
452
+ while (i < content.length && content[i] !== "'") {
453
+ if (content[i] === "\\" && i + 1 < content.length) {
454
+ result += content[i++];
455
+ result += content[i++];
456
+ } else {
457
+ result += content[i++];
458
+ }
459
+ }
460
+ if (i < content.length) {
461
+ result += content[i];
462
+ i++;
463
+ }
464
+ } else {
465
+ result += content[i];
466
+ i++;
467
+ }
468
+ }
469
+ return result;
470
+ }
471
+
178
472
  // src/analyzer/engines/regex-engine.ts
179
473
  var RegexEngine = class {
180
474
  constructor(config) {
@@ -195,6 +489,7 @@ var RegexEngine = class {
195
489
  const projectFileSet = new Set(projectFiles);
196
490
  const files = {};
197
491
  const edges = [];
492
+ const edgeSet = /* @__PURE__ */ new Set();
198
493
  for (const filePath of projectFiles) {
199
494
  const relPath = relative(absRootDir, filePath);
200
495
  files[relPath] = {
@@ -212,7 +507,8 @@ var RegexEngine = class {
212
507
  } catch {
213
508
  continue;
214
509
  }
215
- const imports = this.extractImports(content);
510
+ const stripped = stripComments(content, this.config.commentStyle);
511
+ const imports = this.extractImports(stripped);
216
512
  for (const importPath of imports) {
217
513
  const resolved = this.config.resolveImport(
218
514
  importPath,
@@ -223,6 +519,10 @@ var RegexEngine = class {
223
519
  if (!resolved) continue;
224
520
  const relTarget = relative(absRootDir, resolved);
225
521
  if (!files[relTarget]) continue;
522
+ if (relSource === relTarget) continue;
523
+ const edgeKey = `${relSource}\0${relTarget}`;
524
+ if (edgeSet.has(edgeKey)) continue;
525
+ edgeSet.add(edgeKey);
226
526
  edges.push({
227
527
  source: relSource,
228
528
  target: relTarget,
@@ -243,6 +543,9 @@ var RegexEngine = class {
243
543
  };
244
544
  }
245
545
  extractImports(content) {
546
+ if (this.config.extractImports) {
547
+ return this.config.extractImports(content);
548
+ }
246
549
  const imports = [];
247
550
  for (const pattern of this.config.importPatterns) {
248
551
  const regex = new RegExp(pattern.regex.source, pattern.regex.flags);
@@ -393,10 +696,12 @@ async function scanExtensions(dir, counts, maxDepth, currentDepth) {
393
696
  }
394
697
 
395
698
  // src/analyzer/engines/languages.ts
699
+ import { readFileSync } from "fs";
396
700
  import { join as join3, dirname, resolve as resolve3 } from "path";
397
701
  var python = {
398
702
  id: "python",
399
703
  extensions: [".py"],
704
+ commentStyle: "python",
400
705
  importPatterns: [
401
706
  // from package.module import something
402
707
  { regex: /^from\s+(\.[\w.]*|\w[\w.]*)\s+import\b/gm },
@@ -424,12 +729,23 @@ function tryPythonResolve(base, projectFiles) {
424
729
  var rust = {
425
730
  id: "rust",
426
731
  extensions: [".rs"],
427
- importPatterns: [
428
- // use crate::module::item;
429
- { regex: /\buse\s+crate::(\w[\w:]*)/gm },
430
- // mod child;
431
- { regex: /\bmod\s+(\w+)\s*;/gm }
432
- ],
732
+ commentStyle: "c-style",
733
+ importPatterns: [],
734
+ // handled by extractImports
735
+ extractImports(content) {
736
+ const imports = [];
737
+ const modRegex = /\bmod\s+(\w+)\s*;/gm;
738
+ let match;
739
+ while ((match = modRegex.exec(content)) !== null) {
740
+ imports.push(match[1]);
741
+ }
742
+ const useRegex = /\buse\s+crate::([\s\S]*?);/gm;
743
+ while ((match = useRegex.exec(content)) !== null) {
744
+ const body = match[1].trim();
745
+ extractRustUsePaths(body, "", imports);
746
+ }
747
+ return imports;
748
+ },
433
749
  resolveImport(importPath, sourceFile, rootDir, projectFiles) {
434
750
  const srcDir = join3(rootDir, "src");
435
751
  if (!importPath.includes("::")) {
@@ -452,15 +768,84 @@ var rust = {
452
768
  },
453
769
  defaultExclude: ["target"]
454
770
  };
771
+ function extractRustUsePaths(body, prefix, results) {
772
+ const trimmed = body.trim();
773
+ const braceStart = trimmed.indexOf("{");
774
+ if (braceStart === -1) {
775
+ const path = prefix ? `${prefix}::${trimmed}` : trimmed;
776
+ if (path && !path.includes("{")) {
777
+ results.push(path);
778
+ }
779
+ return;
780
+ }
781
+ let pathPrefix = trimmed.slice(0, braceStart).trim();
782
+ if (pathPrefix.endsWith("::")) {
783
+ pathPrefix = pathPrefix.slice(0, -2);
784
+ }
785
+ const fullPrefix = prefix ? `${prefix}::${pathPrefix}` : pathPrefix;
786
+ let depth = 0;
787
+ let braceEnd = -1;
788
+ for (let i = braceStart; i < trimmed.length; i++) {
789
+ if (trimmed[i] === "{") depth++;
790
+ else if (trimmed[i] === "}") {
791
+ depth--;
792
+ if (depth === 0) {
793
+ braceEnd = i;
794
+ break;
795
+ }
796
+ }
797
+ }
798
+ if (braceEnd === -1) return;
799
+ const inner = trimmed.slice(braceStart + 1, braceEnd).trim();
800
+ const items = splitByTopLevelComma(inner);
801
+ for (const item of items) {
802
+ const cleaned = item.trim();
803
+ if (cleaned === "self") {
804
+ results.push(fullPrefix);
805
+ } else if (cleaned) {
806
+ extractRustUsePaths(cleaned, fullPrefix, results);
807
+ }
808
+ }
809
+ }
810
+ function splitByTopLevelComma(s) {
811
+ const parts = [];
812
+ let depth = 0;
813
+ let start = 0;
814
+ for (let i = 0; i < s.length; i++) {
815
+ if (s[i] === "{") depth++;
816
+ else if (s[i] === "}") depth--;
817
+ else if (s[i] === "," && depth === 0) {
818
+ parts.push(s.slice(start, i));
819
+ start = i + 1;
820
+ }
821
+ }
822
+ parts.push(s.slice(start));
823
+ return parts;
824
+ }
455
825
  var go = {
456
826
  id: "go",
457
827
  extensions: [".go"],
458
- importPatterns: [
459
- // import "path" or import ( "path" )
460
- { regex: /\bimport\s+(?:\w+\s+)?"([^"]+)"/gm },
461
- // import block entries
462
- { regex: /^\s+(?:\w+\s+)?"([^"]+)"/gm }
463
- ],
828
+ commentStyle: "c-style",
829
+ importPatterns: [],
830
+ // handled by extractImports
831
+ extractImports(content) {
832
+ const imports = [];
833
+ const singleRegex = /\bimport\s+(?:\w+\s+)?"([^"]+)"/gm;
834
+ let match;
835
+ while ((match = singleRegex.exec(content)) !== null) {
836
+ imports.push(match[1]);
837
+ }
838
+ const blockRegex = /\bimport\s*\(([^)]*)\)/gms;
839
+ while ((match = blockRegex.exec(content)) !== null) {
840
+ const block = match[1];
841
+ const entryRegex = /(?:\w+\s+)?"([^"]+)"/g;
842
+ let entry;
843
+ while ((entry = entryRegex.exec(block)) !== null) {
844
+ imports.push(entry[1]);
845
+ }
846
+ }
847
+ return imports;
848
+ },
464
849
  resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
465
850
  const modPrefix = goModulePrefix(rootDir);
466
851
  if (!modPrefix || !importPath.startsWith(modPrefix)) return null;
@@ -477,8 +862,7 @@ var goModCache = /* @__PURE__ */ new Map();
477
862
  function goModulePrefix(rootDir) {
478
863
  if (goModCache.has(rootDir)) return goModCache.get(rootDir);
479
864
  try {
480
- const fs = __require("fs");
481
- const content = fs.readFileSync(join3(rootDir, "go.mod"), "utf-8");
865
+ const content = readFileSync(join3(rootDir, "go.mod"), "utf-8");
482
866
  const match = content.match(/^module\s+(.+)$/m);
483
867
  const prefix = match ? match[1].trim() : null;
484
868
  goModCache.set(rootDir, prefix);
@@ -491,6 +875,7 @@ function goModulePrefix(rootDir) {
491
875
  var java = {
492
876
  id: "java",
493
877
  extensions: [".java"],
878
+ commentStyle: "c-style",
494
879
  importPatterns: [
495
880
  // import com.example.ClassName;
496
881
  { regex: /^import\s+(?:static\s+)?([\w.]+);/gm }
@@ -508,6 +893,7 @@ var java = {
508
893
  var cCpp = {
509
894
  id: "c-cpp",
510
895
  extensions: [".c", ".cpp", ".cc", ".cxx", ".h", ".hpp"],
896
+ commentStyle: "c-style",
511
897
  importPatterns: [
512
898
  // #include "file.h" (skip <system> includes)
513
899
  { regex: /^#include\s+"([^"]+)"/gm }
@@ -528,6 +914,7 @@ var cCpp = {
528
914
  var ruby = {
529
915
  id: "ruby",
530
916
  extensions: [".rb"],
917
+ commentStyle: "ruby",
531
918
  importPatterns: [
532
919
  // require_relative 'path'
533
920
  { regex: /\brequire_relative\s+['"]([^'"]+)['"]/gm },
@@ -549,6 +936,7 @@ var ruby = {
549
936
  var php = {
550
937
  id: "php",
551
938
  extensions: [".php"],
939
+ commentStyle: "php",
552
940
  importPatterns: [
553
941
  // require/include/require_once/include_once 'path'
554
942
  { regex: /\b(?:require|include)(?:_once)?\s+['"]([^'"]+)['"]/gm },
@@ -576,11 +964,12 @@ var php = {
576
964
  var swift = {
577
965
  id: "swift",
578
966
  extensions: [".swift"],
967
+ commentStyle: "c-style",
579
968
  importPatterns: [
580
- // import ModuleName
581
- { regex: /^import\s+(?:class|struct|enum|protocol|func|var|let|typealias)?\s*(\w+)/gm }
969
+ // import ModuleName (for cross-module dependencies)
970
+ { regex: /^import\s+(?:class\s+|struct\s+|enum\s+|protocol\s+|func\s+|var\s+|let\s+|typealias\s+)?(\w+)/gm }
582
971
  ],
583
- resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
972
+ resolveImport(importPath, sourceFile, rootDir, projectFiles) {
584
973
  const spmDir = join3(rootDir, "Sources", importPath);
585
974
  for (const f of projectFiles) {
586
975
  if (f.startsWith(spmDir + "/") && f.endsWith(".swift")) return f;
@@ -592,6 +981,7 @@ var swift = {
592
981
  var kotlin = {
593
982
  id: "kotlin",
594
983
  extensions: [".kt", ".kts"],
984
+ commentStyle: "c-style",
595
985
  importPatterns: [
596
986
  // import com.example.ClassName
597
987
  { regex: /^import\s+([\w.]+)/gm }