arcvision 0.2.3 → 0.2.4

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/index.js CHANGED
@@ -4582,6 +4582,124 @@ var require_source = __commonJS({
4582
4582
  }
4583
4583
  });
4584
4584
 
4585
+ // src/core/tsconfig-utils.js
4586
+ var require_tsconfig_utils = __commonJS({
4587
+ "src/core/tsconfig-utils.js"(exports2, module2) {
4588
+ var fs2 = require("fs");
4589
+ var path2 = require("path");
4590
+ function loadTSConfig(startDir) {
4591
+ let currentDir = startDir;
4592
+ const root = path2.parse(currentDir).root;
4593
+ while (currentDir) {
4594
+ const tsconfigPaths = [
4595
+ path2.join(currentDir, "tsconfig.json"),
4596
+ path2.join(currentDir, "jsconfig.json")
4597
+ ];
4598
+ for (const tsconfigPath of tsconfigPaths) {
4599
+ if (fs2.existsSync(tsconfigPath)) {
4600
+ try {
4601
+ let content = fs2.readFileSync(tsconfigPath, "utf-8");
4602
+ if (content.charCodeAt(0) === 65279)
4603
+ content = content.slice(1);
4604
+ let parsed = null;
4605
+ try {
4606
+ parsed = JSON.parse(content);
4607
+ } catch (e) {
4608
+ const stripped = content.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, "$1").replace(/,\s*([\]}])/g, "$1");
4609
+ parsed = JSON.parse(stripped);
4610
+ }
4611
+ if (parsed) {
4612
+ return {
4613
+ options: parsed.compilerOptions || {},
4614
+ configDir: currentDir
4615
+ };
4616
+ }
4617
+ } catch (error) {
4618
+ console.warn(`Warning: Could not parse ${tsconfigPath}:`, error.message);
4619
+ }
4620
+ }
4621
+ }
4622
+ if (currentDir === root)
4623
+ break;
4624
+ currentDir = path2.dirname(currentDir);
4625
+ }
4626
+ return { options: null, configDir: startDir };
4627
+ }
4628
+ module2.exports = { loadTSConfig };
4629
+ }
4630
+ });
4631
+
4632
+ // src/core/workspace-scanner.js
4633
+ var require_workspace_scanner = __commonJS({
4634
+ "src/core/workspace-scanner.js"(exports2, module2) {
4635
+ var fs2 = require("fs");
4636
+ var path2 = require("path");
4637
+ var { loadTSConfig } = require_tsconfig_utils();
4638
+ var WorkspaceScanner = class {
4639
+ constructor(rootPath) {
4640
+ this.rootPath = path2.resolve(rootPath);
4641
+ this.workspaceMap = /* @__PURE__ */ new Map();
4642
+ this.tsconfigMap = /* @__PURE__ */ new Map();
4643
+ }
4644
+ /**
4645
+ * Performs the scan and returns the context.
4646
+ * @returns {Object} { workspaceMap, tsconfigMap }
4647
+ */
4648
+ scan() {
4649
+ console.log(`Starting workspace scan at ${this.rootPath}`);
4650
+ this._scanRecursively(this.rootPath);
4651
+ const sortedWorkspaceMap = new Map([...this.workspaceMap.entries()].sort());
4652
+ const sortedTsconfigMap = new Map([...this.tsconfigMap.entries()].sort());
4653
+ console.log(`Workspace scan complete. Found ${sortedWorkspaceMap.size} packages.`);
4654
+ return {
4655
+ workspaceMap: sortedWorkspaceMap,
4656
+ tsconfigMap: sortedTsconfigMap
4657
+ };
4658
+ }
4659
+ _scanRecursively(currentPath) {
4660
+ let entries;
4661
+ try {
4662
+ entries = fs2.readdirSync(currentPath, { withFileTypes: true });
4663
+ } catch (e) {
4664
+ console.error(`Failed to read directory ${currentPath}: ${e.message}`);
4665
+ return;
4666
+ }
4667
+ entries.sort((a, b) => a.name.localeCompare(b.name));
4668
+ const tsConfig = loadTSConfig(currentPath);
4669
+ if (tsConfig) {
4670
+ this.tsconfigMap.set(currentPath, tsConfig);
4671
+ }
4672
+ for (const entry of entries) {
4673
+ const fullPath = path2.join(currentPath, entry.name);
4674
+ if (entry.isDirectory()) {
4675
+ if (entry.name === "node_modules" || entry.name.startsWith(".")) {
4676
+ continue;
4677
+ }
4678
+ this._scanRecursively(fullPath);
4679
+ } else if (entry.name === "package.json") {
4680
+ this._processPackageJson(fullPath);
4681
+ }
4682
+ }
4683
+ }
4684
+ _processPackageJson(packageJsonPath) {
4685
+ try {
4686
+ const content = fs2.readFileSync(packageJsonPath, "utf-8");
4687
+ const pkg = JSON.parse(content);
4688
+ if (pkg.name) {
4689
+ if (this.workspaceMap.has(pkg.name)) {
4690
+ } else {
4691
+ this.workspaceMap.set(pkg.name, path2.dirname(packageJsonPath));
4692
+ }
4693
+ }
4694
+ } catch (e) {
4695
+ console.warn(`Failed to parse package.json at ${packageJsonPath}: ${e.message}`);
4696
+ }
4697
+ }
4698
+ };
4699
+ module2.exports = { WorkspaceScanner };
4700
+ }
4701
+ });
4702
+
4585
4703
  // node_modules/balanced-match/index.js
4586
4704
  var require_balanced_match = __commonJS({
4587
4705
  "node_modules/balanced-match/index.js"(exports2, module2) {
@@ -56320,12 +56438,15 @@ var require_parser = __commonJS({
56320
56438
  }
56321
56439
  const metadata = {
56322
56440
  id: filePath,
56441
+ ast,
56442
+ // Include AST for enhanced analyzers
56323
56443
  imports: [],
56324
56444
  exports: [],
56325
56445
  functions: [],
56326
56446
  apiCalls: [],
56327
56447
  classes: [],
56328
56448
  variables: [],
56449
+ types: [],
56329
56450
  dependencies: []
56330
56451
  };
56331
56452
  traverse(ast, {
@@ -56419,6 +56540,18 @@ var require_parser = __commonJS({
56419
56540
  });
56420
56541
  }
56421
56542
  });
56543
+ } else if (node.declaration.type === "TSInterfaceDeclaration" && node.declaration.id) {
56544
+ metadata.exports.push({
56545
+ name: node.declaration.id.name,
56546
+ type: "interface",
56547
+ loc: node.declaration.loc
56548
+ });
56549
+ } else if (node.declaration.type === "TSTypeAliasDeclaration" && node.declaration.id) {
56550
+ metadata.exports.push({
56551
+ name: node.declaration.id.name,
56552
+ type: "type",
56553
+ loc: node.declaration.loc
56554
+ });
56422
56555
  }
56423
56556
  }
56424
56557
  },
@@ -56472,13 +56605,30 @@ var require_parser = __commonJS({
56472
56605
  });
56473
56606
  }
56474
56607
  },
