oioxo-mcp 0.5.78 → 0.5.79

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.
Files changed (2) hide show
  1. package/bundle/cli.js +11 -10
  2. package/package.json +1 -1
package/bundle/cli.js CHANGED
@@ -663,20 +663,21 @@ ${y} left.`},files:a,plan:h}}case"shell":{let c=String(s.command??s.cmd??"").tri
663
663
  ${p||"(no output)"}`},files:a}}catch(d){return{result:{ok:!1,output:`shell failed: ${d instanceof Error?d.message:String(d)}`},files:a}}}case"spawn":{let c=String(s.task??s.prompt??"").trim();if(!c)return{result:{ok:!1,output:'spawn needs a "task" \u2014 one clear, bounded job with a definite deliverable.'},files:a};if(!r.spawn)return{result:{ok:!1,output:"No sub-agents on this surface \u2014 just do the work yourself with writeFile/editFile/shell."},files:a};try{let d=await r.spawn(c,a),h=d.files&&d.files.length?AR(a,d.files.map(y=>({path:y.path,content:y.content}))):a,p=h.length-a.length;return{result:{ok:!0,output:`Sub-agent finished${p>0?` (+${p} file${p===1?"":"s"})`:""}: ${(d.result||"(no summary)").slice(0,1500)}`},files:h}}catch(d){return{result:{ok:!1,output:`Sub-agent failed: ${d instanceof Error?d.message:String(d)}. Do this part yourself.`},files:a}}}case"ask":{let c=String(s.question??"").trim();return c?{result:{ok:!0,output:`(asked the user: ${c})`},files:a,control:{kind:"ask",question:c}}:{result:{ok:!1,output:'ask needs a "question".'},files:a}}case"done":{let c=(e.doneAttempts??0)+1,d=a.some(x=>/(^|\/)(test_|tests?\/)|\.(test|spec)\./i.test(x.path)||/(^|\/)test/i.test(x.path)),h=x=>{if(x.done)return!0;let y=x.title.toLowerCase().replace(/^(build|write|create|add|implement)\s+/,"").trim();return!!(/\b(test|spec)\b/.test(x.title.toLowerCase())&&d||/[\w./-]+\.[a-z0-9]{1,4}$/i.test(y)&&a.some(b=>b.path.toLowerCase().includes(y)))},p=(e.plan??[]).filter(x=>/\b(build|write|create|add|implement)\b/i.test(x.title)&&!/verify|run tests|run \+/i.test(x.title)&&!h(x));if(p.length&&c<=3){let x=p[0];return{result:{ok:!1,output:`Not done yet \u2014 ${p.length} planned file(s) still missing. Do the NEXT one now: "${x.title}". (If it's genuinely covered already, check it off with plan {"check":"${x.title.replace(/^build\s+/,"").slice(0,20)}"}.)`},files:a,doneAttempts:c}}if(r.review&&!e.reviewed){let x=await r.review(e.goal,a).catch(()=>[]);if(x.length)return{result:{ok:!1,output:`Not done yet \u2014 a review found issues to fix first:
664
664
  ${x.slice(0,6).map((y,b)=>`${b+1}. ${y}`).join(`
665
665
  `)}
666
- Fix these, then call done again.`},files:a,reviewed:!0}}return{result:{ok:!0,output:"done"},files:a,control:{kind:"done",summary:String(s.summary??"Done.")}}}default:{let c=String(i).toLowerCase();if(/folder|dir|mkdir|directory/.test(c)){let d=String(s.path??s.name??"pkg");return{result:{ok:!1,output:`There is no folder tool \u2014 folders are created automatically from file paths. Don't create "${d}" as a folder; just writeFile a file inside it, e.g. {"tool":"writeFile","args":{"path":"${d.replace(/\/$/,"")}/__init__.py"}} (or your first real module). Go ahead and write the first file now.`},files:a}}return/^(create|new|add|make)file$/.test(c)||c==="create"||c==="write"?{result:{ok:!1,output:`Use "writeFile" (not "${i}") to create a file, with the full contents in a fenced block right after the tool JSON.`},files:a}:/shell|bash|exec|run(command|cmd)|terminal/.test(c)?{result:{ok:!1,output:r.shell?'Use the "shell" tool: {"tool":"shell","args":{"command":"..."}}.':"No shell on this surface. To verify use runTests {} / typecheck {}; to change files use writeFile / editFile."},files:a}:{result:{ok:!1,output:`unknown tool: ${i}. Use one of: listFiles, readFile, writeFile, editFile, search, typecheck, runTests, updateProgress, ask, done. Folders are implicit \u2014 writeFile a path like "taskflow/storage.py" to create the folder.`},files:a}}}}function xdt(t,e,r,n){let i=t.files.map(A=>A.path).join(", ")||"(no files yet)",s=t.turns.find(A=>A.role==="summary"),a=n&&(n.projectMemory||n.memories.length)?[n.projectMemory?`Project memory (OIOXO.md \u2014 what you know about THIS project; honor it):
666
+ Fix these, then call done again.`},files:a,reviewed:!0}}return{result:{ok:!0,output:"done"},files:a,control:{kind:"done",summary:String(s.summary??"Done.")}}}default:{let c=String(i).toLowerCase();if(/folder|dir|mkdir|directory/.test(c)){let d=String(s.path??s.name??"pkg");return{result:{ok:!1,output:`There is no folder tool \u2014 folders are created automatically from file paths. Don't create "${d}" as a folder; just writeFile a file inside it, e.g. {"tool":"writeFile","args":{"path":"${d.replace(/\/$/,"")}/__init__.py"}} (or your first real module). Go ahead and write the first file now.`},files:a}}return/^(create|new|add|make)file$/.test(c)||c==="create"||c==="write"?{result:{ok:!1,output:`Use "writeFile" (not "${i}") to create a file, with the full contents in a fenced block right after the tool JSON.`},files:a}:/shell|bash|exec|run(command|cmd)|terminal/.test(c)?{result:{ok:!1,output:r.shell?'Use the "shell" tool: {"tool":"shell","args":{"command":"..."}}.':"No shell on this surface. To verify use runTests {} / typecheck {}; to change files use writeFile / editFile."},files:a}:{result:{ok:!1,output:`unknown tool: ${i}. Use one of: listFiles, readFile, writeFile, editFile, search, typecheck, runTests, updateProgress, ask, done. Folders are implicit \u2014 writeFile a path like "taskflow/storage.py" to create the folder.`},files:a}}}}function xdt(t,e,r,n){let i=t.files.map(E=>E.path).join(", ")||"(no files yet)",s=t.turns.find(E=>E.role==="summary"),a=n&&(n.projectMemory||n.memories.length)?[n.projectMemory?`Project memory (OIOXO.md \u2014 what you know about THIS project; honor it):
667
667
  ${n.projectMemory.slice(0,1500)}`:"",n.memories.length?`You remember (from earlier work with this user):
668
- ${n.memories.slice(0,6).map(A=>`- ${A}`).join(`
668
+ ${n.memories.slice(0,6).map(E=>`- ${E}`).join(`
669
669
  `)}`:""].filter(Boolean).join(`
670
670
  `):"",c=r&&(r.symbolIndex||r.relevantFiles.length)?[r.symbolIndex?`What already exists (call these, don't redefine):
671
- ${r.symbolIndex.slice(0,2e3)}`:"",r.relevantFiles.length?`Most relevant files to the task right now: ${r.relevantFiles.map(A=>A.path).join(", ")}`:""].filter(Boolean).join(`
672
- `):"",d=new Set(t.files.map(A=>A.path)),h=e.filter(A=>A.role==="tool").slice(-5),p=h.some(A=>{try{let k=JSON.parse(A.text),C=k?.args?.path;return k?.tool==="writeFile"&&C&&!d.has(C)}catch{return!1}}),y=h.length>=3&&!p&&h.every(A=>{try{let k=JSON.parse(A.text);return k?.tool==="readFile"||k?.args?.path&&d.has(k.args.path)}catch{return!1}})?`
673
- FORWARD PROGRESS: you've been re-reading/re-editing files that already exist. STOP revisiting them \u2014 they're done and on disk. Look at TASK + the file list and writeFile the NEXT file that does NOT exist yet (e.g. the next module the task asks for, or its tests). If every file the task needs already exists and works, call done. Do not readFile or rewrite an existing file this turn.`:"",b=t.plan&&t.plan.length?`PLAN (work through these in order; check off with plan {"check":"..."} or {"done":[i]} as you finish):
674
- ${t.plan.map((A,k)=>`${k+1}. [${A.done?"x":" "}] ${A.title}`).join(`
671
+ ${r.symbolIndex.slice(0,2e3)}`:"",r.relevantFiles.length?`Most relevant files to the task right now: ${r.relevantFiles.map(E=>E.path).join(", ")}`:""].filter(Boolean).join(`
672
+ `):"",d=new Set(t.files.map(E=>E.path)),h=e.filter(E=>E.role==="tool").slice(-5),p=E=>{try{let I=JSON.parse(E.text);return{tool:I?.tool,path:I?.args?.path}}catch{return{}}},x=h.some(E=>{let I=p(E);return I.tool==="writeFile"&&I.path&&!d.has(I.path)}),y=h.length>=3&&!x&&h.every(E=>{let I=p(E);return I.tool==="readFile"||I.path&&d.has(I.path)}),b=new Map;for(let E of h){let I=p(E);I.tool==="writeFile"&&I.path&&d.has(I.path)&&b.set(I.path,(b.get(I.path)??0)+1)}let _=x?void 0:[...b.entries()].find(([,E])=>E>=2)?.[0],A=t.plan?.find(E=>!E.done)?.title,k=_?`
673
+ FORWARD PROGRESS: you keep rewriting ${_} \u2014 it already exists and is on disk. A "multiple files" task means SEPARATE modules; STOP growing ${_} and writeFile the NEXT file now${A?` (your plan's next step: "${A}")`:" (the next module the task needs, e.g. models.py / auth.py / routes.py / its tests)"}. Keep ${_} small and focused; move the rest into their own files.`:y?`
674
+ FORWARD PROGRESS: you've been re-reading/re-editing files that already exist. STOP revisiting them \u2014 they're done and on disk. Look at TASK + the file list and writeFile the NEXT file that does NOT exist yet (e.g. the next module the task asks for, or its tests). If every file the task needs already exists and works, call done. Do not readFile or rewrite an existing file this turn.`:"",C=t.plan&&t.plan.length?`PLAN (work through these in order; check off with plan {"check":"..."} or {"done":[i]} as you finish):
675
+ ${t.plan.map((E,I)=>`${I+1}. [${E.done?"x":" "}] ${E.title}`).join(`
675
676
  `)}
676
- ${t.plan.filter(A=>!A.done).length} step(s) left \u2014 do the FIRST unchecked one now.`:"";return[ldt,`TASK: ${t.goal}`,b,`PROGRESS.md:
677
+ ${t.plan.filter(E=>!E.done).length} step(s) left \u2014 do the FIRST unchecked one now.`:"";return[ldt,`TASK: ${t.goal}`,C,`PROGRESS.md:
677
678
  ${t.progress||"(empty \u2014 start by planning what files this needs)"}`,a,s?`Earlier this session:
678
- ${s.text}`:"",c,`Project files (already created \u2014 don't rewrite these unless fixing a real bug): ${i}`,e.map(A=>A.role==="tool"?`Tool call: ${A.text}`:A.role==="observation"?`Result: ${A.text}`:A.role==="user"?`User: ${A.text}`:A.role==="assistant"?A.text:"").filter(Boolean).join(`
679
- `),y,"Your next tool:"].filter(Boolean).join(`
679
+ ${s.text}`:"",c,`Project files (already created \u2014 don't rewrite these unless fixing a real bug): ${i}`,e.map(E=>E.role==="tool"?`Tool call: ${E.text}`:E.role==="observation"?`Result: ${E.text}`:E.role==="user"?`User: ${E.text}`:E.role==="assistant"?E.text:"").filter(Boolean).join(`
680
+ `),k,"Your next tool:"].filter(Boolean).join(`
680
681
 
681
682
  `)}function km(t,e,r,n){return{id:t,goal:e,files:r,turns:[{role:"task",text:e}],progress:"",done:!1,createdAt:n,updatedAt:n}}function mSe(t,e=Date.now){return async(r,n)=>{let i={...t,spawn:void 0,review:void 0,onEvent:void 0,maxStepsPerYield:Math.min(t.maxStepsPerYield??200,80)},s=km(`sub-${(e()%1e9).toString(36)}`,r,n,e()),a=0,c="";for(;a++<4;){let p=await Nl(s,i,e);if(s=p.state,p.kind==="done"){c=p.summary;break}if(p.kind==="stopped"){c="sub-agent stopped";break}if(p.kind==="ask"){s=$2(s,"Proceed with sensible defaults.",e());continue}if(p.kind==="budget"){c="sub-agent hit its step cap (partial result merged)";continue}}c||(c=s.progress?.slice(0,400)||"sub-agent returned without an explicit summary");let d=new Map(n.map(p=>[p.path,p.content])),h=s.files.filter(p=>d.get(p.path)!==p.content);return{result:c,files:h}}}async function Nl(t,e,r=Date.now){let n=e.maxStepsPerYield??200,i=e.onEvent??(()=>{}),s=t;if(!s.plan&&s.files.length===0){let d=Array.from(new Set((s.goal.match(/[\w./-]+\.[A-Za-z0-9]{1,4}\b/g)??[]).filter(p=>/\.(py|ts|tsx|js|jsx|go|rs|java|rb|php|c|cpp|h|cs|lua|sql|md|json|toml|txt)$/i.test(p)&&!/^https?:/.test(p)))),h=/\btests?\b|pytest|spec|unit test/i.test(s.goal);if(d.length>=1||h){let p=d.map(x=>({title:`build ${x}`,done:!1}));p.length||p.push({title:"build the main implementation",done:!1}),h&&!d.some(x=>/test|spec/i.test(x))&&p.push({title:"write tests",done:!1}),p.push({title:"run + verify it works, fix any failures",done:!1}),p.length>=2&&(s={...s,plan:p},i({type:"note",text:`seeded a ${p.length}-step plan from the goal`}))}}let a=14,c=[];for(let d=0;d<n;d++){if(e.signal?.aborted)return{kind:"stopped",state:s};if(e.summarize&&s.turns.filter(N=>N.role!=="summary").length>a+12){let N=s.turns[0]?.role==="summary"?s.turns.slice(0,1):[],J=N.length?s.turns.slice(1):s.turns,z=J.slice(-a),B=J.slice(0,J.length-a);if(B.length)try{let Y=(await e.summarize([...N,...B])).trim();Y&&(s={...s,turns:[{role:"summary",text:Y.slice(0,4e3)},...z]},i({type:"note",text:"compacted earlier steps to keep the session light"}))}catch{}}let h=s.turns.filter(J=>J.role!=="summary"&&J.role!=="task").slice(-a),p;if(e.retrieve&&s.files.filter(N=>/\.(ts|tsx|js|jsx|py|go|rs|java|rb|php|c|cpp|h|cs|lua|sql)$/i.test(N.path)).length>=2)try{p=await e.retrieve(s.goal,s.files)}catch{}let x;if(e.recall){let N=[...s.turns].reverse().find(z=>z.role==="user")?.text??"",J=`${s.goal}
682
683
  ${N}`.slice(0,500);try{x=await e.recall(J)}catch{}}let y=xdt(s,h,p,x),b,_=c;c=[];try{b=_.length&&e.askWithImages?await e.askWithImages(y,_):await e.ask(y)}catch(N){let J=N instanceof Error?N.message:String(N);return e.signal?.aborted||/abort/i.test(J)?{kind:"stopped",state:s}:(i({type:"note",text:`model error: ${J.slice(0,120)}`}),{kind:"budget",state:s})}let A=iSe(b),k=hdt(b.replace(/```[\s\S]*?```/g,"")).trim();k&&(i({type:"think",text:k.slice(0,400)}),s={...s,turns:[...s.turns,{role:"assistant",text:k.slice(0,600)}]});let C=A,T=pSe(b),E=!C||["readFile","listFiles","search"].includes(String(C.tool));if(T&&E&&!pdt(s,T.path))i(C?{type:"note",text:`model dumped code for a new file ${T.path} alongside a ${C.tool}; writing the file instead`}:{type:"note",text:`recovered a writeFile for ${T.path} from a bare code block`}),C={tool:"writeFile",args:{path:T.path}};else if(!C){s={...s,turns:[...s.turns,{role:"observation",text:'No tool call detected. Reply with ONE ```tool block: {"tool":"writeFile","args":{"path":"calclang/parser.py"}} then the file in a SEPARATE ``` block right after. Do NOT put code in a bare ```python block.'}]};continue}let I=JSON.stringify(C),R=s.turns.filter(N=>N.role==="tool"),U=0;for(let N=R.length-1;N>=0&&R[N].text===I;N--)U++;if(U>=2&&C.tool!=="done"){let N="listFiles, readFile, writeFile, editFile, search, typecheck, runTests, updateProgress, ask, done",J=C.tool==="editFile"?String(C.args?.path??"the file"):"",z=J?`Your editFile on ${J} has failed ${U} times \u2014 the SEARCH text isn't matching. STOP patching. Instead, rewrite the WHOLE file: call writeFile {"path":"${J}"} and put the COMPLETE updated file contents in the fenced block after it. Whole-file rewrite is more reliable than a search/replace that won't match.`:U>=4?`STOP repeating "${C.tool}" \u2014 it has failed ${U} times and will keep failing. Do something DIFFERENT this turn: if you were trying to make a folder, writeFile a real file inside it now (folders are implicit). Pick the next concrete file to write and writeFile it with full contents.`:`You've called "${C.tool}" with the same args ${U} times and it isn't working. Change your approach \u2014 only the tools [${N}] exist, and folders are made implicitly by writing a file at that path. Take a different action now.`;if(i({type:"observation",text:z,ok:!1}),s={...s,turns:[...s.turns,{role:"observation",text:z}],updatedAt:r()},U>=6)return{kind:"budget",state:s};continue}if(C.tool==="editFile"){let N=String(C.args?.path??"");if(N){let J=0;for(let z=s.turns.length-1;z>=0&&s.turns.length-z<=8;z--){let B=s.turns[z];B.role==="observation"&&/Edit (REJECTED|did not apply)/i.test(B.text)&&B.text.includes(N)&&J++}if(J>=2){let z=`editFile keeps missing on ${N} (${J} failed patches). Switch format: readFile ${N} to get the exact current contents, then writeFile {"path":"${N}"} with the COMPLETE updated file. A whole-file rewrite always applies; a search/replace that can't match never will.`;i({type:"observation",text:z,ok:!1}),s={...s,turns:[...s.turns,{role:"observation",text:z}],updatedAt:r()};continue}}}i({type:"tool",text:JSON.stringify(C).slice(0,300),call:C});let{result:j,files:V,progress:G,plan:se,reviewed:te,doneAttempts:H,control:X,screenshot:Q}=await gdt(C,s,e,b);if(Q&&e.askWithImages&&(c=[Q],i({type:"note",text:"attaching the page screenshot to the next turn (vision)"})),s={...s,files:V,...G!=null?{progress:G}:{},...se!=null?{plan:se}:{},...te!=null?{reviewed:te}:{},...H!=null?{doneAttempts:H}:{},...te==null&&(C.tool==="writeFile"||C.tool==="editFile")?{reviewed:!1}:{},turns:[...s.turns,{role:"tool",text:JSON.stringify(C).slice(0,2e3),call:C},{role:"observation",text:j.output.slice(0,4e3)}],updatedAt:r()},j.ok&&C.tool==="writeFile"&&s.plan&&se==null){let N=String(C.args?.path??"").toLowerCase();if(N){let J=/(^|\/)(test_|tests?\/|.*\.test\.|.*_test\.|.*\.spec\.)/.test(N)||/test/.test(N),z=s.plan.map(B=>{if(B.done)return B;let Y=B.title.toLowerCase().replace(/^build\s+/,"");return B.title.toLowerCase().includes(N)||N.includes(Y)||J&&/\b(test|spec)\b/.test(B.title.toLowerCase())?{...B,done:!0}:B});z.some((B,Y)=>B.done!==s.plan[Y].done)&&(s={...s,plan:z})}}G!=null&&i({type:"progress",text:G.slice(0,300)}),i({type:"observation",text:j.output.slice(0,300),ok:j.ok}),e.persist?.(s);let ie=async N=>{if(!e.autoRemember)return;let J=[...s.turns].reverse().find(z=>z.role==="user")?.text??s.goal;try{await e.autoRemember(J,N)}catch{}};if(X?.kind==="ask")return s={...s,pendingQuestion:X.question,updatedAt:r()},e.persist?.(s),await ie(`Asked the user: ${X.question}`),{kind:"ask",question:X.question,state:s};if(X?.kind==="done")return s={...s,done:!0,pendingQuestion:void 0,updatedAt:r()},e.persist?.(s),await ie(X.summary||`Built: ${s.files.map(N=>N.path).slice(0,12).join(", ")}`),{kind:"done",summary:X.summary,state:s}}return e.persist?.(s),{kind:"budget",state:s}}function $2(t,e,r){return{...t,pendingQuestion:void 0,turns:[...t.turns,{role:"user",text:e}],updatedAt:r}}var ldt,TR,kR=fe(()=>{"use strict";sSe();oSe();SR();QA();ldt=`You are OIOXO, a coding agent working in a real project like a developer in an IDE. You take ONE action per turn by calling a tool, see the result, then decide the next action \u2014 looping until the work is genuinely done. You speak briefly in first person when it helps (what you're doing/why), then call the tool.
@@ -1417,7 +1418,7 @@ ${k.snippet}`:""}`).join(`
1417
1418
  `);return Ks(A||"No results.")}catch(b){return Ks(`web_search failed: ${b instanceof Error?b.message:"error"}`)}}),e.tool("fetch_url","Fetch a web page (http/https only) and return its readable text. Use to read a doc/article \u2014 typically a URL found via web_search.",{url:lo.string().describe("The full http(s) URL.")},async({url:x})=>{let y=await hA();if(!y.ok)return dA(y.message);try{let _=await(await fetch(`${Yi}/tools/fetch?url=${encodeURIComponent(x)}`,{signal:AbortSignal.timeout(12e3)})).json();return Ks(_.error?`fetch_url failed: ${_.error}`:_.text??"(empty)")}catch(b){return Ks(`fetch_url failed: ${b instanceof Error?b.message:"error"}`)}});let c=null,d=!1,h=async x=>{if(d=x,c)return c;let{makePlaywrightBackend:y}=await Promise.resolve().then(()=>(OR(),RR)),{BrowserController:b}=await Promise.resolve().then(()=>(Ok(),sK)),_=await y({headless:!1});return c=new b(_,{confirmNavigate:async()=>d,allowEvaluate:!1}),c};e.tool("browser",'Drive a REAL browser on this machine to see and test web pages \u2014 navigate, read a numbered snapshot of clickable elements, click/type by ref, screenshot. Runs LOCALLY (the page never leaves the device). Local URLs (localhost) are always allowed; set allow_external:true to permit other sites. Call one verb at a time; after navigate the result lists elements like [3] button "Sign in" \u2014 click by that ref.',{verb:lo.enum(["navigate","snapshot","screenshot","click","type","waitFor","scrollTo","back","reload","close"]).describe("The action."),url:lo.string().optional().describe("For navigate: the URL."),ref:lo.number().optional().describe("For click/type/scrollTo: element number from the latest snapshot."),text:lo.string().optional().describe("For type: text to enter. For click: visible text to match. For waitFor: text to wait for."),selector:lo.string().optional().describe("Optional CSS selector (prefer ref)."),submit:lo.boolean().optional().describe("For type: press Enter after typing."),allow_external:lo.boolean().optional().describe("Permit navigating to non-local sites (privacy gate; default false).")},async({verb:x,url:y,ref:b,text:_,selector:A,submit:k,allow_external:C})=>{let T=await hA();if(!T.ok)return dA(T.message);try{let E=await h(!!C);if(x==="close")return await E.dispose(),c=null,Ks("Browser closed.");let I=await E.run({verb:x,url:y,ref:b,text:_,selector:A,submit:k});if(!I.ok)return Ks(`browser ${x} failed: ${I.error??"unknown error"}`);let R=`${I.title||""} \u2014 ${I.url}`.trim(),U=I.snapshot?`
1418
1419
  ${I.snapshot}`:"",j=I.consoleErrors?.length?`
1419
1420
  console errors: ${I.consoleErrors.slice(0,3).join(" | ")}`:"",V=`${R}${U}${j}`.trim()||"OK";if(I.screenshot){let G=_It(I.screenshot);return{content:[{type:"text",text:`${V}
1420
- (screenshot attached${G?` \u2014 ${G.width}x${G.height}px`:""}, ${I.screenshot.length} bytes)`},{type:"image",data:Buffer.from(I.screenshot).toString("base64"),mimeType:"image/png"}]}}return Ks(V)}catch(E){return Ks(`browser failed: ${E instanceof Error?E.message:"error"}`)}});let p=new fL;await e.connect(p)}x5();JA();kc();var _ue=(()=>{try{return"0.5.78"}catch{}try{return A$e(import.meta.url)("../../package.json").version}catch{return"0.0.0"}})(),nPt=`OIOXO ${_ue} \u2014 context engine + terminal coding agent (https://oioxo.com)
1421
+ (screenshot attached${G?` \u2014 ${G.width}x${G.height}px`:""}, ${I.screenshot.length} bytes)`},{type:"image",data:Buffer.from(I.screenshot).toString("base64"),mimeType:"image/png"}]}}return Ks(V)}catch(E){return Ks(`browser failed: ${E instanceof Error?E.message:"error"}`)}});let p=new fL;await e.connect(p)}x5();JA();kc();var _ue=(()=>{try{return"0.5.79"}catch{}try{return A$e(import.meta.url)("../../package.json").version}catch{return"0.0.0"}})(),nPt=`OIOXO ${_ue} \u2014 context engine + terminal coding agent (https://oioxo.com)
1421
1422
 
1422
1423
  Private, on your device. Cut your AI bill by up to ~90% in Copilot, Cursor & Claude,
1423
1424
  and code from your terminal \u2014 nothing leaves your machine.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oioxo-mcp",
3
- "version": "0.5.78",
3
+ "version": "0.5.79",
4
4
  "description": "OIOXO — cut your AI bill by up to ~90%: it feeds Claude Code, Copilot, Cursor and any MCP agent only the relevant slice of your codebase instead of whole files. Also a private terminal coding agent (`oioxo code`, on-device with --local or any key). Runs on your device; your own devices can build together over Wi-Fi.",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "type": "module",