@zibby/cli 0.1.79 → 0.1.81

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.
@@ -0,0 +1,17 @@
1
+ import{existsSync as X,readFileSync as Q}from"fs";import{join as oo}from"path";import U from"chalk";import{confirm as eo}from"@inquirer/prompts";import t from"chalk";import w from"ora";import{spawn as M}from"child_process";var f={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 v(){let o;if(process.env.ZIBBY_API_URL)o=process.env.ZIBBY_API_URL;else{let e=process.env.ZIBBY_ENV||"prod";f[e]?o=f[e].apiUrl:o=f.prod.apiUrl}try{let e=new URL(o);return e.protocol!=="http:"&&e.protocol!=="https:"?(console.error(`\u26A0\uFE0F Invalid API URL protocol: ${e.protocol} (only http/https allowed)`),f.prod.apiUrl):o}catch{return console.error(`\u26A0\uFE0F Invalid API URL: ${o}`),f.prod.apiUrl}}import{existsSync as T,mkdirSync as F,readFileSync as Z,writeFileSync as J}from"fs";import{homedir as P}from"os";import{join as h}from"path";function A(){return process.env.ZIBBY_CONFIG_DIR||h(P(),".zibby")}function j(){return h(A(),"config.json")}var K=h(P(),".zibby"),io=h(K,"config.json");function V(){let o=A();T(o)||F(o,{recursive:!0})}function c(){try{let o=j();if(T(o)){let e=Z(o,"utf-8");return JSON.parse(e)}}catch{}return{}}function d(o){V(),J(j(),JSON.stringify(o,null,2))}function _(){return c().sessionToken||null}function B(o){let e=c();e.sessionToken=o,d(e)}function N(){return c().user||null}function L(o){let e=c();e.user=o,d(e)}function R(o){let e=c();e.proxyUrl=o,d(e)}function C(o){let e=c();e.mem0ProxyUrl=o,d(e)}function O(o){let e=c();e.projects=o,d(e)}import{existsSync as ao,mkdirSync as uo,readFileSync as po,writeFileSync as go,unlinkSync as fo}from"fs";import{resolve as ho}from"path";import{homedir as vo}from"os";function G(o){let e=process.platform;try{let n,r;return e==="darwin"?(n="open",r=[o]):e==="win32"?(n="cmd",r=["/c","start","",o]):(n="xdg-open",r=[o]),M(n,r,{detached:!0,stdio:"ignore"}).unref(),!0}catch{return!1}}function H(){let o=_(),e=N();return o&&e?{loggedIn:!0,user:e,token:o}:{loggedIn:!1}}async function $(){try{console.log(t.cyan(`
2
+ \u{1F510} Initiating login...
3
+ `));let o=H();if(o.loggedIn){console.log(t.green("\u2705 Already logged in!")),console.log(t.gray(`User: ${o.user.email}`)),console.log(t.gray(`Name: ${o.user.name}
4
+ `));let{createInterface:e}=await import("readline"),n=e({input:process.stdin,output:process.stdout});return new Promise((r,a)=>{let i=()=>{n.close(),process.stdin.isTTY&&process.stdin.setRawMode(!1)},u=()=>{console.log(t.yellow(`
5
+
6
+ \u26A0\uFE0F Login cancelled
7
+ `)),i(),process.exit(0)};process.on("SIGINT",u),n.question(t.yellow("Continue with this session? (Y/n): "),async p=>{process.removeListener("SIGINT",u),i();try{if(p.toLowerCase()==="n"||p.toLowerCase()==="no"){console.log(t.gray(`Starting new login...
8
+ `));let y=await z();r(y)}else console.log(t.green(`Using existing session.
9
+ `)),r({success:!0,...o})}catch(y){a(y)}})})}return await z()}catch(o){return console.error(t.red(`
10
+ \u274C Login failed:`,o.message)),{success:!1,error:o.message}}}async function W(o){let e=v();try{let n=await fetch(`${e}/projects`,{headers:{Authorization:`Bearer ${o}`}});if(n.ok){let a=((await n.json()).projects||[]).map(i=>({name:i.name,projectId:i.projectId,apiToken:i.apiToken}));return O(a),a}}catch(n){console.log(t.gray(`\u26A0\uFE0F Could not fetch projects: ${n.message}`))}return[]}async function z(){let o=v(),e=w("Requesting login code...").start(),n=await fetch(`${o}/cli/login/initiate`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!n.ok){e.fail("Failed to request login code");let l=await n.json();throw new Error(l.error||"Failed to initiate login")}let{deviceCode:r,userCode:a,verificationUrl:i,expiresIn:u,interval:p}=await n.json();e.succeed("Login code generated"),console.log(""),console.log(t.cyan("\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\u2557")),console.log(t.cyan("\u2551")+t.white.bold(" Complete login in your browser ")+t.cyan("\u2551")),console.log(t.cyan("\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\u255D")),console.log(""),console.log(t.white("Opening browser to login page...")),console.log(t.gray(`Code expires in ${Math.floor(u/60)} minutes`)),console.log(""),await G(i)||(console.log(t.yellow("\u26A0\uFE0F Could not open browser automatically.")),console.log(t.white("Please open this URL manually: ")+t.blue(i)),console.log(""));let g=w("Waiting for authorization...").start(),Y=(p||3)*1e3,D=Math.floor(u/(p||3)),k=0,S=!1,x=()=>{S=!0,g.stop(),console.log(t.yellow(`
11
+
12
+ \u26A0\uFE0F Login cancelled
13
+ `)),process.exit(0)};process.on("SIGINT",x);try{for(;k<D&&!S;){await q(Y),k++;let l=await fetch(`${o}/cli/login/poll`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({deviceCode:r})});if(l.status===202)continue;if(!l.ok){g.fail("Authorization failed");let m=await l.json();throw new Error(m.error||"Authorization failed")}let s=await l.json();if(s.status==="authorized"){g.succeed(t.white("Authorization successful!")),B(s.token),L(s.user),s.proxyUrl&&R(s.proxyUrl),s.mem0ProxyUrl&&C(s.mem0ProxyUrl),console.log(""),console.log(t.gray(`User: ${s.user.email}`));let m=w("Fetching projects...").start(),b=await W(s.token);return m.succeed(`Fetched ${b.length} project${b.length!==1?"s":""}`),console.log(t.gray(`Session saved to: ~/.zibby/config.json
14
+ `)),{success:!0,loggedIn:!0,user:s.user,token:s.token}}if(s.status==="denied")throw g.fail("Authorization denied"),new Error("User denied authorization")}throw g.fail("Login timeout"),new Error("Login timed out - please try again")}finally{process.removeListener("SIGINT",x)}}function q(o){return new Promise(e=>setTimeout(e,o))}function E(){try{let o=process.env.HOME||process.env.USERPROFILE;if(!o)return null;let e=oo(o,".zibby","config.json");return X(e)&&JSON.parse(Q(e,"utf-8")).sessionToken||null}catch{return null}}function I(){console.log(`
15
+ Not authenticated.`),console.log(` Run ${U.cyan("zibby login")} or set ${U.cyan("ZIBBY_API_KEY")} in your environment.
16
+ `)}async function Ro(o={}){let e=o.apiKey||process.env.ZIBBY_API_KEY||null,n=E();if(n||e)return{sessionToken:n,apiKey:e};if(!process.stdin.isTTY){if(o.optional)return{sessionToken:null,apiKey:null};I(),process.exit(1)}console.log(U.yellow(`
17
+ Not logged in.`));let r;try{r=await eo({message:"Open browser to log in now?",default:!0})}catch{r=!1}if(!r){if(o.optional)return{sessionToken:null,apiKey:null};I(),process.exit(1)}if(await $(),n=E(),!n){if(o.optional)return{sessionToken:null,apiKey:null};I(),process.exit(1)}return{sessionToken:n,apiKey:null}}export{Ro as ensureAuthenticated};
package/dist/bin/zibby.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- process.stdout.on("error",o=>{o.code}),process.stderr.on("error",o=>{o.code}),process.env.DOTENV_CONFIG_QUIET="true";import"@zibby/skills";import{Command as l}from"commander";import{initCommand as m}from"../commands/init.js";import{runCommand as u}from"../commands/run.js";import{videoCommand as f}from"../commands/video.js";import{uploadCommand as w}from"../commands/upload.js";import{ciSetupCommand as y}from"../commands/ci-setup.js";import{setupPlaywrightMcpCommand as g,setupCiCommand as h,testWithVideoCommand as k}from"../commands/setup-scripts.js";import{readFileSync as b}from"fs";import{fileURLToPath as v}from"url";import{dirname as I,join as C}from"path";import{bootstrapAgentEnv as P}from"../utils/agent-credentials.js";const S=v(import.meta.url),j=I(S),s=JSON.parse(b(C(j,"../package.json"),"utf-8"));function _(o,e){return e.push(o),e}const c=`zibby v${s.version}`,i=process.argv.slice(2),D=i.includes("-h")||i.includes("--help"),A=i.includes("-v")||i.includes("-V")||i.includes("--version");A&&(console.log(c),process.exit(0));const d=i[0],E=i[1],x=["logs","uninstall"],B=d==="workflow"&&E==="list";!x.includes(d)&&!B&&console.log(`${c}
3
- `),P(process.cwd());const Z=i[0]==="chat",O=["--verbose","--agent","--stream","-s"],Y=i.length>0&&i.every(o=>O.includes(o)||i[i.indexOf(o)-1]==="--agent"),R=i.length===0||Z||Y;if(R&&!D){const o={},e=i.indexOf("--agent");e!==-1&&i[e+1]&&(o.agent=i[e+1]),i.includes("--verbose")&&(o.verbose=!0),(i.includes("--stream")||i.includes("-s"))&&(o.stream=!0);const{chatCommand:n}=await import("../commands/chat.js");await n(o),process.exit(0)}const t=new l;t.name("zibby").description("Zibby Test Automation - AI-powered test generation").version(s.version,"-V, --version"),t.command("init").description("Initialize a new Zibby test project (like rails new)").argument("[project-name]","Project name (optional, uses current directory if not provided)").option("--agent <type>","Agent to use (claude, cursor, codex, gemini)").option("--memory-backend <backend>","Memory backend to configure (dolt, mem0)","dolt").option("--skip-install","Skip npm install").option("--skip-memory","Skip test memory setup during initialization").option("-f, --force","Force reinitialize (overwrite existing config)").option("--headed","Run MCP browser in headed mode (visible browser)").option("--headless","Run MCP browser in headless mode (hidden browser)").option("--api-key <key>","Zibby API key for cloud sync").option("--cloud-sync","Enable cloud sync and install Zibby MCP").action(m),t.command("test").description("Run a test specification").argument("[spec-path]","Path to test spec file or inline test description in quotes").option("--sources <ids>","Comma-separated test case IDs to fetch from cloud").option("--execution <id>","Execution ID containing the test cases (required with --sources)").option("--agent <type>","Agent to use (claude, cursor, codex, gemini) - overrides config").option("--workflow <name>","Workflow to use (e.g., QuickSmokeWorkflow, quick-smoke)").option("--headless","Run browser in headless mode").option("--node <name>","Run only a specific node (e.g., execute_live, generate_script)").option("--session <id>",'Use existing session (e.g., 1768974629717 or "last") - requires --node').option("--session-path <dir>","Use this session folder (absolute or relative to cwd); Studio pins artifacts here").option("--project <id>","Project ID (optional, auto-detected from ZIBBY_API_KEY)").option("--collection <id-or-name>","Collection ID or name (creates new if name doesn't exist)").option("--folder <path>","Folder path within collection (optional, requires --collection)").option("--sync","Force upload to cloud (overrides cloudSync: false)").option("--no-sync","Skip upload to cloud (overrides cloudSync: true)").option("--config <path>","Path to config file",".zibby.config.mjs").option("--auto-approve","Auto-approve MCP tools (for CI/CD)").option("-o, --open","Open test results in browser after completion").option("--verbose","Show info level logs").option("--debug","Show debug level logs (most verbose)").option("-m, --mem","Enable test memory (Dolt-backed knowledge from previous runs)").action((o,e)=>(e.debug?process.env.ZIBBY_DEBUG="true":e.verbose&&(process.env.ZIBBY_VERBOSE="true"),u(o,e))),t.command("implement").description("Implement a Jira ticket using AI agent (runs in ECS container)").action(async(...o)=>{const{implementCommand:e}=await import("../commands/implement.js");return e(...o)}),t.command("analyze").description("Analyze a Jira ticket against the codebase (runs in ECS container)").option("--workflow <path>","Path to a local workflow JSON file (e.g., .zibby/workflow-analysis.json)").action(async(...o)=>{const{analyzeCommand:e}=await import("../commands/analyze-graph.js");return e(...o)}),t.command("video").description("Organize test videos next to test files").action(f),t.command("upload <spec-path>").description("Upload existing test artifacts to Zibby Cloud").option("--project <id>","Project ID (REQUIRED - use flag or ZIBBY_PROJECT_ID env)").option("--collection <id-or-name>","Collection ID or name (creates new if name doesn't exist)").option("--folder <path>","Folder path within collection (optional)").option("--agent <type>","Agent used (for metadata)").action(w),t.command("login").description("Log in to Zibby (opens browser for authentication)").action(async()=>{const{loginCli:o}=await import("../auth/cli-login.js");await o(),process.exit(0)}),t.command("logout").description("Log out from Zibby (clears saved session)").action(async()=>{const{logoutCli:o}=await import("../auth/cli-login.js");o(),process.exit(0)}),t.command("status").description("Show current authentication status and token details").option("--json","Output in JSON format (for scripts)").action(async o=>{const{showLoginStatus:e}=await import("../auth/cli-login.js");await e(o),process.exit(0)}),t.command("list").description("List your projects and API tokens").action(async()=>{const{listProjectsCommand:o}=await import("../commands/list-projects.js");await o()}),t.command("ci-setup").description("Setup Cursor Agent for CI/CD (patch and configure)").option("--get-keys","Get MCP approval keys").option("--save","Save approval keys to project").action(y),t.command("setup-playwright").description("Setup official Playwright MCP (from cursor-agent-package)").option("--headed","Configure MCP in headed mode (visible browser)").option("--viewport-width <width>","Viewport width (default: 1280)","1280").option("--viewport-height <height>","Viewport height (default: 720)","720").action(o=>{const e={width:parseInt(o.viewportWidth,10)||1280,height:parseInt(o.viewportHeight,10)||720};return g({...o,viewport:e})}),t.command("setup-ci-full").description("Complete CI/CD setup from scratch").action(h),t.command("test-video").description("Run Playwright tests with video recording").argument("[test-file]","Test file to run (default: tests/)").option("--headed","Run in headed mode (visible browser)").action(k),t.command("generate").description("Generate test specs from a ticket + codebase (local analysis using real AI agent)").option("-t, --ticket <key>","Jira ticket key (fetches automatically)").option("-i, --input <file>","Input file with ticket description/requirements").option("-d, --description <text>","Inline ticket description").option("--repo <path>","Path to the codebase (default: current directory)").option("--agent <type>","Agent to use (codex, claude, cursor, gemini)").option("--model <model>","Model override").option("-o, --output <dir>","Output directory for spec files (default: test-specs)").action(async o=>{const{generateCommand:e}=await import("../commands/generate.js");return e(o)}),t.command("studio").description("Launch Zibby Studio desktop (installs from CDN if needed). Uses this folder as the Zibby project.").option("-p, --port <port>","Port for the Studio API bridge (default: 3847)").option("--no-open","Start the API only; do not launch desktop app").option("--update","Force re-download of the latest Studio binary").action(async o=>{const{studioCommand:e}=await import("../commands/studio.js");return e(o)});const z=t.command("g").description("Generate scaffolds");z.command("workflow [name]").description("Scaffold a new custom workflow in .zibby/workflows/<name>/ (auto-generates name if omitted)").action(async o=>{const{generateWorkflowCommand:e}=await import("../commands/workflows/generate.js");return e(o)}),t.command("start <workflow-name>").description("Start a local dev server for a custom workflow").option("-p, --port <port>","Port for the workflow server (default: 3848)").action(async(o,e)=>{const{startWorkflowCommand:n}=await import("../commands/workflows/start.js");return n(o,e)}),t.command("deploy [workflow-name]").description("Deploy a custom workflow to Zibby Cloud (interactive selection if workflow-name not provided)").option("--project <id>","Project ID (interactive prompt if not provided, or ZIBBY_PROJECT_ID env)").option("--api-key <key>","API key (or ZIBBY_API_KEY env)").option("--dedicated-ip <action>","Manage dedicated egress IP addon: enable | status | disable | use | unuse").action(async(o,e)=>{if(e.dedicatedIp){const{dedicatedEgressCommand:p}=await import("../commands/workflows/dedicated-egress.js");return p(e.dedicatedIp,e.project)}const{deployWorkflowCommand:n}=await import("../commands/workflows/deploy.js");return n(o,e)}),t.command("trigger [uuid]").description("Trigger a deployed workflow by UUID (interactive selection if not provided). Get UUIDs from `zibby workflow list`.").option("--project <id>","Project ID (interactive prompt if not provided, or ZIBBY_PROJECT_ID env)").option("--api-key <key>","API key (or ZIBBY_API_KEY env)").option("-p, --param <key=value>","Input param (repeatable, e.g. -p url=https://x.com -p count=5)",_,[]).option("--input <json>",`Input as JSON string \u2014 overridden by --param (e.g. '{"key":"value"}')`).option("--input-file <path>","Input as JSON/YAML file \u2014 lowest precedence").option("--idempotency-key <key>","Idempotency key to prevent duplicate executions").action(async(o,e)=>{const{triggerWorkflowCommand:n}=await import("../commands/workflows/trigger.js");return n(o,e)}),t.command("logs [jobId]").description("Fetch and display logs from a workflow execution (use -t to stream in real-time)").option("--project <id>","Project ID (or ZIBBY_PROJECT_ID env)").option("--workflow <name>","Workflow name (fetches the latest run)").option("--all","Show interleaved logs from all runs (requires --workflow)").option("-t, --follow","Stream logs in real-time (like Heroku logs -t)").option("--api-key <key>","API key (or ZIBBY_API_KEY env)").option("--lines <n>","Max log lines per fetch (default: 500)").action(async(o,e)=>{const{logsCommand:n}=await import("../commands/workflows/logs.js");return n(o,e)}),t.command("run-workflow").description("Run a deployed workflow from S3 sources (used by ECS containers)").action(async()=>{const{runWorkflowCommand:o}=await import("../commands/workflows/run.js");return o()});const a=t.command("memory").description("Test memory database \u2014 version-controlled knowledge from runs");a.command("stats").description("Show memory database statistics").action(async()=>{const{memoryStatsCommand:o}=await import("../commands/memory.js");return o()}),a.command("init").description("Initialize the memory database (Dolt)").action(async()=>{const{memoryInitCommand:o}=await import("../commands/memory.js");return o()}),a.command("compact").description("Prune old data and run Dolt GC to reclaim storage").option("--max-runs <n>","Keep last N runs per spec (default: 50)",parseInt).option("--max-age <days>","Remove data older than N days (default: 90)",parseInt).action(async o=>{const{memoryCompactCommand:e}=await import("../commands/memory.js");return e(o)}),a.command("reset").description("Wipe the memory database").option("-f, --force","Confirm reset").action(async o=>{const{memoryResetCommand:e}=await import("../commands/memory.js");return e(o)});const r=t.command("workflow").description("Manage workflow graphs (download, upload, list)");r.command("download").description("Download a workflow graph from Zibby Cloud to .zibby/").option("--project <id>","Project ID (or ZIBBY_PROJECT_ID env)").option("--type <type>","Workflow type: analysis, implementation, run_test").option("--api-key <key>","API key (or ZIBBY_API_KEY env)").option("--output <dir>","Output directory (default: .zibby/)").option("--include-default","Download the built-in default graph if no custom one exists").action(async o=>{const{workflowDownloadCommand:e}=await import("../commands/workflow.js");return e(o)}),r.command("list").description("List all workflows (local + remote if credentials available)").option("--local-only","Show only local workflows").option("--remote-only","Show only remote workflows (requires --project)").option("--project <id>","Project ID (optional, uses ZIBBY_PROJECT_ID env)").option("--api-key <key>","API key (or ZIBBY_API_KEY env)").action(async o=>{if(o.remoteOnly){const{workflowListCommand:n}=await import("../commands/workflow.js");return n(o)}if(o.localOnly){const{listLocalWorkflowsCommand:n}=await import("../commands/workflows/list.js");return n(o)}const{listAllWorkflowsCommand:e}=await import("../commands/workflows/list.js");return e(o)}),r.command("delete <uuid>").description("Delete a deployed workflow by UUID").action(async o=>{const{deleteWorkflowCommand:e}=await import("../commands/workflows/delete.js");return e(o,{})});const W=t.command("project").description("Manage Zibby projects");W.command("list").description("List all projects").option("--api-key <key>","API key (or ZIBBY_API_KEY env)").action(async o=>{const{projectListCommand:e}=await import("../commands/project.js");return e(o)});const L=t.command("run-queue").description("Parallel capacity \u2014 on-disk wait queue (see parallel.waitWhenAtCapacity in .zibby.config.mjs)");L.command("list").description("List CLI processes waiting for a run slot").option("--config <path>","Path to config file",".zibby.config.mjs").action(async o=>{const{runCapacityQueueListCommand:e}=await import("../commands/run-capacity-queue-cli.js");await e(o),process.exit(0)}),t.command("uninstall").description("Remove all Zibby data: global CLI, ~/.zibby, Cursor MCP config, Studio, and current project").option("--dry-run","Show what would be deleted without deleting").option("--deep","Also remove npx cache dirs containing zibby").action(async o=>{const{uninstallCommand:e}=await import("../commands/uninstall.js");await e(o),process.exit(0)}),t.parse();
2
+ process.stdout.on("error",o=>{o.code}),process.stderr.on("error",o=>{o.code}),process.env.DOTENV_CONFIG_QUIET="true";import"@zibby/skills";import{Command as m}from"commander";import{initCommand as u}from"../commands/init.js";import{runCommand as f}from"../commands/run.js";import{videoCommand as w}from"../commands/video.js";import{uploadCommand as y}from"../commands/upload.js";import{ciSetupCommand as g}from"../commands/ci-setup.js";import{setupPlaywrightMcpCommand as k,setupCiCommand as b,testWithVideoCommand as h}from"../commands/setup-scripts.js";import{readFileSync as I}from"fs";import{fileURLToPath as v}from"url";import{dirname as C,join as P}from"path";import{bootstrapAgentEnv as _}from"../utils/agent-credentials.js";const S=v(import.meta.url),j=C(S),s=JSON.parse(I(P(j,"../package.json"),"utf-8"));function c(o,e){return e.push(o),e}const p=`zibby v${s.version}`,n=process.argv.slice(2),B=n.includes("-h")||n.includes("--help"),D=n.includes("-v")||n.includes("-V")||n.includes("--version");D&&(console.log(p),process.exit(0));const l=n[0],A=n[1],E=["logs","uninstall"],x=l==="workflow"&&A==="list";!E.includes(l)&&!x&&console.log(`${p}
3
+ `),_(process.cwd());const Z=n[0]==="chat",Y=["--verbose","--agent","--stream","-s"],O=n.length>0&&n.every(o=>Y.includes(o)||n[n.indexOf(o)-1]==="--agent"),z=n.length===0||Z||O;if(z&&!B){const o={},e=n.indexOf("--agent");e!==-1&&n[e+1]&&(o.agent=n[e+1]),n.includes("--verbose")&&(o.verbose=!0),(n.includes("--stream")||n.includes("-s"))&&(o.stream=!0);const{chatCommand:i}=await import("../commands/chat.js");await i(o),process.exit(0)}const t=new m;t.name("zibby").description("Zibby Test Automation - AI-powered test generation").version(s.version,"-V, --version"),t.command("init").description("Initialize a new Zibby test project (like rails new)").argument("[project-name]","Project name (optional, uses current directory if not provided)").option("--agent <type>","Agent to use (claude, cursor, codex, gemini)").option("--memory-backend <backend>","Memory backend to configure (dolt, mem0)","dolt").option("--skip-install","Skip npm install").option("--skip-memory","Skip test memory setup during initialization").option("-f, --force","Force reinitialize (overwrite existing config)").option("--headed","Run MCP browser in headed mode (visible browser)").option("--headless","Run MCP browser in headless mode (hidden browser)").option("--api-key <key>","Zibby API key for cloud sync").option("--cloud-sync","Enable cloud sync and install Zibby MCP").action(u),t.command("test").description("Run a test specification").argument("[spec-path]","Path to test spec file or inline test description in quotes").option("--sources <ids>","Comma-separated test case IDs to fetch from cloud").option("--execution <id>","Execution ID containing the test cases (required with --sources)").option("--agent <type>","Agent to use (claude, cursor, codex, gemini) - overrides config").option("--workflow <name>","Workflow to use (e.g., QuickSmokeWorkflow, quick-smoke)").option("--headless","Run browser in headless mode").option("--node <name>","Run only a specific node (e.g., execute_live, generate_script)").option("--session <id>",'Use existing session (e.g., 1768974629717 or "last") - requires --node').option("--session-path <dir>","Use this session folder (absolute or relative to cwd); Studio pins artifacts here").option("--project <id>","Project ID (optional, auto-detected from ZIBBY_API_KEY)").option("--collection <id-or-name>","Collection ID or name (creates new if name doesn't exist)").option("--folder <path>","Folder path within collection (optional, requires --collection)").option("--sync","Force upload to cloud (overrides cloudSync: false)").option("--no-sync","Skip upload to cloud (overrides cloudSync: true)").option("--config <path>","Path to config file",".zibby.config.mjs").option("--auto-approve","Auto-approve MCP tools (for CI/CD)").option("-o, --open","Open test results in browser after completion").option("--verbose","Show info level logs").option("--debug","Show debug level logs (most verbose)").option("-m, --mem","Enable test memory (Dolt-backed knowledge from previous runs)").action((o,e)=>(e.debug?process.env.ZIBBY_DEBUG="true":e.verbose&&(process.env.ZIBBY_VERBOSE="true"),f(o,e))),t.command("implement").description("Implement a Jira ticket using AI agent (runs in ECS container)").action(async(...o)=>{const{implementCommand:e}=await import("../commands/implement.js");return e(...o)}),t.command("analyze").description("Analyze a Jira ticket against the codebase (runs in ECS container)").option("--workflow <path>","Path to a local workflow JSON file (e.g., .zibby/workflow-analysis.json)").action(async(...o)=>{const{analyzeCommand:e}=await import("../commands/analyze-graph.js");return e(...o)}),t.command("video").description("Organize test videos next to test files").action(w),t.command("upload <spec-path>").description("Upload existing test artifacts to Zibby Cloud").option("--project <id>","Project ID (REQUIRED - use flag or ZIBBY_PROJECT_ID env)").option("--collection <id-or-name>","Collection ID or name (creates new if name doesn't exist)").option("--folder <path>","Folder path within collection (optional)").option("--agent <type>","Agent used (for metadata)").action(y),t.command("login").description("Log in to Zibby (opens browser for authentication)").action(async()=>{const{loginCli:o}=await import("../auth/cli-login.js");await o(),process.exit(0)}),t.command("logout").description("Log out from Zibby (clears saved session)").action(async()=>{const{logoutCli:o}=await import("../auth/cli-login.js");o(),process.exit(0)}),t.command("status").description("Show current authentication status and token details").option("--json","Output in JSON format (for scripts)").action(async o=>{const{showLoginStatus:e}=await import("../auth/cli-login.js");await e(o),process.exit(0)}),t.command("list").description("List your projects and API tokens").action(async()=>{const{listProjectsCommand:o}=await import("../commands/list-projects.js");await o()}),t.command("ci-setup").description("Setup Cursor Agent for CI/CD (patch and configure)").option("--get-keys","Get MCP approval keys").option("--save","Save approval keys to project").action(g),t.command("setup-playwright").description("Setup official Playwright MCP (from cursor-agent-package)").option("--headed","Configure MCP in headed mode (visible browser)").option("--viewport-width <width>","Viewport width (default: 1280)","1280").option("--viewport-height <height>","Viewport height (default: 720)","720").action(o=>{const e={width:parseInt(o.viewportWidth,10)||1280,height:parseInt(o.viewportHeight,10)||720};return k({...o,viewport:e})}),t.command("setup-ci-full").description("Complete CI/CD setup from scratch").action(b),t.command("test-video").description("Run Playwright tests with video recording").argument("[test-file]","Test file to run (default: tests/)").option("--headed","Run in headed mode (visible browser)").action(h),t.command("generate").description("Generate test specs from a ticket + codebase (local analysis using real AI agent)").option("-t, --ticket <key>","Jira ticket key (fetches automatically)").option("-i, --input <file>","Input file with ticket description/requirements").option("-d, --description <text>","Inline ticket description").option("--repo <path>","Path to the codebase (default: current directory)").option("--agent <type>","Agent to use (codex, claude, cursor, gemini)").option("--model <model>","Model override").option("-o, --output <dir>","Output directory for spec files (default: test-specs)").action(async o=>{const{generateCommand:e}=await import("../commands/generate.js");return e(o)}),t.command("studio").description("Launch Zibby Studio desktop (installs from CDN if needed). Uses this folder as the Zibby project.").option("-p, --port <port>","Port for the Studio API bridge (default: 3847)").option("--no-open","Start the API only; do not launch desktop app").option("--update","Force re-download of the latest Studio binary").action(async o=>{const{studioCommand:e}=await import("../commands/studio.js");return e(o)});const R=t.command("g").description("Generate scaffolds");R.command("workflow [name]").description("Scaffold a new custom workflow in .zibby/workflows/<name>/ (auto-generates name if omitted)").option("--skip-install","Skip running `npm install` in the new workflow folder").action(async(o,e)=>{const{generateWorkflowCommand:i}=await import("../commands/workflows/generate.js");return i(o,e)}),t.command("start <workflow-name>").description("Start a local dev server for a custom workflow").option("-p, --port <port>","Port for the workflow server (default: 3848)").action(async(o,e)=>{const{startWorkflowCommand:i}=await import("../commands/workflows/start.js");return i(o,e)}),t.command("deploy [workflow-name]").description("Deploy a custom workflow to Zibby Cloud (interactive selection if workflow-name not provided)").option("--project <id>","Project ID (interactive prompt if not provided, or ZIBBY_PROJECT_ID env)").option("--api-key <key>","API key (or ZIBBY_API_KEY env)").option("--dedicated-ip <action>","Manage dedicated egress IP addon: enable | status | disable | use | unuse").action(async(o,e)=>{if(e.dedicatedIp){const{dedicatedEgressCommand:d}=await import("../commands/workflows/dedicated-egress.js");return d(e.dedicatedIp,e.project)}const{deployWorkflowCommand:i}=await import("../commands/workflows/deploy.js");return i(o,e)}),t.command("trigger [uuid]").description("Trigger a deployed workflow by UUID (interactive selection if not provided). Get UUIDs from `zibby workflow list`.").option("--project <id>","Project ID (interactive prompt if not provided, or ZIBBY_PROJECT_ID env)").option("--api-key <key>","API key (or ZIBBY_API_KEY env)").option("-p, --param <key=value>","Input param (repeatable, e.g. -p url=https://x.com -p count=5)",c,[]).option("--input <json>",`Input as JSON string \u2014 overridden by --param (e.g. '{"key":"value"}')`).option("--input-file <path>","Input as JSON/YAML file \u2014 lowest precedence").option("--idempotency-key <key>","Idempotency key to prevent duplicate executions").action(async(o,e)=>{const{triggerWorkflowCommand:i}=await import("../commands/workflows/trigger.js");return i(o,e)}),t.command("logs [jobId]").description("Fetch and display logs from a workflow execution (use -t to stream in real-time)").option("--project <id>","Project ID (or ZIBBY_PROJECT_ID env)").option("--workflow <name>","Workflow name (fetches the latest run)").option("--all","Show interleaved logs from all runs (requires --workflow)").option("-t, --follow","Stream logs in real-time (like Heroku logs -t)").option("--api-key <key>","API key (or ZIBBY_API_KEY env)").option("--lines <n>","Max log lines per fetch (default: 500)").action(async(o,e)=>{const{logsCommand:i}=await import("../commands/workflows/logs.js");return i(o,e)}),t.command("run-workflow").description("Run a deployed workflow from S3 sources (used by ECS containers)").action(async()=>{const{runWorkflowCommand:o}=await import("../commands/workflows/run.js");return o()});const a=t.command("memory").description("Test memory database \u2014 version-controlled knowledge from runs");a.command("stats").description("Show memory database statistics").action(async()=>{const{memoryStatsCommand:o}=await import("../commands/memory.js");return o()}),a.command("init").description("Initialize the memory database (Dolt)").action(async()=>{const{memoryInitCommand:o}=await import("../commands/memory.js");return o()}),a.command("compact").description("Prune old data and run Dolt GC to reclaim storage").option("--max-runs <n>","Keep last N runs per spec (default: 50)",parseInt).option("--max-age <days>","Remove data older than N days (default: 90)",parseInt).action(async o=>{const{memoryCompactCommand:e}=await import("../commands/memory.js");return e(o)}),a.command("reset").description("Wipe the memory database").option("-f, --force","Confirm reset").action(async o=>{const{memoryResetCommand:e}=await import("../commands/memory.js");return e(o)});const r=t.command("workflow").description("Manage workflow graphs \u2014 new, start, deploy, trigger, logs, list, download, delete");r.command("new [name]").description("Scaffold a new custom workflow (alias of `zibby g workflow`)").option("--skip-install","Skip running `npm install` in the new workflow folder").action(async(o,e)=>{const{generateWorkflowCommand:i}=await import("../commands/workflows/generate.js");return i(o,e)}),r.command("start <workflow-name>").description("Start a local dev server for a workflow (alias of `zibby start`)").option("-p, --port <port>","Port for the workflow server (default: 3848)").action(async(o,e)=>{const{startWorkflowCommand:i}=await import("../commands/workflows/start.js");return i(o,e)}),r.command("deploy [workflow-name]").description("Deploy a workflow to Zibby Cloud (alias of `zibby deploy`)").option("--project <id>","Project ID (interactive prompt if not provided, or ZIBBY_PROJECT_ID env)").option("--api-key <key>","API key (or ZIBBY_API_KEY env)").action(async(o,e)=>{const{deployWorkflowCommand:i}=await import("../commands/workflows/deploy.js");return i(o,e)}),r.command("trigger [uuid]").description("Trigger a deployed workflow by UUID (alias of `zibby trigger`)").option("--project <id>","Project ID (interactive prompt if not provided, or ZIBBY_PROJECT_ID env)").option("--api-key <key>","API key (or ZIBBY_API_KEY env)").option("-p, --param <key=value>","Input param (repeatable, e.g. -p url=https://x.com -p count=5)",c,[]).option("--input <json>",`Input as JSON string \u2014 overridden by --param (e.g. '{"key":"value"}')`).option("--input-file <path>","Input as JSON/YAML file \u2014 lowest precedence").option("--idempotency-key <key>","Idempotency key to prevent duplicate executions").action(async(o,e)=>{const{triggerWorkflowCommand:i}=await import("../commands/workflows/trigger.js");return i(o,e)}),r.command("logs [jobId]").description("Tail logs from a workflow execution (alias of `zibby logs`)").option("--project <id>","Project ID (or ZIBBY_PROJECT_ID env)").option("--workflow <name>","Workflow name (fetches the latest run)").option("--all","Show interleaved logs from all runs (requires --workflow)").option("-t, --follow","Stream logs in real-time (like Heroku logs -t)").option("--api-key <key>","API key (or ZIBBY_API_KEY env)").option("--lines <n>","Max log lines per fetch (default: 500)").action(async(o,e)=>{const{logsCommand:i}=await import("../commands/workflows/logs.js");return i(o,e)}),r.command("download [uuid]").description("Download a workflow from Zibby Cloud to .zibby/workflows/<name>/. Pass a UUID (from `zibby workflow list`) for custom workflows, or use --type for built-ins.").option("--project <id>","Project ID (or ZIBBY_PROJECT_ID env)").option("--type <type>","Built-in workflow type: analysis, implementation, run_test (alternative to positional <uuid>)").option("--api-key <key>","API key (or ZIBBY_API_KEY env)").option("--output <dir>","Output directory base (default: .zibby/workflows)").option("--include-default","Download the built-in default graph if no custom one exists").action(async(o,e)=>{const{workflowDownloadCommand:i}=await import("../commands/workflow.js");return i({...e,uuid:o})}),r.command("list").description("List all workflows (local + remote if credentials available)").option("--local-only","Show only local workflows").option("--remote-only","Show only remote workflows (requires --project)").option("--project <id>","Project ID (optional, uses ZIBBY_PROJECT_ID env)").option("--api-key <key>","API key (or ZIBBY_API_KEY env)").action(async o=>{if(o.remoteOnly){const{workflowListCommand:i}=await import("../commands/workflow.js");return i(o)}if(o.localOnly){const{listLocalWorkflowsCommand:i}=await import("../commands/workflows/list.js");return i(o)}const{listAllWorkflowsCommand:e}=await import("../commands/workflows/list.js");return e(o)}),r.command("delete <uuid>").description("Delete a deployed workflow by UUID").action(async o=>{const{deleteWorkflowCommand:e}=await import("../commands/workflows/delete.js");return e(o,{})});const W=t.command("project").description("Manage Zibby projects");W.command("list").description("List all projects").option("--api-key <key>","API key (or ZIBBY_API_KEY env)").action(async o=>{const{projectListCommand:e}=await import("../commands/project.js");return e(o)});const J=t.command("run-queue").description("Parallel capacity \u2014 on-disk wait queue (see parallel.waitWhenAtCapacity in .zibby.config.mjs)");J.command("list").description("List CLI processes waiting for a run slot").option("--config <path>","Path to config file",".zibby.config.mjs").action(async o=>{const{runCapacityQueueListCommand:e}=await import("../commands/run-capacity-queue-cli.js");await e(o),process.exit(0)}),t.command("uninstall").description("Remove all Zibby data: global CLI, ~/.zibby, Cursor MCP config, Studio, and current project").option("--dry-run","Show what would be deleted without deleting").option("--deep","Also remove npx cache dirs containing zibby").action(async o=>{const{uninstallCommand:e}=await import("../commands/uninstall.js");await e(o),process.exit(0)}),t.parse();
@@ -92,7 +92,7 @@ Use provided tools when external data/actions are required. Do not claim actions
92
92
  - FIRST call get_skill_context("workflow-builder") to load the full framework reference.
