entkapp 5.3.1 β†’ 5.4.0

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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # πŸ•ΈοΈ entkapp Ultimate v5.3.1
1
+ # πŸ•ΈοΈ entkapp Ultimate v5.4.0
2
2
 
3
3
  > **The Ultimate Enterprise Codebase Janitor.** High-speed OXC integration, type-aware analysis, and automated structural healing. Fully standalone architectural orchestrator.
4
4
 
@@ -35,12 +35,6 @@ Starts the interactive analysis:
35
35
  npx entkapp -r
36
36
  ```
37
37
 
38
- ### Headless Analysis Mode
39
- Ideal for CI/CD pipelines. Performs analysis without prompts and outputs JSON:
40
- ```bash
41
- npx entkapp --analyze
42
- ```
43
-
44
38
  ### Auto-Fix Mode
45
39
  Automatically resolves dependency conflicts and structural issues:
46
40
  ```bash
package/bin/cli.js CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  /**
4
4
  * ============================================================================
5
- * 🏁 loui CLI Entry Point
5
+ * πŸš€ entkapp: The High-Performance Codebase Orchestrator
6
6
  * ============================================================================
7
- * Handles option compilation, environment orchestration, option validation,
8
- * and initiates the primary operational pipeline loop.
7
+ * Command-line interface for executing architectural audits, structural
8
+ * refactoring cycles, and autonomous codebase healing.
9
9
  */
10
10
 
11
11
  import { Command } from 'commander';
@@ -13,163 +13,104 @@ import ansis from 'ansis';
13
13
  import path from 'path';
14
14
  import fs from 'fs/promises';
15
15
  import { fileURLToPath } from 'url';
16
- import readline from 'readline/promises';
16
+ import { ConfigLoader } from '../src/resolution/ConfigLoader.js';
17
17
 
18
- const __filename = fileURLToPath(import.meta.url);
19
- const __dirname = path.dirname(__filename);
18
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
+ const pkg = JSON.parse(await fs.readFile(path.join(__dirname, '../package.json'), 'utf8'));
20
20
 
21
21
  const program = new Command();
22
22
 
