entkapp 4.2.0 → 4.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/.scaffold-ignore CHANGED
@@ -1,5 +1,5 @@
1
1
  # ============================================================================
2
- # ⚙️ entkapp Rule Suppression & Intent Profiles
2
+ # ⚙️ loui Rule Suppression & Intent Profiles
3
3
  # ============================================================================
4
4
  # Define exact symbols, file patterns, or dependency keys that must remain
5
5
  # preserved during dead-code scanning and transactional refactoring pruning.
package/README.md CHANGED
@@ -28,19 +28,6 @@ It represents the process of stripping away the unnecessary layers of a codebase
28
28
  ## 📦 Installation
29
29
 
30
30
  ```bash
31
- npm install -g entkapp
31
+ npm install -D entkapp
32
32
  # or
33
- pnpm add -g entkapp
34
- ```
35
-
36
- ## Usage
37
- ```bash
38
- npx entkapp
39
- # or
40
- entkapp
41
- # and then
42
- npx entkapp -r
43
- # or
44
- entkapp -r
45
- # It is recommended to run with --no-fix tag on your first run!
46
- ```
33
+ pnpm add -D entkapp
package/bin/cli.js CHANGED
@@ -28,13 +28,12 @@ async function bootstrap() {
28
28
  program
29
29
  .name('entkapp')
30
30
  .description(ansis.cyan('Enterprise-Grade AST Syntax Refactoring & Self-Healing Engine'))
31
- .version(packageJsonContent.version || '4.2.0');
31
+ .version(packageJsonContent.version || '4.4.0');
32
32
 
33
33
  program
34
34
  .option('-c, --cwd <path>', 'Specify the execution context root directory', process.cwd())
35
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', true)
37
- .option('--no-fix', 'Disable direct file manipulation modifications (dry-run reporting mode)')
36
+ .option('--fix', 'Enable atomic code updates, structural file pruning, and active type sanitization', false)
38
37
  .option('--tsconfig <filename>', 'Specify path to custom layout configurations', 'tsconfig.json')
39
38
  .option('--test-command <command>', 'Integrated continuous safety test validation script execution path', 'npm test')
40
39
  .option('--workspace', 'Enable high-density workspace workspace/monorepo cluster mesh evaluation parsing', false)
@@ -132,7 +131,7 @@ async function bootstrap() {
132
131
  }, timeoutMs);
133
132
  timeoutTimer.unref(); // Allow process to exit if work finishes
134
133
 
135
- console.log(ansis.bold.green(`\n📦 entkapp v${packageJsonContent.version || '4.2.0'} Engine Activation`));
134
+ console.log(ansis.bold.green(`\n📦 entkapp v${packageJsonContent.version || '4.4.0'} Engine Activation`));
136
135
  console.log(ansis.dim('------------------------------------------------------------'));
137
136
  console.log(`${ansis.bold('Target Workspace Root :')} ${ansis.blue(targetCwd)}`);
138
137
  console.log(`${ansis.bold('Refactoring Mode :')} ${options.fix ? ansis.yellow('Active Fixing & Self-Healing Enabled') : ansis.gray('Dry-Run Reporting Only')}`);
