arcvision 0.2.14 → 0.2.15
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/ARCVISION_DIRECTORY_STRUCTURE.md +104 -0
- package/CLI_STRUCTURE.md +110 -0
- package/CONFIGURATION.md +119 -0
- package/IMPLEMENTATION_SUMMARY.md +99 -0
- package/README.md +149 -89
- package/architecture.authority.ledger.json +46 -0
- package/arcvision-0.2.3.tgz +0 -0
- package/arcvision-0.2.4.tgz +0 -0
- package/arcvision-0.2.5.tgz +0 -0
- package/arcvision.context.diff.json +2181 -0
- package/arcvision.context.json +1021 -0
- package/arcvision.context.v1.json +2163 -0
- package/arcvision.context.v2.json +2173 -0
- package/arcvision_context/README.md +93 -0
- package/arcvision_context/architecture.authority.ledger.json +83 -0
- package/arcvision_context/arcvision.context.json +6884 -0
- package/debug-cycle-detection.js +56 -0
- package/dist/index.js +1626 -25
- package/docs/ENHANCED_ACCURACY_SAFETY_PROTOCOL.md +172 -0
- package/docs/accuracy-enhancement-artifacts/enhanced-validation-config.json +98 -0
- package/docs/acig-robustness-guide.md +164 -0
- package/docs/authoritative-gate-implementation.md +168 -0
- package/docs/cli-strengthening-summary.md +232 -0
- package/docs/invariant-system-summary.md +100 -0
- package/docs/invariant-system.md +112 -0
- package/generate_large_test.js +42 -0
- package/large_test_repo.json +1 -0
- package/output1.json +2163 -0
- package/output2.json +2163 -0
- package/package.json +46 -36
- package/scan_calcom_report.txt +0 -0
- package/scan_leafmint_report.txt +0 -0
- package/scan_output.txt +0 -0
- package/scan_trigger_report.txt +0 -0
- package/schema/arcvision_context_schema_v1.json +136 -1
- package/src/arcvision-guard.js +433 -0
- package/src/core/authority-core-detector.js +382 -0
- package/src/core/authority-ledger.js +300 -0
- package/src/core/blastRadius.js +299 -0
- package/src/core/call-resolver.js +196 -0
- package/src/core/change-evaluator.js +509 -0
- package/src/core/change-evaluator.js.backup +424 -0
- package/src/core/change-evaluator.ts +285 -0
- package/src/core/chunked-uploader.js +180 -0
- package/src/core/circular-dependency-detector.js +404 -0
- package/src/core/cli-error-handler.js +458 -0
- package/src/core/cli-validator.js +458 -0
- package/src/core/compression.js +64 -0
- package/src/core/context_builder.js +741 -0
- package/src/core/dependency-manager.js +134 -0
- package/src/core/di-detector.js +202 -0
- package/src/core/diff-analyzer.js +76 -0
- package/src/core/example-invariants.js +135 -0
- package/src/core/failure-mode-synthesizer.js +341 -0
- package/src/core/invariant-analyzer.js +294 -0
- package/src/core/invariant-detector.js +548 -0
- package/src/core/invariant-enforcer.js +171 -0
- package/src/core/invariant-evaluation-utils.js +172 -0
- package/src/core/invariant-hooks.js +152 -0
- package/src/core/invariant-integration-example.js +186 -0
- package/src/core/invariant-registry.js +298 -0
- package/src/core/invariant-registry.ts +100 -0
- package/src/core/invariant-types.js +66 -0
- package/src/core/invariants-index.js +88 -0
- package/src/core/method-tracker.js +170 -0
- package/src/core/override-handler.js +304 -0
- package/src/core/ownership-resolver.js +227 -0
- package/src/core/parser-enhanced.js +80 -0
- package/src/core/parser.js +610 -0
- package/src/core/path-resolver.js +240 -0
- package/src/core/pattern-matcher.js +246 -0
- package/src/core/progress-tracker.js +71 -0
- package/src/core/react-nextjs-detector.js +245 -0
- package/src/core/readme-generator.js +167 -0
- package/src/core/retry-handler.js +57 -0
- package/src/core/scanner.js +289 -0
- package/src/core/semantic-analyzer.js +204 -0
- package/src/core/structural-context-owner.js +442 -0
- package/src/core/symbol-indexer.js +164 -0
- package/src/core/tsconfig-utils.js +73 -0
- package/src/core/type-analyzer.js +272 -0
- package/src/core/watcher.js +18 -0
- package/src/core/workspace-scanner.js +88 -0
- package/src/engine/context_builder.js +280 -0
- package/src/engine/context_sorter.js +59 -0
- package/src/engine/context_validator.js +200 -0
- package/src/engine/id-generator.js +16 -0
- package/src/engine/pass1_facts.js +260 -0
- package/src/engine/pass2_semantics.js +333 -0
- package/src/engine/pass3_lifter.js +99 -0
- package/src/engine/pass4_signals.js +201 -0
- package/src/index.js +830 -0
- package/src/plugins/express-plugin.js +48 -0
- package/src/plugins/plugin-manager.js +58 -0
- package/src/plugins/react-plugin.js +54 -0
- package/temp_original.js +0 -0
- package/test/determinism-test.js +83 -0
- package/test-authoritative-context.js +53 -0
- package/test-real-authoritative-context.js +118 -0
- package/test-upload-enhancements.js +111 -0
- package/test_repos/allowed-clean-architecture/.arcvision/invariants.json +57 -0
- package/test_repos/allowed-clean-architecture/adapters/controllers/UserController.js +95 -0
- package/test_repos/allowed-clean-architecture/adapters/http/HttpServer.js +78 -0
- package/test_repos/allowed-clean-architecture/application/dtos/CreateUserRequest.js +37 -0
- package/test_repos/allowed-clean-architecture/application/services/UserService.js +61 -0
- package/test_repos/allowed-clean-architecture/arcvision_context/README.md +93 -0
- package/test_repos/allowed-clean-architecture/arcvision_context/arcvision.context.json +2796 -0
- package/test_repos/allowed-clean-architecture/domain/interfaces/UserRepository.js +25 -0
- package/test_repos/allowed-clean-architecture/domain/models/User.js +39 -0
- package/test_repos/allowed-clean-architecture/index.js +45 -0
- package/test_repos/allowed-clean-architecture/infrastructure/database/DatabaseConnection.js +56 -0
- package/test_repos/allowed-clean-architecture/infrastructure/repositories/InMemoryUserRepository.js +61 -0
- package/test_repos/allowed-clean-architecture/package.json +15 -0
- package/test_repos/blocked-legacy-monolith/.arcvision/invariants.json +78 -0
- package/test_repos/blocked-legacy-monolith/arcvision_context/README.md +93 -0
- package/test_repos/blocked-legacy-monolith/arcvision_context/arcvision.context.json +2882 -0
- package/test_repos/blocked-legacy-monolith/database/dbConnection.js +35 -0
- package/test_repos/blocked-legacy-monolith/index.js +38 -0
- package/test_repos/blocked-legacy-monolith/modules/emailService.js +31 -0
- package/test_repos/blocked-legacy-monolith/modules/paymentProcessor.js +37 -0
- package/test_repos/blocked-legacy-monolith/package.json +15 -0
- package/test_repos/blocked-legacy-monolith/shared/utils.js +19 -0
- package/test_repos/blocked-legacy-monolith/utils/helpers.js +23 -0
- package/test_repos/risky-microservices-concerns/.arcvision/invariants.json +69 -0
- package/test_repos/risky-microservices-concerns/arcvision_context/README.md +93 -0
- package/test_repos/risky-microservices-concerns/arcvision_context/arcvision.context.json +3070 -0
- package/test_repos/risky-microservices-concerns/common/utils.js +77 -0
- package/test_repos/risky-microservices-concerns/gateways/apiGateway.js +84 -0
- package/test_repos/risky-microservices-concerns/index.js +20 -0
- package/test_repos/risky-microservices-concerns/libs/deprecatedHelper.js +36 -0
- package/test_repos/risky-microservices-concerns/package.json +15 -0
- package/test_repos/risky-microservices-concerns/services/orderService.js +42 -0
- package/test_repos/risky-microservices-concerns/services/userService.js +48 -0
- package/verify_engine.js +116 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const crypto = require('crypto');
|
|
8
|
+
const { execSync } = require('child_process');
|
|
9
|
+
|
|
10
|
+
const program = new Command();
|
|
11
|
+
|
|
12
|
+
program
|
|
13
|
+
.name('arcvision-guard')
|
|
14
|
+
.description('ArcVision Impact Guard - Enforce architectural safety')
|
|
15
|
+
.version('1.0.0');
|
|
16
|
+
|
|
17
|
+
program
|
|
18
|
+
.command('check')
|
|
19
|
+
.description('Check current changes against architectural invariants')
|
|
20
|
+
.option('-d, --dir <directory>', 'Working directory', '.')
|
|
21
|
+
.option('-v, --verbose', 'Show detailed output')
|
|
22
|
+
.option('--fail-on-warnings', 'Exit with error code on warnings')
|
|
23
|
+
.option('--bypass-token <token>', 'Bypass token for overriding critical violations')
|
|
24
|
+
.action((options) => {
|
|
25
|
+
console.log(chalk.blue('🛡️ ArcVision Impact Guard'));
|
|
26
|
+
console.log(chalk.blue('Checking architectural safety...\n'));
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// Load configuration
|
|
30
|
+
const config = loadConfig(options.dir);
|
|
31
|
+
|
|
32
|
+
// Check for bypass token
|
|
33
|
+
const bypassToken = options.bypassToken || process.env.ARCVISION_BYPASS_TOKEN;
|
|
34
|
+
let bypassValid = false;
|
|
35
|
+
|
|
36
|
+
if (bypassToken) {
|
|
37
|
+
bypassValid = validateBypassToken(bypassToken, options.dir);
|
|
38
|
+
if (bypassValid) {
|
|
39
|
+
console.log(chalk.yellow('⚠️ Bypass token detected - proceeding with caution'));
|
|
40
|
+
} else {
|
|
41
|
+
console.log(chalk.red('❌ Invalid bypass token - continuing with normal validation'));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Generate current context
|
|
46
|
+
console.log(chalk.gray('Generating current architectural context...'));
|
|
47
|
+
execSync('arcvision scan --output current-check.json', { stdio: 'inherit' });
|
|
48
|
+
|
|
49
|
+
// Check for git changes if in a repo
|
|
50
|
+
let hasChanges = false;
|
|
51
|
+
try {
|
|
52
|
+
execSync('git diff --quiet HEAD', { stdio: 'pipe' });
|
|
53
|
+
} catch (error) {
|
|
54
|
+
hasChanges = true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (hasChanges) {
|
|
58
|
+
console.log(chalk.gray('Comparing with HEAD context...'));
|
|
59
|
+
execSync('git stash push -m "temp-for-guard-check"', { stdio: 'pipe' });
|
|
60
|
+
execSync('git checkout HEAD --quiet');
|
|
61
|
+
execSync('arcvision scan --output head-context.json', { stdio: 'inherit' });
|
|
62
|
+
execSync('git stash pop --quiet', { stdio: 'pipe' });
|
|
63
|
+
|
|
64
|
+
// Generate diff
|
|
65
|
+
execSync('arcvision diff head-context.json current-check.json --output guard-report.json', { stdio: 'inherit' });
|
|
66
|
+
} else {
|
|
67
|
+
// No changes, just validate current state
|
|
68
|
+
execSync('cp current-check.json guard-report.json', { stdio: 'inherit' });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Analyze report
|
|
72
|
+
const report = JSON.parse(fs.readFileSync('guard-report.json', 'utf8'));
|
|
73
|
+
const analysis = analyzeReport(report, config);
|
|
74
|
+
|
|
75
|
+
// Display results
|
|
76
|
+
displayResults(analysis, options.verbose);
|
|
77
|
+
|
|
78
|
+
// Handle bypass logic
|
|
79
|
+
if (analysis.criticalIssues.length > 0 && !bypassValid) {
|
|
80
|
+
console.log(chalk.red('\n❌ Critical architectural violations detected!'));
|
|
81
|
+
console.log(chalk.yellow('\nTo bypass this check (USE WITH EXTREME CAUTION):'));
|
|
82
|
+
console.log(chalk.yellow('1. Generate bypass token: arcvision-guard bypass-request'));
|
|
83
|
+
console.log(chalk.yellow('2. Use token: arcvision-guard check --bypass-token <generated_token>'));
|
|
84
|
+
console.log(chalk.red('\n⚠️ WARNING: Bypassing critical violations can break your system!'));
|
|
85
|
+
process.exit(1);
|
|
86
|
+
} else if (options.failOnWarnings && analysis.warnings.length > 0 && !bypassValid) {
|
|
87
|
+
console.log(chalk.yellow('\n⚠️ Warnings present (failing due to --fail-on-warnings)'));
|
|
88
|
+
process.exit(1);
|
|
89
|
+
} else if (analysis.warnings.length > 0 && !bypassValid) {
|
|
90
|
+
console.log(chalk.yellow('\n⚠️ Architectural warnings detected - review recommended'));
|
|
91
|
+
process.exit(0);
|
|
92
|
+
} else {
|
|
93
|
+
console.log(chalk.green('\n✅ No architectural issues detected'));
|
|
94
|
+
process.exit(0);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Cleanup
|
|
98
|
+
cleanupFiles(['current-check.json', 'head-context.json', 'guard-report.json']);
|
|
99
|
+
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error(chalk.red('Guard check failed:'), error.message);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
program
|
|
107
|
+
.command('bypass-request')
|
|
108
|
+
.description('Request a one-time bypass token for critical changes')
|
|
109
|
+
.option('-d, --dir <directory>', 'Working directory', '.')
|
|
110
|
+
.option('-r, --reason <reason>', 'Reason for bypass request')
|
|
111
|
+
.action((options) => {
|
|
112
|
+
console.log(chalk.blue('🛡️ ArcVision Bypass Request'));
|
|
113
|
+
|
|
114
|
+
const reason = options.reason || 'No reason provided';
|
|
115
|
+
const timestamp = new Date().toISOString();
|
|
116
|
+
const hashInput = `${timestamp}-${reason}-${Math.random()}`;
|
|
117
|
+
const token = crypto.createHash('sha256').update(hashInput).digest('hex').substring(0, 32);
|
|
118
|
+
|
|
119
|
+
// Store token info for validation
|
|
120
|
+
const tokenInfo = {
|
|
121
|
+
token: token,
|
|
122
|
+
reason: reason,
|
|
123
|
+
created: timestamp,
|
|
124
|
+
used: false,
|
|
125
|
+
expires: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString() // 24 hours
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const tokensDir = path.join(options.dir, '.arcvision', 'tokens');
|
|
129
|
+
if (!fs.existsSync(tokensDir)) {
|
|
130
|
+
fs.mkdirSync(tokensDir, { recursive: true });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const tokenFile = path.join(tokensDir, `${token}.json`);
|
|
134
|
+
fs.writeFileSync(tokenFile, JSON.stringify(tokenInfo, null, 2));
|
|
135
|
+
|
|
136
|
+
console.log(chalk.green('\n✅ Bypass token generated!'));
|
|
137
|
+
console.log(chalk.yellow('\nToken:'), chalk.bold(token));
|
|
138
|
+
console.log(chalk.yellow('Reason:'), reason);
|
|
139
|
+
console.log(chalk.yellow('Expires:'), tokenInfo.expires);
|
|
140
|
+
console.log(chalk.red('\n⚠️ CRITICAL: This token can only be used ONCE and expires in 24 hours'));
|
|
141
|
+
console.log(chalk.red('⚠️ Use only for absolutely necessary architectural changes'));
|
|
142
|
+
console.log(chalk.yellow('\nUsage: arcvision-guard check --bypass-token'), chalk.bold(token));
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
program
|
|
146
|
+
.command('bypass-status')
|
|
147
|
+
.description('Check status of bypass tokens')
|
|
148
|
+
.option('-d, --dir <directory>', 'Working directory', '.')
|
|
149
|
+
.action((options) => {
|
|
150
|
+
const tokensDir = path.join(options.dir, '.arcvision', 'tokens');
|
|
151
|
+
|
|
152
|
+
if (!fs.existsSync(tokensDir)) {
|
|
153
|
+
console.log(chalk.yellow('No bypass tokens found'));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const tokenFiles = fs.readdirSync(tokensDir).filter(f => f.endsWith('.json'));
|
|
158
|
+
|
|
159
|
+
if (tokenFiles.length === 0) {
|
|
160
|
+
console.log(chalk.yellow('No bypass tokens found'));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log(chalk.blue('🛡️ ArcVision Bypass Token Status'));
|
|
165
|
+
console.log(chalk.blue('==================================='));
|
|
166
|
+
|
|
167
|
+
tokenFiles.forEach(file => {
|
|
168
|
+
const tokenInfo = JSON.parse(fs.readFileSync(path.join(tokensDir, file), 'utf8'));
|
|
169
|
+
const status = tokenInfo.used ? chalk.red('USED') :
|
|
170
|
+
new Date(tokenInfo.expires) < new Date() ? chalk.gray('EXPIRED') :
|
|
171
|
+
chalk.green('ACTIVE');
|
|
172
|
+
|
|
173
|
+
console.log(`\nToken: ${file.replace('.json', '')}`);
|
|
174
|
+
console.log(`Status: ${status}`);
|
|
175
|
+
console.log(`Reason: ${tokenInfo.reason}`);
|
|
176
|
+
console.log(`Created: ${tokenInfo.created}`);
|
|
177
|
+
console.log(`Expires: ${tokenInfo.expires}`);
|
|
178
|
+
console.log(`Used: ${tokenInfo.used ? 'Yes' : 'No'}`);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
program
|
|
183
|
+
.command('setup')
|
|
184
|
+
.description('Initialize ArcVision Impact Guard in current repository')
|
|
185
|
+
.option('-f, --force', 'Force re-initialization')
|
|
186
|
+
.action((options) => {
|
|
187
|
+
console.log(chalk.blue('🔧 Setting up ArcVision Impact Guard...\n'));
|
|
188
|
+
|
|
189
|
+
const arcDir = path.join('.', '.arcvision');
|
|
190
|
+
|
|
191
|
+
// Check if already initialized
|
|
192
|
+
if (fs.existsSync(arcDir) && !options.force) {
|
|
193
|
+
console.log(chalk.yellow('ArcVision Impact Guard is already initialized.'));
|
|
194
|
+
console.log(chalk.yellow('Use --force to reinitialize.'));
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Create directory structure
|
|
199
|
+
if (!fs.existsSync(arcDir)) {
|
|
200
|
+
fs.mkdirSync(arcDir, { recursive: true });
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Create tokens directory
|
|
204
|
+
const tokensDir = path.join(arcDir, 'tokens');
|
|
205
|
+
if (!fs.existsSync(tokensDir)) {
|
|
206
|
+
fs.mkdirSync(tokensDir, { recursive: true });
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Copy configuration templates
|
|
210
|
+
const templateDir = path.join(__dirname, '..', '.arcvision');
|
|
211
|
+
if (fs.existsSync(templateDir)) {
|
|
212
|
+
copyFile(path.join(templateDir, 'config.json'), path.join(arcDir, 'config.json'));
|
|
213
|
+
copyFile(path.join(templateDir, 'invariants.json'), path.join(arcDir, 'invariants.json'));
|
|
214
|
+
console.log(chalk.green('✅ Configuration files created'));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Update .gitignore
|
|
218
|
+
updateGitIgnore();
|
|
219
|
+
|
|
220
|
+
// Add to package.json if it exists
|
|
221
|
+
updatePackageJson();
|
|
222
|
+
|
|
223
|
+
console.log(chalk.green('\n🎉 ArcVision Impact Guard initialized successfully!'));
|
|
224
|
+
console.log(chalk.yellow('\nNext steps:'));
|
|
225
|
+
console.log(chalk.yellow('1. Review .arcvision/config.json for project settings'));
|
|
226
|
+
console.log(chalk.yellow('2. Customize .arcvision/invariants.json with your architectural rules'));
|
|
227
|
+
console.log(chalk.yellow('3. Run: arcvision-guard check'));
|
|
228
|
+
console.log(chalk.yellow('4. For critical changes: arcvision-guard bypass-request --reason "your reason"'));
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
program
|
|
232
|
+
.command('add-invariant')
|
|
233
|
+
.description('Add a custom architectural invariant')
|
|
234
|
+
.argument('<name>', 'Invariant name')
|
|
235
|
+
.argument('<description>', 'Invariant description')
|
|
236
|
+
.option('-s, --severity <level>', 'Severity (critical|warning)', 'warning')
|
|
237
|
+
.option('-c, --category <category>', 'Category (structural|domain|architectural|integration)', 'architectural')
|
|
238
|
+
.action((name, description, options) => {
|
|
239
|
+
const invariant = {
|
|
240
|
+
id: name.toLowerCase().replace(/\s+/g, '_'),
|
|
241
|
+
name: name,
|
|
242
|
+
description: description,
|
|
243
|
+
category: options.category,
|
|
244
|
+
severity: options.severity,
|
|
245
|
+
detection_pattern: "custom_pattern",
|
|
246
|
+
remediation: "Define appropriate remediation steps"
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const invariantsPath = path.join('.arcvision', 'invariants.json');
|
|
250
|
+
if (!fs.existsSync(invariantsPath)) {
|
|
251
|
+
console.log(chalk.red('❌ ArcVision not initialized. Run "arcvision-guard setup" first.'));
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const invariants = JSON.parse(fs.readFileSync(invariantsPath, 'utf8'));
|
|
256
|
+
invariants.project_specific_invariants.push(invariant);
|
|
257
|
+
|
|
258
|
+
fs.writeFileSync(invariantsPath, JSON.stringify(invariants, null, 2));
|
|
259
|
+
console.log(chalk.green(`✅ Added invariant: ${name}`));
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Helper functions
|
|
263
|
+
function loadConfig(dir) {
|
|
264
|
+
const configPath = path.join(dir, '.arcvision', 'config.json');
|
|
265
|
+
if (fs.existsSync(configPath)) {
|
|
266
|
+
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
267
|
+
}
|
|
268
|
+
return { guard_rules: {} };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function validateBypassToken(token, dir) {
|
|
272
|
+
const tokenFile = path.join(dir, '.arcvision', 'tokens', `${token}.json`);
|
|
273
|
+
|
|
274
|
+
if (!fs.existsSync(tokenFile)) {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const tokenInfo = JSON.parse(fs.readFileSync(tokenFile, 'utf8'));
|
|
279
|
+
|
|
280
|
+
// Check if expired
|
|
281
|
+
if (new Date(tokenInfo.expires) < new Date()) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Check if already used
|
|
286
|
+
if (tokenInfo.used) {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Mark as used
|
|
291
|
+
tokenInfo.used = true;
|
|
292
|
+
fs.writeFileSync(tokenFile, JSON.stringify(tokenInfo, null, 2));
|
|
293
|
+
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function analyzeReport(report, config) {
|
|
298
|
+
const criticalIssues = [];
|
|
299
|
+
const warnings = [];
|
|
300
|
+
|
|
301
|
+
// Check blast radius changes
|
|
302
|
+
if (report.blast_radius_changes) {
|
|
303
|
+
report.blast_radius_changes.forEach(change => {
|
|
304
|
+
const threshold = config.guard_rules?.blast_radius?.critical_threshold || 50;
|
|
305
|
+
if (change.percentage_increase > threshold) {
|
|
306
|
+
criticalIssues.push({
|
|
307
|
+
type: 'blast_radius',
|
|
308
|
+
message: `High blast radius increase: ${change.file} (+${change.percentage_increase}%)`,
|
|
309
|
+
file: change.file
|
|
310
|
+
});
|
|
311
|
+
} else if (change.percentage_increase > 20) {
|
|
312
|
+
warnings.push({
|
|
313
|
+
type: 'blast_radius',
|
|
314
|
+
message: `Moderate blast radius increase: ${change.file} (+${change.percentage_increase}%)`,
|
|
315
|
+
file: change.file
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Check invariant violations
|
|
322
|
+
if (report.invariant_violations) {
|
|
323
|
+
report.invariant_violations.forEach(violation => {
|
|
324
|
+
const issue = {
|
|
325
|
+
type: 'invariant',
|
|
326
|
+
message: `${violation.invariant_id}: ${violation.description}`,
|
|
327
|
+
file: violation.file
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
if (violation.severity === 'critical') {
|
|
331
|
+
criticalIssues.push(issue);
|
|
332
|
+
} else {
|
|
333
|
+
warnings.push(issue);
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Check authority core changes
|
|
339
|
+
if (report.authority_core_changes) {
|
|
340
|
+
report.authority_core_changes.forEach(change => {
|
|
341
|
+
criticalIssues.push({
|
|
342
|
+
type: 'authority_core',
|
|
343
|
+
message: `Authority core modified: ${change.file} - ${change.reason}`,
|
|
344
|
+
file: change.file
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return { criticalIssues, warnings };
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function displayResults(analysis, verbose) {
|
|
353
|
+
if (analysis.criticalIssues.length === 0 && analysis.warnings.length === 0) {
|
|
354
|
+
console.log(chalk.green('✅ No architectural issues detected'));
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (analysis.criticalIssues.length > 0) {
|
|
359
|
+
console.log(chalk.red(`\n❌ Critical Issues (${analysis.criticalIssues.length}):`));
|
|
360
|
+
analysis.criticalIssues.forEach(issue => {
|
|
361
|
+
console.log(chalk.red(` • ${issue.message}`));
|
|
362
|
+
if (verbose && issue.file) {
|
|
363
|
+
console.log(chalk.gray(` File: ${issue.file}`));
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (analysis.warnings.length > 0) {
|
|
369
|
+
console.log(chalk.yellow(`\n⚠️ Warnings (${analysis.warnings.length}):`));
|
|
370
|
+
analysis.warnings.forEach(warning => {
|
|
371
|
+
console.log(chalk.yellow(` • ${warning.message}`));
|
|
372
|
+
if (verbose && warning.file) {
|
|
373
|
+
console.log(chalk.gray(` File: ${warning.file}`));
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function updateGitIgnore() {
|
|
380
|
+
const gitignorePath = '.gitignore';
|
|
381
|
+
const ignoreEntries = [
|
|
382
|
+
'# ArcVision Impact Guard',
|
|
383
|
+
'.arcvision/temp/',
|
|
384
|
+
'.arcvision/cache/',
|
|
385
|
+
'.arcvision/tokens/',
|
|
386
|
+
'*-check.json',
|
|
387
|
+
'*-context.json',
|
|
388
|
+
'guard-report.json'
|
|
389
|
+
];
|
|
390
|
+
|
|
391
|
+
let content = '';
|
|
392
|
+
if (fs.existsSync(gitignorePath)) {
|
|
393
|
+
content = fs.readFileSync(gitignorePath, 'utf8');
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const newEntries = ignoreEntries.filter(entry => !content.includes(entry));
|
|
397
|
+
if (newEntries.length > 0) {
|
|
398
|
+
fs.appendFileSync(gitignorePath, '\n' + newEntries.join('\n') + '\n');
|
|
399
|
+
console.log(chalk.green('✅ Updated .gitignore'));
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function updatePackageJson() {
|
|
404
|
+
const pkgPath = 'package.json';
|
|
405
|
+
if (fs.existsSync(pkgPath)) {
|
|
406
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
407
|
+
pkg.scripts = pkg.scripts || {};
|
|
408
|
+
|
|
409
|
+
if (!pkg.scripts['arcvision:check']) {
|
|
410
|
+
pkg.scripts['arcvision:check'] = 'arcvision-guard check';
|
|
411
|
+
pkg.scripts['arcvision:setup'] = 'arcvision-guard setup';
|
|
412
|
+
pkg.scripts['arcvision:bypass'] = 'arcvision-guard bypass-request';
|
|
413
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
|
|
414
|
+
console.log(chalk.green('✅ Updated package.json scripts'));
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function copyFile(src, dest) {
|
|
420
|
+
if (fs.existsSync(src)) {
|
|
421
|
+
fs.copyFileSync(src, dest);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function cleanupFiles(files) {
|
|
426
|
+
files.forEach(file => {
|
|
427
|
+
if (fs.existsSync(file)) {
|
|
428
|
+
fs.unlinkSync(file);
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
program.parse();
|