@lisa.ai/agent 2.2.1 → 2.2.2

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.
@@ -206,15 +206,26 @@ async function coverageCommand(command, modelProvider, attempt = 1, maxRetries =
206
206
  }
207
207
  let testCode = '';
208
208
  const fileContent = fs.readFileSync(absoluteTarget, 'utf-8');
209
- if (existingSpecContent) {
210
- console.log(`[Lisa.ai Coverage] Existing test suite discovered for ${targetFilePath}. Requesting single-pass logic coverage append...`);
211
- // Bug 8 fix: pass detectedFramework so the LLM generates correct syntax for the project's framework.
212
- testCode = await (0, llm_service_1.updateTestForFile)(targetFilePath, fileContent, path.relative(process.cwd(), targetSpecPath), existingSpecContent, modelProvider, apiKey, detectedFramework);
209
+ // Bug 9 fix: wrap the LLM call in its own try-catch so that a context-overflow
210
+ // or garbage response for ONE file doesn't crash the whole coverage loop.
211
+ // On failure we write a minimal placeholder spec so:
212
+ // (a) Jest doesn't error on a missing/corrupt file
213
+ // (b) The coverage report records this file so it won't be re-attempted
214
+ // (c) The loop continues straight to the next uncovered file.
215
+ try {
216
+ if (existingSpecContent) {
217
+ console.log(`[Lisa.ai Coverage] Existing test suite discovered for ${targetFilePath}. Requesting single-pass logic coverage append...`);
218
+ testCode = await (0, llm_service_1.updateTestForFile)(targetFilePath, fileContent, path.relative(process.cwd(), targetSpecPath), existingSpecContent, modelProvider, apiKey, detectedFramework);
219
+ }
220
+ else {
221
+ console.log(`[Lisa.ai Coverage] Requesting newly generated test suite for ${targetFilePath}...`);
222
+ testCode = await (0, llm_service_1.generateTestForFile)(targetFilePath, fileContent, modelProvider, apiKey, detectedFramework);
223
+ }
213
224
  }
214
- else {
215
- console.log(`[Lisa.ai Coverage] Requesting newly generated test suite for ${targetFilePath}...`);
216
- // Bug 8 fix: pass detectedFramework so the LLM generates correct syntax for the project's framework.
217
- testCode = await (0, llm_service_1.generateTestForFile)(targetFilePath, fileContent, modelProvider, apiKey, detectedFramework);
225
+ catch (llmError) {
226
+ console.warn(`[Lisa.ai Coverage] Skipping ${targetFilePath} LLM call failed: ${llmError.message}`);
227
+ // Placeholder keeps Jest happy and marks the file as "attempted"
228
+ testCode = `// Lisa.ai: Auto-skipped — ${llmError.message}\ntest('placeholder', () => { expect(true).toBe(true); });\n`;
218
229
  }
219
230
  fs.writeFileSync(targetSpecPath, testCode, 'utf-8');
220
231
  console.log(`[Lisa.ai Coverage] Wrote Spec File to ${targetSpecPath}`);
@@ -109,7 +109,33 @@ class AutoDiscoveryService {
109
109
  return untested;
110
110
  const files = fs.readdirSync(dir);
111
111
  const ignoreDirs = ['node_modules', 'dist', 'build', '.git', '.angular', 'coverage', 'public', 'assets'];
112
- const ignoreFiles = ['main.ts', 'index.ts', 'app.config.ts', 'app.routes.ts', 'styles.css', 'styles.scss', 'tailwind.config.js', 'eslint.config.js'];
112
+ const ignoreFiles = [
113
+ // Angular / framework entry points
114
+ 'main.ts', 'index.ts', 'app.config.ts', 'app.routes.ts',
115
+ 'styles.css', 'styles.scss',
116
+ // Common config files (exact names)
117
+ 'tailwind.config.js', 'tailwind.config.ts',
118
+ 'eslint.config.js', 'eslint.config.ts',
119
+ 'jest.config.js', 'jest.config.ts', 'jest.config.mjs',
120
+ 'jest.setup.js', 'jest.setup.ts',
121
+ 'webpack.config.js', 'webpack.config.ts',
122
+ 'babel.config.js', 'babel.config.ts',
123
+ 'rollup.config.js', 'rollup.config.ts',
124
+ 'vite.config.js', 'vite.config.ts',
125
+ 'vitest.config.js', 'vitest.config.ts',
126
+ 'karma.conf.js', 'karma.config.js',
127
+ 'prettier.config.js', 'prettier.config.ts',
128
+ 'postcss.config.js', 'postcss.config.ts',
129
+ 'gulpfile.js', 'Gulpfile.js',
130
+ // Dotfile configs often present in Node projects
131
+ '.eslintrc.js', '.eslintrc.ts', '.babelrc.js',
132
+ ];
133
+ // Pattern-based exclusions: dotfiles and *.config.* files not in the list above
134
+ const ignorePatterns = [
135
+ /^\./, // any dotfile (e.g. .eslintrc.js, .prettierrc.js)
136
+ /\.config\.(js|ts|mjs|cjs)$/, // foo.config.js / foo.config.ts
137
+ /\.conf\.(js|ts|mjs|cjs)$/, // foo.conf.js
138
+ ];
113
139
  for (const file of files) {
114
140
  const fullPath = path.join(dir, file);
115
141
  if (ignoreDirs.includes(file))
@@ -127,6 +153,8 @@ class AutoDiscoveryService {
127
153
  else if (file.match(/\.(ts|tsx|js|jsx|vue)$/) && !file.includes('.spec.') && !file.includes('.test.')) {
128
154
  if (ignoreFiles.includes(file))
129
155
  continue;
156
+ if (ignorePatterns.some(p => p.test(file)))
157
+ continue;
130
158
  const ext = path.extname(file);
131
159
  const base = file.slice(0, -ext.length);
132
160
  const possibleSpecs = [
@@ -44,6 +44,26 @@ const google_1 = require("@ai-sdk/google");
44
44
  const path = __importStar(require("path"));
45
45
  const dotenv = __importStar(require("dotenv"));
46
46
  dotenv.config({ path: path.resolve(__dirname, '../../.env'), quiet: true });
47
+ // Context-window guard: large files cause the LLM to hit its input limit and return
48
+ // its internal context-compression prompt instead of code, corrupting the spec file.
49
+ // 15 000 chars ≈ ~4 000 tokens — safe for all supported models.
50
+ const MAX_SOURCE_CHARS = 15000;
51
+ function truncateSource(content, label) {
52
+ if (content.length <= MAX_SOURCE_CHARS)
53
+ return content;
54
+ console.warn(`[Lisa.ai LLM] ${label} is ${content.length} chars — truncating to ${MAX_SOURCE_CHARS} to stay within context window.`);
55
+ return content.slice(0, MAX_SOURCE_CHARS) + '\n\n// ... (truncated)';
56
+ }
57
+ // Sanity-check the LLM response before it gets written to disk.
58
+ // If the response contains none of the standard test primitives it is almost certainly
59
+ // a meta-prompt leak or hallucination — throw so the caller can write a placeholder
60
+ // instead of permanently corrupting the spec file.
61
+ function assertLooksLikeTestCode(text, filePath) {
62
+ const testKeywords = /\b(describe|it\(|test\(|expect\(|beforeEach|afterEach|suite|assert\.|cy\.)\b/;
63
+ if (!testKeywords.test(text)) {
64
+ throw new Error(`LLM returned a non-code response for ${filePath} (possible context overflow). Skipping to prevent file corruption.`);
65
+ }
66
+ }
47
67
  function getProvider(provider, apiKey) {
48
68
  if (provider === 'claude') {
49
69
  const key = apiKey || process.env.ANTHROPIC_API_KEY;
@@ -125,7 +145,7 @@ async function generateTestForFile(sourceFilePath, sourceFileContent, modelProvi
125
145
  : "3. Include all necessary imports assuming a standard testing framework (Jest/Karma/Vitest) is available.\n";
126
146
  const prompt = "You are Lisa.ai, an autonomous CI/CD expert platform.\n" +
127
147
  "A source file lacks 100% test coverage. Your task is to generate a comprehensive testing suite covering all branches, lines, and functions.\n\n" +
128
- "--- Target File Content (" + sourceFilePath + ") ---\n" + sourceFileContent + "\n\n" +
148
+ "--- Target File Content (" + sourceFilePath + ") ---\n" + truncateSource(sourceFileContent, sourceFilePath) + "\n\n" +
129
149
  "--- Constraints ---\n" +
130
150
  "1. Return the generated test code wrapped in a markdown code block (```typescript ... ```).\n" +
131
151
  "2. Do not include any explanation or intro text.\n" +
@@ -136,7 +156,9 @@ async function generateTestForFile(sourceFilePath, sourceFileContent, modelProvi
136
156
  prompt,
137
157
  });
138
158
  const match = text.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);
139
- return match ? match[1].trim() : text.trim();
159
+ const result = match ? match[1].trim() : text.trim();
160
+ assertLooksLikeTestCode(result, sourceFilePath);
161
+ return result;
140
162
  }
141
163
  async function updateTestForFile(sourceFilePath, sourceFileContent, testFilePath, existingTestContent, modelProvider, apiKey, framework) {
142
164
  console.log(`[Lisa.ai Coverage] Requesting test update from ${modelProvider} for ${sourceFilePath}...`);
@@ -148,7 +170,7 @@ async function updateTestForFile(sourceFilePath, sourceFileContent, testFilePath
148
170
  : "3. Append missing tests to the existing suite. Do not delete existing passing tests unless they are fundamentally broken.\n";
149
171
  const prompt = "You are Lisa.ai, an autonomous CI/CD expert platform.\n" +
150
172
  "A source file lacks 100% test coverage. You must update its existing test suite to achieve full coverage.\n\n" +
151
- "--- Target File Content (" + sourceFilePath + ") ---\n" + sourceFileContent + "\n\n" +
173
+ "--- Target File Content (" + sourceFilePath + ") ---\n" + truncateSource(sourceFileContent, sourceFilePath) + "\n\n" +
152
174
  "--- Existing Test Suite (" + testFilePath + ") ---\n" + existingTestContent + "\n\n" +
153
175
  "--- Constraints ---\n" +
154
176
  "1. Return the updated complete test code wrapped in a markdown code block (```typescript ... ```).\n" +
@@ -160,7 +182,9 @@ async function updateTestForFile(sourceFilePath, sourceFileContent, testFilePath
160
182
  prompt,
161
183
  });
162
184
  const match = text.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);
163
- return match ? match[1].trim() : text.trim();
185
+ const result = match ? match[1].trim() : text.trim();
186
+ assertLooksLikeTestCode(result, sourceFilePath);
187
+ return result;
164
188
  }
165
189
  async function askLisaGeneral(promptStr, modelProvider, apiKey) {
166
190
  const model = getProvider(modelProvider, apiKey);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lisa.ai/agent",
3
- "version": "2.2.1",
3
+ "version": "2.2.2",
4
4
  "description": "Lisa.ai Autonomous CI/CD Worker Agent",
5
5
  "main": "dist/index.js",
6
6
  "bin": {