@lisa.ai/agent 2.9.0 → 2.9.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 +30 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,11 +28,20 @@ Runs your test command, identifies every failing spec, and autonomously fixes th
|
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
30
|
lisa-agent heal --command "npm test" --model gemini
|
|
31
|
+
|
|
32
|
+
# Heal specific files only (skip discovery)
|
|
33
|
+
lisa-agent heal --files src/app/todo.spec.ts,src/app/auth.spec.ts
|
|
34
|
+
|
|
35
|
+
# Heal E2E tests
|
|
36
|
+
lisa-agent heal --type e2e
|
|
37
|
+
|
|
38
|
+
# Heal specific E2E files
|
|
39
|
+
lisa-agent heal --type e2e --files e2e/login.spec.ts
|
|
31
40
|
```
|
|
32
41
|
|
|
33
42
|
**How it works:**
|
|
34
|
-
1. Runs a single global test pass to discover all failures
|
|
35
|
-
2. Extracts every failing spec file (Karma suite-name matching, Jest `FAIL` header parsing)
|
|
43
|
+
1. Runs a single global test pass to discover all failures (skipped with `--files`)
|
|
44
|
+
2. Extracts every failing spec file (Karma suite-name matching, Jest `FAIL` header parsing, Playwright/Cypress)
|
|
36
45
|
3. For each file: queries the Memory Engine for proven fix patterns, sends context + error log to the LLM, writes the fix, verifies in isolation
|
|
37
46
|
4. Runs a single final global test pass to confirm all fixes together
|
|
38
47
|
5. Files that regress are quarantined after 3 strikes
|
|
@@ -46,6 +55,9 @@ Runs your test suite with coverage enabled, identifies files below 100% coverage
|
|
|
46
55
|
```bash
|
|
47
56
|
lisa-agent unit --model gemini
|
|
48
57
|
lisa-agent unit --command "npx jest --coverage --coverageReporters=json-summary" --model claude
|
|
58
|
+
|
|
59
|
+
# Generate tests for specific source files only
|
|
60
|
+
lisa-agent unit --files src/services/auth.service.ts,src/services/user.service.ts
|
|
49
61
|
```
|
|
50
62
|
|
|
51
63
|
**How it works:**
|
|
@@ -71,6 +83,9 @@ Generates Playwright end-to-end tests by discovering your app's routes and creat
|
|
|
71
83
|
|
|
72
84
|
```bash
|
|
73
85
|
lisa-agent e2e --model gemini
|
|
86
|
+
|
|
87
|
+
# Generate E2E test for a specific spec path
|
|
88
|
+
lisa-agent e2e --files e2e/login.spec.ts
|
|
74
89
|
```
|
|
75
90
|
|
|
76
91
|
**How it works:**
|
|
@@ -86,6 +101,9 @@ Generates integration tests that exercise module seams — testing 2+ modules to
|
|
|
86
101
|
|
|
87
102
|
```bash
|
|
88
103
|
lisa-agent integration --model gemini
|
|
104
|
+
|
|
105
|
+
# Generate integration test for specific files
|
|
106
|
+
lisa-agent integration --files src/services/auth.service.ts
|
|
89
107
|
```
|
|
90
108
|
|
|
91
109
|
**How it works:**
|
|
@@ -140,6 +158,8 @@ All test commands (`heal`, `unit`, `e2e`, `integration`, `audit`) accept:
|
|
|
140
158
|
| `-c, --command <cmd>` | Test/coverage command to run | Auto-detected |
|
|
141
159
|
| `-m, --model <provider>` | `gemini`, `claude`, or `openai` | `gemini` |
|
|
142
160
|
| `-p, --project-id <id>` | Control Plane project for remote config + telemetry | `local` |
|
|
161
|
+
| `-f, --files <paths>` | Target specific files (comma-separated, bypasses discovery) | All files |
|
|
162
|
+
| `-t, --type <scope>` | (heal only) Test type to heal: `unit`, `e2e`, `integration` | `unit` |
|
|
143
163
|
|
|
144
164
|
## Configuration
|
|
145
165
|
|
|
@@ -252,6 +272,14 @@ lisa-agent integration --model openai
|
|
|
252
272
|
lisa-agent audit --model gemini
|
|
253
273
|
```
|
|
254
274
|
|
|
275
|
+
### Target specific files (skip discovery for large projects)
|
|
276
|
+
```bash
|
|
277
|
+
lisa-agent heal --files src/app/todo.spec.ts,src/app/auth.spec.ts --model gemini
|
|
278
|
+
lisa-agent unit --files src/services/auth.service.ts --model claude
|
|
279
|
+
lisa-agent e2e --files e2e/login.spec.ts --model gemini
|
|
280
|
+
lisa-agent integration --files src/services/user.service.ts --model openai
|
|
281
|
+
```
|
|
282
|
+
|
|
255
283
|
### Full CI/CD pipeline with Control Plane
|
|
256
284
|
```bash
|
|
257
285
|
lisa-agent heal --command "npm test" --model gemini --project-id "prod-api"
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var Et=Object.create;var Ue=Object.defineProperty;var jt=Object.getOwnPropertyDescriptor;var Ct=Object.getOwnPropertyNames;var bt=Object.getPrototypeOf,At=Object.prototype.hasOwnProperty;var Ft=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var Pt=(t,e,s,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ct(e))!At.call(t,i)&&i!==s&&Ue(t,i,{get:()=>e[i],enumerable:!(n=jt(e,i))||n.enumerable});return t};var j=(t,e,s)=>(s=t!=null?Et(bt(t)):{},Pt(e||!t||!t.__esModule?Ue(s,"default",{value:t,enumerable:!0}):s,t));var vt=Ft((Zs,Bt)=>{Bt.exports={name:"@lisa.ai/agent",version:"2.9.
|
|
2
|
+
"use strict";var Et=Object.create;var Ue=Object.defineProperty;var jt=Object.getOwnPropertyDescriptor;var Ct=Object.getOwnPropertyNames;var bt=Object.getPrototypeOf,At=Object.prototype.hasOwnProperty;var Ft=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var Pt=(t,e,s,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ct(e))!At.call(t,i)&&i!==s&&Ue(t,i,{get:()=>e[i],enumerable:!(n=jt(e,i))||n.enumerable});return t};var j=(t,e,s)=>(s=t!=null?Et(bt(t)):{},Pt(e||!t||!t.__esModule?Ue(s,"default",{value:t,enumerable:!0}):s,t));var vt=Ft((Zs,Bt)=>{Bt.exports={name:"@lisa.ai/agent",version:"2.9.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 xt=require("commander"),Ee=j(require("fs")),$t=j(require("path")),Lt=j(require("readline"));var Oe=j(require("dotenv"));Oe.config({quiet:!0});async function ge(t){let e=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3000";try{let s=`${e}/api/config/${t}`;console.log(`[Lisa.ai Agent] Fetching dynamic configuration from ${s}...`);let n=await fetch(s);if(!n.ok){let o=n.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 ${n.status}. Falling back to local defaults.`),{ok:!1,reason:o}}return{ok:!0,config:await n.json()}}catch{return console.warn(`[Lisa.ai Agent Warning] Failed to reach Control Plane (${e}). Using local fallback configuration.`),{ok:!1,reason:"unreachable"}}}var ot=require("child_process"),_=j(require("fs")),V=j(require("path"));var S=j(require("path")),T=j(require("fs"));function fe(t){return t.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,"")}function Z(t){let e=fe(t),s=[...e.matchAll(/Executed\s+(\d+)\s+of\s+(\d+)(?:\s+\((\d+)\s+FAILED\))?/g)];if(s.length>0){let u=[...s].reverse().find(m=>m[3]!==void 0)??s[s.length-1],p=parseInt(u[2]),w=u[3]?parseInt(u[3]):0,h=parseInt(u[1])-w;return{total:p,passed:h,failed:w}}let n=e.match(/Tests:\s+(?:(\d+)\s+failed,?\s*)?(?:(\d+)\s+passed,?\s*)?(\d+)\s+total/);if(n){let u=parseInt(n[3]),p=n[1]?parseInt(n[1]):0,w=n[2]?parseInt(n[2]):0;return{total:u,passed:w,failed:p}}let i=e.match(/(\d+)\s+failed/),o=e.match(/(\d+)\s+passed/);if(o||i){let u=i?parseInt(i[1]):0,p=o?parseInt(o[1]):0,w=e.match(/(\d+)\s+skipped/),h=w?parseInt(w[1]):0;return{total:p+u+h,passed:p,failed:u}}let r=e.match(/Tests\s+(?:(\d+)\s+failed\s*\|?\s*)?(\d+)\s+passed\s*\((\d+)\)/);if(r){let u=parseInt(r[3]),p=r[1]?parseInt(r[1]):0,w=parseInt(r[2]);return{total:u,passed:w,failed:p}}let a=e.match(/(\d+)\s+passing/),g=e.match(/(\d+)\s+failing/);if(a){let u=parseInt(a[1]),p=g?parseInt(g[1]):0;return{total:u+p,passed:u,failed:p}}return null}function Ce(t,e=process.cwd(),s=[]){let n=t.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,""),i=s.map(l=>S.resolve(e,l)),o=new Map,r=new Set,a=/(?:Chrome|Firefox|Safari)(?:\s+Headless)?\s+[\d.]+(?:\s+[\d.]+)*\s+\([^)]+\)\s+(.+?)\s+FAILED/g,g;for(;(g=a.exec(n))!==null;){let d=g[1].trim().split(" ");for(let v=1;v<Math.min(d.length,6);v++){let k=d.slice(0,v).join(" ");if(r.has(k))continue;r.add(k);let f=be(k,e,i);if(f){let c=S.resolve(f);o.has(c)||o.set(c,S.relative(e,f));break}}}let u=/^FAIL\s+([a-zA-Z0-9_./-\\]+\.(?:spec|test)\.(ts|tsx|js|jsx))/gm,p;for(;(p=u.exec(n))!==null;){let l=p[1],d=S.isAbsolute(l)?l:S.resolve(e,l);!i.includes(d)&&T.existsSync(d)&&!o.has(d)&&o.set(d,S.relative(e,d))}let w=/\[(?:chromium|firefox|webkit)\]\s+[>›]\s+([^\s:]+\.(?:spec|test)\.[tj]sx?):\d+:\d+\s+[>›]/g,h;for(;(h=w.exec(n))!==null;){let l=h[1],d=S.isAbsolute(l)?l:S.resolve(e,l);!i.includes(d)&&T.existsSync(d)&&!o.has(d)&&o.set(d,S.relative(e,d))}let m=/Running:\s+([^\s]+\.(?:cy|spec|test)\.[tj]sx?)/g,y;for(;(y=m.exec(n))!==null;){let l=y[1],d=S.isAbsolute(l)?l:S.resolve(e,l);if(!i.includes(d)&&T.existsSync(d)&&!o.has(d)){let v=l.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");new RegExp(v+"\\s+\\d+\\s+\\d+\\s+([1-9]\\d*)").test(n)&&o.set(d,S.relative(e,d))}}return[...o.values()]}function me(t,e=[],s=process.cwd()){let n=t.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,""),i=e.map(l=>S.resolve(l));{let l=/(?:Chrome|Firefox|Safari)(?:\s+Headless)?\s+[\d.]+(?:\s+[\d.]+)*\s+\([^)]+\)\s+(.+?)\s+FAILED/g,d=new Set,v;for(;(v=l.exec(n))!==null;){let f=v[1].trim().split(" ");for(let c=1;c<Math.min(f.length,5);c++){let x=f.slice(0,c).join(" ");if(d.has(x))continue;d.add(x);let L=be(x,s,i);if(L)return S.relative(s,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(n))!==null;){let l=r[1];if(l){if(/(?:^|[/\\])(node_modules|dist|build)[/\\]/.test(l))continue;let d=S.isAbsolute(l)?l:S.resolve(s,l);if(!i.includes(d)&&T.existsSync(d))return l}}let a=/FAIL\s+([a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue))/gi,g;for(;(g=a.exec(n))!==null;){let l=g[1],d=S.isAbsolute(l)?l:S.resolve(s,l);if(!i.includes(d)&&T.existsSync(d))return l}{let l=/\[(?:chromium|firefox|webkit)\]\s+[>›]\s+([^\s:]+\.(?:spec|test)\.[tj]sx?):\d+:\d+\s+[>›]/g,d;for(;(d=l.exec(n))!==null;){let v=d[1],k=S.isAbsolute(v)?v:S.resolve(s,v);if(!i.includes(k)&&T.existsSync(k))return v}}let u=/([a-zA-Z]:[a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue)|[a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue))/g,p;for(;(p=u.exec(n))!==null;){let l=p[1];if(l){if(/(?:^|[/\\])(node_modules|dist|build)[/\\]/.test(l))continue;let d=S.isAbsolute(l)?l:S.resolve(s,l);if(!i.includes(d)&&T.existsSync(d))return l}}let w=/\b([A-Z][a-zA-Z0-9]{3,})\b/g,h,m=new Set,y=["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(;(h=w.exec(n))!==null;){let l=h[1];if(!l||y.includes(l)||m.has(l)||/^[A-Z][A-Z0-9]{5,11}$/.test(l)||/^[A-Za-z0-9]{16,}$/.test(l))continue;m.add(l);let d=_e(l,s,i);if(d)return S.relative(s,d)}return null}function be(t,e,s){if(!T.existsSync(e))return null;let n=T.readdirSync(e);for(let i of n){let o=S.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=be(t,o,s);if(a)return a}else if(/\.(spec|test)\.(ts|tsx|js|jsx)$/.test(i)){if(s.includes(S.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 _e(t,e,s){if(!T.existsSync(e))return null;let n=T.readdirSync(e);for(let i of n){let o=S.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=_e(t,o,s);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 g=o,u=S.extname(o),p=o.slice(0,-u.length);if(!i.includes(".spec.")&&!i.includes(".test.")){let w=[`${p}.spec${u}`,`${p}.test${u}`,`${p}.spec.js`,`${p}.test.js`];for(let h of w)if(T.existsSync(h)){g=h;break}}if(!s.includes(S.resolve(g)))return g}}}return null}var de=require("ai"),Ge=require("@ai-sdk/openai"),He=require("@ai-sdk/anthropic"),Be=require("@ai-sdk/google"),qe=j(require("dotenv"));qe.config({quiet:!0});var Ae=15e3;function We(t,e){return t.length<=Ae?t:(console.warn(`[Lisa.ai LLM] ${e} is ${t.length} chars \u2014 truncating to ${Ae} to stay within context window.`),t.slice(0,Ae)+`
|
|
3
3
|
|
|
4
4
|
// ... (truncated)`)}function Ye(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 Ve(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(n=>` \u2022 ${e[n]??n}`).join(`
|