pkg-scaffold 2.3.0 → 2.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.
@@ -0,0 +1,22 @@
1
+ # ============================================================================
2
+ # ⚙️ pkg-scaffold Rule Suppression & Intent Profiles
3
+ # ============================================================================
4
+ # Define exact symbols, file patterns, or dependency keys that must remain
5
+ # preserved during dead-code scanning and transactional refactoring pruning.
6
+
7
+ # Framework & Meta Entrypoints (Implicitly Alive)
8
+ pages/api/*
9
+ app/routes/*
10
+ src/entry-point.js
11
+
12
+ # Intent Suppression: Library Code / Consumer Consumption Contracts
13
+ export:publicApiMethod
14
+ export:initializePlugin
15
+ export:onConfigLoaded
16
+
17
+ # Globally Ignored Module Specifiers
18
+ @types/node
19
+ tslib
20
+
21
+ # Secret Heuristic Exemptions (Explicit False Positive Control)
22
+ exempt_token_pattern
package/bin/cli.js ADDED
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ============================================================================
5
+ * 🏁 pkg-scaffold CLI Entry Point
6
+ * ============================================================================
7
+ * Handles option compilation, environment orchestration, option validation,
8
+ * and initiates the primary operational pipeline loop.
9
+ */
10
+
11
+ import { Command } from 'commander';
12
+ import ansis from 'ansis';
13
+ import path from 'path';
14
+ import fs from 'fs/promises';
15
+ import { fileURLToPath } from 'url';
16
+
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = path.dirname(__filename);
19
+
20
+ const program = new Command();
21
+
22
+ async function bootstrap() {
23
+ try {
24
+ const packageJsonPath = path.resolve(__dirname, '../package.json');
25
+ const packageJsonContent = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
26
+
27
+ program
28
+ .name('pkg-scaffold')
29
+ .description(ansis.cyan('Enterprise-Grade AST Syntax Refactoring & Self-Healing Engine'))
30
+ .version(packageJsonContent.version || '3.0.0');
31
+
32
+ program
33
+ .option('-c, --cwd <path>', 'Specify the execution context root directory', process.cwd())
34
+ .option('--fix', 'Enable atomic code updates, structural file pruning, and active type sanitization', true)
35
+ .option('--no-fix', 'Disable direct file manipulation modifications (dry-run reporting mode)')
36
+ .option('--tsconfig <filename>', 'Specify path to custom layout configurations', 'tsconfig.json')
37
+ .option('--test-command <command>', 'Integrated continuous safety test validation script execution path', 'npm test')
38
+ .option('--workspace', 'Enable high-density workspace workspace/monorepo cluster mesh evaluation parsing', false)
39
+ .option('--verbose', 'Toggle expanded trace telemetry for debug operational diagnostics', false);
40
+
41
+ program.parse(process.argv);
42
+ const options = program.opts();
43
+
44
+ console.log(ansis.bold.green(`\n📦 pkg-scaffold v${packageJsonContent.version || '3.0.0'} Engine Activation`));
45
+ console.log(ansis.dim('------------------------------------------------------------'));
46
+ console.log(`${ansis.bold('Target Workspace Root :')} ${ansis.blue(path.resolve(options.cwd))}`);
47
+ console.log(`${ansis.bold('Refactoring Mode :')} ${options.fix ? ansis.yellow('Active Fixing & Self-Healing Enabled') : ansis.gray('Dry-Run Reporting Only')}`);
48
+ console.log(`${ansis.bold('Validation Sandbox :')} ${ansis.magenta(options.testCommand)}`);
49
+ console.log(ansis.dim('------------------------------------------------------------\n'));
50
+
51
+ const engineModulePath = path.resolve(__dirname, '../src/EngineContext.js');
52
+
53
+ try {
54
+ await fs.access(engineModulePath);
55
+ } catch {
56
+ console.error(ansis.red(`🚨 Execution Fault: Core engine architecture files missing. Ensure src/ directory layout is fully generated.`));
57
+ process.exit(1);
58
+ }
59
+
60
+ // Lazy load execution context wrapper to align with domain initialization
61
+ const { RefactoringEngine } = await import('../src/index.js');
62
+
63
+ if (!RefactoringEngine) {
64
+ console.error(ansis.red('🚨 Architecture Boundary Error: RefactoringEngine could not be resolved from code topology channels.'));
65
+ process.exit(1);
66
+ }
67
+
68
+ const engine = new RefactoringEngine({
69
+ cwd: options.cwd,
70
+ autoFix: options.fix,
71
+ tsconfig: options.tsconfig,
72
+ testCommand: options.testCommand,
73
+ workspace: options.workspace,
74
+ verbose: options.verbose
75
+ });
76
+
77
+ await engine.run();
78
+
79
+ console.log(ansis.bold.green('\n✨ Core cycle execution completed successfully. Structural layout is clean.'));
80
+ process.exit(0);
81
+
82
+ } catch (criticalBootError) {
83
+ console.error(ansis.bold.red(`\n🚨 Critical Lifecycle Boot Instability: ${criticalBootError.message}`));
84
+ if (criticalBootError.stack) {
85
+ console.error(ansis.dim(criticalBootError.stack));
86
+ }
87
+ process.exit(1);
88
+ }
89
+ }
90
+
91
+ bootstrap();
package/index.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,11 +1,16 @@
1
1
  {
2
2
  "name": "pkg-scaffold",
3
- "version": "2.3.0",
4
- "description": "Zero-config workspace initializer with advanced dependency intelligence: detects ghost dependencies (used but undeclared), orphaned packages (declared but unused), unused imports with file locations, deprecated packages, hardcoded secrets, and more.",
3
+ "version": "2.4.0",
4
+ "description": "An advanced, AST-driven dependency resolution, refactoring, and self-healing engine.",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "bin": {
8
- "pkg-scaffold": "./index.js"
8
+ "pkg-scaffold": "./bin/cli.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node bin/cli.js",
12
+ "test": "echo \"Error: no test specified\" && exit 0",
13
+ "test:stability": "npm run test"
9
14
  },
10
15
  "keywords": [
11
16
  "scaffold",
@@ -35,8 +40,15 @@
35
40
  },
36
41
  "homepage": "https://github.com/DreamLongYT/pkg-scaffold#readme",
37
42
  "dependencies": {
38
- "enhanced-resolve": "^5.24.0",
39
- "npm-deprecated-check": "^1.4.0",
40
- "typescript": "^6.0.3"
43
+ "ansis": "^3.0.0",
44
+ "commander": "^12.0.0",
45
+ "enhanced-resolve": "^5.16.0",
46
+ "execa": "^8.0.1",
47
+ "ramda": "^0.29.1",
48
+ "typescript": "^5.4.5",
49
+ "yocto-spinner": "^0.1.0"
50
+ },
51
+ "engines": {
52
+ "node": ">=18.0.0"
41
53
  }
42
54
  }
