@lisa.ai/agent 2.7.0 → 2.7.1
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 +100 -41
- package/dist/index.js +21 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @lisa.ai/agent
|
|
2
2
|
|
|
3
|
-
The **Lisa.ai Agent** is an autonomous CI/CD CLI that heals failing tests, generates missing test coverage, and remediates npm vulnerabilities — all powered by LLMs. Drop it into any JavaScript/TypeScript project and let it fix your build.
|
|
3
|
+
The **Lisa.ai Agent** is an autonomous CI/CD CLI that heals failing tests, generates missing test coverage (unit, E2E, integration), and remediates npm vulnerabilities — all powered by LLMs. Drop it into any JavaScript/TypeScript project and let it fix your build.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -39,30 +39,65 @@ lisa-agent heal --command "npm test" --model gemini
|
|
|
39
39
|
|
|
40
40
|
**Supported frameworks:** Jest, Vitest, Mocha, Karma/Jasmine (Angular), Cypress, Playwright
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
### `unit` — Generate Unit Tests
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
Runs your test suite with coverage enabled, identifies files below 100% coverage, and generates new test specs to close the gaps. Auto-detects your test framework and builds the correct coverage command.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
lisa-agent unit --model gemini
|
|
48
|
+
lisa-agent unit --command "npx jest --coverage --coverageReporters=json-summary" --model claude
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**How it works:**
|
|
52
|
+
1. Auto-detects test framework (Jest, Vitest, Karma, Mocha) and builds coverage command
|
|
53
|
+
2. Runs tests with coverage, parses `coverage-summary.json`
|
|
54
|
+
3. Identifies uncovered source files
|
|
55
|
+
4. Generates unit test specs targeting uncovered code via LLM
|
|
56
|
+
5. If generated tests fail, delegates to the heal loop
|
|
57
|
+
6. Re-runs coverage and reports delta
|
|
58
|
+
|
|
59
|
+
**Auto-detected coverage commands:**
|
|
49
60
|
|
|
50
|
-
|
|
61
|
+
| Framework | Command |
|
|
62
|
+
|---|---|
|
|
63
|
+
| Karma (Angular) | `npx ng test --no-watch --code-coverage --browsers=ChromeHeadless` |
|
|
64
|
+
| Jest | `npx jest --coverage --coverageReporters=json-summary` |
|
|
65
|
+
| Vitest | `npx vitest run --coverage` |
|
|
66
|
+
| Mocha | `npx nyc --reporter=json-summary mocha` |
|
|
51
67
|
|
|
52
|
-
|
|
68
|
+
### `e2e` — Generate E2E Tests
|
|
69
|
+
|
|
70
|
+
Generates Playwright end-to-end tests by discovering your app's routes and creating specs for each page/flow. Auto-installs Playwright if no E2E framework is detected.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
lisa-agent e2e --model gemini
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**How it works:**
|
|
77
|
+
1. Detects existing E2E framework (Playwright or Cypress config files)
|
|
78
|
+
2. If none found, auto-installs Playwright + generates `playwright.config.ts`
|
|
79
|
+
3. Discovers routes from Angular/React router config files
|
|
80
|
+
4. Generates E2E specs for uncovered routes via LLM
|
|
81
|
+
5. Runs specs and heals failures
|
|
82
|
+
|
|
83
|
+
### `integration` — Generate Integration Tests
|
|
84
|
+
|
|
85
|
+
Generates integration tests that exercise module seams — testing 2+ modules together without full E2E overhead.
|
|
53
86
|
|
|
54
87
|
```bash
|
|
55
|
-
lisa-agent
|
|
88
|
+
lisa-agent integration --model gemini
|
|
56
89
|
```
|
|
57
90
|
|
|
58
91
|
**How it works:**
|
|
59
|
-
1.
|
|
60
|
-
2.
|
|
61
|
-
3. Generates
|
|
62
|
-
4.
|
|
63
|
-
5. Repeats until 100% or max retries exhausted
|
|
92
|
+
1. Detects app type (Angular, React, Express, NestJS, Node)
|
|
93
|
+
2. Discovers integration targets: component+service pairs, controller files, multi-import modules
|
|
94
|
+
3. Generates integration specs with real services (not mocked) via LLM
|
|
95
|
+
4. Runs specs and heals failures
|
|
64
96
|
|
|
65
|
-
**
|
|
97
|
+
**What it generates:**
|
|
98
|
+
- **Angular:** TestBed with real services, multi-component interactions, `HttpClientTestingModule`
|
|
99
|
+
- **Express/NestJS:** Supertest-based endpoint tests with test DB, middleware chains
|
|
100
|
+
- **General:** Tests exercising 2+ modules together
|
|
66
101
|
|
|
67
102
|
### `audit` — Vulnerability Scanning & Remediation
|
|
68
103
|
|
|
@@ -79,16 +114,9 @@ lisa-agent audit --model gemini
|
|
|
79
114
|
4. **Targeted upgrades** — applies non-breaking semver-minor fixes
|
|
80
115
|
5. **LLM guidance** — for remaining breaking/unfixable vulnerabilities, consults the LLM and saves a remediation report to `lisa-audit-report.md`
|
|
81
116
|
|
|
82
|
-
**Options:**
|
|
83
|
-
|
|
84
|
-
| Flag | Description | Default |
|
|
85
|
-
|---|---|---|
|
|
86
|
-
| `-m, --model <provider>` | `gemini`, `claude`, or `openai` | `gemini` |
|
|
87
|
-
| `-p, --project-id <id>` | Control Plane project ID | `local` |
|
|
88
|
-
|
|
89
117
|
### `diagnose` — Test LLM Connectivity
|
|
90
118
|
|
|
91
|
-
Sends a simple probe to your configured LLM provider to verify your API key works and the model is reachable.
|
|
119
|
+
Sends a simple probe to your configured LLM provider to verify your API key works and the model is reachable.
|
|
92
120
|
|
|
93
121
|
```bash
|
|
94
122
|
lisa-agent diagnose --model claude
|
|
@@ -103,6 +131,16 @@ lisa-agent init # interactive wizard
|
|
|
103
131
|
lisa-agent init --force # overwrite existing config
|
|
104
132
|
```
|
|
105
133
|
|
|
134
|
+
## Common Options
|
|
135
|
+
|
|
136
|
+
All test commands (`heal`, `unit`, `e2e`, `integration`, `audit`) accept:
|
|
137
|
+
|
|
138
|
+
| Flag | Description | Default |
|
|
139
|
+
|---|---|---|
|
|
140
|
+
| `-c, --command <cmd>` | Test/coverage command to run | Auto-detected |
|
|
141
|
+
| `-m, --model <provider>` | `gemini`, `claude`, or `openai` | `gemini` |
|
|
142
|
+
| `-p, --project-id <id>` | Control Plane project for remote config + telemetry | `local` |
|
|
143
|
+
|
|
106
144
|
## Configuration
|
|
107
145
|
|
|
108
146
|
### `.lisai.json`
|
|
@@ -113,8 +151,10 @@ Place a `.lisai.json` in your project root to configure the agent without CLI fl
|
|
|
113
151
|
{
|
|
114
152
|
"provider": "gemini",
|
|
115
153
|
"model": "gemini-2.0-flash",
|
|
116
|
-
"testCommand": "npm
|
|
154
|
+
"testCommand": "npm test",
|
|
117
155
|
"testingFramework": "jest",
|
|
156
|
+
"e2eFramework": "playwright",
|
|
157
|
+
"e2eCommand": "npx playwright test",
|
|
118
158
|
"testTypes": ["unit", "integration", "e2e"],
|
|
119
159
|
"maxRetries": 5,
|
|
120
160
|
"skipFiles": ["server.js", "app.js"],
|
|
@@ -127,14 +167,18 @@ Place a `.lisai.json` in your project root to configure the agent without CLI fl
|
|
|
127
167
|
|---|---|---|
|
|
128
168
|
| `provider` | `gemini \| claude \| openai` | LLM provider |
|
|
129
169
|
| `model` | `string` | Pin a specific model ID (e.g. `claude-sonnet-4-6`) |
|
|
130
|
-
| `testCommand` | `string` |
|
|
131
|
-
| `testingFramework` | `
|
|
170
|
+
| `testCommand` | `string` | Override unit test command (skips auto-detection) |
|
|
171
|
+
| `testingFramework` | `jest \| vitest \| mocha \| karma \| jasmine` | Override unit test framework detection |
|
|
172
|
+
| `e2eFramework` | `playwright \| cypress` | Override E2E framework detection |
|
|
173
|
+
| `e2eCommand` | `string` | Override E2E test command |
|
|
132
174
|
| `testTypes` | `string[]` | Types of tests to generate: `unit`, `integration`, `e2e` |
|
|
133
|
-
| `maxRetries` | `number` | Max heal retries per session (default 5) |
|
|
134
|
-
| `skipFiles` | `string[]` | Filenames to exclude from
|
|
175
|
+
| `maxRetries` | `number` | Max heal/generation retries per session (default 5) |
|
|
176
|
+
| `skipFiles` | `string[]` | Filenames to exclude from analysis |
|
|
135
177
|
| `skipDirs` | `string[]` | Directory names to exclude |
|
|
136
178
|
| `skipPaths` | `string[]` | Path prefixes to exclude |
|
|
137
179
|
|
|
180
|
+
All fields are **optional**. The agent auto-detects everything. This file is the escape hatch for unusual setups.
|
|
181
|
+
|
|
138
182
|
### Config Priority
|
|
139
183
|
|
|
140
184
|
Settings are resolved in this order (highest wins):
|
|
@@ -152,8 +196,8 @@ lisa-agent heal --command "npm test" --project-id "my-project-123"
|
|
|
152
196
|
```
|
|
153
197
|
|
|
154
198
|
With a project ID, the agent will:
|
|
155
|
-
- **Fetch remote config** (model provider, max retries,
|
|
156
|
-
- **Report telemetry** (heal/
|
|
199
|
+
- **Fetch remote config** (model provider, max retries, agent toggle) from the Dashboard
|
|
200
|
+
- **Report telemetry** (heal/unit/e2e/integration events, pass/fail counts) for the Dashboard charts
|
|
157
201
|
- **Use the Memory Engine** — successful fix patterns are stored and reused across runs, so the agent gets smarter over time
|
|
158
202
|
|
|
159
203
|
Without a project ID, the agent runs fully offline using local config and env vars.
|
|
@@ -167,12 +211,14 @@ The Memory Engine stores proven fix patterns from previous heal runs. When the a
|
|
|
167
211
|
- Confidence = `successCount / totalAttempts`, updated after each heal attempt
|
|
168
212
|
- Memory is project-scoped and stored on the Control Plane (requires `--project-id`)
|
|
169
213
|
|
|
170
|
-
## Auto-
|
|
214
|
+
## Auto-Detection
|
|
215
|
+
|
|
216
|
+
The agent **adapts to your project** — zero config required. If no `--command` is provided, it scans your project to detect:
|
|
171
217
|
|
|
172
|
-
|
|
173
|
-
- **
|
|
174
|
-
- **
|
|
175
|
-
- **
|
|
218
|
+
- **Test framework** — from config files (`jest.config.*`, `karma.conf.js`, `vitest.config.*`, `.mocharc.*`) and `package.json` dependencies
|
|
219
|
+
- **E2E framework** — from `playwright.config.ts` or `cypress.config.ts` (defaults to Playwright if none found)
|
|
220
|
+
- **Coverage command** — built automatically per framework with correct reporter flags
|
|
221
|
+
- **Package manager** — npm/yarn/pnpm detected via lockfile
|
|
176
222
|
- **Missing dependencies** — auto-installs test frameworks that are referenced but not installed
|
|
177
223
|
|
|
178
224
|
## Examples
|
|
@@ -180,25 +226,38 @@ If no `--command` is provided, the agent scans your project to detect:
|
|
|
180
226
|
### Heal a broken Angular project
|
|
181
227
|
```bash
|
|
182
228
|
export GOOGLE_GENERATIVE_AI_API_KEY="your-key"
|
|
183
|
-
lisa-agent heal --command "ng test --watch=
|
|
229
|
+
lisa-agent heal --command "npx ng test --no-watch --browsers=ChromeHeadless" --model gemini
|
|
184
230
|
```
|
|
185
231
|
|
|
186
|
-
### Generate
|
|
232
|
+
### Generate unit tests for a Node.js API
|
|
187
233
|
```bash
|
|
188
234
|
export ANTHROPIC_API_KEY="your-key"
|
|
189
|
-
lisa-agent
|
|
235
|
+
lisa-agent unit --model claude
|
|
190
236
|
```
|
|
191
237
|
|
|
192
|
-
###
|
|
238
|
+
### Generate Playwright E2E tests
|
|
193
239
|
```bash
|
|
194
240
|
export GOOGLE_GENERATIVE_AI_API_KEY="your-key"
|
|
241
|
+
lisa-agent e2e --model gemini
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Generate integration tests
|
|
245
|
+
```bash
|
|
246
|
+
export OPENAI_API_KEY="your-key"
|
|
247
|
+
lisa-agent integration --model openai
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Fix npm vulnerabilities with LLM guidance
|
|
251
|
+
```bash
|
|
195
252
|
lisa-agent audit --model gemini
|
|
196
253
|
```
|
|
197
254
|
|
|
198
255
|
### Full CI/CD pipeline with Control Plane
|
|
199
256
|
```bash
|
|
200
257
|
lisa-agent heal --command "npm test" --model gemini --project-id "prod-api"
|
|
201
|
-
lisa-agent
|
|
258
|
+
lisa-agent unit --model gemini --project-id "prod-api"
|
|
259
|
+
lisa-agent e2e --model gemini --project-id "prod-api"
|
|
260
|
+
lisa-agent integration --model gemini --project-id "prod-api"
|
|
202
261
|
lisa-agent audit --model gemini --project-id "prod-api"
|
|
203
262
|
```
|
|
204
263
|
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var wt=Object.create;var Pe=Object.defineProperty;var xt=Object.getOwnPropertyDescriptor;var Lt=Object.getOwnPropertyNames;var $t=Object.getPrototypeOf,kt=Object.prototype.hasOwnProperty;var jt=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var St=(t,e,n,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Lt(e))!kt.call(t,i)&&i!==n&&Pe(t,i,{get:()=>e[i],enumerable:!(s=xt(e,i))||s.enumerable});return t};var k=(t,e,n)=>(n=t!=null?wt($t(t)):{},St(e||!t||!t.__esModule?Pe(n,"default",{value:t,enumerable:!0}):n,t));var pt=jt((_s,_t)=>{_t.exports={name:"@lisa.ai/agent",version:"2.7.
|
|
2
|
+
"use strict";var wt=Object.create;var Pe=Object.defineProperty;var xt=Object.getOwnPropertyDescriptor;var Lt=Object.getOwnPropertyNames;var $t=Object.getPrototypeOf,kt=Object.prototype.hasOwnProperty;var jt=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var St=(t,e,n,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Lt(e))!kt.call(t,i)&&i!==n&&Pe(t,i,{get:()=>e[i],enumerable:!(s=xt(e,i))||s.enumerable});return t};var k=(t,e,n)=>(n=t!=null?wt($t(t)):{},St(e||!t||!t.__esModule?Pe(n,"default",{value:t,enumerable:!0}):n,t));var pt=jt((_s,_t)=>{_t.exports={name:"@lisa.ai/agent",version:"2.7.1",description:"Lisa.ai Autonomous CI/CD Worker Agent",main:"dist/index.js",bin:{"lisa-agent":"dist/index.js"},files:["dist/"],scripts:{build:"tsup",typecheck:"tsc --noEmit",start:"node dist/index.js",dev:"ts-node src/index.ts",prepublishOnly:"npm run typecheck && npm run build"},keywords:["lisa","ci","cd","agent"],author:"Lisa.ai",license:"ISC",dependencies:{"@ai-sdk/anthropic":"^3.0.46","@ai-sdk/google":"^3.0.30","@ai-sdk/openai":"^3.0.30","@octokit/rest":"^22.0.1",ai:"^6.0.94",commander:"^11.1.0",dotenv:"^17.3.1","simple-git":"^3.31.1"},devDependencies:{"@types/jest":"^30.0.0","@types/node":"^20.0.0","ts-node":"^10.9.1",tsup:"^8.5.1",typescript:"^5.0.0"}}});var gt=require("commander"),ve=k(require("fs")),ft=k(require("path")),mt=k(require("readline"));var Te=k(require("dotenv"));Te.config({quiet:!0});async function ce(t){let e=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3000";try{let n=`${e}/api/config/${t}`;console.log(`[Lisa.ai Agent] Fetching dynamic configuration from ${n}...`);let s=await fetch(n);if(!s.ok){let o=s.status===404?"not_found":"unreachable";return console.warn(o==="not_found"?`[Lisa.ai Agent Warning] Control Plane returned 404. Project '${t}' not found.`:`[Lisa.ai Agent Warning] Control Plane returned ${s.status}. Falling back to local defaults.`),{ok:!1,reason:o}}return{ok:!0,config:await s.json()}}catch{return console.warn(`[Lisa.ai Agent Warning] Failed to reach Control Plane (${e}). Using local fallback configuration.`),{ok:!1,reason:"unreachable"}}}var Qe=require("child_process"),N=k(require("fs")),q=k(require("path"));var A=k(require("path")),T=k(require("fs"));function xe(t,e=process.cwd(),n=[]){let s=t.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,""),i=n.map(d=>A.resolve(e,d)),o=new Map,r=new Set,a=/(?:Chrome|Firefox|Safari)(?:\s+Headless)?\s+[\d.]+(?:\s+[\d.]+)*\s+\([^)]+\)\s+(.+?)\s+FAILED/g,u;for(;(u=a.exec(s))!==null;){let l=u[1].trim().split(" ");for(let g=1;g<Math.min(l.length,6);g++){let m=l.slice(0,g).join(" ");if(r.has(m))continue;r.add(m);let c=Le(m,e,i);if(c){let y=A.resolve(c);o.has(y)||o.set(y,A.relative(e,c));break}}}let p=/^FAIL\s+([a-zA-Z0-9_./-\\]+\.(?:spec|test)\.(ts|tsx|js|jsx))/gm,f;for(;(f=p.exec(s))!==null;){let d=f[1],l=A.isAbsolute(d)?d:A.resolve(e,d);!i.includes(l)&&T.existsSync(l)&&!o.has(l)&&o.set(l,A.relative(e,l))}return[...o.values()]}function ue(t,e=[],n=process.cwd()){let s=t.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,""),i=e.map(c=>A.resolve(c));{let c=/(?:Chrome|Firefox|Safari)(?:\s+Headless)?\s+[\d.]+(?:\s+[\d.]+)*\s+\([^)]+\)\s+(.+?)\s+FAILED/g,y=new Set,h;for(;(h=c.exec(s))!==null;){let w=h[1].trim().split(" ");for(let $=1;$<Math.min(w.length,5);$++){let x=w.slice(0,$).join(" ");if(y.has(x))continue;y.add(x);let L=Le(x,n,i);if(L)return A.relative(n,L)}}}let o=/([a-zA-Z]:[a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue)|[a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue))(?:\s*[:(])/g,r;for(;(r=o.exec(s))!==null;){let c=r[1];if(c){if(/(?:^|[/\\])(node_modules|dist|build)[/\\]/.test(c))continue;let y=A.isAbsolute(c)?c:A.resolve(n,c);if(!i.includes(y)&&T.existsSync(y))return c}}let a=/FAIL\s+([a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue))/gi,u;for(;(u=a.exec(s))!==null;){let c=u[1],y=A.isAbsolute(c)?c:A.resolve(n,c);if(!i.includes(y)&&T.existsSync(y))return c}let p=/([a-zA-Z]:[a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue)|[a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue))/g,f;for(;(f=p.exec(s))!==null;){let c=f[1];if(c){if(/(?:^|[/\\])(node_modules|dist|build)[/\\]/.test(c))continue;let y=A.isAbsolute(c)?c:A.resolve(n,c);if(!i.includes(y)&&T.existsSync(y))return c}}let d=/\b([A-Z][a-zA-Z0-9]{3,})\b/g,l,g=new Set,m=["Error","TypeError","SyntaxError","ReferenceError","RangeError","Object","Boolean","String","Number","Array","NullInjectorError","NullInjector","R3Injector","ChainedInjector","Module","Component","Standalone","Application","App2","TestBed","UserContext","Chrome","ChromeHeadless","Karma","Launching","Starting","Headless","Connected","Executed","INFO","SUCCESS","FAILED","Building","Initial","Names","Lazy","Source","Find","NotFound","NG0201","Windows","Linux","Macintosh","Users","AppData","Local","Temp","Process","Unexpected","Expected","Validation","Directory","Configuration","Documentation"];for(;(l=d.exec(s))!==null;){let c=l[1];if(!c||m.includes(c)||g.has(c)||/^[A-Z][A-Z0-9]{5,11}$/.test(c)||/^[A-Za-z0-9]{16,}$/.test(c))continue;g.add(c);let y=Ie(c,n,i);if(y)return A.relative(n,y)}return null}function Le(t,e,n){if(!T.existsSync(e))return null;let s=T.readdirSync(e);for(let i of s){let o=A.join(e,i);if(["node_modules","dist","build",".git",".angular"].includes(i))continue;let r;try{r=T.statSync(o)}catch{continue}if(r.isDirectory()){let a=Le(t,o,n);if(a)return a}else if(/\.(spec|test)\.(ts|tsx|js|jsx)$/.test(i)){if(n.includes(A.resolve(o)))continue;try{let a=T.readFileSync(o,"utf8");if(a.includes(`describe('${t}'`)||a.includes(`describe("${t}"`))return o}catch{continue}}}return null}function Ie(t,e,n){if(!T.existsSync(e))return null;let s=T.readdirSync(e);for(let i of s){let o=A.join(e,i);if(i==="node_modules"||i==="dist"||i==="build"||i===".git"||i===".angular")continue;let r;try{r=T.statSync(o)}catch{continue}if(r.isDirectory()){let a=Ie(t,o,n);if(a)return a}else if(i.match(/\.(ts|tsx|js|jsx|vue)$/)){let a=T.readFileSync(o,"utf8");if(a.includes(`class ${t}`)||a.includes(`function ${t}`)||a.includes(`const ${t}`)||a.includes(`let ${t}`)||a.includes(`exports.${t}`)||a.includes(`module.exports.${t}`)){let u=o,p=A.extname(o),f=o.slice(0,-p.length);if(!i.includes(".spec.")&&!i.includes(".test.")){let d=[`${f}.spec${p}`,`${f}.test${p}`,`${f}.spec.js`,`${f}.test.js`];for(let l of d)if(T.existsSync(l)){u=l;break}}if(!n.includes(A.resolve(u)))return u}}}return null}var oe=require("ai"),Re=require("@ai-sdk/openai"),Me=require("@ai-sdk/anthropic"),De=require("@ai-sdk/google"),Ne=k(require("dotenv"));Ne.config({quiet:!0});var $e=15e3;function Oe(t,e){return t.length<=$e?t:(console.warn(`[Lisa.ai LLM] ${e} is ${t.length} chars \u2014 truncating to ${$e} to stay within context window.`),t.slice(0,$e)+`
|
|
3
3
|
|
|
4
4
|
// ... (truncated)`)}function Ue(t,e){if(!/\b(describe|it\(|test\(|expect\(|beforeEach|afterEach|suite|assert\.|cy\.)\b/.test(t))throw new Error(`LLM returned a non-code response for ${e} (possible context overflow). Skipping to prevent file corruption.`)}function _e(t){if(!t||t.length===0)return"";let e={unit:"unit tests \u2014 test every function/method in complete isolation, mocking ALL external dependencies, services, and I/O",integration:"integration tests \u2014 test how modules work together using real service/repository layers; for HTTP routes use supertest or equivalent",e2e:"end-to-end tests \u2014 simulate complete user flows using the configured e2e framework (Playwright/Cypress); cover the full path from HTTP request to response or UI action to assertion"};return`5. Test scope \u2014 generate ALL of the following test types for this file:
|
|
5
5
|
${t.map(s=>` \u2022 ${e[s]??s}`).join(`
|
|
@@ -69,7 +69,7 @@ This PR was automatically generated by Lisa.ai to resolve a failing compilation/
|
|
|
69
69
|
**Healed File:** \`${t}\`
|
|
70
70
|
|
|
71
71
|
Please review the changes.`,head:s,base:o});console.log(`\u2705 [Lisa.ai PR Engine] Pull Request created successfully: ${f.data.html_url}`)}else console.log("\u26A0\uFE0F [Lisa.ai PR Engine] GITHUB_TOKEN or GITHUB_REPOSITORY not set. Skipping GitHub Pull Request creation.")}catch(r){console.error(`
|
|
72
|
-
\u{1F6A8} [Lisa.ai PR Engine Error] Failed to create Pull Request:`,r.message)}finally{try{await e.checkout(o)}catch{}}}var We=k(require("dotenv"));We.config({quiet:!0});async function I(t){let n=`${process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3000"}/api/telemetry`;fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}).catch(s=>{console.debug(`[Lisa.ai Agent Debug] Failed to report telemetry: ${s.message}`)})}var _=k(require("fs")),D=k(require("path"));var pe=k(require("fs")),Ye=k(require("path")),V=".lisai.json",F=class{static _config=null;static _loaded=!1;static load(e=process.cwd()){if(this._loaded)return this._config;this._loaded=!0;let n=Ye.resolve(e,V);if(!pe.existsSync(n))return null;try{let s=pe.readFileSync(n,"utf-8");return this._config=JSON.parse(s),console.log(`[Lisa.ai Config] Loaded project config from ${V}`),this._config}catch(s){return console.warn(`[Lisa.ai Config] Warning: ${V} found but failed to parse \u2014 ${s.message}. Using defaults.`),null}}static get(){return this._config}static applyEnvDefaults(e){if(!e.model)return;let n={claude:"LISA_CLAUDE_MODEL",openai:"LISA_OPENAI_MODEL",gemini:"LISA_GOOGLE_MODEL"},s=e.provider;s&&n[s]&&!process.env[n[s]]&&(process.env[n[s]]=e.model,console.log(`[Lisa.ai Config] Model override applied: ${s} \u2192 ${e.model}`))}static printSummary(e){let n=[];e.provider&&n.push(`provider=${e.provider}`),e.model&&n.push(`model=${e.model}`),e.testingFramework&&n.push(`testingFramework=${e.testingFramework}`),e.testTypes?.length&&n.push(`testTypes=[${e.testTypes.join(",")}]`),e.maxRetries!==void 0&&n.push(`maxRetries=${e.maxRetries}`),e.testCommand&&n.push(`testCommand="${e.testCommand}"`),e.e2eFramework&&n.push(`e2eFramework=${e.e2eFramework}`),e.e2eCommand&&n.push(`e2eCommand="${e.e2eCommand}"`),n.length&&console.log(`[Lisa.ai Config] Settings: ${n.join(", ")}`),e.skipFiles?.length&&console.log(`[Lisa.ai Config] Extra skip files : ${e.skipFiles.join(", ")}`),e.skipDirs?.length&&console.log(`[Lisa.ai Config] Extra skip dirs : ${e.skipDirs.join(", ")}`),e.skipPaths?.length&&console.log(`[Lisa.ai Config] Extra skip paths : ${e.skipPaths.join(", ")}`)}};var B=class{static scanRepository(e=process.cwd()){let n=D.resolve(e,"package.json"),s={type:"unknown",testingFramework:"none"};if(!_.existsSync(n))return console.warn(`[Lisa.ai AutoDiscovery] No package.json found at ${n}. Defaulting to Generic Node.`),s.type="node",s;let i=JSON.parse(_.readFileSync(n,"utf8")),o={...i.dependencies||{},...i.devDependencies||{}},r=i.scripts||{};return o["@angular/core"]?s.type="angular":o.react?s.type="react":o.vue?s.type="vue":s.type="node",o.jest||r.test?.includes("jest")?s.testingFramework="jest":o.karma||r.test?.includes("karma")||r.test?.includes("ng test")?s.testingFramework="karma":(o.vitest||r.test?.includes("vitest"))&&(s.testingFramework="vitest"),s.testingFramework!=="none"&&(
|
|
72
|
+
\u{1F6A8} [Lisa.ai PR Engine Error] Failed to create Pull Request:`,r.message)}finally{try{await e.checkout(o)}catch{}}}var We=k(require("dotenv"));We.config({quiet:!0});async function I(t){let n=`${process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3000"}/api/telemetry`;fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}).catch(s=>{console.debug(`[Lisa.ai Agent Debug] Failed to report telemetry: ${s.message}`)})}var _=k(require("fs")),D=k(require("path"));var pe=k(require("fs")),Ye=k(require("path")),V=".lisai.json",F=class{static _config=null;static _loaded=!1;static load(e=process.cwd()){if(this._loaded)return this._config;this._loaded=!0;let n=Ye.resolve(e,V);if(!pe.existsSync(n))return null;try{let s=pe.readFileSync(n,"utf-8");return this._config=JSON.parse(s),console.log(`[Lisa.ai Config] Loaded project config from ${V}`),this._config}catch(s){return console.warn(`[Lisa.ai Config] Warning: ${V} found but failed to parse \u2014 ${s.message}. Using defaults.`),null}}static get(){return this._config}static applyEnvDefaults(e){if(!e.model)return;let n={claude:"LISA_CLAUDE_MODEL",openai:"LISA_OPENAI_MODEL",gemini:"LISA_GOOGLE_MODEL"},s=e.provider;s&&n[s]&&!process.env[n[s]]&&(process.env[n[s]]=e.model,console.log(`[Lisa.ai Config] Model override applied: ${s} \u2192 ${e.model}`))}static printSummary(e){let n=[];e.provider&&n.push(`provider=${e.provider}`),e.model&&n.push(`model=${e.model}`),e.testingFramework&&n.push(`testingFramework=${e.testingFramework}`),e.testTypes?.length&&n.push(`testTypes=[${e.testTypes.join(",")}]`),e.maxRetries!==void 0&&n.push(`maxRetries=${e.maxRetries}`),e.testCommand&&n.push(`testCommand="${e.testCommand}"`),e.e2eFramework&&n.push(`e2eFramework=${e.e2eFramework}`),e.e2eCommand&&n.push(`e2eCommand="${e.e2eCommand}"`),n.length&&console.log(`[Lisa.ai Config] Settings: ${n.join(", ")}`),e.skipFiles?.length&&console.log(`[Lisa.ai Config] Extra skip files : ${e.skipFiles.join(", ")}`),e.skipDirs?.length&&console.log(`[Lisa.ai Config] Extra skip dirs : ${e.skipDirs.join(", ")}`),e.skipPaths?.length&&console.log(`[Lisa.ai Config] Extra skip paths : ${e.skipPaths.join(", ")}`)}};var B=class{static scanRepository(e=process.cwd()){let n=D.resolve(e,"package.json"),s={type:"unknown",testingFramework:"none"};if(!_.existsSync(n))return console.warn(`[Lisa.ai AutoDiscovery] No package.json found at ${n}. Defaulting to Generic Node.`),s.type="node",s;let i=JSON.parse(_.readFileSync(n,"utf8")),o={...i.dependencies||{},...i.devDependencies||{}},r=i.scripts||{};return o["@angular/core"]?s.type="angular":o.react?s.type="react":o.vue?s.type="vue":s.type="node",o.jest||r.test?.includes("jest")?s.testingFramework="jest":o.karma||r.test?.includes("karma")||r.test?.includes("ng test")?s.testingFramework="karma":(o.vitest||r.test?.includes("vitest"))&&(s.testingFramework="vitest"),s.testingFramework!=="none"&&(s.testingFramework==="karma"?s.type==="angular"?s.suggestedTestCommand="npx ng test --no-watch --browsers=ChromeHeadless":s.suggestedTestCommand="npx karma start --single-run --browsers=ChromeHeadless":r.test?s.suggestedTestCommand="npm run test":s.testingFramework==="jest"?s.suggestedTestCommand="npx jest --coverage":s.testingFramework==="vitest"&&(s.suggestedTestCommand="npx vitest run --coverage")),s}static findUntestedFiles(e,n=[]){let s=[];if(!_.existsSync(e))return s;let i=_.readdirSync(e),o=F.get(),r=o?.skipDirs??[],a=o?.skipFiles??[],u=o?.skipPaths??[],p=["node_modules","dist","build",".git",".angular","coverage","public","assets",...r],f=["main.ts","index.ts","index.js","index.jsx","app.config.ts","app.routes.ts","styles.css","styles.scss","tailwind.config.js","tailwind.config.ts","eslint.config.js","eslint.config.ts","jest.config.js","jest.config.ts","jest.config.mjs","jest.setup.js","jest.setup.ts","webpack.config.js","webpack.config.ts","babel.config.js","babel.config.ts","rollup.config.js","rollup.config.ts","vite.config.js","vite.config.ts","vitest.config.js","vitest.config.ts","karma.conf.js","karma.config.js","prettier.config.js","prettier.config.ts","postcss.config.js","postcss.config.ts","gulpfile.js","Gulpfile.js",".eslintrc.js",".eslintrc.ts",".babelrc.js"],d=[/^\./,/\.config\.(js|ts|mjs|cjs)$/,/\.conf\.(js|ts|mjs|cjs)$/];for(let l of i){let g=D.join(e,l);if(p.includes(l))continue;let m;try{m=_.statSync(g)}catch{continue}if(m.isDirectory())s.push(...this.findUntestedFiles(g,n));else if(l.match(/\.(ts|tsx|js|jsx|vue)$/)&&!l.includes(".spec.")&&!l.includes(".test.")){if(f.includes(l)||a.includes(l)||d.some(w=>w.test(l)))continue;let c=D.extname(l),y=l.slice(0,-c.length);if(![D.join(e,`${y}.spec${c}`),D.join(e,`${y}.test${c}`),D.join(e,`${y}.spec.js`),D.join(e,`${y}.test.js`),D.join(e,`${y}.spec.ts`),D.join(e,`${y}.test.ts`)].some(w=>_.existsSync(w))){let w=D.relative(process.cwd(),g);if(u.some($=>w.startsWith($)))continue;n.includes(w)||s.push(w)}}}return s}};var Ke=require("child_process"),Q=class{static async installMissingFramework(e,n=process.cwd()){if(e.testingFramework!=="none")return e;console.log(`
|
|
73
73
|
[Lisa.ai Auto-Installer] \u{1F6A8} No testing framework detected for ${e.type} architecture.`),console.log("[Lisa.ai Auto-Installer] \u{1FA84} Initiating Zero-Config Installation Protocol...");let s="",i="none";switch(e.type){case"angular":console.log("[Lisa.ai Auto-Installer] Provisioning Angular TestBed environment..."),s="npm install --save-dev karma karma-chrome-launcher karma-coverage karma-jasmine jasmine-core @types/jasmine",i="karma",e.suggestedTestCommand="npm run test";break;case"react":case"node":default:console.log("[Lisa.ai Auto-Installer] Provisioning Universal Jest environment..."),s="npm install --save-dev jest @types/jest ts-jest",i="jest",e.suggestedTestCommand="npx jest --coverage";break;case"vue":console.log("[Lisa.ai Auto-Installer] Provisioning Vue Vitest environment..."),s="npm install --save-dev vitest @vue/test-utils jsdom",i="vitest",e.suggestedTestCommand="npx vitest run --coverage";break}if(s){console.log(`[Lisa.ai Executing] ${s}`);let o=s.split(" "),r=process.platform==="win32"?`${o[0]}.cmd`:o[0];return(0,Ke.spawnSync)(r,o.slice(1),{cwd:n,stdio:"inherit"}).status!==0&&(console.error(`
|
|
74
74
|
\u274C [Lisa.ai Auto-Installer] Failed to construct dynamic testing environment.`),process.exit(1)),console.log("\u2705 [Lisa.ai Auto-Installer] Framework successfully injected."),e.testingFramework=i,e}return e}};var ge=k(require("fs")),je=k(require("path"));var X=class{static async provisionConfigurationFiles(e,n,s,i=process.cwd()){if(e.testingFramework==="none")return;if(console.log(`
|
|
75
75
|
[Lisa.ai Auto-Generator] \u{1FA84} Analyzing ${e.type} architecture to generate ${e.testingFramework} configuration specs...`),["jest.config.js","jest.config.ts","karma.conf.js","vitest.config.ts","vitest.config.js"].some(u=>ge.existsSync(je.join(i,u)))){console.log("[Lisa.ai Auto-Generator] Setup file detected. Bypassing initialization.");return}let a=`You are an expert ${e.type} architect.
|
|
@@ -81,12 +81,12 @@ Requirements:
|
|
|
81
81
|
2. Ensure standard transpilation (ts-jest for jest, or standard karma-webpack/karma-typescript implementations).
|
|
82
82
|
3. Do NOT wrap your response in markdown formatting (no \`\`\`javascript).
|
|
83
83
|
4. Return ONLY the raw code string block.`;try{let u=await J(a,n,s),p="";e.testingFramework==="jest"&&(p="jest.config.js"),e.testingFramework==="karma"&&(p="karma.conf.js"),e.testingFramework==="vitest"&&(p="vitest.config.ts");let f=je.join(i,p);ge.writeFileSync(f,u,"utf-8"),console.log(`\u2705 [Lisa.ai Auto-Generator] Natively wrote ${p} to repository root.`)}catch(u){console.error(`
|
|
84
|
-
\u274C [Lisa.ai Auto-Generator] Failed to author configuration file: ${u.message}`),process.exit(1)}}};var Ve=k(require("dotenv"));Ve.config({quiet:!0});function ze(t){let e=t.toLowerCase();return e.includes("karma")||e.includes("ng test")?"karma":e.includes("jest")?"jest":e.includes("vitest")?"vitest":e.includes("playwright")?"playwright":e.includes("cypress")?"cypress":e.includes("mocha")?"mocha":"unknown"}async function Ze(t,e,n){if(n==="local")return[];let s=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3000";try{let i=new URLSearchParams({projectId:n,errorLog:t.slice(0,1e3),framework:e}),o=await fetch(`${s}/api/memory/lookup?${i}`);return o.ok?await o.json():[]}catch{return[]}}async function Je(t,e,n,s){if(s==="local")return;let i=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3000";try{await fetch(`${i}/api/memory/record`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectId:s,errorLog:t.slice(0,1e3),fixHint:e.slice(0,2e3),framework:n})}),console.log("[Lisa.ai Memory] Fix pattern recorded to Control Plane.")}catch(o){console.debug(`[Lisa.ai Agent Debug] Failed to record memory pattern: ${o.message}`)}}var ee=0,fe=0,Se=new Set;function
|
|
85
|
-
`)){let i=s.trim();if(/^FAILED/.test(i)||/ FAILED$/.test(i)){let r=i.replace(/^(?:Chrome|Firefox|Safari)(?:\s+Headless)?\s+[\d.]+(?:[\s.]\d+)*\s+\([^)]+\)\s+/,"").replace(/\s+FAILED$/,"").trim();if(n.push(r||i),n.length>=e)break}}return n}function At(t){let e=t.replace(/\.(spec|test)\.(ts|js|jsx|tsx)$/,".$2");if(e!==t&&N.existsSync(e))try{return console.log(` [Lisa.ai] \u{1F9E0} Sibling source found: ${q.basename(e)}`),N.readFileSync(e,"utf-8")}catch{return}}function
|
|
84
|
+
\u274C [Lisa.ai Auto-Generator] Failed to author configuration file: ${u.message}`),process.exit(1)}}};var Ve=k(require("dotenv"));Ve.config({quiet:!0});function ze(t){let e=t.toLowerCase();return e.includes("karma")||e.includes("ng test")?"karma":e.includes("jest")?"jest":e.includes("vitest")?"vitest":e.includes("playwright")?"playwright":e.includes("cypress")?"cypress":e.includes("mocha")?"mocha":"unknown"}async function Ze(t,e,n){if(n==="local")return[];let s=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3000";try{let i=new URLSearchParams({projectId:n,errorLog:t.slice(0,1e3),framework:e}),o=await fetch(`${s}/api/memory/lookup?${i}`);return o.ok?await o.json():[]}catch{return[]}}async function Je(t,e,n,s){if(s==="local")return;let i=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3000";try{await fetch(`${i}/api/memory/record`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectId:s,errorLog:t.slice(0,1e3),fixHint:e.slice(0,2e3),framework:n})}),console.log("[Lisa.ai Memory] Fix pattern recorded to Control Plane.")}catch(o){console.debug(`[Lisa.ai Agent Debug] Failed to record memory pattern: ${o.message}`)}}var ee=0,fe=0,Se=new Set;function be(t){return t.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,"")}function Ct(t){let n=[...be(t).matchAll(/Executed\s+(\d+)\s+of\s+(\d+)(?:\s+\((\d+)\s+FAILED\))?/g)];if(n.length===0)return null;let s=[...n].reverse().find(a=>a[3]!==void 0)??n[n.length-1],i=parseInt(s[2]),o=s[3]?parseInt(s[3]):0,r=parseInt(s[1])-o;return{total:i,passed:r,failed:o}}function Ce(t){let n=[...be(t).matchAll(/Executed\s+(\d+)\s+of\s+(\d+)(?:\s+\((\d+)\s+FAILED\))?/g)];if(n.length===0)return null;let s=[...n].reverse().find(p=>p[3]!==void 0)??n[n.length-1],i=parseInt(s[2]),o=s[3]?parseInt(s[3]):0,r=parseInt(s[1])-o,a=i>0?Math.round(r/i*100):0;return` [${"\u2588".repeat(Math.round(a/5))+"\u2591".repeat(20-Math.round(a/5))}] ${a}% ${r} passed ${o} failed ${i} total`}function Xe(t,e=10){let n=[];for(let s of be(t).split(`
|
|
85
|
+
`)){let i=s.trim();if(/^FAILED/.test(i)||/ FAILED$/.test(i)){let r=i.replace(/^(?:Chrome|Firefox|Safari)(?:\s+Headless)?\s+[\d.]+(?:[\s.]\d+)*\s+\([^)]+\)\s+/,"").replace(/\s+FAILED$/,"").trim();if(n.push(r||i),n.length>=e)break}}return n}function At(t){let e=t.replace(/\.(spec|test)\.(ts|js|jsx|tsx)$/,".$2");if(e!==t&&N.existsSync(e))try{return console.log(` [Lisa.ai] \u{1F9E0} Sibling source found: ${q.basename(e)}`),N.readFileSync(e,"utf-8")}catch{return}}function bt(t,e){let n=t.toLowerCase(),s=q.parse(e);if(n.includes("ng test")||n.includes("karma"))return`${t} --include **/${s.base}`;if(n.includes("jest")||n.includes("vitest")||n.includes("playwright"))return`${t} ${e}`;if(n.includes("cypress"))return`${t} --spec ${e}`;if(n.includes("npm")||n.includes("yarn")||n.includes("pnpm")){try{let o=JSON.parse(N.readFileSync(q.resolve(process.cwd(),"package.json"),"utf8")),r="test";n.includes("npm run ")?r=n.split("npm run ")[1].split(" ")[0]:n.includes("yarn ")?r=n.split("yarn ")[1].split(" ")[0]:n.includes("pnpm ")&&(r=n.split("pnpm ")[1].split(" ")[0]);let a=o.scripts?.[r]?.toLowerCase()||"",u=n.includes("npm")?" --":"";if(a.includes("ng test")||a.includes("karma"))return`${t}${u} --include **/${s.base}`;if(a.includes("jest")||a.includes("vitest")||a.includes("playwright"))return`${t}${u} ${e}`}catch{}let i=n.includes("npm")?" --":"";return`${t}${i} ${e}`}return t}function Ae(t,e=!1){return new Promise((n,s)=>{let i=(0,Qe.spawn)(t,{shell:!0,stdio:["ignore","pipe","pipe"]}),o="",r="";i.stdout?.on("data",a=>{let u=a.toString();if(o+=u,e)for(let p of u.split(`
|
|
86
86
|
`)){let f=p.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,"").trim();f&&(/Executed\s+\d+\s+of\s+\d+/.test(f)?process.stdout.write(`\r ${f.padEnd(72)}`):/^\s*FAILED\b/.test(f)&&(process.stdout.write(`
|
|
87
|
-
`),console.log(` \u2717 ${f.trim()}`)))}}),i.stderr?.on("data",a=>{r+=a.toString()}),i.on("close",a=>{a===0?n({stdout:o,stderr:r}):s({message:`Process exited with code ${a}`,stdout:o,stderr:r})})})}function
|
|
87
|
+
`),console.log(` \u2717 ${f.trim()}`)))}}),i.stderr?.on("data",a=>{r+=a.toString()}),i.on("close",a=>{a===0?n({stdout:o,stderr:r}):s({message:`Process exited with code ${a}`,stdout:o,stderr:r})})})}function Et(t,e,n){if(!e.match(/\.(spec|test)\.(ts|js|tsx|jsx|vue)$/))return;if((n||"").toLowerCase().includes("ng test")||(n||"").toLowerCase().includes("karma"))try{N.writeFileSync(t,`// Quarantined by Lisa.ai \u2014 could not be automatically healed
|
|
88
88
|
describe('', () => {});
|
|
89
|
-
`,"utf-8"),console.log(" \u{1F6A8} Replaced with empty stub (Angular requires valid TS).")}catch{}else try{N.renameSync(t,t+".broken"),console.log(` \u{1F6A8} Renamed to ${q.basename(t)}.broken to skip in test runner.`)}catch{}}async function Ft(t,e,n,s,i,o,r,a,u,p){let f=1,d=!1,l=n,g,m="",c="",y=At(e),v=(await Ze(n,o,u)).filter(w=>w.confidence>=.5).map(w=>w.fixHint);for(v.length>0&&console.log(` [Memory] ${v.length} proven pattern(s) found \u2014 injecting as LLM hint.`);f<=a&&!d;){console.log(` [Attempt ${f}/${a}] Requesting fix from ${r}...`);let w=N.readFileSync(e,"utf-8"),$;for(let x=0;x<=3;x++)try{$=await Ge(t,w,l,r,p,g,y,v);break}catch(L){let
|
|
89
|
+
`,"utf-8"),console.log(" \u{1F6A8} Replaced with empty stub (Angular requires valid TS).")}catch{}else try{N.renameSync(t,t+".broken"),console.log(` \u{1F6A8} Renamed to ${q.basename(t)}.broken to skip in test runner.`)}catch{}}async function Ft(t,e,n,s,i,o,r,a,u,p){let f=1,d=!1,l=n,g,m="",c="",y=At(e),v=(await Ze(n,o,u)).filter(w=>w.confidence>=.5).map(w=>w.fixHint);for(v.length>0&&console.log(` [Memory] ${v.length} proven pattern(s) found \u2014 injecting as LLM hint.`);f<=a&&!d;){console.log(` [Attempt ${f}/${a}] Requesting fix from ${r}...`);let w=N.readFileSync(e,"utf-8"),$;for(let x=0;x<=3;x++)try{$=await Ge(t,w,l,r,p,g,y,v);break}catch(L){let E=(L?.lastError??L)?.statusCode;if((E===529||E===500)&&x<3){let O=30*(x+1);console.warn(` \u26A0\uFE0F LLM overloaded (HTTP ${E}). Waiting ${O}s...`),await new Promise(H=>setTimeout(H,O*1e3))}else{console.error(` \u{1F6A8} LLM API failed: ${L?.message??String(L)}`);break}}if($===void 0)break;N.writeFileSync(e,$,"utf-8"),m=`### Auto-Heal Analysis
|
|
90
90
|
**Error:**
|
|
91
91
|
\`\`\`bash
|
|
92
92
|
${l.slice(0,2e3)}
|
|
@@ -115,34 +115,34 @@ ${l}`,f++}}return{status:d?"healed":"quarantined",details:m,lastCode:c}}async fu
|
|
|
115
115
|
${g}`);let h=Xe(d,10);if(h.length>0){let v=l?.failed??h.length;console.log(` Failing (${v}):`),h.forEach(w=>console.log(` \u2717 ${w}`)),v>h.length&&console.log(` ... and ${v-h.length} more`)}}let m=xe(d,process.cwd(),f);if(m.length===0){let h=ue(d,f,process.cwd());h||(console.error(`
|
|
116
116
|
\u{1F6A8} [Lisa.ai] Could not identify any failing spec files. Output format may be unsupported.`),process.exit(1)),m=[h]}let c=m.length;console.log(`
|
|
117
117
|
[Lisa.ai] Found ${c} failing spec(s). Healing each in isolation...`),console.log(`${"\u2500".repeat(60)}`);for(let h of m)I({projectId:o,type:"heal",filePath:h,modelUsed:e,status:"running",details:"Queued for isolated healing.",...l&&{testTotal:l.total,testPassed:l.passed,testFailed:l.failed},testFramework:p});for(let h=0;h<m.length;h++){let v=m[h],w=q.resolve(process.cwd(),v);if(console.log(`
|
|
118
|
-
[${h+1}/${c}] ${v}`),!N.existsSync(w)){console.warn(" \u26A0\uFE0F File not found \u2014 skipping.");continue}if(/[\\/](node_modules|dist|build)[\\/]/.test(w)){console.warn(" \u26A0\uFE0F Library file \u2014 refusing to modify.");continue}let $=
|
|
119
|
-
`+x.details,...l&&{testTotal:l.total,testPassed:l.passed,testFailed:l.failed},testFramework:p}),
|
|
118
|
+
[${h+1}/${c}] ${v}`),!N.existsSync(w)){console.warn(" \u26A0\uFE0F File not found \u2014 skipping.");continue}if(/[\\/](node_modules|dist|build)[\\/]/.test(w)){console.warn(" \u26A0\uFE0F Library file \u2014 refusing to modify.");continue}let $=bt(t,v),x=await Ft(v,w,d,t,$,p,e,i,o,a);x.status==="healed"?(ee++,Se.add(v),console.log(` \u2705 Healed [${ee} healed ${fe} quarantined so far]`),I({projectId:o,type:"heal",filePath:v,modelUsed:e,status:"success",details:x.details,...l&&{testTotal:l.total,testPassed:l.passed,testFailed:l.failed},testFramework:p}),await Je(d,x.lastCode,p,o)):(fe++,f.push(v),console.warn(` \u{1F6A8} Quarantined [${ee} healed ${fe} quarantined so far]`),I({projectId:o,type:"heal",filePath:v,modelUsed:e,status:"error",details:`Exhausted all ${i} attempts.
|
|
119
|
+
`+x.details,...l&&{testTotal:l.total,testPassed:l.passed,testFailed:l.failed},testFramework:p}),Et(w,v,t))}if(console.log(`
|
|
120
120
|
${"\u2500".repeat(60)}`),ee>0){console.log("[Lisa.ai] Final verification run...");try{await Ae(t,!0),process.stdout.write(`
|
|
121
121
|
`),console.log(`
|
|
122
122
|
\u2705 All tests passing!`)}catch(h){process.stdout.write(`
|
|
123
123
|
`);let v=(h.stderr||"")+`
|
|
124
124
|
`+(h.stdout||"")+`
|
|
125
125
|
`+(h.message||""),w=Ce(v);w&&console.log(`
|
|
126
|
-
${w}`);let $=xe(v,process.cwd(),[]),x=$.filter(
|
|
127
|
-
\u26A0\uFE0F ${x.length} spec(s) passed isolated verification but still fail globally:`),x.forEach(
|
|
128
|
-
\u2139\uFE0F ${L.length} additional failing spec(s) not in this run's queue:`),L.forEach(
|
|
126
|
+
${w}`);let $=xe(v,process.cwd(),[]),x=$.filter(E=>Se.has(E)),L=$.filter(E=>!Se.has(E)&&!f.includes(E));x.length>0&&(console.warn(`
|
|
127
|
+
\u26A0\uFE0F ${x.length} spec(s) passed isolated verification but still fail globally:`),x.forEach(E=>console.warn(` \u2717 ${E}`)),console.warn(" These likely have cross-test shared state. Try higher maxRetries or fix manually.")),L.length>0&&(console.warn(`
|
|
128
|
+
\u2139\uFE0F ${L.length} additional failing spec(s) not in this run's queue:`),L.forEach(E=>console.warn(` \u2717 ${E}`)),console.warn(" Run lisa-agent heal again to address these."))}}let y=(l?.passed??0)+ee;console.log(`
|
|
129
129
|
\u2705 Healed: ${ee} / ${c}`),console.log(` \u{1F6A8} Quarantined: ${fe}`),l&&console.log(` \u{1F4CA} Suite: ${l.passed} passing \u2192 ~${y} passing (est.)`),console.log(`${"\u2500".repeat(60)}
|
|
130
|
-
`),s&&await ke(s)}var nt=require("child_process"),G=k(require("fs")),R=k(require("path"));var he=k(require("fs")),et=k(require("path"));function tt(t){let e=et.resolve(process.cwd(),t);if(!he.existsSync(e))throw new Error(`[Lisa.ai Coverage Error] Coverage file not found at ${e}`);let n=he.readFileSync(e,"utf-8"),s=JSON.parse(n),i=[];for(let[o,r]of Object.entries(s))o!=="total"&&(r.lines.pct<100||r.statements.pct<100||r.functions.pct<100||r.branches.pct<100)&&i.push(o);return i}var st=new Set,ye=0,
|
|
130
|
+
`),s&&await ke(s)}var nt=require("child_process"),G=k(require("fs")),R=k(require("path"));var he=k(require("fs")),et=k(require("path"));function tt(t){let e=et.resolve(process.cwd(),t);if(!he.existsSync(e))throw new Error(`[Lisa.ai Coverage Error] Coverage file not found at ${e}`);let n=he.readFileSync(e,"utf-8"),s=JSON.parse(n),i=[];for(let[o,r]of Object.entries(s))o!=="total"&&(r.lines.pct<100||r.statements.pct<100||r.functions.pct<100||r.branches.pct<100)&&i.push(o);return i}var st=new Set,ye=0,Ee=3;function Pt(t){let e=t.testingFramework,n=process.cwd();return G.existsSync(R.join(n,"angular.json"))?"npx ng test --no-watch --code-coverage --browsers=ChromeHeadless":e==="jest"?"npx jest --coverage --coverageReporters=json-summary":e==="vitest"?"npx vitest run --coverage":[".mocharc.yml",".mocharc.yaml",".mocharc.json",".mocharc.js"].some(i=>G.existsSync(R.join(n,i)))?"npx nyc --reporter=json-summary mocha":null}async function ie(t,e,n=1,s=3,i="local",o){if(n>s){console.log(`
|
|
131
131
|
[Lisa.ai Unit] Reached maximum retries (${s}). Stopping.`);return}let r=F.load(process.cwd());r&&(F.applyEnvDefaults(r),!t&&r.testCommand&&(t=r.testCommand,console.log(`[Lisa.ai Config] Using testCommand from .lisai.json: ${t}`)));let a=B.scanRepository(),u=r?.testingFramework||(a.testingFramework!=="none"?a.testingFramework:void 0),p=r?.testTypes;if(!t){console.log(`
|
|
132
132
|
[Lisa.ai Auto-Discovery] No explicit --command provided. Auto-detecting coverage command...`);let d=Pt(a);if(d)t=d,console.log(`[Lisa.ai Auto-Discovery] Detected coverage command: ${t}`);else{let l=a;if(l.testingFramework==="none"&&(l=await Q.installMissingFramework(l),await X.provisionConfigurationFiles(l,e,o)),l.suggestedTestCommand){t=l.suggestedTestCommand;let g=l.testingFramework;(g==="jest"||g==="vitest")&&!t.includes("--coverage")&&(t=t.includes("npm run")?`${t} -- --coverage`:`${t} --coverage`,console.log(`[Lisa.ai Unit] Coverage flag appended: ${t}`)),console.log(`[Lisa.ai Auto-Discovery] Bootstrapping with discovered command: ${t}`)}else console.error(`
|
|
133
133
|
[Lisa.ai Fatal Error] Could not auto-detect a test framework. Please pass --command explicitly or add a .lisai.json config.`),process.exit(1)}}console.log(`
|
|
134
134
|
[Lisa.ai Unit] ${t} (Attempt ${n}/${s}) Using Model: ${e}`),await I({projectId:i,type:"unit",filePath:"global-test-suite",modelUsed:e,status:"running",details:"Agent is executing unit test suite and validating coverage..."});let f=(d,l=!1)=>new Promise((g,m)=>{let c=(0,nt.spawn)(d,{shell:!0,stdio:["ignore","pipe","pipe"]}),y="",h="";c.stdout?.on("data",v=>{let w=v.toString();if(y+=w,l){let $=w.split(`
|
|
135
|
-
`);for(let x of $){let L=x.match(/Executed\s+(\d+)\s+of\s+(\d+)/);if(L){let
|
|
135
|
+
`);for(let x of $){let L=x.match(/Executed\s+(\d+)\s+of\s+(\d+)/);if(L){let E=x.match(/(\d+)\s+FAILED/),O=E?E[1]:0,H=`\r[Lisa.ai Testing] Executed ${L[1]} of ${L[2]} (${O} FAILED) `;process.stdout.write(H)}}}}),c.stderr?.on("data",v=>{h+=v.toString()}),c.on("close",v=>{v===0?g():m({message:`Test process exited with code ${v}`,stdout:y,stderr:h})})});try{console.log("[Lisa.ai Unit] Running tests with coverage. This may take a moment..."),await f(t,!0),console.log(`
|
|
136
136
|
[Lisa.ai Unit] Tests passed on attempt ${n}.`)}catch(d){let l=(d.stderr||"")+`
|
|
137
137
|
`+(d.stdout||"")+`
|
|
138
138
|
`+(d.message||"");if(ue(l,[],process.cwd())!==null){console.log(`
|
|
139
139
|
[Lisa.ai Unit] Tests failed. Delegating to Auto-Heal...`),await me(t,e,1,null,s,i,void 0,o),console.log(`
|
|
140
140
|
[Lisa.ai Unit] Auto-Heal complete. Restarting unit analysis...`),await ie(t,e,n+1,s,i,o);return}console.log(`
|
|
141
141
|
[Lisa.ai Unit] No failing spec detected. Tests may not exist yet \u2014 initiating Cold-Start Discovery...`)}try{let d=R.resolve(process.cwd(),"coverage/coverage-summary.json"),l=[];if(G.existsSync(d))console.log("[Lisa.ai Unit] Evaluating coverage summary..."),l=tt("coverage/coverage-summary.json");else{if(console.log(`
|
|
142
|
-
[Lisa.ai Unit] No coverage-summary.json found. Initiating Cold-Start Discovery...`),l=B.findUntestedFiles(process.cwd(),[...st]),l.length===0){console.log("[Lisa.ai Unit] No untested source files discovered.");return}console.log(`[Lisa.ai Unit] Discovered ${l.length} untested file(s). Generating specs...`)}if(l.length===0){console.log("[Lisa.ai Unit] 100% coverage achieved. No uncovered files remaining.");return}console.log(`[Lisa.ai Unit] Found ${l.length} file(s) below threshold:`,l);let g=l[0],m=R.resolve(process.cwd(),g),c=R.parse(m),y=[R.join(c.dir,`${c.name}.spec${c.ext}`),R.join(c.dir,`${c.name}.test${c.ext}`),R.join(c.dir,`${c.name}.spec.js`),R.join(c.dir,`${c.name}.test.js`),R.join(c.dir,`${c.name}.spec.ts`),R.join(c.dir,`${c.name}.test.ts`)],h=null,v=R.join(c.dir,`${c.name}.spec${c.ext}`);for(let L of y)if(G.existsSync(L)){v=L,h=G.readFileSync(L,"utf-8");break}let w="",$=!1,x=G.readFileSync(m,"utf-8");try{h?(console.log(`[Lisa.ai Unit] Existing spec found for ${g}. Requesting coverage append...`),w=await He(g,x,R.relative(process.cwd(),v),h,e,o,u,p)):(console.log(`[Lisa.ai Unit] Generating new spec for ${g}...`),w=await Z(g,x,e,o,u,p)),ye=0,$=!0}catch(L){ye++,st.add(g);let
|
|
143
|
-
[Lisa.ai Unit] LLM call failed for ${g} \u2014 consecutive failure #${ye}/${
|
|
142
|
+
[Lisa.ai Unit] No coverage-summary.json found. Initiating Cold-Start Discovery...`),l=B.findUntestedFiles(process.cwd(),[...st]),l.length===0){console.log("[Lisa.ai Unit] No untested source files discovered.");return}console.log(`[Lisa.ai Unit] Discovered ${l.length} untested file(s). Generating specs...`)}if(l.length===0){console.log("[Lisa.ai Unit] 100% coverage achieved. No uncovered files remaining.");return}console.log(`[Lisa.ai Unit] Found ${l.length} file(s) below threshold:`,l);let g=l[0],m=R.resolve(process.cwd(),g),c=R.parse(m),y=[R.join(c.dir,`${c.name}.spec${c.ext}`),R.join(c.dir,`${c.name}.test${c.ext}`),R.join(c.dir,`${c.name}.spec.js`),R.join(c.dir,`${c.name}.test.js`),R.join(c.dir,`${c.name}.spec.ts`),R.join(c.dir,`${c.name}.test.ts`)],h=null,v=R.join(c.dir,`${c.name}.spec${c.ext}`);for(let L of y)if(G.existsSync(L)){v=L,h=G.readFileSync(L,"utf-8");break}let w="",$=!1,x=G.readFileSync(m,"utf-8");try{h?(console.log(`[Lisa.ai Unit] Existing spec found for ${g}. Requesting coverage append...`),w=await He(g,x,R.relative(process.cwd(),v),h,e,o,u,p)):(console.log(`[Lisa.ai Unit] Generating new spec for ${g}...`),w=await Z(g,x,e,o,u,p)),ye=0,$=!0}catch(L){ye++,st.add(g);let E=L.cause?.message?`${L.message} (cause: ${L.cause.message})`:L.message;if(console.error(`
|
|
143
|
+
[Lisa.ai Unit] LLM call failed for ${g} \u2014 consecutive failure #${ye}/${Ee}`),console.error(` Error: ${E}`),ye>=Ee)throw new Error(`Systematic LLM failure: ${Ee} consecutive API calls failed.
|
|
144
144
|
This usually means your API key has insufficient credits, hit a rate limit, or the model is unavailable.
|
|
145
|
-
Last error: ${
|
|
145
|
+
Last error: ${E}
|
|
146
146
|
Tip: Check your API key, account credits, and rate-limit quota for provider '${e}'.`)}if(!$){await ie(t,e,n+1,s,i,o);return}G.writeFileSync(v,w,"utf-8"),console.log(`[Lisa.ai Unit] Wrote spec to ${v}`),await I({projectId:i,type:"unit",filePath:g,modelUsed:e,status:"success",details:`### Unit Test Generated
|
|
147
147
|
**Auto-Generated Spec (${e}):**
|
|
148
148
|
\`\`\`typescript
|
|
@@ -184,10 +184,10 @@ Use @playwright/test imports. Keep it concise and practical.
|
|
|
184
184
|
`,w=await Z(`e2e/${y}`,v,e,o,p,["e2e"]);j.writeFileSync(h,w,"utf-8"),console.log(`[Lisa.ai E2E] Wrote ${y}`),g++,await I({projectId:i,type:"e2e",filePath:`e2e/${y}`,modelUsed:e,status:"success",details:`Generated E2E spec for route /${m}`})}catch(v){console.error(`[Lisa.ai E2E] LLM failed for /${m}: ${v.message}`),await I({projectId:i,type:"e2e",filePath:`e2e/${y}`,modelUsed:e,status:"error",details:`LLM failed to generate E2E spec: ${v.message}`})}}if(g===0&&l.length===0){console.log("[Lisa.ai E2E] No specs generated. Check LLM connectivity with 'lisa-agent diagnose'.");return}console.log(`
|
|
185
185
|
[Lisa.ai E2E] Running E2E tests: ${t}`);try{let m=(0,re.spawn)(t,{shell:!0,stdio:"inherit",cwd:r});await new Promise((c,y)=>{m.on("close",h=>{h===0?c():y(new Error(`E2E tests exited with code ${h}`))})}),console.log(`
|
|
186
186
|
[Lisa.ai E2E] All E2E tests passed.`)}catch{console.log(`
|
|
187
|
-
[Lisa.ai E2E] Some E2E tests failed. Run 'lisa-agent heal --command "${t}"' to auto-fix.`)}await I({projectId:i,type:"e2e",filePath:"e2e-suite",modelUsed:e,status:"success",details:`E2E generation complete. ${g} new spec(s) generated, ${l.length} existing.`})}var it=require("child_process"),
|
|
187
|
+
[Lisa.ai E2E] Some E2E tests failed. Run 'lisa-agent heal --command "${t}"' to auto-fix.`)}await I({projectId:i,type:"e2e",filePath:"e2e-suite",modelUsed:e,status:"success",details:`E2E generation complete. ${g} new spec(s) generated, ${l.length} existing.`})}var it=require("child_process"),b=k(require("fs")),C=k(require("path"));function Mt(t){let e=C.join(t,"package.json");if(!b.existsSync(e))return"node";let n=JSON.parse(b.readFileSync(e,"utf-8")),s={...n.dependencies||{},...n.devDependencies||{}};return s["@angular/core"]?"angular":s.react?"react":s.vue?"vue":s["@nestjs/core"]?"nestjs":s.express?"express":"node"}function Dt(t,e){let n=[];if(e==="angular"){let s=C.join(t,"src","app");if(!b.existsSync(s))return n;let i=ae(s,/\.(ts)$/).filter(o=>!o.includes(".spec.")&&!o.includes(".test.")&&!o.includes("node_modules"));for(let o of i){let r=b.readFileSync(o,"utf-8");if(r.includes("@Component")&&r.includes("inject(")){let a=C.relative(t,o),u=a.replace(/\.ts$/,".integration.spec.ts");if(b.existsSync(C.join(t,u)))continue;let f=[...r.matchAll(/inject\((\w+)\)/g)].map(l=>l[1]),d=[];for(let l of f){let g=ae(s,new RegExp(`${Nt(l)}\\.ts$`));d.push(...g.map(m=>C.relative(t,m)))}n.push({filePath:a,relatedFiles:d,description:`Angular component+service integration: ${C.basename(o)} injects ${f.join(", ")}`})}}}else if(e==="express"||e==="nestjs"){let s=["src","server","api","routes","controllers"];for(let i of s){let o=C.join(t,i);if(!b.existsSync(o))continue;let r=ae(o,/\.(controller|route|router)\.(ts|js)$/).filter(a=>!a.includes(".spec.")&&!a.includes(".test.")&&!a.includes("node_modules"));for(let a of r){let u=C.relative(t,a),p=u.replace(/\.(ts|js)$/,".integration.spec.$1");b.existsSync(C.join(t,p))||n.push({filePath:u,relatedFiles:[],description:`API endpoint integration test for ${C.basename(a)} \u2014 test with supertest against real middleware chain`})}}}else{let s=C.join(t,"src");if(b.existsSync(s)){let i=ae(s,/\.(ts|js|tsx|jsx)$/).filter(o=>!o.includes(".spec.")&&!o.includes(".test.")&&!o.includes("node_modules"));for(let o of i){let a=[...b.readFileSync(o,"utf-8").matchAll(/from\s+['"]\.\.?\//g)];if(a.length>=2){let u=C.relative(t,o),p=u.replace(/\.(ts|js|tsx|jsx)$/,".integration.spec.$1");if(b.existsSync(C.join(t,p)))continue;n.push({filePath:u,relatedFiles:[],description:`Module integration: ${C.basename(o)} imports ${a.length} local modules`})}}}}return n}function ae(t,e){let n=[],s=["node_modules","dist","build",".git",".angular","coverage"];if(!b.existsSync(t))return n;let i=b.readdirSync(t);for(let o of i){if(s.includes(o))continue;let r=C.join(t,o);try{b.statSync(r).isDirectory()?n.push(...ae(r,e)):e.test(o)&&n.push(r)}catch{continue}}return n}function Nt(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}async function rt(t,e,n=1,s=3,i="local",o){if(n>s){console.log(`
|
|
188
188
|
[Lisa.ai Integration] Reached maximum retries (${s}). Stopping.`);return}let r=process.cwd(),a=F.load(r);a&&(F.applyEnvDefaults(a),!t&&a.testCommand&&(t=a.testCommand,console.log(`[Lisa.ai Config] Using testCommand from .lisai.json: ${t}`)));let u=Mt(r),p=B.scanRepository(),f=a?.testingFramework||(p.testingFramework!=="none"?p.testingFramework:void 0);console.log(`
|
|
189
|
-
[Lisa.ai Integration] App: ${u}, Framework: ${f||"auto"} (Attempt ${n}/${s}) Model: ${e}`),t||(p.suggestedTestCommand?t=p.suggestedTestCommand:
|
|
190
|
-
[Lisa.ai Fatal Error] Could not auto-detect a test command. Please pass --command explicitly.`),process.exit(1)),console.log(`[Lisa.ai Integration] Auto-detected command: ${t}`)),await I({projectId:i,type:"integration",filePath:"integration-suite",modelUsed:e,status:"running",details:`Integration test generation started for ${u} app`});let d=Dt(r,u);if(d.length===0){console.log("[Lisa.ai Integration] No integration targets discovered. The project may already have full integration coverage.");return}console.log(`[Lisa.ai Integration] Found ${d.length} integration target(s):`),d.forEach(g=>console.log(` - ${g.filePath}: ${g.description}`));let l=0;for(let g of d){let m=C.resolve(r,g.filePath);if(!
|
|
189
|
+
[Lisa.ai Integration] App: ${u}, Framework: ${f||"auto"} (Attempt ${n}/${s}) Model: ${e}`),t||(p.suggestedTestCommand?t=p.suggestedTestCommand:b.existsSync(C.join(r,"angular.json"))?t="npx ng test --no-watch --browsers=ChromeHeadless":(console.error(`
|
|
190
|
+
[Lisa.ai Fatal Error] Could not auto-detect a test command. Please pass --command explicitly.`),process.exit(1)),console.log(`[Lisa.ai Integration] Auto-detected command: ${t}`)),await I({projectId:i,type:"integration",filePath:"integration-suite",modelUsed:e,status:"running",details:`Integration test generation started for ${u} app`});let d=Dt(r,u);if(d.length===0){console.log("[Lisa.ai Integration] No integration targets discovered. The project may already have full integration coverage.");return}console.log(`[Lisa.ai Integration] Found ${d.length} integration target(s):`),d.forEach(g=>console.log(` - ${g.filePath}: ${g.description}`));let l=0;for(let g of d){let m=C.resolve(r,g.filePath);if(!b.existsSync(m))continue;let c=C.parse(m),y=C.join(c.dir,`${c.name}.integration.spec${c.ext}`);if(b.existsSync(y)){console.log(`[Lisa.ai Integration] Spec exists for ${g.filePath} \u2014 skipping.`);continue}console.log(`[Lisa.ai Integration] Generating integration spec for ${g.filePath}...`);let h=b.readFileSync(m,"utf-8"),v="";for(let $ of g.relatedFiles.slice(0,3)){let x=C.resolve(r,$);if(b.existsSync(x)){let L=b.readFileSync(x,"utf-8");v+=`
|
|
191
191
|
|
|
192
192
|
// --- Related file: ${$} ---
|
|
193
193
|
${L.slice(0,5e3)}`}}let w=`
|
|
@@ -214,7 +214,7 @@ ${h.slice(0,1e4)}
|
|
|
214
214
|
|
|
215
215
|
${v?`Related modules:
|
|
216
216
|
${v}`:""}
|
|
217
|
-
`;try{let $=await Z(g.filePath,w,e,o,f,["integration"]);
|
|
217
|
+
`;try{let $=await Z(g.filePath,w,e,o,f,["integration"]);b.writeFileSync(y,$,"utf-8"),console.log(`[Lisa.ai Integration] Wrote ${C.relative(r,y)}`),l++,await I({projectId:i,type:"integration",filePath:g.filePath,modelUsed:e,status:"success",details:`Generated integration spec: ${g.description}`})}catch($){console.error(`[Lisa.ai Integration] LLM failed for ${g.filePath}: ${$.message}`)}}if(l===0){console.log("[Lisa.ai Integration] No new integration specs generated.");return}console.log(`
|
|
218
218
|
[Lisa.ai Integration] Running tests to verify generated specs: ${t}`);try{let g=(0,it.spawn)(t,{shell:!0,stdio:"inherit",cwd:r});await new Promise((m,c)=>{g.on("close",y=>{y===0?m():c(new Error(`Tests exited with code ${y}`))})}),console.log(`
|
|
219
219
|
[Lisa.ai Integration] All tests passed including integration specs.`)}catch{console.log(`
|
|
220
220
|
[Lisa.ai Integration] Some tests failed. Run 'lisa-agent heal' to auto-fix.`)}await I({projectId:i,type:"integration",filePath:"integration-suite",modelUsed:e,status:"success",details:`Integration test generation complete. ${l} new spec(s) generated.`})}var ct=k(require("dotenv")),ut=require("child_process"),K=k(require("fs")),Y=k(require("path"));ct.config({quiet:!0});function te(t){return new Promise(e=>{let n=(0,ut.spawn)(t,{shell:!0,stdio:["ignore","pipe","pipe"]}),s="",i="";n.stdout?.on("data",o=>{s+=o.toString()}),n.stderr?.on("data",o=>{i+=o.toString()}),n.on("close",o=>e({stdout:s,stderr:i,code:o??1}))})}function Ot(){return K.existsSync(Y.resolve(process.cwd(),"pnpm-lock.yaml"))?"pnpm":K.existsSync(Y.resolve(process.cwd(),"yarn.lock"))?"yarn":"npm"}var Ut={critical:"\u{1F534}",high:"\u{1F7E0}",moderate:"\u{1F7E1}",low:"\u{1F535}",info:"\u26AA"},at={critical:5,high:4,moderate:3,low:2,info:1};function lt(t){let e=["critical","high","moderate","low","info"];for(let n of e){let s=t[n]??0;s>0&&console.log(` ${Ut[n]} ${n.padEnd(10)} ${s}`)}console.log(` ${"\u2500".repeat(22)}`),console.log(` ${"Total".padEnd(10)} ${t.total}`)}async function Fe(t){try{return JSON.parse(t)}catch{return null}}async function dt(t,e="local",n){K.existsSync(Y.resolve(process.cwd(),"package.json"))||(console.error(`
|
|
@@ -229,7 +229,7 @@ ${v}`:""}
|
|
|
229
229
|
[Lisa.ai Audit] Applying ${h.length} non-breaking targeted upgrade(s)...`);for(let z of h){let U=`npm install ${z.name}@${z.version}`;console.log(` \u2192 ${U}`);let{stderr:ne}=await te(U);ne&&!ne.includes("npm warn")&&console.warn(` ${ne.trim()}`)}let{stdout:S,code:M}=await te(p);if(M===0){console.log(`
|
|
230
230
|
\u2705 [Lisa.ai Audit] All vulnerabilities resolved!`);return}}let{stdout:v}=await te(p),w=await Fe(v)??l,x=Object.values(w.vulnerabilities).sort((S,M)=>(at[M.severity]??0)-(at[S.severity]??0)).slice(0,10).filter(S=>S.fixAvailable===!1?!0:typeof S.fixAvailable=="object"?S.fixAvailable.isSemVerMajor:!1);if(x.length===0){console.log(`
|
|
231
231
|
\u2705 [Lisa.ai Audit] No breaking-change or unfixable vulnerabilities remain.`);return}console.log(`
|
|
232
|
-
[Lisa.ai Audit] ${x.length} vulnerability(-ies) require manual attention.`),console.log(`[Lisa.ai Audit] Consulting ${t} for remediation guidance...`);let L={};try{L=JSON.parse(K.readFileSync(Y.resolve(process.cwd(),"package.json"),"utf8"))}catch{}let
|
|
232
|
+
[Lisa.ai Audit] ${x.length} vulnerability(-ies) require manual attention.`),console.log(`[Lisa.ai Audit] Consulting ${t} for remediation guidance...`);let L={};try{L=JSON.parse(K.readFileSync(Y.resolve(process.cwd(),"package.json"),"utf8"))}catch{}let E={...L.dependencies??{},...L.devDependencies??{}},O=x.map(S=>{let M=S.fixAvailable,z=M===!1?"no fix available yet":`upgrade to ${M.name}@${M.version} (BREAKING \u2014 major version bump)`,U=E[S.name]?`current: ${E[S.name]}`:"transitive dependency";return`\u2022 ${S.name} (${S.severity}) \u2014 ${U} \u2014 ${z}`}).join(`
|
|
233
233
|
`),H=`You are a Node.js security expert helping a developer fix npm vulnerabilities.
|
|
234
234
|
|
|
235
235
|
These vulnerabilities remain after safe auto-fixes were applied. Each requires either a breaking upgrade or has no automated fix:
|