pkg-scaffold 3.3.4 โ 3.3.5
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 +3 -4
- package/bin/cli.js +2 -2
- package/package.json +4 -3
- package/src/EngineContext.js +33 -19
- package/src/ast/ASTAnalyzer.js +105 -77
- package/src/ast/BarrelParser.js +24 -4
- package/src/ast/MagicDetector.js +111 -13
- package/src/ast/OxcAnalyzer.js +72 -13
- package/src/healing/GitSandbox.js +44 -122
- package/src/healing/SelfHealer.js +29 -130
- package/src/index.js +115 -98
- package/src/performance/WorkerTaskRunner.js +17 -5
- package/src/plugins/PluginRegistry.js +27 -1
- package/src/resolution/DependencyProfiler.js +261 -9
- package/src/resolution/WorkSpaceGraph.js +133 -34
- package/src/performance/SecretDetector.js +0 -378
package/src/ast/OxcAnalyzer.js
CHANGED
|
@@ -81,11 +81,15 @@ export class OxcAnalyzer {
|
|
|
81
81
|
const specifier = node.source.value;
|
|
82
82
|
fileNode.explicitImports.add(specifier);
|
|
83
83
|
|
|
84
|
+
// Track external package usage for dependency analysis
|
|
85
|
+
if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
|
|
86
|
+
fileNode.externalPackageUsage.add(this._extractPackageName(specifier));
|
|
87
|
+
}
|
|
88
|
+
|
|
84
89
|
if (node.specifiers) {
|
|
85
90
|
node.specifiers.forEach((spec) => {
|
|
86
91
|
if (spec.type === "ImportSpecifier") {
|
|
87
92
|
const importedName = spec.imported.name;
|
|
88
|
-
const localName = spec.local.name;
|
|
89
93
|
fileNode.importedSymbols.add(`${specifier}:${importedName}`);
|
|
90
94
|
} else if (spec.type === "ImportDefaultSpecifier") {
|
|
91
95
|
fileNode.importedSymbols.add(`${specifier}:default`);
|
|
@@ -103,20 +107,45 @@ export class OxcAnalyzer {
|
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
if (node.type === "ExportAllDeclaration") {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
const sourceSpecifier = node.source ? node.source.value : null;
|
|
111
|
+
if (sourceSpecifier) {
|
|
112
|
+
// FIX: Register re-export source as an explicit import so the graph linker
|
|
113
|
+
// creates an incomingEdge on the re-exported file.
|
|
114
|
+
fileNode.explicitImports.add(sourceSpecifier);
|
|
115
|
+
|
|
116
|
+
// Track external package usage from re-exports
|
|
117
|
+
if (!sourceSpecifier.startsWith('.') && !sourceSpecifier.startsWith('/')) {
|
|
118
|
+
fileNode.externalPackageUsage.add(this._extractPackageName(sourceSpecifier));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (node.exported) {
|
|
122
|
+
// export * as name from 'module'
|
|
123
|
+
const name = node.exported.name || (node.exported.type === "Identifier" ? node.exported.name : null);
|
|
124
|
+
if (name) {
|
|
125
|
+
fileNode.internalExports.set(name, { type: "re-export-namespace", source: sourceSpecifier, originalName: "*", start: node.start, end: node.end });
|
|
126
|
+
fileNode.importedSymbols.add(`${sourceSpecifier}:*`);
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
// export * from 'module'
|
|
130
|
+
fileNode.internalExports.set("*", { type: "re-export-all", source: sourceSpecifier });
|
|
131
|
+
// FIX: Register as wildcard importedSymbol so graph linker creates incomingEdge
|
|
132
|
+
fileNode.importedSymbols.add(`${sourceSpecifier}:*`);
|
|
133
|
+
}
|
|
113
134
|
}
|
|
114
135
|
return;
|
|
115
136
|
}
|
|
116
137
|
|
|
117
138
|
if (node.source) {
|
|
118
|
-
// Re-export
|
|
139
|
+
// Re-export with source: export { x } from 'module'
|
|
119
140
|
const specifier = node.source.value;
|
|
141
|
+
// FIX: Register re-export source as an explicit import
|
|
142
|
+
fileNode.explicitImports.add(specifier);
|
|
143
|
+
|
|
144
|
+
// Track external package usage from re-exports
|
|
145
|
+
if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
|
|
146
|
+
fileNode.externalPackageUsage.add(this._extractPackageName(specifier));
|
|
147
|
+
}
|
|
148
|
+
|
|
120
149
|
if (node.specifiers) {
|
|
121
150
|
node.specifiers.forEach((spec) => {
|
|
122
151
|
const exportedName = spec.exported.name;
|
|
@@ -128,6 +157,8 @@ export class OxcAnalyzer {
|
|
|
128
157
|
start: node.start,
|
|
129
158
|
end: node.end,
|
|
130
159
|
});
|
|
160
|
+
// FIX: Register as importedSymbol so barrel-tracer can resolve origin file
|
|
161
|
+
fileNode.importedSymbols.add(`${specifier}:${localName}`);
|
|
131
162
|
});
|
|
132
163
|
}
|
|
133
164
|
} else if (node.declaration) {
|
|
@@ -184,12 +215,28 @@ export class OxcAnalyzer {
|
|
|
184
215
|
}
|
|
185
216
|
|
|
186
217
|
handleCallExpression(node, fileNode) {
|
|
187
|
-
|
|
218
|
+
// Dynamic import(): import('./module')
|
|
219
|
+
if (node.callee.type === "Import" && node.arguments.length > 0) {
|
|
220
|
+
const arg = node.arguments[0];
|
|
221
|
+
if (arg.type === "StringLiteral") {
|
|
222
|
+
const specifier = arg.value;
|
|
223
|
+
fileNode.explicitImports.add(specifier);
|
|
224
|
+
fileNode.dynamicImports.add(specifier);
|
|
225
|
+
if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
|
|
226
|
+
fileNode.externalPackageUsage.add(this._extractPackageName(specifier));
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
// Non-literal dynamic import: record as calculated
|
|
230
|
+
if (fileNode.calculatedDynamicImports) {
|
|
231
|
+
fileNode.calculatedDynamicImports.push({ kind: arg.type, start: arg.start });
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
} else if (node.callee.type === "Identifier" && node.callee.name === "require" && node.arguments.length > 0 && node.arguments[0].type === "StringLiteral") {
|
|
188
235
|
const specifier = node.arguments[0].value;
|
|
189
236
|
fileNode.explicitImports.add(specifier);
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
237
|
+
if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
|
|
238
|
+
fileNode.externalPackageUsage.add(this._extractPackageName(specifier));
|
|
239
|
+
}
|
|
193
240
|
}
|
|
194
241
|
}
|
|
195
242
|
|
|
@@ -233,4 +280,16 @@ export class OxcAnalyzer {
|
|
|
233
280
|
});
|
|
234
281
|
}
|
|
235
282
|
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Extracts the root npm package name from an import specifier.
|
|
286
|
+
* Handles scoped packages (@scope/pkg) and subpath imports (pkg/utils, @scope/pkg/utils).
|
|
287
|
+
*/
|
|
288
|
+
_extractPackageName(specifier) {
|
|
289
|
+
if (specifier.startsWith('@')) {
|
|
290
|
+
const parts = specifier.split('/');
|
|
291
|
+
return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : specifier;
|
|
292
|
+
}
|
|
293
|
+
return specifier.split('/')[0];
|
|
294
|
+
}
|
|
236
295
|
}
|
|
@@ -1,160 +1,82 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execa } from 'execa';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
* Manages atomic
|
|
5
|
+
* Deterministic Version Control Guard for Structural Healing Operations.
|
|
6
|
+
* Manages atomic state rollbacks when automated refactoring breaks the build.
|
|
7
7
|
*/
|
|
8
8
|
export class GitSandbox {
|
|
9
9
|
constructor(context) {
|
|
10
10
|
this.context = context;
|
|
11
|
-
this.
|
|
12
|
-
this.
|
|
13
|
-
this.hasStashedChanges = false;
|
|
14
|
-
this.isGitRepository = false;
|
|
11
|
+
this.initialBranch = '';
|
|
12
|
+
this.healingBranch = `scaffold-healing-${Date.now()}`;
|
|
15
13
|
}
|
|
16
14
|
|
|
17
15
|
/**
|
|
18
|
-
*
|
|
16
|
+
* Captures the current repository state before applying structural modifications.
|
|
19
17
|
*/
|
|
20
|
-
async
|
|
21
|
-
this.verifyGitRepositoryPresence();
|
|
22
|
-
if (!this.isGitRepository) return;
|
|
23
|
-
|
|
18
|
+
async captureState() {
|
|
24
19
|
try {
|
|
25
|
-
|
|
26
|
-
this.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
// Check for uncommitted working directory changes
|
|
32
|
-
const statusOutput = execSync('git status --porcelain', {
|
|
33
|
-
cwd: this.context.cwd,
|
|
34
|
-
encoding: 'utf8'
|
|
35
|
-
}).trim();
|
|
36
|
-
|
|
37
|
-
if (statusOutput.length > 0) {
|
|
38
|
-
if (this.context.verbose) {
|
|
39
|
-
console.log('๐ฆ Working tree contains uncommitted changes. Stashing before running pipeline...');
|
|
40
|
-
}
|
|
41
|
-
execSync('git stash save "pkg-scaffold: Ephemeral Checkpoint Stash"', {
|
|
42
|
-
cwd: this.context.cwd,
|
|
43
|
-
stdio: 'ignore'
|
|
44
|
-
});
|
|
45
|
-
this.hasStashedChanges = true;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Generate an isolated tracking branch name
|
|
49
|
-
this.sandboxBranch = `scaffold-heal-${Date.now()}`;
|
|
50
|
-
execSync(`git checkout -b ${this.sandboxBranch}`, {
|
|
51
|
-
cwd: this.context.cwd,
|
|
52
|
-
stdio: 'ignore'
|
|
53
|
-
});
|
|
54
|
-
|
|
20
|
+
const { stdout } = await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: this.context.cwd });
|
|
21
|
+
this.initialBranch = stdout.trim();
|
|
22
|
+
|
|
23
|
+
// Create a temporary recovery branch
|
|
24
|
+
await execa('git', ['checkout', '-b', this.healingBranch], { cwd: this.context.cwd });
|
|
55
25
|
if (this.context.verbose) {
|
|
56
|
-
console.log(
|
|
26
|
+
console.log(`[Git] State captured in temporary branch: ${this.healingBranch}`);
|
|
57
27
|
}
|
|
58
|
-
} catch (
|
|
59
|
-
throw new Error(`Git
|
|
28
|
+
} catch (e) {
|
|
29
|
+
throw new Error(`Git state capture failed: Ensure the directory is a git repository. (${e.message})`);
|
|
60
30
|
}
|
|
61
31
|
}
|
|
62
32
|
|
|
63
33
|
/**
|
|
64
|
-
*
|
|
34
|
+
* Reverts all changes applied during the healing cycle if verification fails.
|
|
65
35
|
*/
|
|
66
|
-
async
|
|
67
|
-
if (!this.isGitRepository) return;
|
|
68
|
-
this.assertActiveIsolation();
|
|
69
|
-
|
|
36
|
+
async rollback() {
|
|
70
37
|
try {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}).trim();
|
|
78
|
-
|
|
79
|
-
if (diffIndex.length === 0) return; // No modifications to verify
|
|
80
|
-
|
|
81
|
-
execSync(`git commit -m "${commitMessage}" --no-verify`, {
|
|
82
|
-
cwd: this.context.cwd,
|
|
83
|
-
stdio: 'ignore'
|
|
84
|
-
});
|
|
85
|
-
} catch (error) {
|
|
86
|
-
throw new Error(`Failed to stage changes inside the sandbox: ${error.message}`);
|
|
38
|
+
console.log(`[Git] Rolling back structural modifications...`);
|
|
39
|
+
await execa('git', ['reset', '--hard', 'HEAD'], { cwd: this.context.cwd });
|
|
40
|
+
await execa('git', ['checkout', this.initialBranch], { cwd: this.context.cwd });
|
|
41
|
+
await execa('git', ['branch', '-D', this.healingBranch], { cwd: this.context.cwd });
|
|
42
|
+
} catch (e) {
|
|
43
|
+
console.error(`[Git] Critical rollback failure: ${e.message}`);
|
|
87
44
|
}
|
|
88
45
|
}
|
|
89
46
|
|
|
90
47
|
/**
|
|
91
|
-
*
|
|
48
|
+
* Finalizes the healing cycle by merging changes back to the original branch.
|
|
92
49
|
*/
|
|
93
|
-
async
|
|
94
|
-
if (!this.isGitRepository) return;
|
|
95
|
-
this.assertActiveIsolation();
|
|
96
|
-
|
|
50
|
+
async commit() {
|
|
97
51
|
try {
|
|
98
|
-
|
|
99
|
-
|
|
52
|
+
await execa('git', ['add', '.'], { cwd: this.context.cwd });
|
|
53
|
+
await execa('git', ['commit', '-m', 'chore: automated structural healing (pkg-scaffold)'], { cwd: this.context.cwd });
|
|
100
54
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
this.restorePreviousWorkingState();
|
|
111
|
-
} catch (error) {
|
|
112
|
-
throw new Error(`Failed to merge sandbox modifications into mainline branch: ${error.message}`);
|
|
55
|
+
await execa('git', ['checkout', this.initialBranch], { cwd: this.context.cwd });
|
|
56
|
+
await execa('git', ['merge', this.healingBranch], { cwd: this.context.cwd });
|
|
57
|
+
await execa('git', ['branch', '-D', this.healingBranch], { cwd: this.context.cwd });
|
|
58
|
+
|
|
59
|
+
if (this.context.verbose) {
|
|
60
|
+
console.log(`[Git] Structural modifications successfully merged into ${this.initialBranch}`);
|
|
61
|
+
}
|
|
62
|
+
} catch (e) {
|
|
63
|
+
console.error(`[Git] Commit failed: ${e.message}`);
|
|
113
64
|
}
|
|
114
65
|
}
|
|
115
66
|
|
|
116
67
|
/**
|
|
117
|
-
*
|
|
68
|
+
* Runs a verification command (e.g., npm test) to ensure structural integrity.
|
|
118
69
|
*/
|
|
119
|
-
async
|
|
120
|
-
if (!this.isGitRepository) return;
|
|
121
|
-
if (!this.sandboxBranch) return;
|
|
122
|
-
|
|
70
|
+
async verifyIntegrity() {
|
|
123
71
|
try {
|
|
72
|
+
const [cmd, ...args] = this.context.testCommand.split(' ');
|
|
73
|
+
await execa(cmd, args, { cwd: this.context.cwd });
|
|
74
|
+
return true;
|
|
75
|
+
} catch (e) {
|
|
124
76
|
if (this.context.verbose) {
|
|
125
|
-
console.
|
|
77
|
+
console.warn(`[Git] Integrity verification failed: ${e.message}`);
|
|
126
78
|
}
|
|
127
|
-
|
|
128
|
-
execSync('git add . && git reset --hard HEAD', { cwd: this.context.cwd, stdio: 'ignore' });
|
|
129
|
-
execSync(`git checkout ${this.originalBranch}`, { cwd: this.context.cwd, stdio: 'ignore' });
|
|
130
|
-
execSync(`git branch -D ${this.sandboxBranch}`, { cwd: this.context.cwd, stdio: 'ignore' });
|
|
131
|
-
|
|
132
|
-
this.restorePreviousWorkingState();
|
|
133
|
-
} catch (error) {
|
|
134
|
-
console.error(`๐จ Critical Recovery Error: Failed to drop sandbox branch cleanly: ${error.message}`);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
restorePreviousWorkingState() {
|
|
139
|
-
if (this.hasStashedChanges) {
|
|
140
|
-
execSync('git stash pop', { cwd: this.context.cwd, stdio: 'ignore' });
|
|
141
|
-
this.hasStashedChanges = false;
|
|
142
|
-
}
|
|
143
|
-
this.sandboxBranch = null;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
verifyGitRepositoryPresence() {
|
|
147
|
-
try {
|
|
148
|
-
execSync('git rev-parse --is-inside-work-tree', { cwd: this.context.cwd, stdio: 'ignore' });
|
|
149
|
-
this.isGitRepository = true;
|
|
150
|
-
} catch {
|
|
151
|
-
this.isGitRepository = false;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
assertActiveIsolation() {
|
|
156
|
-
if (!this.sandboxBranch) {
|
|
157
|
-
throw new Error('Git Sandbox operation error: No active sandbox tracking context exists.');
|
|
79
|
+
return false;
|
|
158
80
|
}
|
|
159
81
|
}
|
|
160
82
|
}
|
|
@@ -1,150 +1,49 @@
|
|
|
1
|
-
import
|
|
2
|
-
import path from 'path';
|
|
1
|
+
import ansis from 'ansis';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Automated Structural Healing Orchestrator.
|
|
5
|
+
* Manages the lifecycle of applying structural fixes and verifying codebase health.
|
|
6
|
+
* This is a deterministic engine and does not use AI/LLMs.
|
|
7
7
|
*/
|
|
8
8
|
export class SelfHealer {
|
|
9
|
-
constructor(context,
|
|
9
|
+
constructor(context, txManager, gitSandbox) {
|
|
10
10
|
this.context = context;
|
|
11
|
-
this.
|
|
11
|
+
this.txManager = txManager;
|
|
12
12
|
this.gitSandbox = gitSandbox;
|
|
13
|
-
this.executionTimeout = 45000; // 45-second execution timeout wall
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
|
-
*
|
|
18
|
-
* @param {Function}
|
|
16
|
+
* Executes a structural healing cycle with automatic rollback on failure.
|
|
17
|
+
* @param {Function} refactorLogic - Async function that stages structural changes
|
|
19
18
|
*/
|
|
20
|
-
async runSelfHealingLifecycle(
|
|
21
|
-
console.log('
|
|
19
|
+
async runSelfHealingLifecycle(refactorLogic) {
|
|
20
|
+
console.log(ansis.bold.blue('\n๐ฉน Initiating Automated Structural Healing Cycle...'));
|
|
22
21
|
|
|
23
|
-
// Step 1: Open the transaction boundaries
|
|
24
|
-
await this.gitSandbox.establishIsolationCheckpoint();
|
|
25
|
-
await this.transactionManager.begin();
|
|
26
|
-
|
|
27
22
|
try {
|
|
28
|
-
//
|
|
29
|
-
await
|
|
23
|
+
// 1. Capture current stable state
|
|
24
|
+
await this.gitSandbox.captureState();
|
|
25
|
+
|
|
26
|
+
// 2. Execute the provided refactoring logic (staging deletions/writes)
|
|
27
|
+
await refactorLogic();
|
|
30
28
|
|
|
31
|
-
//
|
|
32
|
-
await this.
|
|
29
|
+
// 3. Commit staged changes to disk
|
|
30
|
+
await this.txManager.commitAll();
|
|
33
31
|
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
// 4. Verify structural integrity (e.g., run tests)
|
|
33
|
+
console.log(ansis.dim('๐งช Verifying codebase integrity...'));
|
|
34
|
+
const isHealthy = await this.gitSandbox.verifyIntegrity();
|
|
36
35
|
|
|
37
|
-
if (
|
|
38
|
-
console.log('
|
|
39
|
-
await this.
|
|
40
|
-
await this.gitSandbox.acceptAndMergeOptimizations();
|
|
41
|
-
return true;
|
|
36
|
+
if (isHealthy) {
|
|
37
|
+
console.log(ansis.bold.green('โ
Structural integrity verified. Finalizing changes.'));
|
|
38
|
+
await this.gitSandbox.commit();
|
|
42
39
|
} else {
|
|
43
|
-
console.
|
|
44
|
-
await this.
|
|
45
|
-
|
|
40
|
+
console.log(ansis.bold.red('โ Structural integrity compromised. Rolling back changes.'));
|
|
41
|
+
await this.gitSandbox.rollback();
|
|
42
|
+
this.context.metrics.securityVulnerabilitiesMitigated = 0; // Reset metrics for failed cycle
|
|
46
43
|
}
|
|
47
|
-
} catch (
|
|
48
|
-
console.error(
|
|
49
|
-
await this.
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Spawns testing processes to detect exit code alerts or compiler trace metrics.
|
|
56
|
-
* @returns {Promise<boolean>} True if the test suite runs with an exit code of 0
|
|
57
|
-
*/
|
|
58
|
-
verifyProjectHealthStatus() {
|
|
59
|
-
return new Promise((resolve) => {
|
|
60
|
-
const commandTokens = this.context.testCommand.split(' ');
|
|
61
|
-
const executionBinary = commandTokens.shift();
|
|
62
|
-
|
|
63
|
-
const processBuffer = [];
|
|
64
|
-
const testProcess = spawn(executionBinary, commandTokens, {
|
|
65
|
-
cwd: this.context.cwd,
|
|
66
|
-
shell: true
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
// Implement an execution timeout monitor to prevent hanging test suites from blocking the pipeline
|
|
70
|
-
const timeoutWatch = setTimeout(() => {
|
|
71
|
-
console.warn('โฑ๏ธ Test execution exceeded timeout limit. Terminating subprocess...');
|
|
72
|
-
testProcess.kill('SIGKILL');
|
|
73
|
-
}, this.executionTimeout);
|
|
74
|
-
|
|
75
|
-
testProcess.stdout.on('data', (data) => {
|
|
76
|
-
processBuffer.push(data);
|
|
77
|
-
if (this.context.verbose) {
|
|
78
|
-
process.stdout.write(data);
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
testProcess.stderr.on('data', (data) => {
|
|
83
|
-
processBuffer.push(data);
|
|
84
|
-
if (this.context.verbose) {
|
|
85
|
-
process.stderr.write(data);
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
testProcess.on('close', (exitCode) => {
|
|
90
|
-
clearTimeout(timeoutWatch);
|
|
91
|
-
|
|
92
|
-
if (exitCode !== 0) {
|
|
93
|
-
const fullConsoleOutput = Buffer.concat(processBuffer).toString('utf8');
|
|
94
|
-
this.diagnoseErrorOutput(fullConsoleOutput);
|
|
95
|
-
resolve(false);
|
|
96
|
-
} else {
|
|
97
|
-
resolve(true);
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
testProcess.on('error', (err) => {
|
|
102
|
-
clearTimeout(timeoutWatch);
|
|
103
|
-
if (this.context.verbose) {
|
|
104
|
-
console.error(`Subprocess execution error: ${err.message}`);
|
|
105
|
-
}
|
|
106
|
-
resolve(false);
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Traces error codes and console log outputs to identify the root cause of a broken build.
|
|
113
|
-
*/
|
|
114
|
-
diagnoseErrorOutput(consoleLogOutput) {
|
|
115
|
-
console.log('\n๐ [Diagnostics] Reviewing error output...');
|
|
116
|
-
|
|
117
|
-
// Scan for standard TypeScript Compilation errors
|
|
118
|
-
const tsErrorPattern = /error\s+TS(\d+):\s+([\s\S]*?)$/m;
|
|
119
|
-
const tsMatches = consoleLogOutput.match(tsErrorPattern);
|
|
120
|
-
|
|
121
|
-
if (tsMatches) {
|
|
122
|
-
console.error(`โ Mapped Type Error Code [TS${tsMatches[1]}]: ${tsMatches[2].trim()}`);
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Scan for missing file imports or runtime resolution exceptions
|
|
127
|
-
const missingModulePattern = /Cannot find module '([^']+)'/i;
|
|
128
|
-
const moduleMatches = consoleLogOutput.match(missingModulePattern);
|
|
129
|
-
|
|
130
|
-
if (moduleMatches) {
|
|
131
|
-
console.error(`โ Missing Dependency Reference Pointer: Cannot locate element [${moduleMatches[1]}]`);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
console.error('โ Test suite execution failed. Check standard console output for details.');
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Automatically executes a rollback event, restoring both file states and Git status.
|
|
140
|
-
*/
|
|
141
|
-
async revertWorkspaceToSafeState() {
|
|
142
|
-
try {
|
|
143
|
-
await this.transactionManager.rollback();
|
|
144
|
-
await this.gitSandbox.rejectAndAbortOptimizations();
|
|
145
|
-
console.log('๐ [Self-Healing] Rollback complete. Original project states restored.');
|
|
146
|
-
} catch (recoveryError) {
|
|
147
|
-
console.error(`๐จ Fatal Recovery Error: Failed to restore prior workspace configuration: ${recoveryError.message}`);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error(ansis.bold.red(`\n๐จ Healing Cycle Aborted: ${error.message}`));
|
|
46
|
+
await this.gitSandbox.rollback();
|
|
148
47
|
}
|
|
149
48
|
}
|
|
150
49
|
}
|