@lisa.ai/agent 2.10.0 → 2.11.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.
Files changed (3) hide show
  1. package/README.md +40 -0
  2. package/dist/index.js +283 -170
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -146,6 +146,46 @@ lisa-agent audit
146
146
  4. **Targeted upgrades** — applies non-breaking semver-minor fixes
147
147
  5. **LLM guidance** — for remaining breaking/unfixable vulnerabilities, consults the LLM and saves a remediation report to `lisa-audit-report.md`
148
148
 
149
+ ### `chat` — Interactive Mode
150
+
151
+ Multi-turn interactive REPL with streaming LLM responses. Ask questions about your project, analyze test failures, heal specific files, and generate tests — all in a conversational loop.
152
+
153
+ ```bash
154
+ lisa-agent chat
155
+
156
+ # Use a specific LLM provider
157
+ lisa-agent chat -m claude
158
+ ```
159
+
160
+ **Slash commands:**
161
+
162
+ | Command | Description |
163
+ |---|---|
164
+ | `/test [cmd]` | Run tests and show results |
165
+ | `/explain <file>` | Analyze why a test is failing (no file changes) |
166
+ | `/heal <file>` | Auto-heal a specific failing test file |
167
+ | `/generate <file>` | Generate test spec for a source file |
168
+ | `/status` | Show test pass/fail summary |
169
+ | `/help` | Show available commands |
170
+ | `/exit` | Exit chat |
171
+
172
+ **Example session:**
173
+ ```
174
+ lisa> why would an Angular test get NullInjectorError for HttpClient?
175
+ [streams explanation suggesting HttpClientTestingModule]
176
+
177
+ lisa> /test
178
+ Running: npx ng test --no-watch --browsers=ChromeHeadless...
179
+ ✗ 3 of 47 tests failed
180
+
181
+ lisa> /heal src/app/services/auth.service.spec.ts
182
+ Healing src/app/services/auth.service.spec.ts...
183
+ ✅ Healed via Cloud.
184
+
185
+ lisa> /status
186
+ [████████████████████] 100% ✅ 47 passed 47 total
187
+ ```
188
+
149
189
  ### `diagnose` — Test LLM Connectivity
150
190
 
