arcvision 0.2.5 → 0.2.6

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/README.md CHANGED
@@ -1,74 +1,85 @@
1
- # ArcVision CLI
2
-
3
- Architecture scanner for modern codebases that generates dependency graphs and visualizes your project structure.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install -g arcvision
9
- ```
10
-
11
- ## Usage
12
-
13
- ### Quick Start
14
- 1. Sign up at the ArcVision dashboard
15
- 2. Create a project and name it
16
- 3. Generate a CLI token
17
- 4. Run: `arcvision link <token>`
18
- 5. Run: `arcvision scan --upload`
19
- 6. Open dashboard to see results
20
-
21
- ### Commands
22
-
23
- #### Link to Project
24
- ```bash
25
- arcvision link <token>
26
- ```
27
- Link this CLI to a project via upload token.
28
-
29
- #### Scan Project
30
- ```bash
31
- arcvision scan [directory] [options]
32
- ```
33
- Scan the current directory and generate architecture map.
34
-
35
- Options:
36
- - `-u, --upload`: Upload to database
37
-
38
- The scan command will also output a blast radius analysis showing the file with the highest blast radius and its potential role on your codebase.
39
-
40
- **Blast Radius Analysis**: After scanning, the CLI will display a warning identifying the most critical file in your codebase:
41
- ```
42
- ⚠️ [filename] at the center of the system (blast radius: X out of Y). This file represents a single point of systemic failure. Changes here may affect many parts of the system.
43
- ```
44
- This feature helps identify high-structure files that are imported by many other files, allowing you to be more cautious when modifying them.
45
-
46
- ## Features
47
-
48
- - **Modern Import Resolution**: Handles path aliases (`@/components/*`), barrel files (`./utils` `./utils/index.ts`), and directory imports
49
- - **AST-based Parsing**: Uses Babel parser for accurate import detection
50
- - **Multi-framework Support**: Works with React, Next.js, and other modern JavaScript frameworks
51
- - **Dependency Graph Generation**: Creates comprehensive node-edge relationships
52
- - **Blast Radius Analysis**: Identifies high-structure files that are imported by many other files
53
- - **Cloud Integration**: Uploads results to ArcVision dashboard for visualization
54
-
55
- ## Supported Import Patterns
56
-
57
- - Relative imports: `./utils`, `../components`
58
- - Path aliases: `@/components/Button`, `~/lib/utils`
59
- - Barrel files: `./utils` resolves to `./utils/index.ts(x)`
60
- - Directory imports: `@/utils` resolves to `src/utils/index.ts(x)`
61
- - Implicit extensions: `.ts`, `.tsx`, `.js`, `.jsx`
62
-
63
- ## Architecture
64
-
65
- The ArcVision CLI runs locally on your machine and:
66
- 1. Scans your project files using AST parsing
67
- 2. Detects import relationships between files
68
- 3. Resolves path aliases using tsconfig.json/jsconfig.json
69
- 4. Generates a dependency graph in JSON format
70
- 5. Uploads the graph to the ArcVision dashboard via API
71
-
72
- ## License
73
-
74
- MIT
1
+ # ArcVision System Context Artifact
2
+
3
+ ## What This Is
4
+
5
+ This file is the **canonical structural context** of this codebase.
6
+ It represents how the system actually works — not how it is described.
7
+
8
+ It is generated directly from source code by ArcVision.
9
+
10
+ ## What This Replaces
11
+
12
+ This artifact replaces:
13
+ - Manual repository scanning
14
+ - Tribal knowledge held by senior engineers
15
+ - Re-explaining the system to new developers
16
+ - Re-prompting LLMs with partial or incorrect context
17
+ - Guessing blast radius of changes
18
+
19
+ ## When You Must Use This
20
+
21
+ Use this artifact when:
22
+ - Onboarding a new developer
23
+ - Prompting an AI assistant about this codebase
24
+ - Making architectural changes
25
+ - Investigating unexpected behavior
26
+ - Assessing risk before modifying core modules
27
+
28
+ ## What This Artifact Contains
29
+
30
+ - Canonical module and dependency graph
31
+ - Execution and data flow relationships
32
+ - Structural roles (service, store, boundary, etc.)
33
+ - Invariants inferred from the system
34
+ - Impact metrics (blast radius, coupling)
35
+
36
+ ## Determinism & Trust
37
+
38
+ - Generated from commit: b63e62db4aedf6ffb21055dd88d13c8a947a7422
39
+ - Generation timestamp: 2026-01-13T03:50:34.571Z
40
+ - Tool version: 0.2.5
41
+ - Deterministic: same input → same output
42
+ - Explicit assumptions listed inside the artifact
43
+
44
+ If this artifact conflicts with human memory, **trust the artifact**.
45
+
46
+ ## Structural Context Hubs
47
+
48
+ The following files have the highest blast radius and represent critical structural hubs in the system:
49
+
50
+ - **src/lib/utils.ts**
51
+ - Blast Radius: 62 files (14.73% of codebase)
52
+ - Risk: Changes here may silently propagate across the system.
53
+
54
+ - **src/components/ui/button.tsx**
55
+ - Blast Radius: 49 files (11.64% of codebase)
56
+ - Risk: Acts as a coordination layer between components.
57
+
58
+ - **src/lib/supabase/client.ts**
59
+ - Blast Radius: 45 files (10.69% of codebase)
60
+ - Risk: Modifications can cause widespread inconsistencies.
61
+
62
+
63
+
64
+ ## How to Use With AI
65
+
66
+ When prompting AI tools, include this file as system context.
67
+ Do not ask the AI to infer architecture without it.
68
+
69
+ ## When to Regenerate
70
+
71
+ Regenerate this artifact when:
72
+ - Core modules change
73
+ - New services are added
74
+ - Dependency structure shifts
75
+
76
+ Run:
77
+
78
+ ```
79
+ arcvision scan --upload
80
+ ```
81
+
82
+ ## Source of Truth
83
+
84
+ This artifact is the **source of truth** for system structure.
85
+ All explanations, decisions, and AI reasoning should reference it.
package/dist/index.js CHANGED
@@ -57524,7 +57524,7 @@ var require_pass1_facts = __commonJS({
57524
57524
  if (fs2.existsSync(pluginDir)) {
57525
57525
  pluginManager.loadPluginsFromDirectory(pluginDir);
57526
57526
  }
57527
- const files = await glob("**/*.{js,jsx,ts,tsx,json}", {
57527
+ const files = await glob("**/*.{js,jsx,ts,tsx,json,lua}", {
57528
57528
  ...scanOptions,
57529
57529
  ignore: [...scanOptions.ignore, "**/*.d.ts", "**/.next/**", "**/coverage/**", "**/arcvision.context.json"]
57530
57530
  });
