@zibby/cli 0.1.59 → 0.1.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/auth/cli-login.js +12 -12
  2. package/dist/bin/zibby.js +2 -2
  3. package/dist/commands/agent-reliability.js +4 -4
  4. package/dist/commands/analyze-graph.js +14 -14
  5. package/dist/commands/chat-session-store.js +1 -1
  6. package/dist/commands/chat.js +71 -62
  7. package/dist/commands/ci-setup.js +3 -3
  8. package/dist/commands/generate.js +60 -21
  9. package/dist/commands/implement.js +15 -15
  10. package/dist/commands/init.js +87 -67
  11. package/dist/commands/list-projects.js +9 -9
  12. package/dist/commands/memory.js +13 -13
  13. package/dist/commands/project.js +15 -0
  14. package/dist/commands/run-capacity-queue-cli.js +2 -2
  15. package/dist/commands/run.js +52 -50
  16. package/dist/commands/setup-scripts.js +6 -6
  17. package/dist/commands/studio.js +21 -15
  18. package/dist/commands/uninstall.js +10 -10
  19. package/dist/commands/upload.js +15 -15
  20. package/dist/commands/video.js +1 -1
  21. package/dist/commands/workflow.js +36 -34
  22. package/dist/commands/workflows/deploy.js +14 -24
  23. package/dist/commands/workflows/generate.js +15 -14
  24. package/dist/commands/workflows/list.js +19 -0
  25. package/dist/commands/workflows/logs.js +28 -19
  26. package/dist/commands/workflows/run.js +7 -7
  27. package/dist/commands/workflows/start.js +6 -6
  28. package/dist/commands/workflows/trigger.js +10 -0
  29. package/dist/config/config-loader.js +1 -0
  30. package/dist/config/config.js +1 -1
  31. package/dist/config/environments.js +1 -1
  32. package/dist/package.json +4 -4
  33. package/dist/utils/agent-credentials.js +3 -3
  34. package/dist/utils/chat-run-lifecycle.js +3 -3
  35. package/dist/utils/execution-context.js +1 -1
  36. package/dist/utils/progress-reporter.js +1 -1
  37. package/dist/utils/studio-cli-log-mirror.js +1 -1
  38. package/dist/utils/studio-installer.js +5 -5
  39. package/dist/utils/studio-launcher.js +1 -1
  40. package/package.json +1 -1
