@lisa.ai/agent 1.0.3 ā 1.0.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/dist/commands/coverage.js +9 -6
- package/dist/commands/heal.js +3 -3
- package/dist/index.js +6 -2
- package/dist/services/llm.service.js +22 -33
- package/package.json +1 -1
|
@@ -41,7 +41,7 @@ const coverage_parser_1 = require("../utils/coverage.parser");
|
|
|
41
41
|
const llm_service_1 = require("../services/llm.service");
|
|
42
42
|
const heal_1 = require("./heal");
|
|
43
43
|
const telemetry_service_1 = require("../services/telemetry.service");
|
|
44
|
-
async function coverageCommand(command, modelProvider, attempt = 1, maxRetries = 3, projectId = 'local') {
|
|
44
|
+
async function coverageCommand(command, modelProvider, attempt = 1, maxRetries = 3, projectId = 'local', apiKey) {
|
|
45
45
|
console.log(`\n[Lisa.ai Coverage] ${command} (Attempt ${attempt}/${maxRetries}) Using Model: ${modelProvider}`);
|
|
46
46
|
let executionPassed = true;
|
|
47
47
|
try {
|
|
@@ -53,9 +53,12 @@ async function coverageCommand(command, modelProvider, attempt = 1, maxRetries =
|
|
|
53
53
|
catch (error) {
|
|
54
54
|
console.log(`\nā [Lisa.ai Coverage] Tests failed. Delegating to Auto-Heal...`);
|
|
55
55
|
// If tests themselves fail to compile or run, we fallback exactly to auto-heal
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
await (0, heal_1.healCommand)(command, modelProvider, 1, null, maxRetries, projectId, undefined, apiKey);
|
|
57
|
+
// Once the auto-heal engine successfully returns, compilation errors are fixed!
|
|
58
|
+
// We gracefully restart the coverage engine to evaluate the newly working tests.
|
|
59
|
+
console.log(`\nš [Lisa.ai Coverage] Auto-Heal successful. Restarting coverage analysis...`);
|
|
60
|
+
await coverageCommand(command, modelProvider, attempt + 1, maxRetries, projectId, apiKey);
|
|
61
|
+
return;
|
|
59
62
|
}
|
|
60
63
|
// 2. If it passed without compile/runtime errors, evaluate coverage
|
|
61
64
|
if (executionPassed) {
|
|
@@ -82,7 +85,7 @@ async function coverageCommand(command, modelProvider, attempt = 1, maxRetries =
|
|
|
82
85
|
const absoluteTarget = path.resolve(process.cwd(), targetFilePath);
|
|
83
86
|
console.log(`[Lisa.ai Coverage] Requesting generated test suite for ${targetFilePath}...`);
|
|
84
87
|
const fileContent = fs.readFileSync(absoluteTarget, 'utf-8');
|
|
85
|
-
const testCode = await (0, llm_service_1.generateTestForFile)(targetFilePath, fileContent, modelProvider);
|
|
88
|
+
const testCode = await (0, llm_service_1.generateTestForFile)(targetFilePath, fileContent, modelProvider, apiKey);
|
|
86
89
|
// Derive spec name from original file name (e.g. app.component.ts -> app.component.spec.ts)
|
|
87
90
|
const parsedPath = path.parse(absoluteTarget);
|
|
88
91
|
const specPath = path.join(parsedPath.dir, `${parsedPath.name}.spec${parsedPath.ext}`);
|
|
@@ -97,7 +100,7 @@ async function coverageCommand(command, modelProvider, attempt = 1, maxRetries =
|
|
|
97
100
|
details: `### Missing Logic Coverage Detected\n**Auto-Generated Specification Suite (${modelProvider}):**\n\`\`\`typescript\n${testCode}\n\`\`\``
|
|
98
101
|
});
|
|
99
102
|
// 4. Recursive iteration to verify newly written test and hunt for next gap
|
|
100
|
-
await coverageCommand(command, modelProvider, attempt + 1, maxRetries, projectId);
|
|
103
|
+
await coverageCommand(command, modelProvider, attempt + 1, maxRetries, projectId, apiKey);
|
|
101
104
|
}
|
|
102
105
|
catch (e) {
|
|
103
106
|
console.error(`[Lisa.ai Coverage loop failure]:`, e.message);
|
package/dist/commands/heal.js
CHANGED
|
@@ -41,7 +41,7 @@ const parser_1 = require("../utils/parser");
|
|
|
41
41
|
const llm_service_1 = require("../services/llm.service");
|
|
42
42
|
const git_service_1 = require("../services/git.service");
|
|
43
43
|
const telemetry_service_1 = require("../services/telemetry.service");
|
|
44
|
-
async function healCommand(command, modelProvider, attempt = 1, healedFilePath = null, maxRetries = 3, projectId = 'local', lastFixDetails) {
|
|
44
|
+
async function healCommand(command, modelProvider, attempt = 1, healedFilePath = null, maxRetries = 3, projectId = 'local', lastFixDetails, apiKey) {
|
|
45
45
|
console.log(`\n[Lisa.ai Executing] ${command} (Attempt ${attempt}/${maxRetries}) Using Model: ${modelProvider}`);
|
|
46
46
|
try {
|
|
47
47
|
// Execute command synchronously
|
|
@@ -88,12 +88,12 @@ async function healCommand(command, modelProvider, attempt = 1, healedFilePath =
|
|
|
88
88
|
// Read file contents to send to LLM
|
|
89
89
|
const fileContent = fs.readFileSync(absolutePath, 'utf-8');
|
|
90
90
|
// Call LLM for a fix
|
|
91
|
-
const fixedCode = await (0, llm_service_1.askLisaForFix)(filePath, fileContent, errorLog, modelProvider);
|
|
91
|
+
const fixedCode = await (0, llm_service_1.askLisaForFix)(filePath, fileContent, errorLog, modelProvider, apiKey);
|
|
92
92
|
// Write the fix to the file
|
|
93
93
|
fs.writeFileSync(absolutePath, fixedCode, 'utf-8');
|
|
94
94
|
console.log(`[Lisa.ai Auto-Heal] Applied patch to ${filePath}`);
|
|
95
95
|
// Recursive retry, passing the healed file path state
|
|
96
96
|
const generatedDetails = `### Auto-Heal Analysis Triggered\n**Caught Error:**\n\`\`\`bash\n${errorLog}\n\`\`\`\n\n**Applied Fix (${modelProvider}):**\n\`\`\`typescript\n${fixedCode}\n\`\`\``;
|
|
97
|
-
await healCommand(command, modelProvider, attempt + 1, filePath, maxRetries, projectId, generatedDetails);
|
|
97
|
+
await healCommand(command, modelProvider, attempt + 1, filePath, maxRetries, projectId, generatedDetails, apiKey);
|
|
98
98
|
}
|
|
99
99
|
}
|
package/dist/index.js
CHANGED
|
@@ -19,6 +19,7 @@ program
|
|
|
19
19
|
.action(async (options) => {
|
|
20
20
|
let maxRetries = 3;
|
|
21
21
|
let model = options.model;
|
|
22
|
+
let apiKey = undefined;
|
|
22
23
|
if (options.projectId) {
|
|
23
24
|
const config = await (0, config_service_1.fetchRemoteConfig)(options.projectId);
|
|
24
25
|
if (!config) {
|
|
@@ -28,12 +29,13 @@ program
|
|
|
28
29
|
console.log(`[Lisa.ai Agent] Dynamic Config Loaded: Provider=${config.modelProvider}, MaxRetries=${config.maxRetries}`);
|
|
29
30
|
model = config.modelProvider;
|
|
30
31
|
maxRetries = config.maxRetries;
|
|
32
|
+
apiKey = config.apiKey;
|
|
31
33
|
if (config.autoHealEnabled === false) {
|
|
32
34
|
console.log(`[Lisa.ai Agent] Auto-heal is disabled by Control Plane.`);
|
|
33
35
|
process.exit(1);
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
|
-
await (0, heal_1.healCommand)(options.command, model, 1, null, maxRetries, options.projectId || 'local');
|
|
38
|
+
await (0, heal_1.healCommand)(options.command, model, 1, null, maxRetries, options.projectId || 'local', undefined, apiKey);
|
|
37
39
|
});
|
|
38
40
|
program
|
|
39
41
|
.command('coverage')
|
|
@@ -44,6 +46,7 @@ program
|
|
|
44
46
|
.action(async (options) => {
|
|
45
47
|
let maxRetries = 3;
|
|
46
48
|
let model = options.model;
|
|
49
|
+
let apiKey = undefined;
|
|
47
50
|
if (options.projectId) {
|
|
48
51
|
const config = await (0, config_service_1.fetchRemoteConfig)(options.projectId);
|
|
49
52
|
if (!config) {
|
|
@@ -53,7 +56,8 @@ program
|
|
|
53
56
|
console.log(`[Lisa.ai Agent] Dynamic Config Loaded: Provider=${config.modelProvider}, MaxRetries=${config.maxRetries}`);
|
|
54
57
|
model = config.modelProvider;
|
|
55
58
|
maxRetries = config.maxRetries;
|
|
59
|
+
apiKey = config.apiKey;
|
|
56
60
|
}
|
|
57
|
-
await (0, coverage_1.coverageCommand)(options.command, model, 1, maxRetries, options.projectId || 'local');
|
|
61
|
+
await (0, coverage_1.coverageCommand)(options.command, model, 1, maxRetries, options.projectId || 'local', apiKey);
|
|
58
62
|
});
|
|
59
63
|
program.parse(process.argv);
|
|
@@ -44,24 +44,27 @@ const dotenv = __importStar(require("dotenv"));
|
|
|
44
44
|
// Force dotenv to load the `.env` file from the agent's root directory
|
|
45
45
|
// regardless of which folder the user is currently running the CLI inside.
|
|
46
46
|
dotenv.config({ path: path.resolve(__dirname, '../../.env') });
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
break;
|
|
60
|
-
default:
|
|
61
|
-
console.warn(`[Lisa.ai Warning] Unrecognized modelProvider '${modelProvider}', defaulting to 'gemini'`);
|
|
62
|
-
model = (0, google_1.google)('gemini-1.5-pro-latest');
|
|
63
|
-
break;
|
|
47
|
+
function getProvider(provider, apiKey) {
|
|
48
|
+
if (provider === 'claude') {
|
|
49
|
+
const key = apiKey || process.env.ANTHROPIC_API_KEY;
|
|
50
|
+
if (!key)
|
|
51
|
+
throw new Error('No Anthropic API key provided by local ENV or Control Plane');
|
|
52
|
+
return (0, anthropic_1.createAnthropic)({ apiKey: key })('claude-3-haiku-20240307');
|
|
53
|
+
}
|
|
54
|
+
if (provider === 'openai') {
|
|
55
|
+
const key = apiKey || process.env.OPENAI_API_KEY;
|
|
56
|
+
if (!key)
|
|
57
|
+
throw new Error('No OpenAI API key provided by local ENV or Control Plane');
|
|
58
|
+
return (0, openai_1.createOpenAI)({ apiKey: key })('gpt-3.5-turbo');
|
|
64
59
|
}
|
|
60
|
+
const key = apiKey || process.env.GOOGLE_GENERATIVE_AI_API_KEY;
|
|
61
|
+
if (!key)
|
|
62
|
+
throw new Error('No Google API key provided by local ENV or Control Plane');
|
|
63
|
+
return (0, google_1.createGoogleGenerativeAI)({ apiKey: key })('gemini-1.5-pro-latest');
|
|
64
|
+
}
|
|
65
|
+
async function askLisaForFix(filePath, fileContent, errorLog, modelProvider, apiKey) {
|
|
66
|
+
console.log(`[Lisa.ai Auto-Heal] Requesting fix from ${modelProvider} for ${filePath}...`);
|
|
67
|
+
const model = getProvider(modelProvider, apiKey);
|
|
65
68
|
const prompt = `You are Lisa.ai, an autonomous CI/CD expert platform.
|
|
66
69
|
A build/compilation error occurred. Your task is to fix the provided file so that the error resolves.
|
|
67
70
|
|
|
@@ -88,23 +91,9 @@ ${fileContent}
|
|
|
88
91
|
const match = text.match(/```(?:typescript|ts)?\n([\s\S]*?)```/);
|
|
89
92
|
return match ? match[1].trim() : text.trim();
|
|
90
93
|
}
|
|
91
|
-
async function generateTestForFile(sourceFilePath, sourceFileContent, modelProvider) {
|
|
94
|
+
async function generateTestForFile(sourceFilePath, sourceFileContent, modelProvider, apiKey) {
|
|
92
95
|
console.log(`[Lisa.ai Coverage] Requesting test generation from ${modelProvider} for ${sourceFilePath}...`);
|
|
93
|
-
|
|
94
|
-
switch (modelProvider) {
|
|
95
|
-
case 'openai':
|
|
96
|
-
model = (0, openai_1.openai)('gpt-4o');
|
|
97
|
-
break;
|
|
98
|
-
case 'claude':
|
|
99
|
-
model = (0, anthropic_1.anthropic)('claude-3-haiku-20240307');
|
|
100
|
-
break;
|
|
101
|
-
case 'gemini':
|
|
102
|
-
model = (0, google_1.google)('gemini-1.5-pro-latest');
|
|
103
|
-
break;
|
|
104
|
-
default:
|
|
105
|
-
model = (0, google_1.google)('gemini-1.5-pro-latest');
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
96
|
+
const model = getProvider(modelProvider, apiKey);
|
|
108
97
|
const prompt = `You are Lisa.ai, an autonomous CI/CD expert platform.
|
|
109
98
|
A TypeScript/Angular file lacks 100% test coverage. Your task is to generate a comprehensive Jest unit test file (.spec.ts) covering all branches, lines, and functions.
|
|
110
99
|
|