tenicli 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +91 -73
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import{createRequire as a0}from"node:module";var m0=Object.defineProperty;var d0=(Q)=>Q;function l0(Q,Z){this[Q]=d0.bind(null,Z)}var _0=(Q,Z)=>{for(var $ in Z)m0(Q,$,{get:Z[$],enumerable:!0,configurable:!0,set:l0.bind(Z,$)})};var P=(Q,Z)=>()=>(Q&&(Z=Q(Q=0)),Z);var r=a0(import.meta.url);import{existsSync as g,readFileSync as t,writeFileSync as i0,mkdirSync as c0}from"fs";import{join as C}from"path";function s(){let Q=process.env.HOME||process.env.USERPROFILE||"";return C(Q,".tenicli")}function o(){return C(s(),"config.json")}function u(){try{if(g(o()))return JSON.parse(t(o(),"utf8"))}catch{}return{}}function p(Q){let Z=s();if(!g(Z))c0(Z,{recursive:!0});let $=u(),V={...$,...Q,keys:{...$.keys,...Q.keys},baseUrls:{...$.baseUrls,...Q.baseUrls}};i0(o(),JSON.stringify(V,null,2),"utf8")}function m(){let Q=process.cwd();U0(C(Q,".tenicli.env")),U0(C(Q,".env"));let Z=u(),$=process.env,V=$.TENICLI_MODEL||Z.activeModel||k[0].id,Y=k.find((N)=>N.id===V)?.provider||$.TENICLI_PROVIDER||"anthropic",X=n0(Y,Z,$),z=Y==="openai"?"https://api.openai.com":"https://api.anthropic.com",J=$.TENICLI_BASE_URL||Z.baseUrls?.[Y]||z;return{provider:{type:Y,baseUrl:J,apiKey:X,model:V},maxTokens:parseInt($.TENICLI_MAX_TOKENS||"8192"),systemPrompt:r0(Q),cwd:Q}}function n0(Q,Z,$){if(Q==="anthropic")return $.TENICLI_API_KEY||$.ANTHROPIC_API_KEY||Z.keys?.anthropic||"";if(Q==="openai")return $.TENICLI_API_KEY||$.OPENAI_API_KEY||Z.keys?.openai||"";return $.TENICLI_API_KEY||""}function U0(Q){try{if(!g(Q))return;for(let Z of t(Q,"utf8").split(`
3
- `)){let $=Z.trim();if(!$||$.startsWith("#"))continue;let V=$.indexOf("=");if(V===-1)continue;let K=$.slice(0,V).trim(),Y=$.slice(V+1).trim();if(Y.startsWith('"')&&Y.endsWith('"')||Y.startsWith("'")&&Y.endsWith("'"))Y=Y.slice(1,-1);if(!process.env[K])process.env[K]=Y}}catch{}}function r0(Q){for(let Z of[C(Q,"TENICLI.md"),C(s(),"TENICLI.md")])if(g(Z))return t(Z,"utf8");return o0}var k,o0=`You are TeniCLI, a fast AI coding assistant in the terminal.
2
+ import{createRequire as c0}from"node:module";var a0=Object.defineProperty;var i0=(Z)=>Z;function n0(Z,$){this[Z]=i0.bind(null,$)}var E0=(Z,$)=>{for(var Q in $)a0(Z,Q,{get:$[Q],enumerable:!0,configurable:!0,set:n0.bind($,Q)})};var L=(Z,$)=>()=>(Z&&($=Z(Z=0)),$);var u=c0(import.meta.url);import{existsSync as p,readFileSync as e,writeFileSync as o0,mkdirSync as r0}from"fs";import{join as k}from"path";function Z0(){let Z=process.env.HOME||process.env.USERPROFILE||"";return k(Z,".tenicli")}function s(){return k(Z0(),"config.json")}function m(){try{if(p(s()))return JSON.parse(e(s(),"utf8"))}catch{}return{}}function d(Z){let $=Z0();if(!p($))r0($,{recursive:!0});let Q=m(),K={...Q,...Z,keys:{...Q.keys,...Z.keys},baseUrls:{...Q.baseUrls,...Z.baseUrls}};o0(s(),JSON.stringify(K,null,2),"utf8")}function l(){let Z=process.cwd();H0(k(Z,".tenicli.env")),H0(k(Z,".env"));let $=m(),Q=process.env,K=Q.TENICLI_MODEL||$.activeModel||h[0].id,Y=h.find((W)=>W.id===K)?.provider||Q.TENICLI_PROVIDER||"anthropic",X=t0(Y,$,Q),z=Y==="openai"?"https://api.openai.com":"https://api.anthropic.com",J=Q.TENICLI_BASE_URL||$.baseUrls?.[Y]||z;return{provider:{type:Y,baseUrl:J,apiKey:X,model:K},maxTokens:parseInt(Q.TENICLI_MAX_TOKENS||"8192"),systemPrompt:s0(Z),cwd:Z}}function t0(Z,$,Q){if(Z==="anthropic")return Q.TENICLI_API_KEY||Q.ANTHROPIC_API_KEY||$.keys?.anthropic||"";if(Z==="openai")return Q.TENICLI_API_KEY||Q.OPENAI_API_KEY||$.keys?.openai||"";return Q.TENICLI_API_KEY||""}function H0(Z){try{if(!p(Z))return;for(let $ of e(Z,"utf8").split(`
3
+ `)){let Q=$.trim();if(!Q||Q.startsWith("#"))continue;let K=Q.indexOf("=");if(K===-1)continue;let V=Q.slice(0,K).trim(),Y=Q.slice(K+1).trim();if(Y.startsWith('"')&&Y.endsWith('"')||Y.startsWith("'")&&Y.endsWith("'"))Y=Y.slice(1,-1);if(!process.env[V])process.env[V]=Y}}catch{}}function s0(Z){for(let $ of[k(Z,"TENICLI.md"),k(Z0(),"TENICLI.md")])if(p($))return e($,"utf8");return e0}var h,e0=`You are TeniCLI, a fast AI coding assistant in the terminal.
4
4
 
5
5
  TOOLS: read/write files, execute commands, search code, list directories.
6
6
 
@@ -9,42 +9,42 @@ RULES:
9
9
  - Use tools proactively — read before edit, verify after changes.
10
10
  - Ask before destructive operations (delete, overwrite).
11
11
  - The user may write in Vietnamese — respond in the same language they use.
