@burtson-labs/bandit-stealth-cli 1.7.88 → 1.7.90

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 (3) hide show
  1. package/README.md +18 -7
  2. package/dist/cli.js +4 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -129,16 +129,27 @@ Pull one with `ollama pull <model>`. Bandit auto-detects each model's capabiliti
129
129
 
130
130
  | Model | Where | Notes |
131
131
  |---|---|---|
132
- | `qwen2.5-coder:7b` | Local / Mac (~4.7 GB) | **Fast default** native tool calling, strong coding quality. |
133
- | `gemma4:26b` | Local / Mac 32GB+ (~17 GB) | Sane default for recent Macs fast, solid quality. |
134
- | `gemma4:31b` | Local / Mac 64GB+, GPU node | Bigger context, better reasoning. |
135
- | `qwen2.5-coder:32b-instruct-q8_0` | Local / Mac 48GB+, high-VRAM GPU | **Best for agents** native tool calling, strong coding quality. |
136
- | `qwen2.5-coder:14b-instruct-q8_0` | Local / Mac (~18 GB), 24GB GPUs | Lighter Qwen native tool calling, fast. |
137
- | `devstral:latest` | Local / Mac 32GB+ | Mistral's agent-tuned model — excellent tool use. |
132
+ | `bandit-logic` (cloud) | Bandit gateway (API key) | **Default for cloud.** Agent-tuned wrapper around Qwen 3.6 27B with thinking mode. Best reliability on multi-step agent tasks — what we recommend trying first. |
133
+ | `qwen3.6:27b` | Local / Mac 48GB+, high-VRAM GPU (~17 GB) | **Best local pick.** Same family as `bandit-logic`, runs offline. Probes the filesystem instead of asking for clarificationreal agent behavior. |
134
+ | `gemma4:26b` | Local / Mac 32GB+ (~17 GB) | Solid alternative when Qwen 3.6 is too heavy for your hardware. Multimodal, 128K context. |
135
+ | `gemma4:31b` | Local / Mac 64GB+, GPU node | Bigger context, better reasoning for complex refactors. |
136
+ | `qwen2.5-coder:7b` | Local / Mac (~4.7 GB) | Fast lightweight pick. Native tool calling. Best for "given context, do X" tasks rather than autonomous discovery. |
137
+ | `devstral:latest` | Local / Mac 32GB+ | Mistral's agent-tuned model — strong tool use. |
138
+ | `bandit-core-1` (cloud) | Bandit gateway (API key) | Lightweight cloud option. Faster first-token than `bandit-logic`, less reliable on multi-step agent tasks. |
139
+
140
+ ### Models we don't recommend (for agent work)
141
+
142
+ Bandit is an autonomous agent harness — it expects the model to discover repo structure, plan edits, and emit tool calls without being hand-held. Some otherwise-impressive models aren't trained for that workflow and produce unexpected results:
143
+
144
+ - **`gpt-oss:120b` and other reasoning-tuned models** — post-trained for OpenAI's harmony tool-call format, not the XML/native protocols Bandit uses. Tends to narrate intent ("I'll search for the controllers...") without ever emitting an actual tool call.
145
+ - **`qwen2.5-coder:32b` and other code-completion-tuned models** — post-trained for fully-specified code-generation benchmarks. On ambiguous prompts it asks for paths instead of probing. Solid for concrete tasks; underwhelming as an autonomous agent.
146
+ - **`qwen3.6:35b`** — the larger Qwen 3.6 variant stalls in reasoning-only output and ignores the harness's "act now" nudges. The 27B is the better production pick from this family.
147
+
148
+ If you want to test models outside the recommended list, expect the reasoning-only / narrate-but-no-action / partial-completion detectors to fire frequently. Those are signal — they mean the model isn't a great fit for autonomous agent work.
138
149
 
139
150
  **Capability dispatch**:
140
151
 
141
- - **Native tool calling** — Qwen 2.5 Coder, Llama 3.1+, Devstral, DeepSeek-Coder-V2+. Tool schemas go in Ollama's `tools:` field. Saves ~1500–3000 tokens per turn.
152
+ - **Native tool calling** — Qwen 3.6, Qwen 2.5 Coder, Llama 3.1+, Devstral, DeepSeek-Coder-V2+. Tool schemas go in Ollama's `tools:` field. Saves ~1500–3000 tokens per turn.
142
153
  - **Text-parsing fallback** — Gemma 3/4 and anything else. XML-style tool block lives in the system prompt with the full mitigation stack armed.
143
154
 
144
155
  Any Ollama model works — capabilities auto-detect via `/api/show`.
package/dist/cli.js CHANGED
@@ -74,7 +74,7 @@ ${m}
74
74
  `).filter(oe=>oe.length>0).length;return`<tool_result name="${S}">[earlier run, ${ee} lines elided \u2014 summary: ${V}]</tool_result>`}t(wxt,"summarizeToolResult");function Ext(p,m={}){let S=m.tokenBudget??xxt,T=m.keepRecentToolResults??Txt,k=m.charsPerToken??kxt,j=p.reduce((ye,Y)=>ye+x1e(Y.content,k),0);if(j<=S)return{compacted:p,beforeTokens:j,afterTokens:j,messagesCompacted:0};let V=[];for(let ye=0;ye<p.length;ye++)nJe(p[ye])&&V.push(ye);let ee=new Set(V.slice(-T)),oe=[],de=0;for(let ye=0;ye<p.length;ye++){let Y=p[ye];nJe(Y)&&!ee.has(ye)?(oe.push({role:Y.role,content:wxt(Y.content)}),de++):oe.push(Y)}let _e=oe.reduce((ye,Y)=>ye+x1e(Y.content,k),0);if(_e>S){let ye=new Set,Y=oe.findIndex(Je=>Je.role==="user");oe.forEach((Je,qe)=>{Je.role==="system"&&ye.add(qe)});let rt=Y+1;for(;_e>S&&rt<oe.length-2;){if(ye.has(rt)){rt++;continue}let Je=x1e(oe[rt].content,k);oe.splice(rt,1),_e-=Je,de++}}return{compacted:oe,beforeTokens:j,afterTokens:_e,messagesCompacted:de}}t(Ext,"compactToolMessages")});var rJe=hi(YB=>{"use strict";Object.defineProperty(YB,"__esModule",{value:!0});YB.ToolUseLoop=void 0;YB.createToolUseLoop=Dxt;var ex=S1e(),Pxt=k1e(),XY=class{static{t(this,"ToolUseLoop")}constructor(m,S,T={}){this.registry=m,this.ctx=S,this.maxIterations=T.maxIterations??10,this.defaultEmit=T.emitEvent??(()=>{}),this.defaultBeforeToolExecute=T.beforeToolExecute??(()=>({allow:!0}))}async run(m,S,T,k){return this.runWithMessages([{role:"user",content:m}],S,T,k)}async runWithMessages(m,S,T,k){let j=k?.emitEvent??this.defaultEmit,V=k?.maxIterations??this.maxIterations,ee=k?.beforeToolExecute??this.defaultBeforeToolExecute,oe=k?.signal,de=Math.max(1,k?.maxParallelTools??8),_e=Math.max(1,k?.maxTotalTools??60),ye=0,Y=t((No,Lo,Oo="")=>({finalResponse:Oo||"[cancelled]",iterations:Lo,messages:No,hitLimit:!1,cancelled:!0}),"buildCancelledResult"),rt=k?.nativeTools??!1,Je=rt?"":this.registry.buildSystemPromptBlock(),qe=T?Je?`${T}
75
75
 
76
76
  ${Je}`:T:Je,un=rt?this.registry.buildNativeToolsSchema():void 0,Wt=[];qe&&Wt.push({role:"system",content:qe});let Ut="";for(let No of m)No.role==="user"&&typeof No.content=="string"&&No.content.trim()&&(Ut=No.content);let Rn=!1;for(let No of m)No.role!=="system"&&Wt.push(No);let Jt=0,_n=!1,At=0,Xt=0,Cr=0,Ln=2,ci=2,Hn=[],hs=2,ms=!1,Jn=[],oa=3,ca=0,hn=3,Ao=!1,Ms=-1,Qs=0,Nr=!1,Ro=3,Ks=2,Hs=!1;{let No=m.filter(Lo=>Lo.role==="user").map(Lo=>Lo.content).join(`
