@lisa.ai/agent 2.0.3 → 2.1.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/dist/commands/coverage.js +14 -12
- package/dist/commands/heal.js +49 -32
- package/dist/index.js +6 -0
- package/dist/services/llm.service.js +7 -1
- package/package.json +1 -1
|
@@ -64,6 +64,14 @@ async function coverageCommand(command, modelProvider, attempt = 1, maxRetries =
|
|
|
64
64
|
}
|
|
65
65
|
console.log(`\n[Lisa.ai Coverage] ${command} (Attempt ${attempt}/${maxRetries}) Using Model: ${modelProvider}`);
|
|
66
66
|
let executionPassed = true;
|
|
67
|
+
await (0, telemetry_service_1.reportTelemetry)({
|
|
68
|
+
projectId,
|
|
69
|
+
type: 'coverage',
|
|
70
|
+
filePath: 'global-test-suite',
|
|
71
|
+
modelUsed: modelProvider,
|
|
72
|
+
status: 'running',
|
|
73
|
+
details: 'Agent is currently executing testing suite and validating coverage drops...'
|
|
74
|
+
});
|
|
67
75
|
const runSilentlyAndIntercept = (cmd, printProgress = false) => {
|
|
68
76
|
return new Promise((resolve, reject) => {
|
|
69
77
|
// Node 21+ Deprecation Fix: When shell is true, pass the entire raw command string
|
|
@@ -77,16 +85,12 @@ async function coverageCommand(command, modelProvider, attempt = 1, maxRetries =
|
|
|
77
85
|
if (printProgress) {
|
|
78
86
|
const lines = text.split('\n');
|
|
79
87
|
for (const line of lines) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
process.stdout.write(`\r⏳ [Lisa.ai Testing] ${cleanLine}`);
|
|
89
|
-
}
|
|
88
|
+
const match = line.match(/Executed\s+(\d+)\s+of\s+(\d+)/);
|
|
89
|
+
if (match) {
|
|
90
|
+
const failedMatch = line.match(/(\d+)\s+FAILED/);
|
|
91
|
+
const failedCount = failedMatch ? failedMatch[1] : 0;
|
|
92
|
+
const progressStr = `\r⏳ [Lisa.ai Testing] Executed ${match[1]} of ${match[2]} (${failedCount} FAILED) `;
|
|
93
|
+
process.stdout.write(progressStr);
|
|
90
94
|
}
|
|
91
95
|
}
|
|
92
96
|
}
|
|
@@ -95,8 +99,6 @@ async function coverageCommand(command, modelProvider, attempt = 1, maxRetries =
|
|
|
95
99
|
stderrData += data.toString();
|
|
96
100
|
});
|
|
97
101
|
child.on('close', (code) => {
|
|
98
|
-
if (printProgress)
|
|
99
|
-
process.stdout.write('\n');
|
|
100
102
|
if (code === 0) {
|
|
101
103
|
resolve();
|
|
102
104
|
}
|
package/dist/commands/heal.js
CHANGED
|
@@ -44,6 +44,20 @@ const telemetry_service_1 = require("../services/telemetry.service");
|
|
|
44
44
|
const discovery_service_1 = require("../services/discovery.service");
|
|
45
45
|
const installer_service_1 = require("../services/installer.service");
|
|
46
46
|
const generator_service_1 = require("../services/generator.service");
|
|
47
|
+
function fetchSiblingContext(specPath) {
|
|
48
|
+
// Drop the .spec or .test extensions to find the actual Component / Service logic
|
|
49
|
+
const baseSiblingPath = specPath.replace(/\.(spec|test)\.(ts|js|jsx|tsx)$/, '.$2');
|
|
50
|
+
if (baseSiblingPath !== specPath && fs.existsSync(baseSiblingPath)) {
|
|
51
|
+
try {
|
|
52
|
+
console.log(`[Lisa.ai Context Engine] 🧠 Located sibling logic structure at ${baseSiblingPath}`);
|
|
53
|
+
return fs.readFileSync(baseSiblingPath, 'utf-8');
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
47
61
|
function isolateTestCommand(globalCommand, targetFile) {
|
|
48
62
|
const cmd = globalCommand.toLowerCase();
|
|
49
63
|
const parsed = path.parse(targetFile);
|
|
@@ -123,31 +137,14 @@ async function healCommand(command, modelProvider, attempt = 1, healedFilePath =
|
|
|
123
137
|
const text = data.toString();
|
|
124
138
|
stdoutData += text;
|
|
125
139
|
// [Lisa.ai Stream Interceptor]
|
|
126
|
-
// The user explicitly requested to see
|
|
127
|
-
//
|
|
128
|
-
|
|
129
|
-
const lines = text.split('\n');
|
|
130
|
-
for (const line of lines) {
|
|
131
|
-
if (line.includes('Executed')) {
|
|
132
|
-
const cleanLine = line.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '').trim();
|
|
133
|
-
if (process.stdout.isTTY) {
|
|
134
|
-
process.stdout.clearLine(0);
|
|
135
|
-
process.stdout.cursorTo(0);
|
|
136
|
-
process.stdout.write(`⏳ [Lisa.ai Testing] ${cleanLine}`);
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
process.stdout.write(`\r⏳ [Lisa.ai Testing] ${cleanLine}`);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
140
|
+
// The user explicitly requested to see a single clean loading line instead of
|
|
141
|
+
// thousands of milliseconds of Redundant 'Executed' loops.
|
|
142
|
+
// Output is fully swallowed, and instead we log a spinner on boot.
|
|
144
143
|
});
|
|
145
144
|
child.stderr?.on('data', (data) => {
|
|
146
145
|
stderrData += data.toString();
|
|
147
146
|
});
|
|
148
147
|
child.on('close', (code) => {
|
|
149
|
-
if (printProgress)
|
|
150
|
-
process.stdout.write('\n');
|
|
151
148
|
if (code === 0) {
|
|
152
149
|
resolve({ stdout: stdoutData, stderr: stderrData });
|
|
153
150
|
}
|
|
@@ -158,7 +155,9 @@ async function healCommand(command, modelProvider, attempt = 1, healedFilePath =
|
|
|
158
155
|
});
|
|
159
156
|
};
|
|
160
157
|
try {
|
|
161
|
-
|
|
158
|
+
if (true)
|
|
159
|
+
console.log(`⏳ [Lisa.ai Testing] Booting Headless Test Suite in background...`);
|
|
160
|
+
await runSilentlyAndIntercept(command, false); // False = output is suppressed natively
|
|
162
161
|
console.log(`\n✅ [Lisa.ai Success] Global Command executed successfully.`);
|
|
163
162
|
if (healedFilePath) {
|
|
164
163
|
await (0, git_service_1.createPullRequestForHeal)(healedFilePath);
|
|
@@ -190,22 +189,22 @@ async function healCommand(command, modelProvider, attempt = 1, healedFilePath =
|
|
|
190
189
|
let previousContext = undefined;
|
|
191
190
|
let generatedDetails = '';
|
|
192
191
|
const isolatedCommand = isolateTestCommand(command, filePath);
|
|
192
|
+
const siblingContext = fetchSiblingContext(absolutePath);
|
|
193
|
+
await (0, telemetry_service_1.reportTelemetry)({
|
|
194
|
+
projectId,
|
|
195
|
+
type: 'heal',
|
|
196
|
+
filePath: filePath,
|
|
197
|
+
modelUsed: modelProvider,
|
|
198
|
+
status: 'running',
|
|
199
|
+
details: 'Agent is currently analyzing and applying patches...'
|
|
200
|
+
});
|
|
193
201
|
while (localAttempt <= maxRetries && !isFixed) {
|
|
194
202
|
console.log(`\n[Lisa.ai Micro-Heal] Isolating ${filePath} (Attempt ${localAttempt}/${maxRetries})`);
|
|
195
203
|
const fileContent = fs.readFileSync(absolutePath, 'utf-8');
|
|
196
|
-
const fixedCode = await (0, llm_service_1.askLisaForFix)(filePath, fileContent, currentErrorLog, modelProvider, apiKey, previousContext);
|
|
204
|
+
const fixedCode = await (0, llm_service_1.askLisaForFix)(filePath, fileContent, currentErrorLog, modelProvider, apiKey, previousContext, siblingContext);
|
|
197
205
|
fs.writeFileSync(absolutePath, fixedCode, 'utf-8');
|
|
198
206
|
console.log(`[Lisa.ai Micro-Heal] Applied isolated patch to ${filePath}`);
|
|
199
207
|
generatedDetails = `### Auto-Heal Analysis Triggered\n**Caught Error:**\n\`\`\`bash\n${currentErrorLog}\n\`\`\`\n\n**Applied Fix (${modelProvider}):**\n\`\`\`typescript\n${fixedCode}\n\`\`\``;
|
|
200
|
-
// Dispatch telemetry instantly so the dashboard updates exactly when the file is patched natively
|
|
201
|
-
await (0, telemetry_service_1.reportTelemetry)({
|
|
202
|
-
projectId,
|
|
203
|
-
type: 'heal',
|
|
204
|
-
filePath: filePath,
|
|
205
|
-
modelUsed: modelProvider,
|
|
206
|
-
status: 'success',
|
|
207
|
-
details: generatedDetails
|
|
208
|
-
});
|
|
209
208
|
try {
|
|
210
209
|
// Verify the isolated file silently without exploding the Global terminal with 400 other spec errors
|
|
211
210
|
console.log(`[Lisa.ai Micro-Heal] Verifying fix with isolated command: ${isolatedCommand}`);
|
|
@@ -223,8 +222,26 @@ async function healCommand(command, modelProvider, attempt = 1, healedFilePath =
|
|
|
223
222
|
localAttempt++;
|
|
224
223
|
}
|
|
225
224
|
}
|
|
226
|
-
if (
|
|
225
|
+
if (isFixed) {
|
|
226
|
+
await (0, telemetry_service_1.reportTelemetry)({
|
|
227
|
+
projectId,
|
|
228
|
+
type: 'heal',
|
|
229
|
+
filePath: filePath,
|
|
230
|
+
modelUsed: modelProvider,
|
|
231
|
+
status: 'success',
|
|
232
|
+
details: generatedDetails
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
227
236
|
console.warn(`\n[Lisa.ai Auto-Heal] ⚠️ Agent failed to locally heal ${filePath} after ${maxRetries} isolated attempts. Marking as Skipped to avoid infinite Loop.`);
|
|
237
|
+
await (0, telemetry_service_1.reportTelemetry)({
|
|
238
|
+
projectId,
|
|
239
|
+
type: 'heal',
|
|
240
|
+
filePath: filePath,
|
|
241
|
+
modelUsed: modelProvider,
|
|
242
|
+
status: 'error',
|
|
243
|
+
details: `Agent exhausted all ${maxRetries} attempts without success.\n\n` + generatedDetails
|
|
244
|
+
});
|
|
228
245
|
skipLedger.push(filePath);
|
|
229
246
|
// Advanced Path Neutralization for generic JS tests across the V2 Ecosystem Architecture
|
|
230
247
|
if (filePath.match(/\.(spec|test)\.(ts|js|tsx|jsx|vue)$/)) {
|
package/dist/index.js
CHANGED
|
@@ -35,6 +35,9 @@ program
|
|
|
35
35
|
process.exit(1);
|
|
36
36
|
}
|
|
37
37
|
console.log(`[Lisa.ai Agent] Dynamic Config Loaded: Provider=${config.modelProvider}, MaxRetries=${config.maxRetries}`);
|
|
38
|
+
if (config.maxRetries < 5) {
|
|
39
|
+
console.warn(`\n⚠️ [Lisa.ai Warning] Your Dashboard Analytics Config has maxRetries set to ${config.maxRetries}. Consider increasing this to 5+ in the WEB UI for complex Angular Healing!`);
|
|
40
|
+
}
|
|
38
41
|
model = config.modelProvider;
|
|
39
42
|
maxRetries = config.maxRetries;
|
|
40
43
|
apiKey = config.apiKey;
|
|
@@ -63,6 +66,9 @@ program
|
|
|
63
66
|
process.exit(1);
|
|
64
67
|
}
|
|
65
68
|
console.log(`[Lisa.ai Agent] Dynamic Config Loaded: Provider=${config.modelProvider}, MaxRetries=${config.maxRetries}`);
|
|
69
|
+
if (config.maxRetries < 5) {
|
|
70
|
+
console.warn(`\n⚠️ [Lisa.ai Warning] Your Dashboard Analytics Config has maxRetries set to ${config.maxRetries}. Consider increasing this to 5+ in the WEB UI for complex Angular Healing!`);
|
|
71
|
+
}
|
|
66
72
|
model = config.modelProvider;
|
|
67
73
|
maxRetries = config.maxRetries;
|
|
68
74
|
apiKey = config.apiKey;
|
|
@@ -64,7 +64,7 @@ function getProvider(provider, apiKey) {
|
|
|
64
64
|
throw new Error('No Google API key provided by local ENV or Control Plane');
|
|
65
65
|
return (0, google_1.createGoogleGenerativeAI)({ apiKey: key })('gemini-1.5-pro-latest');
|
|
66
66
|
}
|
|
67
|
-
async function askLisaForFix(filePath, fileContent, errorLog, modelProvider, apiKey, previousFixAttempt) {
|
|
67
|
+
async function askLisaForFix(filePath, fileContent, errorLog, modelProvider, apiKey, previousFixAttempt, siblingContext) {
|
|
68
68
|
console.log(`[Lisa.ai Auto-Heal] Requesting fix from ${modelProvider} for ${filePath}...`);
|
|
69
69
|
const model = getProvider(modelProvider, apiKey);
|
|
70
70
|
let prompt = `You are Lisa.ai, an autonomous CI/CD expert platform.
|
|
@@ -83,6 +83,12 @@ ${fileContent}
|
|
|
83
83
|
4. CRITICAL AGILE RULE: If the error is 'Cannot find module' or 'Property DOES NOT EXIST' and you do NOT know the correct import path or cannot resolve it from the context, you MUST silently comment out the failing import and completely comment out/stub the code that relies on it. Your ultimate goal is to make the file compile successfully so the Agent can proceed to the next file.
|
|
84
84
|
5. FRAMEWORK TEST RULE: If fixing a testing file (\`.spec\` or \`.test\`) and an Angular error happens like 'NullInjectorError: No provider for X', you MUST add X to the 'providers' array in 'TestBed.configureTestingModule' or import the required testing module. If a React testing library error arises, fix the render context.
|
|
85
85
|
6. Return the code wrapped in a markdown code block (\`\`\`typescript ... \`\`\`). Do not include any explanation or intro text.`;
|
|
86
|
+
if (siblingContext) {
|
|
87
|
+
prompt += `\n\n--- Sibling Component / Service Context ---
|
|
88
|
+
You are fixing a \`.spec\` test file. Here is the actual implementation code for the component you are testing.
|
|
89
|
+
Use this to identify EXACTLY which imports, Services, and Variables need to be mocked inside your \`TestBed\`.
|
|
90
|
+
${siblingContext}`;
|
|
91
|
+
}
|
|
86
92
|
if (previousFixAttempt) {
|
|
87
93
|
console.log(`[Lisa.ai Auto-Heal] Warning! Agent is looping on ${filePath}. Injecting previous failed context...`);
|
|
88
94
|
prompt += `\n\n--- CRITICAL WARNING ---\nYou previously attempted to fix this file but the compiler REJECTED your fix!
|