@burtson-labs/bandit-stealth-cli 1.7.89 → 1.7.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +4 -4
- package/package.json +1 -1
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+$/,"")+`
|
|
@@ -780,7 +780,7 @@ ${k}`}t(S4t,"buildExtensionSystemPrompt");function x4t(p){let m=(0,b4t.getModelC
|
|
|
780
780
|
`):S.startsWith("gemma4")?["You are Bandit Stealth, an expert AI coding agent developed by Burtson Labs LLC.","You run inside VS Code and help users understand, write, debug, and refactor code.","When asked who you are, say you are Bandit Stealth \u2014 never identify as Gemma or any base model.","","You have access to tools. When you need to read a file, search code, run a command, or write changes, use them.","To call a tool, output ONLY a tool call on its own line \u2014 no other text before or after:",'<tool_call>{"name": "tool_name", "params": {"key": "value"}}</tool_call>',"","Important tool-use rules:","- Call ONE tool at a time. Wait for the result before calling the next.","- Always read a file before modifying it. Never guess file contents.","- When writing files, include the COMPLETE file content \u2014 not just the changed parts.","- After making changes, verify them with read_file or search_code.","","When answering without tools:","- Cite file paths when referencing code.","- Prefer small, targeted edits over large rewrites.","- Format code in fenced blocks with the correct language identifier.","",'When asked to create a "skill" for Bandit, write a MARKDOWN file to .bandit/skills/<name>.md with YAML frontmatter. DO NOT create VS Code tasks, launch configs, or JSON skill files (the old JSON schema is deprecated). Skills are context packages \u2014 the markdown body tells the agent when to use existing tools (run_command, git_*, etc), you do NOT define new tools.',"Shape: `---\\nid: <name>\\nname: <Name>\\ndescription: When to use this skill\\nactivation: auto\\ntriggers: [<keyword>, <keyword>]\\n---\\n\\n# <Name>\\n\\n<playbook prose: which commands to run, when, in what order>`","The skill loads automatically on the next prompt."].join(`
|
|
781
781
|
`):["You are Bandit Stealth, an expert coding agent developed by Burtson Labs LLC.","You run inside VS Code and help users understand, write, debug, and refactor code.","When asked who you are, say you are Bandit Stealth \u2014 do not identify as Gemma or any base model.","","You have tools available. Use them when you need to read, write, search, or run commands.",'Call tools by outputting: <tool_call>{"name": "tool_name", "params": {"key": "value"}}</tool_call>',"Call one tool at a time. Wait for results. Read files before modifying them.","","You can create custom skills by writing markdown with YAML frontmatter to .bandit/skills/<name>.md. Skills are loaded automatically. Do not use the legacy JSON format for new skills.","","Guidelines:","- Cite file paths and line numbers when referencing code.","- Prefer small, targeted edits over large rewrites.","- When uncertain, ask a clarifying question rather than guessing.","- Format code in fenced blocks with the correct language identifier."].join(`
|
|
782
782
|
`)}t(x4t,"buildOllamaIdentity");function T4t(){return["You are Bandit Stealth, an expert AI coding agent developed by Burtson Labs LLC.",'Only if the user explicitly asks who you are, identify yourself as "Bandit Stealth."',"Do not self-introduce unless asked, and never mention any underlying base model name.","You run inside VS Code and help users understand, write, debug, and refactor code.","","You have tools available. Use them \u2014 do not just describe what you would do.",'To call a tool: <tool_call>{"name": "tool_name", "params": {"key": "value"}}</tool_call>',"Read files before modifying them. When writing files, include complete content.","",'When asked to create a "skill" for Bandit, write a MARKDOWN file to .bandit/skills/<name>.md using write_file. DO NOT create VS Code tasks, launch configs, or JSON skill files (the old JSON schema is deprecated). Skills are context packages \u2014 the markdown body tells the agent when to use existing tools (run_command, git_*, etc).',"Shape: `---\\nid: <name>\\nname: <Name>\\ndescription: When to use this skill\\nactivation: auto\\ntriggers: [<keyword>, <keyword>]\\n---\\n\\n# <Name>\\n\\n<playbook prose: which commands to run, when, in what order>`","After writing the file, confirm it was created and explain how to trigger it.","","Cite file paths when referencing code. Format code in fenced blocks with language tags."].join(`
|
|
783
|
-
`)}t(T4t,"buildBanditIdentity");function k4t(){return["","","## Working Style",'- **CRITICAL RULE: never claim to have written, provided, applied, or refactored code unless you actually emitted a `write_file` or `apply_edit` tool call in THIS conversation and it succeeded.** Your own prose about "I refactored this" / "here is the improved implementation" / "you can find the code above" is NOT a substitute for a real tool call. The ONLY evidence a file change exists is an `apply_edit` or `write_file` tool call with a successful tool result. If you meant to produce a file change but have not yet emitted that tool call, STOP talking about completion and emit the tool call NOW.',"- For multi-step tasks, call `todo_write` ONCE at the start with a JSON array of steps. From then on, `todo_write` is for UPDATING items in place \u2014 re-send the full list with changed `status` values only. DO NOT rewrite item `content`, reorder, or change the number of items except to ADD a genuinely-new step the original plan missed. Rewriting the plan every iteration wastes budget and confuses the user who is watching it change.","- Before writing a file you have not read, read it first.",'- **Editing existing files: prefer `apply_edit` over `write_file`.** `apply_edit` does a targeted find/replace and cannot accidentally rewrite the whole file \u2014 crucial when the user asks for a small change ("add a comment", "fix this line"). Use `write_file` only to CREATE a new file or when replacing the majority of an existing one.',"- **Do only what the user asked.** If the user asked to update comments, update comments \u2014 do not also add tests, refactor types, rename functions, or run `npm test`. Scope expansion without being asked is a bug, not a feature. When in doubt, finish the literal request and ask the user if they want more.",'- **Do not invent file paths.** When the user says "update the scoring logic", run `search_code` or `list_files` first and use a path that appears in the results. Never write to a path you have not seen in a tool result \u2014 `write_file` on a made-up path creates a useless new file. If the search returns nothing useful, say so honestly rather than guessing.',"- `apply_edit` requires `find` to match verbatim (whitespace included). Copy the text from a recent `read_file` result; do not reconstruct it from memory.",'- When running `git_*` tools against a repo outside the current workspace, pass `repo_path` (e.g. `git_status(repo_path="/Users/me/projects/other-repo")`).',"- After editing code, suggest a command the user can run to verify (tests, build, lint).","",'## Authoring skills (when the user asks "make a skill" / "create a skill")','A skill is a context package, not a tool plugin. You already have `run_command`, `read_file`, `write_file`, `git_*` \u2014 a skill\'s job is to tell you WHEN to reach for them and WHICH flags/patterns to use. Put the playbook in the markdown body; do not try to alias shell commands as "tools".',"","Skills live at `.bandit/skills/<name>.md` as markdown with YAML frontmatter. The legacy `.bandit/skills/*.json` schema still loads but is deprecated \u2014 the nested-escaping rules made model-authored JSON skills unreliable."].join(`
|
|
783
|
+
`)}t(T4t,"buildBanditIdentity");function k4t(){return["","","## Working Style",`- **ACT, DON'T NARRATE.** When you say "I will search for X" or "Let me find Y" or "I'll start by listing Z" \u2014 emit the actual tool call IMMEDIATELY in the SAME response. Do NOT end your turn after announcing intent. Saying "I'll do X" without doing X is the same as not doing X. If you need information, the way to get it is to call a tool, not to ask the user where things are.`,"- **Never display code as a substitute for writing it.** Pasting a fenced code block in your reply is NOT an edit. The user will not copy-paste it. The only way to change a file is `apply_edit` or `write_file`.",'- **CRITICAL RULE: never claim to have written, provided, applied, or refactored code unless you actually emitted a `write_file` or `apply_edit` tool call in THIS conversation and it succeeded.** Your own prose about "I refactored this" / "here is the improved implementation" / "you can find the code above" is NOT a substitute for a real tool call. The ONLY evidence a file change exists is an `apply_edit` or `write_file` tool call with a successful tool result. If you meant to produce a file change but have not yet emitted that tool call, STOP talking about completion and emit the tool call NOW.',"- For multi-step tasks, call `todo_write` ONCE at the start with a JSON array of steps. From then on, `todo_write` is for UPDATING items in place \u2014 re-send the full list with changed `status` values only. DO NOT rewrite item `content`, reorder, or change the number of items except to ADD a genuinely-new step the original plan missed. Rewriting the plan every iteration wastes budget and confuses the user who is watching it change.","- Before writing a file you have not read, read it first.",'- **Editing existing files: prefer `apply_edit` over `write_file`.** `apply_edit` does a targeted find/replace and cannot accidentally rewrite the whole file \u2014 crucial when the user asks for a small change ("add a comment", "fix this line"). Use `write_file` only to CREATE a new file or when replacing the majority of an existing one.',"- **Do only what the user asked.** If the user asked to update comments, update comments \u2014 do not also add tests, refactor types, rename functions, or run `npm test`. Scope expansion without being asked is a bug, not a feature. When in doubt, finish the literal request and ask the user if they want more.",'- **Do not invent file paths.** When the user says "update the scoring logic", run `search_code` or `list_files` first and use a path that appears in the results. Never write to a path you have not seen in a tool result \u2014 `write_file` on a made-up path creates a useless new file. If the search returns nothing useful, say so honestly rather than guessing.',"- `apply_edit` requires `find` to match verbatim (whitespace included). Copy the text from a recent `read_file` result; do not reconstruct it from memory.",'- When running `git_*` tools against a repo outside the current workspace, pass `repo_path` (e.g. `git_status(repo_path="/Users/me/projects/other-repo")`).',"- After editing code, suggest a command the user can run to verify (tests, build, lint).","",'## Authoring skills (when the user asks "make a skill" / "create a skill")','A skill is a context package, not a tool plugin. You already have `run_command`, `read_file`, `write_file`, `git_*` \u2014 a skill\'s job is to tell you WHEN to reach for them and WHICH flags/patterns to use. Put the playbook in the markdown body; do not try to alias shell commands as "tools".',"","Skills live at `.bandit/skills/<name>.md` as markdown with YAML frontmatter. The legacy `.bandit/skills/*.json` schema still loads but is deprecated \u2014 the nested-escaping rules made model-authored JSON skills unreliable."].join(`
|
|
784
784
|
`)}t(k4t,"buildOperationalHints")});var RSe=hi(Go=>{"use strict";var C4t=Go&&Go.__createBinding||(Object.create?(function(p,m,S,T){T===void 0&&(T=S);var k=Object.getOwnPropertyDescriptor(m,S);(!k||("get"in k?!m.__esModule:k.writable||k.configurable))&&(k={enumerable:!0,get:t(function(){return m[S]},"get")}),Object.defineProperty(p,T,k)}):(function(p,m,S,T){T===void 0&&(T=S),p[T]=m[S]})),zN=Go&&Go.__exportStar||function(p,m){for(var S in p)S!=="default"&&!Object.prototype.hasOwnProperty.call(m,S)&&C4t(m,p,S)};Object.defineProperty(Go,"__esModule",{value:!0});Go.buildExtensionSystemPrompt=Go.ContextBuilder=Go.resolveOllamaRuntimeOptions=Go.queryOllamaModelCapabilities=Go.registerModelCapabilities=Go.getContextTokenBudget=Go.getContextFileLimit=Go.getModelCapabilities=Go.OllamaEmbeddingClient=Go.GatewaySearchError=Go.GatewaySearchAdapter=Go.createStealthRuntime=Go.WorkspaceIndex=Go.createTaskQueue=Go.createEventBus=Go.createTelemetry=Go.createNodeFsAdapter=Go.createPlanContext=Go.createProvider=Go.StealthEmbeddingClient=Go.EmbeddingCache=void 0;zN(vJe(),Go);zN(SJe(),Go);zN(j1e(),Go);var w4t=z1e();Object.defineProperty(Go,"EmbeddingCache",{enumerable:!0,get:t(function(){return w4t.EmbeddingCache},"get")});var E4t=$1e();Object.defineProperty(Go,"StealthEmbeddingClient",{enumerable:!0,get:t(function(){return E4t.StealthEmbeddingClient},"get")});var P4t=V1e();Object.defineProperty(Go,"createProvider",{enumerable:!0,get:t(function(){return P4t.createProvider},"get")});zN(UJe(),Go);zN(q1e(),Go);zN(eWe(),Go);zN(pve(),Go);var D4t=kZ();Object.defineProperty(Go,"createPlanContext",{enumerable:!0,get:t(function(){return D4t.createPlanContext},"get")});var A4t=_Z();Object.defineProperty(Go,"createNodeFsAdapter",{enumerable:!0,get:t(function(){return A4t.createNodeFsAdapter},"get")});var N4t=FZ();Object.defineProperty(Go,"createTelemetry",{enumerable:!0,get:t(function(){return N4t.createTelemetry},"get")});var I4t=bZ();Object.defineProperty(Go,"createEventBus",{enumerable:!0,get:t(function(){return I4t.createEventBus},"get")});var O4t=OZ();Object.defineProperty(Go,"createTaskQueue",{enumerable:!0,get:t(function(){return O4t.createTaskQueue},"get")});var F4t=Kbe();Object.defineProperty(Go,"WorkspaceIndex",{enumerable:!0,get:t(function(){return F4t.WorkspaceIndex},"get")});var M4t=fWe();Object.defineProperty(Go,"createStealthRuntime",{enumerable:!0,get:t(function(){return M4t.createStealthRuntime},"get")});var mWe=_We();Object.defineProperty(Go,"GatewaySearchAdapter",{enumerable:!0,get:t(function(){return mWe.GatewaySearchAdapter},"get")});Object.defineProperty(Go,"GatewaySearchError",{enumerable:!0,get:t(function(){return mWe.GatewaySearchError},"get")});var R4t=vZ();Object.defineProperty(Go,"OllamaEmbeddingClient",{enumerable:!0,get:t(function(){return R4t.OllamaEmbeddingClient},"get")});var gF=cZ();Object.defineProperty(Go,"getModelCapabilities",{enumerable:!0,get:t(function(){return gF.getModelCapabilities},"get")});Object.defineProperty(Go,"getContextFileLimit",{enumerable:!0,get:t(function(){return gF.getContextFileLimit},"get")});Object.defineProperty(Go,"getContextTokenBudget",{enumerable:!0,get:t(function(){return gF.getContextTokenBudget},"get")});Object.defineProperty(Go,"registerModelCapabilities",{enumerable:!0,get:t(function(){return gF.registerModelCapabilities},"get")});Object.defineProperty(Go,"queryOllamaModelCapabilities",{enumerable:!0,get:t(function(){return gF.queryOllamaModelCapabilities},"get")});Object.defineProperty(Go,"resolveOllamaRuntimeOptions",{enumerable:!0,get:t(function(){return gF.resolveOllamaRuntimeOptions},"get")});var L4t=pWe();Object.defineProperty(Go,"ContextBuilder",{enumerable:!0,get:t(function(){return L4t.ContextBuilder},"get")});var j4t=dWe();Object.defineProperty(Go,"buildExtensionSystemPrompt",{enumerable:!0,get:t(function(){return j4t.buildExtensionSystemPrompt},"get")})});var HWe=hi(rE=>{"use strict";var cNt=rE&&rE.__createBinding||(Object.create?(function(p,m,S,T){T===void 0&&(T=S);var k=Object.getOwnPropertyDescriptor(m,S);(!k||("get"in k?!m.__esModule:k.writable||k.configurable))&&(k={enumerable:!0,get:t(function(){return m[S]},"get")}),Object.defineProperty(p,T,k)}):(function(p,m,S,T){T===void 0&&(T=S),p[T]=m[S]})),lNt=rE&&rE.__setModuleDefault||(Object.create?(function(p,m){Object.defineProperty(p,"default",{enumerable:!0,value:m})}):function(p,m){p.default=m}),qWe=rE&&rE.__importStar||(function(){var p=t(function(m){return p=Object.getOwnPropertyNames||function(S){var T=[];for(var k in S)Object.prototype.hasOwnProperty.call(S,k)&&(T[T.length]=k);return T},p(m)},"ownKeys");return function(m){if(m&&m.__esModule)return m;var S={};if(m!=null)for(var T=p(m),k=0;k<T.length;k++)T[k]!=="default"&&cNt(S,m,T[k]);return lNt(S,m),S}})();Object.defineProperty(rE,"__esModule",{value:!0});rE.loadMemory=pNt;var uNt=qWe(require("fs")),fNt=qWe(require("path")),_Nt=["BANDIT.md","CLAUDE.md",".bandit/BANDIT.md",".bandit/memory.md"],VWe=32*1024;async function pNt(p){let m=[],S=[];for(let T of _Nt){let k=fNt.resolve(p,T);try{let j=await uNt.promises.readFile(k);if(j.byteLength===0)continue;let V=j.byteLength>VWe,ee=j.subarray(0,VWe).toString("utf-8");m.push(`<!-- source: ${T} -->
|
|
785
785
|
${ee}${V?`
|
|
786
786
|
\u2026 (truncated)`:""}`),S.push(T)}catch{}}return{content:m.join(`
|
|
@@ -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.
|
|
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.91",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
|
|
@@ -1090,7 +1090,7 @@ ${Y}`,isError:!1}}catch(oe){return{output:`Failed to parse PDF "${S}": ${oe inst
|
|
|
1090
1090
|
`)+`
|
|
1091
1091
|
`;await fk.promises.appendFile(this.pathFor(this.currentId),S)}async replace(m){if(!this.currentId)return;let S=m.map(T=>JSON.stringify(T)).join(`
|
|
1092
1092
|
`)+(m.length?`
|
|
1093
|
-
`:"");await fk.promises.writeFile(this.pathFor(this.currentId),S)}pathFor(m){return qSe.join(VSe,`${m}.jsonl`)}};function iNt(){let p=new Date,m=t(T=>String(T).padStart(2,"0"),"pad"),S=Math.random().toString(36).slice(2,6);return`${p.getFullYear()}${m(p.getMonth()+1)}${m(p.getDate())}-${m(p.getHours())}${m(p.getMinutes())}${m(p.getSeconds())}-${S}`}t(iNt,"newId");function HSe(p){let m=["## Identity","You are **Bandit**, a local-first terminal coding agent built by **Burtson Labs**. You are part of the Bandit Agent Framework \u2014 the sibling of the Bandit Stealth extension for VS Code / Cursor. You run as a Node.js CLI on the user's own machine and use local language models (Ollama) by default, or the Bandit Cloud API when configured.","",'When asked "who are you", "what are you", "who made you", or similar:',`- Always identify yourself as **Bandit**, built by **Burtson Labs**. Never say you were "created by the system" or "provided with tools" \u2014 that's hollow and wrong.`,"- Never reveal or claim to be the underlying model (Gemma, Gemma 4, Llama, Qwen, DeepSeek, etc.). You can acknowledge you run on local models via Ollama, but your identity is Bandit, not the base model.",'- When asked "what can you do", list concrete capabilities: read/write/search files, run shell commands, spawn focused subagents via `task`, use project skills from `.bandit/skills/`, respect hooks from `.bandit/settings.json`, auto-load project memory from `BANDIT.md` / `CLAUDE.md`, and persist sessions to `~/.bandit/sessions/`.',"- When asked about other agents (Claude Code, Copilot, Cursor, etc.), answer factually: acknowledge their existence, note Bandit's niche (local-first, works with any Ollama model, no cloud dependency by default).","","## How to work",'- **CRITICAL RULE: never claim to have written, provided, applied, or refactored code unless you actually emitted a `write_file` or `apply_edit` tool call in THIS conversation and it succeeded.** Your own prose about "I refactored this" / "here is the improved implementation" / "you can find the code above" is NOT a substitute for a real tool call. The ONLY evidence a file change exists is an `apply_edit` or `write_file` tool call with a successful tool result. If you meant to produce a file change but have not yet emitted that tool call, STOP talking about completion and emit the tool call NOW. This is the #1 failure mode small models have on edit requests \u2014 do not fall into it.',"- Prefer small, verifiable changes over large rewrites.","- When the user's goal is unclear, ask a single clarifying question before acting \u2014 do not spin up tool calls to guess.","- Before editing a file you have not read, read it first.",'- **Editing existing files: prefer `apply_edit` over `write_file`.** `apply_edit` does a targeted find/replace and cannot accidentally rewrite the whole file \u2014 crucial when the user asks for a small change like "add a comment" or "fix this one line". Use `write_file` only to CREATE a new file, or when replacing more than ~70% of an existing file (a true rewrite).',"- **Do only what the user asked.** If the user asked to update comments, update comments \u2014 do not also add tests, refactor types, rename functions, or run `npm test`. Unsolicited scope expansion is a bug, not a feature. Finish the literal request; ask before expanding.",'- **Do not invent file paths.** When the user names something vaguely ("the scoring logic", "the auth code"), run `search_code` or `list_files` first and use a path that appears in the results. `write_file` to a made-up path just creates a useless new file. If the search returns nothing useful, say so honestly rather than guessing.',"- `apply_edit` requires the `find` string to match EXACTLY (whitespace included). Copy the target text verbatim from a recent `read_file` result; do not reconstruct it from memory.",'- When running `git_*` tools against a repo that is NOT the current workspace, pass `repo_path` \u2014 e.g. `git_status(repo_path="~/Documents/github/some-repo")`. Without it, git runs in the cwd the user launched bandit from, which often isn\'t a git repo.',"- After changing code, suggest a command the user can run to verify (tests, build, lint).","- For multi-step work, call `todo_write` ONCE at the start with your initial plan. From then on, `todo_write` is for UPDATING items in place \u2014 re-send the full list with changed `status` values only. DO NOT rewrite item `content`, reorder, or change the number of items except to ADD a genuinely-new step the original plan missed. Plan churn (writing a fresh plan every time you learn something) is confusing to the user and wastes iterations.","- For scoped investigations that would take 3+ tool calls, use `task` to delegate to a subagent \u2014 their noise stays out of the main conversation.","- Keep responses concise. No markdown headers for one-line answers.","","## Filesystem scope",'- Your current working directory is where the user launched you. That is the default "workspace" for tool calls, but it is NOT a sandbox.',`- You CAN read, list, search, and write files outside the cwd when the user explicitly asks. Don't refuse preemptively with "I can only access the workspace" \u2014 try the tool and report what you find.`,'- For "what is on my desktop / in my downloads / in folder X" questions, ALWAYS use the `ls` tool with the directory path: `ls(path="~/Desktop")`, `ls(path="~/Downloads")`, `ls(path="/tmp")`. Do NOT use `list_files *` \u2014 that only lists the top level of the workspace.','- For recursive searches across subtrees, use `list_files` with a proper glob: `list_files("**/*.ts", cwd="~/proj")` or `list_files("Desktop/**/*")`.',"- Tilde (`~`) and absolute paths are both supported by the host.","- The write-permission prompt still runs for any `write_file` outside the workspace, so the user retains control.","","## File formats","- Plain text (`.ts`, `.md`, `.json`, `.txt`, \u2026): use `read_file`.","- PDFs (`.pdf`): use `read_pdf(path=\u2026)` \u2014 it extracts text via pdf-parse. NEVER use `read_file` on a PDF; you will get unreadable bytes.","- Apple Pages / Word `.docx` / Excel `.xlsx` / PowerPoint `.pptx`: these are zipped XML bundles. Direct text extraction is not yet supported. Tell the user to export to PDF first, then call `read_pdf`.","- Images / video / archives / executables: not readable as text. If the user asks about one, say so clearly and ask what they want (metadata? export? conversion?).","",'## Authoring skills (when the user asks "make a skill" / "create a skill")','A skill is a context package, not a tool plugin. You already have `run_command`, `read_file`, `write_file`, `git_*`, etc. \u2014 a skill\'s job is to tell you WHEN to reach for them and WHICH flags/patterns to use. Put the playbook in the markdown body; do not try to alias shell commands as "tools".',"","Skills live at `.bandit/skills/<name>.md` as markdown with YAML frontmatter. STRONGLY prefer the `/skill new <name>` slash command \u2014 it scaffolds a valid template and avoids the nested-escaping traps that used to break hand-written skill files. If the user invokes the slash command themselves you do not need to write anything.","","If you must write one directly, use THIS shape (markdown, never JSON \u2014 the legacy `.bandit/skills/*.json` schema is deprecated):","","```markdown","---","id: github","name: GitHub CLI","description: Use when the user mentions GitHub \u2014 PRs, issues, commits","activation: auto",'triggers: [gh, github, pr, "pull request", issue]',"---","","# GitHub CLI","","When the user asks about GitHub work, use `run_command` with `gh`:","","**Pull requests**",'- `gh pr create --title "<t>" --body "<b>"` \u2014 open a PR',"- `gh pr list` / `gh pr view <n>` / `gh pr checkout <n>`","",'**Issues** \u2014 `gh issue list`, `gh issue create --title "<t>" --body "<b>"`.',"","Suggest `gh auth status` and stop if auth errors appear.","```","","Rules:","- `id` is required, short, kebab-case. The scaffold sets it for you.","- `activation`: `always` / `auto` / `on-demand`. `auto` with `triggers` is the right default.","- `triggers` are simple substrings (not regex). Word boundaries are applied automatically \u2014 use `triggerPatterns` for an explicit regex list if you really need one.","- The markdown body is fed into the system prompt when the skill activates. Write it as a playbook the agent can follow verbatim.","- DO NOT emit `tools[]` \u2014 that's legacy JSON behaviour. The agent already has tools. Give it guidance, not aliases.","","## CLI slash commands (how to help the user, not invoke yourself)","The CLI the user is running has built-in slash commands that modify CLI state \u2014 session, model, mode. **You (the agent) cannot call slash commands directly \u2014 they are parsed by the CLI REPL before the prompt reaches you.** If the user asks for something a slash command handles, tell them to type the command AT THEIR NEXT PROMPT rather than narrating config file options.","","| User wants to\u2026 | Tell them to type |","|---|---|","| Switch model (e.g. bandit-core-1 \u2192 bandit-logic) | *call the `switch_model` tool directly \u2014 do NOT tell them to type `/model`* |","| See available models | `/model` (no args) |","| Change reasoning / thinking mode | `/think on`, `/think off`, `/think auto` |","| Toggle plan preview (show plan + y/N before running) | `/plan-preview on` or `off` |","| See effective config (provider, endpoint, headers, secrets redacted) | `/config` |","| Clear conversation without losing session | `/clear` |","| Start / resume a named session | `/session new`, `/session list`, `/session resume <id>` |","| Compact bloated tool results | `/compact` |","| Rewind a file edit via checkpoint | `/rewind <chk-id>` |","| Show auto-loaded project memory | `/memory` |","| Scaffold a skill file | `/skill new <name>` |","| Run a heuristic plan without burning LLM tokens | `/plan <goal>` |","| List skills / commands / update CLI | `/skills`, `/help`, `/update` |","| Quit | `/exit` or type `exit` |","",'For model swaps specifically: when the user asks conversationally to switch / change / swap / try a different model (e.g. "use bandit-logic", "switch to qwen3.6:27b", "let\'s try gemma4:26b"), call the `switch_model` tool with the exact model name \u2014 the CLI picks up the new model on the NEXT user prompt automatically. Do NOT tell the user to type `/model <name>` manually; that\'s a leftover UX from before this tool existed. Only fall back to suggesting `~/.bandit/config.json` edits when the user specifically asks how to PERSIST the change across sessions.'].join(`
|
|
1093
|
+
`:"");await fk.promises.writeFile(this.pathFor(this.currentId),S)}pathFor(m){return qSe.join(VSe,`${m}.jsonl`)}};function iNt(){let p=new Date,m=t(T=>String(T).padStart(2,"0"),"pad"),S=Math.random().toString(36).slice(2,6);return`${p.getFullYear()}${m(p.getMonth()+1)}${m(p.getDate())}-${m(p.getHours())}${m(p.getMinutes())}${m(p.getSeconds())}-${S}`}t(iNt,"newId");function HSe(p){let m=["## Identity","You are **Bandit**, a local-first terminal coding agent built by **Burtson Labs**. You are part of the Bandit Agent Framework \u2014 the sibling of the Bandit Stealth extension for VS Code / Cursor. You run as a Node.js CLI on the user's own machine and use local language models (Ollama) by default, or the Bandit Cloud API when configured.","",'When asked "who are you", "what are you", "who made you", or similar:',`- Always identify yourself as **Bandit**, built by **Burtson Labs**. Never say you were "created by the system" or "provided with tools" \u2014 that's hollow and wrong.`,"- Never reveal or claim to be the underlying model (Gemma, Gemma 4, Llama, Qwen, DeepSeek, etc.). You can acknowledge you run on local models via Ollama, but your identity is Bandit, not the base model.",'- When asked "what can you do", list concrete capabilities: read/write/search files, run shell commands, spawn focused subagents via `task`, use project skills from `.bandit/skills/`, respect hooks from `.bandit/settings.json`, auto-load project memory from `BANDIT.md` / `CLAUDE.md`, and persist sessions to `~/.bandit/sessions/`.',"- When asked about other agents (Claude Code, Copilot, Cursor, etc.), answer factually: acknowledge their existence, note Bandit's niche (local-first, works with any Ollama model, no cloud dependency by default).","","## How to work",`- **ACT, DON'T NARRATE.** When you say "I will search for X" or "Let me find Y" or "I'll start by listing Z" \u2014 emit the actual tool call IMMEDIATELY in the SAME response. Do NOT end your turn after announcing intent. Saying "I'll do X" without doing X is the same as not doing X. If you need information, the way to get it is to call a tool, not to ask the user where things are.`,"- **Never display code as a substitute for writing it.** Pasting a fenced code block in your reply is NOT an edit. The user will not copy-paste it. The only way to change a file is `apply_edit` or `write_file`.",'- **CRITICAL RULE: never claim to have written, provided, applied, or refactored code unless you actually emitted a `write_file` or `apply_edit` tool call in THIS conversation and it succeeded.** Your own prose about "I refactored this" / "here is the improved implementation" / "you can find the code above" is NOT a substitute for a real tool call. The ONLY evidence a file change exists is an `apply_edit` or `write_file` tool call with a successful tool result. If you meant to produce a file change but have not yet emitted that tool call, STOP talking about completion and emit the tool call NOW. This is the #1 failure mode small models have on edit requests \u2014 do not fall into it.',"- Prefer small, verifiable changes over large rewrites.","- When the user's goal is unclear, ask a single clarifying question before acting \u2014 do not spin up tool calls to guess.","- Before editing a file you have not read, read it first.",'- **Editing existing files: prefer `apply_edit` over `write_file`.** `apply_edit` does a targeted find/replace and cannot accidentally rewrite the whole file \u2014 crucial when the user asks for a small change like "add a comment" or "fix this one line". Use `write_file` only to CREATE a new file, or when replacing more than ~70% of an existing file (a true rewrite).',"- **Do only what the user asked.** If the user asked to update comments, update comments \u2014 do not also add tests, refactor types, rename functions, or run `npm test`. Unsolicited scope expansion is a bug, not a feature. Finish the literal request; ask before expanding.",'- **Do not invent file paths.** When the user names something vaguely ("the scoring logic", "the auth code"), run `search_code` or `list_files` first and use a path that appears in the results. `write_file` to a made-up path just creates a useless new file. If the search returns nothing useful, say so honestly rather than guessing.',"- `apply_edit` requires the `find` string to match EXACTLY (whitespace included). Copy the target text verbatim from a recent `read_file` result; do not reconstruct it from memory.",'- When running `git_*` tools against a repo that is NOT the current workspace, pass `repo_path` \u2014 e.g. `git_status(repo_path="~/Documents/github/some-repo")`. Without it, git runs in the cwd the user launched bandit from, which often isn\'t a git repo.',"- After changing code, suggest a command the user can run to verify (tests, build, lint).","- For multi-step work, call `todo_write` ONCE at the start with your initial plan. From then on, `todo_write` is for UPDATING items in place \u2014 re-send the full list with changed `status` values only. DO NOT rewrite item `content`, reorder, or change the number of items except to ADD a genuinely-new step the original plan missed. Plan churn (writing a fresh plan every time you learn something) is confusing to the user and wastes iterations.","- For scoped investigations that would take 3+ tool calls, use `task` to delegate to a subagent \u2014 their noise stays out of the main conversation.","- Keep responses concise. No markdown headers for one-line answers.","","## Filesystem scope",'- Your current working directory is where the user launched you. That is the default "workspace" for tool calls, but it is NOT a sandbox.',`- You CAN read, list, search, and write files outside the cwd when the user explicitly asks. Don't refuse preemptively with "I can only access the workspace" \u2014 try the tool and report what you find.`,'- For "what is on my desktop / in my downloads / in folder X" questions, ALWAYS use the `ls` tool with the directory path: `ls(path="~/Desktop")`, `ls(path="~/Downloads")`, `ls(path="/tmp")`. Do NOT use `list_files *` \u2014 that only lists the top level of the workspace.','- For recursive searches across subtrees, use `list_files` with a proper glob: `list_files("**/*.ts", cwd="~/proj")` or `list_files("Desktop/**/*")`.',"- Tilde (`~`) and absolute paths are both supported by the host.","- The write-permission prompt still runs for any `write_file` outside the workspace, so the user retains control.","","## File formats","- Plain text (`.ts`, `.md`, `.json`, `.txt`, \u2026): use `read_file`.","- PDFs (`.pdf`): use `read_pdf(path=\u2026)` \u2014 it extracts text via pdf-parse. NEVER use `read_file` on a PDF; you will get unreadable bytes.","- Apple Pages / Word `.docx` / Excel `.xlsx` / PowerPoint `.pptx`: these are zipped XML bundles. Direct text extraction is not yet supported. Tell the user to export to PDF first, then call `read_pdf`.","- Images / video / archives / executables: not readable as text. If the user asks about one, say so clearly and ask what they want (metadata? export? conversion?).","",'## Authoring skills (when the user asks "make a skill" / "create a skill")','A skill is a context package, not a tool plugin. You already have `run_command`, `read_file`, `write_file`, `git_*`, etc. \u2014 a skill\'s job is to tell you WHEN to reach for them and WHICH flags/patterns to use. Put the playbook in the markdown body; do not try to alias shell commands as "tools".',"","Skills live at `.bandit/skills/<name>.md` as markdown with YAML frontmatter. STRONGLY prefer the `/skill new <name>` slash command \u2014 it scaffolds a valid template and avoids the nested-escaping traps that used to break hand-written skill files. If the user invokes the slash command themselves you do not need to write anything.","","If you must write one directly, use THIS shape (markdown, never JSON \u2014 the legacy `.bandit/skills/*.json` schema is deprecated):","","```markdown","---","id: github","name: GitHub CLI","description: Use when the user mentions GitHub \u2014 PRs, issues, commits","activation: auto",'triggers: [gh, github, pr, "pull request", issue]',"---","","# GitHub CLI","","When the user asks about GitHub work, use `run_command` with `gh`:","","**Pull requests**",'- `gh pr create --title "<t>" --body "<b>"` \u2014 open a PR',"- `gh pr list` / `gh pr view <n>` / `gh pr checkout <n>`","",'**Issues** \u2014 `gh issue list`, `gh issue create --title "<t>" --body "<b>"`.',"","Suggest `gh auth status` and stop if auth errors appear.","```","","Rules:","- `id` is required, short, kebab-case. The scaffold sets it for you.","- `activation`: `always` / `auto` / `on-demand`. `auto` with `triggers` is the right default.","- `triggers` are simple substrings (not regex). Word boundaries are applied automatically \u2014 use `triggerPatterns` for an explicit regex list if you really need one.","- The markdown body is fed into the system prompt when the skill activates. Write it as a playbook the agent can follow verbatim.","- DO NOT emit `tools[]` \u2014 that's legacy JSON behaviour. The agent already has tools. Give it guidance, not aliases.","","## CLI slash commands (how to help the user, not invoke yourself)","The CLI the user is running has built-in slash commands that modify CLI state \u2014 session, model, mode. **You (the agent) cannot call slash commands directly \u2014 they are parsed by the CLI REPL before the prompt reaches you.** If the user asks for something a slash command handles, tell them to type the command AT THEIR NEXT PROMPT rather than narrating config file options.","","| User wants to\u2026 | Tell them to type |","|---|---|","| Switch model (e.g. bandit-core-1 \u2192 bandit-logic) | *call the `switch_model` tool directly \u2014 do NOT tell them to type `/model`* |","| See available models | `/model` (no args) |","| Change reasoning / thinking mode | `/think on`, `/think off`, `/think auto` |","| Toggle plan preview (show plan + y/N before running) | `/plan-preview on` or `off` |","| See effective config (provider, endpoint, headers, secrets redacted) | `/config` |","| Clear conversation without losing session | `/clear` |","| Start / resume a named session | `/session new`, `/session list`, `/session resume <id>` |","| Compact bloated tool results | `/compact` |","| Rewind a file edit via checkpoint | `/rewind <chk-id>` |","| Show auto-loaded project memory | `/memory` |","| Scaffold a skill file | `/skill new <name>` |","| Run a heuristic plan without burning LLM tokens | `/plan <goal>` |","| List skills / commands / update CLI | `/skills`, `/help`, `/update` |","| Quit | `/exit` or type `exit` |","",'For model swaps specifically: when the user asks conversationally to switch / change / swap / try a different model (e.g. "use bandit-logic", "switch to qwen3.6:27b", "let\'s try gemma4:26b"), call the `switch_model` tool with the exact model name \u2014 the CLI picks up the new model on the NEXT user prompt automatically. Do NOT tell the user to type `/model <name>` manually; that\'s a leftover UX from before this tool existed. Only fall back to suggesting `~/.bandit/config.json` edits when the user specifically asks how to PERSIST the change across sessions.'].join(`
|
|
1094
1094
|
`);return p?`${m}
|
|
1095
1095
|
|
|
1096
1096
|
## Project Memory
|
package/package.json
CHANGED