@zibby/cli 0.1.60 → 0.1.62
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/bin/zibby.js +2 -2
- 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 +88 -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/delete.js +8 -0
- 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 +14 -8
- 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 +2 -2
- 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 +2 -2
|
@@ -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(`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("No transport configured for final status \u2014 neither PROGRESS_API_URL nor PROGRESS_QUEUE_URL set");return}console.log(`Final status ${r} sent via ${p}`)}catch(y){console.error(`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)
|
|
@@ -97,6 +117,7 @@ ${b}
|
|
|
97
117
|
specs: 'test-specs', // Where your .txt test specs are
|
|
98
118
|
generated: 'tests', // Where generated .spec.js files go
|
|
99
119
|
output: '.zibby/output', // Where workflow execution results are saved (default: .zibby/output)
|
|
120
|
+
workflows: '.zibby/workflows', // Where custom workflows are stored
|
|
100
121
|
// sessionPrefix: 'run', // Optional: prefix for session folders (e.g., run_1772788458045)
|
|
101
122
|
},
|
|
102
123
|
|
|
@@ -131,13 +152,13 @@ ${b}
|
|
|
131
152
|
},
|
|
132
153
|
|
|
133
154
|
// Cloud sync - auto-upload test results & videos (requires ZIBBY_API_KEY in .env)
|
|
134
|
-
cloudSync: ${
|
|
155
|
+
cloudSync: ${o.cloudSync||!1}
|
|
135
156
|
};
|
|
136
|
-
`}function
|
|
157
|
+
`}function Oe(o,e="dolt"){return`# Zibby Test Automation - Environment Variables
|
|
137
158
|
|
|
138
159
|
# AI Provider Keys
|
|
139
160
|
${{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"}[
|
|
161
|
+
# 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
162
|
|
|
142
163
|
# Zibby Cloud Sync (for uploading test results & videos)
|
|
143
164
|
# Get your API key from: https://zibby.app/settings/tokens
|
|
@@ -149,14 +170,14 @@ ${{claude:"ANTHROPIC_API_KEY=sk-ant-your_key_here",cursor:`# Cursor Agent uses c
|
|
|
149
170
|
# ZIBBY_MEMORY_COMPACT_EVERY=1500 # Auto-compact every N runs (0 to disable)
|
|
150
171
|
|
|
151
172
|
# Chat memory backend
|
|
152
|
-
ZIBBY_MEMORY_BACKEND=${
|
|
153
|
-
`}function
|
|
173
|
+
ZIBBY_MEMORY_BACKEND=${e}
|
|
174
|
+
`}function De(o,e,a="dolt"){return`# Zibby Test Automation - Environment Variables
|
|
154
175
|
|
|
155
176
|
# 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"}[
|
|
177
|
+
${{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
178
|
|
|
158
179
|
# Zibby Cloud Sync
|
|
159
|
-
ZIBBY_API_KEY=${
|
|
180
|
+
ZIBBY_API_KEY=${e}
|
|
160
181
|
|
|
161
182
|
# Test Memory (Dolt DB) - Auto-compaction settings
|
|
162
183
|
# ZIBBY_MEMORY_MAX_RUNS=3000 # Max test runs to keep per spec
|
|
@@ -164,8 +185,8 @@ ZIBBY_API_KEY=${o}
|
|
|
164
185
|
# ZIBBY_MEMORY_COMPACT_EVERY=1500 # Auto-compact every N runs (0 to disable)
|
|
165
186
|
|
|
166
187
|
# Chat memory backend
|
|
167
|
-
ZIBBY_MEMORY_BACKEND=${
|
|
168
|
-
`}function
|
|
188
|
+
ZIBBY_MEMORY_BACKEND=${a}
|
|
189
|
+
`}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
190
|
node_modules/
|
|
170
191
|
|
|
171
192
|
# Test artifacts
|
|
@@ -193,11 +214,11 @@ Thumbs.db
|
|
|
193
214
|
# Zibby memory (Dolt DB synced separately via dolt remote, not git)
|
|
194
215
|
.zibby/memory/
|
|
195
216
|
.zibby/memory-context.md
|
|
196
|
-
`}function
|
|
217
|
+
`}function Ze(o="off",e=null){return`import { defineConfig} from '@playwright/test';
|
|
197
218
|
|
|
198
219
|
export default defineConfig({
|
|
199
220
|
testDir: './tests',
|
|
200
|
-
${
|
|
221
|
+
${e?` outputDir: '${e}',
|
|
201
222
|
`:` outputDir: 'test-results/playwright', // Keep Playwright artifacts separate from Zibby workflow output
|
|
202
223
|
`} timeout: 30000,
|
|
203
224
|
retries: 0,
|
|
@@ -215,7 +236,7 @@ ${o?` outputDir: '${o}',
|
|
|
215
236
|
['list']
|
|
216
237
|
],
|
|
217
238
|
});
|
|
218
|
-
`}function
|
|
239
|
+
`}function Ve(){return`Test Specification: Example Domain Navigation
|
|
219
240
|
==============================================
|
|
220
241
|
|
|
221
242
|
Application: Example Domain
|
|
@@ -252,7 +273,7 @@ Notes:
|
|
|
252
273
|
- Uses a stable, public domain (example.com) maintained by IANA
|
|
253
274
|
- No authentication required
|
|
254
275
|
- Suitable for CI/CD smoke testing
|
|
255
|
-
`}function
|
|
276
|
+
`}function Ge(){return`# Example command template
|
|
256
277
|
|
|
257
278
|
Use this command when the user asks for a concise testing task breakdown.
|
|
258
279
|
|
|
@@ -263,7 +284,7 @@ Required output format:
|
|
|
263
284
|
4. Expected result
|
|
264
285
|
|
|
265
286
|
Keep the response actionable and short.
|
|
266
|
-
`}function
|
|
287
|
+
`}function He(o,e){return`# ${o}
|
|
267
288
|
|
|
268
289
|
AI-powered test automation with Zibby.
|
|
269
290
|
|
|
@@ -277,7 +298,7 @@ npm install
|
|
|
277
298
|
2. Configure environment:
|
|
278
299
|
\`\`\`bash
|
|
279
300
|
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)"}[
|
|
301
|
+
# 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
302
|
\`\`\`
|
|
282
303
|
|
|
283
304
|
3. Run example test:
|
|
@@ -328,13 +349,13 @@ npx playwright test --ui
|
|
|
328
349
|
Edit \`.zibby.config.mjs\` to customize:
|
|
329
350
|
- Agent settings (model, temperature)
|
|
330
351
|
- Browser settings (headless, viewport)
|
|
331
|
-
- Cloud sync${
|
|
352
|
+
- Cloud sync${e.cloudSync?" (enabled)":" (disabled)"}
|
|
332
353
|
- Self-healing behavior
|
|
333
354
|
|
|
334
355
|
## Project Structure
|
|
335
356
|
|
|
336
357
|
\`\`\`
|
|
337
|
-
${
|
|
358
|
+
${o}/
|
|
338
359
|
\u251C\u2500\u2500 .zibby/
|
|
339
360
|
\u2502 \u251C\u2500\u2500 graph.mjs # Workflow definition
|
|
340
361
|
\u2502 \u251C\u2500\u2500 nodes/ # Custom nodes
|
|
@@ -352,4 +373,4 @@ ${d}/
|
|
|
352
373
|
|
|
353
374
|
- Documentation: https://docs.zibby.dev
|
|
354
375
|
- Examples: https://github.com/zibby/examples
|
|
355
|
-
`}export{
|
|
376
|
+
`}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};
|