pkg-scaffold 3.2.0 → 3.3.2

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.
@@ -1,21 +1,35 @@
1
- import { parseSync } from 'oxc-parser';
1
+ let oxcParser;
2
+ try {
3
+ oxcParser = await import('oxc-parser');
4
+ } catch (e) {
5
+ // OXC is optional; will fall back to TypeScript in ASTAnalyzer
6
+ }
7
+
2
8
  import fs from 'fs/promises';
3
9
 
4
10
  /**
5
11
  * High-Performance AST Analyzer using OXC (Rust-based)
6
12
  * Designed to outpace Knip v6 by utilizing the fastest parser in the JS ecosystem.
13
+ * Includes automatic fallback if OXC is not available in the environment.
7
14
  */
8
15
  export class OxcAnalyzer {
9
16
  constructor(context) {
10
17
  this.context = context;
18
+ this.isAvailable = !!oxcParser;
11
19
  }
12
20
 
13
21
  async processFile(filePath, fileNode) {
22
+ if (!this.isAvailable) {
23
+ if (this.context.verbose) {
24
+ console.warn(`[OXC] Library not available, skipping fast-scan for: ${filePath}`);
25
+ }
26
+ return false;
27
+ }
28
+
14
29
  try {
15
30
  const sourceText = await fs.readFile(filePath, 'utf8');
16
31
 
17
- // OXC is significantly faster than TypeScript for single-pass analysis
18
- const { program } = parseSync(sourceText, {
32
+ const { program } = oxcParser.parseSync(sourceText, {
19
33
  sourceFilename: filePath,
20
34
  sourceType: filePath.endsWith('.ts') || filePath.endsWith('.tsx') ? 'typescript' : 'script'
21
35
  });
@@ -33,8 +47,6 @@ export class OxcAnalyzer {
33
47
  walkOxcNode(node, fileNode) {
34
48
  if (!node) return;
35
49
 
36
- // OXC AST structure is slightly different from TS
37
- // We focus on imports, exports, and namespace members
38
50
  if (node.type === 'ImportDeclaration') {
39
51
  const specifier = node.source.value;
40
52
  fileNode.explicitImports.add(specifier);
@@ -64,7 +76,6 @@ export class OxcAnalyzer {
64
76
  fileNode.internalExports.set('default', { type: 'default-export' });
65
77
  }
66
78
 
67
- // Phase 3: Namespace Member Tracking
68
79
  if (node.type === 'TSModuleDeclaration' && node.id.type === 'Identifier') {
69
80
  const namespaceName = node.id.name;
70
81
  if (node.body && node.body.type === 'TSModuleBlock') {
@@ -86,7 +97,6 @@ export class OxcAnalyzer {
86
97
  }
87
98
  }
88
99
 
89
- // Recursively walk
90
100
  for (const key in node) {
91
101
  const child = node[key];
92
102
  if (child && typeof child === 'object') {
package/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * ============================================================================
3
- * šŸ“¦ pkg-scaffold v3.1.1: Unified Architectural Refactoring Orchestrator
3
+ * šŸ“¦ pkg-scaffold v3.3.2: Unified Architectural Refactoring Orchestrator
4
4
  * ============================================================================
5
5
  * Main execution bridge managing multi-pass compilation cycles, semantic cross-linking,
6
6
  * supply-chain validation audits, and automated git self-healing rollbacks.
@@ -19,6 +19,7 @@ import { MagicDetector } from './ast/MagicDetector.js';
19
19
  import { PathMapper } from './resolution/PathMapper.js';
20
20
  import { WorkspaceGraph } from './resolution/WorkSpaceGraph.js';
21
21
  import { DependencyResolver } from './resolution/DepencyResolver.js';
22
+ import { CircularDetector } from './resolution/CircularDetector.js';
22
23
  import { TransactionManager } from './refractor/TransactionManager.js';
23
24
  import { ImpactAnalyzer } from './refractor/ImpactAnalyzer.js';
24
25
  import { SourceRewriter } from './refractor/SourceRewriter.js';
@@ -28,6 +29,7 @@ import { SelfHealer } from './healing/SelfHealer.js';
28
29
  import { IncrementalCacheManager } from './performance/GraphCache.js';
29
30
  import { WorkerPool } from './performance/WorkerPool.js';
30
31
  import { SupplyChainGuard } from './performance/SupplyChainGuard.js';
32
+ import { SecretDetector } from './performance/SecretDetector.js';
31
33
 
32
34
  /**
33
35
  * Primary Refactoring Engine Core Coordination Controller
@@ -41,6 +43,7 @@ export class RefactoringEngine {
41
43
  this.pathMapper = new PathMapper(this.context);
42
44
  this.workspaceGraph = new WorkspaceGraph(this.context);
43
45
  this.resolver = new DependencyResolver(this.context, this.pathMapper, this.workspaceGraph);
46
+ this.circularDetector = new CircularDetector(this.context);
44
47
 
45
48
  // Stage 3: Wire official AST Syntax parsers and framework processors
46
49
  this.analyzer = new ASTAnalyzer(this.context);
@@ -55,6 +58,7 @@ export class RefactoringEngine {
55
58
 
56
59
  // Stage 5: Bind security audit utilities and performance cache rings
57
60
  this.supplyChainGuard = new SupplyChainGuard(this.context);
61
+ this.secretDetector = new SecretDetector(this.context);
58
62
  this.cacheManager = new IncrementalCacheManager(this.context);
59
63
  this.workerPool = new WorkerPool(this.context);
60
64
  this.gitSandbox = new GitSandbox(this.context);
@@ -68,10 +72,13 @@ export class RefactoringEngine {
68
72
  try {
69
73
  console.log(ansis.bold.green('šŸŽÆ Starting pkg-scaffold Operational Optimization Cycle...'));
70
74
 
71
- const rl = readline.createInterface({
72
- input: process.stdin,
73
- output: process.stdout
74
- });
75
+ let rl;
76
+ if (!this.context.skipConfirm) {
77
+ rl = readline.createInterface({
78
+ input: process.stdin,
79
+ output: process.stdout
80
+ });
81
+ }
75
82
 
76
83
  // Pass 1: Boot environment contexts and load alias configuration maps
77
84
  await this.context.initialize();
@@ -103,37 +110,39 @@ export class RefactoringEngine {
103
110
  }
104
111
  }
105
112
 
113
+ // Initialize TypeScript program for AST analysis (required before processFile)
114
+ if (sourceCodeFilesList.length > 0) {
115
+ try {
116
+ this.analyzer.initProgram(sourceCodeFilesList);
117
+ } catch (e) {
118
+ if (this.context.verbose) {
119
+ console.warn('Warning: Failed to initialize TypeScript program:', e.message);
120
+ }
121
+ }
122
+ }
123
+
106
124
  // Pass 3: Process source file tokens using high-performance concurrent workers
107
125
  let parallelParseCompleted = false;
108
126
  if (sourceCodeFilesList.length > 10) {
109
127
  parallelParseCompleted = await this.workerPool.parallelAnalyzeCodebase(sourceCodeFilesList, this);
110
128
  }
111
129
 
112
- // Synchronous fallback loop to parse remaining files or cache misses.
113
- // IMPORTANT: The `parallelParseCompleted` flag only controls whether the *worker pool*
114
- // already processed all files. It must NOT be set per-file inside this loop — doing so
115
- // would cause all files after the first cache-hit to be silently skipped.
116
130
  for (const filePath of sourceCodeFilesList) {
117
131
  const node = this.context.createNode(filePath);
118
132
  const currentHash = await this.cacheManager.computeHash(filePath);
119
133
  node.contentHash = currentHash;
120
134
 
121
- // Check if this individual file is already up-to-date in the cache
122
135
  const isFileCached = cacheManifest[filePath] && cacheManifest[filePath].hash === currentHash;
123
136
 
124
137
  if (isFileCached) {
125
138
  this.context.metrics.cacheHits++;
126
139
  this.hydrateNodeFromCache(node, cacheManifest[filePath]);
127
140
  } else if (!parallelParseCompleted) {
128
- // Only parse if the worker pool did not already handle this file
129
141
  this.context.metrics.cacheMisses++;
130
142
  await this.analyzer.processFile(filePath, node);
131
143
  }
132
144
 
133
- // Apply ecosystem overrides directly to our parsed memory maps
134
145
  this.magicDetector.injectVirtualConsumerEdges(filePath, node, activeFrameworkEcosystems);
135
-
136
- // Aggregate used external packages for the dependency audit
137
146
  node.externalPackageUsage.forEach(pkg => this.context.usedExternalPackages.add(pkg));
138
147
  }
139
148
 
@@ -141,15 +150,31 @@ export class RefactoringEngine {
141
150
  console.log(ansis.dim('šŸ”— Linking graph edges and checking structural usage paths...'));
142
151
  await this.linkDependencyGraph();
143
152
 
153
+ // NEW: Circular Dependency Detection
154
+ console.log(ansis.dim('šŸ”„ Detecting circular dependencies...'));
155
+ const cycles = this.circularDetector.detectCycles(this.context.graph, this.context);
156
+ if (cycles.length > 0) {
157
+ console.warn(ansis.bold.yellow(`\nāš ļø Detected ${cycles.length} circular dependencies:`));
158
+ this.circularDetector.formatCycles().forEach(c => console.log(ansis.dim(` • ${c}`)));
159
+ }
160
+
161
+ // NEW: Secret Detection
162
+ console.log(ansis.dim('šŸ” Scanning for hardcoded secrets...'));
163
+ const detectedSecrets = await this.secretDetector.scanCodebaseForSecrets(this.context);
164
+ const secretStats = this.secretDetector.getSecretStats();
165
+
144
166
  // Pass 5: Compile metrics summary and print diagnostics report
145
167
  const analysisSummary = this.context.generateSummaryReport();
168
+ analysisSummary.detectedSecrets = detectedSecrets;
169
+ analysisSummary.secretStats = secretStats;
146
170
  this.displayConsoleDiagnostics(analysisSummary);
147
171
 
148
172
  // Pass 6: Run self-healing automated transformations if --fix is set
149
173
  if (this.context.allowAutoFix) {
150
174
  const structuralModificationsStaged =
151
175
  analysisSummary.structuralIssuesDetected.deadFiles.length > 0 ||
152
- analysisSummary.structuralIssuesDetected.deadExports.length > 0;
176
+ analysisSummary.structuralIssuesDetected.deadExports.length > 0 ||
177
+ analysisSummary.structuralIssuesDetected.unusedDependencies.length > 0;
153
178
 
154
179
  if (structuralModificationsStaged) {
155
180
  console.log(ansis.bold.yellow('\nšŸ“‹ Proposed Optimization Plan:'));
@@ -167,7 +192,7 @@ export class RefactoringEngine {
167
192
 
168
193
  if (analysisSummary.structuralIssuesDetected.unusedDependencies.length > 0) {
169
194
  console.log(ansis.bold(` šŸ“¦ Remove ${analysisSummary.structuralIssuesDetected.unusedDependencies.length} unused dependencies:`));
170
- analysisSummary.structuralIssuesDetected.unusedDependencies.forEach(d => console.log(ansis.dim(` • ${d.package} (in ${d.manifest})`)));
195
+ analysisSummary.structuralIssuesDetected.unusedDependencies.forEach(d => console.log(ansis.dim(` • ${d.package} (${d.type} in ${d.manifest})`)));
171
196
  }
172
197
  console.log(ansis.dim('------------------------------------------------------------'));
173
198
 
@@ -179,31 +204,23 @@ export class RefactoringEngine {
179
204
 
180
205
  if (proceed) {
181
206
  await this.selfHealer.runSelfHealingLifecycle(async () => {
182
- // Sub-Task A: Purge completely unreferenced dangling components
183
207
  for (const relPath of analysisSummary.structuralIssuesDetected.deadFiles) {
184
208
  const absPath = path.resolve(this.context.cwd, relPath);
185
209
  console.log(ansis.red(`āœ‚ļø Removing unreferenced file: ${relPath}`));
186
210
  await this.txManager.stageDeletion(absPath);
187
211
  }
188
212
 
189
- // Sub-Task B: Surgically remove unused named export blocks from active files
190
213
  for (const unusedExport of analysisSummary.structuralIssuesDetected.deadExports) {
191
214
  const absPath = path.resolve(this.context.cwd, unusedExport.file);
192
215
  const node = this.context.graph.get(absPath);
193
-
194
216
  if (!node) continue;
195
217
  const meta = node.internalExports.get(unusedExport.symbol);
196
218
 
197
- // Perform safety analysis to ensure the token isn't called via dynamic runtime methods
198
219
  const safetyVerdict = await this.impactAnalyzer.verifyRefactorSafety(absPath, unusedExport.symbol, this.context.graph);
199
-
200
220
  if (safetyVerdict.isSafeToPrune) {
201
221
  console.log(ansis.yellow(`⚔ Stripping unused export [${unusedExport.symbol}] from: ${unusedExport.file}:${unusedExport.line}`));
202
222
  const nextText = await this.sourceRewriter.stripNamedExportSignature(absPath, unusedExport.symbol, meta);
203
-
204
223
  await this.txManager.stageWrite(absPath, nextText);
205
-
206
- // Align matching type declaration boundaries (.d.ts) to prevent compilation errors
207
224
  await this.typeIntegrity.synchronizeDeclarationFile(absPath, unusedExport.symbol);
208
225
  } else if (this.context.verbose) {
209
226
  console.log(ansis.gray(`šŸ›”ļø Preserving symbol export [${unusedExport.symbol}] due to: ${safetyVerdict.blockReason}`));
@@ -216,9 +233,8 @@ export class RefactoringEngine {
216
233
  }
217
234
  }
218
235
 
219
- // Pass 7: Save optimized graph footprints back to the cache directory
220
236
  await this.cacheManager.saveCacheManifest(this.context.graph);
221
- rl.close();
237
+ if (rl) rl.close();
222
238
  console.log(ansis.bold.green('\n✨ Core optimization cycle completed smoothly. Codebase workspace is healthy.'));
223
239
 
224
240
  } catch (criticalFault) {
@@ -228,13 +244,24 @@ export class RefactoringEngine {
228
244
  }
229
245
  }
230
246
 
231
- /**
232
- * Links nodes and processes barrel files recursively without regular expressions.
233
- */
247
+ async discoverSourceFiles(dir, fileList) {
248
+ const entries = await fs.readdir(dir, { withFileTypes: true });
249
+ for (const entry of entries) {
250
+ const res = path.resolve(dir, entry.name);
251
+ if (entry.isDirectory()) {
252
+ if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === '.scaffold-cache') continue;
253
+ await this.discoverSourceFiles(res, fileList);
254
+ } else {
255
+ const ext = path.extname(entry.name);
256
+ if (['.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte'].includes(ext) || entry.name === 'package.json') {
257
+ fileList.push(res);
258
+ }
259
+ }
260
+ }
261
+ }
262
+
234
263
  async linkDependencyGraph() {
235
264
  for (const [filePath, node] of this.context.graph.entries()) {
236
-
237
- // Connect standard module imports
238
265
  for (const specifier of node.explicitImports) {
239
266
  const resolvedPath = this.resolver.resolveModulePath(filePath, specifier);
240
267
  if (resolvedPath && this.context.graph.has(resolvedPath)) {
@@ -243,23 +270,14 @@ export class RefactoringEngine {
243
270
  }
244
271
  }
245
272
 
246
- // Unroll re-exports and unwrap barrel file links recursively
247
273
  for (const specToken of node.importedSymbols) {
248
274
  const delimiterIndex = specToken.indexOf(':');
249
275
  if (delimiterIndex === -1) continue;
250
-
251
276
  const specifier = specToken.slice(0, delimiterIndex);
252
277
  const symbol = specToken.slice(delimiterIndex + 1);
253
-
254
278
  const resolvedPath = this.resolver.resolveModulePath(filePath, specifier);
255
-
256
279
  if (resolvedPath && symbol !== '*') {
257
- const traceResolution = await this.barrelParser.determineSymbolDeclarationOrigin(
258
- resolvedPath,
259
- symbol,
260
- this.context.graph
261
- );
262
-
280
+ const traceResolution = await this.barrelParser.determineSymbolDeclarationOrigin(resolvedPath, symbol, this.context.graph);
263
281
  if (traceResolution && this.context.graph.has(traceResolution.originFile)) {
264
282
  this.context.graph.get(traceResolution.originFile).incomingEdges.add(filePath);
265
283
  node.outgoingEdges.add(traceResolution.originFile);
@@ -269,142 +287,95 @@ export class RefactoringEngine {
269
287
  }
270
288
  }
271
289
 
272
- /**
273
- * Audits package json files using token equality checks instead of fragile regex searches.
274
- */
275
290
  async auditManifestSupplyChain(packageJsonPath) {
276
291
  try {
277
292
  const text = await fs.readFile(packageJsonPath, 'utf8');
278
293
  const data = JSON.parse(text);
279
-
280
294
  const prodDeps = Object.keys(data.dependencies || {});
281
295
  const devDeps = Object.keys(data.devDependencies || {});
282
296
  const totalDependencies = [...prodDeps, ...devDeps];
283
297
 
284
- // Register dependencies for later audit
285
298
  this.context.manifestDependencies.set(packageJsonPath, {
286
299
  dependencies: prodDeps,
287
- devDependencies: devDeps
300
+ devDependencies: devDeps,
301
+ peerDependencies: Object.keys(data.peerDependencies || {}),
302
+ optionalDependencies: Object.keys(data.optionalDependencies || {})
288
303
  });
289
304
 
290
- // Pass dependencies down to our Levenshtein string-distance guard to find typosquat targets
291
305
  const supplyChainThreats = this.supplyChainGuard.detectTyposquattingAnomalies(totalDependencies);
292
-
293
306
  for (const anomaly of supplyChainThreats) {
294
307
  console.warn(ansis.bold.red(`🚨 Supply Chain Alert: Malicious package masking candidate discovered [${anomaly.maliciousCandidate}]. Mimics trusted library [${anomaly.targetMimicked}].`));
295
308
  }
296
-
297
- // Verify lockfile hash configurations to catch poisoned manifests
298
309
  await this.supplyChainGuard.verifyIntegrityLockfileHashes(packageJsonPath);
299
- } catch {
300
- // Manifest unreadable or locked; skip gracefully
301
- }
310
+ } catch {}
302
311
  }
303
312
 
304
- hydrateNodeFromCache(node, cachedRecord) {
305
- cachedRecord.explicitImports.forEach(i => node.explicitImports.add(i));
306
- cachedRecord.dynamicImports.forEach(i => node.dynamicImports.add(i));
307
- cachedRecord.importedSymbols.forEach(s => node.importedSymbols.add(s));
308
- cachedRecord.rawStringReferences.forEach(r => node.rawStringReferences.add(r));
309
- cachedRecord.instantiatedIdentifiers.forEach(i => node.instantiatedIdentifiers.add(i));
310
- cachedRecord.propertyAccessChains.forEach(c => node.propertyAccessChains.add(c));
311
-
312
- if (cachedRecord.internalExports) {
313
- Object.entries(cachedRecord.internalExports).forEach(([k, v]) => {
314
- node.internalExports.set(k, v);
315
- });
316
- }
317
- node.isLibraryEntry = cachedRecord.isLibraryEntry || false;
318
- node.securityThreats = cachedRecord.securityThreats || [];
319
- if (cachedRecord.localSuppressedRules) {
320
- cachedRecord.localSuppressedRules.forEach(r => node.localSuppressedRules.add(r));
321
- }
322
- if (cachedRecord.symbolSourceLocations) {
323
- Object.entries(cachedRecord.symbolSourceLocations).forEach(([k, v]) => {
324
- node.symbolSourceLocations.set(k, v);
325
- });
326
- }
327
- if (cachedRecord.externalPackageUsage) {
328
- cachedRecord.externalPackageUsage.forEach(p => node.externalPackageUsage.add(p));
329
- }
330
- }
313
+ displayConsoleDiagnostics(summary) {
314
+ console.log(ansis.bold.cyan('\nšŸ“Š Operational Diagnostics Summary:'));
315
+ console.log(ansis.dim('------------------------------------------------------------'));
316
+ console.log(`ā±ļø Duration : ${summary.executionDuration}`);
317
+ console.log(`šŸ“ Files Processed : ${summary.totalFilesProcessed}`);
318
+ console.log(`🧠 Cache Hit Ratio : ${summary.graphCacheOptimization.ratio}`);
319
+ console.log(ansis.dim('------------------------------------------------------------'));
331
320
 
332
- /**
333
- * Crawls project roots using precise token extension checking instead of high-risk text matching.
334
- */
335
- async discoverSourceFiles(currentDirectory, fileAccumulator) {
336
- try {
337
- const entries = await fs.readdir(currentDirectory, { withFileTypes: true });
338
-
339
- for (const entry of entries) {
340
- const absolutePath = path.join(currentDirectory, entry.name);
341
-
342
- if (entry.isDirectory()) {
343
- // Standard systemic exclusions filters
344
- if (entry.name === 'node_modules' ||
345
- entry.name === '.git' ||
346
- entry.name === '.scaffold-cache' ||
347
- entry.name === 'dist' ||
348
- entry.name === 'build') {
349
- continue;
350
- }
351
- await this.discoverSourceFiles(absolutePath, fileAccumulator);
352
- } else if (entry.isFile()) {
353
- const extension = path.extname(entry.name);
354
- if (extension === '.js' ||
355
- extension === '.ts' ||
356
- extension === '.tsx' ||
357
- extension === '.jsx' ||
358
- entry.name === 'package.json') {
359
- fileAccumulator.push(absolutePath);
360
- }
361
- }
362
- }
363
- } catch {
364
- // Path unreadable or access locked; close loop gracefully
321
+ if (summary.structuralIssuesDetected.deadFiles.length > 0) {
322
+ console.log(ansis.bold.red(`\nšŸ’€ Dead Files Detected (${summary.structuralIssuesDetected.deadFiles.length}):`));
323
+ summary.structuralIssuesDetected.deadFiles.forEach(f => console.log(ansis.dim(` • ${f}`)));
365
324
  }
366
- }
367
325
 
368
- displayConsoleDiagnostics(report) {
369
- console.log(ansis.bold.green('\nšŸ“Š Codebase Structural Diagnostics Summary'));
370
- console.log(ansis.dim('============================================================'));
371
- console.log(`${ansis.bold('Processing Cycle Wall Duration:')} ${ansis.cyan(report.executionDuration)}`);
372
- console.log(`${ansis.bold('Total Files Indexed Globally :')} ${ansis.white(report.totalFilesProcessed)}`);
373
- console.log(`${ansis.bold('Delta Cache Optimization Ratio:')} ${ansis.green(report.graphCacheOptimization.ratio)} (Hits: ${report.graphCacheOptimization.hits} / Misses: ${report.graphCacheOptimization.misses})`);
374
- console.log(ansis.dim('------------------------------------------------------------'));
375
-
376
- if (report.structuralIssuesDetected.deadFiles.length > 0) {
377
- console.log(ansis.bold.red(`\nOrphaned Files Flagged (${report.structuralIssuesDetected.deadFiles.length}):`));
378
- report.structuralIssuesDetected.deadFiles.forEach(f => console.log(ansis.dim(` • ${f}`)));
379
- } else {
380
- console.log(ansis.green('\n✨ No orphaned or unreferenced component files found.'));
326
+ if (summary.structuralIssuesDetected.deadExports.length > 0) {
327
+ console.log(ansis.bold.red(`\nāœ‚ļø Dead Exports Detected (${summary.structuralIssuesDetected.deadExports.length}):`));
328
+ summary.structuralIssuesDetected.deadExports.forEach(e => console.log(ansis.dim(` • ${e.symbol} in ${e.file}:${e.line}`)));
381
329
  }
382
330
 
383
- if (report.structuralIssuesDetected.deadExports.length > 0) {
384
- console.log(ansis.bold.yellow(`\nUnused Named Symbol Exports Flagged (${report.structuralIssuesDetected.deadExports.length}):`));
385
- report.structuralIssuesDetected.deadExports.forEach(e => {
386
- console.log(` • ${ansis.dim(e.file)}:${e.line}:${e.column} -> [${ansis.yellow(e.symbol)}] (Type: ${e.type})`);
387
- });
388
- } else {
389
- console.log(ansis.green('✨ No dead or unused named symbols exported across components.'));
331
+ if (summary.structuralIssuesDetected.unusedDependencies.length > 0) {
332
+ console.log(ansis.bold.red(`\nšŸ“¦ Unused Dependencies (${summary.structuralIssuesDetected.unusedDependencies.length}):`));
333
+ summary.structuralIssuesDetected.unusedDependencies.forEach(d => console.log(ansis.dim(` • ${d.package} (${d.type} in ${d.manifest})`)));
390
334
  }
391
335
 
392
- if (report.structuralIssuesDetected.securityThreats.length > 0) {
393
- console.log(ansis.bold.red(`\nāš ļø High-Risk Variable Assignment Alerts (${report.structuralIssuesDetected.securityThreats.length}):`));
394
- report.structuralIssuesDetected.securityThreats.forEach(threat => {
395
- console.log(` • ${ansis.red(threat.file)}:${threat.line} -> Variable [${ansis.bold(threat.identifier)}] contains a high-entropy secret (Shannon Score: ${threat.entropy})`);
396
- });
336
+ if (summary.structuralIssuesDetected.securityThreats.length > 0) {
337
+ console.log(ansis.bold.red(`
338
+ šŸ›”ļø Security Threats (${summary.structuralIssuesDetected.securityThreats.length}):`));
339
+ summary.structuralIssuesDetected.securityThreats.forEach(t => console.log(ansis.dim(` • ${t.identifier} in ${t.file}:${t.line} (Risk: ${t.riskCode})`)));
397
340
  }
398
341
 
399
- if (report.structuralIssuesDetected.unusedDependencies.length > 0) {
400
- console.log(ansis.bold.red(`\nUnused External Dependencies Flagged (${report.structuralIssuesDetected.unusedDependencies.length}):`));
401
- report.structuralIssuesDetected.unusedDependencies.forEach(d => {
402
- console.log(` • Package [${ansis.red(d.package)}] in ${ansis.dim(d.manifest)}`);
403
- });
404
- } else {
405
- console.log(ansis.green('\n✨ No unused external dependencies found in manifest files.'));
342
+ // Display detected secrets
343
+ if (summary.detectedSecrets && summary.detectedSecrets.length > 0) {
344
+ const criticalSecrets = summary.detectedSecrets.filter(s => s.severity === 'CRITICAL');
345
+ const highSecrets = summary.detectedSecrets.filter(s => s.severity === 'HIGH');
346
+
347
+ console.log(ansis.bold.red(`
348
+ šŸ” Hardcoded Secrets Detected (${summary.detectedSecrets.length}):`));
349
+
350
+ if (criticalSecrets.length > 0) {
351
+ console.log(ansis.bold.red(` CRITICAL (${criticalSecrets.length}):`));
352
+ criticalSecrets.forEach(s => {
353
+ const relPath = s.file.split(/[\\/]/).slice(-2).join('/');
354
+ console.log(ansis.dim(` • ${s.type} in ${relPath}:${s.line} [${s.variable}]`));
355
+ });
356
+ }
357
+
358
+ if (highSecrets.length > 0) {
359
+ console.log(ansis.bold.yellow(` HIGH (${highSecrets.length}):`));
360
+ highSecrets.forEach(s => {
361
+ const relPath = s.file.split(/[\\/]/).slice(-2).join('/');
362
+ console.log(ansis.dim(` • ${s.type} in ${relPath}:${s.line} [${s.variable}]`));
363
+ });
364
+ }
406
365
  }
407
366
 
408
- console.log(ansis.dim('\n============================================================\n'));
367
+ console.log(ansis.dim('\n------------------------------------------------------------\n'));
368
+ }
369
+
370
+ hydrateNodeFromCache(node, cachedRecord) {
371
+ if (cachedRecord.explicitImports) cachedRecord.explicitImports.forEach(i => node.explicitImports.add(i));
372
+ if (cachedRecord.dynamicImports) cachedRecord.dynamicImports.forEach(i => node.dynamicImports.add(i));
373
+ if (cachedRecord.importedSymbols) cachedRecord.importedSymbols.forEach(s => node.importedSymbols.add(s));
374
+ if (cachedRecord.internalExports) {
375
+ Object.entries(cachedRecord.internalExports).forEach(([k, v]) => node.internalExports.set(k, v));
376
+ }
377
+ if (cachedRecord.symbolSourceLocations) {
378
+ Object.entries(cachedRecord.symbolSourceLocations).forEach(([k, v]) => node.symbolSourceLocations.set(k, v));
379
+ }
409
380
  }
410
381
  }