23
- async function bootstrap() {
23
+ async function main() {
24
24
  try {
25
- const packageJsonPath = path.resolve(__dirname, '../package.json');
26
- const packageJsonContent = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
27
-
28
25
  program
29
26
  .name('entkapp')
30
- .description(ansis.cyan('The Ultimate Enterprise Codebase Janitor with OXC integration, type-aware analysis, and automated structural healing.'))
31
- .version(packageJsonContent.version || '5.3.1');
32
-
33
- program
34
- .option('-c, --cwd <path>', 'Specify the execution context root directory', process.cwd())
35
- .option('-d, --debug', 'Developer`s comprehensive telemetry debug diagnostics', false)
36
- .option('--fix', 'Enable atomic code updates, structural file pruning, and active type sanitization', false)
27
+ .description('Autonomous architectural refactoring and codebase optimization engine')
28
+ .version(pkg.version)
29
+ .argument('[path]', 'Target directory for codebase analysis and refactoring', '.')
30
+ .option('-f, --fix', 'Automatically apply identified structural optimizations and healing', false)
37
31
  .option('--tsconfig <filename>', 'Specify path to custom layout configurations', 'tsconfig.json')
38
32
  .option('--test-command <command>', 'Integrated continuous safety test validation script execution path', 'npm test')
39
33
  .option('--workspace', 'Enable high-density workspace workspace/monorepo cluster mesh evaluation parsing', false)
40
34
  .option('--verbose', 'Toggle expanded trace telemetry for debug operational diagnostics', false)
41
35
  .option('--visualize', 'Generate an interactive execution graph visualization', false)
36
+ .option('-ef, --entry-file <path>', 'Manually specify a primary entry file for analysis')
42
37
  .option('-r, --run', 'Execute the primary operational pipeline loop', false)
43
38
  .option('-y, --yes', 'Skip confirmation prompts and execute planned structural modifications automatically', false)
44
39
  .option('--timeout <ms>', 'Set execution timeout in milliseconds', '30000');
45
40
 
46
41
  program.parse(process.argv);
47
- const options = program.opts();
48
-
49
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
50
-
51
- // --- Onboarding Check (Skipped in Non-Interactive Mode) ---
52
- // FIX: Ensure options.cwd is always a string, never undefined
53
- const targetCwd = path.resolve(options.cwd || process.cwd());
54
- const pkgJsonPath = path.join(targetCwd, 'package.json');
55
- const configDirPath = path.join(targetCwd, 'entkapp');
56
-
57
- let pkgJson;
58
- try {
59
- pkgJson = JSON.parse(await fs.readFile(pkgJsonPath, 'utf8'));
60
- } catch (e) {}
61
-
62
- let configInstalled = false;
63
- if (!options.yes && !options.run) {
64
- // 1. Ask to install script
65
- if (pkgJson && !pkgJson.scripts?.['entkapp:run']) {
66
- const answer = await rl.question(ansis.bold.yellow('❓ No "entkapp:run" script found in package.json. Install it? (y/n): '));
67
- if (answer.toLowerCase() === 'y') {
68
- pkgJson.scripts = pkgJson.scripts || {};
69
- pkgJson.scripts['entkapp:run'] = 'npx entkapp --fix';
70
- await fs.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
71
- console.log(ansis.green('βœ… "entkapp:run" script added to package.json.'));
72
- }
73
- }
74
-
75
- // 2. Ask to install config folder
76
- try {
77
- await fs.access(configDirPath);
78
- configInstalled = true;
79
- } catch (e) {
80
- const answer = await rl.question(ansis.bold.yellow('❓ No "/entkapp" configuration folder found. Create it with defaults? (y/n): '));
81
- if (answer.toLowerCase() === 'y') {
82
- await fs.mkdir(configDirPath, { recursive: true });
83
- await fs.mkdir(path.join(configDirPath, 'plugins'), { recursive: true });
84
- const defaultConfig = {
85
- interface: "CLI",
86
- useBuiltinPlugins: true,
87
- useCustomPlugins: true,
88
-
89
- options: { verbose: false, fastMode: true, selfHealing: true },
90
- enabledPlugins: ["nextjs", "nuxt", "remix", "sveltekit", "astro"]
91
- };
92
- await fs.writeFile(path.join(configDirPath, 'config.json'), JSON.stringify(defaultConfig, null, 2));
93
- console.log(ansis.green('βœ… "/entkapp" folder? and default config created.'));
94
- configInstalled = true;
95
- }
96
- }
97
-
98
- if (pkgJson?.scripts?.['entkapp:run'] || configInstalled) {
99
- console.log(ansis.bold.cyan('\nπŸš€ Setup complete! To start the engine, run:'));
100
- console.log(ansis.white(` - npx entkapp -r`));
101
- console.log(ansis.white(` - npm run entkapp:run\n`));
102
- }
103
- }
104
42
 
105
- rl.close();
106
-
107
- // Load local config if available
108
- let localConfig = {};
109
- try {
110
- const { ConfigLoader } = await import('../src/resolution/ConfigLoader.js');
111
- const loader = new ConfigLoader(targetCwd);
112
- localConfig = await loader.loadConfig();
113
- } catch (e) {}
114
-
115
- // Merge options with local config
116
- const finalInterface = localConfig.interface || 'CLI';
117
- if (finalInterface === 'GUI') {
118
- console.log(ansis.bold.magenta('🎨 GUI Mode Detected. Starting Web Interface...'));
119
- return;
120
- }
121
-
122
- // Only proceed to execution if -r/--run is provided
123
- if (!options.run) {
124
- return;
43
+ const options = program.opts();
44
+ const targetCwd = path.resolve(process.cwd(), program.args[0] || '.');
45
+
46
+ // Load configuration
47
+ const configLoader = new ConfigLoader(targetCwd);
48
+ const localConfig = await configLoader.loadConfig(options);
49
+
50
+ if (options.verbose) {
51
+ const summary = {
52
+ packages: localConfig.workspacePackages?.length || 0,
53
+ tsconfigs: localConfig.workspaceTsConfigs?.length || 0,
54
+ configs: localConfig.workspaceConfigFiles?.length || 0
55
+ };
56
+ console.log(ansis.dim(`[ConfigLoader] Monorepo detected – ${summary.packages} package(s), ${summary.tsconfigs} tsconfig(s), ${summary.configs} *.config file(s) loaded`));
125
57
  }
126
58
 
127
- // --- Timeout Handling ---
128
- const timeoutMs = parseInt(options.timeout);
129
59
  const timeoutTimer = setTimeout(() => {
130
- console.error(ansis.bold.red(`\n🚨 Execution Timeout: The process exceeded the limit of ${timeoutMs}ms.`));
60
+ console.error(ansis.red(`\n❌ Execution Timeout: Operation exceeded ${options.timeout}ms limit.`));
131
61
  process.exit(1);
132
- }, timeoutMs);
133
- timeoutTimer.unref(); // Allow process to exit if work finishes
62
+ }, parseInt(options.timeout));
134
63
 
135
- console.log(ansis.bold.green(`\nπŸ“¦ entkapp v${packageJsonContent.version || '5.3.1'} Engine Activation`));
64
+ console.log('\n' + ansis.bold.cyan(`πŸ“¦ entkapp v${pkg.version} Engine Activation`));
136
65
  console.log(ansis.dim('------------------------------------------------------------'));
137
- console.log(`${ansis.bold('Target Workspace Root :')} ${ansis.blue(targetCwd)}`);
138
- console.log(`${ansis.bold('Refactoring Mode :')} ${options.fix ? ansis.yellow('Active Fixing & Self-Healing Enabled') : ansis.gray('Dry-Run Reporting Only')}`);
139
- console.log(`${ansis.bold('Validation Sandbox :')} ${ansis.magenta(options.testCommand)}`);
66
+ console.log(`${ansis.bold('Target Workspace Root')} : ${targetCwd}`);
67
+ console.log(`${ansis.bold('Refactoring Mode')} : ${options.fix ? ansis.yellow('Automated Healing Active') : 'Dry-Run Reporting Only'}`);
68
+ console.log(`${ansis.bold('Validation Sandbox')} : ${options.testCommand}`);
140
69
  console.log(ansis.dim('------------------------------------------------------------\n'));
141
70
 
142
71
  const { RefactoringEngine } = await import('../src/index.js');
143
72
 
73
+ // Prepare entry points - EXTREMELY ROBUST ARRAY HANDLING
74
+ let entryPoints = [];
75
+ if (localConfig.entryPoints && typeof localConfig.entryPoints[Symbol.iterator] === 'function') {
76
+ entryPoints = [...localConfig.entryPoints];
77
+ } else if (localConfig.entryPoints) {
78
+ entryPoints = [localConfig.entryPoints];
79
+ }
80
+
81
+ if (options.entryFile) {
82
+ const absEntry = path.resolve(targetCwd, options.entryFile).replace(/\\/g, '/');
83
+ if (!entryPoints.includes(absEntry)) {
84
+ entryPoints.push(absEntry);
85
+ }
86
+ }
87
+
144
88
  const engine = new RefactoringEngine({
145
89
  cwd: targetCwd,
146
90
  autoFix: options.fix,
147
91
  tsconfig: options.tsconfig,
148
92
  testCommand: options.testCommand,
149
- workspace: options.workspace,
93
+ workspace: options.workspace || localConfig.workspace || false,
150
94
  verbose: options.verbose,
151
95
  skipConfirm: options.yes,
152
- // Pass through local config settings
153
- entryPoints: localConfig.entryPoints,
154
- exclude: localConfig.exclude,
155
- rules: localConfig.rules,
96
+ entryPoints: entryPoints,
97
+ exclude: localConfig.exclude || [],
98
+ rules: localConfig.rules || {},
156
99
  debug: options.debug,
157
100
  visualize: options.visualize,
101
+ workspacePackages: localConfig.workspacePackages || [],
102
+ workspaceTsConfigs: localConfig.workspaceTsConfigs || [],
103
+ workspaceConfigFiles: localConfig.workspaceConfigFiles || [],
158
104
  });
159
105
 
160
106
  await engine.run();
161
107
 
162
108
  clearTimeout(timeoutTimer);
163
- console.log(ansis.bold.green('\n✨ Core cycle execution completed successfully. Structural layout is clean.'));
164
- process.exit(0);
165
-
166
- } catch (criticalBootError) {
167
- console.error(ansis.bold.red(`\n🚨 Critical Lifecycle Boot Instability: ${criticalBootError.message}`));
168
- if (criticalBootError.stack) {
169
- console.error(ansis.dim(criticalBootError.stack));
170
- }
109
+ } catch (err) {
110
+ console.error(ansis.red(`\n❌ Engine Failure: ${err.message}`));
111
+ if (program.opts().verbose) console.error(err);
171
112
  process.exit(1);
172
113
  }
173
114
  }
