@lisa.ai/agent 2.9.1 → 2.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +46 -44
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var
|
|
2
|
+
"use strict";var bt=Object.create;var Ue=Object.defineProperty;var Et=Object.getOwnPropertyDescriptor;var jt=Object.getOwnPropertyNames;var Ct=Object.getPrototypeOf,At=Object.prototype.hasOwnProperty;var Ft=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var Pt=(t,e,s,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of jt(e))!At.call(t,i)&&i!==s&&Ue(t,i,{get:()=>e[i],enumerable:!(n=Et(e,i))||n.enumerable});return t};var E=(t,e,s)=>(s=t!=null?bt(Ct(t)):{},Pt(e||!t||!t.__esModule?Ue(s,"default",{value:t,enumerable:!0}):s,t));var vt=Ft((Zs,Bt)=>{Bt.exports={name:"@lisa.ai/agent",version:"2.9.2",description:"Lisa.ai Autonomous CI/CD Worker Agent",main:"dist/index.js",bin:{"lisa-agent":"dist/index.js"},files:["dist/"],scripts:{build:"tsup",typecheck:"tsc --noEmit",start:"node dist/index.js",dev:"ts-node src/index.ts",prepublishOnly:"npm run typecheck && npm run build"},keywords:["lisa","ci","cd","agent"],author:"Lisa.ai",license:"ISC",dependencies:{"@ai-sdk/anthropic":"^3.0.46","@ai-sdk/google":"^3.0.30","@ai-sdk/openai":"^3.0.30","@octokit/rest":"^22.0.1",ai:"^6.0.94",commander:"^11.1.0",dotenv:"^17.3.1","simple-git":"^3.31.1"},devDependencies:{"@types/jest":"^30.0.0","@types/node":"^20.0.0","ts-node":"^10.9.1",tsup:"^8.5.1",typescript:"^5.0.0"}}});var xt=require("commander"),be=E(require("fs")),$t=E(require("path")),Lt=E(require("readline"));var Oe=E(require("dotenv"));Oe.config({quiet:!0});async function ge(t){let e=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3000";try{let s=`${e}/api/config/${t}`;console.log(`[Lisa.ai Agent] Fetching dynamic configuration from ${s}...`);let n=await fetch(s);if(!n.ok){let o=n.status===404?"not_found":"unreachable";return console.warn(o==="not_found"?`[Lisa.ai Agent Warning] Control Plane returned 404. Project '${t}' not found.`:`[Lisa.ai Agent Warning] Control Plane returned ${n.status}. Falling back to local defaults.`),{ok:!1,reason:o}}return{ok:!0,config:await n.json()}}catch{return console.warn(`[Lisa.ai Agent Warning] Failed to reach Control Plane (${e}). Using local fallback configuration.`),{ok:!1,reason:"unreachable"}}}var ot=require("child_process"),_=E(require("fs")),V=E(require("path"));var S=E(require("path")),T=E(require("fs"));function fe(t){return t.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,"")}function Z(t){let e=fe(t),s=[...e.matchAll(/Executed\s+(\d+)\s+of\s+(\d+)(?:\s+\((\d+)\s+FAILED\))?/g)];if(s.length>0){let u=[...s].reverse().find(m=>m[3]!==void 0)??s[s.length-1],p=parseInt(u[2]),w=u[3]?parseInt(u[3]):0,h=parseInt(u[1])-w;return{total:p,passed:h,failed:w}}let n=e.match(/Tests:\s+(?:(\d+)\s+failed,?\s*)?(?:(\d+)\s+passed,?\s*)?(\d+)\s+total/);if(n){let u=parseInt(n[3]),p=n[1]?parseInt(n[1]):0,w=n[2]?parseInt(n[2]):0;return{total:u,passed:w,failed:p}}let i=e.match(/(\d+)\s+failed/),o=e.match(/(\d+)\s+passed/);if(o||i){let u=i?parseInt(i[1]):0,p=o?parseInt(o[1]):0,w=e.match(/(\d+)\s+skipped/),h=w?parseInt(w[1]):0;return{total:p+u+h,passed:p,failed:u}}let r=e.match(/Tests\s+(?:(\d+)\s+failed\s*\|?\s*)?(\d+)\s+passed\s*\((\d+)\)/);if(r){let u=parseInt(r[3]),p=r[1]?parseInt(r[1]):0,w=parseInt(r[2]);return{total:u,passed:w,failed:p}}let a=e.match(/(\d+)\s+passing/),g=e.match(/(\d+)\s+failing/);if(a){let u=parseInt(a[1]),p=g?parseInt(g[1]):0;return{total:u+p,passed:u,failed:p}}return null}function je(t,e=process.cwd(),s=[]){let n=t.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,""),i=s.map(l=>S.resolve(e,l)),o=new Map,r=new Set,a=/(?:Chrome|Firefox|Safari)(?:\s+Headless)?\s+[\d.]+(?:\s+[\d.]+)*\s+\([^)]+\)\s+(.+?)\s+FAILED/g,g;for(;(g=a.exec(n))!==null;){let d=g[1].trim().split(" ");for(let v=1;v<Math.min(d.length,6);v++){let k=d.slice(0,v).join(" ");if(r.has(k))continue;r.add(k);let f=Ce(k,e,i);if(f){let c=S.resolve(f);o.has(c)||o.set(c,S.relative(e,f));break}}}let u=/^FAIL\s+([a-zA-Z0-9_./-\\]+\.(?:spec|test)\.(ts|tsx|js|jsx))/gm,p;for(;(p=u.exec(n))!==null;){let l=p[1],d=S.isAbsolute(l)?l:S.resolve(e,l);!i.includes(d)&&T.existsSync(d)&&!o.has(d)&&o.set(d,S.relative(e,d))}let w=/\[(?:chromium|firefox|webkit)\]\s+[>›]\s+([^\s:]+\.(?:spec|test)\.[tj]sx?):\d+:\d+\s+[>›]/g,h;for(;(h=w.exec(n))!==null;){let l=h[1],d=S.isAbsolute(l)?l:S.resolve(e,l);!i.includes(d)&&T.existsSync(d)&&!o.has(d)&&o.set(d,S.relative(e,d))}let m=/Running:\s+([^\s]+\.(?:cy|spec|test)\.[tj]sx?)/g,y;for(;(y=m.exec(n))!==null;){let l=y[1],d=S.isAbsolute(l)?l:S.resolve(e,l);if(!i.includes(d)&&T.existsSync(d)&&!o.has(d)){let v=l.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");new RegExp(v+"\\s+\\d+\\s+\\d+\\s+([1-9]\\d*)").test(n)&&o.set(d,S.relative(e,d))}}return[...o.values()]}function me(t,e=[],s=process.cwd()){let n=t.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,""),i=e.map(l=>S.resolve(l));{let l=/(?:Chrome|Firefox|Safari)(?:\s+Headless)?\s+[\d.]+(?:\s+[\d.]+)*\s+\([^)]+\)\s+(.+?)\s+FAILED/g,d=new Set,v;for(;(v=l.exec(n))!==null;){let f=v[1].trim().split(" ");for(let c=1;c<Math.min(f.length,5);c++){let x=f.slice(0,c).join(" ");if(d.has(x))continue;d.add(x);let L=Ce(x,s,i);if(L)return S.relative(s,L)}}}let o=/([a-zA-Z]:[a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue)|[a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue))(?:\s*[:(])/g,r;for(;(r=o.exec(n))!==null;){let l=r[1];if(l){if(/(?:^|[/\\])(node_modules|dist|build)[/\\]/.test(l))continue;let d=S.isAbsolute(l)?l:S.resolve(s,l);if(!i.includes(d)&&T.existsSync(d))return l}}let a=/FAIL\s+([a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue))/gi,g;for(;(g=a.exec(n))!==null;){let l=g[1],d=S.isAbsolute(l)?l:S.resolve(s,l);if(!i.includes(d)&&T.existsSync(d))return l}{let l=/\[(?:chromium|firefox|webkit)\]\s+[>›]\s+([^\s:]+\.(?:spec|test)\.[tj]sx?):\d+:\d+\s+[>›]/g,d;for(;(d=l.exec(n))!==null;){let v=d[1],k=S.isAbsolute(v)?v:S.resolve(s,v);if(!i.includes(k)&&T.existsSync(k))return v}}let u=/([a-zA-Z]:[a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue)|[a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue))/g,p;for(;(p=u.exec(n))!==null;){let l=p[1];if(l){if(/(?:^|[/\\])(node_modules|dist|build)[/\\]/.test(l))continue;let d=S.isAbsolute(l)?l:S.resolve(s,l);if(!i.includes(d)&&T.existsSync(d))return l}}let w=/\b([A-Z][a-zA-Z0-9]{3,})\b/g,h,m=new Set,y=["Error","TypeError","SyntaxError","ReferenceError","RangeError","Object","Boolean","String","Number","Array","NullInjectorError","NullInjector","R3Injector","ChainedInjector","Module","Component","Standalone","Application","App2","TestBed","UserContext","Chrome","ChromeHeadless","Karma","Launching","Starting","Headless","Connected","Executed","INFO","SUCCESS","FAILED","Building","Initial","Names","Lazy","Source","Find","NotFound","NG0201","Windows","Linux","Macintosh","Users","AppData","Local","Temp","Process","Unexpected","Expected","Validation","Directory","Configuration","Documentation"];for(;(h=w.exec(n))!==null;){let l=h[1];if(!l||y.includes(l)||m.has(l)||/^[A-Z][A-Z0-9]{5,11}$/.test(l)||/^[A-Za-z0-9]{16,}$/.test(l))continue;m.add(l);let d=_e(l,s,i);if(d)return S.relative(s,d)}return null}function Ce(t,e,s){if(!T.existsSync(e))return null;let n=T.readdirSync(e);for(let i of n){let o=S.join(e,i);if(["node_modules","dist","build",".git",".angular"].includes(i))continue;let r;try{r=T.statSync(o)}catch{continue}if(r.isDirectory()){let a=Ce(t,o,s);if(a)return a}else if(/\.(spec|test)\.(ts|tsx|js|jsx)$/.test(i)){if(s.includes(S.resolve(o)))continue;try{let a=T.readFileSync(o,"utf8");if(a.includes(`describe('${t}'`)||a.includes(`describe("${t}"`))return o}catch{continue}}}return null}function _e(t,e,s){if(!T.existsSync(e))return null;let n=T.readdirSync(e);for(let i of n){let o=S.join(e,i);if(i==="node_modules"||i==="dist"||i==="build"||i===".git"||i===".angular")continue;let r;try{r=T.statSync(o)}catch{continue}if(r.isDirectory()){let a=_e(t,o,s);if(a)return a}else if(i.match(/\.(ts|tsx|js|jsx|vue)$/)){let a=T.readFileSync(o,"utf8");if(a.includes(`class ${t}`)||a.includes(`function ${t}`)||a.includes(`const ${t}`)||a.includes(`let ${t}`)||a.includes(`exports.${t}`)||a.includes(`module.exports.${t}`)){let g=o,u=S.extname(o),p=o.slice(0,-u.length);if(!i.includes(".spec.")&&!i.includes(".test.")){let w=[`${p}.spec${u}`,`${p}.test${u}`,`${p}.spec.js`,`${p}.test.js`];for(let h of w)if(T.existsSync(h)){g=h;break}}if(!s.includes(S.resolve(g)))return g}}}return null}var de=require("ai"),Ge=require("@ai-sdk/openai"),He=require("@ai-sdk/anthropic"),Be=require("@ai-sdk/google"),qe=E(require("dotenv"));qe.config({quiet:!0});var Ae=15e3;function We(t,e){return t.length<=Ae?t:(console.warn(`[Lisa.ai LLM] ${e} is ${t.length} chars \u2014 truncating to ${Ae} to stay within context window.`),t.slice(0,Ae)+`
|
|
3
3
|
|
|
4
4
|
// ... (truncated)`)}function Ye(t,e){if(!/\b(describe|it\(|test\(|expect\(|beforeEach|afterEach|suite|assert\.|cy\.)\b/.test(t))throw new Error(`LLM returned a non-code response for ${e} (possible context overflow). Skipping to prevent file corruption.`)}function Ve(t){if(!t||t.length===0)return"";let e={unit:"unit tests \u2014 test every function/method in complete isolation, mocking ALL external dependencies, services, and I/O",integration:"integration tests \u2014 test how modules work together using real service/repository layers; for HTTP routes use supertest or equivalent",e2e:"end-to-end tests \u2014 simulate complete user flows using the configured e2e framework (Playwright/Cypress); cover the full path from HTTP request to response or UI action to assertion"};return`5. Test scope \u2014 generate ALL of the following test types for this file:
|
|
5
5
|
${t.map(n=>` \u2022 ${e[n]??n}`).join(`
|
|
@@ -62,16 +62,16 @@ A source file lacks 100% test coverage. You must update its existing test suite
|
|
|
62
62
|
|
|
63
63
|
--- Existing Test Suite (`+s+`) ---
|
|
64
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:h}=await(0,de.generateText)({model:g,prompt:w}),m=h.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/),y=m?m[1].trim():h.trim();return Ye(y,t),y}async function se(t,e,s){let n=he(e,s),{text:i}=await(0,de.generateText)({model:n,prompt:t}),o=i.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);return o?o[1].trim():i.trim()}var Ze=
|
|
65
|
+
`+p,{text:h}=await(0,de.generateText)({model:g,prompt:w}),m=h.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/),y=m?m[1].trim():h.trim();return Ye(y,t),y}async function se(t,e,s){let n=he(e,s),{text:i}=await(0,de.generateText)({model:n,prompt:t}),o=i.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);return o?o[1].trim():i.trim()}var Ze=E(require("simple-git")),Je=require("@octokit/rest"),zt=require("dotenv/config");async function Fe(t){console.log(`
|
|
66
66
|
[Lisa.ai PR Engine] Initializing Pull Request for ${t}...`);let e=(0,Ze.default)(),n=`lisa-fix/build-error-${new Date().getTime()}`,i=`fix: automated auto-heal by Lisa.ai for ${t}`,o="main";try{o=(await e.revparse(["--abbrev-ref","HEAD"])).trim()}catch{}try{await e.addConfig("user.name","Lisa.ai"),await e.addConfig("user.email","lisa@lisa.ai"),await e.checkoutLocalBranch(n),await e.add(t),await e.commit(i),console.log(`[Lisa.ai PR Engine] Committed changes to branch ${n}`),console.log("[Lisa.ai PR Engine] Pushing branch to remote origin..."),await e.push("origin",n,{"--set-upstream":null});let r=new Je.Octokit({auth:process.env.GITHUB_TOKEN}),a=process.env.GITHUB_REPOSITORY;if(a&&process.env.GITHUB_TOKEN){let[g,u]=a.split("/");console.log(`[Lisa.ai PR Engine] Opening Pull Request against ${g}/${u}...`);let p=await r.rest.pulls.create({owner:g,repo:u,title:i,body:`### Lisa.ai Auto-Healed Pull Request
|
|
67
67
|
This PR was automatically generated by Lisa.ai to resolve a failing compilation/build step.
|
|
68
68
|
|
|
69
69
|
**Healed File:** \`${t}\`
|
|
70
70
|
|
|
71
71
|
Please review the changes.`,head:n,base:o});console.log(`\u2705 [Lisa.ai PR Engine] Pull Request created successfully: ${p.data.html_url}`)}else console.log("\u26A0\uFE0F [Lisa.ai PR Engine] GITHUB_TOKEN or GITHUB_REPOSITORY not set. Skipping GitHub Pull Request creation.")}catch(r){console.error(`
|
|
72
|
-
\u{1F6A8} [Lisa.ai PR Engine Error] Failed to create Pull Request:`,r.message)}finally{try{await e.checkout(o)}catch{}}}var Qe=
|
|
72
|
+
\u{1F6A8} [Lisa.ai PR Engine Error] Failed to create Pull Request:`,r.message)}finally{try{await e.checkout(o)}catch{}}}var Qe=E(require("dotenv"));Qe.config({quiet:!0});async function I(t){let s=`${process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3000"}/api/telemetry`;fetch(s,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}).catch(n=>{console.debug(`[Lisa.ai Agent Debug] Failed to report telemetry: ${n.message}`)})}var q=E(require("fs")),U=E(require("path"));var ye=E(require("fs")),Xe=E(require("path")),ee=".lisai.json",R=class{static _config=null;static _loaded=!1;static load(e=process.cwd()){if(this._loaded)return this._config;this._loaded=!0;let s=Xe.resolve(e,ee);if(!ye.existsSync(s))return null;try{let n=ye.readFileSync(s,"utf-8");return this._config=JSON.parse(n),console.log(`[Lisa.ai Config] Loaded project config from ${ee}`),this._config}catch(n){return console.warn(`[Lisa.ai Config] Warning: ${ee} found but failed to parse \u2014 ${n.message}. Using defaults.`),null}}static get(){return this._config}static applyEnvDefaults(e){if(!e.model)return;let s={claude:"LISA_CLAUDE_MODEL",openai:"LISA_OPENAI_MODEL",gemini:"LISA_GOOGLE_MODEL"},n=e.provider;n&&s[n]&&!process.env[s[n]]&&(process.env[s[n]]=e.model,console.log(`[Lisa.ai Config] Model override applied: ${n} \u2192 ${e.model}`))}static printSummary(e){let s=[];e.provider&&s.push(`provider=${e.provider}`),e.model&&s.push(`model=${e.model}`),e.testingFramework&&s.push(`testingFramework=${e.testingFramework}`),e.testTypes?.length&&s.push(`testTypes=[${e.testTypes.join(",")}]`),e.maxRetries!==void 0&&s.push(`maxRetries=${e.maxRetries}`),e.testCommand&&s.push(`testCommand="${e.testCommand}"`),e.e2eFramework&&s.push(`e2eFramework=${e.e2eFramework}`),e.e2eCommand&&s.push(`e2eCommand="${e.e2eCommand}"`),s.length&&console.log(`[Lisa.ai Config] Settings: ${s.join(", ")}`),e.skipFiles?.length&&console.log(`[Lisa.ai Config] Extra skip files : ${e.skipFiles.join(", ")}`),e.skipDirs?.length&&console.log(`[Lisa.ai Config] Extra skip dirs : ${e.skipDirs.join(", ")}`),e.skipPaths?.length&&console.log(`[Lisa.ai Config] Extra skip paths : ${e.skipPaths.join(", ")}`)}};var Y=class{static scanRepository(e=process.cwd()){let s=U.resolve(e,"package.json"),n={type:"unknown",testingFramework:"none"};if(!q.existsSync(s))return console.warn(`[Lisa.ai AutoDiscovery] No package.json found at ${s}. Defaulting to Generic Node.`),n.type="node",n;let i=JSON.parse(q.readFileSync(s,"utf8")),o={...i.dependencies||{},...i.devDependencies||{}},r=i.scripts||{};return o["@angular/core"]?n.type="angular":o.react?n.type="react":o.vue?n.type="vue":n.type="node",o.jest||r.test?.includes("jest")?n.testingFramework="jest":o.karma||r.test?.includes("karma")||r.test?.includes("ng test")?n.testingFramework="karma":(o.vitest||r.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":r.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(e,s=[]){let n=[];if(!q.existsSync(e))return n;let i=q.readdirSync(e),o=R.get(),r=o?.skipDirs??[],a=o?.skipFiles??[],g=o?.skipPaths??[],u=["node_modules","dist","build",".git",".angular","coverage","public","assets",...r],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"],w=[/^\./,/\.config\.(js|ts|mjs|cjs)$/,/\.conf\.(js|ts|mjs|cjs)$/];for(let h of i){let m=U.join(e,h);if(u.includes(h))continue;let y;try{y=q.statSync(m)}catch{continue}if(y.isDirectory())n.push(...this.findUntestedFiles(m,s));else if(h.match(/\.(ts|tsx|js|jsx|vue)$/)&&!h.includes(".spec.")&&!h.includes(".test.")){if(p.includes(h)||a.includes(h)||w.some(f=>f.test(h)))continue;let l=U.extname(h),d=h.slice(0,-l.length);if(![U.join(e,`${d}.spec${l}`),U.join(e,`${d}.test${l}`),U.join(e,`${d}.spec.js`),U.join(e,`${d}.test.js`),U.join(e,`${d}.spec.ts`),U.join(e,`${d}.test.ts`)].some(f=>q.existsSync(f))){let f=U.relative(process.cwd(),m);if(g.some(c=>f.startsWith(c)))continue;s.includes(f)||n.push(f)}}}return n}};var et=require("child_process"),ne=class{static async installMissingFramework(e,s=process.cwd()){if(e.testingFramework!=="none")return e;console.log(`
|
|
73
73
|
[Lisa.ai Auto-Installer] \u{1F6A8} No testing framework detected for ${e.type} architecture.`),console.log("[Lisa.ai Auto-Installer] \u{1FA84} Initiating Zero-Config Installation Protocol...");let n="",i="none";switch(e.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",i="karma",e.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",i="jest",e.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",i="vitest",e.suggestedTestCommand="npx vitest run --coverage";break}if(n){console.log(`[Lisa.ai Executing] ${n}`);let o=n.split(" "),r=process.platform==="win32"?`${o[0]}.cmd`:o[0];return(0,et.spawnSync)(r,o.slice(1),{cwd:s,stdio:"inherit"}).status!==0&&(console.error(`
|
|
74
|
-
\u274C [Lisa.ai Auto-Installer] Failed to construct dynamic testing environment.`),process.exit(1)),console.log("\u2705 [Lisa.ai Auto-Installer] Framework successfully injected."),e.testingFramework=i,e}return e}};var we=
|
|
74
|
+
\u274C [Lisa.ai Auto-Installer] Failed to construct dynamic testing environment.`),process.exit(1)),console.log("\u2705 [Lisa.ai Auto-Installer] Framework successfully injected."),e.testingFramework=i,e}return e}};var we=E(require("fs")),Pe=E(require("path"));var oe=class{static async provisionConfigurationFiles(e,s,n,i=process.cwd()){if(e.testingFramework==="none")return;if(console.log(`
|
|
75
75
|
[Lisa.ai Auto-Generator] \u{1FA84} Analyzing ${e.type} architecture to generate ${e.testingFramework} configuration specs...`),["jest.config.js","jest.config.ts","karma.conf.js","vitest.config.ts","vitest.config.js"].some(g=>we.existsSync(Pe.join(i,g)))){console.log("[Lisa.ai Auto-Generator] Setup file detected. Bypassing initialization.");return}let a=`You are an expert ${e.type} architect.
|
|
76
76
|
Write a production-ready '${e.testingFramework}' configuration file for a standard '${e.type}' application.
|
|
77
77
|
The application resides in the root directory.
|
|
@@ -81,7 +81,7 @@ Requirements:
|
|
|
81
81
|
2. Ensure standard transpilation (ts-jest for jest, or standard karma-webpack/karma-typescript implementations).
|
|
82
82
|
3. Do NOT wrap your response in markdown formatting (no \`\`\`javascript).
|
|
83
83
|
4. Return ONLY the raw code string block.`;try{let g=await se(a,s,n),u="";e.testingFramework==="jest"&&(u="jest.config.js"),e.testingFramework==="karma"&&(u="karma.conf.js"),e.testingFramework==="vitest"&&(u="vitest.config.ts");let p=Pe.join(i,u);we.writeFileSync(p,g,"utf-8"),console.log(`\u2705 [Lisa.ai Auto-Generator] Natively wrote ${u} to repository root.`)}catch(g){console.error(`
|
|
84
|
-
\u274C [Lisa.ai Auto-Generator] Failed to author configuration file: ${g.message}`),process.exit(1)}}};var tt=
|
|
84
|
+
\u274C [Lisa.ai Auto-Generator] Failed to author configuration file: ${g.message}`),process.exit(1)}}};var tt=E(require("dotenv"));tt.config({quiet:!0});function ie(t){let e=t.toLowerCase();return e.includes("karma")||e.includes("ng test")?"karma":e.includes("jest")?"jest":e.includes("vitest")?"vitest":e.includes("playwright")?"playwright":e.includes("cypress")?"cypress":e.includes("mocha")?"mocha":"unknown"}async function st(t,e,s){if(s==="local")return[];let n=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3000";try{let i=new URLSearchParams({projectId:s,errorLog:t.slice(0,1e3),framework:e}),o=await fetch(`${n}/api/memory/lookup?${i}`);return o.ok?await o.json():[]}catch{return[]}}async function nt(t,e,s,n){if(n==="local")return;let i=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3000";try{await fetch(`${i}/api/memory/record`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectId:n,errorLog:t.slice(0,1e3),fixHint:e.slice(0,2e3),framework:s})}),console.log("[Lisa.ai Memory] Fix pattern recorded to Control Plane.")}catch(o){console.debug(`[Lisa.ai Agent Debug] Failed to record memory pattern: ${o.message}`)}}var M=E(require("fs")),O=E(require("path")),Te=require("child_process");function ve(t){if(M.existsSync(O.join(t,"playwright.config.ts"))||M.existsSync(O.join(t,"playwright.config.js")))return{framework:"playwright",configFile:M.existsSync(O.join(t,"playwright.config.ts"))?"playwright.config.ts":"playwright.config.js",command:"npx playwright test",installed:!0};if(M.existsSync(O.join(t,"cypress.config.ts"))||M.existsSync(O.join(t,"cypress.config.js"))||M.existsSync(O.join(t,"cypress.json")))return{framework:"cypress",configFile:M.existsSync(O.join(t,"cypress.config.ts"))?"cypress.config.ts":M.existsSync(O.join(t,"cypress.config.js"))?"cypress.config.js":"cypress.json",command:"npx cypress run",installed:!0};let e=O.join(t,"package.json");if(M.existsSync(e)){let s=JSON.parse(M.readFileSync(e,"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 xe(t){console.log("[Lisa.ai E2E] No E2E framework detected. Installing Playwright...");try{(0,Te.execSync)("npm install -D @playwright/test",{cwd:t,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,Te.execSync)("npx playwright install --with-deps chromium",{cwd:t,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.")}M.existsSync(O.join(t,"playwright.config.ts"))||(M.writeFileSync(O.join(t,"playwright.config.ts"),`import { defineConfig } from '@playwright/test';
|
|
85
85
|
|
|
86
86
|
export default defineConfig({
|
|
87
87
|
testDir: './e2e',
|
|
@@ -103,7 +103,7 @@ export default defineConfig({
|
|
|
103
103
|
`)){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(`
|
|
104
104
|
`),console.log(` \u2717 ${p.trim()}`)))}}),i.stderr?.on("data",a=>{r+=a.toString()}),i.on("close",a=>{a===0?s({stdout:o,stderr:r}):n({message:`Process exited with code ${a}`,stdout:o,stderr:r})})})}function Rt(t,e,s){if(!e.match(/\.(spec|test)\.(ts|js|tsx|jsx|vue)$/))return;if((s||"").toLowerCase().includes("ng test")||(s||"").toLowerCase().includes("karma"))try{_.writeFileSync(t,`// Quarantined by Lisa.ai \u2014 could not be automatically healed
|
|
105
105
|
describe('', () => {});
|
|
106
|
-
`,"utf-8"),console.log(" \u{1F6A8} Replaced with empty stub (Angular requires valid TS).")}catch{}else try{_.renameSync(t,t+".broken"),console.log(` \u{1F6A8} Renamed to ${V.basename(t)}.broken to skip in test runner.`)}catch{}}async function Mt(t,e,s,n,i,o,r,a,g,u){let p=1,w=!1,h=s,m,y="",l="",d=Tt(e),k=(await st(s,o,g)).filter(f=>f.confidence>=.5).map(f=>f.fixHint);for(k.length>0&&console.log(` [Memory] ${k.length} proven pattern(s) found \u2014 injecting as LLM hint.`);p<=a&&!w;){console.log(` [Attempt ${p}/${a}] Requesting fix from ${r}...`);let f=_.readFileSync(e,"utf-8"),c;for(let x=0;x<=3;x++)try{c=await Ke(t,f,h,r,u,m,d,k);break}catch(L){let $=(L?.lastError??L)?.statusCode;if(($===529||$===500)&&x<3){let
|
|
106
|
+
`,"utf-8"),console.log(" \u{1F6A8} Replaced with empty stub (Angular requires valid TS).")}catch{}else try{_.renameSync(t,t+".broken"),console.log(` \u{1F6A8} Renamed to ${V.basename(t)}.broken to skip in test runner.`)}catch{}}async function Mt(t,e,s,n,i,o,r,a,g,u){let p=1,w=!1,h=s,m,y="",l="",d=Tt(e),k=(await st(s,o,g)).filter(f=>f.confidence>=.5).map(f=>f.fixHint);for(k.length>0&&console.log(` [Memory] ${k.length} proven pattern(s) found \u2014 injecting as LLM hint.`);p<=a&&!w;){console.log(` [Attempt ${p}/${a}] Requesting fix from ${r}...`);let f=_.readFileSync(e,"utf-8"),c;for(let x=0;x<=3;x++)try{c=await Ke(t,f,h,r,u,m,d,k);break}catch(L){let $=(L?.lastError??L)?.statusCode;if(($===529||$===500)&&x<3){let b=30*(x+1);console.warn(` \u26A0\uFE0F LLM overloaded (HTTP ${$}). Waiting ${b}s...`),await new Promise(A=>setTimeout(A,b*1e3))}else{console.error(` \u{1F6A8} LLM API failed: ${L?.message??String(L)}`);break}}if(c===void 0)break;_.writeFileSync(e,c,"utf-8"),y=`### Auto-Heal Analysis
|
|
107
107
|
**Error:**
|
|
108
108
|
\`\`\`bash
|
|
109
109
|
${h.slice(0,2e3)}
|
|
@@ -114,7 +114,7 @@ ${h.slice(0,2e3)}
|
|
|
114
114
|
${c}
|
|
115
115
|
\`\`\``;try{await Me(i,!1),console.log(" \u2705 Isolated verification passed."),w=!0,l=c}catch(x){let L=x.stdout?x.stdout.toString():"";h=(x.stderr?x.stderr.toString():"")+`
|
|
116
116
|
`+L+`
|
|
117
|
-
`+(x.message||""),console.log(" \u274C Isolated verification failed.");let
|
|
117
|
+
`+(x.message||""),console.log(" \u274C Isolated verification failed.");let b=Re(h);if(b&&console.log(b),it(h,3).forEach(A=>console.log(` \u2717 ${A}`)),(n||"").toLowerCase().includes("ng test")||(n||"").toLowerCase().includes("karma")){let A=h.includes(V.basename(t)+":"),z=/ FAILED/.test(h);if(!A&&!z){console.log(" \u2705 Fix verified (build error is from other spec files, not this one)."),w=!0,l=c;continue}}m=`### Attempt ${p} Failed
|
|
118
118
|
\`\`\`typescript
|
|
119
119
|
${c}
|
|
120
120
|
\`\`\`
|
|
@@ -131,7 +131,7 @@ ${h}`,p++}}return{status:w?"healed":"quarantined",details:y,lastCode:l}}async fu
|
|
|
131
131
|
`),y=(c.stderr||"")+`
|
|
132
132
|
`+(c.stdout||"")+`
|
|
133
133
|
`+(c.message||"")}l=Z(y);let f=Re(y);if(f){console.log(`
|
|
134
|
-
${f}`);let c=it(y,10);if(c.length>0){let x=l?.failed??c.length;console.log(` Failing (${x}):`),c.forEach(L=>console.log(` \u2717 ${L}`)),x>c.length&&console.log(` ... and ${x-c.length} more`)}}if(d=
|
|
134
|
+
${f}`);let c=it(y,10);if(c.length>0){let x=l?.failed??c.length;console.log(` Failing (${x}):`),c.forEach(L=>console.log(` \u2717 ${L}`)),x>c.length&&console.log(` ... and ${x-c.length} more`)}}if(d=je(y,process.cwd(),m),d.length===0){let c=me(y,m,process.cwd());c||(console.error(`
|
|
135
135
|
\u{1F6A8} [Lisa.ai] Could not identify any failing spec files. Output format may be unsupported.`),process.exit(1)),d=[c]}}let v=d.length;console.log(`
|
|
136
136
|
[Lisa.ai] Found ${v} failing spec(s). Healing each in isolation...`),console.log(`${"\u2500".repeat(60)}`);for(let f of d)I({projectId:o,type:"heal",filePath:f,modelUsed:e,status:"running",details:"Queued for isolated healing.",...l&&{testTotal:l.total,testPassed:l.passed,testFailed:l.failed},testFramework:h});for(let f=0;f<d.length;f++){let c=d[f],x=V.resolve(process.cwd(),c);if(console.log(`
|
|
137
137
|
[${f+1}/${v}] ${c}`),!_.existsSync(x)){console.warn(" \u26A0\uFE0F File not found \u2014 skipping.");continue}if(/[\\/](node_modules|dist|build)[\\/]/.test(x)){console.warn(" \u26A0\uFE0F Library file \u2014 refusing to modify.");continue}let L=It(t,c),$=await Mt(c,x,y,t,L,h,e,i,o,a);$.status==="healed"?(re++,Ie.add(c),console.log(` \u2705 Healed [${re} healed ${$e} quarantined so far]`),I({projectId:o,type:"heal",filePath:c,modelUsed:e,status:"success",details:$.details,...l&&{testTotal:l.total,testPassed:l.passed,testFailed:l.failed},testFramework:h}),await nt(y,$.lastCode,h,o)):($e++,m.push(c),console.warn(` \u{1F6A8} Quarantined [${re} healed ${$e} quarantined so far]`),I({projectId:o,type:"heal",filePath:c,modelUsed:e,status:"error",details:`Exhausted all ${i} attempts.
|
|
@@ -142,16 +142,16 @@ ${"\u2500".repeat(60)}`),re>0){console.log("[Lisa.ai] Final verification run..."
|
|
|
142
142
|
`);let c=(f.stderr||"")+`
|
|
143
143
|
`+(f.stdout||"")+`
|
|
144
144
|
`+(f.message||""),x=Re(c);x&&console.log(`
|
|
145
|
-
${x}`);let L=
|
|
146
|
-
\u26A0\uFE0F ${$.length} spec(s) passed isolated verification but still fail globally:`),$.forEach(A=>console.warn(` \u2717 ${A}`)),console.warn(" These likely have cross-test shared state. Try higher maxRetries or fix manually.")),
|
|
147
|
-
\u2139\uFE0F ${
|
|
145
|
+
${x}`);let L=je(c,process.cwd(),[]),$=L.filter(A=>Ie.has(A)),b=L.filter(A=>!Ie.has(A)&&!m.includes(A));$.length>0&&(console.warn(`
|
|
146
|
+
\u26A0\uFE0F ${$.length} spec(s) passed isolated verification but still fail globally:`),$.forEach(A=>console.warn(` \u2717 ${A}`)),console.warn(" These likely have cross-test shared state. Try higher maxRetries or fix manually.")),b.length>0&&(console.warn(`
|
|
147
|
+
\u2139\uFE0F ${b.length} additional failing spec(s) not in this run's queue:`),b.forEach(A=>console.warn(` \u2717 ${A}`)),console.warn(" Run lisa-agent heal again to address these."))}}let k=(l?.passed??0)+re;console.log(`
|
|
148
148
|
\u2705 Healed: ${re} / ${v}`),console.log(` \u{1F6A8} Quarantined: ${$e}`),l&&console.log(` \u{1F4CA} Suite: ${l.passed} passing \u2192 ~${k} passing (est.)`),console.log(`${"\u2500".repeat(60)}
|
|
149
|
-
`),n&&await Fe(n)}var ct=require("child_process"),W=
|
|
149
|
+
`),n&&await Fe(n)}var ct=require("child_process"),W=E(require("fs")),D=E(require("path"));var ke=E(require("fs")),rt=E(require("path"));function at(t){let e=rt.resolve(process.cwd(),t);if(!ke.existsSync(e))throw new Error(`[Lisa.ai Coverage Error] Coverage file not found at ${e}`);let s=ke.readFileSync(e,"utf-8"),n=JSON.parse(s),i=[];for(let[o,r]of Object.entries(n))o!=="total"&&(r.lines.pct<100||r.statements.pct<100||r.functions.pct<100||r.branches.pct<100)&&i.push(o);return i}var lt=new Set,Se=0,De=3;function Dt(t){let e=t.testingFramework,s=process.cwd();return W.existsSync(D.join(s,"angular.json"))?"npx ng test --no-watch --code-coverage --browsers=ChromeHeadless":e==="jest"?"npx jest --coverage --coverageReporters=json-summary":e==="vitest"?"npx vitest run --coverage":[".mocharc.yml",".mocharc.yaml",".mocharc.json",".mocharc.js"].some(i=>W.existsSync(D.join(s,i)))?"npx nyc --reporter=json-summary mocha":null}async function pe(t,e,s=1,n=3,i="local",o,r){if(s>n){console.log(`
|
|
150
150
|
[Lisa.ai Unit] Reached maximum retries (${n}). Stopping.`);return}let a=R.load(process.cwd());a&&(R.applyEnvDefaults(a),!t&&a.testCommand&&(t=a.testCommand,console.log(`[Lisa.ai Config] Using testCommand from .lisai.json: ${t}`)));let g=Y.scanRepository(),u=a?.testingFramework||(g.testingFramework!=="none"?g.testingFramework:void 0),p=a?.testTypes;if(!t){console.log(`
|
|
151
151
|
[Lisa.ai Auto-Discovery] No explicit --command provided. Auto-detecting coverage command...`);let m=Dt(g);if(m)t=m,console.log(`[Lisa.ai Auto-Discovery] Detected coverage command: ${t}`);else{let y=g;if(y.testingFramework==="none"&&(y=await ne.installMissingFramework(y),await oe.provisionConfigurationFiles(y,e,o)),y.suggestedTestCommand){t=y.suggestedTestCommand;let l=y.testingFramework;(l==="jest"||l==="vitest")&&!t.includes("--coverage")&&(t=t.includes("npm run")?`${t} -- --coverage`:`${t} --coverage`,console.log(`[Lisa.ai Unit] Coverage flag appended: ${t}`)),console.log(`[Lisa.ai Auto-Discovery] Bootstrapping with discovered command: ${t}`)}else console.error(`
|
|
152
152
|
[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(`
|
|
153
153
|
[Lisa.ai Unit] ${t} (Attempt ${s}/${n}) Using Model: ${e}`);let w=ie(t);await I({projectId:i,type:"unit",filePath:"global-test-suite",modelUsed:e,status:"running",details:"Agent is executing unit test suite and validating coverage...",testFramework:w});let h=(m,y=!1)=>new Promise((l,d)=>{let v=(0,ct.spawn)(m,{shell:!0,stdio:["ignore","pipe","pipe"]}),k="",f="";v.stdout?.on("data",c=>{let x=c.toString();if(k+=x,y){let L=x.split(`
|
|
154
|
-
`);for(let $ of L){let
|
|
154
|
+
`);for(let $ of L){let b=$.match(/Executed\s+(\d+)\s+of\s+(\d+)/);if(b){let A=$.match(/(\d+)\s+FAILED/),z=A?A[1]:0,H=`\r[Lisa.ai Testing] Executed ${b[1]} of ${b[2]} (${z} FAILED) `;process.stdout.write(H)}}}}),v.stderr?.on("data",c=>{f+=c.toString()}),v.on("close",c=>{c===0?l({stdout:k,stderr:f}):d({message:`Test process exited with code ${c}`,stdout:k,stderr:f})})});try{console.log("[Lisa.ai Unit] Running tests with coverage. This may take a moment...");let m=await h(t,!0),y=m.stdout+`
|
|
155
155
|
`+m.stderr,l=Z(y);l&&await I({projectId:i,type:"unit",filePath:"global-test-suite",modelUsed:e,status:"success",details:`Tests passed on attempt ${s}.`,testTotal:l.total,testPassed:l.passed,testFailed:l.failed,testFramework:w}),console.log(`
|
|
156
156
|
[Lisa.ai Unit] Tests passed on attempt ${s}.`)}catch(m){let y=(m.stderr||"")+`
|
|
157
157
|
`+(m.stdout||"")+`
|
|
@@ -160,15 +160,15 @@ ${x}`);let L=Ce(c,process.cwd(),[]),$=L.filter(A=>Ie.has(A)),E=L.filter(A=>!Ie.h
|
|
|
160
160
|
[Lisa.ai Unit] Auto-Heal complete. Restarting unit analysis...`),await pe(t,e,s+1,n,i,o,r);return}console.log(`
|
|
161
161
|
[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(`
|
|
162
162
|
[Lisa.ai Unit] Targeting ${r.length} file(s) from --files`),m.forEach($=>console.log(` - ${$}`));else{let $=D.resolve(process.cwd(),"coverage/coverage-summary.json");if(W.existsSync($))console.log("[Lisa.ai Unit] Evaluating coverage summary..."),m=at("coverage/coverage-summary.json");else{if(console.log(`
|
|
163
|
-
[Lisa.ai Unit] No coverage-summary.json found. Initiating Cold-Start Discovery...`),m=Y.findUntestedFiles(process.cwd(),[...lt]),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...`)}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 y=m[0],l=D.resolve(process.cwd(),y),d=D.parse(l),v=[D.join(d.dir,`${d.name}.spec${d.ext}`),D.join(d.dir,`${d.name}.test${d.ext}`),D.join(d.dir,`${d.name}.spec.js`),D.join(d.dir,`${d.name}.test.js`),D.join(d.dir,`${d.name}.spec.ts`),D.join(d.dir,`${d.name}.test.ts`)],k=null,f=D.join(d.dir,`${d.name}.spec${d.ext}`);for(let $ of v)if(W.existsSync($)){f=$,k=W.readFileSync($,"utf-8");break}let c="",x=!1,L=W.readFileSync(l,"utf-8");try{k?(console.log(`[Lisa.ai Unit] Existing spec found for ${y}. Requesting coverage append...`),c=await ze(y,L,D.relative(process.cwd(),f),k,e,o,u,p)):(console.log(`[Lisa.ai Unit] Generating new spec for ${y}...`),c=await X(y,L,e,o,u,p)),Se=0,x=!0}catch($){Se++,lt.add(y);let
|
|
164
|
-
[Lisa.ai Unit] LLM call failed for ${y} \u2014 consecutive failure #${Se}/${De}`),console.error(` Error: ${
|
|
163
|
+
[Lisa.ai Unit] No coverage-summary.json found. Initiating Cold-Start Discovery...`),m=Y.findUntestedFiles(process.cwd(),[...lt]),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...`)}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 y=m[0],l=D.resolve(process.cwd(),y),d=D.parse(l),v=[D.join(d.dir,`${d.name}.spec${d.ext}`),D.join(d.dir,`${d.name}.test${d.ext}`),D.join(d.dir,`${d.name}.spec.js`),D.join(d.dir,`${d.name}.test.js`),D.join(d.dir,`${d.name}.spec.ts`),D.join(d.dir,`${d.name}.test.ts`)],k=null,f=D.join(d.dir,`${d.name}.spec${d.ext}`);for(let $ of v)if(W.existsSync($)){f=$,k=W.readFileSync($,"utf-8");break}let c="",x=!1,L=W.readFileSync(l,"utf-8");try{k?(console.log(`[Lisa.ai Unit] Existing spec found for ${y}. Requesting coverage append...`),c=await ze(y,L,D.relative(process.cwd(),f),k,e,o,u,p)):(console.log(`[Lisa.ai Unit] Generating new spec for ${y}...`),c=await X(y,L,e,o,u,p)),Se=0,x=!0}catch($){Se++,lt.add(y);let b=$.cause?.message?`${$.message} (cause: ${$.cause.message})`:$.message;if(console.error(`
|
|
164
|
+
[Lisa.ai Unit] LLM call failed for ${y} \u2014 consecutive failure #${Se}/${De}`),console.error(` Error: ${b}`),Se>=De)throw new Error(`Systematic LLM failure: ${De} consecutive API calls failed.
|
|
165
165
|
This usually means your API key has insufficient credits, hit a rate limit, or the model is unavailable.
|
|
166
|
-
Last error: ${
|
|
166
|
+
Last error: ${b}
|
|
167
167
|
Tip: Check your API key, account credits, and rate-limit quota for provider '${e}'.`)}if(!x){await pe(t,e,s+1,n,i,o,r);return}W.writeFileSync(f,c,"utf-8"),console.log(`[Lisa.ai Unit] Wrote spec to ${f}`),await I({projectId:i,type:"unit",filePath:y,modelUsed:e,status:"success",details:`### Unit Test Generated
|
|
168
168
|
**Auto-Generated Spec (${e}):**
|
|
169
169
|
\`\`\`typescript
|
|
170
170
|
${c}
|
|
171
|
-
\`\`\``,testFramework:w}),await pe(t,e,s+1,n,i,o,r)}catch(m){console.error("[Lisa.ai Unit loop failure]:",m.message),await I({projectId:i,type:"unit",filePath:"unit-loop",modelUsed:e,status:"error",details:`Unit test generation encountered an unrecoverable error: ${m.message}`,testFramework:w})}}var dt=require("child_process"),P=
|
|
171
|
+
\`\`\``,testFramework:w}),await pe(t,e,s+1,n,i,o,r)}catch(m){console.error("[Lisa.ai Unit loop failure]:",m.message),await I({projectId:i,type:"unit",filePath:"unit-loop",modelUsed:e,status:"error",details:`Unit test generation encountered an unrecoverable error: ${m.message}`,testFramework:w})}}var dt=require("child_process"),P=E(require("fs")),G=E(require("path"));function Nt(t){let e=[],s=["src/app/app.routes.ts","src/app/app-routing.module.ts"];for(let i of s){let o=G.join(t,i);if(P.existsSync(o)){let a=P.readFileSync(o,"utf-8").matchAll(/path:\s*['"`]([^'"`]+)['"`]/g);for(let g of a)g[1]&&g[1]!=="**"&&e.push(g[1])}}let n=["src/App.tsx","src/App.jsx","src/routes.tsx","src/routes.jsx"];for(let i of n){let o=G.join(t,i);if(P.existsSync(o)){let a=P.readFileSync(o,"utf-8").matchAll(/path=["']([^"']+)["']/g);for(let g of a)g[1]&&e.push(g[1])}}return[...new Set(e)].sort()}async function pt(t,e,s=1,n=3,i="local",o,r){if(s>n){console.log(`
|
|
172
172
|
[Lisa.ai E2E] Reached maximum retries (${n}). Stopping.`);return}let a=process.cwd(),g=R.load(a);g&&(R.applyEnvDefaults(g),!t&&g.e2eCommand&&(t=g.e2eCommand,console.log(`[Lisa.ai Config] Using e2eCommand from .lisai.json: ${t}`)));let u=ve(a),p=g?.e2eFramework||u.framework;console.log(`
|
|
173
173
|
[Lisa.ai E2E] Framework: ${p} (Attempt ${s}/${n}) Model: ${e}`),!u.installed&&p==="playwright"&&xe(a),t||(t=u.command,console.log(`[Lisa.ai E2E] Using auto-detected command: ${t}`)),await I({projectId:i,type:"e2e",filePath:"e2e-suite",modelUsed:e,status:"running",details:`E2E test generation started with ${p}`,testFramework:p});let w=0;if(r&&r.length>0){console.log(`
|
|
174
174
|
[Lisa.ai E2E] Targeting ${r.length} file(s) from --files`),r.forEach(v=>console.log(` - ${v}`));for(let v of r){let k=G.resolve(a,v);if(P.existsSync(k)){console.log(`[Lisa.ai E2E] Spec already exists: ${v} \u2014 will run as-is.`);continue}let f=G.dirname(k);P.existsSync(f)||P.mkdirSync(f,{recursive:!0}),console.log(`[Lisa.ai E2E] Generating E2E spec: ${v}...`);let c=`
|
|
@@ -186,7 +186,7 @@ The test should:
|
|
|
186
186
|
4. Assert critical UI elements are visible
|
|
187
187
|
|
|
188
188
|
Use @playwright/test imports. Keep it concise and practical.
|
|
189
|
-
`;try{let x=await X(v,c,e,o,p,["e2e"]);P.writeFileSync(k,x,"utf-8"),console.log(`[Lisa.ai E2E] Wrote ${v}`),w++,await I({projectId:i,type:"e2e",filePath:v,modelUsed:e,status:"success",details:`Generated E2E spec: ${v}`,testFramework:p})}catch(x){console.error(`[Lisa.ai E2E] LLM failed for ${v}: ${x.message}`),await I({projectId:i,type:"e2e",filePath:v,modelUsed:e,status:"error",details:`LLM failed to generate E2E spec: ${x.message}`,testFramework:p})}}if(w===0&&r.every(v=>!P.existsSync(G.resolve(a,v)))){console.log("[Lisa.ai E2E] No specs generated. Check LLM connectivity with 'lisa-agent diagnose'.");return}}else{let v=Nt(a);v.length===0?(console.log("[Lisa.ai E2E] No routes discovered. Will generate basic smoke test."),v.push("")):console.log(`[Lisa.ai E2E] Discovered ${v.length} route(s):`,v);let k=G.join(a,"e2e");P.existsSync(k)||P.mkdirSync(k,{recursive:!0});let f=P.existsSync(k)?P.readdirSync(k).filter(c=>c.endsWith(".spec.ts")||c.endsWith(".test.ts")):[];for(let c of v){let L=`${(c||"home").replace(/\//g,"-")}.spec.ts`,$=G.join(k,L);if(f.includes(L)){console.log(`[Lisa.ai E2E] Spec already exists for /${c} \u2014 skipping.`);continue}console.log(`[Lisa.ai E2E] Generating E2E spec for /${c}...`);try{let
|
|
189
|
+
`;try{let x=await X(v,c,e,o,p,["e2e"]);P.writeFileSync(k,x,"utf-8"),console.log(`[Lisa.ai E2E] Wrote ${v}`),w++,await I({projectId:i,type:"e2e",filePath:v,modelUsed:e,status:"success",details:`Generated E2E spec: ${v}`,testFramework:p})}catch(x){console.error(`[Lisa.ai E2E] LLM failed for ${v}: ${x.message}`),await I({projectId:i,type:"e2e",filePath:v,modelUsed:e,status:"error",details:`LLM failed to generate E2E spec: ${x.message}`,testFramework:p})}}if(w===0&&r.every(v=>!P.existsSync(G.resolve(a,v)))){console.log("[Lisa.ai E2E] No specs generated. Check LLM connectivity with 'lisa-agent diagnose'.");return}}else{let v=Nt(a);v.length===0?(console.log("[Lisa.ai E2E] No routes discovered. Will generate basic smoke test."),v.push("")):console.log(`[Lisa.ai E2E] Discovered ${v.length} route(s):`,v);let k=G.join(a,"e2e");P.existsSync(k)||P.mkdirSync(k,{recursive:!0});let f=P.existsSync(k)?P.readdirSync(k).filter(c=>c.endsWith(".spec.ts")||c.endsWith(".test.ts")):[];for(let c of v){let L=`${(c||"home").replace(/\//g,"-")}.spec.ts`,$=G.join(k,L);if(f.includes(L)){console.log(`[Lisa.ai E2E] Spec already exists for /${c} \u2014 skipping.`);continue}console.log(`[Lisa.ai E2E] Generating E2E spec for /${c}...`);try{let b=`
|
|
190
190
|
E2E Test Generation Context:
|
|
191
191
|
- Framework: ${p}
|
|
192
192
|
- Route: /${c}
|
|
@@ -201,15 +201,15 @@ The test should:
|
|
|
201
201
|
4. Assert critical UI elements are visible
|
|
202
202
|
|
|
203
203
|
Use @playwright/test imports. Keep it concise and practical.
|
|
204
|
-
`,A=await X(`e2e/${L}`,
|
|
204
|
+
`,A=await X(`e2e/${L}`,b,e,o,p,["e2e"]);P.writeFileSync($,A,"utf-8"),console.log(`[Lisa.ai E2E] Wrote ${L}`),w++,await I({projectId:i,type:"e2e",filePath:`e2e/${L}`,modelUsed:e,status:"success",details:`Generated E2E spec for route /${c}`,testFramework:p})}catch(b){console.error(`[Lisa.ai E2E] LLM failed for /${c}: ${b.message}`),await I({projectId:i,type:"e2e",filePath:`e2e/${L}`,modelUsed:e,status:"error",details:`LLM failed to generate E2E spec: ${b.message}`,testFramework:p})}}if(w===0&&f.length===0){console.log("[Lisa.ai E2E] No specs generated. Check LLM connectivity with 'lisa-agent diagnose'.");return}}console.log(`
|
|
205
205
|
[Lisa.ai E2E] Running E2E tests: ${t}`);let h="",m="",y=!1;try{await new Promise((v,k)=>{let f=(0,dt.spawn)(t,{shell:!0,stdio:["ignore","pipe","pipe"],cwd:a});f.stdout?.on("data",c=>{let x=c.toString();h+=x,process.stdout.write(x)}),f.stderr?.on("data",c=>{let x=c.toString();m+=x,process.stderr.write(x)}),f.on("close",c=>{c===0?v():k(new Error(`E2E tests exited with code ${c}`))})}),y=!0,console.log(`
|
|
206
206
|
[Lisa.ai E2E] All E2E tests passed.`)}catch{console.log(`
|
|
207
207
|
[Lisa.ai E2E] Some E2E tests failed. Run 'lisa-agent heal --type e2e' to auto-fix.`)}let l=h+`
|
|
208
|
-
`+m,d=Z(l);await I({projectId:i,type:"e2e",filePath:"e2e-suite",modelUsed:e,status:y?"success":"error",details:`E2E generation complete. ${w} new spec(s) generated.`,...d&&{testTotal:d.total,testPassed:d.passed,testFailed:d.failed},testFramework:p})}var ut=require("child_process"),F=
|
|
208
|
+
`+m,d=Z(l);await I({projectId:i,type:"e2e",filePath:"e2e-suite",modelUsed:e,status:y?"success":"error",details:`E2E generation complete. ${w} new spec(s) generated.`,...d&&{testTotal:d.total,testPassed:d.passed,testFailed:d.failed},testFramework:p})}var ut=require("child_process"),F=E(require("fs")),C=E(require("path"));function Ut(t){let e=C.join(t,"package.json");if(!F.existsSync(e))return"node";let s=JSON.parse(F.readFileSync(e,"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 Ot(t,e){let s=[];if(e==="angular"){let n=C.join(t,"src","app");if(!F.existsSync(n))return s;let i=ue(n,/\.(ts)$/).filter(o=>!o.includes(".spec.")&&!o.includes(".test.")&&!o.includes("node_modules"));for(let o of i){let r=F.readFileSync(o,"utf-8");if(r.includes("@Component")&&r.includes("inject(")){let a=C.relative(t,o),g=a.replace(/\.ts$/,".integration.spec.ts");if(F.existsSync(C.join(t,g)))continue;let p=[...r.matchAll(/inject\((\w+)\)/g)].map(h=>h[1]),w=[];for(let h of p){let m=ue(n,new RegExp(`${_t(h)}\\.ts$`));w.push(...m.map(y=>C.relative(t,y)))}s.push({filePath:a,relatedFiles:w,description:`Angular component+service integration: ${C.basename(o)} injects ${p.join(", ")}`})}}}else if(e==="express"||e==="nestjs"){let n=["src","server","api","routes","controllers"];for(let i of n){let o=C.join(t,i);if(!F.existsSync(o))continue;let r=ue(o,/\.(controller|route|router)\.(ts|js)$/).filter(a=>!a.includes(".spec.")&&!a.includes(".test.")&&!a.includes("node_modules"));for(let a of r){let g=C.relative(t,a),u=g.replace(/\.(ts|js)$/,".integration.spec.$1");F.existsSync(C.join(t,u))||s.push({filePath:g,relatedFiles:[],description:`API endpoint integration test for ${C.basename(a)} \u2014 test with supertest against real middleware chain`})}}}else{let n=C.join(t,"src");if(F.existsSync(n)){let i=ue(n,/\.(ts|js|tsx|jsx)$/).filter(o=>!o.includes(".spec.")&&!o.includes(".test.")&&!o.includes("node_modules"));for(let o of i){let a=[...F.readFileSync(o,"utf-8").matchAll(/from\s+['"]\.\.?\//g)];if(a.length>=2){let g=C.relative(t,o),u=g.replace(/\.(ts|js|tsx|jsx)$/,".integration.spec.$1");if(F.existsSync(C.join(t,u)))continue;s.push({filePath:g,relatedFiles:[],description:`Module integration: ${C.basename(o)} imports ${a.length} local modules`})}}}}return s}function ue(t,e){let s=[],n=["node_modules","dist","build",".git",".angular","coverage"];if(!F.existsSync(t))return s;let i=F.readdirSync(t);for(let o of i){if(n.includes(o))continue;let r=C.join(t,o);try{F.statSync(r).isDirectory()?s.push(...ue(r,e)):e.test(o)&&s.push(r)}catch{continue}}return s}function _t(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}async function gt(t,e,s=1,n=3,i="local",o,r){if(s>n){console.log(`
|
|
209
209
|
[Lisa.ai Integration] Reached maximum retries (${n}). Stopping.`);return}let a=process.cwd(),g=R.load(a);g&&(R.applyEnvDefaults(g),!t&&g.testCommand&&(t=g.testCommand,console.log(`[Lisa.ai Config] Using testCommand from .lisai.json: ${t}`)));let u=Ut(a),p=Y.scanRepository(),w=g?.testingFramework||(p.testingFramework!=="none"?p.testingFramework:void 0);console.log(`
|
|
210
|
-
[Lisa.ai Integration] App: ${u}, Framework: ${w||"auto"} (Attempt ${s}/${n}) Model: ${e}`),t||(p.suggestedTestCommand?t=p.suggestedTestCommand:F.existsSync(
|
|
210
|
+
[Lisa.ai Integration] App: ${u}, Framework: ${w||"auto"} (Attempt ${s}/${n}) Model: ${e}`),t||(p.suggestedTestCommand?t=p.suggestedTestCommand:F.existsSync(C.join(a,"angular.json"))?t="npx ng test --no-watch --browsers=ChromeHeadless":(console.error(`
|
|
211
211
|
[Lisa.ai Fatal Error] Could not auto-detect a test command. Please pass --command explicitly.`),process.exit(1)),console.log(`[Lisa.ai Integration] Auto-detected command: ${t}`));let h=ie(t);await I({projectId:i,type:"integration",filePath:"integration-suite",modelUsed:e,status:"running",details:`Integration test generation started for ${u} app`,testFramework:h});let m;if(r&&r.length>0)m=r.map(c=>({filePath:c,relatedFiles:[],description:"User-specified"})),console.log(`
|
|
212
|
-
[Lisa.ai Integration] Targeting ${r.length} file(s) from --files`),m.forEach(c=>console.log(` - ${c.filePath}`));else{if(m=Ot(a,u),m.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 ${m.length} integration target(s):`),m.forEach(c=>console.log(` - ${c.filePath}: ${c.description}`))}let y=0;for(let c of m){let x=
|
|
212
|
+
[Lisa.ai Integration] Targeting ${r.length} file(s) from --files`),m.forEach(c=>console.log(` - ${c.filePath}`));else{if(m=Ot(a,u),m.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 ${m.length} integration target(s):`),m.forEach(c=>console.log(` - ${c.filePath}: ${c.description}`))}let y=0;for(let c of m){let x=C.resolve(a,c.filePath);if(!F.existsSync(x))continue;let L=C.parse(x),$=C.join(L.dir,`${L.name}.integration.spec${L.ext}`);if(F.existsSync($)){console.log(`[Lisa.ai Integration] Spec exists for ${c.filePath} \u2014 skipping.`);continue}console.log(`[Lisa.ai Integration] Generating integration spec for ${c.filePath}...`);let b=F.readFileSync(x,"utf-8"),A="";for(let H of c.relatedFiles.slice(0,3)){let j=C.resolve(a,H);if(F.existsSync(j)){let N=F.readFileSync(j,"utf-8");A+=`
|
|
213
213
|
|
|
214
214
|
// --- Related file: ${H} ---
|
|
215
215
|
${N.slice(0,5e3)}`}}let z=`
|
|
@@ -232,32 +232,32 @@ ${u==="angular"?`
|
|
|
232
232
|
`}
|
|
233
233
|
|
|
234
234
|
Source file:
|
|
235
|
-
${
|
|
235
|
+
${b.slice(0,1e4)}
|
|
236
236
|
|
|
237
237
|
${A?`Related modules:
|
|
238
238
|
${A}`:""}
|
|
239
|
-
`;try{let H=await X(c.filePath,z,e,o,w,["integration"]);F.writeFileSync($,H,"utf-8"),console.log(`[Lisa.ai Integration] Wrote ${
|
|
240
|
-
[Lisa.ai Integration] Running tests to verify generated specs: ${t}`);let l="",d="",v=!1;try{await new Promise((c,x)=>{let L=(0,ut.spawn)(t,{shell:!0,stdio:["ignore","pipe","pipe"],cwd:a});L.stdout?.on("data",$=>{let
|
|
239
|
+
`;try{let H=await X(c.filePath,z,e,o,w,["integration"]);F.writeFileSync($,H,"utf-8"),console.log(`[Lisa.ai Integration] Wrote ${C.relative(a,$)}`),y++,await I({projectId:i,type:"integration",filePath:c.filePath,modelUsed:e,status:"success",details:`Generated integration spec: ${c.description}`,testFramework:h})}catch(H){console.error(`[Lisa.ai Integration] LLM failed for ${c.filePath}: ${H.message}`)}}if(y===0){console.log("[Lisa.ai Integration] No new integration specs generated.");return}console.log(`
|
|
240
|
+
[Lisa.ai Integration] Running tests to verify generated specs: ${t}`);let l="",d="",v=!1;try{await new Promise((c,x)=>{let L=(0,ut.spawn)(t,{shell:!0,stdio:["ignore","pipe","pipe"],cwd:a});L.stdout?.on("data",$=>{let b=$.toString();l+=b,process.stdout.write(b)}),L.stderr?.on("data",$=>{let b=$.toString();d+=b,process.stderr.write(b)}),L.on("close",$=>{$===0?c():x(new Error(`Tests exited with code ${$}`))})}),v=!0,console.log(`
|
|
241
241
|
[Lisa.ai Integration] All tests passed including integration specs.`)}catch{console.log(`
|
|
242
242
|
[Lisa.ai Integration] Some tests failed. Run 'lisa-agent heal --type integration' to auto-fix.`)}let k=l+`
|
|
243
|
-
`+d,f=Z(k);await I({projectId:i,type:"integration",filePath:"integration-suite",modelUsed:e,status:v?"success":"error",details:`Integration test generation complete. ${y} new spec(s) generated.`,...f&&{testTotal:f.total,testPassed:f.passed,testFailed:f.failed},testFramework:h})}var ht=
|
|
243
|
+
`+d,f=Z(k);await I({projectId:i,type:"integration",filePath:"integration-suite",modelUsed:e,status:v?"success":"error",details:`Integration test generation complete. ${y} new spec(s) generated.`,...f&&{testTotal:f.total,testPassed:f.passed,testFailed:f.failed},testFramework:h})}var ht=E(require("dotenv")),yt=require("child_process"),Q=E(require("fs")),J=E(require("path"));ht.config({quiet:!0});function ae(t){return new Promise(e=>{let s=(0,yt.spawn)(t,{shell:!0,stdio:["ignore","pipe","pipe"]}),n="",i="";s.stdout?.on("data",o=>{n+=o.toString()}),s.stderr?.on("data",o=>{i+=o.toString()}),s.on("close",o=>e({stdout:n,stderr:i,code:o??1}))})}function Gt(){return Q.existsSync(J.resolve(process.cwd(),"pnpm-lock.yaml"))?"pnpm":Q.existsSync(J.resolve(process.cwd(),"yarn.lock"))?"yarn":"npm"}var Ht={critical:"\u{1F534}",high:"\u{1F7E0}",moderate:"\u{1F7E1}",low:"\u{1F535}",info:"\u26AA"},ft={critical:5,high:4,moderate:3,low:2,info:1};function mt(t){let e=["critical","high","moderate","low","info"];for(let s of e){let n=t[s]??0;n>0&&console.log(` ${Ht[s]} ${s.padEnd(10)} ${n}`)}console.log(` ${"\u2500".repeat(22)}`),console.log(` ${"Total".padEnd(10)} ${t.total}`)}async function Ne(t){try{return JSON.parse(t)}catch{return null}}async function wt(t,e="local",s){Q.existsSync(J.resolve(process.cwd(),"package.json"))||(console.error(`
|
|
244
244
|
\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=Gt();console.log(`[Lisa.ai Audit] Package manager: ${n}`),console.log(`
|
|
245
245
|
[Lisa.ai Audit] Scanning for vulnerabilities...`);let{stdout:i,code:o}=await ae(n==="npm"?"npm audit --json":n==="yarn"?"yarn audit --json":"pnpm audit --json");if(o===0){console.log(`
|
|
246
246
|
\u2705 [Lisa.ai Audit] No vulnerabilities found! Repository is clean.`);return}let r=await Ne(i);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(`
|
|
247
247
|
Vulnerabilities found:`),mt(a),console.log(`
|
|
248
|
-
[Lisa.ai Audit] Applying safe automatic fixes (npm audit fix)...`);let{stdout:g}=await ae(n==="npm"?"npm audit fix":n==="yarn"?"yarn upgrade":"pnpm audit fix");for(let
|
|
249
|
-
`)){let N=
|
|
248
|
+
[Lisa.ai Audit] Applying safe automatic fixes (npm audit fix)...`);let{stdout:g}=await ae(n==="npm"?"npm audit fix":n==="yarn"?"yarn upgrade":"pnpm audit fix");for(let j of g.split(`
|
|
249
|
+
`)){let N=j.trim();N&&!N.startsWith("npm warn")&&!N.startsWith("npm notice")&&console.log(` ${N}`)}let u=n==="npm"?"npm audit --json":n==="yarn"?"yarn audit --json":"pnpm audit --json",{stdout:p,code:w}=await ae(u);if(w===0){console.log(`
|
|
250
250
|
\u2705 [Lisa.ai Audit] All vulnerabilities fixed by safe auto-fix!`);return}let h=await Ne(p);if(!h){console.warn("[Lisa.ai Audit] Could not parse post-fix audit output.");return}let m=Object.values(h.vulnerabilities),y=h.metadata.vulnerabilities,l=a.total-y.total;console.log(`
|
|
251
|
-
[Lisa.ai Audit] Safe auto-fix resolved ${l} vulnerability(-ies).`),y.total>0&&(console.log(" Remaining:"),mt(y));let d=m.filter(
|
|
252
|
-
[Lisa.ai Audit] Applying ${v.length} non-breaking targeted upgrade(s)...`);for(let te of v){let B=`npm install ${te.name}@${te.version}`;console.log(` \u2192 ${B}`);let{stderr:ce}=await ae(B);ce&&!ce.includes("npm warn")&&console.warn(` ${ce.trim()}`)}let{stdout:
|
|
253
|
-
\u2705 [Lisa.ai Audit] All vulnerabilities resolved!`);return}}let{stdout:k}=await ae(u),f=await Ne(k)??h,x=Object.values(f.vulnerabilities).sort((
|
|
251
|
+
[Lisa.ai Audit] Safe auto-fix resolved ${l} vulnerability(-ies).`),y.total>0&&(console.log(" Remaining:"),mt(y));let d=m.filter(j=>typeof j.fixAvailable=="object"&&!j.fixAvailable.isSemVerMajor).map(j=>j.fixAvailable),v=[...new Map(d.map(j=>[j.name,j])).values()];if(v.length>0){console.log(`
|
|
252
|
+
[Lisa.ai Audit] Applying ${v.length} non-breaking targeted upgrade(s)...`);for(let te of v){let B=`npm install ${te.name}@${te.version}`;console.log(` \u2192 ${B}`);let{stderr:ce}=await ae(B);ce&&!ce.includes("npm warn")&&console.warn(` ${ce.trim()}`)}let{stdout:j,code:N}=await ae(u);if(N===0){console.log(`
|
|
253
|
+
\u2705 [Lisa.ai Audit] All vulnerabilities resolved!`);return}}let{stdout:k}=await ae(u),f=await Ne(k)??h,x=Object.values(f.vulnerabilities).sort((j,N)=>(ft[N.severity]??0)-(ft[j.severity]??0)).slice(0,10).filter(j=>j.fixAvailable===!1?!0:typeof j.fixAvailable=="object"?j.fixAvailable.isSemVerMajor:!1);if(x.length===0){console.log(`
|
|
254
254
|
\u2705 [Lisa.ai Audit] No breaking-change or unfixable vulnerabilities remain.`);return}console.log(`
|
|
255
|
-
[Lisa.ai Audit] ${x.length} vulnerability(-ies) require manual attention.`),console.log(`[Lisa.ai Audit] Consulting ${t} for remediation guidance...`);let L={};try{L=JSON.parse(Q.readFileSync(J.resolve(process.cwd(),"package.json"),"utf8"))}catch{}let $={...L.dependencies??{},...L.devDependencies??{}},
|
|
255
|
+
[Lisa.ai Audit] ${x.length} vulnerability(-ies) require manual attention.`),console.log(`[Lisa.ai Audit] Consulting ${t} for remediation guidance...`);let L={};try{L=JSON.parse(Q.readFileSync(J.resolve(process.cwd(),"package.json"),"utf8"))}catch{}let $={...L.dependencies??{},...L.devDependencies??{}},b=x.map(j=>{let N=j.fixAvailable,te=N===!1?"no fix available yet":`upgrade to ${N.name}@${N.version} (BREAKING \u2014 major version bump)`,B=$[j.name]?`current: ${$[j.name]}`:"transitive dependency";return`\u2022 ${j.name} (${j.severity}) \u2014 ${B} \u2014 ${te}`}).join(`
|
|
256
256
|
`),A=`You are a Node.js security expert helping a developer fix npm vulnerabilities.
|
|
257
257
|
|
|
258
258
|
These vulnerabilities remain after safe auto-fixes were applied. Each requires either a breaking upgrade or has no automated fix:
|
|
259
259
|
|
|
260
|
-
${
|
|
260
|
+
${b}
|
|
261
261
|
|
|
262
262
|
For each vulnerability:
|
|
263
263
|
1. Briefly explain the security risk (1 sentence).
|
|
@@ -265,28 +265,30 @@ For each vulnerability:
|
|
|
265
265
|
3. If the upgrade is breaking (major version), note the most likely code change needed (e.g. renamed API, changed import path).
|
|
266
266
|
4. If no fix exists, suggest a workaround or safe alternative package.
|
|
267
267
|
|
|
268
|
-
Be concrete. Use numbered items. If a vulnerability is a transitive dependency, explain how to force a resolution override in package.json.`;try{let
|
|
268
|
+
Be concrete. Use numbered items. If a vulnerability is a transitive dependency, explain how to force a resolution override in package.json.`;try{let j=await se(A,t,s);console.log(`
|
|
269
269
|
Remediation guidance:
|
|
270
|
-
`);for(let B of
|
|
271
|
-
`))console.log(` ${B}`);let N=J.resolve(process.cwd(),"lisa-audit-report.md"),te=["# Lisa.ai Security Audit Report","",`Generated: ${new Date().toISOString()}`,`Project: ${L.name??J.basename(process.cwd())}`,"","## Summary","","| Severity | Initial | Remaining |","|----------|---------|-----------|",...["critical","high","moderate","low"].map(B=>{let ce=a[B]??0,St=f.metadata.vulnerabilities[B]??0;return`| ${B} | ${ce} | ${St} |`}),"","## Vulnerabilities Requiring Manual Action","",
|
|
270
|
+
`);for(let B of j.split(`
|
|
271
|
+
`))console.log(` ${B}`);let N=J.resolve(process.cwd(),"lisa-audit-report.md"),te=["# Lisa.ai Security Audit Report","",`Generated: ${new Date().toISOString()}`,`Project: ${L.name??J.basename(process.cwd())}`,"","## Summary","","| Severity | Initial | Remaining |","|----------|---------|-----------|",...["critical","high","moderate","low"].map(B=>{let ce=a[B]??0,St=f.metadata.vulnerabilities[B]??0;return`| ${B} | ${ce} | ${St} |`}),"","## Vulnerabilities Requiring Manual Action","",b,"",`## Remediation Guidance (${t})`,"",j].join(`
|
|
272
272
|
`);Q.writeFileSync(N,te,"utf-8"),console.log(`
|
|
273
|
-
[Lisa.ai Audit] Full report saved to: lisa-audit-report.md`)}catch(
|
|
274
|
-
[Lisa.ai Audit] LLM guidance failed: ${
|
|
273
|
+
[Lisa.ai Audit] Full report saved to: lisa-audit-report.md`)}catch(j){console.error(`
|
|
274
|
+
[Lisa.ai Audit] LLM guidance failed: ${j.message}`),console.log(" Run 'npm audit' manually and address the remaining vulnerabilities.")}let z=f.metadata.vulnerabilities,H=a.total-z.total;console.log(`
|
|
275
275
|
${"\u2500".repeat(50)}`),console.log(" Lisa.ai Audit Complete"),console.log(` \u2705 Fixed: ${H} / ${a.total}`),console.log(` \u26A0\uFE0F Remaining: ${z.total} (see lisa-audit-report.md)`),console.log(`${"\u2500".repeat(50)}
|
|
276
|
-
`)}var kt=vt();function le(){console.log(`
|
|
277
|
-
|
|
278
|
-
|
|
276
|
+
`)}var kt=vt();function le(){let t=kt.version;console.log(`
|
|
277
|
+
\x1B[38;2;99;102;241m\u2554\u2550\u2557\x1B[0m \x1B[1mLisa.ai Agent\x1B[0m v${t}
|
|
278
|
+
\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
|
|
279
|
+
\x1B[38;2;99;102;241m\u255A\u2550\u255D\x1B[0m \x1B[2mheal \xB7 unit \xB7 e2e \xB7 integration \xB7 audit\x1B[0m
|
|
280
|
+
`)}async function Ee(t){let e=5,s=t.model,n;if(t.projectId){let o=await ge(t.projectId);if(!o.ok)o.reason==="not_found"?(console.error(`
|
|
279
281
|
[Lisa.ai Agent Error] Project '${t.projectId}' 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(`
|
|
280
282
|
[Lisa.ai Warning] Control Plane is unreachable. Continuing with local CLI defaults (model=${s}, maxRetries=${e}).`);else{let r=o.config;console.log(`[Lisa.ai Agent] Dynamic Config Loaded: Provider=${r.modelProvider}, MaxRetries=${r.maxRetries}`),r.maxRetries<5&&console.warn(`
|
|
281
283
|
[Lisa.ai Warning] maxRetries is ${r.maxRetries}. Consider increasing to 5+ for complex projects.`),s=r.modelProvider,e=r.maxRetries,n=r.apiKey,r.agentEnabled===!1&&(console.log("[Lisa.ai Agent] Agent is disabled by Control Plane for this project."),process.exit(1))}}let i=R.load(process.cwd());return i&&(R.applyEnvDefaults(i),!t.projectId&&i.maxRetries!==void 0&&(e=i.maxRetries),!t.projectId&&i.provider&&t.model==="gemini"&&(s=i.provider),R.printSummary(i)),{model:s,maxRetries:e,apiKey:n,projectId:t.projectId||"local"}}var K=new xt.Command;K.name("lisa-agent").description("Lisa.ai - Autonomous CI/CD Platform Worker Agent").version(kt.version);K.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)").action(async t=>{le();let e=["unit","e2e","integration"],s=t.type;e.includes(s)||(console.error(`
|
|
282
|
-
[Lisa.ai Error] Invalid heal type '${t.type}'. Valid types: ${e.join(", ")}`),process.exit(1));let n=t.files?t.files.split(",").map(o=>o.trim()):void 0,i=await
|
|
284
|
+
[Lisa.ai Error] Invalid heal type '${t.type}'. Valid types: ${e.join(", ")}`),process.exit(1));let n=t.files?t.files.split(",").map(o=>o.trim()):void 0,i=await Ee(t);await Le(t.command,i.model,1,null,i.maxRetries,i.projectId,void 0,i.apiKey,s,n)});K.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 t=>{le();let e=t.files?t.files.split(",").map(n=>n.trim()):void 0,s=await Ee(t);await pe(t.command,s.model,1,s.maxRetries,s.projectId,s.apiKey,e)});K.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 t=>{le();let e=t.files?t.files.split(",").map(n=>n.trim()):void 0,s=await Ee(t);await pt(t.command,s.model,1,s.maxRetries,s.projectId,s.apiKey,e)});K.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 t=>{le();let e=t.files?t.files.split(",").map(n=>n.trim()):void 0,s=await Ee(t);await gt(t.command,s.model,1,s.maxRetries,s.projectId,s.apiKey,e)});K.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 t=>{le();let e=t.model,s;if(t.projectId){let o=await ge(t.projectId);o.ok?(e=o.config.modelProvider,s=o.config.apiKey,console.log(`[Lisa.ai Diagnose] Config fetched from Control Plane. Provider=${e}`)):console.warn(`[Lisa.ai Diagnose] Control Plane ${o.reason}. Using local env for API key.`)}let i={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"}[e]||e;console.log(`
|
|
283
285
|
[Lisa.ai Diagnose] Testing ${e} \u2192 model ID: ${i}`);try{let o=await se("Reply with exactly: LISA_OK",e,s);console.log(`
|
|
284
286
|
[Lisa.ai Diagnose] ${e} is working correctly.`),console.log(` Model response: "${o}"`)}catch(o){console.error(`
|
|
285
287
|
[Lisa.ai Diagnose] ${e} call FAILED.`),console.error(` Error: ${o.message}`),o.cause&&console.error(` Cause: ${o.cause?.message??o.cause}`),console.error(`
|
|
286
|
-
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)}});K.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 t=>{let e=$t.resolve(process.cwd(),ee);
|
|
288
|
+
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)}});K.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 t=>{let e=$t.resolve(process.cwd(),ee);be.existsSync(e)&&!t.force&&(console.error(`
|
|
287
289
|
[Lisa.ai Init] ${ee} already exists. Use --force to overwrite.`),process.exit(1));let s=Lt.createInterface({input:process.stdin,output:process.stdout}),n=(h,m="")=>new Promise(y=>s.question(m?`${h} [${m}]: `:`${h}: `,l=>y(l.trim()||m)));console.log(`
|
|
288
290
|
Lisa.ai Project Setup \u2014 press Enter to accept the default shown in [ ]
|
|
289
|
-
`);let i=await n("LLM provider (gemini / claude / openai)","gemini"),o=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)",""),a=await n("Test types to generate (unit / integration / e2e \u2014 comma-separated)","unit"),g=await n("Test command (leave empty for auto-discovery)",""),u=await n("E2E command (leave empty for auto-discovery)",""),p=await n("Max heal retries","5");s.close();let w={provider:i,testTypes:a.split(",").map(h=>h.trim()).filter(Boolean),maxRetries:parseInt(p,10)||5,skipFiles:[],skipDirs:[],skipPaths:[]};o&&(w.testingFramework=o),r&&(w.e2eFramework=r),g&&(w.testCommand=g),u&&(w.e2eCommand=u),
|
|
291
|
+
`);let i=await n("LLM provider (gemini / claude / openai)","gemini"),o=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)",""),a=await n("Test types to generate (unit / integration / e2e \u2014 comma-separated)","unit"),g=await n("Test command (leave empty for auto-discovery)",""),u=await n("E2E command (leave empty for auto-discovery)",""),p=await n("Max heal retries","5");s.close();let w={provider:i,testTypes:a.split(",").map(h=>h.trim()).filter(Boolean),maxRetries:parseInt(p,10)||5,skipFiles:[],skipDirs:[],skipPaths:[]};o&&(w.testingFramework=o),r&&(w.e2eFramework=r),g&&(w.testCommand=g),u&&(w.e2eCommand=u),be.writeFileSync(e,JSON.stringify(w,null,2)+`
|
|
290
292
|
`,"utf-8"),console.log(`
|
|
291
293
|
Created ${ee}:
|
|
292
294
|
`),console.log(JSON.stringify(w,null,2)),console.log(`
|