@lisa.ai/agent 2.6.4 → 2.6.5
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 +108 -75
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var
|
|
2
|
+
"use strict";var ct=Object.create;var Ce=Object.defineProperty;var ut=Object.getOwnPropertyDescriptor;var dt=Object.getOwnPropertyNames;var pt=Object.getPrototypeOf,gt=Object.prototype.hasOwnProperty;var ft=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var mt=(t,e,n,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of dt(e))!gt.call(t,o)&&o!==n&&Ce(t,o,{get:()=>e[o],enumerable:!(s=ut(e,o))||s.enumerable});return t};var C=(t,e,n)=>(n=t!=null?ct(pt(t)):{},mt(e||!t||!t.__esModule?Ce(n,"default",{value:t,enumerable:!0}):n,t));var st=ft((ps,Ct)=>{Ct.exports={name:"@lisa.ai/agent",version:"2.6.5",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 nt=require("commander"),ue=C(require("fs")),ot=C(require("path")),it=C(require("readline"));var ke=C(require("dotenv"));ke.config({quiet:!0});async function Q(t){let e=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3000";try{let n=`${e}/api/config/${t}`;console.log(`[Lisa.ai Agent] Fetching dynamic configuration from ${n}...`);let s=await fetch(n);if(!s.ok){let i=s.status===404?"not_found":"unreachable";return console.warn(i==="not_found"?`[Lisa.ai Agent Warning] Control Plane returned 404. Project '${t}' not found.`:`[Lisa.ai Agent Warning] Control Plane returned ${s.status}. Falling back to local defaults.`),{ok:!1,reason:i}}return{ok:!0,config:await s.json()}}catch{return console.warn(`[Lisa.ai Agent Warning] Failed to reach Control Plane (${e}). Using local fallback configuration.`),{ok:!1,reason:"unreachable"}}}var Ye=require("child_process"),P=C(require("fs")),N=C(require("path"));var k=C(require("path")),S=C(require("fs"));function pe(t,e=process.cwd(),n=[]){let s=t.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,""),o=n.map(u=>k.resolve(e,u)),i=new Map,r=new Set,l=/(?:Chrome|Firefox|Safari)(?:\s+Headless)?\s+[\d.]+(?:\s+[\d.]+)*\s+\([^)]+\)\s+(.+?)\s+FAILED/g,g;for(;(g=l.exec(s))!==null;){let a=g[1].trim().split(" ");for(let m=1;m<Math.min(a.length,6);m++){let w=a.slice(0,m).join(" ");if(r.has(w))continue;r.add(w);let c=ge(w,e,o);if(c){let v=k.resolve(c);i.has(v)||i.set(v,k.relative(e,c));break}}}let d=/^FAIL\s+([a-zA-Z0-9_./-\\]+\.(?:spec|test)\.(ts|tsx|js|jsx))/gm,p;for(;(p=d.exec(s))!==null;){let u=p[1],a=k.isAbsolute(u)?u:k.resolve(e,u);!o.includes(a)&&S.existsSync(a)&&!i.has(a)&&i.set(a,k.relative(e,a))}return[...i.values()]}function se(t,e=[],n=process.cwd()){let s=t.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,""),o=e.map(c=>k.resolve(c));{let c=/(?:Chrome|Firefox|Safari)(?:\s+Headless)?\s+[\d.]+(?:\s+[\d.]+)*\s+\([^)]+\)\s+(.+?)\s+FAILED/g,v=new Set,f;for(;(f=c.exec(s))!==null;){let h=f[1].trim().split(" ");for(let A=1;A<Math.min(h.length,5);A++){let x=h.slice(0,A).join(" ");if(v.has(x))continue;v.add(x);let L=ge(x,n,o);if(L)return k.relative(n,L)}}}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 v=k.isAbsolute(c)?c:k.resolve(n,c);if(!o.includes(v)&&S.existsSync(v))return c}}let l=/FAIL\s+([a-zA-Z0-9_.\-\/\\]+\.(?:ts|tsx|js|jsx|vue))/gi,g;for(;(g=l.exec(s))!==null;){let c=g[1],v=k.isAbsolute(c)?c:k.resolve(n,c);if(!o.includes(v)&&S.existsSync(v))return c}let d=/([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=d.exec(s))!==null;){let c=p[1];if(c){if(/(?:^|[/\\])(node_modules|dist|build)[/\\]/.test(c))continue;let v=k.isAbsolute(c)?c:k.resolve(n,c);if(!o.includes(v)&&S.existsSync(v))return c}}let u=/\b([A-Z][a-zA-Z0-9]{3,})\b/g,a,m=new Set,w=["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(;(a=u.exec(s))!==null;){let c=a[1];if(!c||w.includes(c)||m.has(c)||/^[A-Z][A-Z0-9]{5,11}$/.test(c)||/^[A-Za-z0-9]{16,}$/.test(c))continue;m.add(c);let v=Ae(c,n,o);if(v)return k.relative(n,v)}return null}function ge(t,e,n){if(!S.existsSync(e))return null;let s=S.readdirSync(e);for(let o of s){let i=k.join(e,o);if(["node_modules","dist","build",".git",".angular"].includes(o))continue;let r;try{r=S.statSync(i)}catch{continue}if(r.isDirectory()){let l=ge(t,i,n);if(l)return l}else if(/\.(spec|test)\.(ts|tsx|js|jsx)$/.test(o)){if(n.includes(k.resolve(i)))continue;try{let l=S.readFileSync(i,"utf8");if(l.includes(`describe('${t}'`)||l.includes(`describe("${t}"`))return i}catch{continue}}}return null}function Ae(t,e,n){if(!S.existsSync(e))return null;let s=S.readdirSync(e);for(let o of s){let i=k.join(e,o);if(o==="node_modules"||o==="dist"||o==="build"||o===".git"||o===".angular")continue;let r;try{r=S.statSync(i)}catch{continue}if(r.isDirectory()){let l=Ae(t,i,n);if(l)return l}else if(o.match(/\.(ts|tsx|js|jsx|vue)$/)){let l=S.readFileSync(i,"utf8");if(l.includes(`class ${t}`)||l.includes(`function ${t}`)||l.includes(`const ${t}`)||l.includes(`let ${t}`)||l.includes(`exports.${t}`)||l.includes(`module.exports.${t}`)){let g=i,d=k.extname(i),p=i.slice(0,-d.length);if(!o.includes(".spec.")&&!o.includes(".test.")){let u=[`${p}.spec${d}`,`${p}.test${d}`,`${p}.spec.js`,`${p}.test.js`];for(let a of u)if(S.existsSync(a)){g=a;break}}if(!n.includes(k.resolve(g)))return g}}}return null}var X=require("ai"),be=require("@ai-sdk/openai"),Se=require("@ai-sdk/anthropic"),je=require("@ai-sdk/google"),Fe=C(require("dotenv"));Fe.config({quiet:!0});var fe=15e3;function Ie(t,e){return t.length<=fe?t:(console.warn(`[Lisa.ai LLM] ${e} is ${t.length} chars \u2014 truncating to ${fe} to stay within context window.`),t.slice(0,fe)+`
|
|
3
3
|
|
|
4
|
-
// ... (truncated)`)}function
|
|
5
|
-
${t.map(
|
|
4
|
+
// ... (truncated)`)}function Ee(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 Pe(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
|
+
${t.map(s=>` \u2022 ${e[s]??s}`).join(`
|
|
6
6
|
`)}
|
|
7
|
-
`}function
|
|
7
|
+
`}function ne(t,e){if(t==="claude"){let o=e||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,Se.createAnthropic)({apiKey:o})(i)}if(t==="openai"){let o=e||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,be.createOpenAI)({apiKey:o})(i)}let n=e||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,je.createGoogleGenerativeAI)({apiKey:n})(s)}async function Te(t,e,n,s,o,i,r,l){console.log(`[Lisa.ai Auto-Heal] Requesting fix from ${s} for ${t}...`);let g=ne(s,o),d=`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
|
-
`+
|
|
11
|
+
`+n+`
|
|
12
12
|
|
|
13
13
|
--- Target File Content (`+t+`) ---
|
|
14
14
|
`+e+`
|
|
@@ -45,34 +45,34 @@ 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:
|
|
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,X.generateText)({model:g,prompt:d}),u=p.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);return u?u[1].trim():p.trim()}async function Re(t,e,n,s,o,i){console.log(`[Lisa.ai Coverage] Requesting test generation from ${n} for ${t}...`);let r=ne(n,s),l=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
|
-
`,
|
|
50
|
+
`,g=Pe(i),d=`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 (`+t+`) ---
|
|
54
|
-
`+
|
|
55
|
-
`+
|
|
54
|
+
`+Ie(e,t)+"\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"+l+`4. Aim for 100% logic coverage.
|
|
55
|
+
`+g,{text:p}=await(0,X.generateText)({model:r,prompt:d}),u=p.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/),a=u?u[1].trim():p.trim();return Ee(a,t),a}async function De(t,e,n,s,o,i,r,l){console.log(`[Lisa.ai Coverage] Requesting test update from ${o} for ${t}...`);let g=ne(o,i),d=r?`3. You MUST use the '${r}' testing framework exclusively. All new tests must follow '${r}' 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
|
-
`,
|
|
57
|
+
`,p=Pe(l),u=`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 (`+t+`) ---
|
|
61
|
-
`+
|
|
61
|
+
`+Ie(e,t)+`
|
|
62
62
|
|
|
63
|
-
--- Existing Test Suite (`+
|
|
64
|
-
`+
|
|
65
|
-
`+
|
|
66
|
-
[Lisa.ai PR Engine] Initializing Pull Request for ${t}...`);let e=(0,
|
|
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"+d+`4. Aim for 100% logic coverage across branches, lines, and functions.
|
|
65
|
+
`+p,{text:a}=await(0,X.generateText)({model:g,prompt:u}),m=a.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/),w=m?m[1].trim():a.trim();return Ee(w,t),w}async function W(t,e,n){let s=ne(e,n),{text:o}=await(0,X.generateText)({model:s,prompt:t}),i=o.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);return i?i[1].trim():o.trim()}var Me=C(require("simple-git")),Ne=require("@octokit/rest"),Ft=require("dotenv/config");async function me(t){console.log(`
|
|
66
|
+
[Lisa.ai PR Engine] Initializing Pull Request for ${t}...`);let e=(0,Me.default)(),s=`lisa-fix/build-error-${new Date().getTime()}`,o=`fix: automated auto-heal by Lisa.ai for ${t}`,i="main";try{i=(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(s),await e.add(t),await e.commit(o),console.log(`[Lisa.ai PR Engine] Committed changes to branch ${s}`),console.log("[Lisa.ai PR Engine] Pushing branch to remote origin..."),await e.push("origin",s,{"--set-upstream":null});let r=new Ne.Octokit({auth:process.env.GITHUB_TOKEN}),l=process.env.GITHUB_REPOSITORY;if(l&&process.env.GITHUB_TOKEN){let[g,d]=l.split("/");console.log(`[Lisa.ai PR Engine] Opening Pull Request against ${g}/${d}...`);let p=await r.rest.pulls.create({owner:g,repo:d,title:o,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
|
-
Please review the changes.`,head:
|
|
72
|
-
\u{1F6A8} [Lisa.ai PR Engine Error] Failed to create Pull Request:`,r.message)}finally{try{await e.checkout(i)}catch{}}}var
|
|
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
|
|
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=o,e}return e}};var
|
|
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(
|
|
71
|
+
Please review the changes.`,head:s,base:i});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(i)}catch{}}}var Oe=C(require("dotenv"));Oe.config({quiet:!0});async function O(t){let n=`${process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3000"}/api/telemetry`;fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}).catch(s=>{console.debug(`[Lisa.ai Agent Debug] Failed to report telemetry: ${s.message}`)})}var D=C(require("fs")),E=C(require("path"));var oe=C(require("fs")),_e=C(require("path")),H=".lisai.json",j=class{static _config=null;static _loaded=!1;static load(e=process.cwd()){if(this._loaded)return this._config;this._loaded=!0;let n=_e.resolve(e,H);if(!oe.existsSync(n))return null;try{let s=oe.readFileSync(n,"utf-8");return this._config=JSON.parse(s),console.log(`[Lisa.ai Config] Loaded project config from ${H}`),this._config}catch(s){return console.warn(`[Lisa.ai Config] Warning: ${H} found but failed to parse \u2014 ${s.message}. Using defaults.`),null}}static get(){return this._config}static applyEnvDefaults(e){if(!e.model)return;let n={claude:"LISA_CLAUDE_MODEL",openai:"LISA_OPENAI_MODEL",gemini:"LISA_GOOGLE_MODEL"},s=e.provider;s&&n[s]&&!process.env[n[s]]&&(process.env[n[s]]=e.model,console.log(`[Lisa.ai Config] Model override applied: ${s} \u2192 ${e.model}`))}static printSummary(e){let n=[];e.provider&&n.push(`provider=${e.provider}`),e.model&&n.push(`model=${e.model}`),e.testingFramework&&n.push(`testingFramework=${e.testingFramework}`),e.testTypes?.length&&n.push(`testTypes=[${e.testTypes.join(",")}]`),e.maxRetries!==void 0&&n.push(`maxRetries=${e.maxRetries}`),e.testCommand&&n.push(`testCommand="${e.testCommand}"`),n.length&&console.log(`[Lisa.ai Config] Settings: ${n.join(", ")}`),e.skipFiles?.length&&console.log(`[Lisa.ai Config] Extra skip files : ${e.skipFiles.join(", ")}`),e.skipDirs?.length&&console.log(`[Lisa.ai Config] Extra skip dirs : ${e.skipDirs.join(", ")}`),e.skipPaths?.length&&console.log(`[Lisa.ai Config] Extra skip paths : ${e.skipPaths.join(", ")}`)}};var q=class{static scanRepository(e=process.cwd()){let n=E.resolve(e,"package.json"),s={type:"unknown",testingFramework:"none"};if(!D.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(D.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"&&(r.test?s.suggestedTestCommand="npm run test":s.testingFramework==="karma"?s.suggestedTestCommand="npx karma start":s.testingFramework==="jest"?s.suggestedTestCommand="npx jest --coverage":s.testingFramework==="vitest"&&(s.suggestedTestCommand="npx vitest run --coverage")),s}static findUntestedFiles(e,n=[]){let s=[];if(!D.existsSync(e))return s;let o=D.readdirSync(e),i=j.get(),r=i?.skipDirs??[],l=i?.skipFiles??[],g=i?.skipPaths??[],d=["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"],u=[/^\./,/\.config\.(js|ts|mjs|cjs)$/,/\.conf\.(js|ts|mjs|cjs)$/];for(let a of o){let m=E.join(e,a);if(d.includes(a))continue;let w;try{w=D.statSync(m)}catch{continue}if(w.isDirectory())s.push(...this.findUntestedFiles(m,n));else if(a.match(/\.(ts|tsx|js|jsx|vue)$/)&&!a.includes(".spec.")&&!a.includes(".test.")){if(p.includes(a)||l.includes(a)||u.some(h=>h.test(a)))continue;let c=E.extname(a),v=a.slice(0,-c.length);if(![E.join(e,`${v}.spec${c}`),E.join(e,`${v}.test${c}`),E.join(e,`${v}.spec.js`),E.join(e,`${v}.test.js`),E.join(e,`${v}.spec.ts`),E.join(e,`${v}.test.ts`)].some(h=>D.existsSync(h))){let h=E.relative(process.cwd(),m);if(g.some(A=>h.startsWith(A)))continue;n.includes(h)||s.push(h)}}}return s}};var Ue=require("child_process"),V=class{static async installMissingFramework(e,n=process.cwd()){if(e.testingFramework!=="none")return e;console.log(`
|
|
73
|
+
[Lisa.ai Auto-Installer] \u{1F6A8} No testing framework detected for ${e.type} architecture.`),console.log("[Lisa.ai Auto-Installer] \u{1FA84} Initiating Zero-Config Installation Protocol...");let s="",o="none";switch(e.type){case"angular":console.log("[Lisa.ai Auto-Installer] Provisioning Angular TestBed environment..."),s="npm install --save-dev karma karma-chrome-launcher karma-coverage karma-jasmine jasmine-core @types/jasmine",o="karma",e.suggestedTestCommand="npm run test";break;case"react":case"node":default:console.log("[Lisa.ai Auto-Installer] Provisioning Universal Jest environment..."),s="npm install --save-dev jest @types/jest ts-jest",o="jest",e.suggestedTestCommand="npx jest --coverage";break;case"vue":console.log("[Lisa.ai Auto-Installer] Provisioning Vue Vitest environment..."),s="npm install --save-dev vitest @vue/test-utils jsdom",o="vitest",e.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,Ue.spawnSync)(r,i.slice(1),{cwd:n,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=o,e}return e}};var ie=C(require("fs")),he=C(require("path"));var z=class{static async provisionConfigurationFiles(e,n,s,o=process.cwd()){if(e.testingFramework==="none")return;if(console.log(`
|
|
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=>ie.existsSync(he.join(o,g)))){console.log("[Lisa.ai Auto-Generator] Setup file detected. Bypassing initialization.");return}let l=`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.
|
|
78
78
|
|
|
@@ -80,13 +80,13 @@ Requirements:
|
|
|
80
80
|
1. Ensure it specifically instruments code coverage.
|
|
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
|
-
4. Return ONLY the raw code string block.`;try{let
|
|
84
|
-
\u274C [Lisa.ai Auto-Generator] Failed to author configuration file: ${
|
|
85
|
-
`)){let o=
|
|
86
|
-
`)){let
|
|
87
|
-
`),console.log(` \u2717 ${
|
|
83
|
+
4. Return ONLY the raw code string block.`;try{let g=await W(l,n,s),d="";e.testingFramework==="jest"&&(d="jest.config.js"),e.testingFramework==="karma"&&(d="karma.conf.js"),e.testingFramework==="vitest"&&(d="vitest.config.ts");let p=he.join(o,d);ie.writeFileSync(p,g,"utf-8"),console.log(`\u2705 [Lisa.ai Auto-Generator] Natively wrote ${d} to repository root.`)}catch(g){console.error(`
|
|
84
|
+
\u274C [Lisa.ai Auto-Generator] Failed to author configuration file: ${g.message}`),process.exit(1)}}};var Ge=C(require("dotenv"));Ge.config({quiet:!0});function He(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 qe(t,e,n){if(n==="local")return[];let s=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3000";try{let o=new URLSearchParams({projectId:n,errorLog:t.slice(0,1e3),framework:e}),i=await fetch(`${s}/api/memory/lookup?${o}`);return i.ok?await i.json():[]}catch{return[]}}async function Be(t,e,n,s){if(s==="local")return;let o=process.env.LISA_CONTROL_PLANE_URL||"http://localhost:3000";try{await fetch(`${o}/api/memory/record`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectId:s,errorLog:t.slice(0,1e3),fixHint:e.slice(0,2e3),framework:n})}),console.log("[Lisa.ai Memory] Fix pattern recorded to Control Plane.")}catch(i){console.debug(`[Lisa.ai Agent Debug] Failed to record memory pattern: ${i.message}`)}}var K=0,re=0,ye=new Set;function xe(t){return t.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,"")}function ht(t){let n=[...xe(t).matchAll(/Executed\s+(\d+)\s+of\s+(\d+)(?:\s+\((\d+)\s+FAILED\))?/g)];if(n.length===0)return null;let s=[...n].reverse().find(l=>l[3]!==void 0)??n[n.length-1],o=parseInt(s[2]),i=s[3]?parseInt(s[3]):0,r=parseInt(s[1])-i;return{total:o,passed:r,failed:i}}function ve(t){let n=[...xe(t).matchAll(/Executed\s+(\d+)\s+of\s+(\d+)(?:\s+\((\d+)\s+FAILED\))?/g)];if(n.length===0)return null;let s=[...n].reverse().find(d=>d[3]!==void 0)??n[n.length-1],o=parseInt(s[2]),i=s[3]?parseInt(s[3]):0,r=parseInt(s[1])-i,l=o>0?Math.round(r/o*100):0;return` [${"\u2588".repeat(Math.round(l/5))+"\u2591".repeat(20-Math.round(l/5))}] ${l}% ${r} passed ${i} failed ${o} total`}function We(t,e=10){let n=[];for(let s of xe(t).split(`
|
|
85
|
+
`)){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>=e)break}}return n}function yt(t){let e=t.replace(/\.(spec|test)\.(ts|js|jsx|tsx)$/,".$2");if(e!==t&&P.existsSync(e))try{return console.log(` [Lisa.ai] \u{1F9E0} Sibling source found: ${N.basename(e)}`),P.readFileSync(e,"utf-8")}catch{return}}function vt(t,e){let n=t.toLowerCase(),s=N.parse(e);if(n.includes("ng test")||n.includes("karma"))return`${t} --include **/${s.base}`;if(n.includes("jest")||n.includes("vitest")||n.includes("playwright"))return`${t} ${e}`;if(n.includes("cypress"))return`${t} --spec ${e}`;if(n.includes("npm")||n.includes("yarn")||n.includes("pnpm")){try{let i=JSON.parse(P.readFileSync(N.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 l=i.scripts?.[r]?.toLowerCase()||"",g=n.includes("npm")?" --":"";if(l.includes("ng test")||l.includes("karma"))return`${t}${g} --include **/${s.base}`;if(l.includes("jest")||l.includes("vitest")||l.includes("playwright"))return`${t}${g} ${e}`}catch{}let o=n.includes("npm")?" --":"";return`${t}${o} ${e}`}return t}function we(t,e=!1){return new Promise((n,s)=>{let o=(0,Ye.spawn)(t,{shell:!0,stdio:["ignore","pipe","pipe"]}),i="",r="";o.stdout?.on("data",l=>{let g=l.toString();if(i+=g,e)for(let d of g.split(`
|
|
86
|
+
`)){let p=d.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(`
|
|
87
|
+
`),console.log(` \u2717 ${p.trim()}`)))}}),o.stderr?.on("data",l=>{r+=l.toString()}),o.on("close",l=>{l===0?n({stdout:i,stderr:r}):s({message:`Process exited with code ${l}`,stdout:i,stderr:r})})})}function wt(t,e,n){if(!e.match(/\.(spec|test)\.(ts|js|tsx|jsx|vue)$/))return;if((n||"").toLowerCase().includes("ng test")||(n||"").toLowerCase().includes("karma"))try{P.writeFileSync(t,`// Quarantined by Lisa.ai \u2014 could not be automatically healed
|
|
88
88
|
describe('', () => {});
|
|
89
|
-
`,"utf-8"),console.log(" \u{1F6A8} Replaced with empty stub (Angular requires valid TS).")}catch{}else try{
|
|
89
|
+
`,"utf-8"),console.log(" \u{1F6A8} Replaced with empty stub (Angular requires valid TS).")}catch{}else try{P.renameSync(t,t+".broken"),console.log(` \u{1F6A8} Renamed to ${N.basename(t)}.broken to skip in test runner.`)}catch{}}async function xt(t,e,n,s,o,i,r,l,g,d){let p=1,u=!1,a=n,m,w="",c="",v=yt(e),y=(await qe(n,i,g)).filter(h=>h.confidence>=.5).map(h=>h.fixHint);for(y.length>0&&console.log(` [Memory] ${y.length} proven pattern(s) found \u2014 injecting as LLM hint.`);p<=l&&!u;){console.log(` [Attempt ${p}/${l}] Requesting fix from ${r}...`);let h=P.readFileSync(e,"utf-8"),A;for(let x=0;x<=3;x++)try{A=await Te(t,h,a,r,d,m,v,y);break}catch(L){let b=(L?.lastError??L)?.statusCode;if((b===529||b===500)&&x<3){let T=30*(x+1);console.warn(` \u26A0\uFE0F LLM overloaded (HTTP ${b}). Waiting ${T}s...`),await new Promise(M=>setTimeout(M,T*1e3))}else{console.error(` \u{1F6A8} LLM API failed: ${L?.message??String(L)}`);break}}if(A===void 0)break;P.writeFileSync(e,A,"utf-8"),w=`### Auto-Heal Analysis
|
|
90
90
|
**Error:**
|
|
91
91
|
\`\`\`bash
|
|
92
92
|
${a.slice(0,2e3)}
|
|
@@ -94,76 +94,109 @@ ${a.slice(0,2e3)}
|
|
|
94
94
|
|
|
95
95
|
**Fix (${r}):**
|
|
96
96
|
\`\`\`typescript
|
|
97
|
-
${
|
|
98
|
-
\`\`\``;try{await
|
|
99
|
-
`+
|
|
100
|
-
`+(
|
|
97
|
+
${A}
|
|
98
|
+
\`\`\``;try{await we(o,!1),console.log(" \u2705 Isolated verification passed."),u=!0,c=A}catch(x){let L=x.stdout?x.stdout.toString():"";a=(x.stderr?x.stderr.toString():"")+`
|
|
99
|
+
`+L+`
|
|
100
|
+
`+(x.message||""),console.log(" \u274C Isolated verification failed.");let T=ve(a);if(T&&console.log(T),We(a,3).forEach(M=>console.log(` \u2717 ${M}`)),(s||"").toLowerCase().includes("ng test")||(s||"").toLowerCase().includes("karma")){let M=a.includes(N.basename(t)+":"),te=/ FAILED/.test(a);if(!M&&!te){console.log(" \u2705 Fix verified (build error is from other spec files, not this one)."),u=!0,c=A;continue}}m=`### Attempt ${p} Failed
|
|
101
101
|
\`\`\`typescript
|
|
102
|
-
${
|
|
102
|
+
${A}
|
|
103
103
|
\`\`\`
|
|
104
104
|
|
|
105
105
|
**New Error:**
|
|
106
|
-
${a}`,
|
|
107
|
-
[Lisa.ai Auto-Discovery] No --command provided. Scanning framework...`);let f=
|
|
108
|
-
\u{1F6A8} [Lisa.ai] Could not discover a test command. Pass --command explicitly.`),process.exit(1))}let d=
|
|
109
|
-
[Lisa.ai] Running test suite (discovery pass)...`),console.log(`[Lisa.ai Executing] ${t} Model: ${e}`);let u;try{await
|
|
106
|
+
${a}`,p++}}return{status:u?"healed":"quarantined",details:w,lastCode:c}}async function ae(t,e,n=1,s=null,o=3,i="local",r,l){let g=j.load(process.cwd());if(g&&(j.applyEnvDefaults(g),!t&&g.testCommand&&(t=g.testCommand,console.log(`[Lisa.ai Config] Using testCommand from .lisai.json: ${t}`))),!t){console.log(`
|
|
107
|
+
[Lisa.ai Auto-Discovery] No --command provided. Scanning framework...`);let f=q.scanRepository();f.testingFramework==="none"&&(f=await V.installMissingFramework(f),await z.provisionConfigurationFiles(f,e,l)),f.suggestedTestCommand?(t=f.suggestedTestCommand,console.log(`[Lisa.ai Auto-Discovery] Discovered command: ${t}`)):(console.error(`
|
|
108
|
+
\u{1F6A8} [Lisa.ai] Could not discover a test command. Pass --command explicitly.`),process.exit(1))}let d=He(t),p=[];console.log(`
|
|
109
|
+
[Lisa.ai] Running test suite (discovery pass)...`),console.log(`[Lisa.ai Executing] ${t} Model: ${e}`);let u;try{await we(t,!0),process.stdout.write(`
|
|
110
110
|
`),console.log(`
|
|
111
|
-
\u2705 [Lisa.ai] All tests passing. Nothing to heal!`),
|
|
111
|
+
\u2705 [Lisa.ai] All tests passing. Nothing to heal!`),s&&await me(s);return}catch(f){process.stdout.write(`
|
|
112
112
|
`),u=(f.stderr||"")+`
|
|
113
113
|
`+(f.stdout||"")+`
|
|
114
|
-
`+(f.message||"")}let a=
|
|
115
|
-
${m}`);let f=
|
|
114
|
+
`+(f.message||"")}let a=ht(u),m=ve(u);if(m){console.log(`
|
|
115
|
+
${m}`);let f=We(u,10);if(f.length>0){let y=a?.failed??f.length;console.log(` Failing (${y}):`),f.forEach(h=>console.log(` \u2717 ${h}`)),y>f.length&&console.log(` ... and ${y-f.length} more`)}}let w=pe(u,process.cwd(),p);if(w.length===0){let f=se(u,p,process.cwd());f||(console.error(`
|
|
116
116
|
\u{1F6A8} [Lisa.ai] Could not identify any failing spec files. Output format may be unsupported.`),process.exit(1)),w=[f]}let c=w.length;console.log(`
|
|
117
|
-
[Lisa.ai] Found ${c} failing spec(s). Healing each in isolation...`),console.log(`${"\u2500".repeat(60)}`);for(let f of w)
|
|
118
|
-
[${f+1}/${c}] ${
|
|
119
|
-
`+
|
|
120
|
-
${"\u2500".repeat(60)}`),
|
|
117
|
+
[Lisa.ai] Found ${c} failing spec(s). Healing each in isolation...`),console.log(`${"\u2500".repeat(60)}`);for(let f of w)O({projectId:i,type:"heal",filePath:f,modelUsed:e,status:"running",details:"Queued for isolated healing.",...a&&{testTotal:a.total,testPassed:a.passed,testFailed:a.failed},testFramework:d});for(let f=0;f<w.length;f++){let y=w[f],h=N.resolve(process.cwd(),y);if(console.log(`
|
|
118
|
+
[${f+1}/${c}] ${y}`),!P.existsSync(h)){console.warn(" \u26A0\uFE0F File not found \u2014 skipping.");continue}if(/[\\/](node_modules|dist|build)[\\/]/.test(h)){console.warn(" \u26A0\uFE0F Library file \u2014 refusing to modify.");continue}let A=vt(t,y),x=await xt(y,h,u,t,A,d,e,o,i,l);x.status==="healed"?(K++,ye.add(y),console.log(` \u2705 Healed [${K} healed ${re} quarantined so far]`),O({projectId:i,type:"heal",filePath:y,modelUsed:e,status:"success",details:x.details,...a&&{testTotal:a.total,testPassed:a.passed,testFailed:a.failed},testFramework:d}),await Be(u,x.lastCode,d,i)):(re++,p.push(y),console.warn(` \u{1F6A8} Quarantined [${K} healed ${re} quarantined so far]`),O({projectId:i,type:"heal",filePath:y,modelUsed:e,status:"error",details:`Exhausted all ${o} attempts.
|
|
119
|
+
`+x.details,...a&&{testTotal:a.total,testPassed:a.passed,testFailed:a.failed},testFramework:d}),wt(h,y,t))}if(console.log(`
|
|
120
|
+
${"\u2500".repeat(60)}`),K>0){console.log("[Lisa.ai] Final verification run...");try{await we(t,!0),process.stdout.write(`
|
|
121
121
|
`),console.log(`
|
|
122
122
|
\u2705 All tests passing!`)}catch(f){process.stdout.write(`
|
|
123
|
-
`);let
|
|
123
|
+
`);let y=(f.stderr||"")+`
|
|
124
124
|
`+(f.stdout||"")+`
|
|
125
|
-
`+(f.message||""),
|
|
126
|
-
${
|
|
127
|
-
\u26A0\uFE0F ${
|
|
128
|
-
\u2139\uFE0F ${
|
|
129
|
-
\u2705 Healed: ${
|
|
130
|
-
`),
|
|
131
|
-
[Lisa.ai Auto-Discovery] No explicit --command provided. Initiating Autonomous Framework Fingerprinting...`);let u=l;if(u.testingFramework==="none"&&(u=await
|
|
125
|
+
`+(f.message||""),h=ve(y);h&&console.log(`
|
|
126
|
+
${h}`);let A=pe(y,process.cwd(),[]),x=A.filter(b=>ye.has(b)),L=A.filter(b=>!ye.has(b)&&!p.includes(b));x.length>0&&(console.warn(`
|
|
127
|
+
\u26A0\uFE0F ${x.length} spec(s) passed isolated verification but still fail globally:`),x.forEach(b=>console.warn(` \u2717 ${b}`)),console.warn(" These likely have cross-test shared state. Try higher maxRetries or fix manually.")),L.length>0&&(console.warn(`
|
|
128
|
+
\u2139\uFE0F ${L.length} additional failing spec(s) not in this run's queue:`),L.forEach(b=>console.warn(` \u2717 ${b}`)),console.warn(" Run lisa-agent heal again to address these."))}}let v=(a?.passed??0)+K;console.log(`
|
|
129
|
+
\u2705 Healed: ${K} / ${c}`),console.log(` \u{1F6A8} Quarantined: ${re}`),a&&console.log(` \u{1F4CA} Suite: ${a.passed} passing \u2192 ~${v} passing (est.)`),console.log(`${"\u2500".repeat(60)}
|
|
130
|
+
`),s&&await me(s)}var Ze=require("child_process"),_=C(require("fs")),F=C(require("path"));var le=C(require("fs")),Ve=C(require("path"));function ze(t){let e=Ve.resolve(process.cwd(),t);if(!le.existsSync(e))throw new Error(`[Lisa.ai Coverage Error] Coverage file not found at ${e}`);let n=le.readFileSync(e,"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 Ke=new Set,ce=0,Le=3;async function ee(t,e,n=1,s=3,o="local",i){let r=j.load(process.cwd());r&&(j.applyEnvDefaults(r),!t&&r.testCommand&&(t=r.testCommand,console.log(`[Lisa.ai Config] Using testCommand from .lisai.json: ${t}`)));let l=q.scanRepository(),g=r?.testingFramework||(l.testingFramework!=="none"?l.testingFramework:void 0),d=r?.testTypes;if(!t){console.log(`
|
|
131
|
+
[Lisa.ai Auto-Discovery] No explicit --command provided. Initiating Autonomous Framework Fingerprinting...`);let u=l;if(u.testingFramework==="none"&&(u=await V.installMissingFramework(u),await z.provisionConfigurationFiles(u,e,i)),u.suggestedTestCommand){t=u.suggestedTestCommand;let a=u.testingFramework;(a==="jest"||a==="vitest")&&!t.includes("--coverage")&&(t=t.includes("npm run")?`${t} -- --coverage`:`${t} --coverage`,console.log(`[Lisa.ai Coverage] Coverage flag appended: ${t}`)),console.log(`[Lisa.ai Auto-Discovery] Bootstrapping execution with natively discovered command: ${t}`)}else console.error(`
|
|
132
132
|
\u{1F6A8} [Lisa.ai Fatal Error] Agent could not dynamically extrapolate a testing command for a Generic Node Environment. Please pass --command explicitly.`),process.exit(1)}console.log(`
|
|
133
|
-
[Lisa.ai Coverage] ${t} (Attempt ${
|
|
134
|
-
`);for(let
|
|
135
|
-
\u2705 [Lisa.ai Coverage] Tests passed successfully on attempt ${
|
|
133
|
+
[Lisa.ai Coverage] ${t} (Attempt ${n}/${s}) Using Model: ${e}`),await O({projectId:o,type:"coverage",filePath:"global-test-suite",modelUsed:e,status:"running",details:"Agent is currently executing testing suite and validating coverage drops..."});let p=(u,a=!1)=>new Promise((m,w)=>{let c=(0,Ze.spawn)(u,{shell:!0,stdio:["ignore","pipe","pipe"]}),v="",f="";c.stdout?.on("data",y=>{let h=y.toString();if(v+=h,a){let A=h.split(`
|
|
134
|
+
`);for(let x of A){let L=x.match(/Executed\s+(\d+)\s+of\s+(\d+)/);if(L){let b=x.match(/(\d+)\s+FAILED/),T=b?b[1]:0,M=`\r\u23F3 [Lisa.ai Testing] Executed ${L[1]} of ${L[2]} (${T} FAILED) `;process.stdout.write(M)}}}}),c.stderr?.on("data",y=>{f+=y.toString()}),c.on("close",y=>{y===0?m():w({message:`Test process exited with code ${y}`,stdout:v,stderr:f})})});try{console.log("[Lisa.ai Coverage] Booting testing framework in the background. This may take a moment..."),await p(t,!0),console.log(`
|
|
135
|
+
\u2705 [Lisa.ai Coverage] Tests passed successfully on attempt ${n}.`)}catch(u){let a=(u.stderr||"")+`
|
|
136
136
|
`+(u.stdout||"")+`
|
|
137
|
-
`+(u.message||"");if(
|
|
138
|
-
\u274C [Lisa.ai Coverage] Tests failed. Delegating to Auto-Heal...`),await
|
|
139
|
-
\u{1F504} [Lisa.ai Coverage] Auto-Heal successful. Restarting coverage analysis...`),await
|
|
140
|
-
[Lisa.ai Coverage] No failing spec file detected in error output. Tests may not exist yet \u2014 initiating Cold-Start Discovery...`)}try{let u=
|
|
141
|
-
[Lisa.ai Coverage] No coverage-summary.json found. Initiating Cold-Start Project Crawler...`),a=
|
|
142
|
-
\u274C [Lisa.ai Coverage] LLM call failed for ${m} \u2014 consecutive failure #${
|
|
137
|
+
`+(u.message||"");if(se(a,[],process.cwd())!==null){console.log(`
|
|
138
|
+
\u274C [Lisa.ai Coverage] Tests failed. Delegating to Auto-Heal...`),await ae(t,e,1,null,s,o,void 0,i),console.log(`
|
|
139
|
+
\u{1F504} [Lisa.ai Coverage] Auto-Heal successful. Restarting coverage analysis...`),await ee(t,e,n+1,s,o,i);return}console.log(`
|
|
140
|
+
[Lisa.ai Coverage] No failing spec file detected in error output. Tests may not exist yet \u2014 initiating Cold-Start Discovery...`)}try{let u=F.resolve(process.cwd(),"coverage/coverage-summary.json"),a=[];if(_.existsSync(u))console.log("[Lisa.ai Coverage] Evaluating summary..."),a=ze("coverage/coverage-summary.json");else{if(console.log(`
|
|
141
|
+
[Lisa.ai Coverage] No coverage-summary.json found. Initiating Cold-Start Project Crawler...`),a=q.findUntestedFiles(process.cwd(),[...Ke]),a.length===0){console.log("\u2705 [Lisa.ai Coverage] Zero-Test scan complete. No untested source files discovered.");return}console.log(`[Lisa.ai Coverage] Discovered ${a.length} untested file(s). Seeding first test suite...`)}if(a.length===0){console.log("\u2705 [Lisa.ai Coverage] 100% Logic Coverage Verified! No un-covered files remaining.");return}console.log(`[Lisa.ai Coverage] Found ${a.length} file(s) below 100% threshold:`,a);let m=a[0],w=F.resolve(process.cwd(),m),c=F.parse(w),v=[F.join(c.dir,`${c.name}.spec${c.ext}`),F.join(c.dir,`${c.name}.test${c.ext}`),F.join(c.dir,`${c.name}.spec.js`),F.join(c.dir,`${c.name}.test.js`),F.join(c.dir,`${c.name}.spec.ts`),F.join(c.dir,`${c.name}.test.ts`)],f=null,y=F.join(c.dir,`${c.name}.spec${c.ext}`);for(let L of v)if(_.existsSync(L)){y=L,f=_.readFileSync(L,"utf-8");break}let h="",A=!1,x=_.readFileSync(w,"utf-8");try{f?(console.log(`[Lisa.ai Coverage] Existing test suite discovered for ${m}. Requesting single-pass logic coverage append...`),h=await De(m,x,F.relative(process.cwd(),y),f,e,i,g,d)):(console.log(`[Lisa.ai Coverage] Requesting newly generated test suite for ${m}...`),h=await Re(m,x,e,i,g,d)),ce=0,A=!0}catch(L){ce++,Ke.add(m);let b=L.cause?.message?`${L.message} (cause: ${L.cause.message})`:L.message;if(console.error(`
|
|
142
|
+
\u274C [Lisa.ai Coverage] LLM call failed for ${m} \u2014 consecutive failure #${ce}/${Le}`),console.error(` Error: ${b}`),ce>=Le)throw new Error(`Systematic LLM failure: ${Le} consecutive API calls failed.
|
|
143
143
|
This usually means your API key has insufficient credits, hit a rate limit, or the model is unavailable.
|
|
144
|
-
Last error: ${
|
|
145
|
-
Tip: Check your API key, account credits, and rate-limit quota for provider '${e}'.`)}if(
|
|
144
|
+
Last error: ${b}
|
|
145
|
+
Tip: Check your API key, account credits, and rate-limit quota for provider '${e}'.`)}if(!A){await ee(t,e,n+1,s,o,i);return}_.writeFileSync(y,h,"utf-8"),console.log(`[Lisa.ai Coverage] Wrote Spec File to ${y}`),await O({projectId:o,type:"coverage",filePath:m,modelUsed:e,status:"success",details:`### Logic Coverage Action Detected
|
|
146
146
|
**Auto-Generated Specification Append (${e}):**
|
|
147
147
|
\`\`\`typescript
|
|
148
|
-
${
|
|
149
|
-
\`\`\``}),await
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
${h}
|
|
149
|
+
\`\`\``}),await ee(t,e,n+1,s,o,i)}catch(u){console.error("[Lisa.ai Coverage loop failure]:",u.message),await O({projectId:o,type:"coverage",filePath:"coverage-loop",modelUsed:e,status:"error",details:`Coverage loop encountered an unrecoverable error: ${u.message}`})}}var Xe=C(require("dotenv")),et=require("child_process"),G=C(require("fs")),U=C(require("path"));Xe.config({quiet:!0});function Z(t){return new Promise(e=>{let n=(0,et.spawn)(t,{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=>e({stdout:s,stderr:o,code:i??1}))})}function Lt(){return G.existsSync(U.resolve(process.cwd(),"pnpm-lock.yaml"))?"pnpm":G.existsSync(U.resolve(process.cwd(),"yarn.lock"))?"yarn":"npm"}var $t={critical:"\u{1F534}",high:"\u{1F7E0}",moderate:"\u{1F7E1}",low:"\u{1F535}",info:"\u26AA"},Je={critical:5,high:4,moderate:3,low:2,info:1};function Qe(t){let e=["critical","high","moderate","low","info"];for(let n of e){let s=t[n]??0;s>0&&console.log(` ${$t[n]} ${n.padEnd(10)} ${s}`)}console.log(` ${"\u2500".repeat(22)}`),console.log(` ${"Total".padEnd(10)} ${t.total}`)}async function $e(t){try{return JSON.parse(t)}catch{return null}}async function tt(t,e="local",n){G.existsSync(U.resolve(process.cwd(),"package.json"))||(console.error(`
|
|
150
|
+
\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=Lt();console.log(`[Lisa.ai Audit] Package manager: ${s}`),console.log(`
|
|
151
|
+
[Lisa.ai Audit] Scanning for vulnerabilities...`);let{stdout:o,code:i}=await Z(s==="npm"?"npm audit --json":s==="yarn"?"yarn audit --json":"pnpm audit --json");if(i===0){console.log(`
|
|
152
|
+
\u2705 [Lisa.ai Audit] No vulnerabilities found! Repository is clean.`);return}let r=await $e(o);r||(console.error("[Lisa.ai Audit] Could not parse audit output. Run 'npm audit' manually to investigate."),process.exit(1));let l=r.metadata.vulnerabilities;console.log(`
|
|
153
|
+
Vulnerabilities found:`),Qe(l),console.log(`
|
|
154
|
+
[Lisa.ai Audit] Applying safe automatic fixes (npm audit fix)...`);let{stdout:g}=await Z(s==="npm"?"npm audit fix":s==="yarn"?"yarn upgrade":"pnpm audit fix");for(let $ of g.split(`
|
|
155
|
+
`)){let I=$.trim();I&&!I.startsWith("npm warn")&&!I.startsWith("npm notice")&&console.log(` ${I}`)}let d=s==="npm"?"npm audit --json":s==="yarn"?"yarn audit --json":"pnpm audit --json",{stdout:p,code:u}=await Z(d);if(u===0){console.log(`
|
|
156
|
+
\u2705 [Lisa.ai Audit] All vulnerabilities fixed by safe auto-fix!`);return}let a=await $e(p);if(!a){console.warn("[Lisa.ai Audit] Could not parse post-fix audit output.");return}let m=Object.values(a.vulnerabilities),w=a.metadata.vulnerabilities,c=l.total-w.total;console.log(`
|
|
157
|
+
[Lisa.ai Audit] Safe auto-fix resolved ${c} vulnerability(-ies).`),w.total>0&&(console.log(" Remaining:"),Qe(w));let v=m.filter($=>typeof $.fixAvailable=="object"&&!$.fixAvailable.isSemVerMajor).map($=>$.fixAvailable),f=[...new Map(v.map($=>[$.name,$])).values()];if(f.length>0){console.log(`
|
|
158
|
+
[Lisa.ai Audit] Applying ${f.length} non-breaking targeted upgrade(s)...`);for(let Y of f){let R=`npm install ${Y.name}@${Y.version}`;console.log(` \u2192 ${R}`);let{stderr:J}=await Z(R);J&&!J.includes("npm warn")&&console.warn(` ${J.trim()}`)}let{stdout:$,code:I}=await Z(d);if(I===0){console.log(`
|
|
159
|
+
\u2705 [Lisa.ai Audit] All vulnerabilities resolved!`);return}}let{stdout:y}=await Z(d),h=await $e(y)??a,x=Object.values(h.vulnerabilities).sort(($,I)=>(Je[I.severity]??0)-(Je[$.severity]??0)).slice(0,10).filter($=>$.fixAvailable===!1?!0:typeof $.fixAvailable=="object"?$.fixAvailable.isSemVerMajor:!1);if(x.length===0){console.log(`
|
|
160
|
+
\u2705 [Lisa.ai Audit] No breaking-change or unfixable vulnerabilities remain.`);return}console.log(`
|
|
161
|
+
[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(G.readFileSync(U.resolve(process.cwd(),"package.json"),"utf8"))}catch{}let b={...L.dependencies??{},...L.devDependencies??{}},T=x.map($=>{let I=$.fixAvailable,Y=I===!1?"no fix available yet":`upgrade to ${I.name}@${I.version} (BREAKING \u2014 major version bump)`,R=b[$.name]?`current: ${b[$.name]}`:"transitive dependency";return`\u2022 ${$.name} (${$.severity}) \u2014 ${R} \u2014 ${Y}`}).join(`
|
|
162
|
+
`),M=`You are a Node.js security expert helping a developer fix npm vulnerabilities.
|
|
163
|
+
|
|
164
|
+
These vulnerabilities remain after safe auto-fixes were applied. Each requires either a breaking upgrade or has no automated fix:
|
|
165
|
+
|
|
166
|
+
${T}
|
|
167
|
+
|
|
168
|
+
For each vulnerability:
|
|
169
|
+
1. Briefly explain the security risk (1 sentence).
|
|
170
|
+
2. Give the exact command to fix it (e.g. npm install package@version or a replacement).
|
|
171
|
+
3. If the upgrade is breaking (major version), note the most likely code change needed (e.g. renamed API, changed import path).
|
|
172
|
+
4. If no fix exists, suggest a workaround or safe alternative package.
|
|
173
|
+
|
|
174
|
+
Be concrete. Use numbered items. If a vulnerability is a transitive dependency, explain how to force a resolution override in package.json.`;try{let $=await W(M,t,n);console.log(`
|
|
175
|
+
Remediation guidance:
|
|
176
|
+
`);for(let R of $.split(`
|
|
177
|
+
`))console.log(` ${R}`);let I=U.resolve(process.cwd(),"lisa-audit-report.md"),Y=["# Lisa.ai Security Audit Report","",`Generated: ${new Date().toISOString()}`,`Project: ${L.name??U.basename(process.cwd())}`,"","## Summary","","| Severity | Initial | Remaining |","|----------|---------|-----------|",...["critical","high","moderate","low"].map(R=>{let J=l[R]??0,lt=h.metadata.vulnerabilities[R]??0;return`| ${R} | ${J} | ${lt} |`}),"","## Vulnerabilities Requiring Manual Action","",T,"",`## Remediation Guidance (${t})`,"",$].join(`
|
|
178
|
+
`);G.writeFileSync(I,Y,"utf-8"),console.log(`
|
|
179
|
+
[Lisa.ai Audit] Full report saved to: lisa-audit-report.md`)}catch($){console.error(`
|
|
180
|
+
[Lisa.ai Audit] LLM guidance failed: ${$.message}`),console.log(" Run 'npm audit' manually and address the remaining vulnerabilities.")}let te=h.metadata.vulnerabilities,at=l.total-te.total;console.log(`
|
|
181
|
+
${"\u2500".repeat(50)}`),console.log(" Lisa.ai Audit Complete"),console.log(` \u2705 Fixed: ${at} / ${l.total}`),console.log(` \u26A0\uFE0F Remaining: ${te.total} (see lisa-audit-report.md)`),console.log(`${"\u2500".repeat(50)}
|
|
182
|
+
`)}var rt=st();function de(){console.log(`
|
|
183
|
+
======================================================`),console.log(`\u2728 Lisa.ai Agent v${rt.version} Running... `),console.log(" Where pure magic happens! \u{1FA84}"),console.log(`======================================================
|
|
184
|
+
`)}var B=new nt.Command;B.name("lisa-agent").description("Lisa.ai - Autonomous CI/CD Platform Worker Agent").version(rt.version);B.command("heal").description("Run a command and autonomously heal errors").option("-c, --command <type>","Command to execute (Optional for Auto-Discovery)").option("-m, --model <provider>","LLM provider to use (gemini, claude, openai)","gemini").option("-p, --project-id <id>","Control Plane Project ID to fetch dynamic config").action(async t=>{de();let e=5,n=t.model,s;if(t.projectId){let i=await Q(t.projectId);if(!i.ok)i.reason==="not_found"?(console.error(`
|
|
152
185
|
\u{1F6A8} [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(`
|
|
153
|
-
\u26A0\uFE0F [Lisa.ai Warning] Control Plane is unreachable. Continuing with local CLI defaults (model=${
|
|
154
|
-
\u26A0\uFE0F [Lisa.ai Warning] Your Dashboard Analytics Config has maxRetries set to ${r.maxRetries}. Consider increasing this to 5+ in the WEB UI for complex Angular Healing!`),
|
|
186
|
+
\u26A0\uFE0F [Lisa.ai Warning] Control Plane is unreachable. Continuing with local CLI defaults (model=${n}, maxRetries=${e}).`);else{let r=i.config;console.log(`[Lisa.ai Agent] Dynamic Config Loaded: Provider=${r.modelProvider}, MaxRetries=${r.maxRetries}`),r.maxRetries<5&&console.warn(`
|
|
187
|
+
\u26A0\uFE0F [Lisa.ai Warning] Your Dashboard Analytics Config has maxRetries set to ${r.maxRetries}. Consider increasing this to 5+ in the WEB UI for complex Angular Healing!`),n=r.modelProvider,e=r.maxRetries,s=r.apiKey,r.autoHealEnabled===!1&&(console.log("[Lisa.ai Agent] Auto-heal is disabled by Control Plane."),process.exit(1))}}let o=j.load(process.cwd());o&&(j.applyEnvDefaults(o),!t.projectId&&o.maxRetries!==void 0&&(e=o.maxRetries),!t.projectId&&o.provider&&t.model==="gemini"&&(n=o.provider),j.printSummary(o)),await ae(t.command,n,1,null,e,t.projectId||"local",void 0,s)});B.command("coverage").description("Run test command dynamically generating missing specs for 100% coverage").option("-c, --command <type>","Test command to execute (Optional for Auto-Discovery)").option("-m, --model <provider>","LLM provider to use (gemini, claude, openai)","gemini").option("-p, --project-id <id>","Control Plane Project ID to fetch dynamic config").action(async t=>{de();let e=5,n=t.model,s;if(t.projectId){let i=await Q(t.projectId);if(!i.ok)i.reason==="not_found"?(console.error(`
|
|
155
188
|
\u{1F6A8} [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(`
|
|
156
|
-
\u26A0\uFE0F [Lisa.ai Warning] Control Plane is unreachable. Continuing with local CLI defaults (model=${
|
|
157
|
-
\u26A0\uFE0F [Lisa.ai Warning] Your Dashboard Analytics Config has maxRetries set to ${r.maxRetries}. Consider increasing this to 5+ in the WEB UI for complex Angular Healing!`),
|
|
158
|
-
[Lisa.ai Diagnose] Testing ${e} \u2192 model ID: ${o}`);try{let i=await
|
|
189
|
+
\u26A0\uFE0F [Lisa.ai Warning] Control Plane is unreachable. Continuing with local CLI defaults (model=${n}, maxRetries=${e}).`);else{let r=i.config;console.log(`[Lisa.ai Agent] Dynamic Config Loaded: Provider=${r.modelProvider}, MaxRetries=${r.maxRetries}`),r.maxRetries<5&&console.warn(`
|
|
190
|
+
\u26A0\uFE0F [Lisa.ai Warning] Your Dashboard Analytics Config has maxRetries set to ${r.maxRetries}. Consider increasing this to 5+ in the WEB UI for complex Angular Healing!`),n=r.modelProvider,e=r.maxRetries,s=r.apiKey,r.autoHealEnabled===!1&&(console.log("[Lisa.ai Agent] Auto-heal is disabled by Control Plane."),process.exit(1))}}let o=j.load(process.cwd());o&&(j.applyEnvDefaults(o),!t.projectId&&o.maxRetries!==void 0&&(e=o.maxRetries),!t.projectId&&o.provider&&t.model==="gemini"&&(n=o.provider),j.printSummary(o)),await ee(t.command,n,1,e,t.projectId||"local",s)});B.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=>{de();let e=t.model,n;if(t.projectId){let i=await Q(t.projectId);i.ok?(e=i.config.modelProvider,n=i.config.apiKey,console.log(`[Lisa.ai Diagnose] Config fetched from Control Plane. Provider=${e}`)):console.warn(`[Lisa.ai Diagnose] Control Plane ${i.reason}. Using local env for API key.`)}let o={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(`
|
|
191
|
+
[Lisa.ai Diagnose] Testing ${e} \u2192 model ID: ${o}`);try{let i=await W("Reply with exactly: LISA_OK",e,n);console.log(`
|
|
159
192
|
\u2705 [Lisa.ai Diagnose] ${e} is working correctly.`),console.log(` Model response: "${i}"`)}catch(i){console.error(`
|
|
160
193
|
\u274C [Lisa.ai Diagnose] ${e} call FAILED.`),console.error(` Error: ${i.message}`),i.cause&&console.error(` Cause: ${i.cause?.message??i.cause}`),console.error(`
|
|
161
|
-
Common causes:`),console.error(" \u2022 API key is invalid, expired, or has no credits"),console.error(" \u2022 Model ID is deprecated (current: claude-sonnet-4-6, gemini-2.0-flash, gpt-4o-mini)"),console.error(" \u2022 Rate limit exceeded \u2014 wait a minute and retry"),console.error(" \u2022 Network/proxy issue blocking api.anthropic.com"),process.exit(1)}});
|
|
162
|
-
\u274C [Lisa.ai Init] ${
|
|
194
|
+
Common causes:`),console.error(" \u2022 API key is invalid, expired, or has no credits"),console.error(" \u2022 Model ID is deprecated (current: claude-sonnet-4-6, gemini-2.0-flash, gpt-4o-mini)"),console.error(" \u2022 Rate limit exceeded \u2014 wait a minute and retry"),console.error(" \u2022 Network/proxy issue blocking api.anthropic.com"),process.exit(1)}});B.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=ot.resolve(process.cwd(),H);ue.existsSync(e)&&!t.force&&(console.error(`
|
|
195
|
+
\u274C [Lisa.ai Init] ${H} already exists. Use --force to overwrite.`),process.exit(1));let n=it.createInterface({input:process.stdin,output:process.stdout}),s=(p,u="")=>new Promise(a=>n.question(u?`${p} [${u}]: `:`${p}: `,m=>a(m.trim()||u)));console.log(`
|
|
163
196
|
\u{1F680} Lisa.ai Project Setup \u2014 press Enter to accept the default shown in [ ]
|
|
164
|
-
`);let o=await
|
|
197
|
+
`);let o=await s("LLM provider (gemini / claude / openai)","gemini"),i=await s("Testing framework (jest / vitest / mocha / karma / cypress / playwright \u2014 leave empty to auto-detect)",""),r=await s("Test types to generate (unit / integration / e2e \u2014 comma-separated)","unit"),l=await s("Test command (leave empty for auto-discovery)",""),g=await s("Max heal retries","5");n.close();let d={provider:o,testTypes:r.split(",").map(p=>p.trim()).filter(Boolean),maxRetries:parseInt(g,10)||5,skipFiles:[],skipDirs:[],skipPaths:[]};i&&(d.testingFramework=i),l&&(d.testCommand=l),ue.writeFileSync(e,JSON.stringify(d,null,2)+`
|
|
165
198
|
`,"utf-8"),console.log(`
|
|
166
|
-
\u2705 Created ${
|
|
199
|
+
\u2705 Created ${H}:
|
|
167
200
|
`),console.log(JSON.stringify(d,null,2)),console.log(`
|
|
168
201
|
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 coverage analysis.
|
|
169
|
-
`)});
|
|
202
|
+
`)});B.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 t=>{de();let e=t.model,n;if(t.projectId){let s=await Q(t.projectId);s.ok?(e=s.config.modelProvider,n=s.config.apiKey,console.log(`[Lisa.ai Audit] Config fetched from Control Plane. Provider=${e}`)):console.warn(`[Lisa.ai Audit] Control Plane ${s.reason}. Using local env for API key.`)}await tt(e,t.projectId||"local",n)});B.parse(process.argv);
|