174
115
 
175
- bootstrap();
116
+ main();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "name": "entkapp",
4
- "version": "5.3.1",
4
+ "version": "5.4.0",
5
5
  "description": "The Ultimate Enterprise Codebase Janitor. High-speed OXC integration, type-aware analysis, and automated structural healing. Fully standalone architectural orchestrator.",
6
6
  "type": "module",
7
7
  "main": "index.js",
@@ -419,6 +419,12 @@ export class EngineContext {
419
419
  report.importedUnusedPackages = Array.from(this.importedUnusedPackages);
420
420
  report.unusedBinaries = Array.from(this.unusedBinaries);
421
421
 
422
+ // UPGRADE 5.4.3: Integrate enhanced results from GraphAnalyzer
423
+ const analyzer = new (await import('./resolution/GraphAnalyzer.js')).GraphAnalyzer(this);
424
+ const { unusedImports, boundaryViolations } = await analyzer.findDeadCode();
425
+ report.unusedImports = unusedImports;
426
+ report.boundaryViolations = boundaryViolations;
427
+
422
428
  return report;
423
429
  }
424
430
  }
@@ -265,7 +265,16 @@ export class ASTAnalyzer {
265
265
  fileNode.explicitImports.add(specifier);
266
266
  fileNode.importedSymbols.add(`${specifier}:*`);
267
267
  if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
268
- fileNode.externalPackageUsage.add(this._extractPackageName(specifier));
268
+ // UPGRADE: Apply alias/workspace filtering before adding to externalPackageUsage
269
+ const pkgNameReq = this._extractPackageName(specifier);
270
+ let isInternalReq = false;
271
+ if (this.context.pathMapper && typeof this.context.pathMapper.isTsconfigAlias === 'function') {
272
+ if (this.context.pathMapper.isTsconfigAlias(specifier) || this.context.pathMapper.isTsconfigAlias(pkgNameReq)) isInternalReq = true;
273
+ }
274
+ if (!isInternalReq && this.context.workspaceGraph && typeof this.context.workspaceGraph.isLocalWorkspaceSpecifier === 'function') {
275
+ if (this.context.workspaceGraph.isLocalWorkspaceSpecifier(specifier) || this.context.workspaceGraph.isLocalWorkspaceSpecifier(pkgNameReq)) isInternalReq = true;
276
+ }
277
+ if (!isInternalReq) fileNode.externalPackageUsage.add(pkgNameReq);
269
278
  }