@@ -1,9 +1,9 @@
1
- import{existsSync as f,readFileSync as I,writeFileSync as b}from"fs";import{join as o,dirname as x}from"path";import{fileURLToPath as j}from"url";import{invokeAgent as D}from"@zibby/core";import{fetchExecutionContext as L}from"../utils/execution-context.js";import{reportProgress as S,reportFinalStatus as O}from"../utils/progress-reporter.js";const K=j(import.meta.url),F=x(K);async function z(t){const{EXECUTION_ID:i,TICKET_KEY:l,PROJECT_ID:n,REPOS:e,_PRIMARY_REPO:s,_GITHUB_TOKEN:y,MODEL:k}=process.env;(!i||!l||!n)&&(console.error("\u274C Missing required environment variables:"),console.error(" EXECUTION_ID, TICKET_KEY, PROJECT_ID"),process.exit(1));const a=await L(i,n),g=a.ticketContext,m=e?JSON.parse(e):a.repos,R=m.find(_=>_.isPrimary)||m[0],h=process.cwd(),p={status:"running",steps:[]};try{await w("Start Environment",async()=>{}),await w("Clone Repositories",async()=>{const r=process.env.GITHUB_TOKEN,c=process.env.GITLAB_TOKEN||"",u=process.env.GITLAB_URL||"";for(const d of m){const T=o(h,d.name);let E=d.url;const P=d.provider==="gitlab"||u&&d.url.includes(new URL(u).host);if((d.provider==="github"||d.url.includes("github.com"))&&r)E=d.url.replace("https://github.com",`https://${r}@github.com`);else if(P&&c&&u)try{const v=new URL(u).host;E=d.url.replace(`https://${v}`,`https://oauth2:${c}@${v}`)}catch(v){console.warn(`\u26A0\uFE0F Failed to parse GITLAB_URL: ${v.message}`)}if(C(["git","clone",E,T],h),C(["git","checkout",d.branch],T),d.isPrimary){const v=`feature/${l.toLowerCase()}`;C(["git","checkout","-b",v],T)}}p.steps.push({name:"clone",status:"success",repoCount:m.length})});const _=await w("Load Ticket Context",async()=>(p.steps.push({name:"load_ticket",status:"success"}),g));await w("Install Dependencies",async()=>{for(const r of m){const c=o(h,r.name),u=U(c);try{C(u.installCommand,c)}catch{}}p.steps.push({name:"install_deps",status:"success"})});const A=await w("Detect Dev Command",async()=>{const r=o(h,R.name),c=["docker-compose.yml","docker-compose.yaml","compose.yml","compose.yaml"];for(const P of c)if(f(o(r,P)))return p.steps.push({name:"detect_dev",status:"success",command:"docker-compose up",type:"docker-compose"}),{command:"docker-compose up",type:"docker-compose",configFile:P};const u=o(r,"package.json");if(!f(u))return console.log(" \u26A0\uFE0F No package.json or docker-compose found"),p.steps.push({name:"detect_dev",status:"skipped"}),null;const T=JSON.parse(I(u,"utf-8")).scripts||{};let E=null;return T.dev?E="npm run dev":T.start?E="npm start":T["dev:local"]&&(E="npm run dev:local"),E?(p.steps.push({name:"detect_dev",status:"success",command:E,type:"npm"}),{command:E,type:"npm"}):(p.steps.push({name:"detect_dev",status:"skipped"}),null)});await w("Start Dev Server",async()=>{const r=o(h,R.name),c="docker-compose.test.yml";return f(o(r,c))?(C(["docker","compose","-f",c,"up","-d"],r),await new Promise(u=>setTimeout(u,1e4)),p.steps.push({name:"start_server",status:"success"}),!0):(console.log(` \u26A0\uFE0F No ${c} found, skipping server startup`),p.steps.push({name:"start_server",status:"skipped"}),null)}),await w("Run AI Agent Implementation",async()=>{const r=m.map(d=>{const T=o(h,d.name);return{...d,...U(T)}}),c=G(_,r,A),u=o(h,".cursor-prompt.md");b(u,c),await D(c,{state:{model:k,workspace:h}},{print:!0}),p.steps.push({name:"ai_agent",status:"success"})});const J=await w("Run E2E Tests",async()=>{const r=o(h,R.name);if(!f(o(r,"playwright.config.js"))&&!f(o(r,"playwright.config.ts")))return p.steps.push({name:"e2e_tests",status:"skipped"}),null;try{return C("npx playwright test --reporter=json",r),p.steps.push({name:"e2e_tests",status:"success"}),{passed:!0}}catch(c){throw C("docker compose -f docker-compose.test.yml down",r,{allowFailure:!0}),new Error(`E2E tests failed: ${c.message}`,{cause:c})}});try{C("docker compose -f docker-compose.test.yml down",o(h,R.name),{allowFailure:!0})}catch{}const $=await w("Create Pull Request",async()=>{const r=o(h,R.name),c=`feature/${l.toLowerCase()}`;return C(["git","add","."],r),C(["git","commit","-m",`feat(${l}): ${_.summary}`],r),C(["git","push","origin",c],r),console.log(" \u26A0\uFE0F PR creation via API removed (using SQS flow)"),p.steps.push({name:"create_pr",status:"skipped"}),null});await w("Report Results",async()=>{const r=o(h,R.name),c=o(r,"test-results"),u=[];f(c),p.status="completed",p.prUrl=$,p.videoUrls=u,await O(N(),{status:"completed",artifacts:{prUrl:$,videoUrls:u}})}),process.exit(0)}catch(_){console.error(""),console.error("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),console.error("\u2551 \u274C FAILED! \u2551"),console.error("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"),console.error(""),console.error("Error:",_.message),console.error("Stack:",_.stack);try{await O(N(),{status:"failed",error:_.message})}catch(A){console.error("Failed to report error:",A.message)}process.exit(1)}}function N(){return{EXECUTION_ID:process.env.EXECUTION_ID,PROGRESS_API_URL:process.env.PROGRESS_API_URL,PROGRESS_QUEUE_URL:process.env.PROGRESS_QUEUE_URL,SQS_AUTH_TOKEN:process.env.SQS_AUTH_TOKEN,PROJECT_API_TOKEN:process.env.PROJECT_API_TOKEN}}async function w(t,i){const l=Date.now(),n=[];let e="";const s=console.log;console.log=(...a)=>{const g=a.join(" ");n.push(g),s(...a)};const y=N(),k=setInterval(()=>{const a=n.join(`
2
- `);a!==e&&a.length>0&&(e=a,S(t,"running",a,y).catch(()=>{}))},2e3);try{await S(t,"running","",y);const a=await i(),g=`${((Date.now()-l)/1e3).toFixed(1)}s`;clearInterval(k),console.log=s;const m=n.join(`
3
- `);return await S(t,"success",m||`Completed in ${g}`,y),a}catch(a){clearInterval(k),console.log=s;const g=n.join(`
4
- `);throw await S(t,"failed",`${g}
1
+ var A=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(r,n)=>(typeof require<"u"?require:r)[n]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});import{existsSync as h,readFileSync as O,writeFileSync as J}from"fs";import{join as i,dirname as B}from"path";import{fileURLToPath as Q}from"url";import{invokeAgent as X}from"@zibby/core";async function b(t,r){let n=process.env.CONTEXT_PRESIGNED_URL;if(!n)throw new Error("CONTEXT_PRESIGNED_URL env var is required");console.log("\u{1F4E6} Fetching execution context via pre-signed URL");let o=await fetch(n);if(!o.ok)throw new Error(`Failed to fetch execution context: ${o.status}`);let e=await o.json();return console.log(` \u2705 Got ticketContext (${JSON.stringify(e.ticketContext||{}).length} chars)`),e.nodeConfigs&&Object.keys(e.nodeConfigs).length>0&&console.log(` \u2705 Got nodeConfigs (${Object.keys(e.nodeConfigs).length} nodes configured)`),{ticketContext:e.ticketContext||{},nodeConfigs:e.nodeConfigs||{},graphConfig:e.graphConfig||null,repos:e.repos||[]}}import{SQSClient as K,SendMessageCommand as F}from"@aws-sdk/client-sqs";var v=null;function G(){return v||(v=new K({region:process.env.AWS_REGION||"ap-southeast-2"})),v}async function k(t,r,n,o){let{EXECUTION_ID:e,SQS_AUTH_TOKEN:s,PROGRESS_API_URL:c,PROGRESS_QUEUE_URL:f,PROJECT_API_TOKEN:a}=o;if(!e)return;let p={executionId:e,...s&&{sqsAuthToken:s},step:{name:t,status:r,logs:n,timestamp:new Date().toISOString(),...r==="success"&&{completedAt:new Date().toISOString()}},status:r==="failed"?"failed":"running"};try{c?await D(c,e,p,a):f&&await L(f,e,p)}catch(m){console.error(`\u26A0\uFE0F Failed to send progress: ${m.message}`)}}async function U(t,{status:r,error:n}){let{EXECUTION_ID:o,SQS_AUTH_TOKEN:e,PROGRESS_API_URL:s,PROGRESS_QUEUE_URL:c,PROJECT_API_TOKEN:f}=t;if(!o)return;let a={executionId:o,...e&&{sqsAuthToken:e},status:r,...n&&{error:n},timestamp:new Date().toISOString()},p=s?"HTTP":c?"SQS":"NONE",m=JSON.stringify(a).length;console.log(`\u{1F4CA} Sending final status: ${r} via ${p} (${(m/1024).toFixed(1)}KB)`);try{if(s)await D(s,o,a,f);else if(c){let y=["completed","failed","insufficient_context","blocked"].includes(r)?"execution_completed":"progress_update";await L(c,o,a,y)}else{console.warn("\u26A0\uFE0F No transport configured for final status \u2014 neither PROGRESS_API_URL nor PROGRESS_QUEUE_URL set");return}console.log(`\u2705 Final status ${r} sent via ${p}`)}catch(y){console.error(`\u274C Failed to send final status (${r}) via ${p}:`),console.error(` Payload: ${(m/1024).toFixed(1)}KB`),console.error(` Error: ${y.message}`),y.name&&console.error(` Error type: ${y.name}`),y.code&&console.error(` Error code: ${y.code}`)}}async function D(t,r,n,o){let e=`${t}/${r}/progress`,s={"Content-Type":"application/json"};o&&(s.Authorization=`Bearer ${o}`);let c=await fetch(e,{method:"POST",headers:s,body:JSON.stringify(n)});if(!c.ok){let f=await c.text();throw new Error(`HTTP ${c.status}: ${f}`)}}async function L(t,r,n,o="progress_update"){let e=JSON.stringify(n),s=(e.length/1024).toFixed(1);e.length>256*1024&&console.error(`\u274C SQS message too large: ${s}KB (limit 256KB) for ${r} [${o}]`),await G().send(new F({QueueUrl:t,MessageBody:e,MessageGroupId:r,MessageAttributes:{executionId:{DataType:"String",StringValue:r},messageType:{DataType:"String",StringValue:o}}}))}var H=Q(import.meta.url),M=B(H);async function ie(t){let{EXECUTION_ID:r,TICKET_KEY:n,PROJECT_ID:o,REPOS:e,_PRIMARY_REPO:s,_GITHUB_TOKEN:c,MODEL:f}=process.env;(!r||!n||!o)&&(console.error("\u274C Missing required environment variables:"),console.error(" EXECUTION_ID, TICKET_KEY, PROJECT_ID"),process.exit(1));let a=await b(r,o),p=a.ticketContext,m=e?JSON.parse(e):a.repos,y=m.find(w=>w.isPrimary)||m[0],_=process.cwd(),d={status:"running",steps:[]};try{await T("Start Environment",async()=>{}),await T("Clone Repositories",async()=>{let l=process.env.GITHUB_TOKEN,u=process.env.GITLAB_TOKEN||"",g=process.env.GITLAB_URL||"";for(let E of m){let R=i(_,E.name),S=E.url,I=E.provider==="gitlab"||g&&E.url.includes(new URL(g).host);if((E.provider==="github"||E.url.includes("github.com"))&&l)S=E.url.replace("https://github.com",`https://${l}@github.com`);else if(I&&u&&g)try{let P=new URL(g).host;S=E.url.replace(`https://${P}`,`https://oauth2:${u}@${P}`)}catch(P){console.warn(`\u26A0\uFE0F Failed to parse GITLAB_URL: ${P.message}`)}if(C(["git","clone",S,R],_),C(["git","checkout",E.branch],R),E.isPrimary){let P=`feature/${n.toLowerCase()}`;C(["git","checkout","-b",P],R)}}d.steps.push({name:"clone",status:"success",repoCount:m.length})});let w=await T("Load Ticket Context",async()=>(d.steps.push({name:"load_ticket",status:"success"}),p));await T("Install Dependencies",async()=>{for(let l of m){let u=i(_,l.name),g=j(u);try{C(g.installCommand,u)}catch{}}d.steps.push({name:"install_deps",status:"success"})});let $=await T("Detect Dev Command",async()=>{let l=i(_,y.name),u=["docker-compose.yml","docker-compose.yaml","compose.yml","compose.yaml"];for(let I of u)if(h(i(l,I)))return d.steps.push({name:"detect_dev",status:"success",command:"docker-compose up",type:"docker-compose"}),{command:"docker-compose up",type:"docker-compose",configFile:I};let g=i(l,"package.json");if(!h(g))return console.log(" \u26A0\uFE0F No package.json or docker-compose found"),d.steps.push({name:"detect_dev",status:"skipped"}),null;let R=JSON.parse(O(g,"utf-8")).scripts||{},S=null;return R.dev?S="npm run dev":R.start?S="npm start":R["dev:local"]&&(S="npm run dev:local"),S?(d.steps.push({name:"detect_dev",status:"success",command:S,type:"npm"}),{command:S,type:"npm"}):(d.steps.push({name:"detect_dev",status:"skipped"}),null)});await T("Start Dev Server",async()=>{let l=i(_,y.name),u="docker-compose.test.yml";return h(i(l,u))?(C(["docker","compose","-f",u,"up","-d"],l),await new Promise(g=>setTimeout(g,1e4)),d.steps.push({name:"start_server",status:"success"}),!0):(console.log(` \u26A0\uFE0F No ${u} found, skipping server startup`),d.steps.push({name:"start_server",status:"skipped"}),null)}),await T("Run AI Agent Implementation",async()=>{let l=m.map(E=>{let R=i(_,E.name);return{...E,...j(R)}}),u=q(w,l,$),g=i(_,".cursor-prompt.md");J(g,u),await X(u,{state:{model:f,workspace:_}},{print:!0}),d.steps.push({name:"ai_agent",status:"success"})});let Y=await T("Run E2E Tests",async()=>{let l=i(_,y.name);if(!h(i(l,"playwright.config.js"))&&!h(i(l,"playwright.config.ts")))return d.steps.push({name:"e2e_tests",status:"skipped"}),null;try{return C("npx playwright test --reporter=json",l),d.steps.push({name:"e2e_tests",status:"success"}),{passed:!0}}catch(u){throw C("docker compose -f docker-compose.test.yml down",l,{allowFailure:!0}),new Error(`E2E tests failed: ${u.message}`,{cause:u})}});try{C("docker compose -f docker-compose.test.yml down",i(_,y.name),{allowFailure:!0})}catch{}let x=await T("Create Pull Request",async()=>{let l=i(_,y.name),u=`feature/${n.toLowerCase()}`;return C(["git","add","."],l),C(["git","commit","-m",`feat(${n}): ${w.summary}`],l),C(["git","push","origin",u],l),console.log(" \u26A0\uFE0F PR creation via API removed (using SQS flow)"),d.steps.push({name:"create_pr",status:"skipped"}),null});await T("Report Results",async()=>{let l=i(_,y.name),u=i(l,"test-results"),g=[];h(u),d.status="completed",d.prUrl=x,d.videoUrls=g,await U(N(),{status:"completed",artifacts:{prUrl:x,videoUrls:g}})}),process.exit(0)}catch(w){console.error(""),console.error("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),console.error("\u2551 \u274C FAILED! \u2551"),console.error("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"),console.error(""),console.error("Error:",w.message),console.error("Stack:",w.stack);try{await U(N(),{status:"failed",error:w.message})}catch($){console.error("Failed to report error:",$.message)}process.exit(1)}}function N(){return{EXECUTION_ID:process.env.EXECUTION_ID,PROGRESS_API_URL:process.env.PROGRESS_API_URL,PROGRESS_QUEUE_URL:process.env.PROGRESS_QUEUE_URL,SQS_AUTH_TOKEN:process.env.SQS_AUTH_TOKEN,PROJECT_API_TOKEN:process.env.PROJECT_API_TOKEN}}async function T(t,r){let n=Date.now(),o=[],e="",s=console.log;console.log=(...a)=>{let p=a.join(" ");o.push(p),s(...a)};let c=N(),f=setInterval(()=>{let a=o.join(`
2
+ `);a!==e&&a.length>0&&(e=a,k(t,"running",a,c).catch(()=>{}))},2e3);try{await k(t,"running","",c);let a=await r(),p=`${((Date.now()-n)/1e3).toFixed(1)}s`;clearInterval(f),console.log=s;let m=o.join(`
3
+ `);return await k(t,"success",m||`Completed in ${p}`,c),a}catch(a){clearInterval(f),console.log=s;let p=o.join(`
4
+ `);throw await k(t,"failed",`${p}
5
5
 
6
- Error: ${a.message}`,y),a}}function C(t,i,l={}){try{const{spawnSync:n}=require("child_process");let e;if(Array.isArray(t)){const[s,...y]=t;e=n(s,y,{cwd:i,encoding:"utf-8",stdio:["pipe","pipe","pipe"]})}else e=n(t,{cwd:i,shell:!0,encoding:"utf-8",stdio:["pipe","pipe","pipe"]});if(e.stdout&&console.log(e.stdout),e.stderr&&console.log(e.stderr),e.status!==0&&!l.allowFailure){const s=Array.isArray(t)?t.join(" "):t;throw new Error(`Command failed with exit code ${e.status}: ${s}`)}return e.stdout||e.stderr}catch(n){if(l.allowFailure)return null;throw n}}function U(t){const i=o(t,".zibby.yml");if(f(i))try{const e=require("js-yaml").load(I(i,"utf-8"));return{name:e.name||"Custom Project",framework:e.framework||"Custom",language:e.language||"Custom",testCommand:e.test||"make test",installCommand:e.install||"make install",custom:!0}}catch{console.warn("Invalid .zibby.yml, falling back to auto-detection")}const l=o(t,"package.json");if(f(l)){const n=JSON.parse(I(l,"utf-8")),e={...n.dependencies,...n.devDependencies};let s="Node.js";return e.next?s="Next.js":e["react-scripts"]?s="Create React App":e.vite&&e.react?s="React + Vite":e["@angular/core"]?s="Angular":e.vue?s="Vue.js":e.express&&(s="Express.js"),{name:n.name||"Unknown Project",framework:s,language:"JavaScript/TypeScript",testCommand:n.scripts?.test||"npm test",installCommand:"npm install"}}return f(o(t,"requirements.txt"))||f(o(t,"pyproject.toml"))?{name:"Python Project",framework:f(o(t,"manage.py"))?"Django":f(o(t,"app.py"))?"Flask":"Python",language:"Python",testCommand:"pytest",installCommand:"pip install -r requirements.txt"}:f(o(t,"Gemfile"))?{name:"Ruby Project",framework:"Rails",language:"Ruby",testCommand:"bundle exec rspec",installCommand:"bundle install"}:f(o(t,"go.mod"))?{name:"Go Project",framework:"Go",language:"Go",testCommand:"go test ./...",installCommand:"go mod download"}:f(o(t,"pom.xml"))?{name:"Java Project",framework:"Spring Boot",language:"Java",testCommand:"./mvnw test",installCommand:"./mvnw install"}:{name:"Unknown Project",framework:"Unknown",language:"Unknown",testCommand:"make test",installCommand:"make install"}}function G(t,i,l){const n=o(F,"../../prompts/implement-ticket.md");let e;try{e=I(n,"utf-8")}catch{e=`
6
+ Error: ${a.message}`,c),a}}function C(t,r,n={}){try{let{spawnSync:o}=A("child_process"),e;if(Array.isArray(t)){let[s,...c]=t;e=o(s,c,{cwd:r,encoding:"utf-8",stdio:["pipe","pipe","pipe"]})}else e=o(t,{cwd:r,shell:!0,encoding:"utf-8",stdio:["pipe","pipe","pipe"]});if(e.stdout&&console.log(e.stdout),e.stderr&&console.log(e.stderr),e.status!==0&&!n.allowFailure){let s=Array.isArray(t)?t.join(" "):t;throw new Error(`Command failed with exit code ${e.status}: ${s}`)}return e.stdout||e.stderr}catch(o){if(n.allowFailure)return null;throw o}}function j(t){let r=i(t,".zibby.yml");if(h(r))try{let e=A("js-yaml").load(O(r,"utf-8"));return{name:e.name||"Custom Project",framework:e.framework||"Custom",language:e.language||"Custom",testCommand:e.test||"make test",installCommand:e.install||"make install",custom:!0}}catch{console.warn("Invalid .zibby.yml, falling back to auto-detection")}let n=i(t,"package.json");if(h(n)){let o=JSON.parse(O(n,"utf-8")),e={...o.dependencies,...o.devDependencies},s="Node.js";return e.next?s="Next.js":e["react-scripts"]?s="Create React App":e.vite&&e.react?s="React + Vite":e["@angular/core"]?s="Angular":e.vue?s="Vue.js":e.express&&(s="Express.js"),{name:o.name||"Unknown Project",framework:s,language:"JavaScript/TypeScript",testCommand:o.scripts?.test||"npm test",installCommand:"npm install"}}return h(i(t,"requirements.txt"))||h(i(t,"pyproject.toml"))?{name:"Python Project",framework:h(i(t,"manage.py"))?"Django":h(i(t,"app.py"))?"Flask":"Python",language:"Python",testCommand:"pytest",installCommand:"pip install -r requirements.txt"}:h(i(t,"Gemfile"))?{name:"Ruby Project",framework:"Rails",language:"Ruby",testCommand:"bundle exec rspec",installCommand:"bundle install"}:h(i(t,"go.mod"))?{name:"Go Project",framework:"Go",language:"Go",testCommand:"go test ./...",installCommand:"go mod download"}:h(i(t,"pom.xml"))?{name:"Java Project",framework:"Spring Boot",language:"Java",testCommand:"./mvnw test",installCommand:"./mvnw install"}:{name:"Unknown Project",framework:"Unknown",language:"Unknown",testCommand:"make test",installCommand:"make install"}}function q(t,r,n){let o=i(M,"../../prompts/implement-ticket.md"),e;try{e=O(o,"utf-8")}catch{e=`
7
7
  # Implement Ticket: {{TICKET_KEY}}
8
8
 
9
9
  ## Project Context
@@ -34,32 +34,32 @@ You are implementing this ticket. Follow these steps:
34
34
  5. Fix any linter errors
35
35
 
36
36
  Now implement this ticket completely!
37
- `.trim()}const s=i.find(g=>g.isPrimary)||i[0];let y;l?.type==="docker-compose"?y=`\`docker-compose up\` (using ${l.configFile})`:l?.command?y=`\`cd ${s.name} && ${l.command}\``:y="`npm run dev` (or check package.json scripts)";let k;if(i.length===1)k=`
37
+ `.trim()}let s=r.find(p=>p.isPrimary)||r[0],c;n?.type==="docker-compose"?c=`\`docker-compose up\` (using ${n.configFile})`:n?.command?c=`\`cd ${s.name} && ${n.command}\``:c="`npm run dev` (or check package.json scripts)";let f;if(r.length===1)f=`
38
38
  You are working in **${s.name}**, a ${s.framework} project.
39
39
 
40
40
  **Commands:**
41
- - Dev server: ${y}
41
+ - Dev server: ${c}
42
42
  - Run tests: \`cd ${s.name} && ${s.testCommand}\`
43
43
 
44
44
  You have full access to the codebase in the current directory.
45
- `.trim();else{const g=i.map(m=>`- **${m.name}/** (${m.framework})${m.isPrimary?" \u2190 **MAKE CHANGES HERE**":" (reference only)"}`).join(`
46
- `);k=`
47
- You are working in a **multi-repository** setup with ${i.length} repositories:
45
+ `.trim();else{let p=r.map(m=>`- **${m.name}/** (${m.framework})${m.isPrimary?" \u2190 **MAKE CHANGES HERE**":" (reference only)"}`).join(`
46
+ `);f=`
47
+ You are working in a **multi-repository** setup with ${r.length} repositories:
48
48
 
49
- ${g}
49
+ ${p}
50
50
 
51
51
  **Primary Repository:** ${s.name}
52
52
  - This is where you should implement the feature
53
53
  - Framework: ${s.framework}
54
- - Dev server: ${y}
54
+ - Dev server: ${c}
55
55
  - Run tests: \`cd ${s.name} && ${s.testCommand}\`
56
56
 
57
57
  **Other Repositories:**
58
- ${i.filter(m=>!m.isPrimary).map(m=>`- **${m.name}**: You can read code from here for reference (shared libraries, services, etc.)`).join(`
58
+ ${r.filter(m=>!m.isPrimary).map(m=>`- **${m.name}**: You can read code from here for reference (shared libraries, services, etc.)`).join(`
59
59
  `)||"(none)"}
60
60
 
61
61
  **Important:** Make all code changes in the \`${s.name}/\` directory only.
62
- `.trim()}let a=e.replace(/\{\{TICKET_KEY\}\}/g,t.ticketKey||t.key||"UNKNOWN").replace(/\{\{PROJECT_CONTEXT\}\}/g,k).replace(/\{\{TICKET_SUMMARY\}\}/g,t.summary||"No summary").replace(/\{\{TICKET_DESCRIPTION\}\}/g,t.description||"No description provided").replace(/\{\{ACCEPTANCE_CRITERIA\}\}/g,t.acceptanceCriteria||"Not specified");if(t.additionalContext){const g=`## Additional Context from User
62
+ `.trim()}let a=e.replace(/\{\{TICKET_KEY\}\}/g,t.ticketKey||t.key||"UNKNOWN").replace(/\{\{PROJECT_CONTEXT\}\}/g,f).replace(/\{\{TICKET_SUMMARY\}\}/g,t.summary||"No summary").replace(/\{\{TICKET_DESCRIPTION\}\}/g,t.description||"No description provided").replace(/\{\{ACCEPTANCE_CRITERIA\}\}/g,t.acceptanceCriteria||"Not specified");if(t.additionalContext){let p=`## Additional Context from User
63
63
  ${t.additionalContext}
64
64
 
65
- `;a=a.replace(/\{\{#if ADDITIONAL_CONTEXT\}\}[\s\S]*?\{\{\/if\}\}/g,g)}else a=a.replace(/\{\{#if ADDITIONAL_CONTEXT\}\}[\s\S]*?\{\{\/if\}\}/g,"");return a}export{z as implementCommand};
65
+ `;a=a.replace(/\{\{#if ADDITIONAL_CONTEXT\}\}[\s\S]*?\{\{\/if\}\}/g,p)}else a=a.replace(/\{\{#if ADDITIONAL_CONTEXT\}\}[\s\S]*?\{\{\/if\}\}/g,"");return a}export{ie as implementCommand};
@@ -1,57 +1,77 @@
1
- import{mkdir as S,writeFile as p,readFile as w}from"fs/promises";import{existsSync as h,readdirSync as G}from"fs";import{join as a,resolve as O,dirname as H}from"path";import{homedir as P}from"os";import Y from"inquirer";import e from"chalk";import I from"ora";import{spawn as K,execSync as R}from"child_process";import{fileURLToPath as V}from"url";import{createRequire as Z}from"module";import{AGENT_KEY_MAP as D,saveAgentApiKey as F,readGlobalConfig as W}from"../utils/agent-credentials.js";const U=V(import.meta.url),q=H(U),N=Z(import.meta.url);async function X(){try{const d=process.platform==="win32",o=R("npm config get prefix",{encoding:"utf-8"}).trim(),f=d?o:`${o}/bin`,m=f.replace(/^~/,P()),l=d?";":":";if(process.env.PATH.split(l).some(y=>{const x=y.replace(/^~/,P());return x===m||x===f}))return;if(process.env.CI||process.env.ZIBBY_CI||!process.stdin.isTTY){console.log(e.yellow("\u26A0\uFE0F npm global bin not in PATH")),console.log(e.gray(` Location: ${m}`)),console.log(e.gray(` Add it manually: export PATH="${m}:$PATH"
2
- `));return}console.log(e.yellow("\u26A0\uFE0F npm global bin not in PATH")),console.log(e.gray(` Location: ${m}`)),console.log();const{shouldAddPath:s}=await Y.prompt([{type:"confirm",name:"shouldAddPath",message:"Add npm global bin to your shell PATH automatically?",default:!0}]);if(!s){d?(console.log(e.gray(`
3
- \u{1F4A1} To add manually on Windows:`)),console.log(e.gray(' 1. Search "Environment Variables" in Start menu')),console.log(e.gray(' 2. Edit "Path" in User variables')),console.log(e.gray(` 3. Add: ${m}
4
- `))):(console.log(e.gray(`
5
- \u{1F4A1} To add manually, run:`)),console.log(e.gray(` echo 'export PATH="${m}:$PATH"' >> ~/.zshrc`)),console.log(e.gray(` source ~/.zshrc
6
- `)));return}if(d){console.log(e.yellow("\u26A0\uFE0F Cannot auto-add PATH on Windows")),console.log(e.gray(" Please add manually:")),console.log(e.gray(' 1. Search "Environment Variables" in Start menu')),console.log(e.gray(' 2. Edit "Path" in User variables')),console.log(e.gray(` 3. Add: ${m}
7
- `));return}const C=process.env.SHELL||"";let g="";if(C.includes("zsh"))g=a(P(),".zshrc");else if(C.includes("bash"))g=h(a(P(),".bashrc"))?a(P(),".bashrc"):a(P(),".bash_profile");else{console.log(e.yellow(`\u26A0\uFE0F Unknown shell: ${C}`)),console.log(e.gray(" Please add manually:")),console.log(e.gray(` export PATH="${m}:$PATH"`));return}if(h(g)){const y=await w(g,"utf-8");if(y.includes(m)||y.includes("npm")&&y.includes("global")&&y.includes("bin")){console.log(e.yellow(`\u26A0\uFE0F PATH entry found in ${g} but not active`)),console.log(e.gray(` Run: source ${g}
8
- `));return}}const v=`
1
+ var me=Object.defineProperty;var ge=(o,e)=>()=>(o&&(e=o(o=0)),e);var fe=(o,e)=>{for(var a in e)me(o,a,{get:e[a],enumerable:!0})};var le={};fe(le,{setupCiCommand:()=>$e,setupPlaywrightMcpCommand:()=>ae,testWithVideoCommand:()=>Ke});import{spawn as H,execSync as F}from"child_process";import{join as K,dirname as W,resolve as te}from"path";import{readFileSync as ne,writeFileSync as oe,existsSync as T,mkdirSync as se}from"fs";import{homedir as ie}from"os";import{createRequire as ve}from"module";import x from"chalk";function xe(){try{return W(G.resolve("@zibby/core/package.json"))}catch{return null}}function re(o){try{return F(`command -v ${o}`,{stdio:"pipe"}),!0}catch{return!1}}function Pe(){try{let o=G.resolve("@zibby/mcp-browser/bin/mcp-browser-zibby.js");if(T(o))return o}catch{}try{let o=G.resolve("@playwright/mcp/package.json"),e=W(o),a=JSON.parse(ne(o,"utf-8")),l=typeof a.bin=="string"?a.bin:a.bin?.["playwright-mcp"];if(l){let m=K(e,l);if(T(m))return m}let t=K(e,"cli.js");if(T(t))return t}catch{}return null}function Q(o,e,a={}){return new Promise((l,t)=>{let m=H(o,e,{stdio:"inherit",...a});m.on("close",y=>l(y??1)),m.on("error",t)})}async function Ce(o){return re("playwright")?await Q("playwright",["install","ffmpeg"],{cwd:o})===0:await Q("npx",["--yes","playwright","install","ffmpeg"],{cwd:o})===0}function Ae({headed:o=!1,viewport:e=null}){let a=process.execPath,l=W(a),t=Pe(),m=Number(e?.width)||1280,y=Number(e?.height)||720,r=`${m}x${y}`,w=[];w.push(`--viewport-size=${r}`,"--output-dir=test-results"),o||w.push("--headless");let g;t?g={command:a,args:[t,...w],env:{PATH:`${l}:/usr/bin:/bin:/usr/sbin:/sbin`}}:g={command:"npx",args:["-y","@playwright/mcp",...w],env:{PATH:`${l}:/usr/bin:/bin:/usr/sbin:/sbin`}},g.description="Zibby MCP Browser - Playwright MCP with Zibby defaults";let C=K(ie(),".cursor"),f=K(C,"mcp.json");T(C)||se(C,{recursive:!0});let v={mcpServers:{}};if(T(f))try{v=JSON.parse(ne(f,"utf-8")),(!v.mcpServers||typeof v.mcpServers!="object")&&(v.mcpServers={})}catch{v={mcpServers:{}}}v.mcpServers["playwright-official"]=g,oe(f,`${JSON.stringify(v,null,2)}
2
+ `,"utf-8")}async function Ie(){let o=xe();if(!o)return;let e=K(o,"scripts","patch-cursor-mcp.js");if(T(e))try{await new Promise((a,l)=>{let t=H(process.execPath,[e],{cwd:process.cwd(),stdio:"ignore"});t.on("close",m=>a(m??0)),t.on("error",l)})}catch{}}function Ee(o){return te(o).replace(/^\/+/,"").replace(/\//g,"-")}function ke(o){let e=String(o||""),a=e.split(`
3
+ `).find(m=>/playwright-official/i.test(m)&&/APPROVAL|🔑/i.test(m));if(a){let m=a.match(/=>\s*(\S+)/);if(m)return m[1]}let l=e.match(/playwright-official[^=]*=>\s*(\S+)/);if(l)return l[1];let t=e.match(/playwright-official-[a-f0-9]+/i);return t?t[0]:null}function Se(o,e){let a=Ee(o),l=K(ie(),".cursor","projects",a);se(l,{recursive:!0});let t=K(l,"mcp-approvals.json");return oe(t,`${JSON.stringify([e],null,2)}
4
+ `,"utf-8"),t}function ee(o){try{return F("cursor-agent mcp list",{cwd:o,encoding:"utf-8",stdio:["ignore","pipe","pipe"],env:process.env,maxBuffer:2*1024*1024})}catch(e){let a=e.stdout!=null?String(e.stdout):"",l=e.stderr!=null?String(e.stderr):"";return a+l}}function _e(o){try{return F("cursor-agent mcp enable playwright-official",{cwd:o,encoding:"utf-8",stdio:["ignore","pipe","pipe"],env:process.env,maxBuffer:1024*1024}),!0}catch{return!1}}async function ae(o={}){let e=!!o.headed,a=o.viewport||{width:1280,height:720};if(!process.execPath)throw new Error("Node.js runtime not found");let l=await Ce(process.cwd());console.log(l?x.gray(" \u2713 ffmpeg installed for Playwright video"):x.yellow(" \u26A0 Could not install ffmpeg (video may be unavailable)")),Ae({headed:e,viewport:a}),await Ie();let t=te(process.cwd());if(re("cursor-agent")){ee(t);let m=_e(t);m&&console.log(x.gray(" \u2713 cursor-agent: mcp enable playwright-official"));let y=ee(t),r=ke(y);if(r)try{let w=Se(t,r);console.log(x.gray(` \u2713 MCP auto-approval key saved (${w})`))}catch(w){console.log(x.yellow(` \u26A0 Could not write mcp-approvals.json: ${w.message}`))}else m||(console.log(x.yellow(" \u26A0 Could not auto-approve MCP (mcp enable failed and no approval key in cursor-agent output)")),console.log(x.gray(" Try: cursor-agent mcp enable playwright-official")))}else console.log(x.yellow(" \u26A0 cursor-agent not found; install it to use Cursor MCP tools"))}async function $e(o){try{console.log(x.cyan(`
5
+ \u{1F680} Running CI setup...
6
+ `)),await ae({headed:!1,cloudSync:!0,viewport:{width:1280,height:720}}),console.log(x.green(`
7
+ \u2705 CI/CD setup complete!
8
+ `))}catch(e){console.log(x.red(`
9
+ \u274C Error: ${e.message}
10
+ `)),process.exit(1)}}async function Ke(o,e){try{let a=["playwright","test",o||"tests/","--project=chromium"];e.headed&&a.push("--headed"),console.log(x.cyan(`
11
+ \u{1F3A5} Running tests with video recording...
12
+ `)),console.log(x.gray("\u2501".repeat(50)));let l=H("npx",a,{cwd:process.cwd(),stdio:"inherit"});l.on("close",t=>{t===0?console.log(x.green(`
13
+ \u2705 Tests complete!
14
+ `)):(console.log(x.red(`
15
+ \u274C Tests failed with code ${t}
16
+ `)),process.exit(t))}),l.on("error",t=>{console.log(x.red(`
17
+ \u274C Error: ${t.message}
18
+ `)),process.exit(1)})}catch(a){console.log(x.red(`
19
+ \u274C Error: ${a.message}
20
+ `)),process.exit(1)}}var G,ce=ge(()=>{G=ve(import.meta.url)});import{mkdir as M,writeFile as h,readFile as A}from"fs/promises";import{existsSync as P,readdirSync as Me}from"fs";import{join as p,resolve as pe,dirname as Be}from"path";import{homedir as k}from"os";import D from"inquirer";import n from"chalk";import I from"ora";import{spawn as R,execSync as L}from"child_process";import{fileURLToPath as ze}from"url";import{createRequire as de}from"module";import{existsSync as ye,readFileSync as he,writeFileSync as be,mkdirSync as we}from"fs";import{join as q}from"path";import{homedir as U}from"os";var O={cursor:{envVar:"CURSOR_API_KEY",label:"Cursor API Key",url:"https://cursor.com/settings"},claude:{envVar:"ANTHROPIC_API_KEY",label:"Anthropic API Key",url:"https://console.anthropic.com/settings/keys"},codex:{envVar:"OPENAI_API_KEY",label:"OpenAI API Key",url:"https://platform.openai.com/api-keys"},gemini:{envVar:"GEMINI_API_KEY",label:"Gemini API Key",url:"https://aistudio.google.com/app/apikey"}};function J(){return q(U(),".zibby","config.json")}function V(){try{let o=J();return ye(o)?JSON.parse(he(o,"utf-8")):{}}catch{return{}}}function X(o,e){let a=q(U(),".zibby");we(a,{recursive:!0});let l=J(),t=V(),m=e?O[e]:null;m&&((!t.agentKeys||typeof t.agentKeys!="object")&&(t.agentKeys={}),t.agentKeys[m.envVar]=String(o).trim()),delete t.agentApiKey,be(l,`${JSON.stringify(t,null,2)}
21
+ `,"utf-8")}var Te=ze(import.meta.url),Re=Be(Te),ue=de(import.meta.url);async function Ye(){try{let o=process.platform==="win32",e=L("npm config get prefix",{encoding:"utf-8"}).trim(),a=o?e:`${e}/bin`,l=a.replace(/^~/,k()),t=o?";":":";if(process.env.PATH.split(t).some(f=>{let v=f.replace(/^~/,k());return v===l||v===a}))return;if(process.env.CI||process.env.ZIBBY_CI||!process.stdin.isTTY){console.log(n.yellow("\u26A0\uFE0F npm global bin not in PATH")),console.log(n.gray(` Location: ${l}`)),console.log(n.gray(` Add it manually: export PATH="${l}:$PATH"
22
+ `));return}console.log(n.yellow("\u26A0\uFE0F npm global bin not in PATH")),console.log(n.gray(` Location: ${l}`)),console.log();let{shouldAddPath:r}=await D.prompt([{type:"confirm",name:"shouldAddPath",message:"Add npm global bin to your shell PATH automatically?",default:!0}]);if(!r){o?(console.log(n.gray(`
23
+ \u{1F4A1} To add manually on Windows:`)),console.log(n.gray(' 1. Search "Environment Variables" in Start menu')),console.log(n.gray(' 2. Edit "Path" in User variables')),console.log(n.gray(` 3. Add: ${l}
24
+ `))):(console.log(n.gray(`
25
+ \u{1F4A1} To add manually, run:`)),console.log(n.gray(` echo 'export PATH="${l}:$PATH"' >> ~/.zshrc`)),console.log(n.gray(` source ~/.zshrc
26
+ `)));return}if(o){console.log(n.yellow("\u26A0\uFE0F Cannot auto-add PATH on Windows")),console.log(n.gray(" Please add manually:")),console.log(n.gray(' 1. Search "Environment Variables" in Start menu')),console.log(n.gray(' 2. Edit "Path" in User variables')),console.log(n.gray(` 3. Add: ${l}
27
+ `));return}let w=process.env.SHELL||"",g="";if(w.includes("zsh"))g=p(k(),".zshrc");else if(w.includes("bash"))g=P(p(k(),".bashrc"))?p(k(),".bashrc"):p(k(),".bash_profile");else{console.log(n.yellow(`\u26A0\uFE0F Unknown shell: ${w}`)),console.log(n.gray(" Please add manually:")),console.log(n.gray(` export PATH="${l}:$PATH"`));return}if(P(g)){let f=await A(g,"utf-8");if(f.includes(l)||f.includes("npm")&&f.includes("global")&&f.includes("bin")){console.log(n.yellow(`\u26A0\uFE0F PATH entry found in ${g} but not active`)),console.log(n.gray(` Run: source ${g}
28
+ `));return}}let C=`
9
29
  # npm global bin (added by zibby)
10
- export PATH="${m}:$PATH"
11
- `;await p(g,(h(g)?await w(g,"utf-8"):"")+v),console.log(e.green(`\u2705 Added to ${g}`)),console.log(e.yellow(`
12
- \u{1F4A1} Run this to activate in current session:`)),console.log(e.gray(` source ${g}
13
- `))}catch{}}async function be(d,o){console.log(e.bold.cyan(`
30
+ export PATH="${l}:$PATH"
31
+ `;await h(g,(P(g)?await A(g,"utf-8"):"")+C),console.log(n.green(`\u2705 Added to ${g}`)),console.log(n.yellow(`
32
+ \u{1F4A1} Run this to activate in current session:`)),console.log(n.gray(` source ${g}
33
+ `))}catch{}}async function ft(o,e){console.log(n.bold.cyan(`
14
34
  \u{1F3AD} Welcome to Zibby Test Automation!
15
- `));const f=!o.skipMemory,m=["dolt","mem0"].includes(String(o.memoryBackend||"").toLowerCase())?String(o.memoryBackend).toLowerCase():"dolt";await X();const l=d?O(process.cwd(),d):process.cwd(),$=d||"zibby-tests",b=!!d;b&&h(l)&&(console.log(e.red(`
16
- \u274C Directory "${d}" already exists!
17
- `)),process.exit(1)),!b&&h(a(l,".zibby.config.mjs"))&&!o.force&&(console.log(e.yellow(`
35
+ `));let a=!e.skipMemory,l=["dolt","mem0"].includes(String(e.memoryBackend||"").toLowerCase())?String(e.memoryBackend).toLowerCase():"dolt";await Ye();let t=o?pe(process.cwd(),o):process.cwd(),m=o||"zibby-tests",y=!!o;y&&P(t)&&(console.log(n.red(`
36
+ \u274C Directory "${o}" already exists!
37
+ `)),process.exit(1)),!y&&P(p(t,".zibby.config.mjs"))&&!e.force&&(console.log(n.yellow(`
18
38
  \u26A0\uFE0F Zibby is already initialized in this directory!
19
- `)),console.log(e.white("Config file found: .zibby.config.mjs")),console.log(e.gray(`Use --force or -f to reinitialize
20
- `)),process.exit(0)),o.force&&!b&&console.log(e.cyan(`
39
+ `)),console.log(n.white("Config file found: .zibby.config.mjs")),console.log(n.gray(`Use --force or -f to reinitialize
40
+ `)),process.exit(0)),e.force&&!y&&console.log(n.cyan(`
21
41
  Reinitializing Zibby configuration...
22
- `));let s;if(o.agent&&(o.headed||o.headless))console.log(e.cyan(`Setting up with provided options...
23
- `)),s={agent:o.agent,browserMode:o.headless?"headless":"headed",apiKey:o.apiKey||null,cloudSync:!!(o.cloudSync||o.apiKey)};else{if(o.agent)s={agent:o.agent};else{const{agent:E}=await Y.prompt([{type:"select",name:"agent",message:"Which AI agent do you prefer?",choices:[{name:"Cursor",value:"cursor"},{name:"Claude (Anthropic)",value:"claude"},{name:"Codex (OpenAI)",value:"codex"},{name:"Gemini (Google)",value:"gemini"}],default:"cursor"}]);s={agent:E}}const y=D[s.agent];if(y&&!o.agentKey&&!(process.env.CI||process.env.ZIBBY_CI||!process.stdin.isTTY)){const E=W(),B=process.env[y.envVar]||E.agentKeys?.[y.envVar],A=B?`***${B.slice(-4)}`:null,t=A?`${y.label} found (${A}). Press Enter to keep, or paste a new key:`:`Enter ${y.label} (from ${y.url}, or press Enter to skip):`,{agentKey:i}=await Y.prompt([{type:"password",name:"agentKey",message:t,mask:"*"}]);i&&i.trim()&&(s.agentKey=i.trim())}const x=[];if(!o.headed&&!o.headless&&x.push({type:"select",name:"browserMode",message:"Browser mode during live AI execution?",choices:[{name:"Headed - Visible browser (recommended for development)",value:"headed"},{name:"Headless - Hidden browser (for CI/CD)",value:"headless"}],default:"headed"}),o.apiKey||x.push({type:"input",name:"apiKey",message:"Enable cloud sync? Enter project ZIBBY_API_KEY (or press Enter to skip):"}),x.length>0){const E=await Y.prompt(x);Object.assign(s,E)}s.browserMode=o.headless?"headless":o.headed?"headed":s.browserMode,s.apiKey=o.apiKey||s.apiKey,s.cloudSync=!!(o.cloudSync||o.apiKey||s.apiKey&&s.apiKey.trim())}o.agentKey&&(s.agentKey=o.agentKey);const g=D[s.agent];s.agentKey&&(F(s.agentKey,s.agent),console.log(e.gray(" \u2713 Agent API key saved to ~/.zibby/config.json"))),s.mcp="playwright",s.setupMcp=s.agent==="cursor";const v=I("Setting up Zibby...").start();try{if(b&&await S(l,{recursive:!0}),await S(a(l,"test-specs/examples"),{recursive:!0}),await S(a(l,"tests"),{recursive:!0}),await S(a(l,".zibby/output"),{recursive:!0}),await S(a(l,".zibby/commands"),{recursive:!0}),f&&m==="dolt")try{const{initMemory:t,DoltDB:i}=await import("@zibby/memory");if(i.isAvailable()){const{created:n}=t(l);n&&(v.text="Initialized test memory database (Dolt)...")}else v.text="Dolt not found \u2014 skipping memory database (brew install dolt)"}catch{}v.text="Scaffolding workflow graph...";const{TemplateFactory:y}=await import("@zibby/core/templates"),x=o.template||"browser-test-automation";try{const{graphPath:t,nodesPath:i,readmePath:n,resultHandlerPath:c,template:r}=y.getTemplateFiles(x),u=a(l,".zibby"),M=await w(t,"utf-8");if(await p(a(u,"graph.mjs"),M),c){const k=await w(c,"utf-8");await p(a(u,"result-handler.mjs"),k)}const z=await w(n,"utf-8");await p(a(u,"README.md"),z),await S(a(u,"nodes"),{recursive:!0});const{readdirSync:_}=await import("fs"),T=_(i);for(const k of T){let j=await w(a(i,k),"utf-8");!f&&k==="execute-live.mjs"&&(j=j.replace("skills: [SKILLS.BROWSER, SKILLS.MEMORY],","skills: [SKILLS.BROWSER],")),await p(a(u,"nodes",k),j)}const L=a(r.path,"chat.mjs");if(h(L)){const k=await w(L,"utf-8");await p(a(u,"chat.mjs"),k)}}catch(t){throw v.fail(`Failed to scaffold template: ${t.message}`),t}v.text="Generating configuration files...";const E=J(s,o,{memoryBackend:m});if(await p(a(l,".zibby.config.mjs"),E),await p(a(l,".env.example"),Q(s,m)),s.apiKey&&s.apiKey.trim()){const t=a(l,".env"),i=s.apiKey.trim();if(h(t)){let n=await w(t,"utf8");/^ZIBBY_API_KEY=/m.test(n)?n=n.replace(/^ZIBBY_API_KEY=.*/m,`ZIBBY_API_KEY=${i}`):/^#\s*ZIBBY_API_KEY=/m.test(n)?n=n.replace(/^#\s*ZIBBY_API_KEY=.*/m,`ZIBBY_API_KEY=${i}`):n=`${n.trimEnd()}
42
+ `));let r;if(e.agent&&(e.headed||e.headless))console.log(n.cyan(`Setting up with provided options...
43
+ `)),r={agent:e.agent,browserMode:e.headless?"headless":"headed",apiKey:e.apiKey||null,cloudSync:!!(e.cloudSync||e.apiKey)};else{if(e.agent)r={agent:e.agent};else{let{agent:S}=await D.prompt([{type:"select",name:"agent",message:"Which AI agent do you prefer?",choices:[{name:"Cursor",value:"cursor"},{name:"Claude (Anthropic)",value:"claude"},{name:"Codex (OpenAI)",value:"codex"},{name:"Gemini (Google)",value:"gemini"}],default:"cursor"}]);r={agent:S}}let f=O[r.agent];if(f&&!e.agentKey&&!(process.env.CI||process.env.ZIBBY_CI||!process.stdin.isTTY)){let S=V(),B=process.env[f.envVar]||S.agentKeys?.[f.envVar],E=B?`***${B.slice(-4)}`:null,s=E?`${f.label} found (${E}). Press Enter to keep, or paste a new key:`:`Enter ${f.label} (from ${f.url}, or press Enter to skip):`,{agentKey:c}=await D.prompt([{type:"password",name:"agentKey",message:s,mask:"*"}]);c&&c.trim()&&(r.agentKey=c.trim())}let v=[];if(!e.headed&&!e.headless&&v.push({type:"select",name:"browserMode",message:"Browser mode during live AI execution?",choices:[{name:"Headed - Visible browser (recommended for development)",value:"headed"},{name:"Headless - Hidden browser (for CI/CD)",value:"headless"}],default:"headed"}),e.apiKey||v.push({type:"input",name:"apiKey",message:"Enable cloud sync? Enter project ZIBBY_API_KEY (or press Enter to skip):"}),v.length>0){let S=await D.prompt(v);Object.assign(r,S)}r.browserMode=e.headless?"headless":e.headed?"headed":r.browserMode,r.apiKey=e.apiKey||r.apiKey,r.cloudSync=!!(e.cloudSync||e.apiKey||r.apiKey&&r.apiKey.trim())}e.agentKey&&(r.agentKey=e.agentKey);let g=O[r.agent];r.agentKey&&(X(r.agentKey,r.agent),console.log(n.gray(" \u2713 Agent API key saved to ~/.zibby/config.json"))),r.mcp="playwright",r.setupMcp=r.agent==="cursor";let C=I("Setting up Zibby...").start();try{if(y&&await M(t,{recursive:!0}),await M(p(t,"test-specs/examples"),{recursive:!0}),await M(p(t,"tests"),{recursive:!0}),await M(p(t,".zibby/output"),{recursive:!0}),await M(p(t,".zibby/commands"),{recursive:!0}),a&&l==="dolt")try{let{initMemory:s,DoltDB:c}=await import("@zibby/memory");if(c.isAvailable()){let{created:i}=s(t);i&&(C.text="Initialized test memory database (Dolt)...")}else C.text="Dolt not found \u2014 skipping memory database (brew install dolt)"}catch{}C.text="Scaffolding workflow graph...";let{TemplateFactory:f}=await import("@zibby/core/templates"),v=e.template||"browser-test-automation";try{let{graphPath:s,nodesPath:c,readmePath:i,resultHandlerPath:u,template:d}=f.getTemplateFiles(v),b=p(t,".zibby"),z=await A(s,"utf-8");if(await h(p(b,"graph.mjs"),z),u){let $=await A(u,"utf-8");await h(p(b,"result-handler.mjs"),$)}let Y=await A(i,"utf-8");await h(p(b,"README.md"),Y),await M(p(b,"nodes"),{recursive:!0});let{readdirSync:_}=await import("fs"),j=_(c);for(let $ of j){let Z=await A(p(c,$),"utf-8");!a&&$==="execute-live.mjs"&&(Z=Z.replace("skills: [SKILLS.BROWSER, SKILLS.MEMORY],","skills: [SKILLS.BROWSER],")),await h(p(b,"nodes",$),Z)}let N=p(d.path,"chat.mjs");if(P(N)){let $=await A(N,"utf-8");await h(p(b,"chat.mjs"),$)}}catch(s){throw C.fail(`Failed to scaffold template: ${s.message}`),s}C.text="Generating configuration files...";let S=je(r,e,{memoryBackend:l});if(await h(p(t,".zibby.config.mjs"),S),await h(p(t,".env.example"),Oe(r,l)),r.apiKey&&r.apiKey.trim()){let s=p(t,".env"),c=r.apiKey.trim();if(P(s)){let i=await A(s,"utf8");/^ZIBBY_API_KEY=/m.test(i)?i=i.replace(/^ZIBBY_API_KEY=.*/m,`ZIBBY_API_KEY=${c}`):/^#\s*ZIBBY_API_KEY=/m.test(i)?i=i.replace(/^#\s*ZIBBY_API_KEY=.*/m,`ZIBBY_API_KEY=${c}`):i=`${i.trimEnd()}
24
44
 
25
45
  # Zibby Cloud Sync
26
- ZIBBY_API_KEY=${i}
27
- `,await p(t,n)}else await p(t,ee(s,i,m))}if(s.agentKey&&g){const t=a(l,".env"),i=s.agentKey;if(h(t)){let n=await w(t,"utf8");const c=new RegExp(`^${g.envVar}=.*`,"m"),r=new RegExp(`^#\\s*${g.envVar}=.*`,"m");c.test(n)?n=n.replace(c,`${g.envVar}=${i}`):r.test(n)?n=n.replace(r,`${g.envVar}=${i}`):n=`${n.trimEnd()}
46
+ ZIBBY_API_KEY=${c}
47
+ `,await h(s,i)}else await h(s,De(r,c,l))}if(r.agentKey&&g){let s=p(t,".env"),c=r.agentKey;if(P(s)){let i=await A(s,"utf8"),u=new RegExp(`^${g.envVar}=.*`,"m"),d=new RegExp(`^#\\s*${g.envVar}=.*`,"m");u.test(i)?i=i.replace(u,`${g.envVar}=${c}`):d.test(i)?i=i.replace(d,`${g.envVar}=${c}`):i=`${i.trimEnd()}
28
48
 
29
49
  # AI Agent Key
30
- ${g.envVar}=${i}
31
- `,await p(t,n)}}if(b){const t=te($,s,{memoryBackend:m});await p(a(l,"package.json"),t)}if(!h(a(l,".gitignore"))){const t=ne();await p(a(l,".gitignore"),t)}if(!h(a(l,"playwright.config.js"))){const t=oe("on");await p(a(l,"playwright.config.js"),t)}if(!h(a(l,"test-specs/examples/example-domain.txt"))){const t=se();await p(a(l,"test-specs/examples/example-domain.txt"),t)}const B=O(q,"../../../../examples/.zibby/commands");if(h(B)){const t=G(B).filter(i=>i.toLowerCase().endsWith(".md"));for(const i of t){const n=a(l,".zibby/commands",i);if(o.force||!h(n)){const c=await w(a(B,i),"utf-8");await p(n,c)}}}if(!h(a(l,".zibby/commands/example.md"))){const t=ie();await p(a(l,".zibby/commands/example.md"),t)}if(b){const t=ae($,s);await p(a(l,"README.md"),t)}if(v.succeed(b?"Project created!":"Zibby initialized!"),b&&!o.skipInstall){const t=I("Installing dependencies...").start();await new Promise((i,n)=>{K("npm",["install"],{cwd:l,stdio:"pipe"}).on("close",r=>{r===0?(t.succeed("Dependencies installed!"),i()):(t.fail("Failed to install dependencies"),n(new Error("npm install failed")))})})}else if(!b){const t=["@zibby/cli","@zibby/core"],i=[];for(const n of t)try{Z(a(l,"package.json")).resolve(`${n}/package.json`)}catch{i.push(n)}if(i.length>0){const n=N("../../package.json"),c=i.map(r=>r==="@zibby/cli"?`${r}@latest`:r==="@zibby/core"?`${r}@${n.dependencies?.["@zibby/core"]||"latest"}`:r);if(o.skipInstall)console.log(e.yellow(`
32
- Missing required Zibby dependencies in this project:`)),console.log(e.white(` ${i.join(", ")}`)),console.log(e.gray("Install them manually to avoid runtime errors:")),console.log(e.white(` npm install --save-dev ${c.join(" ")}
33
- `));else{const r=I(`Installing ${c.join(", ")}...`).start();await new Promise(M=>{K("npm",["install","--save-dev",...c],{cwd:l,stdio:"pipe"}).on("close",_=>M(_??1))})===0?r.succeed("Installed missing Zibby dependencies"):(r.warn("Could not auto-install Zibby dependencies"),console.log(e.gray("Run this manually:")),console.log(e.white(` npm install --save-dev ${c.join(" ")}
34
- `)))}}}if(!b&&f&&m==="mem0"&&(console.log(e.yellow(`
35
- Using mem0 backend requires mem0ai in your project dependencies.`)),console.log(e.gray("Run this manually in your project when ready:")),console.log(e.white(` npm install mem0ai
36
- `))),!o.skipInstall){const t=I("Installing Playwright browsers...").start();await new Promise(i=>{const n=K("npx",["playwright","install","chromium"],{cwd:l,stdio:"pipe"});let c="";n.stdout.on("data",r=>{c+=r.toString()}),n.stderr.on("data",r=>{c+=r.toString()}),n.on("close",r=>{r===0?(c.includes("already installed")||c.includes("up to date")?t.succeed("Playwright browsers already installed"):t.succeed("Playwright browsers installed!"),i()):(t.warn("Could not verify Playwright browsers"),console.log(e.yellow(`
50
+ ${g.envVar}=${c}
51
+ `,await h(s,i)}}if(y){let s=Le(m,r,{memoryBackend:l});await h(p(t,"package.json"),s)}if(!P(p(t,".gitignore"))){let s=Ne();await h(p(t,".gitignore"),s)}if(!P(p(t,"playwright.config.js"))){let s=Ze("on");await h(p(t,"playwright.config.js"),s)}if(!P(p(t,"test-specs/examples/example-domain.txt"))){let s=Ve();await h(p(t,"test-specs/examples/example-domain.txt"),s)}let B=pe(Re,"../../../../examples/.zibby/commands");if(P(B)){let s=Me(B).filter(c=>c.toLowerCase().endsWith(".md"));for(let c of s){let i=p(t,".zibby/commands",c);if(e.force||!P(i)){let u=await A(p(B,c),"utf-8");await h(i,u)}}}if(!P(p(t,".zibby/commands/example.md"))){let s=Ge();await h(p(t,".zibby/commands/example.md"),s)}if(y){let s=He(m,r);await h(p(t,"README.md"),s)}if(C.succeed(y?"Project created!":"Zibby initialized!"),y&&!e.skipInstall){let s=I("Installing dependencies...").start();await new Promise((c,i)=>{R("npm",["install"],{cwd:t,stdio:"pipe"}).on("close",d=>{d===0?(s.succeed("Dependencies installed!"),c()):(s.fail("Failed to install dependencies"),i(new Error("npm install failed")))})})}else if(!y){let s=["@zibby/cli","@zibby/core"],c=[];for(let i of s)try{de(p(t,"package.json")).resolve(`${i}/package.json`)}catch{c.push(i)}if(c.length>0){let i=ue("../../package.json"),u=c.map(d=>d==="@zibby/cli"?`${d}@latest`:d==="@zibby/core"?`${d}@${i.dependencies?.["@zibby/core"]||"latest"}`:d);if(e.skipInstall)console.log(n.yellow(`
52
+ Missing required Zibby dependencies in this project:`)),console.log(n.white(` ${c.join(", ")}`)),console.log(n.gray("Install them manually to avoid runtime errors:")),console.log(n.white(` npm install --save-dev ${u.join(" ")}
53
+ `));else{let d=I(`Installing ${u.join(", ")}...`).start();await new Promise(z=>{R("npm",["install","--save-dev",...u],{cwd:t,stdio:"pipe"}).on("close",_=>z(_??1))})===0?d.succeed("Installed missing Zibby dependencies"):(d.warn("Could not auto-install Zibby dependencies"),console.log(n.gray("Run this manually:")),console.log(n.white(` npm install --save-dev ${u.join(" ")}
54
+ `)))}}}if(!y&&a&&l==="mem0"&&(console.log(n.yellow(`
55
+ Using mem0 backend requires mem0ai in your project dependencies.`)),console.log(n.gray("Run this manually in your project when ready:")),console.log(n.white(` npm install mem0ai
56
+ `))),!e.skipInstall){let s=I("Installing Playwright browsers...").start();await new Promise(c=>{let i=R("npx",["playwright","install","chromium"],{cwd:t,stdio:"pipe"}),u="";i.stdout.on("data",d=>{u+=d.toString()}),i.stderr.on("data",d=>{u+=d.toString()}),i.on("close",d=>{d===0?(u.includes("already installed")||u.includes("up to date")?s.succeed("Playwright browsers already installed"):s.succeed("Playwright browsers installed!"),c()):(s.warn("Could not verify Playwright browsers"),console.log(n.yellow(`
37
57
  \u26A0\uFE0F If tests fail, run: npx playwright install
38
- `)),i())})})}if(s.agent==="cursor"&&!o.skipInstall){const t=I("Checking cursor-agent CLI...").start();try{R("cursor-agent --version",{encoding:"utf-8",timeout:5e3,stdio:"pipe"}),t.succeed("cursor-agent CLI already installed")}catch{t.text="Installing cursor-agent CLI...";try{await new Promise((i,n)=>{K("bash",["-c","curl https://cursor.com/install -fsS | bash"],{stdio:"pipe"}).on("close",r=>{if(r===0){const u=a(P(),".local","bin");process.env.PATH.includes(u)||(process.env.PATH=`${u}:${process.env.PATH}`),t.succeed("cursor-agent CLI installed!"),i()}else n(new Error("cursor-agent install failed"))})})}catch{t.fail("Could not install cursor-agent CLI"),console.log(e.yellow(` Install manually: curl https://cursor.com/install -fsS | bash
39
- `))}}}if(s.agent==="codex"&&!o.skipInstall){const t=I("Checking Codex CLI...").start();try{R("codex --version",{encoding:"utf-8",timeout:5e3,stdio:"pipe"}),t.succeed("Codex CLI already installed")}catch{t.text="Installing Codex CLI...";try{await new Promise((i,n)=>{K("npm",["install","-g","@openai/codex"],{stdio:"pipe"}).on("close",r=>{r===0?(t.succeed("Codex CLI installed!"),i()):n(new Error("npm install -g @openai/codex failed"))})})}catch{t.fail("Could not install Codex CLI"),console.log(e.yellow(` Install manually: npm install -g @openai/codex
40
- `))}}}if(s.agent==="gemini"&&!o.skipInstall){const t=I("Checking Gemini CLI...").start();try{R("gemini --version",{encoding:"utf-8",timeout:5e3,stdio:"pipe"}),t.succeed("Gemini CLI already installed")}catch{t.text="Installing Gemini CLI...";try{await new Promise((n,c)=>{K("npm",["install","-g","@google/gemini-cli"],{stdio:"pipe"}).on("close",u=>{u===0?(t.succeed("Gemini CLI installed!"),n()):c(new Error("npm install -g @google/gemini-cli failed"))})})}catch{t.fail("Could not install Gemini CLI"),console.log(e.yellow(` Install manually: npm install -g @google/gemini-cli
41
- `))}}const i=I("Configuring Gemini MCP servers...").start();try{const n=a(P(),".gemini"),c=a(n,"settings.json");h(n)||await S(n,{recursive:!0});let r={mcpServers:{}};if(h(c))try{const T=await w(c,"utf-8");r=JSON.parse(T),r.mcpServers||(r.mcpServers={})}catch{}let u;try{const{createRequire:T}=await import("module");u=T(import.meta.url).resolve("@zibby/mcp-browser/bin/mcp-browser-zibby.js")}catch{u="@playwright/mcp"}const M=s.browserMode!=="headless",z=u==="@playwright/mcp"?["-y","@playwright/mcp","--isolated","--viewport-size=1280x720","--output-dir","test-results"]:[u,"--isolated","--viewport-size=1280x720","--output-dir=test-results"];M||z.push("--headless"),r.mcpServers["playwright-official"]={command:u==="@playwright/mcp"?"npx":"node",args:z},await p(c,`${JSON.stringify(r,null,2)}
42
- `,"utf-8");let _="Gemini MCP configured";M?_+=" (headed mode - visible browser)":_+=" (headless mode - hidden browser)",i.succeed(_)}catch(n){i.fail("MCP setup failed"),console.log(e.yellow(" You may need to configure ~/.gemini/settings.json manually")),console.log(e.gray(` Error: ${n.message}
43
- `))}}if(s.agent==="cursor"&&s.setupMcp){const t=I("Setting up Playwright MCP...").start();try{const{setupPlaywrightMcpCommand:i}=await import("./setup-scripts.js"),n=s.cloudSync||!1,c=s.browserMode!=="headless";await i({headed:c,cloudSync:n,viewport:{width:1280,height:720}});let r="Playwright MCP configured";c?r+=" (headed mode - visible browser)":r+=" (headless mode - hidden browser)",n&&(r+=" + Zibby MCP (cloud sync)"),t.succeed(r),n&&!(s.apiKey&&s.apiKey.trim())&&console.log(e.gray(`
58
+ `)),c())})})}if(r.agent==="cursor"&&!e.skipInstall){let s=I("Checking cursor-agent CLI...").start();try{L("cursor-agent --version",{encoding:"utf-8",timeout:5e3,stdio:"pipe"}),s.succeed("cursor-agent CLI already installed")}catch{s.text="Installing cursor-agent CLI...";try{await new Promise((c,i)=>{R("bash",["-c","curl https://cursor.com/install -fsS | bash"],{stdio:"pipe"}).on("close",d=>{if(d===0){let b=p(k(),".local","bin");process.env.PATH.includes(b)||(process.env.PATH=`${b}:${process.env.PATH}`),s.succeed("cursor-agent CLI installed!"),c()}else i(new Error("cursor-agent install failed"))})})}catch{s.fail("Could not install cursor-agent CLI"),console.log(n.yellow(` Install manually: curl https://cursor.com/install -fsS | bash
59
+ `))}}}if(r.agent==="codex"&&!e.skipInstall){let s=I("Checking Codex CLI...").start();try{L("codex --version",{encoding:"utf-8",timeout:5e3,stdio:"pipe"}),s.succeed("Codex CLI already installed")}catch{s.text="Installing Codex CLI...";try{await new Promise((c,i)=>{R("npm",["install","-g","@openai/codex"],{stdio:"pipe"}).on("close",d=>{d===0?(s.succeed("Codex CLI installed!"),c()):i(new Error("npm install -g @openai/codex failed"))})})}catch{s.fail("Could not install Codex CLI"),console.log(n.yellow(` Install manually: npm install -g @openai/codex
60
+ `))}}}if(r.agent==="gemini"&&!e.skipInstall){let s=I("Checking Gemini CLI...").start();try{L("gemini --version",{encoding:"utf-8",timeout:5e3,stdio:"pipe"}),s.succeed("Gemini CLI already installed")}catch{s.text="Installing Gemini CLI...";try{await new Promise((i,u)=>{R("npm",["install","-g","@google/gemini-cli"],{stdio:"pipe"}).on("close",b=>{b===0?(s.succeed("Gemini CLI installed!"),i()):u(new Error("npm install -g @google/gemini-cli failed"))})})}catch{s.fail("Could not install Gemini CLI"),console.log(n.yellow(` Install manually: npm install -g @google/gemini-cli
61
+ `))}}let c=I("Configuring Gemini MCP servers...").start();try{let i=p(k(),".gemini"),u=p(i,"settings.json");P(i)||await M(i,{recursive:!0});let d={mcpServers:{}};if(P(u))try{let j=await A(u,"utf-8");d=JSON.parse(j),d.mcpServers||(d.mcpServers={})}catch{}let b;try{let{createRequire:j}=await import("module");b=j(import.meta.url).resolve("@zibby/mcp-browser/bin/mcp-browser-zibby.js")}catch{b="@playwright/mcp"}let z=r.browserMode!=="headless",Y=b==="@playwright/mcp"?["-y","@playwright/mcp","--isolated","--viewport-size=1280x720","--output-dir","test-results"]:[b,"--isolated","--viewport-size=1280x720","--output-dir=test-results"];z||Y.push("--headless"),d.mcpServers["playwright-official"]={command:b==="@playwright/mcp"?"npx":"node",args:Y},await h(u,`${JSON.stringify(d,null,2)}
62
+ `,"utf-8");let _="Gemini MCP configured";z?_+=" (headed mode - visible browser)":_+=" (headless mode - hidden browser)",c.succeed(_)}catch(i){c.fail("MCP setup failed"),console.log(n.yellow(" You may need to configure ~/.gemini/settings.json manually")),console.log(n.gray(` Error: ${i.message}
63
+ `))}}if(r.agent==="cursor"&&r.setupMcp){let s=I("Setting up Playwright MCP...").start();try{let{setupPlaywrightMcpCommand:c}=await Promise.resolve().then(()=>(ce(),le)),i=r.cloudSync||!1,u=r.browserMode!=="headless";await c({headed:u,cloudSync:i,viewport:{width:1280,height:720}});let d="Playwright MCP configured";u?d+=" (headed mode - visible browser)":d+=" (headless mode - hidden browser)",i&&(d+=" + Zibby MCP (cloud sync)"),s.succeed(d),i&&!(r.apiKey&&r.apiKey.trim())&&console.log(n.gray(`
44
64
  Copy .env.example to .env and set ZIBBY_API_KEY to enable uploads
45
- `))}catch(i){t.fail("MCP setup script failed"),console.log(e.yellow(" Check if MCP is already configured:")),console.log(e.gray(' \u2022 Open Cursor settings \u2192 Check "playwright-official" MCP')),console.log(e.gray(" \u2022 Run: cursor-agent mcp list")),console.log(e.gray(` \u2022 Or run manually: zibby setup-playwright
46
- `)),console.log(e.gray(` Error: ${i.message}
47
- `))}}console.log(e.bold.green(`
65
+ `))}catch(c){s.fail("MCP setup script failed"),console.log(n.yellow(" Check if MCP is already configured:")),console.log(n.gray(' \u2022 Open Cursor settings \u2192 Check "playwright-official" MCP')),console.log(n.gray(" \u2022 Run: cursor-agent mcp list")),console.log(n.gray(` \u2022 Or run manually: zibby setup-playwright
66
+ `)),console.log(n.gray(` Error: ${c.message}
67
+ `))}}console.log(n.bold.green(`
48
68
  \u{1F389} All set!
49
- `)),console.log(e.cyan("Start the Zibby Chat Agent:")),b&&console.log(e.white(` cd ${d}`)),console.log(e.white(` zibby
50
- `)),console.log(e.cyan("Or run a test directly:")),console.log(e.white(` zibby run test-specs/examples/example-domain.txt
51
- `)),console.log(e.cyan("Next steps:"));let A=1;console.log(e.white(` ${A++}. cp .env.example .env ${e.gray("# then add your API keys")}`)),f&&console.log(e.white(` ${A++}. Set ${e.bold("ZIBBY_MEMORY_BACKEND")} in .env ${e.gray(`# currently: ${m}`)}`)),console.log(e.white(` ${A++}. Type ${e.bold("zibby")} to chat with the AI testing assistant`)),console.log(e.white(` ${A++}. Write test specs in test-specs/`)),console.log(e.white(` ${A++}. Run: npx zibby run <spec-file>
52
- `))}catch(y){v.fail("Failed to create project"),console.error(e.red(`
53
- \u274C Error: ${y.message}
54
- `)),process.exit(1)}}function J(d,o={},f={}){const m=["dolt","mem0"].includes(String(f.memoryBackend||"").toLowerCase())?String(f.memoryBackend).toLowerCase():"dolt",l={claude:`
69
+ `)),console.log(n.cyan("Start the Zibby Chat Agent:")),y&&console.log(n.white(` cd ${o}`)),console.log(n.white(` zibby
70
+ `)),console.log(n.cyan("Or run a test directly:")),console.log(n.white(` zibby run test-specs/examples/example-domain.txt
71
+ `)),console.log(n.cyan("Next steps:"));let E=1;console.log(n.white(` ${E++}. cp .env.example .env ${n.gray("# then add your API keys")}`)),a&&console.log(n.white(` ${E++}. Set ${n.bold("ZIBBY_MEMORY_BACKEND")} in .env ${n.gray(`# currently: ${l}`)}`)),console.log(n.white(` ${E++}. Type ${n.bold("zibby")} to chat with the AI testing assistant`)),console.log(n.white(` ${E++}. Write test specs in test-specs/`)),console.log(n.white(` ${E++}. Run: npx zibby run <spec-file>
72
+ `))}catch(f){C.fail("Failed to create project"),console.error(n.red(`
73
+ \u274C Error: ${f.message}
74
+ `)),process.exit(1)}}function je(o,e={},a={}){let l=["dolt","mem0"].includes(String(a.memoryBackend||"").toLowerCase())?String(a.memoryBackend).toLowerCase():"dolt",t={claude:`
55
75
  claude: {
56
76
  model: 'auto', // Options: 'auto', 'sonnet-4.6', 'opus-4.6', 'sonnet-4.5', 'opus-4.5'
57
77
  maxTokens: 4096,
@@ -64,13 +84,13 @@ Using mem0 backend requires mem0ai in your project dependencies.`)),console.log(
64
84
  },`,gemini:`
65
85
  gemini: {
66
86
  model: 'gemini-2.5-pro', // Options: 'auto', 'gemini-2.5-pro', 'gemini-2.5-flash'
67
- },`},$=d.agent,b=Object.entries(l).filter(([s])=>s!==$).map(([,s])=>s.split(`
68
- `).map(C=>C.trim()?` // ${C.trimStart()}`:C).join(`
87
+ },`},m=o.agent,y=Object.entries(t).filter(([r])=>r!==m).map(([,r])=>r.split(`
88
+ `).map(w=>w.trim()?` // ${w.trimStart()}`:w).join(`
69
89
  `)).join(`
70
90
  `);return`export default {
71
91
  // AI agent settings
72
- agent: {${l[$]}
73
- ${b}
92
+ agent: {${t[m]}
93
+ ${y}
74
94
  strictMode: false,
75
95
  },
76
96
 
@@ -78,12 +98,12 @@ ${b}
78
98
  // and workflow config. Runtime strategies attach MCP per run (no global Gemini settings mutation).
79
99
  browser: {
80
100
  mcp: 'playwright',
81
- headless: ${d.browserMode==="headless"},
101
+ headless: ${o.browserMode==="headless"},
82
102
  },
83
103
 
84
104
  // Chat memory backend adapter (dolt | mem0)
85
105
  memory: {
86
- backend: '${m}',
106
+ backend: '${l}',
87
107
  },
88
108
 
89
109
  // Advanced: Override models per node (optional)
@@ -131,13 +151,13 @@ ${b}
131
151
  },
132
152
 
133
153
  // Cloud sync - auto-upload test results & videos (requires ZIBBY_API_KEY in .env)
134
- cloudSync: ${d.cloudSync||!1}
154
+ cloudSync: ${o.cloudSync||!1}
135
155
  };
136
- `}function Q(d,o="dolt"){return`# Zibby Test Automation - Environment Variables
156
+ `}function Oe(o,e="dolt"){return`# Zibby Test Automation - Environment Variables
137
157
 
138
158
  # AI Provider Keys
139
159
  ${{claude:"ANTHROPIC_API_KEY=sk-ant-your_key_here",cursor:`# Cursor Agent uses cursor-agent CLI \u2014 no API key needed
140
- # Install: curl https://cursor.com/install -fsS | bash`,codex:"OPENAI_API_KEY=sk-your_key_here",gemini:"GEMINI_API_KEY=your_key_here"}[d.agent]||""}
160
+ # Install: curl https://cursor.com/install -fsS | bash`,codex:"OPENAI_API_KEY=sk-your_key_here",gemini:"GEMINI_API_KEY=your_key_here"}[o.agent]||""}
141
161
 
142
162
  # Zibby Cloud Sync (for uploading test results & videos)
143
163
  # Get your API key from: https://zibby.app/settings/tokens
@@ -149,14 +169,14 @@ ${{claude:"ANTHROPIC_API_KEY=sk-ant-your_key_here",cursor:`# Cursor Agent uses c
149
169
  # ZIBBY_MEMORY_COMPACT_EVERY=1500 # Auto-compact every N runs (0 to disable)
150
170
 
151
171
  # Chat memory backend
152
- ZIBBY_MEMORY_BACKEND=${o}
153
- `}function ee(d,o,f="dolt"){return`# Zibby Test Automation - Environment Variables
172
+ ZIBBY_MEMORY_BACKEND=${e}
173
+ `}function De(o,e,a="dolt"){return`# Zibby Test Automation - Environment Variables
154
174
 
155
175
  # AI Provider Keys
156
- ${{claude:"ANTHROPIC_API_KEY=sk-ant-your_key_here",cursor:"# Cursor Agent uses cursor-agent CLI \u2014 no API key needed",codex:"OPENAI_API_KEY=sk-your_key_here",gemini:"GEMINI_API_KEY=your_key_here"}[d.agent]||""}
176
+ ${{claude:"ANTHROPIC_API_KEY=sk-ant-your_key_here",cursor:"# Cursor Agent uses cursor-agent CLI \u2014 no API key needed",codex:"OPENAI_API_KEY=sk-your_key_here",gemini:"GEMINI_API_KEY=your_key_here"}[o.agent]||""}
157
177
 
158
178
  # Zibby Cloud Sync
159
- ZIBBY_API_KEY=${o}
179
+ ZIBBY_API_KEY=${e}
160
180
 
161
181
  # Test Memory (Dolt DB) - Auto-compaction settings
162
182
  # ZIBBY_MEMORY_MAX_RUNS=3000 # Max test runs to keep per spec
@@ -164,8 +184,8 @@ ZIBBY_API_KEY=${o}
164
184
  # ZIBBY_MEMORY_COMPACT_EVERY=1500 # Auto-compact every N runs (0 to disable)
165
185
 
166
186
  # Chat memory backend
167
- ZIBBY_MEMORY_BACKEND=${f}
168
- `}function te(d,o,f={}){const l={"@zibby/cli":"latest","@zibby/core":N("../../package.json").dependencies?.["@zibby/core"]||"latest"};return f.memoryBackend==="mem0"&&(l.mem0ai="^2.4.6"),JSON.stringify({name:d,version:"1.0.0",type:"module",private:!0,scripts:{"test:spec":"zibby run",test:"playwright test","test:headed":"playwright test --headed"},dependencies:l,devDependencies:{"@playwright/test":"^1.49.0",dotenv:"^17.2.3"}},null,2)}function ne(){return`# Dependencies
187
+ ZIBBY_MEMORY_BACKEND=${a}
188
+ `}function Le(o,e,a={}){let t={"@zibby/cli":"latest","@zibby/core":ue("../../package.json").dependencies?.["@zibby/core"]||"latest"};return a.memoryBackend==="mem0"&&(t.mem0ai="^2.4.6"),JSON.stringify({name:o,version:"1.0.0",type:"module",private:!0,scripts:{"test:spec":"zibby run",test:"playwright test","test:headed":"playwright test --headed"},dependencies:t,devDependencies:{"@playwright/test":"^1.49.0",dotenv:"^17.2.3"}},null,2)}function Ne(){return`# Dependencies
169
189
  node_modules/
170
190
 
171
191
  # Test artifacts
@@ -193,11 +213,11 @@ Thumbs.db
193
213
  # Zibby memory (Dolt DB synced separately via dolt remote, not git)
194
214
  .zibby/memory/
195
215
  .zibby/memory-context.md
196
- `}function oe(d="off",o=null){return`import { defineConfig} from '@playwright/test';
216
+ `}function Ze(o="off",e=null){return`import { defineConfig} from '@playwright/test';
197
217
 
198
218
  export default defineConfig({
199
219
  testDir: './tests',
200
- ${o?` outputDir: '${o}',
220
+ ${e?` outputDir: '${e}',
201
221
  `:` outputDir: 'test-results/playwright', // Keep Playwright artifacts separate from Zibby workflow output
202
222
  `} timeout: 30000,
203
223
  retries: 0,
@@ -215,7 +235,7 @@ ${o?` outputDir: '${o}',
215
235
  ['list']
216
236
  ],
217
237
  });
218
- `}function se(){return`Test Specification: Example Domain Navigation
238
+ `}function Ve(){return`Test Specification: Example Domain Navigation
219
239
  ==============================================
220
240
 
221
241
  Application: Example Domain
@@ -252,7 +272,7 @@ Notes:
252
272
  - Uses a stable, public domain (example.com) maintained by IANA
253
273
  - No authentication required
254
274
  - Suitable for CI/CD smoke testing
255
- `}function ie(){return`# Example command template
275
+ `}function Ge(){return`# Example command template
256
276
 
257
277
  Use this command when the user asks for a concise testing task breakdown.
258
278
 
@@ -263,7 +283,7 @@ Required output format:
263
283
  4. Expected result
264
284
 
265
285
  Keep the response actionable and short.
266
- `}function ae(d,o){return`# ${d}
286
+ `}function He(o,e){return`# ${o}
267
287
 
268
288
  AI-powered test automation with Zibby.
269
289
 
@@ -277,7 +297,7 @@ npm install
277
297
  2. Configure environment:
278
298
  \`\`\`bash
279
299
  cp .env.example .env
280
- # Edit .env and add your ${{claude:"ANTHROPIC_API_KEY",codex:"OPENAI_API_KEY",gemini:"GEMINI_API_KEY",cursor:"CURSOR_API_KEY (optional)"}[o.agent]}
300
+ # Edit .env and add your ${{claude:"ANTHROPIC_API_KEY",codex:"OPENAI_API_KEY",gemini:"GEMINI_API_KEY",cursor:"CURSOR_API_KEY (optional)"}[e.agent]}
281
301
  \`\`\`
282
302
 
283
303
  3. Run example test:
@@ -328,13 +348,13 @@ npx playwright test --ui
328
348
  Edit \`.zibby.config.mjs\` to customize:
329
349
  - Agent settings (model, temperature)
330
350
  - Browser settings (headless, viewport)
331
- - Cloud sync${o.cloudSync?" (enabled)":" (disabled)"}
351
+ - Cloud sync${e.cloudSync?" (enabled)":" (disabled)"}
332
352
  - Self-healing behavior
333
353
 
334
354
  ## Project Structure
335
355
 
336
356
  \`\`\`
337
- ${d}/
357
+ ${o}/
338
358
  \u251C\u2500\u2500 .zibby/
339
359
  \u2502 \u251C\u2500\u2500 graph.mjs # Workflow definition
340
360
  \u2502 \u251C\u2500\u2500 nodes/ # Custom nodes
@@ -352,4 +372,4 @@ ${d}/
352
372
 
353
373
  - Documentation: https://docs.zibby.dev
354
374
  - Examples: https://github.com/zibby/examples
355
- `}export{be as initCommand};
375
+ `}export{ft as initCommand};
@@ -1,11 +1,11 @@
1
- import t from"chalk";import k from"ora";import{getApiUrl as u}from"../config/environments.js";import{getSessionToken as y,getUserInfo as I,saveProjects as E}from"../config/config.js";async function z(){const s=k("Fetching projects...").start();try{const n=y(),r=I();n||(s.fail("Not logged in"),console.log(t.yellow(`
2
- Please log in first:`)),console.log(t.gray(` zibby login
3
- `)),process.exit(1)),r&&(console.log(t.gray(`Logged in as: ${r.email}`)),r.account_id&&console.log(t.gray(`Account ID: ${r.account_id}
4
- `)));const p=u(),c=await fetch(`${p}/projects`,{headers:{Authorization:`Bearer ${n}`}});if(!c.ok){const o=await c.json().catch(()=>({}));throw new Error(o.error||"Failed to fetch projects")}const e=(await c.json()).projects||[],g=e.map(o=>({name:o.name,projectId:o.projectId,apiToken:o.apiToken}));if(E(g),s.stop(),e.length===0){console.log(`
1
+ import s from"chalk";import C from"ora";var r={local:{name:"Local Development",apiUrl:"http://localhost:3001",accountApiUrl:"http://localhost:3001",frontendUrl:"http://localhost:3000",description:"Local backend running on port 3001"},prod:{name:"Production",apiUrl:process.env.ZIBBY_PROD_API_URL||"https://api-prod.zibby.app",accountApiUrl:process.env.ZIBBY_PROD_ACCOUNT_API_URL||"https://account-api-prod.zibby.app",frontendUrl:process.env.ZIBBY_PROD_FRONTEND_URL||"https://studio.zibby.app",description:"Production environment"}};function u(){let o;if(process.env.ZIBBY_API_URL)o=process.env.ZIBBY_API_URL;else{let n=process.env.ZIBBY_ENV||"prod";r[n]?o=r[n].apiUrl:o=r.prod.apiUrl}try{let n=new URL(o);return n.protocol!=="http:"&&n.protocol!=="https:"?(console.error(`\u26A0\uFE0F Invalid API URL protocol: ${n.protocol} (only http/https allowed)`),r.prod.apiUrl):o}catch{return console.error(`\u26A0\uFE0F Invalid API URL: ${o}`),r.prod.apiUrl}}import{existsSync as g,mkdirSync as A,readFileSync as E,writeFileSync as $}from"fs";import{homedir as U}from"os";import{join as i}from"path";function m(){return process.env.ZIBBY_CONFIG_DIR||i(U(),".zibby")}function I(){return i(m(),"config.json")}var L=i(U(),".zibby"),F=i(L,"config.json");function T(){let o=m();g(o)||A(o,{recursive:!0})}function d(){try{let o=I();if(g(o)){let n=E(o,"utf-8");return JSON.parse(n)}}catch{}return{}}function k(o){T(),$(I(),JSON.stringify(o,null,2))}function h(){return d().sessionToken||null}function y(){return d().user||null}function _(o){let n=d();n.projects=o,k(n)}async function G(){let o=C("Fetching projects...").start();try{let n=h(),c=y();n||(o.fail("Not logged in"),console.log(s.yellow(`
2
+ Please log in first:`)),console.log(s.gray(` zibby login
3
+ `)),process.exit(1)),c&&(console.log(s.gray(`Logged in as: ${c.email}`)),c.account_id&&console.log(s.gray(`Account ID: ${c.account_id}
4
+ `)));let v=u(),p=await fetch(`${v}/projects`,{headers:{Authorization:`Bearer ${n}`}});if(!p.ok){let e=await p.json().catch(()=>({}));throw new Error(e.error||"Failed to fetch projects")}let t=(await p.json()).projects||[],j=t.map(e=>({name:e.name,projectId:e.projectId,apiToken:e.apiToken}));if(_(j),o.stop(),t.length===0){console.log(`
5
5
  No projects found`),console.log(`Create a project at: https://zibby.app/projects
6
- `);return}const d=o=>o&&o.length>8?`${o.substring(0,8)}\u2022\u2022\u2022\u2022`:o||"-",a=Math.max(4,...e.map(o=>o.name.length)),i=36,l=14,j=` ${"Name".padEnd(a)} ${"Project ID".padEnd(i)} ${"API Token".padEnd(l)}`,m=` ${"\u2500".repeat(a)} ${"\u2500".repeat(i)} ${"\u2500".repeat(l)}`;console.log(`
7
- Your Projects (${e.length})
8
- `),console.log(j),console.log(m);for(const o of e){const f=o.name.padEnd(a),h=o.projectId.padEnd(i),$=d(o.apiToken).padEnd(l);console.log(` ${f} ${h} ${$}`)}console.log(""),console.log("\u{1F4A1} Use the Project ID with --project flag:"),console.log(` zibby run test-specs/login.txt --project <project-id> --sync
9
- `)}catch(n){s.fail("Failed to fetch projects"),console.error(t.red(`
6
+ `);return}let x=e=>e&&e.length>8?`${e.substring(0,8)}\u2022\u2022\u2022\u2022`:e||"-",l=Math.max(4,...t.map(e=>e.name.length)),a=36,f=14,B=` ${"Name".padEnd(l)} ${"Project ID".padEnd(a)} ${"API Token".padEnd(f)}`,P=` ${"\u2500".repeat(l)} ${"\u2500".repeat(a)} ${"\u2500".repeat(f)}`;console.log(`
7
+ Your Projects (${t.length})
8
+ `),console.log(B),console.log(P);for(let e of t){let N=e.name.padEnd(l),b=e.projectId.padEnd(a),R=x(e.apiToken).padEnd(f);console.log(` ${N} ${b} ${R}`)}console.log(""),console.log("\u{1F4A1} Use the Project ID with --project flag:"),console.log(` zibby run test-specs/login.txt --project <project-id> --sync
9
+ `)}catch(n){o.fail("Failed to fetch projects"),console.error(s.red(`
10
10
  \u274C Error: ${n.message}
11
- `)),process.exit(1)}}export{z as listProjectsCommand};
11
+ `)),process.exit(1)}}export{G as listProjectsCommand};