arcvision 0.2.5 → 0.2.7

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,69 @@ 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
+ // Matches defineCommand(commandName, { ... lua: ... })
58213
+ /defineCommand\(\s*\{\s*lua\s*:.*?\}/gi,
58214
+ // Original pattern
58215
+ /require\(['"].*\.lua['"].*\)/gi,
58216
+ /fs\.readFileSync\(['"].*\.lua['"].*\)/gi,
58217
+ /import\s+.*?from\s+['"].*\.lua['"].*\)?/gi,
58218
+ /import\(['"].*\.lua['"].*\)/gi,
58219
+ /loadScript\(['"].*\.lua['"].*\)/gi,
58220
+ /loadLua\(['"].*\.lua['"].*\)/gi,
58221
+ /load\(['"].*\.lua['"].*\)/gi
58222
+ ];
58223
+ for (const pattern of luaInvocationPatterns) {
58224
+ let match;
58225
+ while ((match = pattern.exec(content)) !== null) {
58226
+ const luaFileMatch = match[0].match(/['"].*?\.lua['"]/);
58227
+ if (luaFileMatch) {
58228
+ let luaFilePath = luaFileMatch[0].slice(1, -1);
58229
+ if (luaFilePath.startsWith("./") || luaFilePath.startsWith("../")) {
58230
+ luaFilePath = path2.resolve(path2.dirname(node.filePath), luaFilePath);
58231
+ luaFilePath = normalize(path2.relative(rootDir, luaFilePath));
58232
+ } else if (luaFilePath.startsWith("/")) {
58233
+ luaFilePath = normalize(path2.relative(rootDir, path2.join(rootDir, luaFilePath)));
58234
+ } else {
58235
+ const commonLuaDirs = ["src", "scripts", "commands", "lua", "lib"];
58236
+ let foundPath = null;
58237
+ for (const dir of commonLuaDirs) {
58238
+ const candidatePath = normalize(path2.join(dir, luaFilePath));
58239
+ if (fileSet.has(candidatePath)) {
58240
+ foundPath = candidatePath;
58241
+ break;
58242
+ }
58243
+ }
58244
+ if (foundPath) {
58245
+ luaFilePath = foundPath;
58246
+ } else {
58247
+ const nodeDir = path2.dirname(node.id);
58248
+ luaFilePath = normalize(path2.join(nodeDir, luaFilePath));
58249
+ }
58250
+ }
58251
+ if (fileSet.has(luaFilePath)) {
58252
+ addEdge(node.id, luaFilePath, "invokes_execution_script", 1, {
58253
+ type: "lua_invocation",
58254
+ invocation_pattern: match[0],
58255
+ original_match: match[0],
58256
+ resolved_path: luaFilePath
58257
+ });
58258
+ luaInvocationCount++;
58259
+ }
58260
+ }
58261
+ }
58262
+ }
58263
+ }
58264
+ console.log(` \u2713 Detected ${luaInvocationCount} Lua script invocations`);
58168
58265
  let typeEdgesCount = 0;
58169
58266
  for (const node of rawNodes) {
58170
58267
  if (!node.facts.typeAnalysis)
@@ -58522,6 +58619,8 @@ var require_id_generator = __commonJS({
58522
58619
  var require_context_builder = __commonJS({
58523
58620
  "src/core/context_builder.js"(exports2, module2) {
58524
58621
  var path2 = require("path");
58622
+ var { spawnSync } = require("child_process");
58623
+ var crypto = require("crypto");
58525
58624
  var { stableId } = require_id_generator();
58526
58625
  function buildContext(fileNodes, edges, symbols, options = {}) {
58527
58626
  const {
@@ -58619,9 +58718,11 @@ var require_context_builder = __commonJS({
58619
58718
  total_dependencies: schemaEdges.length,
58620
58719
  files_with_high_blast_radius: nodes.filter((n) => n.blast_radius > 5).length
58621
58720
  };
58721
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
58722
+ const dateOnly = timestamp.split("T")[0];
58622
58723
  const context = {
58623
58724
  schema_version: "1.0.0",
58624
- generated_at: (/* @__PURE__ */ new Date()).toISOString(),
58725
+ generated_at: timestamp,
58625
58726
  system: {
58626
58727
  name: projectName,
58627
58728
  root_path: path2.resolve(directory),
@@ -58631,11 +58732,453 @@ var require_context_builder = __commonJS({
58631
58732
  edges: schemaEdges,
58632
58733
  symbols: symbols || [],
58633
58734
  metrics,
58634
- contextSurface: options.contextSurface || []
58735
+ contextSurface: options.contextSurface || [],
58736
+ // Phase 2 requirement: Explicit Assumptions Section
58737
+ assumptions: generateAssumptions(),
58738
+ // Phase 2 requirement: Embed Commit Identity
58739
+ source: {
58740
+ repo: getGitInfo().repo,
58741
+ commit: getGitInfo().commit,
58742
+ generated_at: dateOnly,
58743
+ // Use date only to avoid time variations
58744
+ arcvision_version: options.arcvisionVersion || "0.1.0"
58745
+ },
58746
+ // Phase 3 requirement: Structural layers definition
58747
+ structural_layers: generateAdaptiveStructuralLayers(directory),
58748
+ // Phase 3 requirement: Project envelope
58749
+ project_envelope: generateProjectEnvelope(directory)
58750
+ };
58751
+ const contextWithoutIntegrity = { ...context };
58752
+ delete contextWithoutIntegrity.integrity;
58753
+ const hash = crypto.createHash("sha256").update(JSON.stringify(contextWithoutIntegrity)).digest("hex");
58754
+ context.integrity = {
58755
+ sha256: hash
58635
58756
  };
58636
58757
  return context;
58637
58758
  }
58638
- module2.exports = { buildContext };
58759
+ function getGitInfo() {
58760
+ try {
58761
+ const originResult = spawnSync("git", ["remote", "get-url", "origin"], {
58762
+ stdio: ["pipe", "pipe", "pipe"]
58763
+ });
58764
+ let repoUrl = "unknown";
58765
+ if (originResult.status === 0) {
58766
+ let url = originResult.stdout.toString().trim();
58767
+ if (url.startsWith("git@")) {
58768
+ url = url.replace(/^git@(.*?):/, "https://$1/");
58769
+ url = url.replace(/\.git$/, "");
58770
+ } else if (url.endsWith(".git")) {
58771
+ url = url.replace(/\.git$/, "");
58772
+ }
58773
+ repoUrl = url;
58774
+ }
58775
+ const commitResult = spawnSync("git", ["rev-parse", "HEAD"], {
58776
+ stdio: ["pipe", "pipe", "pipe"]
58777
+ });
58778
+ let commitHash = "unknown";
58779
+ if (commitResult.status === 0) {
58780
+ commitHash = commitResult.stdout.toString().trim();
58781
+ }
58782
+ return {
58783
+ repo: repoUrl,
58784
+ commit: commitHash
58785
+ };
58786
+ } catch (error) {
58787
+ return {
58788
+ repo: "unknown",
58789
+ commit: "unknown"
58790
+ };
58791
+ }
58792
+ }
58793
+ function calculateIntegrityHash(obj) {
58794
+ try {
58795
+ const objCopy = JSON.parse(JSON.stringify(obj));
58796
+ if (objCopy.integrity) {
58797
+ delete objCopy.integrity;
58798
+ }
58799
+ const jsonString = JSON.stringify(objCopy);
58800
+ return crypto.createHash("sha256").update(jsonString).digest("hex");
58801
+ } catch (error) {
58802
+ return "calculation_error";
58803
+ }
58804
+ }
58805
+ function generateStructuralLayers() {
58806
+ return {
58807
+ "runtime_code": {
58808
+ "status": "included",
58809
+ "description": "Source files that directly participate in runtime execution and system behavior"
58810
+ },
58811
+ "execution_scripts": {
58812
+ "status": "included",
58813
+ "description": "Scripts invoked by runtime code to perform atomic or system-critical operations"
58814
+ },
58815
+ "infrastructure_scripts": {
58816
+ "status": "included",
58817
+ "description": "Scripts that manage infrastructure-level behavior (queues, databases, orchestration)"
58818
+ },
58819
+ "configuration": {
58820
+ "status": "optional",
58821
+ "description": "Project configuration files represented as metadata, not execution graph nodes"
58822
+ },
58823
+ "documentation": {
58824
+ "status": "optional",
58825
+ "description": "Documentation files represented as metadata only"
58826
+ },
58827
+ "assets": {
58828
+ "status": "excluded",
58829
+ "description": "Static assets with no effect on execution semantics"
58830
+ }
58831
+ };
58832
+ }
58833
+ function generateAdaptiveStructuralLayers(directory) {
58834
+ const fs2 = require("fs");
58835
+ const path3 = require("path");
58836
+ const hasPackageJson = fs2.existsSync(path3.join(directory, "package.json"));
58837
+ const hasConfigFiles = fs2.existsSync(path3.join(directory, "tsconfig.json")) || fs2.existsSync(path3.join(directory, "config")) || fs2.existsSync(path3.join(directory, "webpack.config.js"));
58838
+ const hasSrcDir = fs2.existsSync(path3.join(directory, "src"));
58839
+ const hasTestDir = fs2.existsSync(path3.join(directory, "test")) || fs2.existsSync(path3.join(directory, "tests"));
58840
+ const hasDocsDir = fs2.existsSync(path3.join(directory, "docs"));
58841
+ let totalFiles = 0;
58842
+ let jsFiles = 0;
58843
+ let tsFiles = 0;
58844
+ let luaFiles = 0;
58845
+ let configFiles = 0;
58846
+ function walk(dir, depth = 0) {
58847
+ if (depth > 3)
58848
+ return;
58849
+ if (!fs2.existsSync(dir))
58850
+ return;
58851
+ const items = fs2.readdirSync(dir, { withFileTypes: true });
58852
+ for (const item of items) {
58853
+ const fullPath = path3.join(dir, item.name);
58854
+ if (item.isDirectory()) {
58855
+ if (!item.name.startsWith(".") && item.name !== "node_modules" && item.name !== "dist" && item.name !== "build" && item.name !== ".git") {
58856
+ walk(fullPath, depth + 1);
58857
+ }
58858
+ } else {
58859
+ totalFiles++;
58860
+ if (item.name.endsWith(".js"))
58861
+ jsFiles++;
58862
+ if (item.name.endsWith(".ts"))
58863
+ tsFiles++;
58864
+ if (item.name.endsWith(".lua"))
58865
+ luaFiles++;
58866
+ if ([".json", ".yaml", ".yml", ".toml", ".config.js", ".config.ts", ".rc"].some((ext) => item.name.endsWith(ext)))
58867
+ configFiles++;
58868
+ }
58869
+ }
58870
+ }
58871
+ walk(directory);
58872
+ const isLuaProject = luaFiles > 0 && luaFiles / totalFiles > 0.1;
58873
+ const isLargeProject = totalFiles > 1e3;
58874
+ const layers = {
58875
+ "runtime_code": {
58876
+ "status": "included",
58877
+ "description": "Source files that directly participate in runtime execution and system behavior",
58878
+ "project_characteristics": {
58879
+ "has_package_json": hasPackageJson,
58880
+ "has_src_directory": hasSrcDir,
58881
+ "total_files": totalFiles,
58882
+ "javascript_files": jsFiles,
58883
+ "typescript_files": tsFiles,
58884
+ "lua_files": luaFiles
58885
+ }
58886
+ },
58887
+ "execution_scripts": {
58888
+ "status": isLuaProject ? "included" : "included",
58889
+ "description": "Scripts invoked by runtime code to perform atomic or system-critical operations",
58890
+ "project_characteristics": {
58891
+ "lua_files_count": luaFiles,
58892
+ "is_lua_project": isLuaProject
58893
+ }
58894
+ },
58895
+ "infrastructure_scripts": {
58896
+ "status": "included",
58897
+ "description": "Scripts that manage infrastructure-level behavior (queues, databases, orchestration)"
58898
+ },
58899
+ "configuration": {
58900
+ "status": hasConfigFiles ? "optional" : "excluded",
58901
+ "description": "Project configuration files represented as metadata, not execution graph nodes"
58902
+ },
58903
+ "documentation": {
58904
+ "status": hasDocsDir || hasPackageJson ? "optional" : "excluded",
58905
+ "description": "Documentation files represented as metadata only"
58906
+ },
58907
+ "assets": {
58908
+ "status": "excluded",
58909
+ "description": "Static assets with no effect on execution semantics"
58910
+ }
58911
+ };
58912
+ layers.project_metadata = {
58913
+ "size_category": isLargeProject ? "large" : totalFiles > 100 ? "medium" : "small",
58914
+ "file_count": totalFiles,
58915
+ "language_mix": {
58916
+ "javascript": jsFiles,
58917
+ "typescript": tsFiles,
58918
+ "lua": luaFiles,
58919
+ "config": configFiles
58920
+ },
58921
+ "has_package_management": hasPackageJson,
58922
+ "has_standard_structure": hasSrcDir,
58923
+ "has_tests": hasTestDir,
58924
+ "is_lua_intensive": isLuaProject
58925
+ };
58926
+ return layers;
58927
+ }
58928
+ function generateProjectEnvelope(directory) {
58929
+ const fs2 = require("fs");
58930
+ const path3 = require("path");
58931
+ const envelope = {
58932
+ configuration_files: [],
58933
+ documentation: [],
58934
+ build_tools: []
58935
+ };
58936
+ const configFiles = [
58937
+ "package.json",
58938
+ "tsconfig.json",
58939
+ "jsconfig.json",
58940
+ "webpack.config.js",
58941
+ "webpack.config.ts",
58942
+ "vite.config.js",
58943
+ "vite.config.ts",
58944
+ "rollup.config.js",
58945
+ "rollup.config.ts",
58946
+ "babel.config.js",
58947
+ "babel.config.ts",
58948
+ ".babelrc",
58949
+ ".babelrc.js",
58950
+ "eslint.config.mjs",
58951
+ "eslint.config.js",
58952
+ ".eslintrc",
58953
+ ".eslintrc.js",
58954
+ ".eslintrc.json",
58955
+ "jest.config.js",
58956
+ "jest.config.ts",
58957
+ "jest.config.json",
58958
+ "mocha.opts",
58959
+ "nodemon.json",
58960
+ "nodemon.js",
58961
+ "next.config.ts",
58962
+ "next.config.js",
58963
+ "nuxt.config.js",
58964
+ "nuxt.config.ts",
58965
+ "postcss.config.mjs",
58966
+ "postcss.config.js",
58967
+ "tailwind.config.js",
58968
+ "tailwind.config.ts",
58969
+ "supabase/config.toml",
58970
+ "pnpm-workspace.yaml",
58971
+ "yarn.lock",
58972
+ "package-lock.json",
58973
+ "tsconfig.base.json",
58974
+ "tsconfig.node.json",
58975
+ "tsconfig.app.json",
58976
+ "turbo.json",
58977
+ "biome.json",
58978
+ "biome.jsonc",
58979
+ "angular.json",
58980
+ "nest-cli.json",
58981
+ "remix.config.js",
58982
+ "remix.config.ts"
58983
+ ];
58984
+ const docFiles = [
58985
+ "README.md",
58986
+ "README.txt",
58987
+ "readme.md",
58988
+ "Readme.md",
58989
+ "CHANGELOG.md",
58990
+ "CHANGELOG.txt",
58991
+ "LICENSE",
58992
+ "LICENSE.md",
58993
+ "CONTRIBUTING.md",
58994
+ "NOTICE",
58995
+ "NOTICE.md",
58996
+ "SECURITY.md",
58997
+ "CODE_OF_CONDUCT.md",
58998
+ "PULL_REQUEST_TEMPLATE.md",
58999
+ "ISSUE_TEMPLATE.md",
59000
+ "HISTORY.md",
59001
+ "AUTHORS",
59002
+ "THANKS",
59003
+ "CITATION.cff",
59004
+ "CITATION.bib"
59005
+ ];
59006
+ const buildToolFiles = [
59007
+ "typedoc.config.cjs",
59008
+ "typedoc.json",
59009
+ "typedoc.config.js",
59010
+ "typedoc.config.ts",
59011
+ "documentation.config.js",
59012
+ "docs.config.js",
59013
+ "api-extractor.json",
59014
+ "api-documenter.json",
59015
+ "jsdoc.json",
59016
+ "jsdoc.config.json",
59017
+ "compodoc.json",
59018
+ "documentation.yml"
59019
+ ];
59020
+ function findFilesRecursively(dir, fileNames, results, maxDepth = 3, currentDepth = 0) {
59021
+ if (currentDepth > maxDepth)
59022
+ return;
59023
+ if (!fs2.existsSync(dir))
59024
+ return;
59025
+ const items = fs2.readdirSync(dir, { withFileTypes: true });
59026
+ for (const item of items) {
59027
+ const fullPath = path3.join(dir, item.name);
59028
+ if (item.isDirectory()) {
59029
+ if (!item.name.startsWith(".") && item.name !== "node_modules" && item.name !== "dist" && item.name !== "build") {
59030
+ findFilesRecursively(fullPath, fileNames, results, maxDepth, currentDepth + 1);
59031
+ }
59032
+ } else {
59033
+ if (fileNames.includes(item.name)) {
59034
+ const relativePath = path3.relative(directory, fullPath);
59035
+ results.push({
59036
+ path: relativePath,
59037
+ name: item.name
59038
+ });
59039
+ }
59040
+ }
59041
+ }
59042
+ }
59043
+ const foundConfigFiles = [];
59044
+ findFilesRecursively(directory, configFiles, foundConfigFiles);
59045
+ foundConfigFiles.forEach((fileObj) => {
59046
+ envelope.configuration_files.push({
59047
+ path: fileObj.path,
59048
+ role: getRoleForConfigFile(fileObj.name)
59049
+ });
59050
+ });
59051
+ const foundDocFiles = [];
59052
+ findFilesRecursively(directory, docFiles, foundDocFiles);
59053
+ foundDocFiles.forEach((fileObj) => {
59054
+ envelope.documentation.push({
59055
+ path: fileObj.path,
59056
+ role: getRoleForDocFile(fileObj.name)
59057
+ });
59058
+ });
59059
+ const foundBuildToolFiles = [];
59060
+ findFilesRecursively(directory, buildToolFiles, foundBuildToolFiles);
59061
+ foundBuildToolFiles.forEach((fileObj) => {
59062
+ envelope.build_tools.push({
59063
+ path: fileObj.path,
59064
+ role: getRoleForBuildToolFile(fileObj.name)
59065
+ });
59066
+ });
59067
+ envelope.configuration_files = envelope.configuration_files.filter(
59068
+ (file, index, self2) => index === self2.findIndex((f) => f.path === file.path)
59069
+ );
59070
+ envelope.documentation = envelope.documentation.filter(
59071
+ (file, index, self2) => index === self2.findIndex((f) => f.path === file.path)
59072
+ );
59073
+ envelope.build_tools = envelope.build_tools.filter(
59074
+ (file, index, self2) => index === self2.findIndex((f) => f.path === file.path)
59075
+ );
59076
+ return envelope;
59077
+ }
59078
+ function getRoleForConfigFile(fileName) {
59079
+ const roles = {
59080
+ "package.json": "dependency_manifest",
59081
+ "tsconfig.json": "compiler_configuration",
59082
+ "jsconfig.json": "compiler_configuration",
59083
+ "webpack.config.js": "bundler_configuration",
59084
+ "webpack.config.ts": "bundler_configuration",
59085
+ "vite.config.js": "bundler_configuration",
59086
+ "vite.config.ts": "bundler_configuration",
59087
+ "rollup.config.js": "bundler_configuration",
59088
+ "rollup.config.ts": "bundler_configuration",
59089
+ "babel.config.js": "transpiler_configuration",
59090
+ "babel.config.ts": "transpiler_configuration",
59091
+ ".babelrc": "transpiler_configuration",
59092
+ ".babelrc.js": "transpiler_configuration",
59093
+ "eslint.config.mjs": "linting_configuration",
59094
+ "eslint.config.js": "linting_configuration",
59095
+ ".eslintrc": "linting_configuration",
59096
+ ".eslintrc.js": "linting_configuration",
59097
+ ".eslintrc.json": "linting_configuration",
59098
+ "jest.config.js": "testing_configuration",
59099
+ "jest.config.ts": "testing_configuration",
59100
+ "jest.config.json": "testing_configuration",
59101
+ "mocha.opts": "testing_configuration",
59102
+ "nodemon.json": "development_configuration",
59103
+ "nodemon.js": "development_configuration",
59104
+ "next.config.ts": "framework_configuration",
59105
+ "next.config.js": "framework_configuration",
59106
+ "nuxt.config.js": "framework_configuration",
59107
+ "nuxt.config.ts": "framework_configuration",
59108
+ "postcss.config.mjs": "css_processing_configuration",
59109
+ "postcss.config.js": "css_processing_configuration",
59110
+ "tailwind.config.js": "css_framework_configuration",
59111
+ "tailwind.config.ts": "css_framework_configuration",
59112
+ "supabase/config.toml": "database_service_configuration",
59113
+ "pnpm-workspace.yaml": "package_manager_configuration",
59114
+ "yarn.lock": "dependency_lockfile",
59115
+ "package-lock.json": "dependency_lockfile",
59116
+ "tsconfig.base.json": "compiler_base_configuration",
59117
+ "tsconfig.node.json": "compiler_node_configuration",
59118
+ "tsconfig.app.json": "compiler_app_configuration",
59119
+ "turbo.json": "build_system_configuration",
59120
+ "biome.json": "formatter_linter_configuration",
59121
+ "biome.jsonc": "formatter_linter_configuration",
59122
+ "angular.json": "framework_configuration",
59123
+ "nest-cli.json": "framework_configuration",
59124
+ "remix.config.js": "framework_configuration",
59125
+ "remix.config.ts": "framework_configuration"
59126
+ };
59127
+ return roles[fileName] || "general_configuration";
59128
+ }
59129
+ function getRoleForDocFile(fileName) {
59130
+ const roles = {
59131
+ "README.md": "system_overview",
59132
+ "README.txt": "system_overview",
59133
+ "readme.md": "system_overview",
59134
+ "Readme.md": "system_overview",
59135
+ "CHANGELOG.md": "change_history",
59136
+ "CHANGELOG.txt": "change_history",
59137
+ "LICENSE": "license_terms",
59138
+ "LICENSE.md": "license_terms",
59139
+ "CONTRIBUTING.md": "contribution_guidelines",
59140
+ "NOTICE": "legal_notice",
59141
+ "NOTICE.md": "legal_notice",
59142
+ "SECURITY.md": "security_policy",
59143
+ "CODE_OF_CONDUCT.md": "code_of_conduct",
59144
+ "PULL_REQUEST_TEMPLATE.md": "pull_request_guidelines",
59145
+ "ISSUE_TEMPLATE.md": "issue_reporting_guidelines",
59146
+ "HISTORY.md": "project_history",
59147
+ "AUTHORS": "authorship_information",
59148
+ "THANKS": "acknowledgements",
59149
+ "CITATION.cff": "citation_information",
59150
+ "CITATION.bib": "citation_information"
59151
+ };
59152
+ return roles[fileName] || "documentation";
59153
+ }
59154
+ function getRoleForBuildToolFile(fileName) {
59155
+ const roles = {
59156
+ "typedoc.config.cjs": "documentation_generator",
59157
+ "typedoc.json": "documentation_generator",
59158
+ "typedoc.config.js": "documentation_generator",
59159
+ "typedoc.config.ts": "documentation_generator",
59160
+ "documentation.config.js": "documentation_generator",
59161
+ "docs.config.js": "documentation_generator",
59162
+ "api-extractor.json": "api_documentation_generator",
59163
+ "api-documenter.json": "api_documentation_generator",
59164
+ "jsdoc.json": "documentation_generator",
59165
+ "jsdoc.config.json": "documentation_generator",
59166
+ "compodoc.json": "angular_documentation_generator",
59167
+ "documentation.yml": "documentation_generator"
59168
+ };
59169
+ return roles[fileName] || "build_tool_configuration";
59170
+ }
59171
+ function generateAssumptions() {
59172
+ return [
59173
+ "Dynamic imports resolved statically where possible",
59174
+ "Runtime reflection not fully captured",
59175
+ "Only code reachable from entry points analyzed",
59176
+ "Third-party dependencies treated as black boxes",
59177
+ "Build-time code generation not reflected in static analysis",
59178
+ "ArcVision prioritizes execution-relevant structure over exhaustive file indexing. Non-runtime files are represented as envelope metadata unless they directly affect execution."
59179
+ ];
59180
+ }
59181
+ module2.exports = { buildContext, getGitInfo, calculateIntegrityHash, generateAssumptions, generateStructuralLayers, generateProjectEnvelope, generateAdaptiveStructuralLayers };
58639
59182
  }
58640
59183
  });
58641
59184
 
@@ -65222,7 +65765,9 @@ var require_scanner = __commonJS({
65222
65765
  const contextOptions = {
65223
65766
  directory,
65224
65767
  projectName: path2.basename(directory),
65225
- contextSurface
65768
+ contextSurface,
65769
+ arcvisionVersion: process.env.npm_package_version || "0.1.0"
65770
+ // Pass version from package.json
65226
65771
  };
65227
65772
  let context = buildContext(nodes, edges, symbols, contextOptions);
65228
65773
  console.log(" Validating structural context...");
@@ -65244,6 +65789,199 @@ var require_scanner = __commonJS({
65244
65789
  }
65245
65790
  });
65246
65791
 
65792
+ // src/core/diff-analyzer.js
65793
+ var require_diff_analyzer = __commonJS({
65794
+ "src/core/diff-analyzer.js"(exports2, module2) {
65795
+ function generateDiffSummary2(oldContext, newContext) {
65796
+ const diffSummary = {
65797
+ nodes_added: 0,
65798
+ nodes_removed: 0,
65799
+ edges_added: 0,
65800
+ edges_removed: 0,
65801
+ roles_changed: 0,
65802
+ blast_radius_changes: 0
65803
+ };
65804
+ const oldNodeMap = new Map(oldContext.nodes.map((node) => [node.path, node]));
65805
+ const newNodeMap = new Map(newContext.nodes.map((node) => [node.path, node]));
65806
+ for (const [path2, node] of newNodeMap) {
65807
+ if (!oldNodeMap.has(path2)) {
65808
+ diffSummary.nodes_added++;
65809
+ }
65810
+ }
65811
+ for (const [path2, node] of oldNodeMap) {
65812
+ if (!newNodeMap.has(path2)) {
65813
+ diffSummary.nodes_removed++;
65814
+ }
65815
+ }
65816
+ const oldEdgeMap = new Map(
65817
+ oldContext.edges.map((edge) => [`${edge.from}::${edge.to}::${edge.relation}`, edge])
65818
+ );
65819
+ const newEdgeMap = new Map(
65820
+ newContext.edges.map((edge) => [`${edge.from}::${edge.to}::${edge.relation}`, edge])
65821
+ );
65822
+ for (const [key, edge] of newEdgeMap) {
65823
+ if (!oldEdgeMap.has(key)) {
65824
+ diffSummary.edges_added++;
65825
+ }
65826
+ }
65827
+ for (const [key, edge] of oldEdgeMap) {
65828
+ if (!newEdgeMap.has(key)) {
65829
+ diffSummary.edges_removed++;
65830
+ }
65831
+ }
65832
+ for (const newNode of newContext.nodes) {
65833
+ const oldNode = oldNodeMap.get(newNode.path);
65834
+ if (oldNode && oldNode.role !== newNode.role) {
65835
+ diffSummary.roles_changed++;
65836
+ }
65837
+ }
65838
+ for (const newNode of newContext.nodes) {
65839
+ const oldNode = oldNodeMap.get(newNode.path);
65840
+ if (oldNode && oldNode.blast_radius !== newNode.blast_radius) {
65841
+ diffSummary.blast_radius_changes++;
65842
+ }
65843
+ }
65844
+ return {
65845
+ diff_summary: diffSummary
65846
+ };
65847
+ }
65848
+ module2.exports = { generateDiffSummary: generateDiffSummary2 };
65849
+ }
65850
+ });
65851
+
65852
+ // src/core/readme-generator.js
65853
+ var require_readme_generator = __commonJS({
65854
+ "src/core/readme-generator.js"(exports2, module2) {
65855
+ var fs2 = require("fs");
65856
+ var path2 = require("path");
65857
+ var { spawnSync } = require("child_process");
65858
+ function getCommitHash() {
65859
+ try {
65860
+ const result = spawnSync("git", ["rev-parse", "HEAD"], {
65861
+ stdio: ["pipe", "pipe", "pipe"]
65862
+ });
65863
+ if (result.status === 0) {
65864
+ return result.stdout.toString().trim();
65865
+ }
65866
+ return "unknown";
65867
+ } catch (error) {
65868
+ return "unknown";
65869
+ }
65870
+ }
65871
+ function generateReadmeContent(commitHash, timestamp, toolVersion, blastRadiusData = null) {
65872
+ let content = `# ArcVision System Context Artifact
65873
+
65874
+
65875
+ ## What This Is [arcvision.context.json](./arcvision.context.json)
65876
+
65877
+ This file is the **canonical structural context** of this codebase.
65878
+ It represents how the system actually works \u2014 not how it is described.
65879
+
65880
+ It is generated directly from source code by ArcVision.
65881
+
65882
+ ## What This Replaces
65883
+
65884
+ This artifact replaces:
65885
+ - Manual repository scanning
65886
+ - Tribal knowledge held by senior engineers
65887
+ - Re-explaining the system to new developers
65888
+ - Re-prompting LLMs with partial or incorrect context
65889
+ - Guessing blast radius of changes
65890
+
65891
+ ## When You Must Use This
65892
+
65893
+ Use this artifact when:
65894
+ - Onboarding a new developer
65895
+ - Prompting an AI assistant about this codebase
65896
+ - Making architectural changes
65897
+ - Investigating unexpected behavior
65898
+ - Assessing risk before modifying core modules
65899
+
65900
+ ## What This Artifact Contains
65901
+
65902
+ - Canonical module and dependency graph
65903
+ - Execution and data flow relationships
65904
+ - Structural roles (service, store, boundary, etc.)
65905
+ - Invariants inferred from the system
65906
+ - Impact metrics (blast radius, coupling)
65907
+
65908
+ ## Determinism & Trust
65909
+
65910
+ - Generated from commit: ${commitHash}
65911
+ - Generation timestamp: ${timestamp}
65912
+ - Tool version: ${toolVersion}
65913
+ - Deterministic: same input \u2192 same output
65914
+ - Explicit assumptions listed inside the artifact
65915
+
65916
+ If this artifact conflicts with human memory, **trust the artifact**.`;
65917
+ if (blastRadiusData && blastRadiusData.topFiles && blastRadiusData.topFiles.length > 0) {
65918
+ content += `
65919
+
65920
+ ## Structural Context Hubs
65921
+
65922
+ The following files have the highest blast radius and represent critical structural hubs in the system:
65923
+
65924
+ `;
65925
+ blastRadiusData.topFiles.forEach((item, index) => {
65926
+ let warningMessage = "";
65927
+ if (index === 0) {
65928
+ warningMessage = "Changes here may silently propagate across the system.";
65929
+ } else if (index === 1) {
65930
+ warningMessage = "Acts as a coordination layer between components.";
65931
+ } else {
65932
+ warningMessage = "Modifications can cause widespread inconsistencies.";
65933
+ }
65934
+ content += `- **${item.file}**
65935
+ - Blast Radius: ${item.blastRadius} files (${item.percentOfGraph}% of codebase)
65936
+ - Risk: ${warningMessage}
65937
+
65938
+ `;
65939
+ });
65940
+ } else {
65941
+ content += `
65942
+
65943
+ ## Structural Context Hubs
65944
+
65945
+ No high-structure files detected based on import dependencies.`;
65946
+ }
65947
+ content += `
65948
+
65949
+ ## How to Use With AI
65950
+
65951
+ When prompting AI tools, include this file as system context.
65952
+ Do not ask the AI to infer architecture without it.
65953
+
65954
+ ## When to Regenerate
65955
+
65956
+ Regenerate this artifact when:
65957
+ - Core modules change
65958
+ - New services are added
65959
+ - Dependency structure shifts
65960
+
65961
+ Run:
65962
+
65963
+ \`\`\`
65964
+ arcvision scan --upload
65965
+ \`\`\`
65966
+
65967
+ ## Source of Truth
65968
+
65969
+ This artifact is the **source of truth** for system structure.
65970
+ All explanations, decisions, and AI reasoning should reference it.`;
65971
+ return content;
65972
+ }
65973
+ function generateReadme(outputDir, toolVersion, blastRadiusData = null) {
65974
+ const commitHash = getCommitHash();
65975
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
65976
+ const readmeContent = generateReadmeContent(commitHash, timestamp, toolVersion, blastRadiusData);
65977
+ const readmePath = path2.join(outputDir, "README.md");
65978
+ fs2.writeFileSync(readmePath, readmeContent);
65979
+ console.log(`\u2705 System context README generated at ${readmePath}`);
65980
+ }
65981
+ module2.exports = { generateReadme, generateReadmeContent, getCommitHash };
65982
+ }
65983
+ });
65984
+
65247
65985
  // src/index.js
65248
65986
  var { Command } = require_commander();
65249
65987
  var chalk = require_source();
@@ -65251,6 +65989,7 @@ var path = require("path");
65251
65989
  var fs = require("fs");
65252
65990
  var os = require("os");
65253
65991
  var scanner = require_scanner();
65992
+ var { generateDiffSummary } = require_diff_analyzer();
65254
65993
  var version = "1.0.0";
65255
65994
  try {
65256
65995
  const packageJsonPath = path.join(__dirname, "../package.json");
@@ -65267,7 +66006,9 @@ function analyzeBlastRadius(architectureMap) {
65267
66006
  if (architectureMap.nodes && architectureMap.nodes.length > 0) {
65268
66007
  architectureMap.nodes.forEach((node) => {
65269
66008
  let blastRadius = 0;
65270
- if (node.metadata && node.metadata.blast_radius !== void 0) {
66009
+ if (node.blast_radius !== void 0) {
66010
+ blastRadius = node.blast_radius;
66011
+ } else if (node.metadata && node.metadata.blast_radius !== void 0) {
65271
66012
  blastRadius = node.metadata.blast_radius;
65272
66013
  } else if (node.signals && node.signals.blast_radius !== void 0) {
65273
66014
  blastRadius = node.signals.blast_radius;
@@ -65463,11 +66204,15 @@ program.command("scan").description("Scan the current directory and generate arc
65463
66204
  }
65464
66205
  if (options.upload) {
65465
66206
  await uploadToDatabase(map);
66207
+ const { generateReadme } = require_readme_generator();
66208
+ generateReadme(".", version, blastRadiusAnalysis);
65466
66209
  } else {
65467
66210
  const fs2 = require("fs");
65468
66211
  const outputFileName = "arcvision.context.json";
65469
66212
  fs2.writeFileSync(outputFileName, JSON.stringify(map, null, 2));
65470
66213
  console.log(chalk.green(`\u2705 Structural context saved to ${outputFileName}`));
66214
+ const { generateReadme } = require_readme_generator();
66215
+ generateReadme(".", version, blastRadiusAnalysis);
65471
66216
  console.log(chalk.dim("\nUse --upload to send to dashboard."));
65472
66217
  }
65473
66218
  } catch (error) {
@@ -65492,4 +66237,29 @@ program.command("scan").description("Scan the current directory and generate arc
65492
66237
  }
65493
66238
  }
65494
66239
  });
66240
+ 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) => {
66241
+ try {
66242
+ console.log(chalk.blue(`Comparing context artifacts:`));
66243
+ console.log(chalk.blue(` Old: ${oldFile}`));
66244
+ console.log(chalk.blue(` New: ${newFile}`));
66245
+ const oldContext = JSON.parse(fs.readFileSync(oldFile, "utf8"));
66246
+ const newContext = JSON.parse(fs.readFileSync(newFile, "utf8"));
66247
+ const diffResult = generateDiffSummary(oldContext, newContext);
66248
+ newContext.diff_summary = diffResult.diff_summary;
66249
+ const outputFileName = "arcvision.context.diff.json";
66250
+ fs.writeFileSync(outputFileName, JSON.stringify(newContext, null, 2));
66251
+ console.log(chalk.green("\u2705 Structural diff completed!"));
66252
+ console.log(chalk.green(`\u2705 Diff summary saved to ${outputFileName}`));
66253
+ console.log(chalk.yellow("\nDiff Summary:"));
66254
+ console.log(chalk.yellow(` Nodes Added: ${diffResult.diff_summary.nodes_added}`));
66255
+ console.log(chalk.yellow(` Nodes Removed: ${diffResult.diff_summary.nodes_removed}`));
66256
+ console.log(chalk.yellow(` Edges Added: ${diffResult.diff_summary.edges_added}`));
66257
+ console.log(chalk.yellow(` Edges Removed: ${diffResult.diff_summary.edges_removed}`));
66258
+ console.log(chalk.yellow(` Roles Changed: ${diffResult.diff_summary.roles_changed}`));
66259
+ console.log(chalk.yellow(` Blast Radius Changes: ${diffResult.diff_summary.blast_radius_changes}`));
66260
+ } catch (error) {
66261
+ console.error(chalk.red("\u274C Diff failed:"), error.message);
66262
+ process.exit(1);
66263
+ }
66264
+ });
65495
66265
  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.7",
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
- }