@zibby/cli 0.1.60 → 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.
- package/dist/auth/cli-login.js +12 -12
- package/dist/commands/agent-reliability.js +4 -4
- package/dist/commands/analyze-graph.js +14 -14
- package/dist/commands/chat-session-store.js +1 -1
- package/dist/commands/chat.js +71 -62
- package/dist/commands/ci-setup.js +3 -3
- package/dist/commands/generate.js +60 -21
- package/dist/commands/implement.js +15 -15
- package/dist/commands/init.js +87 -67
- package/dist/commands/list-projects.js +9 -9
- package/dist/commands/memory.js +13 -13
- package/dist/commands/project.js +8 -8
- package/dist/commands/run-capacity-queue-cli.js +2 -2
- package/dist/commands/run.js +52 -50
- package/dist/commands/setup-scripts.js +6 -6
- package/dist/commands/studio.js +21 -15
- package/dist/commands/uninstall.js +10 -10
- package/dist/commands/upload.js +15 -15
- package/dist/commands/video.js +1 -1
- package/dist/commands/workflow.js +35 -35
- package/dist/commands/workflows/deploy.js +10 -10
- package/dist/commands/workflows/generate.js +17 -17
- package/dist/commands/workflows/list.js +15 -15
- package/dist/commands/workflows/logs.js +13 -13
- package/dist/commands/workflows/run.js +7 -7
- package/dist/commands/workflows/start.js +6 -6
- package/dist/commands/workflows/trigger.js +7 -7
- package/dist/config/config-loader.js +1 -1
- package/dist/config/config.js +1 -1
- package/dist/config/environments.js +1 -1
- package/dist/package.json +4 -4
- package/dist/utils/agent-credentials.js +3 -3
- package/dist/utils/chat-run-lifecycle.js +3 -3
- package/dist/utils/execution-context.js +1 -1
- package/dist/utils/progress-reporter.js +1 -1
- package/dist/utils/studio-cli-log-mirror.js +1 -1
- package/dist/utils/studio-installer.js +5 -5
- package/dist/utils/studio-launcher.js +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import{existsSync as
|
|
2
|
-
`);a!==e&&a.length>0&&(e=a,
|
|
3
|
-
`);return await
|
|
4
|
-
`);throw await
|
|
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}`,
|
|
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()}
|
|
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: ${
|
|
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{
|
|
46
|
-
`);
|
|
47
|
-
You are working in a **multi-repository** setup with ${
|
|
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
|
-
${
|
|
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: ${
|
|
54
|
+
- Dev server: ${c}
|
|
55
55
|
- Run tests: \`cd ${s.name} && ${s.testCommand}\`
|
|
56
56
|
|
|
57
57
|
**Other Repositories:**
|
|
58
|
-
${
|
|
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,
|
|
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,
|
|
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};
|
package/dist/commands/init.js
CHANGED
|
@@ -1,57 +1,77 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
\u{
|
|
6
|
-
`))
|
|
7
|
-
|
|
8
|
-
`))
|
|
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="${
|
|
11
|
-
`;await
|
|
12
|
-
\u{1F4A1} Run this to activate in current session:`)),console.log(
|
|
13
|
-
`))}catch{}}async function
|
|
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
|
-
`));
|
|
16
|
-
\u274C Directory "${
|
|
17
|
-
`)),process.exit(1)),!
|
|
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(
|
|
20
|
-
`)),process.exit(0)),
|
|
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
|
|
23
|
-
`)),
|
|
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=${
|
|
27
|
-
`,await
|
|
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}=${
|
|
31
|
-
`,await
|
|
32
|
-
Missing required Zibby dependencies in this project:`)),console.log(
|
|
33
|
-
`));else{
|
|
34
|
-
`)))}}}if(!
|
|
35
|
-
Using mem0 backend requires mem0ai in your project dependencies.`)),console.log(
|
|
36
|
-
`))),!
|
|
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
|
-
`)),
|
|
39
|
-
`))}}}if(
|
|
40
|
-
`))}}}if(
|
|
41
|
-
`))}}
|
|
42
|
-
`,"utf-8");let _="Gemini MCP configured";
|
|
43
|
-
`))}}if(
|
|
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(
|
|
46
|
-
`)),console.log(
|
|
47
|
-
`))}}console.log(
|
|
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(
|
|
50
|
-
`)),console.log(
|
|
51
|
-
`)),console.log(
|
|
52
|
-
`))}catch(
|
|
53
|
-
\u274C Error: ${
|
|
54
|
-
`)),process.exit(1)}}function
|
|
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
|
-
},`}
|
|
68
|
-
`).map(
|
|
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: {${
|
|
73
|
-
${
|
|
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: ${
|
|
101
|
+
headless: ${o.browserMode==="headless"},
|
|
82
102
|
},
|
|
83
103
|
|
|
84
104
|
// Chat memory backend adapter (dolt | mem0)
|
|
85
105
|
memory: {
|
|
86
|
-
backend: '${
|
|
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: ${
|
|
154
|
+
cloudSync: ${o.cloudSync||!1}
|
|
135
155
|
};
|
|
136
|
-
`}function
|
|
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"}[
|
|
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=${
|
|
153
|
-
`}function
|
|
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"}[
|
|
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=${
|
|
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=${
|
|
168
|
-
`}function
|
|
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
|
|
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
|
-
${
|
|
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
|
|
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
|
|
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
|
|
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)"}[
|
|
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${
|
|
351
|
+
- Cloud sync${e.cloudSync?" (enabled)":" (disabled)"}
|
|
332
352
|
- Self-healing behavior
|
|
333
353
|
|
|
334
354
|
## Project Structure
|
|
335
355
|
|
|
336
356
|
\`\`\`
|
|
337
|
-
${
|
|
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{
|
|
375
|
+
`}export{ft as initCommand};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
Please log in first:`)),console.log(
|
|
3
|
-
`)),process.exit(1)),
|
|
4
|
-
`)));
|
|
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}
|
|
7
|
-
Your Projects (${
|
|
8
|
-
`),console.log(
|
|
9
|
-
`)}catch(n){
|
|
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{
|
|
11
|
+
`)),process.exit(1)}}export{G as listProjectsCommand};
|