270
279
  } else if (arg && ts.isTemplateExpression(arg)) {
271
280
  // Dynamic require with template literal
@@ -351,8 +360,40 @@ export class ASTAnalyzer {
351
360
  const specifier = node.moduleSpecifier.text;
352
361
  fileNode.explicitImports.add(specifier);
353
362
 
363
+ // UPGRADE 5.4.2: Distinguish between local aliases and workspace packages
354
364
  if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
355
- fileNode.externalPackageUsage.add(this._extractPackageName(specifier));
365
+ let isInternalAlias = false;
366
+ const pkgName = this._extractPackageName(specifier);
367
+
368
+ // UPGRADE: First check via isTsconfigAlias() β€” works even when the target file doesn't exist yet
369
+ if (this.context.pathMapper && typeof this.context.pathMapper.isTsconfigAlias === 'function') {
370
+ if (this.context.pathMapper.isTsconfigAlias(specifier) || this.context.pathMapper.isTsconfigAlias(pkgName)) {
371
+ isInternalAlias = true;
372
+ }
373
+ }
374
+
375
+ // UPGRADE: Check workspace packages (pnpm-workspace.yaml / package.json workspaces)
376
+ if (!isInternalAlias && this.context.workspaceGraph && typeof this.context.workspaceGraph.isLocalWorkspaceSpecifier === 'function') {
377
+ if (this.context.workspaceGraph.isLocalWorkspaceSpecifier(specifier) || this.context.workspaceGraph.isLocalWorkspaceSpecifier(pkgName)) {
378
+ isInternalAlias = true;
379
+ }
380
+ }
381
+
382
+ // Fallback: try resolvePath() β€” works when the target file exists on disk
383
+ if (!isInternalAlias && this.context.pathMapper && typeof this.context.pathMapper.resolvePath === 'function') {
384
+ const resolved = this.context.pathMapper.resolvePath(specifier);
385
+ if (resolved !== specifier && (resolved.startsWith('/') || resolved.includes(':'))) {
386
+ // Only mark as internal alias if it's NOT a registered workspace package
387
+ const isWorkspacePkg = this.context.workspaceGraph && this.context.workspaceGraph.isLocalWorkspaceSpecifier(pkgName);
388
+ if (!isWorkspacePkg) {
389
+ isInternalAlias = true;
390
+ }
391
+ }
392
+ }
393
+
394
+ if (!isInternalAlias) {
395
+ fileNode.externalPackageUsage.add(pkgName);
396
+ }
356
397
  }
357
398
 
358
399
  if (this.context.workspaceGraph && typeof this.context.workspaceGraph.auditImportSpecifier === 'function') {
@@ -373,14 +414,29 @@ export class ASTAnalyzer {
373
414
  if (ts.isNamespaceImport(node.importClause.namedBindings)) {
374
415
  fileNode.importedSymbols.add(`${specifier}:*`);
375
416
  if (targetFile) this.context.importUsageRegistry.add(`${targetFile}:*`);
417
+
418
+ // Track local name for unused import detection
419
+ if (!fileNode.localImportBindings) fileNode.localImportBindings = new Map();
420
+ fileNode.localImportBindings.set(node.importClause.namedBindings.name.text, { specifier, originalName: '*' });
376
421
  } else if (ts.isNamedImports(node.importClause.namedBindings)) {
377
422
  node.importClause.namedBindings.elements.forEach(element => {
378
423
  const importedName = element.propertyName ? element.propertyName.text : element.name.text;
424
+ const localName = element.name.text;
379
425
  fileNode.importedSymbols.add(`${specifier}:${importedName}`);
380
426
  if (targetFile) this.context.importUsageRegistry.add(`${targetFile}:${importedName}`);
427
+
428
+ // Track local name for unused import detection
429
+ if (!fileNode.localImportBindings) fileNode.localImportBindings = new Map();
430
+ fileNode.localImportBindings.set(localName, { specifier, originalName: importedName });
381
431
  });
382
432
  }
383
433
  }
434
+
435
+ // Handle default import binding
436
+ if (node.importClause.name) {
437
+ if (!fileNode.localImportBindings) fileNode.localImportBindings = new Map();
438
+ fileNode.localImportBindings.set(node.importClause.name.text, { specifier, originalName: 'default' });
439
+ }
384
440
  } else {
385
441
  // Side-Effect Import (import '...')
386
442
  // FIX: Mark the target file as used even if no symbols are imported
@@ -423,7 +479,22 @@ export class ASTAnalyzer {
423
479
  if (specifier) {
424
480
  fileNode.explicitImports.add(specifier);
425
481
  if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
426
- fileNode.externalPackageUsage.add(this._extractPackageName(specifier));
482
+ // UPGRADE: Apply the same alias/workspace filtering as handleImportDeclaration
483
+ const pkgNameExport = this._extractPackageName(specifier);
484
+ let isInternalAliasExport = false;
485
+ if (this.context.pathMapper && typeof this.context.pathMapper.isTsconfigAlias === 'function') {
486
+ if (this.context.pathMapper.isTsconfigAlias(specifier) || this.context.pathMapper.isTsconfigAlias(pkgNameExport)) {
487
+ isInternalAliasExport = true;
488
+ }
489
+ }
490
+ if (!isInternalAliasExport && this.context.workspaceGraph && typeof this.context.workspaceGraph.isLocalWorkspaceSpecifier === 'function') {
491
+ if (this.context.workspaceGraph.isLocalWorkspaceSpecifier(specifier) || this.context.workspaceGraph.isLocalWorkspaceSpecifier(pkgNameExport)) {
492
+ isInternalAliasExport = true;
493
+ }
494
+ }
495
+ if (!isInternalAliasExport) {
496
+ fileNode.externalPackageUsage.add(pkgNameExport);
497
+ }
427
498
  }
428
499
 
429
500
  if (!node.exportClause) {
@@ -587,7 +658,16 @@ export class ASTAnalyzer {
587
658
  fileNode.dynamicImports.add(specifier);
588
659
  fileNode.importedSymbols.add(`${specifier}:*`);
589
660
  if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
590
- fileNode.externalPackageUsage.add(this._extractPackageName(specifier));
661
+ // UPGRADE: Apply alias/workspace filtering before adding to externalPackageUsage
662
+ const pkgNameDyn = this._extractPackageName(specifier);
663
+ let isInternalDyn = false;
664
+ if (this.context.pathMapper && typeof this.context.pathMapper.isTsconfigAlias === 'function') {
665
+ if (this.context.pathMapper.isTsconfigAlias(specifier) || this.context.pathMapper.isTsconfigAlias(pkgNameDyn)) isInternalDyn = true;
666
+ }
667
+ if (!isInternalDyn && this.context.workspaceGraph && typeof this.context.workspaceGraph.isLocalWorkspaceSpecifier === 'function') {
668
+ if (this.context.workspaceGraph.isLocalWorkspaceSpecifier(specifier) || this.context.workspaceGraph.isLocalWorkspaceSpecifier(pkgNameDyn)) isInternalDyn = true;
669
+ }
670
+ if (!isInternalDyn) fileNode.externalPackageUsage.add(pkgNameDyn);
591
671
  }
592
672
  } else {
593
673
  // Non-literal dynamic import
@@ -608,9 +688,17 @@ export class ASTAnalyzer {
608
688
  if (this.context.verbose) console.log(`[AST-DEBUG] Added import: ${specifier}`);
609
689
  fileNode.explicitImports.add(specifier);
610
690
 
611
- // UPGRADE: Also track package usage for unlisted dependency detection
691
+ // UPGRADE: Apply alias/workspace filtering before adding to externalPackageUsage
612
692
  if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
613
- fileNode.externalPackageUsage.add(this._extractPackageName(specifier));
693
+ const pkgNameCE = this._extractPackageName(specifier);
694
+ let isInternalCE = false;
695
+ if (this.context.pathMapper && typeof this.context.pathMapper.isTsconfigAlias === 'function') {
696
+ if (this.context.pathMapper.isTsconfigAlias(specifier) || this.context.pathMapper.isTsconfigAlias(pkgNameCE)) isInternalCE = true;
697
+ }
698
+ if (!isInternalCE && this.context.workspaceGraph && typeof this.context.workspaceGraph.isLocalWorkspaceSpecifier === 'function') {
699
+ if (this.context.workspaceGraph.isLocalWorkspaceSpecifier(specifier) || this.context.workspaceGraph.isLocalWorkspaceSpecifier(pkgNameCE)) isInternalCE = true;
700
+ }
701
+ if (!isInternalCE) fileNode.externalPackageUsage.add(pkgNameCE);
614
702
  }
615
703
  }
616
704
  }
@@ -6,15 +6,7 @@ import { PluginRegistry } from '../plugins/PluginRegistry.js';
6
6
  * Ecosystem Entry Point Manifest & Dynamic Framework Router Heuristic Validator
7
7
  * Intercepts implicit conventions to handle cases where direct import statements are absent.
8
8
  * Now refactored to use a pluggable architecture.
9
- *
10
- * Improvements over v1:
11
- * - Extended config-file detection list (Biome, Oxlint, tsup, unbuild, etc.)
12
- * - Next.js App Router conventions (page.tsx, layout.tsx, loading.tsx, error.tsx, etc.)
13
- * - Remix conventions (route files under app/routes/)
14
- * - SvelteKit conventions (+page.svelte, +layout.svelte, etc.)
15
- * - Astro page/layout conventions
16
- * - Common entry-point patterns (bin/, cli.ts, server.ts, main.ts, app.ts)
17
- * - Test file patterns extended to cover Vitest workspace files
9
+ * Version 5.4.0: Added detectEntryPointsFromPlugins() for dynamic framework entry detection.
18
10
  */
19
11
  export class MagicDetector {
20
12
  constructor(context) {
@@ -31,18 +23,26 @@ export class MagicDetector {
31
23
 
32
24
  /**
33
25
  * Audits the project context to map active micro-framework ecosystems.
34
- * @param {string} baseContextDirectory - Root file workspace context execution vector path
35
26
  */
36
27
  async identifyActiveProjectEcosystems(baseContextDirectory) {
37
28
  await this.ensureInitialized(baseContextDirectory);
38
29
  const activePlugins = await this.registry.getActivePlugins(baseContextDirectory);
39
30
  const activeFrameworkFlags = activePlugins.map(p => p.name);
40
-
41
- // Universal infrastructure overrides (testing platforms and common bundlers)
42
31
  activeFrameworkFlags.push('universal-tooling-vectors');
43
32
  return activeFrameworkFlags;
44
33
  }
45
34
 
35
+ /**
36
+ * Version 5.4.0: Delegates entry point detection to the plugin registry.
37
+ * @param {string} content - The file content
38
+ * @param {string} filePath - The absolute path to the file
39
+ * @returns {Array<string>} List of detected entry points
40
+ */
41
+ async detectEntryPointsFromPlugins(content, filePath) {
42
+ await this.ensureInitialized();
43
+ return this.registry.detectEntryPointsFromContent(content, filePath);
44
+ }
45
+
46
46
  /**
47
47
  * Assesses if a file path acts as an implicit route entry point.
48
48
  */
@@ -60,7 +60,6 @@ export class MagicDetector {
60
60
  }
61
61
  }
62
62
 
63
- // Apply baseline platform rules (Test suites, lint parameters, continuous integration files)
64
63
  if (this.isCoreToolingSuiteElement(normalizedSystemPath)) {
65
64
  return true;
66
65
  }
@@ -68,49 +67,46 @@ export class MagicDetector {
68
67
  return false;
69
68
  }
70
69
 
70
+ /**
71
+ * Version 5.3.0: Delegates content analysis to the plugin registry.
72
+ */
73
+ async runPluginContentAnalysis(node, absolutePath) {
74
+ await this.ensureInitialized();
75
+ await this.registry.runPluginAnalysis(node, absolutePath);
76
+ }
77
+
71
78
  isCoreToolingSuiteElement(normalizedPath) {
72
- // ── Test / spec files ──────────────────────────────────────────────────────
73
79
  if (/\.(test|spec|e2e|cy)\.(js|ts|tsx|jsx)$/i.test(normalizedPath)) return true;
74
80
  if (/\.stories\.(js|ts|tsx|jsx)$/i.test(normalizedPath)) return true;
75
81
 
76
- // ── Build / bundler config files ───────────────────────────────────────────
77
82
  const configFragments = [
78
- // Test runners
79
83
  'jest.config.', 'vitest.config.', 'vitest.workspace.',
80
84
  'playwright.config.', 'cypress.config.',
81
- // Bundlers
82
85
  'webpack.config.', 'vite.config.', 'rollup.config.',
83
86
  'esbuild.config.', 'parcel.config.',
84
87
  'tsup.config.', 'unbuild.config.', 'pkgroll.config.',
85
- // CSS / styling
86
88
  'tailwind.config.', 'postcss.config.', '.postcssrc.',
87
- // Linters / formatters
88
89
  '.eslintrc.', 'eslint.config.', 'prettier.config.', '.prettierrc.',
89
90
  '.stylelintrc.', 'stylelint.config.',
90
91
  'biome.json', '.oxlintrc.',
91
- // Babel / transpilation
92
92
  '.babelrc.', 'babel.config.',
93
- // Commit / git hooks
94
93
  '.commitlintrc.', 'commitlint.config.',
95
94
  '.lintstagedrc.', 'lint-staged.config.',
96
- // Documentation
97
95
  'typedoc.config.', 'typedoc.json',
98
- // Monorepo tools
99
96
  'turbo.json', 'nx.json', 'lerna.json',
100
- // Misc tooling
101
97
  'knip.config.', 'knip.json',
102
98
  'syncpack.config.',
103
- // Internal worker
104
99
  'WorkerTaskRunner.js'
105
100
  ];
106
101
  if (configFragments.some(f => normalizedPath.includes(f))) return true;
107
102
 
108
- // ── Common application entry points ───────────────────────────────────────
109
103
  const entryPatterns = [
110
104
  // CLI binaries
111
105
  '/bin/cli.js', '/bin/cli.ts', '/bin/cli.mjs',
112
106
  '/bin/index.js', '/bin/index.ts',
113
- // Server / app entry points (Reduced in v3.3.8 to avoid false positives in libraries)
107
+ // Standard package entry points (poly-extension support)
108
+ '/index.js', '/index.ts', '/index.jsx', '/index.tsx', '/index.mjs', '/index.cjs',
109
+ // Server / app entry points
114
110
  '/src/main.ts', '/src/main.js',
115
111
  '/src/app.ts', '/src/app.js',
116
112
  '/src/api/HeadlessAPI.js', '/src/api/PluginSDK.js',
@@ -119,50 +115,37 @@ export class MagicDetector {
119
115
  ];
120
116
  if (entryPatterns.some(p => normalizedPath.endsWith(p))) return true;
121
117
 
122
- // ── Next.js App Router conventions ────────────────────────────────────────
123
- // Files under app/ directory with Next.js special names
124
118
  if (/\/app\/(page|layout|loading|error|not-found|template|default|route|middleware)\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
125
- // Next.js Pages Router
126
119
  if (/\/pages\/.*\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
127
- // Next.js API routes
128
120
  if (/\/pages\/api\/.*\.(js|ts)$/.test(normalizedPath)) return true;
129
- // Next.js middleware
130
121
  if (/\/middleware\.(js|ts)$/.test(normalizedPath)) return true;
131
- // Next.js config
132
122
  if (/\/next\.config\.(js|ts|mjs|cjs)$/.test(normalizedPath)) return true;
133
123
 
134
- // ── Remix conventions ─────────────────────────────────────────────────────
135
124
  if (/\/app\/routes\/.*\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
136
125
  if (/\/app\/root\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
137
126
  if (/\/app\/entry\.(client|server)\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
138
127
 
139
- // ── SvelteKit conventions ─────────────────────────────────────────────────
140
128
  if (/\/\+page(\.(server|client))?\.(svelte|js|ts)$/.test(normalizedPath)) return true;
141
129
  if (/\/\+layout(\.(server|client))?\.(svelte|js|ts)$/.test(normalizedPath)) return true;
142
130
  if (/\/\+error\.(svelte|js|ts)$/.test(normalizedPath)) return true;
143
131
  if (/\/\+server\.(js|ts)$/.test(normalizedPath)) return true;
144
132
  if (/\/svelte\.config\.(js|ts|mjs)$/.test(normalizedPath)) return true;
145
133
 
146
- // ── Astro conventions ─────────────────────────────────────────────────────
147
134
  if (/\/src\/pages\/.*\.astro$/.test(normalizedPath)) return true;
148
135
  if (/\/src\/layouts\/.*\.astro$/.test(normalizedPath)) return true;
149
136
  if (/\/astro\.config\.(mjs|js|ts)$/.test(normalizedPath)) return true;
150
137
 
151
- // ── Nuxt conventions ──────────────────────────────────────────────────────
152
138
  if (/\/pages\/.*\.vue$/.test(normalizedPath)) return true;
153
139
  if (/\/layouts\/.*\.vue$/.test(normalizedPath)) return true;
154
140
  if (/\/server\/api\/.*\.(js|ts)$/.test(normalizedPath)) return true;
155
141
  if (/\/nuxt\.config\.(js|ts|mjs)$/.test(normalizedPath)) return true;
156
142
 
157
- // ── React / Vite entry points ─────────────────────────────────────────────
158
143
  if (/\/vite\.config\.(js|ts|mjs)$/.test(normalizedPath)) return true;
159
144
 
160
- // ── Angular conventions ───────────────────────────────────────────────────
161
145
  if (/\/main\.(ts|js)$/.test(normalizedPath)) return true;
162
146
  if (/\/app\.module\.(ts|js)$/.test(normalizedPath)) return true;
163
147
  if (/\/angular\.json$/.test(normalizedPath)) return true;
164
148
 
165
- // ── Expo / React Native ───────────────────────────────────────────────────
166
149
  if (/\/app\/_layout\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
167
150
  if (/\/app\/index\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
168
151
 
@@ -176,10 +159,8 @@ export class MagicDetector {
176
159
  await this.ensureInitialized();
177
160
  if (!await this.isImplicitlyRequiredByEcosystem(filePath, activeFrameworks)) return;
178
161
 
179
- // Retain entry point elements within memory to keep verification safe
180
162
  fileNode.isEntry = true;
181
163
 
182
- // Apply dynamic exports coverage metrics based on active platform contracts
183
164
  const normalizedPath = filePath.replace(/\\/g, '/');
184
165
  const plugins = this.registry.getPlugins();
185
166
 
@@ -192,7 +173,6 @@ export class MagicDetector {
192
173
  const contracts = plugin.getRequiredSystemContracts();
193
174
  contracts.forEach(contractMethodToken => {
194
175
  if (fileNode.internalExports.has(contractMethodToken)) {
195
- // Emulate active local reference linkages to protect the export
196
176
  fileNode.instantiatedIdentifiers.add(contractMethodToken);
197
177
  }
198
178
  });