@zibby/cli 0.1.49 → 0.1.51
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/commands/init.js +13 -13
- package/dist/package.json +1 -1
- package/dist/utils/agent-credentials.js +3 -2
- package/package.json +1 -1
package/dist/commands/init.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import{mkdir as S,writeFile as p,readFile as w}from"fs/promises";import{existsSync as h,readdirSync as G}from"fs";import{join as a,resolve as O,dirname as H}from"path";import{homedir as P}from"os";import Y from"inquirer";import e from"chalk";import I from"ora";import{spawn as K,execSync as R}from"child_process";import{fileURLToPath as V}from"url";import{createRequire as Z}from"module";import{AGENT_KEY_MAP as D,saveAgentApiKey as F,readGlobalConfig as W}from"../utils/agent-credentials.js";const U=V(import.meta.url),q=H(U),N=Z(import.meta.url);async function X(){try{const d=process.platform==="win32",o=R("npm config get prefix",{encoding:"utf-8"}).trim(),
|
|
1
|
+
import{mkdir as S,writeFile as p,readFile as w}from"fs/promises";import{existsSync as h,readdirSync as G}from"fs";import{join as a,resolve as O,dirname as H}from"path";import{homedir as P}from"os";import Y from"inquirer";import e from"chalk";import I from"ora";import{spawn as K,execSync as R}from"child_process";import{fileURLToPath as V}from"url";import{createRequire as Z}from"module";import{AGENT_KEY_MAP as D,saveAgentApiKey as F,readGlobalConfig as W}from"../utils/agent-credentials.js";const U=V(import.meta.url),q=H(U),N=Z(import.meta.url);async function X(){try{const d=process.platform==="win32",o=R("npm config get prefix",{encoding:"utf-8"}).trim(),f=d?o:`${o}/bin`,m=f.replace(/^~/,P()),l=d?";":":";if(process.env.PATH.split(l).some(y=>{const x=y.replace(/^~/,P());return x===m||x===f}))return;if(process.env.CI||process.env.ZIBBY_CI||!process.stdin.isTTY){console.log(e.yellow("\u26A0\uFE0F npm global bin not in PATH")),console.log(e.gray(` Location: ${m}`)),console.log(e.gray(` Add it manually: export PATH="${m}:$PATH"
|
|
2
2
|
`));return}console.log(e.yellow("\u26A0\uFE0F npm global bin not in PATH")),console.log(e.gray(` Location: ${m}`)),console.log();const{shouldAddPath:s}=await Y.prompt([{type:"confirm",name:"shouldAddPath",message:"Add npm global bin to your shell PATH automatically?",default:!0}]);if(!s){d?(console.log(e.gray(`
|
|
3
3
|
\u{1F4A1} To add manually on Windows:`)),console.log(e.gray(' 1. Search "Environment Variables" in Start menu')),console.log(e.gray(' 2. Edit "Path" in User variables')),console.log(e.gray(` 3. Add: ${m}
|
|
4
4
|
`))):(console.log(e.gray(`
|
|
5
5
|
\u{1F4A1} To add manually, run:`)),console.log(e.gray(` echo 'export PATH="${m}:$PATH"' >> ~/.zshrc`)),console.log(e.gray(` source ~/.zshrc
|
|
6
6
|
`)));return}if(d){console.log(e.yellow("\u26A0\uFE0F Cannot auto-add PATH on Windows")),console.log(e.gray(" Please add manually:")),console.log(e.gray(' 1. Search "Environment Variables" in Start menu')),console.log(e.gray(' 2. Edit "Path" in User variables')),console.log(e.gray(` 3. Add: ${m}
|
|
7
|
-
`));return}const
|
|
7
|
+
`));return}const C=process.env.SHELL||"";let g="";if(C.includes("zsh"))g=a(P(),".zshrc");else if(C.includes("bash"))g=h(a(P(),".bashrc"))?a(P(),".bashrc"):a(P(),".bash_profile");else{console.log(e.yellow(`\u26A0\uFE0F Unknown shell: ${C}`)),console.log(e.gray(" Please add manually:")),console.log(e.gray(` export PATH="${m}:$PATH"`));return}if(h(g)){const y=await w(g,"utf-8");if(y.includes(m)||y.includes("npm")&&y.includes("global")&&y.includes("bin")){console.log(e.yellow(`\u26A0\uFE0F PATH entry found in ${g} but not active`)),console.log(e.gray(` Run: source ${g}
|
|
8
8
|
`));return}}const v=`
|
|
9
9
|
# npm global bin (added by zibby)
|
|
10
10
|
export PATH="${m}:$PATH"
|
|
@@ -12,7 +12,7 @@ export PATH="${m}:$PATH"
|
|
|
12
12
|
\u{1F4A1} Run this to activate in current session:`)),console.log(e.gray(` source ${g}
|
|
13
13
|
`))}catch{}}async function be(d,o){console.log(e.bold.cyan(`
|
|
14
14
|
\u{1F3AD} Welcome to Zibby Test Automation!
|
|
15
|
-
`));const
|
|
15
|
+
`));const f=!o.skipMemory,m=["dolt","mem0"].includes(String(o.memoryBackend||"").toLowerCase())?String(o.memoryBackend).toLowerCase():"dolt";await X();const l=d?O(process.cwd(),d):process.cwd(),$=d||"zibby-tests",b=!!d;b&&h(l)&&(console.log(e.red(`
|
|
16
16
|
\u274C Directory "${d}" already exists!
|
|
17
17
|
`)),process.exit(1)),!b&&h(a(l,".zibby.config.mjs"))&&!o.force&&(console.log(e.yellow(`
|
|
18
18
|
\u26A0\uFE0F Zibby is already initialized in this directory!
|
|
@@ -20,7 +20,7 @@ export PATH="${m}:$PATH"
|
|
|
20
20
|
`)),process.exit(0)),o.force&&!b&&console.log(e.cyan(`
|
|
21
21
|
Reinitializing Zibby configuration...
|
|
22
22
|
`));let s;if(o.agent&&(o.headed||o.headless))console.log(e.cyan(`Setting up with provided options...
|
|
23
|
-
`)),s={agent:o.agent,browserMode:o.headless?"headless":"headed",apiKey:o.apiKey||null,cloudSync:!!(o.cloudSync||o.apiKey)};else{if(o.agent)s={agent:o.agent};else{const{agent:
|
|
23
|
+
`)),s={agent:o.agent,browserMode:o.headless?"headless":"headed",apiKey:o.apiKey||null,cloudSync:!!(o.cloudSync||o.apiKey)};else{if(o.agent)s={agent:o.agent};else{const{agent:E}=await Y.prompt([{type:"select",name:"agent",message:"Which AI agent do you prefer?",choices:[{name:"Cursor",value:"cursor"},{name:"Claude (Anthropic)",value:"claude"},{name:"Codex (OpenAI)",value:"codex"},{name:"Gemini (Google)",value:"gemini"}],default:"cursor"}]);s={agent:E}}const y=D[s.agent];if(y&&!o.agentKey&&!(process.env.CI||process.env.ZIBBY_CI||!process.stdin.isTTY)){const E=W(),B=process.env[y.envVar]||E.agentKeys?.[y.envVar],A=B?`***${B.slice(-4)}`:null,t=A?`${y.label} found (${A}). Press Enter to keep, or paste a new key:`:`Enter ${y.label} (from ${y.url}, or press Enter to skip):`,{agentKey:i}=await Y.prompt([{type:"password",name:"agentKey",message:t,mask:"*"}]);i&&i.trim()&&(s.agentKey=i.trim())}const x=[];if(!o.headed&&!o.headless&&x.push({type:"select",name:"browserMode",message:"Browser mode during live AI execution?",choices:[{name:"Headed - Visible browser (recommended for development)",value:"headed"},{name:"Headless - Hidden browser (for CI/CD)",value:"headless"}],default:"headed"}),o.apiKey||x.push({type:"input",name:"apiKey",message:"Enable cloud sync? Enter project ZIBBY_API_KEY (or press Enter to skip):"}),x.length>0){const E=await Y.prompt(x);Object.assign(s,E)}s.browserMode=o.headless?"headless":o.headed?"headed":s.browserMode,s.apiKey=o.apiKey||s.apiKey,s.cloudSync=!!(o.cloudSync||o.apiKey||s.apiKey&&s.apiKey.trim())}o.agentKey&&(s.agentKey=o.agentKey);const g=D[s.agent];s.agentKey&&(F(s.agentKey,s.agent),console.log(e.gray(" \u2713 Agent API key saved to ~/.zibby/config.json"))),s.mcp="playwright",s.setupMcp=s.agent==="cursor";const v=I("Setting up Zibby...").start();try{if(b&&await S(l,{recursive:!0}),await S(a(l,"test-specs/examples"),{recursive:!0}),await S(a(l,"tests"),{recursive:!0}),await S(a(l,".zibby/output"),{recursive:!0}),await S(a(l,".zibby/commands"),{recursive:!0}),f&&m==="dolt")try{const{initMemory:t,DoltDB:i}=await import("@zibby/memory");if(i.isAvailable()){const{created:n}=t(l);n&&(v.text="Initialized test memory database (Dolt)...")}else v.text="Dolt not found \u2014 skipping memory database (brew install dolt)"}catch{}v.text="Scaffolding workflow graph...";const{TemplateFactory:y}=await import("@zibby/core/templates"),x=o.template||"browser-test-automation";try{const{graphPath:t,nodesPath:i,readmePath:n,resultHandlerPath:c,template:r}=y.getTemplateFiles(x),u=a(l,".zibby"),M=await w(t,"utf-8");if(await p(a(u,"graph.mjs"),M),c){const k=await w(c,"utf-8");await p(a(u,"result-handler.mjs"),k)}const z=await w(n,"utf-8");await p(a(u,"README.md"),z),await S(a(u,"nodes"),{recursive:!0});const{readdirSync:_}=await import("fs"),T=_(i);for(const k of T){let j=await w(a(i,k),"utf-8");!f&&k==="execute-live.mjs"&&(j=j.replace("skills: [SKILLS.BROWSER, SKILLS.MEMORY],","skills: [SKILLS.BROWSER],")),await p(a(u,"nodes",k),j)}const L=a(r.path,"chat.mjs");if(h(L)){const k=await w(L,"utf-8");await p(a(u,"chat.mjs"),k)}}catch(t){throw v.fail(`Failed to scaffold template: ${t.message}`),t}v.text="Generating configuration files...";const E=J(s,o,{memoryBackend:m});if(await p(a(l,".zibby.config.mjs"),E),await p(a(l,".env.example"),Q(s,m)),s.apiKey&&s.apiKey.trim()){const t=a(l,".env"),i=s.apiKey.trim();if(h(t)){let n=await w(t,"utf8");/^ZIBBY_API_KEY=/m.test(n)?n=n.replace(/^ZIBBY_API_KEY=.*/m,`ZIBBY_API_KEY=${i}`):/^#\s*ZIBBY_API_KEY=/m.test(n)?n=n.replace(/^#\s*ZIBBY_API_KEY=.*/m,`ZIBBY_API_KEY=${i}`):n=`${n.trimEnd()}
|
|
24
24
|
|
|
25
25
|
# Zibby Cloud Sync
|
|
26
26
|
ZIBBY_API_KEY=${i}
|
|
@@ -31,7 +31,7 @@ ${g.envVar}=${i}
|
|
|
31
31
|
`,await p(t,n)}}if(b){const t=te($,s,{memoryBackend:m});await p(a(l,"package.json"),t)}if(!h(a(l,".gitignore"))){const t=ne();await p(a(l,".gitignore"),t)}if(!h(a(l,"playwright.config.js"))){const t=oe("on");await p(a(l,"playwright.config.js"),t)}if(!h(a(l,"test-specs/examples/example-domain.txt"))){const t=se();await p(a(l,"test-specs/examples/example-domain.txt"),t)}const B=O(q,"../../../../examples/.zibby/commands");if(h(B)){const t=G(B).filter(i=>i.toLowerCase().endsWith(".md"));for(const i of t){const n=a(l,".zibby/commands",i);if(o.force||!h(n)){const c=await w(a(B,i),"utf-8");await p(n,c)}}}if(!h(a(l,".zibby/commands/example.md"))){const t=ie();await p(a(l,".zibby/commands/example.md"),t)}if(b){const t=ae($,s);await p(a(l,"README.md"),t)}if(v.succeed(b?"Project created!":"Zibby initialized!"),b&&!o.skipInstall){const t=I("Installing dependencies...").start();await new Promise((i,n)=>{K("npm",["install"],{cwd:l,stdio:"pipe"}).on("close",r=>{r===0?(t.succeed("Dependencies installed!"),i()):(t.fail("Failed to install dependencies"),n(new Error("npm install failed")))})})}else if(!b){const t=["@zibby/cli","@zibby/core"],i=[];for(const n of t)try{Z(a(l,"package.json")).resolve(`${n}/package.json`)}catch{i.push(n)}if(i.length>0){const n=N("../../package.json"),c=i.map(r=>r==="@zibby/cli"?`${r}@latest`:r==="@zibby/core"?`${r}@${n.dependencies?.["@zibby/core"]||"^0.1.29"}`:r);if(o.skipInstall)console.log(e.yellow(`
|
|
32
32
|
Missing required Zibby dependencies in this project:`)),console.log(e.white(` ${i.join(", ")}`)),console.log(e.gray("Install them manually to avoid runtime errors:")),console.log(e.white(` npm install --save-dev ${c.join(" ")}
|
|
33
33
|
`));else{const r=I(`Installing ${c.join(", ")}...`).start();await new Promise(M=>{K("npm",["install","--save-dev",...c],{cwd:l,stdio:"pipe"}).on("close",_=>M(_??1))})===0?r.succeed("Installed missing Zibby dependencies"):(r.warn("Could not auto-install Zibby dependencies"),console.log(e.gray("Run this manually:")),console.log(e.white(` npm install --save-dev ${c.join(" ")}
|
|
34
|
-
`)))}}}if(!b&&
|
|
34
|
+
`)))}}}if(!b&&f&&m==="mem0"&&(console.log(e.yellow(`
|
|
35
35
|
Using mem0 backend requires mem0ai in your project dependencies.`)),console.log(e.gray("Run this manually in your project when ready:")),console.log(e.white(` npm install mem0ai
|
|
36
36
|
`))),!o.skipInstall){const t=I("Installing Playwright browsers...").start();await new Promise(i=>{const n=K("npx",["playwright","install","chromium"],{cwd:l,stdio:"pipe"});let c="";n.stdout.on("data",r=>{c+=r.toString()}),n.stderr.on("data",r=>{c+=r.toString()}),n.on("close",r=>{r===0?(c.includes("already installed")||c.includes("up to date")?t.succeed("Playwright browsers already installed"):t.succeed("Playwright browsers installed!"),i()):(t.warn("Could not verify Playwright browsers"),console.log(e.yellow(`
|
|
37
37
|
\u26A0\uFE0F If tests fail, run: npx playwright install
|
|
@@ -48,10 +48,10 @@ Using mem0 backend requires mem0ai in your project dependencies.`)),console.log(
|
|
|
48
48
|
\u{1F389} All set!
|
|
49
49
|
`)),console.log(e.cyan("Start the Zibby Chat Agent:")),b&&console.log(e.white(` cd ${d}`)),console.log(e.white(` zibby
|
|
50
50
|
`)),console.log(e.cyan("Or run a test directly:")),console.log(e.white(` zibby run test-specs/examples/example-domain.txt
|
|
51
|
-
`)),console.log(e.cyan("Next steps:"));let
|
|
52
|
-
`))}catch(
|
|
53
|
-
\u274C Error: ${
|
|
54
|
-
`)),process.exit(1)}}function J(d,o={},
|
|
51
|
+
`)),console.log(e.cyan("Next steps:"));let A=1;console.log(e.white(` ${A++}. cp .env.example .env ${e.gray("# then add your API keys")}`)),f&&console.log(e.white(` ${A++}. Set ${e.bold("ZIBBY_MEMORY_BACKEND")} in .env ${e.gray(`# currently: ${m}`)}`)),console.log(e.white(` ${A++}. Type ${e.bold("zibby")} to chat with the AI testing assistant`)),console.log(e.white(` ${A++}. Write test specs in test-specs/`)),console.log(e.white(` ${A++}. Run: npx zibby run <spec-file>
|
|
52
|
+
`))}catch(y){v.fail("Failed to create project"),console.error(e.red(`
|
|
53
|
+
\u274C Error: ${y.message}
|
|
54
|
+
`)),process.exit(1)}}function J(d,o={},f={}){const m=["dolt","mem0"].includes(String(f.memoryBackend||"").toLowerCase())?String(f.memoryBackend).toLowerCase():"dolt",l={claude:`
|
|
55
55
|
claude: {
|
|
56
56
|
model: 'auto', // Options: 'auto', 'sonnet-4.6', 'opus-4.6', 'sonnet-4.5', 'opus-4.5'
|
|
57
57
|
maxTokens: 4096,
|
|
@@ -65,7 +65,7 @@ Using mem0 backend requires mem0ai in your project dependencies.`)),console.log(
|
|
|
65
65
|
gemini: {
|
|
66
66
|
model: 'gemini-2.5-pro', // Options: 'auto', 'gemini-2.5-pro', 'gemini-2.5-flash'
|
|
67
67
|
},`},$=d.agent,b=Object.entries(l).filter(([s])=>s!==$).map(([,s])=>s.split(`
|
|
68
|
-
`).map(
|
|
68
|
+
`).map(C=>C.trim()?` // ${C.trimStart()}`:C).join(`
|
|
69
69
|
`)).join(`
|
|
70
70
|
`);return`export default {
|
|
71
71
|
// AI agent settings
|
|
@@ -150,7 +150,7 @@ ${{claude:"ANTHROPIC_API_KEY=sk-ant-your_key_here",cursor:`# Cursor Agent uses c
|
|
|
150
150
|
|
|
151
151
|
# Chat memory backend
|
|
152
152
|
ZIBBY_MEMORY_BACKEND=${o}
|
|
153
|
-
`}function ee(d,o,
|
|
153
|
+
`}function ee(d,o,f="dolt"){return`# Zibby Test Automation - Environment Variables
|
|
154
154
|
|
|
155
155
|
# AI Provider Keys
|
|
156
156
|
${{claude:"ANTHROPIC_API_KEY=sk-ant-your_key_here",cursor:"# Cursor Agent uses cursor-agent CLI \u2014 no API key needed",codex:"OPENAI_API_KEY=sk-your_key_here",gemini:"GEMINI_API_KEY=your_key_here"}[d.agent]||""}
|
|
@@ -164,8 +164,8 @@ ZIBBY_API_KEY=${o}
|
|
|
164
164
|
# ZIBBY_MEMORY_COMPACT_EVERY=1500 # Auto-compact every N runs (0 to disable)
|
|
165
165
|
|
|
166
166
|
# Chat memory backend
|
|
167
|
-
ZIBBY_MEMORY_BACKEND=${
|
|
168
|
-
`}function te(d,o,
|
|
167
|
+
ZIBBY_MEMORY_BACKEND=${f}
|
|
168
|
+
`}function te(d,o,f={}){const l={"@zibby/cli":"latest","@zibby/core":N("../../package.json").dependencies?.["@zibby/core"]||"^0.1.29"};return f.memoryBackend==="mem0"&&(l.mem0ai="^2.4.6"),JSON.stringify({name:d,version:"1.0.0",type:"module",private:!0,scripts:{"test:spec":"zibby run",test:"playwright test","test:headed":"playwright test --headed"},dependencies:l,devDependencies:{"@playwright/test":"^1.49.0",dotenv:"^17.2.3"}},null,2)}function ne(){return`# Dependencies
|
|
169
169
|
node_modules/
|
|
170
170
|
|
|
171
171
|
# Test artifacts
|
package/dist/package.json
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import{existsSync as
|
|
2
|
-
`,"utf-8")}function
|
|
1
|
+
import{existsSync as a,readFileSync as f,writeFileSync as b,mkdirSync as h}from"fs";import{join as i}from"path";import{homedir as u}from"os";const m={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 v(){return i(u(),".zibby","config.json")}function A(){try{const n=v();return a(n)?JSON.parse(f(n,"utf-8")):{}}catch{return{}}}function V(n,r){const o=i(u(),".zibby");h(o,{recursive:!0});const t=v(),e=A(),s=r?m[r]:null;s&&((!e.agentKeys||typeof e.agentKeys!="object")&&(e.agentKeys={}),e.agentKeys[s.envVar]=String(n).trim()),delete e.agentApiKey,b(t,`${JSON.stringify(e,null,2)}
|
|
2
|
+
`,"utf-8")}function I(n){const r=[i(n,".zibby.config.mjs"),i(u(),".zibby.config.mjs")];for(const o of r)if(a(o))try{const t=f(o,"utf-8");for(const e of["cursor","claude","codex","gemini"])if(new RegExp(`agent\\s*:\\s*\\{[^}]*${e}\\s*:`,"s").test(t))return e}catch{}return null}function _(n){const r=n||process.cwd(),o=I(r);if(!o)return;const t=m[o];if(!t||process.env[t.envVar])return;const e=[i(r,".env.local"),i(r,".env")];for(const l of e)if(a(l))try{for(const d of f(l,"utf-8").split(`
|
|
3
|
+
`)){const c=d.trim();if(c.startsWith("#")||!c.includes("="))continue;const y=c.indexOf("="),K=c.slice(0,y).trim(),g=c.slice(y+1).trim();if(K===t.envVar&&g){process.env[t.envVar]=g;return}}}catch{}const p=A().agentKeys?.[t.envVar];p&&(process.env[t.envVar]=p)}export{m as AGENT_KEY_MAP,_ as bootstrapAgentEnv,I as detectAgentType,A as readGlobalConfig,V as saveAgentApiKey};
|