package/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  /**
4
4
  * ============================================================================
5
- * 📦 entkapp v4.2.0: Ultimate Enterprise Codebase Janitor & Self-Healing Engine
5
+ * 📦 entkapp v4.1.0: Ultimate Enterprise Codebase Janitor & Self-Healing Engine
6
6
  * ============================================================================
7
7
  * * Eine hochgradig integrierte Code-Analyse- und Projektbootstrapping-Engine.
8
8
  * Kombiniert rekursive Erreichbarkeitsanalysen (Reachability Graphs) auf
@@ -648,7 +648,7 @@ function smartPrepend(originalCode, declarationBlock) {
648
648
  async function inspectNpmPackage(pkgName) {
649
649
  try {
650
650
  const response = await fetch(`https://registry.npmjs.org/${pkgName}/latest`, {
651
- headers: { 'User-Agent': 'entkapp-dx-client/4.2.0' },
651
+ headers: { 'User-Agent': 'entkapp-dx-client/4.1.0' },
652
652
  signal: AbortSignal.timeout(4000)
653
653
  });
654
654
  if (response.status === 200) {
@@ -682,7 +682,7 @@ async function inspectNpmPackage(pkgName) {
682
682
  async function fetchRemoteLicense(licenseKey) {
683
683
  try {
684
684
  const response = await fetch(`https://api.github.com/licenses/${licenseKey.toLowerCase()}`, {
685
- headers: { 'User-Agent': 'entkapp-dx-client/4.2.0' },
685
+ headers: { 'User-Agent': 'entkapp-dx-client/4.1.0' },
686
686
  signal: AbortSignal.timeout(5000)
687
687
  });
688
688
  if (response.status === 200) {
@@ -1681,7 +1681,7 @@ function postProcessAnalysis(stats, graphEngine) {
1681
1681
  // ============================================================
1682
1682
  async function main() {
1683
1683
  if (process.argv.includes('--help') || process.argv.includes('-h')) {
1684
- console.log(`\n📦 entkapp v4.2.0: Advanced Dependency Intelligence Engine\n`);
1684
+ console.log(`\n📦 entkapp v4.1.0: Advanced Dependency Intelligence Engine\n`);
1685
1685
  console.log(`Usage: npx entkapp [options]\n`);
1686
1686
  console.log(`Options:`);
1687
1687
  console.log(` -h, --help Show this comprehensive workspace helper panel`);
@@ -1766,7 +1766,7 @@ async function main() {
1766
1766
  let detectedFrameworks = [];
1767
1767
 
1768
1768
  console.log(`\n${'═'.repeat(67)}`);
1769
- console.log(`🚀 entkapp v4.2.0: Enterprise Graph Intelligence Analyzer`);
1769
+ console.log(`🚀 entkapp v4.1.0: Enterprise Graph Intelligence Analyzer`);
1770
1770
  console.log(`${'═'.repeat(67)}\n`);
1771
1771
 
1772
1772
  const topLevelItems = fs.readdirSync(targetDir);
package/logo.png CHANGED
Binary file
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": "4.2.0",
4
+ "version": "4.4.0",
5
5
  "description": "The Ultimate Enterprise Codebase Janitor. Faster than Knip with OXC integration, type-aware analysis, and automated structural healing. Fully standalone - solving what Knip cannot.",
6
6
  "type": "module",
7
7
  "main": "index.js",
@@ -64,12 +64,7 @@
64
64
  "unresolved",
65
65
  "unused",
66
66
  "unused-packages",
67
- "workspace",
68
- "entkapp",
69
- "cut",
70
- "kapp",
71
- "dreamlong",
72
- "DreamLong"
67
+ "workspace"
73
68
  ],
74
69
  "author": "DreamLongYT",
75
70
  "license": "Apache-2.0",
@@ -31,7 +31,11 @@ export class GraphNode {
31
31
  this.localSuppressedRules = new Set();
32
32
  }
33
33
 
34
- isSymbolReferencedExternally(symbolName, projectGraph) {
34
+ isSymbolReferencedExternally(symbolName, projectGraph, visited = new Set()) {
35
+ const visitKey = `${this.filePath}:${symbolName}`;
36
+ if (visited.has(visitKey)) return false;
37
+ visited.add(visitKey);
38
+
35
39
  if (this.isLibraryEntry) return true;
36
40
 
37
41
  // --- NEW: Self-reference check (Fix for SecretSeverity bug) ---
@@ -72,6 +76,20 @@ export class GraphNode {
72
76
  return true;
73
77
  }
74
78
 
79
+ // NEW: Check for re-exports that might use this symbol (Recursive Barrel Resolution)
80
+ for (const [exportedName, exportMeta] of parentNode.internalExports.entries()) {
81
+ const isMatch = (exportMeta.type === 're-export' || exportMeta.type === 're-export-namespace') &&
82
+ (exportMeta.originalName === symbolName || exportMeta.originalName === '*') &&
83
+ (exportMeta.source === this.filePath || (exportMeta.source && path.resolve(path.dirname(parentPath), exportMeta.source) === this.filePath));
84
+
85
+ if (isMatch) {
86
+ // If this parent re-exports our symbol, check if the re-exported symbol is used
87
+ if (parentNode.isSymbolReferencedExternally(exportedName, projectGraph, visited)) {
88
+ return true;
89
+ }
90
+ }
91
+ }
92
+
75
93
  if (parentNode.instantiatedIdentifiers.has(symbolName)) return true;
76
94
 
77
95
  for (const accessChain of parentNode.propertyAccessChains) {
@@ -158,9 +176,11 @@ export class EngineContext {
158
176
  for (const [manifestPath, deps] of this.manifestDependencies.entries()) {
159
177
  const allDeps = [...(deps.dependencies || []), ...(deps.devDependencies || [])];
160
178
 
179
+ const hasTypeScriptFiles = Array.from(this.projectGraph.keys()).some(f => f.endsWith('.ts') || f.endsWith('.tsx'));
180
+
161
181
  for (const dep of allDeps) {
162
182
  // Skip @types packages and known safe packages
163
- if (dep.startsWith('@types/') || dep === 'entkapp') {
183
+ if (dep.startsWith('@types/') || dep === 'entkapp' || (dep === 'typescript' && hasTypeScriptFiles)) {
164
184
  continue;
165
185
  }
166
186
 
@@ -0,0 +1,383 @@
1
+ export class OxcAnalyzer {
2
+ constructor(context) {
3
+ this.context = context;
4
+ this.oxc = null;
5
+ this.isAvailable = false;
6
+ }
7
+
8
+ async init() {
9
+ if (this.isAvailable) return true;
10
+ try {
11
+ const oxc = await import("oxc-parser");
12
+ this.oxc = oxc;
13
+ this.isAvailable = true;
14
+ return true;
15
+ } catch (e) {
16
+ try {
17
+ const { createRequire } = await import('module');
18
+ const require = createRequire(import.meta.url);
19
+ this.oxc = require("oxc-parser");
20
+ this.isAvailable = true;
21
+ return true;
22
+ } catch (err) {
23
+ this.isAvailable = false;
24
+ if (this.context.verbose) {
25
+ console.warn(`[OxcAnalyzer] oxc-parser failed to load: ${err.message}`);
26
+ }
27
+ return false;
28
+ }
29
+ }
30
+ }
31
+
32
+ async parseFile(filePath, content, fileNode) {
33
+ if (!this.isAvailable) {
34
+ const initialized = await this.init();
35
+ if (!initialized) return false;
36
+ }
37
+
38
+ try {
39
+ // Fix: Ensure filePath is correctly handled for OXC (Windows path issue)
40
+ const normalizedPath = filePath.replace(/\\/g, '/');
41
+
42
+ // Try passing the path directly as the second argument if the object-based options fail
43
+ // Some versions of oxc-parser expect a string as the second argument for the filename
44
+ let result;
45
+ try {
46
+ result = this.oxc.parseSync(content, {
47
+ sourceType: "module",
48
+ sourceFilename: normalizedPath,
49
+ lang: "typescript"
50
+ });
51
+ } catch (e) {
52
+ // Fallback for versions that expect the path as second argument
53
+ result = this.oxc.parseSync(content, normalizedPath);
54
+ }
55
+
56
+ // Fix: Handle cases where OXC returns a JSON string instead of an object
57
+ // Stabilize result through JSON round-trip to fix N-API conversion issues on Windows
58
+ let parsedResult;
59
+ try {
60
+ parsedResult = typeof result === 'string' ? JSON.parse(result) : JSON.parse(JSON.stringify(result));
61
+ } catch (err) {
62
+ if (typeof result === 'object') {
63
+ parsedResult = result; // Last resort
64
+ } else {
65
+ throw new Error("OXC returned an invalid format");
66
+ }
67
+ }
68
+
69
+ let ast;
70
+ if (parsedResult && typeof parsedResult === 'object') {
71
+ if (parsedResult.program) {
72
+ ast = parsedResult;
73
+ } else if (parsedResult.ast) {
74
+ ast = { program: parsedResult.ast };
75
+ } else {
76
+ ast = { program: parsedResult };
77
+ }
78
+ } else {
79
+ throw new Error("OXC returned an invalid AST format");
80
+ }
81
+
82
+ fileNode.ast = ast.program; // Store the AST for advanced analysis
83
+ fileNode.jsxComponents = new Set();
84
+ fileNode.jsxProps = new Set();
85
+ fileNode.decorators = new Set();
86
+
87
+ this.walkOxcAst(ast.program, fileNode, content);
88
+
89
+ const lines = content.split('\n');
90
+ const getLineCol = (pos) => {
91
+ let count = 0;
92
+ for (let i = 0; i < lines.length; i++) {
93
+ if (count + lines[i].length + 1 > pos) {
94
+ return { line: i + 1, column: pos - count + 1 };
95
+ }
96
+ count += lines[i].length + 1;
97
+ }
98
+ return { line: 1, column: 1 };
99
+ };
100
+
101
+ for (const [name, meta] of fileNode.internalExports.entries()) {
102
+ if (meta.start !== undefined) {
103
+ fileNode.symbolSourceLocations.set(name, getLineCol(meta.start));
104
+ }
105
+ }
106
+
107
+ return true;
108
+ } catch (e) {
109
+ if (this.context.verbose) {
110
+ console.warn(`[OXC] Failed to parse ${filePath}. Error: ${e.message}`);
111
+ if (e.stack) console.debug(e.stack);
112
+ console.info(`[OXC] Switching back to TypeScript Compiler API for ${filePath}`);
113
+ }
114
+ return false;
115
+ }
116
+ }
117
+
118
+ walkOxcAst(node, fileNode, content) {
119
+ if (!node) return;
120
+
121
+ switch (node.type) {
122
+ case "ImportDeclaration":
123
+ this.handleImportDeclaration(node, fileNode);
124
+ break;
125
+ case "ExportNamedDeclaration":
126
+ case "ExportDefaultDeclaration":
127
+ case "ExportAllDeclaration":
128
+ this.handleExportDeclaration(node, fileNode, content);
129
+ break;
130
+ case "CallExpression":
131
+ this.handleCallExpression(node, fileNode);
132
+ break;
133
+ case "JSXElement":
134
+ case "JSXFragment":
135
+ this.handleJsxElement(node, fileNode);
136
+ break;
137
+ case "Decorator":
138
+ this.handleDecorator(node, fileNode);
139
+ break;
140
+ case "StringLiteral":
141
+ fileNode.rawStringReferences.add(node.value);
142
+ break;
143
+ }
144
+
145
+ for (const key in node) {
146
+ if (node[key] && typeof node[key] === "object") {
147
+ if (Array.isArray(node[key])) {
148
+ node[key].forEach((child) => this.walkOxcAst(child, fileNode, content));
149
+ } else {
150
+ this.walkOxcAst(node[key], fileNode, content);
151
+ }
152
+ }
153
+ }
154
+ }
155
+
156
+ handleImportDeclaration(node, fileNode) {
157
+ const specifier = node.source.value;
158
+ fileNode.explicitImports.add(specifier);
159
+
160
+ if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
161
+ fileNode.externalPackageUsage.add(this._extractPackageName(specifier));
162
+ }
163
+
164
+ if (node.specifiers) {
165
+ node.specifiers.forEach((spec) => {
166
+ if (spec.type === "ImportSpecifier") {
167
+ const importedName = spec.imported.name || (spec.imported.type === "Identifier" ? spec.imported.name : spec.imported.value);
168
+ fileNode.importedSymbols.add(`${specifier}:${importedName}`);
169
+ } else if (spec.type === "ImportDefaultSpecifier") {
170
+ fileNode.importedSymbols.add(`${specifier}:default`);
171
+ } else if (spec.type === "ImportNamespaceSpecifier") {
172
+ fileNode.importedSymbols.add(`${specifier}:*`);
173
+ }
174
+ });
175
+ }
176
+ }
177
+
178
+ handleExportDeclaration(node, fileNode, content) {
179
+ // 1. Default Exports
180
+ if (node.type === "ExportDefaultDeclaration") {
181
+ fileNode.internalExports.set("default", {
182
+ type: "default",
183
+ start: node.start,
184
+ end: node.end
185
+ });
186
+ return;
187
+ }
188
+
189
+ // 2. Re-export All: export * from 'mod' or export * as ns from 'mod'
190
+ if (node.type === "ExportAllDeclaration") {
191
+ const sourceSpecifier = node.source.value;
192
+ fileNode.explicitImports.add(sourceSpecifier);
193
+ if (!sourceSpecifier.startsWith('.') && !sourceSpecifier.startsWith('/')) {
194
+ fileNode.externalPackageUsage.add(this._extractPackageName(sourceSpecifier));
195
+ }
196
+
197
+ if (node.exported) {
198
+ // export * as ns from 'mod'
199
+ const name = node.exported.name || (node.exported.type === "Identifier" ? node.exported.name : null);
200
+ if (name) {
201
+ fileNode.internalExports.set(name, {
202
+ type: "re-export-namespace",
203
+ source: sourceSpecifier,
204
+ originalName: "*",
205
+ start: node.start,
206
+ end: node.end
207
+ });
208
+ fileNode.importedSymbols.add(`${sourceSpecifier}:*`);
209
+ }
210
+ } else {
211
+ // export * from 'mod'
212
+ fileNode.internalExports.set("*", {
213
+ type: "re-export-all",
214
+ source: sourceSpecifier
215
+ });
216
+ fileNode.importedSymbols.add(`${sourceSpecifier}:*`);
217
+ }
218
+ return;
219
+ }
220
+
221
+ // 3. Named Exports & Re-exports with specifiers
222
+ if (node.source) {
223
+ // Re-export: export { x } from 'mod'
224
+ const specifier = node.source.value;
225
+ fileNode.explicitImports.add(specifier);
226
+ if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
227
+ fileNode.externalPackageUsage.add(this._extractPackageName(specifier));
228
+ }
229
+
230
+ if (node.specifiers) {
231
+ node.specifiers.forEach((spec) => {
232
+ const exportedName = spec.exported.name || (spec.exported.type === "Identifier" ? spec.exported.name : spec.exported.value);
233
+ const localName = spec.local.name || (spec.local.type === "Identifier" ? spec.local.name : spec.local.value);
234
+ fileNode.internalExports.set(exportedName, {
235
+ type: "re-export",
236
+ source: specifier,
237
+ originalName: localName,
238
+ start: spec.start,
239
+ end: spec.end,
240
+ });
241
+ fileNode.importedSymbols.add(`${specifier}:${localName}`);
242
+ });
243
+ }
244
+ } else if (node.declaration) {
245
+ // Direct declaration export: export const x = 1, export function f() {}
246
+ const decl = node.declaration;
247
+ if (decl.type === "VariableDeclaration") {
248
+ decl.declarations.forEach((d) => {
249
+ this._extractNamesFromPattern(d.id, (name) => {
250
+ fileNode.internalExports.set(name, {
251
+ type: "variable",
252
+ start: d.start,
253
+ end: d.end
254
+ });
255
+ });
256
+ });
257
+ } else if (decl.id && decl.id.name) {
258
+ let type = "unknown";
259
+ if (decl.type === "FunctionDeclaration") type = "function";
260
+ else if (decl.type === "ClassDeclaration") type = "class";
261
+ else if (decl.type === "TSEnumDeclaration") type = "enum";
262
+ else if (decl.type === "TSInterfaceDeclaration") type = "interface";
263
+ else if (decl.type === "TSTypeAliasDeclaration") type = "type";
264
+ else if (decl.type === "TSModuleDeclaration") type = "namespace";
265
+
266
+ fileNode.internalExports.set(decl.id.name, {
267
+ type,
268
+ start: decl.start,
269
+ end: decl.end
270
+ });
271
+ }
272
+ } else if (node.specifiers) {
273
+ // Export existing locals: export { x, y as z }
274
+ node.specifiers.forEach((spec) => {
275
+ const exportedName = spec.exported.name || (spec.exported.type === "Identifier" ? spec.exported.name : spec.exported.value);
276
+ const localName = spec.local.name || (spec.local.type === "Identifier" ? spec.local.name : spec.local.value);
277
+ fileNode.internalExports.set(exportedName, {
278
+ type: "export",
279
+ originalName: localName,
280
+ start: spec.start,
281
+ end: spec.end,
282
+ });
283
+ });
284
+ }
285
+ }
286
+
287
+ _extractNamesFromPattern(node, callback) {
288
+ if (!node) return;
289
+ if (node.type === "Identifier") {
290
+ callback(node.name);
291
+ } else if (node.type === "ObjectPattern") {
292
+ node.properties.forEach(p => {
293
+ if (p.type === "Property") {
294
+ this._extractNamesFromPattern(p.value, callback);
295
+ } else if (p.type === "RestElement") {
296
+ this._extractNamesFromPattern(p.argument, callback);
297
+ }
298
+ });
299
+ } else if (node.type === "ArrayPattern") {
300
+ node.elements.forEach(e => {
301
+ if (e) this._extractNamesFromPattern(e, callback);
302
+ });
303
+ } else if (node.type === "AssignmentPattern") {
304
+ this._extractNamesFromPattern(node.left, callback);
305
+ } else if (node.type === "RestElement") {
306
+ this._extractNamesFromPattern(node.argument, callback);
307
+ }
308
+ }
309
+
310
+ handleCallExpression(node, fileNode) {
311
+ // Dynamic import(): import('./module')
312
+ if (node.callee.type === "Import" && node.arguments.length > 0) {
313
+ const arg = node.arguments[0];
314
+ if (arg.type === "StringLiteral") {
315
+ const specifier = arg.value;
316
+ fileNode.explicitImports.add(specifier);
317
+ fileNode.dynamicImports.add(specifier);
318
+ fileNode.importedSymbols.add(`${specifier}:*`); // Dynamic import usually consumes the whole namespace
319
+ if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
320
+ fileNode.externalPackageUsage.add(this._extractPackageName(specifier));
321
+ }
322
+ } else {
323
+ if (fileNode.calculatedDynamicImports) {
324
+ fileNode.calculatedDynamicImports.push({ kind: arg.type, start: arg.start });
325
+ }
326
+ }
327
+ } else if (node.callee.type === "Identifier" && node.callee.name === "require" && node.arguments.length > 0 && node.arguments[0].type === "StringLiteral") {
328
+ const specifier = node.arguments[0].value;
329
+ fileNode.explicitImports.add(specifier);
330
+ fileNode.importedSymbols.add(`${specifier}:*`);
331
+ if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
332
+ fileNode.externalPackageUsage.add(this._extractPackageName(specifier));
333
+ }
334
+ }
335
+ }
336
+
337
+ handleJsxElement(node, fileNode) {
338
+ const getElementName = (nameNode) => {
339
+ if (nameNode.type === "JSXIdentifier") return nameNode.name;
340
+ if (nameNode.type === "JSXMemberExpression") return `${getElementName(nameNode.object)}.${nameNode.property.name}`;
341
+ if (nameNode.type === "JSXNamespacedName") return `${nameNode.namespace.name}:${nameNode.name.name}`;
342
+ return "unknown";
343
+ };
344
+
345
+ if (node.openingElement) {
346
+ const tagName = getElementName(node.openingElement.name);
347
+ fileNode.jsxComponents.add(tagName);
348
+
349
+ if (node.openingElement.attributes) {
350
+ node.openingElement.attributes.forEach(attr => {
351
+ if (attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier") {
352
+ fileNode.jsxProps.add(`${tagName}:${attr.name.name}`);
353
+ }
354
+ });
355
+ }
356
+ }
357
+ }
358
+
359
+ handleDecorator(node, fileNode) {
360
+ const getDecoratorName = (expr) => {
361
+ if (expr.type === "Identifier") return expr.name;
362
+ if (expr.type === "CallExpression") return getDecoratorName(expr.callee);
363
+ if (expr.type === "MemberExpression") {
364
+ const prop = expr.property.name || expr.property.value;
365
+ return prop || "unknown";
366
+ }
367
+ return "unknown";
368
+ };
369
+
370
+ const decoratorName = getDecoratorName(node.expression);
371
+ if (decoratorName !== "unknown") {
372
+ fileNode.decorators.add(decoratorName);
373
+ }
374
+ }
375
+
376
+ _extractPackageName(specifier) {
377
+ if (specifier.startsWith('@')) {
378
+ const parts = specifier.split('/');
379
+ return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : specifier;
380
+ }
381
+ return specifier.split('/')[0];
382
+ }
383
+ }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * ============================================================================
3
- * Headless API for entkapp v4.2.0
3
+ * Headless API for entkapp v4.1.0
4
4
  * ============================================================================
5
5
  * Provides a programmatic interface for integrating entkapp into
6
6
  * custom workflows, CI/CD pipelines, and third-party tools.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * ============================================================================
3
- * Plugin SDK for entkapp v4.2.0
3
+ * Plugin SDK for entkapp v4.0.0
4
4
  * ============================================================================
5
5
  * Provides utilities and helpers for developing custom plugins that extend
6
6
  * entkapp's analysis and healing capabilities.
@@ -35,6 +35,7 @@ export class ASTAnalyzer {
35
35
  this.scopeStack.push(this.currentScope);
36
36
 
37
37
  this.extractTopLevelJSDocSuppreessions(sourceFile, fileNode);
38
+ fileNode.ast = sourceFile; // Store the AST for advanced analysis
38
39
  this.walkAST(sourceFile, fileNode, sourceFile);
39
40
 
40
41
  this.scopeStack.pop(); // Pop the global scope