arcvision 0.2.26 → 0.2.28

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcvision",
3
- "version": "0.2.26",
3
+ "version": "0.2.28",
4
4
  "description": "ArcVision CLI - Architectural Governance and Invariant Detection Tool",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -33,9 +33,22 @@ class ArtifactManager {
33
33
  createDirectories() {
34
34
  this.requiredDirs.forEach(dir => {
35
35
  const fullPath = path.join(this.projectRoot, dir);
36
- if (!fs.existsSync(fullPath)) {
37
- fs.mkdirSync(fullPath, { recursive: true });
38
- this.logCreation(dir);
36
+ try {
37
+ if (!fs.existsSync(fullPath)) {
38
+ fs.mkdirSync(fullPath, { recursive: true });
39
+ this.logCreation(dir);
40
+ // Verify directory was created
41
+ if (fs.existsSync(fullPath)) {
42
+ console.log(`✅ Verified directory creation: ${fullPath}`);
43
+ } else {
44
+ console.warn(`⚠️ Directory may not have been created: ${fullPath}`);
45
+ }
46
+ } else {
47
+ console.log(`✅ Directory already exists: ${fullPath}`);
48
+ }
49
+ } catch (error) {
50
+ console.error(`❌ Failed to create directory ${fullPath}: ${error.message}`);
51
+ throw error;
39
52
  }
40
53
  });
41
54
  }
@@ -46,16 +59,32 @@ class ArtifactManager {
46
59
  createMissingFiles() {
47
60
  // Create default ledger if missing
48
61
  const ledgerPath = path.join(this.projectRoot, this.requiredFiles.ledger);
49
- if (!fs.existsSync(ledgerPath)) {
50
- const defaultLedger = {
51
- schema_version: '1.0',
52
- system_id: path.basename(this.projectRoot),
53
- created_at: new Date().toISOString(),
54
- ledger: []
55
- };
56
-
57
- fs.writeFileSync(ledgerPath, JSON.stringify(defaultLedger, null, 2));
58
- this.logCreation(this.requiredFiles.ledger);
62
+ try {
63
+ if (!fs.existsSync(ledgerPath)) {
64
+ console.log(`🔍 Creating ledger file: ${ledgerPath}`);
65
+ const defaultLedger = {
66
+ schema_version: '1.0',
67
+ system_id: path.basename(this.projectRoot),
68
+ created_at: new Date().toISOString(),
69
+ ledger: []
70
+ };
71
+
72
+ fs.writeFileSync(ledgerPath, JSON.stringify(defaultLedger, null, 2));
73
+ this.logCreation(this.requiredFiles.ledger);
74
+
75
+ // Verify file was created
76
+ if (fs.existsSync(ledgerPath)) {
77
+ const stats = fs.statSync(ledgerPath);
78
+ console.log(`✅ Verified ledger file creation: ${ledgerPath} (${stats.size} bytes)`);
79
+ } else {
80
+ console.warn(`⚠️ Ledger file may not have been created: ${ledgerPath}`);
81
+ }
82
+ } else {
83
+ console.log(`✅ Ledger file already exists: ${ledgerPath}`);
84
+ }
85
+ } catch (error) {
86
+ console.error(`❌ Failed to create ledger file ${ledgerPath}: ${error.message}`);
87
+ throw error;
59
88
  }
60
89
  }
61
90
 
@@ -68,21 +68,35 @@ class FailureModeSynthesizer {
68
68
  }
69
69
 
70
70
  /**
71
- * Get default symptom for an invariant
71
+ * Get default symptom for an invariant based on its statement
72
72
  */
73
73
  getDefaultSymptom(invariant) {
74
- if (invariant.statement.toLowerCase().includes('availability')) {
75
- return 'UI does not update';
76
- } else if (invariant.statement.toLowerCase().includes('state')) {
77
- return 'Stale data presented to user';
78
- } else if (invariant.statement.toLowerCase().includes('sync') ||
79
- invariant.statement.toLowerCase().includes('synchronize')) {
74
+ if (!invariant) {
75
+ return 'System behavior deviates from expected requirements';
76
+ }
77
+
78
+ // Handle cases where invariant might have description but no statement
79
+ const statementText = invariant.statement || invariant.description || invariant.id || '';
80
+ if (!statementText) {
81
+ return 'System behavior deviates from expected requirements';
82
+ }
83
+
84
+ // Ensure we have a string before calling toLowerCase
85
+ const statementStr = String(statementText);
86
+ const statement = statementStr.toLowerCase();
87
+
88
+ if (statement.includes('availability')) {
89
+ return 'UI does not update or service unreachable';
90
+ } else if (statement.includes('state')) {
91
+ return 'Stale or inconsistent data presented to user';
92
+ } else if (statement.includes('sync') || statement.includes('synchronize')) {
80
93
  return 'Data inconsistency between components';
81
- } else if (invariant.statement.toLowerCase().includes('permission') ||
82
- invariant.statement.toLowerCase().includes('auth')) {
94
+ } else if (statement.includes('permission') || statement.includes('auth')) {
83
95
  return 'Unauthorized access or denied permissions';
96
+ } else if (statement.includes('performance') || statement.includes('latency')) {
97
+ return 'System response time exceeds threshold';
84
98
  } else {
85
- return 'Unexpected behavior or incorrect output';
99
+ return 'Unexpected behavior or incorrect logical output';
86
100
  }
87
101
  }
88
102
 
@@ -90,17 +104,34 @@ class FailureModeSynthesizer {
90
104
  * Get blast radius for an invariant based on related nodes
91
105
  */
92
106
  getInvariantBlastRadius(invariant, architectureMap) {
107
+ if (!invariant) {
108
+ return ['unknown'];
109
+ }
110
+
111
+ // Handle cases where invariant might have description but no statement
112
+ const statementText = invariant.statement || invariant.description || invariant.id || '';
113
+ if (!statementText) {
114
+ return ['unknown'];
115
+ }
116
+
93
117
  const { nodes } = architectureMap;
94
118
  const blastRadius = [];
119
+ // Ensure we have a string before calling toLowerCase
120
+ const statementStr = String(statementText);
121
+ const statement = statementStr.toLowerCase();
95
122
 
96
123
  // Look for nodes that are related to the invariant
97
124
  for (const node of nodes) {
98
- if (node.path && invariant.statement.toLowerCase().includes(node.path.toLowerCase())) {
99
- blastRadius.push(node.path);
100
- }
101
-
102
- if (node.id && invariant.statement.toLowerCase().includes(node.id.toLowerCase())) {
103
- blastRadius.push(node.id);
125
+ try {
126
+ if (node.path && statement.includes(node.path.toLowerCase())) {
127
+ blastRadius.push(node.path);
128
+ }
129
+
130
+ if (node.id && statement.includes(node.id.toLowerCase())) {
131
+ blastRadius.push(node.id);
132
+ }
133
+ } catch (e) {
134
+ // Ignore errors
104
135
  }
105
136
  }
106
137
 
@@ -229,16 +260,19 @@ class FailureModeSynthesizer {
229
260
  */
230
261
  isRelevantToInvariant(source, invariant) {
231
262
  // Check if the source location is mentioned in the invariant
232
- if (invariant.owner && source.location &&
233
- invariant.owner.toLowerCase().includes(source.location.toLowerCase())) {
263
+ if (invariant.owner && source.location &&
264
+ invariant.owner.toLowerCase().includes(source.location.toLowerCase())) {
234
265
  return true;
235
266
  }
236
267
 
237
- // Check if the invariant statement mentions similar concepts
238
- if (invariant.statement && source.location) {
239
- const invariantWords = invariant.statement.toLowerCase().split(/\W+/);
268
+ // Check if the invariant statement/description mentions similar concepts
269
+ const statementText = invariant.statement || invariant.description || invariant.id || '';
270
+ if (statementText && source.location) {
271
+ // Ensure we have a string before calling toLowerCase
272
+ const statementStr = String(statementText);
273
+ const invariantWords = statementStr.toLowerCase().split(/\W+/);
240
274
  const locationWords = source.location.toLowerCase().split(/[\/\\.-_]+/);
241
-
275
+
242
276
  // Check for overlapping terms
243
277
  for (const word of invariantWords) {
244
278
  if (word.length > 3 && locationWords.includes(word)) {
@@ -254,16 +288,25 @@ class FailureModeSynthesizer {
254
288
  * Predict symptoms based on invariant and trigger
255
289
  */
256
290
  predictSymptom(invariant, trigger) {
291
+ // Handle cases where invariant might have description but no statement
292
+ const statementText = invariant.statement || invariant.description || invariant.id || '';
293
+ if (!statementText) {
294
+ return 'Unexpected behavior or incorrect output';
295
+ }
296
+
297
+ // Ensure we have a string before calling toLowerCase
298
+ const statementStr = String(statementText);
299
+
257
300
  // Default symptom prediction based on invariant type
258
- if (invariant.statement.toLowerCase().includes('availability')) {
301
+ if (statementStr.toLowerCase().includes('availability')) {
259
302
  return 'UI does not update';
260
- } else if (invariant.statement.toLowerCase().includes('state')) {
303
+ } else if (statementStr.toLowerCase().includes('state')) {
261
304
  return 'Stale data presented to user';
262
- } else if (invariant.statement.toLowerCase().includes('sync') ||
263
- invariant.statement.toLowerCase().includes('synchronize')) {
305
+ } else if (statementStr.toLowerCase().includes('sync') ||
306
+ statementStr.toLowerCase().includes('synchronize')) {
264
307
  return 'Data inconsistency between components';
265
- } else if (invariant.statement.toLowerCase().includes('permission') ||
266
- invariant.statement.toLowerCase().includes('auth')) {
308
+ } else if (statementStr.toLowerCase().includes('permission') ||
309
+ statementStr.toLowerCase().includes('auth')) {
267
310
  return 'Unauthorized access or denied permissions';
268
311
  } else {
269
312
  return 'Unexpected behavior or incorrect output';
@@ -325,16 +368,16 @@ class FailureModeSynthesizer {
325
368
  */
326
369
  containsMutationPattern(func) {
327
370
  if (!func.name) return false;
328
-
371
+
329
372
  const name = func.name.toLowerCase();
330
- return name.includes('set') ||
331
- name.includes('update') ||
332
- name.includes('modify') ||
333
- name.includes('change') ||
334
- name.includes('mutate') ||
335
- name.includes('assign') ||
336
- name.includes('put') ||
337
- name.includes('post');
373
+ return name.includes('set') ||
374
+ name.includes('update') ||
375
+ name.includes('modify') ||
376
+ name.includes('change') ||
377
+ name.includes('mutate') ||
378
+ name.includes('assign') ||
379
+ name.includes('put') ||
380
+ name.includes('post');
338
381
  }
339
382
  }
340
383
 
@@ -23,18 +23,45 @@ class InvariantDetector {
23
23
  this.detectedInvariants = [];
24
24
 
25
25
  try {
26
+ // Validate inputs
27
+ if (!scanResult || !scanResult.nodes || !scanResult.edges) {
28
+ console.warn('⚠️ Invalid scan result provided to invariant detector');
29
+ return [];
30
+ }
31
+
32
+ console.log('\n🔍 Starting Invariant Detection Analysis');
33
+ console.log(` Analyzing ${scanResult.nodes.length} nodes and ${scanResult.edges.length} relationships`);
34
+
26
35
  // Analyze the structural context for potential invariants
27
36
  await this.analyzeNodesForInvariants(scanResult.nodes, directory);
28
37
  await this.analyzeEdgesForInvariants(scanResult.edges);
29
38
  await this.analyzeCodeFilesForInvariants(directory);
30
39
  await this.analyzeStructuralPatterns(scanResult.nodes, scanResult.edges, directory);
31
40
 
41
+ // Filter out any invariants without proper statements
42
+ this.detectedInvariants = this.detectedInvariants.filter(inv => {
43
+ if (!inv.statement || inv.statement.trim() === '') {
44
+ console.warn(`⚠️ Removing invariant ${inv.id} due to missing statement`);
45
+ return false;
46
+ }
47
+ return true;
48
+ });
49
+
32
50
  console.log(`\n🛡️ Invariant Detection Complete`);
33
51
  console.log(` Found ${this.detectedInvariants.length} potential system invariants`);
52
+
53
+ // Log summary of invariant types
54
+ const typeCounts = {};
55
+ this.detectedInvariants.forEach(inv => {
56
+ const type = inv.system || 'unknown';
57
+ typeCounts[type] = (typeCounts[type] || 0) + 1;
58
+ });
59
+ console.log(` Types: ${JSON.stringify(typeCounts)}`);
34
60
 
35
61
  return this.detectedInvariants;
36
62
  } catch (error) {
37
63
  console.error('Invariant detection failed:', error.message);
64
+ console.error('Stack trace:', error.stack);
38
65
  return [];
39
66
  }
40
67
  }
@@ -63,7 +90,7 @@ class InvariantDetector {
63
90
  this.detectedInvariants.push({
64
91
  id: invariantId,
65
92
  system: 'automatic',
66
- statement: invariant.description,
93
+ statement: invariant.description || `Invariant detected in ${nodeId}`, // Ensure statement exists
67
94
  description: invariant.description,
68
95
  scope: { files: [nodeId] },
69
96
  critical_path: invariant.isCritical,
@@ -101,7 +128,7 @@ class InvariantDetector {
101
128
  this.detectedInvariants.push({
102
129
  id: this.generateInvariantId('dependency_constraint', constraint.from + '_' + constraint.to),
103
130
  system: 'automatic',
104
- statement: constraint.description,
131
+ statement: constraint.description || `Dependency constraint from ${constraint.from} to ${constraint.to}`, // Ensure statement exists
105
132
  description: constraint.description,
106
133
  scope: { files: [constraint.from, constraint.to] },
107
134
  critical_path: constraint.critical,
@@ -145,7 +172,7 @@ class InvariantDetector {
145
172
  this.detectedInvariants.push({
146
173
  id: this.generateInvariantId(pattern.type, relativePath, i),
147
174
  system: 'automatic',
148
- statement: `System invariant implemented in ${relativePath}: ${pattern.description}`,
175
+ statement: `System invariant implemented in ${relativePath}: ${pattern.description}` || `Pattern-based invariant in ${relativePath}`, // Ensure statement exists
149
176
  description: `System invariant implemented in ${relativePath}: ${pattern.description}`,
150
177
  scope: { files: [relativePath] },
151
178
  critical_path: true, // File specifically for invariants is likely critical
@@ -191,10 +218,13 @@ class InvariantDetector {
191
218
  for (const pattern of validationPatterns) {
192
219
  const matches = content.match(pattern);
193
220
  if (matches) {
221
+ const matchCount = matches.length;
222
+ const patternName = pattern.source.replace(/\\/g, '').replace(/function\s+/, '');
194
223
  invariants.push({
195
224
  pattern: pattern.source,
196
- description: `Validation function pattern found in ${filePath}: ${matches.length} matches`,
197
- isCritical: true
225
+ description: `Validation function pattern found in ${filePath}: ${matchCount} matches`,
226
+ isCritical: true,
227
+ position: content.indexOf(matches[0])
198
228
  });
199
229
  }
200
230
  }
@@ -214,7 +244,8 @@ class InvariantDetector {
214
244
  invariants.push({
215
245
  pattern: pattern.source,
216
246
  description: `Constraint enforcement pattern found in ${filePath}`,
217
- isCritical: false
247
+ isCritical: false,
248
+ position: match.index
218
249
  });
219
250
  }
220
251
  }
@@ -236,7 +267,8 @@ class InvariantDetector {
236
267
  invariants.push({
237
268
  pattern: pattern.source,
238
269
  description: `State transition constraint pattern found in ${filePath}`,
239
- isCritical: true
270
+ isCritical: true,
271
+ position: match.index
240
272
  });
241
273
  }
242
274
  }
@@ -259,7 +291,8 @@ class InvariantDetector {
259
291
  invariants.push({
260
292
  pattern: pattern.source,
261
293
  description: `Boundary enforcement pattern found in ${filePath}`,
262
- isCritical: true
294
+ isCritical: true,
295
+ position: match.index
263
296
  });
264
297
  }
265
298
  }
@@ -676,7 +709,7 @@ class InvariantDetector {
676
709
  this.detectedInvariants.push({
677
710
  id: this.generateInvariantId(`layer_${fromLayer}_not_depend_${toLayer}`, `${fromLayer}_${toLayer}`),
678
711
  system: 'architectural',
679
- statement: `Layer dependency rule: ${fromLayer} components should not depend on ${toLayer} components`,
712
+ statement: `Layer dependency rule: ${fromLayer} components should not depend on ${toLayer} components` || `Architectural layer constraint: ${fromLayer} -> ${toLayer}`, // Ensure statement exists
680
713
  description: `Prevent ${fromLayer} layer from depending on ${toLayer} layer to maintain separation of concerns`,
681
714
  scope: { files: dependencies.map(d => d.from) },
682
715
  critical_path: true,
@@ -729,7 +762,7 @@ class InvariantDetector {
729
762
  this.detectedInvariants.push({
730
763
  id: this.generateInvariantId(`hub_protection_${nodeId}`, 'high_degree'),
731
764
  system: 'architectural',
732
- statement: `Protect high-degree node ${nodeId} from unauthorized modifications`,
765
+ statement: `Protect high-degree node ${nodeId} from unauthorized modifications` || `Hub node protection: ${nodeId}`, // Ensure statement exists
733
766
  description: `Node ${nodeId} has ${degrees.incoming} incoming dependencies, changes here affect many components`,
734
767
  scope: { files: [nodeId] },
735
768
  critical_path: true,
@@ -781,7 +814,7 @@ class InvariantDetector {
781
814
  this.detectedInvariants.push({
782
815
  id: this.generateInvariantId(`tight_coupling_${nodeA}_${nodeB}`, 'bidirectional'),
783
816
  system: 'architectural',
784
- statement: `Prevent tight coupling between ${nodeA} and ${nodeB}`,
817
+ statement: `Prevent tight coupling between ${nodeA} and ${nodeB}` || `Coupling constraint: ${nodeA} and ${nodeB}`, // Ensure statement exists
785
818
  description: `Bidirectional dependency detected between ${nodeA} and ${nodeB}, indicating tight coupling`,
786
819
  scope: { files: [nodeA, nodeB] },
787
820
  critical_path: false,
@@ -181,24 +181,69 @@ async function scan(directory) {
181
181
  existingIds.add(existingInv.id);
182
182
  }
183
183
  }
184
+
185
+ // Ensure all invariants have statement fields as fallback
186
+ console.log(`🔍 Ensuring all ${allInvariants.length} invariants have statement fields...`);
187
+ for (let i = 0; i < allInvariants.length; i++) {
188
+ const inv = allInvariants[i];
189
+ if (!inv.statement) {
190
+ // Create statement from available fields
191
+ if (inv.description) {
192
+ inv.statement = inv.description;
193
+ } else if (inv.id) {
194
+ inv.statement = `Invariant ${inv.id}`;
195
+ } else {
196
+ inv.statement = 'Unnamed architectural invariant';
197
+ }
198
+ console.log(`🔧 Added missing statement to invariant ${i + 1}/${allInvariants.length}: ${inv.id || 'unnamed'}`);
199
+ }
200
+ }
201
+ console.log('✅ All invariants now have statement fields');
184
202
 
185
203
  // Initialize components for new features
186
204
  const ownershipResolver = new OwnershipResolver();
187
205
  const failureModeSynthesizer = new FailureModeSynthesizer();
188
206
 
189
207
  // Resolve ownership
190
- const ownership = ownershipResolver.resolveOwnership(
191
- { nodes, edges }
192
- );
208
+ let ownership = {};
209
+ try {
210
+ console.log('🔍 Resolving ownership...');
211
+ ownership = ownershipResolver.resolveOwnership(
212
+ { nodes, edges }
213
+ );
214
+ console.log('✅ Ownership resolved');
215
+ } catch (ownershipError) {
216
+ console.warn(`⚠️ Failed to resolve ownership: ${ownershipError.message}`);
217
+ console.warn('Continuing with empty ownership data...');
218
+ ownership = {};
219
+ }
193
220
 
194
221
  // Synthesize failure modes
195
- const failure_modes = failureModeSynthesizer.synthesizeFailureModes(
196
- { nodes, edges },
197
- allInvariants
198
- );
222
+ let failure_modes = [];
223
+ try {
224
+ console.log('🔍 Synthesizing failure modes for ${allInvariants.length} invariants...');
225
+ failure_modes = failureModeSynthesizer.synthesizeFailureModes(
226
+ { nodes, edges },
227
+ allInvariants
228
+ );
229
+ console.log(`✅ Generated ${failure_modes.length} failure modes`);
230
+ } catch (error) {
231
+ console.warn(`⚠️ Failed to synthesize failure modes: ${error.message}`);
232
+ console.warn('Continuing with empty failure modes...');
233
+ failure_modes = [];
234
+ }
199
235
 
200
236
  // Generate decision guidance
201
- const decision_guidance = generateDecisionGuidance(allInvariants, failure_modes);
237
+ let decision_guidance = {};
238
+ try {
239
+ console.log('🔍 Generating decision guidance...');
240
+ decision_guidance = generateDecisionGuidance(allInvariants, failure_modes);
241
+ console.log('✅ Decision guidance generated');
242
+ } catch (error) {
243
+ console.warn(`⚠️ Failed to generate decision guidance: ${error.message}`);
244
+ console.warn('Continuing with empty decision guidance...');
245
+ decision_guidance = {};
246
+ }
202
247
 
203
248
  console.log(`📊 Total invariants to "Set" in context: ${allInvariants.length}`);
204
249
 
@@ -209,12 +254,18 @@ async function scan(directory) {
209
254
  if (allInvariants && allInvariants.length > 0) {
210
255
  // Analyze all invariants (detected + existing)
211
256
  const invariantAnalyzer = new InvariantAnalyzer();
212
- invariantAnalysis = invariantAnalyzer.analyzeInvariants(allInvariants, {
213
- directory,
214
- projectName: path.basename(directory),
215
- nodeCount: nodes.length,
216
- edgeCount: edges.length
217
- });
257
+ try {
258
+ invariantAnalysis = invariantAnalyzer.analyzeInvariants(allInvariants, {
259
+ directory,
260
+ projectName: path.basename(directory),
261
+ nodeCount: nodes.length,
262
+ edgeCount: edges.length
263
+ });
264
+ } catch (analysisError) {
265
+ console.warn(`⚠️ Failed to analyze invariants: ${analysisError.message}`);
266
+ console.warn('Continuing with empty invariant analysis...');
267
+ invariantAnalysis = null;
268
+ }
218
269
 
219
270
  // Assess architectural health based on all invariants
220
271
  try {
@@ -234,17 +285,31 @@ async function scan(directory) {
234
285
  } else {
235
286
  // Even if no invariants are detected, create a baseline analysis
236
287
  const invariantAnalyzer = new InvariantAnalyzer();
237
- invariantAnalysis = invariantAnalyzer.analyzeInvariants([], {
238
- directory,
239
- projectName: path.basename(directory),
240
- nodeCount: nodes.length,
241
- edgeCount: edges.length
242
- });
288
+ try {
289
+ invariantAnalysis = invariantAnalyzer.analyzeInvariants([], {
290
+ directory,
291
+ projectName: path.basename(directory),
292
+ nodeCount: nodes.length,
293
+ edgeCount: edges.length
294
+ });
295
+ } catch (baselineError) {
296
+ console.warn(`⚠️ Failed to create baseline invariant analysis: ${baselineError.message}`);
297
+ invariantAnalysis = null;
298
+ }
243
299
 
244
- architecturalHealth = invariantAnalyzer.assessArchitecturalHealth([], {
245
- directory,
246
- projectName: path.basename(directory)
247
- });
300
+ try {
301
+ architecturalHealth = invariantAnalyzer.assessArchitecturalHealth([], {
302
+ directory,
303
+ projectName: path.basename(directory)
304
+ });
305
+ } catch (baselineHealthError) {
306
+ console.warn(`⚠️ Failed to assess baseline architectural health: ${baselineHealthError.message}`);
307
+ architecturalHealth = {
308
+ score: 0,
309
+ level: 'unknown',
310
+ assessment: 'Could not assess architectural health due to analysis error'
311
+ };
312
+ }
248
313
  }
249
314
 
250
315
  // --- FINAL BUILD & VALIDATION ---
@@ -343,8 +408,32 @@ async function scan(directory) {
343
408
 
344
409
  // Ensure authority ledger directory exists
345
410
  const arcvisionDir = path.join(directory, 'arcvision_context');
411
+ console.log(`🔍 Ensuring arcvision_context directory exists at: ${arcvisionDir}`);
346
412
  if (!fs.existsSync(arcvisionDir)) {
413
+ console.log('📁 Creating arcvision_context directory...');
347
414
  fs.mkdirSync(arcvisionDir, { recursive: true });
415
+ console.log('✅ arcvision_context directory created');
416
+ } else {
417
+ console.log('✅ arcvision_context directory already exists');
418
+ }
419
+
420
+ // Write context to file
421
+ const contextFilePath = path.join(arcvisionDir, 'arcvision.context.json');
422
+ console.log(`💾 Writing context to: ${contextFilePath}`);
423
+ try {
424
+ fs.writeFileSync(contextFilePath, JSON.stringify(sortedContext, null, 2));
425
+ console.log('✅ Context file written successfully');
426
+
427
+ // Verify file was written
428
+ if (fs.existsSync(contextFilePath)) {
429
+ const stats = fs.statSync(contextFilePath);
430
+ console.log(`📊 Context file size: ${stats.size} bytes`);
431
+ } else {
432
+ console.warn('⚠️ Context file was not found after write operation');
433
+ }
434
+ } catch (writeError) {
435
+ console.error(`❌ Failed to write context file: ${writeError.message}`);
436
+ throw writeError;
348
437
  }
349
438
 
350
439
  console.log(' ✅ Context "Set" & Verified');