@zibby/cli 0.1.46 → 0.1.47

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.
@@ -1,56 +1,56 @@
1
- import{invokeAgent as _e,getAgentStrategy as ve,getSkill as kt}from"@zibby/core";import{existsSync as $t,readFileSync as oe,readdirSync as Te}from"fs";import{resolve as mt,join as Dt,dirname as Ce,basename as Ie}from"path";import{createInterface as Pe,moveCursor as M,cursorTo as O,clearLine as q,emitKeypressEvents as Ae}from"readline";import{fileURLToPath as Ee}from"url";import e from"chalk";import{highlight as ne}from"cli-highlight";import Le from"dotenv";import{getSessionToken as Wt,getUserInfo as Me,getProjects as Be,saveSessionToken as Ne,saveUserInfo as Oe,clearSession as Re,saveProxyUrl as Ue,getProxyUrl as Fe,getMem0ProxyUrl as je,saveMem0ProxyUrl as De}from"../config/config.js";import{getApiUrl as re}from"../config/environments.js";import{loadActiveSkills as We,loadChatHistory as Ye,saveActiveSkills as It,saveChatHistory as Pt}from"./chat-session-store.js";import{buildReliabilityInstruction as He}from"./agent-reliability.js";const ze=Ee(import.meta.url),Je=Ce(ze),qe=JSON.parse(oe(Dt(Je,"../../package.json"),"utf-8")),ie=30,Ze=54,Ge=18e3,ce=12e3,ae=42e3;function Ke(o){return new Promise(t=>setTimeout(t,o))}const At=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],Yt=[{cmd:"/help",desc:"Show this help"},{cmd:"/skills",desc:"List active & available skills"},{cmd:"/history",desc:"Show conversation history (--all or -n 50)"},{cmd:"/clear",desc:"Clear conversation history"},{cmd:"/memory",desc:"View stored memories, tasks, sessions"},{cmd:"/exit",desc:"Exit the chat"},{cmd:"/quit",desc:"Exit the chat"}];function le(){let o=0,t="thinking...";const s=setInterval(()=>{const h=e.cyan(At[o%At.length]),k=o%3,v=k===0?e.white(".")+e.gray(".")+e.dim("."):k===1?e.dim(".")+e.white(".")+e.gray("."):e.gray(".")+e.dim(".")+e.white("."),C=t.replace(/\.+$/,""),I=` ${h} ${e.gray(C)}${v}`;process.stdout.write(`\r${" ".repeat(80)}\r${I}`),o++},300),r=()=>{clearInterval(s),process.stdout.write(`\r${" ".repeat(80)}\r`)};return r.setLabel=h=>{t=h},r}function Os(o){const t=o?.agent;return t?t.provider?t.provider:t.gemini?"gemini":t.codex?"codex":t.claude?"claude":t.cursor?"cursor":process.env.AGENT_TYPE||"cursor":process.env.AGENT_TYPE||"cursor"}function Ve(o){return o.slice(-ie).map(t=>`${t.role==="human"?"H":"AI"}: ${t.content}`).join(`
2
- `)}function Rs(o,t,s){let r=o;return t.length>0&&(r+=`
1
+ import{invokeAgent as xe,getAgentStrategy as ve,getSkill as $t}from"@zibby/core";import{existsSync as gt,readFileSync as oe,readdirSync as Te}from"fs";import{resolve as at,join as Dt,dirname as Ce,basename as Ie}from"path";import{createInterface as Pe,moveCursor as M,cursorTo as O,clearLine as G,emitKeypressEvents as Ae}from"readline";import{fileURLToPath as Ee}from"url";import{homedir as Le}from"os";import e from"chalk";import{highlight as ne}from"cli-highlight";import Me from"dotenv";import{getSessionToken as Wt,getUserInfo as Ne,getProjects as Be,saveSessionToken as Oe,saveUserInfo as Re,clearSession as Ue,saveProxyUrl as Fe,getProxyUrl as je,getMem0ProxyUrl as De,saveMem0ProxyUrl as We}from"../config/config.js";import{getApiUrl as re}from"../config/environments.js";import{loadActiveSkills as Ye,loadChatHistory as He,saveActiveSkills as It,saveChatHistory as Pt}from"./chat-session-store.js";import{buildReliabilityInstruction as ze}from"./agent-reliability.js";const Je=Ee(import.meta.url),Ge=Ce(Je),qe=JSON.parse(oe(Dt(Ge,"../../package.json"),"utf-8")),ie=30,Ze=54,Ke=18e3,ce=12e3,ae=42e3;function Ve(n){return new Promise(t=>setTimeout(t,n))}const At=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],Yt=[{cmd:"/help",desc:"Show this help"},{cmd:"/skills",desc:"List active & available skills"},{cmd:"/history",desc:"Show conversation history (--all or -n 50)"},{cmd:"/clear",desc:"Clear conversation history"},{cmd:"/memory",desc:"View stored memories, tasks, sessions"},{cmd:"/exit",desc:"Exit the chat"},{cmd:"/quit",desc:"Exit the chat"}];function le(){let n=0,t="thinking...";const s=setInterval(()=>{const h=e.cyan(At[n%At.length]),k=n%3,v=k===0?e.white(".")+e.gray(".")+e.dim("."):k===1?e.dim(".")+e.white(".")+e.gray("."):e.gray(".")+e.dim(".")+e.white("."),C=t.replace(/\.+$/,""),I=` ${h} ${e.gray(C)}${v}`;process.stdout.write(`\r${" ".repeat(80)}\r${I}`),n++},300),o=()=>{clearInterval(s),process.stdout.write(`\r${" ".repeat(80)}\r`)};return o.setLabel=h=>{t=h},o}function Us(n){const t=n?.agent;return t?t.provider?t.provider:t.gemini?"gemini":t.codex?"codex":t.claude?"claude":t.cursor?"cursor":process.env.AGENT_TYPE||"cursor":process.env.AGENT_TYPE||"cursor"}function Xe(n){return n.slice(-ie).map(t=>`${t.role==="human"?"H":"AI"}: ${t.content}`).join(`
2
+ `)}function Fs(n,t,s){let o=n;return t.length>0&&(o+=`
3
3
 
4
- ${Ve(t)}`),r+=`
4
+ ${Xe(t)}`),o+=`
5
5
  H: ${s}
6
- AI:`,r}function Xe(o){const t=o.filter(s=>!process.env[s]);return{ok:t.length===0,missing:t}}function Qe(o){const t=mt(o,".zibby.config.mjs");if(!$t(t))return{};try{return import(t).then(s=>s.default||{})}catch{return{}}}async function ts(o){const t=mt(o,".zibby","chat.mjs");if($t(t))try{const s=await import(t);return s.CHAT_CONFIG||s.default||{}}catch(s){console.warn(`\u26A0\uFE0F Could not load ${t}: ${s.message}`),console.warn(' Falling back to built-in chat config. Run "zibby init -f" to regenerate.')}try{const s=await import("@zibby/core/templates/browser-test-automation/chat.mjs");return s.CHAT_CONFIG||s.default||{}}catch{return{systemPrompt:"You are Zibby, a friendly AI assistant for test automation.",skills:[]}}}function ue(o){try{const t=mt(o,".zibby","commands");return $t(t)?Te(t).filter(s=>s.toLowerCase().endsWith(".md")).sort((s,r)=>s.localeCompare(r)):[]}catch{return[]}}function es(o,t){const s=String(t||"").trim();if(!s.startsWith("/"))return s;const[r,...h]=s.split(/\s+/),k=h.join(" ").trim(),v=r.slice(1);if(!v)return s;const C=v.toLowerCase().endsWith(".md")?[v]:[v,`${v}.md`],I=mt(o,".zibby","commands"),U=C.find(R=>$t(Dt(I,R)));if(!U)return s;try{const R=oe(Dt(I,U),"utf-8").trim();return k?`${R}
6
+ AI:`,o}function Qe(n){const t=n.filter(s=>!process.env[s]);return{ok:t.length===0,missing:t}}function ts(n){const t=at(n,".zibby.config.mjs");if(!gt(t))return{};try{return import(t).then(s=>s.default||{})}catch{return{}}}async function es(n){const t=at(n,".zibby","chat.mjs");if(gt(t))try{const o=await import(t);if(o.CHAT_CONFIG||o.default)return o.CHAT_CONFIG||o.default}catch{}const s=at(Le(),".zibby","chat.mjs");if(s!==t&&gt(s))try{const o=await import(s);if(o.CHAT_CONFIG||o.default)return o.CHAT_CONFIG||o.default}catch{}try{const o=await import("@zibby/core/templates/browser-test-automation/chat.mjs");return o.CHAT_CONFIG||o.default||{}}catch{return{systemPrompt:"You are Zibby, a friendly AI assistant for test automation.",skills:[]}}}function ue(n){try{const t=at(n,".zibby","commands");return gt(t)?Te(t).filter(s=>s.toLowerCase().endsWith(".md")).sort((s,o)=>s.localeCompare(o)):[]}catch{return[]}}function ss(n,t){const s=String(t||"").trim();if(!s.startsWith("/"))return s;const[o,...h]=s.split(/\s+/),k=h.join(" ").trim(),v=o.slice(1);if(!v)return s;const C=v.toLowerCase().endsWith(".md")?[v]:[v,`${v}.md`],I=at(n,".zibby","commands"),U=C.find(R=>gt(Dt(I,R)));if(!U)return s;try{const R=oe(Dt(I,U),"utf-8").trim();return k?`${R}
7
7
 
8
- ${k}`:R}catch{return s}}function ss(o){const t=o.slice(-ie),s=[];let r=0;for(let h=t.length-1;h>=0;h--){const k=t[h],v=String(k?.content||""),C=k?.role==="human"?"user":"assistant",I=v.length;if(s.length>=6&&r+I>Ge)break;s.push({role:C,content:v}),r+=I}return s.reverse()}function xt(o,t){const s=String(o||"");return s.length<=t?s:`${s.slice(0,Math.max(0,t-24))}
8
+ ${k}`:R}catch{return s}}function os(n){const t=n.slice(-ie),s=[];let o=0;for(let h=t.length-1;h>=0;h--){const k=t[h],v=String(k?.content||""),C=k?.role==="human"?"user":"assistant",I=v.length;if(s.length>=6&&o+I>Ke)break;s.push({role:C,content:v}),o+=I}return s.reverse()}function _t(n,t){const s=String(n||"");return s.length<=t?s:`${s.slice(0,Math.max(0,t-24))}
9
9
 
10
- [truncated for proxy size]`}function Ht(o){return o.reduce((t,s)=>t+String(s?.content||"").length+64,0)}function os(o){let t=[...o];if(t.length===0||(t[0]?.role==="system"&&(t[0]={...t[0],content:xt(t[0].content,ce)}),Ht(t)<=ae))return t;const s=t[0],r=t[t.length-1],h=t.slice(1,-1).slice(-4).map(k=>({...k,content:xt(k.content,2500)}));return t=[s,...h,r],Ht(t)<=ae||(t=[{...s,content:xt(s?.content,6e3)},{...r,content:xt(r?.content,8e3)}]),t}function ns(o){const t=[];for(const s of o){const r=kt(s),h=String(r?.description||"").trim();if(!h){t.push(`- ${s}`);continue}t.push(`- ${s}: ${Et(h,80)}`)}return t.length===0?"":`## Active skills (call get_skill_context before first use)
10
+ [truncated for proxy size]`}function Ht(n){return n.reduce((t,s)=>t+String(s?.content||"").length+64,0)}function ns(n){let t=[...n];if(t.length===0||(t[0]?.role==="system"&&(t[0]={...t[0],content:_t(t[0].content,ce)}),Ht(t)<=ae))return t;const s=t[0],o=t[t.length-1],h=t.slice(1,-1).slice(-4).map(k=>({...k,content:_t(k.content,2500)}));return t=[s,...h,o],Ht(t)<=ae||(t=[{...s,content:_t(s?.content,6e3)},{...o,content:_t(o?.content,8e3)}]),t}function rs(n){const t=[];for(const s of n){const o=$t(s),h=String(o?.description||"").trim();if(!h){t.push(`- ${s}`);continue}t.push(`- ${s}: ${Et(h,80)}`)}return t.length===0?"":`## Active skills (call get_skill_context before first use)
11
11
  ${t.join(`
12
12
  `)}`}const pe={cli_plain:["## Response format for this channel","- Output plain terminal text with optional fenced code blocks.","- Use fenced code blocks (```language) when showing code or config. Always include the language tag.","- Outside of code blocks, do NOT use Markdown syntax (no **bold**, __underline__, headings, inline backticks, or markdown tables).","- Keep responses concise and readable in a terminal.","- Write in natural, conversational English. Avoid dumping raw JSON, run IDs, or technical jargon.",'- When reporting test results: summarize in plain language (e.g. "Both tests finished \u2014 the checkbox test passed but the login test failed because..."). Include ticket keys but skip internal run IDs unless the user asks.',"- When something fails, explain the root cause simply and suggest a fix."].join(`
13
- `)};function rs(o={},t={}){const s=String(t.outputProfile||process.env.ZIBBY_OUTPUT_PROFILE||o.outputProfile||"cli_plain").trim(),r=o?.outputProfiles?.[s];return typeof r=="string"&&r.trim()?r.trim():r&&typeof r.instruction=="string"&&r.instruction.trim()?r.instruction.trim():pe[s]||pe.cli_plain}function Et(o,t){const s=String(o??"");return s.length<=t?s:t<=1?s.slice(0,t):`${s.slice(0,t-1)}\u2026`}function is(){return!1}function Us(o){return o}function cs(o){const t=/^```(\w*)\n([\s\S]*?)^```$/gm;return o.replace(t,(s,r,h)=>{const k=h.replace(/\n$/,"");try{const v=r?ne(k,{language:r,ignoreIllegals:!0}):ne(k,{ignoreIllegals:!0}),C=e.gray("\u2500".repeat(Math.min(process.stdout.columns-6||54,72))),I=r?e.dim(` ${r}`):"";return`${C}${I}
13
+ `)};function is(n={},t={}){const s=String(t.outputProfile||process.env.ZIBBY_OUTPUT_PROFILE||n.outputProfile||"cli_plain").trim(),o=n?.outputProfiles?.[s];return typeof o=="string"&&o.trim()?o.trim():o&&typeof o.instruction=="string"&&o.instruction.trim()?o.instruction.trim():pe[s]||pe.cli_plain}function Et(n,t){const s=String(n??"");return s.length<=t?s:t<=1?s.slice(0,t):`${s.slice(0,t-1)}\u2026`}function cs(){return!1}function js(n){return n}function as(n){const t=/^```(\w*)\n([\s\S]*?)^```$/gm;return n.replace(t,(s,o,h)=>{const k=h.replace(/\n$/,"");try{const v=o?ne(k,{language:o,ignoreIllegals:!0}):ne(k,{ignoreIllegals:!0}),C=e.gray("\u2500".repeat(Math.min(process.stdout.columns-6||54,72))),I=o?e.dim(` ${o}`):"";return`${C}${I}
14
14
  ${v}
15
- ${C}`}catch{return k}})}function zt(){const o=Number(process.stdout?.columns)||0,t=o>8?Math.max(30,o-4):Ze;return` ${"\u2500".repeat(t)}`}function de(o=[]){const t=Yt.map(r=>({cmd:r.cmd,source:"builtin",name:r.cmd.slice(1),desc:r.desc})),s=o.map(r=>({cmd:`/${r}`,source:"template",name:r,desc:"Command template"}));return[...t,...s]}function as(o,t){const s=String(o||""),r=Number.isFinite(t)?t:s.length;return!(!s.startsWith("/")||r!==s.length||s.includes(" "))}function ls({userName:o,cwd:t,projectName:s,restoredCount:r,skillsLine:h}){const k=Number(process.stdout?.columns)||0,v=Math.max(40,Math.min(k-8,100)),C=Math.max(20,Math.floor((v-3)*.55)),I=v-C-3,U=o||"there",R=s||"No project linked",gt=Ie(t||process.cwd()),W=[`Restored ${r} messages from previous session.`,"",h],rt=["Workspace details","",`v${qe.version} | Hi, ${U}`,`Company: ${R}`,`Directory: ~/${gt}`,`Path: ${t}`,"","Use /help for commands"],E=Math.max(W.length,rt.length),V=(()=>{if(W.length>=E)return W;const F=Math.floor((E-W.length)/2),st=E-W.length-F;return[...Array(F).fill(""),...W,...Array(st).fill("")]})(),H="\u2500",L=` \u250C${H.repeat(C+2)}\u252C${H.repeat(I+2)}\u2510`,et=` \u2514${H.repeat(C+2)}\u2534${H.repeat(I+2)}\u2518`;console.log(e.gray(L));for(let F=0;F<E;F++){const st=Et(V[F]||"",C),it=Et(rt[F]||"",I),ht=" ".repeat(Math.max(0,C-st.length)),l=" ".repeat(Math.max(0,I-it.length)),w=` \u2502 ${st}${ht} \u2502 ${it}${l} \u2502`;console.log(e.gray(w))}console.log(e.gray(et)),console.log("")}function us(){process.stdout?.isTTY&&(M(process.stdout,0,-2),O(process.stdout,4))}function ps(o){const t=!!(process.stdout?.isTTY&&process.stdin?.isTTY);let s=!1,r=!1,h=!1,k=null,v=0,C=0,I=0;function U(){return e.gray(zt())}function R(){k&&(clearInterval(k),k=null)}function gt(l){!t||!s||!r||!h||(process.stdout.write("\x1B7"),M(process.stdout,0,-2),O(process.stdout,0),q(process.stdout,0),process.stdout.write(` ${l}`),process.stdout.write("\x1B8"))}function W(){if(!(!t||!s)&&C!==0){process.stdout.write("\x1B7"),M(process.stdout,0,2);for(let l=0;l<C;l++)O(process.stdout,0),q(process.stdout,0),l<C-1&&M(process.stdout,0,1);process.stdout.write("\x1B8"),C=0,r&&rt()}}function rt(){!t||!s||!r||(process.stdout.write("\x1B7"),M(process.stdout,0,1),O(process.stdout,0),q(process.stdout,0),process.stdout.write(U()),process.stdout.write("\x1B8"))}function E(l,w){if(!t||!s||!r)return;W();const c=l.slice(0,6);if(c.length!==0){M(process.stdout,0,1);for(let y=0;y<c.length;y++)process.stdout.write(`
16
- `);M(process.stdout,0,-(1+c.length)),process.stdout.write("\x1B7"),M(process.stdout,0,2);for(let y=0;y<c.length;y++){O(process.stdout,0),q(process.stdout,0);const Z=y===w?e.cyan("\u203A"):" ",f=y===w?e.white(c[y]):e.gray(c[y]);process.stdout.write(` ${Z} /${f}`),y<c.length-1&&M(process.stdout,0,1)}process.stdout.write("\x1B8"),C=c.length,rt()}}function V(){if(!t||!s||!r)return;W();const l=process.stdout.columns||80,w=I>0?Math.ceil(I/l):1;O(process.stdout,0),M(process.stdout,0,-w),h&&M(process.stdout,0,-2),process.stdout.write("\x1B[J"),r=!1}function H(l={}){const w=l.preserveInput===!0;if(!t){o.prompt();return}w||(o.line="",o.cursor=0),I=zt().length;const c=U();process.stdout.write(`${c}
17
- `),o.prompt(),process.stdout.write(`
15
+ ${C}`}catch{return k}})}function zt(){const n=Number(process.stdout?.columns)||0,t=n>8?Math.max(30,n-4):Ze;return` ${"\u2500".repeat(t)}`}function de(n=[]){const t=Yt.map(o=>({cmd:o.cmd,source:"builtin",name:o.cmd.slice(1),desc:o.desc})),s=n.map(o=>({cmd:`/${o}`,source:"template",name:o,desc:"Command template"}));return[...t,...s]}function ls(n,t){const s=String(n||""),o=Number.isFinite(t)?t:s.length;return!(!s.startsWith("/")||o!==s.length||s.includes(" "))}function us({userName:n,cwd:t,projectName:s,restoredCount:o,skillsLine:h}){const k=Number(process.stdout?.columns)||0,v=Math.max(40,Math.min(k-8,100)),C=Math.max(20,Math.floor((v-3)*.55)),I=v-C-3,U=n||"there",R=s||"No project linked",ht=Ie(t||process.cwd()),W=[`Restored ${o} messages from previous session.`,"",h],rt=["Workspace details","",`v${qe.version} | Hi, ${U}`,`Company: ${R}`,`Directory: ~/${ht}`,`Path: ${t}`,"","Use /help for commands"],E=Math.max(W.length,rt.length),V=(()=>{if(W.length>=E)return W;const F=Math.floor((E-W.length)/2),st=E-W.length-F;return[...Array(F).fill(""),...W,...Array(st).fill("")]})(),H="\u2500",L=` \u250C${H.repeat(C+2)}\u252C${H.repeat(I+2)}\u2510`,et=` \u2514${H.repeat(C+2)}\u2534${H.repeat(I+2)}\u2518`;console.log(e.gray(L));for(let F=0;F<E;F++){const st=Et(V[F]||"",C),it=Et(rt[F]||"",I),yt=" ".repeat(Math.max(0,C-st.length)),l=" ".repeat(Math.max(0,I-it.length)),w=` \u2502 ${st}${yt} \u2502 ${it}${l} \u2502`;console.log(e.gray(w))}console.log(e.gray(et)),console.log("")}function ps(){process.stdout?.isTTY&&(M(process.stdout,0,-2),O(process.stdout,4))}function ds(n){const t=!!(process.stdout?.isTTY&&process.stdin?.isTTY);let s=!1,o=!1,h=!1,k=null,v=0,C=0,I=0;function U(){return e.gray(zt())}function R(){k&&(clearInterval(k),k=null)}function ht(l){!t||!s||!o||!h||(process.stdout.write("\x1B7"),M(process.stdout,0,-2),O(process.stdout,0),G(process.stdout,0),process.stdout.write(` ${l}`),process.stdout.write("\x1B8"))}function W(){if(!(!t||!s)&&C!==0){process.stdout.write("\x1B7"),M(process.stdout,0,2);for(let l=0;l<C;l++)O(process.stdout,0),G(process.stdout,0),l<C-1&&M(process.stdout,0,1);process.stdout.write("\x1B8"),C=0,o&&rt()}}function rt(){!t||!s||!o||(process.stdout.write("\x1B7"),M(process.stdout,0,1),O(process.stdout,0),G(process.stdout,0),process.stdout.write(U()),process.stdout.write("\x1B8"))}function E(l,w){if(!t||!s||!o)return;W();const c=l.slice(0,6);if(c.length!==0){M(process.stdout,0,1);for(let y=0;y<c.length;y++)process.stdout.write(`
16
+ `);M(process.stdout,0,-(1+c.length)),process.stdout.write("\x1B7"),M(process.stdout,0,2);for(let y=0;y<c.length;y++){O(process.stdout,0),G(process.stdout,0);const q=y===w?e.cyan("\u203A"):" ",f=y===w?e.white(c[y]):e.gray(c[y]);process.stdout.write(` ${q} /${f}`),y<c.length-1&&M(process.stdout,0,1)}process.stdout.write("\x1B8"),C=c.length,rt()}}function V(){if(!t||!s||!o)return;W();const l=process.stdout.columns||80,w=I>0?Math.ceil(I/l):1;O(process.stdout,0),M(process.stdout,0,-w),h&&M(process.stdout,0,-2),process.stdout.write("\x1B[J"),o=!1}function H(l={}){const w=l.preserveInput===!0;if(!t){n.prompt();return}w||(n.line="",n.cursor=0),I=zt().length;const c=U();process.stdout.write(`${c}
17
+ `),n.prompt(),process.stdout.write(`
18
18
  ${c}
19
- `),us(),r=!0}function L(){if(!t||!s||!r)return;const l=process.stdout.columns||80,w=I>0?Math.ceil(I/l):1,c=U();process.stdout.write("\x1B7"),M(process.stdout,0,-w);for(let y=0;y<w;y++)O(process.stdout,0),q(process.stdout,0),y<w-1&&M(process.stdout,0,1);O(process.stdout,0),process.stdout.write(c),M(process.stdout,0,2),O(process.stdout,0),q(process.stdout,0),process.stdout.write(c);for(let y=1;y<w;y++)M(process.stdout,0,1),O(process.stdout,0),q(process.stdout,0);process.stdout.write("\x1B8"),I=zt().length}function et(){if(!t||!s||!r)return;const l=(()=>{if(typeof o.getCursorPos!="function")return 0;try{return Math.max(0,Number(o.getCursorPos()?.rows||0))}catch{return 0}})();process.stdout.write("\x1B7"),M(process.stdout,0,-(l+1)),O(process.stdout,0),q(process.stdout,0),process.stdout.write(U()),M(process.stdout,0,l+2),O(process.stdout,0),q(process.stdout,0),process.stdout.write(U()),process.stdout.write("\x1B8")}function F(l){R(),V(),console.log();const w=String(l??"").replace(/\r/g,"").split(`
19
+ `),ps(),o=!0}function L(){if(!t||!s||!o)return;const l=process.stdout.columns||80,w=I>0?Math.ceil(I/l):1,c=U();process.stdout.write("\x1B7"),M(process.stdout,0,-w);for(let y=0;y<w;y++)O(process.stdout,0),G(process.stdout,0),y<w-1&&M(process.stdout,0,1);O(process.stdout,0),process.stdout.write(c),M(process.stdout,0,2),O(process.stdout,0),G(process.stdout,0),process.stdout.write(c);for(let y=1;y<w;y++)M(process.stdout,0,1),O(process.stdout,0),G(process.stdout,0);process.stdout.write("\x1B8"),I=zt().length}function et(){if(!t||!s||!o)return;const l=(()=>{if(typeof n.getCursorPos!="function")return 0;try{return Math.max(0,Number(n.getCursorPos()?.rows||0))}catch{return 0}})();process.stdout.write("\x1B7"),M(process.stdout,0,-(l+1)),O(process.stdout,0),G(process.stdout,0),process.stdout.write(U()),M(process.stdout,0,l+2),O(process.stdout,0),G(process.stdout,0),process.stdout.write(U()),process.stdout.write("\x1B8")}function F(l){R(),V(),console.log();const w=String(l??"").replace(/\r/g,"").split(`
20
20
  `);for(;w.length>0&&w[w.length-1]==="";)w.pop();for(const c of w)console.log(c?` ${c}`:"");h=!1}function st(l){const w=String(l??"").replace(/\r/g,"").split(`
21
21
  `);w.length===0&&w.push("");const c=[];for(const y of w)c.push(e.bgGray.white(` ${y||" "} `));return c.join(`
22
- `)}function it(){R();const l=()=>e.gray(`${At[v%At.length]} thinking`);if(!t){console.log(` ${l()}`),h=!0;return}W(),V(),console.log(),console.log(` ${l()}`),H({preserveInput:!0}),h=!0,v+=1,k=setInterval(()=>{h&&(gt(l()),v+=1)},120)}function ht(l){R();const w=String(l??"").replace(/\r/g,"").replace(/⎿/g,"");if(!h){F(w);return}V(),console.log();const c=w.split(`
23
- `);for(;c.length>0&&c[c.length-1]==="";)c.pop();for(const y of c)console.log(y?` ${y}`:"");console.log(),h=!1}return{enabled:t,mount(){if(!s){if(!t){o.prompt(),s=!0;return}s=!0,H()}},refreshPrompt(l={}){if(t&&s){r&&V(),H(l);return}o.prompt()},pushSystem(l){F(l)},pushUser(l){F(st(l))},pushAssistant(l){ht(l)},showAssistantLoading:it,dismissTransientLoading(){R(),h&&t&&s&&r&&(process.stdout.write("\x1B7"),M(process.stdout,0,-2),O(process.stdout,0),q(process.stdout,0),M(process.stdout,0,-1),O(process.stdout,0),q(process.stdout,0),process.stdout.write("\x1B8")),h=!1},touchInputFrame:et,handleResize:L,showCommandDropdown:E,clearCommandDropdown:W}}function ds(){console.log(""),console.log(e.cyan(" Available commands:"));for(const o of Yt)console.log(e.white(` ${o.cmd.padEnd(10)} `)+e.gray(o.desc));console.log(""),console.log(e.cyan(" Chat options:")),console.log(e.white(" --stream, -s ")+e.gray("Enable typewriter effect (default: instant)")),console.log(""),console.log(e.gray(" To install/uninstall skills, just ask naturally:")),console.log(e.gray(' "connect to Jira" \u2022 "add GitHub" \u2022 "remove Slack"')),console.log("")}const fs=Xe;async function Fs(o={}){const t=process.cwd();o.verbose&&(process.env.ZIBBY_VERBOSE="true");const s=o.stream||!1;[mt(t,".env.local"),mt(t,".env")].forEach(c=>{$t(c)&&Le.config({path:c,override:!1})});try{await import("@zibby/skills")}catch{}function h(){const c=Wt();if(!c)return!1;try{const y=JSON.parse(atob(c.split(".")[1]));return y.exp&&y.exp*1e3>Date.now()}catch{return!1}}if(!h()){Re();const c=re(),{spawn:y}=await import("child_process"),Z=await fetch(`${c}/cli/login/initiate`,{method:"POST",headers:{"Content-Type":"application/json"}});Z.ok||(console.log(e.red("\n Could not start login. Try `zibby login`.\n")),process.exit(1));const{deviceCode:f,verificationUrl:n,expiresIn:N,interval:at}=await Z.json(),ot=process.platform;y(ot==="darwin"?"open":ot==="win32"?"cmd":"xdg-open",ot==="win32"?["/c","start","",n]:[n],{detached:!0,stdio:"ignore"}).unref();const lt=le(),ut=(at||3)*1e3,pt=Math.floor(N/(at||3));let X=!1;for(let nt=0;nt<pt;nt++){await Ke(ut);try{const Q=await fetch(`${c}/cli/login/poll`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({deviceCode:f})});if(Q.status===202)continue;if(!Q.ok)break;const z=await Q.json();if(z.status==="authorized"){Ne(z.token),Oe(z.user),z.proxyUrl&&Ue(z.proxyUrl),z.mem0ProxyUrl&&De(z.mem0ProxyUrl),X=!0;break}if(z.status==="denied")break}catch{break}}lt(),X||(console.log(e.red("\n Login failed or timed out. Run `zibby login` to try again.\n")),process.exit(1))}const k=await Qe(t),v=await ts(t),C=o.agent||"assistant",I=Wt();I&&!process.env.ZIBBY_USER_TOKEN&&(process.env.ZIBBY_USER_TOKEN=I);const U=je()||Fe();U&&!process.env.ZIBBY_MEM0_OPENAI_BASE_URL&&(process.env.ZIBBY_MEM0_OPENAI_BASE_URL=U),process.env.AGENT_TYPE=C,process.env.ZIBBY_CHAT_OWNER_PID=String(process.pid);try{const{cleanupStalePidFiles:c}=await import("../utils/chat-run-lifecycle.js");c(t,k)}catch{}let R;try{R=ve({state:{agentType:C}})}catch(c){console.log(e.red(`
22
+ `)}function it(){R();const l=()=>e.gray(`${At[v%At.length]} thinking`);if(!t){console.log(` ${l()}`),h=!0;return}W(),V(),console.log(),console.log(` ${l()}`),H({preserveInput:!0}),h=!0,v+=1,k=setInterval(()=>{h&&(ht(l()),v+=1)},120)}function yt(l){R();const w=String(l??"").replace(/\r/g,"").replace(/⎿/g,"");if(!h){F(w);return}V(),console.log();const c=w.split(`
23
+ `);for(;c.length>0&&c[c.length-1]==="";)c.pop();for(const y of c)console.log(y?` ${y}`:"");console.log(),h=!1}return{enabled:t,mount(){if(!s){if(!t){n.prompt(),s=!0;return}s=!0,H()}},refreshPrompt(l={}){if(t&&s){o&&V(),H(l);return}n.prompt()},pushSystem(l){F(l)},pushUser(l){F(st(l))},pushAssistant(l){yt(l)},showAssistantLoading:it,dismissTransientLoading(){R(),h&&t&&s&&o&&(process.stdout.write("\x1B7"),M(process.stdout,0,-2),O(process.stdout,0),G(process.stdout,0),M(process.stdout,0,-1),O(process.stdout,0),G(process.stdout,0),process.stdout.write("\x1B8")),h=!1},touchInputFrame:et,handleResize:L,showCommandDropdown:E,clearCommandDropdown:W}}function fs(){console.log(""),console.log(e.cyan(" Available commands:"));for(const n of Yt)console.log(e.white(` ${n.cmd.padEnd(10)} `)+e.gray(n.desc));console.log(""),console.log(e.cyan(" Chat options:")),console.log(e.white(" --stream, -s ")+e.gray("Enable typewriter effect (default: instant)")),console.log(""),console.log(e.gray(" To install/uninstall skills, just ask naturally:")),console.log(e.gray(' "connect to Jira" \u2022 "add GitHub" \u2022 "remove Slack"')),console.log("")}const ms=Qe;async function Ds(n={}){const t=process.cwd();n.verbose&&(process.env.ZIBBY_VERBOSE="true");const s=n.stream||!1;[at(t,".env.local"),at(t,".env")].forEach(c=>{gt(c)&&Me.config({path:c,override:!1})});try{await import("@zibby/skills")}catch{}function h(){const c=Wt();if(!c)return!1;try{const y=JSON.parse(atob(c.split(".")[1]));return y.exp&&y.exp*1e3>Date.now()}catch{return!1}}if(!h()){Ue();const c=re(),{spawn:y}=await import("child_process"),q=await fetch(`${c}/cli/login/initiate`,{method:"POST",headers:{"Content-Type":"application/json"}});q.ok||(console.log(e.red("\n Could not start login. Try `zibby login`.\n")),process.exit(1));const{deviceCode:f,verificationUrl:r,expiresIn:B,interval:lt}=await q.json(),ot=process.platform;y(ot==="darwin"?"open":ot==="win32"?"cmd":"xdg-open",ot==="win32"?["/c","start","",r]:[r],{detached:!0,stdio:"ignore"}).unref();const ut=le(),pt=(lt||3)*1e3,dt=Math.floor(B/(lt||3));let X=!1;for(let nt=0;nt<dt;nt++){await Ve(pt);try{const Q=await fetch(`${c}/cli/login/poll`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({deviceCode:f})});if(Q.status===202)continue;if(!Q.ok)break;const z=await Q.json();if(z.status==="authorized"){Oe(z.token),Re(z.user),z.proxyUrl&&Fe(z.proxyUrl),z.mem0ProxyUrl&&We(z.mem0ProxyUrl),X=!0;break}if(z.status==="denied")break}catch{break}}ut(),X||(console.log(e.red("\n Login failed or timed out. Run `zibby login` to try again.\n")),process.exit(1))}const k=await ts(t),v=await es(t),C=n.agent||"assistant",I=Wt();I&&!process.env.ZIBBY_USER_TOKEN&&(process.env.ZIBBY_USER_TOKEN=I);const U=De()||je();U&&!process.env.ZIBBY_MEM0_OPENAI_BASE_URL&&(process.env.ZIBBY_MEM0_OPENAI_BASE_URL=U),process.env.AGENT_TYPE=C,process.env.ZIBBY_CHAT_OWNER_PID=String(process.pid);try{const{cleanupStalePidFiles:c}=await import("../utils/chat-run-lifecycle.js");c(t,k)}catch{}let R;try{R=ve({state:{agentType:C}})}catch(c){console.log(e.red(`
24
24
  ${c.message}
25
- `)),process.exit(1)}const gt=Me(),rt=Be()?.[0]?.name,E=Ye(t),V=v.skills||[],H=We(t),L=H?[...new Set([...V,...H])]:[...V];let et={data:null,timestamp:0};const F=300*1e3,st={jira:"jira",github:"github",slack:"slack",sentry:"sentry"};try{const c=Wt();if(c){const y=re(),Z=await fetch(`${y}/integrations/status`,{method:"GET",headers:{Authorization:`Bearer ${c}`}});if(Z.ok){const f=await Z.json();for(const[n,N]of Object.entries(st))f[n]?.connected&&!L.includes(N)&&kt(N)&&L.push(N);It(t,L)}}}catch{}const it=L.filter(c=>c!=="skill-installer"&&c!=="core-tools"),ht=it.length>0?`Skills: ${it.join(", ")}`:"Skills: (none)";ls({userName:gt?.name?.split(" ")[0],cwd:t,projectName:rt,restoredCount:E.length,skillsLine:ht});let l=ue(t),w=de(l);return new Promise(c=>{const y=process.env.ZIBBY_CHAT_TAB_COMPLETION==="1",Z={input:process.stdin,output:process.stdout,prompt:e.green(" > "),terminal:!0};y&&(Z.completer=u=>{const i=String(u||"");if(!i.startsWith("/"))return[[],i];const m=w.map(P=>P.cmd),$=m.filter(P=>P.startsWith(i));return[$.length>0?$:m,i]});const f=Pe(Z),n=ps(f);let N=null,at=!1;const ot=[],fe=0,me=1200;let lt=!1,ut=!1,pt=!1,X=!1,nt="",Q=0;const z=typeof f._ttyWrite=="function"?f._ttyWrite.bind(f):null;z&&(f._ttyWrite=(...u)=>{if(!(X||Date.now()<Q))return z(...u)});function ge(){pt||!process.stdout?.isTTY||typeof process.stdout.write!="function"||(process.stdout.write("\x1B[?2004h"),pt=!0)}function Jt(){pt&&(!process.stdout?.isTTY||typeof process.stdout.write!="function"||(process.stdout.write("\x1B[?2004l"),pt=!1))}function he(u){const i="\x1B[200~",m="\x1B[201~";if(!X&&!u.includes(i))return!1;let $=u,P=!1;for(;$.length>0;){if(!X){const A=$.indexOf(i);if(A===-1)break;P=!0,Kt(),X=!0,nt="",$=$.slice(A+i.length);continue}const J=$.indexOf(m);if(J===-1){P=!0,nt+=$;break}P=!0,nt+=$.slice(0,J);const tt=String(nt||"").replace(/\r\n/g,`
25
+ `)),process.exit(1)}const ht=Ne(),rt=Be()?.[0]?.name,E=He(t),V=v.skills||[],H=Ye(t),L=H?[...new Set([...V,...H])]:[...V];let et={data:null,timestamp:0};const F=300*1e3,st={jira:"jira",github:"github",slack:"slack",sentry:"sentry"};try{const c=Wt();if(c){const y=re(),q=await fetch(`${y}/integrations/status`,{method:"GET",headers:{Authorization:`Bearer ${c}`}});if(q.ok){const f=await q.json();for(const[r,B]of Object.entries(st))f[r]?.connected&&!L.includes(B)&&$t(B)&&L.push(B);It(t,L)}}}catch{}const it=L.filter(c=>c!=="skill-installer"&&c!=="core-tools"),yt=it.length>0?`Skills: ${it.join(", ")}`:"Skills: (none)";us({userName:ht?.name?.split(" ")[0],cwd:t,projectName:rt,restoredCount:E.length,skillsLine:yt});let l=ue(t),w=de(l);return new Promise(c=>{const y=process.env.ZIBBY_CHAT_TAB_COMPLETION==="1",q={input:process.stdin,output:process.stdout,prompt:e.green(" > "),terminal:!0};y&&(q.completer=u=>{const i=String(u||"");if(!i.startsWith("/"))return[[],i];const m=w.map(P=>P.cmd),$=m.filter(P=>P.startsWith(i));return[$.length>0?$:m,i]});const f=Pe(q),r=ds(f);let B=null,lt=!1;const ot=[],fe=0,me=1200;let ut=!1,pt=!1,dt=!1,X=!1,nt="",Q=0;const z=typeof f._ttyWrite=="function"?f._ttyWrite.bind(f):null;z&&(f._ttyWrite=(...u)=>{if(!(X||Date.now()<Q))return z(...u)});function ge(){dt||!process.stdout?.isTTY||typeof process.stdout.write!="function"||(process.stdout.write("\x1B[?2004h"),dt=!0)}function Jt(){dt&&(!process.stdout?.isTTY||typeof process.stdout.write!="function"||(process.stdout.write("\x1B[?2004l"),dt=!1))}function he(u){const i="\x1B[200~",m="\x1B[201~";if(!X&&!u.includes(i))return!1;let $=u,P=!1;for(;$.length>0;){if(!X){const A=$.indexOf(i);if(A===-1)break;P=!0,Kt(),X=!0,nt="",$=$.slice(A+i.length);continue}const J=$.indexOf(m);if(J===-1){P=!0,nt+=$;break}P=!0,nt+=$.slice(0,J);const tt=String(nt||"").replace(/\r\n/g,`
26
26
  `).replace(/\r/g,`
27
27
  `);if(tt.length>0){const A=tt.split(`
28
- `);for(const G of A)Rt(G)}vt(),nt="",X=!1,$=$.slice(J+m.length)}return P}function qt(){if(!lt){lt=!0;try{Pt(t,E),It(t,L)}catch{}try{Jt()}catch{}try{f.close()}catch{}process.exit(0)}}async function Lt(){if(!lt){lt=!0,typeof R?.cleanup=="function"&&await R.cleanup().catch(()=>{});try{const{killAllChatOrchestratedRuns:u}=await import("../utils/chat-run-lifecycle.js"),{postCliInterruptedRunIndex:i}=await import("@zibby/core/utils/run-index-post-cli.js");u(t,process.pid,k),i({cwd:t,config:k})}catch{}Pt(t,E),It(t,L),n.pushSystem(e.gray("Session saved. Goodbye!")),Jt(),f.close(),process.exit(0)}}if(n.mount(),ge(),process.stdout?.isTTY){let u;process.stdout.on("resize",()=>{clearTimeout(u),u=setTimeout(()=>{n.handleResize()},80)})}let Mt=0;const _t=u=>{if(typeof u=="string"?u==="\x1B":u&&typeof u.length=="number"?u.length===1&&u[0]===27:!1){N&&N.abort(),qt();return}const m=typeof u=="string"?u:u&&typeof u.length=="number"?Buffer.from(u).toString("utf8"):"";if(!m)return;if(he(m)){Mt=Date.now()+1200,Q=Date.now()+1200,Ot();return}const $=(m.includes(`
28
+ `);for(const Z of A)Rt(Z)}vt(),nt="",X=!1,$=$.slice(J+m.length)}return P}function Gt(){if(!ut){ut=!0;try{Pt(t,E),It(t,L)}catch{}try{Jt()}catch{}try{f.close()}catch{}process.exit(0)}}async function Lt(){if(!ut){ut=!0,typeof R?.cleanup=="function"&&await R.cleanup().catch(()=>{});try{const{killAllChatOrchestratedRuns:u}=await import("../utils/chat-run-lifecycle.js"),{postCliInterruptedRunIndex:i}=await import("@zibby/core/utils/run-index-post-cli.js");u(t,process.pid,k),i({cwd:t,config:k})}catch{}Pt(t,E),It(t,L),r.pushSystem(e.gray("Session saved. Goodbye!")),Jt(),f.close(),process.exit(0)}}if(r.mount(),ge(),process.stdout?.isTTY){let u;process.stdout.on("resize",()=>{clearTimeout(u),u=setTimeout(()=>{r.handleResize()},80)})}let Mt=0;const xt=u=>{if(typeof u=="string"?u==="\x1B":u&&typeof u.length=="number"?u.length===1&&u[0]===27:!1){B&&B.abort(),Gt();return}const m=typeof u=="string"?u:u&&typeof u.length=="number"?Buffer.from(u).toString("utf8"):"";if(!m)return;if(he(m)){Mt=Date.now()+1200,Q=Date.now()+1200,Ot();return}const $=(m.includes(`
29
29
  `)||m.includes("\r"))&&m!=="\r"&&m!==`
30
30
  `&&m!==`\r
31
- `,P=m.includes("\x1B[200~")||m.includes("\x1B[201~"),J=m.length>=16&&/\s/.test(m)&&!m.startsWith("\x1B");($||P||J)&&(Mt=Date.now()+1200,Q=Date.now()+1200,Kt(),Ot())},B={query:"",matches:[],selected:0},Y={prefix:"",matches:[],nextIndex:0},_={active:!1,prefix:"",lines:[],timer:null},Bt=[];let Zt=0,Nt="";const ye=new Set(Yt.map(u=>u.cmd));function dt(u,{preserveFrame:i=!1}={}){const m=String(u||"");f.line=m,f.cursor=m.length,Nt=m,typeof f._refreshLine=="function"?f._refreshLine():f.prompt(),i&&n.touchInputFrame()}function Gt(){Y.prefix="",Y.matches=[],Y.nextIndex=0}function vt(){if(_.timer&&(clearTimeout(_.timer),_.timer=null),_.active&&_.lines.length>0){Zt+=1;const u=_.lines.length,i=`[Pasted text #${Zt} +${u} lines]`,m=_.lines.join(`
32
- `);Bt.push({placeholder:i,text:m});const $=String(_.prefix||"").trimEnd(),P=$?`${$} ${i}`:i;dt(P,{preserveFrame:!0}),ft(f.line)}_.active=!1,_.prefix="",_.lines=[],Q=0}function Ot(){_.timer&&clearTimeout(_.timer),_.timer=setTimeout(()=>{vt()},180)}function Kt(){_.active||(_.active=!0,_.prefix=String(Nt||f.line||""),_.lines=[],dt(_.prefix,{preserveFrame:!0}))}function Rt(u){return _.active?(_.lines.push(String(u||"")),dt(_.prefix,{preserveFrame:!0}),Ot(),!0):!1}function we(u){let i=String(u||"");for(const m of Bt)i=i.split(m.placeholder).join(m.text);return i}function ft(u){const i=String(u||"");if(!i.startsWith("/")||i.includes(" ")||i.length===0){B.query="",B.matches=[],B.selected=0,n.clearCommandDropdown();return}l=ue(t),w=de(l);const m=i.slice(1).toLowerCase(),$=l.filter(P=>P.toLowerCase().startsWith(m));B.query=m,B.matches=$,B.selected=Math.min(B.selected,Math.max(0,$.length-1)),n.showCommandDropdown($,B.selected)}const Ut=(u,i)=>{if(i?.name==="escape"||i?.sequence==="\x1B"){N&&N.abort(),qt();return}if((i?.name==="return"||i?.name==="enter")&&(ut=!0),(i?.name==="backspace"||i?.name==="delete")&&n.touchInputFrame(),i?.name==="tab"&&!i?.shift){const P=String(f.line||""),J=Number(f.cursor||P.length);if(!as(P,J)){Gt(),ft(f.line);return}const tt=P.slice(1).toLowerCase();if(Y.prefix!==tt&&(Y.prefix=tt,Y.matches=w.map(G=>G.cmd).filter(G=>G.toLowerCase().startsWith(`/${tt}`)),Y.nextIndex=0),Y.matches.length===0)return;const A=Y.nextIndex%Y.matches.length;Y.nextIndex+=1,dt(Y.matches[A]),ft(f.line);return}const m=String(f.line||""),$=m.startsWith("/")&&!m.includes(" ")&&B.matches.length>0;if(i?.name==="up"||i?.name==="down"){if(!$){ft(f.line);return}const P=i.name==="up"?-1:1,J=B.matches.length;B.selected=(B.selected+P+J)%J;const tt=B.matches[B.selected];dt(`/${tt}`,{preserveFrame:!0}),n.showCommandDropdown(B.matches,B.selected);return}i?.name!=="tab"&&Gt(),setTimeout(()=>{ft(f.line),_.active||(Nt=String(f.line||""))},50)};process.stdin?.on&&(Ae(process.stdin,f),process.stdin.on("keypress",Ut)),n.enabled&&process.stdin?.on&&(typeof process.stdin.prependListener=="function"?process.stdin.prependListener("data",_t):process.stdin.on("data",_t)),f.on("line",async u=>{try{let i=String(u||"");const m=ut;if(ut=!1,!m&&(_.active||X||Date.now()<Mt||Date.now()<Q)){if(_.active&&i.length>0){Rt(i);return}_.active&&(vt(),n.refreshPrompt());return}if(_.active&&(vt(),i=String(f.line||i||"")),Rt(i))return;const $=i.trim(),P=B.matches[B.selected];if($.startsWith("/")&&!$.includes(" ")&&B.matches.length>0&&!ye.has($)&&$!==`/${P}`){n.clearCommandDropdown(),n.refreshPrompt(),dt(`/${P}`),ft(`/${P}`);return}n.clearCommandDropdown();const A=String(i||"").replace(/[\u0000-\u001F\u007F-\u009F]/g,"").trim();if(!A){n.enabled&&process.stdout?.isTTY&&(M(process.stdout,0,-1),O(process.stdout,4));return}if(at&&A!=="/exit"&&A!=="/quit"){N&&N.abort(),ot.push(A),n.pushSystem(e.gray("Processing your message...")),n.refreshPrompt({preserveInput:!0});return}const G=1e4;if(A.length>G){n.pushSystem(e.red(`\u26A0 Input too long (${A.length} chars). Maximum: ${G} characters.`)),n.pushSystem(e.gray("Tip: If you need to share logs, use a file instead:")),n.pushSystem(e.gray(" 1. Save to file: pbpaste > debug.log")),n.pushSystem(e.gray(' 2. Ask: "analyze debug.log in current directory"')),n.refreshPrompt();return}if(A==="/exit"||A==="/quit"){Lt();return}if(A==="/help"){ds(),n.refreshPrompt();return}if(A==="/skills"){const x=kt("skill-installer")?.catalog||{},g=L.filter(b=>b!=="skill-installer");console.log(e.cyan(`
33
- Active skills:`)),g.length===0&&console.log(e.gray(" (none)"));for(const b of g){const S=x[b]||{};console.log(e.green(` \u2713 ${b}`)+e.gray(S.description?` \u2014 ${S.description}`:""))}const T=Object.keys(x).filter(b=>!L.includes(b));if(T.length>0){console.log(e.cyan(`
34
- Available:`));for(const b of T){const S=x[b],d=S.envKeys?.length>0?fs(S.envKeys).ok?e.green(" \u2713 configured"):e.yellow(` \u26A0 needs: ${S.envKeys.join(", ")}`):"";console.log(e.white(` - ${b}`)+e.gray(` \u2014 ${S.description}`)+d)}}console.log(e.gray(`
31
+ `,P=m.includes("\x1B[200~")||m.includes("\x1B[201~"),J=m.length>=16&&/\s/.test(m)&&!m.startsWith("\x1B");($||P||J)&&(Mt=Date.now()+1200,Q=Date.now()+1200,Kt(),Ot())},N={query:"",matches:[],selected:0},Y={prefix:"",matches:[],nextIndex:0},x={active:!1,prefix:"",lines:[],timer:null},Nt=[];let qt=0,Bt="";const ye=new Set(Yt.map(u=>u.cmd));function ft(u,{preserveFrame:i=!1}={}){const m=String(u||"");f.line=m,f.cursor=m.length,Bt=m,typeof f._refreshLine=="function"?f._refreshLine():f.prompt(),i&&r.touchInputFrame()}function Zt(){Y.prefix="",Y.matches=[],Y.nextIndex=0}function vt(){if(x.timer&&(clearTimeout(x.timer),x.timer=null),x.active&&x.lines.length>0){qt+=1;const u=x.lines.length,i=`[Pasted text #${qt} +${u} lines]`,m=x.lines.join(`
32
+ `);Nt.push({placeholder:i,text:m});const $=String(x.prefix||"").trimEnd(),P=$?`${$} ${i}`:i;ft(P,{preserveFrame:!0}),mt(f.line)}x.active=!1,x.prefix="",x.lines=[],Q=0}function Ot(){x.timer&&clearTimeout(x.timer),x.timer=setTimeout(()=>{vt()},180)}function Kt(){x.active||(x.active=!0,x.prefix=String(Bt||f.line||""),x.lines=[],ft(x.prefix,{preserveFrame:!0}))}function Rt(u){return x.active?(x.lines.push(String(u||"")),ft(x.prefix,{preserveFrame:!0}),Ot(),!0):!1}function we(u){let i=String(u||"");for(const m of Nt)i=i.split(m.placeholder).join(m.text);return i}function mt(u){const i=String(u||"");if(!i.startsWith("/")||i.includes(" ")||i.length===0){N.query="",N.matches=[],N.selected=0,r.clearCommandDropdown();return}l=ue(t),w=de(l);const m=i.slice(1).toLowerCase(),$=l.filter(P=>P.toLowerCase().startsWith(m));N.query=m,N.matches=$,N.selected=Math.min(N.selected,Math.max(0,$.length-1)),r.showCommandDropdown($,N.selected)}const Ut=(u,i)=>{if(i?.name==="escape"||i?.sequence==="\x1B"){B&&B.abort(),Gt();return}if((i?.name==="return"||i?.name==="enter")&&(pt=!0),(i?.name==="backspace"||i?.name==="delete")&&r.touchInputFrame(),i?.name==="tab"&&!i?.shift){const P=String(f.line||""),J=Number(f.cursor||P.length);if(!ls(P,J)){Zt(),mt(f.line);return}const tt=P.slice(1).toLowerCase();if(Y.prefix!==tt&&(Y.prefix=tt,Y.matches=w.map(Z=>Z.cmd).filter(Z=>Z.toLowerCase().startsWith(`/${tt}`)),Y.nextIndex=0),Y.matches.length===0)return;const A=Y.nextIndex%Y.matches.length;Y.nextIndex+=1,ft(Y.matches[A]),mt(f.line);return}const m=String(f.line||""),$=m.startsWith("/")&&!m.includes(" ")&&N.matches.length>0;if(i?.name==="up"||i?.name==="down"){if(!$){mt(f.line);return}const P=i.name==="up"?-1:1,J=N.matches.length;N.selected=(N.selected+P+J)%J;const tt=N.matches[N.selected];ft(`/${tt}`,{preserveFrame:!0}),r.showCommandDropdown(N.matches,N.selected);return}i?.name!=="tab"&&Zt(),setTimeout(()=>{mt(f.line),x.active||(Bt=String(f.line||""))},50)};process.stdin?.on&&(Ae(process.stdin,f),process.stdin.on("keypress",Ut)),r.enabled&&process.stdin?.on&&(typeof process.stdin.prependListener=="function"?process.stdin.prependListener("data",xt):process.stdin.on("data",xt)),f.on("line",async u=>{try{let i=String(u||"");const m=pt;if(pt=!1,!m&&(x.active||X||Date.now()<Mt||Date.now()<Q)){if(x.active&&i.length>0){Rt(i);return}x.active&&(vt(),r.refreshPrompt());return}if(x.active&&(vt(),i=String(f.line||i||"")),Rt(i))return;const $=i.trim(),P=N.matches[N.selected];if($.startsWith("/")&&!$.includes(" ")&&N.matches.length>0&&!ye.has($)&&$!==`/${P}`){r.clearCommandDropdown(),r.refreshPrompt(),ft(`/${P}`),mt(`/${P}`);return}r.clearCommandDropdown();const A=String(i||"").replace(/[\u0000-\u001F\u007F-\u009F]/g,"").trim();if(!A){r.enabled&&process.stdout?.isTTY&&(M(process.stdout,0,-1),O(process.stdout,4));return}if(lt&&A!=="/exit"&&A!=="/quit"){B&&B.abort(),ot.push(A),r.pushSystem(e.gray("Processing your message...")),r.refreshPrompt({preserveInput:!0});return}const Z=1e4;if(A.length>Z){r.pushSystem(e.red(`\u26A0 Input too long (${A.length} chars). Maximum: ${Z} characters.`)),r.pushSystem(e.gray("Tip: If you need to share logs, use a file instead:")),r.pushSystem(e.gray(" 1. Save to file: pbpaste > debug.log")),r.pushSystem(e.gray(' 2. Ask: "analyze debug.log in current directory"')),r.refreshPrompt();return}if(A==="/exit"||A==="/quit"){Lt();return}if(A==="/help"){fs(),r.refreshPrompt();return}if(A==="/skills"){const _=$t("skill-installer")?.catalog||{},g=L.filter(b=>b!=="skill-installer");console.log(e.cyan(`
33
+ Active skills:`)),g.length===0&&console.log(e.gray(" (none)"));for(const b of g){const S=_[b]||{};console.log(e.green(` \u2713 ${b}`)+e.gray(S.description?` \u2014 ${S.description}`:""))}const T=Object.keys(_).filter(b=>!L.includes(b));if(T.length>0){console.log(e.cyan(`
34
+ Available:`));for(const b of T){const S=_[b],d=S.envKeys?.length>0?ms(S.envKeys).ok?e.green(" \u2713 configured"):e.yellow(` \u26A0 needs: ${S.envKeys.join(", ")}`):"";console.log(e.white(` - ${b}`)+e.gray(` \u2014 ${S.description}`)+d)}}console.log(e.gray(`
35
35
  Just ask to install: "connect to Jira", "add GitHub", etc.
36
- `)),n.refreshPrompt();return}if(A.startsWith("/history")){if(E.length===0)console.log(e.gray(`
36
+ `)),r.refreshPrompt();return}if(A.startsWith("/history")){if(E.length===0)console.log(e.gray(`
37
37
  No conversation history.
38
- `));else{const a=A.split(/\s+/).slice(1),x=a.includes("--all"),g=a.indexOf("-n"),T=g>=0&&a[g+1]?parseInt(a[g+1],10):NaN,b=x?E.length:isNaN(T)?10:T,S=E.slice(-b);console.log(e.gray(`
39
- Showing ${S.length} of ${E.length} messages${x?" (all)":""}:
40
- `));for(const d of S){const p=d.role==="human"?e.green(" You"):e.cyan(" Zibby"),j=x||T>10?500:100,D=d.content.length>j?`${d.content.substring(0,j)}...`:d.content;console.log(`${p}: ${e.white(D)}`)}console.log("")}n.refreshPrompt();return}if(A==="/clear"){E.length=0,Pt(t,E),console.log(e.gray(`
38
+ `));else{const a=A.split(/\s+/).slice(1),_=a.includes("--all"),g=a.indexOf("-n"),T=g>=0&&a[g+1]?parseInt(a[g+1],10):NaN,b=_?E.length:isNaN(T)?10:T,S=E.slice(-b);console.log(e.gray(`
39
+ Showing ${S.length} of ${E.length} messages${_?" (all)":""}:
40
+ `));for(const d of S){const p=d.role==="human"?e.green(" You"):e.cyan(" Zibby"),j=_||T>10?500:100,D=d.content.length>j?`${d.content.substring(0,j)}...`:d.content;console.log(`${p}: ${e.white(D)}`)}console.log("")}r.refreshPrompt();return}if(A==="/clear"){E.length=0,Pt(t,E),console.log(e.gray(`
41
41
  History cleared.
42
- `)),n.refreshPrompt();return}if(A.startsWith("/memory")){const a=L.includes("chat-memory")?kt("chat-memory"):null;if(!a?.handleToolCall){console.log(e.yellow(`
42
+ `)),r.refreshPrompt();return}if(A.startsWith("/memory")){const a=L.includes("chat-memory")?$t("chat-memory"):null;if(!a?.handleToolCall){console.log(e.yellow(`
43
43
  Chat memory not active. Install with: "add chat memory"
44
- `)),n.refreshPrompt();return}const x=A.split(/\s+/).slice(1),g=x[0]||"brief",T={options:{workspace:t}};try{if(g==="facts"||g==="all"){const b=parseInt(x[1],10)||30,S=await a.handleToolCall("memory_recall",{limit:b},T),d=JSON.parse(S);if(console.log(e.cyan(`
44
+ `)),r.refreshPrompt();return}const _=A.split(/\s+/).slice(1),g=_[0]||"brief",T={options:{workspace:t}};try{if(g==="facts"||g==="all"){const b=parseInt(_[1],10)||30,S=await a.handleToolCall("memory_recall",{limit:b},T),d=JSON.parse(S);if(console.log(e.cyan(`
45
45
  Stored memories (${d.total}):
46
46
  `)),d.total===0)console.log(e.gray(` (empty \u2014 memories are saved as the agent learns things)
47
- `));else{for(const p of d.memories){const j=e.yellow(`[${p.category}]`),D=Number(p.relevance)<1?e.gray(` (${(Number(p.relevance)*100).toFixed(0)}%)`):"",bt=p.ticket_key?e.magenta(` ${p.ticket_key}`):"",St=p.source?e.gray(` via ${p.source}`):"";console.log(` ${j}${bt} ${e.white(p.content)}${D}${St}`)}console.log("")}}else if(g==="tasks"){const b=parseInt(x[1],10)||20,S=await a.handleToolCall("task_history",{limit:b},T),d=JSON.parse(S);if(console.log(e.cyan(`
47
+ `));else{for(const p of d.memories){const j=e.yellow(`[${p.category}]`),D=Number(p.relevance)<1?e.gray(` (${(Number(p.relevance)*100).toFixed(0)}%)`):"",St=p.ticket_key?e.magenta(` ${p.ticket_key}`):"",kt=p.source?e.gray(` via ${p.source}`):"";console.log(` ${j}${St} ${e.white(p.content)}${D}${kt}`)}console.log("")}}else if(g==="tasks"){const b=parseInt(_[1],10)||20,S=await a.handleToolCall("task_history",{limit:b},T),d=JSON.parse(S);if(console.log(e.cyan(`
48
48
  Task history (${d.total}):
49
49
  `)),d.total===0)console.log(e.gray(` (no tasks logged yet)
50
- `));else{for(const p of d.tasks){const j=p.status==="passed"?e.green("\u2713"):p.status==="failed"?e.red("\u2717"):e.yellow("\u25CB"),D=p.ticket_key?e.magenta(` ${p.ticket_key}`):"",bt=e.gray(`[${p.type}]`),St=p.result_summary?e.gray(` \u2014 ${p.result_summary.slice(0,80)}`):"";console.log(` ${j} ${bt}${D} ${e.white(p.title)}${St}`)}console.log("")}}else if(g==="sessions"){const b=await a.handleToolCall("memory_brief",{},T),S=JSON.parse(b);if(console.log(e.cyan(`
50
+ `));else{for(const p of d.tasks){const j=p.status==="passed"?e.green("\u2713"):p.status==="failed"?e.red("\u2717"):e.yellow("\u25CB"),D=p.ticket_key?e.magenta(` ${p.ticket_key}`):"",St=e.gray(`[${p.type}]`),kt=p.result_summary?e.gray(` \u2014 ${p.result_summary.slice(0,80)}`):"";console.log(` ${j} ${St}${D} ${e.white(p.title)}${kt}`)}console.log("")}}else if(g==="sessions"){const b=await a.handleToolCall("memory_brief",{},T),S=JSON.parse(b);if(console.log(e.cyan(`
51
51
  Recent sessions (${S.recentSessions?.length||0}):
52
52
  `)),!S.recentSessions?.length)console.log(e.gray(` (no sessions saved yet)
53
- `));else{for(const d of S.recentSessions){const p=d.tickets?e.magenta(` [${d.tickets}]`):"",j=d.tasks_run>0?e.gray(` \u2014 ${d.tasks_run} tasks (${d.tasks_passed} passed, ${d.tasks_failed} failed)`):"",D=d.created_at?e.gray(` ${d.created_at.split("T")[0]}`):"";if(console.log(` ${e.white(d.summary)}${p}${j}${D}`),d.key_facts)for(const bt of d.key_facts.split(";").map(St=>St.trim()).filter(Boolean))console.log(e.gray(` \u2192 ${bt}`))}console.log("")}}else if(g==="search"){const b=x.slice(1).join(" ");if(!b)console.log(e.yellow(`
53
+ `));else{for(const d of S.recentSessions){const p=d.tickets?e.magenta(` [${d.tickets}]`):"",j=d.tasks_run>0?e.gray(` \u2014 ${d.tasks_run} tasks (${d.tasks_passed} passed, ${d.tasks_failed} failed)`):"",D=d.created_at?e.gray(` ${d.created_at.split("T")[0]}`):"";if(console.log(` ${e.white(d.summary)}${p}${j}${D}`),d.key_facts)for(const St of d.key_facts.split(";").map(kt=>kt.trim()).filter(Boolean))console.log(e.gray(` \u2192 ${St}`))}console.log("")}}else if(g==="search"){const b=_.slice(1).join(" ");if(!b)console.log(e.yellow(`
54
54
  Usage: /memory search <keyword>
55
55
  `));else{const S=await a.handleToolCall("memory_recall",{query:b,limit:20},T),d=JSON.parse(S);console.log(e.cyan(`
56
56
  Search "${b}" \u2014 ${d.total} results:
@@ -59,11 +59,11 @@ ${c}
59
59
  `)),console.log(e.white(` Facts stored: ${d>0?e.cyan(d):e.gray("0")}`)),console.log(e.white(` Sessions saved: ${p>0?e.cyan(p):e.gray("0")}`)),j&&console.log(e.white(` Task history: ${e.gray(j)}`)),console.log(e.gray(`
60
60
  Subcommands:`)),console.log(e.gray(" /memory facts [n] \u2014 List all stored memories")),console.log(e.gray(" /memory tasks [n] \u2014 List task history")),console.log(e.gray(" /memory sessions \u2014 List session summaries")),console.log(e.gray(" /memory search <keyword> \u2014 Search memories")),console.log("")}}catch(b){console.log(e.red(`
61
61
  Memory error: ${b.message}
62
- `))}n.refreshPrompt();return}n.enabled&&process.stdout?.isTTY&&(M(process.stdout,0,-1),O(process.stdout,0)),n.pushUser(A),n.refreshPrompt(),n.showAssistantLoading();const Vt=we(A),Tt=es(t,Vt);if(Bt.length=0,!String(Tt||"").trim()){n.dismissTransientLoading(),n.refreshPrompt();return}if(Tt.length>G){n.pushAssistant(e.red(`Input too long after command expansion (${Tt.length} chars).`)),n.pushSystem(e.gray(`Try shortening the command template or user text. Limit: ${G} chars.`)),n.refreshPrompt();return}at=!0;let K=v.systemPrompt||"You are Zibby, a helpful AI assistant.";const Xt=rs(v,o);Xt&&(K+=`
62
+ `))}r.refreshPrompt();return}r.enabled&&process.stdout?.isTTY&&(M(process.stdout,0,-1),O(process.stdout,0)),r.pushUser(A),r.refreshPrompt(),r.showAssistantLoading();const Vt=we(A),Tt=ss(t,Vt);if(Nt.length=0,!String(Tt||"").trim()){r.dismissTransientLoading(),r.refreshPrompt();return}if(Tt.length>Z){r.pushAssistant(e.red(`Input too long after command expansion (${Tt.length} chars).`)),r.pushSystem(e.gray(`Try shortening the command template or user text. Limit: ${Z} chars.`)),r.refreshPrompt();return}lt=!0;let K=v.systemPrompt||"You are Zibby, a helpful AI assistant.";const Xt=is(v,n);Xt&&(K+=`
63
63
 
64
- ${Xt}`);const Qt=He({activeSkills:L,chatConfig:v,options:o});Qt&&(K+=`
64
+ ${Xt}`);const Qt=ze({activeSkills:L,chatConfig:v,options:n});Qt&&(K+=`
65
65
 
66
- ${Qt}`);const te=ns(L);if(te&&(K+=`
66
+ ${Qt}`);const te=rs(L);if(te&&(K+=`
67
67
 
68
68
  ${te}`),K+=`
69
69
 
@@ -82,8 +82,8 @@ Use provided tools when external data/actions are required. Do not claim actions
82
82
  - Conversation flow: ask about purpose \u2192 input data \u2192 processing steps \u2192 output format \u2192 conditional logic.
83
83
  - Then call design_workflow to create a spec, review with user, then build_workflow to generate code.
84
84
  - After building: remind user about \`zibby start <name>\` (test), \`zibby deploy <name>\` (deploy), \`zibby logs\` (logs).
85
- - Use list_workflows to show existing workflows, add_node to extend them, deploy_workflow to ship.`),L.includes("chat-memory"))try{const a=kt("chat-memory");if(typeof a?.buildPromptContext=="function"){const x=Date.now();let g;et.data&&x-et.timestamp<F?(g=et.data,process.env.ZIBBY_VERBOSE==="true"&&n.pushSystem(e.gray("Using cached memory context"))):(g=await a.buildPromptContext({options:{workspace:t}},{}),et={data:g,timestamp:x},process.env.ZIBBY_VERBOSE==="true"&&n.pushSystem(e.gray("Refreshed memory context cache")));const T=String(g?.backend||"dolt");process.env.ZIBBY_VERBOSE==="true"&&(n.pushSystem(e.gray(`memory backend: ${T}`)),n.pushSystem(e.gray(`memory retrieval output: ${Et(JSON.stringify(g?.debugPreview||{}),1400)}`)),g?.error&&n.pushSystem(e.yellow(`memory backend warning: ${g.error}`))),g?.promptContext&&(K+=`
85
+ - Use list_workflows to show existing workflows, add_node to extend them, deploy_workflow to ship.`),L.includes("chat-memory"))try{const a=$t("chat-memory");if(typeof a?.buildPromptContext=="function"){const _=Date.now();let g;et.data&&_-et.timestamp<F?(g=et.data,process.env.ZIBBY_VERBOSE==="true"&&r.pushSystem(e.gray("Using cached memory context"))):(g=await a.buildPromptContext({options:{workspace:t}},{}),et={data:g,timestamp:_},process.env.ZIBBY_VERBOSE==="true"&&r.pushSystem(e.gray("Refreshed memory context cache")));const T=String(g?.backend||"dolt");process.env.ZIBBY_VERBOSE==="true"&&(r.pushSystem(e.gray(`memory backend: ${T}`)),r.pushSystem(e.gray(`memory retrieval output: ${Et(JSON.stringify(g?.debugPreview||{}),1400)}`)),g?.error&&r.pushSystem(e.yellow(`memory backend warning: ${g.error}`))),g?.promptContext&&(K+=`
86
86
 
87
- ${g.promptContext}`)}}catch(a){process.env.ZIBBY_VERBOSE==="true"&&n.pushSystem(e.yellow(`memory backend warning: ${String(a?.message||a)}`))}K=xt(K,ce);const Se=ss(E),Ft=os([{role:"system",content:K},...Se,{role:"user",content:Tt}]),ee=Ht(Ft),ke=Buffer.byteLength(JSON.stringify(Ft),"utf8");process.env.ZIBBY_VERBOSE==="true"&&n.pushSystem(e.gray(`payload estimate: ${ee} chars, ${ke} bytes (~${Math.round(ee/4)} tokens)`)),E.push({role:"human",content:Vt});const yt=n.enabled?(()=>{const a=()=>{};return a.setLabel=()=>{},a})():le();let ct=!0;const Ct=[];let se="",wt="";const gs=50,hs=500,ys=3,ws=60,bs=500,Ss=15,ks=25,$s=4,xs=new Set([".","!","?",",",";",":"]),$e=a=>{ct&&(yt(),ct=!1),wt+=a},xe=(a,x)=>{if(!a){ct&&yt.setLabel("thinking...");return}Ct.push(a);const g=70;let T=a;if(x&&typeof x=="object"){const S=Object.values(x).map(p=>typeof p=="string"?p:JSON.stringify(p)).join(", "),d=`${a}(${S})`;d.length<=g?T=d:T=`${a}(${S.slice(0,g-a.length-4)}...)`}T!==se&&(se=T,n.pushSystem(e.gray(`\u23BF ${T}`)),n.refreshPrompt({preserveInput:!0}),ct&&n.showAssistantLoading()),ct&&yt.setLabel(T)},jt=new AbortController;N=jt;try{const a=await _e("",{state:{agentType:C,config:k,cwd:t,workspace:t}},{model:k?.agent?.assistant?.model||v.model||"auto",workspace:t,skills:L,activeSkills:L,config:k,timeout:v.timeout||3e5,messages:Ft,skipPromptFragments:!0,stream:!0,onToken:$e,onToolCall:xe,signal:jt.signal});ct&&yt();const x=typeof a=="string"?a:a?.structured?JSON.stringify(a.structured,null,2):a?.raw||String(a),g=wt&&wt.trim().length>0?wt.trim():x.trim(),T=is(g,Ct)?g:cs(g);n.pushAssistant(T);const b=Ct.length>0?`[tools used: ${Ct.join(", ")}]
88
- ${g}`:g;E.push({role:"ai",content:b}),Pt(t,E),It(t,L)}catch(a){if(ct&&yt(),n.dismissTransientLoading(),jt.signal.aborted){ot.length===0&&n.pushAssistant(e.gray("[cancelled]"));let x="";try{const{testRunnerSkill:T}=await import("../../skills/index.js"),b=await T.handleToolCall("run_status",{runId:"all"},{options:{workspace:t}}),S=JSON.parse(b);S.runs?.length>0&&(x=`
89
- [Active test runs: ${S.runs.map(p=>`${p.runId}: ${p.ticketKey||p.spec} (${p.status})`).join(", ")}. Use run_status("all") to check progress.]`)}catch{}const g=wt.trim();g?E.push({role:"ai",content:g+x}):E.push({role:"ai",content:`[interrupted \u2014 new message]${x}`})}else{const x=String(a?.message||a||"Unknown error");/413|payload too large|request payload too large/i.test(x)?(n.pushAssistant(e.yellow("Request became too large for the proxy (413).")),n.pushSystem(e.gray("Try: /clear, then retry your command; or shorten command/context."))):n.pushAssistant(e.red(`Error: ${x}`)),E.push({role:"ai",content:`[Error: ${a.message}]`})}}finally{N=null,at=!1}if(n.refreshPrompt(),ot.length>0){const a=ot.shift();ut=!0,setImmediate(()=>f.emit("line",a))}}catch(i){n.pushSystem(e.red(`Input handling error: ${i?.message||String(i)}`)),n.refreshPrompt()}}),f.on("SIGINT",()=>{if(N){N.abort();return}Lt()}),f.on("close",Lt);const be=()=>{process.stdin?.off?(process.stdin.off("data",_t),process.stdin.off("keypress",Ut)):process.stdin?.removeListener&&(process.stdin.removeListener("data",_t),process.stdin.removeListener("keypress",Ut))};f.on("close",be)})}export{Rs as _buildPrompt,Xe as _checkEnvKeys,Ve as _formatHistory,Os as _inferAgentType,Fs as chatCommand};
87
+ ${g.promptContext}`)}}catch(a){process.env.ZIBBY_VERBOSE==="true"&&r.pushSystem(e.yellow(`memory backend warning: ${String(a?.message||a)}`))}K=_t(K,ce);const Se=os(E),Ft=ns([{role:"system",content:K},...Se,{role:"user",content:Tt}]),ee=Ht(Ft),ke=Buffer.byteLength(JSON.stringify(Ft),"utf8");process.env.ZIBBY_VERBOSE==="true"&&r.pushSystem(e.gray(`payload estimate: ${ee} chars, ${ke} bytes (~${Math.round(ee/4)} tokens)`)),E.push({role:"human",content:Vt});const wt=r.enabled?(()=>{const a=()=>{};return a.setLabel=()=>{},a})():le();let ct=!0;const Ct=[];let se="",bt="";const hs=50,ys=500,ws=3,bs=60,Ss=500,ks=15,$s=25,_s=4,xs=new Set([".","!","?",",",";",":"]),$e=a=>{ct&&(wt(),ct=!1),bt+=a},_e=(a,_)=>{if(!a){ct&&wt.setLabel("thinking...");return}Ct.push(a);const g=70;let T=a;if(_&&typeof _=="object"){const S=Object.values(_).map(p=>typeof p=="string"?p:JSON.stringify(p)).join(", "),d=`${a}(${S})`;d.length<=g?T=d:T=`${a}(${S.slice(0,g-a.length-4)}...)`}T!==se&&(se=T,r.pushSystem(e.gray(`\u23BF ${T}`)),r.refreshPrompt({preserveInput:!0}),ct&&r.showAssistantLoading()),ct&&wt.setLabel(T)},jt=new AbortController;B=jt;try{const a=await xe("",{state:{agentType:C,config:k,cwd:t,workspace:t}},{model:k?.agent?.assistant?.model||v.model||"auto",workspace:t,skills:L,activeSkills:L,config:k,timeout:v.timeout||3e5,messages:Ft,skipPromptFragments:!0,stream:!0,onToken:$e,onToolCall:_e,signal:jt.signal});ct&&wt();const _=typeof a=="string"?a:a?.structured?JSON.stringify(a.structured,null,2):a?.raw||String(a),g=bt&&bt.trim().length>0?bt.trim():_.trim(),T=cs(g,Ct)?g:as(g);r.pushAssistant(T);const b=Ct.length>0?`[tools used: ${Ct.join(", ")}]
88
+ ${g}`:g;E.push({role:"ai",content:b}),Pt(t,E),It(t,L)}catch(a){if(ct&&wt(),r.dismissTransientLoading(),jt.signal.aborted){ot.length===0&&r.pushAssistant(e.gray("[cancelled]"));let _="";try{const{testRunnerSkill:T}=await import("../../skills/index.js"),b=await T.handleToolCall("run_status",{runId:"all"},{options:{workspace:t}}),S=JSON.parse(b);S.runs?.length>0&&(_=`
89
+ [Active test runs: ${S.runs.map(p=>`${p.runId}: ${p.ticketKey||p.spec} (${p.status})`).join(", ")}. Use run_status("all") to check progress.]`)}catch{}const g=bt.trim();g?E.push({role:"ai",content:g+_}):E.push({role:"ai",content:`[interrupted \u2014 new message]${_}`})}else{const _=String(a?.message||a||"Unknown error");/413|payload too large|request payload too large/i.test(_)?(r.pushAssistant(e.yellow("Request became too large for the proxy (413).")),r.pushSystem(e.gray("Try: /clear, then retry your command; or shorten command/context."))):r.pushAssistant(e.red(`Error: ${_}`)),E.push({role:"ai",content:`[Error: ${a.message}]`})}}finally{B=null,lt=!1}if(r.refreshPrompt(),ot.length>0){const a=ot.shift();pt=!0,setImmediate(()=>f.emit("line",a))}}catch(i){r.pushSystem(e.red(`Input handling error: ${i?.message||String(i)}`)),r.refreshPrompt()}}),f.on("SIGINT",()=>{if(B){B.abort();return}Lt()}),f.on("close",Lt);const be=()=>{process.stdin?.off?(process.stdin.off("data",xt),process.stdin.off("keypress",Ut)):process.stdin?.removeListener&&(process.stdin.removeListener("data",xt),process.stdin.removeListener("keypress",Ut))};f.on("close",be)})}export{Fs as _buildPrompt,Qe as _checkEnvKeys,Xe as _formatHistory,Us as _inferAgentType,Ds as chatCommand};
@@ -1,4 +1,5 @@
1
- import{mkdir as E,writeFile as g,readFile as w}from"fs/promises";import{existsSync as f,readdirSync as Z}from"fs";import{join as i,resolve as L,dirname as N}from"path";import{homedir as C}from"os";import D from"inquirer";import e from"chalk";import v from"ora";import{spawn as S,execSync as Y}from"child_process";import{fileURLToPath as G}from"url";import{createRequire as O}from"module";const H=G(import.meta.url),F=N(H),j=O(import.meta.url);async function W(){try{const d=process.platform==="win32",n=Y("npm config get prefix",{encoding:"utf-8"}).trim(),y=d?n:`${n}/bin`,m=y.replace(/^~/,C()),a=d?";":":";if(process.env.PATH.split(a).some(I=>{const z=I.replace(/^~/,C());return z===m||z===y}))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 D.prompt([{type:"confirm",name:"shouldAddPath",message:"Add npm global bin to your shell PATH automatically?",default:!0}]);if(!s){d?(console.log(e.gray(`
1
+ import{mkdir as _,writeFile as g,readFile as w}from"fs/promises";import{existsSync as f,readdirSync as j}from"fs";import{join as i,resolve as L,dirname as N}from"path";import{homedir as C}from"os";import D from"inquirer";import e from"chalk";import v from"ora";import{spawn as S,execSync as T}from"child_process";import{fileURLToPath as H}from"url";import{createRequire as O}from"module";const G=H(import.meta.url),F=N(G),Z=O(import.meta.url);async function W(){try{const d=process.platform==="win32",n=T("npm config get prefix",{encoding:"utf-8"}).trim(),y=d?n:`${n}/bin`,m=y.replace(/^~/,C()),a=d?";":":";if(process.env.PATH.split(a).some(I=>{const z=I.replace(/^~/,C());return z===m||z===y}))return;if(process.env.CI||process.env.ZIBBY_CI||!process.stdin.isTTY){console.log(e.yellow("\u26A0\uFE0F npm global bin not in PATH")),console.log(e.gray(` Location: ${m}`)),console.log(e.gray(` Add it manually: export PATH="${m}:$PATH"
2
+ `));return}console.log(e.yellow("\u26A0\uFE0F npm global bin not in PATH")),console.log(e.gray(` Location: ${m}`)),console.log();const{shouldAddPath:s}=await D.prompt([{type:"confirm",name:"shouldAddPath",message:"Add npm global bin to your shell PATH automatically?",default:!0}]);if(!s){d?(console.log(e.gray(`
2
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}
3
4
  `))):(console.log(e.gray(`
4
5
  \u{1F4A1} To add manually, run:`)),console.log(e.gray(` echo 'export PATH="${m}:$PATH"' >> ~/.zshrc`)),console.log(e.gray(` source ~/.zshrc
@@ -11,7 +12,7 @@ export PATH="${m}:$PATH"
11
12
  \u{1F4A1} Run this to activate in current session:`)),console.log(e.gray(` source ${p}
12
13
  `))}catch{}}async function ue(d,n){console.log(e.bold.cyan(`
13
14
  \u{1F3AD} Welcome to Zibby Test Automation!
14
- `));const y=!n.skipMemory,m=["dolt","mem0"].includes(String(n.memoryBackend||"").toLowerCase())?String(n.memoryBackend).toLowerCase():"dolt";await W();const a=d?L(process.cwd(),d):process.cwd(),_=d||"zibby-tests",h=!!d;h&&f(a)&&(console.log(e.red(`
15
+ `));const y=!n.skipMemory,m=["dolt","mem0"].includes(String(n.memoryBackend||"").toLowerCase())?String(n.memoryBackend).toLowerCase():"dolt";await W();const a=d?L(process.cwd(),d):process.cwd(),E=d||"zibby-tests",h=!!d;h&&f(a)&&(console.log(e.red(`
15
16
  \u274C Directory "${d}" already exists!
16
17
  `)),process.exit(1)),!h&&f(i(a,".zibby.config.mjs"))&&!n.force&&(console.log(e.yellow(`
17
18
  \u26A0\uFE0F Zibby is already initialized in this directory!
@@ -19,21 +20,21 @@ export PATH="${m}:$PATH"
19
20
  `)),process.exit(0)),n.force&&!h&&console.log(e.cyan(`
20
21
  Reinitializing Zibby configuration...
21
22
  `));let s;if(n.agent&&(n.headed||n.headless))console.log(e.cyan(`Setting up with provided options...
22
- `)),s={agent:n.agent,browserMode:n.headless?"headless":"headed",apiKey:n.apiKey||null,cloudSync:!!(n.cloudSync||n.apiKey)};else{const b=[];n.agent||b.push({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"}),!n.headed&&!n.headless&&b.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"}),n.apiKey||b.push({type:"input",name:"apiKey",message:"Enable cloud sync? Enter project ZIBBY_API_KEY (or press Enter to skip):"}),s=b.length>0?await D.prompt(b):{},s.agent=n.agent||s.agent,s.browserMode=n.headless?"headless":n.headed?"headed":s.browserMode,s.apiKey=n.apiKey||s.apiKey,s.cloudSync=!!(n.cloudSync||n.apiKey||s.apiKey&&s.apiKey.trim())}s.mcp="playwright",s.setupMcp=s.agent==="cursor";const p=v("Setting up Zibby...").start();try{if(h&&await E(a,{recursive:!0}),await E(i(a,"test-specs/examples"),{recursive:!0}),await E(i(a,"tests"),{recursive:!0}),await E(i(a,".zibby/output"),{recursive:!0}),await E(i(a,".zibby/commands"),{recursive:!0}),y&&m==="dolt")try{const{initMemory:t,DoltDB:r}=await import("@zibby/memory");if(r.isAvailable()){const{created:o}=t(a);o&&(p.text="Initialized test memory database (Dolt)...")}else p.text="Dolt not found \u2014 skipping memory database (brew install dolt)"}catch{}p.text="Scaffolding workflow graph...";const{TemplateFactory:b}=await import("@zibby/core/templates"),I=n.template||"browser-test-automation";try{const{graphPath:t,nodesPath:r,readmePath:o,resultHandlerPath:c,template:l}=b.getTemplateFiles(I),u=i(a,".zibby"),k=await w(t,"utf-8");if(await g(i(u,"graph.mjs"),k),c){const A=await w(c,"utf-8");await g(i(u,"result-handler.mjs"),A)}const B=await w(o,"utf-8");await g(i(u,"README.md"),B),await E(i(u,"nodes"),{recursive:!0});const{readdirSync:P}=await import("fs"),$=P(r);for(const A of $){let K=await w(i(r,A),"utf-8");!y&&A==="execute-live.mjs"&&(K=K.replace("skills: [SKILLS.BROWSER, SKILLS.MEMORY],","skills: [SKILLS.BROWSER],")),await g(i(u,"nodes",A),K)}const T=i(l.path,"chat.mjs");if(f(T)){const A=await w(T,"utf-8");await g(i(u,"chat.mjs"),A)}}catch(t){throw p.fail(`Failed to scaffold template: ${t.message}`),t}p.text="Generating configuration files...";const z=U(s,n,{memoryBackend:m});if(await g(i(a,".zibby.config.mjs"),z),await g(i(a,".env.example"),V(s,m)),s.apiKey&&s.apiKey.trim()){const t=i(a,".env"),r=s.apiKey.trim();if(f(t)){let o=await w(t,"utf8");/^ZIBBY_API_KEY=/m.test(o)?o=o.replace(/^ZIBBY_API_KEY=.*/m,`ZIBBY_API_KEY=${r}`):/^#\s*ZIBBY_API_KEY=/m.test(o)?o=o.replace(/^#\s*ZIBBY_API_KEY=.*/m,`ZIBBY_API_KEY=${r}`):o=`${o.trimEnd()}
23
+ `)),s={agent:n.agent,browserMode:n.headless?"headless":"headed",apiKey:n.apiKey||null,cloudSync:!!(n.cloudSync||n.apiKey)};else{const b=[];n.agent||b.push({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"}),!n.headed&&!n.headless&&b.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"}),n.apiKey||b.push({type:"input",name:"apiKey",message:"Enable cloud sync? Enter project ZIBBY_API_KEY (or press Enter to skip):"}),s=b.length>0?await D.prompt(b):{},s.agent=n.agent||s.agent,s.browserMode=n.headless?"headless":n.headed?"headed":s.browserMode,s.apiKey=n.apiKey||s.apiKey,s.cloudSync=!!(n.cloudSync||n.apiKey||s.apiKey&&s.apiKey.trim())}s.mcp="playwright",s.setupMcp=s.agent==="cursor";const p=v("Setting up Zibby...").start();try{if(h&&await _(a,{recursive:!0}),await _(i(a,"test-specs/examples"),{recursive:!0}),await _(i(a,"tests"),{recursive:!0}),await _(i(a,".zibby/output"),{recursive:!0}),await _(i(a,".zibby/commands"),{recursive:!0}),y&&m==="dolt")try{const{initMemory:t,DoltDB:r}=await import("@zibby/memory");if(r.isAvailable()){const{created:o}=t(a);o&&(p.text="Initialized test memory database (Dolt)...")}else p.text="Dolt not found \u2014 skipping memory database (brew install dolt)"}catch{}p.text="Scaffolding workflow graph...";const{TemplateFactory:b}=await import("@zibby/core/templates"),I=n.template||"browser-test-automation";try{const{graphPath:t,nodesPath:r,readmePath:o,resultHandlerPath:c,template:l}=b.getTemplateFiles(I),u=i(a,".zibby"),k=await w(t,"utf-8");if(await g(i(u,"graph.mjs"),k),c){const A=await w(c,"utf-8");await g(i(u,"result-handler.mjs"),A)}const M=await w(o,"utf-8");await g(i(u,"README.md"),M),await _(i(u,"nodes"),{recursive:!0});const{readdirSync:P}=await import("fs"),$=P(r);for(const A of $){let K=await w(i(r,A),"utf-8");!y&&A==="execute-live.mjs"&&(K=K.replace("skills: [SKILLS.BROWSER, SKILLS.MEMORY],","skills: [SKILLS.BROWSER],")),await g(i(u,"nodes",A),K)}const R=i(l.path,"chat.mjs");if(f(R)){const A=await w(R,"utf-8");await g(i(u,"chat.mjs"),A)}}catch(t){throw p.fail(`Failed to scaffold template: ${t.message}`),t}p.text="Generating configuration files...";const z=U(s,n,{memoryBackend:m});if(await g(i(a,".zibby.config.mjs"),z),await g(i(a,".env.example"),V(s,m)),s.apiKey&&s.apiKey.trim()){const t=i(a,".env"),r=s.apiKey.trim();if(f(t)){let o=await w(t,"utf8");/^ZIBBY_API_KEY=/m.test(o)?o=o.replace(/^ZIBBY_API_KEY=.*/m,`ZIBBY_API_KEY=${r}`):/^#\s*ZIBBY_API_KEY=/m.test(o)?o=o.replace(/^#\s*ZIBBY_API_KEY=.*/m,`ZIBBY_API_KEY=${r}`):o=`${o.trimEnd()}
23
24
 
24
25
  # Zibby Cloud Sync
25
26
  ZIBBY_API_KEY=${r}
26
- `,await g(t,o)}else await g(t,q(s,r,m))}if(h){const t=X(_,s,{memoryBackend:m});await g(i(a,"package.json"),t)}if(!f(i(a,".gitignore"))){const t=J();await g(i(a,".gitignore"),t)}if(!f(i(a,"playwright.config.js"))){const t=Q("on");await g(i(a,"playwright.config.js"),t)}if(!f(i(a,"test-specs/examples/example-domain.txt"))){const t=ee();await g(i(a,"test-specs/examples/example-domain.txt"),t)}const R=L(F,"../../../../examples/.zibby/commands");if(f(R)){const t=Z(R).filter(r=>r.toLowerCase().endsWith(".md"));for(const r of t){const o=i(a,".zibby/commands",r);if(n.force||!f(o)){const c=await w(i(R,r),"utf-8");await g(o,c)}}}if(!f(i(a,".zibby/commands/example.md"))){const t=te();await g(i(a,".zibby/commands/example.md"),t)}if(h){const t=ne(_,s);await g(i(a,"README.md"),t)}if(p.succeed(h?"Project created!":"Zibby initialized!"),h&&!n.skipInstall){const t=v("Installing dependencies...").start();await new Promise((r,o)=>{S("npm",["install"],{cwd:a,stdio:"pipe"}).on("close",l=>{l===0?(t.succeed("Dependencies installed!"),r()):(t.fail("Failed to install dependencies"),o(new Error("npm install failed")))})})}else if(!h){const t=["@zibby/cli","@zibby/core"],r=[];for(const o of t)try{O(i(a,"package.json")).resolve(`${o}/package.json`)}catch{r.push(o)}if(r.length>0){const o=j("../../package.json"),c=r.map(l=>l==="@zibby/cli"?`${l}@latest`:l==="@zibby/core"?`${l}@${o.dependencies?.["@zibby/core"]||"^0.1.29"}`:l);if(n.skipInstall)console.log(e.yellow(`
27
+ `,await g(t,o)}else await g(t,q(s,r,m))}if(h){const t=X(E,s,{memoryBackend:m});await g(i(a,"package.json"),t)}if(!f(i(a,".gitignore"))){const t=J();await g(i(a,".gitignore"),t)}if(!f(i(a,"playwright.config.js"))){const t=Q("on");await g(i(a,"playwright.config.js"),t)}if(!f(i(a,"test-specs/examples/example-domain.txt"))){const t=ee();await g(i(a,"test-specs/examples/example-domain.txt"),t)}const Y=L(F,"../../../../examples/.zibby/commands");if(f(Y)){const t=j(Y).filter(r=>r.toLowerCase().endsWith(".md"));for(const r of t){const o=i(a,".zibby/commands",r);if(n.force||!f(o)){const c=await w(i(Y,r),"utf-8");await g(o,c)}}}if(!f(i(a,".zibby/commands/example.md"))){const t=te();await g(i(a,".zibby/commands/example.md"),t)}if(h){const t=ne(E,s);await g(i(a,"README.md"),t)}if(p.succeed(h?"Project created!":"Zibby initialized!"),h&&!n.skipInstall){const t=v("Installing dependencies...").start();await new Promise((r,o)=>{S("npm",["install"],{cwd:a,stdio:"pipe"}).on("close",l=>{l===0?(t.succeed("Dependencies installed!"),r()):(t.fail("Failed to install dependencies"),o(new Error("npm install failed")))})})}else if(!h){const t=["@zibby/cli","@zibby/core"],r=[];for(const o of t)try{O(i(a,"package.json")).resolve(`${o}/package.json`)}catch{r.push(o)}if(r.length>0){const o=Z("../../package.json"),c=r.map(l=>l==="@zibby/cli"?`${l}@latest`:l==="@zibby/core"?`${l}@${o.dependencies?.["@zibby/core"]||"^0.1.29"}`:l);if(n.skipInstall)console.log(e.yellow(`
27
28
  Missing required Zibby dependencies in this project:`)),console.log(e.white(` ${r.join(", ")}`)),console.log(e.gray("Install them manually to avoid runtime errors:")),console.log(e.white(` npm install --save-dev ${c.join(" ")}
28
29
  `));else{const l=v(`Installing ${c.join(", ")}...`).start();await new Promise(k=>{S("npm",["install","--save-dev",...c],{cwd:a,stdio:"pipe"}).on("close",P=>k(P??1))})===0?l.succeed("Installed missing Zibby dependencies"):(l.warn("Could not auto-install Zibby dependencies"),console.log(e.gray("Run this manually:")),console.log(e.white(` npm install --save-dev ${c.join(" ")}
29
30
  `)))}}}if(!h&&y&&m==="mem0"&&(console.log(e.yellow(`
30
31
  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
31
32
  `))),!n.skipInstall){const t=v("Installing Playwright browsers...").start();await new Promise(r=>{const o=S("npx",["playwright","install","chromium"],{cwd:a,stdio:"pipe"});let c="";o.stdout.on("data",l=>{c+=l.toString()}),o.stderr.on("data",l=>{c+=l.toString()}),o.on("close",l=>{l===0?(c.includes("already installed")||c.includes("up to date")?t.succeed("Playwright browsers already installed"):t.succeed("Playwright browsers installed!"),r()):(t.warn("Could not verify Playwright browsers"),console.log(e.yellow(`
32
33
  \u26A0\uFE0F If tests fail, run: npx playwright install
33
- `)),r())})})}if(s.agent==="cursor"&&!n.skipInstall){const t=v("Checking cursor-agent CLI...").start();try{Y("cursor-agent --version",{encoding:"utf-8",timeout:5e3,stdio:"pipe"}),t.succeed("cursor-agent CLI already installed")}catch{t.text="Installing cursor-agent CLI...";try{await new Promise((r,o)=>{S("bash",["-c","curl https://cursor.com/install -fsS | bash"],{stdio:"pipe"}).on("close",l=>{if(l===0){const u=i(C(),".local","bin");process.env.PATH.includes(u)||(process.env.PATH=`${u}:${process.env.PATH}`),t.succeed("cursor-agent CLI installed!"),r()}else o(new Error("cursor-agent install failed"))})})}catch{t.fail("Could not install cursor-agent CLI"),console.log(e.yellow(` Install manually: curl https://cursor.com/install -fsS | bash
34
- `))}}}if(s.agent==="codex"&&!n.skipInstall){const t=v("Checking Codex CLI...").start();try{Y("codex --version",{encoding:"utf-8",timeout:5e3,stdio:"pipe"}),t.succeed("Codex CLI already installed")}catch{t.text="Installing Codex CLI...";try{await new Promise((r,o)=>{S("npm",["install","-g","@openai/codex"],{stdio:"pipe"}).on("close",l=>{l===0?(t.succeed("Codex CLI installed!"),r()):o(new Error("npm install -g @openai/codex failed"))})})}catch{t.fail("Could not install Codex CLI"),console.log(e.yellow(` Install manually: npm install -g @openai/codex
35
- `))}}}if(s.agent==="gemini"&&!n.skipInstall){const t=v("Checking Gemini CLI...").start();try{Y("gemini --version",{encoding:"utf-8",timeout:5e3,stdio:"pipe"}),t.succeed("Gemini CLI already installed")}catch{t.text="Installing Gemini CLI...";try{await new Promise((o,c)=>{S("npm",["install","-g","@google/gemini-cli"],{stdio:"pipe"}).on("close",u=>{u===0?(t.succeed("Gemini CLI installed!"),o()):c(new Error("npm install -g @google/gemini-cli failed"))})})}catch{t.fail("Could not install Gemini CLI"),console.log(e.yellow(` Install manually: npm install -g @google/gemini-cli
36
- `))}}const r=v("Configuring Gemini MCP servers...").start();try{const o=i(C(),".gemini"),c=i(o,"settings.json");f(o)||await E(o,{recursive:!0});let l={mcpServers:{}};if(f(c))try{const $=await w(c,"utf-8");l=JSON.parse($),l.mcpServers||(l.mcpServers={})}catch{}let u;try{const{createRequire:$}=await import("module");u=$(import.meta.url).resolve("@zibby/mcp-browser/bin/mcp-browser-zibby.js")}catch{u="@playwright/mcp"}const k=s.browserMode!=="headless",B=u==="@playwright/mcp"?["-y","@playwright/mcp","--isolated","--save-video=1280x720","--viewport-size=1280x720","--output-dir","test-results"]:[u,"--isolated","--save-video=1280x720","--viewport-size=1280x720","--output-dir=test-results"];k||B.push("--headless"),l.mcpServers["playwright-official"]={command:u==="@playwright/mcp"?"npx":"node",args:B},await g(c,`${JSON.stringify(l,null,2)}
34
+ `)),r())})})}if(s.agent==="cursor"&&!n.skipInstall){const t=v("Checking cursor-agent CLI...").start();try{T("cursor-agent --version",{encoding:"utf-8",timeout:5e3,stdio:"pipe"}),t.succeed("cursor-agent CLI already installed")}catch{t.text="Installing cursor-agent CLI...";try{await new Promise((r,o)=>{S("bash",["-c","curl https://cursor.com/install -fsS | bash"],{stdio:"pipe"}).on("close",l=>{if(l===0){const u=i(C(),".local","bin");process.env.PATH.includes(u)||(process.env.PATH=`${u}:${process.env.PATH}`),t.succeed("cursor-agent CLI installed!"),r()}else o(new Error("cursor-agent install failed"))})})}catch{t.fail("Could not install cursor-agent CLI"),console.log(e.yellow(` Install manually: curl https://cursor.com/install -fsS | bash
35
+ `))}}}if(s.agent==="codex"&&!n.skipInstall){const t=v("Checking Codex CLI...").start();try{T("codex --version",{encoding:"utf-8",timeout:5e3,stdio:"pipe"}),t.succeed("Codex CLI already installed")}catch{t.text="Installing Codex CLI...";try{await new Promise((r,o)=>{S("npm",["install","-g","@openai/codex"],{stdio:"pipe"}).on("close",l=>{l===0?(t.succeed("Codex CLI installed!"),r()):o(new Error("npm install -g @openai/codex failed"))})})}catch{t.fail("Could not install Codex CLI"),console.log(e.yellow(` Install manually: npm install -g @openai/codex
36
+ `))}}}if(s.agent==="gemini"&&!n.skipInstall){const t=v("Checking Gemini CLI...").start();try{T("gemini --version",{encoding:"utf-8",timeout:5e3,stdio:"pipe"}),t.succeed("Gemini CLI already installed")}catch{t.text="Installing Gemini CLI...";try{await new Promise((o,c)=>{S("npm",["install","-g","@google/gemini-cli"],{stdio:"pipe"}).on("close",u=>{u===0?(t.succeed("Gemini CLI installed!"),o()):c(new Error("npm install -g @google/gemini-cli failed"))})})}catch{t.fail("Could not install Gemini CLI"),console.log(e.yellow(` Install manually: npm install -g @google/gemini-cli
37
+ `))}}const r=v("Configuring Gemini MCP servers...").start();try{const o=i(C(),".gemini"),c=i(o,"settings.json");f(o)||await _(o,{recursive:!0});let l={mcpServers:{}};if(f(c))try{const $=await w(c,"utf-8");l=JSON.parse($),l.mcpServers||(l.mcpServers={})}catch{}let u;try{const{createRequire:$}=await import("module");u=$(import.meta.url).resolve("@zibby/mcp-browser/bin/mcp-browser-zibby.js")}catch{u="@playwright/mcp"}const k=s.browserMode!=="headless",M=u==="@playwright/mcp"?["-y","@playwright/mcp","--isolated","--save-video=1280x720","--viewport-size=1280x720","--output-dir","test-results"]:[u,"--isolated","--save-video=1280x720","--viewport-size=1280x720","--output-dir=test-results"];k||M.push("--headless"),l.mcpServers["playwright-official"]={command:u==="@playwright/mcp"?"npx":"node",args:M},await g(c,`${JSON.stringify(l,null,2)}
37
38
  `,"utf-8");let P="Gemini MCP configured";k?P+=" (headed mode - visible browser)":P+=" (headless mode - hidden browser)",r.succeed(P)}catch(o){r.fail("MCP setup failed"),console.log(e.yellow(" You may need to configure ~/.gemini/settings.json manually")),console.log(e.gray(` Error: ${o.message}
38
39
  `))}}if(s.agent==="cursor"&&s.setupMcp){const t=v("Setting up Playwright MCP...").start();try{const{setupPlaywrightMcpCommand:r}=await import("./setup-scripts.js"),o=s.cloudSync||!1,c=s.browserMode!=="headless";await r({headed:c,cloudSync:o,video:"on",viewport:{width:1280,height:720}});let l="Playwright MCP configured";c?l+=" (headed mode - visible browser)":l+=" (headless mode - hidden browser)",o&&(l+=" + Zibby MCP (cloud sync)"),t.succeed(l),o&&!(s.apiKey&&s.apiKey.trim())&&console.log(e.gray(`
39
40
  Copy .env.example to .env and set ZIBBY_API_KEY to enable uploads
@@ -43,7 +44,7 @@ Using mem0 backend requires mem0ai in your project dependencies.`)),console.log(
43
44
  \u{1F389} All set!
44
45
  `)),console.log(e.cyan("Start the Zibby Chat Agent:")),h&&console.log(e.white(` cd ${d}`)),console.log(e.white(` zibby
45
46
  `)),console.log(e.cyan("Or run a test directly:")),console.log(e.white(` zibby run test-specs/examples/example-domain.txt
46
- `)),console.log(e.cyan("Next steps:"));let M=1;console.log(e.white(` ${M++}. cp .env.example .env ${e.gray("# then add your API keys")}`)),y&&console.log(e.white(` ${M++}. Set ${e.bold("ZIBBY_MEMORY_BACKEND")} in .env ${e.gray(`# currently: ${m}`)}`)),console.log(e.white(` ${M++}. Type ${e.bold("zibby")} to chat with the AI testing assistant`)),console.log(e.white(` ${M++}. Write test specs in test-specs/`)),console.log(e.white(` ${M++}. Run: npx zibby run <spec-file>
47
+ `)),console.log(e.cyan("Next steps:"));let B=1;console.log(e.white(` ${B++}. cp .env.example .env ${e.gray("# then add your API keys")}`)),y&&console.log(e.white(` ${B++}. Set ${e.bold("ZIBBY_MEMORY_BACKEND")} in .env ${e.gray(`# currently: ${m}`)}`)),console.log(e.white(` ${B++}. Type ${e.bold("zibby")} to chat with the AI testing assistant`)),console.log(e.white(` ${B++}. Write test specs in test-specs/`)),console.log(e.white(` ${B++}. Run: npx zibby run <spec-file>
47
48
  `))}catch(b){p.fail("Failed to create project"),console.error(e.red(`
48
49
  \u274C Error: ${b.message}
49
50
  `)),process.exit(1)}}function U(d,n={},y={}){const m=["dolt","mem0"].includes(String(y.memoryBackend||"").toLowerCase())?String(y.memoryBackend).toLowerCase():"dolt",a={claude:`
@@ -59,12 +60,12 @@ Using mem0 backend requires mem0ai in your project dependencies.`)),console.log(
59
60
  },`,gemini:`
60
61
  gemini: {
61
62
  model: 'gemini-2.5-pro', // Options: 'auto', 'gemini-2.5-pro', 'gemini-2.5-flash'
62
- },`},_=d.agent,h=Object.entries(a).filter(([s])=>s!==_).map(([,s])=>s.split(`
63
+ },`},E=d.agent,h=Object.entries(a).filter(([s])=>s!==E).map(([,s])=>s.split(`
63
64
  `).map(x=>x.trim()?` // ${x.trimStart()}`:x).join(`
64
65
  `)).join(`
65
66
  `);return`export default {
66
67
  // AI agent settings
67
- agent: {${a[_]}
68
+ agent: {${a[E]}
68
69
  ${h}
69
70
  strictMode: false,
70
71
  },
@@ -160,7 +161,7 @@ ZIBBY_API_KEY=${n}
160
161
 
161
162
  # Chat memory backend
162
163
  ZIBBY_MEMORY_BACKEND=${y}
163
- `}function X(d,n,y={}){const a={"@zibby/cli":"latest","@zibby/core":j("../../package.json").dependencies?.["@zibby/core"]||"^0.1.29"};return y.memoryBackend==="mem0"&&(a.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:a,devDependencies:{"@playwright/test":"^1.49.0",dotenv:"^17.2.3"}},null,2)}function J(){return`# Dependencies
164
+ `}function X(d,n,y={}){const a={"@zibby/cli":"latest","@zibby/core":Z("../../package.json").dependencies?.["@zibby/core"]||"^0.1.29"};return y.memoryBackend==="mem0"&&(a.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:a,devDependencies:{"@playwright/test":"^1.49.0",dotenv:"^17.2.3"}},null,2)}function J(){return`# Dependencies
164
165
  node_modules/
165
166
 
166
167
  # Test artifacts
@@ -1,17 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import It from"express";import{createServer as Mt}from"http";import{WebSocketServer as Kt}from"ws";import{spawn as st,execSync as Y}from"child_process";import{readFileSync as _,existsSync as u,readdirSync as $,statSync as L,createReadStream as Yt,createWriteStream as Ht,mkdirSync as bt,openSync as Zt,readSync as Vt,closeSync as qt,writeFileSync as H,unlinkSync as Qt}from"fs";import{join as a,resolve as O,dirname as jt,basename as Xt}from"path";import{homedir as te}from"os";import{inspect as ee}from"util";import{fileURLToPath as se,pathToFileURL as ne}from"url";import oe from"dotenv";import{promptAndInstallStudio as re,isStudioInstalled as Pt}from"../utils/studio-installer.js";import{launchStudio as ie}from"../utils/studio-launcher.js";import{mergeSessionRunState as B,listRunningSessionStatesFromSessionsRoot as ce}from"@zibby/core/utils/run-state-session.js";import{liveRunsFromSessionStateRows as ae}from"@zibby/core/utils/session-state-live-runs.js";import{findLatestLiveFrameFileSync as vt,readLatestLiveFramePayloadSync as wt}from"@zibby/core/utils/live-frame-discovery.js";import{getApiUrl as Tt}from"../config/environments.js";const le=se(import.meta.url),ue=jt(le);function Nt(v){const g=a(v,"video.webm");if(u(g))return g;const y=a(v,"execute_live");if(!u(y))return null;try{const k=$(y).filter(I=>I.endsWith(".webm"));return k.length===0?null:k.map(I=>{const w=a(y,I);try{return{p:w,mtime:L(w).mtimeMs}}catch{return{p:w,mtime:0}}}).sort((I,w)=>w.mtime-I.mtime)[0].p}catch{return null}}function At(v){if(process.platform==="win32")try{const g=Y("netstat -ano",{encoding:"utf8"}),y=new Set;for(const k of g.split(`
3
- `)){if(!k.includes("LISTENING"))continue;const E=k.trim().split(/\s+/);if(!(E[1]||"").endsWith(`:${v}`))continue;const w=E[E.length-1];/^\d+$/.test(w)&&y.add(parseInt(w,10))}return[...y].filter(k=>k!==process.pid)}catch{return[]}try{const g=Y(`lsof -tiTCP:${v} -sTCP:LISTEN`,{encoding:"utf8"}).trim();return[...new Set(g.split(`
4
- `).filter(Boolean))].map(y=>parseInt(y,10)).filter(y=>!Number.isNaN(y)&&y!==process.pid)}catch{return[]}}async function _t(v){let g=At(v);if(g.length!==0){console.log(`[Studio] Port ${v} in use \u2014 stopping previous Studio listener(s): ${g.join(", ")}`);for(const y of g)try{process.kill(y,"SIGTERM")}catch{}await new Promise(y=>setTimeout(y,450)),g=At(v);for(const y of g)try{process.kill(y,"SIGKILL")}catch{}await new Promise(y=>setTimeout(y,200))}}async function ke(v={}){const g=It(),y=Mt(g),k=new Kt({server:y}),E=v.port||3847,I=process.cwd(),w=te();process.env.DOTENV_CONFIG_QUIET="true";const kt=process.env.NODE_ENV||"development";[O(I,".env.local"),O(I,`.env.${kt}`),O(I,".env")].forEach(s=>{u(s)&&oe.config({path:s,override:!1})});const R=new Map,D=new Map;let Et=null;const G=96e3,nt="studio-cli.log",U="studio-run.json",Ot=".zibby-studio-stop",Z=512*1024;g.use(It.json()),g.get("/api/session-run-states",(s,t)=>{try{let e=x();try{const r=new URL(s.url,"http://zibby.studio").searchParams.get("sessionsRoot");if(r&&String(r).trim()){const i=O(decodeURIComponent(String(r).trim()));u(i)&&L(i).isDirectory()&&(e=i)}}catch{}const o=ce(e),{liveIdList:n,progressByKey:c}=ae(o);t.json({rows:o,liveIdList:n,progressByKey:c,sessionsRoot:e,unavailable:!1})}catch(e){t.status(500).json({error:e.message,rows:[],liveIdList:[],progressByKey:{},unavailable:!0})}}),g.use((s,t,e)=>(t.header("Access-Control-Allow-Origin","*"),t.header("Access-Control-Allow-Methods","GET, POST, PUT, DELETE"),t.header("Access-Control-Allow-Headers","Content-Type"),e()));const V=a(ue,"../../../../studio"),ot=u(a(V,"package.json"));function xt(){const s=a(V,"node_modules",".bin",process.platform==="win32"?"electron.cmd":"electron"),t=u(s),n=st(t?s:"npx",t?["."]:["electron","."],{cwd:V,detached:!0,stdio:"ignore",shell:!1,env:{...process.env,ZIBBY_STUDIO_PROJECT_ROOT:I,ZIBBY_STUDIO_API_BASE:`http://localhost:${E}/api`}});n.unref(),Et=n}async function Rt(){if(ot){xt();return}if(v.update||!Pt()){if(v.update&&Pt()){console.log(`
2
+ import It from"express";import{createServer as Mt}from"http";import{WebSocketServer as Kt}from"ws";import{spawn as st,execSync as Y}from"child_process";import{readFileSync as _,existsSync as u,readdirSync as z,statSync as L,createReadStream as Yt,createWriteStream as Ht,mkdirSync as bt,openSync as Zt,readSync as Vt,closeSync as qt,writeFileSync as H,unlinkSync as Qt}from"fs";import{join as a,resolve as O,dirname as jt,basename as Xt}from"path";import{homedir as te}from"os";import{inspect as ee}from"util";import{fileURLToPath as se,pathToFileURL as ne}from"url";import oe from"dotenv";import{promptAndInstallStudio as re,isStudioInstalled as Pt}from"../utils/studio-installer.js";import{launchStudio as ie}from"../utils/studio-launcher.js";import{mergeSessionRunState as D,listRunningSessionStatesFromSessionsRoot as ce}from"@zibby/core/utils/run-state-session.js";import{liveRunsFromSessionStateRows as ae}from"@zibby/core/utils/session-state-live-runs.js";import{findLatestLiveFrameFileSync as vt,readLatestLiveFramePayloadSync as wt}from"@zibby/core/utils/live-frame-discovery.js";import{getApiUrl as Tt}from"../config/environments.js";const le=se(import.meta.url),ue=jt(le);function Nt(v){const m=a(v,"video.webm");if(u(m))return m;const y=a(v,"execute_live");if(!u(y))return null;try{const k=z(y).filter(I=>I.endsWith(".webm"));return k.length===0?null:k.map(I=>{const w=a(y,I);try{return{p:w,mtime:L(w).mtimeMs}}catch{return{p:w,mtime:0}}}).sort((I,w)=>w.mtime-I.mtime)[0].p}catch{return null}}function At(v){if(process.platform==="win32")try{const m=Y("netstat -ano",{encoding:"utf8"}),y=new Set;for(const k of m.split(`
3
+ `)){if(!k.includes("LISTENING"))continue;const E=k.trim().split(/\s+/);if(!(E[1]||"").endsWith(`:${v}`))continue;const w=E[E.length-1];/^\d+$/.test(w)&&y.add(parseInt(w,10))}return[...y].filter(k=>k!==process.pid)}catch{return[]}try{const m=Y(`lsof -tiTCP:${v} -sTCP:LISTEN`,{encoding:"utf8"}).trim();return[...new Set(m.split(`
4
+ `).filter(Boolean))].map(y=>parseInt(y,10)).filter(y=>!Number.isNaN(y)&&y!==process.pid)}catch{return[]}}async function _t(v){let m=At(v);if(m.length!==0){console.log(`[Studio] Port ${v} in use \u2014 stopping previous Studio listener(s): ${m.join(", ")}`);for(const y of m)try{process.kill(y,"SIGTERM")}catch{}await new Promise(y=>setTimeout(y,450)),m=At(v);for(const y of m)try{process.kill(y,"SIGKILL")}catch{}await new Promise(y=>setTimeout(y,200))}}async function ke(v={}){const m=It(),y=Mt(m),k=new Kt({server:y}),E=v.port||3847,I=process.cwd(),w=te();process.env.DOTENV_CONFIG_QUIET="true";const kt=process.env.NODE_ENV||"development";[O(I,".env.local"),O(I,`.env.${kt}`),O(I,".env")].forEach(s=>{u(s)&&oe.config({path:s,override:!1})});const R=new Map,G=new Map;let Et=null;const U=96e3,nt="studio-cli.log",W="studio-run.json",Ot=".zibby-studio-stop",Z=512*1024;m.use(It.json()),m.get("/api/session-run-states",(s,t)=>{try{let e=x();try{const r=new URL(s.url,"http://zibby.studio").searchParams.get("sessionsRoot");if(r&&String(r).trim()){const c=O(decodeURIComponent(String(r).trim()));u(c)&&L(c).isDirectory()&&(e=c)}}catch{}const o=ce(e),{liveIdList:n,progressByKey:i}=ae(o);t.json({rows:o,liveIdList:n,progressByKey:i,sessionsRoot:e,unavailable:!1})}catch(e){t.status(500).json({error:e.message,rows:[],liveIdList:[],progressByKey:{},unavailable:!0})}}),m.use((s,t,e)=>(t.header("Access-Control-Allow-Origin","*"),t.header("Access-Control-Allow-Methods","GET, POST, PUT, DELETE"),t.header("Access-Control-Allow-Headers","Content-Type"),e()));const V=a(ue,"../../../../studio"),ot=u(a(V,"package.json"));function xt(){const s=a(V,"node_modules",".bin",process.platform==="win32"?"electron.cmd":"electron"),t=u(s),n=st(t?s:"npx",t?["."]:["electron","."],{cwd:V,detached:!0,stdio:"ignore",shell:!1,env:{...process.env,ZIBBY_STUDIO_PROJECT_ROOT:I,ZIBBY_STUDIO_API_BASE:`http://localhost:${E}/api`}});n.unref(),Et=n}async function Rt(){if(ot){xt();return}if(v.update||!Pt()){if(v.update&&Pt()){console.log(`
5
5
  Updating Zibby Studio...
6
6
  `);const{installStudio:s}=await import("../utils/studio-installer.js");await s()}else if(!await re()){y.close(),process.exit(0);return}}console.log(`
7
7
  \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\u2557
8
8
  \u2551 Zibby Studio \u2551
9
9
  \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\u255D
10
- `),await ie({projectRoot:I}),setTimeout(()=>{y.close(),process.exit(0)},1e3)}function rt(){const s=a(I,".zibby.config.mjs");return u(s)?s:a(w,".zibby.config.mjs")}function x(){rt();const s=a(I,".zibby","output","sessions");return u(s)?s:a(w,".zibby","output","sessions")}function q(s,t){let e=null,o=null,n=null;const c=a(s,"codegen");if(u(c)){const r=a(c,"test.spec.ts"),i=a(c,"generated-test.spec.js");e=u(r)?r:u(i)?i:null;const d=a(c,"test.selenium.py"),p=a(c,"generated-test-selenium.js");o=u(d)?d:u(p)?p:null;const m=a(c,"trace.zip");n=u(m)?m:null}const l=a(s,"generate_script","result.json");if(!e&&u(l))try{const i=JSON.parse(_(l,"utf8"))?.scriptPath;if(typeof i=="string"&&i.trim()){const d=i.trim(),p=d.startsWith("/")||process.platform==="win32"&&/^[A-Za-z]:[\\/]/.test(d)?d:a(t,d);u(p)&&(e=p)}}catch(r){console.warn("[Studio API] generate_script result.json:",r.message)}return!e&&!o&&!n?null:{playwrightFile:e,seleniumFile:o,tracePath:n}}function Ct(s,t,e){try{H(a(s,U),JSON.stringify({sessionId:t,pid:e??null,startedAt:Date.now()},null,2),"utf8")}catch(o){console.error("[Studio API] writeStudioRunMeta:",o.message)}}function z(s){try{const t=a(s,U);u(t)&&Qt(t)}catch{}}function Ft(s){const t=Number(s);if(!Number.isFinite(t)||t<=0)return[];try{const e=Y(`pgrep -P ${t}`,{encoding:"utf8",stdio:["ignore","pipe","ignore"],maxBuffer:524288}).trim();return e?e.split(/\n/).map(o=>parseInt(o.trim(),10)).filter(o=>Number.isFinite(o)&&o>0):[]}catch{return[]}}function W(s,t){const e=Number(s);if(!Number.isFinite(e)||e<=0)return;const o=new Set;function n(c){if(!o.has(c)){o.add(c);for(const l of Ft(c))n(l);try{process.kill(c,t)}catch{}}}n(e)}function it(s){const t=Number(s);if(!(!Number.isFinite(t)||t<=0))try{Y(`taskkill /PID ${t} /T /F`,{stdio:"ignore",windowsHide:!0})}catch{}}function ct(s){if(!Number.isFinite(s)||s<=0)return;if(process.platform==="win32"){it(s);return}W(s,"SIGTERM");const t=setTimeout(()=>{W(s,"SIGKILL")},800);typeof t.unref=="function"&&t.unref()}function Lt(s,t){const o=C(s)||a(x(),s);try{bt(o,{recursive:!0}),H(a(o,Ot),JSON.stringify({requestedAt:Date.now()}),"utf8")}catch(r){console.error("[Studio API] write studio stop request:",r.message)}const n=R.get(s);if(n){const r=n.pid;if(process.platform==="win32")it(r);else{W(r,"SIGTERM");const i=setTimeout(()=>{W(r,"SIGKILL")},800);typeof i.unref=="function"&&i.unref()}return R.delete(s),z(o),!0}const c=a(o,U);if(u(c)){let r=null;try{const i=JSON.parse(_(c,"utf8"));r=Number(i.pid)}catch(i){console.error("[Studio API] studio-run.json read:",i.message)}if(z(o),Number.isFinite(r)&&r>0)return ct(r),!0}const l=Number(t);return Number.isFinite(l)&&l>0?(ct(l),z(o),!0):!1}function J(s){try{const t=s?.query?.sessionsRoot;if(typeof t!="string")return"";const e=t.trim();if(!e)return"";const o=decodeURIComponent(e),n=O(o);return u(n)?n:""}catch{return""}}function C(s,t=""){const e=typeof s=="string"?decodeURIComponent(s).trim():String(s||"").trim();if(!e)return null;const o=[e],n=e.split("_")[0]?.trim()||"";n&&n!==e&&o.push(n);const c=new Set,l=[],r=new Set,i=[],d=h=>{if(!h)return;const f=O(h);r.has(f)||(r.add(f),i.push(f))},p=h=>{if(h){d(h);for(const f of o){const S=a(h,f);c.has(S)||(c.add(S),l.push(S))}}};p(t),p(x()),p(a(w,".zibby","output","sessions"));let m;try{m=$(I)}catch{m=[]}for(const h of m){if(h.startsWith("."))continue;const f=a(I,h);try{if(!L(f).isDirectory())continue}catch{continue}p(a(f,".zibby","output","sessions"));let S;try{S=$(f,{withFileTypes:!0})}catch{S=[]}for(const N of S)N?.isDirectory?.()&&String(N.name||"").startsWith(".zibby")&&p(a(f,N.name,"output","sessions"))}const T=l.filter(h=>u(h));if(T.length===0)for(const h of i){if(!u(h))continue;let f;try{f=$(h,{withFileTypes:!0})}catch{f=[]}for(const S of f){if(!S?.isDirectory?.())continue;const N=String(S.name||"");if(N===e||N===n||N.startsWith(`${e}_`)||n&&N.startsWith(`${n}_`)){const F=a(h,N);if(c.has(F))continue;c.add(F),T.push(F)}}}if(T.length===0)return null;if(T.length===1)return T[0];const M=h=>{let f=0;u(a(h,"execute_live"))&&(f+=20),vt(h)&&(f+=15),u(a(h,"execute_live","events.json"))&&(f+=8),u(a(h,"execute_live","result.json"))&&(f+=6),u(a(h,U))&&(f+=4),u(a(h,".session-info.json"))&&(f+=2);try{const S=a(h,"execute_live");u(S)&&(f+=Math.min(L(S).mtimeMs/1e12,3))}catch{}return f};return T.sort((h,f)=>M(f)-M(h)),T[0]}function $t(s,t){try{const e=typeof s=="string"?decodeURIComponent(s).trim():String(s||"").trim();if(!e||!t)return null;const o=(e.split("_")[0]||"").trim(),n=jt(t);if(!o||!n||!u(n))return null;let c=[];try{c=$(n,{withFileTypes:!0})}catch{c=[]}let l=null;for(const r of c){if(!r?.isDirectory?.())continue;const i=String(r.name||"");if(!(i===e||i===o||i.startsWith(`${e}_`)||i.startsWith(`${o}_`)))continue;const d=a(n,i),p=wt(d);p&&(!l||Number(p.mtime||0)>Number(l.mtime||0))&&(l=p)}return l}catch{return null}}g.get("/api/config/check",(s,t)=>{const e=rt();t.json({exists:u(e),path:e,isProjectLevel:e.startsWith(I)})}),g.get("/api/projects",(s,t)=>{try{const e=a(w,".zibby","config.json");if(!u(e))return t.json({projects:[]});const o=JSON.parse(_(e,"utf8")),n=typeof o.sessionToken=="string"&&o.sessionToken.trim()!==""?o.sessionToken.trim():"",c=Array.isArray(o.projects)?o.projects:[];if(!n)return t.json({projects:c});const l=String(Tt()).replace(/\/$/,"");fetch(`${l}/projects`,{headers:{Authorization:`Bearer ${n}`}}).then(r=>r.json().catch(()=>({})).then(i=>({ok:r.ok,status:r.status,body:i}))).then(({ok:r,body:i})=>{if(!r)return t.json({projects:c});const p=(Array.isArray(i?.projects)?i.projects:[]).map(m=>({name:m?.name,projectId:m?.projectId,apiToken:m?.apiToken||null,createdAt:m?.createdAt||null,updatedAt:m?.updatedAt||null}));o.projects=p;try{H(e,`${JSON.stringify(o,null,2)}
11
- `,"utf8")}catch{}return t.json({projects:p})}).catch(()=>t.json({projects:c}))}catch(e){t.status(500).json({error:e.message})}}),g.post("/api/projects/create",async(s,t)=>{try{const e=typeof s.body?.name=="string"?s.body.name.trim():"";if(!e)return t.status(400).json({error:"Project name is required"});const o=a(w,".zibby","config.json");if(!u(o))return t.status(401).json({error:"Not logged in"});const n=JSON.parse(_(o,"utf8")),c=typeof n.sessionToken=="string"&&n.sessionToken.trim()!==""?n.sessionToken.trim():"";if(!c)return t.status(401).json({error:"Not logged in"});const l=String(Tt()).replace(/\/$/,""),r=await fetch(`${l}/projects`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${c}`},body:JSON.stringify({name:e})}),i=await r.json().catch(()=>({}));if(!r.ok)return t.status(r.status).json({error:i.error||i.message||`HTTP ${r.status}`});const d=i?.project&&typeof i.project=="object"?i.project:null;if(d?.projectId){const m=(Array.isArray(n.projects)?n.projects:[]).filter(T=>String(T?.projectId||"")!==String(d.projectId));m.push({name:d.name||e,projectId:d.projectId,apiToken:d.apiToken||i?.apiToken||null,createdAt:d.createdAt||null,updatedAt:d.updatedAt||null}),n.projects=m;try{H(o,`${JSON.stringify(n,null,2)}
12
- `,"utf8")}catch{}}return t.json(i)}catch(e){return t.status(500).json({error:e.message||String(e)})}});function zt(s,t){const e=Array.isArray(t)?t.filter(d=>typeof d=="string"):[],o=process.argv[1]?O(process.argv[1]):"",n=o?Xt(o):"";let c;try{c=!!o&&u(o)&&L(o).isFile()&&!/\.(cmd|ps1|bat)$/i.test(o)&&(/\.(m|c)?js$/i.test(o)||n==="zibby")}catch{c=!1}if(c){const d=[o,"test",s,...e],p=[process.execPath,...d].map(m=>/\s/.test(String(m))?`"${String(m).replace(/"/g,'\\"')}"`:m).join(" ");return{useShell:!1,cmd:process.execPath,args:d,display:p}}const l=s.replace(/"/g,'\\"'),r=e.join(" "),i=`zibby test "${l}" ${r}`.trim();return{useShell:!0,cmd:i,args:[],display:i}}g.get("/api/run/result/:sessionId",(s,t)=>{const{sessionId:e}=s.params,o=D.get(e);if(!o)return t.status(404).json({pending:!0,error:"unknown_session"});t.json(o)}),g.post("/api/run",async(s,t)=>{const{task:e,args:o=[],sessionId:n,studioTestCaseId:c}=s.body;if(!e||!n)return t.status(400).json({success:!1,error:"task and sessionId are required"});const l=x(),r=a(l,n),i=O(r),d=c!=null&&String(c).trim()!==""?String(c).trim():String(n);D.set(n,{pending:!0});let p=!1,m=null;const T=()=>{if(m&&!m.destroyed)try{m.end()}catch{}m=null},M=(f,S)=>{D.delete(n),T(),p||t.status(f).json(S)},h=()=>{const f=[];return k.clients.forEach(S=>{S.sessionId===n&&f.push(S)}),f};try{const f=zt(e,o);console.log("[Studio API] Running:",f.display),f.useShell?console.warn('[Studio API] Falling back to shell "zibby" from PATH \u2014 if runs do nothing, run Studio from this repo or ensure `zibby` points to the same install.'):console.log("[Studio API] Using same CLI as this server (argv[1]):",process.argv[1]),bt(r,{recursive:!0}),m=Ht(a(r,nt),{flags:"w"}),B(i,{sessionId:String(n),studioTestCaseId:d,status:"running",runSource:"studio",activeStageIndex:0,activeNode:"preflight",cwd:I,outputBase:".zibby/output",sessionPathAbs:i});const S=st(f.cmd,f.args,{cwd:I,shell:f.useShell,env:{...process.env,ZIBBY_SESSION_ID:n,ZIBBY_SESSION_PATH:O(r)}});R.set(n,S),Ct(r,n,S.pid),B(i,{sessionId:String(n),studioTestCaseId:d,status:"running",runSource:"studio",activeStageIndex:0,activeNode:"preflight",cwd:I,outputBase:".zibby/output",sessionPathAbs:i,pid:S.pid??null});let N="",F="",dt=!1,ft=0;const pt=setInterval(()=>{try{const b=vt(r);if(!b||b.mtime<=ft)return;ft=b.mtime;const j=_(b.p).toString("base64"),P=b.p.toLowerCase().endsWith(".png")?"image/png":"image/jpeg";h().forEach(K=>{try{K.send(JSON.stringify({type:"video-frame",sessionId:n,frame:j,mime:P}))}catch{}})}catch{}},320),mt=b=>{if(dt)return;dt=!0,T();const j={pending:!1,...b};typeof j.stdout=="string"&&j.stdout.length>G&&(j.stdout=`\u2026(truncated)
13
- ${j.stdout.slice(-G)}`),typeof j.stderr=="string"&&j.stderr.length>G&&(j.stderr=`\u2026(truncated)
14
- ${j.stderr.slice(-G)}`),D.set(n,j),h().forEach(P=>{try{P.send(JSON.stringify({type:"run-complete",sessionId:n,result:b}))}catch{}})};S.stdout.on("data",b=>{const j=b.toString();if(N+=j,m&&!m.destroyed)try{m.write(j)}catch(P){console.error("[Studio API] studio-cli.log write failed:",P.message)}h().forEach(P=>{try{P.send(JSON.stringify({type:"stdout",data:j}))}catch{}})}),S.stderr.on("data",b=>{const j=b.toString();if(F+=j,m&&!m.destroyed)try{m.write(`[stderr] ${j}`)}catch(P){console.error("[Studio API] studio-cli.log write failed:",P.message)}h().forEach(P=>{try{P.send(JSON.stringify({type:"stderr",data:j}))}catch{}})}),S.on("error",b=>{clearInterval(pt),R.delete(n),B(i,{sessionId:String(n),studioTestCaseId:d,status:"failed",runSource:"studio",activeNode:null,activeStageIndex:null,errorMessage:b.message,cwd:I,outputBase:".zibby/output",sessionPathAbs:i}),console.error("[Studio API] Spawn error:",b.message),mt({heldUntilExit:!0,success:!1,exitCode:null,error:b.message,stderr:F,stdout:N,runName:null,videoPath:null,eventsPath:null,codegenFiles:null,metadata:{sessionId:n,task:e}})}),S.on("close",b=>{clearInterval(pt),R.delete(n),B(i,{sessionId:String(n),studioTestCaseId:d,status:b===0?"completed":b===130||b===143?"interrupted":"failed",runSource:"studio",activeNode:null,activeStageIndex:null,exitCode:b,cwd:I,outputBase:".zibby/output",sessionPathAbs:i}),z(r),h().forEach(A=>{try{A.send(JSON.stringify({type:"exit",code:b}))}catch{}});const j=b===0;let P=null;const K=a(r,".session-info.json"),gt=a(r,"result.json");if(u(K))try{const A=JSON.parse(_(K,"utf8"));P=A.name||A.task}catch(A){console.error("[Studio API] Failed to read session info:",A.message)}else if(u(gt))try{const A=JSON.parse(_(gt,"utf8"));P=A.name||A.task}catch(A){console.error("[Studio API] Failed to read result:",A.message)}const Wt=[/\[SessionRecorder\] Run name set: (.+)/,/Run name: (.+)/];if(!P)for(const A of Wt){const St=N.match(A);if(St){P=St[1].trim();break}}const Jt=q(r,I),tt=Nt(r),ht=a(r,"events.json"),yt=a(r,"execute_live","events.json"),et=u(ht)?ht:u(yt)?yt:null;mt({heldUntilExit:!0,success:j,exitCode:b,stdout:N,stderr:F,runName:P,videoPath:tt&&u(tt)?tt:null,eventsPath:et&&u(et)?et:null,codegenFiles:Jt,metadata:{sessionId:n,task:e}})}),p=!0,t.status(202).json({accepted:!0,sessionId:n,pid:S.pid!=null&&Number.isFinite(Number(S.pid))?Number(S.pid):null,pollPath:`/api/run/result/${n}`,message:"Run started. Poll GET /api/run/result/:sessionId until pending is false."})}catch(f){M(500,{success:!1,error:f.message})}}),g.post("/api/stop/:sessionId",(s,t)=>{const{sessionId:e}=s.params,o=s.body&&s.body.pid!=null?s.body.pid:null;if(Lt(e,o))return t.json({success:!0});t.status(404).json({success:!1,error:"Session not found"})}),g.get("/api/recordings",(s,t)=>{try{const e=x();if(!u(e))return t.json({recordings:[],path:e});const n=$(e).map(c=>{const l=a(e,c),r=a(l,".session-info.json"),i=a(l,"result.json");let d={};return u(r)?d=JSON.parse(_(r,"utf8")):u(i)&&(d=JSON.parse(_(i,"utf8"))),{sessionId:c,...d,path:l}});t.json({path:e,recordings:n})}catch(e){t.status(500).json({error:e.message})}}),g.get("/api/sessions/:sessionId/cli-log",(s,t)=>{try{const{sessionId:e}=s.params;let o=parseInt(String(s.query.offset||"0"),10);(Number.isNaN(o)||o<0)&&(o=0);const n=J(s),c=C(e,n),l=a(c||a(x(),e),nt);if(!u(l))return t.json({exists:!1,content:"",size:0,fromByte:0,nextOffset:0});const r=L(l).size;o>r&&(o=r);let i=o,d=r-i;d>Z&&(i=r-Z,d=Z);const p=Buffer.alloc(d),m=Zt(l,"r");try{Vt(m,p,0,d,i)}finally{qt(m)}t.json({exists:!0,content:p.toString("utf8"),size:r,fromByte:i,nextOffset:r})}catch(e){t.status(500).json({error:e.message})}}),g.get("/api/sessions/:sessionId/events",(s,t)=>{try{const{sessionId:e}=s.params,o=J(s),n=C(e,o);if(!n)return t.json({events:[]});let c=a(n,"events.json");if(!u(c)){const r=a(n,"execute_live","events.json");if(u(r))c=r;else return t.json({events:[]})}const l=JSON.parse(_(c,"utf8"));t.json({events:l})}catch(e){t.status(500).json({error:e.message})}}),g.get("/api/sessions/:sessionId/live-preview",(s,t)=>{try{const{sessionId:e}=s.params,o=J(s),n=C(e,o);if(!n)return t.json({ok:!1,error:"session_not_found"});const l=wt(n)||$t(e,n);if(!l)return t.json({ok:!1,error:"no_frame",sessionId:e,sessionPath:n});t.json({ok:!0,frame:l.base64,mime:l.mime,mtime:l.mtime,path:l.path})}catch(e){t.status(500).json({ok:!1,error:e.message})}}),g.get("/api/sessions/:sessionId/video",(s,t)=>{try{const{sessionId:e}=s.params,o=J(s),n=C(e,o);if(!n)return t.status(404).json({error:"Session not found"});const c=Nt(n);if(!c||!u(c))return t.status(404).json({error:"Video not found"});const l=L(c);t.writeHead(200,{"Content-Type":"video/webm","Content-Length":l.size}),Yt(c).pipe(t)}catch(e){t.status(500).json({error:e.message})}}),g.get("/api/sessions/:sessionId/codegen/playwright",(s,t)=>{try{const{sessionId:e}=s.params,o=C(e)||a(x(),e);if(!u(o))return t.status(404).type("text/plain").send("Session not found");const n=q(o,I);if(!n?.playwrightFile||!u(n.playwrightFile))return t.status(404).type("text/plain").send("No Playwright artifact");t.type("text/plain; charset=utf-8").send(_(n.playwrightFile,"utf8"))}catch(e){t.status(500).type("text/plain").send(e.message)}}),g.get("/api/sessions/:sessionId/codegen/selenium",(s,t)=>{try{const{sessionId:e}=s.params,o=C(e)||a(x(),e);if(!u(o))return t.status(404).type("text/plain").send("Session not found");const n=q(o,I);if(!n?.seleniumFile||!u(n.seleniumFile))return t.status(404).type("text/plain").send("No Selenium artifact");t.type("text/plain; charset=utf-8").send(_(n.seleniumFile,"utf8"))}catch(e){t.status(500).type("text/plain").send(e.message)}}),g.post("/api/init",async(s,t)=>{const{agentType:e,apiKey:o}=s.body;try{const n=`zibby init --agent ${e} --headed`,c=st(n,[],{cwd:I,env:{...process.env,CURSOR_API_KEY:e==="cursor"?o:process.env.CURSOR_API_KEY,ANTHROPIC_API_KEY:e==="claude"?o:process.env.ANTHROPIC_API_KEY,OPENAI_API_KEY:e==="codex"?o:process.env.OPENAI_API_KEY,GEMINI_API_KEY:e==="gemini"?o:process.env.GEMINI_API_KEY,GOOGLE_API_KEY:e==="gemini"?o:process.env.GOOGLE_API_KEY,CI:"true",ZIBBY_CI:"true"},shell:!0});let l="",r="";c.stdout.on("data",p=>{l+=p.toString()}),c.stderr.on("data",p=>{r+=p.toString()});let i=!1;const d=(p,m)=>{if(!i)if(i=!0,p===0)t.json({success:!0,stdout:l});else{const T=m||r||"zibby init failed";t.status(500).json({success:!1,error:T,stdout:l})}};c.on("close",p=>d(p)),c.on("error",p=>d(1,p.message))}catch(n){t.status(500).json({success:!1,error:n.message})}});const Q=(s,t)=>{const e={type:s,message:t,time:new Date().toLocaleTimeString()};k.clients.forEach(o=>{if(o.listenType==="console")try{o.send(JSON.stringify(e))}catch{}})},Bt=console.log,Dt=console.error,Gt=console.warn,X=s=>s.map(t=>{if(t==null)return String(t);if(typeof t=="object")try{return ee(t,{depth:4,colors:!1,breakLength:100})}catch{return String(t)}return String(t)}).join(" ");console.log=(...s)=>{Bt(...s),Q("info",X(s))},console.error=(...s)=>{Dt(...s),Q("error",X(s))},console.warn=(...s)=>{Gt(...s),Q("warn",X(s))},k.on("connection",(s,t)=>{const e=t.url;if(e.includes("/console"))s.listenType="console",console.log("[WebSocket] Console listener connected"),s.send(JSON.stringify({type:"success",message:"\u{1F3AD} Connected to Studio server logs",time:new Date().toLocaleTimeString()}));else{let o=e.split("/").pop()||"";try{o=decodeURIComponent(o)}catch{}s.sessionId=o,s.listenType="session",console.log(`[WebSocket] Session listener connected: ${o}`)}s.on("close",()=>{s.listenType==="console"?console.log("[WebSocket] Console listener disconnected"):console.log(`[WebSocket] Session listener disconnected: ${s.sessionId}`)})}),g.get("/api/workflow/graph",async(s,t)=>{try{const e=a(I,".zibby","graph.mjs");if(!u(e))return console.warn("[Workflow Graph API] Graph file not found at",e),t.json({graph:null,error:"No graph.mjs found in .zibby folder"});const o=await import(`${ne(e).href}?t=${Date.now()}`),n=o.default||Object.values(o)[0];if(!n||typeof n!="function")return console.error("[Workflow Graph API] Invalid graph module, got:",typeof n),t.json({graph:null,error:"Invalid graph module"});const l=new n().buildGraph(),r=l.toJSON?l.toJSON():l.serialize();console.log(`[Workflow Graph API] graph OK nodes=${r.nodes?.length??0} edges=${r.edges?.length??0} (${e})`),t.json({graph:r})}catch(e){console.error("[Workflow Graph API] Failed to load workflow graph:",e),t.status(500).json({graph:null,error:e.message})}});const Ut=()=>{ot&&console.log(`
10
+ `),await ie({projectRoot:I}),setTimeout(()=>{y.close(),process.exit(0)},1e3)}function rt(){const s=a(I,".zibby.config.mjs");return u(s)?s:a(w,".zibby.config.mjs")}function x(){rt();const s=a(I,".zibby","output","sessions");return u(s)?s:a(w,".zibby","output","sessions")}function q(s,t){let e=null,o=null,n=null;const i=a(s,"codegen");if(u(i)){const r=a(i,"test.spec.ts"),c=a(i,"generated-test.spec.js");e=u(r)?r:u(c)?c:null;const f=a(i,"test.selenium.py"),h=a(i,"generated-test-selenium.js");o=u(f)?f:u(h)?h:null;const d=a(i,"trace.zip");n=u(d)?d:null}const l=a(s,"generate_script","result.json");if(!e&&u(l))try{const c=JSON.parse(_(l,"utf8"))?.scriptPath;if(typeof c=="string"&&c.trim()){const f=c.trim(),h=f.startsWith("/")||process.platform==="win32"&&/^[A-Za-z]:[\\/]/.test(f)?f:a(t,f);u(h)&&(e=h)}}catch(r){console.warn("[Studio API] generate_script result.json:",r.message)}return!e&&!o&&!n?null:{playwrightFile:e,seleniumFile:o,tracePath:n}}function Ct(s,t,e){try{H(a(s,W),JSON.stringify({sessionId:t,pid:e??null,startedAt:Date.now()},null,2),"utf8")}catch(o){console.error("[Studio API] writeStudioRunMeta:",o.message)}}function B(s){try{const t=a(s,W);u(t)&&Qt(t)}catch{}}function Ft(s){const t=Number(s);if(!Number.isFinite(t)||t<=0)return[];try{const e=Y(`pgrep -P ${t}`,{encoding:"utf8",stdio:["ignore","pipe","ignore"],maxBuffer:524288}).trim();return e?e.split(/\n/).map(o=>parseInt(o.trim(),10)).filter(o=>Number.isFinite(o)&&o>0):[]}catch{return[]}}function J(s,t){const e=Number(s);if(!Number.isFinite(e)||e<=0)return;const o=new Set;function n(i){if(!o.has(i)){o.add(i);for(const l of Ft(i))n(l);try{process.kill(i,t)}catch{}}}n(e)}function it(s){const t=Number(s);if(!(!Number.isFinite(t)||t<=0))try{Y(`taskkill /PID ${t} /T /F`,{stdio:"ignore",windowsHide:!0})}catch{}}function ct(s){if(!Number.isFinite(s)||s<=0)return;if(process.platform==="win32"){it(s);return}J(s,"SIGTERM");const t=setTimeout(()=>{J(s,"SIGKILL")},800);typeof t.unref=="function"&&t.unref()}function Lt(s,t){const o=C(s)||a(x(),s);try{bt(o,{recursive:!0}),H(a(o,Ot),JSON.stringify({requestedAt:Date.now()}),"utf8")}catch(r){console.error("[Studio API] write studio stop request:",r.message)}const n=R.get(s);if(n){const r=n.pid;if(process.platform==="win32")it(r);else{J(r,"SIGTERM");const c=setTimeout(()=>{J(r,"SIGKILL")},800);typeof c.unref=="function"&&c.unref()}return R.delete(s),B(o),!0}const i=a(o,W);if(u(i)){let r=null;try{const c=JSON.parse(_(i,"utf8"));r=Number(c.pid)}catch(c){console.error("[Studio API] studio-run.json read:",c.message)}if(B(o),Number.isFinite(r)&&r>0)return ct(r),!0}const l=Number(t);return Number.isFinite(l)&&l>0?(ct(l),B(o),!0):!1}function M(s){try{const t=s?.query?.sessionsRoot;if(typeof t!="string")return"";const e=t.trim();if(!e)return"";const o=decodeURIComponent(e),n=O(o);return u(n)?n:""}catch{return""}}function C(s,t=""){const e=typeof s=="string"?decodeURIComponent(s).trim():String(s||"").trim();if(!e)return null;const o=[e],n=e.split("_")[0]?.trim()||"";n&&n!==e&&o.push(n);const i=new Set,l=[],r=new Set,c=[],f=g=>{if(!g)return;const p=O(g);r.has(p)||(r.add(p),c.push(p))},h=g=>{if(g){f(g);for(const p of o){const S=a(g,p);i.has(S)||(i.add(S),l.push(S))}}};h(t),h(x()),h(a(w,".zibby","output","sessions"));let d;try{d=z(I)}catch{d=[]}for(const g of d){if(g.startsWith("."))continue;const p=a(I,g);try{if(!L(p).isDirectory())continue}catch{continue}h(a(p,".zibby","output","sessions"));let S;try{S=z(p,{withFileTypes:!0})}catch{S=[]}for(const N of S)N?.isDirectory?.()&&String(N.name||"").startsWith(".zibby")&&h(a(p,N.name,"output","sessions"))}const T=l.filter(g=>u(g));if(T.length===0)for(const g of c){if(!u(g))continue;let p;try{p=z(g,{withFileTypes:!0})}catch{p=[]}for(const S of p){if(!S?.isDirectory?.())continue;const N=String(S.name||"");if(N===e||N===n||N.startsWith(`${e}_`)||n&&N.startsWith(`${n}_`)){const F=a(g,N);if(i.has(F))continue;i.add(F),T.push(F)}}}if(T.length===0)return null;if(T.length===1)return T[0];const $=g=>{let p=0;u(a(g,"execute_live"))&&(p+=20),vt(g)&&(p+=15),u(a(g,"execute_live","events.json"))&&(p+=8),u(a(g,"execute_live","result.json"))&&(p+=6),u(a(g,W))&&(p+=4),u(a(g,".session-info.json"))&&(p+=2);try{const S=a(g,"execute_live");u(S)&&(p+=Math.min(L(S).mtimeMs/1e12,3))}catch{}return p};return T.sort((g,p)=>$(p)-$(g)),T[0]}function $t(s,t){try{const e=typeof s=="string"?decodeURIComponent(s).trim():String(s||"").trim();if(!e||!t)return null;const o=(e.split("_")[0]||"").trim(),n=jt(t);if(!o||!n||!u(n))return null;let i=[];try{i=z(n,{withFileTypes:!0})}catch{i=[]}let l=null;for(const r of i){if(!r?.isDirectory?.())continue;const c=String(r.name||"");if(!(c===e||c===o||c.startsWith(`${e}_`)||c.startsWith(`${o}_`)))continue;const f=a(n,c),h=wt(f);h&&(!l||Number(h.mtime||0)>Number(l.mtime||0))&&(l=h)}return l}catch{return null}}m.get("/api/config/check",(s,t)=>{const e=rt();t.json({exists:u(e),path:e,isProjectLevel:e.startsWith(I)})}),m.get("/api/projects",(s,t)=>{try{const e=a(w,".zibby","config.json");if(!u(e))return t.json({projects:[]});const o=JSON.parse(_(e,"utf8")),n=typeof o.sessionToken=="string"&&o.sessionToken.trim()!==""?o.sessionToken.trim():"",i=Array.isArray(o.projects)?o.projects:[];if(!n)return t.json({projects:i});const l=String(Tt()).replace(/\/$/,"");fetch(`${l}/projects`,{headers:{Authorization:`Bearer ${n}`}}).then(r=>r.json().catch(()=>({})).then(c=>({ok:r.ok,status:r.status,body:c}))).then(({ok:r,body:c})=>{if(!r)return t.json({projects:i});const h=(Array.isArray(c?.projects)?c.projects:[]).map(d=>({name:d?.name,projectId:d?.projectId,apiToken:d?.apiToken||null,createdAt:d?.createdAt||null,updatedAt:d?.updatedAt||null}));o.projects=h;try{H(e,`${JSON.stringify(o,null,2)}
11
+ `,"utf8")}catch{}return t.json({projects:h})}).catch(()=>t.json({projects:i}))}catch(e){t.status(500).json({error:e.message})}}),m.post("/api/projects/create",async(s,t)=>{try{const e=typeof s.body?.name=="string"?s.body.name.trim():"";if(!e)return t.status(400).json({error:"Project name is required"});const o=a(w,".zibby","config.json");if(!u(o))return t.status(401).json({error:"Not logged in"});const n=JSON.parse(_(o,"utf8")),i=typeof n.sessionToken=="string"&&n.sessionToken.trim()!==""?n.sessionToken.trim():"";if(!i)return t.status(401).json({error:"Not logged in"});const l=String(Tt()).replace(/\/$/,""),r=await fetch(`${l}/projects`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`},body:JSON.stringify({name:e})}),c=await r.json().catch(()=>({}));if(!r.ok)return t.status(r.status).json({error:c.error||c.message||`HTTP ${r.status}`});const f=c?.project&&typeof c.project=="object"?c.project:null;if(f?.projectId){const d=(Array.isArray(n.projects)?n.projects:[]).filter(T=>String(T?.projectId||"")!==String(f.projectId));d.push({name:f.name||e,projectId:f.projectId,apiToken:f.apiToken||c?.apiToken||null,createdAt:f.createdAt||null,updatedAt:f.updatedAt||null}),n.projects=d;try{H(o,`${JSON.stringify(n,null,2)}
12
+ `,"utf8")}catch{}}return t.json(c)}catch(e){return t.status(500).json({error:e.message||String(e)})}});function zt(s,t){const e=Array.isArray(t)?t.filter(f=>typeof f=="string"):[],o=process.argv[1]?O(process.argv[1]):"",n=o?Xt(o):"";let i;try{i=!!o&&u(o)&&L(o).isFile()&&!/\.(cmd|ps1|bat)$/i.test(o)&&(/\.(m|c)?js$/i.test(o)||n==="zibby")}catch{i=!1}if(i){const f=[o,"test",s,...e],h=[process.execPath,...f].map(d=>/\s/.test(String(d))?`"${String(d).replace(/"/g,'\\"')}"`:d).join(" ");return{useShell:!1,cmd:process.execPath,args:f,display:h}}const l=s.replace(/"/g,'\\"'),r=e.join(" "),c=`zibby test "${l}" ${r}`.trim();return{useShell:!0,cmd:c,args:[],display:c}}m.get("/api/run/result/:sessionId",(s,t)=>{const{sessionId:e}=s.params,o=G.get(e);if(!o)return t.status(404).json({pending:!0,error:"unknown_session"});t.json(o)}),m.post("/api/run",async(s,t)=>{const{task:e,args:o=[],sessionId:n,studioTestCaseId:i}=s.body;if(!e||!n)return t.status(400).json({success:!1,error:"task and sessionId are required"});const l=x(),r=a(l,n),c=O(r),f=i!=null&&String(i).trim()!==""?String(i).trim():String(n);G.set(n,{pending:!0});let h=!1,d=null;const T=()=>{if(d&&!d.destroyed)try{d.end()}catch{}d=null},$=(p,S)=>{G.delete(n),T(),h||t.status(p).json(S)},g=()=>{const p=[];return k.clients.forEach(S=>{S.sessionId===n&&p.push(S)}),p};try{const p=zt(e,o);console.log("[Studio API] Running:",p.display),p.useShell?console.warn('[Studio API] Falling back to shell "zibby" from PATH \u2014 if runs do nothing, run Studio from this repo or ensure `zibby` points to the same install.'):console.log("[Studio API] Using same CLI as this server (argv[1]):",process.argv[1]),bt(r,{recursive:!0}),d=Ht(a(r,nt),{flags:"w"}),D(c,{sessionId:String(n),studioTestCaseId:f,status:"running",runSource:"studio",activeStageIndex:0,activeNode:"preflight",cwd:I,outputBase:".zibby/output",sessionPathAbs:c});const S=st(p.cmd,p.args,{cwd:I,shell:p.useShell,env:{...process.env,ZIBBY_SESSION_ID:n,ZIBBY_SESSION_PATH:O(r)}});R.set(n,S),Ct(r,n,S.pid),D(c,{sessionId:String(n),studioTestCaseId:f,status:"running",runSource:"studio",activeStageIndex:0,activeNode:"preflight",cwd:I,outputBase:".zibby/output",sessionPathAbs:c,pid:S.pid??null});let N="",F="",dt=!1,ft=0;const pt=setInterval(()=>{try{const b=vt(r);if(!b||b.mtime<=ft)return;ft=b.mtime;const j=_(b.p).toString("base64"),P=b.p.toLowerCase().endsWith(".png")?"image/png":"image/jpeg";g().forEach(K=>{try{K.send(JSON.stringify({type:"video-frame",sessionId:n,frame:j,mime:P}))}catch{}})}catch{}},320),mt=b=>{if(dt)return;dt=!0,T();const j={pending:!1,...b};typeof j.stdout=="string"&&j.stdout.length>U&&(j.stdout=`\u2026(truncated)
13
+ ${j.stdout.slice(-U)}`),typeof j.stderr=="string"&&j.stderr.length>U&&(j.stderr=`\u2026(truncated)
14
+ ${j.stderr.slice(-U)}`),G.set(n,j),g().forEach(P=>{try{P.send(JSON.stringify({type:"run-complete",sessionId:n,result:b}))}catch{}})};S.stdout.on("data",b=>{const j=b.toString();if(N+=j,d&&!d.destroyed)try{d.write(j)}catch(P){console.error("[Studio API] studio-cli.log write failed:",P.message)}g().forEach(P=>{try{P.send(JSON.stringify({type:"stdout",data:j}))}catch{}})}),S.stderr.on("data",b=>{const j=b.toString();if(F+=j,d&&!d.destroyed)try{d.write(`[stderr] ${j}`)}catch(P){console.error("[Studio API] studio-cli.log write failed:",P.message)}g().forEach(P=>{try{P.send(JSON.stringify({type:"stderr",data:j}))}catch{}})}),S.on("error",b=>{clearInterval(pt),R.delete(n),D(c,{sessionId:String(n),studioTestCaseId:f,status:"failed",runSource:"studio",activeNode:null,activeStageIndex:null,errorMessage:b.message,cwd:I,outputBase:".zibby/output",sessionPathAbs:c}),console.error("[Studio API] Spawn error:",b.message),mt({heldUntilExit:!0,success:!1,exitCode:null,error:b.message,stderr:F,stdout:N,runName:null,videoPath:null,eventsPath:null,codegenFiles:null,metadata:{sessionId:n,task:e}})}),S.on("close",b=>{clearInterval(pt),R.delete(n),D(c,{sessionId:String(n),studioTestCaseId:f,status:b===0?"completed":b===130||b===143?"interrupted":"failed",runSource:"studio",activeNode:null,activeStageIndex:null,exitCode:b,cwd:I,outputBase:".zibby/output",sessionPathAbs:c}),B(r),g().forEach(A=>{try{A.send(JSON.stringify({type:"exit",code:b}))}catch{}});const j=b===0;let P=null;const K=a(r,".session-info.json"),gt=a(r,"result.json");if(u(K))try{const A=JSON.parse(_(K,"utf8"));P=A.name||A.task}catch(A){console.error("[Studio API] Failed to read session info:",A.message)}else if(u(gt))try{const A=JSON.parse(_(gt,"utf8"));P=A.name||A.task}catch(A){console.error("[Studio API] Failed to read result:",A.message)}const Wt=[/\[SessionRecorder\] Run name set: (.+)/,/Run name: (.+)/];if(!P)for(const A of Wt){const St=N.match(A);if(St){P=St[1].trim();break}}const Jt=q(r,I),tt=Nt(r),ht=a(r,"events.json"),yt=a(r,"execute_live","events.json"),et=u(ht)?ht:u(yt)?yt:null;mt({heldUntilExit:!0,success:j,exitCode:b,stdout:N,stderr:F,runName:P,videoPath:tt&&u(tt)?tt:null,eventsPath:et&&u(et)?et:null,codegenFiles:Jt,metadata:{sessionId:n,task:e}})}),h=!0,t.status(202).json({accepted:!0,sessionId:n,pid:S.pid!=null&&Number.isFinite(Number(S.pid))?Number(S.pid):null,pollPath:`/api/run/result/${n}`,message:"Run started. Poll GET /api/run/result/:sessionId until pending is false."})}catch(p){$(500,{success:!1,error:p.message})}}),m.post("/api/stop/:sessionId",(s,t)=>{const{sessionId:e}=s.params,o=s.body&&s.body.pid!=null?s.body.pid:null;if(Lt(e,o))return t.json({success:!0});t.status(404).json({success:!1,error:"Session not found"})}),m.get("/api/recordings",(s,t)=>{try{const e=x();if(!u(e))return t.json({recordings:[],path:e});const n=z(e).map(i=>{const l=a(e,i),r=a(l,".session-info.json"),c=a(l,"result.json");let f={};return u(r)?f=JSON.parse(_(r,"utf8")):u(c)&&(f=JSON.parse(_(c,"utf8"))),{sessionId:i,...f,path:l}});t.json({path:e,recordings:n})}catch(e){t.status(500).json({error:e.message})}}),m.get("/api/sessions/:sessionId/cli-log",(s,t)=>{try{const{sessionId:e}=s.params;let o=parseInt(String(s.query.offset||"0"),10);(Number.isNaN(o)||o<0)&&(o=0);const n=M(s),i=C(e,n),l=a(i||a(x(),e),nt);if(!u(l))return t.json({exists:!1,content:"",size:0,fromByte:0,nextOffset:0});const r=L(l).size;o>r&&(o=r);let c=o,f=r-c;f>Z&&(c=r-Z,f=Z);const h=Buffer.alloc(f),d=Zt(l,"r");try{Vt(d,h,0,f,c)}finally{qt(d)}t.json({exists:!0,content:h.toString("utf8"),size:r,fromByte:c,nextOffset:r})}catch(e){t.status(500).json({error:e.message})}}),m.get("/api/sessions/:sessionId/events",(s,t)=>{try{const{sessionId:e}=s.params,o=M(s),n=C(e,o);if(!n)return t.json({events:[]});let i=a(n,"events.json");if(!u(i)){const r=a(n,"execute_live","events.json");if(u(r))i=r;else return t.json({events:[]})}const l=JSON.parse(_(i,"utf8"));t.json({events:l})}catch(e){t.status(500).json({error:e.message})}}),m.get("/api/sessions/:sessionId/live-preview",(s,t)=>{try{const{sessionId:e}=s.params,o=M(s),n=C(e,o);if(!n)return t.json({ok:!1,error:"session_not_found"});const l=wt(n)||$t(e,n);if(!l)return t.json({ok:!1,error:"no_frame",sessionId:e,sessionPath:n});t.json({ok:!0,frame:l.base64,mime:l.mime,mtime:l.mtime,path:l.path})}catch(e){t.status(500).json({ok:!1,error:e.message})}}),m.get("/api/sessions/:sessionId/video",(s,t)=>{try{const{sessionId:e}=s.params,o=M(s),n=C(e,o);if(!n)return t.status(404).json({error:"Session not found"});const i=Nt(n);if(!i||!u(i))return t.status(404).json({error:"Video not found"});const l=L(i);t.writeHead(200,{"Content-Type":"video/webm","Content-Length":l.size}),Yt(i).pipe(t)}catch(e){t.status(500).json({error:e.message})}}),m.get("/api/sessions/:sessionId/codegen/playwright",(s,t)=>{try{const{sessionId:e}=s.params,o=C(e)||a(x(),e);if(!u(o))return t.status(404).type("text/plain").send("Session not found");const n=q(o,I);if(!n?.playwrightFile||!u(n.playwrightFile))return t.status(404).type("text/plain").send("No Playwright artifact");t.type("text/plain; charset=utf-8").send(_(n.playwrightFile,"utf8"))}catch(e){t.status(500).type("text/plain").send(e.message)}}),m.get("/api/sessions/:sessionId/codegen/selenium",(s,t)=>{try{const{sessionId:e}=s.params,o=C(e)||a(x(),e);if(!u(o))return t.status(404).type("text/plain").send("Session not found");const n=q(o,I);if(!n?.seleniumFile||!u(n.seleniumFile))return t.status(404).type("text/plain").send("No Selenium artifact");t.type("text/plain; charset=utf-8").send(_(n.seleniumFile,"utf8"))}catch(e){t.status(500).type("text/plain").send(e.message)}}),m.post("/api/init",async(s,t)=>{const{agentType:e,apiKey:o}=s.body;try{const n=`zibby init --agent ${e} --headed`,i=st(n,[],{cwd:I,env:{...process.env,CURSOR_API_KEY:e==="cursor"?o:process.env.CURSOR_API_KEY,ANTHROPIC_API_KEY:e==="claude"?o:process.env.ANTHROPIC_API_KEY,OPENAI_API_KEY:e==="codex"?o:process.env.OPENAI_API_KEY,GEMINI_API_KEY:e==="gemini"?o:process.env.GEMINI_API_KEY,GOOGLE_API_KEY:e==="gemini"?o:process.env.GOOGLE_API_KEY,CI:"true",ZIBBY_CI:"true"},shell:!0});let l="",r="";i.stdout.on("data",d=>{l+=d.toString()}),i.stderr.on("data",d=>{r+=d.toString()});let c=!1;const f=(d,T)=>{if(!c)if(c=!0,d===0)t.json({success:!0,stdout:l});else{const $=T||r||"zibby init failed";t.status(500).json({success:!1,error:$,stdout:l})}},h=setTimeout(()=>{try{i.kill()}catch{}f(1,`zibby init timed out after 3 minutes. stdout so far:
15
+ ${l}
16
+ stderr:
17
+ ${r}`)},18e4);i.on("close",d=>{clearTimeout(h),f(d)}),i.on("error",d=>{clearTimeout(h),f(1,d.message)})}catch(n){t.status(500).json({success:!1,error:n.message})}});const Q=(s,t)=>{const e={type:s,message:t,time:new Date().toLocaleTimeString()};k.clients.forEach(o=>{if(o.listenType==="console")try{o.send(JSON.stringify(e))}catch{}})},Bt=console.log,Dt=console.error,Gt=console.warn,X=s=>s.map(t=>{if(t==null)return String(t);if(typeof t=="object")try{return ee(t,{depth:4,colors:!1,breakLength:100})}catch{return String(t)}return String(t)}).join(" ");console.log=(...s)=>{Bt(...s),Q("info",X(s))},console.error=(...s)=>{Dt(...s),Q("error",X(s))},console.warn=(...s)=>{Gt(...s),Q("warn",X(s))},k.on("connection",(s,t)=>{const e=t.url;if(e.includes("/console"))s.listenType="console",console.log("[WebSocket] Console listener connected"),s.send(JSON.stringify({type:"success",message:"\u{1F3AD} Connected to Studio server logs",time:new Date().toLocaleTimeString()}));else{let o=e.split("/").pop()||"";try{o=decodeURIComponent(o)}catch{}s.sessionId=o,s.listenType="session",console.log(`[WebSocket] Session listener connected: ${o}`)}s.on("close",()=>{s.listenType==="console"?console.log("[WebSocket] Console listener disconnected"):console.log(`[WebSocket] Session listener disconnected: ${s.sessionId}`)})}),m.get("/api/workflow/graph",async(s,t)=>{try{const e=a(I,".zibby","graph.mjs");if(!u(e))return console.warn("[Workflow Graph API] Graph file not found at",e),t.json({graph:null,error:"No graph.mjs found in .zibby folder"});const o=await import(`${ne(e).href}?t=${Date.now()}`),n=o.default||Object.values(o)[0];if(!n||typeof n!="function")return console.error("[Workflow Graph API] Invalid graph module, got:",typeof n),t.json({graph:null,error:"Invalid graph module"});const l=new n().buildGraph(),r=l.toJSON?l.toJSON():l.serialize();console.log(`[Workflow Graph API] graph OK nodes=${r.nodes?.length??0} edges=${r.edges?.length??0} (${e})`),t.json({graph:r})}catch(e){console.error("[Workflow Graph API] Failed to load workflow graph:",e),t.status(500).json({graph:null,error:e.message})}});const Ut=()=>{ot&&console.log(`
15
18
  \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\u2557
16
19
  \u2551 Zibby Studio \u2551
17
20
  \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\u255D
@@ -21,4 +24,4 @@ ${j.stderr.slice(-G)}`),D.set(n,j),h().forEach(P=>{try{P.send(JSON.stringify({ty
21
24
 
22
25
  Press Ctrl+C to stop
23
26
  `),v.open!==!1&&Rt().catch(s=>{console.error(`Failed to launch Zibby Studio: ${s.message}`)})};let at=!1;function lt(){const s=async t=>{if(y.removeListener("error",s),t.code==="EADDRINUSE"&&!at){at=!0,console.log(`[Studio] Port ${E} still busy \u2014 forcing cleanup and retrying...`),await _t(E),lt();return}console.error("[Studio] Server error:",t.message),process.exit(1)};y.once("error",s),y.listen(E,()=>{y.removeListener("error",s),y.on("error",t=>{console.error("[Studio] Runtime error:",t.message)}),Ut()})}await _t(E),lt();const ut=()=>{console.log(`
24
- Stopping Studio...`);const s=x();for(const t of R.keys()){const e=O(a(s,t));try{B(e,{status:"interrupted",runSource:"studio",activeNode:null,activeStageIndex:null,exitReason:"studio-shutdown"})}catch{}try{z(a(s,t))}catch{}}R.forEach(t=>t.kill()),y.close(()=>{console.log("Studio stopped"),process.exit(0)})};process.on("SIGINT",ut),process.on("SIGTERM",ut)}export{ke as studioCommand};
27
+ Stopping Studio...`);const s=x();for(const t of R.keys()){const e=O(a(s,t));try{D(e,{status:"interrupted",runSource:"studio",activeNode:null,activeStageIndex:null,exitReason:"studio-shutdown"})}catch{}try{B(a(s,t))}catch{}}R.forEach(t=>t.kill()),y.close(()=>{console.log("Studio stopped"),process.exit(0)})};process.on("SIGINT",ut),process.on("SIGTERM",ut)}export{ke as studioCommand};
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/cli",
3
- "version": "0.1.46",
3
+ "version": "0.1.47",
4
4
  "description": "Zibby CLI - Test automation generator and runner",
5
5
  "type": "module",
6
6
  "bin": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/cli",
3
- "version": "0.1.46",
3
+ "version": "0.1.47",
4
4
  "description": "Zibby CLI - Test automation generator and runner",
5
5
  "type": "module",
6
6
  "bin": {