tenicli 0.1.5 → 0.1.7

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 +60 -59
  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 K0}from"node:module";var d=K0(import.meta.url);import{existsSync as L,readFileSync as y,writeFileSync as W0,mkdirSync as J0}from"fs";import{join as M}from"path";var O=[{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"}];function f(){let Q=process.env.HOME||process.env.USERPROFILE||"";return M(Q,".tenicli")}function h(){return M(f(),"config.json")}function T(){try{if(L(h()))return JSON.parse(y(h(),"utf8"))}catch{}return{}}function b(Q){let Y=f();if(!L(Y))J0(Y,{recursive:!0});let Z=T(),$={...Z,...Q,keys:{...Z.keys,...Q.keys},baseUrls:{...Z.baseUrls,...Q.baseUrls}};W0(h(),JSON.stringify($,null,2),"utf8")}function a(){let Q=process.cwd();l(M(Q,".tenicli.env")),l(M(Q,".env"));let Y=T(),Z=process.env,$=Z.TENICLI_MODEL||Y.activeModel||O[0].id,V=O.find((W)=>W.id===$)?.provider||Z.TENICLI_PROVIDER||"anthropic",G=N0(V,Y,Z),X=V==="openai"?"https://api.openai.com":"https://api.anthropic.com",K=Z.TENICLI_BASE_URL||Y.baseUrls?.[V]||X;return{provider:{type:V,baseUrl:K,apiKey:G,model:$},maxTokens:parseInt(Z.TENICLI_MAX_TOKENS||"8192"),systemPrompt:B0(Q),cwd:Q}}function N0(Q,Y,Z){if(Q==="anthropic")return Z.TENICLI_API_KEY||Z.ANTHROPIC_API_KEY||Y.keys?.anthropic||"";if(Q==="openai")return Z.TENICLI_API_KEY||Z.OPENAI_API_KEY||Y.keys?.openai||"";return Z.TENICLI_API_KEY||""}function l(Q){try{if(!L(Q))return;for(let Y of y(Q,"utf8").split(`
3
- `)){let Z=Y.trim();if(!Z||Z.startsWith("#"))continue;let $=Z.indexOf("=");if($===-1)continue;let q=Z.slice(0,$).trim(),V=Z.slice($+1).trim();if(V.startsWith('"')&&V.endsWith('"')||V.startsWith("'")&&V.endsWith("'"))V=V.slice(1,-1);if(!process.env[q])process.env[q]=V}}catch{}}function B0(Q){for(let Y of[M(Q,"TENICLI.md"),M(f(),"TENICLI.md")])if(L(Y))return y(Y,"utf8");return _0}var _0=`You are TeniCLI, a fast AI coding assistant in the terminal.
2
+ import{createRequire as U0}from"node:module";var n=U0(import.meta.url);import{existsSync as S,readFileSync as p,writeFileSync as A0,mkdirSync as R0}from"fs";import{join as L}from"path";var P=[{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"}];function u(){let Q=process.env.HOME||process.env.USERPROFILE||"";return L(Q,".tenicli")}function g(){return L(u(),"config.json")}function w(){try{if(S(g()))return JSON.parse(p(g(),"utf8"))}catch{}return{}}function x(Q){let Y=u();if(!S(Y))R0(Y,{recursive:!0});let Z=w(),q={...Z,...Q,keys:{...Z.keys,...Q.keys},baseUrls:{...Z.baseUrls,...Q.baseUrls}};A0(g(),JSON.stringify(q,null,2),"utf8")}function o(){let Q=process.cwd();r(L(Q,".tenicli.env")),r(L(Q,".env"));let Y=w(),Z=process.env,q=Z.TENICLI_MODEL||Y.activeModel||P[0].id,X=P.find((W)=>W.id===q)?.provider||Z.TENICLI_PROVIDER||"anthropic",G=H0(X,Y,Z),K=X==="openai"?"https://api.openai.com":"https://api.anthropic.com",V=Z.TENICLI_BASE_URL||Y.baseUrls?.[X]||K;return{provider:{type:X,baseUrl:V,apiKey:G,model:q},maxTokens:parseInt(Z.TENICLI_MAX_TOKENS||"8192"),systemPrompt:D0(Q),cwd:Q}}function H0(Q,Y,Z){if(Q==="anthropic")return Z.TENICLI_API_KEY||Z.ANTHROPIC_API_KEY||Y.keys?.anthropic||"";if(Q==="openai")return Z.TENICLI_API_KEY||Z.OPENAI_API_KEY||Y.keys?.openai||"";return Z.TENICLI_API_KEY||""}function r(Q){try{if(!S(Q))return;for(let Y of p(Q,"utf8").split(`
3
+ `)){let Z=Y.trim();if(!Z||Z.startsWith("#"))continue;let q=Z.indexOf("=");if(q===-1)continue;let z=Z.slice(0,q).trim(),X=Z.slice(q+1).trim();if(X.startsWith('"')&&X.endsWith('"')||X.startsWith("'")&&X.endsWith("'"))X=X.slice(1,-1);if(!process.env[z])process.env[z]=X}}catch{}}function D0(Q){for(let Y of[L(Q,"TENICLI.md"),L(u(),"TENICLI.md")])if(S(Y))return p(Y,"utf8");return E0}var 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,81 +9,82 @@ 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.`;async function*v(Q,Y,Z,$,q){if(Q.type==="openai")yield*R0(Q,Y,Z,$,q);else yield*U0(Q,Y,Z,$,q)}async function*U0(Q,Y,Z,$,q){let V=`${Q.baseUrl.replace(/\/$/,"")}/v1/messages`,G={model:Q.model,max_tokens:q,system:Z,messages:Y,stream:!0};if($.length)G.tools=$;let X=await i(V,G,{"anthropic-version":"2023-06-01","x-api-key":Q.apiKey,authorization:`Bearer ${Q.apiKey}`});for await(let K of n(X))switch(K.type){case"message_start":if(K.message?.usage)yield{type:"usage",input:K.message.usage.input_tokens||0,output:0};break;case"content_block_start":if(K.content_block?.type==="text")yield{type:"text",text:""};else if(K.content_block?.type==="tool_use")yield{type:"tool_start",id:K.content_block.id,name:K.content_block.name};break;case"content_block_delta":if(K.delta?.type==="text_delta")yield{type:"text",text:K.delta.text};else if(K.delta?.type==="input_json_delta")yield{type:"tool_input",partial:K.delta.partial_json};break;case"content_block_stop":yield{type:"tool_end"};break;case"message_delta":if(K.usage)yield{type:"usage",input:0,output:K.usage.output_tokens||0};yield{type:"done",stopReason:K.delta?.stop_reason||"end_turn"};break}}async function*R0(Q,Y,Z,$,q){let V=`${Q.baseUrl.replace(/\/$/,"")}/v1/chat/completions`,G=A0(Y,Z),X=$.map((N)=>({type:"function",function:{name:N.name,description:N.description,parameters:N.input_schema}})),K={model:Q.model,max_tokens:q,messages:G,stream:!0,stream_options:{include_usage:!0}};if(X.length)K.tools=X;let W=await i(V,K,{authorization:`Bearer ${Q.apiKey}`}),U=new Map;for await(let N of n(W)){let J=N.choices?.[0];if(!J){if(N.usage)yield{type:"usage",input:N.usage.prompt_tokens||0,output:N.usage.completion_tokens||0};continue}let R=J.delta||{};if(R.content)yield{type:"text",text:R.content};if(R.tool_calls)for(let _ of R.tool_calls){if(_.id)U.set(_.index,{id:_.id,name:_.function?.name||"",args:""}),yield{type:"tool_start",id:_.id,name:_.function?.name||""};if(_.function?.arguments){let E=U.get(_.index);if(E)E.args+=_.function.arguments;yield{type:"tool_input",partial:_.function.arguments}}}if(J.finish_reason){for(let[,_]of U)yield{type:"tool_end"};yield{type:"done",stopReason:J.finish_reason==="tool_calls"?"tool_use":J.finish_reason}}}}function A0(Q,Y){let Z=[{role:"system",content:Y}];for(let $ of Q)if($.role==="user")if(typeof $.content==="string")Z.push({role:"user",content:$.content});else{let q=$.content;for(let V of q)if(V.type==="tool_result")Z.push({role:"tool",tool_call_id:V.tool_use_id,content:V.content||""});else Z.push({role:"user",content:V.text||""})}else if(typeof $.content==="string")Z.push({role:"assistant",content:$.content});else{let q=$.content,V=q.filter((X)=>X.type==="tool_use"),G=q.filter((X)=>X.type==="text").map((X)=>X.text).join("");if(V.length)Z.push({role:"assistant",content:G||null,tool_calls:V.map((X)=>({id:X.id,type:"function",function:{name:X.name,arguments:JSON.stringify(X.input||{})}}))});else Z.push({role:"assistant",content:G})}return Z}async function i(Q,Y,Z){let $=await fetch(Q,{method:"POST",headers:{"content-type":"application/json",...Z},body:JSON.stringify(Y)});if(!$.ok){let q=await $.text();throw Error(`API ${$.status}: ${q.slice(0,300)}`)}return $}async function*n(Q){let Y=Q.body.getReader(),Z=new TextDecoder,$="";while(!0){let{done:q,value:V}=await Y.read();if(q)break;$+=Z.decode(V,{stream:!0});let G=$.split(`
13
- `);$=G.pop();for(let X of G)if(X.startsWith("data: ")){let K=X.slice(6).trim();if(K==="[DONE]")return;try{yield JSON.parse(K)}catch{}}}}import{readFileSync as p,writeFileSync as e,existsSync as x,readdirSync as Q0,statSync as Y0,mkdirSync as I0}from"fs";import{resolve as D0,relative as w,join as Z0,dirname as j0}from"path";var r=(Q)=>`\x1B[${Q}m`,k=(Q,Y)=>(Z)=>`${r(Q)}${Z}${r(Y)}`,I=(Q)=>(Y)=>`\x1B[38;5;${Q}m${Y}\x1B[39m`,z={bold:k("1","22"),dim:k("2","22"),italic:k("3","23"),under:k("4","24"),blue:I(111),purple:I(141),green:I(149),yellow:I(179),pink:I(210),cyan:I(117),gray:I(60),text:I(146),orange:I(215)};var B={prompt:z.blue("❯"),ai:z.purple("◆"),tool:z.yellow("⚙"),ok:z.green("✓"),err:z.pink("✗"),warn:z.yellow("⚠"),arrow:z.gray("→"),dot:z.gray("•"),spinner:["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"]};function E0(){let Q=[" ██ ██ ","██████████","███ ██ █","██████████","██████████"," ██ ██ ██ "],Y={T:["█████"," █ "," █ "," █ "," █ "],E:["████ ","█ ","███ ","█ ","████ "],N:["█ █ ","██ █ ","█ ██ ","█ █ ","█ █ "],I:["███"," █ "," █ "," █ ","███"],space:[" "," "," "," "," "],C:[" ███","█ ","█ ","█ "," ███"],L:["█ ","█ ","█ ","█ ","████"]},Z=[Y.T,Y.E,Y.N,Y.I,Y.space,Y.C,Y.L,Y.I],$=["","","","",""];for(let G=0;G<5;G++)$[G]=Z.map((X)=>X[G]).join(" ");let q=Q.map((G)=>z.cyan(G.padEnd(14," "))),V=[" ".repeat($[0].length),...$].map((G)=>z.blue(G));return q.map((G,X)=>`${G} ${V[X]||""}`).join(`
14
- `)}var A={h:"─",v:"│",tl:"╭",tr:"╮",bl:"╰",br:"╯",line:(Q)=>"─".repeat(Q)};function g(Q,Y=60){let Z=($,q)=>{let V=$.replace(/\x1b\[[0-9;]*m/g,""),G=q-V.length;return G>0?$+" ".repeat(G):$};console.log(z.gray(` ${A.tl}${A.line(Y)}${A.tr}`));for(let $ of Q)console.log(z.gray(` ${A.v}`)+` ${Z($,Y-2)} `+z.gray(A.v));console.log(z.gray(` ${A.bl}${A.line(Y)}${A.br}`))}function o(){console.clear(),console.log(),console.log(E0()),console.log(),g([z.gray("type to chat")+` ${B.dot} `+z.gray("/help for commands")+` ${B.dot} `+z.gray("v0.1.0")],60),console.log()}class S{i=0;timer=null;msg;constructor(Q="Thinking"){this.msg=Q}start(){return this.timer=setInterval(()=>{process.stdout.write(`\x1B[2K\r ${z.blue(B.spinner[this.i%B.spinner.length])} ${z.gray(this.msg)}`),this.i++},80),this}stop(){if(this.timer)clearInterval(this.timer),this.timer=null;process.stdout.write("\x1B[2K\r")}}var c=["/model","/auth","/mode","/compact","/diff","/undo","/init","/update","/clear","/cost","/help","/exit"];function D(Q,Y=!1){return new Promise((Z,$)=>{if(process.stdout.write(Q),!Y||!process.stdin.isTTY){let N="",J=(_)=>{let E=typeof _==="string"?_:_.toString("utf8");if(E.charCodeAt(0)===3)process.stdout.write(`
15
- `),process.exit(0);if(E.charCodeAt(0)===4){R(),$(Error("EOF"));return}N+=E;let H=N.indexOf(`
16
- `);if(H!==-1)R(),Z(N.slice(0,H).replace(/\r$/,""))},R=()=>{process.stdin.removeListener("data",J)};if(!process.stdin.readableEncoding)process.stdin.setEncoding("utf8");process.stdin.on("data",J),process.stdin.resume();return}let q="",V=0,G=process.stdin.isRaw;if(process.stdin.setRawMode(!0),!process.stdin.readableEncoding)process.stdin.setEncoding("utf8");process.stdin.resume();let X=()=>{if(V>0)process.stdout.write(`\x1B[${V}D\x1B[0K`),V=0},K=()=>{if(X(),q.startsWith("/")&&q.length>1){let N=c.filter((J)=>J.startsWith(q));if(N.length>0){let J=N[0].slice(q.length);if(J){let R=z.gray(J);process.stdout.write(R),V=J.length,process.stdout.write(`\x1B[${J.length}D`)}}}},W=(N)=>{let J=typeof N==="string"?N:N.toString("utf8");for(let R of J){let _=R.charCodeAt(0);if(_===3)process.stdin.setRawMode(G),process.stdout.write(`
17
- `),process.exit(0);if(_===4){X(),process.stdin.setRawMode(G),U(),$(Error("EOF"));return}if(_===13||_===10){X(),process.stdin.setRawMode(G),process.stdout.write(`
18
- `),U(),Z(q);return}if(_===127||_===8){if(q.length>0)X(),q=q.slice(0,-1),process.stdout.write("\b \b"),K();continue}if(_===9){if(q.startsWith("/")){let E=c.filter((H)=>H.startsWith(q));if(E.length>0){X();let H=E[0].slice(q.length);q=E[0],process.stdout.write(H)}}continue}if(_<32)continue;X(),q+=R,process.stdout.write(R),K()}},U=()=>{process.stdin.removeListener("data",W)};process.stdin.on("data",W)})}async function t(){let Q=[],Y=!0;while(!0){let Z=Y?`
19
- ${z.gray(A.tl+A.line(3))} ${B.prompt} `:` ${z.gray(A.v)} `,$=await D(Z,Y);if(Y=!1,$.endsWith("\\"))Q.push($.slice(0,-1));else{Q.push($);break}}return Q.join(`
20
- `)}async function u(Q,Y){console.log(`
21
- ${z.bold(Q)}`),Y.forEach((Z,$)=>{let q=z.blue(` ${$+1}.`),V=Z.desc?z.gray(` (${Z.desc})`):"";console.log(`${q} ${Z.label}${V}`)});while(!0){let Z=await D(`
22
- ${z.gray("choose")} ${z.blue("❯")} `),$=parseInt(Z.trim());if($>=1&&$<=Y.length)return $-1;console.log(` ${B.warn} enter 1-${Y.length}`)}}function F(Q,Y){console.log(`
23
- ${B.tool} ${z.yellow(Q)} ${z.gray(Y)}`)}function j(Q){console.error(` ${B.err} ${z.pink(Q)}`)}import{spawn as G0}from"child_process";class $0{writes=[];recordWrite(Q,Y){let Z=x(Q)?p(Q,"utf8"):null;this.writes.push({path:Q,backup:Z,newLines:Y.split(`
24
- `).length,time:new Date})}getChanges(){let Q=new Map;for(let Y of this.writes)Q.set(Y.path,{isNew:Y.backup===null,lines:Y.newLines,time:Y.time});return Array.from(Q.entries()).map(([Y,Z])=>({path:Y,...Z}))}undo(){let Q=this.writes.pop();if(!Q)return null;if(Q.backup!==null)return e(Q.path,Q.backup,"utf8"),{path:Q.path,restored:!0};else{try{d("fs").unlinkSync(Q.path)}catch{}return{path:Q.path,restored:!1}}}get count(){return this.writes.length}clear(){this.writes=[]}}var P=new $0,q0=[{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"]}}],O0=30000;async function z0(Q,Y,Z){try{let $;switch(Q){case"read_file":$=C0(Y,Z),F("read_file",z.dim(w(Z,C(Y.path,Z))));break;case"write_file":$=H0(Y,Z),F("write_file",z.dim(w(Z,C(Y.path,Z))));break;case"list_dir":$=M0(Y,Z),F("list_dir",z.dim(Y.path||"."));break;case"search_files":$=await F0(Y,Z),F("search_files",z.dim(`"${Y.pattern}"`));break;case"exec_command":$=await P0(Y,Z),F("exec_command",z.dim(s(Y.command,60)));break;default:$=`Unknown tool: ${Q}`}return{type:"tool_result",content:s($,O0)}}catch($){return{type:"tool_result",content:`Error: ${$.message}`,is_error:!0}}}function C(Q,Y){return D0(Y,Q)}function C0(Q,Y){let Z=C(Q.path,Y);if(!x(Z))return`File not found: ${Q.path}`;let $=p(Z,"utf8"),q=$.split(`
25
- `);if(Q.start_line||Q.end_line){let V=Math.max(1,Q.start_line||1)-1,G=Math.min(q.length,Q.end_line||q.length);return q.slice(V,G).map((X,K)=>`${V+K+1}: ${X}`).join(`
26
- `)}if(q.length>50)return q.map((V,G)=>`${G+1}: ${V}`).join(`
27
- `);return $}function H0(Q,Y){let Z=C(Q.path,Y),$=j0(Z);if(!x($))I0($,{recursive:!0});return P.recordWrite(Z,Q.content),e(Z,Q.content,"utf8"),`Written ${Q.content.split(`
28
- `).length} lines to ${Q.path}`}function M0(Q,Y){let Z=C(Q.path||".",Y);if(!x(Z))return`Directory not found: ${Q.path||"."}`;let $=Q.depth||1,q=[];function V(G,X){if(X>$)return;try{let K=Q0(G);for(let W of K){if(W.startsWith(".")||W==="node_modules")continue;let U=Z0(G,W),N=w(Z,U);try{let J=Y0(U),R=" ".repeat(X);if(J.isDirectory())q.push(`${R}${N}/`),V(U,X+1);else{let _=J.size>1024?`${(J.size/1024).toFixed(1)}KB`:`${J.size}B`;q.push(`${R}${N} (${_})`)}}catch{}}}catch{}}return V(Z,0),q.length>0?q.join(`
29
- `):"(empty directory)"}async function F0(Q,Y){let Z=C(Q.path||".",Y),$=Q.pattern;try{let G=["-n","--max-count=50","--no-heading"];if(Q.include)G.push("--glob",Q.include);return G.push($,Z),await new Promise((K,W)=>{let U=G0("rg",G,{shell:!0}),N="";U.stdout.on("data",(J)=>N+=J.toString()),U.on("close",(J)=>{if(J===0||J===1)K(N.trim()||"No matches found.");else W(Error("rg failed"))}),U.on("error",W)})}catch{}let q=[];function V(G){try{for(let X of Q0(G)){if(X.startsWith(".")||X==="node_modules")continue;let K=Z0(G,X);try{let W=Y0(K);if(W.isDirectory()){V(K);continue}if(W.size>500000)continue;if(Q.include&&!L0(X,Q.include))continue;let N=p(K,"utf8").split(`
30
- `);for(let J=0;J<N.length;J++)if(N[J].includes($)){if(q.push(`${w(Y,K)}:${J+1}: ${N[J].trim()}`),q.length>=50)return}}catch{}}}catch{}}return V(Z),q.length>0?q.join(`
31
- `):"No matches found."}async function P0(Q,Y){let Z=C(Q.cwd||".",Y),$=process.platform==="win32",q=$?"cmd":"sh",V=$?"/c":"-c";return new Promise((G)=>{let X=G0(q,[V,Q.command],{cwd:Z,env:{...process.env,PAGER:"cat"}}),K="",W="";X.stdout.on("data",(N)=>K+=N.toString()),X.stderr.on("data",(N)=>W+=N.toString());let U=setTimeout(()=>X.kill(),30000);X.on("close",(N)=>{clearTimeout(U);let J="";if(K.trim())J+=K.trim();if(W.trim())J+=(J?`
32
- `:"")+`[stderr] ${W.trim()}`;J+=`
33
- [exit code: ${N}]`,G(J)}),X.on("error",(N)=>{clearTimeout(U),G(`[error] Failed to start process: ${N.message}`)})})}function s(Q,Y){if(Q.length<=Y)return Q;return Q.slice(0,Y)+`
34
- ... (truncated, ${Q.length-Y} chars omitted)`}function L0(Q,Y){if(Y.startsWith("*."))return Q.endsWith(Y.slice(1));return Q.includes(Y.replace(/\*/g,""))}class m{messages=[];tokens={input:0,output:0};cfg;autoMode=!1;constructor(Q){this.cfg=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==="tool_use"){let Z=Q.content.filter((q)=>q.type==="tool_use"),$=[];for(let q of Z){if(!this.autoMode&&(q.name==="write_file"||q.name==="exec_command")){let G=q.name==="write_file"?q.input?.path:q.input?.command?.slice(0,80);console.log(`
35
- ${B.warn} ${z.yellow(q.name)} ${z.gray(G||"")}`);let K=(await D(` ${z.gray("allow?")} ${z.blue("[y/n/auto]")} `)).trim().toLowerCase();if(K==="auto")this.autoMode=!0;else if(K!=="y"&&K!=="yes"&&K!==""){$.push({type:"tool_result",tool_use_id:q.id,content:"User denied this action.",is_error:!0});continue}}let V=await z0(q.name,q.input,this.cfg.cwd);$.push({type:"tool_result",tool_use_id:q.id,content:V.content,is_error:V.is_error})}this.messages.push({role:"assistant",content:Q.content}),this.messages.push({role:"user",content:$});continue}let Y=Q.content.filter((Z)=>Z.type==="text").map((Z)=>Z.text).join("");if(Y)this.messages.push({role:"assistant",content:Y});break}console.log(`
36
- ${z.gray(`tokens: ${this.tokens.input} ${this.tokens.output}↓`)}`)}async streamResponse(){let Q=new S("Thinking").start(),Y=[],Z="",$="",q="",V="",G="end_turn",X=!1;try{let K=v(this.cfg.provider,this.messages,this.cfg.systemPrompt,q0,this.cfg.maxTokens);for await(let W of K)switch(W.type){case"text":if(!X)Q.stop(),X=!0,process.stdout.write(`
37
- ${B.ai} `);if(W.text)process.stdout.write(W.text);Z+=W.text;break;case"tool_start":if(!X)Q.stop(),X=!0;if(Z)Y.push({type:"text",text:Z}),Z="";$=W.id,q=W.name,V="";break;case"tool_input":V+=W.partial;break;case"tool_end":if($){let U={};try{U=JSON.parse(V)}catch{}Y.push({type:"tool_use",id:$,name:q,input:U}),$="",V=""}break;case"usage":this.tokens.input+=W.input,this.tokens.output+=W.output;break;case"done":G=W.stopReason;break}}catch(K){return Q.stop(),j(K.message),{content:[],stopReason:"error"}}if(Z)Y.push({type:"text",text:Z}),process.stdout.write(`
38
- `);if(!X)Q.stop();return{content:Y,stopReason:G}}async compact(){if(this.messages.length<4){console.log(` ${B.warn} Not enough messages to compact.`);return}let Q=new S("Compacting").start();try{let Y="";for(let G of this.messages)if(typeof G.content==="string")Y+=`${G.role}: ${G.content.slice(0,500)}
39
- `;else{let X=G.content.filter((W)=>W.type==="text").map((W)=>W.text?.slice(0,300)).join(" ");if(X)Y+=`${G.role}: ${X}
40
- `;let K=G.content.filter((W)=>W.type==="tool_use").map((W)=>`[tool: ${W.name}]`).join(", ");if(K)Y+=` tools: ${K}
41
- `}let Z=this.messages.length,$=`Summarize this conversation concisely. Keep key decisions, file changes, and current state. Be brief:
12
+ - Write production-quality code matching the project's style.`;async function*m(Q,Y,Z,q,z){if(Q.type==="openai")yield*B0(Q,Y,Z,q,z);else yield*j0(Q,Y,Z,q,z)}async function*j0(Q,Y,Z,q,z){let X=`${Q.baseUrl.replace(/\/$/,"")}/v1/messages`,G={model:Q.model,max_tokens:z,system:Z,messages:Y,stream:!0};if(q.length)G.tools=q;let K=await c(X,G,{"anthropic-version":"2023-06-01","x-api-key":Q.apiKey,authorization:`Bearer ${Q.apiKey}`});for await(let V of t(K))switch(V.type){case"message_start":if(V.message?.usage)yield{type:"usage",input:V.message.usage.input_tokens||0,output:0};break;case"content_block_start":if(V.content_block?.type==="text")yield{type:"text",text:""};else if(V.content_block?.type==="tool_use")yield{type:"tool_start",id:V.content_block.id,name:V.content_block.name};break;case"content_block_delta":if(V.delta?.type==="text_delta")yield{type:"text",text:V.delta.text};else if(V.delta?.type==="input_json_delta")yield{type:"tool_input",partial:V.delta.partial_json};break;case"content_block_stop":yield{type:"tool_end"};break;case"message_delta":if(V.usage)yield{type:"usage",input:0,output:V.usage.output_tokens||0};yield{type:"done",stopReason:V.delta?.stop_reason||"end_turn"};break}}async function*B0(Q,Y,Z,q,z){let X=`${Q.baseUrl.replace(/\/$/,"")}/v1/chat/completions`,G=O0(Y,Z),K=q.map((J)=>({type:"function",function:{name:J.name,description:J.description,parameters:J.input_schema}})),V={model:Q.model,max_tokens:z,messages:G,stream:!0,stream_options:{include_usage:!0}};if(K.length)V.tools=K;let W=await c(X,V,{authorization:`Bearer ${Q.apiKey}`}),A=new Map;for await(let J of t(W)){let U=J.choices?.[0];if(!U){if(J.usage)yield{type:"usage",input:J.usage.prompt_tokens||0,output:J.usage.completion_tokens||0};continue}let B=U.delta||{};if(B.content)yield{type:"text",text:B.content};if(B.tool_calls)for(let N of B.tool_calls){if(N.id)A.set(N.index,{id:N.id,name:N.function?.name||"",args:""}),yield{type:"tool_start",id:N.id,name:N.function?.name||""};if(N.function?.arguments){let H=A.get(N.index);if(H)H.args+=N.function.arguments;yield{type:"tool_input",partial:N.function.arguments}}}if(U.finish_reason){for(let[,N]of A)yield{type:"tool_end"};yield{type:"done",stopReason:U.finish_reason==="tool_calls"?"tool_use":U.finish_reason}}}}function O0(Q,Y){let Z=[{role:"system",content:Y}];for(let q of Q)if(q.role==="user")if(typeof q.content==="string")Z.push({role:"user",content:q.content});else{let z=q.content;for(let X of z)if(X.type==="tool_result")Z.push({role:"tool",tool_call_id:X.tool_use_id,content:X.content||""});else Z.push({role:"user",content:X.text||""})}else if(typeof q.content==="string")Z.push({role:"assistant",content:q.content});else{let z=q.content,X=z.filter((K)=>K.type==="tool_use"),G=z.filter((K)=>K.type==="text").map((K)=>K.text).join("");if(X.length)Z.push({role:"assistant",content:G||null,tool_calls:X.map((K)=>({id:K.id,type:"function",function:{name:K.name,arguments:JSON.stringify(K.input||{})}}))});else Z.push({role:"assistant",content:G})}return Z}async function c(Q,Y,Z){let q=await fetch(Q,{method:"POST",headers:{"content-type":"application/json",...Z},body:JSON.stringify(Y)});if(!q.ok){let z=await q.text();throw Error(`API ${q.status}: ${z.slice(0,300)}`)}return q}async function*t(Q){let Y=Q.body.getReader(),Z=new TextDecoder,q="";while(!0){let{done:z,value:X}=await Y.read();if(z)break;q+=Z.decode(X,{stream:!0});let G=q.split(`
13
+ `);q=G.pop();for(let K of G)if(K.startsWith("data: ")){let V=K.slice(6).trim();if(V==="[DONE]")return;try{yield JSON.parse(V)}catch{}}}}import{readFileSync as a,writeFileSync as Z0,existsSync as f,readdirSync as $0,statSync as q0,mkdirSync as I0}from"fs";import{resolve as P0,relative as v,join as z0,dirname as M0}from"path";var s=(Q)=>`\x1B[${Q}m`,y=(Q,Y)=>(Z)=>`${s(Q)}${Z}${s(Y)}`,O=(Q)=>(Y)=>`\x1B[38;5;${Q}m${Y}\x1B[39m`,$={bold:y("1","22"),dim:y("2","22"),italic:y("3","23"),under:y("4","24"),blue:O(111),purple:O(141),green:O(149),yellow:O(179),pink:O(210),cyan:O(117),gray:O(60),text:O(146),orange:O(215)};var _={prompt:$.blue("❯"),ai:$.purple("◆"),tool:$.yellow("⚙"),ok:$.green("✓"),err:$.pink("✗"),warn:$.yellow("⚠"),arrow:$.gray("→"),dot:$.gray("•"),spinner:["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"]};function C0(){let Q=[" ██ ██ ","██████████","███ ██ █","██████████","██████████"," ██ ██ ██ "],Y={T:["█████"," █ "," █ "," █ "," █ "],E:["████ ","█ ","███ ","█ ","████ "],N:["█ █ ","██ █ ","█ ██ ","█ █ ","█ █ "],I:["███"," █ "," █ "," █ ","███"],space:[" "," "," "," "," "],C:[" ███","█ ","█ ","█ "," ███"],L:["█ ","█ ","█ ","█ ","████"]},Z=[Y.T,Y.E,Y.N,Y.I,Y.space,Y.C,Y.L,Y.I],q=["","","","",""];for(let G=0;G<5;G++)q[G]=Z.map((K)=>K[G]).join(" ");let z=Q.map((G)=>$.cyan(G.padEnd(14," "))),X=[" ".repeat(q[0].length),...q].map((G)=>$.blue(G));return z.map((G,K)=>`${G} ${X[K]||""}`).join(`
14
+ `)}var j={h:"─",v:"│",tl:"╭",tr:"╮",bl:"╰",br:"╯",line:(Q)=>"─".repeat(Q)};function d(Q,Y=60){let Z=(q,z)=>{let X=q.replace(/\x1b\[[0-9;]*m/g,""),G=z-X.length;return G>0?q+" ".repeat(G):q};console.log($.gray(` ${j.tl}${j.line(Y)}${j.tr}`));for(let q of Q)console.log($.gray(` ${j.v}`)+` ${Z(q,Y-2)} `+$.gray(j.v));console.log($.gray(` ${j.bl}${j.line(Y)}${j.br}`))}function e(Q="0.0.0"){console.clear(),console.log(),console.log(C0()),console.log(),d([$.gray("type to chat")+` ${_.dot} `+$.gray("/help for commands")+` ${_.dot} `+$.gray(`v${Q}`)],60),console.log()}class h{i=0;timer=null;msg;constructor(Q="Thinking"){this.msg=Q}start(){return this.timer=setInterval(()=>{process.stdout.write(`\x1B[2K\r ${$.blue(_.spinner[this.i%_.spinner.length])} ${$.gray(this.msg)}`),this.i++},80),this}stop(){if(this.timer)clearInterval(this.timer),this.timer=null;process.stdout.write("\x1B[2K\r")}}var F0=[{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:"/update",desc:"update tenicli"},{cmd:"/clear",desc:"clear screen"},{cmd:"/cost",desc:"show token usage"},{cmd:"/help",desc:"list commands"},{cmd:"/exit",desc:"quit"}];function F(Q,Y=!1){return new Promise((Z,q)=>{if(process.stdout.write(Q),!Y||!process.stdin.isTTY){let N="",H=(C)=>{let D=typeof C==="string"?C:C.toString("utf8");if(D.charCodeAt(0)===3)process.stdout.write(`
15
+ `),process.exit(0);if(D.charCodeAt(0)===4){E(),q(Error("EOF"));return}N+=D;let R=N.indexOf(`
16
+ `);if(R!==-1)E(),Z(N.slice(0,R).replace(/\r$/,""))},E=()=>{process.stdin.removeListener("data",H)};if(!process.stdin.readableEncoding)process.stdin.setEncoding("utf8");process.stdin.on("data",H),process.stdin.resume();return}let z="",X=0,G=0,K=process.stdin.isRaw;if(process.stdin.setRawMode(!0),!process.stdin.readableEncoding)process.stdin.setEncoding("utf8");process.stdin.resume();let V=()=>{if(!z.startsWith("/")||z.length<1)return[];return F0.filter((N)=>N.cmd.startsWith(z))},W=()=>{if(X>0){for(let N=0;N<X;N++)process.stdout.write("\x1B[1B"),process.stdout.write("\x1B[2K");process.stdout.write(`\x1B[${X}A`),X=0}process.stdout.write("\x1B[0K")},A=()=>{W();let N=V();if(N.length===0)return;if(G>=N.length)G=N.length-1;if(G<0)G=0;process.stdout.write("\x1B[s");for(let H=0;H<N.length;H++){process.stdout.write(`
17
+ \x1B[2K`);let E=N[H];if(H===G)process.stdout.write(` ${$.blue($.bold(E.cmd))} ${$.gray(E.desc)}`);else process.stdout.write(` ${$.gray(E.cmd)} ${$.gray($.dim(E.desc))}`)}X=N.length,process.stdout.write("\x1B[u")},J="",U=(N)=>{let H=typeof N==="string"?N:N.toString("utf8");for(let E=0;E<H.length;E++){let C=H[E],D=C.charCodeAt(0);if(J.length>0||D===27){if(J+=C,J.length===1)continue;if(J.length===2&&J[1]==="[")continue;if(J.length>=3){let R=V();if(J==="\x1B[A"&&R.length>0)G=(G-1+R.length)%R.length,A();else if(J==="\x1B[B"&&R.length>0)G=(G+1)%R.length,A();J="";continue}continue}if(D===3)W(),process.stdin.setRawMode(K),process.stdout.write(`
18
+ `),process.exit(0);if(D===4){W(),process.stdin.setRawMode(K),B(),q(Error("EOF"));return}if(D===13||D===10){let R=V();if(R.length>0&&z!==R[G].cmd){W();let k=R[G].cmd;process.stdout.write("\b \b".repeat(z.length)),z=k,process.stdout.write(z)}W(),process.stdin.setRawMode(K),process.stdout.write(`
19
+ `),B(),Z(z);return}if(D===127||D===8){if(z.length>0)W(),z=z.slice(0,-1),process.stdout.write("\b \b"),G=0,A();continue}if(D===9){let R=V();if(R.length>0){W();let k=R[G].cmd,_0=k.slice(z.length);z=k,process.stdout.write(_0),A()}continue}if(D<32)continue;W(),z+=C,process.stdout.write(C),G=0,A()}},B=()=>{process.stdin.removeListener("data",U)};process.stdin.on("data",U)})}async function Q0(){let Q=[],Y=!0;while(!0){let Z=Y?`
20
+ ${$.gray(j.tl+j.line(3))} ${_.prompt} `:` ${$.gray(j.v)} `,q=await F(Z,Y);if(Y=!1,q.endsWith("\\"))Q.push(q.slice(0,-1));else{Q.push(q);break}}return Q.join(`
21
+ `)}async function l(Q,Y){console.log(`
22
+ ${$.bold(Q)}`),Y.forEach((Z,q)=>{let z=$.blue(` ${q+1}.`),X=Z.desc?$.gray(` (${Z.desc})`):"";console.log(`${z} ${Z.label}${X}`)});while(!0){let Z=await F(`
23
+ ${$.gray("choose")} ${$.blue("❯")} `),q=parseInt(Z.trim());if(q>=1&&q<=Y.length)return q-1;console.log(` ${_.warn} enter 1-${Y.length}`)}}function T(Q,Y){console.log(`
24
+ ${_.tool} ${$.yellow(Q)} ${$.gray(Y)}`)}function I(Q){console.error(` ${_.err} ${$.pink(Q)}`)}import{spawn as V0}from"child_process";class G0{writes=[];recordWrite(Q,Y){let Z=f(Q)?a(Q,"utf8"):null;this.writes.push({path:Q,backup:Z,newLines:Y.split(`
25
+ `).length,time:new Date})}getChanges(){let Q=new Map;for(let Y of this.writes)Q.set(Y.path,{isNew:Y.backup===null,lines:Y.newLines,time:Y.time});return Array.from(Q.entries()).map(([Y,Z])=>({path:Y,...Z}))}undo(){let Q=this.writes.pop();if(!Q)return null;if(Q.backup!==null)return Z0(Q.path,Q.backup,"utf8"),{path:Q.path,restored:!0};else{try{n("fs").unlinkSync(Q.path)}catch{}return{path:Q.path,restored:!1}}}get count(){return this.writes.length}clear(){this.writes=[]}}var b=new G0,X0=[{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"]}}],L0=30000;async function K0(Q,Y,Z){try{let q;switch(Q){case"read_file":q=T0(Y,Z),T("read_file",$.dim(v(Z,M(Y.path,Z))));break;case"write_file":q=b0(Y,Z),T("write_file",$.dim(v(Z,M(Y.path,Z))));break;case"list_dir":q=k0(Y,Z),T("list_dir",$.dim(Y.path||"."));break;case"search_files":q=await S0(Y,Z),T("search_files",$.dim(`"${Y.pattern}"`));break;case"exec_command":q=await w0(Y,Z),T("exec_command",$.dim(Y0(Y.command,60)));break;default:q=`Unknown tool: ${Q}`}return{type:"tool_result",content:Y0(q,L0)}}catch(q){return{type:"tool_result",content:`Error: ${q.message}`,is_error:!0}}}function M(Q,Y){return P0(Y,Q)}function T0(Q,Y){let Z=M(Q.path,Y);if(!f(Z))return`File not found: ${Q.path}`;let q=a(Z,"utf8"),z=q.split(`
26
+ `);if(Q.start_line||Q.end_line){let X=Math.max(1,Q.start_line||1)-1,G=Math.min(z.length,Q.end_line||z.length);return z.slice(X,G).map((K,V)=>`${X+V+1}: ${K}`).join(`
27
+ `)}if(z.length>50)return z.map((X,G)=>`${G+1}: ${X}`).join(`
28
+ `);return q}function b0(Q,Y){let Z=M(Q.path,Y),q=M0(Z);if(!f(q))I0(q,{recursive:!0});return b.recordWrite(Z,Q.content),Z0(Z,Q.content,"utf8"),`Written ${Q.content.split(`
29
+ `).length} lines to ${Q.path}`}function k0(Q,Y){let Z=M(Q.path||".",Y);if(!f(Z))return`Directory not found: ${Q.path||"."}`;let q=Q.depth||1,z=[];function X(G,K){if(K>q)return;try{let V=$0(G);for(let W of V){if(W.startsWith(".")||W==="node_modules")continue;let A=z0(G,W),J=v(Z,A);try{let U=q0(A),B=" ".repeat(K);if(U.isDirectory())z.push(`${B}${J}/`),X(A,K+1);else{let N=U.size>1024?`${(U.size/1024).toFixed(1)}KB`:`${U.size}B`;z.push(`${B}${J} (${N})`)}}catch{}}}catch{}}return X(Z,0),z.length>0?z.join(`
30
+ `):"(empty directory)"}async function S0(Q,Y){let Z=M(Q.path||".",Y),q=Q.pattern;try{let G=["-n","--max-count=50","--no-heading"];if(Q.include)G.push("--glob",Q.include);return G.push(q,Z),await new Promise((V,W)=>{let A=V0("rg",G,{shell:!0}),J="";A.stdout.on("data",(U)=>J+=U.toString()),A.on("close",(U)=>{if(U===0||U===1)V(J.trim()||"No matches found.");else W(Error("rg failed"))}),A.on("error",W)})}catch{}let z=[];function X(G){try{for(let K of $0(G)){if(K.startsWith(".")||K==="node_modules")continue;let V=z0(G,K);try{let W=q0(V);if(W.isDirectory()){X(V);continue}if(W.size>500000)continue;if(Q.include&&!x0(K,Q.include))continue;let J=a(V,"utf8").split(`
31
+ `);for(let U=0;U<J.length;U++)if(J[U].includes(q)){if(z.push(`${v(Y,V)}:${U+1}: ${J[U].trim()}`),z.length>=50)return}}catch{}}}catch{}}return X(Z),z.length>0?z.join(`
32
+ `):"No matches found."}async function w0(Q,Y){let Z=M(Q.cwd||".",Y),q=process.platform==="win32",z=q?"cmd":"sh",X=q?"/c":"-c";return new Promise((G)=>{let K=V0(z,[X,Q.command],{cwd:Z,env:{...process.env,PAGER:"cat"}}),V="",W="";K.stdout.on("data",(J)=>V+=J.toString()),K.stderr.on("data",(J)=>W+=J.toString());let A=setTimeout(()=>K.kill(),30000);K.on("close",(J)=>{clearTimeout(A);let U="";if(V.trim())U+=V.trim();if(W.trim())U+=(U?`
33
+ `:"")+`[stderr] ${W.trim()}`;U+=`
34
+ [exit code: ${J}]`,G(U)}),K.on("error",(J)=>{clearTimeout(A),G(`[error] Failed to start process: ${J.message}`)})})}function Y0(Q,Y){if(Q.length<=Y)return Q;return Q.slice(0,Y)+`
35
+ ... (truncated, ${Q.length-Y} chars omitted)`}function x0(Q,Y){if(Y.startsWith("*."))return Q.endsWith(Y.slice(1));return Q.includes(Y.replace(/\*/g,""))}class i{messages=[];tokens={input:0,output:0};cfg;autoMode=!1;constructor(Q){this.cfg=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==="tool_use"){let Z=Q.content.filter((z)=>z.type==="tool_use"),q=[];for(let z of Z){if(!this.autoMode&&(z.name==="write_file"||z.name==="exec_command")){let G=z.name==="write_file"?z.input?.path:z.input?.command?.slice(0,80);console.log(`
36
+ ${_.warn} ${$.yellow(z.name)} ${$.gray(G||"")}`);let V=(await F(` ${$.gray("allow?")} ${$.blue("[y/n/auto]")} `)).trim().toLowerCase();if(V==="auto")this.autoMode=!0;else if(V!=="y"&&V!=="yes"&&V!==""){q.push({type:"tool_result",tool_use_id:z.id,content:"User denied this action.",is_error:!0});continue}}let X=await K0(z.name,z.input,this.cfg.cwd);q.push({type:"tool_result",tool_use_id:z.id,content:X.content,is_error:X.is_error})}this.messages.push({role:"assistant",content:Q.content}),this.messages.push({role:"user",content:q});continue}let Y=Q.content.filter((Z)=>Z.type==="text").map((Z)=>Z.text).join("");if(Y)this.messages.push({role:"assistant",content:Y});break}console.log(`
37
+ ${$.gray(`tokens: ${this.tokens.input} ${this.tokens.output}↓`)}`)}async streamResponse(){let Q=new h("Thinking").start(),Y=[],Z="",q="",z="",X="",G="end_turn",K=!1;try{let V=m(this.cfg.provider,this.messages,this.cfg.systemPrompt,X0,this.cfg.maxTokens);for await(let W of V)switch(W.type){case"text":if(!K)Q.stop(),K=!0,process.stdout.write(`
38
+ ${_.ai} `);if(W.text)process.stdout.write(W.text);Z+=W.text;break;case"tool_start":if(!K)Q.stop(),K=!0;if(Z)Y.push({type:"text",text:Z}),Z="";q=W.id,z=W.name,X="";break;case"tool_input":X+=W.partial;break;case"tool_end":if(q){let A={};try{A=JSON.parse(X)}catch{}Y.push({type:"tool_use",id:q,name:z,input:A}),q="",X=""}break;case"usage":this.tokens.input+=W.input,this.tokens.output+=W.output;break;case"done":G=W.stopReason;break}}catch(V){return Q.stop(),I(V.message),{content:[],stopReason:"error"}}if(Z)Y.push({type:"text",text:Z}),process.stdout.write(`
39
+ `);if(!K)Q.stop();return{content:Y,stopReason:G}}async compact(){if(this.messages.length<4){console.log(` ${_.warn} Not enough messages to compact.`);return}let Q=new h("Compacting").start();try{let Y="";for(let G of this.messages)if(typeof G.content==="string")Y+=`${G.role}: ${G.content.slice(0,500)}
40
+ `;else{let K=G.content.filter((W)=>W.type==="text").map((W)=>W.text?.slice(0,300)).join(" ");if(K)Y+=`${G.role}: ${K}
41
+ `;let V=G.content.filter((W)=>W.type==="tool_use").map((W)=>`[tool: ${W.name}]`).join(", ");if(V)Y+=` tools: ${V}
42
+ `}let Z=this.messages.length,q=`Summarize this conversation concisely. Keep key decisions, file changes, and current state. Be brief:
42
43
 
43
- ${Y.slice(0,6000)}`;this.messages=[{role:"user",content:$},{role:"assistant",content:`[Conversation compacted from ${Z} messages. Summary of what happened:]`}];let q=v(this.cfg.provider,[{role:"user",content:$}],"You are a conversation summarizer. Create a brief summary preserving key facts, decisions, and file changes.",[],this.cfg.maxTokens),V="";for await(let G of q){if(G.type==="text"&&G.text)V+=G.text;if(G.type==="usage")this.tokens.input+=G.input,this.tokens.output+=G.output}this.messages=[{role:"user",content:`[Previous conversation summary]
44
- ${V}`},{role:"assistant",content:"Understood. I have the context from our previous conversation. How can I continue helping you?"}],Q.stop(),console.log(` ${B.ok} Compacted ${Z} messages → 2 ${z.gray(`(saved ~${Math.round(Y.length/4)} tokens)`)}`)}catch(Y){Q.stop(),j(`Compact failed: ${Y.message}`)}}get stats(){return this.tokens}get messageCount(){return this.messages.length}clear(){this.messages=[],this.tokens={input:0,output:0}}}import{writeFileSync as b0,existsSync as k0}from"fs";import{join as S0,relative as X0}from"path";var V0={name:"tenicli",version:"0.1.5",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 w0=V0.version;function x0(Q){let Y={prompt:"",print:!1},Z=0;while(Z<Q.length){switch(Q[Z]){case"-p":case"--print":if(Y.print=!0,Z+1<Q.length&&!Q[Z+1].startsWith("-"))Y.prompt=Q[++Z];break;case"-m":case"--model":Y.model=Q[++Z];break;case"--base-url":Y.baseUrl=Q[++Z];break;case"-v":case"--version":console.log(`teni v${w0}`),process.exit(0);case"-h":case"--help":h0(),process.exit(0);default:if(!Q[Z].startsWith("-"))Y.prompt=Q.slice(Z).join(" "),Z=Q.length}Z++}return Y}function h0(){console.log(`
45
- ${z.bold(z.blue("TeniCLI"))} — Lightweight AI Coding Agent
44
+ ${Y.slice(0,6000)}`;this.messages=[{role:"user",content:q},{role:"assistant",content:`[Conversation compacted from ${Z} messages. Summary of what happened:]`}];let z=m(this.cfg.provider,[{role:"user",content:q}],"You are a conversation summarizer. Create a brief summary preserving key facts, decisions, and file changes.",[],this.cfg.maxTokens),X="";for await(let G of z){if(G.type==="text"&&G.text)X+=G.text;if(G.type==="usage")this.tokens.input+=G.input,this.tokens.output+=G.output}this.messages=[{role:"user",content:`[Previous conversation summary]
45
+ ${X}`},{role:"assistant",content:"Understood. I have the context from our previous conversation. How can I continue helping you?"}],Q.stop(),console.log(` ${_.ok} Compacted ${Z} messages → 2 ${$.gray(`(saved ~${Math.round(Y.length/4)} tokens)`)}`)}catch(Y){Q.stop(),I(`Compact failed: ${Y.message}`)}}get stats(){return this.tokens}get messageCount(){return this.messages.length}clear(){this.messages=[],this.tokens={input:0,output:0}}}import{writeFileSync as h0,existsSync as v0}from"fs";import{join as f0,relative as J0}from"path";var W0={name:"tenicli",version:"0.1.7",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 N0=W0.version;function g0(Q){let Y={prompt:"",print:!1},Z=0;while(Z<Q.length){switch(Q[Z]){case"-p":case"--print":if(Y.print=!0,Z+1<Q.length&&!Q[Z+1].startsWith("-"))Y.prompt=Q[++Z];break;case"-m":case"--model":Y.model=Q[++Z];break;case"--base-url":Y.baseUrl=Q[++Z];break;case"-v":case"--version":console.log(`teni v${N0}`),process.exit(0);case"-h":case"--help":p0(),process.exit(0);default:if(!Q[Z].startsWith("-"))Y.prompt=Q.slice(Z).join(" "),Z=Q.length}Z++}return Y}function p0(){console.log(`
46
+ ${$.bold($.blue("TeniCLI"))} — Lightweight AI Coding Agent
46
47
 
47
- ${z.bold("USAGE")}
48
+ ${$.bold("USAGE")}
48
49
  teni Start chatting
49
50
  teni "prompt" Start with a prompt
50
51
  teni -p "prompt" Non-interactive mode
51
52
 
52
- ${z.bold("OPTIONS")}
53
+ ${$.bold("OPTIONS")}
53
54
  -p, --print <prompt> Print response and exit
54
55
  -m, --model <model> Override model
55
56
  --base-url <url> Override API base URL
56
57
  -v, --version Show version
57
58
  -h, --help Show help
58
59
 
59
- ${z.bold("IN-CHAT")}
60
+ ${$.bold("IN-CHAT")}
60
61
  /model Select model /auth Set API key
61
62
  /mode Ask/Auto toggle /compact Summarize chat
62
63
  /diff Files changed /undo Revert last write
63
64
  /init Create TENICLI.md /clear New conversation
64
65
  /cost Token usage /exit Quit
65
66
  \\\\ Multiline input
66
- `)}async function y0(Q,Y){switch(Q.toLowerCase().split(" ")[0]){case"/exit":case"/quit":case"/q":console.log(`
67
- ${z.gray("Bye!")} \uD83D\uDC4B
68
- `),process.exit(0);case"/clear":return Y.clear(),P.clear(),console.log(` ${B.ok} Conversation cleared`),!0;case"/compact":return await Y.compact(),!0;case"/diff":{let Z=P.getChanges();if(Z.length===0)console.log(` ${z.gray("No files changed in this session.")}`);else{console.log(`
69
- ${z.bold("Files changed this session:")}`);for(let $ of Z){let q=X0(Y.cfg.cwd,$.path),V=$.isNew?z.green("[NEW]"):z.yellow("[MOD]");console.log(` ${V} ${z.cyan(q)} ${z.gray(`(${$.lines} lines)`)}`)}console.log(` ${z.gray(`total: ${Z.length} files`)}`)}return!0}case"/undo":{let Z=P.undo();if(!Z)console.log(` ${z.gray("Nothing to undo.")}`);else{let $=X0(Y.cfg.cwd,Z.path);if(Z.restored)console.log(` ${B.ok} Restored: ${z.cyan($)}`);else console.log(` ${B.ok} Deleted (was new): ${z.cyan($)}`)}return!0}case"/init":{let Z=S0(Y.cfg.cwd,"TENICLI.md");if(k0(Z))console.log(` ${B.warn} TENICLI.md already exists.`);else b0(Z,v0,"utf8"),console.log(` ${B.ok} Created ${z.cyan("TENICLI.md")}`);return!0}case"/mode":{Y.autoMode=!Y.autoMode;let Z=Y.autoMode?z.yellow("auto"):z.green("ask");return console.log(` ${B.ok} Mode: ${Z} ${z.gray(Y.autoMode?"(tools run without asking)":"(confirm write/exec)")}`),!0}case"/cost":{let Z=Y.stats;return console.log(` ${B.ai} ${z.blue(String(Z.input))}↑ input ${z.blue(String(Z.output))}↓ output ${z.gray(`(${Y.messageCount} msgs)`)}`),!0}case"/model":{let Z=O.map((q)=>({label:`${q.name} ${Y.cfg.provider.model===q.id?z.green("●"):""}`,desc:`${q.provider} • ${q.speed}`}));Z.push({label:"Custom model...",desc:"type model ID"});let $=await u("Select model",Z);if($<O.length){let q=O[$];Y.cfg.provider.model=q.id,Y.cfg.provider.type=q.provider;let V=T(),G=q.provider==="openai"?process.env.OPENAI_API_KEY||V.keys?.openai||"":process.env.ANTHROPIC_API_KEY||V.keys?.anthropic||"";if(G)Y.cfg.provider.apiKey=G;if(!V.baseUrls?.[q.provider])Y.cfg.provider.baseUrl=q.provider==="openai"?"https://api.openai.com":"https://api.anthropic.com";b({activeModel:q.id}),console.log(` ${B.ok} Model: ${z.blue(q.name)}`)}else{let q=await D(` ${z.gray("model ID")} ${z.blue("❯")} `);if(q.trim())Y.cfg.provider.model=q.trim(),b({activeModel:q.trim()}),console.log(` ${B.ok} Model: ${z.blue(q.trim())}`)}return!0}case"/auth":{let Z=await u("Provider",[{label:"Anthropic",desc:"Claude models"},{label:"OpenAI",desc:"GPT models"},{label:"Custom",desc:"Anthropic-compatible proxy"}]),q=["anthropic","openai","anthropic"][Z],V=await D(` ${z.gray("API Key")} ${z.blue("❯")} `);if(!V.trim())return console.log(` ${B.warn} Cancelled`),!0;let G={[q]:V.trim()},X={};if(Z===2){let K=await D(` ${z.gray("Base URL")} ${z.blue("❯")} `);if(K.trim())X[q]=K.trim()}if(b({keys:G,baseUrls:X}),Y.cfg.provider.apiKey=V.trim(),Y.cfg.provider.type=q,X[q])Y.cfg.provider.baseUrl=X[q];return console.log(` ${B.ok} ${q} key saved to ~/.tenicli/config.json`),!0}case"/help":return console.log(`
70
- ${z.bold("Commands")}
71
- ${z.blue("/model")} Select AI model
72
- ${z.blue("/auth")} Set API key
73
- ${z.blue("/mode")} Toggle ask/auto ${z.gray("(confirm before write/exec)")}
74
- ${z.blue("/compact")} Summarize conversation ${z.gray("(save tokens)")}
75
- ${z.blue("/diff")} List files changed this session
76
- ${z.blue("/undo")} Revert last file write
77
- ${z.blue("/init")} Create TENICLI.md template
78
- ${z.blue("/update")} Update to latest version
79
- ${z.blue("/clear")} New conversation
80
- ${z.blue("/cost")} Show token usage
81
- ${z.blue("/exit")} Quit
82
- ${z.gray("\\\\")} Continue on next line`),!0;case"/update":{console.log(`
83
- ${B.tool} ${z.yellow("Checking for updates...")}`);try{let{execSync:Z}=await import("child_process"),$=Z("npm i -g tenicli@latest 2>&1",{encoding:"utf8"});console.log(` ${B.ok} ${z.green("Updated!")} Restart teni to use the new version.`),console.log(z.gray(` ${$.trim().split(`
84
- `).pop()}`))}catch(Z){j(`Update failed: ${Z.message}`)}return!0}default:return console.log(` ${B.warn} Unknown: ${Q.split(" ")[0]} — try /help`),!0}}async function f0(){let Q=x0(process.argv.slice(2)),Y=a();if(Q.model)Y.provider.model=Q.model;if(Q.baseUrl)Y.provider.baseUrl=Q.baseUrl;let Z=new m(Y);if(Q.print&&Q.prompt){if(!Y.provider.apiKey)j("No API key. Run: teni then /auth"),process.exit(1);await Z.send(Q.prompt),process.exit(0)}o();let $=O.find((G)=>G.id===Y.provider.model)?.name||Y.provider.model,q=Z.autoMode?z.yellow("auto"):z.green("ask"),V=[`${z.gray("model")} ${z.blue($)} ${z.gray("mode")} ${q} ${z.gray("cwd")} ${z.cyan(Y.cwd)}`];if(!Y.provider.apiKey)V.push(""),V.push(`${B.warn} ${z.yellow("No API key configured. Run /auth to set one.")}`);if(g(V,60),console.log(),Q.prompt){if(console.log(` ${B.prompt} ${Q.prompt}`),Y.provider.apiKey)await Z.send(Q.prompt)}while(!0)try{let X=(await t()).trim();if(!X)continue;if(X.startsWith("/")){await y0(X,Z);continue}if(!Z.cfg.provider.apiKey){console.log(` ${B.warn} ${z.yellow("No API key. Run /auth first.")}`);continue}await Z.send(X)}catch(G){if(G.message==="EOF")console.log(`
85
- ${z.gray("Bye!")} \uD83D\uDC4B
86
- `),process.exit(0);j(G.message)}}f0().catch((Q)=>{j(Q.message),process.exit(1)});var v0=`# Project Instructions
67
+ `)}async function u0(Q,Y){switch(Q.toLowerCase().split(" ")[0]){case"/exit":case"/quit":case"/q":console.log(`
68
+ ${$.gray("Bye!")} \uD83D\uDC4B
69
+ `),process.exit(0);case"/clear":return Y.clear(),b.clear(),console.log(` ${_.ok} Conversation cleared`),!0;case"/compact":return await Y.compact(),!0;case"/diff":{let Z=b.getChanges();if(Z.length===0)console.log(` ${$.gray("No files changed in this session.")}`);else{console.log(`
70
+ ${$.bold("Files changed this session:")}`);for(let q of Z){let z=J0(Y.cfg.cwd,q.path),X=q.isNew?$.green("[NEW]"):$.yellow("[MOD]");console.log(` ${X} ${$.cyan(z)} ${$.gray(`(${q.lines} lines)`)}`)}console.log(` ${$.gray(`total: ${Z.length} files`)}`)}return!0}case"/undo":{let Z=b.undo();if(!Z)console.log(` ${$.gray("Nothing to undo.")}`);else{let q=J0(Y.cfg.cwd,Z.path);if(Z.restored)console.log(` ${_.ok} Restored: ${$.cyan(q)}`);else console.log(` ${_.ok} Deleted (was new): ${$.cyan(q)}`)}return!0}case"/init":{let Z=f0(Y.cfg.cwd,"TENICLI.md");if(v0(Z))console.log(` ${_.warn} TENICLI.md already exists.`);else h0(Z,d0,"utf8"),console.log(` ${_.ok} Created ${$.cyan("TENICLI.md")}`);return!0}case"/mode":{Y.autoMode=!Y.autoMode;let Z=Y.autoMode?$.yellow("auto"):$.green("ask");return console.log(` ${_.ok} Mode: ${Z} ${$.gray(Y.autoMode?"(tools run without asking)":"(confirm write/exec)")}`),!0}case"/cost":{let Z=Y.stats;return console.log(` ${_.ai} ${$.blue(String(Z.input))}↑ input ${$.blue(String(Z.output))}↓ output ${$.gray(`(${Y.messageCount} msgs)`)}`),!0}case"/model":{let Z=P.map((z)=>({label:`${z.name} ${Y.cfg.provider.model===z.id?$.green("●"):""}`,desc:`${z.provider} • ${z.speed}`}));Z.push({label:"Custom model...",desc:"type model ID"});let q=await l("Select model",Z);if(q<P.length){let z=P[q];Y.cfg.provider.model=z.id,Y.cfg.provider.type=z.provider;let X=w(),G=z.provider==="openai"?process.env.OPENAI_API_KEY||X.keys?.openai||"":process.env.ANTHROPIC_API_KEY||X.keys?.anthropic||"";if(G)Y.cfg.provider.apiKey=G;if(!X.baseUrls?.[z.provider])Y.cfg.provider.baseUrl=z.provider==="openai"?"https://api.openai.com":"https://api.anthropic.com";x({activeModel:z.id}),console.log(` ${_.ok} Model: ${$.blue(z.name)}`)}else{let z=await F(` ${$.gray("model ID")} ${$.blue("❯")} `);if(z.trim())Y.cfg.provider.model=z.trim(),x({activeModel:z.trim()}),console.log(` ${_.ok} Model: ${$.blue(z.trim())}`)}return!0}case"/auth":{let Z=await l("Provider",[{label:"Anthropic",desc:"Claude models"},{label:"OpenAI",desc:"GPT models"},{label:"Custom",desc:"Anthropic-compatible proxy"}]),z=["anthropic","openai","anthropic"][Z],X=await F(` ${$.gray("API Key")} ${$.blue("❯")} `);if(!X.trim())return console.log(` ${_.warn} Cancelled`),!0;let G={[z]:X.trim()},K={};if(Z===2){let V=await F(` ${$.gray("Base URL")} ${$.blue("❯")} `);if(V.trim())K[z]=V.trim()}if(x({keys:G,baseUrls:K}),Y.cfg.provider.apiKey=X.trim(),Y.cfg.provider.type=z,K[z])Y.cfg.provider.baseUrl=K[z];return console.log(` ${_.ok} ${z} key saved to ~/.tenicli/config.json`),!0}case"/help":return console.log(`
71
+ ${$.bold("Commands")}
72
+ ${$.blue("/model")} Select AI model
73
+ ${$.blue("/auth")} Set API key
74
+ ${$.blue("/mode")} Toggle ask/auto ${$.gray("(confirm before write/exec)")}
75
+ ${$.blue("/compact")} Summarize conversation ${$.gray("(save tokens)")}
76
+ ${$.blue("/diff")} List files changed this session
77
+ ${$.blue("/undo")} Revert last file write
78
+ ${$.blue("/init")} Create TENICLI.md template
79
+ ${$.blue("/update")} Update to latest version
80
+ ${$.blue("/clear")} New conversation
81
+ ${$.blue("/cost")} Show token usage
82
+ ${$.blue("/exit")} Quit
83
+ ${$.gray("\\\\")} Continue on next line`),!0;case"/update":{console.log(`
84
+ ${_.tool} ${$.yellow("Checking for updates...")}`);try{let{execSync:Z}=await import("child_process"),q=Z("npm i -g tenicli@latest 2>&1",{encoding:"utf8"});console.log(` ${_.ok} ${$.green("Updated!")} Restart teni to use the new version.`),console.log($.gray(` ${q.trim().split(`
85
+ `).pop()}`))}catch(Z){I(`Update failed: ${Z.message}`)}return!0}default:return console.log(` ${_.warn} Unknown: ${Q.split(" ")[0]} — try /help`),!0}}async function m0(){let Q=g0(process.argv.slice(2)),Y=o();if(Q.model)Y.provider.model=Q.model;if(Q.baseUrl)Y.provider.baseUrl=Q.baseUrl;let Z=new i(Y);if(Q.print&&Q.prompt){if(!Y.provider.apiKey)I("No API key. Run: teni then /auth"),process.exit(1);await Z.send(Q.prompt),process.exit(0)}e(N0);let q=P.find((G)=>G.id===Y.provider.model)?.name||Y.provider.model,z=Z.autoMode?$.yellow("auto"):$.green("ask"),X=[`${$.gray("model")} ${$.blue(q)} ${$.gray("mode")} ${z} ${$.gray("cwd")} ${$.cyan(Y.cwd)}`];if(!Y.provider.apiKey)X.push(""),X.push(`${_.warn} ${$.yellow("No API key configured. Run /auth to set one.")}`);if(d(X,60),console.log(),Q.prompt){if(console.log(` ${_.prompt} ${Q.prompt}`),Y.provider.apiKey)await Z.send(Q.prompt)}while(!0)try{let K=(await Q0()).trim();if(!K)continue;if(K.startsWith("/")){await u0(K,Z);continue}if(!Z.cfg.provider.apiKey){console.log(` ${_.warn} ${$.yellow("No API key. Run /auth first.")}`);continue}await Z.send(K)}catch(G){if(G.message==="EOF")console.log(`
86
+ ${$.gray("Bye!")} \uD83D\uDC4B
87
+ `),process.exit(0);I(G.message)}}m0().catch((Q)=>{I(Q.message),process.exit(1)});var d0=`# Project Instructions
87
88
 
88
89
  ## Overview
89
90
  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.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Lightweight AI coding CLI — fast, compact, multi-provider",
5
5
  "type": "module",
6
6
  "bin": {