12
- - Write production-quality code matching the project's style.`;var e=P(()=>{k=[{id:"claude-sonnet-4-20250514",name:"Claude Sonnet 4",provider:"anthropic",speed:"fast"},{id:"claude-haiku-3-5-20241022",name:"Claude Haiku 3.5",provider:"anthropic",speed:"fast"},{id:"claude-opus-4-20250514",name:"Claude Opus 4",provider:"anthropic",speed:"slow"},{id:"gpt-4o",name:"GPT-4o",provider:"openai",speed:"fast"},{id:"gpt-4o-mini",name:"GPT-4o Mini",provider:"openai",speed:"fast"},{id:"o3-mini",name:"o3-mini",provider:"openai",speed:"normal"}]});async function*Z0(Q,Z,$,V,K){if(Q.type==="openai")yield*s0(Q,Z,$,V,K);else yield*t0(Q,Z,$,V,K)}async function*t0(Q,Z,$,V,K){let Y=`${Q.baseUrl.replace(/\/$/,"")}/v1/messages`,X={model:Q.model,max_tokens:K,system:$,messages:Z,stream:!0};if(V.length)X.tools=V;let z=await q0(Y,X,{"anthropic-version":"2023-06-01","x-api-key":Q.apiKey,authorization:`Bearer ${Q.apiKey}`});for await(let J of E0(z))switch(J.type){case"message_start":if(J.message?.usage)yield{type:"usage",input:J.message.usage.input_tokens||0,output:0};break;case"content_block_start":if(J.content_block?.type==="text")yield{type:"text",text:""};else if(J.content_block?.type==="tool_use")yield{type:"tool_start",id:J.content_block.id,name:J.content_block.name};break;case"content_block_delta":if(J.delta?.type==="text_delta")yield{type:"text",text:J.delta.text};else if(J.delta?.type==="input_json_delta")yield{type:"tool_input",partial:J.delta.partial_json};break;case"content_block_stop":yield{type:"tool_end"};break;case"message_delta":if(J.usage)yield{type:"usage",input:0,output:J.usage.output_tokens||0};yield{type:"done",stopReason:J.delta?.stop_reason||"end_turn"};break}}async function*s0(Q,Z,$,V,K){let Y=`${Q.baseUrl.replace(/\/$/,"")}/v1/chat/completions`,X=e0(Z,$),z=V.map((_)=>({type:"function",function:{name:_.name,description:_.description,parameters:_.input_schema}})),J={model:Q.model,max_tokens:K,messages:X,stream:!0,stream_options:{include_usage:!0}};if(z.length)J.tools=z;let N=await q0(Y,J,{authorization:`Bearer ${Q.apiKey}`}),W=new Map;for await(let _ of E0(N)){let q=_.choices?.[0];if(!q){if(_.usage)yield{type:"usage",input:_.usage.prompt_tokens||0,output:_.usage.completion_tokens||0};continue}let A=q.delta||{};if(A.content)yield{type:"text",text:A.content};if(A.tool_calls)for(let U of A.tool_calls){if(U.id)W.set(U.index,{id:U.id,name:U.function?.name||"",args:""}),yield{type:"tool_start",id:U.id,name:U.function?.name||""};if(U.function?.arguments){let D=W.get(U.index);if(D)D.args+=U.function.arguments;yield{type:"tool_input",partial:U.function.arguments}}}if(q.finish_reason){for(let[,U]of W)yield{type:"tool_end"};yield{type:"done",stopReason:q.finish_reason==="tool_calls"?"tool_use":q.finish_reason}}}}function e0(Q,Z){let $=[{role:"system",content:Z}];for(let V of Q)if(V.role==="user")if(typeof V.content==="string")$.push({role:"user",content:V.content});else{let K=V.content;for(let Y of K)if(Y.type==="tool_result")$.push({role:"tool",tool_call_id:Y.tool_use_id,content:Y.content||""});else $.push({role:"user",content:Y.text||""})}else if(typeof V.content==="string")$.push({role:"assistant",content:V.content});else{let K=V.content,Y=K.filter((z)=>z.type==="tool_use"),X=K.filter((z)=>z.type==="text").map((z)=>z.text).join("");if(Y.length)$.push({role:"assistant",content:X||null,tool_calls:Y.map((z)=>({id:z.id,type:"function",function:{name:z.name,arguments:JSON.stringify(z.input||{})}}))});else $.push({role:"assistant",content:X})}return $}async function q0(Q,Z,$){let V=await fetch(Q,{method:"POST",headers:{"content-type":"application/json",...$},body:JSON.stringify(Z)});if(!V.ok){let H=await V.text();throw Error(`API ${V.status}: ${H.slice(0,300)}`)}let K={},Y=V.headers.get("anthropic-ratelimit-requests-limit");if(Y)K.requestsLimit=parseInt(Y);let X=V.headers.get("anthropic-ratelimit-requests-remaining");if(X)K.requestsRemaining=parseInt(X);let z=V.headers.get("anthropic-ratelimit-requests-reset");if(z)K.requestsReset=z;let J=V.headers.get("anthropic-ratelimit-tokens-limit");if(J)K.tokensLimit=parseInt(J);let N=V.headers.get("anthropic-ratelimit-tokens-remaining");if(N)K.tokensRemaining=parseInt(N);let W=V.headers.get("anthropic-ratelimit-tokens-reset");if(W)K.tokensReset=W;let _=V.headers.get("x-ratelimit-limit-requests");if(_)K.requestsLimit=parseInt(_);let q=V.headers.get("x-ratelimit-remaining-requests");if(q)K.requestsRemaining=parseInt(q);let A=V.headers.get("x-ratelimit-reset-requests");if(A)K.requestsReset=A;let U=V.headers.get("x-ratelimit-limit-tokens");if(U)K.tokensLimit=parseInt(U);let D=V.headers.get("x-ratelimit-remaining-tokens");if(D)K.tokensRemaining=parseInt(D);let R=V.headers.get("x-ratelimit-reset-tokens");if(R)K.tokensReset=R;if(Object.keys(K).length>0)Q0=K;return V}async function*E0(Q){let Z=Q.body.getReader(),$=new TextDecoder,V="";while(!0){let{done:K,value:Y}=await Z.read();if(K)break;V+=$.decode(Y,{stream:!0});let X=V.split(`
13
- `);V=X.pop();for(let z of X)if(z.startsWith("data: ")){let J=z.slice(6).trim();if(J==="[DONE]")return;try{yield JSON.parse(J)}catch{}}}}var Q0;var $0=P(()=>{Q0={}});function Q1(){let Q=[" ██ ██ ","██████████","███ ██ █","██████████","██████████"," ██ ██ ██ "],Z={T:["█████"," █ "," █ "," █ "," █ "],E:["████ ","█ ","███ ","█ ","████ "],N:["█ █ ","██ █ ","█ ██ ","█ █ ","█ █ "],I:["███"," █ "," █ "," █ ","███"],space:[" "," "," "," "," "],C:[" ███","█ ","█ ","█ "," ███"],L:["█ ","█ ","█ ","█ ","████"]},$=[Z.T,Z.E,Z.N,Z.I,Z.space,Z.C,Z.L,Z.I],V=["","","","",""];for(let X=0;X<5;X++)V[X]=$.map((z)=>z[X]).join(" ");let K=Q.map((X)=>G.cyan(X.padEnd(14," "))),Y=[" ".repeat(V[0].length),...V].map((X)=>G.blue(X));return K.map((X,z)=>`${X} ${Y[z]||""}`).join(`
14
- `)}function x(Q,Z=60){let $=(V,K)=>{let Y=V.replace(/\x1b\[[0-9;]*m/g,""),X=K-Y.length;return X>0?V+" ".repeat(X):V};console.log(G.gray(` ${j.tl}${j.line(Z)}${j.tr}`));for(let V of Q)console.log(G.gray(` ${j.v}`)+` ${$(V,Z-2)} `+G.gray(j.v));console.log(G.gray(` ${j.bl}${j.line(Z)}${j.br}`))}function H0(Q="0.0.0"){console.clear(),console.log(),console.log(Q1()),console.log(),x([G.gray("type to chat")+` ${E.dot} `+G.gray("/help for commands")+` ${E.dot} `+G.gray(`v${Q}`)],60),console.log()}class l{i=0;timer=null;msg;constructor(Q="Thinking"){this.msg=Q}start(){return this.timer=setInterval(()=>{process.stdout.write(`\x1B[2K\r ${G.blue(E.spinner[this.i%E.spinner.length])} ${G.gray(this.msg)}`),this.i++},80),this}stop(){if(this.timer)clearInterval(this.timer),this.timer=null;process.stdout.write("\x1B[2K\r")}}function L(Q,Z=!1){return new Promise(($,V)=>{if(process.stdout.write(Q),!Z||!process.stdin.isTTY){let U="",D=(H)=>{let B=typeof H==="string"?H:H.toString("utf8");if(B.charCodeAt(0)===3)process.stdout.write(`
15
- `),process.exit(0);if(B.charCodeAt(0)===4){R(),V(Error("EOF"));return}U+=B;let O=U.indexOf(`
16
- `);if(O!==-1)R(),$(U.slice(0,O).replace(/\r$/,""))},R=()=>{process.stdin.removeListener("data",D)};if(!process.stdin.readableEncoding)process.stdin.setEncoding("utf8");process.stdin.on("data",D),process.stdin.resume();return}let K="",Y=0,X=0,z=process.stdin.isRaw;if(process.stdin.setRawMode(!0),!process.stdin.readableEncoding)process.stdin.setEncoding("utf8");process.stdin.resume();let J=()=>{if(!K.startsWith("/")||K.length<1)return[];return Z1.filter((U)=>U.cmd.startsWith(K))},N=()=>{if(Y>0){for(let U=0;U<Y;U++)process.stdout.write("\x1B[1B"),process.stdout.write("\x1B[2K");process.stdout.write(`\x1B[${Y}A`),Y=0}process.stdout.write("\x1B[0K")},W=()=>{N();let U=J();if(U.length===0)return;if(X>=U.length)X=U.length-1;if(X<0)X=0;process.stdout.write("\x1B[s");for(let D=0;D<U.length;D++){process.stdout.write(`
17
- \x1B[2K`);let R=U[D];if(D===X)process.stdout.write(` ${G.blue(G.bold(R.cmd))} ${G.gray(R.desc)}`);else process.stdout.write(` ${G.gray(R.cmd)} ${G.gray(G.dim(R.desc))}`)}Y=U.length,process.stdout.write("\x1B[u")},_="",q=(U)=>{let D=typeof U==="string"?U:U.toString("utf8");for(let R=0;R<D.length;R++){let H=D[R],B=H.charCodeAt(0);if(_.length>0||B===27){if(_+=H,_.length===1)continue;if(_.length===2&&_[1]==="[")continue;if(_.length>=3){let O=J();if(_==="\x1B[A"&&O.length>0)X=(X-1+O.length)%O.length,W();else if(_==="\x1B[B"&&O.length>0)X=(X+1)%O.length,W();_="";continue}continue}if(B===3)N(),process.stdin.setRawMode(z),process.stdout.write(`
18
- `),process.exit(0);if(B===4){N(),process.stdin.setRawMode(z),A(),V(Error("EOF"));return}if(B===13||B===10){let O=J();if(O.length>0&&K!==O[X].cmd){N();let f=O[X].cmd;process.stdout.write("\b \b".repeat(K.length)),K=f,process.stdout.write(K)}N(),process.stdin.setRawMode(z),process.stdout.write(`
19
- `),A(),$(K);return}if(B===127||B===8){if(K.length>0)N(),K=K.slice(0,-1),process.stdout.write("\b \b"),X=0,W();continue}if(B===9){let O=J();if(O.length>0){N();let f=O[X].cmd,p0=f.slice(K.length);K=f,process.stdout.write(p0),W()}continue}if(B<32)continue;N(),K+=H,process.stdout.write(H),X=0,W()}},A=()=>{process.stdin.removeListener("data",q)};process.stdin.on("data",q)})}async function D0(){let Q=[],Z=!0;while(!0){let $=Z?`
20
- ${G.gray(j.tl+j.line(3))} ${E.prompt} `:` ${G.gray(j.v)} `,V=await L($,Z);if(Z=!1,V.endsWith("\\"))Q.push(V.slice(0,-1));else{Q.push(V);break}}return Q.join(`
21
- `)}async function T(Q,Z){console.log(`
22
- ${G.bold(Q)}`),Z.forEach(($,V)=>{let K=G.blue(` ${V+1}.`),Y=$.desc?G.gray(` (${$.desc})`):"";console.log(`${K} ${$.label}${Y}`)}),console.log(` ${G.gray(" 0. Cancel")}`);while(!0){let V=(await L(`
23
- ${G.gray("choose")} ${G.blue("❯")} `)).trim().toLowerCase();if(V==="0"||V==="q"||V==="cancel"||V==="exit"||V==="")return-1;let K=parseInt(V);if(K>=1&&K<=Z.length)return K-1;console.log(` ${E.warn} enter 1-${Z.length} or 0 to cancel`)}}function S(Q,Z){console.log(`
24
- ${E.tool} ${G.yellow(Q)} ${G.gray(Z)}`)}function M(Q){console.error(` ${E.err} ${G.pink(Q)}`)}var A0=(Q)=>`\x1B[${Q}m`,d=(Q,Z)=>($)=>`${A0(Q)}${$}${A0(Z)}`,F=(Q)=>(Z)=>`\x1B[38;5;${Q}m${Z}\x1B[39m`,G,E,j,Z1;var a=P(()=>{G={bold:d("1","22"),dim:d("2","22"),italic:d("3","23"),under:d("4","24"),blue:F(111),purple:F(141),green:F(149),yellow:F(179),pink:F(210),cyan:F(117),gray:F(60),text:F(146),orange:F(215)},E={prompt:G.blue("❯"),ai:G.purple("◆"),tool:G.yellow("⚙"),ok:G.green("✓"),err:G.pink("✗"),warn:G.yellow("⚠"),arrow:G.gray("→"),dot:G.gray("•"),spinner:["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"]};j={h:"─",v:"│",tl:"╭",tr:"╮",bl:"╰",br:"╯",line:(Q)=>"─".repeat(Q)};Z1=[{cmd:"/model",desc:"switch AI model"},{cmd:"/auth",desc:"set API key"},{cmd:"/mode",desc:"toggle mode"},{cmd:"/compact",desc:"toggle compact"},{cmd:"/diff",desc:"show file changes"},{cmd:"/undo",desc:"revert last change"},{cmd:"/init",desc:"init project context"},{cmd:"/remote",desc:"start web remote"},{cmd:"/history",desc:"past conversations"},{cmd:"/quota",desc:"API rate limits"},{cmd:"/update",desc:"update tenicli"},{cmd:"/clear",desc:"clear screen"},{cmd:"/cost",desc:"show token usage"},{cmd:"/help",desc:"list commands"},{cmd:"/exit",desc:"quit"}]});import{readFileSync as V0,writeFileSync as B0,existsSync as c,readdirSync as O0,statSync as j0,mkdirSync as $1}from"fs";import{resolve as V1,relative as i,join as F0,dirname as G1}from"path";import{spawn as I0}from"child_process";class P0{writes=[];recordWrite(Q,Z){let $=c(Q)?V0(Q,"utf8"):null;this.writes.push({path:Q,backup:$,newLines:Z.split(`
25
- `).length,time:new Date})}getChanges(){let Q=new Map;for(let Z of this.writes)Q.set(Z.path,{isNew:Z.backup===null,lines:Z.newLines,time:Z.time});return Array.from(Q.entries()).map(([Z,$])=>({path:Z,...$}))}undo(){let Q=this.writes.pop();if(!Q)return null;if(Q.backup!==null)return B0(Q.path,Q.backup,"utf8"),{path:Q.path,restored:!0};else{try{r("fs").unlinkSync(Q.path)}catch{}return{path:Q.path,restored:!1}}}get count(){return this.writes.length}clear(){this.writes=[]}}async function M0(Q,Z,$){try{let V;switch(Q){case"read_file":V=X1(Z,$),S("read_file",G.dim(i($,I(Z.path,$))));break;case"write_file":V=z1(Z,$),S("write_file",G.dim(i($,I(Z.path,$))));break;case"list_dir":V=J1(Z,$),S("list_dir",G.dim(Z.path||"."));break;case"search_files":V=await Y1(Z,$),S("search_files",G.dim(`"${Z.pattern}"`));break;case"exec_command":V=await N1(Z,$),S("exec_command",G.dim(R0(Z.command,60)));break;default:V=`Unknown tool: ${Q}`}return{type:"tool_result",content:R0(V,K1)}}catch(V){return{type:"tool_result",content:`Error: ${V.message}`,is_error:!0}}}function I(Q,Z){return V1(Z,Q)}function X1(Q,Z){let $=I(Q.path,Z);if(!c($))return`File not found: ${Q.path}`;let V=V0($,"utf8"),K=V.split(`
26
- `);if(Q.start_line||Q.end_line){let Y=Math.max(1,Q.start_line||1)-1,X=Math.min(K.length,Q.end_line||K.length);return K.slice(Y,X).map((z,J)=>`${Y+J+1}: ${z}`).join(`
27
- `)}if(K.length>50)return K.map((Y,X)=>`${X+1}: ${Y}`).join(`
28
- `);return V}function z1(Q,Z){let $=I(Q.path,Z),V=G1($);if(!c(V))$1(V,{recursive:!0});return w.recordWrite($,Q.content),B0($,Q.content,"utf8"),`Written ${Q.content.split(`
29
- `).length} lines to ${Q.path}`}function J1(Q,Z){let $=I(Q.path||".",Z);if(!c($))return`Directory not found: ${Q.path||"."}`;let V=Q.depth||1,K=[];function Y(X,z){if(z>V)return;try{let J=O0(X);for(let N of J){if(N.startsWith(".")||N==="node_modules")continue;let W=F0(X,N),_=i($,W);try{let q=j0(W),A=" ".repeat(z);if(q.isDirectory())K.push(`${A}${_}/`),Y(W,z+1);else{let U=q.size>1024?`${(q.size/1024).toFixed(1)}KB`:`${q.size}B`;K.push(`${A}${_} (${U})`)}}catch{}}}catch{}}return Y($,0),K.length>0?K.join(`
30
- `):"(empty directory)"}async function Y1(Q,Z){let $=I(Q.path||".",Z),V=Q.pattern;try{let X=["-n","--max-count=50","--no-heading"];if(Q.include)X.push("--glob",Q.include);return X.push(V,$),await new Promise((J,N)=>{let W=I0("rg",X,{shell:!0}),_="";W.stdout.on("data",(q)=>_+=q.toString()),W.on("close",(q)=>{if(q===0||q===1)J(_.trim()||"No matches found.");else N(Error("rg failed"))}),W.on("error",N)})}catch{}let K=[];function Y(X){try{for(let z of O0(X)){if(z.startsWith(".")||z==="node_modules")continue;let J=F0(X,z);try{let N=j0(J);if(N.isDirectory()){Y(J);continue}if(N.size>500000)continue;if(Q.include&&!W1(z,Q.include))continue;let _=V0(J,"utf8").split(`
31
- `);for(let q=0;q<_.length;q++)if(_[q].includes(V)){if(K.push(`${i(Z,J)}:${q+1}: ${_[q].trim()}`),K.length>=50)return}}catch{}}}catch{}}return Y($),K.length>0?K.join(`
32
- `):"No matches found."}async function N1(Q,Z){let $=I(Q.cwd||".",Z),V=process.platform==="win32",K=V?"cmd":"sh",Y=V?"/c":"-c";return new Promise((X)=>{let z=I0(K,[Y,Q.command],{cwd:$,env:{...process.env,PAGER:"cat"}}),J="",N="";z.stdout.on("data",(_)=>J+=_.toString()),z.stderr.on("data",(_)=>N+=_.toString());let W=setTimeout(()=>z.kill(),30000);z.on("close",(_)=>{clearTimeout(W);let q="";if(J.trim())q+=J.trim();if(N.trim())q+=(q?`
33
- `:"")+`[stderr] ${N.trim()}`;q+=`
34
- [exit code: ${_}]`,X(q)}),z.on("error",(_)=>{clearTimeout(W),X(`[error] Failed to start process: ${_.message}`)})})}function R0(Q,Z){if(Q.length<=Z)return Q;return Q.slice(0,Z)+`
35
- ... (truncated, ${Q.length-Z} chars omitted)`}function W1(Q,Z){if(Z.startsWith("*."))return Q.endsWith(Z.slice(1));return Q.includes(Z.replace(/\*/g,""))}var w,L0,K1=30000;var G0=P(()=>{a();w=new P0,L0=[{name:"read_file",description:"Read contents of a file. Returns the file text.",input_schema:{type:"object",properties:{path:{type:"string",description:"File path (relative to cwd or absolute)"},start_line:{type:"number",description:"Optional: start line (1-indexed)"},end_line:{type:"number",description:"Optional: end line (1-indexed, inclusive)"}},required:["path"]}},{name:"write_file",description:"Write content to a file. Creates parent directories if needed.",input_schema:{type:"object",properties:{path:{type:"string",description:"File path"},content:{type:"string",description:"Full file content to write"}},required:["path","content"]}},{name:"list_dir",description:"List files and directories in a path. Returns names with type indicators.",input_schema:{type:"object",properties:{path:{type:"string",description:"Directory path (default: cwd)"},depth:{type:"number",description:"Max depth (default: 1)"}},required:[]}},{name:"search_files",description:"Search for text in files using pattern matching (like grep). Returns matching lines with file paths.",input_schema:{type:"object",properties:{pattern:{type:"string",description:"Text or regex pattern to search for"},path:{type:"string",description:"Directory to search in (default: cwd)"},include:{type:"string",description:'Glob pattern to filter files, e.g. "*.ts"'}},required:["pattern"]}},{name:"exec_command",description:"Execute a shell command. Returns stdout and stderr.",input_schema:{type:"object",properties:{command:{type:"string",description:"Shell command to execute"},cwd:{type:"string",description:"Working directory (default: project cwd)"}},required:["command"]}}]});class y{messages=[];tokens={input:0,output:0};cfg;autoMode=!1;onOutput;onConfirm;constructor(Q){this.cfg=Q}emit(Q){this.onOutput?.(Q)}write(Q){if(this.onOutput)this.emit({type:"text",text:Q});else process.stdout.write(Q)}log(Q){if(this.onOutput)this.emit({type:"text",text:Q+`
36
- `});else console.log(Q)}async send(Q){this.messages.push({role:"user",content:Q}),await this.agentLoop()}async agentLoop(){while(!0){let Q=await this.streamResponse();if(Q.stopReason==="error")break;if(Q.stopReason==="tool_use"){let $=Q.content.filter((K)=>K.type==="tool_use"),V=[];for(let K of $){if(!this.autoMode&&(K.name==="write_file"||K.name==="exec_command")){let X=K.name==="write_file"?K.input?.path:K.input?.command?.slice(0,80),z="y";if(this.onConfirm)z=await this.onConfirm(K.id,K.name,X||"");else this.log(`
37
- ${E.warn} ${G.yellow(K.name)} ${G.gray(X||"")}`),z=(await L(` ${G.gray("allow?")} ${G.blue("[y/n/auto]")} `)).trim().toLowerCase();if(z==="auto")this.autoMode=!0;else if(z!=="y"&&z!=="yes"&&z!==""){V.push({type:"tool_result",tool_use_id:K.id,content:"User denied this action.",is_error:!0});continue}}this.emit({type:"tool",name:K.name,detail:JSON.stringify(K.input||{}).slice(0,200)});let Y=await M0(K.name,K.input,this.cfg.cwd);this.emit({type:"tool_result",name:K.name,content:(Y.content||"").slice(0,500),is_error:!!Y.is_error}),V.push({type:"tool_result",tool_use_id:K.id,content:Y.content,is_error:Y.is_error})}this.messages.push({role:"assistant",content:Q.content}),this.messages.push({role:"user",content:V});continue}let Z=Q.content.filter(($)=>$.type==="text").map(($)=>$.text).join("");if(Z)this.messages.push({role:"assistant",content:Z});break}if(this.emit({type:"tokens",input:this.tokens.input,output:this.tokens.output,messages:this.messages.length}),!this.onOutput)console.log(`
38
- ${G.gray(`tokens: ${this.tokens.input}↑ ${this.tokens.output}↓`)}`)}async streamResponse(){let Q=!this.onOutput,Z=Q?new l("Thinking").start():null,$=[],V="",K="",Y="",X="",z="end_turn",J=!1;try{let N=Z0(this.cfg.provider,this.messages,this.cfg.systemPrompt,L0,this.cfg.maxTokens);for await(let W of N)switch(W.type){case"text":if(!J){if(Z?.stop(),J=!0,Q)process.stdout.write(`
39
- ${E.ai} `)}if(W.text)this.write(W.text);V+=W.text;break;case"tool_start":if(!J)Z?.stop(),J=!0;if(V)$.push({type:"text",text:V}),V="";K=W.id,Y=W.name,X="";break;case"tool_input":X+=W.partial;break;case"tool_end":if(K){let _={};try{_=JSON.parse(X)}catch{}$.push({type:"tool_use",id:K,name:Y,input:_}),K="",X=""}break;case"usage":this.tokens.input+=W.input,this.tokens.output+=W.output;break;case"done":z=W.stopReason;break}}catch(N){if(Z?.stop(),Q)console.log();let W=N.message||String(N);if(this.emit({type:"error",message:W}),Q)M(W);return{content:[],stopReason:"error"}}if(V){if($.push({type:"text",text:V}),this.emit({type:"text_done"}),Q)process.stdout.write(`
40
- `)}if(!J)Z?.stop();return{content:$,stopReason:z}}async compact(){if(this.messages.length<4){this.log(` ${E.warn} Not enough messages to compact.`);return}let Q=!this.onOutput?new l("Compacting").start():null;try{let Z="";for(let X of this.messages)if(typeof X.content==="string")Z+=`${X.role}: ${X.content.slice(0,500)}
41
- `;else{let z=X.content.filter((N)=>N.type==="text").map((N)=>N.text?.slice(0,300)).join(" ");if(z)Z+=`${X.role}: ${z}
42
- `;let J=X.content.filter((N)=>N.type==="tool_use").map((N)=>`[tool: ${N.name}]`).join(", ");if(J)Z+=` tools: ${J}
43
- `}let $=this.messages.length,V=`Summarize this conversation concisely. Keep key decisions, file changes, and current state. Be brief:
12
+ - Write production-quality code matching the project's style.`;var $0=L(()=>{h=[{id:"claude-sonnet-4-20250514",name:"Claude Sonnet 4",provider:"anthropic",speed:"fast"},{id:"claude-haiku-3-5-20241022",name:"Claude Haiku 3.5",provider:"anthropic",speed:"fast"},{id:"claude-opus-4-20250514",name:"Claude Opus 4",provider:"anthropic",speed:"slow"},{id:"gpt-4o",name:"GPT-4o",provider:"openai",speed:"fast"},{id:"gpt-4o-mini",name:"GPT-4o Mini",provider:"openai",speed:"fast"},{id:"o3-mini",name:"o3-mini",provider:"openai",speed:"normal"}]});async function*G0(Z,$,Q,K,V){if(Z.type==="openai")yield*$1(Z,$,Q,K,V);else yield*Z1(Z,$,Q,K,V)}async function*Z1(Z,$,Q,K,V){let Y=`${Z.baseUrl.replace(/\/$/,"")}/v1/messages`,X={model:Z.model,max_tokens:V,system:Q,messages:$,stream:!0};if(K.length)X.tools=K;let z=await D0(Y,X,{"anthropic-version":"2023-06-01","x-api-key":Z.apiKey,authorization:`Bearer ${Z.apiKey}`});for await(let J of B0(z))switch(J.type){case"message_start":if(J.message?.usage)yield{type:"usage",input:J.message.usage.input_tokens||0,output:0};break;case"content_block_start":if(J.content_block?.type==="text")yield{type:"text",text:""};else if(J.content_block?.type==="tool_use")yield{type:"tool_start",id:J.content_block.id,name:J.content_block.name};break;case"content_block_delta":if(J.delta?.type==="text_delta")yield{type:"text",text:J.delta.text};else if(J.delta?.type==="input_json_delta")yield{type:"tool_input",partial:J.delta.partial_json};break;case"content_block_stop":yield{type:"tool_end"};break;case"message_delta":if(J.usage)yield{type:"usage",input:0,output:J.usage.output_tokens||0};yield{type:"done",stopReason:J.delta?.stop_reason||"end_turn"};break}}async function*$1(Z,$,Q,K,V){let Y=`${Z.baseUrl.replace(/\/$/,"")}/v1/chat/completions`,X=Q1($,Q),z=K.map((_)=>({type:"function",function:{name:_.name,description:_.description,parameters:_.input_schema}})),J={model:Z.model,max_tokens:V,messages:X,stream:!0,stream_options:{include_usage:!0}};if(z.length)J.tools=z;let W=await D0(Y,J,{authorization:`Bearer ${Z.apiKey}`}),N=new Map;for await(let _ of B0(W)){let q=_.choices?.[0];if(!q){if(_.usage)yield{type:"usage",input:_.usage.prompt_tokens||0,output:_.usage.completion_tokens||0};continue}let H=q.delta||{};if(H.content)yield{type:"text",text:H.content};if(H.tool_calls)for(let U of H.tool_calls){if(U.id)N.set(U.index,{id:U.id,name:U.function?.name||"",args:""}),yield{type:"tool_start",id:U.id,name:U.function?.name||""};if(U.function?.arguments){let D=N.get(U.index);if(D)D.args+=U.function.arguments;yield{type:"tool_input",partial:U.function.arguments}}}if(q.finish_reason){for(let[,U]of N)yield{type:"tool_end"};yield{type:"done",stopReason:q.finish_reason==="tool_calls"?"tool_use":q.finish_reason}}}}function Q1(Z,$){let Q=[{role:"system",content:$}];for(let K of Z)if(K.role==="user")if(typeof K.content==="string")Q.push({role:"user",content:K.content});else{let V=K.content;for(let Y of V)if(Y.type==="tool_result")Q.push({role:"tool",tool_call_id:Y.tool_use_id,content:Y.content||""});else Q.push({role:"user",content:Y.text||""})}else if(typeof K.content==="string")Q.push({role:"assistant",content:K.content});else{let V=K.content,Y=V.filter((z)=>z.type==="tool_use"),X=V.filter((z)=>z.type==="text").map((z)=>z.text).join("");if(Y.length)Q.push({role:"assistant",content:X||null,tool_calls:Y.map((z)=>({id:z.id,type:"function",function:{name:z.name,arguments:JSON.stringify(z.input||{})}}))});else Q.push({role:"assistant",content:X})}return Q}async function D0(Z,$,Q){let K=await fetch(Z,{method:"POST",headers:{"content-type":"application/json",...Q},body:JSON.stringify($)});if(!K.ok){let E=await K.text();throw Error(`API ${K.status}: ${E.slice(0,300)}`)}let V={},Y=K.headers.get("anthropic-ratelimit-requests-limit");if(Y)V.requestsLimit=parseInt(Y);let X=K.headers.get("anthropic-ratelimit-requests-remaining");if(X)V.requestsRemaining=parseInt(X);let z=K.headers.get("anthropic-ratelimit-requests-reset");if(z)V.requestsReset=z;let J=K.headers.get("anthropic-ratelimit-tokens-limit");if(J)V.tokensLimit=parseInt(J);let W=K.headers.get("anthropic-ratelimit-tokens-remaining");if(W)V.tokensRemaining=parseInt(W);let N=K.headers.get("anthropic-ratelimit-tokens-reset");if(N)V.tokensReset=N;let _=K.headers.get("x-ratelimit-limit-requests");if(_)V.requestsLimit=parseInt(_);let q=K.headers.get("x-ratelimit-remaining-requests");if(q)V.requestsRemaining=parseInt(q);let H=K.headers.get("x-ratelimit-reset-requests");if(H)V.requestsReset=H;let U=K.headers.get("x-ratelimit-limit-tokens");if(U)V.tokensLimit=parseInt(U);let D=K.headers.get("x-ratelimit-remaining-tokens");if(D)V.tokensRemaining=parseInt(D);let R=K.headers.get("x-ratelimit-reset-tokens");if(R)V.tokensReset=R;if(Object.keys(V).length>0)Q0=V;return K}async function*B0(Z){let $=Z.body.getReader(),Q=new TextDecoder,K="";while(!0){let{done:V,value:Y}=await $.read();if(V)break;K+=Q.decode(Y,{stream:!0});let X=K.split(`
13
+ `);K=X.pop();for(let z of X)if(z.startsWith("data: ")){let J=z.slice(6).trim();if(J==="[DONE]")return;try{yield JSON.parse(J)}catch{}}}}var Q0;var K0=L(()=>{Q0={}});function G1(){let Z=[" ██ ██ ","██████████","███ ██ █","██████████","██████████"," ██ ██ ██ "],$={T:["█████"," █ "," █ "," █ "," █ "],E:["████ ","█ ","███ ","█ ","████ "],N:["█ █ ","██ █ ","█ ██ ","█ █ ","█ █ "],I:["███"," █ "," █ "," █ ","███"],space:[" "," "," "," "," "],C:[" ███","█ ","█ ","█ "," ███"],L:["█ ","█ ","█ ","█ ","████"]},Q=[$.T,$.E,$.N,$.I,$.space,$.C,$.L,$.I],K=["","","","",""];for(let X=0;X<5;X++)K[X]=Q.map((z)=>z[X]).join(" ");let V=Z.map((X)=>G.cyan(X.padEnd(14," "))),Y=[" ".repeat(K[0].length),...K].map((X)=>G.blue(X));return V.map((X,z)=>`${X} ${Y[z]||""}`).join(`
14
+ `)}function v(Z,$=60){let Q=(K,V)=>{let Y=K.replace(/\x1b\[[0-9;]*m/g,""),X=V-Y.length;return X>0?K+" ".repeat(X):K};console.log(G.gray(` ${j.tl}${j.line($)}${j.tr}`));for(let K of Z)console.log(G.gray(` ${j.v}`)+` ${Q(K,$-2)} `+G.gray(j.v));console.log(G.gray(` ${j.bl}${j.line($)}${j.br}`))}function O0(Z="0.0.0"){console.clear(),console.log(),console.log(G1()),console.log(),v([G.gray("type to chat")+` ${A.dot} `+G.gray("/help for commands")+` ${A.dot} `+G.gray(`v${Z}`)],60),console.log()}class i{i=0;timer=null;msg;constructor(Z="Thinking"){this.msg=Z}start(){return this.timer=setInterval(()=>{process.stdout.write(`\x1B[2K\r ${G.blue(A.spinner[this.i%A.spinner.length])} ${G.gray(this.msg)}`),this.i++},80),this}stop(){if(this.timer)clearInterval(this.timer),this.timer=null;process.stdout.write("\x1B[2K\r")}}function C(Z,$=!1){return new Promise((Q,K)=>{if(process.stdout.write(Z),!$||!process.stdin.isTTY){let U="",D=(E)=>{let B=typeof E==="string"?E:E.toString("utf8");if(B.charCodeAt(0)===3)process.stdout.write(`
15
+ `),process.exit(0);if(B.charCodeAt(0)===4){R(),K(Error("EOF"));return}U+=B;let O=U.indexOf(`
16
+ `);if(O!==-1)R(),Q(U.slice(0,O).replace(/\r$/,""))},R=()=>{process.stdin.removeListener("data",D)};if(!process.stdin.readableEncoding)process.stdin.setEncoding("utf8");process.stdin.on("data",D),process.stdin.resume();return}let V="",Y=0,X=0,z=process.stdin.isRaw;if(process.stdin.setRawMode(!0),!process.stdin.readableEncoding)process.stdin.setEncoding("utf8");process.stdin.resume();let J=()=>{if(!V.startsWith("/")||V.length<1)return[];return K1.filter((U)=>U.cmd.startsWith(V))},W=()=>{if(Y>0){for(let U=0;U<Y;U++)process.stdout.write("\x1B[1B"),process.stdout.write("\x1B[2K");process.stdout.write(`\x1B[${Y}A`),Y=0}process.stdout.write("\x1B[0K")},N=()=>{W();let U=J();if(U.length===0)return;if(X>=U.length)X=U.length-1;if(X<0)X=0;let D=Z.replace(/\x1b\[[0-9;]*m/g,"").length;for(let R=0;R<U.length;R++){process.stdout.write(`
17
+ \x1B[2K\r`);let E=U[R];if(R===X)process.stdout.write(` ${G.blue(G.bold(E.cmd))} ${G.gray(E.desc)}`);else process.stdout.write(` ${G.gray(E.cmd)} ${G.gray(G.dim(E.desc))}`)}Y=U.length,process.stdout.write(`\x1B[${Y}A\r\x1B[${D+V.length}C`)},_="",q=(U)=>{let D=typeof U==="string"?U:U.toString("utf8");for(let R=0;R<D.length;R++){let E=D[R],B=E.charCodeAt(0);if(_.length>0||B===27){if(_+=E,_.length===1)continue;if(_.length===2&&_[1]==="[")continue;if(_.length>=3){let O=J();if(_==="\x1B[A"&&O.length>0)X=(X-1+O.length)%O.length,N();else if(_==="\x1B[B"&&O.length>0)X=(X+1)%O.length,N();_="";continue}continue}if(B===3)W(),process.stdin.setRawMode(z),process.stdout.write(`
18
+ `),process.exit(0);if(B===4){W(),process.stdin.setRawMode(z),H(),K(Error("EOF"));return}if(B===13||B===10){let O=J();if(O.length>0&&V!==O[X].cmd){W();let b=O[X].cmd;process.stdout.write("\b \b".repeat(V.length)),V=b,process.stdout.write(V)}W(),process.stdin.setRawMode(z),process.stdout.write(`
19
+ `),H(),Q(V);return}if(B===127||B===8){if(V.length>0)W(),V=V.slice(0,-1),process.stdout.write("\b \b"),X=0,N();continue}if(B===9){let O=J();if(O.length>0){W();let b=O[X].cmd,t=b.slice(V.length);V=b,process.stdout.write(t),N()}continue}if(B<32)continue;W(),V+=E,process.stdout.write(E),X=0,N()}},H=()=>{process.stdin.removeListener("data",q)};process.stdin.on("data",q)})}async function j0(){let Z=[],$=!0;while(!0){let Q=$?`
20
+ ${G.gray(j.tl+j.line(3))} ${A.prompt} `:` ${G.gray(j.v)} `,K=await C(Q,$);if($=!1,K.endsWith("\\"))Z.push(K.slice(0,-1));else{Z.push(K);break}}return Z.join(`
21
+ `)}async function T(Z,$){console.log(`
22
+ ${G.bold(Z)}`),$.forEach((Q,K)=>{let V=G.blue(` ${K+1}.`),Y=Q.desc?G.gray(` (${Q.desc})`):"";console.log(`${V} ${Q.label}${Y}`)}),console.log(` ${G.gray(" 0. Cancel")}`);while(!0){let K=(await C(`
23
+ ${G.gray("choose")} ${G.blue("❯")} `)).trim().toLowerCase();if(K==="0"||K==="q"||K==="cancel"||K==="exit"||K==="")return-1;let V=parseInt(K);if(V>=1&&V<=$.length)return V-1;console.log(` ${A.warn} enter 1-${$.length} or 0 to cancel`)}}function x(Z,$){console.log(`
24
+ ${A.tool} ${G.yellow(Z)} ${G.gray($)}`)}function M(Z){console.error(` ${A.err} ${G.pink(Z)}`)}var R0=(Z)=>`\x1B[${Z}m`,a=(Z,$)=>(Q)=>`${R0(Z)}${Q}${R0($)}`,P=(Z)=>($)=>`\x1B[38;5;${Z}m${$}\x1B[39m`,G,A,j,K1;var n=L(()=>{G={bold:a("1","22"),dim:a("2","22"),italic:a("3","23"),under:a("4","24"),blue:P(111),purple:P(141),green:P(149),yellow:P(179),pink:P(210),cyan:P(117),gray:P(60),text:P(146),orange:P(215)},A={prompt:G.blue("❯"),ai:G.purple("◆"),tool:G.yellow("⚙"),ok:G.green("✓"),err:G.pink("✗"),warn:G.yellow("⚠"),arrow:G.gray("→"),dot:G.gray("•"),spinner:["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"]};j={h:"─",v:"│",tl:"╭",tr:"╮",bl:"╰",br:"╯",line:(Z)=>"─".repeat(Z)};K1=[{cmd:"/model",desc:"switch AI model"},{cmd:"/auth",desc:"set API key"},{cmd:"/mode",desc:"toggle mode"},{cmd:"/compact",desc:"toggle compact"},{cmd:"/diff",desc:"show file changes"},{cmd:"/undo",desc:"revert last change"},{cmd:"/init",desc:"init project context"},{cmd:"/remote",desc:"start web remote"},{cmd:"/history",desc:"past conversations"},{cmd:"/quota",desc:"API rate limits"},{cmd:"/update",desc:"update tenicli"},{cmd:"/clear",desc:"clear screen"},{cmd:"/cost",desc:"show token usage"},{cmd:"/help",desc:"list commands"},{cmd:"/exit",desc:"quit"}]});import{readFileSync as V0,writeFileSync as P0,existsSync as o,readdirSync as M0,statSync as I0,mkdirSync as V1}from"fs";import{resolve as X1,relative as c,join as L0,dirname as z1}from"path";import{spawn as b0}from"child_process";class C0{writes=[];recordWrite(Z,$){let Q=o(Z)?V0(Z,"utf8"):null;this.writes.push({path:Z,backup:Q,newLines:$.split(`
25
+ `).length,time:new Date})}getChanges(){let Z=new Map;for(let $ of this.writes)Z.set($.path,{isNew:$.backup===null,lines:$.newLines,time:$.time});return Array.from(Z.entries()).map(([$,Q])=>({path:$,...Q}))}undo(){let Z=this.writes.pop();if(!Z)return null;if(Z.backup!==null)return P0(Z.path,Z.backup,"utf8"),{path:Z.path,restored:!0};else{try{u("fs").unlinkSync(Z.path)}catch{}return{path:Z.path,restored:!1}}}get count(){return this.writes.length}clear(){this.writes=[]}}async function S0(Z,$,Q){try{let K;switch(Z){case"read_file":K=Y1($,Q),x("read_file",G.dim(c(Q,S($.path,Q))));break;case"write_file":K=W1($,Q),x("write_file",G.dim(c(Q,S($.path,Q))));break;case"list_dir":K=N1($,Q),x("list_dir",G.dim($.path||"."));break;case"search_files":K=await _1($,Q),x("search_files",G.dim(`"${$.pattern}"`));break;case"exec_command":K=await U1($,Q),x("exec_command",G.dim(F0($.command,60)));break;default:K=`Unknown tool: ${Z}`}return{type:"tool_result",content:F0(K,J1)}}catch(K){return{type:"tool_result",content:`Error: ${K.message}`,is_error:!0}}}function S(Z,$){return X1($,Z)}function Y1(Z,$){let Q=S(Z.path,$);if(!o(Q))return`File not found: ${Z.path}`;let K=V0(Q,"utf8"),V=K.split(`
26
+ `);if(Z.start_line||Z.end_line){let Y=Math.max(1,Z.start_line||1)-1,X=Math.min(V.length,Z.end_line||V.length);return V.slice(Y,X).map((z,J)=>`${Y+J+1}: ${z}`).join(`
27
+ `)}if(V.length>50)return V.map((Y,X)=>`${X+1}: ${Y}`).join(`
28
+ `);return K}function W1(Z,$){let Q=S(Z.path,$),K=z1(Q);if(!o(K))V1(K,{recursive:!0});return F.recordWrite(Q,Z.content),P0(Q,Z.content,"utf8"),`Written ${Z.content.split(`
29
+ `).length} lines to ${Z.path}`}function N1(Z,$){let Q=S(Z.path||".",$);if(!o(Q))return`Directory not found: ${Z.path||"."}`;let K=Z.depth||1,V=[];function Y(X,z){if(z>K)return;try{let J=M0(X);for(let W of J){if(W.startsWith(".")||W==="node_modules")continue;let N=L0(X,W),_=c(Q,N);try{let q=I0(N),H=" ".repeat(z);if(q.isDirectory())V.push(`${H}${_}/`),Y(N,z+1);else{let U=q.size>1024?`${(q.size/1024).toFixed(1)}KB`:`${q.size}B`;V.push(`${H}${_} (${U})`)}}catch{}}}catch{}}return Y(Q,0),V.length>0?V.join(`
30
+ `):"(empty directory)"}async function _1(Z,$){let Q=S(Z.path||".",$),K=Z.pattern;try{let X=["-n","--max-count=50","--no-heading"];if(Z.include)X.push("--glob",Z.include);return X.push(K,Q),await new Promise((J,W)=>{let N=b0("rg",X,{shell:!0}),_="";N.stdout.on("data",(q)=>_+=q.toString()),N.on("close",(q)=>{if(q===0||q===1)J(_.trim()||"No matches found.");else W(Error("rg failed"))}),N.on("error",W)})}catch{}let V=[];function Y(X){try{for(let z of M0(X)){if(z.startsWith(".")||z==="node_modules")continue;let J=L0(X,z);try{let W=I0(J);if(W.isDirectory()){Y(J);continue}if(W.size>500000)continue;if(Z.include&&!q1(z,Z.include))continue;let _=V0(J,"utf8").split(`
31
+ `);for(let q=0;q<_.length;q++)if(_[q].includes(K)){if(V.push(`${c($,J)}:${q+1}: ${_[q].trim()}`),V.length>=50)return}}catch{}}}catch{}}return Y(Q),V.length>0?V.join(`
32
+ `):"No matches found."}async function U1(Z,$){let Q=S(Z.cwd||".",$),K=process.platform==="win32",V=K?"cmd":"sh",Y=K?"/c":"-c";return new Promise((X)=>{let z=b0(V,[Y,Z.command],{cwd:Q,env:{...process.env,PAGER:"cat"}}),J="",W="";z.stdout.on("data",(_)=>J+=_.toString()),z.stderr.on("data",(_)=>W+=_.toString());let N=setTimeout(()=>z.kill(),30000);z.on("close",(_)=>{clearTimeout(N);let q="";if(J.trim())q+=J.trim();if(W.trim())q+=(q?`
33
+ `:"")+`[stderr] ${W.trim()}`;q+=`
34
+ [exit code: ${_}]`,X(q)}),z.on("error",(_)=>{clearTimeout(N),X(`[error] Failed to start process: ${_.message}`)})})}function F0(Z,$){if(Z.length<=$)return Z;return Z.slice(0,$)+`
35
+ ... (truncated, ${Z.length-$} chars omitted)`}function q1(Z,$){if($.startsWith("*."))return Z.endsWith($.slice(1));return Z.includes($.replace(/\*/g,""))}var F,T0,J1=30000;var X0=L(()=>{n();F=new C0,T0=[{name:"read_file",description:"Read contents of a file. Returns the file text.",input_schema:{type:"object",properties:{path:{type:"string",description:"File path (relative to cwd or absolute)"},start_line:{type:"number",description:"Optional: start line (1-indexed)"},end_line:{type:"number",description:"Optional: end line (1-indexed, inclusive)"}},required:["path"]}},{name:"write_file",description:"Write content to a file. Creates parent directories if needed.",input_schema:{type:"object",properties:{path:{type:"string",description:"File path"},content:{type:"string",description:"Full file content to write"}},required:["path","content"]}},{name:"list_dir",description:"List files and directories in a path. Returns names with type indicators.",input_schema:{type:"object",properties:{path:{type:"string",description:"Directory path (default: cwd)"},depth:{type:"number",description:"Max depth (default: 1)"}},required:[]}},{name:"search_files",description:"Search for text in files using pattern matching (like grep). Returns matching lines with file paths.",input_schema:{type:"object",properties:{pattern:{type:"string",description:"Text or regex pattern to search for"},path:{type:"string",description:"Directory to search in (default: cwd)"},include:{type:"string",description:'Glob pattern to filter files, e.g. "*.ts"'}},required:["pattern"]}},{name:"exec_command",description:"Execute a shell command. Returns stdout and stderr.",input_schema:{type:"object",properties:{command:{type:"string",description:"Shell command to execute"},cwd:{type:"string",description:"Working directory (default: project cwd)"}},required:["command"]}}]});class w{messages=[];tokens={input:0,output:0};cfg;autoMode=!1;onOutput;onConfirm;constructor(Z){this.cfg=Z}emit(Z){this.onOutput?.(Z)}write(Z){if(this.onOutput)this.emit({type:"text",text:Z});else process.stdout.write(Z)}log(Z){if(this.onOutput)this.emit({type:"text",text:Z+`
36
+ `});else console.log(Z)}async send(Z){this.messages.push({role:"user",content:Z}),await this.agentLoop()}async agentLoop(){while(!0){let Z=await this.streamResponse();if(Z.stopReason==="error")break;if(Z.stopReason==="tool_use"){let Q=Z.content.filter((V)=>V.type==="tool_use"),K=[];for(let V of Q){if(!this.autoMode&&(V.name==="write_file"||V.name==="exec_command")){let X=V.name==="write_file"?V.input?.path:V.input?.command?.slice(0,80),z="y";if(this.onConfirm)z=await this.onConfirm(V.id,V.name,X||"");else this.log(`
37
+ ${A.warn} ${G.yellow(V.name)} ${G.gray(X||"")}`),z=(await C(` ${G.gray("allow?")} ${G.blue("[y/n/auto]")} `)).trim().toLowerCase();if(z==="auto")this.autoMode=!0;else if(z!=="y"&&z!=="yes"&&z!==""){K.push({type:"tool_result",tool_use_id:V.id,content:"User denied this action.",is_error:!0});continue}}this.emit({type:"tool",name:V.name,detail:JSON.stringify(V.input||{}).slice(0,200)});let Y=await S0(V.name,V.input,this.cfg.cwd);this.emit({type:"tool_result",name:V.name,content:(Y.content||"").slice(0,500),is_error:!!Y.is_error}),K.push({type:"tool_result",tool_use_id:V.id,content:Y.content,is_error:Y.is_error})}this.messages.push({role:"assistant",content:Z.content}),this.messages.push({role:"user",content:K});continue}let $=Z.content.filter((Q)=>Q.type==="text").map((Q)=>Q.text).join("");if($)this.messages.push({role:"assistant",content:$});break}if(this.emit({type:"tokens",input:this.tokens.input,output:this.tokens.output,messages:this.messages.length}),!this.onOutput)console.log(`
38
+ ${G.gray(`tokens: ${this.tokens.input}↑ ${this.tokens.output}↓`)}`)}async streamResponse(){let Z=!this.onOutput,$=Z?new i("Thinking").start():null,Q=[],K="",V="",Y="",X="",z="end_turn",J=!1;try{let W=G0(this.cfg.provider,this.messages,this.cfg.systemPrompt,T0,this.cfg.maxTokens);for await(let N of W)switch(N.type){case"text":if(!J){if($?.stop(),J=!0,Z)process.stdout.write(`
39
+ ${A.ai} `)}if(N.text)this.write(N.text);K+=N.text;break;case"tool_start":if(!J)$?.stop(),J=!0;if(K)Q.push({type:"text",text:K}),K="";V=N.id,Y=N.name,X="";break;case"tool_input":X+=N.partial;break;case"tool_end":if(V){let _={};try{_=JSON.parse(X)}catch{}Q.push({type:"tool_use",id:V,name:Y,input:_}),V="",X=""}break;case"usage":this.tokens.input+=N.input,this.tokens.output+=N.output;break;case"done":z=N.stopReason;break}}catch(W){if($?.stop(),Z)console.log();let N=W.message||String(W);if(this.emit({type:"error",message:N}),Z)M(N);return{content:[],stopReason:"error"}}if(K){if(Q.push({type:"text",text:K}),this.emit({type:"text_done"}),Z)process.stdout.write(`
40
+ `)}if(!J)$?.stop();return{content:Q,stopReason:z}}async compact(){if(this.messages.length<4){this.log(` ${A.warn} Not enough messages to compact.`);return}let Z=!this.onOutput?new i("Compacting").start():null;try{let $="";for(let X of this.messages)if(typeof X.content==="string")$+=`${X.role}: ${X.content.slice(0,500)}
41
+ `;else{let z=X.content.filter((W)=>W.type==="text").map((W)=>W.text?.slice(0,300)).join(" ");if(z)$+=`${X.role}: ${z}
42
+ `;let J=X.content.filter((W)=>W.type==="tool_use").map((W)=>`[tool: ${W.name}]`).join(", ");if(J)$+=` tools: ${J}
43
+ `}let Q=this.messages.length,K=`Summarize this conversation concisely. Keep key decisions, file changes, and current state. Be brief:
44
44
 
45
- ${Z.slice(0,6000)}`;this.messages=[{role:"user",content:V},{role:"assistant",content:`[Conversation compacted from ${$} messages. Summary of what happened:]`}];let K=Z0(this.cfg.provider,[{role:"user",content:V}],"You are a conversation summarizer. Create a brief summary preserving key facts, decisions, and file changes.",[],this.cfg.maxTokens),Y="";for await(let X of K){if(X.type==="text"&&X.text)Y+=X.text;if(X.type==="usage")this.tokens.input+=X.input,this.tokens.output+=X.output}this.messages=[{role:"user",content:`[Previous conversation summary]
46
- ${Y}`},{role:"assistant",content:"Understood. I have the context from our previous conversation. How can I continue helping you?"}],Q?.stop(),this.log(` ${E.ok} Compacted ${$} messages → 2 ${G.gray(`(saved ~${Math.round(Z.length/4)} tokens)`)}`)}catch(Z){Q?.stop();let $=`Compact failed: ${Z.message}`;if(this.emit({type:"error",message:$}),!this.onOutput)M($)}}get stats(){return this.tokens}get messageCount(){return this.messages.length}clear(){this.messages=[],this.tokens={input:0,output:0}}exportState(){return{messages:this.messages,tokens:{...this.tokens},autoMode:this.autoMode}}importState(Q){if(Q.messages)this.messages=Q.messages;if(Q.tokens)this.tokens=Q.tokens;if(Q.autoMode!==void 0)this.autoMode=Q.autoMode}getTitle(){let Q=this.messages.find((Z)=>Z.role==="user"&&typeof Z.content==="string");if(Q)return Q.content.slice(0,50);return"New conversation"}}var K0=P(()=>{$0();G0();a()});var v;var z0=P(()=>{v={name:"tenicli",version:"0.2.2",description:"Lightweight AI coding CLI — fast, compact, multi-provider",type:"module",bin:{teni:"./dist/index.js"},files:["dist/","README.md","LICENSE"],scripts:{dev:"bun run src/index.ts","build:npm":"bun build src/index.ts --outfile dist/index.js --target node --minify",build:"bun build --compile --minify src/index.ts --outfile teni","build:win":"bun build --compile --minify --target=bun-windows-x64 src/index.ts --outfile teni.exe","build:linux":"bun build --compile --minify --target=bun-linux-x64 src/index.ts --outfile teni","build:mac":"bun build --compile --minify --target=bun-darwin-x64 src/index.ts --outfile teni",prepublishOnly:"bun run build:npm"},keywords:["ai","cli","coding","agent","anthropic","openai","terminal"],author:"Yan Tenica",license:"MIT",repository:{type:"git",url:"https://github.com/Nhqvu2005/TeniCli.git"},homepage:"https://github.com/Nhqvu2005/TeniCli",engines:{node:">=18"},dependencies:{},devDependencies:{"@types/bun":"latest","@types/figlet":"^1.7.0",figlet:"^1.11.0"}}});var h0={};_0(h0,{renderQR:()=>H1,getLocalIPs:()=>D1});function H1(Q){let Z=[],$=Math.max(Q.length+4,30),V="██",K=" ";for(let X=0;X<7;X++){let z="";for(let J=0;J<7;J++)z+=J0.FINDER[X][J]?"██":" ";z+=" ";for(let J=0;J<$-15;J++)z+=X===6&&J%2===0?"██":X===6?" ":" ";z+=" ";for(let J=0;J<7;J++)z+=J0.FINDER[X][J]?"██":" ";Z.push(z)}Z.push(" ".repeat($/2+1));let Y=5;for(let X=0;X<Y;X++){let z="";for(let J=0;J<$+1;J++){if(J===6){z+=X%2===0?"██":" ";continue}z+=(J+X*3+J*X)%3===0?"██":" "}Z.push(z)}Z.push(" ".repeat($/2+1));for(let X=0;X<7;X++){let z="";for(let J=0;J<7;J++)z+=J0.FINDER[X][J]?"██":" ";z+=" ";for(let J=0;J<$-14;J++)z+=(J+X*5)%3===0?"██":" ";Z.push(z)}return Z.map((X)=>" "+X).join(`
47
- `)}function D1(){try{let{networkInterfaces:Q}=r("os"),Z=Q(),$=[];for(let V of Object.keys(Z))for(let K of Z[V])if(K.family==="IPv4"&&!K.internal)$.push(K.address);return $}catch{return[]}}var J0;var v0=P(()=>{J0={FINDER:[[1,1,1,1,1,1,1],[1,0,0,0,0,0,1],[1,0,1,1,1,0,1],[1,0,1,1,1,0,1],[1,0,1,1,1,0,1],[1,0,0,0,0,0,1],[1,1,1,1,1,1,1]]}});var N0={};_0(N0,{startServer:()=>M1,getActiveServers:()=>O1});import{createServer as R1}from"http";import{createHash as B1}from"crypto";function O1(){return Y0}function j1(Q){if(Q.length<2)return null;let Z=Q[0]&15,$=(Q[1]&128)!==0,V=Q[1]&127,K=2;if(V===126){if(Q.length<4)return null;V=Q.readUInt16BE(2),K=4}else if(V===127){if(Q.length<10)return null;V=Number(Q.readBigUInt64BE(2)),K=10}let X=K+($?4:0)+V;if(Q.length<X)return null;if($){let z=Q.slice(K,K+4),J=Buffer.alloc(V);for(let N=0;N<V;N++)J[N]=Q[K+4+N]^z[N%4];return{opcode:Z,payload:J,totalSize:X}}return{opcode:Z,payload:Q.slice(K,K+V),totalSize:X}}function F1(Q){let Z=Buffer.from(Q,"utf8"),$=Z.length,V;if($<126)V=Buffer.alloc(2),V[0]=129,V[1]=$;else if($<65536)V=Buffer.alloc(4),V[0]=129,V[1]=126,V.writeUInt16BE($,2);else V=Buffer.alloc(10),V[0]=129,V[1]=127,V.writeBigUInt64BE(BigInt($),2);return Buffer.concat([V,Z])}function P1(Q){let Z=Buffer.alloc(2);Z[0]=137,Z[1]=0,Q.write(Z)}function L1(Q,Z){return`<!DOCTYPE html>
45
+ ${$.slice(0,6000)}`;this.messages=[{role:"user",content:K},{role:"assistant",content:`[Conversation compacted from ${Q} messages. Summary of what happened:]`}];let V=G0(this.cfg.provider,[{role:"user",content:K}],"You are a conversation summarizer. Create a brief summary preserving key facts, decisions, and file changes.",[],this.cfg.maxTokens),Y="";for await(let X of V){if(X.type==="text"&&X.text)Y+=X.text;if(X.type==="usage")this.tokens.input+=X.input,this.tokens.output+=X.output}this.messages=[{role:"user",content:`[Previous conversation summary]
46
+ ${Y}`},{role:"assistant",content:"Understood. I have the context from our previous conversation. How can I continue helping you?"}],Z?.stop(),this.log(` ${A.ok} Compacted ${Q} messages → 2 ${G.gray(`(saved ~${Math.round($.length/4)} tokens)`)}`)}catch($){Z?.stop();let Q=`Compact failed: ${$.message}`;if(this.emit({type:"error",message:Q}),!this.onOutput)M(Q)}}get stats(){return this.tokens}get messageCount(){return this.messages.length}clear(){this.messages=[],this.tokens={input:0,output:0}}exportState(){return{messages:this.messages,tokens:{...this.tokens},autoMode:this.autoMode}}importState(Z){if(Z.messages)this.messages=Z.messages;if(Z.tokens)this.tokens=Z.tokens;if(Z.autoMode!==void 0)this.autoMode=Z.autoMode}getTitle(){let Z=this.messages.find(($)=>$.role==="user"&&typeof $.content==="string");if(Z)return Z.content.slice(0,50);return"New conversation"}}var z0=L(()=>{K0();X0();n()});var g;var Y0=L(()=>{g={name:"tenicli",version:"0.3.0",description:"Lightweight AI coding CLI — fast, compact, multi-provider",type:"module",bin:{teni:"./dist/index.js"},files:["dist/","README.md","LICENSE"],scripts:{dev:"bun run src/index.ts","build:npm":"bun build src/index.ts --outfile dist/index.js --target node --minify",build:"bun build --compile --minify src/index.ts --outfile teni","build:win":"bun build --compile --minify --target=bun-windows-x64 src/index.ts --outfile teni.exe","build:linux":"bun build --compile --minify --target=bun-linux-x64 src/index.ts --outfile teni","build:mac":"bun build --compile --minify --target=bun-darwin-x64 src/index.ts --outfile teni",prepublishOnly:"bun run build:npm"},keywords:["ai","cli","coding","agent","anthropic","openai","terminal"],author:"Yan Tenica",license:"MIT",repository:{type:"git",url:"https://github.com/Nhqvu2005/TeniCli.git"},homepage:"https://github.com/Nhqvu2005/TeniCli",engines:{node:">=18"},dependencies:{},devDependencies:{"@types/bun":"latest","@types/figlet":"^1.7.0",figlet:"^1.11.0"}}});var u0={};E0(u0,{renderQR:()=>R1,getLocalIPs:()=>O1});function R1(Z){let $=[],Q=Math.max(Z.length+4,30),K="██",V=" ";for(let X=0;X<7;X++){let z="";for(let J=0;J<7;J++)z+=W0.FINDER[X][J]?"██":" ";z+=" ";for(let J=0;J<Q-15;J++)z+=X===6&&J%2===0?"██":X===6?" ":" ";z+=" ";for(let J=0;J<7;J++)z+=W0.FINDER[X][J]?"██":" ";$.push(z)}$.push(" ".repeat(Q/2+1));let Y=5;for(let X=0;X<Y;X++){let z="";for(let J=0;J<Q+1;J++){if(J===6){z+=X%2===0?"██":" ";continue}z+=(J+X*3+J*X)%3===0?"██":" "}$.push(z)}$.push(" ".repeat(Q/2+1));for(let X=0;X<7;X++){let z="";for(let J=0;J<7;J++)z+=W0.FINDER[X][J]?"██":" ";z+=" ";for(let J=0;J<Q-14;J++)z+=(J+X*5)%3===0?"██":" ";$.push(z)}return $.map((X)=>" "+X).join(`
47
+ `)}function O1(){try{let{networkInterfaces:Z}=u("os"),$=Z(),Q=[];for(let K of Object.keys($))for(let V of $[K])if(V.family==="IPv4"&&!V.internal)Q.push(V.address);return Q}catch{return[]}}var W0;var p0=L(()=>{W0={FINDER:[[1,1,1,1,1,1,1],[1,0,0,0,0,0,1],[1,0,1,1,1,0,1],[1,0,1,1,1,0,1],[1,0,1,1,1,0,1],[1,0,0,0,0,0,1],[1,1,1,1,1,1,1]]}});var _0={};E0(_0,{startServer:()=>T1,getActiveServers:()=>P1});import{createServer as j1}from"http";import{createHash as F1}from"crypto";function P1(){return N0}function M1(Z){if(Z.length<2)return null;let $=Z[0]&15,Q=(Z[1]&128)!==0,K=Z[1]&127,V=2;if(K===126){if(Z.length<4)return null;K=Z.readUInt16BE(2),V=4}else if(K===127){if(Z.length<10)return null;K=Number(Z.readBigUInt64BE(2)),V=10}let X=V+(Q?4:0)+K;if(Z.length<X)return null;if(Q){let z=Z.slice(V,V+4),J=Buffer.alloc(K);for(let W=0;W<K;W++)J[W]=Z[V+4+W]^z[W%4];return{opcode:$,payload:J,totalSize:X}}return{opcode:$,payload:Z.slice(V,V+K),totalSize:X}}function I1(Z){let $=Buffer.from(Z,"utf8"),Q=$.length,K;if(Q<126)K=Buffer.alloc(2),K[0]=129,K[1]=Q;else if(Q<65536)K=Buffer.alloc(4),K[0]=129,K[1]=126,K.writeUInt16BE(Q,2);else K=Buffer.alloc(10),K[0]=129,K[1]=127,K.writeBigUInt64BE(BigInt(Q),2);return Buffer.concat([K,$])}function L1(Z){let $=Buffer.alloc(2);$[0]=137,$[1]=0,Z.write($)}function C1(Z,$){return`<!DOCTYPE html>
48
48
  <html lang="en">
49
49
  <head>
50
50
  <meta charset="utf-8">
@@ -112,7 +112,7 @@ ${Y}`},{role:"assistant",content:"Understood. I have the context from our previo
112
112
  <div class="sidebar">
113
113
  <div class="sidebar-header">
114
114
  <div class="dot" id="statusDot"></div>
115
- <h1>TeniCLI v${Q}</h1>
115
+ <h1>TeniCLI v${Z}</h1>
116
116
  </div>
117
117
  <div class="session-list" id="sessionList"></div>
118
118
  <div class="new-session" id="newSessionBtn">+ New Session</div>
@@ -134,7 +134,7 @@ ${Y}`},{role:"assistant",content:"Understood. I have the context from our previo
134
134
  </div>
135
135
  <script>
136
136
  (function(){
137
- var TOKEN = '${Z}';
137
+ var TOKEN = '${$}';
138
138
  var wsUrl = (location.protocol==='https:'?'wss://':'ws://') + location.host + '/ws?token=' + encodeURIComponent(TOKEN);
139
139
  var ws, sessions = {}, activeId = null, counter = 0;
140
140
 
@@ -289,44 +289,56 @@ ${Y}`},{role:"assistant",content:"Understood. I have the context from our previo
289
289
  })();
290
290
  </script>
291
291
  </body>
292
- </html>`}function M1(Q,Z){let $=m(),V=new Map,K=new Map,Y=R1((z,J)=>{if(new URL(z.url||"/",`http://${z.headers.host||"localhost"}`).pathname==="/health"){J.writeHead(200,{"content-type":"application/json","access-control-allow-origin":"*"}),J.end(JSON.stringify({status:"ok",sessions:V.size,version:v.version}));return}J.writeHead(200,{"content-type":"text/html; charset=utf-8","cache-control":"no-cache"}),J.end(L1(v.version,Z))});Y.on("upgrade",(z,J)=>{if(new URL(z.url||"/",`http://${z.headers.host||"localhost"}`).searchParams.get("token")!==Z){J.write(`HTTP/1.1 401 Unauthorized\r
292
+ </html>`}function T1(Z,$){let Q=l(),K=new Map,V=new Map,Y=j1((z,J)=>{if(new URL(z.url||"/",`http://${z.headers.host||"localhost"}`).pathname==="/health"){J.writeHead(200,{"content-type":"application/json","access-control-allow-origin":"*"}),J.end(JSON.stringify({status:"ok",sessions:K.size,version:g.version}));return}J.writeHead(200,{"content-type":"text/html; charset=utf-8","cache-control":"no-cache"}),J.end(C1(g.version,$))});Y.on("upgrade",(z,J)=>{if(new URL(z.url||"/",`http://${z.headers.host||"localhost"}`).searchParams.get("token")!==$){J.write(`HTTP/1.1 401 Unauthorized\r
293
293
  \r
294
- `),J.destroy();return}let _=z.headers["sec-websocket-key"];if(!_){J.destroy();return}let q=B1("sha1").update(_+"258EAFA5-E914-47DA-95CA-5ABB5C0A2C15").digest("base64");J.write(`HTTP/1.1 101 Switching Protocols\r
294
+ `),J.destroy();return}let _=z.headers["sec-websocket-key"];if(!_){J.destroy();return}let q=F1("sha1").update(_+"258EAFA5-E914-47DA-95CA-5ABB5C0A2C15").digest("base64");J.write(`HTTP/1.1 101 Switching Protocols\r
295
295
  Upgrade: websocket\r
296
296
  Connection: Upgrade\r
297
297
  Sec-WebSocket-Accept: `+q+`\r
298
298
  \r
299
- `);let A=Buffer.alloc(0),U=(R)=>{try{J.write(F1(JSON.stringify(R)))}catch{}};J.on("data",(R)=>{A=Buffer.concat([A,R]);while(A.length>0){let H=j1(A);if(!H)break;if(A=A.slice(H.totalSize),H.opcode===8){J.end();return}if(H.opcode===9){let B=Buffer.alloc(2+H.payload.length);B[0]=138,B[1]=H.payload.length,H.payload.copy(B,2),J.write(B);continue}if(H.opcode===10)continue;try{let B=JSON.parse(H.payload.toString("utf8"));I1(B,U,V,K,$)}catch{}}});let D=setInterval(()=>{try{P1(J)}catch{clearInterval(D)}},30000);J.on("close",()=>{clearInterval(D);for(let[R,H]of V)if(H.send===U)V.delete(R)}),J.on("error",()=>{try{J.destroy()}catch{}})});let X=()=>{Y.close(),Y0.delete(Q)};return Y.listen(Q,"0.0.0.0",()=>{Y0.set(Q,{password:Z,close:X})}),{close:X}}function I1(Q,Z,$,V,K){switch(Q.type){case"new_session":{let Y={...K,provider:{...K.provider}},X=new y(Y);X.autoMode=!0,X.onOutput=(z)=>{Z({...z,sessionId:Q.sessionId})},X.onConfirm=(z,J,N)=>{return new Promise((W)=>{Z({type:"confirm",sessionId:Q.sessionId,id:z,tool:J,preview:N}),V.set(z,W),setTimeout(()=>{if(V.has(z))V.delete(z),W("y")},60000)})},$.set(Q.sessionId,{id:Q.sessionId,chat:X,send:Z,alive:!0});break}case"close_session":$.delete(Q.sessionId);break;case"message":{let Y=$.get(Q.sessionId);if(!Y)return;let X=Q.text?.trim();if(!X)return;if(X==="/clear"){Y.chat.clear(),Z({type:"system",sessionId:Q.sessionId,text:"Conversation cleared"});return}if(X==="/compact"){Y.chat.compact();return}if(X==="/cost"){let z=Y.chat.stats;Z({type:"tokens",sessionId:Q.sessionId,input:z.input,output:z.output,messages:Y.chat.messageCount});return}Y.chat.send(X).catch((z)=>{Z({type:"error",sessionId:Q.sessionId,message:z.message})});break}case"confirm_response":{let Y=V.get(Q.id);if(Y)V.delete(Q.id),Y(Q.answer);break}}}var Y0;var W0=P(()=>{K0();e();z0();Y0=new Map});e();K0();$0();G0();a();import{writeFileSync as C1,existsSync as T1}from"fs";import{join as S1,relative as f0}from"path";import{randomBytes as g0}from"crypto";import{existsSync as T0,readFileSync as S0,writeFileSync as b0,mkdirSync as _1,readdirSync as U1,unlinkSync as k0}from"fs";import{join as b}from"path";var C0=20;function h(){let Q=process.env.HOME||process.env.USERPROFILE||"",Z=b(Q,".tenicli","history");if(!T0(Z))_1(Z,{recursive:!0});return Z}function q1(){return Date.now().toString(36)+Math.random().toString(36).slice(2,6)}function x0(Q){let Z=b(h(),`${Q.id}.json`);b0(Z,JSON.stringify(Q,null,0),"utf8"),E1()}function X0(){let Q=h();try{return U1(Q).filter((Z)=>Z.endsWith(".json")).map((Z)=>{try{return JSON.parse(S0(b(Q,Z),"utf8"))}catch{return null}}).filter((Z)=>Z!==null).sort((Z,$)=>$.updatedAt.localeCompare(Z.updatedAt))}catch{return[]}}function E1(){let Q=X0();if(Q.length<=C0)return;let Z=h();for(let $ of Q.slice(C0))try{k0(b(Z,`${$.id}.json`))}catch{}}function n(Q){let Z=new Date().toISOString();return{id:q1(),title:"New conversation",model:Q,createdAt:Z,updatedAt:Z,messages:[],tokens:{input:0,output:0}}}function w0(Q){let Z=b(h(),"__resume__.json");return b0(Z,JSON.stringify(Q,null,0),"utf8"),Z}function y0(){let Q=b(h(),"__resume__.json");try{if(T0(Q)){let Z=JSON.parse(S0(Q,"utf8"));return k0(Q),Z}}catch{}return null}z0();var u0=v.version;function b1(Q){let Z={prompt:"",print:!1},$=0;while($<Q.length){switch(Q[$]){case"serve":Z.serve=!0;break;case"--port":Z.port=parseInt(Q[++$]);break;case"--password":Z.password=Q[++$];break;case"-p":case"--print":if(Z.print=!0,$+1<Q.length&&!Q[$+1].startsWith("-"))Z.prompt=Q[++$];break;case"-m":case"--model":Z.model=Q[++$];break;case"--base-url":Z.baseUrl=Q[++$];break;case"-v":case"--version":console.log(`teni v${u0}`),process.exit(0);case"-h":case"--help":k1(),process.exit(0);default:if(!Q[$].startsWith("-"))Z.prompt=Q.slice($).join(" "),$=Q.length}$++}return Z}function k1(){console.log(`
300
- ${G.bold(G.blue("TeniCLI"))} — Lightweight AI Coding Agent
299
+ `);let H=Buffer.alloc(0),U=(R)=>{try{J.write(I1(JSON.stringify(R)))}catch{}};J.on("data",(R)=>{H=Buffer.concat([H,R]);while(H.length>0){let E=M1(H);if(!E)break;if(H=H.slice(E.totalSize),E.opcode===8){J.end();return}if(E.opcode===9){let B=Buffer.alloc(2+E.payload.length);B[0]=138,B[1]=E.payload.length,E.payload.copy(B,2),J.write(B);continue}if(E.opcode===10)continue;try{let B=JSON.parse(E.payload.toString("utf8"));S1(B,U,K,V,Q)}catch{}}});let D=setInterval(()=>{try{L1(J)}catch{clearInterval(D)}},30000);J.on("close",()=>{clearInterval(D);for(let[R,E]of K)if(E.send===U)K.delete(R)}),J.on("error",()=>{try{J.destroy()}catch{}})});let X=()=>{Y.close(),N0.delete(Z)};return Y.listen(Z,"0.0.0.0",()=>{N0.set(Z,{password:$,close:X})}),{close:X}}function S1(Z,$,Q,K,V){switch(Z.type){case"new_session":{let Y={...V,provider:{...V.provider}},X=new w(Y);X.autoMode=!0,X.onOutput=(z)=>{$({...z,sessionId:Z.sessionId})},X.onConfirm=(z,J,W)=>{return new Promise((N)=>{$({type:"confirm",sessionId:Z.sessionId,id:z,tool:J,preview:W}),K.set(z,N),setTimeout(()=>{if(K.has(z))K.delete(z),N("y")},60000)})},Q.set(Z.sessionId,{id:Z.sessionId,chat:X,send:$,alive:!0});break}case"close_session":Q.delete(Z.sessionId);break;case"message":{let Y=Q.get(Z.sessionId);if(!Y)return;let X=Z.text?.trim();if(!X)return;if(X==="/clear"){Y.chat.clear(),$({type:"system",sessionId:Z.sessionId,text:"Conversation cleared"});return}if(X==="/compact"){Y.chat.compact();return}if(X==="/cost"){let z=Y.chat.stats;$({type:"tokens",sessionId:Z.sessionId,input:z.input,output:z.output,messages:Y.chat.messageCount});return}Y.chat.send(X).catch((z)=>{$({type:"error",sessionId:Z.sessionId,message:z.message})});break}case"confirm_response":{let Y=K.get(Z.id);if(Y)K.delete(Z.id),Y(Z.answer);break}}}var N0;var U0=L(()=>{z0();$0();Y0();N0=new Map});$0();z0();K0();X0();n();import{writeFileSync as b1,existsSync as k1}from"fs";import{join as x1,relative as I}from"path";import{randomBytes as m0}from"crypto";import{existsSync as x0,readFileSync as w0,writeFileSync as y0,mkdirSync as A1,readdirSync as E1,unlinkSync as h0}from"fs";import{join as y}from"path";var k0=20;function f(){let Z=process.env.HOME||process.env.USERPROFILE||"",$=y(Z,".tenicli","history");if(!x0($))A1($,{recursive:!0});return $}function H1(){return Date.now().toString(36)+Math.random().toString(36).slice(2,6)}function v0(Z){let $=y(f(),`${Z.id}.json`);y0($,JSON.stringify(Z,null,0),"utf8"),D1()}function J0(){let Z=f();try{return E1(Z).filter(($)=>$.endsWith(".json")).map(($)=>{try{return JSON.parse(w0(y(Z,$),"utf8"))}catch{return null}}).filter(($)=>$!==null).sort(($,Q)=>Q.updatedAt.localeCompare($.updatedAt))}catch{return[]}}function D1(){let Z=J0();if(Z.length<=k0)return;let $=f();for(let Q of Z.slice(k0))try{h0(y($,`${Q.id}.json`))}catch{}}function r(Z){let $=new Date().toISOString();return{id:H1(),title:"New conversation",model:Z,createdAt:$,updatedAt:$,messages:[],tokens:{input:0,output:0}}}function f0(Z){let $=y(f(),"__resume__.json");return y0($,JSON.stringify(Z,null,0),"utf8"),$}function g0(){let Z=y(f(),"__resume__.json");try{if(x0(Z)){let $=JSON.parse(w0(Z,"utf8"));return h0(Z),$}}catch{}return null}Y0();var q0=g.version;function w1(Z){let $={command:"chat",prompt:"",print:!1,json:!1,yes:!1,quiet:!1},Q=0;if(Z[0]&&!Z[0].startsWith("-")){let K=Z[0].toLowerCase();if(["run","diff","undo","log","chat","serve"].includes(K))$.command=K,Q=1}while(Q<Z.length){switch(Z[Q]){case"serve":$.command="serve";break;case"--port":$.port=parseInt(Z[++Q]);break;case"--password":$.password=Z[++Q];break;case"-p":case"--print":if($.print=!0,$.command="run",Q+1<Z.length&&!Z[Q+1].startsWith("-"))$.prompt=Z[++Q];break;case"-m":case"--model":$.model=Z[++Q];break;case"--base-url":$.baseUrl=Z[++Q];break;case"--json":$.json=!0;break;case"-y":case"--yes":$.yes=!0;break;case"-q":case"--quiet":$.quiet=!0;break;case"--all":$.all=!0;break;case"-v":case"--version":console.log(`teni v${q0}`),process.exit(0);case"-h":case"--help":y1(),process.exit(0);default:if(!Z[Q].startsWith("-"))$.prompt=Z.slice(Q).join(" "),Q=Z.length}Q++}return $}function y1(){console.log(`
300
+ ${G.bold(G.blue("TeniCLI"))} v${q0} — Lightweight AI Coding Agent
301
301
 
302
- ${G.bold("USAGE")}
303
- teni Start chatting
304
- teni "prompt" Start with a prompt
305
- teni -p "prompt" Non-interactive mode
306
- teni serve Start web remote server
302
+ ${G.bold("COMMANDS")}
303
+ teni Interactive chat (default)
304
+ teni chat Same as above
305
+ teni run "<prompt>" Non-interactive: execute and exit
306
+ teni diff Show files changed by AI in this session
307
+ teni undo [--all] Revert last AI file change (or all)
308
+ teni log List AI actions with timestamps
309
+ teni serve Start web remote server
307
310
 
308
- ${G.bold("OPTIONS")}
309
- -p, --print <prompt> Print response and exit
310
- -m, --model <model> Override model
311
- --base-url <url> Override API base URL
312
- -v, --version Show version
313
- -h, --help Show help
311
+ ${G.bold("GLOBAL FLAGS")}
312
+ -m, --model <model> Override model
313
+ --base-url <url> Override API base URL
314
+ --json Machine-readable JSON output
315
+ -y, --yes Skip all confirmation prompts
316
+ -q, --quiet Suppress non-essential output
317
+ -v, --version Show version
318
+ -h, --help Show help
314
319
 
315
- ${G.bold("SERVE OPTIONS")}
316
- --port <port> Server port (default: 3000)
317
- --password <pw> Access password (auto-generated if omitted)
320
+ ${G.bold("SERVE FLAGS")}
321
+ --port <port> Server port (default: 3000)
322
+ --password <pw> Access password (auto-generated if omitted)
318
323
 
319
- ${G.bold("IN-CHAT")}
320
- /model Select model /auth Set API key
321
- /mode Ask/Auto toggle /compact Summarize chat
322
- /diff Files changed /undo Revert last write
323
- /init Create TENICLI.md /clear New conversation
324
- /update Update tenicli /cost Token usage
325
- /exit Quit \\\\ Multiline input
326
- `)}async function x1(Q,Z){switch(Q.toLowerCase().split(" ")[0]){case"/exit":case"/quit":case"/q":console.log(`
324
+ ${G.bold("IN-CHAT COMMANDS")}
325
+ /model Change model /auth Set API key
326
+ /mode Ask/Auto toggle /compact Summarize chat
327
+ /diff Files changed /undo Revert last write
328
+ /init Create TENICLI.md /clear New conversation
329
+ /update Update tenicli /cost Token usage
330
+ /remote Remote access /quota API rate limits
331
+ /exit Quit
332
+
333
+ ${G.bold("EXIT CODES")}
334
+ 0 Success
335
+ 1 API/runtime error
336
+ 2 User abort
337
+ 3 Tool execution failure
338
+ `)}async function h1(Z,$){switch(Z.toLowerCase().split(" ")[0]){case"/exit":case"/quit":case"/q":console.log(`
327
339
  ${G.gray("Bye!")} \uD83D\uDC4B
328
- `),process.exit(0);case"/clear":return Z.clear(),w.clear(),console.log(` ${E.ok} Conversation cleared`),!0;case"/compact":return await Z.compact(),!0;case"/diff":{let $=w.getChanges();if($.length===0)console.log(` ${G.gray("No files changed in this session.")}`);else{console.log(`
329
- ${G.bold("Files changed this session:")}`);for(let V of $){let K=f0(Z.cfg.cwd,V.path),Y=V.isNew?G.green("[NEW]"):G.yellow("[MOD]");console.log(` ${Y} ${G.cyan(K)} ${G.gray(`(${V.lines} lines)`)}`)}console.log(` ${G.gray(`total: ${$.length} files`)}`)}return!0}case"/undo":{let $=w.undo();if(!$)console.log(` ${G.gray("Nothing to undo.")}`);else{let V=f0(Z.cfg.cwd,$.path);if($.restored)console.log(` ${E.ok} Restored: ${G.cyan(V)}`);else console.log(` ${E.ok} Deleted (was new): ${G.cyan(V)}`)}return!0}case"/init":{let $=S1(Z.cfg.cwd,"TENICLI.md");if(T1($))console.log(` ${E.warn} TENICLI.md already exists.`);else C1($,y1,"utf8"),console.log(` ${E.ok} Created ${G.cyan("TENICLI.md")}`);return!0}case"/mode":{Z.autoMode=!Z.autoMode;let $=Z.autoMode?G.yellow("auto"):G.green("ask");return console.log(` ${E.ok} Mode: ${$} ${G.gray(Z.autoMode?"(tools run without asking)":"(confirm write/exec)")}`),!0}case"/cost":{let $=Z.stats;return console.log(` ${E.ai} ${G.blue(String($.input))}↑ input ${G.blue(String($.output))}↓ output ${G.gray(`(${Z.messageCount} msgs)`)}`),!0}case"/model":{let $=u(),V=$.customModels||[],K=[...k.map((z)=>({id:z.id,name:z.name,provider:z.provider,speed:z.speed,custom:!1})),...V.map((z)=>({id:z.id,name:z.id,provider:z.provider,speed:"custom",custom:!0}))],Y=K.map((z)=>({label:`${z.name} ${Z.cfg.provider.model===z.id?G.green("●"):""}`,desc:`${z.provider} • ${z.speed}`}));Y.push({label:"Custom model...",desc:"type model ID"});let X=await T("Select model",Y);if(X===-1)return console.log(` ${G.gray("Cancelled")}`),!0;if(X<K.length){let z=K[X];Z.cfg.provider.model=z.id,Z.cfg.provider.type=z.provider;let J=z.provider==="openai"?process.env.OPENAI_API_KEY||$.keys?.openai||"":process.env.ANTHROPIC_API_KEY||$.keys?.anthropic||"";if(J)Z.cfg.provider.apiKey=J;if(!$.baseUrls?.[z.provider])Z.cfg.provider.baseUrl=z.provider==="openai"?"https://api.openai.com":"https://api.anthropic.com";p({activeModel:z.id}),console.log(` ${E.ok} Model: ${G.blue(z.name)}`)}else{let z=await T("Provider for custom model",[{label:"Anthropic",desc:"Claude-compatible"},{label:"OpenAI",desc:"GPT-compatible"}]);if(z===-1)return console.log(` ${G.gray("Cancelled")}`),!0;let J=z===0?"anthropic":"openai",N=await L(` ${G.gray("model ID")} ${G.blue("❯")} `);if(!N.trim())return console.log(` ${G.gray("Cancelled")}`),!0;let W=N.trim();Z.cfg.provider.model=W,Z.cfg.provider.type=J;let _=V.filter((A)=>A.id!==W);_.push({id:W,provider:J}),p({activeModel:W,customModels:_});let q=J==="openai"?process.env.OPENAI_API_KEY||$.keys?.openai||"":process.env.ANTHROPIC_API_KEY||$.keys?.anthropic||"";if(q)Z.cfg.provider.apiKey=q;console.log(` ${E.ok} Model: ${G.blue(W)} ${G.gray("(saved to list)")}`)}return!0}case"/auth":{let $=await T("Provider",[{label:"Anthropic",desc:"Claude models"},{label:"OpenAI",desc:"GPT models"},{label:"Custom",desc:"Anthropic-compatible proxy"}]);if($===-1)return console.log(` ${G.gray("Cancelled")}`),!0;let K=["anthropic","openai","anthropic"][$],Y=await L(` ${G.gray("API Key")} ${G.blue("❯")} `);if(!Y.trim())return console.log(` ${E.warn} Cancelled`),!0;let X={[K]:Y.trim()},z={};if($===2){let J=await L(` ${G.gray("Base URL")} ${G.blue("❯")} `);if(J.trim())z[K]=J.trim()}if(p({keys:X,baseUrls:z}),Z.cfg.provider.apiKey=Y.trim(),Z.cfg.provider.type=K,z[K])Z.cfg.provider.baseUrl=z[K];return console.log(` ${E.ok} ${K} key saved to ~/.tenicli/config.json`),!0}case"/help":return console.log(`
340
+ `),process.exit(0);case"/clear":return $.clear(),F.clear(),console.log(` ${A.ok} Conversation cleared`),!0;case"/compact":return await $.compact(),!0;case"/diff":{let Q=F.getChanges();if(Q.length===0)console.log(` ${G.gray("No files changed in this session.")}`);else{console.log(`
341
+ ${G.bold("Files changed this session:")}`);for(let K of Q){let V=I($.cfg.cwd,K.path),Y=K.isNew?G.green("[NEW]"):G.yellow("[MOD]");console.log(` ${Y} ${G.cyan(V)} ${G.gray(`(${K.lines} lines)`)}`)}console.log(` ${G.gray(`total: ${Q.length} files`)}`)}return!0}case"/undo":{let Q=F.undo();if(!Q)console.log(` ${G.gray("Nothing to undo.")}`);else{let K=I($.cfg.cwd,Q.path);if(Q.restored)console.log(` ${A.ok} Restored: ${G.cyan(K)}`);else console.log(` ${A.ok} Deleted (was new): ${G.cyan(K)}`)}return!0}case"/init":{let Q=x1($.cfg.cwd,"TENICLI.md");if(k1(Q))console.log(` ${A.warn} TENICLI.md already exists.`);else b1(Q,f1,"utf8"),console.log(` ${A.ok} Created ${G.cyan("TENICLI.md")}`);return!0}case"/mode":{$.autoMode=!$.autoMode;let Q=$.autoMode?G.yellow("auto"):G.green("ask");return console.log(` ${A.ok} Mode: ${Q} ${G.gray($.autoMode?"(tools run without asking)":"(confirm write/exec)")}`),!0}case"/cost":{let Q=$.stats;return console.log(` ${A.ai} ${G.blue(String(Q.input))}↑ input ${G.blue(String(Q.output))}↓ output ${G.gray(`(${$.messageCount} msgs)`)}`),!0}case"/model":{let Q=m(),K=Q.customModels||[],V=[...h.map((z)=>({id:z.id,name:z.name,provider:z.provider,speed:z.speed,custom:!1})),...K.map((z)=>({id:z.id,name:z.id,provider:z.provider,speed:"custom",custom:!0}))],Y=V.map((z)=>({label:`${z.name} ${$.cfg.provider.model===z.id?G.green("●"):""}`,desc:`${z.provider} • ${z.speed}`}));Y.push({label:"Custom model...",desc:"type model ID"});let X=await T("Select model",Y);if(X===-1)return console.log(` ${G.gray("Cancelled")}`),!0;if(X<V.length){let z=V[X];$.cfg.provider.model=z.id,$.cfg.provider.type=z.provider;let J=z.provider==="openai"?process.env.OPENAI_API_KEY||Q.keys?.openai||"":process.env.ANTHROPIC_API_KEY||Q.keys?.anthropic||"";if(J)$.cfg.provider.apiKey=J;if(!Q.baseUrls?.[z.provider])$.cfg.provider.baseUrl=z.provider==="openai"?"https://api.openai.com":"https://api.anthropic.com";d({activeModel:z.id}),console.log(` ${A.ok} Model: ${G.blue(z.name)}`)}else{let z=await T("Provider for custom model",[{label:"Anthropic",desc:"Claude-compatible"},{label:"OpenAI",desc:"GPT-compatible"}]);if(z===-1)return console.log(` ${G.gray("Cancelled")}`),!0;let J=z===0?"anthropic":"openai",W=await C(` ${G.gray("model ID")} ${G.blue("❯")} `);if(!W.trim())return console.log(` ${G.gray("Cancelled")}`),!0;let N=W.trim();$.cfg.provider.model=N,$.cfg.provider.type=J;let _=K.filter((H)=>H.id!==N);_.push({id:N,provider:J}),d({activeModel:N,customModels:_});let q=J==="openai"?process.env.OPENAI_API_KEY||Q.keys?.openai||"":process.env.ANTHROPIC_API_KEY||Q.keys?.anthropic||"";if(q)$.cfg.provider.apiKey=q;console.log(` ${A.ok} Model: ${G.blue(N)} ${G.gray("(saved to list)")}`)}return!0}case"/auth":{let Q=await T("Provider",[{label:"Anthropic",desc:"Claude models"},{label:"OpenAI",desc:"GPT models"},{label:"Custom",desc:"Anthropic-compatible proxy"}]);if(Q===-1)return console.log(` ${G.gray("Cancelled")}`),!0;let V=["anthropic","openai","anthropic"][Q],Y=await C(` ${G.gray("API Key")} ${G.blue("❯")} `);if(!Y.trim())return console.log(` ${A.warn} Cancelled`),!0;let X={[V]:Y.trim()},z={};if(Q===2){let J=await C(` ${G.gray("Base URL")} ${G.blue("❯")} `);if(J.trim())z[V]=J.trim()}if(d({keys:X,baseUrls:z}),$.cfg.provider.apiKey=Y.trim(),$.cfg.provider.type=V,z[V])$.cfg.provider.baseUrl=z[V];return console.log(` ${A.ok} ${V} key saved to ~/.tenicli/config.json`),!0}case"/help":return console.log(`
330
342
  ${G.bold("Commands")}
331
343
  ${G.blue("/model")} Select AI model
332
344
  ${G.blue("/auth")} Set API key
@@ -342,14 +354,20 @@ ${G.bold("IN-CHAT")}
342
354
  ${G.blue("/clear")} New conversation
343
355
  ${G.blue("/cost")} Show token usage
344
356
  ${G.blue("/exit")} Quit
345
- ${G.gray("\\\\")} Continue on next line`),!0;case"/remote":{let{getLocalIPs:$}=await Promise.resolve().then(() => (v0(),h0)),{startServer:V,getActiveServers:K}=await Promise.resolve().then(() => (W0(),N0)),Y=K();if(Y.size>0){let _=Array.from(Y.entries()).map(([A,U])=>({label:`Port ${A} ${G.green("●")}`,desc:`password: ${U.password}`}));_.push({label:G.cyan("+ New server"),desc:"start on random port"}),_.push({label:G.yellow("Stop all"),desc:"close all remote servers"});let q=await T("Remote servers",_);if(q===-1)return console.log(` ${G.gray("Cancelled")}`),!0;if(q<Y.size){let A=Array.from(Y.entries())[q],D=$()[0]||"localhost";return console.log(`
346
- ${G.gray("URL:")} ${G.cyan(`http://${D}:${A[0]}`)}`),console.log(` ${G.gray("Password:")} ${G.yellow(A[1].password)}`),!0}else if(q===Y.size);else{for(let[A,U]of Y)U.close();return console.log(` ${E.ok} All remote servers stopped`),!0}}let X=3000+Math.floor(Math.random()*7000),z=g0(6).toString("hex"),W=`http://${$()[0]||"localhost"}:${X}`;return V(X,z),console.log(),x([G.bold(G.green("Remote Access Enabled")),"",`${G.gray("URL:")} ${G.cyan(W)}`,`${G.gray("Password:")} ${G.yellow(z)}`,`${G.gray("Port:")} ${G.blue(String(X))}`,"",G.gray("Anyone on the same WiFi can access this URL."),G.gray("Use /remote again to manage servers.")],58),console.log(),!0}case"/quota":{let $=Q0;if(!$.requestsLimit&&!$.tokensLimit)return console.log(` ${G.gray("No rate limit data yet. Send a message first.")}`),!0;let V=[G.bold("API Rate Limits"),""];if($.requestsLimit!==void 0){let K=$.requestsLimit-($.requestsRemaining||0),Y=Math.round(($.requestsRemaining||0)/$.requestsLimit*100),X=Y>50?G.green:Y>20?G.yellow:G.pink;V.push(`${G.gray("Requests:")} ${X(String($.requestsRemaining))}/${$.requestsLimit} remaining ${G.gray(`(${Y}%)`)}`)}if($.tokensLimit!==void 0){let K=Math.round(($.tokensRemaining||0)/$.tokensLimit*100),Y=K>50?G.green:K>20?G.yellow:G.pink;V.push(`${G.gray("Tokens:")} ${Y(String($.tokensRemaining?.toLocaleString()))}/${$.tokensLimit.toLocaleString()} remaining ${G.gray(`(${K}%)`)}`)}if($.requestsReset){let K=new Date($.requestsReset);V.push(`${G.gray("Resets at:")} ${G.cyan(K.toLocaleTimeString())}`)}return console.log(),x(V,58),!0}case"/history":{let $=X0();if($.length===0)return console.log(` ${G.gray("No saved conversations.")}`),!0;let V=$.slice(0,10).map((X)=>({label:X.title.slice(0,40),desc:`${X.model} • ${new Date(X.updatedAt).toLocaleDateString()}`})),K=await T("Resume conversation",V);if(K===-1)return console.log(` ${G.gray("Cancelled")}`),!0;let Y=$[K];return Z.clear(),Z.importState({messages:Y.messages,tokens:Y.tokens}),Z.__convId=Y.id,console.log(` ${E.ok} Restored: ${G.blue(Y.title)} ${G.gray(`(${Y.messages.length} msgs)`)}`),!0}case"/update":{console.log(`
347
- ${E.tool} ${G.yellow("Updating tenicli...")}`);try{let $=Z.exportState(),V=n(Z.cfg.provider.model);V.title=Z.getTitle(),V.messages=$.messages,V.tokens=$.tokens,w0(V);let{execSync:K,spawn:Y}=await import("child_process");return K("npm i -g tenicli@latest 2>&1",{encoding:"utf8"}),console.log(` ${E.ok} ${G.green("Updated! Restarting...")}
348
- `),Y(process.execPath,process.argv.slice(1),{stdio:"inherit",detached:!1}).on("exit",(z)=>process.exit(z||0)),!0}catch($){M(`Update failed: ${$.message}`)}return!0}default:return console.log(` ${E.warn} Unknown: ${Q.split(" ")[0]} — try /help`),!0}}async function w1(){let Q=b1(process.argv.slice(2)),Z=m();if(Q.model)Z.provider.model=Q.model;if(Q.baseUrl)Z.provider.baseUrl=Q.baseUrl;if(Q.serve){let N=Q.port||3000,W=Q.password||g0(6).toString("hex"),{startServer:_}=await Promise.resolve().then(() => (W0(),N0));_(N,W);return}let $=new y(Z);if(Q.print&&Q.prompt){if(!Z.provider.apiKey)M("No API key. Run: teni then /auth"),process.exit(1);await $.send(Q.prompt),process.exit(0)}H0(u0);let V=k.find((N)=>N.id===Z.provider.model)?.name||Z.provider.model,K=$.autoMode?G.yellow("auto"):G.green("ask"),Y=[`${G.gray("model")} ${G.blue(V)} ${G.gray("mode")} ${K} ${G.gray("cwd")} ${G.cyan(Z.cwd)}`];if(!Z.provider.apiKey)Y.push(""),Y.push(`${E.warn} ${G.yellow("No API key configured. Run /auth to set one.")}`);x(Y,60),console.log();let X=y0(),z=n(Z.provider.model);if(X)$.importState({messages:X.messages,tokens:X.tokens}),z=X,console.log(` ${E.ok} ${G.green("Session restored after update")} ${G.gray(`(${X.messages.length} msgs)`)}`),console.log();let J=()=>{if($.messageCount===0)return;let N=$.exportState();z.title=$.getTitle(),z.messages=N.messages,z.tokens=N.tokens,z.model=Z.provider.model,z.updatedAt=new Date().toISOString(),x0(z)};if(process.on("SIGINT",()=>{J(),console.log(`
357
+ ${G.gray("\\\\")} Continue on next line`),!0;case"/remote":{let{getLocalIPs:Q,renderQR:K}=await Promise.resolve().then(() => (p0(),u0)),{startServer:V,getActiveServers:Y}=await Promise.resolve().then(() => (U0(),_0)),X=Y();if(X.size>0){let q=Array.from(X.entries()).map(([U,D])=>({label:`Port ${U} ${G.green("●")}`,desc:`password: ${D.password}`}));q.push({label:G.cyan("+ New server"),desc:"start on random port"}),q.push({label:G.yellow("Stop all"),desc:"close all remote servers"});let H=await T("Remote servers",q);if(H===-1)return console.log(` ${G.gray("Cancelled")}`),!0;if(H<X.size){let U=Array.from(X.entries())[H],D=U[0],R=await T(`Server on port ${D}`,[{label:"Show QR & Local URL",desc:"display QR code for Wi-Fi access"},{label:"Publish to Internet",desc:"use localtunnel (free public URL)"},{label:"Stop server",desc:"close this port"}]);if(R===0){let O=`http://${Q()[0]||"localhost"}:${D}`;console.log(`
358
+ ${G.gray("URL:")} ${G.cyan(O)}`),console.log(` ${G.gray("Password:")} ${G.yellow(U[1].password)}
359
+ `),console.log(K(O)),console.log()}else if(R===1){console.log(`
360
+ ${A.tool} ${G.yellow("Starting localtunnel...")}`);try{let{spawn:E}=await import("child_process"),B=E(/^win/.test(process.platform)?"npx.cmd":"npx",["--yes","localtunnel","--port",String(D)]);B.unref(),await new Promise((O,b)=>{let t=setTimeout(()=>{b(Error("Localtunnel timed out"))},1e4);B.stdout.on("data",(d0)=>{let A0=d0.toString();if(A0.includes("your url is:")){clearTimeout(t);let l0=A0.split("your url is:")[1].trim();console.log(` ${A.ok} ${G.green("Published to Internet!")}
361
+ `),console.log(` ${G.gray("Public URL:")} ${G.cyan(l0)}`),console.log(` ${G.gray("Password:")} ${G.yellow(U[1].password)}
362
+ `),console.log(` ${G.gray("Note: The tunnel runs in the background.")}`),O()}}),B.stderr.on("data",()=>{})}),U[1].tunnel=B}catch(E){console.log(` ${A.warn} ${G.pink(`Failed to publish: ${E.message}`)}`)}}else if(R===2){if(U[1].close(),U[1].tunnel)try{U[1].tunnel.kill()}catch{}console.log(` ${A.ok} Stopped server on port ${D}`)}return!0}else if(H===X.size);else{for(let[U,D]of X)if(D.close(),D.tunnel)try{D.tunnel.kill()}catch{}return console.log(` ${A.ok} All remote servers stopped`),!0}}let z=3000+Math.floor(Math.random()*7000),J=m0(6).toString("hex"),_=`http://${Q()[0]||"localhost"}:${z}`;return V(z,J),console.log(),v([G.bold(G.green("Remote Access Enabled")),"",`${G.gray("URL:")} ${G.cyan(_)}`,`${G.gray("Password:")} ${G.yellow(J)}`,`${G.gray("Port:")} ${G.blue(String(z))}`,"",G.gray("Use /remote again to manage or publish this server.")],58),console.log(),console.log(K(_)),console.log(),!0}case"/quota":{let Q=Q0;if(!Q.requestsLimit&&!Q.tokensLimit)return console.log(` ${G.gray("No rate limit data yet. Send a message first.")}`),!0;let K=[G.bold("API Rate Limits"),""];if(Q.requestsLimit!==void 0){let V=Q.requestsLimit-(Q.requestsRemaining||0),Y=Math.round((Q.requestsRemaining||0)/Q.requestsLimit*100),X=Y>50?G.green:Y>20?G.yellow:G.pink;K.push(`${G.gray("Requests:")} ${X(String(Q.requestsRemaining))}/${Q.requestsLimit} remaining ${G.gray(`(${Y}%)`)}`)}if(Q.tokensLimit!==void 0){let V=Math.round((Q.tokensRemaining||0)/Q.tokensLimit*100),Y=V>50?G.green:V>20?G.yellow:G.pink;K.push(`${G.gray("Tokens:")} ${Y(String(Q.tokensRemaining?.toLocaleString()))}/${Q.tokensLimit.toLocaleString()} remaining ${G.gray(`(${V}%)`)}`)}if(Q.requestsReset){let V=new Date(Q.requestsReset);K.push(`${G.gray("Resets at:")} ${G.cyan(V.toLocaleTimeString())}`)}return console.log(),v(K,58),!0}case"/history":{let Q=J0();if(Q.length===0)return console.log(` ${G.gray("No saved conversations.")}`),!0;let K=Q.slice(0,10).map((X)=>({label:X.title.slice(0,40),desc:`${X.model} • ${new Date(X.updatedAt).toLocaleDateString()}`})),V=await T("Resume conversation",K);if(V===-1)return console.log(` ${G.gray("Cancelled")}`),!0;let Y=Q[V];return $.clear(),$.importState({messages:Y.messages,tokens:Y.tokens}),$.__convId=Y.id,console.log(` ${A.ok} Restored: ${G.blue(Y.title)} ${G.gray(`(${Y.messages.length} msgs)`)}`),!0}case"/update":{console.log(`
363
+ ${A.tool} ${G.yellow("Updating tenicli...")}`);try{let Q=$.exportState(),K=r($.cfg.provider.model);K.title=$.getTitle(),K.messages=Q.messages,K.tokens=Q.tokens,f0(K);let{execSync:V,spawn:Y}=await import("child_process");return V("npm i -g tenicli@latest 2>&1",{encoding:"utf8"}),console.log(` ${A.ok} ${G.green("Updated! Restarting...")}
364
+ `),Y(process.execPath,process.argv.slice(1),{stdio:"inherit",detached:!1}).on("exit",(z)=>process.exit(z||0)),!0}catch(Q){M(`Update failed: ${Q.message}`)}return!0}default:return console.log(` ${A.warn} Unknown: ${Z.split(" ")[0]} — try /help`),!0}}async function v1(){let Z=w1(process.argv.slice(2)),$=l();if(Z.model)$.provider.model=Z.model;if(Z.baseUrl)$.provider.baseUrl=Z.baseUrl;switch(Z.command){case"serve":{let W=Z.port||3000,N=Z.password||m0(6).toString("hex"),{startServer:_}=await Promise.resolve().then(() => (U0(),_0));_(W,N);return}case"run":{if(!Z.prompt)M('Usage: teni run "<prompt>"'),process.exit(1);if(!$.provider.apiKey)M("No API key. Run: teni then /auth"),process.exit(1);let W=new w($);if(Z.yes)W.autoMode=!0;if(await W.send(Z.prompt),Z.json){let N=F.getChanges();console.log(JSON.stringify({ok:!0,tokens:W.stats,filesChanged:N.map((_)=>({path:I($.cwd,_.path),isNew:_.isNew,lines:_.lines}))}))}process.exit(0)}case"diff":{let W=F.getChanges();if(Z.json)console.log(JSON.stringify({files:W.map((N)=>({path:I($.cwd,N.path),isNew:N.isNew,lines:N.lines}))}));else if(W.length===0)console.log(` ${G.gray("No files changed in this session.")}`);else{console.log(`
365
+ ${G.bold("Files changed this session:")}`);for(let N of W){let _=I($.cwd,N.path),q=N.isNew?G.green("[NEW]"):G.yellow("[MOD]");console.log(` ${q} ${G.cyan(_)} ${G.gray(`(${N.lines} lines)`)}`)}console.log(` ${G.gray(`total: ${W.length} files`)}`)}process.exit(0)}case"undo":{if(Z.all){let W=0;while(F.count>0)F.undo(),W++;if(Z.json)console.log(JSON.stringify({ok:!0,reverted:W}));else console.log(W>0?` ${A.ok} Reverted ${W} file changes`:` ${G.gray("Nothing to undo.")}`)}else{let W=F.undo();if(Z.json)console.log(JSON.stringify({ok:!!W,file:W?I($.cwd,W.path):null}));else if(!W)console.log(` ${G.gray("Nothing to undo.")}`);else{let N=I($.cwd,W.path);console.log(W.restored?` ${A.ok} Restored: ${G.cyan(N)}`:` ${A.ok} Deleted (was new): ${G.cyan(N)}`)}}process.exit(0)}case"log":{let W=F.getChanges();if(Z.json)console.log(JSON.stringify({actions:W.map((N)=>({path:I($.cwd,N.path),isNew:N.isNew,lines:N.lines,time:N.time.toISOString()}))}));else if(W.length===0)console.log(` ${G.gray("No AI actions in this session.")}`);else{console.log(`
366
+ ${G.bold("AI Action Log:")}`);for(let N of W){let _=I($.cwd,N.path),q=N.isNew?G.green("CREATE"):G.yellow("MODIFY"),H=N.time.toLocaleTimeString();console.log(` ${G.gray(H)} ${q} ${G.cyan(_)} ${G.gray(`(${N.lines} lines)`)}`)}}process.exit(0)}}let Q=new w($);if(Z.yes)Q.autoMode=!0;O0(q0);let K=h.find((W)=>W.id===$.provider.model)?.name||$.provider.model,V=Q.autoMode?G.yellow("auto"):G.green("ask"),Y=[`${G.gray("model")} ${G.blue(K)} ${G.gray("mode")} ${V} ${G.gray("cwd")} ${G.cyan($.cwd)}`];if(!$.provider.apiKey)Y.push(""),Y.push(`${A.warn} ${G.yellow("No API key configured. Run /auth to set one.")}`);v(Y,60),console.log();let X=g0(),z=r($.provider.model);if(X)Q.importState({messages:X.messages,tokens:X.tokens}),z=X,console.log(` ${A.ok} ${G.green("Session restored after update")} ${G.gray(`(${X.messages.length} msgs)`)}`),console.log();let J=()=>{if(Q.messageCount===0)return;let W=Q.exportState();z.title=Q.getTitle(),z.messages=W.messages,z.tokens=W.tokens,z.model=$.provider.model,z.updatedAt=new Date().toISOString(),v0(z)};if(process.on("SIGINT",()=>{J(),console.log(`
349
367
  ${G.gray("Bye!")} \uD83D\uDC4B
350
- `),process.exit(0)}),Q.prompt){if(console.log(` ${E.prompt} ${Q.prompt}`),Z.provider.apiKey)await $.send(Q.prompt);J()}while(!0)try{let W=(await D0()).trim();if(!W)continue;if(W.startsWith("/")){if(W==="/clear")J(),z=n(Z.provider.model);await x1(W,$);continue}if(!$.cfg.provider.apiKey){console.log(` ${E.warn} ${G.yellow("No API key. Run /auth first.")}`);continue}await $.send(W),J()}catch(N){if(N.message==="EOF")J(),console.log(`
368
+ `),process.exit(0)}),Z.prompt){if(console.log(` ${A.prompt} ${Z.prompt}`),$.provider.apiKey)await Q.send(Z.prompt);J()}while(!0)try{let N=(await j0()).trim();if(!N)continue;if(N.startsWith("/")){if(N==="/clear")J(),z=r($.provider.model);await h1(N,Q);continue}if(!Q.cfg.provider.apiKey){console.log(` ${A.warn} ${G.yellow("No API key. Run /auth first.")}`);continue}await Q.send(N),J()}catch(W){if(W.message==="EOF")J(),console.log(`
351
369
  ${G.gray("Bye!")} \uD83D\uDC4B
352
- `),process.exit(0);M(N.message)}}w1().catch((Q)=>{M(Q.message),process.exit(1)});var y1=`# Project Instructions
370
+ `),process.exit(0);M(W.message)}}v1().catch((Z)=>{M(Z.message),process.exit(1)});var f1=`# Project Instructions
353
371
 
354
372
  ## Overview
355
373
  Describe your project here so the AI understands the context.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tenicli",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Lightweight AI coding CLI — fast, compact, multi-provider",
5
5
  "type": "module",
6
6
  "bin": {