@@ -0,0 +1,282 @@
1
+ /**
2
+ * ============================================================================
3
+ * 📦 pkg-scaffold v3.0.0: Enterprise In-Memory Codebase State Manifest
4
+ * ============================================================================
5
+ * Implements a high-density, centralized graph database context for tracking
6
+ * software engineering debt, dependencies, types, and vulnerabilities.
7
+ */
8
+
9
+ import path from 'path';
10
+ import fs from 'fs/promises';
11
+
12
+ /**
13
+ * High-Fidelity Graph Element Node representing a single file asset boundary.
14
+ */
15
+ export class GraphNode {
16
+ constructor(filePath) {
17
+ this.filePath = path.normalize(filePath);
18
+ this.contentHash = '';
19
+ this.isLibraryEntry = false;
20
+ this.isFrameworkContract = false;
21
+ this.scriptKind = 0; // Ambient enum mapping
22
+
23
+ // Explicit and Computed Dynamic Syntax Boundaries
24
+ this.explicitImports = new Set();
25
+ this.dynamicImports = new Set();
26
+ this.importedSymbols = new Set(); // Format: 'specifier:symbol' or 'specifier:*'
27
+
28
+ // Internal API Exposed Interfaces (Symbol Name -> ExportMetadata)
29
+ this.internalExports = new Map();
30
+ this.typeOnlyExports = new Set();
31
+
32
+ // Semantic Reference Verification Registries
33
+ this.instantiatedIdentifiers = new Set();
34
+ this.rawStringReferences = new Set();
35
+ this.propertyAccessChains = new Set();
36
+
37
+ // Dependency Mesh Connection Maps
38
+ this.incomingEdges = new Set(); // Set of absolute filePaths depending on this component
39
+ this.outgoingEdges = new Set(); // Set of absolute internal filePaths this component calls
40
+
41
+ // Security & Compliance Anomaly Matrices
42
+ this.securityThreats = [];
43
+ this.calculatedDynamicImports = [];
44
+ this.localSuppressedRules = new Set();
45
+
46
+ // Detailed AST Location Diagnostics (Symbol -> Structural Location Mapping)
47
+ this.symbolSourceLocations = new Map(); // Symbol -> { line: number, column: number, length: number }
48
+ }
49
+
50
+ /**
51
+ * Evaluates if a specific exposed symbol token is utilized by any incoming edges.
52
+ * Leverages precise syntax identity collections.
53
+ */
54
+ isSymbolReferencedExternally(symbolName, projectGraph) {
55
+ if (this.isLibraryEntry) return true;
56
+
57
+ for (const parentPath of this.incomingEdges) {
58
+ const parentNode = projectGraph.get(parentPath);
59
+ if (!parentNode) continue;
60
+
61
+ // Direct identity reference check
62
+ if (parentNode.instantiatedIdentifiers.has(symbolName)) return true;
63
+
64
+ // Property lookup reference checks (e.g., config.databaseUrl)
65
+ for (const accessChain of parentNode.propertyAccessChains) {
66
+ if (accessChain.endsWith(`.${symbolName}`) || accessChain.includes(`.${symbolName}.`)) {
67
+ return true;
68
+ }
69
+ }
70
+
71
+ // Safe fallback lookup inside string reference caches (e.g., obj['databaseUrl'])
72
+ if (parentNode.rawStringReferences.has(symbolName)) return true;
73
+ }
74
+
75
+ return false;
76
+ }
77
+
78
+ /**
79
+ * Compiles complete localized diagnostic telemetry tracking metrics for this node instance.
80
+ */
81
+ compileNodeTelemetry() {
82
+ return {
83
+ path: this.filePath,
84
+ totalExplicitImportsCount: this.explicitImports.size,
85
+ totalExposedExportsCount: this.internalExports.size,
86
+ incomingDependenciesCount: this.incomingEdges.size,
87
+ outgoingDependenciesCount: this.outgoingEdges.size,
88
+ isDanglingOrphan: this.incomingEdges.size === 0 && !this.isLibraryEntry,
89
+ trackedThreatsCount: this.securityThreats.length
90
+ };
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Enterprise Engine Run State Registry & Suppression Context Matrix
96
+ */
97
+ export class EngineContext {
98
+ constructor(options = {}) {
99
+ this.cwd = path.normalize(options.cwd || process.cwd());
100
+ this.cacheDir = path.join(this.cwd, '.scaffold-cache');
101
+ this.ignoreFilePath = path.join(this.cwd, '.scaffold-ignore');
102
+ this.tsconfigFilename = options.tsconfig || 'tsconfig.json';
103
+ this.testCommand = options.testCommand || 'npm test';
104
+
105
+ this.allowAutoFix = options.autoFix ?? true;
106
+ this.isWorkspaceEnabled = options.workspace ?? false;
107
+ this.verbose = options.verbose ?? false;
108
+
109
+ // Core Memory Repositories
110
+ this.graph = new Map(); // Absolute File Path -> GraphNode
111
+ this.registryHashes = new Map(); // Package Name -> Secure Lockfile Signature String
112
+ this.globallyIgnoredSymbols = new Set();
113
+ this.globallyIgnoredPaths = [];
114
+ this.monorepoPackageRoots = new Set();
115
+
116
+ // Structural Heuristic Verification Metrics Tracker
117
+ this.metrics = {
118
+ startTime: 0,
119
+ endTime: 0,
120
+ totalFilesScanned: 0,
121
+ cacheHits: 0,
122
+ cacheMisses: 0,
123
+ prunedFilesCount: 0,
124
+ prunedExportsCount: 0,
125
+ totalSymbolsAnalyzed: 0,
126
+ securityVulnerabilitiesMitigated: 0
127
+ };
128
+ }
129
+
130
+ /**
131
+ * Initializes baseline context options, directory footprints, and suppression maps.
132
+ */
133
+ async initialize() {
134
+ this.metrics.startTime = Date.now();
135
+ await fs.mkdir(this.cacheDir, { recursive: true });
136
+ await this.compileIgnoreConfigurations();
137
+ }
138
+
139
+ /**
140
+ * Parses .scaffold-ignore layers using precise token segment matching
141
+ * instead of high-risk loose regex blocks.
142
+ */
143
+ async compileIgnoreConfigurations() {
144
+ try {
145
+ const content = await fs.readFile(this.ignoreFilePath, 'utf8');
146
+ const lines = content.split('\n');
147
+
148
+ for (let line of lines) {
149
+ line = line.trim();
150
+ if (!line || line.startsWith('#')) continue;
151
+
152
+ if (line.startsWith('export:')) {
153
+ const symbolToken = line.replace('export:', '').trim();
154
+ this.globallyIgnoredSymbols.add(symbolToken);
155
+ } else if (line.startsWith('path:')) {
156
+ const pathToken = line.replace('path:', '').trim();
157
+ this.globallyIgnoredPaths.push(path.normalize(pathToken));
158
+ } else {
159
+ // Standard structural path rule fallback
160
+ this.globallyIgnoredPaths.push(path.normalize(line));
161
+ }
162
+ }
163
+ } catch {
164
+ // Configuration optionally omitted; proceed with default execution flags
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Allocates or resolves a unified GraphNode reference inside our memory map index.
170
+ */
171
+ createNode(absoluteFilePath) {
172
+ const normalizedPath = path.normalize(absoluteFilePath);
173
+ if (this.graph.has(normalizedPath)) {
174
+ return this.graph.get(normalizedPath);
175
+ }
176
+ const node = new GraphNode(normalizedPath);
177
+ this.graph.set(normalizedPath, node);
178
+ return node;
179
+ }
180
+
181
+ /**
182
+ * Checks if an absolute file token path matches configuration ignore directives.
183
+ * Evaluates sub-path sequences exactly to prevent regular expression parsing drops.
184
+ */
185
+ isPathIgnored(absoluteFilePath) {
186
+ const relativeText = path.relative(this.cwd, absoluteFilePath);
187
+
188
+ for (const ignoredTarget of this.globallyIgnoredPaths) {
189
+ if (relativeText === ignoredTarget || relativeText.startsWith(path.join(ignoredTarget, path.sep))) {
190
+ return true;
191
+ }
192
+ // Handle explicit wildcard terminal indicators
193
+ if (ignoredTarget.endsWith('*')) {
194
+ const baseSegment = ignoredTarget.slice(0, -1);
195
+ if (relativeText.startsWith(baseSegment)) return true;
196
+ }
197
+ }
198
+ return false;
199
+ }
200
+
201
+ /**
202
+ * Processes the entire active dependency map to compile structural issue indices.
203
+ * Evaluates orphaned components, dead exports, and supply-chain threats.
204
+ */
205
+ generateSummaryReport() {
206
+ this.metrics.endTime = Date.now();
207
+ const durationSeconds = ((this.metrics.endTime - this.metrics.startTime) / 1000).toFixed(2);
208
+
209
+ const summary = {
210
+ executionDuration: `${durationSeconds}s`,
211
+ totalFilesProcessed: this.metrics.totalFilesScanned,
212
+ graphCacheOptimization: {
213
+ hits: this.metrics.cacheHits,
214
+ misses: this.metrics.cacheMisses,
215
+ ratio: this.metrics.totalFilesScanned > 0
216
+ ? `${((this.metrics.cacheHits / this.metrics.totalFilesScanned) * 100).toFixed(1)}%`
217
+ : '0%'
218
+ },
219
+ structuralIssuesDetected: {
220
+ deadFiles: [],
221
+ deadExports: [],
222
+ securityThreats: []
223
+ },
224
+ modificationsExecuted: {
225
+ filesUnlinked: this.metrics.prunedFilesCount,
226
+ exportsStripped: this.metrics.prunedExportsCount
227
+ }
228
+ };
229
+
230
+ for (const [filePath, node] of this.graph.entries()) {
231
+ // Skip package control files from standard structural dead-code checks
232
+ if (filePath.endsWith('package.json')) continue;
233
+ if (this.isPathIgnored(filePath)) continue;
234
+
235
+ const relativePath = path.relative(this.cwd, filePath);
236
+
237
+ // Category A: Completely orphaned components (no references, not library/framework entries)
238
+ if (node.incomingEdges.size === 0 && !node.isLibraryEntry && !node.isFrameworkContract) {
239
+ summary.structuralIssuesDetected.deadFiles.push(relativePath);
240
+ continue; // An orphaned file implies all internal sub-exports are dead; skip sub-checks
241
+ }
242
+
243
+ // Category B: Dead Named Exports inside active files
244
+ for (const [exportName, meta] of node.internalExports.entries()) {
245
+ this.metrics.totalSymbolsAnalyzed++;
246
+
247
+ // Skip entry configurations, global suppresses, and type-suppressed symbols
248
+ if (exportName === 'default' ||
249
+ this.globallyIgnoredSymbols.has(exportName) ||
250
+ node.localSuppressedRules.has(exportName)) {
251
+ continue;
252
+ }
253
+
254
+ if (!node.isSymbolReferencedExternally(exportName, this.graph)) {
255
+ const diagnosticLocation = node.symbolSourceLocations.get(exportName) || { line: 1, column: 1 };
256
+ summary.structuralIssuesDetected.deadExports.push({
257
+ file: relativePath,
258
+ symbol: exportName,
259
+ type: meta.type || 'named',
260
+ line: diagnosticLocation.line,
261
+ column: diagnosticLocation.column
262
+ });
263
+ }
264
+ }
265
+
266
+ // Category C: High-Entropy Password / Key Hardcode Vulnerabilities
267
+ if (node.securityThreats && node.securityThreats.length > 0) {
268
+ node.securityThreats.forEach(threat => {
269
+ summary.structuralIssuesDetected.securityThreats.push({
270
+ file: relativePath,
271
+ identifier: threat.variableKey,
272
+ riskCode: threat.riskCode || 'HIGH_RISK_SECRET_LEAK',
273
+ entropy: threat.entropyValue,
274
+ line: threat.line || 1
275
+ });
276
+ });
277
+ }
278
+ }
279
+
280
+ return summary;
281
+ }
282
+ }