arcvision 0.2.28 → 0.2.29
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 +2 -2
- package/src/arcvision_context/README.md +103 -0
- package/src/arcvision_context/architecture.authority.ledger.json +6 -0
- package/src/arcvision_context/arcvision.context.json +32907 -0
- package/src/core/call-resolver.js +29 -24
- package/src/core/context_builder.js +52 -48
- package/src/core/invariant-detector.js +15 -3
- package/src/core/readme-generator.js +64 -11
- package/src/core/scanner.js +12 -4
- package/src/engine/context_builder.js +24 -15
- package/src/engine/pass5_intelligence.js +852 -0
- package/src/index.js +150 -57
package/src/index.js
CHANGED
|
@@ -93,6 +93,16 @@ function analyzeBlastRadius(architectureMap) {
|
|
|
93
93
|
};
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Print the PASS 5 Architecture Intelligence Summary to the terminal.
|
|
98
|
+
* All values are derived from actual graph computation in pass5_intelligence.js.
|
|
99
|
+
* This is NOT a template — it reads real data from map.intelligence.
|
|
100
|
+
*
|
|
101
|
+
* @param {Object} map - Full context object with intelligence block attached
|
|
102
|
+
* @param {Object} chalkInstance - chalk instance for colors
|
|
103
|
+
*/
|
|
104
|
+
|
|
105
|
+
|
|
96
106
|
function saveToken(token) {
|
|
97
107
|
try {
|
|
98
108
|
// Validate token format before saving
|
|
@@ -497,87 +507,103 @@ program
|
|
|
497
507
|
.description('Scan the current directory and generate architecture map')
|
|
498
508
|
.argument('[directory]', 'Directory to scan', '.')
|
|
499
509
|
.option('-u, --upload', 'Upload to database')
|
|
510
|
+
.option('-o, --output <file>', 'Save context to a specific file')
|
|
511
|
+
.option('--raw', 'Show verbose pass output and structural analysis details')
|
|
512
|
+
.option('--debug', 'Show all internal logs (preflight, plugins, ownership resolution, etc.)')
|
|
500
513
|
.action(async (directory, options) => {
|
|
501
514
|
const targetDir = path.resolve(directory);
|
|
502
515
|
|
|
516
|
+
// Wire verbosity flags to global so scanner.js can check them
|
|
517
|
+
process.env.ARCVISION_RAW = options.raw ? '1' : '';
|
|
518
|
+
process.env.ARCVISION_DEBUG = options.debug ? '1' : '';
|
|
519
|
+
|
|
503
520
|
try {
|
|
504
|
-
// Pre-flight validation
|
|
505
|
-
|
|
506
|
-
directory: targetDir
|
|
507
|
-
}
|
|
521
|
+
// Pre-flight validation (silenced in default mode)
|
|
522
|
+
if (options.raw || options.debug) {
|
|
523
|
+
await cliValidator.preFlightValidation('scan', { directory: targetDir });
|
|
524
|
+
} else {
|
|
525
|
+
// Run validation silently — capture console temporarily
|
|
526
|
+
const origLog = console.log;
|
|
527
|
+
const origWarn = console.warn;
|
|
528
|
+
console.log = () => { };
|
|
529
|
+
console.warn = () => { };
|
|
530
|
+
try { await cliValidator.preFlightValidation('scan', { directory: targetDir }); } catch (e) { /* rethrow after restoring */ console.log = origLog; console.warn = origWarn; throw e; }
|
|
531
|
+
console.log = origLog;
|
|
532
|
+
console.warn = origWarn;
|
|
533
|
+
}
|
|
508
534
|
|
|
509
535
|
// Initialize Artifact Manager
|
|
510
536
|
const { ArtifactManager } = require('./core/artifact-manager');
|
|
511
537
|
const artifactManager = new ArtifactManager(targetDir);
|
|
512
|
-
|
|
513
|
-
// Ensure all required artifacts exist
|
|
514
538
|
artifactManager.ensureArtifacts();
|
|
515
539
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
console.log(chalk.green('Scan complete!'));
|
|
520
|
-
|
|
521
|
-
// Analyze and print blast radius insight
|
|
522
|
-
const blastRadiusAnalysis = analyzeBlastRadius(map);
|
|
523
|
-
if (blastRadiusAnalysis && blastRadiusAnalysis.topFiles && blastRadiusAnalysis.topFiles.length > 0) {
|
|
524
|
-
console.log('\n🔍 STRUCTURAL ANALYSIS RESULTS:\n');
|
|
525
|
-
|
|
526
|
-
blastRadiusAnalysis.topFiles.forEach((item, index) => {
|
|
527
|
-
let warningMessage = '';
|
|
528
|
-
if (index === 0) {
|
|
529
|
-
warningMessage = 'Changes here may silently propagate across the system.';
|
|
530
|
-
} else if (index === 1) {
|
|
531
|
-
warningMessage = 'Acts as a coordination layer between components.';
|
|
532
|
-
} else {
|
|
533
|
-
warningMessage = 'Modifications can cause widespread inconsistencies.';
|
|
534
|
-
}
|
|
540
|
+
if (options.raw || options.debug) {
|
|
541
|
+
console.log(chalk.blue(`\nScanning directory: ${targetDir}`));
|
|
542
|
+
}
|
|
535
543
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
544
|
+
// Run the full 5-pass engine
|
|
545
|
+
// In default mode, suppress the noisy pass-level console output from scanner.js
|
|
546
|
+
let map;
|
|
547
|
+
if (options.raw || options.debug) {
|
|
548
|
+
map = await scanner.scan(targetDir);
|
|
540
549
|
} else {
|
|
541
|
-
console.log
|
|
550
|
+
// Capture ALL console.log/warn during scan — show only our curated summary
|
|
551
|
+
const origLog = console.log;
|
|
552
|
+
const origWarn = console.warn;
|
|
553
|
+
const origError = console.error;
|
|
554
|
+
const capturedLogs = [];
|
|
555
|
+
console.log = (...args) => capturedLogs.push(args.join(' '));
|
|
556
|
+
console.warn = (...args) => capturedLogs.push('[WARN] ' + args.join(' '));
|
|
557
|
+
// Keep errors visible even in default mode (failures matter)
|
|
558
|
+
try {
|
|
559
|
+
map = await scanner.scan(targetDir);
|
|
560
|
+
} finally {
|
|
561
|
+
console.log = origLog;
|
|
562
|
+
console.warn = origWarn;
|
|
563
|
+
console.error = origError;
|
|
564
|
+
}
|
|
542
565
|
}
|
|
543
566
|
|
|
544
|
-
//
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
if (
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
567
|
+
// ── PASS 5: ARCHITECTURE INTELLIGENCE SUMMARY (always printed) ──
|
|
568
|
+
printIntelligenceSummary(map, chalk);
|
|
569
|
+
|
|
570
|
+
// ── STRUCTURAL ANALYSIS (blast radius) — only in --raw mode ──
|
|
571
|
+
if (options.raw) {
|
|
572
|
+
const blastRadiusAnalysis = analyzeBlastRadius(map);
|
|
573
|
+
if (blastRadiusAnalysis && blastRadiusAnalysis.topFiles && blastRadiusAnalysis.topFiles.length > 0) {
|
|
574
|
+
console.log(chalk.dim('\n── Blast Radius Analysis ──'));
|
|
575
|
+
blastRadiusAnalysis.topFiles.forEach((item, index) => {
|
|
576
|
+
const messages = [
|
|
577
|
+
'Changes here may silently propagate across the system.',
|
|
578
|
+
'Acts as a coordination layer between components.',
|
|
579
|
+
'Modifications can cause widespread inconsistencies.'
|
|
580
|
+
];
|
|
581
|
+
console.log(`${index + 1}. 📁 ${item.file}`);
|
|
582
|
+
console.log(` 🎯 Blast Radius: ${item.blastRadius} files (${item.percentOfGraph}%)`);
|
|
583
|
+
console.log(` ⚠️ Risk: ${messages[index] || ''}\n`);
|
|
584
|
+
});
|
|
585
|
+
}
|
|
552
586
|
}
|
|
553
587
|
|
|
554
|
-
//
|
|
588
|
+
// ── SAVE / UPLOAD ──
|
|
555
589
|
const arcvisionDir = path.join(targetDir, 'arcvision_context');
|
|
556
590
|
if (!fs.existsSync(arcvisionDir)) {
|
|
557
591
|
fs.mkdirSync(arcvisionDir, { recursive: true });
|
|
558
|
-
console.log(chalk.green(`📁 Created arcvision_context directory: ${arcvisionDir}`));
|
|
559
592
|
}
|
|
560
593
|
|
|
561
|
-
|
|
594
|
+
const { generateReadme } = require('./core/readme-generator');
|
|
595
|
+
|
|
562
596
|
if (options.upload) {
|
|
563
597
|
await uploadToDatabase(map, arcvisionDir);
|
|
564
|
-
|
|
565
|
-
// Generate README for system context when uploading
|
|
566
|
-
const { generateReadme } = require('./core/readme-generator');
|
|
567
|
-
generateReadme(arcvisionDir, version, blastRadiusAnalysis);
|
|
598
|
+
generateReadme(arcvisionDir, version, analyzeBlastRadius(map), map.intelligence);
|
|
568
599
|
} else {
|
|
569
|
-
|
|
570
|
-
const fs = require('fs');
|
|
571
|
-
const outputFileName = path.join(arcvisionDir, 'arcvision.context.json');
|
|
600
|
+
const outputFileName = options.output ? path.resolve(options.output) : path.join(arcvisionDir, 'arcvision.context.json');
|
|
572
601
|
fs.writeFileSync(outputFileName, JSON.stringify(map, null, 2));
|
|
573
|
-
console.log(chalk.green(
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
const { generateReadme } = require('./core/readme-generator');
|
|
577
|
-
generateReadme(arcvisionDir, version, blastRadiusAnalysis);
|
|
578
|
-
|
|
579
|
-
console.log(chalk.dim('\nUse --upload to send to dashboard.'));
|
|
602
|
+
console.log(chalk.green(`\n✅ Context saved → ${outputFileName}`));
|
|
603
|
+
generateReadme(arcvisionDir, version, analyzeBlastRadius(map), map.intelligence);
|
|
604
|
+
console.log(chalk.dim('Use --upload to send to dashboard.'));
|
|
580
605
|
}
|
|
606
|
+
|
|
581
607
|
} catch (error) {
|
|
582
608
|
cliErrorHandler.handleFatalError(error, {
|
|
583
609
|
operation: 'scan',
|
|
@@ -592,7 +618,8 @@ program
|
|
|
592
618
|
.description('Compare two context artifacts and generate diff summary')
|
|
593
619
|
.argument('<old-file>', 'Path to the old context artifact')
|
|
594
620
|
.argument('<new-file>', 'Path to the new context artifact')
|
|
595
|
-
.
|
|
621
|
+
.option('-o, --output <file>', 'Save diff summary to a specific file')
|
|
622
|
+
.action(async (oldFile, newFile, options) => {
|
|
596
623
|
try {
|
|
597
624
|
// Pre-flight validation
|
|
598
625
|
await cliValidator.preFlightValidation('diff', {
|
|
@@ -619,7 +646,7 @@ program
|
|
|
619
646
|
if (!fs.existsSync(arcvisionDir)) {
|
|
620
647
|
fs.mkdirSync(arcvisionDir, { recursive: true });
|
|
621
648
|
}
|
|
622
|
-
const outputFileName = path.join(arcvisionDir, 'arcvision.context.diff.json');
|
|
649
|
+
const outputFileName = options.output ? path.resolve(options.output) : path.join(arcvisionDir, 'arcvision.context.diff.json');
|
|
623
650
|
fs.writeFileSync(outputFileName, JSON.stringify(newContext, null, 2));
|
|
624
651
|
|
|
625
652
|
console.log(chalk.green('✅ Structural diff completed!'));
|
|
@@ -977,4 +1004,70 @@ program
|
|
|
977
1004
|
}
|
|
978
1005
|
});
|
|
979
1006
|
|
|
1007
|
+
/**
|
|
1008
|
+
* Print the Architecture Intelligence Summary from Pass 5
|
|
1009
|
+
* @param {Object} context - The structural context result
|
|
1010
|
+
* @param {Object} chalk - Chalk instance for formatting
|
|
1011
|
+
*/
|
|
1012
|
+
function printIntelligenceSummary(context, chalk) {
|
|
1013
|
+
if (!context || !context.intelligence) return;
|
|
1014
|
+
|
|
1015
|
+
const intel = context.intelligence;
|
|
1016
|
+
const sep = '═'.repeat(60);
|
|
1017
|
+
|
|
1018
|
+
console.log(`\n${chalk.bold.cyan('🧠 ARCHITECTURE INTELLIGENCE SUMMARY (Pass 5)')}`);
|
|
1019
|
+
console.log(chalk.cyan(sep));
|
|
1020
|
+
|
|
1021
|
+
// 1. Archetype Classification
|
|
1022
|
+
if (intel.architectural_classification) {
|
|
1023
|
+
const arch = intel.architectural_classification;
|
|
1024
|
+
console.log(`\n${chalk.bold('Architectural Archetype:')}`);
|
|
1025
|
+
console.log(` ${chalk.magenta(arch.type.toUpperCase().replace(/_/g, ' '))}`);
|
|
1026
|
+
console.log(` → ${chalk.dim(arch.description)}`);
|
|
1027
|
+
}
|
|
1028
|
+
// 1. Risk Score
|
|
1029
|
+
const riskColor = intel.risk_assessment.risk_level === 'high' ? chalk.red :
|
|
1030
|
+
intel.risk_assessment.risk_level === 'medium' ? chalk.yellow : chalk.green;
|
|
1031
|
+
console.log(`\n${chalk.bold('Risk Score:')} ${riskColor(intel.risk_assessment.risk_score + '/10')} (${intel.risk_assessment.risk_level.toUpperCase()})`);
|
|
1032
|
+
|
|
1033
|
+
// 1.a Architectural Entropy
|
|
1034
|
+
if (intel.entropy) {
|
|
1035
|
+
const entropyColor = intel.entropy.level === 'critical' ? chalk.red :
|
|
1036
|
+
intel.entropy.level === 'high' ? chalk.red :
|
|
1037
|
+
intel.entropy.level === 'medium' ? chalk.yellow : chalk.green;
|
|
1038
|
+
console.log(`${chalk.bold('Architectural Entropy:')} ${entropyColor(intel.entropy.score)} [${intel.entropy.level.toUpperCase()}]`);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// 2. Structural Hotspots
|
|
1042
|
+
console.log(`\n${chalk.bold('Structural Hotspots:')}`);
|
|
1043
|
+
console.log(` • ${chalk.yellow(intel.hotspots.total_cycles)} circular dependency cycles detected`);
|
|
1044
|
+
console.log(` • ${chalk.yellow(intel.hotspots.clusters.length)} dense coupling clusters identified`);
|
|
1045
|
+
if (intel.hotspots.boundary_violations && intel.hotspots.boundary_violations.length > 0) {
|
|
1046
|
+
console.log(` • ${chalk.red(intel.hotspots.boundary_violations.length)} architectural boundary violations detected`);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// 3. Architectural Gravity (Centrality)
|
|
1050
|
+
console.log(`\n${chalk.bold('Most Central File (Gravity Hub):')}`);
|
|
1051
|
+
console.log(` • ${chalk.magenta(intel.centrality.most_central || 'None identified')}`);
|
|
1052
|
+
|
|
1053
|
+
// 4. Critical Fragility
|
|
1054
|
+
const highFragility = (intel.fragility.top_fragile_files || []).filter(f => f.score > 2);
|
|
1055
|
+
if (highFragility.length > 0) {
|
|
1056
|
+
console.log(`\n${chalk.bold('Highly Fragile Components:')}`);
|
|
1057
|
+
highFragility.slice(0, 3).forEach(f => {
|
|
1058
|
+
console.log(` • ${chalk.red(f.file)} (fragility: ${f.score})`);
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// 5. Recommendations
|
|
1063
|
+
if (intel.recommendations && intel.recommendations.length > 0) {
|
|
1064
|
+
console.log(`\n${chalk.bold.green('AI Recommendations:')}`);
|
|
1065
|
+
intel.recommendations.forEach((rec, idx) => {
|
|
1066
|
+
console.log(` ${idx + 1}. ${rec}`);
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
console.log(`\n${chalk.cyan('='.repeat(45))}\n`);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
980
1073
|
program.parse();
|