@zibby/core 0.1.26 → 0.1.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/constants.js CHANGED
@@ -1 +1 @@
1
- const o={ASSISTANT:"gpt-5.4-nano-2026-03-17",CLAUDE:"claude-sonnet-4-6",CURSOR:"auto",CODEX:"o4-mini",GEMINI:"gemini-2.5-pro",OPENAI_POSTPROCESSING:"gpt-4o-mini"},n={ASSISTANT:"assistant",CLAUDE:"claude",CURSOR:"cursor",CODEX:"codex",GEMINI:"gemini"},e={DEBUG:"debug",INFO:"info",WARN:"warn",ERROR:"error",SILENT:"silent"},t={auto:"claude-sonnet-4-6","sonnet-4.6":"claude-sonnet-4-6","sonnet-4-6":"claude-sonnet-4-6","opus-4.6":"claude-opus-4-6","opus-4-6":"claude-opus-4-6","sonnet-4.5":"claude-sonnet-4-5-20250929","sonnet-4-5":"claude-sonnet-4-5-20250929","opus-4.5":"claude-opus-4-20250514","opus-4-5":"claude-opus-4-20250514","claude-sonnet-4-6":"claude-sonnet-4-6","claude-opus-4-6":"claude-opus-4-6","claude-sonnet-4-5-20250929":"claude-sonnet-4-5-20250929","claude-opus-4-20250514":"claude-opus-4-20250514"},s={auto:"o4-mini","o4-mini":"o4-mini",o3:"o3","o3-mini":"o3-mini","codex-mini":"codex-mini-latest","gpt-4o":"gpt-4o","gpt-4o-mini":"gpt-4o-mini","gpt-5.2-codex":"gpt-5.2-codex","gpt-5.2":"gpt-5.2","gpt-5.3":"gpt-5.3","gpt-5.4":"gpt-5.4"},i={auto:"gemini-2.5-pro","gemini-2.5-pro":"gemini-2.5-pro","gemini-2.5-flash":"gemini-2.5-flash"},p={CURSOR_AGENT_DEFAULT:1200*1e3,OPENAI_REQUEST:3e4};export{n as AGENT_TYPES,t as CLAUDE_MODEL_MAP,s as CODEX_MODEL_MAP,o as DEFAULT_MODELS,i as GEMINI_MODEL_MAP,e as LOG_LEVELS,p as TIMEOUTS};
1
+ const o={ASSISTANT:"gpt-5.4-nano-2026-03-17",CLAUDE:"claude-sonnet-4-6",CURSOR:"auto",CODEX:"o4-mini",GEMINI:"gemini-2.5-pro",OPENAI_POSTPROCESSING:"gpt-4o-mini"},n={ASSISTANT:"assistant",CLAUDE:"claude",CURSOR:"cursor",CODEX:"codex",GEMINI:"gemini"},e={DEBUG:"debug",INFO:"info",WARN:"warn",ERROR:"error",SILENT:"silent"},t={auto:"claude-sonnet-4-6","sonnet-4.6":"claude-sonnet-4-6","sonnet-4-6":"claude-sonnet-4-6","opus-4.6":"claude-opus-4-6","opus-4-6":"claude-opus-4-6","sonnet-4.5":"claude-sonnet-4-5-20250929","sonnet-4-5":"claude-sonnet-4-5-20250929","opus-4.5":"claude-opus-4-20250514","opus-4-5":"claude-opus-4-20250514","claude-sonnet-4-6":"claude-sonnet-4-6","claude-opus-4-6":"claude-opus-4-6","claude-sonnet-4-5-20250929":"claude-sonnet-4-5-20250929","claude-opus-4-20250514":"claude-opus-4-20250514"},s={auto:"o4-mini","o4-mini":"o4-mini",o3:"o3","o3-mini":"o3-mini","codex-mini":"codex-mini-latest","gpt-4o":"gpt-4o","gpt-4o-mini":"gpt-4o-mini","gpt-5.2-codex":"gpt-5.2-codex","gpt-5.2":"gpt-5.2","gpt-5.3":"gpt-5.3","gpt-5.4":"gpt-5.4"},i={auto:"gemini-2.5-pro","gemini-2.5-pro":"gemini-2.5-pro","gemini-2.5-flash":"gemini-2.5-flash"},p={CURSOR_AGENT_DEFAULT:1200*1e3,OPENAI_REQUEST:18e4};export{n as AGENT_TYPES,t as CLAUDE_MODEL_MAP,s as CODEX_MODEL_MAP,o as DEFAULT_MODELS,i as GEMINI_MODEL_MAP,e as LOG_LEVELS,p as TIMEOUTS};
@@ -1,4 +1,4 @@
1
- import{AgentStrategy as Y}from"./base.js";import{spawn as q,execSync as N}from"child_process";import{writeFileSync as j,readFileSync as J,mkdirSync as z,existsSync as H,accessSync as B,constants as W,unlinkSync as V}from"fs";import{join as C,resolve as X}from"path";import{homedir as D}from"os";import{logger as t}from"../../utils/logger.js";import{DEFAULT_MODELS as Q,TIMEOUTS as ee}from"../../constants.js";import{DEFAULT_OUTPUT_BASE as te,SESSION_INFO_FILE as re,STUDIO_STOP_REQUEST_FILE as oe}from"../constants.js";import{getAllSkills as ne,getSkill as Z}from"../skill-registry.js";import{StreamingParser as se}from"../../utils/streaming-parser.js";import{StructuredOutputFormatter as ie}from"./utils/structured-output-formatter.js";import{formatWithOpenAIProxy as le}from"./utils/openai-proxy-formatter.js";import{timeline as G}from"../../utils/timeline.js";import{shouldUseIsolatedCursorMcpHome as ae,createIsolatedCursorAgentHome as ue,removeIsolatedCursorAgentHome as ce}from"../../utils/cursor-mcp-isolated-home.js";class Ee extends Y{constructor(){super("cursor","Cursor (CLI)",100)}canHandle(s){const l=[C(D(),".local","bin","cursor-agent"),C(D(),".cursor","bin","cursor-agent"),"/usr/local/bin/cursor-agent","/usr/local/bin/agent","/Applications/Cursor.app/Contents/Resources/app/bin/cursor","agent","cursor-agent"];for(const r of l)try{if(r.startsWith("/")){B(r,W.X_OK);const f=N(`"${r}" --version 2>&1`,{encoding:"utf-8",timeout:3e3,stdio:"pipe"});if(f&&f.length>0)return t.debug(`[Cursor] Found agent at: ${r} (version: ${f.trim().slice(0,50)})`),!0}else{const f=N(`which ${r}`,{encoding:"utf-8",timeout:2e3,stdio:"pipe"}).trim();if(!f)continue;const a=N(`${r} --version 2>&1`,{encoding:"utf-8",timeout:3e3,stdio:"pipe"});if(a&&a.length>0)return t.debug(`[Cursor] Found '${r}' in PATH at ${f} (version: ${a.trim().slice(0,50)})`),!0}}catch{continue}return t.warn("[Cursor] \u274C Cursor Agent CLI not found or not working. Run: agent --version"),!1}async invoke(s,l={}){const{workspace:r=process.cwd(),print:f=!1,schema:a=null,skills:b=null,sessionPath:u=null,nodeName:g=null,timeout:m=ee.CURSOR_AGENT_DEFAULT,config:h={}}=l,$=h?.agent?.strictMode||!1,v=l.model??h?.agent?.cursor?.model??Q.CURSOR;t.debug(`[Cursor] Invoking (model: ${v}, timeout: ${m/1e3}s, skills: ${JSON.stringify(b)})`);const E=(this._setupMcpConfig(u,r,h,b,g)||{}).isolatedMcpHome??null,S=[C(D(),".local","bin","cursor-agent"),C(D(),".cursor","bin","cursor-agent"),"/usr/local/bin/cursor-agent","/usr/local/bin/agent","/Applications/Cursor.app/Contents/Resources/app/bin/cursor","agent","cursor-agent"];let n=null;for(const c of S)try{if(c.startsWith("/"))B(c,W.X_OK),N(`"${c}" --version 2>&1`,{encoding:"utf-8",timeout:3e3,stdio:"pipe"});else{if(!N(`which ${c}`,{encoding:"utf-8",timeout:2e3}).trim())throw new Error("not in PATH");N(`${c} --version 2>&1`,{encoding:"utf-8",timeout:3e3,stdio:"pipe"})}n=c,t.debug(`[Agent] Using binary: ${c}`);break}catch(w){t.debug(`[Agent] Binary '${c}' check failed: ${w.message}`);continue}if(!n)throw new Error(`Cursor Agent CLI not found or not working.
1
+ import{AgentStrategy as Y}from"./base.js";import{spawn as q,execSync as N}from"child_process";import{writeFileSync as j,readFileSync as J,mkdirSync as z,existsSync as H,accessSync as B,constants as W,unlinkSync as V}from"fs";import{join as C,resolve as X}from"path";import{homedir as D}from"os";import{logger as t}from"../../utils/logger.js";import{DEFAULT_MODELS as Q,TIMEOUTS as ee}from"../../constants.js";import{DEFAULT_OUTPUT_BASE as te,SESSION_INFO_FILE as re,STUDIO_STOP_REQUEST_FILE as oe}from"../constants.js";import{getAllSkills as ne,getSkill as Z}from"../skill-registry.js";import{StreamingParser as se}from"../../utils/streaming-parser.js";import{StructuredOutputFormatter as ie}from"./utils/structured-output-formatter.js";import{formatWithOpenAIProxy as le}from"./utils/openai-proxy-formatter.js";import{timeline as G}from"../../utils/timeline.js";import{shouldUseIsolatedCursorMcpHome as ae,createIsolatedCursorAgentHome as ue,removeIsolatedCursorAgentHome as ce}from"../../utils/cursor-mcp-isolated-home.js";class Ee extends Y{constructor(){super("cursor","Cursor (CLI)",100)}canHandle(i){const l=[C(D(),".local","bin","cursor-agent"),C(D(),".cursor","bin","cursor-agent"),"/usr/local/bin/cursor-agent","/usr/local/bin/agent","/Applications/Cursor.app/Contents/Resources/app/bin/cursor","agent","cursor-agent"];for(const r of l)try{if(r.startsWith("/")){B(r,W.X_OK);const f=N(`"${r}" --version 2>&1`,{encoding:"utf-8",timeout:3e3,stdio:"pipe"});if(f&&f.length>0)return t.debug(`[Cursor] Found agent at: ${r} (version: ${f.trim().slice(0,50)})`),!0}else{const f=N(`which ${r}`,{encoding:"utf-8",timeout:2e3,stdio:"pipe"}).trim();if(!f)continue;const a=N(`${r} --version 2>&1`,{encoding:"utf-8",timeout:3e3,stdio:"pipe"});if(a&&a.length>0)return t.debug(`[Cursor] Found '${r}' in PATH at ${f} (version: ${a.trim().slice(0,50)})`),!0}}catch{continue}return t.warn("[Cursor] \u274C Cursor Agent CLI not found or not working. Run: agent --version"),!1}async invoke(i,l={}){const{workspace:r=process.cwd(),print:f=!1,schema:a=null,skills:b=null,sessionPath:u=null,nodeName:g=null,timeout:m=ee.CURSOR_AGENT_DEFAULT,config:h={}}=l,$=h?.agent?.strictMode||!1,v=l.model??h?.agent?.cursor?.model??Q.CURSOR;t.debug(`[Cursor] Invoking (model: ${v}, timeout: ${m/1e3}s, skills: ${JSON.stringify(b)})`);const E=(this._setupMcpConfig(u,r,h,b,g)||{}).isolatedMcpHome??null,S=[C(D(),".local","bin","cursor-agent"),C(D(),".cursor","bin","cursor-agent"),"/usr/local/bin/cursor-agent","/usr/local/bin/agent","/Applications/Cursor.app/Contents/Resources/app/bin/cursor","agent","cursor-agent"];let n=null;for(const c of S)try{if(c.startsWith("/"))B(c,W.X_OK),N(`"${c}" --version 2>&1`,{encoding:"utf-8",timeout:3e3,stdio:"pipe"});else{if(!N(`which ${c}`,{encoding:"utf-8",timeout:2e3}).trim())throw new Error("not in PATH");N(`${c} --version 2>&1`,{encoding:"utf-8",timeout:3e3,stdio:"pipe"})}n=c,t.debug(`[Agent] Using binary: ${c}`);break}catch(y){t.debug(`[Agent] Binary '${c}' check failed: ${y.message}`);continue}if(!n)throw new Error(`Cursor Agent CLI not found or not working.
2
2
 
3
3
  Checked paths:
4
4
  ${S.map(c=>` - ${c}`).join(`
@@ -10,23 +10,23 @@ Install cursor-agent:
10
10
  Then add to PATH:
11
11
  echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc
12
12
 
13
- Test with: agent --version`);let y=null;if(a){const c=`zibby-result-${Date.now()}.json`;y=C(r,".zibby","tmp",c);const w=C(r,".zibby","tmp");H(w)||z(w,{recursive:!0});const T=ie.generateFileOutputInstructions(a,y);s=`${s}
13
+ Test with: agent --version`);let w=null;if(a){const c=`zibby-result-${Date.now()}.json`;w=C(r,".zibby","tmp",c);const y=C(r,".zibby","tmp");H(y)||z(y,{recursive:!0});const T=ie.generateFileOutputInstructions(a,w);i=`${i}
14
14
 
15
15
  ${T}`}const P=process.env.CURSOR_API_KEY,x=P?` | key: ***${P.slice(-4)}`:" | key: not set";console.log(`
16
16
  \u25C6 Model: ${v||"auto"}${x}
17
17
  `);const M=(await import("chalk")).default;console.log(`
18
- ${M.bold("Prompt sent to LLM:")}`),console.log(M.dim("\u2500".repeat(60))),console.log(M.dim(s)),console.log(M.dim("\u2500".repeat(60)));const L=["--print","--force","--approve-mcps","--output-format","stream-json","--stream-partial-output","--model",v||"auto"];if(process.env.CURSOR_API_KEY&&L.push("--api-key",process.env.CURSOR_API_KEY),L.push(s),t.debug(`[Agent] Prompt: ${s.length} chars, model: ${v||"auto"}`),t.debug(`[Agent] Workspace: ${r}`),process.env.LOG_LEVEL==="debug"||process.env.ZIBBY_LOG_CURSOR_CLI==="1")try{console.log(`\u{1F527} Cursor CLI --model ${v||"auto"} (from .zibby.config.js agent.cursor.model)
19
- `)}catch{}let _,o=null;try{const c=u||(process.env.ZIBBY_SESSION_PATH?String(process.env.ZIBBY_SESSION_PATH).trim():null);_=await this._spawnWithStreaming(n,L,r,m,null,c,E)}catch(c){o=c}const A=_?.stdout||"";if(a){const c=typeof a.parse=="function";let w=null;const T=!!(y&&H(y));if(y&&t.info(`[Agent] Result file: ${T?"present":"missing"} at ${y}`),T)try{const e=J(y,"utf-8").trim();w=JSON.parse(e),t.info(`[Agent] Parsed JSON from result file OK (${e.length} chars) \u2192 object ready for validation`),o&&t.debug("[Agent] Agent exited non-zero but result file was written \u2014 recovering")}catch(e){t.warn(`\u26A0\uFE0F [Agent] Result file exists on disk but is not valid JSON: ${e.message}`)}else if(o)t.warn(`[Agent] Result file missing at ${y} (agent process error \u2014 may still recover if strictMode repairs)`);else throw t.error(`\u274C [Agent] Result file was never created at ${y}`),new Error(`Agent did not write required result file at ${y}`);if(w&&c)try{const e=a.parse(w);return t.info("\u2705 [Agent] Zod validation passed for structured result file"),$&&t.debug("[Agent] strictMode enabled but not needed \u2014 agent wrote valid file"),{raw:A,structured:e}}catch(e){t.warn(`\u26A0\uFE0F [Agent] JSON parsed but Zod rejected it (wrong types/shape): ${e.message?.slice(0,400)}`)}else{if(w)return t.info("\u2705 [Agent] File-based output extracted (no Zod parse fn) \u2014 accepting as structured"),$&&t.debug("[Agent] strictMode enabled but not needed \u2014 agent wrote valid file"),{raw:A,structured:w};T&&t.error("\u274C [Agent] Result file exists but produced no in-memory JSON (parse failed earlier)")}if($&&!o){const e=_.parsedText,d=w?JSON.stringify(w):e;t.info(`[Agent] strictMode: calling OpenAI proxy to fix structured output (${d.length} chars in)`);const i=await le(d,a);if(c){const p=a.parse(i.structured);return t.info("\u2705 [Agent] Proxy output passed Zod validation"),{raw:A,structured:p}}return{raw:A,...i}}if(o)throw o;const k=T?w==null?"file existed but JSON.parse failed \u2014 see WARN log above":c?"JSON was valid but Zod validation failed \u2014 see WARN log above":"no structured object after read (unexpected)":"file never appeared (agent may not have run Write tool to the path above)";throw t.error(`\u274C [Agent] No validated structured output: ${k}`),t.error("\u{1F4A1} Tip: Set strictMode=true in .zibby.config.js for OpenAI proxy fallback"),new Error(`Agent did not produce a valid result file at ${y}. Enable strictMode for proxy fallback.`)}if(o)throw o;return this._extractFinalResult(A)||_?.parsedText||A}_extractFinalResult(s){if(!s)return null;const l=s.split(`
20
- `);let r=null;for(const f of l){const a=f.trim();if(a)try{const b=JSON.parse(a);if(b.type==="assistant"&&b.message?.content){const u=b.message.content;if(Array.isArray(u)){const g=u.filter(m=>m.type==="text"&&m.text).map(m=>m.text).join("");g&&(r=g)}else typeof u=="string"&&u&&(r=u)}}catch{}}return r?.trim()||null}_setupMcpConfig(s,l,r,f=null,a=null){const b=r?.headless,u=C(D(),".cursor"),g=C(u,"mcp.json");let m={};if(H(g))try{m=JSON.parse(J(g,"utf-8"))}catch{}const h=m.mcpServers||{},$=r?.paths?.output||te,v=C(l||process.cwd(),$,re),I=Array.isArray(f)?f.map(n=>Z(n)).filter(Boolean):[...ne()].map(([,n])=>n),E=new Set;for(const n of I)typeof n.resolve=="function"&&(E.has(n.serverName)||(E.add(n.serverName),this._ensureSkillConfigured(h,n,s,v,a,b)));if(s){const n=Z("browser");n&&typeof n.resolve=="function"&&!E.has(n.serverName)&&this._ensureSkillConfigured(h,n,s,v,"execute_live",b)}if(Object.keys(h).length===0)return t.debug("[MCP] No MCP servers configured - agent will run without tool access"),{isolatedMcpHome:null};const S=`${JSON.stringify({mcpServers:h},null,2)}
21
- `;if(ae(s)){const n=ue(l||process.cwd()),y=C(n,".cursor","mcp.json");return j(y,S,"utf8"),t.debug(`[MCP] Isolated cursor-agent HOME (session-scoped mcp.json): ${n} | servers: ${Object.keys(h).join(", ")}`),{isolatedMcpHome:n}}return H(u)||z(u,{recursive:!0}),j(g,S,"utf8"),t.debug(`[MCP] Global ~/.cursor/mcp.json | servers: ${Object.keys(h).join(", ")}`),{isolatedMcpHome:null}}_ensureSkillConfigured(s,l,r,f,a=null,b){const u=l.cursorKey||l.serverName,g=s[u]?u:s[l.serverName]?l.serverName:null;if(g&&r){const h=typeof l.resolve=="function"?l.resolve({sessionPath:r,nodeName:a,headless:b}):null;h?.args?s[g].args=h.args:s[g].args=(s[g].args||[]).map(I=>I.startsWith("--output-dir=")?`--output-dir=${r}`:I);const $=h?.env||{},v=l.sessionEnvKey?{[l.sessionEnvKey]:f}:{};s[g].env={...s[g].env||{},...$,...v},t.debug(`[MCP] Updated ${g} session \u2192 ${r}`);return}if(g)return;const m=l.resolve({sessionPath:r,nodeName:a,headless:b});m&&(s[u]={...m,...l.sessionEnvKey&&{env:{...m.env||{},[l.sessionEnvKey]:f}}},t.debug(`[MCP] Configured ${u}`))}_spawnWithStreaming(s,l,r,f,a=null,b=null,u=null){return new Promise((g,m)=>{const h=Date.now();let $="",v="",I=Date.now(),E=0,S=!1,n=null,y=!1,P=!1,x=null;if(b)try{x=C(X(String(b)),oe)}catch{x=null}let M=!1;const L=()=>{M||(M=!0,ce(u))},_={...process.env};u&&(_.HOME=u,process.platform==="win32"&&(_.USERPROFILE=u),t.debug(`[Agent] cursor-agent HOME=${u} (isolated MCP config)`));const o=q(s,l,{cwd:r,shell:!1,stdio:["pipe","pipe","pipe"],env:_});t.debug(`[Agent] PID: ${o.pid}`),o.stdin.on("error",e=>{e.code!=="EPIPE"&&t.warn(`[Agent] stdin error: ${e.message}`)}),o.stdout.on("error",e=>{e.code!=="EPIPE"&&t.warn(`[Agent] stdout error: ${e.message}`)}),o.stderr.on("error",e=>{e.code!=="EPIPE"&&t.warn(`[Agent] stderr error: ${e.message}`)}),a?(o.stdin.write(a,e=>{e&&e.code!=="EPIPE"&&t.warn(`[Agent] Failed to write to stdin: ${e.message}`),o.stdin.end()}),t.debug(`[Agent] Prompt also piped to stdin (${a.length} chars)`)):o.stdin.end();let A=null;x&&(A=setInterval(()=>{if(!(S||P))try{if(H(x)){S=!0,n="studio-stop";try{V(x)}catch{}t.warn("\u{1F6D1} Studio stop requested \u2014 terminating Cursor agent (and MCP browser session)"),o.kill("SIGTERM"),setTimeout(()=>{o.killed||o.kill("SIGKILL")},2e3)}}catch{}},600));const F=new Set,c=new Date(h).toISOString().replace(/\.\d+Z$/,""),w=setInterval(()=>{const e=Math.round((Date.now()-h)/1e3),d=Math.round((Date.now()-I)/1e3),i=[];try{const R=Math.ceil(e/60)+1,O=N(`find "${r}" -type f -mmin -${R} -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/target/*' 2>/dev/null | head -20`,{encoding:"utf-8",timeout:5e3}).trim();if(O)for(const U of O.split(`
22
- `)){const K=U.replace(`${r}/`,"");F.has(K)||(F.add(K),i.push(K))}}catch{}let p="";i.length>0&&(p=` | \u{1F4C1} new: ${i.map(O=>O.split("/").pop()).join(", ")}`),F.size>0&&(p+=` | \u{1F4E6} total: ${F.size} files`),t.debug(`\u{1F493} [Agent] Running for ${e}s | ${E} lines output${p}`),E===0&&e>=30&&F.size===0&&(e<35&&t.warn(`\u26A0\uFE0F [Agent] No output after ${e}s \u2014 agent may be stuck. Check your CURSOR_API_KEY.`),e>=60&&(S=!0,n=n||"stall",t.error(`\u274C [Agent] No response after ${e}s \u2014 killing. Verify CURSOR_API_KEY is valid and agent CLI works: agent --version`),o.kill("SIGTERM"),setTimeout(()=>{o.killed||o.kill("SIGKILL")},3e3)))},3e4),T=setTimeout(()=>{S=!0,n=n||"timeout";const e=Math.round((Date.now()-h)/1e3);t.error(`\u23F1\uFE0F [Agent] Timeout after ${e}s \u2014 killing process (PID: ${o.pid})`),$.trim()&&t.warn(`\u{1F4E4} [Agent] Partial output (${$.length} chars) before timeout:
23
- ${$.slice(-2e3)}`),o.kill("SIGTERM"),setTimeout(()=>{o.killed||o.kill("SIGKILL")},5e3)},f),k=new se;k.onToolCall=(e,d)=>{let i=e,p=d;if(e==="mcpToolCall"&&d?.name)i=d.name.replace(/^mcp_+[^_]+_+/,""),i.includes("-")&&i.split("-")[0]===i.split("-")[1]&&(i=i.split("-")[0]),p=d.args??d.input??d;else{if(e==="readToolCall"||e==="editToolCall"||e==="writeToolCall")return;(e.startsWith("mcp__")||e.includes("ToolCall"))&&(i=e.replace(/^mcp_+[^_]+_+/,"").replace(/ToolCall$/,""))}if(i.includes("memory")?G.stepMemory(`Tool: ${i}`):G.stepTool(`Tool: ${i}`),p!=null&&typeof p=="object"&&Object.keys(p).length>0&&!P){const O=JSON.stringify(p),U=O.length>100?`${O.substring(0,100)}...`:O;console.log(` Input: ${U}`)}},o.stdout.on("data",e=>{const d=e.toString();$+=d,I=Date.now(),y||(y=!0);const i=k.processChunk(d);i&&!P&&process.stdout.write(i);const p=d.split(`
24
- `).filter(R=>R.trim());E+=p.length}),o.stderr.on("data",e=>{const d=e.toString();v+=d,I=Date.now(),y||(y=!0);const i=d.split(`
25
- `).filter(p=>p.trim());for(const p of i)t.warn(`\u26A0\uFE0F [Agent stderr] ${p}`)}),o.on("close",(e,d)=>{P=!0,L(),clearTimeout(T),clearInterval(w),A&&clearInterval(A),k.flush();const i=Math.round((Date.now()-h)/1e3);if(t.debug(`[Agent] Exited: code=${e}, signal=${d}, elapsed=${i}s, output=${$.length} chars`),S){if(n==="studio-stop"){m(new Error("Stopped from Zibby Studio"));return}m(new Error(`Cursor Agent timed out after ${i}s (limit: ${f/1e3}s). ${E} lines produced. Last output ${Math.round((Date.now()-I)/1e3)}s ago. ${$.trim()?`
18
+ ${M.bold("Prompt sent to LLM:")}`),console.log(M.dim("\u2500".repeat(60))),console.log(M.dim(i)),console.log(M.dim("\u2500".repeat(60)));const L=["--print","--force","--approve-mcps","--output-format","stream-json","--stream-partial-output","--model",v||"auto"];if(process.env.CURSOR_API_KEY&&L.push("--api-key",process.env.CURSOR_API_KEY),L.push(i),t.debug(`[Agent] Prompt: ${i.length} chars, model: ${v||"auto"}`),t.debug(`[Agent] Workspace: ${r}`),process.env.LOG_LEVEL==="debug"||process.env.ZIBBY_LOG_CURSOR_CLI==="1")try{console.log(`\u{1F527} Cursor CLI --model ${v||"auto"} (from .zibby.config.js agent.cursor.model)
19
+ `)}catch{}let _,o=null;try{const c=u||(process.env.ZIBBY_SESSION_PATH?String(process.env.ZIBBY_SESSION_PATH).trim():null);_=await this._spawnWithStreaming(n,L,r,m,null,c,E)}catch(c){o=c}const A=_?.stdout||"";if(a){const c=typeof a.parse=="function";let y=null;const T=!!(w&&H(w));if(w&&t.info(`[Agent] Result file: ${T?"present":"missing"} at ${w}`),T)try{const e=J(w,"utf-8").trim();y=JSON.parse(e),t.info(`[Agent] Parsed JSON from result file OK (${e.length} chars) \u2192 object ready for validation`),o&&t.debug("[Agent] Agent exited non-zero but result file was written \u2014 recovering")}catch(e){t.warn(`\u26A0\uFE0F [Agent] Result file exists on disk but is not valid JSON: ${e.message}`)}else if(o)t.warn(`[Agent] Result file missing at ${w} (agent process error \u2014 may still recover if strictMode repairs)`);else throw t.error(`\u274C [Agent] Result file was never created at ${w}`),new Error(`Agent did not write required result file at ${w}`);if(y&&c)try{const e=a.parse(y);return t.info("\u2705 [Agent] Zod validation passed for structured result file"),$&&t.debug("[Agent] strictMode enabled but not needed \u2014 agent wrote valid file"),{raw:A,structured:e}}catch(e){t.warn(`\u26A0\uFE0F [Agent] JSON parsed but Zod rejected it (wrong types/shape): ${e.message?.slice(0,400)}`)}else{if(y)return t.info("\u2705 [Agent] File-based output extracted (no Zod parse fn) \u2014 accepting as structured"),$&&t.debug("[Agent] strictMode enabled but not needed \u2014 agent wrote valid file"),{raw:A,structured:y};T&&t.error("\u274C [Agent] Result file exists but produced no in-memory JSON (parse failed earlier)")}if($&&!o){const e=_.parsedText,d=y?JSON.stringify(y):e;t.info(`[Agent] strictMode: calling OpenAI proxy to fix structured output (${d.length} chars in)`);try{const s=await le(d,a);if(c){const p=a.parse(s.structured);return t.info("\u2705 [Agent] Proxy output passed Zod validation"),{raw:A,structured:p}}return{raw:A,...s}}catch(s){if(t.warn(`\u26A0\uFE0F [Agent] strictMode proxy failed: ${s.message}`),y)return t.warn("[Agent] Using agent's original result file as fallback"),{raw:A,structured:y}}}if(o)throw o;const k=T?y==null?"file existed but JSON.parse failed \u2014 see WARN log above":c?"JSON was valid but Zod validation failed \u2014 see WARN log above":"no structured object after read (unexpected)":"file never appeared (agent may not have run Write tool to the path above)";throw t.error(`\u274C [Agent] No validated structured output: ${k}`),t.error("\u{1F4A1} Tip: Set strictMode=true in .zibby.config.js for OpenAI proxy fallback"),new Error(`Agent did not produce a valid result file at ${w}. Enable strictMode for proxy fallback.`)}if(o)throw o;return this._extractFinalResult(A)||_?.parsedText||A}_extractFinalResult(i){if(!i)return null;const l=i.split(`
20
+ `);let r=null;for(const f of l){const a=f.trim();if(a)try{const b=JSON.parse(a);if(b.type==="assistant"&&b.message?.content){const u=b.message.content;if(Array.isArray(u)){const g=u.filter(m=>m.type==="text"&&m.text).map(m=>m.text).join("");g&&(r=g)}else typeof u=="string"&&u&&(r=u)}}catch{}}return r?.trim()||null}_setupMcpConfig(i,l,r,f=null,a=null){const b=r?.headless,u=C(D(),".cursor"),g=C(u,"mcp.json");let m={};if(H(g))try{m=JSON.parse(J(g,"utf-8"))}catch{}const h=m.mcpServers||{},$=r?.paths?.output||te,v=C(l||process.cwd(),$,re),I=Array.isArray(f)?f.map(n=>Z(n)).filter(Boolean):[...ne()].map(([,n])=>n),E=new Set;for(const n of I)typeof n.resolve=="function"&&(E.has(n.serverName)||(E.add(n.serverName),this._ensureSkillConfigured(h,n,i,v,a,b)));if(i){const n=Z("browser");n&&typeof n.resolve=="function"&&!E.has(n.serverName)&&this._ensureSkillConfigured(h,n,i,v,"execute_live",b)}if(Object.keys(h).length===0)return t.debug("[MCP] No MCP servers configured - agent will run without tool access"),{isolatedMcpHome:null};const S=`${JSON.stringify({mcpServers:h},null,2)}
21
+ `;if(ae(i)){const n=ue(l||process.cwd()),w=C(n,".cursor","mcp.json");return j(w,S,"utf8"),t.debug(`[MCP] Isolated cursor-agent HOME (session-scoped mcp.json): ${n} | servers: ${Object.keys(h).join(", ")}`),{isolatedMcpHome:n}}return H(u)||z(u,{recursive:!0}),j(g,S,"utf8"),t.debug(`[MCP] Global ~/.cursor/mcp.json | servers: ${Object.keys(h).join(", ")}`),{isolatedMcpHome:null}}_ensureSkillConfigured(i,l,r,f,a=null,b){const u=l.cursorKey||l.serverName,g=i[u]?u:i[l.serverName]?l.serverName:null;if(g&&r){const h=typeof l.resolve=="function"?l.resolve({sessionPath:r,nodeName:a,headless:b}):null;h?.args?i[g].args=h.args:i[g].args=(i[g].args||[]).map(I=>I.startsWith("--output-dir=")?`--output-dir=${r}`:I);const $=h?.env||{},v=l.sessionEnvKey?{[l.sessionEnvKey]:f}:{};i[g].env={...i[g].env||{},...$,...v},t.debug(`[MCP] Updated ${g} session \u2192 ${r}`);return}if(g)return;const m=l.resolve({sessionPath:r,nodeName:a,headless:b});m&&(i[u]={...m,...l.sessionEnvKey&&{env:{...m.env||{},[l.sessionEnvKey]:f}}},t.debug(`[MCP] Configured ${u}`))}_spawnWithStreaming(i,l,r,f,a=null,b=null,u=null){return new Promise((g,m)=>{const h=Date.now();let $="",v="",I=Date.now(),E=0,S=!1,n=null,w=!1,P=!1,x=null;if(b)try{x=C(X(String(b)),oe)}catch{x=null}let M=!1;const L=()=>{M||(M=!0,ce(u))},_={...process.env};u&&(_.HOME=u,process.platform==="win32"&&(_.USERPROFILE=u),t.debug(`[Agent] cursor-agent HOME=${u} (isolated MCP config)`));const o=q(i,l,{cwd:r,shell:!1,stdio:["pipe","pipe","pipe"],env:_});t.debug(`[Agent] PID: ${o.pid}`),o.stdin.on("error",e=>{e.code!=="EPIPE"&&t.warn(`[Agent] stdin error: ${e.message}`)}),o.stdout.on("error",e=>{e.code!=="EPIPE"&&t.warn(`[Agent] stdout error: ${e.message}`)}),o.stderr.on("error",e=>{e.code!=="EPIPE"&&t.warn(`[Agent] stderr error: ${e.message}`)}),a?(o.stdin.write(a,e=>{e&&e.code!=="EPIPE"&&t.warn(`[Agent] Failed to write to stdin: ${e.message}`),o.stdin.end()}),t.debug(`[Agent] Prompt also piped to stdin (${a.length} chars)`)):o.stdin.end();let A=null;x&&(A=setInterval(()=>{if(!(S||P))try{if(H(x)){S=!0,n="studio-stop";try{V(x)}catch{}t.warn("\u{1F6D1} Studio stop requested \u2014 terminating Cursor agent (and MCP browser session)"),o.kill("SIGTERM"),setTimeout(()=>{o.killed||o.kill("SIGKILL")},2e3)}}catch{}},600));const F=new Set,c=new Date(h).toISOString().replace(/\.\d+Z$/,""),y=setInterval(()=>{const e=Math.round((Date.now()-h)/1e3),d=Math.round((Date.now()-I)/1e3),s=[];try{const R=Math.ceil(e/60)+1,O=N(`find "${r}" -type f -mmin -${R} -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/target/*' 2>/dev/null | head -20`,{encoding:"utf-8",timeout:5e3}).trim();if(O)for(const U of O.split(`
22
+ `)){const K=U.replace(`${r}/`,"");F.has(K)||(F.add(K),s.push(K))}}catch{}let p="";s.length>0&&(p=` | \u{1F4C1} new: ${s.map(O=>O.split("/").pop()).join(", ")}`),F.size>0&&(p+=` | \u{1F4E6} total: ${F.size} files`),t.debug(`\u{1F493} [Agent] Running for ${e}s | ${E} lines output${p}`),E===0&&e>=30&&F.size===0&&(e<35&&t.warn(`\u26A0\uFE0F [Agent] No output after ${e}s \u2014 agent may be stuck. Check your CURSOR_API_KEY.`),e>=60&&(S=!0,n=n||"stall",t.error(`\u274C [Agent] No response after ${e}s \u2014 killing. Verify CURSOR_API_KEY is valid and agent CLI works: agent --version`),o.kill("SIGTERM"),setTimeout(()=>{o.killed||o.kill("SIGKILL")},3e3)))},3e4),T=setTimeout(()=>{S=!0,n=n||"timeout";const e=Math.round((Date.now()-h)/1e3);t.error(`\u23F1\uFE0F [Agent] Timeout after ${e}s \u2014 killing process (PID: ${o.pid})`),$.trim()&&t.warn(`\u{1F4E4} [Agent] Partial output (${$.length} chars) before timeout:
23
+ ${$.slice(-2e3)}`),o.kill("SIGTERM"),setTimeout(()=>{o.killed||o.kill("SIGKILL")},5e3)},f),k=new se;k.onToolCall=(e,d)=>{let s=e,p=d;if(e==="mcpToolCall"&&d?.name)s=d.name.replace(/^mcp_+[^_]+_+/,""),s.includes("-")&&s.split("-")[0]===s.split("-")[1]&&(s=s.split("-")[0]),p=d.args??d.input??d;else{if(e==="readToolCall"||e==="editToolCall"||e==="writeToolCall")return;(e.startsWith("mcp__")||e.includes("ToolCall"))&&(s=e.replace(/^mcp_+[^_]+_+/,"").replace(/ToolCall$/,""))}if(s.includes("memory")?G.stepMemory(`Tool: ${s}`):G.stepTool(`Tool: ${s}`),p!=null&&typeof p=="object"&&Object.keys(p).length>0&&!P){const O=JSON.stringify(p),U=O.length>100?`${O.substring(0,100)}...`:O;console.log(` Input: ${U}`)}},o.stdout.on("data",e=>{const d=e.toString();$+=d,I=Date.now(),w||(w=!0);const s=k.processChunk(d);s&&!P&&process.stdout.write(s);const p=d.split(`
24
+ `).filter(R=>R.trim());E+=p.length}),o.stderr.on("data",e=>{const d=e.toString();v+=d,I=Date.now(),w||(w=!0);const s=d.split(`
25
+ `).filter(p=>p.trim());for(const p of s)t.warn(`\u26A0\uFE0F [Agent stderr] ${p}`)}),o.on("close",(e,d)=>{P=!0,L(),clearTimeout(T),clearInterval(y),A&&clearInterval(A),k.flush();const s=Math.round((Date.now()-h)/1e3);if(t.debug(`[Agent] Exited: code=${e}, signal=${d}, elapsed=${s}s, output=${$.length} chars`),S){if(n==="studio-stop"){m(new Error("Stopped from Zibby Studio"));return}m(new Error(`Cursor Agent timed out after ${s}s (limit: ${f/1e3}s). ${E} lines produced. Last output ${Math.round((Date.now()-I)/1e3)}s ago. ${$.trim()?`
26
26
  Partial output (last 500 chars):
27
27
  ${$.slice(-500)}`:"No output captured."}`));return}if(e!==0){m(new Error(`Cursor Agent failed: exit code ${e}, signal ${d}. ${v.trim()?`
28
28
  Stderr: ${v.slice(-1e3)}`:""}${$.trim()?`
29
- Stdout (last 500 chars): ${$.slice(-500)}`:""}`));return}const p=k.getResult(),R=p?JSON.stringify(p,null,2):k.getRawText()||$||"";g({stdout:$||v||"",parsedText:R})}),o.on("error",e=>{L(),clearTimeout(T),clearInterval(w),A&&clearInterval(A),m(new Error(`Cursor Agent spawn error: ${e.message}
30
- Binary: ${s}
29
+ Stdout (last 500 chars): ${$.slice(-500)}`:""}`));return}const p=k.getResult(),R=p?JSON.stringify(p,null,2):k.getRawText()||$||"";g({stdout:$||v||"",parsedText:R})}),o.on("error",e=>{L(),clearTimeout(T),clearInterval(y),A&&clearInterval(A),m(new Error(`Cursor Agent spawn error: ${e.message}
30
+ Binary: ${i}
31
31
  This usually means the binary is not in PATH. Try:
32
32
  echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc`))})})}}export{Ee as CursorAgentStrategy};
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/core",
3
- "version": "0.1.26",
3
+ "version": "0.1.29",
4
4
  "description": "Core test automation engine with multi-agent and multi-MCP support",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/core",
3
- "version": "0.1.26",
3
+ "version": "0.1.29",
4
4
  "description": "Core test automation engine with multi-agent and multi-MCP support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",