56475
- ArrowFunctionExpression({ node }) {
56608
+ ArrowFunctionExpression(path3) {
56609
+ const parent = path3.parent;
56610
+ if (parent.type === "VariableDeclarator" && parent.id.type === "Identifier") {
56611
+ metadata.functions.push({
56612
+ name: parent.id.name,
56613
+ params: path3.node.params.map((p) => p.name || "?"),
56614
+ loc: parent.loc
56615
+ });
56616
+ }
56617
+ },
56618
+ TSInterfaceDeclaration({ node }) {
56619
+ if (node.id && node.id.name) {
56620
+ metadata.types.push({
56621
+ name: node.id.name,
56622
+ kind: "interface",
56623
+ loc: node.loc
56624
+ });
56625
+ }
56476
56626
  },
56477
- ClassDeclaration({ node }) {
56627
+ TSTypeAliasDeclaration({ node }) {
56478
56628
  if (node.id && node.id.name) {
56479
- metadata.classes.push({
56629
+ metadata.types.push({
56480
56630
  name: node.id.name,
56481
- superClass: node.superClass ? node.superClass.name : null,
56631
+ kind: "type",
56482
56632
  loc: node.loc
56483
56633
  });
56484
56634
  }
@@ -56486,11 +56636,23 @@ var require_parser = __commonJS({
56486
56636
  VariableDeclaration({ node }) {
56487
56637
  node.declarations.forEach((decl) => {
56488
56638
  if (decl.id.name) {
56489
- metadata.variables.push({
56639
+ const variableData = {
56490
56640
  name: decl.id.name,
56491
56641
  type: "variable",
56492
56642
  loc: decl.loc
56493
- });
56643
+ };
56644
+ if (decl.init && decl.init.type === "ObjectExpression") {
56645
+ variableData.properties = decl.init.properties.map((p) => {
56646
+ if (p.key && (p.key.name || p.key.value)) {
56647
+ return {
56648
+ name: p.key.name || p.key.value,
56649
+ loc: p.loc
56650
+ };
56651
+ }
56652
+ return null;
56653
+ }).filter(Boolean);
56654
+ }
56655
+ metadata.variables.push(variableData);
56494
56656
  } else if (decl.id.type === "ObjectPattern") {
56495
56657
  decl.id.properties.forEach((prop) => {
56496
56658
  if (prop.key && prop.key.name) {
@@ -56553,9 +56715,27 @@ var require_parser = __commonJS({
56553
56715
  },
56554
56716
  VariableDeclarator({ node }) {
56555
56717
  if (node.init && node.init.type === "CallExpression" && node.init.callee.name === "require" && node.init.arguments.length > 0 && node.init.arguments[0].type === "StringLiteral") {
56718
+ const specifiers = [];
56719
+ if (node.id.type === "ObjectPattern") {
56720
+ node.id.properties.forEach((prop) => {
56721
+ const importedName = prop.key && (prop.key.name || prop.key.value) || null;
56722
+ const localName = prop.value && prop.value.name || importedName;
56723
+ if (importedName) {
56724
+ specifiers.push({
56725
+ imported: importedName,
56726
+ local: localName
56727
+ });
56728
+ }
56729
+ });
56730
+ } else if (node.id.type === "Identifier") {
56731
+ specifiers.push({
56732
+ imported: "default",
56733
+ local: node.id.name
56734
+ });
56735
+ }
56556
56736
  metadata.imports.push({
56557
56737
  source: node.init.arguments[0].value,
56558
- specifiers: [],
56738
+ specifiers,
56559
56739
  type: "require-assignment"
56560
56740
  });
56561
56741
  }
@@ -56740,13 +56920,11 @@ var require_method_tracker = __commonJS({
56740
56920
  const callee = path2.node.callee;
56741
56921
  if (callee.type === "Identifier") {
56742
56922
  const functionName = callee.name;
56743
- if (importedSymbols.has(functionName) || /^[A-Z]/.test(functionName)) {
56744
- functionCalls.push({
56745
- name: functionName,
56746
- type: "function_call",
56747
- loc: path2.node.loc
56748
- });
56749
- }
56923
+ functionCalls.push({
56924
+ name: functionName,
56925
+ type: "function_call",
56926
+ loc: path2.node.loc
56927
+ });
56750
56928
  } else if (callee.type === "MemberExpression") {
56751
56929
  const objectName = extractObjectName(callee.object);
56752
56930
  const methodName = callee.property.name || callee.property.value;
@@ -56765,13 +56943,11 @@ var require_method_tracker = __commonJS({
56765
56943
  const callee = path2.node.callee;
56766
56944
  if (callee.type === "Identifier") {
56767
56945
  const className = callee.name;
56768
- if (importedSymbols.has(className)) {
56769
- constructorCalls.push({
56770
- className,
56771
- type: "constructor_call",
56772
- loc: path2.node.loc
56773
- });
56774
- }
56946
+ constructorCalls.push({
56947
+ className,
56948
+ type: "constructor_call",
56949
+ loc: path2.node.loc
56950
+ });
56775
56951
  }
56776
56952
  }
56777
56953
  });
@@ -57310,34 +57486,96 @@ var require_plugin_manager = __commonJS({
57310
57486
  }
57311
57487
  });
57312
57488
 
57313
- // src/core/tsconfig-utils.js
57314
- var require_tsconfig_utils = __commonJS({
57315
- "src/core/tsconfig-utils.js"(exports2, module2) {
57316
- var fs2 = require("fs");
57489
+ // src/engine/pass1_facts.js
57490
+ var require_pass1_facts = __commonJS({
57491
+ "src/engine/pass1_facts.js"(exports2, module2) {
57492
+ var { glob } = require_commonjs5();
57317
57493
  var path2 = require("path");
57318
- function loadTSConfig(projectRoot) {
57319
- const tsconfigPaths = [
57320
- path2.join(projectRoot, "tsconfig.json"),
57321
- path2.join(projectRoot, "jsconfig.json")
57322
- ];
57323
- for (const tsconfigPath of tsconfigPaths) {
57324
- if (fs2.existsSync(tsconfigPath)) {
57325
- try {
57326
- let content = fs2.readFileSync(tsconfigPath, "utf-8");
57327
- if (content.charCodeAt(0) === 65279) {
57328
- content = content.slice(1);
57494
+ var fs2 = require("fs");
57495
+ var parser = require_parser_enhanced();
57496
+ var pluginManager = require_plugin_manager();
57497
+ async function executePass1(directory, options = {}) {
57498
+ console.log("\u{1F9F1} PASS 1: Syntactic Fact Extraction...");
57499
+ const normalize = (p) => p.replace(/\\/g, "/");
57500
+ const scanOptions = {
57501
+ ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/build/**", ...options.ignore || []],
57502
+ cwd: directory,
57503
+ absolute: true
57504
+ };
57505
+ const pluginDir = path2.join(__dirname, "../plugins");
57506
+ if (fs2.existsSync(pluginDir)) {
57507
+ pluginManager.loadPluginsFromDirectory(pluginDir);
57508
+ }
57509
+ const files = await glob("**/*.{js,jsx,ts,tsx,json}", {
57510
+ ...scanOptions,
57511
+ ignore: [...scanOptions.ignore, "**/*.d.ts", "**/.next/**", "**/coverage/**", "**/arcvision.context.json"]
57512
+ });
57513
+ const rawNodes = [];
57514
+ let totalFacts = 0;
57515
+ for (const file of files) {
57516
+ try {
57517
+ const relativePath = path2.relative(directory, file);
57518
+ const normalizedRelativePath = normalize(relativePath);
57519
+ let metadata = {};
57520
+ if (file.endsWith(".json")) {
57521
+ try {
57522
+ const content = fs2.readFileSync(file, "utf-8");
57523
+ JSON.parse(content);
57524
+ metadata = {
57525
+ id: file,
57526
+ isJson: true,
57527
+ // JSONs might have "imports" in semantic sense (like package.json), but raw syntax is just data
57528
+ raw: content
57529
+ };
57530
+ } catch (e) {
57531
+ console.warn(`\u26A0\uFE0F Invalid JSON in ${normalizedRelativePath}: ${e.message}`);
57532
+ continue;
57329
57533
  }
57330
- const raw = JSON.parse(content);
57331
- return raw.compilerOptions || {};
57332
- } catch (error) {
57333
- console.warn(`Warning: Could not parse ${tsconfigPath}:`, error.message);
57334
- return null;
57335
- }
57534
+ } else {
57535
+ metadata = parser.parseFile(file);
57536
+ }
57537
+ metadata = await pluginManager.processFile(file, metadata);
57538
+ const node = {
57539
+ id: normalizedRelativePath,
57540
+ // Unique ID for this pass
57541
+ filePath: file,
57542
+ type: "file",
57543
+ // The "facts" are the metadata properties
57544
+ facts: {
57545
+ imports: metadata.imports || [],
57546
+ exports: metadata.exports || [],
57547
+ functions: metadata.functions || [],
57548
+ classes: metadata.classes || [],
57549
+ types: metadata.types || [],
57550
+ calls: {
57551
+ functions: metadata.functionCalls || [],
57552
+ methods: metadata.methodCalls || [],
57553
+ constructors: metadata.constructorCalls || []
57554
+ },
57555
+ typeAnalysis: {
57556
+ typeImports: metadata.typeImports || [],
57557
+ interfaceDeps: metadata.interfaceDependencies || [],
57558
+ genericDeps: metadata.genericDependencies || []
57559
+ },
57560
+ di: {
57561
+ injections: metadata.constructorInjections || [],
57562
+ hooks: metadata.hookDependencies || [],
57563
+ context: metadata.contextUsages || []
57564
+ },
57565
+ react: metadata.componentUsage || []
57566
+ }
57567
+ };
57568
+ totalFacts += (metadata.imports?.length || 0) + (metadata.functionCalls?.length || 0);
57569
+ rawNodes.push(node);
57570
+ } catch (e) {
57571
+ console.warn(`\u26A0\uFE0F Pass 1 failed for ${file}: ${e.message}`);
57336
57572
  }
57337
57573
  }
57338
- return null;
57574
+ console.log(` \u2713 Scanned ${rawNodes.length} files`);
57575
+ console.log(` \u2713 Extracted ${totalFacts} raw syntactic facts`);
57576
+ return rawNodes;
57339
57577
  }
57340
- module2.exports = { loadTSConfig };
57578
+ module2.exports = { executePass1 };
57341
57579
  }
57342
57580
  });
57343
57581
 
@@ -57346,7 +57584,7 @@ var require_path_resolver = __commonJS({
57346
57584
  "src/core/path-resolver.js"(exports2, module2) {
57347
57585
  var fs2 = require("fs");
57348
57586
  var path2 = require("path");
57349
- function resolveImport(importPath, importerPath, projectRoot, tsconfig) {
57587
+ function resolveImport(importPath, importerPath, projectRoot, tsconfig, workspaceMap) {
57350
57588
  if (importPath.startsWith("http://") || importPath.startsWith("https://") || importPath.startsWith("data:") || importPath.startsWith("file:")) {
57351
57589
  return null;
57352
57590
  }
@@ -57377,8 +57615,8 @@ var require_path_resolver = __commonJS({
57377
57615
  const aliasTargets = paths[aliasPattern];
57378
57616
  if (aliasPattern.includes("*")) {
57379
57617
  const patternBase = aliasPattern.replace(/\*.*$/, "");
57380
- if (importPath.startsWith(patternBase)) {
57381
- const suffix = importPath.substring(patternBase.length);
57618
+ if (importPath === patternBase.slice(0, -1) || importPath.startsWith(patternBase)) {
57619
+ const suffix = importPath.length >= patternBase.length ? importPath.substring(patternBase.length) : "";
57382
57620
  for (const target of aliasTargets) {
57383
57621
  const targetBase = target.replace(/\*.*$/, "");
57384
57622
  const targetPath = path2.resolve(projectRoot, path2.join(targetBase, suffix));
@@ -57448,6 +57686,51 @@ var require_path_resolver = __commonJS({
57448
57686
  return resolvedPath;
57449
57687
  }
57450
57688
  }
57689
+ if (workspaceMap && workspaceMap.has(importPath)) {
57690
+ const packageRoot = workspaceMap.get(importPath);
57691
+ try {
57692
+ const pkgPath = path2.join(packageRoot, "package.json");
57693
+ if (fs2.existsSync(pkgPath)) {
57694
+ const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
57695
+ const mainFile = pkg.main || pkg.module || "index.js";
57696
+ const searchPath = path2.join(packageRoot, mainFile);
57697
+ const extensions = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".json"];
57698
+ for (const ext of extensions) {
57699
+ const p = searchPath + ext;
57700
+ if (fs2.existsSync(p))
57701
+ return p;
57702
+ }
57703
+ }
57704
+ const indexFiles = ["index.ts", "index.tsx", "index.js", "index.jsx"];
57705
+ for (const idx of indexFiles) {
57706
+ const p = path2.join(packageRoot, idx);
57707
+ if (fs2.existsSync(p))
57708
+ return p;
57709
+ }
57710
+ } catch (e) {
57711
+ }
57712
+ return packageRoot;
57713
+ }
57714
+ if (workspaceMap) {
57715
+ for (const [pkgName, pkgRoot] of workspaceMap.entries()) {
57716
+ if (importPath.startsWith(pkgName + "/")) {
57717
+ const suffix = importPath.substring(pkgName.length + 1);
57718
+ const targetPath = path2.join(pkgRoot, suffix);
57719
+ const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".json", ""];
57720
+ for (const ext of extensions) {
57721
+ if (fs2.existsSync(targetPath + ext)) {
57722
+ return targetPath + ext;
57723
+ }
57724
+ }
57725
+ const indexFiles = ["index.ts", "index.tsx", "index.js", "index.jsx"];
57726
+ for (const idx of indexFiles) {
57727
+ const p = path2.join(targetPath, idx);
57728
+ if (fs2.existsSync(p))
57729
+ return p;
57730
+ }
57731
+ }
57732
+ }
57733
+ }
57451
57734
  if (!importPath.startsWith(".")) {
57452
57735
  try {
57453
57736
  const resolved = require.resolve(importPath, { paths: [path2.dirname(importerPath)] });
@@ -57461,6 +57744,509 @@ var require_path_resolver = __commonJS({
57461
57744
  }
57462
57745
  });
57463
57746
 
57747
+ // src/core/symbol-indexer.js
57748
+ var require_symbol_indexer = __commonJS({
57749
+ "src/core/symbol-indexer.js"(exports2, module2) {
57750
+ var crypto = require("crypto");
57751
+ var SymbolIndexer = class {
57752
+ constructor() {
57753
+ this.symbols = /* @__PURE__ */ new Map();
57754
+ this.fileToSymbols = /* @__PURE__ */ new Map();
57755
+ }
57756
+ /**
57757
+ * Build symbol index from raw files
57758
+ * @param {Array<Object>} rawFiles - List of files with metadata from parser
57759
+ * @returns {Object} { symbols, fileToSymbols }
57760
+ */
57761
+ index(rawFiles) {
57762
+ console.log(`Indexing symbols for ${rawFiles.length} files...`);
57763
+ this.reExports = /* @__PURE__ */ new Map();
57764
+ const sortedFiles = [...rawFiles].sort((a, b) => a.id.localeCompare(b.id));
57765
+ for (const file of sortedFiles) {
57766
+ if (!file.metadata)
57767
+ continue;
57768
+ this._indexFile(file);
57769
+ }
57770
+ this._propagateReExports();
57771
+ console.log(`Indexed ${this.symbols.size} symbols.`);
57772
+ return {
57773
+ symbols: this.symbols,
57774
+ fileToSymbols: this.fileToSymbols
57775
+ };
57776
+ }
57777
+ _indexFile(file) {
57778
+ const fileId = file.id;
57779
+ const metadata = file.metadata;
57780
+ const addSymbol = (name, kind, exported = false, loc = null) => {
57781
+ const id = `${fileId}::${name}`;
57782
+ const symbol = { id, name, kind, fileId, exported, loc };
57783
+ this.symbols.set(id, symbol);
57784
+ if (!this.fileToSymbols.has(fileId)) {
57785
+ this.fileToSymbols.set(fileId, /* @__PURE__ */ new Set());
57786
+ }
57787
+ this.fileToSymbols.get(fileId).add(id);
57788
+ return symbol;
57789
+ };
57790
+ if (metadata.exports) {
57791
+ metadata.exports.forEach((exp) => {
57792
+ addSymbol(exp.name, exp.type || "variable", true, exp.loc);
57793
+ if (exp.type === "default" && exp.name !== "default") {
57794
+ addSymbol("default", "default", true, exp.loc);
57795
+ }
57796
+ });
57797
+ }
57798
+ if (metadata.functions) {
57799
+ metadata.functions.forEach((func) => {
57800
+ const id = `${fileId}::${func.name}`;
57801
+ if (!this.symbols.has(id)) {
57802
+ addSymbol(func.name, "function", false, func.loc);
57803
+ } else {
57804
+ const sym = this.symbols.get(id);
57805
+ if (sym.kind === "variable")
57806
+ sym.kind = "function";
57807
+ }
57808
+ });
57809
+ }
57810
+ if (metadata.imports) {
57811
+ metadata.imports.forEach((imp) => {
57812
+ if (imp.type === "export-all" && imp.resolvedPath) {
57813
+ if (!this.reExports.has(fileId))
57814
+ this.reExports.set(fileId, /* @__PURE__ */ new Set());
57815
+ this.reExports.get(fileId).add(imp.resolvedPath);
57816
+ }
57817
+ });
57818
+ }
57819
+ if (metadata.classes) {
57820
+ metadata.classes.forEach((c) => {
57821
+ const id = `${fileId}::${c.name}`;
57822
+ if (this.symbols.has(id)) {
57823
+ this.symbols.get(id).kind = "class";
57824
+ } else {
57825
+ addSymbol(c.name, "class", false, c.loc);
57826
+ }
57827
+ });
57828
+ }
57829
+ if (metadata.types) {
57830
+ metadata.types.forEach((t) => {
57831
+ const id = `${fileId}::${t.name}`;
57832
+ if (this.symbols.has(id)) {
57833
+ this.symbols.get(id).kind = t.kind;
57834
+ } else {
57835
+ addSymbol(t.name, t.kind, false, t.loc);
57836
+ }
57837
+ });
57838
+ }
57839
+ if (metadata.variables) {
57840
+ metadata.variables.forEach((v) => {
57841
+ addSymbol(v.name, "variable", false, v.loc);
57842
+ if (v.properties && v.properties.length > 0) {
57843
+ v.properties.forEach((prop) => {
57844
+ addSymbol(`${v.name}.${prop.name}`, "method", false, prop.loc);
57845
+ });
57846
+ }
57847
+ });
57848
+ }
57849
+ }
57850
+ _propagateReExports() {
57851
+ const visited = /* @__PURE__ */ new Set();
57852
+ const propagate = (fileId) => {
57853
+ if (visited.has(fileId))
57854
+ return;
57855
+ visited.add(fileId);
57856
+ const targets = this.reExports.get(fileId);
57857
+ if (!targets)
57858
+ return;
57859
+ for (const targetPath of targets) {
57860
+ propagate(targetPath);
57861
+ const targetSymbolIds = this.fileToSymbols.get(targetPath);
57862
+ if (targetSymbolIds) {
57863
+ for (const symId of targetSymbolIds) {
57864
+ const originalSym = this.symbols.get(symId);
57865
+ if (originalSym && originalSym.exported) {
57866
+ const localId = `${fileId}::${originalSym.name}`;
57867
+ if (!this.symbols.has(localId)) {
57868
+ const newSym = { ...originalSym, id: localId, fileId };
57869
+ this.symbols.set(localId, newSym);
57870
+ if (!this.fileToSymbols.has(fileId))
57871
+ this.fileToSymbols.set(fileId, /* @__PURE__ */ new Set());
57872
+ this.fileToSymbols.get(fileId).add(localId);
57873
+ }
57874
+ }
57875
+ }
57876
+ }
57877
+ }
57878
+ };
57879
+ for (const fileId of this.reExports.keys()) {
57880
+ propagate(fileId);
57881
+ }
57882
+ }
57883
+ };
57884
+ module2.exports = { SymbolIndexer };
57885
+ }
57886
+ });
57887
+
57888
+ // src/core/call-resolver.js
57889
+ var require_call_resolver = __commonJS({
57890
+ "src/core/call-resolver.js"(exports2, module2) {
57891
+ var CallResolver = class {
57892
+ constructor(symbolIndex, fileMap) {
57893
+ this.symbolIndex = symbolIndex;
57894
+ this.fileMap = fileMap;
57895
+ }
57896
+ /**
57897
+ * Resolve calls in all files
57898
+ * @returns {Array<Object>} List of edges
57899
+ */
57900
+ resolve(rawFiles) {
57901
+ const edges = [];
57902
+ console.log(`Resolving calls for ${rawFiles.length} files...`);
57903
+ for (const file of rawFiles) {
57904
+ const fileEdges = this._resolveFile(file);
57905
+ edges.push(...fileEdges);
57906
+ }
57907
+ console.log(`Resolved ${edges.length} call/instantiate edges.`);
57908
+ return edges;
57909
+ }
57910
+ _resolveFile(file) {
57911
+ const edges = [];
57912
+ if (!file.methodCalls)
57913
+ return edges;
57914
+ const fileId = file.id;
57915
+ const { functionCalls, constructorCalls, methodCalls } = file.methodCalls;
57916
+ const imports = file.metadata.imports || [];
57917
+ const resolveImportedSymbol = (symbolName) => {
57918
+ for (const imp of imports) {
57919
+ if (imp.specifiers) {
57920
+ const spec = imp.specifiers.find((s) => s.local === symbolName);
57921
+ if (spec) {
57922
+ if (imp.resolvedPath) {
57923
+ const targetFileId = imp.resolvedPath;
57924
+ const targetSymbolName = spec.imported === "default" ? "default" : spec.imported;
57925
+ let candidateId = `${targetFileId}::${targetSymbolName}`;
57926
+ if (this.symbolIndex.has(candidateId))
57927
+ return candidateId;
57928
+ if (spec.imported === "default") {
57929
+ candidateId = `${targetFileId}::default`;
57930
+ if (this.symbolIndex.has(candidateId))
57931
+ return candidateId;
57932
+ }
57933
+ }
57934
+ }
57935
+ }
57936
+ }
57937
+ const localId = `${fileId}::${symbolName}`;
57938
+ if (this.symbolIndex.has(localId)) {
57939
+ return localId;
57940
+ }
57941
+ return null;
57942
+ };
57943
+ if (functionCalls) {
57944
+ for (const call of functionCalls) {
57945
+ const targetSymbolId = resolveImportedSymbol(call.name);
57946
+ if (targetSymbolId) {
57947
+ edges.push({
57948
+ sourceFileId: fileId,
57949
+ targetSymbolId,
57950
+ type: "CALLS",
57951
+ line: call.loc ? call.loc.start.line : 0,
57952
+ confidence: 1
57953
+ });
57954
+ }
57955
+ }
57956
+ }
57957
+ if (constructorCalls) {
57958
+ for (const call of constructorCalls) {
57959
+ const targetSymbolId = resolveImportedSymbol(call.className);
57960
+ if (targetSymbolId) {
57961
+ edges.push({
57962
+ sourceFileId: fileId,
57963
+ targetSymbolId,
57964
+ type: "INSTANTIATES",
57965
+ line: call.loc ? call.loc.start.line : 0,
57966
+ confidence: 1
57967
+ });
57968
+ }
57969
+ }
57970
+ }
57971
+ if (methodCalls) {
57972
+ for (const call of methodCalls) {
57973
+ const imp = imports.find((i) => i.specifiers?.some((s) => s.local === call.object));
57974
+ if (imp && imp.resolvedPath) {
57975
+ const targetFileId = imp.resolvedPath;
57976
+ const spec = imp.specifiers.find((s) => s.local === call.object);
57977
+ const importedAs = spec ? spec.imported : null;
57978
+ let candidateId = `${targetFileId}::${call.method}`;
57979
+ if (this.symbolIndex.has(candidateId)) {
57980
+ edges.push({
57981
+ sourceFileId: fileId,
57982
+ targetSymbolId: candidateId,
57983
+ type: "CALLS",
57984
+ line: call.loc ? call.loc.start.line : 0,
57985
+ confidence: 1
57986
+ });
57987
+ continue;
57988
+ }
57989
+ if (importedAs) {
57990
+ const targetSymbolName = importedAs === "default" ? "default" : importedAs;
57991
+ candidateId = `${targetFileId}::${targetSymbolName}.${call.method}`;
57992
+ if (this.symbolIndex.has(candidateId)) {
57993
+ edges.push({
57994
+ sourceFileId: fileId,
57995
+ targetSymbolId: candidateId,
57996
+ type: "CALLS",
57997
+ line: call.loc ? call.loc.start.line : 0,
57998
+ confidence: 1
57999
+ });
58000
+ continue;
58001
+ }
58002
+ }
58003
+ }
58004
+ const localObjectId = `${fileId}::${call.object}.${call.method}`;
58005
+ if (this.symbolIndex.has(localObjectId)) {
58006
+ edges.push({
58007
+ sourceFileId: fileId,
58008
+ targetSymbolId: localObjectId,
58009
+ type: "CALLS",
58010
+ line: call.loc ? call.loc.start.line : 0,
58011
+ confidence: 1
58012
+ });
58013
+ }
58014
+ }
58015
+ }
58016
+ return edges;
58017
+ }
58018
+ };
58019
+ module2.exports = { CallResolver };
58020
+ }
58021
+ });
58022
+
58023
+ // src/engine/pass2_semantics.js
58024
+ var require_pass2_semantics = __commonJS({
58025
+ "src/engine/pass2_semantics.js"(exports2, module2) {
58026
+ var path2 = require("path");
58027
+ var { loadTSConfig } = require_tsconfig_utils();
58028
+ var { resolveImport } = require_path_resolver();
58029
+ var { SymbolIndexer } = require_symbol_indexer();
58030
+ var { CallResolver } = require_call_resolver();
58031
+ async function executePass2(rawNodes, rootDir, workspaceContext) {
58032
+ console.log("\u{1F9E0} PASS 2: Semantic Resolution...");
58033
+ const config = loadTSConfig(rootDir);
58034
+ const tsconfig = config.options;
58035
+ const configRoot = config.configDir;
58036
+ const normalize = (p) => p.replace(/\\/g, "/");
58037
+ const workspaceMap = workspaceContext ? workspaceContext.workspaceMap : null;
58038
+ const fileSet = new Set(rawNodes.map((n) => n.id));
58039
+ const edges = [];
58040
+ const addEdge = (source, target, type, confidence = 1, metadata = {}) => {
58041
+ if (!source || !target)
58042
+ return;
58043
+ if (source === target)
58044
+ return;
58045
+ edges.push({
58046
+ source,
58047
+ target,
58048
+ type,
58049
+ confidence,
58050
+ metadata
58051
+ });
58052
+ };
58053
+ let resolvedImports = 0;
58054
+ for (const node of rawNodes) {
58055
+ if (!node.facts.imports)
58056
+ continue;
58057
+ for (const imp of node.facts.imports) {
58058
+ const importString = imp.source;
58059
+ let targetId = null;
58060
+ let resolvedPath = null;
58061
+ const resolvedAbs = resolveImport(importString, node.filePath, configRoot, tsconfig, workspaceMap);
58062
+ if (resolvedAbs) {
58063
+ targetId = normalize(path2.relative(rootDir, resolvedAbs));
58064
+ resolvedPath = targetId;
58065
+ imp.resolvedPath = targetId;
58066
+ } else {
58067
+ if (importString.startsWith(".")) {
58068
+ try {
58069
+ const nodeDir = path2.dirname(node.filePath);
58070
+ const simpleAbs = path2.resolve(nodeDir, importString);
58071
+ const exts = [".ts", ".tsx", ".js", ".jsx", ".json", "/index.ts", "/index.js"];
58072
+ for (const ext of [""].concat(exts)) {
58073
+ const candidate = normalize(path2.relative(rootDir, simpleAbs + ext));
58074
+ if (fileSet.has(candidate)) {
58075
+ targetId = candidate;
58076
+ imp.resolvedPath = candidate;
58077
+ break;
58078
+ }
58079
+ }
58080
+ } catch (e) {
58081
+ }
58082
+ }
58083
+ }
58084
+ if (targetId && fileSet.has(targetId)) {
58085
+ let type = "imports";
58086
+ if (workspaceContext && workspaceContext.workspaceMap) {
58087
+ }
58088
+ addEdge(node.id, targetId, type, 1, { specifiers: imp.specifiers });
58089
+ resolvedImports++;
58090
+ }
58091
+ }
58092
+ }
58093
+ console.log(` \u2713 Resolved ${resolvedImports} import dependencies`);
58094
+ const symbolIndexer = new SymbolIndexer();
58095
+ const nodesForIndexing = rawNodes.map((n) => ({
58096
+ id: n.id,
58097
+ metadata: n.facts
58098
+ // Indexer expects metadata.exports, etc. (Pass 1 facts structure matches enough)
58099
+ }));
58100
+ const { symbols, fileToSymbols } = symbolIndexer.index(nodesForIndexing);
58101
+ const callResolver = new CallResolver(symbols, null);
58102
+ const nodesForResolution = rawNodes.map((n) => {
58103
+ const combinedFunctions = [
58104
+ ...n.facts.calls.functions || [],
58105
+ ...(n.facts.react || []).map((r) => ({
58106
+ name: r.component,
58107
+ type: "jsx_component",
58108
+ loc: r.loc
58109
+ }))
58110
+ ];
58111
+ return {
58112
+ id: n.id,
58113
+ metadata: n.facts,
58114
+ methodCalls: {
58115
+ functionCalls: combinedFunctions,
58116
+ methodCalls: n.facts.calls.methods,
58117
+ constructorCalls: n.facts.calls.constructors
58118
+ }
58119
+ };
58120
+ });
58121
+ const callEdges = callResolver.resolve(nodesForResolution);
58122
+ let resolvedCalls = 0;
58123
+ for (const edge of callEdges) {
58124
+ const symbol = symbols.get(edge.targetSymbolId);
58125
+ if (symbol) {
58126
+ const targetFileId = symbol.fileId;
58127
+ if (fileSet.has(targetFileId)) {
58128
+ addEdge(edge.sourceFileId, targetFileId, edge.type, edge.confidence, {
58129
+ symbol: symbol.name,
58130
+ symbolId: edge.targetSymbolId,
58131
+ line: edge.line
58132
+ });
58133
+ resolvedCalls++;
58134
+ }
58135
+ }
58136
+ }
58137
+ console.log(` \u2713 Resolved ${resolvedCalls} semantic call edges`);
58138
+ let typeEdgesCount = 0;
58139
+ for (const node of rawNodes) {
58140
+ if (!node.facts.typeAnalysis)
58141
+ continue;
58142
+ const { typeImports, interfaceDeps, genericDeps } = node.facts.typeAnalysis;
58143
+ for (const imp of typeImports) {
58144
+ const resolvedAbs = resolveImport(imp.source, node.filePath, rootDir, tsconfig, workspaceMap);
58145
+ if (resolvedAbs) {
58146
+ const targetId = normalize(path2.relative(rootDir, resolvedAbs));
58147
+ if (fileSet.has(targetId)) {
58148
+ addEdge(node.id, targetId, "depends_on", 0.9, { type: "type_import" });
58149
+ typeEdgesCount++;
58150
+ }
58151
+ }
58152
+ }
58153
+ const allTypeDeps = [...interfaceDeps, ...genericDeps];
58154
+ for (const dep of allTypeDeps) {
58155
+ const typeName = dep.extends || dep.implements || dep.returnType || dep.type;
58156
+ if (!typeName)
58157
+ continue;
58158
+ for (const imp of node.facts.imports || []) {
58159
+ if (imp.resolvedPath) {
58160
+ const targetFileId = imp.resolvedPath;
58161
+ const candidateId = `${targetFileId}::${typeName}`;
58162
+ if (symbols.has(candidateId)) {
58163
+ addEdge(node.id, targetFileId, "depends_on", 1, { type: "type_reference", symbol: typeName });
58164
+ typeEdgesCount++;
58165
+ break;
58166
+ }
58167
+ }
58168
+ }
58169
+ }
58170
+ }
58171
+ console.log(` \u2713 Resolved ${typeEdgesCount} TypeScript type connections`);
58172
+ return {
58173
+ nodes: rawNodes,
58174
+ edges,
58175
+ symbols: Array.from(symbols.values())
58176
+ // Return symbols list for ContextBuilder
58177
+ };
58178
+ }
58179
+ module2.exports = { executePass2 };
58180
+ }
58181
+ });
58182
+
58183
+ // src/engine/pass3_lifter.js
58184
+ var require_pass3_lifter = __commonJS({
58185
+ "src/engine/pass3_lifter.js"(exports2, module2) {
58186
+ async function executePass3(semanticData) {
58187
+ console.log("\u{1F9EC} PASS 3: Structural Lifting...");
58188
+ const { nodes, edges } = semanticData;
58189
+ const liftedNodes = [];
58190
+ let rolesFound = {};
58191
+ for (const node of nodes) {
58192
+ let role = "module";
58193
+ let layer = "generic";
58194
+ const pathLower = node.id.toLowerCase();
58195
+ const filename = node.id.split("/").pop();
58196
+ if (pathLower.includes("service") || filename.endsWith(".service.ts")) {
58197
+ role = "service";
58198
+ layer = "logic";
58199
+ } else if (pathLower.includes("controller") || filename.endsWith(".controller.ts")) {
58200
+ role = "controller";
58201
+ layer = "api";
58202
+ } else if (pathLower.includes("component") || pathLower.includes(".tsx") || pathLower.includes(".jsx")) {
58203
+ role = "component";
58204
+ layer = "ui";
58205
+ } else if (pathLower.includes("store") || pathLower.includes("redux") || pathLower.includes("context")) {
58206
+ role = "store";
58207
+ layer = "state";
58208
+ } else if (pathLower.includes("util") || pathLower.includes("helper")) {
58209
+ role = "utility";
58210
+ layer = "shared";
58211
+ } else if (pathLower.includes("model") || pathLower.includes("entity") || pathLower.includes("dto")) {
58212
+ role = "model";
58213
+ layer = "data";
58214
+ } else if (pathLower.includes("config") || pathLower.includes("env")) {
58215
+ role = "config";
58216
+ } else if (pathLower.includes("hook") || filename.startsWith("use")) {
58217
+ role = "hook";
58218
+ layer = "ui-logic";
58219
+ }
58220
+ if (node.facts.di && (node.facts.di.injections.length > 0 || node.facts.di.context.length > 0)) {
58221
+ if (role === "module")
58222
+ role = "consumer";
58223
+ }
58224
+ if (node.facts.exports.some((e) => e.type === "class")) {
58225
+ if (role === "module")
58226
+ role = "class_def";
58227
+ }
58228
+ rolesFound[role] = (rolesFound[role] || 0) + 1;
58229
+ liftedNodes.push({
58230
+ ...node,
58231
+ structure: {
58232
+ role,
58233
+ layer
58234
+ }
58235
+ });
58236
+ }
58237
+ console.log(` \u2713 Inferred structural roles: ${JSON.stringify(rolesFound)}`);
58238
+ return {
58239
+ nodes: liftedNodes,
58240
+ edges,
58241
+ // Edges passed through
58242
+ symbols: semanticData.symbols
58243
+ // Propagate symbols
58244
+ };
58245
+ }
58246
+ module2.exports = { executePass3 };
58247
+ }
58248
+ });
58249
+
57464
58250
  // src/core/blastRadius.js
57465
58251
  var require_blastRadius = __commonJS({
57466
58252
  "src/core/blastRadius.js"(exports2, module2) {
@@ -57582,7 +58368,12 @@ var require_blastRadius = __commonJS({
57582
58368
  // Consider files with blast radius > 5 as hubs
57583
58369
  });
57584
58370
  }
57585
- return criticalityAnalysis.sort((a, b) => b.criticalityScore - a.criticalityScore);
58371
+ return criticalityAnalysis.sort((a, b) => {
58372
+ if (b.criticalityScore !== a.criticalityScore) {
58373
+ return b.criticalityScore - a.criticalityScore;
58374
+ }
58375
+ return a.file.localeCompare(b.file);
58376
+ });
57586
58377
  }
57587
58378
  function calculateDependencyStrength(reverseGraph, file) {
57588
58379
  if (!reverseGraph[file] || !Array.isArray(reverseGraph[file])) {
@@ -57608,6 +58399,76 @@ var require_blastRadius = __commonJS({
57608
58399
  }
57609
58400
  });
57610
58401
 
58402
+ // src/engine/pass4_signals.js
58403
+ var require_pass4_signals = __commonJS({
58404
+ "src/engine/pass4_signals.js"(exports2, module2) {
58405
+ var { buildReverseDependencyGraph, computeBlastRadius, analyzeCriticality } = require_blastRadius();
58406
+ async function executePass4(liftedData) {
58407
+ console.log("\u{1F9E0} PASS 4: Signal Extraction & Metrics...");
58408
+ const { nodes, edges } = liftedData;
58409
+ const architectureMap = { nodes, edges };
58410
+ const reverseGraph = buildReverseDependencyGraph(architectureMap);
58411
+ const blastRadiusMap = computeBlastRadius(reverseGraph);
58412
+ nodes.forEach((n) => {
58413
+ n.metadata = n.facts;
58414
+ n.metadata.blast_radius = blastRadiusMap[n.id] || 0;
58415
+ });
58416
+ const criticalityList = analyzeCriticality(blastRadiusMap, reverseGraph, nodes);
58417
+ const criticalityMap = new Map(criticalityList.map((c) => [c.file, c]));
58418
+ const finalNodes = nodes.map((node) => {
58419
+ const signals = {
58420
+ blast_radius: blastRadiusMap[node.id] || 0,
58421
+ criticality: criticalityMap.get(node.id)?.criticalityScore || 0,
58422
+ is_hub: criticalityMap.get(node.id)?.isHub || false,
58423
+ // Calculate instability: closer to 1 means "depends on many things" (outgoing edges)
58424
+ // (coupling)
58425
+ outgoing_deps: edges.filter((e) => e.source === node.id).length,
58426
+ incoming_deps: edges.filter((e) => e.target === node.id).length
58427
+ };
58428
+ return {
58429
+ id: node.id,
58430
+ type: node.type,
58431
+ filePath: node.filePath,
58432
+ // Internal absolute path
58433
+ // The 3 Pillars of Arcvision
58434
+ role: node.structure.role,
58435
+ // From Pass 3
58436
+ layer: node.structure.layer,
58437
+ // From Pass 3
58438
+ signals,
58439
+ // From Pass 4
58440
+ // Deep Code Intelligence (from Pass 1 & 2)
58441
+ intelligence: {
58442
+ ...node.facts,
58443
+ // Add resolved outbound connections for easy traversal
58444
+ connections: edges.filter((e) => e.source === node.id).map((e) => ({
58445
+ target: e.target,
58446
+ type: e.type,
58447
+ confidence: e.confidence
58448
+ }))
58449
+ }
58450
+ };
58451
+ });
58452
+ const totalFiles = finalNodes.length;
58453
+ const maxBlast = Math.max(...finalNodes.map((n) => n.signals.blast_radius));
58454
+ console.log(` \u2713 Signals computed. Max Blast Radius: ${maxBlast}`);
58455
+ const contextSurface = criticalityList.slice(0, 5).map((c) => ({
58456
+ file: c.file,
58457
+ score: c.criticalityScore,
58458
+ reason: `Blast: ${c.blastRadius}, Deps: ${c.dependencies}`
58459
+ }));
58460
+ return {
58461
+ nodes: finalNodes,
58462
+ edges,
58463
+ symbols: liftedData.symbols,
58464
+ // Propagate symbols
58465
+ contextSurface
58466
+ };
58467
+ }
58468
+ module2.exports = { executePass4 };
58469
+ }
58470
+ });
58471
+
57611
58472
  // src/engine/id-generator.js
57612
58473
  var require_id_generator = __commonJS({
57613
58474
  "src/engine/id-generator.js"(exports2, module2) {
@@ -57620,92 +58481,109 @@ var require_id_generator = __commonJS({
57620
58481
  }
57621
58482
  });
57622
58483
 
57623
- // src/engine/context_builder.js
58484
+ // src/core/context_builder.js
57624
58485
  var require_context_builder = __commonJS({
57625
- "src/engine/context_builder.js"(exports2, module2) {
58486
+ "src/core/context_builder.js"(exports2, module2) {
57626
58487
  var path2 = require("path");
57627
58488
  var { stableId } = require_id_generator();
57628
- function buildContext(files, edges, options = {}) {
58489
+ function buildContext(fileNodes, edges, symbols, options = {}) {
57629
58490
  const {
57630
58491
  directory = ".",
57631
58492
  projectName = "arcvision",
57632
58493
  language = "javascript"
57633
58494
  } = options;
57634
- const nodes = files.map((file) => {
57635
- const normalizedPath = file.id;
57636
- const hasFunctions = file.metadata.functions && file.metadata.functions.length > 0;
57637
- const hasClasses = file.metadata.classes && file.metadata.classes.length > 0;
57638
- const hasExports = file.metadata.exports && file.metadata.exports.length > 0;
57639
- const hasApiCalls = file.metadata.apiCalls && file.metadata.apiCalls.length > 0;
57640
- let role = "Structure";
57641
- if (hasFunctions || hasClasses || hasApiCalls) {
57642
- role = "Implementation";
57643
- } else if (hasExports) {
57644
- role = "Interface";
57645
- }
58495
+ const nodes = fileNodes.map((file) => {
58496
+ const role = file.role || "Structure";
58497
+ const layer = file.layer || "generic";
57646
58498
  const dependencies = [];
57647
58499
  const uniqueDeps = /* @__PURE__ */ new Set();
57648
- if (file.metadata.imports && Array.isArray(file.metadata.imports)) {
57649
- file.metadata.imports.forEach((imp) => {
57650
- if (imp.source && typeof imp.source === "string") {
57651
- if (!uniqueDeps.has(imp.source)) {
57652
- uniqueDeps.add(imp.source);
57653
- dependencies.push(imp.source);
57654
- }
57655
- }
57656
- });
58500
+ const addDep = (target) => {
58501
+ if (target && !uniqueDeps.has(target)) {
58502
+ uniqueDeps.add(target);
58503
+ dependencies.push(target);
58504
+ }
58505
+ };
58506
+ if (file.intelligence && file.intelligence.connections) {
58507
+ file.intelligence.connections.forEach((c) => addDep(c.target));
58508
+ } else {
58509
+ edges.filter((e) => e.source === file.id).forEach((e) => addDep(e.target));
57657
58510
  }
57658
- return {
57659
- id: stableId(normalizedPath),
58511
+ const nodeObj = {
58512
+ id: stableId(file.id),
57660
58513
  type: "file",
57661
- path: normalizedPath,
58514
+ path: file.id,
57662
58515
  role,
57663
58516
  dependencies,
57664
- blast_radius: file.metadata.blast_radius || 0
58517
+ blast_radius: file.signals && file.signals.blast_radius || 0,
58518
+ // New Schema Extensions (if supported by backend, otherwise ignored)
58519
+ layer,
58520
+ criticality: file.signals ? file.signals.criticality : 0
57665
58521
  };
58522
+ return nodeObj;
57666
58523
  });
57667
- const schemaEdges = [];
58524
+ const nodeMap = new Map(nodes.map((n) => [n.path, n]));
58525
+ const edgeKeyMap = /* @__PURE__ */ new Map();
57668
58526
  for (const edge of edges) {
57669
- const sourceNode = nodes.find((n) => n.path === edge.source);
57670
- const targetNode = nodes.find((n) => n.path === edge.target);
57671
- if (sourceNode) {
58527
+ const sourceNode = nodeMap.get(edge.source);
58528
+ const targetNode = nodeMap.get(edge.target);
58529
+ if (sourceNode && targetNode) {
57672
58530
  let relationType = "imports";
57673
- if (edge.type === "imports" || edge.type === "require" || edge.type === "export-from" || edge.type === "export-all" || edge.type === "dynamic-import" || edge.type === "require-assignment") {
57674
- relationType = "imports";
57675
- } else if (edge.type === "function_call" || edge.type === "method_call") {
57676
- relationType = "calls";
57677
- } else if (edge.type === "constructor_call") {
57678
- relationType = "calls";
57679
- } else if (edge.type === "component_usage" || edge.type === "jsx_component" || edge.type === "jsx_member_component") {
57680
- relationType = "imports";
57681
- } else if (edge.type === "calls") {
57682
- relationType = "calls";
57683
- } else if (edge.type === "owns") {
57684
- relationType = "owns";
57685
- } else if (edge.type === "depends_on") {
57686
- relationType = "depends_on";
57687
- } else {
57688
- relationType = "imports";
58531
+ switch (edge.type) {
58532
+ case "function_call":
58533
+ case "method_call":
58534
+ case "constructor_call":
58535
+ case "calls":
58536
+ case "CALLS":
58537
+ case "instantiates":
58538
+ case "INSTANTIATES":
58539
+ case "jsx_component":
58540
+ relationType = "calls";
58541
+ break;
58542
+ case "injects":
58543
+ case "depends_on":
58544
+ relationType = "depends_on";
58545
+ break;
58546
+ case "owns":
58547
+ relationType = "owns";
58548
+ break;
58549
+ case "imports":
58550
+ case "require":
58551
+ default:
58552
+ relationType = "imports";
58553
+ break;
57689
58554
  }
57690
- if (targetNode) {
57691
- schemaEdges.push({
58555
+ const key = `${sourceNode.id}::${targetNode.id}::${relationType}`;
58556
+ if (edgeKeyMap.has(key)) {
58557
+ edgeKeyMap.get(key).weight = (edgeKeyMap.get(key).weight || 1) + 1;
58558
+ } else {
58559
+ edgeKeyMap.set(key, {
57692
58560
  from: sourceNode.id,
57693
58561
  to: targetNode.id,
57694
- relation: relationType
58562
+ relation: relationType,
58563
+ weight: 1
57695
58564
  });
57696
- } else {
57697
58565
  }
57698
58566
  }
57699
58567
  }
58568
+ const schemaEdges = Array.from(edgeKeyMap.values()).sort((a, b) => {
58569
+ if (a.from !== b.from)
58570
+ return a.from.localeCompare(b.from);
58571
+ if (a.to !== b.to)
58572
+ return a.to.localeCompare(b.to);
58573
+ return a.relation.localeCompare(b.relation);
58574
+ });
57700
58575
  const metrics = {
57701
58576
  total_files: nodes.length,
57702
58577
  total_nodes: nodes.length,
57703
58578
  total_edges: schemaEdges.length,
57704
- total_imports: schemaEdges.filter((edge) => edge.relation === "imports").length,
57705
- total_calls: schemaEdges.filter((edge) => edge.relation === "calls").length,
57706
- total_owns: schemaEdges.filter((edge) => edge.relation === "owns").length,
57707
- total_depends_on: schemaEdges.filter((edge) => edge.relation === "depends_on").length,
57708
- files_with_functions: nodes.filter((n) => n.role === "Implementation").length
58579
+ total_imports: schemaEdges.filter((edge) => edge.relation === "imports").reduce((sum, e) => sum + (e.weight || 1), 0),
58580
+ total_calls: schemaEdges.filter((edge) => edge.relation === "calls").reduce((sum, e) => sum + (e.weight || 1), 0),
58581
+ total_owns: schemaEdges.filter((edge) => edge.relation === "owns").reduce((sum, e) => sum + (e.weight || 1), 0),
58582
+ total_depends_on: schemaEdges.filter((edge) => edge.relation === "depends_on").reduce((sum, e) => sum + (e.weight || 1), 0),
58583
+ files_with_functions: fileNodes.filter((n) => n.intelligence && n.intelligence.functions && n.intelligence.functions.length > 0).length,
58584
+ // New metrics
58585
+ total_dependencies: schemaEdges.length,
58586
+ files_with_high_blast_radius: nodes.filter((n) => n.blast_radius > 5).length
57709
58587
  };
57710
58588
  const context = {
57711
58589
  schema_version: "1.0.0",
@@ -57717,12 +58595,10 @@ var require_context_builder = __commonJS({
57717
58595
  },
57718
58596
  nodes,
57719
58597
  edges: schemaEdges,
58598
+ symbols: symbols || [],
57720
58599
  metrics,
57721
- contextSurface: options.contextSurface || {}
58600
+ contextSurface: options.contextSurface || []
57722
58601
  };
57723
- context.metrics.files_with_functions = nodes.filter((n) => n.role === "Implementation").length;
57724
- context.metrics.files_with_high_blast_radius = nodes.filter((n) => n.blast_radius > 5).length;
57725
- context.metrics.total_dependencies = schemaEdges.length;
57726
58602
  return context;
57727
58603
  }
57728
58604
  module2.exports = { buildContext };
@@ -64277,550 +65153,56 @@ var require_context_sorter = __commonJS({
64277
65153
  }
64278
65154
  });
64279
65155
 
64280
- // src/core/semantic-analyzer.js
64281
- var require_semantic_analyzer = __commonJS({
64282
- "src/core/semantic-analyzer.js"(exports2, module2) {
64283
- function buildSymbolTable(nodes) {
64284
- const symbolTable = /* @__PURE__ */ new Map();
64285
- nodes.forEach((node) => {
64286
- const filePath = node.id;
64287
- const metadata = node.metadata;
64288
- if (!metadata)
64289
- return;
64290
- if (metadata.exports && Array.isArray(metadata.exports)) {
64291
- metadata.exports.forEach((exp) => {
64292
- const symbolName = exp.name;
64293
- if (!symbolTable.has(symbolName)) {
64294
- symbolTable.set(symbolName, []);
64295
- }
64296
- symbolTable.get(symbolName).push({
64297
- file: filePath,
64298
- type: exp.type,
64299
- loc: exp.loc
64300
- });
64301
- });
64302
- }
64303
- if (metadata.functions && Array.isArray(metadata.functions)) {
64304
- metadata.functions.forEach((func) => {
64305
- const symbolName = func.name;
64306
- if (!symbolTable.has(symbolName)) {
64307
- symbolTable.set(symbolName, []);
64308
- }
64309
- symbolTable.get(symbolName).push({
64310
- file: filePath,
64311
- type: "function",
64312
- loc: func.loc
64313
- });
64314
- });
64315
- }
64316
- if (metadata.classes && Array.isArray(metadata.classes)) {
64317
- metadata.classes.forEach((cls) => {
64318
- const symbolName = cls.name;
64319
- if (!symbolTable.has(symbolName)) {
64320
- symbolTable.set(symbolName, []);
64321
- }
64322
- symbolTable.get(symbolName).push({
64323
- file: filePath,
64324
- type: "class",
64325
- loc: cls.loc
64326
- });
64327
- });
64328
- }
64329
- });
64330
- return symbolTable;
64331
- }
64332
- function resolveSymbolUsage(node, symbolTable) {
64333
- const resolvedUsages = [];
64334
- const metadata = node.metadata;
64335
- if (!metadata)
64336
- return resolvedUsages;
64337
- const importedSymbols = /* @__PURE__ */ new Map();
64338
- if (metadata.imports && Array.isArray(metadata.imports)) {
64339
- metadata.imports.forEach((imp) => {
64340
- if (imp.specifiers && Array.isArray(imp.specifiers)) {
64341
- imp.specifiers.forEach((spec) => {
64342
- const localName = spec.local || spec.imported || spec;
64343
- const importedName = spec.imported || spec.local || spec;
64344
- importedSymbols.set(localName, {
64345
- source: imp.source,
64346
- originalName: importedName
64347
- });
64348
- });
64349
- }
64350
- });
64351
- }
64352
- if (metadata.functionCalls && Array.isArray(metadata.functionCalls)) {
64353
- metadata.functionCalls.forEach((call) => {
64354
- const callName = call.name;
64355
- if (importedSymbols.has(callName)) {
64356
- const importInfo = importedSymbols.get(callName);
64357
- resolvedUsages.push({
64358
- type: "function_call",
64359
- symbol: callName,
64360
- source: importInfo.source,
64361
- loc: call.loc
64362
- });
64363
- }
64364
- });
64365
- }
64366
- if (metadata.constructorCalls && Array.isArray(metadata.constructorCalls)) {
64367
- metadata.constructorCalls.forEach((call) => {
64368
- const className = call.className;
64369
- if (importedSymbols.has(className)) {
64370
- const importInfo = importedSymbols.get(className);
64371
- resolvedUsages.push({
64372
- type: "constructor_call",
64373
- symbol: className,
64374
- source: importInfo.source,
64375
- loc: call.loc
64376
- });
64377
- }
64378
- });
64379
- }
64380
- if (metadata.componentUsage && Array.isArray(metadata.componentUsage)) {
64381
- metadata.componentUsage.forEach((comp) => {
64382
- const componentName = comp.component;
64383
- if (importedSymbols.has(componentName)) {
64384
- const importInfo = importedSymbols.get(componentName);
64385
- resolvedUsages.push({
64386
- type: "component_usage",
64387
- symbol: componentName,
64388
- source: importInfo.source,
64389
- loc: comp.loc
64390
- });
64391
- }
64392
- });
64393
- }
64394
- return resolvedUsages;
64395
- }
64396
- function trackCrossFileReferences(nodes, symbolTable) {
64397
- const usageEdges = [];
64398
- nodes.forEach((node) => {
64399
- const filePath = node.id;
64400
- const resolvedUsages = resolveSymbolUsage(node, symbolTable);
64401
- resolvedUsages.forEach((usage) => {
64402
- usageEdges.push({
64403
- source: filePath,
64404
- target: usage.source,
64405
- type: usage.type,
64406
- symbol: usage.symbol,
64407
- loc: usage.loc
64408
- });
64409
- });
64410
- });
64411
- return usageEdges;
64412
- }
64413
- function analyzeSemantics(nodes) {
64414
- const symbolTable = buildSymbolTable(nodes);
64415
- const usageEdges = trackCrossFileReferences(nodes, symbolTable);
64416
- return {
64417
- symbolTable,
64418
- usageEdges,
64419
- stats: {
64420
- totalSymbols: symbolTable.size,
64421
- totalUsages: usageEdges.length
64422
- }
64423
- };
64424
- }
64425
- module2.exports = {
64426
- buildSymbolTable,
64427
- resolveSymbolUsage,
64428
- trackCrossFileReferences,
64429
- analyzeSemantics
64430
- };
64431
- }
64432
- });
64433
-
64434
65156
  // src/core/scanner.js
64435
65157
  var require_scanner = __commonJS({
64436
65158
  "src/core/scanner.js"(exports2, module2) {
64437
- var { glob } = require_commonjs5();
64438
65159
  var path2 = require("path");
64439
- var fs2 = require("fs");
64440
- var parser = require_parser_enhanced();
64441
- var pluginManager = require_plugin_manager();
64442
65160
  var { loadTSConfig } = require_tsconfig_utils();
64443
- var { resolveImport } = require_path_resolver();
64444
- var { buildReverseDependencyGraph, computeBlastRadius } = require_blastRadius();
65161
+ var { WorkspaceScanner } = require_workspace_scanner();
65162
+ var { executePass1 } = require_pass1_facts();
65163
+ var { executePass2 } = require_pass2_semantics();
65164
+ var { executePass3 } = require_pass3_lifter();
65165
+ var { executePass4 } = require_pass4_signals();
64445
65166
  var { buildContext } = require_context_builder();
64446
65167
  var { validateContext } = require_context_validator();
64447
65168
  var { sortContext } = require_context_sorter();
64448
- var { analyzeSemantics } = require_semantic_analyzer();
64449
65169
  async function scan(directory) {
64450
- const normalize = (p) => p.replace(/\\/g, "/");
64451
- const options = {
64452
- ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/build/**"],
64453
- cwd: directory,
64454
- absolute: true
64455
- };
65170
+ console.log(`
65171
+ \u{1F680} ARCVISION STRUCTURAL ENGINE (4-PASS)`);
65172
+ console.log(` Target: ${directory}
65173
+ `);
64456
65174
  try {
64457
- const pluginDir = path2.join(__dirname, "../plugins");
64458
- pluginManager.loadPluginsFromDirectory(pluginDir);
64459
- const files = await glob("**/*.{js,jsx,ts,tsx,json}", { ...options, ignore: [...options.ignore, "**/*.d.ts", "**/node_modules/**", "**/.git/**", "**/dist/**", "**/build/**", "**/.next/**", "**/coverage/**"] });
64460
- const architectureMap = {
64461
- nodes: [],
64462
- edges: []
64463
- };
64464
- const fileMap = /* @__PURE__ */ new Map();
64465
- let totalImportsFound = 0;
64466
- for (const file of files) {
64467
- try {
64468
- const relativePath = path2.relative(directory, file);
64469
- const normalizedRelativePath = normalize(relativePath);
64470
- let metadata;
64471
- if (file.endsWith(".json")) {
64472
- try {
64473
- const content = fs2.readFileSync(file, "utf-8");
64474
- JSON.parse(content);
64475
- metadata = {
64476
- id: file,
64477
- imports: [],
64478
- exports: [],
64479
- functions: [],
64480
- apiCalls: [],
64481
- isJson: true
64482
- };
64483
- } catch (jsonError) {
64484
- console.warn(`\u26A0\uFE0F Invalid JSON in ${file} \u2014 file skipped`);
64485
- continue;
64486
- }
64487
- } else {
64488
- metadata = parser.parseFile(file);
64489
- }
64490
- metadata = await pluginManager.processFile(file, metadata);
64491
- if (metadata.imports && Array.isArray(metadata.imports)) {
64492
- totalImportsFound += metadata.imports.length;
64493
- }
64494
- const node = {
64495
- id: normalizedRelativePath,
64496
- type: "file",
64497
- metadata
64498
- };
64499
- architectureMap.nodes.push(node);
64500
- fileMap.set(normalizedRelativePath, metadata);
64501
- } catch (e) {
64502
- console.warn(`\u26A0\uFE0F Failed to process ${file} \u2014 file skipped, you should check manually (${e.message})`);
64503
- }
64504
- }
64505
- console.log("Performing semantic analysis...");
64506
- const semanticResults = analyzeSemantics(architectureMap.nodes);
64507
- console.log(`Semantic analysis complete: ${semanticResults.stats.totalSymbols} symbols, ${semanticResults.stats.totalUsages} usage edges`);
64508
- const tsconfig = loadTSConfig(directory);
64509
- const allPossiblePaths = /* @__PURE__ */ new Set();
64510
- const pathToNodeIdMap = /* @__PURE__ */ new Map();
64511
- architectureMap.nodes.forEach((node) => {
64512
- const normalizedPath = normalize(node.id);
64513
- allPossiblePaths.add(normalizedPath);
64514
- pathToNodeIdMap.set(normalizedPath, node.id);
64515
- });
64516
- let unresolvedImports = 0;
64517
- let resolvedImports = 0;
64518
- architectureMap.nodes.forEach((node) => {
64519
- if (node.metadata.imports && Array.isArray(node.metadata.imports)) {
64520
- node.metadata.imports.forEach((imp) => {
64521
- if (imp.source && typeof imp.source === "string") {
64522
- let targetFound = false;
64523
- try {
64524
- if (allPossiblePaths.has(imp.source)) {
64525
- architectureMap.edges.push({
64526
- source: normalize(node.id),
64527
- target: imp.source,
64528
- type: imp.type || "import"
64529
- });
64530
- resolvedImports++;
64531
- targetFound = true;
64532
- }
64533
- if (!targetFound) {
64534
- const resolvedPath = resolveImport(
64535
- imp.source,
64536
- path2.join(directory, node.id),
64537
- directory,
64538
- tsconfig
64539
- );
64540
- if (resolvedPath) {
64541
- const relativeResolvedPath = path2.relative(directory, resolvedPath);
64542
- const normalizedResolvedPath = normalize(relativeResolvedPath);
64543
- if (allPossiblePaths.has(normalizedResolvedPath)) {
64544
- architectureMap.edges.push({
64545
- source: normalize(node.id),
64546
- target: normalizedResolvedPath,
64547
- type: imp.type || "import"
64548
- });
64549
- resolvedImports++;
64550
- targetFound = true;
64551
- }
64552
- }
64553
- }
64554
- if (!targetFound) {
64555
- try {
64556
- const baseDir = path2.dirname(path2.join(directory, node.id));
64557
- const calculatedAbsolutePath = path2.resolve(baseDir, imp.source);
64558
- const calculatedRelativePath = path2.relative(directory, calculatedAbsolutePath);
64559
- const calculatedNormalizedPath = normalize(calculatedRelativePath);
64560
- if (allPossiblePaths.has(calculatedNormalizedPath)) {
64561
- architectureMap.edges.push({
64562
- source: normalize(node.id),
64563
- target: calculatedNormalizedPath,
64564
- type: imp.type || "import"
64565
- });
64566
- resolvedImports++;
64567
- targetFound = true;
64568
- }
64569
- } catch (e) {
64570
- console.warn(`\u26A0\uFE0F Path calculation failed for import '${imp.source}' in file '${node.id}': ${e.message}`);
64571
- }
64572
- }
64573
- if (!targetFound) {
64574
- if (imp.source.startsWith("./") || imp.source.startsWith("../")) {
64575
- const extensions = ["", ".js", ".ts", ".jsx", ".tsx", ".mjs", ".cjs"];
64576
- for (const ext of extensions) {
64577
- let testPath = imp.source;
64578
- if (!imp.source.endsWith(ext)) {
64579
- testPath = imp.source + ext;
64580
- }
64581
- try {
64582
- const baseDir = path2.dirname(path2.join(directory, node.id));
64583
- const calculatedAbsolutePath = path2.resolve(baseDir, testPath);
64584
- const calculatedRelativePath = path2.relative(directory, calculatedAbsolutePath);
64585
- const calculatedNormalizedPath = normalize(calculatedRelativePath);
64586
- if (allPossiblePaths.has(calculatedNormalizedPath)) {
64587
- architectureMap.edges.push({
64588
- source: normalize(node.id),
64589
- target: calculatedNormalizedPath,
64590
- type: imp.type || "import"
64591
- });
64592
- resolvedImports++;
64593
- targetFound = true;
64594
- break;
64595
- }
64596
- } catch (e) {
64597
- console.warn(`\u26A0\uFE0F Extension path calculation failed for import '${imp.source}' with extension '${ext}' in file '${node.id}': ${e.message}`);
64598
- }
64599
- }
64600
- }
64601
- }
64602
- if (!targetFound) {
64603
- if (imp.source.startsWith("./") || imp.source.startsWith("../")) {
64604
- try {
64605
- const baseDir = path2.dirname(path2.join(directory, node.id));
64606
- const calculatedAbsolutePath = path2.resolve(baseDir, imp.source);
64607
- const directoryPath = calculatedAbsolutePath;
64608
- const indexFiles = ["index.js", "index.ts", "index.jsx", "index.tsx", "index.mjs", "index.cjs"];
64609
- for (const indexFile of indexFiles) {
64610
- const indexPath = path2.join(directoryPath, indexFile);
64611
- const indexRelativePath = path2.relative(directory, indexPath);
64612
- const indexNormalizedPath = normalize(indexRelativePath);
64613
- if (allPossiblePaths.has(indexNormalizedPath)) {
64614
- architectureMap.edges.push({
64615
- source: normalize(node.id),
64616
- target: indexNormalizedPath,
64617
- type: imp.type || "import"
64618
- });
64619
- resolvedImports++;
64620
- targetFound = true;
64621
- break;
64622
- }
64623
- }
64624
- } catch (e) {
64625
- console.warn(`\u26A0\uFE0F Index file check failed for import '${imp.source}' in file '${node.id}': ${e.message}`);
64626
- }
64627
- }
64628
- }
64629
- if (!targetFound) {
64630
- if (imp.source.startsWith("@") || imp.source.startsWith("/") || !imp.source.startsWith(".")) {
64631
- const commonSourceDirs = ["src", "app", "lib", "components", "utils", "services", "assets"];
64632
- for (const srcDir of commonSourceDirs) {
64633
- const potentialPath = path2.join(srcDir, imp.source);
64634
- const potentialRelativePath = path2.relative(directory, path2.resolve(directory, potentialPath));
64635
- const potentialNormalizedPath = normalize(potentialRelativePath);
64636
- if (allPossiblePaths.has(potentialNormalizedPath)) {
64637
- architectureMap.edges.push({
64638
- source: normalize(node.id),
64639
- target: potentialNormalizedPath,
64640
- type: imp.type || "import"
64641
- });
64642
- resolvedImports++;
64643
- targetFound = true;
64644
- break;
64645
- }
64646
- const extensions = [
64647
- ".js",
64648
- ".ts",
64649
- ".jsx",
64650
- ".tsx",
64651
- ".mjs",
64652
- ".cjs",
64653
- "index.js",
64654
- "index.ts",
64655
- "index.jsx",
64656
- "index.tsx",
64657
- "index.mjs",
64658
- "index.cjs"
64659
- ];
64660
- for (const ext of extensions) {
64661
- let testPath;
64662
- if (ext.startsWith("index")) {
64663
- testPath = path2.join(srcDir, imp.source, ext);
64664
- } else {
64665
- testPath = path2.join(srcDir, imp.source + ext);
64666
- }
64667
- const testRelativePath = path2.relative(directory, path2.resolve(directory, testPath));
64668
- const testNormalizedPath = normalize(testRelativePath);
64669
- if (allPossiblePaths.has(testNormalizedPath)) {
64670
- architectureMap.edges.push({
64671
- source: normalize(node.id),
64672
- target: testNormalizedPath,
64673
- type: imp.type || "import"
64674
- });
64675
- resolvedImports++;
64676
- targetFound = true;
64677
- break;
64678
- }
64679
- }
64680
- if (targetFound)
64681
- break;
64682
- }
64683
- }
64684
- }
64685
- if (!targetFound) {
64686
- if (imp.source.startsWith("@") || imp.source.startsWith("/")) {
64687
- try {
64688
- const parts = imp.source.split("/");
64689
- if (parts.length > 1) {
64690
- const dirParts = parts.slice(0, -1);
64691
- const fileName = parts[parts.length - 1];
64692
- const dirPath = dirParts.join("/") + "/index";
64693
- const indexFiles = ["index.js", "index.ts", "index.jsx", "index.tsx", "index.mjs", "index.cjs"];
64694
- for (const indexFile of indexFiles) {
64695
- const indexPath = path2.join(dirParts.join("/"), indexFile);
64696
- const indexRelativePath = path2.relative(directory, path2.resolve(directory, indexPath));
64697
- const indexNormalizedPath = normalize(indexRelativePath);
64698
- if (allPossiblePaths.has(indexNormalizedPath)) {
64699
- architectureMap.edges.push({
64700
- source: normalize(node.id),
64701
- target: indexNormalizedPath,
64702
- type: imp.type || "import"
64703
- });
64704
- resolvedImports++;
64705
- targetFound = true;
64706
- break;
64707
- }
64708
- }
64709
- }
64710
- } catch (e) {
64711
- console.warn(`\u26A0\uFE0F Barrel file check failed for import '${imp.source}' in file '${node.id}': ${e.message}`);
64712
- }
64713
- }
64714
- }
64715
- if (!targetFound) {
64716
- for (const possiblePath of allPossiblePaths) {
64717
- if (possiblePath.toLowerCase() === imp.source.toLowerCase()) {
64718
- architectureMap.edges.push({
64719
- source: normalize(node.id),
64720
- target: possiblePath,
64721
- type: imp.type || "import"
64722
- });
64723
- resolvedImports++;
64724
- targetFound = true;
64725
- break;
64726
- }
64727
- }
64728
- }
64729
- } catch (e) {
64730
- console.error(`\u274C Critical error processing import '${imp.source}' in file '${node.id}': ${e.message}`);
64731
- console.error(e.stack);
64732
- unresolvedImports++;
64733
- }
64734
- if (!targetFound) {
64735
- unresolvedImports++;
64736
- }
64737
- }
64738
- });
64739
- }
64740
- });
64741
- console.log("RESOLVED IMPORTS:", resolvedImports);
64742
- console.log("UNRESOLVED IMPORTS:", unresolvedImports);
64743
- console.log("IMPORT EDGES CREATED:", architectureMap.edges.length);
64744
- if (semanticResults && semanticResults.usageEdges) {
64745
- semanticResults.usageEdges.forEach((usageEdge) => {
64746
- const sourceExists = architectureMap.nodes.some((n) => n.id === usageEdge.source);
64747
- const targetExists = architectureMap.nodes.some((n) => n.id === usageEdge.target);
64748
- if (sourceExists && targetExists) {
64749
- architectureMap.edges.push({
64750
- source: normalize(usageEdge.source),
64751
- target: normalize(usageEdge.target),
64752
- type: usageEdge.type
64753
- // 'function_call', 'constructor_call', 'component_usage'
64754
- });
64755
- }
64756
- });
64757
- }
64758
- console.log("TOTAL EDGES (with usage):", architectureMap.edges.length);
64759
- const reverseGraph = buildReverseDependencyGraph(architectureMap);
64760
- const blastRadiusMap = computeBlastRadius(reverseGraph);
64761
- architectureMap.nodes.forEach((node) => {
64762
- node.metadata.blast_radius = blastRadiusMap[node.id] || 0;
64763
- });
64764
- console.log("BEFORE DEDUPLICATION EDGES:", architectureMap.edges.length);
64765
- const validEdges = [];
64766
- const edgeSet = /* @__PURE__ */ new Set();
64767
- architectureMap.edges.forEach((edge) => {
64768
- const normalizedSource = normalize(edge.source);
64769
- const normalizedTarget = normalize(edge.target);
64770
- const edgeKey = `${normalizedSource}\u2192${normalizedTarget}`;
64771
- const sourceNodeExists = architectureMap.nodes.some((node) => node.id === normalizedSource);
64772
- const targetNodeExists = architectureMap.nodes.some((node) => node.id === normalizedTarget);
64773
- if (sourceNodeExists && targetNodeExists && !edgeSet.has(edgeKey)) {
64774
- edgeSet.add(edgeKey);
64775
- validEdges.push({
64776
- source: normalizedSource,
64777
- target: normalizedTarget,
64778
- type: edge.type
64779
- });
64780
- }
64781
- });
64782
- console.log("AFTER DEDUPLICATION EDGES:", validEdges.length);
64783
- architectureMap.edges = validEdges;
64784
- const totalFiles = architectureMap.nodes.length;
64785
- const { computeBlastRadiusWithPercentage: computeBlastRadiusWithPercentage2, analyzeCriticality } = require_blastRadius();
64786
- const allFilesWithPercentage = computeBlastRadiusWithPercentage2(blastRadiusMap, totalFiles);
64787
- const criticalityAnalysis = analyzeCriticality(blastRadiusMap, reverseGraph, architectureMap.nodes);
64788
- const topFiles = allFilesWithPercentage.slice(0, 3);
64789
- architectureMap.contextSurface = {
64790
- topBlastRadiusFiles: topFiles,
64791
- criticalityAnalysis: criticalityAnalysis.slice(0, 5)
64792
- // Top 5 most critical files
64793
- };
64794
- let context = buildContext(architectureMap.nodes, architectureMap.edges, {
65175
+ const start = Date.now();
65176
+ const workspaceScanner = new WorkspaceScanner(directory);
65177
+ const workspaceContext = workspaceScanner.scan();
65178
+ const rawNodes = await executePass1(directory);
65179
+ const semanticData = await executePass2(rawNodes, directory, workspaceContext);
65180
+ const liftedData = await executePass3(semanticData);
65181
+ const structuralContext = await executePass4(liftedData);
65182
+ const { nodes, edges, contextSurface, symbols } = structuralContext;
65183
+ console.log(`
65184
+ \u{1F50D} Analysis Complete (${Date.now() - start}ms)`);
65185
+ console.log(` Nodes: ${nodes.length}`);
65186
+ console.log(` Edges: ${edges.length}`);
65187
+ console.log(` Symbols: ${symbols ? symbols.length : 0}`);
65188
+ const contextOptions = {
64795
65189
  directory,
64796
65190
  projectName: path2.basename(directory),
64797
- language: "javascript",
64798
- contextSurface: architectureMap.contextSurface
64799
- });
64800
- console.log("Validating structural context...");
65191
+ contextSurface
65192
+ };
65193
+ let context = buildContext(nodes, edges, symbols, contextOptions);
65194
+ console.log(" Validating structural context...");
64801
65195
  let validation = validateContext(context);
64802
65196
  if (!validation.valid) {
64803
- console.error("VALIDATION FAILED (Attempt 1)");
64804
- validation.errors.forEach((e) => console.error(" -", e));
64805
- console.log("Re-running generation deterministically...");
64806
- context = buildContext(architectureMap.nodes, architectureMap.edges, {
64807
- directory,
64808
- projectName: path2.basename(directory),
64809
- language: "javascript",
64810
- contextSurface: architectureMap.contextSurface
64811
- });
64812
- validation = validateContext(context);
64813
- if (!validation.valid) {
64814
- console.error("VALIDATION FAILED (Attempt 2)");
64815
- validation.errors.forEach((e) => console.error(" -", e));
64816
- console.error("ABORTING. JSON NOT SAVED.");
64817
- process.exit(1);
64818
- }
65197
+ console.error(" \u274C Validation Failed");
65198
+ validation.errors.forEach((e) => console.error(" -", e));
65199
+ throw new Error("Context validation failed");
64819
65200
  }
64820
65201
  const sortedContext = sortContext(context);
64821
- console.log("STRUCTURAL CONTEXT VALIDATED \u2014 READY FOR UPLOAD");
65202
+ console.log(" \u2705 Context Verified & Ready");
64822
65203
  return sortedContext;
64823
65204
  } catch (err) {
65205
+ console.error("\u274C Scanner Failed:", err);
64824
65206
  throw err;
64825
65207
  }
64826
65208
  }
@@ -65050,7 +65432,6 @@ program.command("scan").description("Scan the current directory and generate arc
65050
65432
  const outputFileName = "arcvision.context.json";
65051
65433
  fs2.writeFileSync(outputFileName, JSON.stringify(map, null, 2));
65052
65434
  console.log(chalk.green(`\u2705 Structural context saved to ${outputFileName}`));
65053
- console.log(JSON.stringify(map, null, 2));
65054
65435
  console.log(chalk.dim("\nUse --upload to send to dashboard."));
65055
65436
  }
65056
65437
  } catch (error) {