@@ -57545,14 +57545,27 @@ var require_pass1_facts = __commonJS({
57545
57545
  isJson: true,
57546
57546
  raw: content
57547
57547
  };
57548
+ } else if (file.endsWith(".lua")) {
57549
+ const content = fs2.readFileSync(file, "utf-8");
57550
+ metadata = {
57551
+ id: file,
57552
+ isLua: true,
57553
+ raw: content,
57554
+ // Extract potential invocations from Lua content
57555
+ potentialInvocations: extractLuaInvocations(content)
57556
+ };
57548
57557
  } else {
57549
57558
  metadata = parser.parseFile(file);
57550
57559
  }
57551
57560
  metadata = await pluginManager.processFile(file, metadata);
57561
+ const isLuaFile = file.endsWith(".lua");
57562
+ const nodeType = isLuaFile ? "execution_script" : "file";
57563
+ const nodeRole = isLuaFile ? "atomic_redis_operation" : "Structure";
57552
57564
  const node = {
57553
57565
  id: normalizedRelativePath,
57554
57566
  filePath: file,
57555
- type: "file",
57567
+ type: nodeType,
57568
+ role: nodeRole,
57556
57569
  facts: {
57557
57570
  imports: metadata.imports || [],
57558
57571
  exports: metadata.exports || [],
@@ -57574,9 +57587,14 @@ var require_pass1_facts = __commonJS({
57574
57587
  hooks: metadata.hookDependencies || [],
57575
57588
  context: metadata.contextUsages || []
57576
57589
  },
57577
- react: metadata.componentUsage || []
57590
+ react: metadata.componentUsage || [],
57591
+ potentialInvocations: metadata.potentialInvocations || []
57578
57592
  }
57579
57593
  };
57594
+ if (isLuaFile) {
57595
+ node.language = "lua";
57596
+ node.execution_boundary = "redis";
57597
+ }
57580
57598
  return { node, factCount: (metadata.imports?.length || 0) + (metadata.functionCalls?.length || 0) };
57581
57599
  } catch (e) {
57582
57600
  console.warn(`\u26A0\uFE0F Pass 1 failed for ${file}: ${e.message}`);
@@ -57596,6 +57614,22 @@ var require_pass1_facts = __commonJS({
57596
57614
  console.log(` \u2713 Extracted ${totalFacts} raw syntactic facts`);
57597
57615
  return rawNodes;
57598
57616
  }
57617
+ function extractLuaInvocations(content) {
57618
+ const invocations = [];
57619
+ const patterns = [
57620
+ /readFileSync\(['"].*\.lua['"].*\)/gi,
57621
+ /defineCommand\(\s*\{\s*lua\s*:.*\}/gi,
57622
+ /redis\.call\(['"].*['"].*\)/gi,
57623
+ /redis\.pcall\(['"].*['"].*\)/gi
57624
+ ];
57625
+ patterns.forEach((pattern) => {
57626
+ let match;
57627
+ while ((match = pattern.exec(content)) !== null) {
57628
+ invocations.push(match[0]);
57629
+ }
57630
+ });
57631
+ return invocations;
57632
+ }
57599
57633
  module2.exports = { executePass1 };
57600
57634
  }
57601
57635
  });
@@ -58165,6 +58199,66 @@ var require_pass2_semantics = __commonJS({
58165
58199
  }
58166
58200
  }
58167
58201
  console.log(` \u2713 Resolved ${resolvedCalls} semantic call edges`);
58202
+ let luaInvocationCount = 0;
58203
+ for (const node of rawNodes) {
58204
+ if (!node.facts.potentialInvocations || node.facts.potentialInvocations.length === 0)
58205
+ continue;
58206
+ const content = node.filePath && require("fs").readFileSync(node.filePath, "utf-8");
58207
+ if (!content)
58208
+ continue;
58209
+ const luaInvocationPatterns = [
58210
+ /readFileSync\(['"].*\.lua['"].*\)/gi,
58211
+ /defineCommand\(\s*\{\s*lua\s*:.*?\}/gi,
58212
+ /require\(['"].*\.lua['"].*\)/gi,
58213
+ /fs\.readFileSync\(['"].*\.lua['"].*\)/gi,
58214
+ /import\s+.*?from\s+['"].*\.lua['"].*\)?/gi,
58215
+ /import\(['"].*\.lua['"].*\)/gi,
58216
+ /loadScript\(['"].*\.lua['"].*\)/gi,
58217
+ /loadLua\(['"].*\.lua['"].*\)/gi,
58218
+ /load\(['"].*\.lua['"].*\)/gi
58219
+ ];
58220
+ for (const pattern of luaInvocationPatterns) {
58221
+ let match;
58222
+ while ((match = pattern.exec(content)) !== null) {
58223
+ const luaFileMatch = match[0].match(/['"].*?\.lua['"]/);
58224
+ if (luaFileMatch) {
58225
+ let luaFilePath = luaFileMatch[0].slice(1, -1);
58226
+ if (luaFilePath.startsWith("./") || luaFilePath.startsWith("../")) {
58227
+ luaFilePath = path2.resolve(path2.dirname(node.filePath), luaFilePath);
58228
+ luaFilePath = normalize(path2.relative(rootDir, luaFilePath));
58229
+ } else if (luaFilePath.startsWith("/")) {
58230
+ luaFilePath = normalize(path2.relative(rootDir, path2.join(rootDir, luaFilePath)));
58231
+ } else {
58232
+ const commonLuaDirs = ["src", "scripts", "commands", "lua", "lib"];
58233
+ let foundPath = null;
58234
+ for (const dir of commonLuaDirs) {
58235
+ const candidatePath = normalize(path2.join(dir, luaFilePath));
58236
+ if (fileSet.has(candidatePath)) {
58237
+ foundPath = candidatePath;
58238
+ break;
58239
+ }
58240
+ }
58241
+ if (foundPath) {
58242
+ luaFilePath = foundPath;
58243
+ } else {
58244
+ const nodeDir = path2.dirname(node.id);
58245
+ luaFilePath = normalize(path2.join(nodeDir, luaFilePath));
58246
+ }
58247
+ }
58248
+ if (fileSet.has(luaFilePath)) {
58249
+ addEdge(node.id, luaFilePath, "invokes_execution_script", 1, {
58250
+ type: "lua_invocation",
58251
+ invocation_pattern: match[0],
58252
+ original_match: match[0],
58253
+ resolved_path: luaFilePath
58254
+ });
58255
+ luaInvocationCount++;
58256
+ }
58257
+ }
58258
+ }
58259
+ }
58260
+ }
58261
+ console.log(` \u2713 Detected ${luaInvocationCount} Lua script invocations`);
58168
58262
  let typeEdgesCount = 0;
58169
58263
  for (const node of rawNodes) {
58170
58264
  if (!node.facts.typeAnalysis)
@@ -58522,6 +58616,8 @@ var require_id_generator = __commonJS({
58522
58616
  var require_context_builder = __commonJS({
58523
58617
  "src/core/context_builder.js"(exports2, module2) {
58524
58618
  var path2 = require("path");
58619
+ var { spawnSync } = require("child_process");
58620
+ var crypto = require("crypto");
58525
58621
  var { stableId } = require_id_generator();
58526
58622
  function buildContext(fileNodes, edges, symbols, options = {}) {
58527
58623
  const {
@@ -58619,9 +58715,11 @@ var require_context_builder = __commonJS({
58619
58715
  total_dependencies: schemaEdges.length,
58620
58716
  files_with_high_blast_radius: nodes.filter((n) => n.blast_radius > 5).length
58621
58717
  };
58718
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
58719
+ const dateOnly = timestamp.split("T")[0];
58622
58720
  const context = {
58623
58721
  schema_version: "1.0.0",
58624
- generated_at: (/* @__PURE__ */ new Date()).toISOString(),
58722
+ generated_at: timestamp,
58625
58723
  system: {
58626
58724
  name: projectName,
58627
58725
  root_path: path2.resolve(directory),
@@ -58631,11 +58729,453 @@ var require_context_builder = __commonJS({
58631
58729
  edges: schemaEdges,
58632
58730
  symbols: symbols || [],
58633
58731
  metrics,
58634
- contextSurface: options.contextSurface || []
58732
+ contextSurface: options.contextSurface || [],
58733
+ // Phase 2 requirement: Explicit Assumptions Section
58734
+ assumptions: generateAssumptions(),
58735
+ // Phase 2 requirement: Embed Commit Identity
58736
+ source: {
58737
+ repo: getGitInfo().repo,
58738
+ commit: getGitInfo().commit,
58739
+ generated_at: dateOnly,
58740
+ // Use date only to avoid time variations
58741
+ arcvision_version: options.arcvisionVersion || "0.1.0"
58742
+ },
58743
+ // Phase 3 requirement: Structural layers definition
58744
+ structural_layers: generateAdaptiveStructuralLayers(directory),
58745
+ // Phase 3 requirement: Project envelope
58746
+ project_envelope: generateProjectEnvelope(directory)
58747
+ };
58748
+ const contextWithoutIntegrity = { ...context };
58749
+ delete contextWithoutIntegrity.integrity;
58750
+ const hash = crypto.createHash("sha256").update(JSON.stringify(contextWithoutIntegrity)).digest("hex");
58751
+ context.integrity = {
58752
+ sha256: hash
58635
58753
  };
58636
58754
  return context;
58637
58755
  }
58638
- module2.exports = { buildContext };
58756
+ function getGitInfo() {
58757
+ try {
58758
+ const originResult = spawnSync("git", ["remote", "get-url", "origin"], {
58759
+ stdio: ["pipe", "pipe", "pipe"]
58760
+ });
58761
+ let repoUrl = "unknown";
58762
+ if (originResult.status === 0) {
58763
+ let url = originResult.stdout.toString().trim();
58764
+ if (url.startsWith("git@")) {
58765
+ url = url.replace(/^git@(.*?):/, "https://$1/");
58766
+ url = url.replace(/\.git$/, "");
58767
+ } else if (url.endsWith(".git")) {
58768
+ url = url.replace(/\.git$/, "");
58769
+ }
58770
+ repoUrl = url;
58771
+ }
58772
+ const commitResult = spawnSync("git", ["rev-parse", "HEAD"], {
58773
+ stdio: ["pipe", "pipe", "pipe"]
58774
+ });
58775
+ let commitHash = "unknown";
58776
+ if (commitResult.status === 0) {
58777
+ commitHash = commitResult.stdout.toString().trim();
58778
+ }
58779
+ return {
58780
+ repo: repoUrl,
58781
+ commit: commitHash
58782
+ };
58783
+ } catch (error) {
58784
+ return {
58785
+ repo: "unknown",
58786
+ commit: "unknown"
58787
+ };
58788
+ }
58789
+ }
58790
+ function calculateIntegrityHash(obj) {
58791
+ try {
58792
+ const objCopy = JSON.parse(JSON.stringify(obj));
58793
+ if (objCopy.integrity) {
58794
+ delete objCopy.integrity;
58795
+ }
58796
+ const jsonString = JSON.stringify(objCopy);
58797
+ return crypto.createHash("sha256").update(jsonString).digest("hex");
58798
+ } catch (error) {
58799
+ return "calculation_error";
58800
+ }
58801
+ }
58802
+ function generateStructuralLayers() {
58803
+ return {
58804
+ "runtime_code": {
58805
+ "status": "included",
58806
+ "description": "Source files that directly participate in runtime execution and system behavior"
58807
+ },
58808
+ "execution_scripts": {
58809
+ "status": "included",
58810
+ "description": "Scripts invoked by runtime code to perform atomic or system-critical operations"
58811
+ },
58812
+ "infrastructure_scripts": {
58813
+ "status": "included",
58814
+ "description": "Scripts that manage infrastructure-level behavior (queues, databases, orchestration)"
58815
+ },
58816
+ "configuration": {
58817
+ "status": "optional",
58818
+ "description": "Project configuration files represented as metadata, not execution graph nodes"
58819
+ },
58820
+ "documentation": {
58821
+ "status": "optional",
58822
+ "description": "Documentation files represented as metadata only"
58823
+ },
58824
+ "assets": {
58825
+ "status": "excluded",
58826
+ "description": "Static assets with no effect on execution semantics"
58827
+ }
58828
+ };
58829
+ }
58830
+ function generateAdaptiveStructuralLayers(directory) {
58831
+ const fs2 = require("fs");
58832
+ const path3 = require("path");
58833
+ const hasPackageJson = fs2.existsSync(path3.join(directory, "package.json"));
58834
+ const hasConfigFiles = fs2.existsSync(path3.join(directory, "tsconfig.json")) || fs2.existsSync(path3.join(directory, "config")) || fs2.existsSync(path3.join(directory, "webpack.config.js"));
58835
+ const hasSrcDir = fs2.existsSync(path3.join(directory, "src"));
58836
+ const hasTestDir = fs2.existsSync(path3.join(directory, "test")) || fs2.existsSync(path3.join(directory, "tests"));
58837
+ const hasDocsDir = fs2.existsSync(path3.join(directory, "docs"));
58838
+ let totalFiles = 0;
58839
+ let jsFiles = 0;
58840
+ let tsFiles = 0;
58841
+ let luaFiles = 0;
58842
+ let configFiles = 0;
58843
+ function walk(dir, depth = 0) {
58844
+ if (depth > 3)
58845
+ return;
58846
+ if (!fs2.existsSync(dir))
58847
+ return;
58848
+ const items = fs2.readdirSync(dir, { withFileTypes: true });
58849
+ for (const item of items) {
58850
+ const fullPath = path3.join(dir, item.name);
58851
+ if (item.isDirectory()) {
58852
+ if (!item.name.startsWith(".") && item.name !== "node_modules" && item.name !== "dist" && item.name !== "build" && item.name !== ".git") {
58853
+ walk(fullPath, depth + 1);
58854
+ }
58855
+ } else {
58856
+ totalFiles++;
58857
+ if (item.name.endsWith(".js"))
58858
+ jsFiles++;
58859
+ if (item.name.endsWith(".ts"))
58860
+ tsFiles++;
58861
+ if (item.name.endsWith(".lua"))
58862
+ luaFiles++;
58863
+ if ([".json", ".yaml", ".yml", ".toml", ".config.js", ".config.ts", ".rc"].some((ext) => item.name.endsWith(ext)))
58864
+ configFiles++;
58865
+ }
58866
+ }
58867
+ }
58868
+ walk(directory);
58869
+ const isLuaProject = luaFiles > 0 && luaFiles / totalFiles > 0.1;
58870
+ const isLargeProject = totalFiles > 1e3;
58871
+ const layers = {
58872
+ "runtime_code": {
58873
+ "status": "included",
58874
+ "description": "Source files that directly participate in runtime execution and system behavior",
58875
+ "project_characteristics": {
58876
+ "has_package_json": hasPackageJson,
58877
+ "has_src_directory": hasSrcDir,
58878
+ "total_files": totalFiles,
58879
+ "javascript_files": jsFiles,
58880
+ "typescript_files": tsFiles,
58881
+ "lua_files": luaFiles
58882
+ }
58883
+ },
58884
+ "execution_scripts": {
58885
+ "status": isLuaProject ? "included" : "included",
58886
+ "description": "Scripts invoked by runtime code to perform atomic or system-critical operations",
58887
+ "project_characteristics": {
58888
+ "lua_files_count": luaFiles,
58889
+ "is_lua_project": isLuaProject
58890
+ }
58891
+ },
58892
+ "infrastructure_scripts": {
58893
+ "status": "included",
58894
+ "description": "Scripts that manage infrastructure-level behavior (queues, databases, orchestration)"
58895
+ },
58896
+ "configuration": {
58897
+ "status": hasConfigFiles ? "optional" : "excluded",
58898
+ "description": "Project configuration files represented as metadata, not execution graph nodes"
58899
+ },
58900
+ "documentation": {
58901
+ "status": hasDocsDir || hasPackageJson ? "optional" : "excluded",
58902
+ "description": "Documentation files represented as metadata only"
58903
+ },
58904
+ "assets": {
58905
+ "status": "excluded",
58906
+ "description": "Static assets with no effect on execution semantics"
58907
+ }
58908
+ };
58909
+ layers.project_metadata = {
58910
+ "size_category": isLargeProject ? "large" : totalFiles > 100 ? "medium" : "small",
58911
+ "file_count": totalFiles,
58912
+ "language_mix": {
58913
+ "javascript": jsFiles,
58914
+ "typescript": tsFiles,
58915
+ "lua": luaFiles,
58916
+ "config": configFiles
58917
+ },
58918
+ "has_package_management": hasPackageJson,
58919
+ "has_standard_structure": hasSrcDir,
58920
+ "has_tests": hasTestDir,
58921
+ "is_lua_intensive": isLuaProject
58922
+ };
58923
+ return layers;
58924
+ }
58925
+ function generateProjectEnvelope(directory) {
58926
+ const fs2 = require("fs");
58927
+ const path3 = require("path");
58928
+ const envelope = {
58929
+ configuration_files: [],
58930
+ documentation: [],
58931
+ build_tools: []
58932
+ };
58933
+ const configFiles = [
58934
+ "package.json",
58935
+ "tsconfig.json",
58936
+ "jsconfig.json",
58937
+ "webpack.config.js",
58938
+ "webpack.config.ts",
58939
+ "vite.config.js",
58940
+ "vite.config.ts",
58941
+ "rollup.config.js",
58942
+ "rollup.config.ts",
58943
+ "babel.config.js",
58944
+ "babel.config.ts",
58945
+ ".babelrc",
58946
+ ".babelrc.js",
58947
+ "eslint.config.mjs",
58948
+ "eslint.config.js",
58949
+ ".eslintrc",
58950
+ ".eslintrc.js",
58951
+ ".eslintrc.json",
58952
+ "jest.config.js",
58953
+ "jest.config.ts",
58954
+ "jest.config.json",
58955
+ "mocha.opts",
58956
+ "nodemon.json",
58957
+ "nodemon.js",
58958
+ "next.config.ts",
58959
+ "next.config.js",
58960
+ "nuxt.config.js",
58961
+ "nuxt.config.ts",
58962
+ "postcss.config.mjs",
58963
+ "postcss.config.js",
58964
+ "tailwind.config.js",
58965
+ "tailwind.config.ts",
58966
+ "supabase/config.toml",
58967
+ "pnpm-workspace.yaml",
58968
+ "yarn.lock",
58969
+ "package-lock.json",
58970
+ "tsconfig.base.json",
58971
+ "tsconfig.node.json",
58972
+ "tsconfig.app.json",
58973
+ "turbo.json",
58974
+ "biome.json",
58975
+ "biome.jsonc",
58976
+ "angular.json",
58977
+ "nest-cli.json",
58978
+ "remix.config.js",
58979
+ "remix.config.ts"
58980
+ ];
58981
+ const docFiles = [
58982
+ "README.md",
58983
+ "README.txt",
58984
+ "readme.md",
58985
+ "Readme.md",
58986
+ "CHANGELOG.md",
58987
+ "CHANGELOG.txt",
58988
+ "LICENSE",
58989
+ "LICENSE.md",
58990
+ "CONTRIBUTING.md",
58991
+ "NOTICE",
58992
+ "NOTICE.md",
58993
+ "SECURITY.md",
58994
+ "CODE_OF_CONDUCT.md",
58995
+ "PULL_REQUEST_TEMPLATE.md",
58996
+ "ISSUE_TEMPLATE.md",
58997
+ "HISTORY.md",
58998
+ "AUTHORS",
58999
+ "THANKS",
59000
+ "CITATION.cff",
59001
+ "CITATION.bib"
59002
+ ];
59003
+ const buildToolFiles = [
59004
+ "typedoc.config.cjs",
59005
+ "typedoc.json",
59006
+ "typedoc.config.js",
59007
+ "typedoc.config.ts",
59008
+ "documentation.config.js",
59009
+ "docs.config.js",
59010
+ "api-extractor.json",
59011
+ "api-documenter.json",
59012
+ "jsdoc.json",
59013
+ "jsdoc.config.json",
59014
+ "compodoc.json",
59015
+ "documentation.yml"
59016
+ ];
59017
+ function findFilesRecursively(dir, fileNames, results, maxDepth = 3, currentDepth = 0) {
59018
+ if (currentDepth > maxDepth)
59019
+ return;
59020
+ if (!fs2.existsSync(dir))
59021
+ return;
59022
+ const items = fs2.readdirSync(dir, { withFileTypes: true });
59023
+ for (const item of items) {
59024
+ const fullPath = path3.join(dir, item.name);
59025
+ if (item.isDirectory()) {
59026
+ if (!item.name.startsWith(".") && item.name !== "node_modules" && item.name !== "dist" && item.name !== "build") {
59027
+ findFilesRecursively(fullPath, fileNames, results, maxDepth, currentDepth + 1);
59028
+ }
59029
+ } else {
59030
+ if (fileNames.includes(item.name)) {
59031
+ const relativePath = path3.relative(directory, fullPath);
59032
+ results.push({
59033
+ path: relativePath,
59034
+ name: item.name
59035
+ });
59036
+ }
59037
+ }
59038
+ }
59039
+ }
59040
+ const foundConfigFiles = [];
59041
+ findFilesRecursively(directory, configFiles, foundConfigFiles);
59042
+ foundConfigFiles.forEach((fileObj) => {
59043
+ envelope.configuration_files.push({
59044
+ path: fileObj.path,
59045
+ role: getRoleForConfigFile(fileObj.name)
59046
+ });
59047
+ });
59048
+ const foundDocFiles = [];
59049
+ findFilesRecursively(directory, docFiles, foundDocFiles);
59050
+ foundDocFiles.forEach((fileObj) => {
59051
+ envelope.documentation.push({
59052
+ path: fileObj.path,
59053
+ role: getRoleForDocFile(fileObj.name)
59054
+ });
59055
+ });
59056
+ const foundBuildToolFiles = [];
59057
+ findFilesRecursively(directory, buildToolFiles, foundBuildToolFiles);
59058
+ foundBuildToolFiles.forEach((fileObj) => {
59059
+ envelope.build_tools.push({
59060
+ path: fileObj.path,
59061
+ role: getRoleForBuildToolFile(fileObj.name)
59062
+ });
59063
+ });
59064
+ envelope.configuration_files = envelope.configuration_files.filter(
59065
+ (file, index, self2) => index === self2.findIndex((f) => f.path === file.path)
59066
+ );
59067
+ envelope.documentation = envelope.documentation.filter(
59068
+ (file, index, self2) => index === self2.findIndex((f) => f.path === file.path)
59069
+ );
59070
+ envelope.build_tools = envelope.build_tools.filter(
59071
+ (file, index, self2) => index === self2.findIndex((f) => f.path === file.path)
59072
+ );
59073
+ return envelope;
59074
+ }
59075
+ function getRoleForConfigFile(fileName) {
59076
+ const roles = {
59077
+ "package.json": "dependency_manifest",
59078
+ "tsconfig.json": "compiler_configuration",
59079
+ "jsconfig.json": "compiler_configuration",
59080
+ "webpack.config.js": "bundler_configuration",
59081
+ "webpack.config.ts": "bundler_configuration",
59082
+ "vite.config.js": "bundler_configuration",
59083
+ "vite.config.ts": "bundler_configuration",
59084
+ "rollup.config.js": "bundler_configuration",
59085
+ "rollup.config.ts": "bundler_configuration",
59086
+ "babel.config.js": "transpiler_configuration",
59087
+ "babel.config.ts": "transpiler_configuration",
59088
+ ".babelrc": "transpiler_configuration",
59089
+ ".babelrc.js": "transpiler_configuration",
59090
+ "eslint.config.mjs": "linting_configuration",
59091
+ "eslint.config.js": "linting_configuration",
59092
+ ".eslintrc": "linting_configuration",
59093
+ ".eslintrc.js": "linting_configuration",
59094
+ ".eslintrc.json": "linting_configuration",
59095
+ "jest.config.js": "testing_configuration",
59096
+ "jest.config.ts": "testing_configuration",
59097
+ "jest.config.json": "testing_configuration",
59098
+ "mocha.opts": "testing_configuration",
59099
+ "nodemon.json": "development_configuration",
59100
+ "nodemon.js": "development_configuration",
59101
+ "next.config.ts": "framework_configuration",
59102
+ "next.config.js": "framework_configuration",
59103
+ "nuxt.config.js": "framework_configuration",
59104
+ "nuxt.config.ts": "framework_configuration",
59105
+ "postcss.config.mjs": "css_processing_configuration",
59106
+ "postcss.config.js": "css_processing_configuration",
59107
+ "tailwind.config.js": "css_framework_configuration",
59108
+ "tailwind.config.ts": "css_framework_configuration",
59109
+ "supabase/config.toml": "database_service_configuration",
59110
+ "pnpm-workspace.yaml": "package_manager_configuration",
59111
+ "yarn.lock": "dependency_lockfile",
59112
+ "package-lock.json": "dependency_lockfile",
59113
+ "tsconfig.base.json": "compiler_base_configuration",
59114
+ "tsconfig.node.json": "compiler_node_configuration",
59115
+ "tsconfig.app.json": "compiler_app_configuration",
59116
+ "turbo.json": "build_system_configuration",
59117
+ "biome.json": "formatter_linter_configuration",
59118
+ "biome.jsonc": "formatter_linter_configuration",
59119
+ "angular.json": "framework_configuration",
59120
+ "nest-cli.json": "framework_configuration",
59121
+ "remix.config.js": "framework_configuration",
59122
+ "remix.config.ts": "framework_configuration"
59123
+ };
59124
+ return roles[fileName] || "general_configuration";
59125
+ }
59126
+ function getRoleForDocFile(fileName) {
59127
+ const roles = {
59128
+ "README.md": "system_overview",
59129
+ "README.txt": "system_overview",
59130
+ "readme.md": "system_overview",
59131
+ "Readme.md": "system_overview",
59132
+ "CHANGELOG.md": "change_history",
59133
+ "CHANGELOG.txt": "change_history",
59134
+ "LICENSE": "license_terms",
59135
+ "LICENSE.md": "license_terms",
59136
+ "CONTRIBUTING.md": "contribution_guidelines",
59137
+ "NOTICE": "legal_notice",
59138
+ "NOTICE.md": "legal_notice",
59139
+ "SECURITY.md": "security_policy",
59140
+ "CODE_OF_CONDUCT.md": "code_of_conduct",
59141
+ "PULL_REQUEST_TEMPLATE.md": "pull_request_guidelines",
59142
+ "ISSUE_TEMPLATE.md": "issue_reporting_guidelines",
59143
+ "HISTORY.md": "project_history",
59144
+ "AUTHORS": "authorship_information",
59145
+ "THANKS": "acknowledgements",
59146
+ "CITATION.cff": "citation_information",
59147
+ "CITATION.bib": "citation_information"
59148
+ };
59149
+ return roles[fileName] || "documentation";
59150
+ }
59151
+ function getRoleForBuildToolFile(fileName) {
59152
+ const roles = {
59153
+ "typedoc.config.cjs": "documentation_generator",
59154
+ "typedoc.json": "documentation_generator",
59155
+ "typedoc.config.js": "documentation_generator",
59156
+ "typedoc.config.ts": "documentation_generator",
59157
+ "documentation.config.js": "documentation_generator",
59158
+ "docs.config.js": "documentation_generator",
59159
+ "api-extractor.json": "api_documentation_generator",
59160
+ "api-documenter.json": "api_documentation_generator",
59161
+ "jsdoc.json": "documentation_generator",
59162
+ "jsdoc.config.json": "documentation_generator",
59163
+ "compodoc.json": "angular_documentation_generator",
59164
+ "documentation.yml": "documentation_generator"
59165
+ };
59166
+ return roles[fileName] || "build_tool_configuration";
59167
+ }
59168
+ function generateAssumptions() {
59169
+ return [
59170
+ "Dynamic imports resolved statically where possible",
59171
+ "Runtime reflection not fully captured",
59172
+ "Only code reachable from entry points analyzed",
59173
+ "Third-party dependencies treated as black boxes",
59174
+ "Build-time code generation not reflected in static analysis",
59175
+ "ArcVision prioritizes execution-relevant structure over exhaustive file indexing. Non-runtime files are represented as envelope metadata unless they directly affect execution."
59176
+ ];
59177
+ }
59178
+ module2.exports = { buildContext, getGitInfo, calculateIntegrityHash, generateAssumptions, generateStructuralLayers, generateProjectEnvelope, generateAdaptiveStructuralLayers };
58639
59179
  }
58640
59180
  });
58641
59181
 
@@ -65222,7 +65762,9 @@ var require_scanner = __commonJS({
65222
65762
  const contextOptions = {
65223
65763
  directory,
65224
65764
  projectName: path2.basename(directory),
65225
- contextSurface
65765
+ contextSurface,
65766
+ arcvisionVersion: process.env.npm_package_version || "0.1.0"
65767
+ // Pass version from package.json
65226
65768
  };
65227
65769
  let context = buildContext(nodes, edges, symbols, contextOptions);
65228
65770
  console.log(" Validating structural context...");
@@ -65244,6 +65786,199 @@ var require_scanner = __commonJS({
65244
65786
  }
65245
65787
  });
65246
65788
 
65789
+ // src/core/diff-analyzer.js
65790
+ var require_diff_analyzer = __commonJS({
65791
+ "src/core/diff-analyzer.js"(exports2, module2) {
65792
+ function generateDiffSummary2(oldContext, newContext) {
65793
+ const diffSummary = {
65794
+ nodes_added: 0,
65795
+ nodes_removed: 0,
65796
+ edges_added: 0,
65797
+ edges_removed: 0,
65798
+ roles_changed: 0,
65799
+ blast_radius_changes: 0
65800
+ };
65801
+ const oldNodeMap = new Map(oldContext.nodes.map((node) => [node.path, node]));
65802
+ const newNodeMap = new Map(newContext.nodes.map((node) => [node.path, node]));
65803
+ for (const [path2, node] of newNodeMap) {
65804
+ if (!oldNodeMap.has(path2)) {
65805
+ diffSummary.nodes_added++;
65806
+ }
65807
+ }
65808
+ for (const [path2, node] of oldNodeMap) {
65809
+ if (!newNodeMap.has(path2)) {
65810
+ diffSummary.nodes_removed++;
65811
+ }
65812
+ }
65813
+ const oldEdgeMap = new Map(
65814
+ oldContext.edges.map((edge) => [`${edge.from}::${edge.to}::${edge.relation}`, edge])
65815
+ );
65816
+ const newEdgeMap = new Map(
65817
+ newContext.edges.map((edge) => [`${edge.from}::${edge.to}::${edge.relation}`, edge])
65818
+ );
65819
+ for (const [key, edge] of newEdgeMap) {
65820
+ if (!oldEdgeMap.has(key)) {
65821
+ diffSummary.edges_added++;
65822
+ }
65823
+ }
65824
+ for (const [key, edge] of oldEdgeMap) {
65825
+ if (!newEdgeMap.has(key)) {
65826
+ diffSummary.edges_removed++;
65827
+ }
65828
+ }
65829
+ for (const newNode of newContext.nodes) {
65830
+ const oldNode = oldNodeMap.get(newNode.path);
65831
+ if (oldNode && oldNode.role !== newNode.role) {
65832
+ diffSummary.roles_changed++;
65833
+ }
65834
+ }
65835
+ for (const newNode of newContext.nodes) {
65836
+ const oldNode = oldNodeMap.get(newNode.path);
65837
+ if (oldNode && oldNode.blast_radius !== newNode.blast_radius) {
65838
+ diffSummary.blast_radius_changes++;
65839
+ }
65840
+ }
65841
+ return {
65842
+ diff_summary: diffSummary
65843
+ };
65844
+ }
65845
+ module2.exports = { generateDiffSummary: generateDiffSummary2 };
65846
+ }
65847
+ });
65848
+
65849
+ // src/core/readme-generator.js
65850
+ var require_readme_generator = __commonJS({
65851
+ "src/core/readme-generator.js"(exports2, module2) {
65852
+ var fs2 = require("fs");
65853
+ var path2 = require("path");
65854
+ var { spawnSync } = require("child_process");
65855
+ function getCommitHash() {
65856
+ try {
65857
+ const result = spawnSync("git", ["rev-parse", "HEAD"], {
65858
+ stdio: ["pipe", "pipe", "pipe"]
65859
+ });
65860
+ if (result.status === 0) {
65861
+ return result.stdout.toString().trim();
65862
+ }
65863
+ return "unknown";
65864
+ } catch (error) {
65865
+ return "unknown";
65866
+ }
65867
+ }
65868
+ function generateReadmeContent(commitHash, timestamp, toolVersion, blastRadiusData = null) {
65869
+ let content = `# ArcVision System Context Artifact
65870
+
65871
+
65872
+ ## What This Is [arcvision.context.json](./arcvision.context.json)
65873
+
65874
+ This file is the **canonical structural context** of this codebase.
65875
+ It represents how the system actually works \u2014 not how it is described.
65876
+
65877
+ It is generated directly from source code by ArcVision.
65878
+
65879
+ ## What This Replaces
65880
+
65881
+ This artifact replaces:
65882
+ - Manual repository scanning
65883
+ - Tribal knowledge held by senior engineers
65884
+ - Re-explaining the system to new developers
65885
+ - Re-prompting LLMs with partial or incorrect context
65886
+ - Guessing blast radius of changes
65887
+
65888
+ ## When You Must Use This
65889
+
65890
+ Use this artifact when:
65891
+ - Onboarding a new developer
65892
+ - Prompting an AI assistant about this codebase
65893
+ - Making architectural changes
65894
+ - Investigating unexpected behavior
65895
+ - Assessing risk before modifying core modules
65896
+
65897
+ ## What This Artifact Contains
65898
+
65899
+ - Canonical module and dependency graph
65900
+ - Execution and data flow relationships
65901
+ - Structural roles (service, store, boundary, etc.)
65902
+ - Invariants inferred from the system
65903
+ - Impact metrics (blast radius, coupling)
65904
+
65905
+ ## Determinism & Trust
65906
+
65907
+ - Generated from commit: ${commitHash}
65908
+ - Generation timestamp: ${timestamp}
65909
+ - Tool version: ${toolVersion}
65910
+ - Deterministic: same input \u2192 same output
65911
+ - Explicit assumptions listed inside the artifact
65912
+
65913
+ If this artifact conflicts with human memory, **trust the artifact**.`;
65914
+ if (blastRadiusData && blastRadiusData.topFiles && blastRadiusData.topFiles.length > 0) {
65915
+ content += `
65916
+
65917
+ ## Structural Context Hubs
65918
+
65919
+ The following files have the highest blast radius and represent critical structural hubs in the system:
65920
+
65921
+ `;
65922
+ blastRadiusData.topFiles.forEach((item, index) => {
65923
+ let warningMessage = "";
65924
+ if (index === 0) {
65925
+ warningMessage = "Changes here may silently propagate across the system.";
65926
+ } else if (index === 1) {
65927
+ warningMessage = "Acts as a coordination layer between components.";
65928
+ } else {
65929
+ warningMessage = "Modifications can cause widespread inconsistencies.";
65930
+ }
65931
+ content += `- **${item.file}**
65932
+ - Blast Radius: ${item.blastRadius} files (${item.percentOfGraph}% of codebase)
65933
+ - Risk: ${warningMessage}
65934
+
65935
+ `;
65936
+ });
65937
+ } else {
65938
+ content += `
65939
+
65940
+ ## Structural Context Hubs
65941
+
65942
+ No high-structure files detected based on import dependencies.`;
65943
+ }
65944
+ content += `
65945
+
65946
+ ## How to Use With AI
65947
+
65948
+ When prompting AI tools, include this file as system context.
65949
+ Do not ask the AI to infer architecture without it.
65950
+
65951
+ ## When to Regenerate
65952
+
65953
+ Regenerate this artifact when:
65954
+ - Core modules change
65955
+ - New services are added
65956
+ - Dependency structure shifts
65957
+
65958
+ Run:
65959
+
65960
+ \`\`\`
65961
+ arcvision scan --upload
65962
+ \`\`\`
65963
+
65964
+ ## Source of Truth
65965
+
65966
+ This artifact is the **source of truth** for system structure.
65967
+ All explanations, decisions, and AI reasoning should reference it.`;
65968
+ return content;
65969
+ }
65970
+ function generateReadme(outputDir, toolVersion, blastRadiusData = null) {
65971
+ const commitHash = getCommitHash();
65972
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
65973
+ const readmeContent = generateReadmeContent(commitHash, timestamp, toolVersion, blastRadiusData);
65974
+ const readmePath = path2.join(outputDir, "README.md");
65975
+ fs2.writeFileSync(readmePath, readmeContent);
65976
+ console.log(`\u2705 System context README generated at ${readmePath}`);
65977
+ }
65978
+ module2.exports = { generateReadme, generateReadmeContent, getCommitHash };
65979
+ }
65980
+ });
65981
+
65247
65982
  // src/index.js
65248
65983
  var { Command } = require_commander();
65249
65984
  var chalk = require_source();
@@ -65251,6 +65986,7 @@ var path = require("path");
65251
65986
  var fs = require("fs");
65252
65987
  var os = require("os");
65253
65988
  var scanner = require_scanner();
65989
+ var { generateDiffSummary } = require_diff_analyzer();
65254
65990
  var version = "1.0.0";
65255
65991
  try {
65256
65992
  const packageJsonPath = path.join(__dirname, "../package.json");
@@ -65267,7 +66003,9 @@ function analyzeBlastRadius(architectureMap) {
65267
66003
  if (architectureMap.nodes && architectureMap.nodes.length > 0) {
65268
66004
  architectureMap.nodes.forEach((node) => {
65269
66005
  let blastRadius = 0;
65270
- if (node.metadata && node.metadata.blast_radius !== void 0) {
66006
+ if (node.blast_radius !== void 0) {
66007
+ blastRadius = node.blast_radius;
66008
+ } else if (node.metadata && node.metadata.blast_radius !== void 0) {
65271
66009
  blastRadius = node.metadata.blast_radius;
65272
66010
  } else if (node.signals && node.signals.blast_radius !== void 0) {
65273
66011
  blastRadius = node.signals.blast_radius;
@@ -65463,11 +66201,15 @@ program.command("scan").description("Scan the current directory and generate arc
65463
66201
  }
65464
66202
  if (options.upload) {
65465
66203
  await uploadToDatabase(map);
66204
+ const { generateReadme } = require_readme_generator();
66205
+ generateReadme(".", version, blastRadiusAnalysis);
65466
66206
  } else {
65467
66207
  const fs2 = require("fs");
65468
66208
  const outputFileName = "arcvision.context.json";
65469
66209
  fs2.writeFileSync(outputFileName, JSON.stringify(map, null, 2));
65470
66210
  console.log(chalk.green(`\u2705 Structural context saved to ${outputFileName}`));
66211
+ const { generateReadme } = require_readme_generator();
66212
+ generateReadme(".", version, blastRadiusAnalysis);
65471
66213
  console.log(chalk.dim("\nUse --upload to send to dashboard."));
65472
66214
  }
65473
66215
  } catch (error) {
@@ -65492,4 +66234,29 @@ program.command("scan").description("Scan the current directory and generate arc
65492
66234
  }
65493
66235
  }
65494
66236
  });
66237
+ program.command("diff").description("Compare two context artifacts and generate diff summary").argument("<old-file>", "Path to the old context artifact").argument("<new-file>", "Path to the new context artifact").action(async (oldFile, newFile) => {
66238
+ try {
66239
+ console.log(chalk.blue(`Comparing context artifacts:`));
66240
+ console.log(chalk.blue(` Old: ${oldFile}`));
66241
+ console.log(chalk.blue(` New: ${newFile}`));
66242
+ const oldContext = JSON.parse(fs.readFileSync(oldFile, "utf8"));
66243
+ const newContext = JSON.parse(fs.readFileSync(newFile, "utf8"));
66244
+ const diffResult = generateDiffSummary(oldContext, newContext);
66245
+ newContext.diff_summary = diffResult.diff_summary;
66246
+ const outputFileName = "arcvision.context.diff.json";
66247
+ fs.writeFileSync(outputFileName, JSON.stringify(newContext, null, 2));
66248
+ console.log(chalk.green("\u2705 Structural diff completed!"));
66249
+ console.log(chalk.green(`\u2705 Diff summary saved to ${outputFileName}`));
66250
+ console.log(chalk.yellow("\nDiff Summary:"));
66251
+ console.log(chalk.yellow(` Nodes Added: ${diffResult.diff_summary.nodes_added}`));
66252
+ console.log(chalk.yellow(` Nodes Removed: ${diffResult.diff_summary.nodes_removed}`));
66253
+ console.log(chalk.yellow(` Edges Added: ${diffResult.diff_summary.edges_added}`));
66254
+ console.log(chalk.yellow(` Edges Removed: ${diffResult.diff_summary.edges_removed}`));
66255
+ console.log(chalk.yellow(` Roles Changed: ${diffResult.diff_summary.roles_changed}`));
66256
+ console.log(chalk.yellow(` Blast Radius Changes: ${diffResult.diff_summary.blast_radius_changes}`));
66257
+ } catch (error) {
66258
+ console.error(chalk.red("\u274C Diff failed:"), error.message);
66259
+ process.exit(1);
66260
+ }
66261
+ });
65495
66262
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcvision",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "Architecture scanner for modern codebases",
5
5
  "bin": {
6
6
  "arcvision": "./dist/index.js"
@@ -8,7 +8,12 @@
8
8
  "system",
9
9
  "nodes",
10
10
  "edges",
11
- "metrics"
11
+ "metrics",
12
+ "assumptions",
13
+ "source",
14
+ "integrity",
15
+ "structural_layers",
16
+ "project_envelope"
12
17
  ],
13
18
  "properties": {
14
19
  "schema_version": {
@@ -79,6 +84,138 @@
79
84
  "additionalProperties": {
80
85
  "type": ["number", "string"]
81
86
  }
87
+ },
88
+ "assumptions": {
89
+ "type": "array",
90
+ "description": "Explicit assumptions about analysis limitations",
91
+ "items": {
92
+ "type": "string"
93
+ }
94
+ },
95
+ "source": {
96
+ "type": "object",
97
+ "description": "Source repository and commit information",
98
+ "required": ["repo", "commit", "generated_at", "arcvision_version"],
99
+ "properties": {
100
+ "repo": { "type": "string" },
101
+ "commit": { "type": "string" },
102
+ "generated_at": { "type": "string" },
103
+ "arcvision_version": { "type": "string" }
104
+ }
105
+ },
106
+ "integrity": {
107
+ "type": "object",
108
+ "description": "Integrity hash of the artifact",
109
+ "required": ["sha256"],
110
+ "properties": {
111
+ "sha256": { "type": "string" }
112
+ }
113
+ },
114
+ "structural_layers": {
115
+ "type": "object",
116
+ "description": "Defines which structural layers are included in the analysis",
117
+ "required": ["runtime_code", "execution_scripts", "infrastructure_scripts", "configuration", "documentation", "assets"],
118
+ "properties": {
119
+ "runtime_code": {
120
+ "type": "object",
121
+ "required": ["status", "description"],
122
+ "properties": {
123
+ "status": { "type": "string", "enum": ["included", "excluded"] },
124
+ "description": { "type": "string" }
125
+ }
126
+ },
127
+ "execution_scripts": {
128
+ "type": "object",
129
+ "required": ["status", "description"],
130
+ "properties": {
131
+ "status": { "type": "string", "enum": ["included", "excluded"] },
132
+ "description": { "type": "string" }
133
+ }
134
+ },
135
+ "infrastructure_scripts": {
136
+ "type": "object",
137
+ "required": ["status", "description"],
138
+ "properties": {
139
+ "status": { "type": "string", "enum": ["included", "excluded"] },
140
+ "description": { "type": "string" }
141
+ }
142
+ },
143
+ "configuration": {
144
+ "type": "object",
145
+ "required": ["status", "description"],
146
+ "properties": {
147
+ "status": { "type": "string", "enum": ["included", "excluded", "optional"] },
148
+ "description": { "type": "string" }
149
+ }
150
+ },
151
+ "documentation": {
152
+ "type": "object",
153
+ "required": ["status", "description"],
154
+ "properties": {
155
+ "status": { "type": "string", "enum": ["included", "excluded", "optional"] },
156
+ "description": { "type": "string" }
157
+ }
158
+ },
159
+ "assets": {
160
+ "type": "object",
161
+ "required": ["status", "description"],
162
+ "properties": {
163
+ "status": { "type": "string", "enum": ["included", "excluded"] },
164
+ "description": { "type": "string" }
165
+ }
166
+ }
167
+ }
168
+ },
169
+ "project_envelope": {
170
+ "type": "object",
171
+ "description": "Project-level files that provide context but are not execution graph nodes",
172
+ "properties": {
173
+ "configuration_files": {
174
+ "type": "array",
175
+ "items": {
176
+ "type": "object",
177
+ "properties": {
178
+ "path": { "type": "string" },
179
+ "role": { "type": "string" }
180
+ },
181
+ "required": ["path", "role"]
182
+ }
183
+ },
184
+ "documentation": {
185
+ "type": "array",
186
+ "items": {
187
+ "type": "object",
188
+ "properties": {
189
+ "path": { "type": "string" },
190
+ "role": { "type": "string" }
191
+ },
192
+ "required": ["path", "role"]
193
+ }
194
+ },
195
+ "build_tools": {
196
+ "type": "array",
197
+ "items": {
198
+ "type": "object",
199
+ "properties": {
200
+ "path": { "type": "string" },
201
+ "role": { "type": "string" }
202
+ },
203
+ "required": ["path", "role"]
204
+ }
205
+ }
206
+ }
207
+ },
208
+ "diff_summary": {
209
+ "type": "object",
210
+ "description": "Structural diff summary between commits",
211
+ "properties": {
212
+ "nodes_added": { "type": "number" },
213
+ "nodes_removed": { "type": "number" },
214
+ "edges_added": { "type": "number" },
215
+ "edges_removed": { "type": "number" },
216
+ "roles_changed": { "type": "number" },
217
+ "blast_radius_changes": { "type": "number" }
218
+ }
82
219
  }
83
220
  }
84
221
  }
@@ -1,84 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "title": "Arcvision System-Level Structural Context",
4
- "type": "object",
5
- "required": [
6
- "schema_version",
7
- "generated_at",
8
- "system",
9
- "nodes",
10
- "edges",
11
- "metrics"
12
- ],
13
- "properties": {
14
- "schema_version": {
15
- "type": "string",
16
- "description": "Semantic version of the Arcvision context schema (e.g. 1.0.0)"
17
- },
18
- "generated_at": {
19
- "type": "string",
20
- "format": "date-time",
21
- "description": "ISO-8601 timestamp when context was generated"
22
- },
23
- "system": {
24
- "type": "object",
25
- "required": ["name", "root_path", "language"],
26
- "properties": {
27
- "name": { "type": "string" },
28
- "root_path": { "type": "string" },
29
- "language": { "type": "string" }
30
- }
31
- },
32
- "nodes": {
33
- "type": "array",
34
- "items": {
35
- "type": "object",
36
- "required": ["id", "type", "path", "role"],
37
- "properties": {
38
- "id": {
39
- "type": "string",
40
- "description": "Deterministic, stable ID (never random)"
41
- },
42
- "type": {
43
- "type": "string",
44
- "enum": ["file", "module", "class", "function", "service"]
45
- },
46
- "path": {
47
- "type": "string",
48
- "description": "Normalized relative path"
49
- },
50
- "role": {
51
- "type": "string",
52
- "description": "Why this node exists in the system"
53
- },
54
- "dependencies": {
55
- "type": "array",
56
- "items": { "type": "string" }
57
- }
58
- }
59
- }
60
- },
61
- "edges": {
62
- "type": "array",
63
- "items": {
64
- "type": "object",
65
- "required": ["from", "to", "relation"],
66
- "properties": {
67
- "from": { "type": "string" },
68
- "to": { "type": "string" },
69
- "relation": {
70
- "type": "string",
71
- "enum": ["imports", "calls", "owns", "depends_on"]
72
- }
73
- }
74
- }
75
- },
76
- "metrics": {
77
- "type": "object",
78
- "description": "System-wide aggregate metrics",
79
- "additionalProperties": {
80
- "type": ["number", "string"]
81
- }
82
- }
83
- }
84
- }