77
- `).toLowerCase();Hs=/\b(update|edit|change|fix|modify|refactor|rewrite|replace|add)\b/.test(No)||/[\w\-./]+\.(?:ts|tsx|js|jsx|py|rb|go|rs|java|kt|cs|swift|php|cpp|c|h|md|json|ya?ml|html|css)\b/.test(No)}let Ji=0,ei=!1,Cc=!1,Uu=!1,sl=[/in (?:my|a|the) previous response/i,/already provided (?:the|an?) (?:implementation|refactored|improved|updated)/i,/you can find (?:the |this )?(?:refactored|improved|updated) (?:code|implementation)/i,/here (?:is|'s) the (?:refactored|improved|updated|revised) (?:code|implementation|file)/i,/(?:i have|i've) (?:refactored|rewritten|updated|improved)/i,/(?:refactored|updated) (?:the )?(?:code|implementation) above/i,/i'll finalize the task here/i,/i've also marked (?:the tasks|these steps) as complet/i,/i apologi[sz]e for the (?:malformed|invalid)/i,/(?:ensure|escape) (?:all )?(?:quotes|newlines|characters).*(?:properly )?escap/i,/in my next tool call/i,/let me know (?:which|what) (?:task|action) (?:i should |to )?resume/i,/please (?:let me know|tell me).*(?:specific action|which task|what.*like me to)/i,/here (?:is|'s) the (?:final|resulting|updated|modified) (?:state|version|content|output) of/i,/(?:comments?|changes?|edits?|annotations?|updates?) (?:have )?been (?:added|made|applied|written|included)/i,/you can verify (?:these|the|your) (?:changes?|edits?|updates?)/i,/check(?:ing)? the files? (?:directly )?in your editor/i,/running (?:a )?build to (?:see|verify|check)/i];for(;;){if(oe?.aborted)return j("tool_loop:cancelled",{iteration:Jt,stage:"pre_iteration"}),Y(Wt,Jt);Jt>=V&&(_n=!0,Wt.push({role:"user",content:"You have reached the tool use limit. Please give your best final answer based on what you have gathered so far."})),ye>=_e&&!_n&&(_n=!0,j("tool_loop:total_tool_cap",{iteration:Jt,totalToolsExecuted:ye}),Wt.push({role:"user",content:`You have executed ${ye} tool calls this turn \u2014 the per-turn cap (${_e}) has been reached. Please give your best final answer based on what you have gathered so far.`}));let No=k?.messageTokenBudget;if(No!==void 0&&No>0&&Number.isFinite(No)){let $s=(0,Pxt.compactToolMessages)(Wt,{tokenBudget:No});$s.messagesCompacted>0&&(j("tool_loop:compacted",{iteration:Jt,messagesCompacted:$s.messagesCompacted,beforeTokens:$s.beforeTokens,afterTokens:$s.afterTokens}),Wt.length=0,Wt.push(...$s.compacted))}Ut&&!Rn&&Jt>=3&&!_n&&Wt.reduce((Dn,Jc)=>Dn+(Jc.content?.length??0),0)>8e3&&(Rn=!0,j("tool_loop:goal_anchor",{iteration:Jt,goalPreview:Ut.slice(0,120)}),Wt.push({role:"user",content:`Reminder of the original request: "${Ut.trim()}". Answer THAT question directly using what you have gathered. Do not pivot to a related-but-different topic that happens to be salient in the recent tool results.`})),j("tool_loop:llm_start",{iteration:Jt,messageCount:Wt.length});let Lo=await this.streamAndAggregate(S,Wt,j,Jt,un,oe);if(j("tool_loop:llm_response",{iteration:Jt,response:Lo.slice(0,200)}),oe?.aborted)return j("tool_loop:cancelled",{iteration:Jt,stage:"post_stream"}),Y(Wt,Jt,Lo);if(!_n&&/<tool_result\b[\s\S]*?<\/tool_result\s*>|<tool_result\b[^<]*$/i.test(Lo)&&Cr<ci){Cr++,j("tool_loop:fake_tool_result_detected",{iteration:Jt,preview:Lo.slice(0,200)});let $s=Lo.replace(/<tool_result\b[\s\S]*?<\/tool_result\s*>/gi,"").replace(/<tool_result\b[^<]*$/i,"").trim();Wt.push({role:"assistant",content:$s}),Wt.push({role:"user",content:'You emitted a `<tool_result>` envelope in your response. Those envelopes are SYSTEM output \u2014 they appear BETWEEN your turns, never inside your own message. If you meant to invoke a tool, emit a single `<tool_call>{"name":"...","params":{...}}</tool_call>` and wait for the real result. If the task is complete, give a plain-prose final answer with no XML envelopes. Retry now.'});continue}Wt.push({role:"assistant",content:Lo});let mt=Lo.replace(/<think\b[\s\S]*?<\/think\s*>/gi,"").replace(/<think\b[\s\S]*$/i,"").replace(/```bandit-reasoning\b[\s\S]*?```/gi,"").replace(/```bandit-reasoning\b[\s\S]*$/i,"").trim(),wn=!mt&&Lo.trim().length>0,zn=/\b(use|call|invoke|execute|run|search|look|read|check|find|list)\b[^.\n]*\.?\s*$/i,Ya=/\b(we (?:will|need to|should)|we'?ll|we'?re going to|i'?ll|i will|let me|let'?s|going to|i'?m going to|i need to)\b/i,fs=/```[a-zA-Z0-9_-]*\s*\n/.test(mt),lo=!(0,ex.hasToolCalls)(Lo)&&!fs&&mt.length>0&&mt.length<240&&Ya.test(mt)&&zn.test(mt);if((!Lo.trim()&&Jt>0||wn||lo)&&!_n&&At<2){At++,j("tool_loop:empty_retry",{iteration:Jt,attempt:At,reasoningOnly:wn,narratedButNoAction:lo});let $s=lo?'You announced your next step in prose ("we will search\u2026" / "let me check\u2026" / "use X to find Y") but did NOT emit a `<tool_call>` envelope. Announcing intent is not enough \u2014 you must actually invoke the tool. Emit the call now in this exact format: `<tool_call>{"name":"<tool>","params":{...}}</tool_call>` \u2014 no commentary, no markdown fence around it. Or, if the task is already answerable from what you know, give a final answer instead.':wn?"You completed reasoning but emitted no tool_call and no final answer. Pick the next concrete action: invoke a tool to make progress, OR write a final answer using what you have. Do not stop after only thinking.":"Your previous response was empty. Please either invoke another tool to continue the task OR produce a final answer using what you've gathered so far. Do not respond with an empty message.";Wt.push({role:"user",content:$s});continue}if(At=0,!_n&&(0,ex.looksLikeAttemptedToolCall)(Lo)&&!(0,ex.hasToolCalls)(Lo)&&Xt<Ln){Xt++,j("tool_loop:parse_retry",{iteration:Jt,attempt:Xt});let $s=Xt===1;Wt.push({role:"user",content:$s?'Your previous tool_call was not valid JSON \u2014 I could not parse it. Common cause: unescaped `"` characters inside a string value (for example `["", "", ""]` inside a `content` string). Retry the tool call with properly escaped JSON: every `"` inside a string value must be written as `\\"`, and every newline as `\\n`. If the content is very long, consider shortening it or breaking the change into smaller edits.':"Your tool_call still did not parse. Do NOT retry with the same shape. Switch tactics: (a) call `write_file` instead \u2014 one `path` + one `content` string, much simpler to escape \u2014 OR (b) split the change into multiple small `apply_edit` calls that each target just one method or block (e.g. 3-5 lines of `find`, 5-10 lines of `replace`) instead of rewriting the whole class. Pick the smallest scope that accomplishes the next step. If you cannot produce a valid tool call, respond with a plain-prose final answer acknowledging you could not complete the edit."});continue}if(!_n&&!(0,ex.hasToolCalls)(Lo)){let $s=Lo.toLowerCase().replace(/\s+/g," ").trim(),Dn=Hn[Hn.length-1],Jc=!!Dn&&(()=>{let g0=Dn.length<$s.length?Dn:$s,Nu=Dn.length<$s.length?$s:Dn,Vv=0;for(;Vv<g0.length&&g0[Vv]===Nu[Vv];)Vv++;return Vv/g0.length>.6})(),qc=($s.match(/wait,? i see/g)??[]).length,Mr=($s.match(/actually,? i'?ll/g)??[]).length,Hc=qc>=3&&Mr>=3,ol=Lo.includes("[stream aborted: self-contradicting prose loop detected]");if(!ms&&(Jc||Hc||ol)){ms=!0,j("tool_loop:prose_loop_nudge",{iteration:Jt,responsePreview:Lo.slice(0,200),reason:ol?"stream_abort":Hc?"self_contradict":"cross_iteration_similarity"}),Wt.push({role:"user",content:'STOP deliberating. Your last response either repeated itself, contradicted itself (e.g. "Wait, I see X / Actually I\'ll try X"), or was aborted mid-stream as a loop. Do NOT continue speculating about what files might exist. Take exactly one of these actions now: (a) invoke a tool (`list_files`, `read_file`, `search_code`, etc.) to answer the question with real data, OR (b) give up and tell the user plainly that you could not complete the task and why. Do not write more than two sentences of prose before either calling a tool or terminating.'}),Hn.length=0;continue}Hn.push($s),Hn.length>hs&&Hn.shift()}else Hn.length=0;if(!_n&&!(0,ex.hasToolCalls)(Lo)&&!Cc){let $s=/```json\s*\n([\s\S]*?)```/i,Dn=Lo.match($s);if(Dn)try{let Jc=JSON.parse(Dn[1].trim());if(Array.isArray(Jc)&&Jc.length>0&&Jc.every(qc=>qc&&typeof qc=="object"&&typeof qc.content=="string")){Cc=!0,j("tool_loop:json_todo_auto_promoted",{iteration:Jt,itemCount:Jc.length});let qc=this.registry.get("todo_write");if(qc){let Mr={name:"todo_write",params:{items:JSON.stringify(Jc)},raw:`<tool_call>{"name":"todo_write","params":{"items":${JSON.stringify(JSON.stringify(Jc))}}}</tool_call>`};j("tool_loop:tool_execute",{name:"todo_write",params:Mr.params,rawSnippet:Mr.raw.slice(0,400)});try{let Hc=await qc.execute(Mr.params,this.ctx);j("tool_loop:tool_result",{name:"todo_write",isError:Hc.isError,outputLength:Hc.output.length,outputSnippet:Hc.output.slice(0,280),outputFull:Hc.output.slice(0,65536)}),Wt.push({role:"user",content:(0,ex.buildToolResultsMessage)([{name:"todo_write",output:Hc.output,isError:Hc.isError}])})}catch(Hc){let ol=Hc instanceof Error?Hc.message:String(Hc);j("tool_loop:tool_error",{name:"todo_write",error:ol}),Wt.push({role:"user",content:(0,ex.buildToolResultsMessage)([{name:"todo_write",output:`Error: ${ol}`,isError:!0}])})}Wt.push({role:"user",content:'Note: I detected a JSON todo list in your response and auto-promoted it to a todo_write call. Next time, emit `<tool_call>{"name":"todo_write","params":{"items":"..."}}</tool_call>` directly instead of pasting JSON as a code block \u2014 pasted JSON does not update your plan, only the tool call does.'}),Jt++;continue}}}catch{}}if(_n||!(0,ex.hasToolCalls)(Lo)){let $s=(0,ex.stripToolCallMarkup)(Lo).trim();if(!_n&&!Uu&&Ji===0&&sl.some(Jc=>Jc.test($s))){Uu=!0,j("tool_loop:false_completion_nudge",{iteration:Jt,responsePreview:$s.slice(0,200)}),Wt.push({role:"user",content:'Your response either claims work is done OR apologizes and asks what to do next \u2014 but I see NO successful `write_file` or `apply_edit` tool call in this turn, so nothing on disk has changed. Do NOT ask the user which task to resume, do NOT promise to escape JSON "in your next tool call", and do NOT defer. Either (a) emit the `write_file` or `apply_edit` tool call NOW with the actual change \u2014 if the content is long, keep the payload as a single properly-escaped string (use `\\n` for every newline, `\\"` for every internal quote), or (b) respond honestly that you could not complete the task and briefly explain why. Retry the tool call yourself; the user cannot help you escape JSON.'});continue}if(!_n&&!Uu&&Ji>0){let Dn=/[`"']?([\w./\\-]+\.(?:cs|ts|tsx|js|jsx|mjs|cjs|py|rb|go|rs|java|kt|swift|cpp|cc|c|h|hpp|md|json|ya?ml|html|css|scss|sql|toml|sh|bash))[`"']?/gi,Jc=new Set,qc;for(;(qc=Dn.exec($s))!==null;){let Mr=qc[1].split(/[/\\]/),Hc=Mr[Mr.length-1].toLowerCase();Jc.add(Hc)}if(Jc.size>Ji){Uu=!0,j("tool_loop:partial_completion_nudge",{iteration:Jt,editToolsInvoked:Ji,claimedFiles:Jc.size,responsePreview:$s.slice(0,200)}),Wt.push({role:"user",content:`Your response describes edits to ${Jc.size} files (${[...Jc].slice(0,8).join(", ")}${Jc.size>8?", \u2026":""}), but only ${Ji} successful edit${Ji===1?"":"s"} actually fired this turn. The remaining ${Jc.size-Ji} file(s) were NOT modified \u2014 nothing landed on disk for them. Either (a) emit the missing \`apply_edit\` / \`write_file\` tool calls now to actually do the work, OR (b) revise your response to honestly describe ONLY the edits that successfully applied. Do not summarize work that did not happen.`});continue}}if(!_n&&!ei&&Ji===0&&Hs){let Dn=/```[a-zA-Z0-9_-]*\n([\s\S]*?)```/g,qc=0,Mr;for(;(Mr=Dn.exec($s))!==null;){let Hc=Mr[1].split(`
77
+ `).toLowerCase();Hs=/\b(update|edit|change|fix|modify|refactor|rewrite|replace|add)\b/.test(No)||/[\w\-./]+\.(?:ts|tsx|js|jsx|py|rb|go|rs|java|kt|cs|swift|php|cpp|c|h|md|json|ya?ml|html|css)\b/.test(No)}let Ji=0,ei=!1,Cc=!1,Uu=!1,sl=[/in (?:my|a|the) previous response/i,/already provided (?:the|an?) (?:implementation|refactored|improved|updated)/i,/you can find (?:the |this )?(?:refactored|improved|updated) (?:code|implementation)/i,/here (?:is|'s) the (?:refactored|improved|updated|revised) (?:code|implementation|file)/i,/(?:i have|i've) (?:refactored|rewritten|updated|improved)/i,/(?:refactored|updated) (?:the )?(?:code|implementation) above/i,/i'll finalize the task here/i,/i've also marked (?:the tasks|these steps) as complet/i,/i apologi[sz]e for the (?:malformed|invalid)/i,/(?:ensure|escape) (?:all )?(?:quotes|newlines|characters).*(?:properly )?escap/i,/in my next tool call/i,/let me know (?:which|what) (?:task|action) (?:i should |to )?resume/i,/please (?:let me know|tell me).*(?:specific action|which task|what.*like me to)/i,/here (?:is|'s) the (?:final|resulting|updated|modified) (?:state|version|content|output) of/i,/(?:comments?|changes?|edits?|annotations?|updates?) (?:have )?been (?:added|made|applied|written|included)/i,/you can verify (?:these|the|your) (?:changes?|edits?|updates?)/i,/check(?:ing)? the files? (?:directly )?in your editor/i,/running (?:a )?build to (?:see|verify|check)/i];for(;;){if(oe?.aborted)return j("tool_loop:cancelled",{iteration:Jt,stage:"pre_iteration"}),Y(Wt,Jt);Jt>=V&&(_n=!0,Wt.push({role:"user",content:"You have reached the tool use limit. Please give your best final answer based on what you have gathered so far."})),ye>=_e&&!_n&&(_n=!0,j("tool_loop:total_tool_cap",{iteration:Jt,totalToolsExecuted:ye}),Wt.push({role:"user",content:`You have executed ${ye} tool calls this turn \u2014 the per-turn cap (${_e}) has been reached. Please give your best final answer based on what you have gathered so far.`}));let No=k?.messageTokenBudget;if(No!==void 0&&No>0&&Number.isFinite(No)){let $s=(0,Pxt.compactToolMessages)(Wt,{tokenBudget:No});$s.messagesCompacted>0&&(j("tool_loop:compacted",{iteration:Jt,messagesCompacted:$s.messagesCompacted,beforeTokens:$s.beforeTokens,afterTokens:$s.afterTokens}),Wt.length=0,Wt.push(...$s.compacted))}Ut&&!Rn&&Jt>=3&&!_n&&Wt.reduce((Dn,Jc)=>Dn+(Jc.content?.length??0),0)>8e3&&(Rn=!0,j("tool_loop:goal_anchor",{iteration:Jt,goalPreview:Ut.slice(0,120)}),Wt.push({role:"user",content:`Reminder of the original request: "${Ut.trim()}". Answer THAT question directly using what you have gathered. Do not pivot to a related-but-different topic that happens to be salient in the recent tool results.`})),j("tool_loop:llm_start",{iteration:Jt,messageCount:Wt.length});let Lo=await this.streamAndAggregate(S,Wt,j,Jt,un,oe);if(j("tool_loop:llm_response",{iteration:Jt,response:Lo.slice(0,200)}),oe?.aborted)return j("tool_loop:cancelled",{iteration:Jt,stage:"post_stream"}),Y(Wt,Jt,Lo);if(!_n&&/<tool_result\b[\s\S]*?<\/tool_result\s*>|<tool_result\b[^<]*$/i.test(Lo)&&Cr<ci){Cr++,j("tool_loop:fake_tool_result_detected",{iteration:Jt,preview:Lo.slice(0,200)});let $s=Lo.replace(/<tool_result\b[\s\S]*?<\/tool_result\s*>/gi,"").replace(/<tool_result\b[^<]*$/i,"").trim();Wt.push({role:"assistant",content:$s}),Wt.push({role:"user",content:'You emitted a `<tool_result>` envelope in your response. Those envelopes are SYSTEM output \u2014 they appear BETWEEN your turns, never inside your own message. If you meant to invoke a tool, emit a single `<tool_call>{"name":"...","params":{...}}</tool_call>` and wait for the real result. If the task is complete, give a plain-prose final answer with no XML envelopes. Retry now.'});continue}Wt.push({role:"assistant",content:Lo});let mt=Lo.replace(/<think\b[\s\S]*?<\/think\s*>/gi,"").replace(/<think\b[\s\S]*$/i,"").replace(/```bandit-reasoning\b[\s\S]*?```/gi,"").replace(/```bandit-reasoning\b[\s\S]*$/i,"").trim(),wn=!mt&&Lo.trim().length>0,zn=/\b(use|uses|used|using|call|calls|called|calling|invoke|invokes|invoked|invoking|execute|executes|executed|executing|run|runs|running|ran|search|searches|searched|searching|look|looks|looked|looking|read|reads|reading|check|checks|checked|checking|find|finds|finding|found|list|lists|listed|listing|fetch|fetches|fetched|fetching|grep|greps|grepped|grepping|explore|explores|explored|exploring|locate|locates|located|locating)\b[^.\n]*\.?\s*$/i,Ya=/\b(we (?:will|need to|should)|we'?ll|we'?re going to|i'?ll|i will|let me|let'?s|going to|i'?m going to|i need to)\b/i,fs=/```[a-zA-Z0-9_-]*\s*\n/.test(mt),lo=!(0,ex.hasToolCalls)(Lo)&&!fs&&mt.length>0&&mt.length<240&&Ya.test(mt)&&zn.test(mt);if((!Lo.trim()&&Jt>0||wn||lo)&&!_n&&At<2){At++,j("tool_loop:empty_retry",{iteration:Jt,attempt:At,reasoningOnly:wn,narratedButNoAction:lo});let $s=lo?'You announced your next step in prose ("we will search\u2026" / "let me check\u2026" / "use X to find Y") but did NOT emit a `<tool_call>` envelope. Announcing intent is not enough \u2014 you must actually invoke the tool. Emit the call now in this exact format: `<tool_call>{"name":"<tool>","params":{...}}</tool_call>` \u2014 no commentary, no markdown fence around it. Or, if the task is already answerable from what you know, give a final answer instead.':wn?"You completed reasoning but emitted no tool_call and no final answer. Pick the next concrete action: invoke a tool to make progress, OR write a final answer using what you have. Do not stop after only thinking.":"Your previous response was empty. Please either invoke another tool to continue the task OR produce a final answer using what you've gathered so far. Do not respond with an empty message.";Wt.push({role:"user",content:$s});continue}if(At=0,!_n&&(0,ex.looksLikeAttemptedToolCall)(Lo)&&!(0,ex.hasToolCalls)(Lo)&&Xt<Ln){Xt++,j("tool_loop:parse_retry",{iteration:Jt,attempt:Xt});let $s=Xt===1;Wt.push({role:"user",content:$s?'Your previous tool_call was not valid JSON \u2014 I could not parse it. Common cause: unescaped `"` characters inside a string value (for example `["", "", ""]` inside a `content` string). Retry the tool call with properly escaped JSON: every `"` inside a string value must be written as `\\"`, and every newline as `\\n`. If the content is very long, consider shortening it or breaking the change into smaller edits.':"Your tool_call still did not parse. Do NOT retry with the same shape. Switch tactics: (a) call `write_file` instead \u2014 one `path` + one `content` string, much simpler to escape \u2014 OR (b) split the change into multiple small `apply_edit` calls that each target just one method or block (e.g. 3-5 lines of `find`, 5-10 lines of `replace`) instead of rewriting the whole class. Pick the smallest scope that accomplishes the next step. If you cannot produce a valid tool call, respond with a plain-prose final answer acknowledging you could not complete the edit."});continue}if(!_n&&!(0,ex.hasToolCalls)(Lo)){let $s=Lo.toLowerCase().replace(/\s+/g," ").trim(),Dn=Hn[Hn.length-1],Jc=!!Dn&&(()=>{let g0=Dn.length<$s.length?Dn:$s,Nu=Dn.length<$s.length?$s:Dn,Vv=0;for(;Vv<g0.length&&g0[Vv]===Nu[Vv];)Vv++;return Vv/g0.length>.6})(),qc=($s.match(/wait,? i see/g)??[]).length,Mr=($s.match(/actually,? i'?ll/g)??[]).length,Hc=qc>=3&&Mr>=3,ol=Lo.includes("[stream aborted: self-contradicting prose loop detected]");if(!ms&&(Jc||Hc||ol)){ms=!0,j("tool_loop:prose_loop_nudge",{iteration:Jt,responsePreview:Lo.slice(0,200),reason:ol?"stream_abort":Hc?"self_contradict":"cross_iteration_similarity"}),Wt.push({role:"user",content:'STOP deliberating. Your last response either repeated itself, contradicted itself (e.g. "Wait, I see X / Actually I\'ll try X"), or was aborted mid-stream as a loop. Do NOT continue speculating about what files might exist. Take exactly one of these actions now: (a) invoke a tool (`list_files`, `read_file`, `search_code`, etc.) to answer the question with real data, OR (b) give up and tell the user plainly that you could not complete the task and why. Do not write more than two sentences of prose before either calling a tool or terminating.'}),Hn.length=0;continue}Hn.push($s),Hn.length>hs&&Hn.shift()}else Hn.length=0;if(!_n&&!(0,ex.hasToolCalls)(Lo)&&!Cc){let $s=/```json\s*\n([\s\S]*?)```/i,Dn=Lo.match($s);if(Dn)try{let Jc=JSON.parse(Dn[1].trim());if(Array.isArray(Jc)&&Jc.length>0&&Jc.every(qc=>qc&&typeof qc=="object"&&typeof qc.content=="string")){Cc=!0,j("tool_loop:json_todo_auto_promoted",{iteration:Jt,itemCount:Jc.length});let qc=this.registry.get("todo_write");if(qc){let Mr={name:"todo_write",params:{items:JSON.stringify(Jc)},raw:`<tool_call>{"name":"todo_write","params":{"items":${JSON.stringify(JSON.stringify(Jc))}}}</tool_call>`};j("tool_loop:tool_execute",{name:"todo_write",params:Mr.params,rawSnippet:Mr.raw.slice(0,400)});try{let Hc=await qc.execute(Mr.params,this.ctx);j("tool_loop:tool_result",{name:"todo_write",isError:Hc.isError,outputLength:Hc.output.length,outputSnippet:Hc.output.slice(0,280),outputFull:Hc.output.slice(0,65536)}),Wt.push({role:"user",content:(0,ex.buildToolResultsMessage)([{name:"todo_write",output:Hc.output,isError:Hc.isError}])})}catch(Hc){let ol=Hc instanceof Error?Hc.message:String(Hc);j("tool_loop:tool_error",{name:"todo_write",error:ol}),Wt.push({role:"user",content:(0,ex.buildToolResultsMessage)([{name:"todo_write",output:`Error: ${ol}`,isError:!0}])})}Wt.push({role:"user",content:'Note: I detected a JSON todo list in your response and auto-promoted it to a todo_write call. Next time, emit `<tool_call>{"name":"todo_write","params":{"items":"..."}}</tool_call>` directly instead of pasting JSON as a code block \u2014 pasted JSON does not update your plan, only the tool call does.'}),Jt++;continue}}}catch{}}if(_n||!(0,ex.hasToolCalls)(Lo)){let $s=(0,ex.stripToolCallMarkup)(Lo).trim();if(!_n&&!Uu&&Ji===0&&sl.some(Jc=>Jc.test($s))){Uu=!0,j("tool_loop:false_completion_nudge",{iteration:Jt,responsePreview:$s.slice(0,200)}),Wt.push({role:"user",content:'Your response either claims work is done OR apologizes and asks what to do next \u2014 but I see NO successful `write_file` or `apply_edit` tool call in this turn, so nothing on disk has changed. Do NOT ask the user which task to resume, do NOT promise to escape JSON "in your next tool call", and do NOT defer. Either (a) emit the `write_file` or `apply_edit` tool call NOW with the actual change \u2014 if the content is long, keep the payload as a single properly-escaped string (use `\\n` for every newline, `\\"` for every internal quote), or (b) respond honestly that you could not complete the task and briefly explain why. Retry the tool call yourself; the user cannot help you escape JSON.'});continue}if(!_n&&!Uu&&Ji>0){let Dn=/[`"']?([\w./\\-]+\.(?:cs|ts|tsx|js|jsx|mjs|cjs|py|rb|go|rs|java|kt|swift|cpp|cc|c|h|hpp|md|json|ya?ml|html|css|scss|sql|toml|sh|bash))[`"']?/gi,Jc=new Set,qc;for(;(qc=Dn.exec($s))!==null;){let Mr=qc[1].split(/[/\\]/),Hc=Mr[Mr.length-1].toLowerCase();Jc.add(Hc)}if(Jc.size>Ji){Uu=!0,j("tool_loop:partial_completion_nudge",{iteration:Jt,editToolsInvoked:Ji,claimedFiles:Jc.size,responsePreview:$s.slice(0,200)}),Wt.push({role:"user",content:`Your response describes edits to ${Jc.size} files (${[...Jc].slice(0,8).join(", ")}${Jc.size>8?", \u2026":""}), but only ${Ji} successful edit${Ji===1?"":"s"} actually fired this turn. The remaining ${Jc.size-Ji} file(s) were NOT modified \u2014 nothing landed on disk for them. Either (a) emit the missing \`apply_edit\` / \`write_file\` tool calls now to actually do the work, OR (b) revise your response to honestly describe ONLY the edits that successfully applied. Do not summarize work that did not happen.`});continue}}if(!_n&&!ei&&Ji===0&&Hs){let Dn=/```[a-zA-Z0-9_-]*\n([\s\S]*?)```/g,qc=0,Mr;for(;(Mr=Dn.exec($s))!==null;){let Hc=Mr[1].split(`
78
78
  `).filter(ol=>ol.trim().length>0).length;Hc>qc&&(qc=Hc)}if(qc>=8){ei=!0,j("tool_loop:code_fence_nudge",{iteration:Jt,fenceLines:qc,responsePreview:$s.slice(0,200)}),Wt.push({role:"user",content:"You produced a substantial code block in your reply but never emitted a `write_file` or `apply_edit` tool call \u2014 so the change is NOT on disk. Do not ask the user to paste your code into a file themselves. Take exactly one of these actions now: (a) call `apply_edit` or `write_file` with the real change to the correct file, OR (b) say plainly that you could not locate the target file and explain what you searched for. Do not wrap up with another prose + code-fence response."});continue}}return{finalResponse:$s,iterations:Jt,messages:Wt,hitLimit:_n}}let ds=(0,ex.parseToolCalls)(Lo);j("tool_loop:tool_calls",{iteration:Jt,tools:ds.map($s=>$s.name)});let al=ds.length>0&&ds.every($s=>$s.name==="todo_write"),kl=ds.length>0&&!al;if(al?ca++:kl&&(ca=0,Ao=!1),al&&ca>=hn&&!Ao){Ao=!0,j("tool_loop:todo_churn_nudge",{iteration:Jt,consecutive:ca}),ds=[],Wt.push({role:"user",content:`You have revised the plan in ${ca+1} consecutive iterations without executing any step. Execute the first pending task now using a concrete tool \u2014 \`search_code\`, \`read_file\`, \`apply_edit\`, \`write_file\`, or \`run_command\`. Once a task is actually DONE (tool call succeeded), you may call \`todo_write\` again to mark it completed \u2014 but not to re-plan. If you cannot identify a next step, respond to the user with a short honest explanation and stop.`}),Jt++;continue}if(ds.length>1){let $s=new Set,Dn=[],Jc=0;for(let qc of ds){let Mr=`${qc.name}::${JSON.stringify(qc.params)}`;if($s.has(Mr)){Jc++;continue}$s.add(Mr),Dn.push(qc)}Jc>0&&j("tool_loop:tool_call_deduped",{iteration:Jt,removed:Jc,kept:Dn.length}),ds=Dn}let Fc=0;ds.length>de&&(Fc=ds.length-de,j("tool_loop:tool_call_capped",{iteration:Jt,requested:ds.length+0,kept:de,dropped:Fc}),ds=ds.slice(0,de));let Df=Math.max(0,_e-ye);ds.length>Df&&(j("tool_loop:tool_call_total_capped",{iteration:Jt,requested:ds.length,kept:Df,totalSoFar:ye,maxTotalTools:_e}),ds=ds.slice(0,Df)),ye+=ds.length;let Il=await Promise.all(ds.map(async $s=>{let Dn=$s.params.path??$s.params.file??$s.params.filepath,Jc=$s.name==="apply_edit"||$s.name==="write_file",qc;if(Dn&&Jc){let ol=`${$s.params.find??""}::${$s.params.replace??""}::${$s.params.content??""}`,g0=0;for(let Nu=0;Nu<ol.length;Nu++)g0=g0*31+ol.charCodeAt(Nu)|0;qc=`${$s.name}::${Dn}::${g0.toString(36)}`}else Dn?qc=`${$s.name}::${Dn}`:qc=`${$s.name}::${JSON.stringify($s.params).slice(0,160)}`;if(Jn.push(qc),Jn.length>oa&&Jn.shift(),Jn.length===oa&&Jn.every(ol=>ol===qc))return j("tool_loop:repeat_breaker",{name:$s.name,key:qc}),{name:$s.name,output:`Loop detected: ${$s.name} has been invoked ${oa} times in a row against the same target (${Dn??"identical params"}) without progress. This usually means the last write landed malformed \u2014 most often an unescaped \`"\` inside the JSON content string truncated the file. STOP retrying. Either (a) produce a final answer that explains the issue to the user, or (b) break the content into smaller edits. Do not call ${$s.name} with these params again.`,isError:!0};let Mr=this.registry.get($s.name);if(!Mr)return j("tool_loop:tool_not_found",{name:$s.name}),{name:$s.name,output:`Error: tool "${$s.name}" is not registered.`,isError:!0};($s.name==="write_file"||$s.name==="apply_edit")&&Ji++,j("tool_loop:tool_execute",{name:$s.name,params:$s.params,rawSnippet:$s.raw.slice(0,400)});let Hc=await ee({name:$s.name,params:$s.params});if(!Hc.allow){let ol=Hc.reason??"blocked by pre-execute guard";return j("tool_loop:tool_blocked",{name:$s.name,reason:ol}),{name:$s.name,output:`Blocked: ${ol}`,isError:!0}}try{let ol=await Mr.execute($s.params,this.ctx);return j("tool_loop:tool_result",{name:$s.name,isError:ol.isError,outputLength:ol.output.length,outputSnippet:ol.output.slice(0,280)}),{name:$s.name,output:ol.output,isError:ol.isError}}catch(ol){let g0=ol instanceof Error?ol.message:String(ol);return j("tool_loop:tool_error",{name:$s.name,error:g0}),{name:$s.name,output:`Error executing tool "${$s.name}": ${g0}`,isError:!0}}})),Fd=(0,ex.buildToolResultsMessage)(Il);if(Fc>0&&(Fd+=`
79
79
 
80
80
  [Note: you emitted ${Fc+ds.length} tool calls in one iteration; only the first ${ds.length} were executed. Do not re-issue duplicates \u2014 instead, read the results above and pick a single most-promising next action.]`),Wt.push({role:"user",content:Fd}),!rt){for(let $s=0;$s<ds.length;$s++){let Dn=ds[$s],Jc=Il[$s];Dn.name==="todo_write"?(Ms=Jt,Qs=0):(Dn.name==="apply_edit"||Dn.name==="write_file")&&Jc&&!Jc.isError&&Qs++}!Nr&&Ms>=0&&Jt-Ms>=Ro&&Qs>=Ks&&(Nr=!0,j("tool_loop:todo_progress_nudge",{iteration:Jt,editsSinceLastTodo:Qs,iterationsSinceLastTodo:Jt-Ms}),Wt.push({role:"user",content:`You set up a plan with \`todo_write\` earlier but have since completed ${Qs} edit${Qs===1?"":"s"} without updating it. Call \`todo_write\` now with the current status \u2014 mark finished items as \`completed\` and leave remaining items as \`pending\`. The Plan block in the user's UI mirrors your last \`todo_write\`, so skipping this leaves them looking at a stale checklist while real work has landed.`}))}Jt++}}async streamAndAggregate(m,S,T,k,j,V){let ee="",Y=[],rt=800,Je=!1;for await(let qe of m(S,j)){if(V?.aborted){Je=!0,T("tool_loop:stream_abort",{iteration:k,reason:"cancelled",length:ee.length});break}if(ee+=qe,T("tool_loop:llm_chunk",{iteration:k,chunk:qe}),ee.length>=24e3){Je=!0,T("tool_loop:stream_abort",{iteration:k,reason:"hard_max",length:ee.length});break}if(ee.length>=rt){rt=ee.length+800;let Wt=ee.slice(Math.max(0,ee.length-400)).toLowerCase().replace(/\s+/g," ").replace(/\d+/g,"#").trim();if(Y.push(Wt),Y.length>3&&Y.shift(),Y.length===3&&Y.every(Ut=>Ut===Wt)){Je=!0,T("tool_loop:stream_abort",{iteration:k,reason:"intra_response_loop",length:ee.length,fingerprintPreview:Wt.slice(0,120)});break}}}return Je&&(ee=ee.replace(/\s+$/,"")+`
@@ -1044,7 +1044,7 @@ ${p.accomplishments.topFiles.length>0?`
1044
1044
  `),T=m.split(`
1045
1045
  `),k=S.length,j=T.length;if(k*j>4e4)return{plus:T.length,minus:S.length};let V=Array.from({length:k+1},()=>new Array(j+1).fill(0));for(let ye=1;ye<=k;ye++)for(let Y=1;Y<=j;Y++)V[ye][Y]=S[ye-1]===T[Y-1]?V[ye-1][Y-1]+1:Math.max(V[ye-1][Y],V[ye][Y-1]);let ee=k,oe=j,de=0,_e=0;for(;ee>0&&oe>0;)S[ee-1]===T[oe-1]?(ee--,oe--):V[ee][oe-1]>=V[ee-1][oe]?(de++,oe--):(_e++,ee--);return de+=oe,_e+=ee,{plus:de,minus:_e}}t(lIt,"lineCountDiff");function uIt(p){if(p.isNewFile)return"(new file)";let m=p.after.split(`
1046
1046
  `),S=p.before.split(`
1047
- `);for(let T=0;T<m.length;T++)if(m[T]!==S[T]){let k=(m[T]??"").trim();if(k)return k.length>80?k.slice(0,77)+"\u2026":k}return"(whitespace-only edit)"}t(uIt,"describeEdit");var i2e=class{static{t(this,"CheckpointStore")}constructor(m){this.counter=0,this.workspaceRoot=m.workspaceRoot,this.dir=n6.join(m.workspaceRoot,".bandit","checkpoints"),this.maxIndexEntries=m.maxIndexEntries??cIt}async create(m){await oE.promises.mkdir(n6.join(this.dir,m.turnId),{recursive:!0}),this.counter++;let S=`chk-${m.turnId.split("-").pop()??"x"}-${String(this.counter).padStart(3,"0")}`,T=n6.relative(this.workspaceRoot,m.absolutePath)||m.absolutePath,k=m.before.length===0,j={id:S,turnId:m.turnId,timestamp:Date.now(),tool:m.tool,path:m.absolutePath,relPath:T,before:m.before,after:m.after,isNewFile:k,iteration:m.iteration,description:uIt({before:m.before,after:m.after,isNewFile:k})},V=n6.join(this.dir,m.turnId,`${S}.json`);return await oE.promises.writeFile(V,JSON.stringify(j,null,2)),await this.appendToIndex(j),j}async list(m=20){return(await this.readIndex()).slice(0,m)}async get(m){let T=(await this.readIndex()).find(j=>j.id===m);if(!T)return null;let k=n6.join(this.dir,T.turnId,`${m}.json`);try{let j=await oE.promises.readFile(k,"utf-8");return JSON.parse(j)}catch{return null}}async rewind(m){let S=await this.get(m);if(!S)return null;try{return S.isNewFile?await oE.promises.unlink(S.path).catch(()=>{}):(await oE.promises.mkdir(n6.dirname(S.path),{recursive:!0}),await oE.promises.writeFile(S.path,S.before)),S}catch{return null}}async readIndex(){let m=n6.join(this.dir,y$e);try{let S=await oE.promises.readFile(m,"utf-8"),T=JSON.parse(S);return Array.isArray(T)?T:[]}catch{return[]}}async writeIndex(m){await oE.promises.mkdir(this.dir,{recursive:!0});let S=n6.join(this.dir,y$e);await oE.promises.writeFile(S,JSON.stringify(m,null,2))}async appendToIndex(m){let{plus:S,minus:T}=lIt(m.before,m.after),k={id:m.id,turnId:m.turnId,timestamp:m.timestamp,tool:m.tool,relPath:m.relPath,iteration:m.iteration,description:m.description,plus:S,minus:T},j=await this.readIndex();j.unshift(k);let V=j.slice(0,this.maxIndexEntries);await this.writeIndex(V)}};pk.CheckpointStore=i2e});var vJ=hi(uc=>{"use strict";Object.defineProperty(uc,"__esModule",{value:!0});uc.CheckpointStore=uc.isChatCapable=uc.suggestOllamaMatch=uc.listInstalledOllamaModels=uc.previewText=uc.openTurnLog=uc.SessionPermissionStore=uc.mergePolicies=uc.emptyPolicy=uc.evaluatePermission=uc.InMemoryBackgroundTaskStore=uc.writeInsightsReport=uc.renderInsightsHtml=uc.computeInsights=uc.buildListTasksTool=uc.buildCheckTaskTool=uc.buildTaskTool=uc.buildWebFetchTool=uc.buildTodoWriteTool=uc.TodoStore=uc.expandMentions=uc.runHooks=uc.persistAllowEntry=uc.loadHookSettings=uc.loadMemory=void 0;var fIt=HWe();Object.defineProperty(uc,"loadMemory",{enumerable:!0,get:t(function(){return fIt.loadMemory},"get")});var s2e=XWe();Object.defineProperty(uc,"loadHookSettings",{enumerable:!0,get:t(function(){return s2e.loadHookSettings},"get")});Object.defineProperty(uc,"persistAllowEntry",{enumerable:!0,get:t(function(){return s2e.persistAllowEntry},"get")});Object.defineProperty(uc,"runHooks",{enumerable:!0,get:t(function(){return s2e.runHooks},"get")});var _It=ZWe();Object.defineProperty(uc,"expandMentions",{enumerable:!0,get:t(function(){return _It.expandMentions},"get")});var a2e=t$e();Object.defineProperty(uc,"TodoStore",{enumerable:!0,get:t(function(){return a2e.TodoStore},"get")});Object.defineProperty(uc,"buildTodoWriteTool",{enumerable:!0,get:t(function(){return a2e.buildTodoWriteTool},"get")});Object.defineProperty(uc,"buildWebFetchTool",{enumerable:!0,get:t(function(){return a2e.buildWebFetchTool},"get")});var o2e=i$e();Object.defineProperty(uc,"buildTaskTool",{enumerable:!0,get:t(function(){return o2e.buildTaskTool},"get")});Object.defineProperty(uc,"buildCheckTaskTool",{enumerable:!0,get:t(function(){return o2e.buildCheckTaskTool},"get")});Object.defineProperty(uc,"buildListTasksTool",{enumerable:!0,get:t(function(){return o2e.buildListTasksTool},"get")});var c2e=l$e();Object.defineProperty(uc,"computeInsights",{enumerable:!0,get:t(function(){return c2e.computeInsights},"get")});Object.defineProperty(uc,"renderInsightsHtml",{enumerable:!0,get:t(function(){return c2e.renderInsightsHtml},"get")});Object.defineProperty(uc,"writeInsightsReport",{enumerable:!0,get:t(function(){return c2e.writeInsightsReport},"get")});var pIt=u$e();Object.defineProperty(uc,"InMemoryBackgroundTaskStore",{enumerable:!0,get:t(function(){return pIt.InMemoryBackgroundTaskStore},"get")});var eee=f$e();Object.defineProperty(uc,"evaluatePermission",{enumerable:!0,get:t(function(){return eee.evaluatePermission},"get")});Object.defineProperty(uc,"emptyPolicy",{enumerable:!0,get:t(function(){return eee.emptyPolicy},"get")});Object.defineProperty(uc,"mergePolicies",{enumerable:!0,get:t(function(){return eee.mergePolicies},"get")});Object.defineProperty(uc,"SessionPermissionStore",{enumerable:!0,get:t(function(){return eee.SessionPermissionStore},"get")});var S$e=g$e();Object.defineProperty(uc,"openTurnLog",{enumerable:!0,get:t(function(){return S$e.openTurnLog},"get")});Object.defineProperty(uc,"previewText",{enumerable:!0,get:t(function(){return S$e.previewText},"get")});var l2e=h$e();Object.defineProperty(uc,"listInstalledOllamaModels",{enumerable:!0,get:t(function(){return l2e.listInstalledOllamaModels},"get")});Object.defineProperty(uc,"suggestOllamaMatch",{enumerable:!0,get:t(function(){return l2e.suggestOllamaMatch},"get")});Object.defineProperty(uc,"isChatCapable",{enumerable:!0,get:t(function(){return l2e.isChatCapable},"get")});var dIt=b$e();Object.defineProperty(uc,"CheckpointStore",{enumerable:!0,get:t(function(){return dIt.CheckpointStore},"get")})});var tee=hi((P5t,mIt)=>{mIt.exports={name:"@burtson-labs/bandit-stealth-cli",version:"1.7.88",description:"Bandit \u2014 a local-first AI coding agent for your terminal. Same runtime as the Bandit Stealth VS Code / Cursor extension.",keywords:["ai","agent","cli","coding-agent","llm","ollama","local-first","bandit","burtson-labs","terminal","repl","developer-tools"],homepage:"https://burtson.ai",bugs:{email:"team@burtson.ai"},license:"MIT",author:{name:"Burtson Labs",email:"team@burtson.ai",url:"https://burtson.ai"},bin:{bandit:"./dist/cli.js"},main:"dist/cli.js",files:["dist/cli.js","README.md","LICENSE"],engines:{node:">=20"},publishConfig:{access:"public"},scripts:{typecheck:"tsc -p tsconfig.json --noEmit",build:"node build.mjs","build:publish":"node build.mjs --publish",dev:"node build.mjs --watch",start:"node dist/cli.js",smoke:"node build.mjs && node dist/__smoke__/smoke.js",integration:"node build.mjs && node dist/__integration__/ollama.js",eval:"node build.mjs && node dist/__eval__/eval.js",benchmark:"node build.mjs && node dist/__eval__/benchmark.js","gen-logo":"node scripts/gen-logo.mjs","preview-banner":"node scripts/preview-banner.mjs",clean:"rm -rf dist",prepack:"node scripts/prepack.mjs",postpack:"node scripts/postpack.mjs",prepublishOnly:"pnpm run clean && pnpm run typecheck && pnpm run build:publish"},dependencies:{"pdf-parse":"^2.4.5"},devDependencies:{"@burtson-labs/agent-core":"workspace:*","@burtson-labs/host-kit":"workspace:*","@burtson-labs/stealth-core-runtime":"workspace:*","@types/node":"^20.11.0","@types/pdf-parse":"^1.1.5","@types/pngjs":"^6.0.5",esbuild:"^0.28.0",pngjs:"^7.0.0",typescript:"^5.4.0"}}});var FIt={};module.exports=B2t(FIt);var Od=Au(require("fs")),u2e=Au(require("os")),rd=Au(require("path")),CF=Au(require("readline")),iee=Au(require("child_process")),CS=Au(aZ()),qN=Au(RSe());var WN=Au(require("fs")),LSe=Au(require("os")),QD=Au(require("path")),dJ=Au(require("child_process"));function _J(p){return p==="~"?LSe.homedir():p.startsWith("~/")?QD.join(LSe.homedir(),p.slice(2)):p}t(_J,"expandHome");var hF=16*1024,pJ=32*1024,gWe=1e4,B4t=3e4,jSe=200,BSe=new Set(["node_modules",".git","dist","build","out",".next",".turbo","coverage","target","__pycache__",".venv","venv"]);function J4t(p){let m=t(T=>{let k=T.match(/^(.*?)\{([^}]+)\}(.*)$/);if(!k)return[T];let[,j,V,ee]=k;return V.split(",").map(oe=>`${j}${oe.trim()}${ee}`)},"braceExpand"),S=p.match(/^([^*{}]+?)\/\*\*\/(.+)$/);if(S){let[,T,k]=S;return{includes:m(k),subDir:T}}return{includes:m(p),subDir:""}}t(J4t,"expandGlobForGrep");var yF=class{constructor(m,S,T={}){this.workspaceRoot=m;this.languageAdapters=S;this.options=T}static{t(this,"CliToolExecutionContext")}async readFile(m){return WN.promises.readFile(_J(m),"utf-8")}async writeFile(m,S){let T=_J(m);if(this.options.approveWrite&&!await this.options.approveWrite(T,S))throw new Error(`Write to ${T} rejected by user`);await WN.promises.mkdir(QD.dirname(T),{recursive:!0}),await WN.promises.writeFile(T,S,"utf-8")}async listFiles(m,S){let T=_J(S??this.workspaceRoot),k=z4t(m),j=[];return await hWe(T,T,k,j),j.slice(0,jSe).sort()}async listDirectoryEntries(m){let S=_J(m),T=await WN.promises.readdir(S,{withFileTypes:!0}),k=[];for(let j of T){if(j.name.startsWith("."))continue;let V=j.isDirectory();if(j.isSymbolicLink())try{V=(await WN.promises.stat(QD.join(S,j.name))).isDirectory()}catch{V=!1}k.push(V?`${j.name}/`:j.name)}return k.sort()}async searchCode(m,S,T){let k=_J(S??this.workspaceRoot);return this.runRipgrep(m,k,T).catch(()=>this.runGrep(m,k,T))}async runCommand(m,S,T){return new Promise(k=>{let j="",V="",ee=dJ.spawn(m,S,{cwd:T??this.workspaceRoot,shell:process.platform==="win32",env:{...process.env}}),oe=setTimeout(()=>{ee.kill("SIGTERM"),k({stdout:j.slice(0,pJ),stderr:V+`
1047
+ `);for(let T=0;T<m.length;T++)if(m[T]!==S[T]){let k=(m[T]??"").trim();if(k)return k.length>80?k.slice(0,77)+"\u2026":k}return"(whitespace-only edit)"}t(uIt,"describeEdit");var i2e=class{static{t(this,"CheckpointStore")}constructor(m){this.counter=0,this.workspaceRoot=m.workspaceRoot,this.dir=n6.join(m.workspaceRoot,".bandit","checkpoints"),this.maxIndexEntries=m.maxIndexEntries??cIt}async create(m){await oE.promises.mkdir(n6.join(this.dir,m.turnId),{recursive:!0}),this.counter++;let S=`chk-${m.turnId.split("-").pop()??"x"}-${String(this.counter).padStart(3,"0")}`,T=n6.relative(this.workspaceRoot,m.absolutePath)||m.absolutePath,k=m.before.length===0,j={id:S,turnId:m.turnId,timestamp:Date.now(),tool:m.tool,path:m.absolutePath,relPath:T,before:m.before,after:m.after,isNewFile:k,iteration:m.iteration,description:uIt({before:m.before,after:m.after,isNewFile:k})},V=n6.join(this.dir,m.turnId,`${S}.json`);return await oE.promises.writeFile(V,JSON.stringify(j,null,2)),await this.appendToIndex(j),j}async list(m=20){return(await this.readIndex()).slice(0,m)}async get(m){let T=(await this.readIndex()).find(j=>j.id===m);if(!T)return null;let k=n6.join(this.dir,T.turnId,`${m}.json`);try{let j=await oE.promises.readFile(k,"utf-8");return JSON.parse(j)}catch{return null}}async rewind(m){let S=await this.get(m);if(!S)return null;try{return S.isNewFile?await oE.promises.unlink(S.path).catch(()=>{}):(await oE.promises.mkdir(n6.dirname(S.path),{recursive:!0}),await oE.promises.writeFile(S.path,S.before)),S}catch{return null}}async readIndex(){let m=n6.join(this.dir,y$e);try{let S=await oE.promises.readFile(m,"utf-8"),T=JSON.parse(S);return Array.isArray(T)?T:[]}catch{return[]}}async writeIndex(m){await oE.promises.mkdir(this.dir,{recursive:!0});let S=n6.join(this.dir,y$e);await oE.promises.writeFile(S,JSON.stringify(m,null,2))}async appendToIndex(m){let{plus:S,minus:T}=lIt(m.before,m.after),k={id:m.id,turnId:m.turnId,timestamp:m.timestamp,tool:m.tool,relPath:m.relPath,iteration:m.iteration,description:m.description,plus:S,minus:T},j=await this.readIndex();j.unshift(k);let V=j.slice(0,this.maxIndexEntries);await this.writeIndex(V)}};pk.CheckpointStore=i2e});var vJ=hi(uc=>{"use strict";Object.defineProperty(uc,"__esModule",{value:!0});uc.CheckpointStore=uc.isChatCapable=uc.suggestOllamaMatch=uc.listInstalledOllamaModels=uc.previewText=uc.openTurnLog=uc.SessionPermissionStore=uc.mergePolicies=uc.emptyPolicy=uc.evaluatePermission=uc.InMemoryBackgroundTaskStore=uc.writeInsightsReport=uc.renderInsightsHtml=uc.computeInsights=uc.buildListTasksTool=uc.buildCheckTaskTool=uc.buildTaskTool=uc.buildWebFetchTool=uc.buildTodoWriteTool=uc.TodoStore=uc.expandMentions=uc.runHooks=uc.persistAllowEntry=uc.loadHookSettings=uc.loadMemory=void 0;var fIt=HWe();Object.defineProperty(uc,"loadMemory",{enumerable:!0,get:t(function(){return fIt.loadMemory},"get")});var s2e=XWe();Object.defineProperty(uc,"loadHookSettings",{enumerable:!0,get:t(function(){return s2e.loadHookSettings},"get")});Object.defineProperty(uc,"persistAllowEntry",{enumerable:!0,get:t(function(){return s2e.persistAllowEntry},"get")});Object.defineProperty(uc,"runHooks",{enumerable:!0,get:t(function(){return s2e.runHooks},"get")});var _It=ZWe();Object.defineProperty(uc,"expandMentions",{enumerable:!0,get:t(function(){return _It.expandMentions},"get")});var a2e=t$e();Object.defineProperty(uc,"TodoStore",{enumerable:!0,get:t(function(){return a2e.TodoStore},"get")});Object.defineProperty(uc,"buildTodoWriteTool",{enumerable:!0,get:t(function(){return a2e.buildTodoWriteTool},"get")});Object.defineProperty(uc,"buildWebFetchTool",{enumerable:!0,get:t(function(){return a2e.buildWebFetchTool},"get")});var o2e=i$e();Object.defineProperty(uc,"buildTaskTool",{enumerable:!0,get:t(function(){return o2e.buildTaskTool},"get")});Object.defineProperty(uc,"buildCheckTaskTool",{enumerable:!0,get:t(function(){return o2e.buildCheckTaskTool},"get")});Object.defineProperty(uc,"buildListTasksTool",{enumerable:!0,get:t(function(){return o2e.buildListTasksTool},"get")});var c2e=l$e();Object.defineProperty(uc,"computeInsights",{enumerable:!0,get:t(function(){return c2e.computeInsights},"get")});Object.defineProperty(uc,"renderInsightsHtml",{enumerable:!0,get:t(function(){return c2e.renderInsightsHtml},"get")});Object.defineProperty(uc,"writeInsightsReport",{enumerable:!0,get:t(function(){return c2e.writeInsightsReport},"get")});var pIt=u$e();Object.defineProperty(uc,"InMemoryBackgroundTaskStore",{enumerable:!0,get:t(function(){return pIt.InMemoryBackgroundTaskStore},"get")});var eee=f$e();Object.defineProperty(uc,"evaluatePermission",{enumerable:!0,get:t(function(){return eee.evaluatePermission},"get")});Object.defineProperty(uc,"emptyPolicy",{enumerable:!0,get:t(function(){return eee.emptyPolicy},"get")});Object.defineProperty(uc,"mergePolicies",{enumerable:!0,get:t(function(){return eee.mergePolicies},"get")});Object.defineProperty(uc,"SessionPermissionStore",{enumerable:!0,get:t(function(){return eee.SessionPermissionStore},"get")});var S$e=g$e();Object.defineProperty(uc,"openTurnLog",{enumerable:!0,get:t(function(){return S$e.openTurnLog},"get")});Object.defineProperty(uc,"previewText",{enumerable:!0,get:t(function(){return S$e.previewText},"get")});var l2e=h$e();Object.defineProperty(uc,"listInstalledOllamaModels",{enumerable:!0,get:t(function(){return l2e.listInstalledOllamaModels},"get")});Object.defineProperty(uc,"suggestOllamaMatch",{enumerable:!0,get:t(function(){return l2e.suggestOllamaMatch},"get")});Object.defineProperty(uc,"isChatCapable",{enumerable:!0,get:t(function(){return l2e.isChatCapable},"get")});var dIt=b$e();Object.defineProperty(uc,"CheckpointStore",{enumerable:!0,get:t(function(){return dIt.CheckpointStore},"get")})});var tee=hi((P5t,mIt)=>{mIt.exports={name:"@burtson-labs/bandit-stealth-cli",version:"1.7.90",description:"Bandit \u2014 a local-first AI coding agent for your terminal. Same runtime as the Bandit Stealth VS Code / Cursor extension.",keywords:["ai","agent","cli","coding-agent","llm","ollama","local-first","bandit","burtson-labs","terminal","repl","developer-tools"],homepage:"https://burtson.ai",bugs:{email:"team@burtson.ai"},license:"MIT",author:{name:"Burtson Labs",email:"team@burtson.ai",url:"https://burtson.ai"},bin:{bandit:"./dist/cli.js"},main:"dist/cli.js",files:["dist/cli.js","README.md","LICENSE"],engines:{node:">=20"},publishConfig:{access:"public"},scripts:{typecheck:"tsc -p tsconfig.json --noEmit",build:"node build.mjs","build:publish":"node build.mjs --publish",dev:"node build.mjs --watch",start:"node dist/cli.js",smoke:"node build.mjs && node dist/__smoke__/smoke.js",integration:"node build.mjs && node dist/__integration__/ollama.js",eval:"node build.mjs && node dist/__eval__/eval.js",benchmark:"node build.mjs && node dist/__eval__/benchmark.js","gen-logo":"node scripts/gen-logo.mjs","preview-banner":"node scripts/preview-banner.mjs",clean:"rm -rf dist",prepack:"node scripts/prepack.mjs",postpack:"node scripts/postpack.mjs",prepublishOnly:"pnpm run clean && pnpm run typecheck && pnpm run build:publish"},dependencies:{"pdf-parse":"^2.4.5"},devDependencies:{"@burtson-labs/agent-core":"workspace:*","@burtson-labs/host-kit":"workspace:*","@burtson-labs/stealth-core-runtime":"workspace:*","@types/node":"^20.11.0","@types/pdf-parse":"^1.1.5","@types/pngjs":"^6.0.5",esbuild:"^0.28.0",pngjs:"^7.0.0",typescript:"^5.4.0"}}});var FIt={};module.exports=B2t(FIt);var Od=Au(require("fs")),u2e=Au(require("os")),rd=Au(require("path")),CF=Au(require("readline")),iee=Au(require("child_process")),CS=Au(aZ()),qN=Au(RSe());var WN=Au(require("fs")),LSe=Au(require("os")),QD=Au(require("path")),dJ=Au(require("child_process"));function _J(p){return p==="~"?LSe.homedir():p.startsWith("~/")?QD.join(LSe.homedir(),p.slice(2)):p}t(_J,"expandHome");var hF=16*1024,pJ=32*1024,gWe=1e4,B4t=3e4,jSe=200,BSe=new Set(["node_modules",".git","dist","build","out",".next",".turbo","coverage","target","__pycache__",".venv","venv"]);function J4t(p){let m=t(T=>{let k=T.match(/^(.*?)\{([^}]+)\}(.*)$/);if(!k)return[T];let[,j,V,ee]=k;return V.split(",").map(oe=>`${j}${oe.trim()}${ee}`)},"braceExpand"),S=p.match(/^([^*{}]+?)\/\*\*\/(.+)$/);if(S){let[,T,k]=S;return{includes:m(k),subDir:T}}return{includes:m(p),subDir:""}}t(J4t,"expandGlobForGrep");var yF=class{constructor(m,S,T={}){this.workspaceRoot=m;this.languageAdapters=S;this.options=T}static{t(this,"CliToolExecutionContext")}async readFile(m){return WN.promises.readFile(_J(m),"utf-8")}async writeFile(m,S){let T=_J(m);if(this.options.approveWrite&&!await this.options.approveWrite(T,S))throw new Error(`Write to ${T} rejected by user`);await WN.promises.mkdir(QD.dirname(T),{recursive:!0}),await WN.promises.writeFile(T,S,"utf-8")}async listFiles(m,S){let T=_J(S??this.workspaceRoot),k=z4t(m),j=[];return await hWe(T,T,k,j),j.slice(0,jSe).sort()}async listDirectoryEntries(m){let S=_J(m),T=await WN.promises.readdir(S,{withFileTypes:!0}),k=[];for(let j of T){if(j.name.startsWith("."))continue;let V=j.isDirectory();if(j.isSymbolicLink())try{V=(await WN.promises.stat(QD.join(S,j.name))).isDirectory()}catch{V=!1}k.push(V?`${j.name}/`:j.name)}return k.sort()}async searchCode(m,S,T){let k=_J(S??this.workspaceRoot);return this.runRipgrep(m,k,T).catch(()=>this.runGrep(m,k,T))}async runCommand(m,S,T){return new Promise(k=>{let j="",V="",ee=dJ.spawn(m,S,{cwd:T??this.workspaceRoot,shell:process.platform==="win32",env:{...process.env}}),oe=setTimeout(()=>{ee.kill("SIGTERM"),k({stdout:j.slice(0,pJ),stderr:V+`
1048
1048
  [process timed out]`,exitCode:124})},B4t);ee.stdout?.on("data",de=>{j+=de.toString(),j.length>pJ&&ee.kill("SIGTERM")}),ee.stderr?.on("data",de=>{V+=de.toString()}),ee.on("close",de=>{clearTimeout(oe),k({stdout:j.slice(0,pJ),stderr:V.slice(0,4*1024),exitCode:de??0})}),ee.on("error",de=>{clearTimeout(oe),k({stdout:"",stderr:de.message,exitCode:1})})})}async watchCommand(m,S,T,k){return new Promise(j=>{let V="",ee="",oe=!1,de=!1,_e=dJ.spawn(m,S,{cwd:T??this.workspaceRoot,shell:process.platform==="win32",env:{...process.env}}),ye=t(rt=>{de||(de=!0,j({stdout:V.slice(0,pJ),stderr:ee.slice(0,4*1024),exitCode:rt,endedEarly:oe}))},"finish"),Y=setTimeout(()=>{try{_e.kill("SIGTERM")}catch{}let rt=setTimeout(()=>{try{_e.kill("SIGKILL")}catch{}ye(null)},1e3);_e.once("close",Je=>{clearTimeout(rt),ye(typeof Je=="number"?Je:null)})},k);_e.stdout?.on("data",rt=>{if(V+=rt.toString(),V.length>pJ)try{_e.kill("SIGTERM")}catch{}}),_e.stderr?.on("data",rt=>{ee+=rt.toString()}),_e.on("close",rt=>{de||(clearTimeout(Y),oe=!0,ye(typeof rt=="number"?rt:null))}),_e.on("error",rt=>{de||(clearTimeout(Y),oe=!0,ee+=rt.message,ye(1))})})}runRipgrep(m,S,T){return new Promise((k,j)=>{let V=["--color=never","--line-number","--max-count=25","--max-filesize=1M",...[...BSe].map(_e=>["--glob",`!${_e}`]).flat()];T&&V.push("--glob",T),V.push(m,S);let ee="",oe=dJ.spawn("rg",V,{shell:!1}),de=setTimeout(()=>{oe.kill("SIGTERM"),k(ee.slice(0,hF))},gWe);oe.stdout?.on("data",_e=>{ee+=_e.toString(),ee.length>hF&&oe.kill("SIGTERM")}),oe.on("close",_e=>{clearTimeout(de),_e!=null&&_e>=2&&ee.length===0?j(new Error(`rg exited with code ${_e}`)):k(ee.slice(0,hF))}),oe.on("error",j)})}runGrep(m,S,T){return new Promise((k,j)=>{let V=[...BSe].map(Je=>["--exclude-dir",Je]).flat(),ee=T?J4t(T):{includes:[],subDir:""},oe=ee.includes.flatMap(Je=>["--include",Je]),de=ee.subDir?`${S}/${ee.subDir}`:S,_e=["-rn","-E","--color=never",...V,...oe,m,de],ye="",Y=dJ.spawn("grep",_e,{shell:!1}),rt=setTimeout(()=>{Y.kill("SIGTERM"),k(ye.slice(0,hF))},gWe);Y.stdout?.on("data",Je=>{ye+=Je.toString(),ye.length>hF&&Y.kill("SIGTERM")}),Y.on("close",Je=>{clearTimeout(rt),Je!=null&&Je>=2&&ye.length===0?j(new Error(`grep exited with code ${Je}`)):k(ye.slice(0,hF))}),Y.on("error",j)})}};function z4t(p){let m=W4t(p);return S=>m.test(S.replace(/\\/g,"/"))}t(z4t,"compileGlob");function W4t(p){let m="^";for(let S=0;S<p.length;S++){let T=p[S];if(T==="*")p[S+1]==="*"?(m+=".*",S++,p[S+1]==="/"&&S++):m+="[^/]*";else if(T==="?")m+="[^/]";else if(T==="{"){let k=p.indexOf("}",S);if(k===-1){m+="\\{";continue}let j=p.slice(S+1,k).split(",").map($4t).join("|");m+=`(?:${j})`,S=k}else/[.+^$()|\\]/.test(T)?m+="\\"+T:m+=T}return m+="$",new RegExp(m)}t(W4t,"globToRegex");function $4t(p){return p.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}t($4t,"escapeRegex");async function hWe(p,m,S,T){if(T.length>=jSe)return;let k;try{k=await WN.promises.readdir(p,{withFileTypes:!0})}catch{return}for(let j of k){if(T.length>=jSe)return;if(BSe.has(j.name))continue;let V=QD.join(p,j.name),ee=QD.relative(m,V);j.isDirectory()?await hWe(V,m,S,T):j.isFile()&&S(ee)&&T.push(V)}}t(hWe,"walk");var vWe=Au(require("child_process")),uk=Au(require("fs")),bWe=Au(require("os")),JSe=Au(require("path")),SWe=Au(require("crypto"));function zSe(){let p=SWe.randomBytes(4).toString("hex");return JSe.join(bWe.tmpdir(),`bandit-paste-${Date.now()}-${p}.png`)}t(zSe,"freshTempPath");function zZ(p,m,S={}){try{let T=vWe.spawnSync(p,m,{...S,encoding:void 0});return{stdout:Buffer.isBuffer(T.stdout)?T.stdout:Buffer.from(T.stdout??""),code:T.status}}catch{return{stdout:Buffer.alloc(0),code:null}}}t(zZ,"tryExec");async function WZ(){return process.platform==="darwin"?U4t():process.platform==="linux"?V4t():process.platform==="win32"?q4t():null}t(WZ,"readClipboardImage");function U4t(){let p=zSe(),m=`set pngData to (the clipboard as \xABclass PNGf\xBB)
1049
1049
  set outFile to (open for access (POSIX file "${p}") with write permission)
1050
1050
  write pngData to outFile
@@ -1099,7 +1099,7 @@ ${p}`:m}t(HSe,"buildSystemPrompt");var GZ=Au(require("readline"));var $N=[{key:"
1099
1099
  `),process.stdout.write(st.accent("\u2570\u2500\u2500 choice [1/2/3/4]: ")),!p)return process.stdout.write(`
1100
1100
  `),{choice:"deny"};let m=(await p()).trim();return m==="2"?{choice:"session"}:m==="3"?{choice:"always"}:m==="4"?{choice:"deny"}:{choice:"once"}}t(sNt,"promptLegacy");async function aNt(p){let{rl:m,readLine:S}=p;m?.pause();let T=process.stdin.isRaw===!0;process.stdin.setRawMode?.(!0),GZ.emitKeypressEvents(process.stdin),process.stdin.resume();let k=0,j=!0,V=2,ee=t(()=>{j||process.stdout.write("\r\x1B["+(V-1)+"A\x1B[0J"),j=!1;let oe=$N.map((ye,Y)=>Y===k?st.accent("\u25B8 ")+st.bold(ye.label):" "+st.dim(ye.label));process.stdout.write(st.accent("\u2502 ")+oe.join(" ")+`
1101
1101
  `);let _e=$N[k].key==="deny"?"\u2191\u2193\u2190\u2192 or 1-4 \xB7 enter to deny \xB7 tab for follow-up":"\u2191\u2193\u2190\u2192 or 1-4 \xB7 enter to confirm";process.stdout.write(st.accent("\u2570\u2500\u2500 ")+st.dim(_e))},"render");return ee(),new Promise((oe,de)=>{let _e=t(()=>{process.stdin.removeListener("keypress",ye),process.stdin.setRawMode?.(T),process.stdout.write(`
1102
- `)},"cleanup"),ye=t((Y,rt)=>{if(rt){if(rt.ctrl&&rt.name==="c"){_e(),m?.resume(),de(new Error("cancelled"));return}if(rt.name==="up"||rt.name==="left"){k=(k-1+$N.length)%$N.length,ee();return}if(rt.name==="down"||rt.name==="right"){k=(k+1)%$N.length,ee();return}if(rt.name&&/^[1-4]$/.test(rt.name)){k=parseInt(rt.name,10)-1,ee();return}if(rt.name==="return"||rt.name==="enter"){_e(),m?.resume(),oe({choice:$N[k].key});return}if(rt.name==="tab"&&$N[k].key==="deny"){_e(),process.stdout.write(st.accent("\u2570\u2500\u2500 ")+st.red("deny + follow-up: ")),m?.resume(),(S??(()=>new Promise(qe=>{let un=GZ.createInterface({input:process.stdin,output:process.stdout});un.once("line",Wt=>{un.close(),qe(Wt)})})))().then(qe=>{oe({choice:"deny",notes:qe.trim()||void 0})}).catch(de);return}}},"onKey");process.stdin.on("keypress",ye)})}t(aNt,"promptInteractive");function JWe(p,m,S){if(p.choice!=="deny")return`permission ${p.choice}`;let T=S?`${m} ${S}`:m;return p.notes?`User denied \`${T}\` and asked you to revise your approach: "${p.notes}". Do not retry this tool call with the same arguments \u2014 adjust your plan based on the user's guidance.`:`User denied \`${T}\`. Do not retry this tool call with the same arguments.`}t(JWe,"formatDenialReason");var $v=Au(require("fs")),gJ=Au(require("os")),e6=Au(require("path"));var g1=e6.join(gJ.homedir(),".bandit","config.json");async function GSe(p){let m=[g1,e6.resolve(p,".bandit/config.json"),e6.resolve(p,".bandit/config.local.json")],S={};for(let T of m)try{let k=await $v.promises.readFile(T,"utf-8"),j=JSON.parse(k);S=oNt(S,j)}catch{}return S}t(GSe,"loadConfigFiles");function XSe(p,m={}){let S=m.provider??process.env.BANDIT_PROVIDER??p.provider??"ollama",T=S==="ollama"?"gemma4:e4b":"bandit-core-1",k=m.model??process.env.BANDIT_MODEL??p.model??T,j=m.ollamaUrl??process.env.OLLAMA_URL??p.ollama?.url??"http://localhost:11434",V=p.ollama?.headers??{},ee=m.apiKey??process.env.BANDIT_API_KEY??p.bandit?.apiKey,oe=m.apiUrl??process.env.BANDIT_API_URL??p.bandit?.apiUrl;return{provider:S,model:k,modelWasExplicit:m.model!==void 0||process.env.BANDIT_MODEL!==void 0||p.model!==void 0,ollamaUrl:j,ollamaHeaders:V,apiKey:ee,apiUrl:oe}}t(XSe,"resolveConfig");function oNt(p,m){return{provider:m.provider??p.provider,model:m.model??p.model,theme:m.theme??p.theme,ollama:{url:m.ollama?.url??p.ollama?.url,headers:{...p.ollama?.headers??{},...m.ollama?.headers??{}}},bandit:{apiKey:m.bandit?.apiKey??p.bandit?.apiKey,apiUrl:m.bandit?.apiUrl??p.bandit?.apiUrl}}}t(oNt,"mergeConfig");function XZ(){return g1}t(XZ,"globalConfigPath");async function zWe(p){let m=e6.join(gJ.homedir(),".bandit");try{await $v.promises.mkdir(m,{recursive:!0})}catch{}let S={};try{let T=await $v.promises.readFile(g1,"utf-8");S=JSON.parse(T)}catch{}return S.bandit={...S.bandit??{},apiKey:p},S.provider="bandit",await $v.promises.writeFile(g1,JSON.stringify(S,null,2),{encoding:"utf-8",mode:384}),g1}t(zWe,"saveApiKey");async function WWe(){let p={};try{let m=await $v.promises.readFile(g1,"utf-8");p=JSON.parse(m)}catch{return g1}return p.bandit&&(delete p.bandit.apiKey,Object.keys(p.bandit).length===0&&delete p.bandit),await $v.promises.writeFile(g1,JSON.stringify(p,null,2),{encoding:"utf-8",mode:384}),g1}t(WWe,"clearApiKey");async function $We(p,m){let S=e6.join(gJ.homedir(),".bandit");try{await $v.promises.mkdir(S,{recursive:!0})}catch{}let T={};try{let k=await $v.promises.readFile(g1,"utf-8");T=JSON.parse(k)}catch{}return T.provider=p,m&&(T.model=m),await $v.promises.writeFile(g1,JSON.stringify(T,null,2),{encoding:"utf-8",mode:384}),g1}t($We,"saveProvider");async function QZ(p){let m=e6.join(gJ.homedir(),".bandit");try{await $v.promises.mkdir(m,{recursive:!0})}catch{}let S={};try{let T=await $v.promises.readFile(g1,"utf-8");S=JSON.parse(T)}catch{}S.theme=p,await $v.promises.writeFile(g1,JSON.stringify(S,null,2),{encoding:"utf-8",mode:384})}t(QZ,"saveTheme");function UWe(p){let m=t(T=>T?`${T.slice(0,6)}\u2026${T.slice(-4)}`:"(unset)","redact"),S=[`provider ${p.provider}`,`model ${p.model}`];if(p.provider==="ollama"){S.push(`ollama url ${p.ollamaUrl}`);let T=Object.keys(p.ollamaHeaders);T.length>0?S.push(`ollama headers ${T.join(", ")} (values redacted)`):S.push("ollama headers (none)")}else S.push(`bandit api url ${p.apiUrl??"(default)"}`),S.push(`bandit api key ${m(p.apiKey)}`);return S.join(`
1102
+ `)},"cleanup"),ye=t((Y,rt)=>{if(rt){if(rt.ctrl&&rt.name==="c"){_e(),m?.resume(),de(new Error("cancelled"));return}if(rt.name==="up"||rt.name==="left"){k=(k-1+$N.length)%$N.length,ee();return}if(rt.name==="down"||rt.name==="right"){k=(k+1)%$N.length,ee();return}if(rt.name&&/^[1-4]$/.test(rt.name)){k=parseInt(rt.name,10)-1,ee();return}if(rt.name==="return"||rt.name==="enter"){_e(),m?.resume(),oe({choice:$N[k].key});return}if(rt.name==="tab"&&$N[k].key==="deny"){_e(),process.stdout.write(st.accent("\u2570\u2500\u2500 ")+st.red("deny + follow-up: ")),m?.resume(),(S??(()=>new Promise(qe=>{let un=GZ.createInterface({input:process.stdin,output:process.stdout});un.once("line",Wt=>{un.close(),qe(Wt)})})))().then(qe=>{oe({choice:"deny",notes:qe.trim()||void 0})}).catch(de);return}}},"onKey");process.stdin.on("keypress",ye)})}t(aNt,"promptInteractive");function JWe(p,m,S){if(p.choice!=="deny")return`permission ${p.choice}`;let T=S?`${m} ${S}`:m;return p.notes?`User denied \`${T}\` and asked you to revise your approach: "${p.notes}". Do not retry this tool call with the same arguments \u2014 adjust your plan based on the user's guidance.`:`User denied \`${T}\`. Do not retry this tool call with the same arguments.`}t(JWe,"formatDenialReason");var $v=Au(require("fs")),gJ=Au(require("os")),e6=Au(require("path"));var g1=e6.join(gJ.homedir(),".bandit","config.json");async function GSe(p){let m=[g1,e6.resolve(p,".bandit/config.json"),e6.resolve(p,".bandit/config.local.json")],S={};for(let T of m)try{let k=await $v.promises.readFile(T,"utf-8"),j=JSON.parse(k);S=oNt(S,j)}catch{}return S}t(GSe,"loadConfigFiles");function XSe(p,m={}){let S=m.provider??process.env.BANDIT_PROVIDER??p.provider??"ollama",T=S==="ollama"?"gemma4:e4b":"bandit-logic",k=m.model??process.env.BANDIT_MODEL??p.model??T,j=m.ollamaUrl??process.env.OLLAMA_URL??p.ollama?.url??"http://localhost:11434",V=p.ollama?.headers??{},ee=m.apiKey??process.env.BANDIT_API_KEY??p.bandit?.apiKey,oe=m.apiUrl??process.env.BANDIT_API_URL??p.bandit?.apiUrl;return{provider:S,model:k,modelWasExplicit:m.model!==void 0||process.env.BANDIT_MODEL!==void 0||p.model!==void 0,ollamaUrl:j,ollamaHeaders:V,apiKey:ee,apiUrl:oe}}t(XSe,"resolveConfig");function oNt(p,m){return{provider:m.provider??p.provider,model:m.model??p.model,theme:m.theme??p.theme,ollama:{url:m.ollama?.url??p.ollama?.url,headers:{...p.ollama?.headers??{},...m.ollama?.headers??{}}},bandit:{apiKey:m.bandit?.apiKey??p.bandit?.apiKey,apiUrl:m.bandit?.apiUrl??p.bandit?.apiUrl}}}t(oNt,"mergeConfig");function XZ(){return g1}t(XZ,"globalConfigPath");async function zWe(p){let m=e6.join(gJ.homedir(),".bandit");try{await $v.promises.mkdir(m,{recursive:!0})}catch{}let S={};try{let T=await $v.promises.readFile(g1,"utf-8");S=JSON.parse(T)}catch{}return S.bandit={...S.bandit??{},apiKey:p},S.provider="bandit",await $v.promises.writeFile(g1,JSON.stringify(S,null,2),{encoding:"utf-8",mode:384}),g1}t(zWe,"saveApiKey");async function WWe(){let p={};try{let m=await $v.promises.readFile(g1,"utf-8");p=JSON.parse(m)}catch{return g1}return p.bandit&&(delete p.bandit.apiKey,Object.keys(p.bandit).length===0&&delete p.bandit),await $v.promises.writeFile(g1,JSON.stringify(p,null,2),{encoding:"utf-8",mode:384}),g1}t(WWe,"clearApiKey");async function $We(p,m){let S=e6.join(gJ.homedir(),".bandit");try{await $v.promises.mkdir(S,{recursive:!0})}catch{}let T={};try{let k=await $v.promises.readFile(g1,"utf-8");T=JSON.parse(k)}catch{}return T.provider=p,m&&(T.model=m),await $v.promises.writeFile(g1,JSON.stringify(T,null,2),{encoding:"utf-8",mode:384}),g1}t($We,"saveProvider");async function QZ(p){let m=e6.join(gJ.homedir(),".bandit");try{await $v.promises.mkdir(m,{recursive:!0})}catch{}let S={};try{let T=await $v.promises.readFile(g1,"utf-8");S=JSON.parse(T)}catch{}S.theme=p,await $v.promises.writeFile(g1,JSON.stringify(S,null,2),{encoding:"utf-8",mode:384})}t(QZ,"saveTheme");function UWe(p){let m=t(T=>T?`${T.slice(0,6)}\u2026${T.slice(-4)}`:"(unset)","redact"),S=[`provider ${p.provider}`,`model ${p.model}`];if(p.provider==="ollama"){S.push(`ollama url ${p.ollamaUrl}`);let T=Object.keys(p.ollamaHeaders);T.length>0?S.push(`ollama headers ${T.join(", ")} (values redacted)`):S.push("ollama headers (none)")}else S.push(`bandit api url ${p.apiUrl??"(default)"}`),S.push(`bandit api key ${m(p.apiKey)}`);return S.join(`
1103
1103
  `)}t(UWe,"describeConfig");var hc=Au(vJ());var x$e=Au(require("child_process")),Uv=Au(require("fs")),VN=Au(require("path"));var TF=Au(aZ()),kF=Au(vJ());var xF="@burtson-labs/bandit-stealth-cli";function gIt(){return new Promise(p=>{let m=x$e.spawn("npm",["view",xF,"version"],{shell:!1,stdio:["ignore","pipe","pipe"]}),S="";m.stdout?.on("data",k=>{S+=k.toString()});let T=setTimeout(()=>{m.kill("SIGTERM"),p(null)},8e3);m.on("close",k=>{clearTimeout(T),p(k===0?S.trim():null)}),m.on("error",()=>{clearTimeout(T),p(null)})})}t(gIt,"fetchLatestVersion");function hIt(p,m){let S=t(de=>de.replace(/[^0-9.]/g,"").split(".").map(_e=>parseInt(_e,10)||0),"parse"),[T,k,j]=S(p),[V,ee,oe]=S(m);return T!==V?T<V?-1:1:k!==ee?k<ee?-1:1:j!==oe?j<oe?-1:1:0}t(hIt,"semverCompare");var T$e=[{name:"help",description:"List available slash commands",run(){let p=T$e.map(m=>` ${st.cyan("/"+m.name.padEnd(10))} ${st.dim(m.description)}`);return[st.bold("Slash commands:"),...p,"",st.dim("Tip: prefix any file with @ to pin it (e.g. @src/foo.ts)")].join(`
1104
1104
  `)}},{name:"clear",description:"Clear the current conversation history",run(p,m){return m.clearConversation(),st.dim("(conversation cleared)")}},{name:"paste",description:"Save the clipboard image to .bandit/pastes/ so you can reference it with @<path>",async run(p,m){let S=await WZ();if(!S)return st.dim("(no image on clipboard \u2014 copy or screenshot an image first, then retry)");let T=VN.join(m.cwd,".bandit","pastes");try{Uv.mkdirSync(T,{recursive:!0})}catch{}let k=`paste-${Date.now()}.png`,j=VN.join(T,k);try{Uv.renameSync(S.path,j)}catch{Uv.copyFileSync(S.path,j);try{Uv.unlinkSync(S.path)}catch{}}let V=VN.relative(m.cwd,j),ee=Math.round(S.sizeBytes/1024);return[st.dim(` ${gc.check} saved clipboard image (${ee} KB) \u2192 ${st.cyan(V)}`),st.dim(` reference it in your next message with ${st.cyan("@"+V)}`)].join(`
1105
1105
  `)}},{name:"rewind",description:"List checkpoints or restore a file edit (/rewind, /rewind <id>, /rewind last)",async run(p,m){let S=new kF.CheckpointStore({workspaceRoot:m.cwd}),T=p.trim();if(!T){let ee=await S.list(10);if(ee.length===0)return st.dim("(no checkpoints yet \u2014 rewind is available after the agent makes an edit)");let oe=ee.map((de,_e)=>{let ye=_e===0?st.accent("\u25CF"):" ",Y=st.dim(`+${de.plus} \u2212${de.minus}`);return` ${ye} ${st.cyan(de.id.padEnd(14))} ${de.tool.padEnd(10)} ${de.relPath} ${Y}`});return[st.bold("Recent checkpoints:"),...oe,"",st.dim(`Use ${st.cyan("/rewind <id>")} or ${st.cyan("/rewind last")} to restore.`)].join(`
@@ -1296,7 +1296,7 @@ ${fs}`},"drainBackgroundCompletions"),ee=[];j.on("complete",fs=>{ee.push(st.dim(
1296
1296
  `)},"renderFooterHint"),Ji=0,ei=t(()=>{let fs=process.stdout.columns||80,lo=Date.now()-T,Oc=lo>=36e5?`${Math.floor(lo/36e5)}h${Math.floor(lo%36e5/6e4)}m`:lo>=6e4?`${Math.floor(lo/6e4)}m${Math.floor(lo%6e4/1e3)}s`:`${Math.floor(lo/1e3)}s`;if(oe===null||Date.now()-de>3e4)try{oe=(iee.spawnSync("git",["rev-parse","--abbrev-ref","HEAD"],{cwd:p,encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3}).stdout?.trim()??"")||"",de=Date.now()}catch{oe="",de=Date.now()}let ds=k>=1e3?`~${(k/1e3).toFixed(1)}K tok`:k>=100?`~${k} tok`:"";for(;ee.length>0;){let Il=ee.shift();process.stdout.write(Il+`
1297
1297
  `)}let al=[un,`${Ji} turn${Ji===1?"":"s"}`,Oc];ds&&al.push(ds),oe&&al.push(oe);let kl=j.listByStatus("running").length;kl>0&&al.push(`bg:${kl} running`),Jt!==void 0&&al.push(Jt?"think:on":"think:off"),ye&&al.push(st.accent(`update v${ye} available`));let Fc=al.join(" \xB7 "),Df=Math.max(0,fs-Fc.length-1);process.stdout.write(" ".repeat(Df)+st.dim(Fc)+`
1298
1298
  `)},"renderStatusBar");process.stdout.on?.("resize",()=>{});let Cc=t((fs,lo)=>{if(!fs||!lo)return;let Oc=lo.replace(/<think\b[\s\S]*?<\/think\s*>/gi,"").replace(/```bandit-reasoning\b[\s\S]*?```/gi,"").replace(/```[\s\S]*?```/g," ").replace(/<tool_call\b[\s\S]*?<\/tool_call\s*>/gi,"").trim();if(Oc.length<40&&fs.trim().length<30)return;let ds=fs.trim().replace(/\s+/g," ").slice(0,80),al=Oc.split(/(?<=[.!?])\s+/)[0]?.replace(/\s+/g," ").slice(0,100)??"";if(!al)return;let kl=process.stdout.columns||80,Fc=`\u273B recap: "${ds}" \u2192 ${al}`,Df=Fc.length>kl-4?Fc.slice(0,kl-5)+"\u2026":Fc;process.stdout.write(st.dim(" "+Df)+`
1299
- `)},"renderRecap");Qs.setPrompt(st.accent(gc.prompt+" ")),Hs(),Qs.prompt();let Uu=new yF(p,(0,CS.createDefaultLanguageAdapters)()),sl={skillRegistry:At,session:m,cwd:p,toolCtx:Uu,model:{get current(){return un},set(fs){un=fs,Ut={...Ut,model:fs},qe=ree(Ut).settings}},setProvider(fs){let lo=fs==="ollama"?"gemma4:e4b":"bandit-core-1",ds=fs==="ollama"&&/:/.test(un)||fs==="bandit"&&un.startsWith("bandit-")?un:lo;un=ds,Ut={...Ut,provider:fs,model:ds};let al=ree(Ut);qe=al.settings,Wt=al.kind},get providerKind(){return Ut.provider},get ollamaUrl(){return qe.ollamaUrl??Ut.ollamaUrl??"http://localhost:11434"},getConversation:t(()=>[...Hn],"getConversation"),setConversation:t(fs=>{Hn.length=0,Hn.push(...fs),m.replace([...fs])},"setConversation"),tokenBudget:t(()=>{let fs=Ut.provider==="ollama"?(0,qN.resolveOllamaRuntimeOptions)(un).num_ctx:32768;return Math.floor(fs*.75)},"tokenBudget"),thinkingMode:{get:t(()=>Jt,"get"),set:t(fs=>{Jt=fs},"set")},planPreview:{get:t(()=>_n,"get"),set:t(fs=>{_n=fs},"set")},getConfig:t(()=>Ut,"getConfig"),exit:t(()=>Qs.close(),"exit"),clearConversation:t(()=>{Hn.length=0,m.replace([])},"clearConversation"),reloadMemory:t(async()=>{let fs=await(0,hc.loadMemory)(p);return Cr.content=fs.content,Cr.sources=fs.sources,fs.content},"reloadMemory"),getLine:t(()=>mt(),"getLine"),queuePrompt:t(fs=>{No.push(fs)},"queuePrompt"),backgroundStore:j},No=[],Lo=null,Oo=!1,mt=t(()=>new Promise(fs=>{if(No.length>0){fs(No.shift());return}Lo=t(lo=>{Lo=null,fs(lo)},"lineIntercept")}),"replGetLine"),wn=t(async()=>{if(!Oo){Oo=!0;try{for(;No.length>0;){if(Lo){let Oc=No.shift(),ds=Lo;Lo=null,ds(Oc);continue}let lo=No.shift().trim();if(!lo){Qs.prompt();continue}if(lo==="exit"||lo==="quit"){Qs.close();return}if(/^[yn]$/i.test(lo)&&Hn.length>0){let Oc=[...Hn].reverse().find(ds=>ds.role==="assistant");Oc&&w$e(Oc.content)&&(lo=lo.toLowerCase()==="y"?"yes":"no")}try{let Oc=k$e(lo);if(Oc){let kl=await Oc.cmd.run(Oc.args,sl);kl&&process.stdout.write(kl+`
1299
+ `)},"renderRecap");Qs.setPrompt(st.accent(gc.prompt+" ")),Hs(),Qs.prompt();let Uu=new yF(p,(0,CS.createDefaultLanguageAdapters)()),sl={skillRegistry:At,session:m,cwd:p,toolCtx:Uu,model:{get current(){return un},set(fs){un=fs,Ut={...Ut,model:fs},qe=ree(Ut).settings}},setProvider(fs){let lo=fs==="ollama"?"gemma4:e4b":"bandit-logic",ds=fs==="ollama"&&/:/.test(un)||fs==="bandit"&&un.startsWith("bandit-")?un:lo;un=ds,Ut={...Ut,provider:fs,model:ds};let al=ree(Ut);qe=al.settings,Wt=al.kind},get providerKind(){return Ut.provider},get ollamaUrl(){return qe.ollamaUrl??Ut.ollamaUrl??"http://localhost:11434"},getConversation:t(()=>[...Hn],"getConversation"),setConversation:t(fs=>{Hn.length=0,Hn.push(...fs),m.replace([...fs])},"setConversation"),tokenBudget:t(()=>{let fs=Ut.provider==="ollama"?(0,qN.resolveOllamaRuntimeOptions)(un).num_ctx:32768;return Math.floor(fs*.75)},"tokenBudget"),thinkingMode:{get:t(()=>Jt,"get"),set:t(fs=>{Jt=fs},"set")},planPreview:{get:t(()=>_n,"get"),set:t(fs=>{_n=fs},"set")},getConfig:t(()=>Ut,"getConfig"),exit:t(()=>Qs.close(),"exit"),clearConversation:t(()=>{Hn.length=0,m.replace([])},"clearConversation"),reloadMemory:t(async()=>{let fs=await(0,hc.loadMemory)(p);return Cr.content=fs.content,Cr.sources=fs.sources,fs.content},"reloadMemory"),getLine:t(()=>mt(),"getLine"),queuePrompt:t(fs=>{No.push(fs)},"queuePrompt"),backgroundStore:j},No=[],Lo=null,Oo=!1,mt=t(()=>new Promise(fs=>{if(No.length>0){fs(No.shift());return}Lo=t(lo=>{Lo=null,fs(lo)},"lineIntercept")}),"replGetLine"),wn=t(async()=>{if(!Oo){Oo=!0;try{for(;No.length>0;){if(Lo){let Oc=No.shift(),ds=Lo;Lo=null,ds(Oc);continue}let lo=No.shift().trim();if(!lo){Qs.prompt();continue}if(lo==="exit"||lo==="quit"){Qs.close();return}if(/^[yn]$/i.test(lo)&&Hn.length>0){let Oc=[...Hn].reverse().find(ds=>ds.role==="assistant");Oc&&w$e(Oc.content)&&(lo=lo.toLowerCase()==="y"?"yes":"no")}try{let Oc=k$e(lo);if(Oc){let kl=await Oc.cmd.run(Oc.args,sl);kl&&process.stdout.write(kl+`
1300
1300
  `),Qs.prompt();continue}if(_n){let kl=CS.planSkill.tools[0];if(kl)try{let Fc=await kl.execute({goal:lo},Uu);if(!Fc.isError){process.stdout.write(`
1301
1301
  `+st.bold("Proposed plan:")+`
1302
1302
  `),process.stdout.write(Fc.output+`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burtson-labs/bandit-stealth-cli",
3
- "version": "1.7.88",
3
+ "version": "1.7.90",
4
4
  "description": "Bandit — a local-first AI coding agent for your terminal. Same runtime as the Bandit Stealth VS Code / Cursor extension.",
5
5
  "keywords": [
6
6
  "ai",