tribunal-kit 9.2.6 → 9.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/.agent/ARCHITECTURE.md +2 -2
- package/.agent/history/architecture-intelligence.html +1487 -401
- package/.agent/history/architecture-model.json +9365 -5697
- package/.agent/history/architecture-verification.json +37 -9
- package/.agent/routing_index.json +2069 -3340
- package/.agent/rules/GEMINI.md +1 -1
- package/.agent/scripts/adaptive_execution.js +511 -0
- package/.agent/scripts/architecture_diff.js +303 -260
- package/.agent/scripts/architecture_extractor.js +792 -753
- package/.agent/scripts/architecture_health.js +293 -256
- package/.agent/scripts/architecture_intelligence.js +3100 -0
- package/.agent/scripts/architecture_verifier.js +303 -270
- package/.agent/scripts/architecture_visualizer.js +880 -873
- package/.agent/scripts/blast_radius_engine.js +274 -234
- package/.agent/scripts/capability_evolution.js +453 -0
- package/.agent/scripts/concept_extractor.js +240 -0
- package/.agent/scripts/consideration_engine.js +284 -0
- package/.agent/scripts/coverage_engine.js +102 -0
- package/.agent/scripts/cross_domain_engine.js +1883 -0
- package/.agent/scripts/engineering_memory.js +1313 -0
- package/.agent/scripts/integrity_manifest.js +1 -5
- package/.agent/scripts/judgment_engine.js +790 -0
- package/.agent/scripts/materiality_engine.js +251 -0
- package/.agent/scripts/outcome_engine.js +624 -0
- package/.agent/scripts/outcome_optimization_engine.js +1632 -0
- package/.agent/scripts/simulation_engine.js +1478 -0
- package/.agent/scripts/skill_intelligence.js +1371 -0
- package/.agent/scripts/validation_engine.js +236 -236
- package/.agent/scripts/verification_auditor.js +266 -0
- package/.agent/skill-intelligence/taxonomy/engineering-concepts.json +555 -0
- package/.agent/skill_topic_map.json +301 -341
- package/.agent/skills_inventory.json +4272 -6922
- package/.agent/workflows/arch.md +96 -80
- package/.agent/workflows/skill-intelligence.md +216 -0
- package/CONTRIBUTING.md +2 -2
- package/README.md +24 -24
- package/bin/adapter-install.js +1 -1
- package/package.json +8 -9
- package/plugins/core-plugin.json +1 -1
- package/plugins/gemini/extension.json +1 -1
- package/src/browser/index.js +2 -1
- package/src/browser/launcher.js +41 -1
- package/src/cli.js +3 -1
- package/src/commands/arch.js +261 -172
- package/src/commands/context.js +27 -8
- package/src/commands/learn.js +21 -6
- package/src/commands/memory.js +10 -1
- package/src/commands/native.js +12 -3
- package/src/commands/skill-intelligence.js +1165 -0
- package/src/commands/system1.js +105 -4
- package/src/context/ranker.js +337 -339
- package/src/evolution/engine.js +296 -295
- package/src/synthesis/claim_check_separator.js +294 -271
- package/src/system1/browser_intelligence.js +300 -0
- package/src/system1/constants.js +49 -0
- package/src/system1/decision_engine.js +498 -0
- package/src/system1/evidence_engine.js +602 -0
- package/src/system1/evolution_pipeline.js +581 -0
- package/src/system1/index.d.ts +114 -0
- package/src/system1/index.js +102 -0
- package/src/system1/reviewer_orchestrator.js +477 -0
- package/src/tui/banner.js +1 -1
- package/src/utils/fs.js +27 -0
|
@@ -1,234 +1,274 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* blast_radius_engine.js — Tribunal Architectural Blast Radius Engine
|
|
4
|
-
* ═════════════════════════════════════════════════════════════════════════════
|
|
5
|
-
* Answers: "If I modify or break this component, what is affected?"
|
|
6
|
-
*
|
|
7
|
-
* Computes:
|
|
8
|
-
* - Direct Dependents (Immediate Callers/Importers)
|
|
9
|
-
* - Transitive Closure (Full Downstream Blast Radius)
|
|
10
|
-
* - Affected API Routes & Endpoints
|
|
11
|
-
* - Affected Data Stores & Queries
|
|
12
|
-
* - Affected Trust Boundaries & Security Zones
|
|
13
|
-
* - Required Test Suites to Re-verify
|
|
14
|
-
* - Architectural Centrality & Risk Score (0.0 to 1.0)
|
|
15
|
-
*
|
|
16
|
-
* Zero external dependencies.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
'use strict';
|
|
20
|
-
|
|
21
|
-
const fs = require('fs');
|
|
22
|
-
const path = require('path');
|
|
23
|
-
const {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
console.log(
|
|
221
|
-
|
|
222
|
-
if (result.
|
|
223
|
-
console.
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* blast_radius_engine.js — Tribunal Architectural Blast Radius Engine
|
|
4
|
+
* ═════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
* Answers: "If I modify or break this component, what is affected?"
|
|
6
|
+
*
|
|
7
|
+
* Computes:
|
|
8
|
+
* - Direct Dependents (Immediate Callers/Importers)
|
|
9
|
+
* - Transitive Closure (Full Downstream Blast Radius)
|
|
10
|
+
* - Affected API Routes & Endpoints
|
|
11
|
+
* - Affected Data Stores & Queries
|
|
12
|
+
* - Affected Trust Boundaries & Security Zones
|
|
13
|
+
* - Required Test Suites to Re-verify
|
|
14
|
+
* - Architectural Centrality & Risk Score (0.0 to 1.0)
|
|
15
|
+
*
|
|
16
|
+
* Zero external dependencies.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
'use strict';
|
|
20
|
+
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const {
|
|
24
|
+
GREEN,
|
|
25
|
+
YELLOW,
|
|
26
|
+
CYAN,
|
|
27
|
+
RED,
|
|
28
|
+
BOLD,
|
|
29
|
+
RESET,
|
|
30
|
+
BOX,
|
|
31
|
+
banner,
|
|
32
|
+
timer,
|
|
33
|
+
formatMs,
|
|
34
|
+
} = require('./_colors');
|
|
35
|
+
|
|
36
|
+
class BlastRadiusEngine {
|
|
37
|
+
constructor(model) {
|
|
38
|
+
this.model = model;
|
|
39
|
+
this.entityMap = new Map();
|
|
40
|
+
this.dependentsMap = new Map(); // target -> Set of sources that depend on target
|
|
41
|
+
this.dependenciesMap = new Map(); // source -> Set of targets that source depends on
|
|
42
|
+
|
|
43
|
+
this.initGraph();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
initGraph() {
|
|
47
|
+
for (const e of this.model.entities || []) {
|
|
48
|
+
this.entityMap.set(e.id, e);
|
|
49
|
+
this.dependentsMap.set(e.id, new Set());
|
|
50
|
+
this.dependenciesMap.set(e.id, new Set());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for (const rel of this.model.relationships || []) {
|
|
54
|
+
if (this.dependentsMap.has(rel.targetId)) {
|
|
55
|
+
this.dependentsMap.get(rel.targetId).add(rel.sourceId);
|
|
56
|
+
}
|
|
57
|
+
if (this.dependenciesMap.has(rel.sourceId)) {
|
|
58
|
+
this.dependenciesMap.get(rel.sourceId).add(rel.targetId);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
findEntity(query) {
|
|
64
|
+
if (!query) return null;
|
|
65
|
+
const clean = query.trim();
|
|
66
|
+
|
|
67
|
+
// Direct ID match
|
|
68
|
+
if (this.entityMap.has(clean)) return this.entityMap.get(clean);
|
|
69
|
+
|
|
70
|
+
// Path or partial match
|
|
71
|
+
for (const [id, e] of this.entityMap.entries()) {
|
|
72
|
+
if (e.name === clean || e.scope === clean) return e;
|
|
73
|
+
if (e.sources && e.sources.some(s => s.file === clean || s.file.endsWith(clean))) return e;
|
|
74
|
+
if (id.toLowerCase().includes(clean.toLowerCase())) return e;
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
computeBlastRadius(targetQuery, maxDepth = 15) {
|
|
80
|
+
const rootEntity = this.findEntity(targetQuery);
|
|
81
|
+
if (!rootEntity) {
|
|
82
|
+
return {
|
|
83
|
+
found: false,
|
|
84
|
+
query: targetQuery,
|
|
85
|
+
error: `Component or file "${targetQuery}" not found in architectural model.`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const visited = new Set([rootEntity.id]);
|
|
90
|
+
const directDependents = new Set();
|
|
91
|
+
const transitiveRings = []; // Array of Sets per depth level
|
|
92
|
+
|
|
93
|
+
let currentLevel = new Set(this.dependentsMap.get(rootEntity.id) || []);
|
|
94
|
+
for (const d of currentLevel) directDependents.add(d);
|
|
95
|
+
|
|
96
|
+
let depth = 1;
|
|
97
|
+
while (currentLevel.size > 0 && depth <= maxDepth) {
|
|
98
|
+
const ringEntities = [];
|
|
99
|
+
const nextLevel = new Set();
|
|
100
|
+
|
|
101
|
+
for (const id of currentLevel) {
|
|
102
|
+
if (!visited.has(id)) {
|
|
103
|
+
visited.add(id);
|
|
104
|
+
const entity = this.entityMap.get(id);
|
|
105
|
+
if (entity) ringEntities.push(entity);
|
|
106
|
+
|
|
107
|
+
const upstreams = this.dependentsMap.get(id) || [];
|
|
108
|
+
for (const up of upstreams) {
|
|
109
|
+
if (!visited.has(up)) {
|
|
110
|
+
nextLevel.add(up);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (ringEntities.length > 0) {
|
|
117
|
+
transitiveRings.push({ depth, count: ringEntities.length, entities: ringEntities });
|
|
118
|
+
}
|
|
119
|
+
currentLevel = nextLevel;
|
|
120
|
+
depth++;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Collect all affected entities (excluding root)
|
|
124
|
+
const affectedEntities = Array.from(visited)
|
|
125
|
+
.filter(id => id !== rootEntity.id)
|
|
126
|
+
.map(id => this.entityMap.get(id))
|
|
127
|
+
.filter(Boolean);
|
|
128
|
+
|
|
129
|
+
// Categorize Impact
|
|
130
|
+
const affectedEndpoints = affectedEntities.filter(e => e.kind === 'endpoint');
|
|
131
|
+
const affectedDatastores = affectedEntities.filter(
|
|
132
|
+
e => e.kind === 'datastore' || e.kind === 'cache',
|
|
133
|
+
);
|
|
134
|
+
const affectedQueues = affectedEntities.filter(
|
|
135
|
+
e => e.kind === 'queue' || e.kind === 'event_bus',
|
|
136
|
+
);
|
|
137
|
+
const affectedTests = affectedEntities.filter(
|
|
138
|
+
e => e.kind === 'test_suite' || e.scope?.includes('test'),
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// Check Trust Zone Crossings
|
|
142
|
+
const rootZone = rootEntity.trustZone || 'unknown';
|
|
143
|
+
const crossedZones = new Set();
|
|
144
|
+
for (const e of affectedEntities) {
|
|
145
|
+
if (e.trustZone && e.trustZone !== rootZone) {
|
|
146
|
+
crossedZones.add(e.trustZone);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Risk Calculation (0.0 to 1.0)
|
|
151
|
+
// Centrality + Blast scope + Security Crossing
|
|
152
|
+
const totalEntities = this.entityMap.size || 1;
|
|
153
|
+
const impactRatio = affectedEntities.length / totalEntities;
|
|
154
|
+
const directCoupling = directDependents.size;
|
|
155
|
+
const securityPenalty =
|
|
156
|
+
crossedZones.has('public_untrusted') || crossedZones.has('dmz_gateway') ? 0.3 : 0.1;
|
|
157
|
+
|
|
158
|
+
let riskScore = Math.min(1.0, impactRatio * 2.5 + directCoupling * 0.05 + securityPenalty);
|
|
159
|
+
riskScore = Math.round(riskScore * 100) / 100;
|
|
160
|
+
|
|
161
|
+
let riskTier = 'LOW';
|
|
162
|
+
if (riskScore >= 0.7) riskTier = 'CRITICAL';
|
|
163
|
+
else if (riskScore >= 0.4) riskTier = 'HIGH';
|
|
164
|
+
else if (riskScore >= 0.2) riskTier = 'MEDIUM';
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
found: true,
|
|
168
|
+
target: {
|
|
169
|
+
id: rootEntity.id,
|
|
170
|
+
name: rootEntity.name,
|
|
171
|
+
kind: rootEntity.kind,
|
|
172
|
+
scope: rootEntity.scope,
|
|
173
|
+
trustZone: rootEntity.trustZone,
|
|
174
|
+
},
|
|
175
|
+
metrics: {
|
|
176
|
+
directDependentsCount: directDependents.size,
|
|
177
|
+
transitiveDependentsCount: affectedEntities.length,
|
|
178
|
+
maxDepthReached: transitiveRings.length,
|
|
179
|
+
riskScore,
|
|
180
|
+
riskTier,
|
|
181
|
+
},
|
|
182
|
+
impactBreakdown: {
|
|
183
|
+
endpoints: affectedEndpoints.map(e => ({ id: e.id, name: e.name, scope: e.scope })),
|
|
184
|
+
datastores: affectedDatastores.map(e => ({ id: e.id, name: e.name })),
|
|
185
|
+
queues: affectedQueues.map(e => ({ id: e.id, name: e.name })),
|
|
186
|
+
testSuites: affectedTests.map(e => ({ id: e.id, name: e.name, scope: e.scope })),
|
|
187
|
+
securityZonesCrossed: Array.from(crossedZones),
|
|
188
|
+
},
|
|
189
|
+
transitiveRings,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ── Standalone CLI ────────────────────────────────────────────────────────────
|
|
195
|
+
if (require.main === module) {
|
|
196
|
+
const t = timer();
|
|
197
|
+
const repoRoot = process.cwd();
|
|
198
|
+
const modelFile = path.join(repoRoot, '.agent', 'history', 'architecture-model.json');
|
|
199
|
+
|
|
200
|
+
const query = process.argv[2];
|
|
201
|
+
if (!query) {
|
|
202
|
+
console.log(banner('Tribunal Architectural Blast Radius Engine'));
|
|
203
|
+
console.error(
|
|
204
|
+
` ${YELLOW}Usage:${RESET} node blast_radius_engine.js <component-id-or-filepath>\n`,
|
|
205
|
+
);
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!fs.existsSync(modelFile)) {
|
|
210
|
+
console.error(
|
|
211
|
+
` ${RED}✖ architecture-model.json not found.${RESET} Run architecture_extractor.js first.`,
|
|
212
|
+
);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const model = JSON.parse(fs.readFileSync(modelFile, 'utf8'));
|
|
217
|
+
const engine = new BlastRadiusEngine(model);
|
|
218
|
+
const result = engine.computeBlastRadius(query);
|
|
219
|
+
|
|
220
|
+
console.log(banner('Tribunal Architectural Blast Radius Engine'));
|
|
221
|
+
|
|
222
|
+
if (!result.found) {
|
|
223
|
+
console.error(` ${RED}✖ ${result.error}${RESET}`);
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const tierColors = {
|
|
228
|
+
CRITICAL: RED,
|
|
229
|
+
HIGH: YELLOW,
|
|
230
|
+
MEDIUM: CYAN,
|
|
231
|
+
LOW: GREEN,
|
|
232
|
+
};
|
|
233
|
+
const color = tierColors[result.metrics.riskTier] || GREEN;
|
|
234
|
+
|
|
235
|
+
console.log(
|
|
236
|
+
` Target: ${BOLD}${result.target.name}${RESET} (${result.target.id})`,
|
|
237
|
+
);
|
|
238
|
+
console.log(
|
|
239
|
+
` Kind: ${CYAN}${result.target.kind}${RESET} | Trust Zone: ${YELLOW}${result.target.trustZone}${RESET}`,
|
|
240
|
+
);
|
|
241
|
+
console.log(
|
|
242
|
+
` Blast Radius Risk: ${color}${BOLD}${result.metrics.riskTier} (${result.metrics.riskScore})${RESET}`,
|
|
243
|
+
);
|
|
244
|
+
console.log(` Direct Callers: ${BOLD}${result.metrics.directDependentsCount}${RESET}`);
|
|
245
|
+
console.log(
|
|
246
|
+
` Transitive Dependents: ${BOLD}${result.metrics.transitiveDependentsCount}${RESET} across ${result.metrics.maxDepthReached} depth rings`,
|
|
247
|
+
);
|
|
248
|
+
console.log(
|
|
249
|
+
` Impacted Endpoints: ${result.impactBreakdown.endpoints.length > 0 ? YELLOW : GREEN}${result.impactBreakdown.endpoints.length}${RESET}`,
|
|
250
|
+
);
|
|
251
|
+
console.log(
|
|
252
|
+
` Impacted Test Suites: ${CYAN}${result.impactBreakdown.testSuites.length}${RESET}`,
|
|
253
|
+
);
|
|
254
|
+
console.log(
|
|
255
|
+
` Zones Crossed: ${result.impactBreakdown.securityZonesCrossed.join(', ') || 'None'}`,
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
if (result.transitiveRings.length > 0) {
|
|
259
|
+
console.log(`\n ${BOLD}Concentric Blast Rings:${RESET}`);
|
|
260
|
+
for (const ring of result.transitiveRings) {
|
|
261
|
+
console.log(
|
|
262
|
+
` Ring ${ring.depth} (depth ${ring.depth}): ${BOLD}${ring.count}${RESET} downstream components`,
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
console.log(
|
|
268
|
+
`\n ${GREEN}${BOX.check} Blast Radius Analysis Complete${RESET} in ${formatMs(t())}\n`,
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
module.exports = {
|
|
273
|
+
BlastRadiusEngine,
|
|
274
|
+
};
|