93
93
  - Conversation flow: ask about purpose \u2192 input data \u2192 processing steps \u2192 output format \u2192 conditional logic.
94
94
  - Then call design_workflow to create a spec, review with user, then build_workflow to generate code.
95
- - After building: remind user about \`zibby start <name>\` (test), \`zibby deploy <name>\` (deploy), \`zibby logs\` (logs).
95
+ - After building: remind user about \`zibby workflow start <name>\` (test), \`zibby workflow deploy <name>\` (deploy), \`zibby workflow logs <uuid>\` (logs).
96
96
  - Use list_workflows to show existing workflows, add_node to extend them, deploy_workflow to ship.`),B.includes("chat-memory"))try{let p=Pt("chat-memory");if(typeof p?.buildPromptContext=="function"){let I=Date.now(),S;st.data&&I-st.timestamp<M?(S=st.data,process.env.ZIBBY_VERBOSE==="true"&&i.pushSystem(o.gray("Using cached memory context"))):(S=await p.buildPromptContext({options:{workspace:t}},{}),st={data:S,timestamp:I},process.env.ZIBBY_VERBOSE==="true"&&i.pushSystem(o.gray("Refreshed memory context cache")));let T=String(S?.backend||"dolt");process.env.ZIBBY_VERBOSE==="true"&&(i.pushSystem(o.gray(`memory backend: ${T}`)),i.pushSystem(o.gray(`memory retrieval output: ${Yt(JSON.stringify(S?.debugPreview||{}),1400)}`)),S?.error&&i.pushSystem(o.yellow(`memory backend warning: ${S.error}`))),S?.promptContext&&(K+=`
97
97
 
98
98
  ${S.promptContext}`)}}catch(p){process.env.ZIBBY_VERBOSE==="true"&&i.pushSystem(o.yellow(`memory backend warning: ${String(p?.message||p)}`))}K=Ct(K,en);let an=co(N),Kt=lo([{role:"system",content:K},...an,{role:"user",content:Bt}]),Se=ae(Kt),un=Buffer.byteLength(JSON.stringify(Kt),"utf8");process.env.ZIBBY_VERBOSE==="true"&&i.pushSystem(o.gray(`payload estimate: ${Se} chars, ${un} bytes (~${Math.round(Se/4)} tokens)`)),N.push({role:"human",content:he});let St=i.enabled?(()=>{let p=()=>{};return p.setLabel=()=>{},p})():Ve(),ut=!0,Et=[],xe="",xt="",xo=50,ko=500,_o=3,vo=60,$o=500,Io=15,Ao=25,To=4,Po=new Set([".","!","?",",",";",":"]),pn=p=>{ut&&(St(),ut=!1),xt+=p},fn=(p,I)=>{if(!p){ut&&St.setLabel("thinking...");return}Et.push(p);let S=70,T=p;if(I&&typeof I=="object"){let v=Object.values(I).map(g=>typeof g=="string"?g:JSON.stringify(g)).join(", "),h=`${p}(${v})`;h.length<=S?T=h:T=`${p}(${v.slice(0,S-p.length-4)}...)`}T!==xe&&(xe=T,i.pushSystem(o.gray(`\u23BF ${T}`)),i.refreshPrompt({preserveInput:!0}),ut&&i.showAssistantLoading()),ut&&St.setLabel(T)},Xt=new AbortController;O=Xt;try{let p=await jn("",{state:{agentType:d,config:c,cwd:t,workspace:t}},{model:c?.agent?.assistant?.model||l.model||"auto",workspace:t,skills:B,activeSkills:B,config:c,timeout:l.timeout||3e5,messages:Kt,skipPromptFragments:!0,stream:!0,onToken:pn,onToolCall:fn,signal:Xt.signal});ut&&St();let I=typeof p=="string"?p:p?.structured?JSON.stringify(p.structured,null,2):p?.raw||String(p),S=xt&&xt.trim().length>0?xt.trim():I.trim(),T=po(S,Et)?S:fo(S);i.pushAssistant(T);let _=Et.length>0?`[tools used: ${Et.join(", ")}]
@@ -1,77 +1,77 @@
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(`
1
+ var me=Object.defineProperty;var ge=(t,e)=>()=>(t&&(e=t(t=0)),e);var fe=(t,e)=>{for(var a in e)me(t,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(t){try{return F(`command -v ${t}`,{stdio:"pipe"}),!0}catch{return!1}}function Pe(){try{let t=G.resolve("@zibby/mcp-browser/bin/mcp-browser-zibby.js");if(T(t))return t}catch{}try{let t=G.resolve("@playwright/mcp/package.json"),e=W(t),a=JSON.parse(ne(t,"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 n=K(e,"cli.js");if(T(n))return n}catch{}return null}function Q(t,e,a={}){return new Promise((l,n)=>{let m=H(t,e,{stdio:"inherit",...a});m.on("close",y=>l(y??1)),m.on("error",n)})}async function Ce(t){return re("playwright")?await Q("playwright",["install","ffmpeg"],{cwd:t})===0:await Q("npx",["--yes","playwright","install","ffmpeg"],{cwd:t})===0}function Ae({headed:t=!1,viewport:e=null}){let a=process.execPath,l=W(a),n=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"),t||w.push("--headless");let g;n?g={command:a,args:[n,...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 t=xe();if(!t)return;let e=K(t,"scripts","patch-cursor-mcp.js");if(T(e))try{await new Promise((a,l)=>{let n=H(process.execPath,[e],{cwd:process.cwd(),stdio:"ignore"});n.on("close",m=>a(m??0)),n.on("error",l)})}catch{}}function Ee(t){return te(t).replace(/^\/+/,"").replace(/\//g,"-")}function ke(t){let e=String(t||""),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 n=e.match(/playwright-official-[a-f0-9]+/i);return n?n[0]:null}function Se(t,e){let a=Ee(t),l=K(ie(),".cursor","projects",a);se(l,{recursive:!0});let n=K(l,"mcp-approvals.json");return oe(n,`${JSON.stringify([e],null,2)}
4
+ `,"utf-8"),n}function ee(t){try{return F("cursor-agent mcp list",{cwd:t,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(t){try{return F("cursor-agent mcp enable playwright-official",{cwd:t,encoding:"utf-8",stdio:["ignore","pipe","pipe"],env:process.env,maxBuffer:1024*1024}),!0}catch{return!1}}async function ae(t={}){let e=!!t.headed,a=t.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 n=te(process.cwd());if(re("cursor-agent")){ee(n);let m=_e(n);m&&console.log(x.gray(" \u2713 cursor-agent: mcp enable playwright-official"));let y=ee(n),r=ke(y);if(r)try{let w=Se(n,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(t){try{console.log(x.cyan(`
5
5
  \u{1F680} Running CI setup...
6
6
  `)),await ae({headed:!1,cloudSync:!0,viewport:{width:1280,height:720}}),console.log(x.green(`
7
7
  \u2705 CI/CD setup complete!
8
8
  `))}catch(e){console.log(x.red(`
9
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(`
10
+ `)),process.exit(1)}}async function Ke(t,e){try{let a=["playwright","test",t||"tests/","--project=chromium"];e.headed&&a.push("--headed"),console.log(x.cyan(`
11
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(`
12
+ `)),console.log(x.gray("\u2501".repeat(50)));let l=H("npx",a,{cwd:process.cwd(),stdio:"inherit"});l.on("close",n=>{n===0?console.log(x.green(`
13
13
  \u2705 Tests complete!
14
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}
15
+ \u274C Tests failed with code ${n}
16
+ `)),process.exit(n))}),l.on("error",n=>{console.log(x.red(`
17
+ \u274C Error: ${n.message}
18
18
  `)),process.exit(1)})}catch(a){console.log(x.red(`
19
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}
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 o 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 t=J();return ye(t)?JSON.parse(he(t,"utf-8")):{}}catch{return{}}}function X(t,e){let a=q(U(),".zibby");we(a,{recursive:!0});let l=J(),n=V(),m=e?O[e]:null;m&&((!n.agentKeys||typeof n.agentKeys!="object")&&(n.agentKeys={}),n.agentKeys[m.envVar]=String(t).trim()),delete n.agentApiKey,be(l,`${JSON.stringify(n,null,2)}
21
+ `,"utf-8")}var Te=ze(import.meta.url),Re=Be(Te),ue=de(import.meta.url);async function Ye(){try{let t=process.platform==="win32",e=L("npm config get prefix",{encoding:"utf-8"}).trim(),a=t?e:`${e}/bin`,l=a.replace(/^~/,k()),n=t?";":":";if(process.env.PATH.split(n).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(o.yellow("\u26A0\uFE0F npm global bin not in PATH")),console.log(o.gray(` Location: ${l}`)),console.log(o.gray(` Add it manually: export PATH="${l}:$PATH"
22
+ `));return}console.log(o.yellow("\u26A0\uFE0F npm global bin not in PATH")),console.log(o.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){t?(console.log(o.gray(`
23
+ \u{1F4A1} To add manually on Windows:`)),console.log(o.gray(' 1. Search "Environment Variables" in Start menu')),console.log(o.gray(' 2. Edit "Path" in User variables')),console.log(o.gray(` 3. Add: ${l}
24
+ `))):(console.log(o.gray(`
25
+ \u{1F4A1} To add manually, run:`)),console.log(o.gray(` echo 'export PATH="${l}:$PATH"' >> ~/.zshrc`)),console.log(o.gray(` source ~/.zshrc
26
+ `)));return}if(t){console.log(o.yellow("\u26A0\uFE0F Cannot auto-add PATH on Windows")),console.log(o.gray(" Please add manually:")),console.log(o.gray(' 1. Search "Environment Variables" in Start menu')),console.log(o.gray(' 2. Edit "Path" in User variables')),console.log(o.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(o.yellow(`\u26A0\uFE0F Unknown shell: ${w}`)),console.log(o.gray(" Please add manually:")),console.log(o.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(o.yellow(`\u26A0\uFE0F PATH entry found in ${g} but not active`)),console.log(o.gray(` Run: source ${g}
28
28
  `));return}}let C=`
29
29
  # npm global bin (added by zibby)
30
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(`
31
+ `;await h(g,(P(g)?await A(g,"utf-8"):"")+C),console.log(o.green(`\u2705 Added to ${g}`)),console.log(o.yellow(`
32
+ \u{1F4A1} Run this to activate in current session:`)),console.log(o.gray(` source ${g}
33
+ `))}catch{}}async function ft(t,e){console.log(o.bold.cyan(`
34
34
  \u{1F3AD} Welcome to Zibby Test Automation!
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(`
35
+ `));let a=!e.skipMemory,l=["dolt","mem0"].includes(String(e.memoryBackend||"").toLowerCase())?String(e.memoryBackend).toLowerCase():"dolt";await Ye();let n=t?pe(process.cwd(),t):process.cwd(),m=t||"zibby-tests",y=!!t;y&&P(n)&&(console.log(o.red(`
36
+ \u274C Directory "${t}" already exists!
37
+ `)),process.exit(1)),!y&&P(p(n,".zibby.config.mjs"))&&!e.force&&(console.log(o.yellow(`
38
38
  \u26A0\uFE0F Zibby is already initialized in this directory!
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(`
39
+ `)),console.log(o.white("Config file found: .zibby.config.mjs")),console.log(o.gray(`Use --force or -f to reinitialize
40
+ `)),process.exit(0)),e.force&&!y&&console.log(o.cyan(`
41
41
  Reinitializing Zibby configuration...
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()}
42
+ `));let r;if(e.agent&&(e.headed||e.headless))console.log(o.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(o.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(n,{recursive:!0}),await M(p(n,"test-specs/examples"),{recursive:!0}),await M(p(n,"tests"),{recursive:!0}),await M(p(n,".zibby/output"),{recursive:!0}),await M(p(n,".zibby/commands"),{recursive:!0}),a&&l==="dolt")try{let{initMemory:s,DoltDB:c}=await import("@zibby/memory");if(c.isAvailable()){let{created:i}=s(n);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(n,".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(n,".zibby.config.mjs"),S),await h(p(n,".env.example"),Oe(r,l)),r.apiKey&&r.apiKey.trim()){let s=p(n,".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()}
44
44
 
45
45
  # Zibby Cloud Sync
46
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()}
47
+ `,await h(s,i)}else await h(s,De(r,c,l))}if(r.agentKey&&g){let s=p(n,".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()}
48
48
 
49
49
  # AI Agent Key
50
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(`
51
+ `,await h(s,i)}}if(y){let s=Le(m,r,{memoryBackend:l});await h(p(n,"package.json"),s)}if(!P(p(n,".gitignore"))){let s=Ne();await h(p(n,".gitignore"),s)}if(!P(p(n,"playwright.config.js"))){let s=Ze("on");await h(p(n,"playwright.config.js"),s)}if(!P(p(n,"test-specs/examples/example-domain.txt"))){let s=Ve();await h(p(n,"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(n,".zibby/commands",c);if(e.force||!P(i)){let u=await A(p(B,c),"utf-8");await h(i,u)}}}if(!P(p(n,".zibby/commands/example.md"))){let s=Ge();await h(p(n,".zibby/commands/example.md"),s)}if(y){let s=He(m,r);await h(p(n,"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:n,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(n,"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(o.yellow(`
52
+ Missing required Zibby dependencies in this project:`)),console.log(o.white(` ${c.join(", ")}`)),console.log(o.gray("Install them manually to avoid runtime errors:")),console.log(o.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:n,stdio:"pipe"}).on("close",_=>z(_??1))})===0?d.succeed("Installed missing Zibby dependencies"):(d.warn("Could not auto-install Zibby dependencies"),console.log(o.gray("Run this manually:")),console.log(o.white(` npm install --save-dev ${u.join(" ")}
54
+ `)))}}}if(!y&&a&&l==="mem0"&&(console.log(o.yellow(`
55
+ Using mem0 backend requires mem0ai in your project dependencies.`)),console.log(o.gray("Run this manually in your project when ready:")),console.log(o.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:n,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(o.yellow(`
57
57
  \u26A0\uFE0F If tests fail, run: npx playwright install
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
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(o.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(o.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(o.yellow(` Install manually: npm install -g @google/gemini-cli
61
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(`
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(o.yellow(" You may need to configure ~/.gemini/settings.json manually")),console.log(o.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(o.gray(`
64
64
  Copy .env.example to .env and set ZIBBY_API_KEY to enable uploads
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(`
65
+ `))}catch(c){s.fail("MCP setup script failed"),console.log(o.yellow(" Check if MCP is already configured:")),console.log(o.gray(' \u2022 Open Cursor settings \u2192 Check "playwright-official" MCP')),console.log(o.gray(" \u2022 Run: cursor-agent mcp list")),console.log(o.gray(` \u2022 Or run manually: zibby setup-playwright
66
+ `)),console.log(o.gray(` Error: ${c.message}
67
+ `))}}console.log(o.bold.green(`
68
68
  \u{1F389} All set!
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(`
69
+ `)),console.log(o.cyan("Start the Zibby Chat Agent:")),y&&console.log(o.white(` cd ${t}`)),console.log(o.white(` zibby
70
+ `)),console.log(o.cyan("Or run a test directly:")),console.log(o.white(` zibby run test-specs/examples/example-domain.txt
71
+ `)),console.log(o.cyan("Next steps:"));let E=1;console.log(o.white(` ${E++}. cp .env.example .env ${o.gray("# then add your API keys")}`)),a&&console.log(o.white(` ${E++}. Set ${o.bold("ZIBBY_MEMORY_BACKEND")} in .env ${o.gray(`# currently: ${l}`)}`)),console.log(o.white(` ${E++}. Type ${o.bold("zibby")} to chat with the AI testing assistant`)),console.log(o.white(` ${E++}. Write test specs in test-specs/`)),console.log(o.white(` ${E++}. Run: npx zibby run <spec-file>
72
+ `))}catch(f){C.fail("Failed to create project"),console.error(o.red(`
73
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:`
74
+ `)),process.exit(1)}}function je(t,e={},a={}){let l=["dolt","mem0"].includes(String(a.memoryBackend||"").toLowerCase())?String(a.memoryBackend).toLowerCase():"dolt",n={claude:`
75
75
  claude: {
76
76
  model: 'auto', // Options: 'auto', 'sonnet-4.6', 'opus-4.6', 'sonnet-4.5', 'opus-4.5'
77
77
  maxTokens: 4096,
@@ -84,12 +84,12 @@ Using mem0 backend requires mem0ai in your project dependencies.`)),console.log(
84
84
  },`,gemini:`
85
85
  gemini: {
86
86
  model: 'gemini-2.5-pro', // Options: 'auto', 'gemini-2.5-pro', 'gemini-2.5-flash'
87
- },`},m=o.agent,y=Object.entries(t).filter(([r])=>r!==m).map(([,r])=>r.split(`
87
+ },`},m=t.agent,y=Object.entries(n).filter(([r])=>r!==m).map(([,r])=>r.split(`
88
88
  `).map(w=>w.trim()?` // ${w.trimStart()}`:w).join(`
89
89
  `)).join(`
90
90
  `);return`export default {
91
91
  // AI agent settings
92
- agent: {${t[m]}
92
+ agent: {${n[m]}
93
93
  ${y}
94
94
  strictMode: false,
95
95
  },
@@ -98,7 +98,7 @@ ${y}
98
98
  // and workflow config. Runtime strategies attach MCP per run (no global Gemini settings mutation).
99
99
  browser: {
100
100
  mcp: 'playwright',
101
- headless: ${o.browserMode==="headless"},
101
+ headless: ${t.browserMode==="headless"},
102
102
  },
103
103
 
104
104
  // Chat memory backend adapter (dolt | mem0)
@@ -117,7 +117,7 @@ ${y}
117
117
  specs: 'test-specs', // Where your .txt test specs are
118
118
  generated: 'tests', // Where generated .spec.js files go
119
119
  output: '.zibby/output', // Where workflow execution results are saved (default: .zibby/output)
120
- workflows: '.zibby/workflows', // Where custom workflows are stored
120
+ workflows: '${t.workflowsPath||".zibby/workflows"}', // Where custom workflows are stored
121
121
  // sessionPrefix: 'run', // Optional: prefix for session folders (e.g., run_1772788458045)
122
122
  },
123
123
 
@@ -152,13 +152,13 @@ ${y}
152
152
  },
153
153
 
154
154
  // Cloud sync - auto-upload test results & videos (requires ZIBBY_API_KEY in .env)
155
- cloudSync: ${o.cloudSync||!1}
155
+ cloudSync: ${t.cloudSync||!1}
156
156
  };
157
- `}function Oe(o,e="dolt"){return`# Zibby Test Automation - Environment Variables
157
+ `}function Oe(t,e="dolt"){return`# Zibby Test Automation - Environment Variables
158
158
 
159
159
  # AI Provider Keys
160
160
  ${{claude:"ANTHROPIC_API_KEY=sk-ant-your_key_here",cursor:`# Cursor Agent uses cursor-agent CLI \u2014 no API key needed
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]||""}
161
+ # Install: curl https://cursor.com/install -fsS | bash`,codex:"OPENAI_API_KEY=sk-your_key_here",gemini:"GEMINI_API_KEY=your_key_here"}[t.agent]||""}
162
162
 
163
163
  # Zibby Cloud Sync (for uploading test results & videos)
164
164
  # Get your API key from: https://zibby.app/settings/tokens
@@ -171,10 +171,10 @@ ${{claude:"ANTHROPIC_API_KEY=sk-ant-your_key_here",cursor:`# Cursor Agent uses c
171
171
 
172
172
  # Chat memory backend
173
173
  ZIBBY_MEMORY_BACKEND=${e}
174
- `}function De(o,e,a="dolt"){return`# Zibby Test Automation - Environment Variables
174
+ `}function De(t,e,a="dolt"){return`# Zibby Test Automation - Environment Variables
175
175
 
176
176
  # AI Provider Keys
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]||""}
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"}[t.agent]||""}
178
178
 
179
179
  # Zibby Cloud Sync
180
180
  ZIBBY_API_KEY=${e}
@@ -186,7 +186,7 @@ ZIBBY_API_KEY=${e}
186
186
 
187
187
  # Chat memory backend
188
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
189
+ `}function Le(t,e,a={}){let n={"@zibby/cli":"latest","@zibby/core":ue("../../package.json").dependencies?.["@zibby/core"]||"latest"};return a.memoryBackend==="mem0"&&(n.mem0ai="^2.4.6"),JSON.stringify({name:t,version:"1.0.0",type:"module",private:!0,scripts:{"test:spec":"zibby run",test:"playwright test","test:headed":"playwright test --headed"},dependencies:n,devDependencies:{"@playwright/test":"^1.49.0",dotenv:"^17.2.3"}},null,2)}function Ne(){return`# Dependencies
190
190
  node_modules/
191
191
 
192
192
  # Test artifacts
@@ -214,7 +214,7 @@ Thumbs.db
214
214
  # Zibby memory (Dolt DB synced separately via dolt remote, not git)
215
215
  .zibby/memory/
216
216
  .zibby/memory-context.md
217
- `}function Ze(o="off",e=null){return`import { defineConfig} from '@playwright/test';
217
+ `}function Ze(t="off",e=null){return`import { defineConfig} from '@playwright/test';
218
218
 
219
219
  export default defineConfig({
220
220
  testDir: './tests',
@@ -284,7 +284,7 @@ Required output format:
284
284
  4. Expected result
285
285
 
286
286
  Keep the response actionable and short.
287
- `}function He(o,e){return`# ${o}
287
+ `}function He(t,e){return`# ${t}
288
288
 
289
289
  AI-powered test automation with Zibby.
290
290
 
@@ -355,7 +355,7 @@ Edit \`.zibby.config.mjs\` to customize:
355
355
  ## Project Structure
356
356
 
357
357
  \`\`\`
358
- ${o}/
358
+ ${t}/
359
359
  \u251C\u2500\u2500 .zibby/
360
360
  \u2502 \u251C\u2500\u2500 graph.mjs # Workflow definition
361
361
  \u2502 \u251C\u2500\u2500 nodes/ # Custom nodes
@@ -373,4 +373,4 @@ ${o}/
373
373
 
374
374
  - Documentation: https://docs.zibby.dev
375
375
  - Examples: https://github.com/zibby/examples
376
- `}export{ft as initCommand};
376
+ `}export{je as generateConfig,ft as initCommand};