151
191
  Sends a simple probe to your configured LLM provider to verify your API key works and the model is reachable.
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
- "use strict";var It=Object.create;var We=Object.defineProperty;var Rt=Object.getOwnPropertyDescriptor;var Mt=Object.getOwnPropertyNames;var Nt=Object.getPrototypeOf,Ut=Object.prototype.hasOwnProperty;var Dt=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var _t=(e,t,n,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of Mt(t))!Ut.call(e,o)&&o!==n&&We(e,o,{get:()=>t[o],enumerable:!(s=Rt(t,o))||s.enumerable});return e};var j=(e,t,n)=>(n=e!=null?It(Nt(e)):{},_t(t||!e||!e.__esModule?We(n,"default",{value:e,enumerable:!0}):n,e));var Et=Dt((un,Qt)=>{Qt.exports={name:"@lisa.ai/agent",version:"2.10.0",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 jt=require("commander"),ee=j(require("fs")),Le=j(require("path")),At=j(require("readline"));var Ze=j(require("dotenv"));Ze.config({quiet:!0});async function xe(e){let t=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3088";try{let n=`${t}/api/v1/lisa/config/${e}`;console.log(`[Lisa.ai Agent] Fetching dynamic configuration from ${n}...`);let s=await fetch(n);if(!s.ok){let i=s.status===404?"not_found":"unreachable";return console.warn(i==="not_found"?`[Lisa.ai Agent Warning] Control Plane returned 404. Project '${e}' not found.`:`[Lisa.ai Agent Warning] Control Plane returned ${s.status}. Falling back to local defaults.`),{ok:!1,reason:i}}return{ok:!0,config:await s.json()}}catch{return console.warn(`[Lisa.ai Agent Warning] Failed to reach Control Plane (${t}). Using local fallback configuration.`),{ok:!1,reason:"unreachable"}}}var ft=require("child_process"),O=j(require("fs")),J=j(require("path"));var F=j(require("path")),N=j(require("fs"));function $e(e){return e.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,"")}function Y(e){let t=$e(e),n=[...t.matchAll(/Executed\s+(\d+)\s+of\s+(\d+)(?:\s+\((\d+)\s+FAILED\))?/g)];if(n.length>0){let p=[...n].reverse().find(x=>x[3]!==void 0)??n[n.length-1],u=parseInt(p[2]),L=p[3]?parseInt(p[3]):0,f=parseInt(p[1])-L;return{total:u,passed:f,failed:L}}let s=t.match(/Tests:\s+(?:(\d+)\s+failed,?\s*)?(?:(\d+)\s+passed,?\s*)?(\d+)\s+total/);if(s){let p=parseInt(s[3]),u=s[1]?parseInt(s[1]):0,L=s[2]?parseInt(s[2]):0;return{total:p,passed:L,failed:u}}let o=t.match(/(\d+)\s+failed/),i=t.match(/(\d+)\s+passed/);if(i||o){let p=o?parseInt(o[1]):0,u=i?parseInt(i[1]):0,L=t.match(/(\d+)\s+skipped/),f=L?parseInt(L[1]):0;return{total:u+p+f,passed:u,failed:p}}let r=t.match(/Tests\s+(?:(\d+)\s+failed\s*\|?\s*)?(\d+)\s+passed\s*\((\d+)\)/);if(r){let p=parseInt(r[3]),u=r[1]?parseInt(r[1]):0,L=parseInt(r[2]);return{total:p,passed:L,failed:u}}let a=t.match(/(\d+)\s+passing/),d=t.match(/(\d+)\s+failing/);if(a){let p=parseInt(a[1]),u=d?parseInt(d[1]):0;return{total:p+u,passed:p,failed:u}}return null}function Ue(e,t=process.cwd(),n=[]){let s=e.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,""),o=n.map(c=>F.resolve(t,c)),i=new Map,r=new Set,a=/(?:Chrome|Firefox|Safari)(?:\s+Headless)?\s+[\d.]+(?:\s+[\d.]+)*\s+\([^)]+\)\s+(.+?)\s+FAILED/g,d;for(;(d=a.exec(s))!==null;){let l=d[1].trim().split(" ");for(let y=1;y<Math.min(l.length,6);y++){let h=l.slice(0,y).join(" ");if(r.has(h))continue;r.add(h);let C=De(h,t,o);if(C){let v=F.resolve(C);i.has(v)||i.set(v,F.relative(t,C));break}}}let p=/^FAIL\s+([a-zA-Z0-9_./-\\]+\.(?:spec|test)\.(ts|tsx|js|jsx))/gm,u;for(;(u=p.exec(s))!==null;){let c=u[1],l=F.isAbsolute(c)?c:F.resolve(t,c);!o.includes(l)&&N.existsSync(l)&&!i.has(l)&&i.set(l,F.relative(t,l))}let L=/\[(?:chromium|firefox|webkit)\]\s+[>›]\s+([^\s:]+\.(?:spec|test)\.[tj]sx?):\d+:\d+\s+[>›]/g,f;for(;(f=L.exec(s))!==null;){let c=f[1],l=F.isAbsolute(c)?c:F.resolve(t,c);!o.includes(l)&&N.existsSync(l)&&!i.has(l)&&i.set(l,F.relative(t,l))}let x=/Running:\s+([^\s]+\.(?:cy|spec|test)\.[tj]sx?)/g,m;for(;(m=x.exec(s))!==null;){let c=m[1],l=F.isAbsolute(c)?c:F.resolve(t,c);if(!o.includes(l)&&N.existsSync(l)&&!i.has(l)){let y=c.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");new RegExp(y+"\\s+\\d+\\s+\\d+\\s+([1-9]\\d*)").test(s)&&i.set(l,F.relative(t,l))}}return[...i.values()]}function Ce(e,t=[],n=process.cwd()){let s=e.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,""),o=t.map(c=>F.resolve(c));{let c=/(?:Chrome|Firefox|Safari)(?:\s+Headless)?\s+[\d.]+(?:\s+[\d.]+)*\s+\([^)]+\)\s+(.+?)\s+FAILED/g,l=new Set,y;for(;(y=c.exec(s))!==null;){let C=y[1].trim().split(" ");for(let v=1;v<Math.min(C.length,5);v++){let $=C.slice(0,v).join(" ");if(l.has($))continue;l.add($);let g=De($,n,o);if(g)return F.relative(n,g)}}}let i=/([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=i.exec(s))!==null;){let c=r[1];if(c){if(/(?:^|[/\\])(node_modules|dist|build)[/\\]/.test(c))continue;let l=F.isAbsolute(c)?c:F.resolve(n,c);if(!o.includes(l)&&N.existsSync(l))return c}}let a=/FAIL\s+([a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue))/gi,d;for(;(d=a.exec(s))!==null;){let c=d[1],l=F.isAbsolute(c)?c:F.resolve(n,c);if(!o.includes(l)&&N.existsSync(l))return c}{let c=/\[(?:chromium|firefox|webkit)\]\s+[>›]\s+([^\s:]+\.(?:spec|test)\.[tj]sx?):\d+:\d+\s+[>›]/g,l;for(;(l=c.exec(s))!==null;){let y=l[1],h=F.isAbsolute(y)?y:F.resolve(n,y);if(!o.includes(h)&&N.existsSync(h))return y}}let p=/([a-zA-Z]:[a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue)|[a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue))/g,u;for(;(u=p.exec(s))!==null;){let c=u[1];if(c){if(/(?:^|[/\\])(node_modules|dist|build)[/\\]/.test(c))continue;let l=F.isAbsolute(c)?c:F.resolve(n,c);if(!o.includes(l)&&N.existsSync(l))return c}}let L=/\b([A-Z][a-zA-Z0-9]{3,})\b/g,f,x=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(;(f=L.exec(s))!==null;){let c=f[1];if(!c||m.includes(c)||x.has(c)||/^[A-Z][A-Z0-9]{5,11}$/.test(c)||/^[A-Za-z0-9]{16,}$/.test(c))continue;x.add(c);let l=ze(c,n,o);if(l)return F.relative(n,l)}return null}function De(e,t,n){if(!N.existsSync(t))return null;let s=N.readdirSync(t);for(let o of s){let i=F.join(t,o);if(["node_modules","dist","build",".git",".angular"].includes(o))continue;let r;try{r=N.statSync(i)}catch{continue}if(r.isDirectory()){let a=De(e,i,n);if(a)return a}else if(/\.(spec|test)\.(ts|tsx|js|jsx)$/.test(o)){if(n.includes(F.resolve(i)))continue;try{let a=N.readFileSync(i,"utf8");if(a.includes(`describe('${e}'`)||a.includes(`describe("${e}"`))return i}catch{continue}}}return null}function ze(e,t,n){if(!N.existsSync(t))return null;let s=N.readdirSync(t);for(let o of s){let i=F.join(t,o);if(o==="node_modules"||o==="dist"||o==="build"||o===".git"||o===".angular")continue;let r;try{r=N.statSync(i)}catch{continue}if(r.isDirectory()){let a=ze(e,i,n);if(a)return a}else if(o.match(/\.(ts|tsx|js|jsx|vue)$/)){let a=N.readFileSync(i,"utf8");if(a.includes(`class ${e}`)||a.includes(`function ${e}`)||a.includes(`const ${e}`)||a.includes(`let ${e}`)||a.includes(`exports.${e}`)||a.includes(`module.exports.${e}`)){let d=i,p=F.extname(i),u=i.slice(0,-p.length);if(!o.includes(".spec.")&&!o.includes(".test.")){let L=[`${u}.spec${p}`,`${u}.test${p}`,`${u}.spec.js`,`${u}.test.js`];for(let f of L)if(N.existsSync(f)){d=f;break}}if(!n.includes(F.resolve(d)))return d}}}return null}var te=require("ai"),ke=require("@ai-sdk/openai"),Je=require("@ai-sdk/anthropic"),Qe=require("@ai-sdk/google"),Xe=j(require("dotenv"));Xe.config({quiet:!0});var _e=15e3;function be(e,t){return e.length<=_e?e:(console.warn(`[Lisa.ai LLM] ${t} is ${e.length} chars \u2014 truncating to ${_e} to stay within context window.`),e.slice(0,_e)+`
2
+ "use strict";var Wt=Object.create;var tt=Object.defineProperty;var zt=Object.getOwnPropertyDescriptor;var Zt=Object.getOwnPropertyNames;var Jt=Object.getPrototypeOf,Qt=Object.prototype.hasOwnProperty;var Xt=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var es=(e,t,s,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of Zt(t))!Qt.call(e,o)&&o!==s&&tt(e,o,{get:()=>t[o],enumerable:!(n=zt(t,o))||n.enumerable});return e};var b=(e,t,s)=>(s=e!=null?Wt(Jt(e)):{},es(t||!e||!e.__esModule?tt(s,"default",{value:e,enumerable:!0}):s,e));var Bt=Xt((Kn,Ls)=>{Ls.exports={name:"@lisa.ai/agent",version:"2.11.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 qt=require("commander"),re=b(require("fs")),be=b(require("path")),Yt=b(require("readline"));var st=b(require("dotenv"));st.config({quiet:!0});async function je(e){let t=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3088";try{let s=`${t}/api/v1/lisa/config/${e}`;console.log(`[Lisa.ai Agent] Fetching dynamic configuration from ${s}...`);let n=await fetch(s);if(!n.ok){let i=n.status===404?"not_found":"unreachable";return console.warn(i==="not_found"?`[Lisa.ai Agent Warning] Control Plane returned 404. Project '${e}' not found.`:`[Lisa.ai Agent Warning] Control Plane returned ${n.status}. Falling back to local defaults.`),{ok:!1,reason:i}}return{ok:!0,config:await n.json()}}catch{return console.warn(`[Lisa.ai Agent Warning] Failed to reach Control Plane (${t}). Using local fallback configuration.`),{ok:!1,reason:"unreachable"}}}var $t=require("child_process"),B=b(require("fs")),te=b(require("path"));var j=b(require("path")),D=b(require("fs"));function Ee(e){return e.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,"")}function G(e){let t=Ee(e),s=[...t.matchAll(/Executed\s+(\d+)\s+of\s+(\d+)(?:\s+\((\d+)\s+FAILED\))?/g)];if(s.length>0){let u=[...s].reverse().find(L=>L[3]!==void 0)??s[s.length-1],p=parseInt(u[2]),$=u[3]?parseInt(u[3]):0,g=parseInt(u[1])-$;return{total:p,passed:g,failed:$}}let n=t.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,$=n[2]?parseInt(n[2]):0;return{total:u,passed:$,failed:p}}let o=t.match(/(\d+)\s+failed/),i=t.match(/(\d+)\s+passed/);if(i||o){let u=o?parseInt(o[1]):0,p=i?parseInt(i[1]):0,$=t.match(/(\d+)\s+skipped/),g=$?parseInt($[1]):0;return{total:p+u+g,passed:p,failed:u}}let a=t.match(/Tests\s+(?:(\d+)\s+failed\s*\|?\s*)?(\d+)\s+passed\s*\((\d+)\)/);if(a){let u=parseInt(a[3]),p=a[1]?parseInt(a[1]):0,$=parseInt(a[2]);return{total:u,passed:$,failed:p}}let r=t.match(/(\d+)\s+passing/),c=t.match(/(\d+)\s+failing/);if(r){let u=parseInt(r[1]),p=c?parseInt(c[1]):0;return{total:u+p,passed:u,failed:p}}return null}function Ge(e,t=process.cwd(),s=[]){let n=e.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,""),o=s.map(d=>j.resolve(t,d)),i=new Map,a=new Set,r=/(?:Chrome|Firefox|Safari)(?:\s+Headless)?\s+[\d.]+(?:\s+[\d.]+)*\s+\([^)]+\)\s+(.+?)\s+FAILED/g,c;for(;(c=r.exec(n))!==null;){let l=c[1].trim().split(" ");for(let y=1;y<Math.min(l.length,6);y++){let h=l.slice(0,y).join(" ");if(a.has(h))continue;a.add(h);let C=Be(h,t,o);if(C){let v=j.resolve(C);i.has(v)||i.set(v,j.relative(t,C));break}}}let u=/^FAIL\s+([a-zA-Z0-9_./-\\]+\.(?:spec|test)\.(ts|tsx|js|jsx))/gm,p;for(;(p=u.exec(n))!==null;){let d=p[1],l=j.isAbsolute(d)?d:j.resolve(t,d);!o.includes(l)&&D.existsSync(l)&&!i.has(l)&&i.set(l,j.relative(t,l))}let $=/\[(?:chromium|firefox|webkit)\]\s+[>›]\s+([^\s:]+\.(?:spec|test)\.[tj]sx?):\d+:\d+\s+[>›]/g,g;for(;(g=$.exec(n))!==null;){let d=g[1],l=j.isAbsolute(d)?d:j.resolve(t,d);!o.includes(l)&&D.existsSync(l)&&!i.has(l)&&i.set(l,j.relative(t,l))}let L=/Running:\s+([^\s]+\.(?:cy|spec|test)\.[tj]sx?)/g,m;for(;(m=L.exec(n))!==null;){let d=m[1],l=j.isAbsolute(d)?d:j.resolve(t,d);if(!o.includes(l)&&D.existsSync(l)&&!i.has(l)){let y=d.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");new RegExp(y+"\\s+\\d+\\s+\\d+\\s+([1-9]\\d*)").test(n)&&i.set(l,j.relative(t,l))}}return[...i.values()]}function Ae(e,t=[],s=process.cwd()){let n=e.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,""),o=t.map(d=>j.resolve(d));{let d=/(?:Chrome|Firefox|Safari)(?:\s+Headless)?\s+[\d.]+(?:\s+[\d.]+)*\s+\([^)]+\)\s+(.+?)\s+FAILED/g,l=new Set,y;for(;(y=d.exec(n))!==null;){let C=y[1].trim().split(" ");for(let v=1;v<Math.min(C.length,5);v++){let x=C.slice(0,v).join(" ");if(l.has(x))continue;l.add(x);let f=Be(x,s,o);if(f)return j.relative(s,f)}}}let i=/([a-zA-Z]:[a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue)|[a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue))(?:\s*[:(])/g,a;for(;(a=i.exec(n))!==null;){let d=a[1];if(d){if(/(?:^|[/\\])(node_modules|dist|build)[/\\]/.test(d))continue;let l=j.isAbsolute(d)?d:j.resolve(s,d);if(!o.includes(l)&&D.existsSync(l))return d}}let r=/FAIL\s+([a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue))/gi,c;for(;(c=r.exec(n))!==null;){let d=c[1],l=j.isAbsolute(d)?d:j.resolve(s,d);if(!o.includes(l)&&D.existsSync(l))return d}{let d=/\[(?:chromium|firefox|webkit)\]\s+[>›]\s+([^\s:]+\.(?:spec|test)\.[tj]sx?):\d+:\d+\s+[>›]/g,l;for(;(l=d.exec(n))!==null;){let y=l[1],h=j.isAbsolute(y)?y:j.resolve(s,y);if(!o.includes(h)&&D.existsSync(h))return y}}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 d=p[1];if(d){if(/(?:^|[/\\])(node_modules|dist|build)[/\\]/.test(d))continue;let l=j.isAbsolute(d)?d:j.resolve(s,d);if(!o.includes(l)&&D.existsSync(l))return d}}let $=/\b([A-Z][a-zA-Z0-9]{3,})\b/g,g,L=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(;(g=$.exec(n))!==null;){let d=g[1];if(!d||m.includes(d)||L.has(d)||/^[A-Z][A-Z0-9]{5,11}$/.test(d)||/^[A-Za-z0-9]{16,}$/.test(d))continue;L.add(d);let l=nt(d,s,o);if(l)return j.relative(s,l)}return null}function Be(e,t,s){if(!D.existsSync(t))return null;let n=D.readdirSync(t);for(let o of n){let i=j.join(t,o);if(["node_modules","dist","build",".git",".angular"].includes(o))continue;let a;try{a=D.statSync(i)}catch{continue}if(a.isDirectory()){let r=Be(e,i,s);if(r)return r}else if(/\.(spec|test)\.(ts|tsx|js|jsx)$/.test(o)){if(s.includes(j.resolve(i)))continue;try{let r=D.readFileSync(i,"utf8");if(r.includes(`describe('${e}'`)||r.includes(`describe("${e}"`))return i}catch{continue}}}return null}function nt(e,t,s){if(!D.existsSync(t))return null;let n=D.readdirSync(t);for(let o of n){let i=j.join(t,o);if(o==="node_modules"||o==="dist"||o==="build"||o===".git"||o===".angular")continue;let a;try{a=D.statSync(i)}catch{continue}if(a.isDirectory()){let r=nt(e,i,s);if(r)return r}else if(o.match(/\.(ts|tsx|js|jsx|vue)$/)){let r=D.readFileSync(i,"utf8");if(r.includes(`class ${e}`)||r.includes(`function ${e}`)||r.includes(`const ${e}`)||r.includes(`let ${e}`)||r.includes(`exports.${e}`)||r.includes(`module.exports.${e}`)){let c=i,u=j.extname(i),p=i.slice(0,-u.length);if(!o.includes(".spec.")&&!o.includes(".test.")){let $=[`${p}.spec${u}`,`${p}.test${u}`,`${p}.spec.js`,`${p}.test.js`];for(let g of $)if(D.existsSync(g)){c=g;break}}if(!s.includes(j.resolve(c)))return c}}}return null}var ce=require("ai"),Te=require("@ai-sdk/openai"),ot=require("@ai-sdk/anthropic"),it=require("@ai-sdk/google"),at=b(require("dotenv"));at.config({quiet:!0});var qe=15e3;function Pe(e,t){return e.length<=qe?e:(console.warn(`[Lisa.ai LLM] ${t} is ${e.length} chars \u2014 truncating to ${qe} to stay within context window.`),e.slice(0,qe)+`
3
3
 
4
- // ... (truncated)`)}function Se(e,t){if(!/\b(describe|it\(|test\(|expect\(|beforeEach|afterEach|suite|assert\.|cy\.)\b/.test(e))throw new Error(`LLM returned a non-code response for ${t} (possible context overflow). Skipping to prevent file corruption.`)}function et(e){if(!e||e.length===0)return"";let t={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
- ${e.map(s=>` \u2022 ${t[s]??s}`).join(`
4
+ // ... (truncated)`)}function Ie(e,t){if(!/\b(describe|it\(|test\(|expect\(|beforeEach|afterEach|suite|assert\.|cy\.)\b/.test(e))throw new Error(`LLM returned a non-code response for ${t} (possible context overflow). Skipping to prevent file corruption.`)}function rt(e){if(!e||e.length===0)return"";let t={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
+ ${e.map(n=>` \u2022 ${t[n]??n}`).join(`
6
6
  `)}
7
- `}function Fe(e,t){if(e==="claude"){let o=t||process.env.ANTHROPIC_API_KEY;if(!o)throw new Error("No Anthropic API key provided by local ENV or Control Plane");let i=process.env.LISA_CLAUDE_MODEL||"claude-haiku-4-5";return(0,Je.createAnthropic)({apiKey:o})(i)}if(e==="openai"){let o=t||process.env.OPENAI_API_KEY;if(!o)throw new Error("No OpenAI API key provided by local ENV or Control Plane");let i=process.env.LISA_OPENAI_MODEL||"gpt-4o-mini";return(0,ke.createOpenAI)({apiKey:o})(i)}let n=t||process.env.GOOGLE_GENERATIVE_AI_API_KEY;if(!n)throw new Error("No Google API key provided by local ENV or Control Plane");let s=process.env.LISA_GOOGLE_MODEL||"gemini-2.0-flash";return(0,Qe.createGoogleGenerativeAI)({apiKey:n})(s)}async function tt(e,t,n,s,o,i,r,a){console.log(`[Lisa.ai Auto-Heal] Requesting fix from ${s} for ${e}...`);let d=Fe(s,o),p=`You are Lisa.ai, an autonomous CI/CD expert platform.
7
+ `}function pe(e,t){if(e==="claude"){let o=t||process.env.ANTHROPIC_API_KEY;if(!o)throw new Error("No Anthropic API key provided by local ENV or Control Plane");let i=process.env.LISA_CLAUDE_MODEL||"claude-haiku-4-5";return(0,ot.createAnthropic)({apiKey:o})(i)}if(e==="openai"){let o=t||process.env.OPENAI_API_KEY;if(!o)throw new Error("No OpenAI API key provided by local ENV or Control Plane");let i=process.env.LISA_OPENAI_MODEL||"gpt-4o-mini";return(0,Te.createOpenAI)({apiKey:o})(i)}let s=t||process.env.GOOGLE_GENERATIVE_AI_API_KEY;if(!s)throw new Error("No Google API key provided by local ENV or Control Plane");let n=process.env.LISA_GOOGLE_MODEL||"gemini-2.0-flash";return(0,it.createGoogleGenerativeAI)({apiKey:s})(n)}async function lt(e,t,s,n,o,i,a,r){console.log(`[Lisa.ai Auto-Heal] Requesting fix from ${n} for ${e}...`);let c=pe(n,o),u=`You are Lisa.ai, an autonomous CI/CD expert platform.
8
8
  A build/compilation error occurred. Your task is to fix the provided file so that the error resolves.
9
9
 
10
10
  --- Error Log ---
11
- `+n+`
11
+ `+s+`
12
12
 
13
13
  --- Target File Content (`+e+`) ---
14
14
  `+t+`
@@ -21,72 +21,72 @@ A build/compilation error occurred. Your task is to fix the provided file so tha
21
21
  5. FRAMEWORK TEST RULE: If fixing a testing file ('.spec' or '.test') and an Angular error happens like 'NullInjectorError: No provider for X', you MUST add X to the 'providers' array in 'TestBed.configureTestingModule' or import the required testing module. If a React testing library error arises, fix the render context.
22
22
  6. CRITICAL ANGULAR RULE: If an Angular component imports 'RouterLink' or uses '[routerLink]' in its template, you MUST provide 'ActivatedRoute' in the providers array or import 'RouterTestingModule'. If it uses HTTP, import 'HttpClientTestingModule'.
23
23
  7. CRITICAL STANDALONE RULE: If a component has 'standalone: true' in its decorator (check the Sibling Context!), you MUST NOT add it to the 'declarations' array. Instead, you MUST add it to the 'imports' array within 'TestBed.configureTestingModule'.
24
- 8. Return the code wrapped in a markdown code block (\`\`\`typescript ... \`\`\`). Do not include any explanation or intro text.`;if(r){if(/standalone\s*:\s*true/.test(r)){let x=r.match(/export class (\w+)/),m=x?x[1]:"the component";p+=`
24
+ 8. Return the code wrapped in a markdown code block (\`\`\`typescript ... \`\`\`). Do not include any explanation or intro text.`;if(a){if(/standalone\s*:\s*true/.test(a)){let L=a.match(/export class (\w+)/),m=L?L[1]:"the component";u+=`
25
25
 
26
26
  --- CRITICAL ARCHITECTURAL REQUIREMENT ---
27
27
  The component `+m+` is marked as STANDALONE (standalone: true).
28
28
  You MUST add `+m+` to the 'imports' array within 'TestBed.configureTestingModule'.
29
- DO NOT add it to the 'declarations' array. If you omit `+m+" from 'imports', the test will fail."}p+=`
29
+ DO NOT add it to the 'declarations' array. If you omit `+m+" from 'imports', the test will fail."}u+=`
30
30
 
31
31
  --- Sibling Component / Service Context ---
32
32
  You are fixing a '.spec' test file. Here is the actual implementation code for the component you are testing.
33
33
  Use this to identify EXACTLY which imports, Services, and Variables need to be mocked inside your 'TestBed'.
34
- `+r}a&&a.length>0&&(p+=`
34
+ `+a}r&&r.length>0&&(u+=`
35
35
 
36
36
  --- MEMORY ENGINE: PROVEN FIX PATTERNS ---
37
37
  The following fix patterns were SUCCESSFULLY used to resolve identical errors in past runs. Prioritise these approaches before trying anything novel:
38
- `+a.map((f,x)=>`
39
- [Pattern ${x+1}]
40
- ${f}`).join(`
41
- `)),i&&(console.log(`[Lisa.ai Auto-Heal] Warning! Agent is looping on ${e}. Injecting previous failed context...`),p+=`
38
+ `+r.map((g,L)=>`
39
+ [Pattern ${L+1}]
40
+ ${g}`).join(`
41
+ `)),i&&(console.log(`[Lisa.ai Auto-Heal] Warning! Agent is looping on ${e}. Injecting previous failed context...`),u+=`
42
42
 
43
43
  --- CRITICAL WARNING ---
44
44
  You previously attempted to fix this file but the compiler REJECTED your fix!
45
45
  Here is the previous analysis and failed fix you attempted:
46
46
  `+i+`
47
47
 
48
- DO NOT repeat the identical code changes. Try a completely different programming approach, fix syntax typos, or check for missing imports.`);let{text:u}=await(0,te.generateText)({model:d,prompt:p}),L=u.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);return L?L[1].trim():u.trim()}async function se(e,t,n,s,o,i){console.log(`[Lisa.ai Coverage] Requesting test generation from ${n} for ${e}...`);let r=Fe(n,s),a=o?`3. You MUST use the '${o}' testing framework exclusively. All imports, describe/it/test blocks, and mock utilities must follow '${o}' conventions.
48
+ DO NOT repeat the identical code changes. Try a completely different programming approach, fix syntax typos, or check for missing imports.`);let{text:p}=await(0,ce.generateText)({model:c,prompt:u}),$=p.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);return $?$[1].trim():p.trim()}async function X(e,t,s,n,o,i){console.log(`[Lisa.ai Coverage] Requesting test generation from ${s} for ${e}...`);let a=pe(s,n),r=o?`3. You MUST use the '${o}' testing framework exclusively. All imports, describe/it/test blocks, and mock utilities must follow '${o}' conventions.
49
49
  `:`3. Include all necessary imports assuming a standard testing framework (Jest/Karma/Vitest) is available.
50
- `,d=et(i),p=`You are Lisa.ai, an autonomous CI/CD expert platform.
50
+ `,c=rt(i),u=`You are Lisa.ai, an autonomous CI/CD expert platform.
51
51
  A source file lacks 100% test coverage. Your task is to generate a comprehensive testing suite covering all branches, lines, and functions.
52
52
 
53
53
  --- Target File Content (`+e+`) ---
54
- `+be(t,e)+"\n\n--- Constraints ---\n1. Return the generated test code wrapped in a markdown code block (```typescript ... ```).\n2. Do not include any explanation or intro text.\n"+a+`4. Aim for 100% logic coverage.
55
- `+d,{text:u}=await(0,te.generateText)({model:r,prompt:p}),L=u.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/),f=L?L[1].trim():u.trim();return Se(f,e),f}async function st(e,t,n,s,o,i,r,a){console.log(`[Lisa.ai Coverage] Requesting test update from ${o} for ${e}...`);let d=Fe(o,i),p=r?`3. You MUST use the '${r}' testing framework exclusively. All new tests must follow '${r}' conventions and integrate cleanly with the existing suite.
54
+ `+Pe(t,e)+"\n\n--- Constraints ---\n1. Return the generated test code wrapped in a markdown code block (```typescript ... ```).\n2. Do not include any explanation or intro text.\n"+r+`4. Aim for 100% logic coverage.
55
+ `+c,{text:p}=await(0,ce.generateText)({model:a,prompt:u}),$=p.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/),g=$?$[1].trim():p.trim();return Ie(g,e),g}async function ct(e,t,s,n,o,i,a,r){console.log(`[Lisa.ai Coverage] Requesting test update from ${o} for ${e}...`);let c=pe(o,i),u=a?`3. You MUST use the '${a}' testing framework exclusively. All new tests must follow '${a}' conventions and integrate cleanly with the existing suite.
56
56
  `:`3. Append missing tests to the existing suite. Do not delete existing passing tests unless they are fundamentally broken.
57
- `,u=et(a),L=`You are Lisa.ai, an autonomous CI/CD expert platform.
57
+ `,p=rt(r),$=`You are Lisa.ai, an autonomous CI/CD expert platform.
58
58
  A source file lacks 100% test coverage. You must update its existing test suite to achieve full coverage.
59
59
 
60
60
  --- Target File Content (`+e+`) ---
61
- `+be(t,e)+`
61
+ `+Pe(t,e)+`
62
62
 
63
- --- Existing Test Suite (`+n+`) ---
64
- `+s+"\n\n--- Constraints ---\n1. Return the updated complete test code wrapped in a markdown code block (```typescript ... ```).\n2. Do not include any explanation or intro text.\n"+p+`4. Aim for 100% logic coverage across branches, lines, and functions.
65
- `+u,{text:f}=await(0,te.generateText)({model:d,prompt:L}),x=f.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/),m=x?x[1].trim():f.trim();return Se(m,e),m}async function nt(e,t,n,s,o){try{let i=new AbortController,r=setTimeout(()=>i.abort(),6e4),a=(0,ke.createOpenAI)({baseURL:o.baseUrl+"/v1",apiKey:"ollama"}),d=`You are Lisa.ai, an autonomous CI/CD expert.
63
+ --- Existing Test Suite (`+s+`) ---
64
+ `+n+"\n\n--- Constraints ---\n1. Return the updated complete test code wrapped in a markdown code block (```typescript ... ```).\n2. Do not include any explanation or intro text.\n"+u+`4. Aim for 100% logic coverage across branches, lines, and functions.
65
+ `+p,{text:g}=await(0,ce.generateText)({model:c,prompt:$}),L=g.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/),m=L?L[1].trim():g.trim();return Ie(m,e),m}async function dt(e,t,s,n,o){try{let i=new AbortController,a=setTimeout(()=>i.abort(),6e4),r=(0,Te.createOpenAI)({baseURL:o.baseUrl+"/v1",apiKey:"ollama"}),c=`You are Lisa.ai, an autonomous CI/CD expert.
66
66
  Apply the PROVEN FIX PATTERN below to resolve the error. Do not deviate from the pattern unless absolutely necessary.
67
67
 
68
68
  --- Error Log ---
69
- `+n.slice(0,4e3)+`
69
+ `+s.slice(0,4e3)+`
70
70
 
71
71
  --- Target File (`+e+`) ---
72
- `+be(t,e)+`
72
+ `+Pe(t,e)+`
73
73
 
74
74
  --- PROVEN FIX PATTERNS ---
75
- `+s.map((f,x)=>`[Pattern ${x+1}]
76
- ${f}`).join(`
77
- `)+"\n\nReturn ONLY the complete fixed file wrapped in ```typescript ... ```. No explanation.",{text:p}=await(0,te.generateText)({model:a(o.model),prompt:d,abortSignal:i.signal});clearTimeout(r);let u=p.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/),L=u?u[1].trim():p.trim();return Se(L,e),L}catch(i){return console.log(` [Lisa LLM] Failed: ${i.message?.slice(0,120)??String(i)}`),null}}async function ot(e,t,n,s,o){try{let i=new AbortController,r=setTimeout(()=>i.abort(),6e4),a=(0,ke.createOpenAI)({baseURL:s.baseUrl+"/v1",apiKey:"ollama"}),d=o?`Use the '${o}' testing framework exclusively.
78
- `:"",p=`You are Lisa.ai, an autonomous CI/CD expert.
75
+ `+n.map((g,L)=>`[Pattern ${L+1}]
76
+ ${g}`).join(`
77
+ `)+"\n\nReturn ONLY the complete fixed file wrapped in ```typescript ... ```. No explanation.",{text:u}=await(0,ce.generateText)({model:r(o.model),prompt:c,abortSignal:i.signal});clearTimeout(a);let p=u.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/),$=p?p[1].trim():u.trim();return Ie($,e),$}catch(i){return console.log(` [Lisa LLM] Failed: ${i.message?.slice(0,120)??String(i)}`),null}}async function ut(e,t,s,n,o){try{let i=new AbortController,a=setTimeout(()=>i.abort(),6e4),r=(0,Te.createOpenAI)({baseURL:n.baseUrl+"/v1",apiKey:"ollama"}),c=o?`Use the '${o}' testing framework exclusively.
78
+ `:"",u=`You are Lisa.ai, an autonomous CI/CD expert.
79
79
  Generate a comprehensive test suite for the file below. Use the PROVEN PATTERNS as a guide for test structure and style.
80
80
 
81
81
  --- Source File (`+e+`) ---
82
- `+be(t,e)+`
82
+ `+Pe(t,e)+`
83
83
 
84
84
  --- PROVEN PATTERNS ---
85
- `+n.map((x,m)=>`[Pattern ${m+1}]
86
- ${x}`).join(`
87
- `)+"\n\n--- Constraints ---\n1. Return ONLY the test code in ```typescript ... ```.\n2. Aim for 100% logic coverage.\n"+d,{text:u}=await(0,te.generateText)({model:a(s.model),prompt:p,abortSignal:i.signal});clearTimeout(r);let L=u.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/),f=L?L[1].trim():u.trim();return Se(f,e),f}catch(i){return console.log(` [Lisa LLM] Generation failed: ${i.message?.slice(0,120)??String(i)}`),null}}async function ae(e,t,n){let s=Fe(t,n),{text:o}=await(0,te.generateText)({model:s,prompt:e}),i=o.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);return i?i[1].trim():o.trim()}var it=j(require("fs")),at=j(require("simple-git")),rt=require("@octokit/rest"),is=require("dotenv/config");function Ot(e){let t=e.healedFiles.map(o=>`- \`${o}\``).join(`
88
- `),n=e.quarantinedFiles.length>0?e.quarantinedFiles.map(o=>`- \`${o}\``).join(`
89
- `):"_None \u2014 all failures healed successfully._",s=e.testCounts?["| Metric | Before | After (est.) |","|---|---|---|",`| Passing | ${e.testCounts.passed} | ~${e.testCounts.passed+e.healedFiles.length} |`,`| Failing | ${e.testCounts.failed} | ~${Math.max(0,e.testCounts.failed-e.healedFiles.length)} |`,`| Total | ${e.testCounts.total} | ${e.testCounts.total} |`].join(`
85
+ `+s.map((L,m)=>`[Pattern ${m+1}]
86
+ ${L}`).join(`
87
+ `)+"\n\n--- Constraints ---\n1. Return ONLY the test code in ```typescript ... ```.\n2. Aim for 100% logic coverage.\n"+c,{text:p}=await(0,ce.generateText)({model:r(n.model),prompt:u,abortSignal:i.signal});clearTimeout(a);let $=p.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/),g=$?$[1].trim():p.trim();return Ie(g,e),g}catch(i){return console.log(` [Lisa LLM] Generation failed: ${i.message?.slice(0,120)??String(i)}`),null}}async function ge(e,t,s){let n=pe(t,s),{text:o}=await(0,ce.generateText)({model:n,prompt:e}),i=o.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);return i?i[1].trim():o.trim()}var pt=b(require("fs")),gt=b(require("simple-git")),ft=require("@octokit/rest"),Es=require("dotenv/config");function ts(e){let t=e.healedFiles.map(o=>`- \`${o}\``).join(`
88
+ `),s=e.quarantinedFiles.length>0?e.quarantinedFiles.map(o=>`- \`${o}\``).join(`
89
+ `):"_None \u2014 all failures healed successfully._",n=e.testCounts?["| Metric | Before | After (est.) |","|---|---|---|",`| Passing | ${e.testCounts.passed} | ~${e.testCounts.passed+e.healedFiles.length} |`,`| Failing | ${e.testCounts.failed} | ~${Math.max(0,e.testCounts.failed-e.healedFiles.length)} |`,`| Total | ${e.testCounts.total} | ${e.testCounts.total} |`].join(`
90
90
  `):"_Test counts not available._";return`## Lisa.ai Auto-Heal Report
91
91
 
92
92
  ### Summary
@@ -96,20 +96,20 @@ Lisa.ai automatically healed **${e.healedFiles.length}** failing test spec(s) us
96
96
  ${t}
97
97
 
98
98
  ### Quarantined Files (could not auto-heal)
99
- ${n}
99
+ ${s}
100
100
 
101
101
  ### Test Suite Impact
102
- ${s}
102
+ ${n}
103
103
 
104
104
  ---
105
105
 
106
106
  > This PR was generated automatically by [Lisa.ai](https://www.npmjs.com/package/@lisa.ai/agent).
107
- > Please review the changes before merging.`}async function lt(e){if(e.healedFiles.length===0)return console.log("[Lisa.ai CI] No files were healed. Skipping PR creation."),null;let t=process.env.GITHUB_TOKEN,n=process.env.GITHUB_REPOSITORY;if(!t||!n)return console.log("[Lisa.ai CI] GITHUB_TOKEN or GITHUB_REPOSITORY not set. Skipping PR creation."),null;let s=(0,at.default)(),i=`lisa-fix/auto-heal-${Date.now()}`,r=process.env.GITHUB_REF_NAME||"main";try{await s.addConfig("user.name","Lisa.ai"),await s.addConfig("user.email","lisa@lisa.ai"),await s.checkoutLocalBranch(i),await s.add(e.healedFiles);let a=e.healedFiles.length===1?`fix: Lisa.ai auto-healed ${e.healedFiles[0]}`:`fix: Lisa.ai auto-healed ${e.healedFiles.length} failing test specs`;await s.commit(a),console.log(`[Lisa.ai CI] Committed ${e.healedFiles.length} file(s) to branch ${i}`),await s.push("origin",i,{"--set-upstream":null}),console.log("[Lisa.ai CI] Pushed branch to origin.");let[d,p]=n.split("/"),L=await new rt.Octokit({auth:t}).rest.pulls.create({owner:d,repo:p,title:a,body:Ot(e),head:i,base:r});return console.log(`[Lisa.ai CI] Pull Request created: ${L.data.html_url}`),L.data.html_url}catch(a){return console.error(`[Lisa.ai CI] Failed to create PR: ${a.message}`),null}}function Oe(e,t){let n=process.env.GITHUB_STEP_SUMMARY;if(!n)return;let s=["## Lisa.ai Heal Results","","| Metric | Value |","|---|---|",`| Files Healed | ${e.healedFiles.length} |`,`| Files Quarantined | ${e.quarantinedFiles.length} |`,`| Model | ${e.modelProvider} |`,`| Framework | ${e.framework} |`,`| Scope | ${e.scope} |`];e.testCounts&&s.push(`| Tests Passing (before) | ${e.testCounts.passed} |`,`| Tests Failing (before) | ${e.testCounts.failed} |`),t&&s.push("",`**Pull Request:** ${t}`),e.healedFiles.length>0&&(s.push("","### Healed Files"),e.healedFiles.forEach(o=>s.push(`- \`${o}\``))),e.quarantinedFiles.length>0&&(s.push("","### Quarantined Files"),e.quarantinedFiles.forEach(o=>s.push(`- \`${o}\``)));try{it.appendFileSync(n,s.join(`
107
+ > Please review the changes before merging.`}async function mt(e){if(e.healedFiles.length===0)return console.log("[Lisa.ai CI] No files were healed. Skipping PR creation."),null;let t=process.env.GITHUB_TOKEN,s=process.env.GITHUB_REPOSITORY;if(!t||!s)return console.log("[Lisa.ai CI] GITHUB_TOKEN or GITHUB_REPOSITORY not set. Skipping PR creation."),null;let n=(0,gt.default)(),i=`lisa-fix/auto-heal-${Date.now()}`,a=process.env.GITHUB_REF_NAME||"main";try{await n.addConfig("user.name","Lisa.ai"),await n.addConfig("user.email","lisa@lisa.ai"),await n.checkoutLocalBranch(i),await n.add(e.healedFiles);let r=e.healedFiles.length===1?`fix: Lisa.ai auto-healed ${e.healedFiles[0]}`:`fix: Lisa.ai auto-healed ${e.healedFiles.length} failing test specs`;await n.commit(r),console.log(`[Lisa.ai CI] Committed ${e.healedFiles.length} file(s) to branch ${i}`),await n.push("origin",i,{"--set-upstream":null}),console.log("[Lisa.ai CI] Pushed branch to origin.");let[c,u]=s.split("/"),$=await new ft.Octokit({auth:t}).rest.pulls.create({owner:c,repo:u,title:r,body:ts(e),head:i,base:a});return console.log(`[Lisa.ai CI] Pull Request created: ${$.data.html_url}`),$.data.html_url}catch(r){return console.error(`[Lisa.ai CI] Failed to create PR: ${r.message}`),null}}function Ye(e,t){let s=process.env.GITHUB_STEP_SUMMARY;if(!s)return;let n=["## Lisa.ai Heal Results","","| Metric | Value |","|---|---|",`| Files Healed | ${e.healedFiles.length} |`,`| Files Quarantined | ${e.quarantinedFiles.length} |`,`| Model | ${e.modelProvider} |`,`| Framework | ${e.framework} |`,`| Scope | ${e.scope} |`];e.testCounts&&n.push(`| Tests Passing (before) | ${e.testCounts.passed} |`,`| Tests Failing (before) | ${e.testCounts.failed} |`),t&&n.push("",`**Pull Request:** ${t}`),e.healedFiles.length>0&&(n.push("","### Healed Files"),e.healedFiles.forEach(o=>n.push(`- \`${o}\``))),e.quarantinedFiles.length>0&&(n.push("","### Quarantined Files"),e.quarantinedFiles.forEach(o=>n.push(`- \`${o}\``)));try{pt.appendFileSync(s,n.join(`
108
108
  `)+`
109
- `)}catch{}}var ct=j(require("dotenv"));ct.config({quiet:!0});async function R(e){let n=`${process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3088"}/api/v1/lisa/telemetry`;fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}).catch(s=>{console.debug(`[Lisa.ai Agent Debug] Failed to report telemetry: ${s.message}`)})}var V=j(require("fs")),H=j(require("path"));var Ee=j(require("fs")),dt=j(require("path")),ne=".lisai.json",ge={enabled:!1,provider:"ollama",model:"qwen2.5-coder:7b",baseUrl:"http://localhost:11434",confidenceThreshold:.8};function He(e){let t=process.env;return{enabled:t.LISA_LLM_ENABLED==="true"||e?.enabled||ge.enabled,provider:e?.provider??ge.provider,model:t.LISA_LLM_MODEL||(e?.model??ge.model),baseUrl:t.LISA_LLM_URL||(e?.baseUrl??ge.baseUrl),confidenceThreshold:e?.confidenceThreshold??ge.confidenceThreshold}}var I=class{static _config=null;static _loaded=!1;static load(t=process.cwd()){if(this._loaded)return this._config;this._loaded=!0;let n=dt.resolve(t,ne);if(!Ee.existsSync(n))return null;try{let s=Ee.readFileSync(n,"utf-8");return this._config=JSON.parse(s),console.log(`[Lisa.ai Config] Loaded project config from ${ne}`),this._config}catch(s){return console.warn(`[Lisa.ai Config] Warning: ${ne} found but failed to parse \u2014 ${s.message}. Using defaults.`),null}}static get(){return this._config}static applyEnvDefaults(t){if(!t.model)return;let n={claude:"LISA_CLAUDE_MODEL",openai:"LISA_OPENAI_MODEL",gemini:"LISA_GOOGLE_MODEL"},s=t.provider;s&&n[s]&&!process.env[n[s]]&&(process.env[n[s]]=t.model,console.log(`[Lisa.ai Config] Model override applied: ${s} \u2192 ${t.model}`))}static printSummary(t){let n=[];t.projectId&&n.push(`projectId=${t.projectId}`),t.provider&&n.push(`provider=${t.provider}`),t.model&&n.push(`model=${t.model}`),t.testingFramework&&n.push(`testingFramework=${t.testingFramework}`),t.testTypes?.length&&n.push(`testTypes=[${t.testTypes.join(",")}]`),t.maxRetries!==void 0&&n.push(`maxRetries=${t.maxRetries}`),t.testCommand&&n.push(`testCommand="${t.testCommand}"`),t.e2eFramework&&n.push(`e2eFramework=${t.e2eFramework}`),t.e2eCommand&&n.push(`e2eCommand="${t.e2eCommand}"`),n.length&&console.log(`[Lisa.ai Config] Settings: ${n.join(", ")}`),t.skipFiles?.length&&console.log(`[Lisa.ai Config] Extra skip files : ${t.skipFiles.join(", ")}`),t.skipDirs?.length&&console.log(`[Lisa.ai Config] Extra skip dirs : ${t.skipDirs.join(", ")}`),t.skipPaths?.length&&console.log(`[Lisa.ai Config] Extra skip paths : ${t.skipPaths.join(", ")}`);let s=He(t.lisaLlm);s.enabled&&console.log(`[Lisa.ai Config] Lisa LLM enabled: ${s.model} @ ${s.baseUrl} (threshold=${s.confidenceThreshold})`)}};var K=class{static scanRepository(t=process.cwd()){let n=H.resolve(t,"package.json"),s={type:"unknown",testingFramework:"none"};if(!V.existsSync(n))return console.warn(`[Lisa.ai AutoDiscovery] No package.json found at ${n}. Defaulting to Generic Node.`),s.type="node",s;let o=JSON.parse(V.readFileSync(n,"utf8")),i={...o.dependencies||{},...o.devDependencies||{}},r=o.scripts||{};return i["@angular/core"]?s.type="angular":i.react?s.type="react":i.vue?s.type="vue":s.type="node",i.jest||r.test?.includes("jest")?s.testingFramework="jest":i.karma||r.test?.includes("karma")||r.test?.includes("ng test")?s.testingFramework="karma":(i.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(t,n=[]){let s=[];if(!V.existsSync(t))return s;let o=V.readdirSync(t),i=I.get(),r=i?.skipDirs??[],a=i?.skipFiles??[],d=i?.skipPaths??[],p=["node_modules","dist","build",".git",".angular","coverage","public","assets",...r],u=["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"],L=[/^\./,/\.config\.(js|ts|mjs|cjs)$/,/\.conf\.(js|ts|mjs|cjs)$/];for(let f of o){let x=H.join(t,f);if(p.includes(f))continue;let m;try{m=V.statSync(x)}catch{continue}if(m.isDirectory())s.push(...this.findUntestedFiles(x,n));else if(f.match(/\.(ts|tsx|js|jsx|vue)$/)&&!f.includes(".spec.")&&!f.includes(".test.")){if(u.includes(f)||a.includes(f)||L.some(C=>C.test(f)))continue;let c=H.extname(f),l=f.slice(0,-c.length);if(![H.join(t,`${l}.spec${c}`),H.join(t,`${l}.test${c}`),H.join(t,`${l}.spec.js`),H.join(t,`${l}.test.js`),H.join(t,`${l}.spec.ts`),H.join(t,`${l}.test.ts`)].some(C=>V.existsSync(C))){let C=H.relative(process.cwd(),x);if(d.some(v=>C.startsWith(v)))continue;n.includes(C)||s.push(C)}}}return s}};var ut=require("child_process"),re=class{static async installMissingFramework(t,n=process.cwd()){if(t.testingFramework!=="none")return t;console.log(`
110
- [Lisa.ai Auto-Installer] \u{1F6A8} No testing framework detected for ${t.type} architecture.`),console.log("[Lisa.ai Auto-Installer] \u{1FA84} Initiating Zero-Config Installation Protocol...");let s="",o="none";switch(t.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",o="karma",t.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",o="jest",t.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",o="vitest",t.suggestedTestCommand="npx vitest run --coverage";break}if(s){console.log(`[Lisa.ai Executing] ${s}`);let i=s.split(" "),r=process.platform==="win32"?`${i[0]}.cmd`:i[0];return(0,ut.spawnSync)(r,i.slice(1),{cwd:n,stdio:"inherit"}).status!==0&&(console.error(`
111
- \u274C [Lisa.ai Auto-Installer] Failed to construct dynamic testing environment.`),process.exit(1)),console.log("\u2705 [Lisa.ai Auto-Installer] Framework successfully injected."),t.testingFramework=o,t}return t}};var je=j(require("fs")),Ge=j(require("path"));var le=class{static async provisionConfigurationFiles(t,n,s,o=process.cwd()){if(t.testingFramework==="none")return;if(console.log(`
112
- [Lisa.ai Auto-Generator] \u{1FA84} Analyzing ${t.type} architecture to generate ${t.testingFramework} configuration specs...`),["jest.config.js","jest.config.ts","karma.conf.js","vitest.config.ts","vitest.config.js"].some(d=>je.existsSync(Ge.join(o,d)))){console.log("[Lisa.ai Auto-Generator] Setup file detected. Bypassing initialization.");return}let a=`You are an expert ${t.type} architect.
109
+ `)}catch{}}var ht=b(require("dotenv"));ht.config({quiet:!0});async function N(e){let s=`${process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3088"}/api/v1/lisa/telemetry`;fetch(s,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}).catch(n=>{console.debug(`[Lisa.ai Agent Debug] Failed to report telemetry: ${n.message}`)})}var J=b(require("fs")),q=b(require("path"));var Re=b(require("fs")),yt=b(require("path")),de=".lisai.json",$e={enabled:!1,provider:"ollama",model:"qwen2.5-coder:7b",baseUrl:"http://localhost:11434",confidenceThreshold:.8};function Ke(e){let t=process.env;return{enabled:t.LISA_LLM_ENABLED==="true"||e?.enabled||$e.enabled,provider:e?.provider??$e.provider,model:t.LISA_LLM_MODEL||(e?.model??$e.model),baseUrl:t.LISA_LLM_URL||(e?.baseUrl??$e.baseUrl),confidenceThreshold:e?.confidenceThreshold??$e.confidenceThreshold}}var M=class{static _config=null;static _loaded=!1;static load(t=process.cwd()){if(this._loaded)return this._config;this._loaded=!0;let s=yt.resolve(t,de);if(!Re.existsSync(s))return null;try{let n=Re.readFileSync(s,"utf-8");return this._config=JSON.parse(n),console.log(`[Lisa.ai Config] Loaded project config from ${de}`),this._config}catch(n){return console.warn(`[Lisa.ai Config] Warning: ${de} found but failed to parse \u2014 ${n.message}. Using defaults.`),null}}static get(){return this._config}static applyEnvDefaults(t){if(!t.model)return;let s={claude:"LISA_CLAUDE_MODEL",openai:"LISA_OPENAI_MODEL",gemini:"LISA_GOOGLE_MODEL"},n=t.provider;n&&s[n]&&!process.env[s[n]]&&(process.env[s[n]]=t.model,console.log(`[Lisa.ai Config] Model override applied: ${n} \u2192 ${t.model}`))}static printSummary(t){let s=[];t.projectId&&s.push(`projectId=${t.projectId}`),t.provider&&s.push(`provider=${t.provider}`),t.model&&s.push(`model=${t.model}`),t.testingFramework&&s.push(`testingFramework=${t.testingFramework}`),t.testTypes?.length&&s.push(`testTypes=[${t.testTypes.join(",")}]`),t.maxRetries!==void 0&&s.push(`maxRetries=${t.maxRetries}`),t.testCommand&&s.push(`testCommand="${t.testCommand}"`),t.e2eFramework&&s.push(`e2eFramework=${t.e2eFramework}`),t.e2eCommand&&s.push(`e2eCommand="${t.e2eCommand}"`),s.length&&console.log(`[Lisa.ai Config] Settings: ${s.join(", ")}`),t.skipFiles?.length&&console.log(`[Lisa.ai Config] Extra skip files : ${t.skipFiles.join(", ")}`),t.skipDirs?.length&&console.log(`[Lisa.ai Config] Extra skip dirs : ${t.skipDirs.join(", ")}`),t.skipPaths?.length&&console.log(`[Lisa.ai Config] Extra skip paths : ${t.skipPaths.join(", ")}`);let n=Ke(t.lisaLlm);n.enabled&&console.log(`[Lisa.ai Config] Lisa LLM enabled: ${n.model} @ ${n.baseUrl} (threshold=${n.confidenceThreshold})`)}};var K=class{static scanRepository(t=process.cwd()){let s=q.resolve(t,"package.json"),n={type:"unknown",testingFramework:"none"};if(!J.existsSync(s))return console.warn(`[Lisa.ai AutoDiscovery] No package.json found at ${s}. Defaulting to Generic Node.`),n.type="node",n;let o=JSON.parse(J.readFileSync(s,"utf8")),i={...o.dependencies||{},...o.devDependencies||{}},a=o.scripts||{};return i["@angular/core"]?n.type="angular":i.react?n.type="react":i.vue?n.type="vue":n.type="node",i.jest||a.test?.includes("jest")?n.testingFramework="jest":i.karma||a.test?.includes("karma")||a.test?.includes("ng test")?n.testingFramework="karma":(i.vitest||a.test?.includes("vitest"))&&(n.testingFramework="vitest"),n.testingFramework!=="none"&&(n.testingFramework==="karma"?n.type==="angular"?n.suggestedTestCommand="npx ng test --no-watch --browsers=ChromeHeadless":n.suggestedTestCommand="npx karma start --single-run --browsers=ChromeHeadless":a.test?n.suggestedTestCommand="npm run test":n.testingFramework==="jest"?n.suggestedTestCommand="npx jest --coverage":n.testingFramework==="vitest"&&(n.suggestedTestCommand="npx vitest run --coverage")),n}static findUntestedFiles(t,s=[]){let n=[];if(!J.existsSync(t))return n;let o=J.readdirSync(t),i=M.get(),a=i?.skipDirs??[],r=i?.skipFiles??[],c=i?.skipPaths??[],u=["node_modules","dist","build",".git",".angular","coverage","public","assets",...a],p=["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"],$=[/^\./,/\.config\.(js|ts|mjs|cjs)$/,/\.conf\.(js|ts|mjs|cjs)$/];for(let g of o){let L=q.join(t,g);if(u.includes(g))continue;let m;try{m=J.statSync(L)}catch{continue}if(m.isDirectory())n.push(...this.findUntestedFiles(L,s));else if(g.match(/\.(ts|tsx|js|jsx|vue)$/)&&!g.includes(".spec.")&&!g.includes(".test.")){if(p.includes(g)||r.includes(g)||$.some(C=>C.test(g)))continue;let d=q.extname(g),l=g.slice(0,-d.length);if(![q.join(t,`${l}.spec${d}`),q.join(t,`${l}.test${d}`),q.join(t,`${l}.spec.js`),q.join(t,`${l}.test.js`),q.join(t,`${l}.spec.ts`),q.join(t,`${l}.test.ts`)].some(C=>J.existsSync(C))){let C=q.relative(process.cwd(),L);if(c.some(v=>C.startsWith(v)))continue;s.includes(C)||n.push(C)}}}return n}};var wt=require("child_process"),fe=class{static async installMissingFramework(t,s=process.cwd()){if(t.testingFramework!=="none")return t;console.log(`
110
+ [Lisa.ai Auto-Installer] \u{1F6A8} No testing framework detected for ${t.type} architecture.`),console.log("[Lisa.ai Auto-Installer] \u{1FA84} Initiating Zero-Config Installation Protocol...");let n="",o="none";switch(t.type){case"angular":console.log("[Lisa.ai Auto-Installer] Provisioning Angular TestBed environment..."),n="npm install --save-dev karma karma-chrome-launcher karma-coverage karma-jasmine jasmine-core @types/jasmine",o="karma",t.suggestedTestCommand="npm run test";break;case"react":case"node":default:console.log("[Lisa.ai Auto-Installer] Provisioning Universal Jest environment..."),n="npm install --save-dev jest @types/jest ts-jest",o="jest",t.suggestedTestCommand="npx jest --coverage";break;case"vue":console.log("[Lisa.ai Auto-Installer] Provisioning Vue Vitest environment..."),n="npm install --save-dev vitest @vue/test-utils jsdom",o="vitest",t.suggestedTestCommand="npx vitest run --coverage";break}if(n){console.log(`[Lisa.ai Executing] ${n}`);let i=n.split(" "),a=process.platform==="win32"?`${i[0]}.cmd`:i[0];return(0,wt.spawnSync)(a,i.slice(1),{cwd:s,stdio:"inherit"}).status!==0&&(console.error(`
111
+ \u274C [Lisa.ai Auto-Installer] Failed to construct dynamic testing environment.`),process.exit(1)),console.log("\u2705 [Lisa.ai Auto-Installer] Framework successfully injected."),t.testingFramework=o,t}return t}};var Me=b(require("fs")),Ve=b(require("path"));var me=class{static async provisionConfigurationFiles(t,s,n,o=process.cwd()){if(t.testingFramework==="none")return;if(console.log(`
112
+ [Lisa.ai Auto-Generator] \u{1FA84} Analyzing ${t.type} architecture to generate ${t.testingFramework} configuration specs...`),["jest.config.js","jest.config.ts","karma.conf.js","vitest.config.ts","vitest.config.js"].some(c=>Me.existsSync(Ve.join(o,c)))){console.log("[Lisa.ai Auto-Generator] Setup file detected. Bypassing initialization.");return}let r=`You are an expert ${t.type} architect.
113
113
  Write a production-ready '${t.testingFramework}' configuration file for a standard '${t.type}' application.
114
114
  The application resides in the root directory.
115
115
 
@@ -117,8 +117,8 @@ Requirements:
117
117
  1. Ensure it specifically instruments code coverage.
118
118
  2. Ensure standard transpilation (ts-jest for jest, or standard karma-webpack/karma-typescript implementations).
119
119
  3. Do NOT wrap your response in markdown formatting (no \`\`\`javascript).
120
- 4. Return ONLY the raw code string block.`;try{let d=await ae(a,n,s),p="";t.testingFramework==="jest"&&(p="jest.config.js"),t.testingFramework==="karma"&&(p="karma.conf.js"),t.testingFramework==="vitest"&&(p="vitest.config.ts");let u=Ge.join(o,p);je.writeFileSync(u,d,"utf-8"),console.log(`\u2705 [Lisa.ai Auto-Generator] Natively wrote ${p} to repository root.`)}catch(d){console.error(`
121
- \u274C [Lisa.ai Auto-Generator] Failed to author configuration file: ${d.message}`),process.exit(1)}}};var pt=j(require("dotenv"));pt.config({quiet:!0});function ce(e){let t=e.toLowerCase();return t.includes("karma")||t.includes("ng test")?"karma":t.includes("jest")?"jest":t.includes("vitest")?"vitest":t.includes("playwright")?"playwright":t.includes("cypress")?"cypress":t.includes("mocha")?"mocha":"unknown"}async function Ae(e,t,n){if(n==="local")return[];let s=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3088";try{let o=new URLSearchParams({projectId:n,errorLog:e.slice(0,1e3),framework:t}),i=await fetch(`${s}/api/v1/lisa/memory/lookup?${o}`);if(!i.ok)return[];let r=await i.json();return r.data??r}catch{return[]}}async function Be(e,t,n,s){if(s==="local")return;let o=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3088";try{await fetch(`${o}/api/v1/lisa/memory/record`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectId:s,errorLog:e.slice(0,1e3),fixHint:t.slice(0,2e3),framework:n})}),console.log("[Lisa.ai Memory] Fix pattern recorded to Control Plane.")}catch(i){console.debug(`[Lisa.ai Agent Debug] Failed to record memory pattern: ${i.message}`)}}var D=j(require("fs")),G=j(require("path")),qe=require("child_process");function Te(e){if(D.existsSync(G.join(e,"playwright.config.ts"))||D.existsSync(G.join(e,"playwright.config.js")))return{framework:"playwright",configFile:D.existsSync(G.join(e,"playwright.config.ts"))?"playwright.config.ts":"playwright.config.js",command:"npx playwright test",installed:!0};if(D.existsSync(G.join(e,"cypress.config.ts"))||D.existsSync(G.join(e,"cypress.config.js"))||D.existsSync(G.join(e,"cypress.json")))return{framework:"cypress",configFile:D.existsSync(G.join(e,"cypress.config.ts"))?"cypress.config.ts":D.existsSync(G.join(e,"cypress.config.js"))?"cypress.config.js":"cypress.json",command:"npx cypress run",installed:!0};let t=G.join(e,"package.json");if(D.existsSync(t)){let n=JSON.parse(D.readFileSync(t,"utf-8")),s={...n.dependencies||{},...n.devDependencies||{}};if(s["@playwright/test"])return{framework:"playwright",configFile:null,command:"npx playwright test",installed:!0};if(s.cypress)return{framework:"cypress",configFile:null,command:"npx cypress run",installed:!0}}return{framework:"playwright",configFile:null,command:"npx playwright test",installed:!1}}function Pe(e){console.log("[Lisa.ai E2E] No E2E framework detected. Installing Playwright...");try{(0,qe.execSync)("npm install -D @playwright/test",{cwd:e,stdio:"pipe"}),console.log("[Lisa.ai E2E] Playwright installed.")}catch(n){throw console.error(`[Lisa.ai E2E] Failed to install Playwright: ${n.message}`),new Error("Could not install @playwright/test. Please install it manually.")}try{console.log("[Lisa.ai E2E] Installing Playwright browsers..."),(0,qe.execSync)("npx playwright install --with-deps chromium",{cwd:e,stdio:"pipe"}),console.log("[Lisa.ai E2E] Chromium browser installed.")}catch{console.warn("[Lisa.ai E2E Warning] Browser install failed. You may need to run 'npx playwright install' manually.")}D.existsSync(G.join(e,"playwright.config.ts"))||(D.writeFileSync(G.join(e,"playwright.config.ts"),`import { defineConfig } from '@playwright/test';
120
+ 4. Return ONLY the raw code string block.`;try{let c=await ge(r,s,n),u="";t.testingFramework==="jest"&&(u="jest.config.js"),t.testingFramework==="karma"&&(u="karma.conf.js"),t.testingFramework==="vitest"&&(u="vitest.config.ts");let p=Ve.join(o,u);Me.writeFileSync(p,c,"utf-8"),console.log(`\u2705 [Lisa.ai Auto-Generator] Natively wrote ${u} to repository root.`)}catch(c){console.error(`
121
+ \u274C [Lisa.ai Auto-Generator] Failed to author configuration file: ${c.message}`),process.exit(1)}}};var vt=b(require("dotenv"));vt.config({quiet:!0});function ne(e){let t=e.toLowerCase();return t.includes("karma")||t.includes("ng test")?"karma":t.includes("jest")?"jest":t.includes("vitest")?"vitest":t.includes("playwright")?"playwright":t.includes("cypress")?"cypress":t.includes("mocha")?"mocha":"unknown"}async function Ne(e,t,s){if(s==="local")return[];let n=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3088";try{let o=new URLSearchParams({projectId:s,errorLog:e.slice(0,1e3),framework:t}),i=await fetch(`${n}/api/v1/lisa/memory/lookup?${o}`);if(!i.ok)return[];let a=await i.json();return a.data??a}catch{return[]}}async function We(e,t,s,n){if(n==="local")return;let o=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3088";try{await fetch(`${o}/api/v1/lisa/memory/record`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectId:n,errorLog:e.slice(0,1e3),fixHint:t.slice(0,2e3),framework:s})}),console.log("[Lisa.ai Memory] Fix pattern recorded to Control Plane.")}catch(i){console.debug(`[Lisa.ai Agent Debug] Failed to record memory pattern: ${i.message}`)}}var _=b(require("fs")),Y=b(require("path")),ze=require("child_process");function Ue(e){if(_.existsSync(Y.join(e,"playwright.config.ts"))||_.existsSync(Y.join(e,"playwright.config.js")))return{framework:"playwright",configFile:_.existsSync(Y.join(e,"playwright.config.ts"))?"playwright.config.ts":"playwright.config.js",command:"npx playwright test",installed:!0};if(_.existsSync(Y.join(e,"cypress.config.ts"))||_.existsSync(Y.join(e,"cypress.config.js"))||_.existsSync(Y.join(e,"cypress.json")))return{framework:"cypress",configFile:_.existsSync(Y.join(e,"cypress.config.ts"))?"cypress.config.ts":_.existsSync(Y.join(e,"cypress.config.js"))?"cypress.config.js":"cypress.json",command:"npx cypress run",installed:!0};let t=Y.join(e,"package.json");if(_.existsSync(t)){let s=JSON.parse(_.readFileSync(t,"utf-8")),n={...s.dependencies||{},...s.devDependencies||{}};if(n["@playwright/test"])return{framework:"playwright",configFile:null,command:"npx playwright test",installed:!0};if(n.cypress)return{framework:"cypress",configFile:null,command:"npx cypress run",installed:!0}}return{framework:"playwright",configFile:null,command:"npx playwright test",installed:!1}}function De(e){console.log("[Lisa.ai E2E] No E2E framework detected. Installing Playwright...");try{(0,ze.execSync)("npm install -D @playwright/test",{cwd:e,stdio:"pipe"}),console.log("[Lisa.ai E2E] Playwright installed.")}catch(s){throw console.error(`[Lisa.ai E2E] Failed to install Playwright: ${s.message}`),new Error("Could not install @playwright/test. Please install it manually.")}try{console.log("[Lisa.ai E2E] Installing Playwright browsers..."),(0,ze.execSync)("npx playwright install --with-deps chromium",{cwd:e,stdio:"pipe"}),console.log("[Lisa.ai E2E] Chromium browser installed.")}catch{console.warn("[Lisa.ai E2E Warning] Browser install failed. You may need to run 'npx playwright install' manually.")}_.existsSync(Y.join(e,"playwright.config.ts"))||(_.writeFileSync(Y.join(e,"playwright.config.ts"),`import { defineConfig } from '@playwright/test';
122
122
 
123
123
  export default defineConfig({
124
124
  testDir: './e2e',
@@ -135,101 +135,101 @@ export default defineConfig({
135
135
  { name: 'chromium', use: { browserName: 'chromium' } },
136
136
  ],
137
137
  });
138
- `,"utf-8"),console.log("[Lisa.ai E2E] Generated playwright.config.ts"));let t=G.join(e,"e2e");D.existsSync(t)||(D.mkdirSync(t,{recursive:!0}),console.log("[Lisa.ai E2E] Created e2e/ directory"))}var z=0,me=0,de=0,he=0,ye=new Set;function Ye(e){let n=[...$e(e).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],o=parseInt(s[2]),i=s[3]?parseInt(s[3]):0,r=parseInt(s[1])-i,a=o>0?Math.round(r/o*100):0;return` [${"\u2588".repeat(Math.round(a/5))+"\u2591".repeat(20-Math.round(a/5))}] ${a}% ${r} passed ${i} failed ${o} total`}function gt(e,t=10){let n=[];for(let s of $e(e).split(`
139
- `)){let o=s.trim();if(/^FAILED/.test(o)||/ FAILED$/.test(o)){let r=o.replace(/^(?:Chrome|Firefox|Safari)(?:\s+Headless)?\s+[\d.]+(?:[\s.]\d+)*\s+\([^)]+\)\s+/,"").replace(/\s+FAILED$/,"").trim();if(n.push(r||o),n.length>=t)break}}return n}function Ht(e){let t=e.replace(/\.(spec|test)\.(ts|js|jsx|tsx)$/,".$2");if(t!==e&&O.existsSync(t))try{return console.log(` [Lisa.ai] \u{1F9E0} Sibling source found: ${J.basename(t)}`),O.readFileSync(t,"utf-8")}catch{return}}function Gt(e,t){let n=e.toLowerCase(),s=J.parse(t);if(n.includes("ng test")||n.includes("karma"))return`${e} --include **/${s.base}`;if(n.includes("jest")||n.includes("vitest")||n.includes("playwright"))return`${e} ${t}`;if(n.includes("cypress"))return`${e} --spec ${t}`;if(n.includes("npm")||n.includes("yarn")||n.includes("pnpm")){try{let i=JSON.parse(O.readFileSync(J.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=i.scripts?.[r]?.toLowerCase()||"",d=n.includes("npm")?" --":"";if(a.includes("ng test")||a.includes("karma"))return`${e}${d} --include **/${s.base}`;if(a.includes("jest")||a.includes("vitest")||a.includes("playwright"))return`${e}${d} ${t}`}catch{}let o=n.includes("npm")?" --":"";return`${e}${o} ${t}`}return e}function Ie(e,t=!1){return new Promise((n,s)=>{let o=(0,ft.spawn)(e,{shell:!0,stdio:["ignore","pipe","pipe"]}),i="",r="";o.stdout?.on("data",a=>{let d=a.toString();if(i+=d,t)for(let p of d.split(`
140
- `)){let u=p.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,"").trim();u&&(/Executed\s+\d+\s+of\s+\d+/.test(u)?process.stdout.write(`\r ${u.padEnd(72)}`):/^\s*FAILED\b/.test(u)&&(process.stdout.write(`
141
- `),console.log(` \u2717 ${u.trim()}`)))}}),o.stderr?.on("data",a=>{r+=a.toString()}),o.on("close",a=>{a===0?n({stdout:i,stderr:r}):s({message:`Process exited with code ${a}`,stdout:i,stderr:r})})})}function Bt(e,t,n){if(!t.match(/\.(spec|test)\.(ts|js|tsx|jsx|vue)$/))return;if((n||"").toLowerCase().includes("ng test")||(n||"").toLowerCase().includes("karma"))try{O.writeFileSync(e,`// Quarantined by Lisa.ai \u2014 could not be automatically healed
138
+ `,"utf-8"),console.log("[Lisa.ai E2E] Generated playwright.config.ts"));let t=Y.join(e,"e2e");_.existsSync(t)||(_.mkdirSync(t,{recursive:!0}),console.log("[Lisa.ai E2E] Created e2e/ directory"))}var ee=0,Le=0,he=0,xe=0,Ce=new Set;function Ze(e){let s=[...Ee(e).matchAll(/Executed\s+(\d+)\s+of\s+(\d+)(?:\s+\((\d+)\s+FAILED\))?/g)];if(s.length===0)return null;let n=[...s].reverse().find(u=>u[3]!==void 0)??s[s.length-1],o=parseInt(n[2]),i=n[3]?parseInt(n[3]):0,a=parseInt(n[1])-i,r=o>0?Math.round(a/o*100):0;return` [${"\u2588".repeat(Math.round(r/5))+"\u2591".repeat(20-Math.round(r/5))}] ${r}% ${a} passed ${i} failed ${o} total`}function Lt(e,t=10){let s=[];for(let n of Ee(e).split(`
139
+ `)){let o=n.trim();if(/^FAILED/.test(o)||/ FAILED$/.test(o)){let a=o.replace(/^(?:Chrome|Firefox|Safari)(?:\s+Headless)?\s+[\d.]+(?:[\s.]\d+)*\s+\([^)]+\)\s+/,"").replace(/\s+FAILED$/,"").trim();if(s.push(a||o),s.length>=t)break}}return s}function ss(e){let t=e.replace(/\.(spec|test)\.(ts|js|jsx|tsx)$/,".$2");if(t!==e&&B.existsSync(t))try{return console.log(` [Lisa.ai] \u{1F9E0} Sibling source found: ${te.basename(t)}`),B.readFileSync(t,"utf-8")}catch{return}}function Je(e,t){let s=e.toLowerCase(),n=te.parse(t);if(s.includes("ng test")||s.includes("karma"))return`${e} --include **/${n.base}`;if(s.includes("jest")||s.includes("vitest")||s.includes("playwright"))return`${e} ${t}`;if(s.includes("cypress"))return`${e} --spec ${t}`;if(s.includes("npm")||s.includes("yarn")||s.includes("pnpm")){try{let i=JSON.parse(B.readFileSync(te.resolve(process.cwd(),"package.json"),"utf8")),a="test";s.includes("npm run ")?a=s.split("npm run ")[1].split(" ")[0]:s.includes("yarn ")?a=s.split("yarn ")[1].split(" ")[0]:s.includes("pnpm ")&&(a=s.split("pnpm ")[1].split(" ")[0]);let r=i.scripts?.[a]?.toLowerCase()||"",c=s.includes("npm")?" --":"";if(r.includes("ng test")||r.includes("karma"))return`${e}${c} --include **/${n.base}`;if(r.includes("jest")||r.includes("vitest")||r.includes("playwright"))return`${e}${c} ${t}`}catch{}let o=s.includes("npm")?" --":"";return`${e}${o} ${t}`}return e}function oe(e,t=!1){return new Promise((s,n)=>{let o=(0,$t.spawn)(e,{shell:!0,stdio:["ignore","pipe","pipe"]}),i="",a="";o.stdout?.on("data",r=>{let c=r.toString();if(i+=c,t)for(let u of c.split(`
140
+ `)){let p=u.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,"").trim();p&&(/Executed\s+\d+\s+of\s+\d+/.test(p)?process.stdout.write(`\r ${p.padEnd(72)}`):/^\s*FAILED\b/.test(p)&&(process.stdout.write(`
141
+ `),console.log(` \u2717 ${p.trim()}`)))}}),o.stderr?.on("data",r=>{a+=r.toString()}),o.on("close",r=>{r===0?s({stdout:i,stderr:a}):n({message:`Process exited with code ${r}`,stdout:i,stderr:a})})})}function ns(e,t,s){if(!t.match(/\.(spec|test)\.(ts|js|tsx|jsx|vue)$/))return;if((s||"").toLowerCase().includes("ng test")||(s||"").toLowerCase().includes("karma"))try{B.writeFileSync(e,`// Quarantined by Lisa.ai \u2014 could not be automatically healed
142
142
  describe('', () => {});
143
- `,"utf-8"),console.log(" \u{1F6A8} Replaced with empty stub (Angular requires valid TS).")}catch{}else try{O.renameSync(e,e+".broken"),console.log(` \u{1F6A8} Renamed to ${J.basename(e)}.broken to skip in test runner.`)}catch{}}async function qt(e,t,n,s,o,i,r,a,d,p,u){let L=1,f=!1,x=n,m,c="",l="",y=Ht(t),h=await Ae(n,i,d),C=h.filter($=>$.confidence>=.5).map($=>$.fixHint);C.length>0&&console.log(` [Memory] ${C.length} proven pattern(s) found \u2014 injecting as LLM hint.`);let v=h.filter($=>$.confidence>=(u?.confidenceThreshold??.8));if(u?.enabled&&v.length>0){let $=v[0].confidence;console.log(` [Lisa LLM] High-confidence pattern (${$.toFixed(2)}) \u2014 trying ${u.model}...`);let g=O.readFileSync(t,"utf-8"),w=await nt(e,g,n,v.map(b=>b.fixHint),u);if(w){O.writeFileSync(t,w,"utf-8");try{return await Ie(o,!1),console.log(" [Lisa LLM] \u2705 Fix verified. Zero API cost."),c=`### Auto-Heal (Lisa LLM)
144
- **Model:** ${u.model}
143
+ `,"utf-8"),console.log(" \u{1F6A8} Replaced with empty stub (Angular requires valid TS).")}catch{}else try{B.renameSync(e,e+".broken"),console.log(` \u{1F6A8} Renamed to ${te.basename(e)}.broken to skip in test runner.`)}catch{}}async function Qe(e,t,s,n,o,i,a,r,c,u,p){let $=1,g=!1,L=s,m,d="",l="",y=ss(t),h=await Ne(s,i,c),C=h.filter(x=>x.confidence>=.5).map(x=>x.fixHint);C.length>0&&console.log(` [Memory] ${C.length} proven pattern(s) found \u2014 injecting as LLM hint.`);let v=h.filter(x=>x.confidence>=(p?.confidenceThreshold??.8));if(p?.enabled&&v.length>0){let x=v[0].confidence;console.log(` [Lisa LLM] High-confidence pattern (${x.toFixed(2)}) \u2014 trying ${p.model}...`);let f=B.readFileSync(t,"utf-8"),w=await dt(e,f,s,v.map(S=>S.fixHint),p);if(w){B.writeFileSync(t,w,"utf-8");try{return await oe(o,!1),console.log(" [Lisa LLM] \u2705 Fix verified. Zero API cost."),d=`### Auto-Heal (Lisa LLM)
144
+ **Model:** ${p.model}
145
145
  **Fix:**
146
146
  \`\`\`typescript
147
147
  ${w}
148
- \`\`\``,await Be(n,w,i,d),{status:"healed",details:c,lastCode:w,resolvedBy:"lisa-llm"}}catch{console.log(" [Lisa LLM] \u274C Verification failed. Escalating to cloud..."),O.writeFileSync(t,g,"utf-8")}}else console.log(" [Lisa LLM] No usable output. Escalating to cloud...")}for(;L<=a&&!f;){console.log(` [Attempt ${L}/${a}] Requesting fix from ${r}...`);let $=O.readFileSync(t,"utf-8"),g;for(let w=0;w<=3;w++)try{g=await tt(e,$,x,r,p,m,y,C);break}catch(b){let S=(b?.lastError??b)?.statusCode;if((S===529||S===500)&&w<3){let E=30*(w+1);console.warn(` \u26A0\uFE0F LLM overloaded (HTTP ${S}). Waiting ${E}s...`),await new Promise(T=>setTimeout(T,E*1e3))}else{console.error(` \u{1F6A8} LLM API failed: ${b?.message??String(b)}`);break}}if(g===void 0)break;O.writeFileSync(t,g,"utf-8"),c=`### Auto-Heal Analysis
148
+ \`\`\``,await We(s,w,i,c),{status:"healed",details:d,lastCode:w,resolvedBy:"lisa-llm"}}catch{console.log(" [Lisa LLM] \u274C Verification failed. Escalating to cloud..."),B.writeFileSync(t,f,"utf-8")}}else console.log(" [Lisa LLM] No usable output. Escalating to cloud...")}for(;$<=r&&!g;){console.log(` [Attempt ${$}/${r}] Requesting fix from ${a}...`);let x=B.readFileSync(t,"utf-8"),f;for(let w=0;w<=3;w++)try{f=await lt(e,x,L,a,u,m,y,C);break}catch(S){let F=(S?.lastError??S)?.statusCode;if((F===529||F===500)&&w<3){let E=30*(w+1);console.warn(` \u26A0\uFE0F LLM overloaded (HTTP ${F}). Waiting ${E}s...`),await new Promise(I=>setTimeout(I,E*1e3))}else{console.error(` \u{1F6A8} LLM API failed: ${S?.message??String(S)}`);break}}if(f===void 0)break;B.writeFileSync(t,f,"utf-8"),d=`### Auto-Heal Analysis
149
149
  **Error:**
150
150
  \`\`\`bash
151
- ${x.slice(0,2e3)}
151
+ ${L.slice(0,2e3)}
152
152
  \`\`\`
153
153
 
154
- **Fix (${r}):**
154
+ **Fix (${a}):**
155
155
  \`\`\`typescript
156
- ${g}
157
- \`\`\``;try{await Ie(o,!1),console.log(" \u2705 Isolated verification passed."),f=!0,l=g}catch(w){let b=w.stdout?w.stdout.toString():"";x=(w.stderr?w.stderr.toString():"")+`
158
- `+b+`
159
- `+(w.message||""),console.log(" \u274C Isolated verification failed.");let E=Ye(x);if(E&&console.log(E),gt(x,3).forEach(T=>console.log(` \u2717 ${T}`)),(s||"").toLowerCase().includes("ng test")||(s||"").toLowerCase().includes("karma")){let T=x.includes(J.basename(e)+":"),k=/ FAILED/.test(x);if(!T&&!k){console.log(" \u2705 Fix verified (build error is from other spec files, not this one)."),f=!0,l=g;continue}}m=`### Attempt ${L} Failed
156
+ ${f}
157
+ \`\`\``;try{await oe(o,!1),console.log(" \u2705 Isolated verification passed."),g=!0,l=f}catch(w){let S=w.stdout?w.stdout.toString():"";L=(w.stderr?w.stderr.toString():"")+`
158
+ `+S+`
159
+ `+(w.message||""),console.log(" \u274C Isolated verification failed.");let E=Ze(L);if(E&&console.log(E),Lt(L,3).forEach(I=>console.log(` \u2717 ${I}`)),(n||"").toLowerCase().includes("ng test")||(n||"").toLowerCase().includes("karma")){let I=L.includes(te.basename(e)+":"),k=/ FAILED/.test(L);if(!I&&!k){console.log(" \u2705 Fix verified (build error is from other spec files, not this one)."),g=!0,l=f;continue}}m=`### Attempt ${$} Failed
160
160
  \`\`\`typescript
161
- ${g}
161
+ ${f}
162
162
  \`\`\`
163
163
 
164
164
  **New Error:**
165
- ${x}`,L++}}return{status:f?"healed":"quarantined",details:c,lastCode:l,resolvedBy:f?r:void 0}}async function ue(e,t,n=1,s=3,o="local",i,r,a="unit",d,p=!1,u){z=0,me=0,de=0,he=0,ye.clear();let L=a==="unit"?"":` (${a})`,f=I.load(process.cwd());if(f&&I.applyEnvDefaults(f),a==="e2e"){if(!e&&f?.e2eCommand&&(e=f.e2eCommand,console.log(`[Lisa.ai Config] Using e2eCommand from .lisai.json: ${e}`)),!e){console.log(`
166
- [Lisa.ai Heal${L}] Auto-detecting E2E framework...`);let g=Te(process.cwd());!g.installed&&g.framework==="playwright"&&Pe(process.cwd()),e=g.command,console.log(`[Lisa.ai Heal${L}] E2E command: ${e}`)}}else if(!e&&f?.testCommand&&(e=f.testCommand,console.log(`[Lisa.ai Config] Using testCommand from .lisai.json: ${e}`)),!e){console.log(`
167
- [Lisa.ai Auto-Discovery] No --command provided. Scanning framework...`);let g=K.scanRepository();g.testingFramework==="none"&&(g=await re.installMissingFramework(g),await le.provisionConfigurationFiles(g,t,r)),g.suggestedTestCommand?(e=g.suggestedTestCommand,console.log(`[Lisa.ai Auto-Discovery] Discovered command: ${e}`)):(console.error(`
168
- \u{1F6A8} [Lisa.ai] Could not discover a test command. Pass --command explicitly.`),process.exit(1))}let x=ce(e),m=[],c="",l=null,y;if(d&&d.length>0)y=d,console.log(`
169
- [Lisa.ai Heal${L}] Targeting ${d.length} file(s) from --files`),y.forEach(g=>console.log(` - ${g}`));else{console.log(`
170
- [Lisa.ai Heal${L}] Running test suite (discovery pass)...`),console.log(`[Lisa.ai Executing] ${e} Model: ${t}`);try{await Ie(e,!0),process.stdout.write(`
165
+ ${L}`,$++}}return{status:g?"healed":"quarantined",details:d,lastCode:l,resolvedBy:g?a:void 0}}async function ye(e,t,s=1,n=3,o="local",i,a,r="unit",c,u=!1,p){ee=0,Le=0,he=0,xe=0,Ce.clear();let $=r==="unit"?"":` (${r})`,g=M.load(process.cwd());if(g&&M.applyEnvDefaults(g),r==="e2e"){if(!e&&g?.e2eCommand&&(e=g.e2eCommand,console.log(`[Lisa.ai Config] Using e2eCommand from .lisai.json: ${e}`)),!e){console.log(`
166
+ [Lisa.ai Heal${$}] Auto-detecting E2E framework...`);let f=Ue(process.cwd());!f.installed&&f.framework==="playwright"&&De(process.cwd()),e=f.command,console.log(`[Lisa.ai Heal${$}] E2E command: ${e}`)}}else if(!e&&g?.testCommand&&(e=g.testCommand,console.log(`[Lisa.ai Config] Using testCommand from .lisai.json: ${e}`)),!e){console.log(`
167
+ [Lisa.ai Auto-Discovery] No --command provided. Scanning framework...`);let f=K.scanRepository();f.testingFramework==="none"&&(f=await fe.installMissingFramework(f),await me.provisionConfigurationFiles(f,t,a)),f.suggestedTestCommand?(e=f.suggestedTestCommand,console.log(`[Lisa.ai Auto-Discovery] Discovered command: ${e}`)):(console.error(`
168
+ \u{1F6A8} [Lisa.ai] Could not discover a test command. Pass --command explicitly.`),process.exit(1))}let L=ne(e),m=[],d="",l=null,y;if(c&&c.length>0)y=c,console.log(`
169
+ [Lisa.ai Heal${$}] Targeting ${c.length} file(s) from --files`),y.forEach(f=>console.log(` - ${f}`));else{console.log(`
170
+ [Lisa.ai Heal${$}] Running test suite (discovery pass)...`),console.log(`[Lisa.ai Executing] ${e} Model: ${t}`);try{await oe(e,!0),process.stdout.write(`
171
171
  `),console.log(`
172
- \u2705 [Lisa.ai] All tests passing. Nothing to heal!`);let w={healedFiles:[],quarantinedFiles:[],testCounts:null,allPassing:!0};return p&&Oe({healedFiles:[],quarantinedFiles:[],testCounts:null,modelProvider:t,framework:"unknown",scope:a},null),w}catch(w){process.stdout.write(`
173
- `),c=(w.stderr||"")+`
172
+ \u2705 [Lisa.ai] All tests passing. Nothing to heal!`);let w={healedFiles:[],quarantinedFiles:[],testCounts:null,allPassing:!0};return u&&Ye({healedFiles:[],quarantinedFiles:[],testCounts:null,modelProvider:t,framework:"unknown",scope:r},null),w}catch(w){process.stdout.write(`
173
+ `),d=(w.stderr||"")+`
174
174
  `+(w.stdout||"")+`
175
- `+(w.message||"")}l=Y(c);let g=Ye(c);if(g){console.log(`
176
- ${g}`);let w=gt(c,10);if(w.length>0){let b=l?.failed??w.length;console.log(` Failing (${b}):`),w.forEach(S=>console.log(` \u2717 ${S}`)),b>w.length&&console.log(` ... and ${b-w.length} more`)}}if(y=Ue(c,process.cwd(),m),y.length===0){let w=Ce(c,m,process.cwd());w||(console.error(`
175
+ `+(w.message||"")}l=G(d);let f=Ze(d);if(f){console.log(`
176
+ ${f}`);let w=Lt(d,10);if(w.length>0){let S=l?.failed??w.length;console.log(` Failing (${S}):`),w.forEach(F=>console.log(` \u2717 ${F}`)),S>w.length&&console.log(` ... and ${S-w.length} more`)}}if(y=Ge(d,process.cwd(),m),y.length===0){let w=Ae(d,m,process.cwd());w||(console.error(`
177
177
  \u{1F6A8} [Lisa.ai] Could not identify any failing spec files. Output format may be unsupported.`),process.exit(1)),y=[w]}}let h=y.length;console.log(`
178
- [Lisa.ai] Found ${h} failing spec(s). Healing each in isolation...`),console.log(`${"\u2500".repeat(60)}`);for(let g of y)R({projectId:o,type:"heal",filePath:g,modelUsed:t,status:"running",details:"Queued for isolated healing.",...l&&{testTotal:l.total,testPassed:l.passed,testFailed:l.failed},testFramework:x});for(let g=0;g<y.length;g++){let w=y[g],b=J.resolve(process.cwd(),w);if(console.log(`
179
- [${g+1}/${h}] ${w}`),!O.existsSync(b)){console.warn(" \u26A0\uFE0F File not found \u2014 skipping.");continue}if(/[\\/](node_modules|dist|build)[\\/]/.test(b)){console.warn(" \u26A0\uFE0F Library file \u2014 refusing to modify.");continue}let S=Gt(e,w),E=await qt(w,b,c,e,S,x,t,s,o,r,u);E.status==="healed"?(z++,E.resolvedBy==="lisa-llm"?de++:he++,ye.add(w),console.log(` \u2705 Healed [${z} healed ${me} quarantined so far]`),R({projectId:o,type:"heal",filePath:w,modelUsed:E.resolvedBy==="lisa-llm"?u.model:t,status:"success",details:E.details,...l&&{testTotal:l.total,testPassed:l.passed,testFailed:l.failed},testFramework:x,resolvedBy:E.resolvedBy}),E.resolvedBy!=="lisa-llm"&&await Be(c,E.lastCode,x,o)):(me++,m.push(w),console.warn(` \u{1F6A8} Quarantined [${z} healed ${me} quarantined so far]`),R({projectId:o,type:"heal",filePath:w,modelUsed:t,status:"error",details:`Exhausted all ${s} attempts.
180
- `+E.details,...l&&{testTotal:l.total,testPassed:l.passed,testFailed:l.failed},testFramework:x}),Bt(b,w,e))}console.log(`
181
- ${"\u2500".repeat(60)}`);let C=!1;if(z>0){console.log("[Lisa.ai] Final verification run...");try{await Ie(e,!0),process.stdout.write(`
178
+ [Lisa.ai] Found ${h} failing spec(s). Healing each in isolation...`),console.log(`${"\u2500".repeat(60)}`);for(let f of y)N({projectId:o,type:"heal",filePath:f,modelUsed:t,status:"running",details:"Queued for isolated healing.",...l&&{testTotal:l.total,testPassed:l.passed,testFailed:l.failed},testFramework:L});for(let f=0;f<y.length;f++){let w=y[f],S=te.resolve(process.cwd(),w);if(console.log(`
179
+ [${f+1}/${h}] ${w}`),!B.existsSync(S)){console.warn(" \u26A0\uFE0F File not found \u2014 skipping.");continue}if(/[\\/](node_modules|dist|build)[\\/]/.test(S)){console.warn(" \u26A0\uFE0F Library file \u2014 refusing to modify.");continue}let F=Je(e,w),E=await Qe(w,S,d,e,F,L,t,n,o,a,p);E.status==="healed"?(ee++,E.resolvedBy==="lisa-llm"?he++:xe++,Ce.add(w),console.log(` \u2705 Healed [${ee} healed ${Le} quarantined so far]`),N({projectId:o,type:"heal",filePath:w,modelUsed:E.resolvedBy==="lisa-llm"?p.model:t,status:"success",details:E.details,...l&&{testTotal:l.total,testPassed:l.passed,testFailed:l.failed},testFramework:L,resolvedBy:E.resolvedBy}),E.resolvedBy!=="lisa-llm"&&await We(d,E.lastCode,L,o)):(Le++,m.push(w),console.warn(` \u{1F6A8} Quarantined [${ee} healed ${Le} quarantined so far]`),N({projectId:o,type:"heal",filePath:w,modelUsed:t,status:"error",details:`Exhausted all ${n} attempts.
180
+ `+E.details,...l&&{testTotal:l.total,testPassed:l.passed,testFailed:l.failed},testFramework:L}),ns(S,w,e))}console.log(`
181
+ ${"\u2500".repeat(60)}`);let C=!1;if(ee>0){console.log("[Lisa.ai] Final verification run...");try{await oe(e,!0),process.stdout.write(`
182
182
  `),console.log(`
183
- \u2705 All tests passing!`),C=!0}catch(g){process.stdout.write(`
184
- `);let w=(g.stderr||"")+`
185
- `+(g.stdout||"")+`
186
- `+(g.message||""),b=Ye(w);b&&console.log(`
187
- ${b}`);let S=Ue(w,process.cwd(),[]),E=S.filter(k=>ye.has(k)),T=S.filter(k=>!ye.has(k)&&!m.includes(k));E.length>0&&(console.warn(`
188
- \u26A0\uFE0F ${E.length} spec(s) passed isolated verification but still fail globally:`),E.forEach(k=>console.warn(` \u2717 ${k}`)),console.warn(" These likely have cross-test shared state. Try higher maxRetries or fix manually.")),T.length>0&&(console.warn(`
189
- \u2139\uFE0F ${T.length} additional failing spec(s) not in this run's queue:`),T.forEach(k=>console.warn(` \u2717 ${k}`)),console.warn(" Run lisa-agent heal again to address these."))}}let v=(l?.passed??0)+z;if(console.log(`
190
- \u2705 Healed: ${z} / ${h}`),u?.enabled&&(de>0||he>0)){let g=z>0?Math.round(de/z*100):0;console.log(` \u{1F9E0} Lisa LLM: ${de} fix${de!==1?"es":""} (${g}%)`),console.log(` \u2601\uFE0F Cloud: ${he} fix${he!==1?"es":""}`)}console.log(` \u{1F6A8} Quarantined: ${me}`),l&&console.log(` \u{1F4CA} Suite: ${l.passed} passing \u2192 ~${v} passing (est.)`),console.log(`${"\u2500".repeat(60)}
191
- `);let $={healedFiles:[...ye],quarantinedFiles:[...m],testCounts:l,allPassing:C};if(p){let g={healedFiles:$.healedFiles,quarantinedFiles:$.quarantinedFiles,testCounts:$.testCounts,modelProvider:t,framework:x,scope:a},w=await lt(g);Oe(g,w),$.allPassing||(process.exitCode=1)}return $}var yt=require("child_process"),W=j(require("fs")),U=j(require("path"));var Re=j(require("fs")),mt=j(require("path"));function ht(e){let t=mt.resolve(process.cwd(),e);if(!Re.existsSync(t))throw new Error(`[Lisa.ai Coverage Error] Coverage file not found at ${t}`);let n=Re.readFileSync(t,"utf-8"),s=JSON.parse(n),o=[];for(let[i,r]of Object.entries(s))i!=="total"&&(r.lines.pct<100||r.statements.pct<100||r.functions.pct<100||r.branches.pct<100)&&o.push(i);return o}var Me=new Set,we=0,Ve=3;function Yt(e){let t=e.testingFramework,n=process.cwd();return W.existsSync(U.join(n,"angular.json"))?"npx ng test --no-watch --code-coverage --browsers=ChromeHeadless":t==="jest"?"npx jest --coverage --coverageReporters=json-summary":t==="vitest"?"npx vitest run --coverage":[".mocharc.yml",".mocharc.yaml",".mocharc.json",".mocharc.js"].some(o=>W.existsSync(U.join(n,o)))?"npx nyc --reporter=json-summary mocha":null}async function wt(e,t,n=1,s=3,o="local",i,r,a){let d=I.load(process.cwd());d&&(I.applyEnvDefaults(d),!e&&d.testCommand&&(e=d.testCommand,console.log(`[Lisa.ai Config] Using testCommand from .lisai.json: ${e}`)));let p=K.scanRepository(),u=d?.testingFramework||(p.testingFramework!=="none"?p.testingFramework:void 0),L=d?.testTypes;if(!e){console.log(`
192
- [Lisa.ai Auto-Discovery] No explicit --command provided. Auto-detecting coverage command...`);let m=Yt(p);if(m)e=m,console.log(`[Lisa.ai Auto-Discovery] Detected coverage command: ${e}`);else{let c=p;if(c.testingFramework==="none"&&(c=await re.installMissingFramework(c),await le.provisionConfigurationFiles(c,t,i)),c.suggestedTestCommand){e=c.suggestedTestCommand;let l=c.testingFramework;(l==="jest"||l==="vitest")&&!e.includes("--coverage")&&(e=e.includes("npm run")?`${e} -- --coverage`:`${e} --coverage`,console.log(`[Lisa.ai Unit] Coverage flag appended: ${e}`)),console.log(`[Lisa.ai Auto-Discovery] Bootstrapping with discovered command: ${e}`)}else console.error(`
183
+ \u2705 All tests passing!`),C=!0}catch(f){process.stdout.write(`
184
+ `);let w=(f.stderr||"")+`
185
+ `+(f.stdout||"")+`
186
+ `+(f.message||""),S=Ze(w);S&&console.log(`
187
+ ${S}`);let F=Ge(w,process.cwd(),[]),E=F.filter(k=>Ce.has(k)),I=F.filter(k=>!Ce.has(k)&&!m.includes(k));E.length>0&&(console.warn(`
188
+ \u26A0\uFE0F ${E.length} spec(s) passed isolated verification but still fail globally:`),E.forEach(k=>console.warn(` \u2717 ${k}`)),console.warn(" These likely have cross-test shared state. Try higher maxRetries or fix manually.")),I.length>0&&(console.warn(`
189
+ \u2139\uFE0F ${I.length} additional failing spec(s) not in this run's queue:`),I.forEach(k=>console.warn(` \u2717 ${k}`)),console.warn(" Run lisa-agent heal again to address these."))}}let v=(l?.passed??0)+ee;if(console.log(`
190
+ \u2705 Healed: ${ee} / ${h}`),p?.enabled&&(he>0||xe>0)){let f=ee>0?Math.round(he/ee*100):0;console.log(` \u{1F9E0} Lisa LLM: ${he} fix${he!==1?"es":""} (${f}%)`),console.log(` \u2601\uFE0F Cloud: ${xe} fix${xe!==1?"es":""}`)}console.log(` \u{1F6A8} Quarantined: ${Le}`),l&&console.log(` \u{1F4CA} Suite: ${l.passed} passing \u2192 ~${v} passing (est.)`),console.log(`${"\u2500".repeat(60)}
191
+ `);let x={healedFiles:[...Ce],quarantinedFiles:[...m],testCounts:l,allPassing:C};if(u){let f={healedFiles:x.healedFiles,quarantinedFiles:x.quarantinedFiles,testCounts:x.testCounts,modelProvider:t,framework:L,scope:r},w=await mt(f);Ye(f,w),x.allPassing||(process.exitCode=1)}return x}var kt=require("child_process"),Q=b(require("fs")),O=b(require("path"));var Oe=b(require("fs")),xt=b(require("path"));function Ct(e){let t=xt.resolve(process.cwd(),e);if(!Oe.existsSync(t))throw new Error(`[Lisa.ai Coverage Error] Coverage file not found at ${t}`);let s=Oe.readFileSync(t,"utf-8"),n=JSON.parse(s),o=[];for(let[i,a]of Object.entries(n))i!=="total"&&(a.lines.pct<100||a.statements.pct<100||a.functions.pct<100||a.branches.pct<100)&&o.push(i);return o}var _e=new Set,ke=0,Xe=3;function os(e){let t=e.testingFramework,s=process.cwd();return Q.existsSync(O.join(s,"angular.json"))?"npx ng test --no-watch --code-coverage --browsers=ChromeHeadless":t==="jest"?"npx jest --coverage --coverageReporters=json-summary":t==="vitest"?"npx vitest run --coverage":[".mocharc.yml",".mocharc.yaml",".mocharc.json",".mocharc.js"].some(o=>Q.existsSync(O.join(s,o)))?"npx nyc --reporter=json-summary mocha":null}async function St(e,t,s=1,n=3,o="local",i,a,r){let c=M.load(process.cwd());c&&(M.applyEnvDefaults(c),!e&&c.testCommand&&(e=c.testCommand,console.log(`[Lisa.ai Config] Using testCommand from .lisai.json: ${e}`)));let u=K.scanRepository(),p=c?.testingFramework||(u.testingFramework!=="none"?u.testingFramework:void 0),$=c?.testTypes;if(!e){console.log(`
192
+ [Lisa.ai Auto-Discovery] No explicit --command provided. Auto-detecting coverage command...`);let m=os(u);if(m)e=m,console.log(`[Lisa.ai Auto-Discovery] Detected coverage command: ${e}`);else{let d=u;if(d.testingFramework==="none"&&(d=await fe.installMissingFramework(d),await me.provisionConfigurationFiles(d,t,i)),d.suggestedTestCommand){e=d.suggestedTestCommand;let l=d.testingFramework;(l==="jest"||l==="vitest")&&!e.includes("--coverage")&&(e=e.includes("npm run")?`${e} -- --coverage`:`${e} --coverage`,console.log(`[Lisa.ai Unit] Coverage flag appended: ${e}`)),console.log(`[Lisa.ai Auto-Discovery] Bootstrapping with discovered command: ${e}`)}else console.error(`
193
193
  [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(`
194
- [Lisa.ai Unit] ${e} (Attempt ${n}/${s}) Using Model: ${t}`);let f=ce(e);await R({projectId:o,type:"unit",filePath:"global-test-suite",modelUsed:t,status:"running",details:"Agent is executing unit test suite and validating coverage...",testFramework:f});let x=(m,c=!1)=>new Promise((l,y)=>{let h=(0,yt.spawn)(m,{shell:!0,stdio:["ignore","pipe","pipe"]}),C="",v="";h.stdout?.on("data",$=>{let g=$.toString();if(C+=g,c){let w=g.split(`
195
- `);for(let b of w){let S=b.match(/Executed\s+(\d+)\s+of\s+(\d+)/);if(S){let E=b.match(/(\d+)\s+FAILED/),T=E?E[1]:0,k=`\r[Lisa.ai Testing] Executed ${S[1]} of ${S[2]} (${T} FAILED) `;process.stdout.write(k)}}}}),h.stderr?.on("data",$=>{v+=$.toString()}),h.on("close",$=>{$===0?l({stdout:C,stderr:v}):y({message:`Test process exited with code ${$}`,stdout:C,stderr:v})})});try{console.log("[Lisa.ai Unit] Running tests with coverage. This may take a moment...");let m=await x(e,!0),c=m.stdout+`
196
- `+m.stderr,l=Y(c);l&&await R({projectId:o,type:"unit",filePath:"global-test-suite",modelUsed:t,status:"success",details:`Tests passed on attempt ${n}.`,testTotal:l.total,testPassed:l.passed,testFailed:l.failed,testFramework:f}),console.log(`
197
- [Lisa.ai Unit] Tests passed on attempt ${n}.`)}catch(m){let c=(m.stderr||"")+`
194
+ [Lisa.ai Unit] ${e} (Attempt ${s}/${n}) Using Model: ${t}`);let g=ne(e);await N({projectId:o,type:"unit",filePath:"global-test-suite",modelUsed:t,status:"running",details:"Agent is executing unit test suite and validating coverage...",testFramework:g});let L=(m,d=!1)=>new Promise((l,y)=>{let h=(0,kt.spawn)(m,{shell:!0,stdio:["ignore","pipe","pipe"]}),C="",v="";h.stdout?.on("data",x=>{let f=x.toString();if(C+=f,d){let w=f.split(`
195
+ `);for(let S of w){let F=S.match(/Executed\s+(\d+)\s+of\s+(\d+)/);if(F){let E=S.match(/(\d+)\s+FAILED/),I=E?E[1]:0,k=`\r[Lisa.ai Testing] Executed ${F[1]} of ${F[2]} (${I} FAILED) `;process.stdout.write(k)}}}}),h.stderr?.on("data",x=>{v+=x.toString()}),h.on("close",x=>{x===0?l({stdout:C,stderr:v}):y({message:`Test process exited with code ${x}`,stdout:C,stderr:v})})});try{console.log("[Lisa.ai Unit] Running tests with coverage. This may take a moment...");let m=await L(e,!0),d=m.stdout+`
196
+ `+m.stderr,l=G(d);l&&await N({projectId:o,type:"unit",filePath:"global-test-suite",modelUsed:t,status:"success",details:`Tests passed on attempt ${s}.`,testTotal:l.total,testPassed:l.passed,testFailed:l.failed,testFramework:g}),console.log(`
197
+ [Lisa.ai Unit] Tests passed on attempt ${s}.`)}catch(m){let d=(m.stderr||"")+`
198
198
  `+(m.stdout||"")+`
199
- `+(m.message||"");Ce(c,[],process.cwd())!==null?(console.log(`
200
- [Lisa.ai Unit] Tests failed. Delegating to Auto-Heal...`),await ue(e,t,1,s,o,void 0,i,"unit",void 0,!1,a),console.log(`
199
+ `+(m.message||"");Ae(d,[],process.cwd())!==null?(console.log(`
200
+ [Lisa.ai Unit] Tests failed. Delegating to Auto-Heal...`),await ye(e,t,1,n,o,void 0,i,"unit",void 0,!1,r),console.log(`
201
201
  [Lisa.ai Unit] Auto-Heal complete. Continuing to test generation...`)):console.log(`
202
- [Lisa.ai Unit] No failing spec detected. Tests may not exist yet \u2014 initiating Cold-Start Discovery...`)}try{let m=[];if(r&&r.length>0)m=r,console.log(`
203
- [Lisa.ai Unit] Targeting ${r.length} file(s) from --files`),m.forEach(l=>console.log(` - ${l}`));else{let l=U.resolve(process.cwd(),"coverage/coverage-summary.json");if(W.existsSync(l))console.log("[Lisa.ai Unit] Evaluating coverage summary..."),m=ht("coverage/coverage-summary.json");else{if(console.log(`
204
- [Lisa.ai Unit] No coverage-summary.json found. Initiating Cold-Start Discovery...`),m=K.findUntestedFiles(process.cwd(),[...Me]),m.length===0){console.log("[Lisa.ai Unit] No untested source files discovered.");return}console.log(`[Lisa.ai Unit] Discovered ${m.length} untested file(s). Generating specs...`)}let y=K.findUntestedFiles(process.cwd(),[...Me]);if(y.length>0){let h=new Set(m.map(v=>U.resolve(process.cwd(),v))),C=y.filter(v=>!h.has(U.resolve(process.cwd(),v)));C.length>0&&(console.log(`[Lisa.ai Unit] Discovered ${C.length} additional source file(s) with no tests.`),m.push(...C))}if(m.length===0){console.log("[Lisa.ai Unit] 100% coverage achieved. No uncovered files remaining.");return}}console.log(`[Lisa.ai Unit] Found ${m.length} file(s) below threshold:`,m);let c=0;for(let l of m){if(Me.has(l))continue;let y=U.resolve(process.cwd(),l),h=U.parse(y),C=[U.join(h.dir,`${h.name}.spec${h.ext}`),U.join(h.dir,`${h.name}.test${h.ext}`),U.join(h.dir,`${h.name}.spec.js`),U.join(h.dir,`${h.name}.test.js`),U.join(h.dir,`${h.name}.spec.ts`),U.join(h.dir,`${h.name}.test.ts`)],v=null,$=U.join(h.dir,`${h.name}.spec${h.ext}`);for(let S of C)if(W.existsSync(S)){$=S,v=W.readFileSync(S,"utf-8");break}let g="",w=!1,b=W.readFileSync(y,"utf-8");if(a?.enabled&&!v&&o!=="local"){let E=(await Ae(l,f,o)).filter(T=>T.confidence>=(a.confidenceThreshold??.8));if(E.length>0){console.log(`
205
- [Lisa.ai Unit] [${c+1}/${m.length}] Trying Lisa LLM for ${l}...`);let T=await ot(l,b,E.map(k=>k.fixHint),a,u);T&&(g=T,we=0,w=!0,console.log(" [Lisa LLM] \u2705 Test generated. Zero API cost."))}}if(!w)try{v?(console.log(`
206
- [Lisa.ai Unit] [${c+1}/${m.length}] Existing spec found for ${l}. Requesting coverage append...`),g=await st(l,b,U.relative(process.cwd(),$),v,t,i,u,L)):(console.log(`
207
- [Lisa.ai Unit] [${c+1}/${m.length}] Generating new spec for ${l}...`),g=await se(l,b,t,i,u,L)),we=0,w=!0}catch(S){we++,Me.add(l);let E=S.cause?.message?`${S.message} (cause: ${S.cause.message})`:S.message;if(console.error(`
208
- [Lisa.ai Unit] LLM call failed for ${l} \u2014 consecutive failure #${we}/${Ve}`),console.error(` Error: ${E}`),we>=Ve)throw new Error(`Systematic LLM failure: ${Ve} consecutive API calls failed.
202
+ [Lisa.ai Unit] No failing spec detected. Tests may not exist yet \u2014 initiating Cold-Start Discovery...`)}try{let m=[];if(a&&a.length>0)m=a,console.log(`
203
+ [Lisa.ai Unit] Targeting ${a.length} file(s) from --files`),m.forEach(l=>console.log(` - ${l}`));else{let l=O.resolve(process.cwd(),"coverage/coverage-summary.json");if(Q.existsSync(l))console.log("[Lisa.ai Unit] Evaluating coverage summary..."),m=Ct("coverage/coverage-summary.json");else{if(console.log(`
204
+ [Lisa.ai Unit] No coverage-summary.json found. Initiating Cold-Start Discovery...`),m=K.findUntestedFiles(process.cwd(),[..._e]),m.length===0){console.log("[Lisa.ai Unit] No untested source files discovered.");return}console.log(`[Lisa.ai Unit] Discovered ${m.length} untested file(s). Generating specs...`)}let y=K.findUntestedFiles(process.cwd(),[..._e]);if(y.length>0){let h=new Set(m.map(v=>O.resolve(process.cwd(),v))),C=y.filter(v=>!h.has(O.resolve(process.cwd(),v)));C.length>0&&(console.log(`[Lisa.ai Unit] Discovered ${C.length} additional source file(s) with no tests.`),m.push(...C))}if(m.length===0){console.log("[Lisa.ai Unit] 100% coverage achieved. No uncovered files remaining.");return}}console.log(`[Lisa.ai Unit] Found ${m.length} file(s) below threshold:`,m);let d=0;for(let l of m){if(_e.has(l))continue;let y=O.resolve(process.cwd(),l),h=O.parse(y),C=[O.join(h.dir,`${h.name}.spec${h.ext}`),O.join(h.dir,`${h.name}.test${h.ext}`),O.join(h.dir,`${h.name}.spec.js`),O.join(h.dir,`${h.name}.test.js`),O.join(h.dir,`${h.name}.spec.ts`),O.join(h.dir,`${h.name}.test.ts`)],v=null,x=O.join(h.dir,`${h.name}.spec${h.ext}`);for(let F of C)if(Q.existsSync(F)){x=F,v=Q.readFileSync(F,"utf-8");break}let f="",w=!1,S=Q.readFileSync(y,"utf-8");if(r?.enabled&&!v&&o!=="local"){let E=(await Ne(l,g,o)).filter(I=>I.confidence>=(r.confidenceThreshold??.8));if(E.length>0){console.log(`
205
+ [Lisa.ai Unit] [${d+1}/${m.length}] Trying Lisa LLM for ${l}...`);let I=await ut(l,S,E.map(k=>k.fixHint),r,p);I&&(f=I,ke=0,w=!0,console.log(" [Lisa LLM] \u2705 Test generated. Zero API cost."))}}if(!w)try{v?(console.log(`
206
+ [Lisa.ai Unit] [${d+1}/${m.length}] Existing spec found for ${l}. Requesting coverage append...`),f=await ct(l,S,O.relative(process.cwd(),x),v,t,i,p,$)):(console.log(`
207
+ [Lisa.ai Unit] [${d+1}/${m.length}] Generating new spec for ${l}...`),f=await X(l,S,t,i,p,$)),ke=0,w=!0}catch(F){ke++,_e.add(l);let E=F.cause?.message?`${F.message} (cause: ${F.cause.message})`:F.message;if(console.error(`
208
+ [Lisa.ai Unit] LLM call failed for ${l} \u2014 consecutive failure #${ke}/${Xe}`),console.error(` Error: ${E}`),ke>=Xe)throw new Error(`Systematic LLM failure: ${Xe} consecutive API calls failed.
209
209
  This usually means your API key has insufficient credits, hit a rate limit, or the model is unavailable.
210
210
  Last error: ${E}
211
- Tip: Check your API key, account credits, and rate-limit quota for provider '${t}'.`)}w&&(W.writeFileSync($,g,"utf-8"),c++,console.log(`[Lisa.ai Unit] Wrote spec to ${$}`),await R({projectId:o,type:"unit",filePath:l,modelUsed:t,status:"success",details:`### Unit Test Generated
211
+ Tip: Check your API key, account credits, and rate-limit quota for provider '${t}'.`)}w&&(Q.writeFileSync(x,f,"utf-8"),d++,console.log(`[Lisa.ai Unit] Wrote spec to ${x}`),await N({projectId:o,type:"unit",filePath:l,modelUsed:t,status:"success",details:`### Unit Test Generated
212
212
  **Auto-Generated Spec (${t}):**
213
213
  \`\`\`typescript
214
- ${g}
215
- \`\`\``,testFramework:f}))}if(console.log(`
216
- [Lisa.ai Unit] Generated ${c} spec file(s).`),c>0){console.log("[Lisa.ai Unit] Running verification pass...");try{let l=await x(e,!0),y=l.stdout+`
217
- `+l.stderr,h=Y(y);console.log(`
218
- [Lisa.ai Unit] Verification passed.`),h&&(console.log(`[Lisa.ai Unit] Final: ${h.passed}/${h.total} passing, ${h.failed} failing.`),await R({projectId:o,type:"unit",filePath:"global-test-suite",modelUsed:t,status:h.failed===0?"success":"error",details:`Verification after generating ${c} specs.`,testTotal:h.total,testPassed:h.passed,testFailed:h.failed,testFramework:f})),h&&h.failed>0&&n<s&&(console.log(`
219
- [Lisa.ai Unit] ${h.failed} generated test(s) failing. Delegating to Auto-Heal...`),await ue(e,t,1,s,o,void 0,i,"unit",void 0,!1,a))}catch(l){let y=(l.stderr||"")+`
220
- `+(l.stdout||""),h=Y(y);h&&h.failed>0&&n<s?(console.log(`
221
- [Lisa.ai Unit] ${h.failed} generated test(s) failing. Delegating to Auto-Heal...`),await ue(e,t,1,s,o,void 0,i,"unit",void 0,!1,a)):console.log(`
222
- [Lisa.ai Unit] Some generated tests failed. Run 'lisa-agent heal' to fix them.`)}}}catch(m){console.error("[Lisa.ai Unit loop failure]:",m.message),await R({projectId:o,type:"unit",filePath:"unit-loop",modelUsed:t,status:"error",details:`Unit test generation encountered an unrecoverable error: ${m.message}`,testFramework:f})}}var vt=require("child_process"),M=j(require("fs")),B=j(require("path"));function Vt(e){let t=[],n=["src/app/app.routes.ts","src/app/app-routing.module.ts"];for(let o of n){let i=B.join(e,o);if(M.existsSync(i)){let a=M.readFileSync(i,"utf-8").matchAll(/path:\s*['"`]([^'"`]+)['"`]/g);for(let d of a)d[1]&&d[1]!=="**"&&t.push(d[1])}}let s=["src/App.tsx","src/App.jsx","src/routes.tsx","src/routes.jsx"];for(let o of s){let i=B.join(e,o);if(M.existsSync(i)){let a=M.readFileSync(i,"utf-8").matchAll(/path=["']([^"']+)["']/g);for(let d of a)d[1]&&t.push(d[1])}}return[...new Set(t)].sort()}async function Lt(e,t,n=1,s=3,o="local",i,r){if(n>s){console.log(`
223
- [Lisa.ai E2E] Reached maximum retries (${s}). Stopping.`);return}let a=process.cwd(),d=I.load(a);d&&(I.applyEnvDefaults(d),!e&&d.e2eCommand&&(e=d.e2eCommand,console.log(`[Lisa.ai Config] Using e2eCommand from .lisai.json: ${e}`)));let p=Te(a),u=d?.e2eFramework||p.framework;console.log(`
224
- [Lisa.ai E2E] Framework: ${u} (Attempt ${n}/${s}) Model: ${t}`),!p.installed&&u==="playwright"&&Pe(a),e||(e=p.command,console.log(`[Lisa.ai E2E] Using auto-detected command: ${e}`)),await R({projectId:o,type:"e2e",filePath:"e2e-suite",modelUsed:t,status:"running",details:`E2E test generation started with ${u}`,testFramework:u});let L=0;if(r&&r.length>0){console.log(`
225
- [Lisa.ai E2E] Targeting ${r.length} file(s) from --files`),r.forEach(y=>console.log(` - ${y}`));for(let y of r){let h=B.resolve(a,y);if(M.existsSync(h)){console.log(`[Lisa.ai E2E] Spec already exists: ${y} \u2014 will run as-is.`);continue}let C=B.dirname(h);M.existsSync(C)||M.mkdirSync(C,{recursive:!0}),console.log(`[Lisa.ai E2E] Generating E2E spec: ${y}...`);let v=`
214
+ ${f}
215
+ \`\`\``,testFramework:g}))}if(console.log(`
216
+ [Lisa.ai Unit] Generated ${d} spec file(s).`),d>0){console.log("[Lisa.ai Unit] Running verification pass...");try{let l=await L(e,!0),y=l.stdout+`
217
+ `+l.stderr,h=G(y);console.log(`
218
+ [Lisa.ai Unit] Verification passed.`),h&&(console.log(`[Lisa.ai Unit] Final: ${h.passed}/${h.total} passing, ${h.failed} failing.`),await N({projectId:o,type:"unit",filePath:"global-test-suite",modelUsed:t,status:h.failed===0?"success":"error",details:`Verification after generating ${d} specs.`,testTotal:h.total,testPassed:h.passed,testFailed:h.failed,testFramework:g})),h&&h.failed>0&&s<n&&(console.log(`
219
+ [Lisa.ai Unit] ${h.failed} generated test(s) failing. Delegating to Auto-Heal...`),await ye(e,t,1,n,o,void 0,i,"unit",void 0,!1,r))}catch(l){let y=(l.stderr||"")+`
220
+ `+(l.stdout||""),h=G(y);h&&h.failed>0&&s<n?(console.log(`
221
+ [Lisa.ai Unit] ${h.failed} generated test(s) failing. Delegating to Auto-Heal...`),await ye(e,t,1,n,o,void 0,i,"unit",void 0,!1,r)):console.log(`
222
+ [Lisa.ai Unit] Some generated tests failed. Run 'lisa-agent heal' to fix them.`)}}}catch(m){console.error("[Lisa.ai Unit loop failure]:",m.message),await N({projectId:o,type:"unit",filePath:"unit-loop",modelUsed:t,status:"error",details:`Unit test generation encountered an unrecoverable error: ${m.message}`,testFramework:g})}}var bt=require("child_process"),U=b(require("fs")),V=b(require("path"));function is(e){let t=[],s=["src/app/app.routes.ts","src/app/app-routing.module.ts"];for(let o of s){let i=V.join(e,o);if(U.existsSync(i)){let r=U.readFileSync(i,"utf-8").matchAll(/path:\s*['"`]([^'"`]+)['"`]/g);for(let c of r)c[1]&&c[1]!=="**"&&t.push(c[1])}}let n=["src/App.tsx","src/App.jsx","src/routes.tsx","src/routes.jsx"];for(let o of n){let i=V.join(e,o);if(U.existsSync(i)){let r=U.readFileSync(i,"utf-8").matchAll(/path=["']([^"']+)["']/g);for(let c of r)c[1]&&t.push(c[1])}}return[...new Set(t)].sort()}async function Ft(e,t,s=1,n=3,o="local",i,a){if(s>n){console.log(`
223
+ [Lisa.ai E2E] Reached maximum retries (${n}). Stopping.`);return}let r=process.cwd(),c=M.load(r);c&&(M.applyEnvDefaults(c),!e&&c.e2eCommand&&(e=c.e2eCommand,console.log(`[Lisa.ai Config] Using e2eCommand from .lisai.json: ${e}`)));let u=Ue(r),p=c?.e2eFramework||u.framework;console.log(`
224
+ [Lisa.ai E2E] Framework: ${p} (Attempt ${s}/${n}) Model: ${t}`),!u.installed&&p==="playwright"&&De(r),e||(e=u.command,console.log(`[Lisa.ai E2E] Using auto-detected command: ${e}`)),await N({projectId:o,type:"e2e",filePath:"e2e-suite",modelUsed:t,status:"running",details:`E2E test generation started with ${p}`,testFramework:p});let $=0;if(a&&a.length>0){console.log(`
225
+ [Lisa.ai E2E] Targeting ${a.length} file(s) from --files`),a.forEach(y=>console.log(` - ${y}`));for(let y of a){let h=V.resolve(r,y);if(U.existsSync(h)){console.log(`[Lisa.ai E2E] Spec already exists: ${y} \u2014 will run as-is.`);continue}let C=V.dirname(h);U.existsSync(C)||U.mkdirSync(C,{recursive:!0}),console.log(`[Lisa.ai E2E] Generating E2E spec: ${y}...`);let v=`
226
226
  E2E Test Generation Context:
227
- - Framework: ${u}
227
+ - Framework: ${p}
228
228
  - Target file: ${y}
229
- - App Type: ${M.existsSync(B.join(a,"angular.json"))?"Angular":"Web App"}
229
+ - App Type: ${U.existsSync(V.join(r,"angular.json"))?"Angular":"Web App"}
230
230
  - Base URL: http://localhost:4200
231
231
 
232
- Generate a ${u} E2E test spec for "${y}".
232
+ Generate a ${p} E2E test spec for "${y}".
233
233
  The test should:
234
234
  1. Navigate to the relevant page
235
235
  2. Verify the page loads correctly
@@ -237,14 +237,14 @@ The test should:
237
237
  4. Assert critical UI elements are visible
238
238
 
239
239
  Use @playwright/test imports. Keep it concise and practical.
240
- `;try{let $=await se(y,v,t,i,u,["e2e"]);M.writeFileSync(h,$,"utf-8"),console.log(`[Lisa.ai E2E] Wrote ${y}`),L++,await R({projectId:o,type:"e2e",filePath:y,modelUsed:t,status:"success",details:`Generated E2E spec: ${y}`,testFramework:u})}catch($){console.error(`[Lisa.ai E2E] LLM failed for ${y}: ${$.message}`),await R({projectId:o,type:"e2e",filePath:y,modelUsed:t,status:"error",details:`LLM failed to generate E2E spec: ${$.message}`,testFramework:u})}}if(L===0&&r.every(y=>!M.existsSync(B.resolve(a,y)))){console.log("[Lisa.ai E2E] No specs generated. Check LLM connectivity with 'lisa-agent diagnose'.");return}}else{let y=Vt(a);y.length===0?(console.log("[Lisa.ai E2E] No routes discovered. Will generate basic smoke test."),y.push("")):console.log(`[Lisa.ai E2E] Discovered ${y.length} route(s):`,y);let h=B.join(a,"e2e");M.existsSync(h)||M.mkdirSync(h,{recursive:!0});let C=M.existsSync(h)?M.readdirSync(h).filter(v=>v.endsWith(".spec.ts")||v.endsWith(".test.ts")):[];for(let v of y){let g=`${(v||"home").replace(/\//g,"-")}.spec.ts`,w=B.join(h,g);if(C.includes(g)){console.log(`[Lisa.ai E2E] Spec already exists for /${v} \u2014 skipping.`);continue}console.log(`[Lisa.ai E2E] Generating E2E spec for /${v}...`);try{let b=`
240
+ `;try{let x=await X(y,v,t,i,p,["e2e"]);U.writeFileSync(h,x,"utf-8"),console.log(`[Lisa.ai E2E] Wrote ${y}`),$++,await N({projectId:o,type:"e2e",filePath:y,modelUsed:t,status:"success",details:`Generated E2E spec: ${y}`,testFramework:p})}catch(x){console.error(`[Lisa.ai E2E] LLM failed for ${y}: ${x.message}`),await N({projectId:o,type:"e2e",filePath:y,modelUsed:t,status:"error",details:`LLM failed to generate E2E spec: ${x.message}`,testFramework:p})}}if($===0&&a.every(y=>!U.existsSync(V.resolve(r,y)))){console.log("[Lisa.ai E2E] No specs generated. Check LLM connectivity with 'lisa-agent diagnose'.");return}}else{let y=is(r);y.length===0?(console.log("[Lisa.ai E2E] No routes discovered. Will generate basic smoke test."),y.push("")):console.log(`[Lisa.ai E2E] Discovered ${y.length} route(s):`,y);let h=V.join(r,"e2e");U.existsSync(h)||U.mkdirSync(h,{recursive:!0});let C=U.existsSync(h)?U.readdirSync(h).filter(v=>v.endsWith(".spec.ts")||v.endsWith(".test.ts")):[];for(let v of y){let f=`${(v||"home").replace(/\//g,"-")}.spec.ts`,w=V.join(h,f);if(C.includes(f)){console.log(`[Lisa.ai E2E] Spec already exists for /${v} \u2014 skipping.`);continue}console.log(`[Lisa.ai E2E] Generating E2E spec for /${v}...`);try{let S=`
241
241
  E2E Test Generation Context:
242
- - Framework: ${u}
242
+ - Framework: ${p}
243
243
  - Route: /${v}
244
- - App Type: ${M.existsSync(B.join(a,"angular.json"))?"Angular":"Web App"}
244
+ - App Type: ${U.existsSync(V.join(r,"angular.json"))?"Angular":"Web App"}
245
245
  - Base URL: http://localhost:4200
246
246
 
247
- Generate a ${u} E2E test spec for the route "/${v}".
247
+ Generate a ${p} E2E test spec for the route "/${v}".
248
248
  The test should:
249
249
  1. Navigate to the route
250
250
  2. Verify the page loads correctly
@@ -252,27 +252,27 @@ The test should:
252
252
  4. Assert critical UI elements are visible
253
253
 
254
254
  Use @playwright/test imports. Keep it concise and practical.
255
- `,S=await se(`e2e/${g}`,b,t,i,u,["e2e"]);M.writeFileSync(w,S,"utf-8"),console.log(`[Lisa.ai E2E] Wrote ${g}`),L++,await R({projectId:o,type:"e2e",filePath:`e2e/${g}`,modelUsed:t,status:"success",details:`Generated E2E spec for route /${v}`,testFramework:u})}catch(b){console.error(`[Lisa.ai E2E] LLM failed for /${v}: ${b.message}`),await R({projectId:o,type:"e2e",filePath:`e2e/${g}`,modelUsed:t,status:"error",details:`LLM failed to generate E2E spec: ${b.message}`,testFramework:u})}}if(L===0&&C.length===0){console.log("[Lisa.ai E2E] No specs generated. Check LLM connectivity with 'lisa-agent diagnose'.");return}}console.log(`
256
- [Lisa.ai E2E] Running E2E tests: ${e}`);let f="",x="",m=!1;try{await new Promise((y,h)=>{let C=(0,vt.spawn)(e,{shell:!0,stdio:["ignore","pipe","pipe"],cwd:a});C.stdout?.on("data",v=>{let $=v.toString();f+=$,process.stdout.write($)}),C.stderr?.on("data",v=>{let $=v.toString();x+=$,process.stderr.write($)}),C.on("close",v=>{v===0?y():h(new Error(`E2E tests exited with code ${v}`))})}),m=!0,console.log(`
255
+ `,F=await X(`e2e/${f}`,S,t,i,p,["e2e"]);U.writeFileSync(w,F,"utf-8"),console.log(`[Lisa.ai E2E] Wrote ${f}`),$++,await N({projectId:o,type:"e2e",filePath:`e2e/${f}`,modelUsed:t,status:"success",details:`Generated E2E spec for route /${v}`,testFramework:p})}catch(S){console.error(`[Lisa.ai E2E] LLM failed for /${v}: ${S.message}`),await N({projectId:o,type:"e2e",filePath:`e2e/${f}`,modelUsed:t,status:"error",details:`LLM failed to generate E2E spec: ${S.message}`,testFramework:p})}}if($===0&&C.length===0){console.log("[Lisa.ai E2E] No specs generated. Check LLM connectivity with 'lisa-agent diagnose'.");return}}console.log(`
256
+ [Lisa.ai E2E] Running E2E tests: ${e}`);let g="",L="",m=!1;try{await new Promise((y,h)=>{let C=(0,bt.spawn)(e,{shell:!0,stdio:["ignore","pipe","pipe"],cwd:r});C.stdout?.on("data",v=>{let x=v.toString();g+=x,process.stdout.write(x)}),C.stderr?.on("data",v=>{let x=v.toString();L+=x,process.stderr.write(x)}),C.on("close",v=>{v===0?y():h(new Error(`E2E tests exited with code ${v}`))})}),m=!0,console.log(`
257
257
  [Lisa.ai E2E] All E2E tests passed.`)}catch{console.log(`
258
- [Lisa.ai E2E] Some E2E tests failed. Run 'lisa-agent heal --type e2e' to auto-fix.`)}let c=f+`
259
- `+x,l=Y(c);await R({projectId:o,type:"e2e",filePath:"e2e-suite",modelUsed:t,status:m?"success":"error",details:`E2E generation complete. ${L} new spec(s) generated.`,...l&&{testTotal:l.total,testPassed:l.passed,testFailed:l.failed},testFramework:u})}var xt=require("child_process"),P=j(require("fs")),A=j(require("path"));function Kt(e){let t=A.join(e,"package.json");if(!P.existsSync(t))return"node";let n=JSON.parse(P.readFileSync(t,"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 Wt(e,t){let n=[];if(t==="angular"){let s=A.join(e,"src","app");if(!P.existsSync(s))return n;let o=ve(s,/\.(ts)$/).filter(i=>!i.includes(".spec.")&&!i.includes(".test.")&&!i.includes("node_modules"));for(let i of o){let r=P.readFileSync(i,"utf-8");if(r.includes("@Component")&&r.includes("inject(")){let a=A.relative(e,i),d=a.replace(/\.ts$/,".integration.spec.ts");if(P.existsSync(A.join(e,d)))continue;let u=[...r.matchAll(/inject\((\w+)\)/g)].map(f=>f[1]),L=[];for(let f of u){let x=ve(s,new RegExp(`${Zt(f)}\\.ts$`));L.push(...x.map(m=>A.relative(e,m)))}n.push({filePath:a,relatedFiles:L,description:`Angular component+service integration: ${A.basename(i)} injects ${u.join(", ")}`})}}}else if(t==="express"||t==="nestjs"){let s=["src","server","api","routes","controllers"];for(let o of s){let i=A.join(e,o);if(!P.existsSync(i))continue;let r=ve(i,/\.(controller|route|router)\.(ts|js)$/).filter(a=>!a.includes(".spec.")&&!a.includes(".test.")&&!a.includes("node_modules"));for(let a of r){let d=A.relative(e,a),p=d.replace(/\.(ts|js)$/,".integration.spec.$1");P.existsSync(A.join(e,p))||n.push({filePath:d,relatedFiles:[],description:`API endpoint integration test for ${A.basename(a)} \u2014 test with supertest against real middleware chain`})}}}else{let s=A.join(e,"src");if(P.existsSync(s)){let o=ve(s,/\.(ts|js|tsx|jsx)$/).filter(i=>!i.includes(".spec.")&&!i.includes(".test.")&&!i.includes("node_modules"));for(let i of o){let a=[...P.readFileSync(i,"utf-8").matchAll(/from\s+['"]\.\.?\//g)];if(a.length>=2){let d=A.relative(e,i),p=d.replace(/\.(ts|js|tsx|jsx)$/,".integration.spec.$1");if(P.existsSync(A.join(e,p)))continue;n.push({filePath:d,relatedFiles:[],description:`Module integration: ${A.basename(i)} imports ${a.length} local modules`})}}}}return n}function ve(e,t){let n=[],s=["node_modules","dist","build",".git",".angular","coverage"];if(!P.existsSync(e))return n;let o=P.readdirSync(e);for(let i of o){if(s.includes(i))continue;let r=A.join(e,i);try{P.statSync(r).isDirectory()?n.push(...ve(r,t)):t.test(i)&&n.push(r)}catch{continue}}return n}function Zt(e){return e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}async function $t(e,t,n=1,s=3,o="local",i,r){if(n>s){console.log(`
260
- [Lisa.ai Integration] Reached maximum retries (${s}). Stopping.`);return}let a=process.cwd(),d=I.load(a);d&&(I.applyEnvDefaults(d),!e&&d.testCommand&&(e=d.testCommand,console.log(`[Lisa.ai Config] Using testCommand from .lisai.json: ${e}`)));let p=Kt(a),u=K.scanRepository(),L=d?.testingFramework||(u.testingFramework!=="none"?u.testingFramework:void 0);console.log(`
261
- [Lisa.ai Integration] App: ${p}, Framework: ${L||"auto"} (Attempt ${n}/${s}) Model: ${t}`),e||(u.suggestedTestCommand?e=u.suggestedTestCommand:P.existsSync(A.join(a,"angular.json"))?e="npx ng test --no-watch --browsers=ChromeHeadless":(console.error(`
262
- [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: ${e}`));let f=ce(e);await R({projectId:o,type:"integration",filePath:"integration-suite",modelUsed:t,status:"running",details:`Integration test generation started for ${p} app`,testFramework:f});let x;if(r&&r.length>0)x=r.map(v=>({filePath:v,relatedFiles:[],description:"User-specified"})),console.log(`
263
- [Lisa.ai Integration] Targeting ${r.length} file(s) from --files`),x.forEach(v=>console.log(` - ${v.filePath}`));else{if(x=Wt(a,p),x.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 ${x.length} integration target(s):`),x.forEach(v=>console.log(` - ${v.filePath}: ${v.description}`))}let m=0;for(let v of x){let $=A.resolve(a,v.filePath);if(!P.existsSync($))continue;let g=A.parse($),w=A.join(g.dir,`${g.name}.integration.spec${g.ext}`);if(P.existsSync(w)){console.log(`[Lisa.ai Integration] Spec exists for ${v.filePath} \u2014 skipping.`);continue}console.log(`[Lisa.ai Integration] Generating integration spec for ${v.filePath}...`);let b=P.readFileSync($,"utf-8"),S="";for(let T of v.relatedFiles.slice(0,3)){let k=A.resolve(a,T);if(P.existsSync(k)){let _=P.readFileSync(k,"utf-8");S+=`
264
-
265
- // --- Related file: ${T} ---
266
- ${_.slice(0,5e3)}`}}let E=`
258
+ [Lisa.ai E2E] Some E2E tests failed. Run 'lisa-agent heal --type e2e' to auto-fix.`)}let d=g+`
259
+ `+L,l=G(d);await N({projectId:o,type:"e2e",filePath:"e2e-suite",modelUsed:t,status:m?"success":"error",details:`E2E generation complete. ${$} new spec(s) generated.`,...l&&{testTotal:l.total,testPassed:l.passed,testFailed:l.failed},testFramework:p})}var jt=require("child_process"),R=b(require("fs")),P=b(require("path"));function as(e){let t=P.join(e,"package.json");if(!R.existsSync(t))return"node";let s=JSON.parse(R.readFileSync(t,"utf-8")),n={...s.dependencies||{},...s.devDependencies||{}};return n["@angular/core"]?"angular":n.react?"react":n.vue?"vue":n["@nestjs/core"]?"nestjs":n.express?"express":"node"}function rs(e,t){let s=[];if(t==="angular"){let n=P.join(e,"src","app");if(!R.existsSync(n))return s;let o=Se(n,/\.(ts)$/).filter(i=>!i.includes(".spec.")&&!i.includes(".test.")&&!i.includes("node_modules"));for(let i of o){let a=R.readFileSync(i,"utf-8");if(a.includes("@Component")&&a.includes("inject(")){let r=P.relative(e,i),c=r.replace(/\.ts$/,".integration.spec.ts");if(R.existsSync(P.join(e,c)))continue;let p=[...a.matchAll(/inject\((\w+)\)/g)].map(g=>g[1]),$=[];for(let g of p){let L=Se(n,new RegExp(`${ls(g)}\\.ts$`));$.push(...L.map(m=>P.relative(e,m)))}s.push({filePath:r,relatedFiles:$,description:`Angular component+service integration: ${P.basename(i)} injects ${p.join(", ")}`})}}}else if(t==="express"||t==="nestjs"){let n=["src","server","api","routes","controllers"];for(let o of n){let i=P.join(e,o);if(!R.existsSync(i))continue;let a=Se(i,/\.(controller|route|router)\.(ts|js)$/).filter(r=>!r.includes(".spec.")&&!r.includes(".test.")&&!r.includes("node_modules"));for(let r of a){let c=P.relative(e,r),u=c.replace(/\.(ts|js)$/,".integration.spec.$1");R.existsSync(P.join(e,u))||s.push({filePath:c,relatedFiles:[],description:`API endpoint integration test for ${P.basename(r)} \u2014 test with supertest against real middleware chain`})}}}else{let n=P.join(e,"src");if(R.existsSync(n)){let o=Se(n,/\.(ts|js|tsx|jsx)$/).filter(i=>!i.includes(".spec.")&&!i.includes(".test.")&&!i.includes("node_modules"));for(let i of o){let r=[...R.readFileSync(i,"utf-8").matchAll(/from\s+['"]\.\.?\//g)];if(r.length>=2){let c=P.relative(e,i),u=c.replace(/\.(ts|js|tsx|jsx)$/,".integration.spec.$1");if(R.existsSync(P.join(e,u)))continue;s.push({filePath:c,relatedFiles:[],description:`Module integration: ${P.basename(i)} imports ${r.length} local modules`})}}}}return s}function Se(e,t){let s=[],n=["node_modules","dist","build",".git",".angular","coverage"];if(!R.existsSync(e))return s;let o=R.readdirSync(e);for(let i of o){if(n.includes(i))continue;let a=P.join(e,i);try{R.statSync(a).isDirectory()?s.push(...Se(a,t)):t.test(i)&&s.push(a)}catch{continue}}return s}function ls(e){return e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}async function Et(e,t,s=1,n=3,o="local",i,a){if(s>n){console.log(`
260
+ [Lisa.ai Integration] Reached maximum retries (${n}). Stopping.`);return}let r=process.cwd(),c=M.load(r);c&&(M.applyEnvDefaults(c),!e&&c.testCommand&&(e=c.testCommand,console.log(`[Lisa.ai Config] Using testCommand from .lisai.json: ${e}`)));let u=as(r),p=K.scanRepository(),$=c?.testingFramework||(p.testingFramework!=="none"?p.testingFramework:void 0);console.log(`
261
+ [Lisa.ai Integration] App: ${u}, Framework: ${$||"auto"} (Attempt ${s}/${n}) Model: ${t}`),e||(p.suggestedTestCommand?e=p.suggestedTestCommand:R.existsSync(P.join(r,"angular.json"))?e="npx ng test --no-watch --browsers=ChromeHeadless":(console.error(`
262
+ [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: ${e}`));let g=ne(e);await N({projectId:o,type:"integration",filePath:"integration-suite",modelUsed:t,status:"running",details:`Integration test generation started for ${u} app`,testFramework:g});let L;if(a&&a.length>0)L=a.map(v=>({filePath:v,relatedFiles:[],description:"User-specified"})),console.log(`
263
+ [Lisa.ai Integration] Targeting ${a.length} file(s) from --files`),L.forEach(v=>console.log(` - ${v.filePath}`));else{if(L=rs(r,u),L.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 ${L.length} integration target(s):`),L.forEach(v=>console.log(` - ${v.filePath}: ${v.description}`))}let m=0;for(let v of L){let x=P.resolve(r,v.filePath);if(!R.existsSync(x))continue;let f=P.parse(x),w=P.join(f.dir,`${f.name}.integration.spec${f.ext}`);if(R.existsSync(w)){console.log(`[Lisa.ai Integration] Spec exists for ${v.filePath} \u2014 skipping.`);continue}console.log(`[Lisa.ai Integration] Generating integration spec for ${v.filePath}...`);let S=R.readFileSync(x,"utf-8"),F="";for(let I of v.relatedFiles.slice(0,3)){let k=P.resolve(r,I);if(R.existsSync(k)){let H=R.readFileSync(k,"utf-8");F+=`
264
+
265
+ // --- Related file: ${I} ---
266
+ ${H.slice(0,5e3)}`}}let E=`
267
267
  INTEGRATION TEST \u2014 ${v.description}
268
268
 
269
269
  Generate an integration test that exercises real module interactions (NOT mocked).
270
- ${p==="angular"?`
270
+ ${u==="angular"?`
271
271
  - Use TestBed with real service providers (not jasmine.createSpyObj)
272
272
  - Import the actual component and its real dependencies
273
273
  - Test that the component and service work together correctly
274
274
  - Use HttpClientTestingModule for HTTP calls
275
- `:p==="express"||p==="nestjs"?`
275
+ `:u==="express"||u==="nestjs"?`
276
276
  - Use supertest to make real HTTP requests against the app
277
277
  - Test the full middleware chain (auth, validation, handler)
278
278
  - Use a test database or in-memory mock for data layer
@@ -283,32 +283,32 @@ ${p==="angular"?`
283
283
  `}
284
284
 
285
285
  Source file:
286
- ${b.slice(0,1e4)}
286
+ ${S.slice(0,1e4)}
287
287
 
288
- ${S?`Related modules:
289
- ${S}`:""}
290
- `;try{let T=await se(v.filePath,E,t,i,L,["integration"]);P.writeFileSync(w,T,"utf-8"),console.log(`[Lisa.ai Integration] Wrote ${A.relative(a,w)}`),m++,await R({projectId:o,type:"integration",filePath:v.filePath,modelUsed:t,status:"success",details:`Generated integration spec: ${v.description}`,testFramework:f})}catch(T){console.error(`[Lisa.ai Integration] LLM failed for ${v.filePath}: ${T.message}`)}}if(m===0){console.log("[Lisa.ai Integration] No new integration specs generated.");return}console.log(`
291
- [Lisa.ai Integration] Running tests to verify generated specs: ${e}`);let c="",l="",y=!1;try{await new Promise((v,$)=>{let g=(0,xt.spawn)(e,{shell:!0,stdio:["ignore","pipe","pipe"],cwd:a});g.stdout?.on("data",w=>{let b=w.toString();c+=b,process.stdout.write(b)}),g.stderr?.on("data",w=>{let b=w.toString();l+=b,process.stderr.write(b)}),g.on("close",w=>{w===0?v():$(new Error(`Tests exited with code ${w}`))})}),y=!0,console.log(`
288
+ ${F?`Related modules:
289
+ ${F}`:""}
290
+ `;try{let I=await X(v.filePath,E,t,i,$,["integration"]);R.writeFileSync(w,I,"utf-8"),console.log(`[Lisa.ai Integration] Wrote ${P.relative(r,w)}`),m++,await N({projectId:o,type:"integration",filePath:v.filePath,modelUsed:t,status:"success",details:`Generated integration spec: ${v.description}`,testFramework:g})}catch(I){console.error(`[Lisa.ai Integration] LLM failed for ${v.filePath}: ${I.message}`)}}if(m===0){console.log("[Lisa.ai Integration] No new integration specs generated.");return}console.log(`
291
+ [Lisa.ai Integration] Running tests to verify generated specs: ${e}`);let d="",l="",y=!1;try{await new Promise((v,x)=>{let f=(0,jt.spawn)(e,{shell:!0,stdio:["ignore","pipe","pipe"],cwd:r});f.stdout?.on("data",w=>{let S=w.toString();d+=S,process.stdout.write(S)}),f.stderr?.on("data",w=>{let S=w.toString();l+=S,process.stderr.write(S)}),f.on("close",w=>{w===0?v():x(new Error(`Tests exited with code ${w}`))})}),y=!0,console.log(`
292
292
  [Lisa.ai Integration] All tests passed including integration specs.`)}catch{console.log(`
293
- [Lisa.ai Integration] Some tests failed. Run 'lisa-agent heal --type integration' to auto-fix.`)}let h=c+`
294
- `+l,C=Y(h);await R({projectId:o,type:"integration",filePath:"integration-suite",modelUsed:t,status:y?"success":"error",details:`Integration test generation complete. ${m} new spec(s) generated.`,...C&&{testTotal:C.total,testPassed:C.passed,testFailed:C.failed},testFramework:f})}var bt=j(require("dotenv")),St=require("child_process"),X=j(require("fs")),Q=j(require("path"));bt.config({quiet:!0});function pe(e){return new Promise(t=>{let n=(0,St.spawn)(e,{shell:!0,stdio:["ignore","pipe","pipe"]}),s="",o="";n.stdout?.on("data",i=>{s+=i.toString()}),n.stderr?.on("data",i=>{o+=i.toString()}),n.on("close",i=>t({stdout:s,stderr:o,code:i??1}))})}function zt(){return X.existsSync(Q.resolve(process.cwd(),"pnpm-lock.yaml"))?"pnpm":X.existsSync(Q.resolve(process.cwd(),"yarn.lock"))?"yarn":"npm"}var Jt={critical:"\u{1F534}",high:"\u{1F7E0}",moderate:"\u{1F7E1}",low:"\u{1F535}",info:"\u26AA"},Ct={critical:5,high:4,moderate:3,low:2,info:1};function kt(e){let t=["critical","high","moderate","low","info"];for(let n of t){let s=e[n]??0;s>0&&console.log(` ${Jt[n]} ${n.padEnd(10)} ${s}`)}console.log(` ${"\u2500".repeat(22)}`),console.log(` ${"Total".padEnd(10)} ${e.total}`)}async function Ke(e){try{return JSON.parse(e)}catch{return null}}async function Ft(e,t="local",n){X.existsSync(Q.resolve(process.cwd(),"package.json"))||(console.error(`
295
- \u{1F6A8} [Lisa.ai Audit] No package.json found in ${process.cwd()}.`),console.error(" The audit command currently supports Node.js / npm projects only."),process.exit(1));let s=zt();console.log(`[Lisa.ai Audit] Package manager: ${s}`),console.log(`
296
- [Lisa.ai Audit] Scanning for vulnerabilities...`);let{stdout:o,code:i}=await pe(s==="npm"?"npm audit --json":s==="yarn"?"yarn audit --json":"pnpm audit --json");if(i===0){console.log(`
297
- \u2705 [Lisa.ai Audit] No vulnerabilities found! Repository is clean.`);return}let r=await Ke(o);r||(console.error("[Lisa.ai Audit] Could not parse audit output. Run 'npm audit' manually to investigate."),process.exit(1));let a=r.metadata.vulnerabilities;console.log(`
298
- Vulnerabilities found:`),kt(a),console.log(`
299
- [Lisa.ai Audit] Applying safe automatic fixes (npm audit fix)...`);let{stdout:d}=await pe(s==="npm"?"npm audit fix":s==="yarn"?"yarn upgrade":"pnpm audit fix");for(let k of d.split(`
300
- `)){let _=k.trim();_&&!_.startsWith("npm warn")&&!_.startsWith("npm notice")&&console.log(` ${_}`)}let p=s==="npm"?"npm audit --json":s==="yarn"?"yarn audit --json":"pnpm audit --json",{stdout:u,code:L}=await pe(p);if(L===0){console.log(`
301
- \u2705 [Lisa.ai Audit] All vulnerabilities fixed by safe auto-fix!`);return}let f=await Ke(u);if(!f){console.warn("[Lisa.ai Audit] Could not parse post-fix audit output.");return}let x=Object.values(f.vulnerabilities),m=f.metadata.vulnerabilities,c=a.total-m.total;console.log(`
302
- [Lisa.ai Audit] Safe auto-fix resolved ${c} vulnerability(-ies).`),m.total>0&&(console.log(" Remaining:"),kt(m));let l=x.filter(k=>typeof k.fixAvailable=="object"&&!k.fixAvailable.isSemVerMajor).map(k=>k.fixAvailable),y=[...new Map(l.map(k=>[k.name,k])).values()];if(y.length>0){console.log(`
303
- [Lisa.ai Audit] Applying ${y.length} non-breaking targeted upgrade(s)...`);for(let ie of y){let q=`npm install ${ie.name}@${ie.version}`;console.log(` \u2192 ${q}`);let{stderr:fe}=await pe(q);fe&&!fe.includes("npm warn")&&console.warn(` ${fe.trim()}`)}let{stdout:k,code:_}=await pe(p);if(_===0){console.log(`
304
- \u2705 [Lisa.ai Audit] All vulnerabilities resolved!`);return}}let{stdout:h}=await pe(p),C=await Ke(h)??f,$=Object.values(C.vulnerabilities).sort((k,_)=>(Ct[_.severity]??0)-(Ct[k.severity]??0)).slice(0,10).filter(k=>k.fixAvailable===!1?!0:typeof k.fixAvailable=="object"?k.fixAvailable.isSemVerMajor:!1);if($.length===0){console.log(`
293
+ [Lisa.ai Integration] Some tests failed. Run 'lisa-agent heal --type integration' to auto-fix.`)}let h=d+`
294
+ `+l,C=G(h);await N({projectId:o,type:"integration",filePath:"integration-suite",modelUsed:t,status:y?"success":"error",details:`Integration test generation complete. ${m} new spec(s) generated.`,...C&&{testTotal:C.total,testPassed:C.passed,testFailed:C.failed},testFramework:g})}var Pt=b(require("dotenv")),It=require("child_process"),ae=b(require("fs")),ie=b(require("path"));Pt.config({quiet:!0});function we(e){return new Promise(t=>{let s=(0,It.spawn)(e,{shell:!0,stdio:["ignore","pipe","pipe"]}),n="",o="";s.stdout?.on("data",i=>{n+=i.toString()}),s.stderr?.on("data",i=>{o+=i.toString()}),s.on("close",i=>t({stdout:n,stderr:o,code:i??1}))})}function cs(){return ae.existsSync(ie.resolve(process.cwd(),"pnpm-lock.yaml"))?"pnpm":ae.existsSync(ie.resolve(process.cwd(),"yarn.lock"))?"yarn":"npm"}var ds={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 Tt(e){let t=["critical","high","moderate","low","info"];for(let s of t){let n=e[s]??0;n>0&&console.log(` ${ds[s]} ${s.padEnd(10)} ${n}`)}console.log(` ${"\u2500".repeat(22)}`),console.log(` ${"Total".padEnd(10)} ${e.total}`)}async function et(e){try{return JSON.parse(e)}catch{return null}}async function Rt(e,t="local",s){ae.existsSync(ie.resolve(process.cwd(),"package.json"))||(console.error(`
295
+ \u{1F6A8} [Lisa.ai Audit] No package.json found in ${process.cwd()}.`),console.error(" The audit command currently supports Node.js / npm projects only."),process.exit(1));let n=cs();console.log(`[Lisa.ai Audit] Package manager: ${n}`),console.log(`
296
+ [Lisa.ai Audit] Scanning for vulnerabilities...`);let{stdout:o,code:i}=await we(n==="npm"?"npm audit --json":n==="yarn"?"yarn audit --json":"pnpm audit --json");if(i===0){console.log(`
297
+ \u2705 [Lisa.ai Audit] No vulnerabilities found! Repository is clean.`);return}let a=await et(o);a||(console.error("[Lisa.ai Audit] Could not parse audit output. Run 'npm audit' manually to investigate."),process.exit(1));let r=a.metadata.vulnerabilities;console.log(`
298
+ Vulnerabilities found:`),Tt(r),console.log(`
299
+ [Lisa.ai Audit] Applying safe automatic fixes (npm audit fix)...`);let{stdout:c}=await we(n==="npm"?"npm audit fix":n==="yarn"?"yarn upgrade":"pnpm audit fix");for(let k of c.split(`
300
+ `)){let H=k.trim();H&&!H.startsWith("npm warn")&&!H.startsWith("npm notice")&&console.log(` ${H}`)}let u=n==="npm"?"npm audit --json":n==="yarn"?"yarn audit --json":"pnpm audit --json",{stdout:p,code:$}=await we(u);if($===0){console.log(`
301
+ \u2705 [Lisa.ai Audit] All vulnerabilities fixed by safe auto-fix!`);return}let g=await et(p);if(!g){console.warn("[Lisa.ai Audit] Could not parse post-fix audit output.");return}let L=Object.values(g.vulnerabilities),m=g.metadata.vulnerabilities,d=r.total-m.total;console.log(`
302
+ [Lisa.ai Audit] Safe auto-fix resolved ${d} vulnerability(-ies).`),m.total>0&&(console.log(" Remaining:"),Tt(m));let l=L.filter(k=>typeof k.fixAvailable=="object"&&!k.fixAvailable.isSemVerMajor).map(k=>k.fixAvailable),y=[...new Map(l.map(k=>[k.name,k])).values()];if(y.length>0){console.log(`
303
+ [Lisa.ai Audit] Applying ${y.length} non-breaking targeted upgrade(s)...`);for(let ue of y){let Z=`npm install ${ue.name}@${ue.version}`;console.log(` \u2192 ${Z}`);let{stderr:ve}=await we(Z);ve&&!ve.includes("npm warn")&&console.warn(` ${ve.trim()}`)}let{stdout:k,code:H}=await we(u);if(H===0){console.log(`
304
+ \u2705 [Lisa.ai Audit] All vulnerabilities resolved!`);return}}let{stdout:h}=await we(u),C=await et(h)??g,x=Object.values(C.vulnerabilities).sort((k,H)=>(At[H.severity]??0)-(At[k.severity]??0)).slice(0,10).filter(k=>k.fixAvailable===!1?!0:typeof k.fixAvailable=="object"?k.fixAvailable.isSemVerMajor:!1);if(x.length===0){console.log(`
305
305
  \u2705 [Lisa.ai Audit] No breaking-change or unfixable vulnerabilities remain.`);return}console.log(`
306
- [Lisa.ai Audit] ${$.length} vulnerability(-ies) require manual attention.`),console.log(`[Lisa.ai Audit] Consulting ${e} for remediation guidance...`);let g={};try{g=JSON.parse(X.readFileSync(Q.resolve(process.cwd(),"package.json"),"utf8"))}catch{}let w={...g.dependencies??{},...g.devDependencies??{}},b=$.map(k=>{let _=k.fixAvailable,ie=_===!1?"no fix available yet":`upgrade to ${_.name}@${_.version} (BREAKING \u2014 major version bump)`,q=w[k.name]?`current: ${w[k.name]}`:"transitive dependency";return`\u2022 ${k.name} (${k.severity}) \u2014 ${q} \u2014 ${ie}`}).join(`
307
- `),S=`You are a Node.js security expert helping a developer fix npm vulnerabilities.
306
+ [Lisa.ai Audit] ${x.length} vulnerability(-ies) require manual attention.`),console.log(`[Lisa.ai Audit] Consulting ${e} for remediation guidance...`);let f={};try{f=JSON.parse(ae.readFileSync(ie.resolve(process.cwd(),"package.json"),"utf8"))}catch{}let w={...f.dependencies??{},...f.devDependencies??{}},S=x.map(k=>{let H=k.fixAvailable,ue=H===!1?"no fix available yet":`upgrade to ${H.name}@${H.version} (BREAKING \u2014 major version bump)`,Z=w[k.name]?`current: ${w[k.name]}`:"transitive dependency";return`\u2022 ${k.name} (${k.severity}) \u2014 ${Z} \u2014 ${ue}`}).join(`
307
+ `),F=`You are a Node.js security expert helping a developer fix npm vulnerabilities.
308
308
 
309
309
  These vulnerabilities remain after safe auto-fixes were applied. Each requires either a breaking upgrade or has no automated fix:
310
310
 
311
- ${b}
311
+ ${S}
312
312
 
313
313
  For each vulnerability:
314
314
  1. Briefly explain the security risk (1 sentence).
@@ -316,35 +316,148 @@ For each vulnerability:
316
316
  3. If the upgrade is breaking (major version), note the most likely code change needed (e.g. renamed API, changed import path).
317
317
  4. If no fix exists, suggest a workaround or safe alternative package.
318
318
 
319
- Be concrete. Use numbered items. If a vulnerability is a transitive dependency, explain how to force a resolution override in package.json.`;try{let k=await ae(S,e,n);console.log(`
319
+ Be concrete. Use numbered items. If a vulnerability is a transitive dependency, explain how to force a resolution override in package.json.`;try{let k=await ge(F,e,s);console.log(`
320
320
  Remediation guidance:
321
- `);for(let q of k.split(`
322
- `))console.log(` ${q}`);let _=Q.resolve(process.cwd(),"lisa-audit-report.md"),ie=["# Lisa.ai Security Audit Report","",`Generated: ${new Date().toISOString()}`,`Project: ${g.name??Q.basename(process.cwd())}`,"","## Summary","","| Severity | Initial | Remaining |","|----------|---------|-----------|",...["critical","high","moderate","low"].map(q=>{let fe=a[q]??0,Pt=C.metadata.vulnerabilities[q]??0;return`| ${q} | ${fe} | ${Pt} |`}),"","## Vulnerabilities Requiring Manual Action","",b,"",`## Remediation Guidance (${e})`,"",k].join(`
323
- `);X.writeFileSync(_,ie,"utf-8"),console.log(`
321
+ `);for(let Z of k.split(`
322
+ `))console.log(` ${Z}`);let H=ie.resolve(process.cwd(),"lisa-audit-report.md"),ue=["# Lisa.ai Security Audit Report","",`Generated: ${new Date().toISOString()}`,`Project: ${f.name??ie.basename(process.cwd())}`,"","## Summary","","| Severity | Initial | Remaining |","|----------|---------|-----------|",...["critical","high","moderate","low"].map(Z=>{let ve=r[Z]??0,Vt=C.metadata.vulnerabilities[Z]??0;return`| ${Z} | ${ve} | ${Vt} |`}),"","## Vulnerabilities Requiring Manual Action","",S,"",`## Remediation Guidance (${e})`,"",k].join(`
323
+ `);ae.writeFileSync(H,ue,"utf-8"),console.log(`
324
324
  [Lisa.ai Audit] Full report saved to: lisa-audit-report.md`)}catch(k){console.error(`
325
- [Lisa.ai Audit] LLM guidance failed: ${k.message}`),console.log(" Run 'npm audit' manually and address the remaining vulnerabilities.")}let E=C.metadata.vulnerabilities,T=a.total-E.total;console.log(`
326
- ${"\u2500".repeat(50)}`),console.log(" Lisa.ai Audit Complete"),console.log(` \u2705 Fixed: ${T} / ${a.total}`),console.log(` \u26A0\uFE0F Remaining: ${E.total} (see lisa-audit-report.md)`),console.log(`${"\u2500".repeat(50)}
327
- `)}var Tt=Et();function oe(){let e=Tt.version;console.log(`
325
+ [Lisa.ai Audit] LLM guidance failed: ${k.message}`),console.log(" Run 'npm audit' manually and address the remaining vulnerabilities.")}let E=C.metadata.vulnerabilities,I=r.total-E.total;console.log(`
326
+ ${"\u2500".repeat(50)}`),console.log(" Lisa.ai Audit Complete"),console.log(` \u2705 Fixed: ${I} / ${r.total}`),console.log(` \u26A0\uFE0F Remaining: ${E.total} (see lisa-audit-report.md)`),console.log(`${"\u2500".repeat(50)}
327
+ `)}var Dt=b(require("readline")),W=b(require("fs")),se=b(require("path")),Ot=require("ai");var Mt=40,Nt=8e3,Ut=15e3,T="\x1B[38;2;99;102;241m",A="\x1B[0m",He="\x1B[2m",_t="\x1B[1m";function us(){let e="unknown";try{e=JSON.parse(W.readFileSync(se.resolve(process.cwd(),"package.json"),"utf-8")).name||"unknown"}catch{}let t={type:"unknown",testingFramework:"none"};try{t=K.scanRepository()}catch{}let s=M.get();return{packageName:e,frameworkType:t.type,testingFramework:s?.testingFramework||t.testingFramework,testCommand:s?.testCommand||t.suggestedTestCommand||null,e2eFramework:s?.e2eFramework||null,hasLisaiConfig:!!s}}function ps(e){return`You are Lisa.ai, an autonomous CI/CD expert focused on test healing and generation.
328
+ You are chatting with a developer about their project in an interactive terminal session.
329
+
330
+ Project: ${e.packageName}
331
+ Framework: ${e.frameworkType}
332
+ Testing framework: ${e.testingFramework}
333
+ Test command: ${e.testCommand||"auto-detected"}
334
+
335
+ Guidelines:
336
+ - Be concise and code-focused.
337
+ - Use markdown code blocks for code snippets.
338
+ - When asked about test failures, analyze the root cause carefully.
339
+ - Do not generate full test files unless explicitly asked \u2014 prefer explaining the approach.
340
+ - If the user shares test output, analyze it and suggest specific fixes.
341
+ - When the user loads a file via /read, use its content to answer questions or generate code.
342
+ - The user can use slash commands (/read, /heal, /test, /explain, /generate, /status, /help, /exit) \u2014 you don't need to explain them unless asked.`}function gs(){console.log(`
343
+ ${_t}Available commands:${A}
344
+
345
+ ${T}/read${A} <file> Load a file into conversation context
346
+ ${T}/test${A} [cmd] Run tests and show results
347
+ ${T}/explain${A} <file> Analyze why a test is failing (no file changes)
348
+ ${T}/heal${A} <file> Auto-heal a specific failing test file
349
+ ${T}/generate${A} <file> Generate test spec for a source file
350
+ ${T}/status${A} Show test pass/fail summary
351
+ ${T}/help${A} Show this help
352
+ ${T}/exit${A} Exit chat
353
+
354
+ Or just type a question to chat with Lisa.ai.
355
+ `)}async function fs(e,t){let s=t.trim()||e.projectContext.testCommand;if(!s){console.log(`
356
+ ${T}Error:${A} No test command found. Use /test <command> or set testCommand in .lisai.json
357
+ `);return}console.log(`
358
+ Running: ${He}${s}${A}
359
+ `);try{let{stdout:n,stderr:o}=await oe(s,!0);process.stdout.write(`
360
+ `);let i=o+`
361
+ `+n,a=G(i);console.log(a?`
362
+ \u2705 ${a.passed} passed ${a.failed} failed ${a.total} total
363
+ `:`
364
+ \u2705 Tests passed.
365
+ `),e.lastTestOutput=i.slice(0,Nt),e.messages.push({role:"user",content:`[Test Output \u2014 all passing]
366
+ ${e.lastTestOutput}`})}catch(n){process.stdout.write(`
367
+ `);let o=(n.stderr||"")+`
368
+ `+(n.stdout||"")+`
369
+ `+(n.message||""),i=G(o);console.log(i?`
370
+ \u2717 ${i.passed} passed ${i.failed} failed ${i.total} total
371
+ `:`
372
+ \u2717 Tests failed.
373
+ `),e.lastTestOutput=o.slice(0,Nt),e.messages.push({role:"user",content:`[Test Output \u2014 failures detected]
374
+ ${e.lastTestOutput}`})}}function ms(e,t){let s=t.trim();if(!s){console.log(`
375
+ ${T}Usage:${A} /read <file-path>
376
+ `);return}let n=se.resolve(process.cwd(),s);if(!W.existsSync(n)){console.log(`
377
+ ${T}Error:${A} File not found: ${s}
378
+ `);return}let o=W.readFileSync(n,"utf-8"),i=o.split(`
379
+ `).length,a=o.length>Ut?o.slice(0,Ut)+`
380
+ // ... (truncated)`:o;e.messages.push({role:"user",content:`[File Loaded: ${s}]
381
+ \`\`\`
382
+ ${a}
383
+ \`\`\``}),console.log(`
384
+ \u2713 Loaded ${s} (${i} lines) into context
385
+ `)}async function hs(e,t){let s=t.trim();if(!s){console.log(`
386
+ ${T}Usage:${A} /explain <file-path>
387
+ `);return}let n=se.resolve(process.cwd(),s);if(!W.existsSync(n)){console.log(`
388
+ ${T}Error:${A} File not found: ${s}
389
+ `);return}let o=W.readFileSync(n,"utf-8"),i=o.length>1e4?o.slice(0,1e4)+`
390
+ // ... (truncated)`:o,a=`The user wants you to analyze why a test file is failing. Do NOT generate a fix \u2014 just explain the root cause.
391
+
392
+ --- File: ${s} ---
393
+ ${i}`;e.lastTestOutput&&(a+=`
394
+
395
+ --- Recent Test Output ---
396
+ ${e.lastTestOutput.slice(0,4e3)}`),e.messages.push({role:"user",content:a}),console.log(),await Ht(e)}async function ys(e,t){let s=t.trim();if(!s){console.log(`
397
+ ${T}Usage:${A} /heal <file-path>
398
+ `);return}let n=se.resolve(process.cwd(),s);if(!W.existsSync(n)){console.log(`
399
+ ${T}Error:${A} File not found: ${s}
400
+ `);return}let o=e.projectContext.testCommand;if(!o){console.log(`
401
+ ${T}Error:${A} No test command found. Set testCommand in .lisai.json
402
+ `);return}let i=ne(o),a=Je(o,s),r="";try{await oe(a,!1),console.log(`
403
+ \u2705 ${s} is already passing. Nothing to heal.
404
+ `),e.messages.push({role:"user",content:`[Heal Result] ${s} \u2014 already passing, no changes needed.`});return}catch(u){r=(u.stderr||"")+`
405
+ `+(u.stdout||"")+`
406
+ `+(u.message||"")}console.log(`
407
+ Healing ${s}...
408
+ `);let c=await Qe(s,n,r,o,a,i,e.model,5,e.projectId,e.apiKey,e.lisaLlmConfig);if(c.status==="healed"){let u=c.resolvedBy==="lisa-llm"?"Lisa LLM":"Cloud";console.log(`
409
+ \u2705 Healed via ${u}.
410
+ `),e.messages.push({role:"user",content:`[Heal Result] ${s} \u2014 healed successfully via ${u} (${c.resolvedBy}).`})}else console.log(`
411
+ \u2717 Could not heal ${s} after all retries.
412
+ `),e.messages.push({role:"user",content:`[Heal Result] ${s} \u2014 failed to heal. Quarantined.`})}async function ws(e,t){let s=t.trim();if(!s){console.log(`
413
+ ${T}Usage:${A} /generate <source-file-path>
414
+ `);return}let n=se.resolve(process.cwd(),s);if(!W.existsSync(n)){console.log(`
415
+ ${T}Error:${A} File not found: ${s}
416
+ `);return}let o=W.readFileSync(n,"utf-8"),i=e.projectContext.testingFramework!=="none"?e.projectContext.testingFramework:void 0;console.log(`
417
+ Generating test for ${s}...
418
+ `);try{let a=await X(s,o,e.model,e.apiKey,i),r=se.extname(s),c=s.replace(r,`.spec${r}`),u=se.resolve(process.cwd(),c);W.writeFileSync(u,a,"utf-8"),console.log(` \u2705 Generated: ${c}
419
+ `),e.messages.push({role:"user",content:`[Generate Result] Test generated for ${s} \u2192 ${c}`})}catch(a){console.log(` \u2717 Generation failed: ${a.message}
420
+ `),e.messages.push({role:"user",content:`[Generate Result] Failed to generate test for ${s}: ${a.message}`})}}async function vs(e){let t=e.projectContext.testCommand;if(!t){console.log(`
421
+ ${T}Error:${A} No test command found.
422
+ `);return}console.log(`
423
+ Running: ${He}${t}${A}`);try{let{stdout:s,stderr:n}=await oe(t,!1),o=n+`
424
+ `+s,i=G(o);if(i){let a=i.total>0?Math.round(i.passed/i.total*100):0,r="\u2588".repeat(Math.round(a/5))+"\u2591".repeat(20-Math.round(a/5));console.log(` [${r}] ${a}% \u2705 ${i.passed} passed ${i.total} total
425
+ `)}else console.log(` \u2705 All tests passing.
426
+ `)}catch(s){let n=(s.stderr||"")+`
427
+ `+(s.stdout||"")+`
428
+ `+(s.message||""),o=G(n);if(o){let i=o.total>0?Math.round(o.passed/o.total*100):0,a="\u2588".repeat(Math.round(i/5))+"\u2591".repeat(20-Math.round(i/5));console.log(` [${a}] ${i}% ${o.passed} passed ${o.failed} failed ${o.total} total
429
+ `)}else console.log(` \u2717 Tests failing.
430
+ `)}}async function Ht(e){try{let t=(0,Ot.streamText)({model:pe(e.model,e.apiKey),messages:e.messages}),s="";for await(let n of t.textStream)process.stdout.write(n),s+=n;if(process.stdout.write(`
431
+
432
+ `),e.messages.push({role:"assistant",content:s}),e.messages.length>Mt){let n=e.messages[0];e.messages=[n,...e.messages.slice(-(Mt-2))]}}catch(t){let s=t.message||String(t);s.includes("API key")?console.log(`
433
+ ${T}Error:${A} Invalid or missing API key for ${e.model}. Check your env vars.
434
+ `):console.log(`
435
+ ${T}Error:${A} ${s.slice(0,200)}
436
+ `)}}async function $s(e){let t=Dt.createInterface({input:process.stdin,output:process.stdout,prompt:`${T}lisa>${A} `});t.prompt(),t.on("line",async s=>{let n=s.trim();if(!n){t.prompt();return}if(t.pause(),n.startsWith("/")){let o=n.indexOf(" "),i=o>0?n.slice(0,o).toLowerCase():n.toLowerCase(),a=o>0?n.slice(o+1):"";switch(i){case"/exit":case"/quit":console.log(`
437
+ ${He}Goodbye!${A}
438
+ `),t.close();return;case"/help":gs();break;case"/read":ms(e,a);break;case"/test":await fs(e,a);break;case"/explain":await hs(e,a);break;case"/heal":await ys(e,a);break;case"/generate":await ws(e,a);break;case"/status":await vs(e);break;default:console.log(`
439
+ Unknown command: ${i}. Type /help for available commands.
440
+ `)}}else e.messages.push({role:"user",content:n}),console.log(),await Ht(e);t.resume(),t.prompt()}),t.on("close",()=>{process.exit(0)})}async function Gt(e,t,s,n){let o=us(),a={messages:[{role:"system",content:ps(o)}],model:e,apiKey:s,projectId:t,projectContext:o,lisaLlmConfig:n},r=o;console.log(` ${T}\u2554\u2550\u2557${A} ${_t}Interactive Mode${A}`),console.log(` ${T}\u2551${A}${T}\u26A1${T}\u2551${A} Type /help for commands, /exit to quit`),console.log(` ${T}\u255A\u2550\u255D${A} ${He}Project: ${r.packageName} | Framework: ${r.testingFramework} | Model: ${e}${A}`),console.log(),await $s(a)}var Kt=Bt();function le(){let e=Kt.version;console.log(`
328
441
  \x1B[38;2;99;102;241m\u2554\u2550\u2557\x1B[0m \x1B[1mLisa.ai Agent\x1B[0m v${e}
329
442
  \x1B[38;2;99;102;241m\u2551\x1B[38;2;129;140;248m\u26A1\x1B[38;2;99;102;241m\u2551\x1B[0m Autonomous CI/CD Platform
330
- \x1B[38;2;99;102;241m\u255A\u2550\u255D\x1B[0m \x1B[2mheal \xB7 unit \xB7 e2e \xB7 integration \xB7 audit\x1B[0m
331
- `)}async function Ne(e){let t=5,n=e.model,s,o=I.load(process.cwd()),i=e.projectId||o?.projectId||"local";if(i!=="local"){let a=await xe(i);if(!a.ok)a.reason==="not_found"?(console.error(`
443
+ \x1B[38;2;99;102;241m\u255A\u2550\u255D\x1B[0m \x1B[2mheal \xB7 unit \xB7 e2e \xB7 integration \xB7 audit \xB7 chat\x1B[0m
444
+ `)}async function Fe(e){let t=5,s=e.model,n,o=M.load(process.cwd()),i=e.projectId||o?.projectId||"local";if(i!=="local"){let r=await je(i);if(!r.ok)r.reason==="not_found"?(console.error(`
332
445
  [Lisa.ai Agent Error] Project '${i}' does not exist on the Control Plane. Please create it in the Dashboard first or run locally without a project ID.`),process.exit(1)):console.warn(`
333
- [Lisa.ai Warning] Control Plane is unreachable. Continuing with local CLI defaults (model=${n}, maxRetries=${t}).`);else{let d=a.config;console.log(`[Lisa.ai Agent] Dynamic Config Loaded: Provider=${d.modelProvider}, MaxRetries=${d.maxRetries}`),n=d.modelProvider,t=d.maxRetries,s=d.apiKey,d.agentEnabled===!1&&(console.log("[Lisa.ai Agent] Agent is disabled by Control Plane for this project."),process.exit(1))}}o&&(I.applyEnvDefaults(o),o.maxRetries!==void 0&&(t=o.maxRetries),o.provider&&e.model==="gemini"&&(n=o.provider),I.printSummary(o)),t<5&&console.warn(`
334
- [Lisa.ai Warning] maxRetries is ${t}. Consider increasing to 5+ for complex projects.`);let r=He(o?.lisaLlm);return r.enabled&&console.log(`[Lisa.ai Config] Lisa LLM enabled: ${r.model} @ ${r.baseUrl}`),{model:n,maxRetries:t,apiKey:s,projectId:i,lisaLlmConfig:r}}var Z=new jt.Command;Z.name("lisa-agent").description("Lisa.ai - Autonomous CI/CD Platform Worker Agent").version(Tt.version);Z.command("heal").description("Run tests and autonomously heal all failures via LLM + Memory Engine").option("-c, --command <type>","Test command to execute (auto-detected if omitted)").option("-m, --model <provider>","LLM provider to use (gemini, claude, openai)","gemini").option("-p, --project-id <id>","Control Plane Project ID").option("-t, --type <scope>","Test type to heal: unit (default), e2e, integration","unit").option("-f, --files <paths>","Target specific files (comma-separated, bypasses discovery)").option("--ci","CI mode: commit healed files, open PR, write GitHub Actions step summary").action(async e=>{oe();let t=["unit","e2e","integration"],n=e.type;t.includes(n)||(console.error(`
335
- [Lisa.ai Error] Invalid heal type '${e.type}'. Valid types: ${t.join(", ")}`),process.exit(1));let s=e.files?e.files.split(",").map(r=>r.trim()):void 0,o=e.ci===!0,i=await Ne(e);await ue(e.command,i.model,1,i.maxRetries,i.projectId,void 0,i.apiKey,n,s,o,i.lisaLlmConfig)});Z.command("unit").description("Generate missing unit test specs to reach 100% coverage (auto-detects framework)").option("-c, --command <type>","Test+coverage command to execute (auto-detected if omitted)").option("-m, --model <provider>","LLM provider to use (gemini, claude, openai)","gemini").option("-p, --project-id <id>","Control Plane Project ID").option("-f, --files <paths>","Target specific files (comma-separated, bypasses discovery)").action(async e=>{oe();let t=e.files?e.files.split(",").map(s=>s.trim()):void 0,n=await Ne(e);await wt(e.command,n.model,1,n.maxRetries,n.projectId,n.apiKey,t,n.lisaLlmConfig)});Z.command("e2e").description("Generate E2E tests via Playwright (auto-installs if none detected)").option("-c, --command <type>","E2E command to execute (auto-detected if omitted)").option("-m, --model <provider>","LLM provider to use (gemini, claude, openai)","gemini").option("-p, --project-id <id>","Control Plane Project ID").option("-f, --files <paths>","Target specific files (comma-separated, bypasses discovery)").action(async e=>{oe();let t=e.files?e.files.split(",").map(s=>s.trim()):void 0,n=await Ne(e);await Lt(e.command,n.model,1,n.maxRetries,n.projectId,n.apiKey,t)});Z.command("integration").description("Generate integration tests for module seams (component+service, API+DB)").option("-c, --command <type>","Test command to execute (auto-detected if omitted)").option("-m, --model <provider>","LLM provider to use (gemini, claude, openai)","gemini").option("-p, --project-id <id>","Control Plane Project ID").option("-f, --files <paths>","Target specific files (comma-separated, bypasses discovery)").action(async e=>{oe();let t=e.files?e.files.split(",").map(s=>s.trim()):void 0,n=await Ne(e);await $t(e.command,n.model,1,n.maxRetries,n.projectId,n.apiKey,t)});Z.command("diagnose").description("Test LLM connectivity and API key health without running the full agent").option("-m, --model <provider>","LLM provider to test (gemini, claude, openai)","claude").option("-p, --project-id <id>","Control Plane Project ID to fetch API key from").action(async e=>{oe();let t=e.model,n,s=I.load(process.cwd()),o=e.projectId||s?.projectId||"local";if(o!=="local"){let a=await xe(o);a.ok?(t=a.config.modelProvider,n=a.config.apiKey,console.log(`[Lisa.ai Diagnose] Config fetched from Control Plane. Provider=${t}`)):console.warn(`[Lisa.ai Diagnose] Control Plane ${a.reason}. Using local env for API key.`)}let r={claude:process.env.LISA_CLAUDE_MODEL||"claude-haiku-4-5",openai:process.env.LISA_OPENAI_MODEL||"gpt-4o-mini",gemini:process.env.LISA_GOOGLE_MODEL||"gemini-2.0-flash"}[t]||t;console.log(`
336
- [Lisa.ai Diagnose] Testing ${t} \u2192 model ID: ${r}`);try{let a=await ae("Reply with exactly: LISA_OK",t,n);console.log(`
337
- [Lisa.ai Diagnose] ${t} is working correctly.`),console.log(` Model response: "${a}"`)}catch(a){console.error(`
338
- [Lisa.ai Diagnose] ${t} call FAILED.`),console.error(` Error: ${a.message}`),a.cause&&console.error(` Cause: ${a.cause?.message??a.cause}`),console.error(`
339
- Common causes:`),console.error(" - API key is invalid, expired, or has no credits"),console.error(" - Model ID is deprecated (current: claude-sonnet-4-6, gemini-2.0-flash, gpt-4o-mini)"),console.error(" - Rate limit exceeded \u2014 wait a minute and retry"),console.error(" - Network/proxy issue blocking api.anthropic.com"),process.exit(1)}});Z.command("init").description("Interactively create a .lisai.json config file in the current project").option("--force","Overwrite existing .lisai.json if it exists").action(async e=>{let t=Le.resolve(process.cwd(),ne);ee.existsSync(t)&&!e.force&&(console.error(`
340
- [Lisa.ai Init] ${ne} already exists. Use --force to overwrite.`),process.exit(1));let n=At.createInterface({input:process.stdin,output:process.stdout}),s=(x,m="")=>new Promise(c=>n.question(m?`${x} [${m}]: `:`${x}: `,l=>c(l.trim()||m)));console.log(`
446
+ [Lisa.ai Warning] Control Plane is unreachable. Continuing with local CLI defaults (model=${s}, maxRetries=${t}).`);else{let c=r.config;console.log(`[Lisa.ai Agent] Dynamic Config Loaded: Provider=${c.modelProvider}, MaxRetries=${c.maxRetries}`),s=c.modelProvider,t=c.maxRetries,n=c.apiKey,c.agentEnabled===!1&&(console.log("[Lisa.ai Agent] Agent is disabled by Control Plane for this project."),process.exit(1))}}o&&(M.applyEnvDefaults(o),o.maxRetries!==void 0&&(t=o.maxRetries),o.provider&&e.model==="gemini"&&(s=o.provider),M.printSummary(o)),t<5&&console.warn(`
447
+ [Lisa.ai Warning] maxRetries is ${t}. Consider increasing to 5+ for complex projects.`);let a=Ke(o?.lisaLlm);return a.enabled&&console.log(`[Lisa.ai Config] Lisa LLM enabled: ${a.model} @ ${a.baseUrl}`),{model:s,maxRetries:t,apiKey:n,projectId:i,lisaLlmConfig:a}}var z=new qt.Command;z.name("lisa-agent").description("Lisa.ai - Autonomous CI/CD Platform Worker Agent").version(Kt.version);z.command("heal").description("Run tests and autonomously heal all failures via LLM + Memory Engine").option("-c, --command <type>","Test command to execute (auto-detected if omitted)").option("-m, --model <provider>","LLM provider to use (gemini, claude, openai)","gemini").option("-p, --project-id <id>","Control Plane Project ID").option("-t, --type <scope>","Test type to heal: unit (default), e2e, integration","unit").option("-f, --files <paths>","Target specific files (comma-separated, bypasses discovery)").option("--ci","CI mode: commit healed files, open PR, write GitHub Actions step summary").action(async e=>{le();let t=["unit","e2e","integration"],s=e.type;t.includes(s)||(console.error(`
448
+ [Lisa.ai Error] Invalid heal type '${e.type}'. Valid types: ${t.join(", ")}`),process.exit(1));let n=e.files?e.files.split(",").map(a=>a.trim()):void 0,o=e.ci===!0,i=await Fe(e);await ye(e.command,i.model,1,i.maxRetries,i.projectId,void 0,i.apiKey,s,n,o,i.lisaLlmConfig)});z.command("unit").description("Generate missing unit test specs to reach 100% coverage (auto-detects framework)").option("-c, --command <type>","Test+coverage command to execute (auto-detected if omitted)").option("-m, --model <provider>","LLM provider to use (gemini, claude, openai)","gemini").option("-p, --project-id <id>","Control Plane Project ID").option("-f, --files <paths>","Target specific files (comma-separated, bypasses discovery)").action(async e=>{le();let t=e.files?e.files.split(",").map(n=>n.trim()):void 0,s=await Fe(e);await St(e.command,s.model,1,s.maxRetries,s.projectId,s.apiKey,t,s.lisaLlmConfig)});z.command("e2e").description("Generate E2E tests via Playwright (auto-installs if none detected)").option("-c, --command <type>","E2E command to execute (auto-detected if omitted)").option("-m, --model <provider>","LLM provider to use (gemini, claude, openai)","gemini").option("-p, --project-id <id>","Control Plane Project ID").option("-f, --files <paths>","Target specific files (comma-separated, bypasses discovery)").action(async e=>{le();let t=e.files?e.files.split(",").map(n=>n.trim()):void 0,s=await Fe(e);await Ft(e.command,s.model,1,s.maxRetries,s.projectId,s.apiKey,t)});z.command("integration").description("Generate integration tests for module seams (component+service, API+DB)").option("-c, --command <type>","Test command to execute (auto-detected if omitted)").option("-m, --model <provider>","LLM provider to use (gemini, claude, openai)","gemini").option("-p, --project-id <id>","Control Plane Project ID").option("-f, --files <paths>","Target specific files (comma-separated, bypasses discovery)").action(async e=>{le();let t=e.files?e.files.split(",").map(n=>n.trim()):void 0,s=await Fe(e);await Et(e.command,s.model,1,s.maxRetries,s.projectId,s.apiKey,t)});z.command("diagnose").description("Test LLM connectivity and API key health without running the full agent").option("-m, --model <provider>","LLM provider to test (gemini, claude, openai)","claude").option("-p, --project-id <id>","Control Plane Project ID to fetch API key from").action(async e=>{le();let t=e.model,s,n=M.load(process.cwd()),o=e.projectId||n?.projectId||"local";if(o!=="local"){let r=await je(o);r.ok?(t=r.config.modelProvider,s=r.config.apiKey,console.log(`[Lisa.ai Diagnose] Config fetched from Control Plane. Provider=${t}`)):console.warn(`[Lisa.ai Diagnose] Control Plane ${r.reason}. Using local env for API key.`)}let a={claude:process.env.LISA_CLAUDE_MODEL||"claude-haiku-4-5",openai:process.env.LISA_OPENAI_MODEL||"gpt-4o-mini",gemini:process.env.LISA_GOOGLE_MODEL||"gemini-2.0-flash"}[t]||t;console.log(`
449
+ [Lisa.ai Diagnose] Testing ${t} \u2192 model ID: ${a}`);try{let r=await ge("Reply with exactly: LISA_OK",t,s);console.log(`
450
+ [Lisa.ai Diagnose] ${t} is working correctly.`),console.log(` Model response: "${r}"`)}catch(r){console.error(`
451
+ [Lisa.ai Diagnose] ${t} call FAILED.`),console.error(` Error: ${r.message}`),r.cause&&console.error(` Cause: ${r.cause?.message??r.cause}`),console.error(`
452
+ Common causes:`),console.error(" - API key is invalid, expired, or has no credits"),console.error(" - Model ID is deprecated (current: claude-sonnet-4-6, gemini-2.0-flash, gpt-4o-mini)"),console.error(" - Rate limit exceeded \u2014 wait a minute and retry"),console.error(" - Network/proxy issue blocking api.anthropic.com"),process.exit(1)}});z.command("init").description("Interactively create a .lisai.json config file in the current project").option("--force","Overwrite existing .lisai.json if it exists").action(async e=>{let t=be.resolve(process.cwd(),de);re.existsSync(t)&&!e.force&&(console.error(`
453
+ [Lisa.ai Init] ${de} already exists. Use --force to overwrite.`),process.exit(1));let s=Yt.createInterface({input:process.stdin,output:process.stdout}),n=(L,m="")=>new Promise(d=>s.question(m?`${L} [${m}]: `:`${L}: `,l=>d(l.trim()||m)));console.log(`
341
454
  Lisa.ai Project Setup \u2014 press Enter to accept the default shown in [ ]
342
- `);let o=await s("Control Plane project ID (leave empty for local-only mode)",""),i=await s("LLM provider (gemini / claude / openai)","gemini"),r=await s("Testing framework (jest / vitest / mocha / karma / jasmine \u2014 leave empty to auto-detect)",""),a=await s("E2E framework (playwright / cypress \u2014 leave empty for Playwright default)",""),d=await s("Test types to generate (unit / integration / e2e \u2014 comma-separated)","unit"),p=await s("Test command (leave empty for auto-discovery)",""),u=await s("E2E command (leave empty for auto-discovery)",""),L=await s("Max heal retries","5");n.close();let f={...o?{projectId:o}:{},provider:i,testTypes:d.split(",").map(x=>x.trim()).filter(Boolean),maxRetries:parseInt(L,10)||5,skipFiles:[],skipDirs:[],skipPaths:[]};r&&(f.testingFramework=r),a&&(f.e2eFramework=a),p&&(f.testCommand=p),u&&(f.e2eCommand=u),ee.writeFileSync(t,JSON.stringify(f,null,2)+`
455
+ `);let o=await n("Control Plane project ID (leave empty for local-only mode)",""),i=await n("LLM provider (gemini / claude / openai)","gemini"),a=await n("Testing framework (jest / vitest / mocha / karma / jasmine \u2014 leave empty to auto-detect)",""),r=await n("E2E framework (playwright / cypress \u2014 leave empty for Playwright default)",""),c=await n("Test types to generate (unit / integration / e2e \u2014 comma-separated)","unit"),u=await n("Test command (leave empty for auto-discovery)",""),p=await n("E2E command (leave empty for auto-discovery)",""),$=await n("Max heal retries","5");s.close();let g={...o?{projectId:o}:{},provider:i,testTypes:c.split(",").map(L=>L.trim()).filter(Boolean),maxRetries:parseInt($,10)||5,skipFiles:[],skipDirs:[],skipPaths:[]};a&&(g.testingFramework=a),r&&(g.e2eFramework=r),u&&(g.testCommand=u),p&&(g.e2eCommand=p),re.writeFileSync(t,JSON.stringify(g,null,2)+`
343
456
  `,"utf-8"),console.log(`
344
- Created ${ne}:
345
- `),console.log(JSON.stringify(f,null,2)),console.log(`
457
+ Created ${de}:
458
+ `),console.log(JSON.stringify(g,null,2)),console.log(`
346
459
  Tip: add "model" to pin a specific model ID (e.g. "claude-sonnet-4-6").`),console.log(` Edit skipFiles / skipDirs / skipPaths to exclude files from test analysis.
347
- `)});function Xt(e,t){return`# Lisa.ai Auto-Heal Workflow
460
+ `)});function xs(e,t){return`# Lisa.ai Auto-Heal Workflow
348
461
  # Generated by: lisa-agent init-ci
349
462
  # Docs: https://www.npmjs.com/package/@lisa.ai/agent
350
463
 
@@ -385,9 +498,9 @@ jobs:
385
498
  ${t}: \${{ secrets.${t} }}
386
499
  GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
387
500
  run: lisa-agent heal --model ${e} --ci
388
- `}Z.command("init-ci").description("Generate a GitHub Actions workflow file for Lisa.ai CI/CD healing").option("--force","Overwrite existing workflow file").option("-m, --model <provider>","LLM provider for the workflow (gemini, claude, openai)","gemini").action(async e=>{oe();let t=Le.resolve(process.cwd(),".github","workflows"),n=Le.join(t,"lisa-heal.yml");ee.existsSync(n)&&!e.force&&(console.error(`
389
- [Lisa.ai Init-CI] .github/workflows/lisa-heal.yml already exists. Use --force to overwrite.`),process.exit(1));let s={gemini:"GOOGLE_GENERATIVE_AI_API_KEY",claude:"ANTHROPIC_API_KEY",openai:"OPENAI_API_KEY"},o=e.model||"gemini",i=s[o]||"GOOGLE_GENERATIVE_AI_API_KEY",r=Xt(o,i);ee.mkdirSync(t,{recursive:!0}),ee.writeFileSync(n,r,"utf-8"),console.log(`
501
+ `}z.command("init-ci").description("Generate a GitHub Actions workflow file for Lisa.ai CI/CD healing").option("--force","Overwrite existing workflow file").option("-m, --model <provider>","LLM provider for the workflow (gemini, claude, openai)","gemini").action(async e=>{le();let t=be.resolve(process.cwd(),".github","workflows"),s=be.join(t,"lisa-heal.yml");re.existsSync(s)&&!e.force&&(console.error(`
502
+ [Lisa.ai Init-CI] .github/workflows/lisa-heal.yml already exists. Use --force to overwrite.`),process.exit(1));let n={gemini:"GOOGLE_GENERATIVE_AI_API_KEY",claude:"ANTHROPIC_API_KEY",openai:"OPENAI_API_KEY"},o=e.model||"gemini",i=n[o]||"GOOGLE_GENERATIVE_AI_API_KEY",a=xs(o,i);re.mkdirSync(t,{recursive:!0}),re.writeFileSync(s,a,"utf-8"),console.log(`
390
503
  Created: .github/workflows/lisa-heal.yml`),console.log(`
391
504
  Required repository secret: ${i}`),console.log(" Set it in: GitHub repo > Settings > Secrets and variables > Actions"),console.log(`
392
505
  The workflow will:`),console.log(" 1. Run on every push to main/master/develop"),console.log(" 2. Install dependencies + run lisa-agent heal --ci"),console.log(" 3. If tests fail and Lisa heals them, open a PR automatically"),console.log(` 4. If all tests pass or nothing can be healed, report status
393
- `)});Z.command("audit").description("Scan for npm vulnerabilities and auto-fix where safe; consult LLM for manual fixes").option("-m, --model <provider>","LLM provider to use (gemini, claude, openai)","gemini").option("-p, --project-id <id>","Control Plane Project ID").action(async e=>{oe();let t=e.model,n,s=I.load(process.cwd()),o=e.projectId||s?.projectId||"local";if(o!=="local"){let i=await xe(o);i.ok?(t=i.config.modelProvider,n=i.config.apiKey,console.log(`[Lisa.ai Audit] Config fetched from Control Plane. Provider=${t}`)):console.warn(`[Lisa.ai Audit] Control Plane ${i.reason}. Using local env for API key.`)}await Ft(t,o,n)});Z.parse(process.argv);
506
+ `)});z.command("audit").description("Scan for npm vulnerabilities and auto-fix where safe; consult LLM for manual fixes").option("-m, --model <provider>","LLM provider to use (gemini, claude, openai)","gemini").option("-p, --project-id <id>","Control Plane Project ID").action(async e=>{le();let t=e.model,s,n=M.load(process.cwd()),o=e.projectId||n?.projectId||"local";if(o!=="local"){let i=await je(o);i.ok?(t=i.config.modelProvider,s=i.config.apiKey,console.log(`[Lisa.ai Audit] Config fetched from Control Plane. Provider=${t}`)):console.warn(`[Lisa.ai Audit] Control Plane ${i.reason}. Using local env for API key.`)}await Rt(t,o,s)});z.command("chat").description("Interactive chat \u2014 ask questions, heal files, run tests").option("-m, --model <provider>","LLM provider to use (gemini, claude, openai)","gemini").option("-p, --project-id <id>","Control Plane Project ID").action(async e=>{le();let t=await Fe(e);await Gt(t.model,t.projectId,t.apiKey,t.lisaLlmConfig)});z.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lisa.ai/agent",
3
- "version": "2.10.0",
3
+ "version": "2.11.1",
4
4
  "description": "Lisa.ai Autonomous CI/CD Worker Agent",
5
5
  "main": "dist/index.js",
6
6
  "bin": {