novacode 0.5.3 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -13
- package/dist/app-QfQR2FN9.mjs +21 -0
- package/dist/app-QfQR2FN9.mjs.map +1 -0
- package/dist/main.mjs +21 -21
- package/dist/main.mjs.map +1 -1
- package/package.json +1 -1
- package/src/provider/stream.ts +1 -3
- package/src/session/store.ts +27 -4
- package/src/tui/app.tsx +23 -216
- package/src/tui/components/liveArea.tsx +73 -0
- package/src/tui/components/message.tsx +113 -0
- package/src/tui/components/statusBar.tsx +58 -0
- package/dist/app-BZ42XPxw.mjs +0 -21
- package/dist/app-BZ42XPxw.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -7,9 +7,6 @@ Open-source, multi-provider coding agent.
|
|
|
7
7
|
<img width="1164" height="720" alt="result" src="https://github.com/user-attachments/assets/a456c41a-ec19-4a4d-b3b7-180e6b83acc3" />
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
|
-
|
|
11
|
-
Requires [Node.js](https://nodejs.org) >= 22.
|
|
12
|
-
|
|
13
10
|
```bash
|
|
14
11
|
# With npm
|
|
15
12
|
npm install -g novacode
|
|
@@ -24,16 +21,6 @@ Then use it anywhere:
|
|
|
24
21
|
nova
|
|
25
22
|
```
|
|
26
23
|
|
|
27
|
-
You can also run without installing:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
# With npx
|
|
31
|
-
npx novacode
|
|
32
|
-
|
|
33
|
-
# With bunx
|
|
34
|
-
bunx novacode
|
|
35
|
-
```
|
|
36
|
-
|
|
37
24
|
## Quick Start
|
|
38
25
|
|
|
39
26
|
### 1. Launch nova
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import{a as e,c as t,d as n,f as r,g as i,h as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,s as p,t as m,u as h}from"./main.mjs";import g from"chalk";import{Box as _,Static as v,Text as y,render as b,useInput as x}from"ink";import{useCallback as S,useEffect as C,useRef as w,useState as T}from"react";import{jsx as E,jsxs as D}from"react/jsx-runtime";function O(e){return typeof e.content==`string`?e.content:e.content.filter(e=>e.type===`text`).map(e=>e.type===`text`?e.text:``).join(``)}function k(e,t){return e.role!==`tool_result`||!(`tool`in e)||e.tool!==t?[]:O(e).split(`
|
|
2
|
+
`).filter(e=>e.trim().length>0)}function A(e,t){return c(e)>t*.8}async function j(e,t,n,r,i,a){if(!A(n,r.contextWindow))return{compacted:!1,msgsRemoved:0};let o=n.slice(0,-10);if(o.length===0)return{compacted:!1,msgsRemoved:0};let s=await M(o.map(e=>e.role===`user`?`User: ${O(e)}`:e.role===`assistant`?`Assistant: ${O(e)}`:e.role===`tool_result`&&`tool`in e?`Tool(${e.tool}): ${O(e).slice(0,200)}`:``).join(`
|
|
3
|
+
|
|
4
|
+
`),r,i,a);if(!s)return{compacted:!1,msgsRemoved:0};let c=[],l=[];for(let e of o)c.push(...k(e,`read`)),c.push(...k(e,`glob`)),l.push(...k(e,`write`)),l.push(...k(e,`edit`));let u=o.length;e.saveCompaction(t,s,[...new Set(c)],[...new Set(l)],u),e.truncateBeforeSeq(t,u+1);let d={role:`user`,content:`[Prior context summary]\n${s}`,ts:Date.now()};return e.append(t,d),{compacted:!0,summary:s,msgsRemoved:o.length}}async function M(e,t,n,r){let a=d(t.provider);if(!a)return null;let o=i({api:a.api,model:t,apiKey:n,baseUrl:r,system:`Summarize this coding session concisely. Cover: what was asked, files touched, what was done, key decisions. Keep it under 300 words.`,messages:[{role:`user`,content:e,ts:Date.now()}],tools:[]}),s=``;for await(let e of o)e.type===`text_delta`&&e.text&&(s+=e.text);return s.trim()||null}async function N(e,t,n){let r=await j(t,n,e.messages,e.model,e.apiKey,e.baseUrl);if(r.compacted){let i=t.messages(n);return e.setMessages(i),g.green(`✓ Context compacted (${r.msgsRemoved} messages removed)`)}return g.yellow(`Context is small enough, no compaction needed.`)}async function P(e,r,i){let a=await t(),o=await p();if(e)return await F(e.trim(),r);if(!i)return g.red(`Prompts not available in this context`);let s=[];for(let e of n){let t=e.id===a.model&&e.provider===a.provider,n=d(e.provider);n&&o.apiKeys[e.provider]&&s.push({value:`${e.provider}:${e.id}`,label:`${t?g.green(`●`):`○`} ${e.id.padEnd(20)} ${I(e.contextWindow).padEnd(8)}`,hint:n.name})}if(!s.length)return g.yellow(`No models available. Use /providers to add a provider API key.`);let c=await i.select({message:`Model`,options:s});if(!c)return``;let[l,u]=c.split(`:`),f=n.find(e=>e.provider===l&&e.id===u),m=d(l);return!f||!m?g.red(`Error: Model or provider not found`):(a.provider=l,a.model=u,await h(a),r.updateConfig({api:m.api,model:f,apiKey:o.apiKeys[l]??``,baseUrl:m.baseUrl}),g.green(`✓ Switched to ${u}`))}async function F(e,r){let i=await t(),a=await p(),o=n.find(t=>t.id===e);if(!o)return g.yellow(`"${e}" not found. Use /models`);let s=o.provider;if(!a.apiKeys[s])return g.yellow(`No API key configured for ${s}. Use /providers`);let c=d(s);return c?(i.provider=s,i.model=e,await h(i),r.updateConfig({api:c.api,model:o,apiKey:a.apiKeys[s],baseUrl:c.baseUrl}),g.green(`✓ Switched to ${e}`)):g.red(`Error: Provider not found`)}const I=e=>e>=1e6?`${e/1e6}M`:`${e/1e3}K`;async function L(e,i){if(!i)return g.red(`Prompts not available in this context`);let a=await t(),o=await p(),s=r.filter(e=>!!o.apiKeys[e.id]),c=s.length===0?g.dim(`No providers configured. Use 'Add Provider' below.`):s.map(e=>{let t=e.id===a.provider,r=t?g.green(` ●`):``,i=t?a.model:n.find(t=>t.provider===e.id)?.id??``;return` ✅ ${e.name.padEnd(24)} ${i}${r}`}).join(`
|
|
5
|
+
`),l=await i.select({message:`Action`,header:c,options:[{value:`add`,label:`Add Provider`},{value:`update`,label:`Update API Key`},{value:`remove`,label:`Remove API Key`},{value:`default`,label:`Set Default Provider`},{value:`back`,label:`Back`}]});return!l||l===`back`?``:l===`add`?R(e,i):l===`update`?z(e,i):l===`remove`?B(e,i):l===`default`?V(e,i):``}async function R(e,i){let a=await p(),o=await t(),c=r.filter(e=>!a.apiKeys[e.id]);if(c.length===0)return g.yellow(`All providers already have API keys configured.`);let l=await i.select({message:`Add Provider`,options:c.map(e=>({value:e.id,label:e.name}))});if(!l)return``;let u=d(l);if(!u)return g.red(`Error: Provider not found`);let f=await i.password({message:`${u.name} API Key`,validate:e=>!e||e.length<8?`Enter a valid key`:void 0});if(!f)return``;if(a.apiKeys[u.id]=f,await s(a),!o.provider){o.provider=u.id;let t=n.find(e=>e.provider===u.id);t&&(o.model=t.id),await h(o),e.updateConfig({api:u.api,model:n.find(e=>e.id===o.model),apiKey:f,baseUrl:u.baseUrl})}return g.green(`✓ ${u.name} configured`)}async function z(e,i){let a=await p(),o=r.filter(e=>!!a.apiKeys[e.id]);if(o.length===0)return g.yellow(`No providers configured. Use 'Add Provider' first.`);let c=await i.select({message:`Update API Key`,options:o.map(e=>({value:e.id,label:e.name}))});if(!c)return``;let l=d(c);if(!l)return g.red(`Error: Provider not found`);let u=await i.password({message:`New key for ${l.name}`});if(!u)return``;a.apiKeys[l.id]=u,await s(a);let f=await t();if(f.provider===l.id){let t=n.find(e=>e.id===f.model&&e.provider===f.provider);t&&e.updateConfig({api:l.api,model:t,apiKey:u,baseUrl:l.baseUrl})}return g.green(`✓ Key updated`)}async function B(e,i){let a=await p(),o=await t(),c=r.filter(e=>!!a.apiKeys[e.id]);if(c.length===0)return g.yellow(`No configured providers to remove.`);let l=await i.select({message:`Remove API Key`,options:c.map(e=>({value:e.id,label:e.name}))});if(!l||!await i.confirm({message:`Are you sure you want to remove the API key for ${l}?`}))return``;if(delete a.apiKeys[l],await s(a),o.provider===l){o.provider=``,o.model=``;let t=Object.keys(a.apiKeys)[0];if(t){let r=d(t),i=n.find(e=>e.provider===t);r&&i&&(o.provider=t,o.model=i.id,e.updateConfig({api:r.api,model:i,apiKey:a.apiKeys[t],baseUrl:r.baseUrl}))}await h(o)}return g.green(`✓ Removed API key for ${l}`)}async function V(e,i){let a=await t(),o=await p(),s=await i.select({message:`Default Provider`,options:r.map(e=>({value:e.id,label:`${o.apiKeys[e.id]?`✅`:`❌`} ${e.name}`}))});if(!s)return``;if(!o.apiKeys[s])return g.yellow(`No API key for ${s}. Please set one first.`);let c=d(s),l=n.find(e=>e.provider===s);return!c||!l?g.red(`Error: Provider or model not found`):(a.provider=s,a.model=l.id,await h(a),e.updateConfig({api:c.api,model:l,apiKey:o.apiKeys[s],baseUrl:c.baseUrl}),g.green(`✓ Default set to ${c.name} (${l.id})`))}const H=[{name:`models`,desc:`Switch model`,aliases:[`model`]},{name:`providers`,desc:`Manage providers`,aliases:[`prov`,`config`,`cfg`]},{name:`compact`,desc:`Compact context`},{name:`update`,desc:`Update novacode`},{name:`help`,desc:`Show help`},{name:`clear`,desc:`Clear screen`},{name:`quit`,desc:`Exit (Ctrl+D)`,aliases:[`exit`]}],U=`
|
|
6
|
+
${g.bold(`Commands:`)}
|
|
7
|
+
${H.map(e=>` /${e.name.padEnd(12)} ${e.desc}`).join(`
|
|
8
|
+
`)}
|
|
9
|
+
|
|
10
|
+
${g.bold(`CLI:`)}
|
|
11
|
+
nova update Update to latest version
|
|
12
|
+
nova session ls List sessions
|
|
13
|
+
|
|
14
|
+
${g.dim(`Keys:`)}
|
|
15
|
+
Esc Abort
|
|
16
|
+
↑ / ↓ History
|
|
17
|
+
`;async function W(e,t,n,r,i){let[a,...o]=e.slice(1).split(` `),s=o.join(` `);switch(a){case`models`:case`model`:return P(s,t,i);case`providers`:case`prov`:case`config`:case`cfg`:return L(t,i);case`compact`:return!n||!r?g.red(`Session store not available`):N(t,n,r);case`update`:return G();case`help`:return U;case`clear`:return console.clear(),``;case`quit`:return process.exit(0),null;case`exit`:return process.exit(0),null;default:return g.yellow(`Unknown: /${a}. Type /help`)}}async function G(){let e=await m();return e?e.hasUpdate?(console.log(g.yellow(`\n⚡ Updating novacode to v${e.latest}...`)),await f(!0)?g.green(`✓ Successfully updated to v${e.latest}! Please restart nova to apply changes.`):g.red(`✗ Update failed. Please try running 'nova update' manually in your terminal.`)):g.green(`✓ Already up to date (v${e.current})`):g.yellow(`Could not check for updates.`)}var K=class{#e=!1;#t=``;renderLine(e){if(e.startsWith("```"))return this.#e?(this.#e=!1,g.dim(`└${`─`.repeat(50)}`)):(this.#e=!0,this.#t=e.slice(3).trim(),g.dim(`┌`+`─`.repeat(10)+` [Code: ${this.#t||`text`}] `+`─`.repeat(40-(this.#t?.length||4))));if(this.#e)return g.cyan(`│ ${e}`);if(e.startsWith(`#`)){let t=e.match(/^(#{1,6})\s+(.*)$/);if(t?.[1]&&t[2]){let e=t[1].length,n=t[2];return e===1?g.bold.magenta.underline(n):e===2?g.bold.blue(n):g.bold.cyan(n)}}let t=e;return(t.startsWith(`- `)||t.startsWith(`* `))&&(t=` ${g.yellow(`•`)} ${t.slice(2)}`),t=t.replace(/`([^`]+)`/g,(e,t)=>g.yellow(t)),t=t.replace(/\*\*([^*]+)\*\*/g,(e,t)=>g.bold(t)),t=t.replace(/__([^_]+)__/g,(e,t)=>g.bold(t)),t=t.replace(/\*([^*]+)\*/g,(e,t)=>g.italic(t)),t=t.replace(/_([^_]+)_/g,(e,t)=>g.italic(t)),t=t.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(e,t,n)=>`${g.blue(t)} ${g.dim(`(${n})`)}`),t}};function q(e){let t=new K;return e.split(`
|
|
18
|
+
`).map(e=>t.renderLine(e)).join(`
|
|
19
|
+
`)}const J=[`⠋`,`⠙`,`⠹`,`⠸`,`⠼`,`⠴`,`⠦`,`⠧`,`⠇`,`⠏`];function Y(){let[e,t]=T(0);return C(()=>{let e=setInterval(()=>{t(e=>(e+1)%J.length)},80);return()=>clearInterval(e)},[]),E(y,{color:`yellow`,children:J[e]})}function X(){let[e,t]=T(!0);return C(()=>{let e=setInterval(()=>t(e=>!e),530);return()=>clearInterval(e)},[]),E(y,{color:`green`,children:e?`│`:` `})}function Z({stream:e,thinkStream:t,busy:n,status:r,hasMessages:i}){return e||t||n?D(_,{flexDirection:`column`,marginTop:+!!i,children:[t&&E(y,{dimColor:!0,italic:!0,children:t}),e&&E(_,{flexDirection:`row`,children:E(_,{flexGrow:1,flexShrink:1,children:D(y,{children:[q(e),E(X,{})]})})}),n&&!e&&D(_,{flexDirection:`row`,children:[E(_,{marginRight:1,children:E(Y,{})}),E(y,{dimColor:!0,children:r?r.replace(`⏳ `,``):g.yellow(`working…`)})]})]}):null}const Q={read:`blue`,write:`magenta`,edit:`yellow`,bash:`cyan`,glob:`green`,find:`green`,grep:`green`,tree:`green`};function ee(e){return e.role===`user`||e.role===`tool_result`?!0:e.role===`assistant`?e.model===`system`?!0:e.content.some(e=>e.type===`thinking`||e.type===`text`?e.text.trim().length>0:!1):!1}function te({msg:e,isFirst:t}){if(e.role===`user`)return D(_,{marginTop:+!t,flexDirection:`row`,children:[E(_,{flexShrink:0,marginRight:1,children:E(y,{bold:!0,color:`green`,children:`>`})}),E(_,{flexGrow:1,flexShrink:1,children:E(y,{children:typeof e.content==`string`?e.content:e.content.map(e=>e.type===`text`?e.text:``).join(``)})})]});if(e.role===`assistant`)return e.model===`system`?E(_,{flexDirection:`column`,marginTop:0,children:e.content.map((e,t)=>e.type===`text`?E(y,{children:q(e.text)},t):null)}):e.content.some(e=>e.type===`text`||e.type===`thinking`)?E(_,{flexDirection:`column`,marginTop:0,children:e.content.map((e,t)=>e.type===`thinking`?E(y,{dimColor:!0,italic:!0,children:e.text},t):e.type===`text`?E(y,{children:q(e.text)},t):null)}):null;if(e.role===`tool_result`){let t=e.args?a(e.args,!0):``,n=e.content.map(e=>e.type===`text`?e.text:``).join(``).trim(),r=e.tool===`read`,i=r?n.split(`
|
|
20
|
+
`).length:0,o=Q[e.tool]||`white`;return D(_,{flexDirection:`row`,children:[D(y,{color:e.isError?`red`:`green`,children:[e.isError?`✗`:`✓`,` `]}),E(y,{color:o,bold:!0,children:e.tool}),t&&D(y,{children:[` `,t]}),r&&!e.isError&&D(y,{dimColor:!0,children:[` (`,i,` lines)`]}),e.isError&&n&&D(y,{color:`red`,children:[` `,n.slice(0,80)]})]})}return null}function $(e){let t=e/1e3;return t>=100?`${Math.round(t)}K`:`${t.toFixed(1)}K`}function ne(e,t){if(e===0)return`0/${$(t)}`;let n=Math.round(e/t*100);return`${$(e)}/${$(t)} (${n}%)`}function re({model:e,usage:t,busy:n,suggestions:r,selCmdIdx:i}){return D(_,{justifyContent:`space-between`,children:[E(_,{children:r.length>0?E(_,{flexDirection:`column`,marginLeft:2,children:r.map((e,t)=>D(_,{children:[D(y,{color:t===i?`black`:`yellow`,backgroundColor:t===i?`yellow`:void 0,children:[`/`,e.name.padEnd(10)]}),D(y,{dimColor:!0,children:[` `,e.desc]})]},e.name))}):E(y,{dimColor:!0,children:`Enter to send · /help for commands`})}),D(_,{children:[E(y,{dimColor:!0,children:ne(t.in,e.contextWindow)}),E(y,{dimColor:!0,children:` │ `}),E(y,{dimColor:!0,children:e.id}),n&&E(y,{dimColor:!0,children:` │ Esc to stop`})]})]})}async function ie(e,t,n){process.stdout.write(`\x1B[?25l`);let r=await l();process.stdout.write(`${g.cyan.bold(`⚡ novacode`)} ${g.gray(`v${r}`)}\n`);try{let{waitUntilExit:r}=b(E(ae,{agent:e,store:t,sessionId:n}));await r()}finally{process.stdout.write(`\x1B[?25h`)}}function ae({agent:t,store:n,sessionId:r}){let[i,a]=T(t.messages),[s,c]=T(``),[l,d]=T(``),[f,p]=T(!1),[h,b]=T(``),[O,k]=T(``),[A,j]=T({in:0,out:0}),[M,N]=T(0),[P,F]=T({type:`chat`}),I=w(null),L=w([]),R=w(-1),z=w(null),[B,V]=T(null);C(()=>{(async()=>{let e=await m();e?.hasUpdate&&V({current:e.current,latest:e.latest})})()},[]);let U=h.startsWith(`/`)&&!h.includes(` `),G=U?H.filter(e=>e.name.startsWith(h.slice(1).toLowerCase())||e.aliases?.some(e=>e.startsWith(h.slice(1).toLowerCase()))):[],K={select:S(e=>new Promise(t=>{I.current=t,F({type:`select`,...e})}),[]),password:S(e=>new Promise(t=>{I.current=t,F({type:`password`,...e})}),[]),confirm:S(e=>new Promise(t=>{I.current=t,F({type:`confirm`,...e})}),[])};function q(e){let t=I.current;I.current=null,F({type:`chat`}),t?.(e)}function J(e){a(t=>[...t,e]),t.setMessages([...t.messages,e]),n.append(r,e)}C(()=>{N(0)},[h]),x((e,i)=>{if(P.type!==`chat`)return;if(i.escape){z.current&&=(z.current.abort(),null);return}if(i.upArrow){if(U&&G.length>0){N(e=>e>0?e-1:G.length-1);return}L.current.length>0&&(R.current=Math.min(R.current+1,L.current.length-1),b(L.current[R.current]??``));return}if(i.downArrow){if(U&&G.length>0){N(e=>e<G.length-1?e+1:0);return}R.current=Math.max(R.current-1,-1),b(R.current>=0?L.current[R.current]??``:``);return}if(i.tab){if(U&&G.length>0){let e=G[M];e&&b(`/${e.name} `)}return}if(!i.return){b(t=>i.backspace||i.delete?t.slice(0,-1):t+(e||``));return}if(f)return;let a=h.trim();if(a){if(U&&G.length>0){let e=G[M];e&&(a=`/${e.name}`)}if(b(``),L.current.unshift(a),R.current=-1,a.startsWith(`/`)){W(a,t,n,r,K).then(e=>{e&&J({role:`assistant`,content:[{type:`text`,text:e}],model:`system`,provider:`system`,usage:{in:0,out:0},stop:`stop`,ts:Date.now()})});return}J({role:`user`,content:a,ts:Date.now()}),z.current=new AbortController,Y(t.prompt(a,z.current.signal))}});async function Y(e){try{for await(let t of e)switch(t.type){case`start`:p(!0),c(``),d(``),k(``);break;case`text_delta`:t.text&&c(e=>e+t.text);break;case`thinking_delta`:t.text&&d(e=>e+t.text);break;case`assistant_msg`:J(t.msg),c(``),d(``);break;case`tool_call`:k(g.dim(`⏳ ${t.call.name}…`));break;case`tool_result`:J(t.result),k(t.result.isError?g.red(`✗ ${t.result.tool}`):g.green(`✓ ${t.result.tool}`));break;case`turn_end`:k(``);break;case`usage`:t.usage&&j(t.usage)}}catch(e){J({role:`assistant`,model:`system`,provider:`system`,content:[{type:`text`,text:g.red(`Error: ${e.message}`)}],usage:{in:0,out:0},stop:`error`,ts:Date.now()})}finally{z.current=null,p(!1),c(``),d(``),k(``)}}if(P.type===`select`)return E(u,{message:P.message,options:P.options,header:P.header,onSelect:q});if(P.type===`password`)return E(e,{message:P.message,validate:P.validate,onSubmit:q});if(P.type===`confirm`)return E(o,{message:P.message,onConfirm:q});let Q=i.filter(ee),$=!!(s||l||f);return D(_,{flexDirection:`column`,paddingX:1,children:[E(v,{items:Q,children:(e,t)=>E(te,{msg:e,isFirst:t===0},`${e.ts}-${t}`)}),E(Z,{stream:s,thinkStream:l,busy:f,status:O,hasMessages:Q.length>0}),D(_,{flexDirection:`column`,marginTop:Q.length>0||$?1:0,children:[B&&D(_,{borderStyle:`round`,borderColor:`yellow`,paddingX:1,marginBottom:1,flexDirection:`column`,children:[D(y,{color:`yellow`,bold:!0,children:[`⬆ Update Available (v`,B.current,` → v`,B.latest,`)`]}),D(y,{dimColor:!0,children:[`Run `,E(y,{color:`cyan`,children:`/update`}),` or `,E(y,{color:`cyan`,children:`nova update`}),` to upgrade.`]})]}),D(_,{flexDirection:`row`,children:[E(_,{flexShrink:0,marginRight:1,children:E(y,{bold:!0,color:`green`,children:`>`})}),E(_,{flexGrow:1,flexShrink:1,children:D(y,{children:[h,E(X,{})]})})]}),E(re,{model:t.model,usage:A,busy:f,suggestions:G,selCmdIdx:M})]})]})}export{ie as interactive};
|
|
21
|
+
//# sourceMappingURL=app-QfQR2FN9.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-QfQR2FN9.mjs","names":["runCompact","#inCodeBlock","#codeBlockLang"],"sources":["../src/session/compact.ts","../src/commands/compact.ts","../src/commands/models.ts","../src/commands/providers.ts","../src/commands/index.ts","../src/tui/markdown.ts","../src/tui/components/liveArea.tsx","../src/tui/components/message.tsx","../src/tui/components/statusBar.tsx","../src/tui/app.tsx"],"sourcesContent":["import { getProvider } from \"../config/providers.ts\"\nimport { stream } from \"../provider/stream.ts\"\nimport type { Model, Msg } from \"../types.ts\"\nimport { estimateTokens } from \"../util.ts\"\nimport type { SessionStore } from \"./store.ts\"\n\nconst COMPACT_THRESHOLD = 0.8\nconst KEEP_RECENT = 10\n\nfunction extractText(msg: Msg): string {\n\tif (typeof msg.content === \"string\") return msg.content\n\treturn msg.content\n\t\t.filter((c) => c.type === \"text\")\n\t\t.map((c) => (c.type === \"text\" ? c.text : \"\"))\n\t\t.join(\"\")\n}\n\nfunction extractToolFiles(msg: Msg, toolName: string): string[] {\n\tif (msg.role !== \"tool_result\") return []\n\tif (!(\"tool\" in msg) || msg.tool !== toolName) return []\n\tconst text = extractText(msg)\n\t// Extract file paths from tool result content\n\tconst lines = text.split(\"\\n\")\n\treturn lines.filter((l) => l.trim().length > 0)\n}\n\nexport interface CompactResult {\n\tcompacted: boolean\n\tsummary?: string\n\tmsgsRemoved: number\n}\n\nexport function needsCompact(messages: Msg[], contextWindow: number): boolean {\n\treturn estimateTokens(messages) > contextWindow * COMPACT_THRESHOLD\n}\n\nexport async function compact(\n\tstore: SessionStore,\n\tsessionId: string,\n\tmessages: Msg[],\n\tmodel: Model,\n\tapiKey: string,\n\tbaseUrl: string,\n): Promise<CompactResult> {\n\tif (!needsCompact(messages, model.contextWindow)) {\n\t\treturn { compacted: false, msgsRemoved: 0 }\n\t}\n\n\tconst old = messages.slice(0, -KEEP_RECENT)\n\tif (old.length === 0) {\n\t\treturn { compacted: false, msgsRemoved: 0 }\n\t}\n\tconst convo = old\n\t\t.map((m) => {\n\t\t\tif (m.role === \"user\") return `User: ${extractText(m)}`\n\t\t\tif (m.role === \"assistant\") return `Assistant: ${extractText(m)}`\n\t\t\tif (m.role === \"tool_result\" && \"tool\" in m)\n\t\t\t\treturn `Tool(${m.tool}): ${extractText(m).slice(0, 200)}`\n\t\t\treturn \"\"\n\t\t})\n\t\t.join(\"\\n\\n\")\n\n\tconst summary = await generateSummary(convo, model, apiKey, baseUrl)\n\tif (!summary) {\n\t\treturn { compacted: false, msgsRemoved: 0 }\n\t}\n\n\tconst filesRead: string[] = []\n\tconst filesWrote: string[] = []\n\tfor (const m of old) {\n\t\tfilesRead.push(...extractToolFiles(m, \"read\"))\n\t\tfilesRead.push(...extractToolFiles(m, \"glob\"))\n\t\tfilesWrote.push(...extractToolFiles(m, \"write\"))\n\t\tfilesWrote.push(...extractToolFiles(m, \"edit\"))\n\t}\n\n\tconst seqBefore = old.length\n\tstore.saveCompaction(\n\t\tsessionId,\n\t\tsummary,\n\t\t[...new Set(filesRead)],\n\t\t[...new Set(filesWrote)],\n\t\tseqBefore,\n\t)\n\tstore.truncateBeforeSeq(sessionId, seqBefore + 1)\n\n\t// Insert the summary as a user message so the model retains context\n\tconst summaryMsg: Msg = {\n\t\trole: \"user\",\n\t\tcontent: `[Prior context summary]\\n${summary}`,\n\t\tts: Date.now(),\n\t}\n\tstore.append(sessionId, summaryMsg)\n\n\treturn { compacted: true, summary, msgsRemoved: old.length }\n}\n\nasync function generateSummary(\n\tconvo: string,\n\tmodel: Model,\n\tapiKey: string,\n\tbaseUrl: string,\n): Promise<string | null> {\n\tconst provider = getProvider(model.provider)\n\tif (!provider) return null\n\n\tconst es = stream({\n\t\tapi: provider.api,\n\t\tmodel,\n\t\tapiKey,\n\t\tbaseUrl,\n\t\tsystem:\n\t\t\t\"Summarize this coding session concisely. Cover: what was asked, files touched, what was done, key decisions. Keep it under 300 words.\",\n\t\tmessages: [{ role: \"user\", content: convo, ts: Date.now() }],\n\t\ttools: [],\n\t})\n\n\tlet summary = \"\"\n\tfor await (const ev of es) {\n\t\tif (ev.type === \"text_delta\" && ev.text) {\n\t\t\tsummary += ev.text\n\t\t}\n\t}\n\n\treturn summary.trim() || null\n}\n","import chalk from \"chalk\"\nimport type { Agent } from \"../agent/agent.ts\"\nimport { compact as runCompact } from \"../session/compact.ts\"\nimport type { SessionStore } from \"../session/store.ts\"\n\nexport async function handleCompact(\n\tagent: Agent,\n\tstore: SessionStore,\n\tsessionId: string,\n): Promise<string> {\n\tconst res = await runCompact(\n\t\tstore,\n\t\tsessionId,\n\t\tagent.messages,\n\t\tagent.model,\n\t\tagent.apiKey,\n\t\tagent.baseUrl,\n\t)\n\n\tif (res.compacted) {\n\t\t// Update agent messages\n\t\tconst msgs = store.messages(sessionId)\n\t\tagent.setMessages(msgs)\n\t\treturn chalk.green(`✓ Context compacted (${res.msgsRemoved} messages removed)`)\n\t}\n\n\treturn chalk.yellow(\"Context is small enough, no compaction needed.\")\n}\n","import chalk from \"chalk\"\nimport type { Agent } from \"../agent/agent.ts\"\nimport { getProvider, MODELS } from \"../config/providers.ts\"\nimport { loadAuth, loadConfig, saveConfig } from \"../config/store.ts\"\nimport type { Prompts } from \"../types.ts\"\n\nexport async function handleModels(args: string, agent: Agent, prompts?: Prompts): Promise<string> {\n\tconst config = await loadConfig()\n\tconst auth = await loadAuth()\n\n\tif (args) return await switchDirect(args.trim(), agent)\n\n\tif (!prompts) return chalk.red(\"Prompts not available in this context\")\n\n\tconst options: Array<{ value: string; label: string; hint?: string }> = []\n\tfor (const m of MODELS) {\n\t\tconst cur = m.id === config.model && m.provider === config.provider\n\t\tconst pDef = getProvider(m.provider)\n\t\tif (!pDef) continue\n\n\t\tconst hasKey = !!auth.apiKeys[m.provider]\n\t\tif (!hasKey) continue\n\n\t\toptions.push({\n\t\t\tvalue: `${m.provider}:${m.id}`,\n\t\t\tlabel: `${cur ? chalk.green(\"●\") : \"○\"} ${m.id.padEnd(20)} ${fmt(m.contextWindow).padEnd(8)}`,\n\t\t\thint: pDef.name,\n\t\t})\n\t}\n\n\tif (!options.length)\n\t\treturn chalk.yellow(\"No models available. Use /providers to add a provider API key.\")\n\n\tconst pick = await prompts.select({ message: \"Model\", options })\n\tif (!pick) return \"\"\n\n\tconst [pk, mid] = pick.split(\":\")\n\tconst selectedModel = MODELS.find((m) => m.provider === pk && m.id === mid)\n\tconst selectedProvider = getProvider(pk!)\n\n\tif (!selectedModel || !selectedProvider) return chalk.red(\"Error: Model or provider not found\")\n\n\tconfig.provider = pk!\n\tconfig.model = mid!\n\tawait saveConfig(config)\n\n\tagent.updateConfig({\n\t\tapi: selectedProvider.api,\n\t\tmodel: selectedModel,\n\t\tapiKey: auth.apiKeys[pk!] ?? \"\",\n\t\tbaseUrl: selectedProvider.baseUrl,\n\t})\n\treturn chalk.green(`✓ Switched to ${mid}`)\n}\n\nasync function switchDirect(id: string, agent: Agent): Promise<string> {\n\tconst config = await loadConfig()\n\tconst auth = await loadAuth()\n\n\tconst m = MODELS.find((m) => m.id === id)\n\tif (!m) return chalk.yellow(`\"${id}\" not found. Use /models`)\n\n\tconst pk = m.provider\n\tif (!auth.apiKeys[pk]) {\n\t\treturn chalk.yellow(`No API key configured for ${pk}. Use /providers`)\n\t}\n\n\tconst selectedProvider = getProvider(pk)\n\tif (!selectedProvider) return chalk.red(\"Error: Provider not found\")\n\n\tconfig.provider = pk\n\tconfig.model = id\n\tawait saveConfig(config)\n\n\tagent.updateConfig({\n\t\tapi: selectedProvider.api,\n\t\tmodel: m,\n\t\tapiKey: auth.apiKeys[pk],\n\t\tbaseUrl: selectedProvider.baseUrl,\n\t})\n\n\treturn chalk.green(`✓ Switched to ${id}`)\n}\n\nconst fmt = (n: number) => (n >= 1_000_000 ? `${n / 1_000_000}M` : `${n / 1000}K`)\n","import chalk from \"chalk\"\nimport type { Agent } from \"../agent/agent.ts\"\nimport { getProvider, MODELS, PROVIDERS } from \"../config/providers.ts\"\nimport { loadAuth, loadConfig, saveAuth, saveConfig } from \"../config/store.ts\"\nimport type { Prompts } from \"../types.ts\"\n\nexport async function handleProviders(agent: Agent, prompts?: Prompts): Promise<string> {\n\tif (!prompts) return chalk.red(\"Prompts not available in this context\")\n\n\tconst config = await loadConfig()\n\tconst auth = await loadAuth()\n\tconst configured = PROVIDERS.filter((p) => !!auth.apiKeys[p.id])\n\n\tconst headerLines =\n\t\tconfigured.length === 0\n\t\t\t? chalk.dim(\"No providers configured. Use 'Add Provider' below.\")\n\t\t\t: configured\n\t\t\t\t\t.map((p) => {\n\t\t\t\t\t\tconst isDefault = p.id === config.provider\n\t\t\t\t\t\tconst active = isDefault ? chalk.green(\" ●\") : \"\"\n\t\t\t\t\t\tconst currentModel = isDefault\n\t\t\t\t\t\t\t? config.model\n\t\t\t\t\t\t\t: (MODELS.find((m) => m.provider === p.id)?.id ?? \"\")\n\t\t\t\t\t\treturn ` ✅ ${p.name.padEnd(24)} ${currentModel}${active}`\n\t\t\t\t\t})\n\t\t\t\t\t.join(\"\\n\")\n\n\tconst act = await prompts.select({\n\t\tmessage: \"Action\",\n\t\theader: headerLines,\n\t\toptions: [\n\t\t\t{ value: \"add\", label: \"Add Provider\" },\n\t\t\t{ value: \"update\", label: \"Update API Key\" },\n\t\t\t{ value: \"remove\", label: \"Remove API Key\" },\n\t\t\t{ value: \"default\", label: \"Set Default Provider\" },\n\t\t\t{ value: \"back\", label: \"Back\" },\n\t\t],\n\t})\n\tif (!act || act === \"back\") return \"\"\n\n\tif (act === \"add\") return addProvider(agent, prompts)\n\tif (act === \"update\") return updateKey(agent, prompts)\n\tif (act === \"remove\") return removeKey(agent, prompts)\n\tif (act === \"default\") return setDefault(agent, prompts)\n\treturn \"\"\n}\n\nasync function addProvider(agent: Agent, prompts: Prompts): Promise<string> {\n\tconst auth = await loadAuth()\n\tconst config = await loadConfig()\n\n\tconst available = PROVIDERS.filter((p) => !auth.apiKeys[p.id])\n\tif (available.length === 0) {\n\t\treturn chalk.yellow(\"All providers already have API keys configured.\")\n\t}\n\n\tconst pick = await prompts.select({\n\t\tmessage: \"Add Provider\",\n\t\toptions: available.map((p) => ({ value: p.id, label: p.name })),\n\t})\n\tif (!pick) return \"\"\n\n\tconst pDef = getProvider(pick)\n\tif (!pDef) return chalk.red(\"Error: Provider not found\")\n\n\tconst key = await prompts.password({\n\t\tmessage: `${pDef.name} API Key`,\n\t\tvalidate: (v) => (!v || v.length < 8 ? \"Enter a valid key\" : undefined),\n\t})\n\tif (!key) return \"\"\n\n\tauth.apiKeys[pDef.id] = key\n\tawait saveAuth(auth)\n\n\tif (!config.provider) {\n\t\tconfig.provider = pDef.id\n\t\tconst mDef = MODELS.find((m) => m.provider === pDef.id)\n\t\tif (mDef) {\n\t\t\tconfig.model = mDef.id\n\t\t}\n\t\tawait saveConfig(config)\n\t\tagent.updateConfig({\n\t\t\tapi: pDef.api,\n\t\t\tmodel: MODELS.find((m) => m.id === config.model)!,\n\t\t\tapiKey: key,\n\t\t\tbaseUrl: pDef.baseUrl,\n\t\t})\n\t}\n\n\treturn chalk.green(`✓ ${pDef.name} configured`)\n}\n\nasync function updateKey(agent: Agent, prompts: Prompts): Promise<string> {\n\tconst auth = await loadAuth()\n\n\tconst configured = PROVIDERS.filter((p) => !!auth.apiKeys[p.id])\n\tif (configured.length === 0) {\n\t\treturn chalk.yellow(\"No providers configured. Use 'Add Provider' first.\")\n\t}\n\n\tconst pick = await prompts.select({\n\t\tmessage: \"Update API Key\",\n\t\toptions: configured.map((p) => ({ value: p.id, label: p.name })),\n\t})\n\tif (!pick) return \"\"\n\n\tconst pDef = getProvider(pick)\n\tif (!pDef) return chalk.red(\"Error: Provider not found\")\n\n\tconst key = await prompts.password({ message: `New key for ${pDef.name}` })\n\tif (!key) return \"\"\n\n\tauth.apiKeys[pDef.id] = key\n\tawait saveAuth(auth)\n\n\tconst config = await loadConfig()\n\tif (config.provider === pDef.id) {\n\t\tconst currentModel = MODELS.find((m) => m.id === config.model && m.provider === config.provider)\n\t\tif (currentModel) {\n\t\t\tagent.updateConfig({\n\t\t\t\tapi: pDef.api,\n\t\t\t\tmodel: currentModel,\n\t\t\t\tapiKey: key,\n\t\t\t\tbaseUrl: pDef.baseUrl,\n\t\t\t})\n\t\t}\n\t}\n\n\treturn chalk.green(\"✓ Key updated\")\n}\n\nasync function removeKey(agent: Agent, prompts: Prompts): Promise<string> {\n\tconst auth = await loadAuth()\n\tconst config = await loadConfig()\n\n\tconst configured = PROVIDERS.filter((p) => !!auth.apiKeys[p.id])\n\tif (configured.length === 0) {\n\t\treturn chalk.yellow(\"No configured providers to remove.\")\n\t}\n\n\tconst pick = await prompts.select({\n\t\tmessage: \"Remove API Key\",\n\t\toptions: configured.map((p) => ({ value: p.id, label: p.name })),\n\t})\n\tif (!pick) return \"\"\n\n\tconst confirm = await prompts.confirm({\n\t\tmessage: `Are you sure you want to remove the API key for ${pick}?`,\n\t})\n\tif (!confirm) return \"\"\n\n\tdelete auth.apiKeys[pick]\n\tawait saveAuth(auth)\n\n\tif (config.provider === pick) {\n\t\tconfig.provider = \"\"\n\t\tconfig.model = \"\"\n\t\tconst next = Object.keys(auth.apiKeys)[0]\n\t\tif (next) {\n\t\t\tconst pDef = getProvider(next)\n\t\t\tconst mDef = MODELS.find((m) => m.provider === next)\n\t\t\tif (pDef && mDef) {\n\t\t\t\tconfig.provider = next\n\t\t\t\tconfig.model = mDef.id\n\t\t\t\tagent.updateConfig({\n\t\t\t\t\tapi: pDef.api,\n\t\t\t\t\tmodel: mDef,\n\t\t\t\t\tapiKey: auth.apiKeys[next]!,\n\t\t\t\t\tbaseUrl: pDef.baseUrl,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\tawait saveConfig(config)\n\t}\n\n\treturn chalk.green(`✓ Removed API key for ${pick}`)\n}\n\nasync function setDefault(agent: Agent, prompts: Prompts): Promise<string> {\n\tconst config = await loadConfig()\n\tconst auth = await loadAuth()\n\n\tconst pick = await prompts.select({\n\t\tmessage: \"Default Provider\",\n\t\toptions: PROVIDERS.map((p) => ({\n\t\t\tvalue: p.id,\n\t\t\tlabel: `${auth.apiKeys[p.id] ? \"✅\" : \"❌\"} ${p.name}`,\n\t\t})),\n\t})\n\tif (!pick) return \"\"\n\n\tif (!auth.apiKeys[pick]) {\n\t\treturn chalk.yellow(`No API key for ${pick}. Please set one first.`)\n\t}\n\n\tconst pDef = getProvider(pick)\n\tconst mDef = MODELS.find((m) => m.provider === pick)\n\n\tif (!pDef || !mDef) return chalk.red(\"Error: Provider or model not found\")\n\n\tconfig.provider = pick\n\tconfig.model = mDef.id\n\tawait saveConfig(config)\n\n\tagent.updateConfig({\n\t\tapi: pDef.api,\n\t\tmodel: mDef,\n\t\tapiKey: auth.apiKeys[pick],\n\t\tbaseUrl: pDef.baseUrl,\n\t})\n\n\treturn chalk.green(`✓ Default set to ${pDef.name} (${mDef.id})`)\n}\n","import chalk from \"chalk\"\nimport type { Agent } from \"../agent/agent.ts\"\nimport type { SessionStore } from \"../session/store.ts\"\nimport type { Cmd, Prompts } from \"../types.ts\"\nimport { checkForUpdate, runUpdate } from \"../update.ts\"\nimport { handleCompact } from \"./compact.ts\"\nimport { handleModels } from \"./models.ts\"\nimport { handleProviders } from \"./providers.ts\"\n\nexport const COMMANDS: Cmd[] = [\n\t{ name: \"models\", desc: \"Switch model\", aliases: [\"model\"] },\n\t{ name: \"providers\", desc: \"Manage providers\", aliases: [\"prov\", \"config\", \"cfg\"] },\n\t{ name: \"compact\", desc: \"Compact context\" },\n\t{ name: \"update\", desc: \"Update novacode\" },\n\t{ name: \"help\", desc: \"Show help\" },\n\t{ name: \"clear\", desc: \"Clear screen\" },\n\t{ name: \"quit\", desc: \"Exit (Ctrl+D)\", aliases: [\"exit\"] },\n]\n\nconst HELP = `\n${chalk.bold(\"Commands:\")}\n${COMMANDS.map((c) => ` /${c.name.padEnd(12)} ${c.desc}`).join(\"\\n\")}\n\n${chalk.bold(\"CLI:\")}\n nova update Update to latest version\n nova session ls List sessions\n\n${chalk.dim(\"Keys:\")}\n Esc Abort\n ↑ / ↓ History\n`\n\nexport async function dispatch(\n\tinput: string,\n\tagent: Agent,\n\tstore?: SessionStore,\n\tsessionId?: string,\n\tprompts?: Prompts,\n): Promise<string | null> {\n\tconst [cmd, ...rest] = input.slice(1).split(\" \")\n\tconst args = rest.join(\" \")\n\n\tswitch (cmd) {\n\t\tcase \"models\":\n\t\tcase \"model\":\n\t\t\treturn handleModels(args, agent, prompts)\n\t\tcase \"providers\":\n\t\tcase \"prov\":\n\t\tcase \"config\":\n\t\tcase \"cfg\":\n\t\t\treturn handleProviders(agent, prompts)\n\t\tcase \"compact\":\n\t\t\tif (!store || !sessionId) return chalk.red(\"Session store not available\")\n\t\t\treturn handleCompact(agent, store, sessionId)\n\t\tcase \"update\":\n\t\t\treturn handleUpdate()\n\t\tcase \"help\":\n\t\t\treturn HELP\n\t\tcase \"clear\":\n\t\t\tconsole.clear()\n\t\t\treturn \"\"\n\t\tcase \"quit\":\n\t\t\tprocess.exit(0)\n\t\t\treturn null\n\t\tcase \"exit\":\n\t\t\tprocess.exit(0)\n\t\t\treturn null\n\t\tdefault:\n\t\t\treturn chalk.yellow(`Unknown: /${cmd}. Type /help`)\n\t}\n}\n\nasync function handleUpdate(): Promise<string> {\n\tconst info = await checkForUpdate()\n\tif (!info) return chalk.yellow(\"Could not check for updates.\")\n\tif (!info.hasUpdate) return chalk.green(`✓ Already up to date (v${info.current})`)\n\n\tconsole.log(chalk.yellow(`\\n⚡ Updating novacode to v${info.latest}...`))\n\tconst success = await runUpdate(true)\n\tif (success) {\n\t\treturn chalk.green(\n\t\t\t`✓ Successfully updated to v${info.latest}! Please restart nova to apply changes.`,\n\t\t)\n\t}\n\treturn chalk.red(\"✗ Update failed. Please try running 'nova update' manually in your terminal.\")\n}\n","import chalk from \"chalk\"\n\nexport class MarkdownRenderer {\n\t#inCodeBlock = false\n\t#codeBlockLang = \"\"\n\n\trenderLine(line: string): string {\n\t\tif (line.startsWith(\"```\")) {\n\t\t\tif (this.#inCodeBlock) {\n\t\t\t\tthis.#inCodeBlock = false\n\t\t\t\treturn chalk.dim(`└${\"─\".repeat(50)}`)\n\t\t\t}\n\t\t\tthis.#inCodeBlock = true\n\t\t\tthis.#codeBlockLang = line.slice(3).trim()\n\t\t\treturn chalk.dim(\n\t\t\t\t\"┌\" +\n\t\t\t\t\t\"─\".repeat(10) +\n\t\t\t\t\t` [Code: ${this.#codeBlockLang || \"text\"}] ` +\n\t\t\t\t\t\"─\".repeat(40 - (this.#codeBlockLang?.length || 4)),\n\t\t\t)\n\t\t}\n\n\t\tif (this.#inCodeBlock) {\n\t\t\treturn chalk.cyan(`│ ${line}`)\n\t\t}\n\n\t\tif (line.startsWith(\"#\")) {\n\t\t\tconst match = line.match(/^(#{1,6})\\s+(.*)$/)\n\t\t\tif (match?.[1] && match[2]) {\n\t\t\t\tconst level = match[1].length\n\t\t\t\tconst content = match[2]\n\t\t\t\tif (level === 1) return chalk.bold.magenta.underline(content)\n\t\t\t\tif (level === 2) return chalk.bold.blue(content)\n\t\t\t\treturn chalk.bold.cyan(content)\n\t\t\t}\n\t\t}\n\n\t\tlet formatted = line\n\t\tif (formatted.startsWith(\"- \") || formatted.startsWith(\"* \")) {\n\t\t\tformatted = ` ${chalk.yellow(\"•\")} ${formatted.slice(2)}`\n\t\t}\n\n\t\tformatted = formatted.replace(/`([^`]+)`/g, (_, code) => chalk.yellow(code))\n\t\tformatted = formatted.replace(/\\*\\*([^*]+)\\*\\*/g, (_, bold) => chalk.bold(bold))\n\t\tformatted = formatted.replace(/__([^_]+)__/g, (_, bold) => chalk.bold(bold))\n\t\tformatted = formatted.replace(/\\*([^*]+)\\*/g, (_, italic) => chalk.italic(italic))\n\t\tformatted = formatted.replace(/_([^_]+)_/g, (_, italic) => chalk.italic(italic))\n\t\tformatted = formatted.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, (_, text, url) => {\n\t\t\treturn `${chalk.blue(text)} ${chalk.dim(`(${url})`)}`\n\t\t})\n\n\t\treturn formatted\n\t}\n}\n\nexport function formatMarkdown(text: string): string {\n\tconst renderer = new MarkdownRenderer()\n\treturn text\n\t\t.split(\"\\n\")\n\t\t.map((line) => renderer.renderLine(line))\n\t\t.join(\"\\n\")\n}\n","import chalk from \"chalk\"\nimport { Box, Text } from \"ink\"\nimport { useEffect, useState } from \"react\"\nimport { formatMarkdown } from \"../markdown.ts\"\n\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"]\n\nexport function Spinner() {\n\tconst [frame, setFrame] = useState(0)\n\n\tuseEffect(() => {\n\t\tconst timer = setInterval(() => {\n\t\t\tsetFrame((f) => (f + 1) % SPINNER_FRAMES.length)\n\t\t}, 80)\n\t\treturn () => clearInterval(timer)\n\t}, [])\n\n\treturn <Text color=\"yellow\">{SPINNER_FRAMES[frame]}</Text>\n}\n\nexport function Cursor() {\n\tconst [visible, setVisible] = useState(true)\n\tuseEffect(() => {\n\t\tconst timer = setInterval(() => setVisible((v) => !v), 530)\n\t\treturn () => clearInterval(timer)\n\t}, [])\n\treturn <Text color=\"green\">{visible ? \"│\" : \" \"}</Text>\n}\n\nexport function LiveArea({\n\tstream,\n\tthinkStream,\n\tbusy,\n\tstatus,\n\thasMessages,\n}: {\n\tstream: string\n\tthinkStream: string\n\tbusy: boolean\n\tstatus: string\n\thasMessages: boolean\n}) {\n\tconst isActive = !!(stream || thinkStream || busy)\n\tif (!isActive) return null\n\n\treturn (\n\t\t<Box flexDirection=\"column\" marginTop={hasMessages ? 1 : 0}>\n\t\t\t{thinkStream && (\n\t\t\t\t<Text dimColor italic>\n\t\t\t\t\t{thinkStream}\n\t\t\t\t</Text>\n\t\t\t)}\n\t\t\t{stream && (\n\t\t\t\t<Box flexDirection=\"row\">\n\t\t\t\t\t<Box flexGrow={1} flexShrink={1}>\n\t\t\t\t\t\t<Text>\n\t\t\t\t\t\t\t{formatMarkdown(stream)}\n\t\t\t\t\t\t\t<Cursor />\n\t\t\t\t\t\t</Text>\n\t\t\t\t\t</Box>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t\t{busy && !stream && (\n\t\t\t\t<Box flexDirection=\"row\">\n\t\t\t\t\t<Box marginRight={1}>\n\t\t\t\t\t\t<Spinner />\n\t\t\t\t\t</Box>\n\t\t\t\t\t<Text dimColor>{status ? status.replace(\"⏳ \", \"\") : chalk.yellow(\"working…\")}</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t</Box>\n\t)\n}\n","import { Box, Text } from \"ink\"\nimport type { Msg } from \"../../types.ts\"\nimport { formatToolArgs } from \"../../util.ts\"\nimport { formatMarkdown } from \"../markdown.ts\"\n\nconst TOOL_STYLE: Record<string, string> = {\n\tread: \"blue\",\n\twrite: \"magenta\",\n\tedit: \"yellow\",\n\tbash: \"cyan\",\n\tglob: \"green\",\n\tfind: \"green\",\n\tgrep: \"green\",\n\ttree: \"green\",\n}\n\nexport function hasMeaningfulContent(msg: Msg): boolean {\n\tif (msg.role === \"user\") return true\n\tif (msg.role === \"tool_result\") return true\n\tif (msg.role === \"assistant\") {\n\t\tif (msg.model === \"system\") return true\n\t\treturn msg.content.some((c) => {\n\t\t\tif (c.type === \"thinking\") return c.text.trim().length > 0\n\t\t\tif (c.type === \"text\") return c.text.trim().length > 0\n\t\t\treturn false\n\t\t})\n\t}\n\treturn false\n}\n\nexport function Message({ msg, isFirst }: { msg: Msg; isFirst: boolean }) {\n\tif (msg.role === \"user\") {\n\t\treturn (\n\t\t\t<Box marginTop={isFirst ? 0 : 1} flexDirection=\"row\">\n\t\t\t\t<Box flexShrink={0} marginRight={1}>\n\t\t\t\t\t<Text bold color=\"green\">\n\t\t\t\t\t\t{\">\"}\n\t\t\t\t\t</Text>\n\t\t\t\t</Box>\n\t\t\t\t<Box flexGrow={1} flexShrink={1}>\n\t\t\t\t\t<Text>\n\t\t\t\t\t\t{typeof msg.content === \"string\"\n\t\t\t\t\t\t\t? msg.content\n\t\t\t\t\t\t\t: msg.content.map((c) => (c.type === \"text\" ? c.text : \"\")).join(\"\")}\n\t\t\t\t\t</Text>\n\t\t\t\t</Box>\n\t\t\t</Box>\n\t\t)\n\t}\n\n\tif (msg.role === \"assistant\") {\n\t\tif (msg.model === \"system\") {\n\t\t\treturn (\n\t\t\t\t<Box flexDirection=\"column\" marginTop={0}>\n\t\t\t\t\t{msg.content.map((c, i) =>\n\t\t\t\t\t\t// biome-ignore lint/suspicious/noArrayIndexKey: stable turn content\n\t\t\t\t\t\tc.type === \"text\" ? <Text key={i}>{formatMarkdown(c.text)}</Text> : null,\n\t\t\t\t\t)}\n\t\t\t\t</Box>\n\t\t\t)\n\t\t}\n\n\t\tconst hasVisibleContent = msg.content.some((c) => c.type === \"text\" || c.type === \"thinking\")\n\t\tif (!hasVisibleContent) return null\n\n\t\treturn (\n\t\t\t<Box flexDirection=\"column\" marginTop={0}>\n\t\t\t\t{msg.content.map((c, i) => {\n\t\t\t\t\tif (c.type === \"thinking\") {\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t// biome-ignore lint/suspicious/noArrayIndexKey: stable turn content\n\t\t\t\t\t\t\t<Text key={i} dimColor italic>\n\t\t\t\t\t\t\t\t{c.text}\n\t\t\t\t\t\t\t</Text>\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tif (c.type === \"text\") {\n\t\t\t\t\t\t// biome-ignore lint/suspicious/noArrayIndexKey: stable turn content\n\t\t\t\t\t\treturn <Text key={i}>{formatMarkdown(c.text)}</Text>\n\t\t\t\t\t}\n\t\t\t\t\treturn null\n\t\t\t\t})}\n\t\t\t</Box>\n\t\t)\n\t}\n\n\tif (msg.role === \"tool_result\") {\n\t\tconst args = msg.args ? formatToolArgs(msg.args, true) : \"\"\n\n\t\tconst resText = msg.content\n\t\t\t.map((c) => (c.type === \"text\" ? c.text : \"\"))\n\t\t\t.join(\"\")\n\t\t\t.trim()\n\n\t\tconst isRead = msg.tool === \"read\"\n\t\tconst lineCount = isRead ? resText.split(\"\\n\").length : 0\n\t\tconst color = TOOL_STYLE[msg.tool] || \"white\"\n\n\t\treturn (\n\t\t\t<Box flexDirection=\"row\">\n\t\t\t\t<Text color={msg.isError ? \"red\" : \"green\"}>{msg.isError ? \"✗\" : \"✓\"} </Text>\n\t\t\t\t<Text color={color} bold>\n\t\t\t\t\t{msg.tool}\n\t\t\t\t</Text>\n\t\t\t\t{args && <Text> {args}</Text>}\n\t\t\t\t{isRead && !msg.isError && <Text dimColor> ({lineCount} lines)</Text>}\n\t\t\t\t{msg.isError && resText && <Text color=\"red\"> {resText.slice(0, 80)}</Text>}\n\t\t\t</Box>\n\t\t)\n\t}\n\n\treturn null\n}\n","import { Box, Text } from \"ink\"\nimport type { Model } from \"../../types.ts\"\n\nfunction fmtK(n: number): string {\n\tconst k = n / 1000\n\treturn k >= 100 ? `${Math.round(k)}K` : `${k.toFixed(1)}K`\n}\n\nfunction formatTokenUsage(used: number, contextWindow: number): string {\n\tif (used === 0) return `0/${fmtK(contextWindow)}`\n\tconst pct = Math.round((used / contextWindow) * 100)\n\treturn `${fmtK(used)}/${fmtK(contextWindow)} (${pct}%)`\n}\n\nexport function StatusBar({\n\tmodel,\n\tusage,\n\tbusy,\n\tsuggestions,\n\tselCmdIdx,\n}: {\n\tmodel: Model\n\tusage: { in: number; out: number }\n\tbusy: boolean\n\tsuggestions: Array<{ name: string; desc: string }>\n\tselCmdIdx: number\n}) {\n\treturn (\n\t\t<Box justifyContent=\"space-between\">\n\t\t\t<Box>\n\t\t\t\t{suggestions.length > 0 ? (\n\t\t\t\t\t<Box flexDirection=\"column\" marginLeft={2}>\n\t\t\t\t\t\t{suggestions.map((s, i) => (\n\t\t\t\t\t\t\t<Box key={s.name}>\n\t\t\t\t\t\t\t\t<Text\n\t\t\t\t\t\t\t\t\tcolor={i === selCmdIdx ? \"black\" : \"yellow\"}\n\t\t\t\t\t\t\t\t\tbackgroundColor={i === selCmdIdx ? \"yellow\" : undefined}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t/{s.name.padEnd(10)}\n\t\t\t\t\t\t\t\t</Text>\n\t\t\t\t\t\t\t\t<Text dimColor> {s.desc}</Text>\n\t\t\t\t\t\t\t</Box>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</Box>\n\t\t\t\t) : (\n\t\t\t\t\t<Text dimColor>Enter to send · /help for commands</Text>\n\t\t\t\t)}\n\t\t\t</Box>\n\n\t\t\t<Box>\n\t\t\t\t<Text dimColor>{formatTokenUsage(usage.in, model.contextWindow)}</Text>\n\t\t\t\t<Text dimColor> │ </Text>\n\t\t\t\t<Text dimColor>{model.id}</Text>\n\t\t\t\t{busy && <Text dimColor> │ Esc to stop</Text>}\n\t\t\t</Box>\n\t\t</Box>\n\t)\n}\n","import chalk from \"chalk\"\nimport { Box, render, Static, Text, useInput } from \"ink\"\nimport { useCallback, useEffect, useRef, useState } from \"react\"\nimport type { Agent } from \"../agent/agent.ts\"\nimport { COMMANDS, dispatch } from \"../commands/index.ts\"\nimport type { SessionStore } from \"../session/store.ts\"\nimport type { Msg, Prompts } from \"../types.ts\"\nimport { checkForUpdate, getCurrentVersion } from \"../update.ts\"\nimport { Cursor, LiveArea } from \"./components/liveArea.tsx\"\nimport { hasMeaningfulContent, Message } from \"./components/message.tsx\"\nimport { StatusBar } from \"./components/statusBar.tsx\"\nimport { ConfirmPrompt, PasswordPrompt, SelectPrompt } from \"./prompts.tsx\"\n\ntype PromptMode =\n\t| { type: \"chat\" }\n\t| {\n\t\t\ttype: \"select\"\n\t\t\tmessage: string\n\t\t\toptions: Array<{ value: string; label: string; hint?: string }>\n\t\t\theader?: string\n\t }\n\t| {\n\t\t\ttype: \"password\"\n\t\t\tmessage: string\n\t\t\tvalidate?: (v: string) => string | undefined\n\t }\n\t| { type: \"confirm\"; message: string }\n\nexport async function interactive(\n\tagent: Agent,\n\tstore: SessionStore,\n\tsessionId: string,\n): Promise<void> {\n\tprocess.stdout.write(\"\\x1B[?25l\")\n\tconst version = await getCurrentVersion()\n\tprocess.stdout.write(`${chalk.cyan.bold(\"⚡ novacode\")} ${chalk.gray(`v${version}`)}\\n`)\n\n\ttry {\n\t\tconst { waitUntilExit } = render(<App agent={agent} store={store} sessionId={sessionId} />)\n\t\tawait waitUntilExit()\n\t} finally {\n\t\tprocess.stdout.write(\"\\x1B[?25h\")\n\t}\n}\n\nfunction App({\n\tagent,\n\tstore,\n\tsessionId,\n}: {\n\tagent: Agent\n\tstore: SessionStore\n\tsessionId: string\n}) {\n\tconst [msgs, setMsgs] = useState<Msg[]>(agent.messages)\n\tconst [stream, setStream] = useState(\"\")\n\tconst [thinkStream, setThinkStream] = useState(\"\")\n\tconst [busy, setBusy] = useState(false)\n\tconst [input, setInput] = useState(\"\")\n\tconst [status, setStatus] = useState(\"\")\n\tconst [usage, setUsage] = useState<{ in: number; out: number }>({ in: 0, out: 0 })\n\tconst [selCmdIdx, setSelCmdIdx] = useState(0)\n\tconst [mode, setMode] = useState<PromptMode>({ type: \"chat\" })\n\tconst resolveRef = useRef<((v: unknown) => void) | null>(null)\n\tconst history = useRef<string[]>([])\n\tconst hIdx = useRef(-1)\n\tconst abortCtrl = useRef<AbortController | null>(null)\n\tconst [updateInfo, setUpdateInfo] = useState<{\n\t\tcurrent: string\n\t\tlatest: string\n\t} | null>(null)\n\n\tuseEffect(() => {\n\t\tconst check = async () => {\n\t\t\tconst info = await checkForUpdate()\n\t\t\tif (info?.hasUpdate) {\n\t\t\t\tsetUpdateInfo({ current: info.current, latest: info.latest })\n\t\t\t}\n\t\t}\n\t\tcheck()\n\t}, [])\n\n\tconst isTypingCmd = input.startsWith(\"/\") && !input.includes(\" \")\n\tconst suggestions = isTypingCmd\n\t\t? COMMANDS.filter(\n\t\t\t\t(c) =>\n\t\t\t\t\tc.name.startsWith(input.slice(1).toLowerCase()) ||\n\t\t\t\t\tc.aliases?.some((a) => a.startsWith(input.slice(1).toLowerCase())),\n\t\t\t)\n\t\t: []\n\n\tconst prompts: Prompts = {\n\t\tselect: useCallback(\n\t\t\t(config) =>\n\t\t\t\tnew Promise((resolve) => {\n\t\t\t\t\tresolveRef.current = resolve as (v: unknown) => void\n\t\t\t\t\tsetMode({ type: \"select\", ...config })\n\t\t\t\t}),\n\t\t\t[],\n\t\t),\n\t\tpassword: useCallback(\n\t\t\t(config) =>\n\t\t\t\tnew Promise((resolve) => {\n\t\t\t\t\tresolveRef.current = resolve as (v: unknown) => void\n\t\t\t\t\tsetMode({ type: \"password\", ...config })\n\t\t\t\t}),\n\t\t\t[],\n\t\t),\n\t\tconfirm: useCallback(\n\t\t\t(config) =>\n\t\t\t\tnew Promise((resolve) => {\n\t\t\t\t\tresolveRef.current = resolve as (v: unknown) => void\n\t\t\t\t\tsetMode({ type: \"confirm\", ...config })\n\t\t\t\t}),\n\t\t\t[],\n\t\t),\n\t}\n\n\tfunction resolvePrompt(value: unknown) {\n\t\tconst fn = resolveRef.current\n\t\tresolveRef.current = null\n\t\tsetMode({ type: \"chat\" })\n\t\tfn?.(value)\n\t}\n\n\tfunction commitMsg(msg: Msg) {\n\t\tsetMsgs((prev) => [...prev, msg])\n\t\tagent.setMessages([...agent.messages, msg])\n\t\tstore.append(sessionId, msg)\n\t}\n\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: reset selection on input change\n\tuseEffect(() => {\n\t\tsetSelCmdIdx(0)\n\t}, [input])\n\n\tuseInput((ch, key) => {\n\t\tif (mode.type !== \"chat\") return\n\n\t\tif (key.escape) {\n\t\t\tif (abortCtrl.current) {\n\t\t\t\tabortCtrl.current.abort()\n\t\t\t\tabortCtrl.current = null\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif (key.upArrow) {\n\t\t\tif (isTypingCmd && suggestions.length > 0) {\n\t\t\t\tsetSelCmdIdx((prev) => (prev > 0 ? prev - 1 : suggestions.length - 1))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (history.current.length > 0) {\n\t\t\t\thIdx.current = Math.min(hIdx.current + 1, history.current.length - 1)\n\t\t\t\tsetInput(history.current[hIdx.current] ?? \"\")\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif (key.downArrow) {\n\t\t\tif (isTypingCmd && suggestions.length > 0) {\n\t\t\t\tsetSelCmdIdx((prev) => (prev < suggestions.length - 1 ? prev + 1 : 0))\n\t\t\t\treturn\n\t\t\t}\n\t\t\thIdx.current = Math.max(hIdx.current - 1, -1)\n\t\t\tsetInput(hIdx.current >= 0 ? (history.current[hIdx.current] ?? \"\") : \"\")\n\t\t\treturn\n\t\t}\n\t\tif (key.tab) {\n\t\t\tif (isTypingCmd && suggestions.length > 0) {\n\t\t\t\tconst match = suggestions[selCmdIdx]\n\t\t\t\tif (match) {\n\t\t\t\t\tsetInput(`/${match.name} `)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif (!key.return) {\n\t\t\tsetInput((prev) => {\n\t\t\t\tif (key.backspace || key.delete) return prev.slice(0, -1)\n\t\t\t\treturn prev + (ch || \"\")\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tif (busy) return\n\n\t\tlet line = input.trim()\n\t\tif (!line) return\n\n\t\tif (isTypingCmd && suggestions.length > 0) {\n\t\t\tconst match = suggestions[selCmdIdx]\n\t\t\tif (match) {\n\t\t\t\tline = `/${match.name}`\n\t\t\t}\n\t\t}\n\n\t\tsetInput(\"\")\n\t\thistory.current.unshift(line)\n\t\thIdx.current = -1\n\n\t\tif (line.startsWith(\"/\")) {\n\t\t\tdispatch(line, agent, store, sessionId, prompts).then((r) => {\n\t\t\t\tif (r) {\n\t\t\t\t\tcommitMsg({\n\t\t\t\t\t\trole: \"assistant\",\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: r }],\n\t\t\t\t\t\tmodel: \"system\",\n\t\t\t\t\t\tprovider: \"system\",\n\t\t\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\t\t\tstop: \"stop\",\n\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tconst userMsg: Msg = { role: \"user\", content: line, ts: Date.now() }\n\t\tcommitMsg(userMsg)\n\n\t\tabortCtrl.current = new AbortController()\n\t\tconst eventStream = agent.prompt(line, abortCtrl.current.signal)\n\n\t\trunEventLoop(eventStream)\n\t})\n\n\tasync function runEventLoop(eventStream: ReturnType<Agent[\"prompt\"]>) {\n\t\ttry {\n\t\t\tfor await (const ev of eventStream) {\n\t\t\t\tswitch (ev.type) {\n\t\t\t\t\tcase \"start\":\n\t\t\t\t\t\tsetBusy(true)\n\t\t\t\t\t\tsetStream(\"\")\n\t\t\t\t\t\tsetThinkStream(\"\")\n\t\t\t\t\t\tsetStatus(\"\")\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase \"text_delta\":\n\t\t\t\t\t\tif (ev.text) setStream((prev) => prev + ev.text)\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase \"thinking_delta\":\n\t\t\t\t\t\tif (ev.text) setThinkStream((prev) => prev + ev.text)\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase \"assistant_msg\":\n\t\t\t\t\t\tcommitMsg(ev.msg)\n\t\t\t\t\t\tsetStream(\"\")\n\t\t\t\t\t\tsetThinkStream(\"\")\n\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase \"tool_call\":\n\t\t\t\t\t\tsetStatus(chalk.dim(`⏳ ${ev.call.name}…`))\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase \"tool_result\":\n\t\t\t\t\t\tcommitMsg(ev.result)\n\t\t\t\t\t\tsetStatus(\n\t\t\t\t\t\t\tev.result.isError\n\t\t\t\t\t\t\t\t? chalk.red(`✗ ${ev.result.tool}`)\n\t\t\t\t\t\t\t\t: chalk.green(`✓ ${ev.result.tool}`),\n\t\t\t\t\t\t)\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase \"turn_end\":\n\t\t\t\t\t\tsetStatus(\"\")\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase \"usage\":\n\t\t\t\t\t\tif (ev.usage) setUsage(ev.usage)\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconst errMsg: Msg = {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tmodel: \"system\",\n\t\t\t\tprovider: \"system\",\n\t\t\t\tcontent: [{ type: \"text\", text: chalk.red(`Error: ${(err as Error).message}`) }],\n\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\tstop: \"error\",\n\t\t\t\tts: Date.now(),\n\t\t\t}\n\t\t\tcommitMsg(errMsg)\n\t\t} finally {\n\t\t\tabortCtrl.current = null\n\t\t\tsetBusy(false)\n\t\t\tsetStream(\"\")\n\t\t\tsetThinkStream(\"\")\n\t\t\tsetStatus(\"\")\n\t\t}\n\t}\n\n\tif (mode.type === \"select\") {\n\t\treturn (\n\t\t\t<SelectPrompt\n\t\t\t\tmessage={mode.message}\n\t\t\t\toptions={mode.options}\n\t\t\t\theader={mode.header}\n\t\t\t\tonSelect={resolvePrompt}\n\t\t\t/>\n\t\t)\n\t}\n\tif (mode.type === \"password\") {\n\t\treturn (\n\t\t\t<PasswordPrompt message={mode.message} validate={mode.validate} onSubmit={resolvePrompt} />\n\t\t)\n\t}\n\tif (mode.type === \"confirm\") {\n\t\treturn <ConfirmPrompt message={mode.message} onConfirm={resolvePrompt} />\n\t}\n\n\tconst visibleMsgs = msgs.filter(hasMeaningfulContent)\n\tconst isLiveActive = !!(stream || thinkStream || busy)\n\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingX={1}>\n\t\t\t<Static items={visibleMsgs}>\n\t\t\t\t{(m, i) => <Message key={`${m.ts}-${i}`} msg={m} isFirst={i === 0} />}\n\t\t\t</Static>\n\n\t\t\t<LiveArea\n\t\t\t\tstream={stream}\n\t\t\t\tthinkStream={thinkStream}\n\t\t\t\tbusy={busy}\n\t\t\t\tstatus={status}\n\t\t\t\thasMessages={visibleMsgs.length > 0}\n\t\t\t/>\n\n\t\t\t<Box flexDirection=\"column\" marginTop={visibleMsgs.length > 0 || isLiveActive ? 1 : 0}>\n\t\t\t\t{updateInfo && (\n\t\t\t\t\t<Box\n\t\t\t\t\t\tborderStyle=\"round\"\n\t\t\t\t\t\tborderColor=\"yellow\"\n\t\t\t\t\t\tpaddingX={1}\n\t\t\t\t\t\tmarginBottom={1}\n\t\t\t\t\t\tflexDirection=\"column\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Text color=\"yellow\" bold>\n\t\t\t\t\t\t\t⬆ Update Available (v{updateInfo.current} → v{updateInfo.latest})\n\t\t\t\t\t\t</Text>\n\t\t\t\t\t\t<Text dimColor>\n\t\t\t\t\t\t\tRun <Text color=\"cyan\">/update</Text> or <Text color=\"cyan\">nova update</Text> to\n\t\t\t\t\t\t\tupgrade.\n\t\t\t\t\t\t</Text>\n\t\t\t\t\t</Box>\n\t\t\t\t)}\n\t\t\t\t<Box flexDirection=\"row\">\n\t\t\t\t\t<Box flexShrink={0} marginRight={1}>\n\t\t\t\t\t\t<Text bold color=\"green\">\n\t\t\t\t\t\t\t{\">\"}\n\t\t\t\t\t\t</Text>\n\t\t\t\t\t</Box>\n\t\t\t\t\t<Box flexGrow={1} flexShrink={1}>\n\t\t\t\t\t\t<Text>\n\t\t\t\t\t\t\t{input}\n\t\t\t\t\t\t\t<Cursor />\n\t\t\t\t\t\t</Text>\n\t\t\t\t\t</Box>\n\t\t\t\t</Box>\n\n\t\t\t\t<StatusBar\n\t\t\t\t\tmodel={agent.model}\n\t\t\t\t\tusage={usage}\n\t\t\t\t\tbusy={busy}\n\t\t\t\t\tsuggestions={suggestions}\n\t\t\t\t\tselCmdIdx={selCmdIdx}\n\t\t\t\t/>\n\t\t\t</Box>\n\t\t</Box>\n\t)\n}\n"],"mappings":"sWASA,SAAS,EAAY,EAAkB,CAEtC,OADI,OAAO,EAAI,SAAY,SAAiB,EAAI,QACzC,EAAI,QACT,OAAQ,GAAM,EAAE,OAAS,MAAM,EAC/B,IAAK,GAAO,EAAE,OAAS,OAAS,EAAE,KAAO,EAAG,EAC5C,KAAK,EAAE,CACV,CAEA,SAAS,EAAiB,EAAU,EAA4B,CAM/D,OALI,EAAI,OAAS,eACb,EAAE,SAAU,IAAQ,EAAI,OAAS,EAAiB,CAAC,EAC1C,EAAY,CAER,EAAE,MAAM;CACd,EAAE,OAAQ,GAAM,EAAE,KAAK,EAAE,OAAS,CAAC,CAC/C,CAQA,SAAgB,EAAa,EAAiB,EAAgC,CAC7E,OAAO,EAAe,CAAQ,EAAI,EAAgB,EACnD,CAEA,eAAsB,EACrB,EACA,EACA,EACA,EACA,EACA,EACyB,CACzB,GAAI,CAAC,EAAa,EAAU,EAAM,aAAa,EAC9C,MAAO,CAAE,UAAW,GAAO,YAAa,CAAE,EAG3C,IAAM,EAAM,EAAS,MAAM,EAAG,GAAY,EAC1C,GAAI,EAAI,SAAW,EAClB,MAAO,CAAE,UAAW,GAAO,YAAa,CAAE,EAY3C,IAAM,EAAU,MAAM,EAVR,EACZ,IAAK,GACD,EAAE,OAAS,OAAe,SAAS,EAAY,CAAC,IAChD,EAAE,OAAS,YAAoB,cAAc,EAAY,CAAC,IAC1D,EAAE,OAAS,eAAiB,SAAU,EAClC,QAAQ,EAAE,KAAK,KAAK,EAAY,CAAC,EAAE,MAAM,EAAG,GAAG,IAChD,EACP,EACA,KAAK;;CAEmC,EAAG,EAAO,EAAQ,CAAO,EACnE,GAAI,CAAC,EACJ,MAAO,CAAE,UAAW,GAAO,YAAa,CAAE,EAG3C,IAAM,EAAsB,CAAC,EACvB,EAAuB,CAAC,EAC9B,IAAK,IAAM,KAAK,EACf,EAAU,KAAK,GAAG,EAAiB,EAAG,MAAM,CAAC,EAC7C,EAAU,KAAK,GAAG,EAAiB,EAAG,MAAM,CAAC,EAC7C,EAAW,KAAK,GAAG,EAAiB,EAAG,OAAO,CAAC,EAC/C,EAAW,KAAK,GAAG,EAAiB,EAAG,MAAM,CAAC,EAG/C,IAAM,EAAY,EAAI,OACtB,EAAM,eACL,EACA,EACA,CAAC,GAAG,IAAI,IAAI,CAAS,CAAC,EACtB,CAAC,GAAG,IAAI,IAAI,CAAU,CAAC,EACvB,CACD,EACA,EAAM,kBAAkB,EAAW,EAAY,CAAC,EAGhD,IAAM,EAAkB,CACvB,KAAM,OACN,QAAS,4BAA4B,IACrC,GAAI,KAAK,IAAI,CACd,EAGA,OAFA,EAAM,OAAO,EAAW,CAAU,EAE3B,CAAE,UAAW,GAAM,UAAS,YAAa,EAAI,MAAO,CAC5D,CAEA,eAAe,EACd,EACA,EACA,EACA,EACyB,CACzB,IAAM,EAAW,EAAY,EAAM,QAAQ,EAC3C,GAAI,CAAC,EAAU,OAAO,KAEtB,IAAM,EAAK,EAAO,CACjB,IAAK,EAAS,IACd,QACA,SACA,UACA,OACC,wIACD,SAAU,CAAC,CAAE,KAAM,OAAQ,QAAS,EAAO,GAAI,KAAK,IAAI,CAAE,CAAC,EAC3D,MAAO,CAAC,CACT,CAAC,EAEG,EAAU,GACd,UAAW,IAAM,KAAM,EAClB,EAAG,OAAS,cAAgB,EAAG,OAClC,GAAW,EAAG,MAIhB,OAAO,EAAQ,KAAK,GAAK,IAC1B,CCxHA,eAAsB,EACrB,EACA,EACA,EACkB,CAClB,IAAM,EAAM,MAAMA,EACjB,EACA,EACA,EAAM,SACN,EAAM,MACN,EAAM,OACN,EAAM,OACP,EAEA,GAAI,EAAI,UAAW,CAElB,IAAM,EAAO,EAAM,SAAS,CAAS,EAErC,OADA,EAAM,YAAY,CAAI,EACf,EAAM,MAAM,wBAAwB,EAAI,YAAY,mBAAmB,CAC/E,CAEA,OAAO,EAAM,OAAO,gDAAgD,CACrE,CCrBA,eAAsB,EAAa,EAAc,EAAc,EAAoC,CAClG,IAAM,EAAS,MAAM,EAAW,EAC1B,EAAO,MAAM,EAAS,EAE5B,GAAI,EAAM,OAAO,MAAM,EAAa,EAAK,KAAK,EAAG,CAAK,EAEtD,GAAI,CAAC,EAAS,OAAO,EAAM,IAAI,uCAAuC,EAEtE,IAAM,EAAkE,CAAC,EACzE,IAAK,IAAM,KAAK,EAAQ,CACvB,IAAM,EAAM,EAAE,KAAO,EAAO,OAAS,EAAE,WAAa,EAAO,SACrD,EAAO,EAAY,EAAE,QAAQ,EAC9B,GAEY,EAAK,QAAQ,EAAE,WAGhC,EAAQ,KAAK,CACZ,MAAO,GAAG,EAAE,SAAS,GAAG,EAAE,KAC1B,MAAO,GAAG,EAAM,EAAM,MAAM,GAAG,EAAI,IAAI,GAAG,EAAE,GAAG,OAAO,EAAE,EAAE,GAAG,EAAI,EAAE,aAAa,EAAE,OAAO,CAAC,IAC1F,KAAM,EAAK,IACZ,CAAC,CACF,CAEA,GAAI,CAAC,EAAQ,OACZ,OAAO,EAAM,OAAO,gEAAgE,EAErF,IAAM,EAAO,MAAM,EAAQ,OAAO,CAAE,QAAS,QAAS,SAAQ,CAAC,EAC/D,GAAI,CAAC,EAAM,MAAO,GAElB,GAAM,CAAC,EAAI,GAAO,EAAK,MAAM,GAAG,EAC1B,EAAgB,EAAO,KAAM,GAAM,EAAE,WAAa,GAAM,EAAE,KAAO,CAAG,EACpE,EAAmB,EAAY,CAAG,EAcxC,MAZI,CAAC,GAAiB,CAAC,EAAyB,EAAM,IAAI,oCAAoC,GAE9F,EAAO,SAAW,EAClB,EAAO,MAAQ,EACf,MAAM,EAAW,CAAM,EAEvB,EAAM,aAAa,CAClB,IAAK,EAAiB,IACtB,MAAO,EACP,OAAQ,EAAK,QAAQ,IAAQ,GAC7B,QAAS,EAAiB,OAC3B,CAAC,EACM,EAAM,MAAM,iBAAiB,GAAK,EAC1C,CAEA,eAAe,EAAa,EAAY,EAA+B,CACtE,IAAM,EAAS,MAAM,EAAW,EAC1B,EAAO,MAAM,EAAS,EAEtB,EAAI,EAAO,KAAM,GAAM,EAAE,KAAO,CAAE,EACxC,GAAI,CAAC,EAAG,OAAO,EAAM,OAAO,IAAI,EAAG,yBAAyB,EAE5D,IAAM,EAAK,EAAE,SACb,GAAI,CAAC,EAAK,QAAQ,GACjB,OAAO,EAAM,OAAO,6BAA6B,EAAG,iBAAiB,EAGtE,IAAM,EAAmB,EAAY,CAAE,EAcvC,OAbK,GAEL,EAAO,SAAW,EAClB,EAAO,MAAQ,EACf,MAAM,EAAW,CAAM,EAEvB,EAAM,aAAa,CAClB,IAAK,EAAiB,IACtB,MAAO,EACP,OAAQ,EAAK,QAAQ,GACrB,QAAS,EAAiB,OAC3B,CAAC,EAEM,EAAM,MAAM,iBAAiB,GAAI,GAbV,EAAM,IAAI,2BAA2B,CAcpE,CAEA,MAAM,EAAO,GAAe,GAAK,IAAY,GAAG,EAAI,IAAU,GAAK,GAAG,EAAI,IAAK,GC9E/E,eAAsB,EAAgB,EAAc,EAAoC,CACvF,GAAI,CAAC,EAAS,OAAO,EAAM,IAAI,uCAAuC,EAEtE,IAAM,EAAS,MAAM,EAAW,EAC1B,EAAO,MAAM,EAAS,EACtB,EAAa,EAAU,OAAQ,GAAM,CAAC,CAAC,EAAK,QAAQ,EAAE,GAAG,EAEzD,EACL,EAAW,SAAW,EACnB,EAAM,IAAI,oDAAoD,EAC9D,EACC,IAAK,GAAM,CACX,IAAM,EAAY,EAAE,KAAO,EAAO,SAC5B,EAAS,EAAY,EAAM,MAAM,IAAI,EAAI,GACzC,EAAe,EAClB,EAAO,MACN,EAAO,KAAM,GAAM,EAAE,WAAa,EAAE,EAAE,GAAG,IAAM,GACnD,MAAO,OAAO,EAAE,KAAK,OAAO,EAAE,EAAE,GAAG,IAAe,GACnD,CAAC,EACA,KAAK;CAAI,EAER,EAAM,MAAM,EAAQ,OAAO,CAChC,QAAS,SACT,OAAQ,EACR,QAAS,CACR,CAAE,MAAO,MAAO,MAAO,cAAe,EACtC,CAAE,MAAO,SAAU,MAAO,gBAAiB,EAC3C,CAAE,MAAO,SAAU,MAAO,gBAAiB,EAC3C,CAAE,MAAO,UAAW,MAAO,sBAAuB,EAClD,CAAE,MAAO,OAAQ,MAAO,MAAO,CAChC,CACD,CAAC,EAOD,MANI,CAAC,GAAO,IAAQ,OAAe,GAE/B,IAAQ,MAAc,EAAY,EAAO,CAAO,EAChD,IAAQ,SAAiB,EAAU,EAAO,CAAO,EACjD,IAAQ,SAAiB,EAAU,EAAO,CAAO,EACjD,IAAQ,UAAkB,EAAW,EAAO,CAAO,EAChD,EACR,CAEA,eAAe,EAAY,EAAc,EAAmC,CAC3E,IAAM,EAAO,MAAM,EAAS,EACtB,EAAS,MAAM,EAAW,EAE1B,EAAY,EAAU,OAAQ,GAAM,CAAC,EAAK,QAAQ,EAAE,GAAG,EAC7D,GAAI,EAAU,SAAW,EACxB,OAAO,EAAM,OAAO,iDAAiD,EAGtE,IAAM,EAAO,MAAM,EAAQ,OAAO,CACjC,QAAS,eACT,QAAS,EAAU,IAAK,IAAO,CAAE,MAAO,EAAE,GAAI,MAAO,EAAE,IAAK,EAAE,CAC/D,CAAC,EACD,GAAI,CAAC,EAAM,MAAO,GAElB,IAAM,EAAO,EAAY,CAAI,EAC7B,GAAI,CAAC,EAAM,OAAO,EAAM,IAAI,2BAA2B,EAEvD,IAAM,EAAM,MAAM,EAAQ,SAAS,CAClC,QAAS,GAAG,EAAK,KAAK,UACtB,SAAW,GAAO,CAAC,GAAK,EAAE,OAAS,EAAI,oBAAsB,IAAA,EAC9D,CAAC,EACD,GAAI,CAAC,EAAK,MAAO,GAKjB,GAHA,EAAK,QAAQ,EAAK,IAAM,EACxB,MAAM,EAAS,CAAI,EAEf,CAAC,EAAO,SAAU,CACrB,EAAO,SAAW,EAAK,GACvB,IAAM,EAAO,EAAO,KAAM,GAAM,EAAE,WAAa,EAAK,EAAE,EAClD,IACH,EAAO,MAAQ,EAAK,IAErB,MAAM,EAAW,CAAM,EACvB,EAAM,aAAa,CAClB,IAAK,EAAK,IACV,MAAO,EAAO,KAAM,GAAM,EAAE,KAAO,EAAO,KAAK,EAC/C,OAAQ,EACR,QAAS,EAAK,OACf,CAAC,CACF,CAEA,OAAO,EAAM,MAAM,KAAK,EAAK,KAAK,YAAY,CAC/C,CAEA,eAAe,EAAU,EAAc,EAAmC,CACzE,IAAM,EAAO,MAAM,EAAS,EAEtB,EAAa,EAAU,OAAQ,GAAM,CAAC,CAAC,EAAK,QAAQ,EAAE,GAAG,EAC/D,GAAI,EAAW,SAAW,EACzB,OAAO,EAAM,OAAO,oDAAoD,EAGzE,IAAM,EAAO,MAAM,EAAQ,OAAO,CACjC,QAAS,iBACT,QAAS,EAAW,IAAK,IAAO,CAAE,MAAO,EAAE,GAAI,MAAO,EAAE,IAAK,EAAE,CAChE,CAAC,EACD,GAAI,CAAC,EAAM,MAAO,GAElB,IAAM,EAAO,EAAY,CAAI,EAC7B,GAAI,CAAC,EAAM,OAAO,EAAM,IAAI,2BAA2B,EAEvD,IAAM,EAAM,MAAM,EAAQ,SAAS,CAAE,QAAS,eAAe,EAAK,MAAO,CAAC,EAC1E,GAAI,CAAC,EAAK,MAAO,GAEjB,EAAK,QAAQ,EAAK,IAAM,EACxB,MAAM,EAAS,CAAI,EAEnB,IAAM,EAAS,MAAM,EAAW,EAChC,GAAI,EAAO,WAAa,EAAK,GAAI,CAChC,IAAM,EAAe,EAAO,KAAM,GAAM,EAAE,KAAO,EAAO,OAAS,EAAE,WAAa,EAAO,QAAQ,EAC3F,GACH,EAAM,aAAa,CAClB,IAAK,EAAK,IACV,MAAO,EACP,OAAQ,EACR,QAAS,EAAK,OACf,CAAC,CAEH,CAEA,OAAO,EAAM,MAAM,eAAe,CACnC,CAEA,eAAe,EAAU,EAAc,EAAmC,CACzE,IAAM,EAAO,MAAM,EAAS,EACtB,EAAS,MAAM,EAAW,EAE1B,EAAa,EAAU,OAAQ,GAAM,CAAC,CAAC,EAAK,QAAQ,EAAE,GAAG,EAC/D,GAAI,EAAW,SAAW,EACzB,OAAO,EAAM,OAAO,oCAAoC,EAGzD,IAAM,EAAO,MAAM,EAAQ,OAAO,CACjC,QAAS,iBACT,QAAS,EAAW,IAAK,IAAO,CAAE,MAAO,EAAE,GAAI,MAAO,EAAE,IAAK,EAAE,CAChE,CAAC,EAMD,GALI,CAAC,GAKD,CAAC,MAHiB,EAAQ,QAAQ,CACrC,QAAS,mDAAmD,EAAK,EAClE,CAAC,EACa,MAAO,GAKrB,GAHA,OAAO,EAAK,QAAQ,GACpB,MAAM,EAAS,CAAI,EAEf,EAAO,WAAa,EAAM,CAC7B,EAAO,SAAW,GAClB,EAAO,MAAQ,GACf,IAAM,EAAO,OAAO,KAAK,EAAK,OAAO,EAAE,GACvC,GAAI,EAAM,CACT,IAAM,EAAO,EAAY,CAAI,EACvB,EAAO,EAAO,KAAM,GAAM,EAAE,WAAa,CAAI,EAC/C,GAAQ,IACX,EAAO,SAAW,EAClB,EAAO,MAAQ,EAAK,GACpB,EAAM,aAAa,CAClB,IAAK,EAAK,IACV,MAAO,EACP,OAAQ,EAAK,QAAQ,GACrB,QAAS,EAAK,OACf,CAAC,EAEH,CACA,MAAM,EAAW,CAAM,CACxB,CAEA,OAAO,EAAM,MAAM,yBAAyB,GAAM,CACnD,CAEA,eAAe,EAAW,EAAc,EAAmC,CAC1E,IAAM,EAAS,MAAM,EAAW,EAC1B,EAAO,MAAM,EAAS,EAEtB,EAAO,MAAM,EAAQ,OAAO,CACjC,QAAS,mBACT,QAAS,EAAU,IAAK,IAAO,CAC9B,MAAO,EAAE,GACT,MAAO,GAAG,EAAK,QAAQ,EAAE,IAAM,IAAM,IAAI,GAAG,EAAE,MAC/C,EAAE,CACH,CAAC,EACD,GAAI,CAAC,EAAM,MAAO,GAElB,GAAI,CAAC,EAAK,QAAQ,GACjB,OAAO,EAAM,OAAO,kBAAkB,EAAK,wBAAwB,EAGpE,IAAM,EAAO,EAAY,CAAI,EACvB,EAAO,EAAO,KAAM,GAAM,EAAE,WAAa,CAAI,EAenD,MAbI,CAAC,GAAQ,CAAC,EAAa,EAAM,IAAI,oCAAoC,GAEzE,EAAO,SAAW,EAClB,EAAO,MAAQ,EAAK,GACpB,MAAM,EAAW,CAAM,EAEvB,EAAM,aAAa,CAClB,IAAK,EAAK,IACV,MAAO,EACP,OAAQ,EAAK,QAAQ,GACrB,QAAS,EAAK,OACf,CAAC,EAEM,EAAM,MAAM,oBAAoB,EAAK,KAAK,IAAI,EAAK,GAAG,EAAE,EAChE,CC3MA,MAAa,EAAkB,CAC9B,CAAE,KAAM,SAAU,KAAM,eAAgB,QAAS,CAAC,OAAO,CAAE,EAC3D,CAAE,KAAM,YAAa,KAAM,mBAAoB,QAAS,CAAC,OAAQ,SAAU,KAAK,CAAE,EAClF,CAAE,KAAM,UAAW,KAAM,iBAAkB,EAC3C,CAAE,KAAM,SAAU,KAAM,iBAAkB,EAC1C,CAAE,KAAM,OAAQ,KAAM,WAAY,EAClC,CAAE,KAAM,QAAS,KAAM,cAAe,EACtC,CAAE,KAAM,OAAQ,KAAM,gBAAiB,QAAS,CAAC,MAAM,CAAE,CAC1D,EAEM,EAAO;EACX,EAAM,KAAK,WAAW,EAAE;EACxB,EAAS,IAAK,GAAM,MAAM,EAAE,KAAK,OAAO,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK;CAAI,EAAE;;EAEpE,EAAM,KAAK,MAAM,EAAE;;;;EAInB,EAAM,IAAI,OAAO,EAAE;;;EAKrB,eAAsB,EACrB,EACA,EACA,EACA,EACA,EACyB,CACzB,GAAM,CAAC,EAAK,GAAG,GAAQ,EAAM,MAAM,CAAC,EAAE,MAAM,GAAG,EACzC,EAAO,EAAK,KAAK,GAAG,EAE1B,OAAQ,EAAR,CACC,IAAK,SACL,IAAK,QACJ,OAAO,EAAa,EAAM,EAAO,CAAO,EACzC,IAAK,YACL,IAAK,OACL,IAAK,SACL,IAAK,MACJ,OAAO,EAAgB,EAAO,CAAO,EACtC,IAAK,UAEJ,MADI,CAAC,GAAS,CAAC,EAAkB,EAAM,IAAI,6BAA6B,EACjE,EAAc,EAAO,EAAO,CAAS,EAC7C,IAAK,SACJ,OAAO,EAAa,EACrB,IAAK,OACJ,OAAO,EACR,IAAK,QAEJ,OADA,QAAQ,MAAM,EACP,GACR,IAAK,OAEJ,OADA,QAAQ,KAAK,CAAC,EACP,KACR,IAAK,OAEJ,OADA,QAAQ,KAAK,CAAC,EACP,KACR,QACC,OAAO,EAAM,OAAO,aAAa,EAAI,aAAa,CACpD,CACD,CAEA,eAAe,GAAgC,CAC9C,IAAM,EAAO,MAAM,EAAe,EAWlC,OAVK,EACA,EAAK,WAEV,QAAQ,IAAI,EAAM,OAAO,6BAA6B,EAAK,OAAO,IAAI,CAAC,EAEnE,MADkB,EAAU,EAAI,EAE5B,EAAM,MACZ,8BAA8B,EAAK,OAAO,wCAC3C,EAEM,EAAM,IAAI,8EAA8E,GATnE,EAAM,MAAM,0BAA0B,EAAK,QAAQ,EAAE,EAD/D,EAAM,OAAO,8BAA8B,CAW9D,CCnFA,IAAa,EAAb,KAA8B,CAC7B,GAAe,GACf,GAAiB,GAEjB,WAAW,EAAsB,CAChC,GAAI,EAAK,WAAW,KAAK,EAOxB,OANI,KAAKC,IACR,KAAKA,GAAe,GACb,EAAM,IAAI,IAAI,IAAI,OAAO,EAAE,GAAG,IAEtC,KAAKA,GAAe,GACpB,KAAKC,GAAiB,EAAK,MAAM,CAAC,EAAE,KAAK,EAClC,EAAM,IACZ,IACC,IAAI,OAAO,EAAE,EACb,WAAW,KAAKA,IAAkB,OAAO,IACzC,IAAI,OAAO,IAAM,KAAKA,IAAgB,QAAU,EAAE,CACpD,GAGD,GAAI,KAAKD,GACR,OAAO,EAAM,KAAK,MAAM,GAAM,EAG/B,GAAI,EAAK,WAAW,GAAG,EAAG,CACzB,IAAM,EAAQ,EAAK,MAAM,mBAAmB,EAC5C,GAAI,IAAQ,IAAM,EAAM,GAAI,CAC3B,IAAM,EAAQ,EAAM,GAAG,OACjB,EAAU,EAAM,GAGtB,OAFI,IAAU,EAAU,EAAM,KAAK,QAAQ,UAAU,CAAO,EACxD,IAAU,EAAU,EAAM,KAAK,KAAK,CAAO,EACxC,EAAM,KAAK,KAAK,CAAO,CAC/B,CACD,CAEA,IAAI,EAAY,EAchB,OAbI,EAAU,WAAW,IAAI,GAAK,EAAU,WAAW,IAAI,KAC1D,EAAY,KAAK,EAAM,OAAO,GAAG,EAAE,GAAG,EAAU,MAAM,CAAC,KAGxD,EAAY,EAAU,QAAQ,cAAe,EAAG,IAAS,EAAM,OAAO,CAAI,CAAC,EAC3E,EAAY,EAAU,QAAQ,oBAAqB,EAAG,IAAS,EAAM,KAAK,CAAI,CAAC,EAC/E,EAAY,EAAU,QAAQ,gBAAiB,EAAG,IAAS,EAAM,KAAK,CAAI,CAAC,EAC3E,EAAY,EAAU,QAAQ,gBAAiB,EAAG,IAAW,EAAM,OAAO,CAAM,CAAC,EACjF,EAAY,EAAU,QAAQ,cAAe,EAAG,IAAW,EAAM,OAAO,CAAM,CAAC,EAC/E,EAAY,EAAU,QAAQ,4BAA6B,EAAG,EAAM,IAC5D,GAAG,EAAM,KAAK,CAAI,EAAE,GAAG,EAAM,IAAI,IAAI,EAAI,EAAE,GAClD,EAEM,CACR,CACD,EAEA,SAAgB,EAAe,EAAsB,CACpD,IAAM,EAAW,IAAI,EACrB,OAAO,EACL,MAAM;CAAI,EACV,IAAK,GAAS,EAAS,WAAW,CAAI,CAAC,EACvC,KAAK;CAAI,CACZ,CCxDA,MAAM,EAAiB,CAAC,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,GAAG,EAExE,SAAgB,GAAU,CACzB,GAAM,CAAC,EAAO,GAAY,EAAS,CAAC,EASpC,OAPA,MAAgB,CACf,IAAM,EAAQ,gBAAkB,CAC/B,EAAU,IAAO,EAAI,GAAK,EAAe,MAAM,CAChD,EAAG,EAAE,EACL,UAAa,cAAc,CAAK,CACjC,EAAG,CAAC,CAAC,EAEE,EAAC,EAAD,CAAM,MAAM,kBAAU,EAAe,EAAa,CAAA,CAC1D,CAEA,SAAgB,GAAS,CACxB,GAAM,CAAC,EAAS,GAAc,EAAS,EAAI,EAK3C,OAJA,MAAgB,CACf,IAAM,EAAQ,gBAAkB,EAAY,GAAM,CAAC,CAAC,EAAG,GAAG,EAC1D,UAAa,cAAc,CAAK,CACjC,EAAG,CAAC,CAAC,EACE,EAAC,EAAD,CAAM,MAAM,iBAAS,EAAU,IAAM,GAAU,CAAA,CACvD,CAEA,SAAgB,EAAS,CACxB,SACA,cACA,OACA,SACA,eAOE,CAIF,OAHoB,GAAU,GAAe,EAI5C,EAAC,EAAD,CAAK,cAAc,SAAS,UAAW,cAAvC,CACE,GACA,EAAC,EAAD,CAAM,SAAA,GAAS,OAAA,YACb,CACI,CAAA,EAEN,GACA,EAAC,EAAD,CAAK,cAAc,eAClB,EAAC,EAAD,CAAK,SAAU,EAAG,WAAY,WAC7B,EAAC,EAAD,CAAA,SAAA,CACE,EAAe,CAAM,EACtB,EAAC,EAAD,CAAS,CAAA,CACJ,CAAA,CAAA,CACF,CAAA,CACD,CAAA,EAEL,GAAQ,CAAC,GACT,EAAC,EAAD,CAAK,cAAc,eAAnB,CACC,EAAC,EAAD,CAAK,YAAa,WACjB,EAAC,EAAD,CAAU,CAAA,CACN,CAAA,EACL,EAAC,EAAD,CAAM,SAAA,YAAU,EAAS,EAAO,QAAQ,KAAM,EAAE,EAAI,EAAM,OAAO,UAAU,CAAQ,CAAA,CAC/E,GAEF,IA3BgB,IA6BvB,CCnEA,MAAM,EAAqC,CAC1C,KAAM,OACN,MAAO,UACP,KAAM,SACN,KAAM,OACN,KAAM,QACN,KAAM,QACN,KAAM,QACN,KAAM,OACP,EAEA,SAAgB,GAAqB,EAAmB,CAWvD,OAVI,EAAI,OAAS,QACb,EAAI,OAAS,cAAsB,GACnC,EAAI,OAAS,YACZ,EAAI,QAAU,SAAiB,GAC5B,EAAI,QAAQ,KAAM,GACpB,EAAE,OAAS,YACX,EAAE,OAAS,OAAe,EAAE,KAAK,KAAK,EAAE,OAAS,EAC9C,EACP,EAEK,EACR,CAEA,SAAgB,GAAQ,CAAE,MAAK,WAA2C,CACzE,GAAI,EAAI,OAAS,OAChB,OACC,EAAC,EAAD,CAAK,UAAW,IAAiB,cAAc,eAA/C,CACC,EAAC,EAAD,CAAK,WAAY,EAAG,YAAa,WAChC,EAAC,EAAD,CAAM,KAAA,GAAK,MAAM,iBACf,GACI,CAAA,CACF,CAAA,EACL,EAAC,EAAD,CAAK,SAAU,EAAG,WAAY,WAC7B,EAAC,EAAD,CAAA,SACE,OAAO,EAAI,SAAY,SACrB,EAAI,QACJ,EAAI,QAAQ,IAAK,GAAO,EAAE,OAAS,OAAS,EAAE,KAAO,EAAG,EAAE,KAAK,EAAE,CAC/D,CAAA,CACF,CAAA,CACD,IAIP,GAAI,EAAI,OAAS,YAehB,OAdI,EAAI,QAAU,SAEhB,EAAC,EAAD,CAAK,cAAc,SAAS,UAAW,WACrC,EAAI,QAAQ,KAAK,EAAG,IAEpB,EAAE,OAAS,OAAS,EAAC,EAAD,CAAA,SAAe,EAAe,EAAE,IAAI,CAAQ,EAAjC,CAAiC,EAAI,IACrE,CACI,CAAA,EAImB,EAAI,QAAQ,KAAM,GAAM,EAAE,OAAS,QAAU,EAAE,OAAS,UAC7D,EAGpB,EAAC,EAAD,CAAK,cAAc,SAAS,UAAW,WACrC,EAAI,QAAQ,KAAK,EAAG,IAChB,EAAE,OAAS,WAGb,EAAC,EAAD,CAAc,SAAA,GAAS,OAAA,YACrB,EAAE,IACE,EAFK,CAEL,EAGJ,EAAE,OAAS,OAEP,EAAC,EAAD,CAAA,SAAe,EAAe,EAAE,IAAI,CAAQ,EAAjC,CAAiC,EAE7C,IACP,CACG,CAAA,EAnByB,KAuBhC,GAAI,EAAI,OAAS,cAAe,CAC/B,IAAM,EAAO,EAAI,KAAO,EAAe,EAAI,KAAM,EAAI,EAAI,GAEnD,EAAU,EAAI,QAClB,IAAK,GAAO,EAAE,OAAS,OAAS,EAAE,KAAO,EAAG,EAC5C,KAAK,EAAE,EACP,KAAK,EAED,EAAS,EAAI,OAAS,OACtB,EAAY,EAAS,EAAQ,MAAM;CAAI,EAAE,OAAS,EAClD,EAAQ,EAAW,EAAI,OAAS,QAEtC,OACC,EAAC,EAAD,CAAK,cAAc,eAAnB,CACC,EAAC,EAAD,CAAM,MAAO,EAAI,QAAU,MAAQ,iBAAnC,CAA6C,EAAI,QAAU,IAAM,IAAI,GAAO,IAC5E,EAAC,EAAD,CAAa,QAAO,KAAA,YAClB,EAAI,IACA,CAAA,EACL,GAAQ,EAAC,EAAD,CAAA,SAAA,CAAM,IAAE,CAAW,CAAA,CAAA,EAC3B,GAAU,CAAC,EAAI,SAAW,EAAC,EAAD,CAAM,SAAA,YAAN,CAAe,KAAG,EAAU,SAAa,IACnE,EAAI,SAAW,GAAW,EAAC,EAAD,CAAM,MAAM,eAAZ,CAAkB,IAAE,EAAQ,MAAM,EAAG,EAAE,CAAQ,GACtE,GAEP,CAEA,OAAO,IACR,CC7GA,SAAS,EAAK,EAAmB,CAChC,IAAM,EAAI,EAAI,IACd,OAAO,GAAK,IAAM,GAAG,KAAK,MAAM,CAAC,EAAE,GAAK,GAAG,EAAE,QAAQ,CAAC,EAAE,EACzD,CAEA,SAAS,GAAiB,EAAc,EAA+B,CACtE,GAAI,IAAS,EAAG,MAAO,KAAK,EAAK,CAAa,IAC9C,IAAM,EAAM,KAAK,MAAO,EAAO,EAAiB,GAAG,EACnD,MAAO,GAAG,EAAK,CAAI,EAAE,GAAG,EAAK,CAAa,EAAE,IAAI,EAAI,GACrD,CAEA,SAAgB,GAAU,CACzB,QACA,QACA,OACA,cACA,aAOE,CACF,OACC,EAAC,EAAD,CAAK,eAAe,yBAApB,CACC,EAAC,EAAD,CAAA,SACE,EAAY,OAAS,EACrB,EAAC,EAAD,CAAK,cAAc,SAAS,WAAY,WACtC,EAAY,KAAK,EAAG,IACpB,EAAC,EAAD,CAAA,SAAA,CACC,EAAC,EAAD,CACC,MAAO,IAAM,EAAY,QAAU,SACnC,gBAAiB,IAAM,EAAY,SAAW,IAAA,YAF/C,CAGC,IACE,EAAE,KAAK,OAAO,EAAE,CACb,IACN,EAAC,EAAD,CAAM,SAAA,YAAN,CAAe,IAAE,EAAE,IAAW,GAC1B,CAAA,EARK,EAAE,IAQP,CACL,CACG,CAAA,EAEL,EAAC,EAAD,CAAM,SAAA,YAAS,oCAAwC,CAAA,CAEpD,CAAA,EAEL,EAAC,EAAD,CAAA,SAAA,CACC,EAAC,EAAD,CAAM,SAAA,YAAU,GAAiB,EAAM,GAAI,EAAM,aAAa,CAAQ,CAAA,EACtE,EAAC,EAAD,CAAM,SAAA,YAAS,KAAS,CAAA,EACxB,EAAC,EAAD,CAAM,SAAA,YAAU,EAAM,EAAS,CAAA,EAC9B,GAAQ,EAAC,EAAD,CAAM,SAAA,YAAS,gBAAoB,CAAA,CACxC,CAAA,CAAA,CACD,GAEP,CC7BA,eAAsB,GACrB,EACA,EACA,EACgB,CAChB,QAAQ,OAAO,MAAM,WAAW,EAChC,IAAM,EAAU,MAAM,EAAkB,EACxC,QAAQ,OAAO,MAAM,GAAG,EAAM,KAAK,KAAK,YAAY,EAAE,GAAG,EAAM,KAAK,IAAI,GAAS,EAAE,GAAG,EAEtF,GAAI,CACH,GAAM,CAAE,iBAAkB,EAAO,EAAC,GAAD,CAAY,QAAc,QAAkB,WAAY,CAAA,CAAC,EAC1F,MAAM,EAAc,CACrB,QAAU,CACT,QAAQ,OAAO,MAAM,WAAW,CACjC,CACD,CAEA,SAAS,GAAI,CACZ,QACA,QACA,aAKE,CACF,GAAM,CAAC,EAAM,GAAW,EAAgB,EAAM,QAAQ,EAChD,CAAC,EAAQ,GAAa,EAAS,EAAE,EACjC,CAAC,EAAa,GAAkB,EAAS,EAAE,EAC3C,CAAC,EAAM,GAAW,EAAS,EAAK,EAChC,CAAC,EAAO,GAAY,EAAS,EAAE,EAC/B,CAAC,EAAQ,GAAa,EAAS,EAAE,EACjC,CAAC,EAAO,GAAY,EAAsC,CAAE,GAAI,EAAG,IAAK,CAAE,CAAC,EAC3E,CAAC,EAAW,GAAgB,EAAS,CAAC,EACtC,CAAC,EAAM,GAAW,EAAqB,CAAE,KAAM,MAAO,CAAC,EACvD,EAAa,EAAsC,IAAI,EACvD,EAAU,EAAiB,CAAC,CAAC,EAC7B,EAAO,EAAO,EAAE,EAChB,EAAY,EAA+B,IAAI,EAC/C,CAAC,EAAY,GAAiB,EAG1B,IAAI,EAEd,MAAgB,EAOf,SAN0B,CACzB,IAAM,EAAO,MAAM,EAAe,EAC9B,GAAM,WACT,EAAc,CAAE,QAAS,EAAK,QAAS,OAAQ,EAAK,MAAO,CAAC,CAE9D,GACM,CACP,EAAG,CAAC,CAAC,EAEL,IAAM,EAAc,EAAM,WAAW,GAAG,GAAK,CAAC,EAAM,SAAS,GAAG,EAC1D,EAAc,EACjB,EAAS,OACR,GACA,EAAE,KAAK,WAAW,EAAM,MAAM,CAAC,EAAE,YAAY,CAAC,GAC9C,EAAE,SAAS,KAAM,GAAM,EAAE,WAAW,EAAM,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC,CACnE,EACC,CAAC,EAEE,EAAmB,CACxB,OAAQ,EACN,GACA,IAAI,QAAS,GAAY,CACxB,EAAW,QAAU,EACrB,EAAQ,CAAE,KAAM,SAAU,GAAG,CAAO,CAAC,CACtC,CAAC,EACF,CAAC,CACF,EACA,SAAU,EACR,GACA,IAAI,QAAS,GAAY,CACxB,EAAW,QAAU,EACrB,EAAQ,CAAE,KAAM,WAAY,GAAG,CAAO,CAAC,CACxC,CAAC,EACF,CAAC,CACF,EACA,QAAS,EACP,GACA,IAAI,QAAS,GAAY,CACxB,EAAW,QAAU,EACrB,EAAQ,CAAE,KAAM,UAAW,GAAG,CAAO,CAAC,CACvC,CAAC,EACF,CAAC,CACF,CACD,EAEA,SAAS,EAAc,EAAgB,CACtC,IAAM,EAAK,EAAW,QACtB,EAAW,QAAU,KACrB,EAAQ,CAAE,KAAM,MAAO,CAAC,EACxB,IAAK,CAAK,CACX,CAEA,SAAS,EAAU,EAAU,CAC5B,EAAS,GAAS,CAAC,GAAG,EAAM,CAAG,CAAC,EAChC,EAAM,YAAY,CAAC,GAAG,EAAM,SAAU,CAAG,CAAC,EAC1C,EAAM,OAAO,EAAW,CAAG,CAC5B,CAGA,MAAgB,CACf,EAAa,CAAC,CACf,EAAG,CAAC,CAAK,CAAC,EAEV,GAAU,EAAI,IAAQ,CACrB,GAAI,EAAK,OAAS,OAAQ,OAE1B,GAAI,EAAI,OAAQ,CACf,AAEC,EAAU,WADV,EAAU,QAAQ,MAAM,EACJ,MAErB,MACD,CACA,GAAI,EAAI,QAAS,CAChB,GAAI,GAAe,EAAY,OAAS,EAAG,CAC1C,EAAc,GAAU,EAAO,EAAI,EAAO,EAAI,EAAY,OAAS,CAAE,EACrE,MACD,CACI,EAAQ,QAAQ,OAAS,IAC5B,EAAK,QAAU,KAAK,IAAI,EAAK,QAAU,EAAG,EAAQ,QAAQ,OAAS,CAAC,EACpE,EAAS,EAAQ,QAAQ,EAAK,UAAY,EAAE,GAE7C,MACD,CACA,GAAI,EAAI,UAAW,CAClB,GAAI,GAAe,EAAY,OAAS,EAAG,CAC1C,EAAc,GAAU,EAAO,EAAY,OAAS,EAAI,EAAO,EAAI,CAAE,EACrE,MACD,CACA,EAAK,QAAU,KAAK,IAAI,EAAK,QAAU,EAAG,EAAE,EAC5C,EAAS,EAAK,SAAW,EAAK,EAAQ,QAAQ,EAAK,UAAY,GAAM,EAAE,EACvE,MACD,CACA,GAAI,EAAI,IAAK,CACZ,GAAI,GAAe,EAAY,OAAS,EAAG,CAC1C,IAAM,EAAQ,EAAY,GACtB,GACH,EAAS,IAAI,EAAM,KAAK,EAAE,CAE5B,CACA,MACD,CACA,GAAI,CAAC,EAAI,OAAQ,CAChB,EAAU,GACL,EAAI,WAAa,EAAI,OAAe,EAAK,MAAM,EAAG,EAAE,EACjD,GAAQ,GAAM,GACrB,EACD,MACD,CAEA,GAAI,EAAM,OAEV,IAAI,EAAO,EAAM,KAAK,EACjB,KAEL,IAAI,GAAe,EAAY,OAAS,EAAG,CAC1C,IAAM,EAAQ,EAAY,GACtB,IACH,EAAO,IAAI,EAAM,OAEnB,CAMA,GAJA,EAAS,EAAE,EACX,EAAQ,QAAQ,QAAQ,CAAI,EAC5B,EAAK,QAAU,GAEX,EAAK,WAAW,GAAG,EAAG,CACzB,EAAS,EAAM,EAAO,EAAO,EAAW,CAAO,EAAE,KAAM,GAAM,CACxD,GACH,EAAU,CACT,KAAM,YACN,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,CAAE,CAAC,EACnC,MAAO,SACP,SAAU,SACV,MAAO,CAAE,GAAI,EAAG,IAAK,CAAE,EACvB,KAAM,OACN,GAAI,KAAK,IAAI,CACd,CAAC,CAEH,CAAC,EACD,MACD,CAGA,EAAU,CADa,KAAM,OAAQ,QAAS,EAAM,GAAI,KAAK,IAAI,CACjD,CAAC,EAEjB,EAAU,QAAU,IAAI,gBAGxB,EAFoB,EAAM,OAAO,EAAM,EAAU,QAAQ,MAElC,CAAC,CA7BxB,CA8BD,CAAC,EAED,eAAe,EAAa,EAA0C,CACrE,GAAI,CACH,UAAW,IAAM,KAAM,EACtB,OAAQ,EAAG,KAAX,CACC,IAAK,QACJ,EAAQ,EAAI,EACZ,EAAU,EAAE,EACZ,EAAe,EAAE,EACjB,EAAU,EAAE,EACZ,MACD,IAAK,aACA,EAAG,MAAM,EAAW,GAAS,EAAO,EAAG,IAAI,EAC/C,MACD,IAAK,iBACA,EAAG,MAAM,EAAgB,GAAS,EAAO,EAAG,IAAI,EACpD,MACD,IAAK,gBACJ,EAAU,EAAG,GAAG,EAChB,EAAU,EAAE,EACZ,EAAe,EAAE,EAEjB,MACD,IAAK,YACJ,EAAU,EAAM,IAAI,KAAK,EAAG,KAAK,KAAK,EAAE,CAAC,EACzC,MACD,IAAK,cACJ,EAAU,EAAG,MAAM,EACnB,EACC,EAAG,OAAO,QACP,EAAM,IAAI,KAAK,EAAG,OAAO,MAAM,EAC/B,EAAM,MAAM,KAAK,EAAG,OAAO,MAAM,CACrC,EACA,MACD,IAAK,WACJ,EAAU,EAAE,EACZ,MACD,IAAK,QACA,EAAG,OAAO,EAAS,EAAG,KAAK,CACjC,CAEF,OAAS,EAAK,CAUb,EAAU,CART,KAAM,YACN,MAAO,SACP,SAAU,SACV,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,EAAM,IAAI,UAAW,EAAc,SAAS,CAAE,CAAC,EAC/E,MAAO,CAAE,GAAI,EAAG,IAAK,CAAE,EACvB,KAAM,QACN,GAAI,KAAK,IAAI,CAEC,CAAC,CACjB,QAAU,CACT,EAAU,QAAU,KACpB,EAAQ,EAAK,EACb,EAAU,EAAE,EACZ,EAAe,EAAE,EACjB,EAAU,EAAE,CACb,CACD,CAEA,GAAI,EAAK,OAAS,SACjB,OACC,EAAC,EAAD,CACC,QAAS,EAAK,QACd,QAAS,EAAK,QACd,OAAQ,EAAK,OACb,SAAU,CACV,CAAA,EAGH,GAAI,EAAK,OAAS,WACjB,OACC,EAAC,EAAD,CAAgB,QAAS,EAAK,QAAS,SAAU,EAAK,SAAU,SAAU,CAAgB,CAAA,EAG5F,GAAI,EAAK,OAAS,UACjB,OAAO,EAAC,EAAD,CAAe,QAAS,EAAK,QAAS,UAAW,CAAgB,CAAA,EAGzE,IAAM,EAAc,EAAK,OAAO,EAAoB,EAC9C,EAAe,CAAC,EAAE,GAAU,GAAe,GAEjD,OACC,EAAC,EAAD,CAAK,cAAc,SAAS,SAAU,WAAtC,CACC,EAAC,EAAD,CAAQ,MAAO,YACZ,EAAG,IAAM,EAAC,GAAD,CAA8B,IAAK,EAAG,QAAS,IAAM,CAAI,EAA3C,GAAG,EAAE,GAAG,GAAG,GAAgC,CAC7D,CAAA,EAER,EAAC,EAAD,CACS,SACK,cACP,OACE,SACR,YAAa,EAAY,OAAS,CAClC,CAAA,EAED,EAAC,EAAD,CAAK,cAAc,SAAS,UAAW,EAAY,OAAS,GAAK,EAAe,EAAI,WAApF,CACE,GACA,EAAC,EAAD,CACC,YAAY,QACZ,YAAY,SACZ,SAAU,EACV,aAAc,EACd,cAAc,kBALf,CAOC,EAAC,EAAD,CAAM,MAAM,SAAS,KAAA,YAArB,CAA0B,wBACH,EAAW,QAAQ,OAAK,EAAW,OAAO,GAC3D,IACN,EAAC,EAAD,CAAM,SAAA,YAAN,CAAe,OACV,EAAC,EAAD,CAAM,MAAM,gBAAO,SAAa,CAAA,EAAC,OAAI,EAAC,EAAD,CAAM,MAAM,gBAAO,aAAiB,CAAA,EAAC,cAEzE,GACF,IAEN,EAAC,EAAD,CAAK,cAAc,eAAnB,CACC,EAAC,EAAD,CAAK,WAAY,EAAG,YAAa,WAChC,EAAC,EAAD,CAAM,KAAA,GAAK,MAAM,iBACf,GACI,CAAA,CACF,CAAA,EACL,EAAC,EAAD,CAAK,SAAU,EAAG,WAAY,WAC7B,EAAC,EAAD,CAAA,SAAA,CACE,EACD,EAAC,EAAD,CAAS,CAAA,CACJ,CAAA,CAAA,CACF,CAAA,CACD,IAEL,EAAC,GAAD,CACC,MAAO,EAAM,MACN,QACD,OACO,cACF,WACX,CAAA,CACG,GACD,GAEP"}
|
package/dist/main.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{parseArgs as e}from"node:util";import t from"chalk";import{dirname as n,extname as r,isAbsolute as i,join as a,relative as o,resolve as s}from"node:path";import c from"node:os";import l from"better-sqlite3";import{chmod as
|
|
3
|
-
`)}}},r=t[t.length-1];r&&r.role===`user`&&r.parts.some(e=>e.function_response)?r.parts.push(e):t.push({role:`user`,parts:[e]})}return t}function re(e){return e.length===0?[]:[{function_declarations:e.map(e=>({name:e.name,description:e.description,parameters:e.parameters}))}]}const ie=e=>{let t=new
|
|
2
|
+
import{parseArgs as e}from"node:util";import t from"chalk";import{dirname as n,extname as r,isAbsolute as i,join as a,relative as o,resolve as s}from"node:path";import c from"node:os";import{unlinkSync as l}from"node:fs";import u from"better-sqlite3";import{chmod as d,mkdir as f,readFile as p,readdir as m,stat as h,writeFile as g}from"node:fs/promises";import{Box as _,Text as v,render as y,useInput as b}from"ink";import{useState as x}from"react";import{jsx as S,jsxs as C}from"react/jsx-runtime";import{spawn as w}from"node:child_process";import{glob as T}from"glob";import{fileURLToPath as ee}from"node:url";import te from"semver";function ne(e){let t=[];for(let n of e)if(n.role===`user`){let e=typeof n.content==`string`?[{text:n.content}]:n.content.map(e=>e.type===`text`?{text:e.text}:e.type===`image`?{inline_data:{mime_type:e.mime,data:e.data}}:{text:``});t.push({role:`user`,parts:e})}else if(n.role===`assistant`){let e=n.content.map(e=>e.type===`text`?{text:e.text,thought_signature:e.signature}:e.type===`thinking`?{thought:!0,text:e.text,thought_signature:e.signature}:e.type===`tool_call`?{function_call:{name:e.name,args:e.args},thought_signature:e.signature}:{text:``});t.push({role:`model`,parts:e})}else if(n.role===`tool_result`){let e={function_response:{name:n.tool,response:{content:n.content.map(e=>e.type===`text`?e.text:JSON.stringify(e)).join(`
|
|
3
|
+
`)}}},r=t[t.length-1];r&&r.role===`user`&&r.parts.some(e=>e.function_response)?r.parts.push(e):t.push({role:`user`,parts:[e]})}return t}function re(e){return e.length===0?[]:[{function_declarations:e.map(e=>({name:e.name,description:e.description,parameters:e.parameters}))}]}const ie=e=>{let t=new E;return(async()=>{try{let n=`${e.baseUrl||`https://generativelanguage.googleapis.com`}/v1beta/models/${e.model.id}:streamGenerateContent?alt=sse&key=${e.apiKey}`,r={contents:ne(e.messages),system_instruction:e.system?{parts:[{text:e.system}]}:void 0,tools:e.tools.length>0?re(e.tools):void 0,generationConfig:{thinkingConfig:e.model.supportsThinking?{thinkingLevel:`low`}:void 0}},i=await fetch(n,{method:`POST`,headers:{"Content-Type":`application/json`,"Api-Revision":`2026-05-20`},body:JSON.stringify(r),signal:e.signal});if(!i.ok){let e=await i.text(),n=e;try{let t=JSON.parse(e);n=t.error?.message||t.message||e}catch{}let r=`Gemini Error (${i.status}): ${n}`;t.push({type:`text_delta`,text:r}),t.finish({content:[{type:`text`,text:r}],usage:{in:0,out:0},stop:`error`});return}let a=i.body?.getReader();if(!a){t.finish({content:[],usage:{in:0,out:0},stop:`error`});return}let o=new TextDecoder,s=``,c={in:0,out:0},l=`stop`,u=[];for(;;){let{done:e,value:n}=await a.read();if(e)break;s+=o.decode(n,{stream:!0});let r=s.split(`
|
|
4
4
|
`);s=r.pop()??``;for(let e of r){let n=e.trim();if(!n?.startsWith(`data: `))continue;let r=n.slice(6);try{let e=JSON.parse(r),n=e.candidates?.[0];if(e.usageMetadata&&(c={in:e.usageMetadata.promptTokenCount||c.in,out:e.usageMetadata.candidatesTokenCount||c.out},t.push({type:`usage`,usage:c})),!n)continue;if(n.finishReason){let e=n.finishReason;e===`STOP`?l=`stop`:e===`MAX_TOKENS`?l=`length`:(e===`SAFETY`||e===`RECITATION`||e===`OTHER`)&&(l=`error`)}let i=n.content?.parts;if(i)for(let e of i){let n=e.thought_signature||e.thoughtSignature;if(e.text)if(e.thought===!0||typeof e.thought==`string`){let r=typeof e.thought==`string`?e.thought:e.text;t.push({type:`thinking_delta`,text:r});let i=u[u.length-1];i?.type===`thinking`?i.text+=r:u.push({type:`thinking`,text:r,signature:n})}else{t.push({type:`text_delta`,text:e.text});let r=u[u.length-1];r?.type===`text`?r.text+=e.text:u.push({type:`text`,text:e.text,signature:n})}let r=e.functionCall||e.function_call;if(r){let e=r.name,i=r.args||{},a={type:`tool_call`,id:`call_${Math.random().toString(36).slice(2,9)}`,name:e,args:i,signature:n};u.push(a),t.push({type:`tool_call`,call:a}),l=`tool_use`}}}catch{r.trim()!==``&&r.trim()}}}t.finish({content:u,usage:c,stop:l})}catch(n){if(e.signal?.aborted)return;let r=`Gemini Network/Request Error: ${n instanceof Error?n.message:String(n)}`;t.push({type:`text_delta`,text:r}),t.finish({content:[{type:`text`,text:r}],usage:{in:0,out:0},stop:`error`})}})(),t};function ae(e){if(e.role===`user`)return{role:`user`,content:typeof e.content==`string`?e.content:e.content.map(e=>e.type===`text`?{type:`text`,text:e.text}:e.type===`image`?{type:`image_url`,image_url:{url:`data:${e.mime};base64,${e.data}`}}:{type:`text`,text:``})};if(e.role===`assistant`){let t=[],n=[];for(let r of e.content)r.type===`text`&&t.push(r.text),r.type===`tool_call`&&n.push({type:`function`,id:r.id,function:{name:r.name,arguments:JSON.stringify(r.args)}});let r={role:`assistant`,content:t.length>0?t.join(``):null};return n.length>0&&(r.tool_calls=n),r}return e.role===`tool_result`?{role:`tool`,tool_call_id:e.callId,content:e.content.map(e=>e.type===`text`?e.text:JSON.stringify(e)).join(`
|
|
5
|
-
`)}:{role:`user`,content:``}}function oe(e){return e.map(e=>({type:`function`,function:{name:e.name,description:e.description,parameters:e.parameters}}))}const se=e=>{let t=new
|
|
6
|
-
`);o=r.pop()??``;for(let e of r){let n=e.trim();if(!n?.startsWith(`data: `))continue;let r=n.slice(6);if(r!==`[DONE]`)try{let e=JSON.parse(r),n=e.choices?.[0]?.delta;if(!n)continue;if(n.content&&(t.push({type:`text_delta`,text:n.content}),l+=n.content),n.tool_calls)for(let e of n.tool_calls){let t=e.index??0;s.has(t)||s.set(t,{id:e.id??``,name:e.function?.name??``,args:``});let n=s.get(t);e.id&&(n.id=e.id),e.function?.name&&(n.name=e.function.name),e.function?.arguments&&(n.args+=e.function.arguments)}e.usage&&(c={in:e.usage.prompt_tokens??0,out:e.usage.completion_tokens??0},t.push({type:`usage`,usage:c}));let i=e.choices?.[0]?.finish_reason;i&&(u=i)}catch{}}}let d=[];l&&d.push({type:`text`,text:l});for(let[,e]of s)d.push({type:`tool_call`,id:e.id,name:e.name,args:JSON.parse(e.args||`{}`)}),t.push({type:`tool_call`,call:{type:`tool_call`,id:e.id,name:e.name,args:JSON.parse(e.args||`{}`)}}),u=`tool_use`;t.finish({content:d,usage:c,stop:u})}catch(n){if(e.signal?.aborted)return;let r=`Unexpected error: ${n instanceof Error?n.message:String(n)}`;t.push({type:`text_delta`,text:r}),t.finish({content:[{type:`text`,text:r}],usage:{in:0,out:0},stop:`error`})}})(),t};var
|
|
5
|
+
`)}:{role:`user`,content:``}}function oe(e){return e.map(e=>({type:`function`,function:{name:e.name,description:e.description,parameters:e.parameters}}))}const se=e=>{let t=new E;return(async()=>{try{let n={model:e.model.id,messages:[{role:`system`,content:e.system},...e.messages.map(ae)],tools:e.tools.length>0?oe(e.tools):void 0,stream:!0},r=await fetch(`${e.baseUrl}/chat/completions`,{method:`POST`,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${e.apiKey}`},body:JSON.stringify(n),signal:e.signal});if(!r.ok){let e=await r.text(),n=`API error ${r.status}: ${e}`;t.push({type:`text_delta`,text:n}),t.finish({content:[{type:`text`,text:n}],usage:{in:0,out:0},stop:`error`});return}let i=r.body?.getReader();if(!i){t.finish({content:[],usage:{in:0,out:0},stop:`error`});return}let a=new TextDecoder,o=``,s=new Map,c={in:0,out:0},l=``,u=`stop`;for(;;){let{done:e,value:n}=await i.read();if(e)break;o+=a.decode(n,{stream:!0});let r=o.split(`
|
|
6
|
+
`);o=r.pop()??``;for(let e of r){let n=e.trim();if(!n?.startsWith(`data: `))continue;let r=n.slice(6);if(r!==`[DONE]`)try{let e=JSON.parse(r),n=e.choices?.[0]?.delta;if(!n)continue;if(n.content&&(t.push({type:`text_delta`,text:n.content}),l+=n.content),n.tool_calls)for(let e of n.tool_calls){let t=e.index??0;s.has(t)||s.set(t,{id:e.id??``,name:e.function?.name??``,args:``});let n=s.get(t);e.id&&(n.id=e.id),e.function?.name&&(n.name=e.function.name),e.function?.arguments&&(n.args+=e.function.arguments)}e.usage&&(c={in:e.usage.prompt_tokens??0,out:e.usage.completion_tokens??0},t.push({type:`usage`,usage:c}));let i=e.choices?.[0]?.finish_reason;i&&(u=i)}catch{}}}let d=[];l&&d.push({type:`text`,text:l});for(let[,e]of s)d.push({type:`tool_call`,id:e.id,name:e.name,args:JSON.parse(e.args||`{}`)}),t.push({type:`tool_call`,call:{type:`tool_call`,id:e.id,name:e.name,args:JSON.parse(e.args||`{}`)}}),u=`tool_use`;t.finish({content:d,usage:c,stop:u})}catch(n){if(e.signal?.aborted)return;let r=`Unexpected error: ${n instanceof Error?n.message:String(n)}`;t.push({type:`text_delta`,text:r}),t.finish({content:[{type:`text`,text:r}],usage:{in:0,out:0},stop:`error`})}})(),t};var E=class{#e=[];#t=!1;#n;#r;#i;#a=!1;push(e){if(!this.#a)if(this.#r){let t=this.#r;this.#r=void 0,t(e)}else this.#e.push(e)}finish(e){this.#t=!0,this.#n=e,this.#r&&this.#r(void 0),this.#i&&this.#i(e)}abort(){this.#a=!0,this.#t=!0,this.#r&&this.#r(void 0),this.#i&&this.#i(void 0)}async*[Symbol.asyncIterator](){for(;!this.#t||this.#e.length>0;){if(this.#e.length>0){yield this.#e.shift();continue}if(this.#t)break;let e=await new Promise(e=>{this.#r=e});e!==void 0&&(yield e)}}get result(){return this.#n}get isDone(){return this.#t}};const ce=new Map([[`openai`,se],[`gemini`,ie]]);function D(e){let t=ce.get(e.api);if(!t)throw Error(`No provider registered for API format: ${e.api}`);let n=t(e),r=new E;return(async()=>{for await(let e of n)e.type===`text_delta`?r.push({type:`text_delta`,text:e.text??``}):e.type===`thinking_delta`?r.push({type:`thinking_delta`,text:e.text??``}):e.type===`tool_call`&&e.call?r.push({type:`tool_call`,call:{type:`tool_call`,id:e.call.id,name:e.call.name,args:e.call.args}}):e.type===`usage`&&e.usage&&r.push({type:`usage`,usage:e.usage});let e=n.result;e?r.finish(e):r.finish({content:[],usage:{in:0,out:0},stop:`stop`})})(),r}function O(e){let t=0;for(let n of e)if(typeof n.content==`string`)t+=n.content.length;else if(Array.isArray(n.content))for(let e of n.content)e.type===`text`&&(t+=e.text.length);return Math.ceil(t/4)}function k(e){return{type:`text`,text:e}}function A(e,t){return t===e||t.startsWith(`${e}/`)?o(e,t)||`.`:t}function le(e){if(typeof e!=`string`)return e;let t=e,n=``;if(e.startsWith(`file://`)&&(t=e.slice(7),n=`file://`),i(t)){let e=process.cwd();return n+A(e,t)}return e}function ue(e,n=!1){return e?Object.entries(e).map(([e,r])=>{let i=typeof r==`string`?le(r):JSON.stringify(r),a=i.length>40?`${i.slice(0,40)}…`:i;return`${n?t.dim(`${e}:`):`${e}:`} ${a}`}).join(` `):``}const de=e=>typeof e==`object`&&!!e&&e.type===`tool_call`;function fe(e,t,n,r){let i=new E,a=[],o=n.maxTurns??50,s={role:`user`,content:e,ts:Date.now()},c={...t,messages:[...t.messages,s]};return a.push(s),(async()=>{i.push({type:`start`});try{let e=0;for(;e<o&&!r?.aborted;){e++,i.push({type:`turn`});let t=O(c.messages);t>n.model.contextWindow*.9&&i.push({type:`text_delta`,text:`[warning] Approaching context limit (~${Math.round(t/1e3)}k / ${Math.round(n.model.contextWindow/1e3)}k tokens)`});let o=await pe(c,n,i,r);if(a.push(o),c={...c,messages:[...c.messages,o]},i.push({type:`assistant_msg`,msg:o}),o.stop===`error`||o.stop===`aborted`){i.push({type:`turn_end`,msg:o,results:[]});break}let s=o.content.filter(de);if(s.length===0){i.push({type:`turn_end`,msg:o,results:[]});break}let l=[];for(let e of s){if(r?.aborted)break;let t=c.tools.find(t=>t.def.name===e.name);if(!t){let t={role:`tool_result`,callId:e.id,tool:e.name,content:[k(`Unknown tool: ${e.name}`)],isError:!0,ts:Date.now()};l.push(t),c={...c,messages:[...c.messages,t]},a.push(t);continue}let o=await n.beforeTool?.(e,e.args,c);if(o?.block){let t={role:`tool_result`,callId:e.id,tool:e.name,content:[k(o.reason??`Blocked`)],isError:!0,ts:Date.now()};l.push(t),c={...c,messages:[...c.messages,t]},a.push(t);continue}let s=await t.execute(e.args,r),u={role:`tool_result`,callId:e.id,tool:e.name,args:e.args,content:s.content,isError:s.isError,ts:Date.now()};l.push(u),c={...c,messages:[...c.messages,u]},a.push(u),i.push({type:`tool_result`,callId:e.id,result:u,args:e.args}),await n.afterTool?.(e,u,c)}i.push({type:`turn_end`,msg:o,results:l})}e>=o&&i.push({type:`text_delta`,text:`[max turns reached (${o})]`})}catch(e){if(e.name===`AbortError`){i.finish(a);return}throw e}i.finish(a)})(),i}async function pe(e,t,n,r){let i=D({api:t.api,model:t.model,apiKey:t.apiKey,baseUrl:t.baseUrl,system:e.system,messages:e.messages,tools:e.tools.map(e=>e.def),signal:r}),a=[],o={in:0,out:0};for await(let e of i)if(e.type===`text_delta`&&e.text){let t=a[a.length-1];t?.type===`text`?t.text+=e.text:a.push(k(e.text)),n.push({type:`text_delta`,text:e.text})}else if(e.type===`thinking_delta`&&e.text){let t=a[a.length-1];t?.type===`thinking`?t.text+=e.text:a.push({type:`thinking`,text:e.text}),n.push({type:`thinking_delta`,text:e.text})}else e.type===`tool_call`&&e.call?(a.push(e.call),n.push({type:`tool_call`,call:e.call})):e.type===`usage`&&e.usage&&(o=e.usage,n.push({type:`usage`,usage:o}));let s=i.result?.content&&i.result.content.length>0?i.result.content:a,c=s.some(e=>e.type===`tool_call`)?s.filter(e=>e.type!==`text`||e.text.trim().length>0):s,l=i.result;return l?{role:`assistant`,content:c,model:t.model.id,provider:t.model.provider,usage:l.usage,stop:l.stop,ts:Date.now()}:{role:`assistant`,content:c,model:t.model.id,provider:t.model.provider,usage:o,stop:c.some(e=>e.type===`tool_call`)?`tool_use`:`stop`,ts:Date.now()}}var me=class{#e;#t;#n;#r=[];#i;#a;#o;constructor(e){this.#e=e.api,this.#t=e.model,this.#a=e.apiKey,this.#o=e.baseUrl,this.#n=e.system,this.#i=e.tools,this.#r=e.messages??[]}get model(){return this.#t}get messages(){return this.#r}get tools(){return this.#i}get apiKey(){return this.#a}get baseUrl(){return this.#o}updateConfig(e){this.#e=e.api,this.#t=e.model,this.#a=e.apiKey,this.#o=e.baseUrl}setTools(e){this.#i=e}setMessages(e){this.#r=e}setModel(e){this.#t=e}prompt(e,t){return fe(e,{system:this.#n,messages:this.#r,tools:this.#i},{api:this.#e,model:this.#t,apiKey:this.#a,baseUrl:this.#o},t)}};function he(e,t){let n=t.map(e=>`- ${e.def.name}: ${e.def.description}`).join(`
|
|
7
7
|
`),r=c.platform(),i=c.arch();return`You are Nova, an expert coding assistant. Help users with coding tasks using the tools available.
|
|
8
8
|
|
|
9
9
|
Format your responses with clean, standard markdown. Use headers (##, ###), bold text (**bold**), inline code (\`code\`), and code blocks (\`\`\`lang) to make your output clear and readable in the terminal.
|
|
@@ -38,7 +38,7 @@ ${n}
|
|
|
38
38
|
- Never delete files outside the working directory.
|
|
39
39
|
- Never run destructive commands unless the user explicitly confirms.
|
|
40
40
|
- If unsure, ask for clarification.
|
|
41
|
-
- Never expose API keys, tokens, or secrets.`}function ge(){return`${Date.now().toString(36)}-${crypto.randomUUID().slice(0,8)}`}var _e=class{#e;constructor(
|
|
41
|
+
- Never expose API keys, tokens, or secrets.`}function ge(){return`${Date.now().toString(36)}-${crypto.randomUUID().slice(0,8)}`}var _e=class e{#e;constructor(t){this.#e=e.#t(t)}static#t(e){let t=e=>(e.pragma(`journal_mode = WAL`),e.pragma(`foreign_keys = ON`),e.exec(`
|
|
42
42
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
43
43
|
id TEXT PRIMARY KEY,
|
|
44
44
|
cwd TEXT NOT NULL,
|
|
@@ -69,28 +69,28 @@ CREATE TABLE IF NOT EXISTS compactions (
|
|
|
69
69
|
seq_before INTEGER NOT NULL,
|
|
70
70
|
ts INTEGER NOT NULL
|
|
71
71
|
);
|
|
72
|
-
`)}create(e,t,n){let r=ge(),i=Date.now();return this.#e.prepare(`INSERT INTO sessions (id, cwd, model, provider, title, created, updated) VALUES ($id, $cwd, $model, $provider, $title, $created, $updated)`).run({id:r,cwd:e,model:t,provider:n,title:null,created:i,updated:i}),{id:r,cwd:e,model:t,provider:n,title:null,created:i,updated:i}}get(e){return this.#e.prepare(`SELECT id, cwd, model, provider, title, created, updated FROM sessions WHERE id = $id`).get({id:e})??null}list(e=50){return this.#e.prepare(`SELECT id, cwd, model, provider, title, created, updated FROM sessions ORDER BY updated DESC LIMIT $limit`).all({limit:e})}delete(e){return this.#e.prepare(`DELETE FROM sessions WHERE id = $id`).run({id:e}).changes>0}append(e,t){let n=this.#
|
|
72
|
+
`),e);try{return t(new u(e))}catch{for(let t of[e,`${e}-wal`,`${e}-shm`])try{l(t)}catch{}return t(new u(e))}}create(e,t,n){let r=ge(),i=Date.now();return this.#e.prepare(`INSERT INTO sessions (id, cwd, model, provider, title, created, updated) VALUES ($id, $cwd, $model, $provider, $title, $created, $updated)`).run({id:r,cwd:e,model:t,provider:n,title:null,created:i,updated:i}),{id:r,cwd:e,model:t,provider:n,title:null,created:i,updated:i}}get(e){return this.#e.prepare(`SELECT id, cwd, model, provider, title, created, updated FROM sessions WHERE id = $id`).get({id:e})??null}list(e=50){return this.#e.prepare(`SELECT id, cwd, model, provider, title, created, updated FROM sessions ORDER BY updated DESC LIMIT $limit`).all({limit:e})}delete(e){return this.#e.prepare(`DELETE FROM sessions WHERE id = $id`).run({id:e}).changes>0}append(e,t){let n=this.#n(e);this.#e.prepare(`INSERT INTO messages (session_id, seq, role, content, ts) VALUES ($sid, $seq, $role, $content, $ts)`).run({sid:e,seq:n,role:t.role,content:JSON.stringify(t),ts:t.ts}),this.#e.prepare(`UPDATE sessions SET updated = $now WHERE id = $id`).run({now:Date.now(),id:e})}appendMany(e,t){this.#e.transaction(()=>{for(let n of t)this.append(e,n)})()}messages(e){return this.#e.prepare(`SELECT content FROM messages WHERE session_id = $sid ORDER BY seq ASC`).all({sid:e}).map(e=>JSON.parse(e.content))}messagesAfter(e,t){return this.#e.prepare(`SELECT content FROM messages WHERE session_id = $sid AND seq > $seq ORDER BY seq ASC`).all({sid:e,seq:t}).map(e=>JSON.parse(e.content))}setTitle(e,t){this.#e.prepare(`UPDATE sessions SET title = $title WHERE id = $id`).run({title:t,id:e})}messageCount(e){return this.#e.prepare(`SELECT COUNT(*) as count FROM messages WHERE session_id = $sid`).get({sid:e}).count}saveCompaction(e,t,n,r,i){this.#e.prepare(`INSERT INTO compactions (session_id, summary, files_read, files_wrote, seq_before, ts) VALUES ($sid, $summary, $read, $wrote, $seq, $ts)`).run({sid:e,summary:t,read:JSON.stringify(n),wrote:JSON.stringify(r),seq:i,ts:Date.now()})}getLatestCompaction(e){return this.#e.prepare(`SELECT summary, seq_before FROM compactions WHERE session_id = $sid ORDER BY ts DESC LIMIT 1`).get({sid:e})??null}truncateBeforeSeq(e,t){this.#e.prepare(`DELETE FROM messages WHERE session_id = $sid AND seq < $seq`).run({sid:e,seq:t})}close(){this.#e.close()}#n(e){return(this.#e.prepare(`SELECT MAX(seq) as maxSeq FROM messages WHERE session_id = $sid`).get({sid:e}).maxSeq??0)+1}};let j=null;function M(e){return j||(j=new _e(a(e??a(process.env.HOME??`~`,`.novacode`),`sessions.db`)),j)}async function ve(e){let t=M(),[n,r]=e;if(n===`list`||n===`ls`){let e=t.list();if(e.length===0){console.log(`No sessions found.`);return}console.log(`ID`.padEnd(25),`MODEL`.padEnd(20),`UPDATED`),console.log(`-`.repeat(70));for(let t of e){let e=new Date(t.updated).toLocaleString();console.log(t.id.padEnd(25),t.model.padEnd(20),e)}return}if(n===`delete`||n===`rm`){r||(console.error(`Usage: novacode session delete <id>`),process.exit(1)),t.delete(r)?console.log(`Deleted session: ${r}`):(console.error(`Session not found: ${r}`),process.exit(1));return}console.error(`Unknown session subcommand. Use 'list' or 'delete'.`),process.exit(1)}const N=[{id:`glm`,name:`GLM (Z.AI)`,api:`openai`,baseUrl:`https://api.z.ai/api/coding/paas/v4`,envKey:`GLM_API_KEY`},{id:`gemini`,name:`Gemini (Google)`,api:`gemini`,baseUrl:`https://generativelanguage.googleapis.com`,envKey:`GEMINI_API_KEY`},{id:`deepseek`,name:`DeepSeek`,api:`openai`,baseUrl:`https://api.deepseek.com`,envKey:`DEEPSEEK_API_KEY`},{id:`openai`,name:`OpenAI`,api:`openai`,baseUrl:`https://api.openai.com/v1`,envKey:`OPENAI_API_KEY`}],P=[{id:`glm-5.1`,name:`GLM-5.1`,provider:`glm`,contextWindow:128e3,maxTokens:4096,supportsThinking:!1},{id:`glm-5`,name:`GLM-5`,provider:`glm`,contextWindow:128e3,maxTokens:4096,supportsThinking:!1},{id:`glm-5-turbo`,name:`GLM-5 Turbo`,provider:`glm`,contextWindow:128e3,maxTokens:4096,supportsThinking:!1},{id:`glm-4.7`,name:`GLM-4.7`,provider:`glm`,contextWindow:128e3,maxTokens:4096,supportsThinking:!1},{id:`glm-4.7-flash`,name:`GLM-4.7 Flash (Free)`,provider:`glm`,contextWindow:128e3,maxTokens:4096,supportsThinking:!1},{id:`glm-4.5-flash`,name:`GLM-4.5 Flash (Free)`,provider:`glm`,contextWindow:128e3,maxTokens:4096,supportsThinking:!1},{id:`gemini-3.5-flash`,name:`Gemini 3.5 Flash`,provider:`gemini`,contextWindow:1e6,maxTokens:65536,supportsThinking:!0},{id:`gemini-3.1-pro-preview`,name:`Gemini 3.1 Pro Preview`,provider:`gemini`,contextWindow:2e6,maxTokens:65536,supportsThinking:!0},{id:`gemini-3.1-pro-preview-customtools`,name:`Gemini 3.1 Pro (Custom Tools)`,provider:`gemini`,contextWindow:2e6,maxTokens:65536,supportsThinking:!0},{id:`gemini-3.1-flash-lite`,name:`Gemini 3.1 Flash-Lite`,provider:`gemini`,contextWindow:1e6,maxTokens:65536,supportsThinking:!0},{id:`gemini-3.1-flash-lite-preview`,name:`Gemini 3.1 Flash-Lite Preview`,provider:`gemini`,contextWindow:1e6,maxTokens:65536,supportsThinking:!0},{id:`gemini-3-flash-preview`,name:`Gemini 3 Flash Preview`,provider:`gemini`,contextWindow:1e6,maxTokens:65536,supportsThinking:!0},{id:`gemini-2.5-pro`,name:`Gemini 2.5 Pro`,provider:`gemini`,contextWindow:2e6,maxTokens:65536,supportsThinking:!1},{id:`gemini-2.5-flash`,name:`Gemini 2.5 Flash`,provider:`gemini`,contextWindow:1e6,maxTokens:65536,supportsThinking:!1},{id:`gemini-2.5-flash-lite`,name:`Gemini 2.5 Flash-Lite`,provider:`gemini`,contextWindow:1e6,maxTokens:65536,supportsThinking:!1},{id:`gemini-2.5-computer-use-preview-10-2025`,name:`Gemini 2.5 Computer Use`,provider:`gemini`,contextWindow:1e6,maxTokens:65536,supportsThinking:!1},{id:`deepseek-chat`,name:`DeepSeek V3`,provider:`deepseek`,contextWindow:64e3,maxTokens:8192,supportsThinking:!1},{id:`deepseek-reasoner`,name:`DeepSeek R1`,provider:`deepseek`,contextWindow:64e3,maxTokens:8192,supportsThinking:!0},{id:`gpt-4o`,name:`GPT-4o`,provider:`openai`,contextWindow:128e3,maxTokens:16384,supportsThinking:!1},{id:`o4-mini`,name:`o4-mini`,provider:`openai`,contextWindow:2e5,maxTokens:1e5,supportsThinking:!0}];function F(e){return N.find(t=>t.id===e)}function ye(e){return P.filter(t=>t.provider===e)}const I=()=>a(process.env.HOME??`~`,`.novacode`),L=()=>a(I(),`config.json`),R=()=>a(I(),`auth.json`),z={provider:``,model:``},B={apiKeys:{}};async function be(){try{return await h(L()),!0}catch{return!1}}async function V(){try{let e=JSON.parse(await p(L(),`utf-8`));return{...z,...e}}catch{return{...z}}}async function H(){try{let e=JSON.parse(await p(R(),`utf-8`));return{...B,...e}}catch{return{...B}}}async function U(){await f(I(),{recursive:!0})}async function W(e){await U(),await g(L(),JSON.stringify(e,null,2))}async function G(e){await U(),await g(R(),JSON.stringify(e,null,2));try{await d(R(),384)}catch{}}function K({message:e,options:t,header:n,onSelect:r}){let[i,a]=x(0);return b((e,n)=>{if(n.escape){r(null);return}if(n.upArrow){a(e=>(e-1+t.length)%t.length);return}if(n.downArrow){a(e=>(e+1)%t.length);return}n.return&&r(t[i]?.value??null)}),C(_,{flexDirection:`column`,paddingX:1,children:[n&&S(_,{marginBottom:1,children:S(v,{children:n})}),S(_,{marginBottom:1,children:S(v,{bold:!0,children:e})}),t.map((e,t)=>C(_,{children:[C(v,{color:t===i?`green`:void 0,children:[t===i?`❯ `:` `,e.label]}),e.hint&&t===i&&C(v,{dimColor:!0,children:[` `,e.hint]})]},e.value)),S(_,{marginTop:1,children:S(v,{dimColor:!0,children:`↑↓ navigate · Enter select · Esc cancel`})})]})}function q({message:e,validate:t,onSubmit:n}){let[r,i]=x(``),[a,o]=x(``);return b((e,a)=>{if(a.escape){n(null);return}if(a.return){let e=t?.(r);if(e){o(e);return}n(r);return}if(a.backspace||a.delete){i(e=>e.slice(0,-1)),o(``);return}e&&(i(t=>t+e),o(``))}),C(_,{flexDirection:`column`,paddingX:1,children:[S(_,{marginBottom:1,children:S(v,{bold:!0,children:e})}),C(_,{children:[S(v,{color:`green`,children:`│ `}),S(v,{dimColor:!0,children:`*`.repeat(r.length)}),S(v,{color:`green`,children:`│`})]}),a&&S(_,{children:S(v,{color:`red`,children:a})}),S(_,{marginTop:1,children:S(v,{dimColor:!0,children:`Enter submit · Esc cancel`})})]})}function xe({message:e,onConfirm:t}){let[n,r]=x(!0);return b((e,i)=>{if(i.escape){t(null);return}if(i.leftArrow||i.rightArrow||i.tab){r(e=>!e);return}i.return&&t(n)}),C(_,{flexDirection:`column`,paddingX:1,children:[S(_,{marginBottom:1,children:S(v,{bold:!0,children:e})}),S(_,{children:C(v,{color:n?`green`:void 0,children:[n?`❯ `:` `,`Yes`]})}),S(_,{children:C(v,{color:n?void 0:`red`,children:[n?` `:`❯ `,`No`]})}),S(_,{marginTop:1,children:S(v,{dimColor:!0,children:`←→ toggle · Enter confirm · Esc cancel`})})]})}function Se(e,t,n){return new Promise(r=>{let{unmount:i}=y(S(K,{message:e,options:t,header:n,onSelect:e=>{i(),r(e)}}))})}function Ce(e,t){return new Promise(n=>{let{unmount:r}=y(S(q,{message:e,validate:t,onSubmit:e=>{r(),n(e)}}))})}async function we(){console.log(t.bold.cyan(`
|
|
73
73
|
⚡ Nova — your coding companion
|
|
74
|
-
`));let e=await Se(`Pick a provider`,
|
|
74
|
+
`));let e=await Se(`Pick a provider`,N.map(e=>({value:e.id,label:e.name})));e||(console.log(t.dim(`Cancelled`)),process.exit(0));let n=F(e);n||(console.log(t.red(`Unknown provider`)),process.exit(1));let r=await Ce(`Enter ${n.name} API key`);r||(console.log(t.dim(`Cancelled`)),process.exit(0));let i=await Se(`Pick a default model`,ye(e).map(e=>({value:e.id,label:`${e.name} (${(e.contextWindow/1e3).toFixed(0)}k ctx)`})));i||(console.log(t.dim(`Cancelled`)),process.exit(0));let a={provider:e,model:i};return await W(a),await G({apiKeys:{[e]:r}}),console.log(t.green(`
|
|
75
75
|
✓ Ready. Type your prompt or /help for commands
|
|
76
|
-
`)),a}const Te=new Set([`.jpg`,`.jpeg`,`.png`,`.gif`,`.webp`]);function
|
|
77
|
-
`),o=Math.max(0,(Number(t.offset??1)||1)-1),s=Number(t.limit??2e3)||2e3,c=a.slice(o,o+s),l=o+s<a.length;return{content:[
|
|
78
|
-
`)+(l?`\n…${a.length-o-s} more lines`:``))],isError:!1}}catch(e){return{content:[
|
|
76
|
+
`)),a}const Te=new Set([`.jpg`,`.jpeg`,`.png`,`.gif`,`.webp`]);function J(e,t){let n=s(e,t);if(n!==e&&!n.startsWith(`${e}/`))throw Error(`Path outside project: ${t}`);return n}function Ee(e){return{def:{name:`read`,description:`Read file contents. Supports text and images (jpg, png, gif, webp). Text output is truncated to 2000 lines.`,parameters:{type:`object`,properties:{path:{type:`string`,description:`Path to file (relative or absolute)`},offset:{type:`number`,description:`Start line (1-based, default 1)`},limit:{type:`number`,description:`Max lines to read (default 2000)`}},required:[`path`]}},async execute(t){try{let n=J(e,t.path),i=r(n).toLowerCase();if(Te.has(i))return{content:[{type:`image`,data:(await p(n)).toString(`base64`),mime:i===`.jpg`?`image/jpeg`:`image/${i.slice(1)}`}],isError:!1};let a=(await p(n,`utf-8`)).split(`
|
|
77
|
+
`),o=Math.max(0,(Number(t.offset??1)||1)-1),s=Number(t.limit??2e3)||2e3,c=a.slice(o,o+s),l=o+s<a.length;return{content:[k(c.join(`
|
|
78
|
+
`)+(l?`\n…${a.length-o-s} more lines`:``))],isError:!1}}catch(e){return{content:[k(`Error reading file: ${e.message}`)],isError:!0}}}}}function De(e){return{def:{name:`write`,description:`Write content to a file. Creates the file and parent directories if needed.`,parameters:{type:`object`,properties:{path:{type:`string`,description:`Path to file`},content:{type:`string`,description:`Content to write`}},required:[`path`,`content`]}},async execute(t){try{let r=J(e,t.path),i=t.content;await f(n(r),{recursive:!0}),await g(r,i);let a=A(e,r);return{content:[k(`Wrote ${i.length} bytes → ${a}`)],isError:!1}}catch(e){return{content:[k(`Error writing file: ${e.message}`)],isError:!0}}}}}function Oe(e){return{def:{name:`edit`,description:`Edit a file using exact text replacement. Each edit's oldText must be unique in the file.`,parameters:{type:`object`,properties:{path:{type:`string`,description:`Path to file`},edits:{type:`array`,description:`Array of {oldText, newText} replacements. oldText must be unique. Non-overlapping.`,items:{type:`object`,properties:{oldText:{type:`string`,description:`Exact text to find (must be unique)`},newText:{type:`string`,description:`Replacement text`}},required:[`oldText`,`newText`]}}},required:[`path`,`edits`]}},async execute(t){try{let n=J(e,t.path),r;try{r=await p(n,`utf-8`)}catch{return{content:[k(`File not found: ${t.path}`)],isError:!0}}let i=t.edits;for(let e of i){let t=r.split(e.oldText).length-1;if(t===0)return{content:[k(`oldText not found: "${e.oldText.slice(0,80)}…"`)],isError:!0};if(t>1)return{content:[k(`oldText found ${t} times — add surrounding context to make it unique: "${e.oldText.slice(0,60)}…"`)],isError:!0}}for(let e of i)r=r.replace(e.oldText,e.newText);return await g(n,r),{content:[k(`Edited ${A(e,n)} (${i.length} replacement${i.length>1?`s`:``})`)],isError:!1}}catch(e){return{content:[k(`Error editing file: ${e.message}`)],isError:!0}}}}}function ke(e){return{def:{name:`git`,description:`Execute safe, non-interactive git commands (status, diff, log, add, commit) in the repository.`,parameters:{type:`object`,properties:{action:{type:`string`,enum:[`status`,`diff`,`log`,`add`,`commit`],description:`The git action to execute`},args:{type:`array`,description:`Optional additional arguments or file paths for the git action`,items:{type:`string`}}},required:[`action`]}},async execute(t,n){let r=t.action,i=t.args||[];if(!new Set([`status`,`diff`,`log`,`add`,`commit`]).has(r))return{content:[k(`Error: Git action '${r}' is not supported.`)],isError:!0};try{let t=[`git`,r,...i],a=w(t[0],t.slice(1),{cwd:e,stdio:[`ignore`,`pipe`,`pipe`],env:{...process.env,PAGER:`cat`}}),o=``,s=``;a.stdout.on(`data`,e=>{o+=e.toString()}),a.stderr.on(`data`,e=>{s+=e.toString()});let c=()=>{a.kill(`SIGKILL`),a.stdout.destroy(),a.stderr.destroy()};n?.addEventListener(`abort`,c,{once:!0});let l;try{l=await new Promise((e,t)=>{a.on(`error`,t),a.on(`close`,t=>e(t??-1))})}finally{n?.removeEventListener(`abort`,c)}let u=5e4,d=``;return o&&(d+=o.slice(0,u)),s&&(d&&(d+=`
|
|
79
79
|
`),d+=s.slice(0,u-d.length)),d.length>=u&&(d+=`
|
|
80
|
-
…truncated`),{content:[
|
|
81
|
-
`):`No files found`)],isError:!1}}catch(e){return{content:[
|
|
80
|
+
…truncated`),{content:[k(d||`(no output)`)],isError:l!==0}}catch(e){return{content:[k(`Error running git: ${e.message}`)],isError:!0}}}}}function Ae(e){return{def:{name:`glob`,description:`Find files by glob pattern (e.g. **/*.ts, src/**/*.test.ts).`,parameters:{type:`object`,properties:{pattern:{type:`string`,description:`Glob pattern (e.g. **/*.ts)`},path:{type:`string`,description:`Directory to search in (default .)`},nocase:{type:`boolean`,description:`Case-insensitive search (default false)`}},required:[`pattern`]}},async execute(t){try{let n=t.path||`.`,r=s(e,n);if(r!==e&&!r.startsWith(`${e}/`))throw Error(`Path outside project: ${n}`);let i=t.pattern,a=(await T(i,{cwd:r,nocase:!!t.nocase})).slice(0,500),c=o(e,r),l=c?`${c}/`:``,u=a.map(e=>l+e);return{content:[k(u.length>0?u.join(`
|
|
81
|
+
`):`No files found`)],isError:!1}}catch(e){return{content:[k(`Error: ${e.message}`)],isError:!0}}}}}function je(e){return{def:{name:`grep`,description:`Search file contents with a regex pattern. Returns matching lines with file paths and line numbers.`,parameters:{type:`object`,properties:{pattern:{type:`string`,description:`Regex pattern to search for`},path:{type:`string`,description:`Directory or file to search in (default .)`},glob:{type:`string`,description:`File filter glob (e.g. *.ts)`}},required:[`pattern`]}},async execute(t,n){try{let r=t.path||`.`,i=s(e,r);if(i!==e&&!i.startsWith(`${e}/`))throw Error(`Path outside project: ${r}`);let a=t.pattern,c=t.glob,l=o(e,i)||`.`;try{let t=[`rg`,`--line-number`,`--max-count`,`200`];c&&t.push(`--glob=${c}`),t.push(`--`,a,l);let r=w(t[0],t.slice(1),{cwd:e,stdio:[`ignore`,`pipe`,`pipe`]}),i=()=>{r.kill(),r.stdout.destroy(),r.stderr.destroy()};n?.addEventListener(`abort`,i,{once:!0});let o=``;r.stdout.on(`data`,e=>{o+=e.toString()});let s;try{s=await new Promise((e,t)=>{r.on(`error`,t),r.on(`close`,t=>e(t??-1))})}finally{n?.removeEventListener(`abort`,i)}if(s===0)return{content:[k(o.split(`
|
|
82
82
|
`).slice(0,200).join(`
|
|
83
|
-
`)||`No matches`)],isError:!1}}catch{}let u=await
|
|
84
|
-
`);for(let n=0;n<t.length&&m.length<200;n++){let r=t[n];r&&
|
|
85
|
-
`)||`No matches`)],isError:!1}}catch(e){return{content:[
|
|
86
|
-
`)||`(empty)`)],isError:!1}}catch(e){return{content:[
|
|
83
|
+
`)||`No matches`)],isError:!1}}catch{}let u=await T(c||`**/*`,{cwd:i,ignore:[`**/node_modules/**`,`**/.git/**`]}),d=l===`.`?``:`${l}/`,f=new RegExp(a,`i`),m=[];for(let e of u.slice(0,500)){if(n?.aborted)break;try{let t=(await p(s(i,e),`utf-8`)).split(`
|
|
84
|
+
`);for(let n=0;n<t.length&&m.length<200;n++){let r=t[n];r&&f.test(r)&&m.push(`${d}${e}:${n+1}:${r}`)}}catch{}}return{content:[k(m.join(`
|
|
85
|
+
`)||`No matches`)],isError:!1}}catch(e){return{content:[k(`Error: ${e.message}`)],isError:!0}}}}}function Me(e){return{def:{name:`ls`,description:`List directory contents.`,parameters:{type:`object`,properties:{path:{type:`string`,description:`Directory to list (default .)`}},required:[]}},async execute(t){try{return{content:[k((await m(s(e,t.path||`.`),{withFileTypes:!0})).map(e=>{let t=e.isDirectory()?`/`:e.isSymbolicLink()?`@`:``;return`${e.name}${t}`}).join(`
|
|
86
|
+
`)||`(empty)`)],isError:!1}}catch(e){return{content:[k(`Error: ${e.message}`)],isError:!0}}}}}function Ne(e){return{def:{name:`tree`,description:`Print a visual directory tree structure, ignoring common ignored folders like node_modules and .git.`,parameters:{type:`object`,properties:{path:{type:`string`,description:`Directory to start tree from (default .)`},depth:{type:`number`,description:`Maximum depth to traverse (default 3)`}},required:[]}},async execute(t){try{let n=s(e,t.path||`.`),r=Number(t.depth??3)||3,i=new Set([`.git`,`node_modules`,`dist`,`build`,`.svelte-kit`,`.next`,`out`,`.scannerwork`,`coverage`]);async function a(e,t,n){if(t>r)return``;let o=``,c=(await m(e,{withFileTypes:!0})).filter(e=>!i.has(e.name)).sort((e,t)=>e.isDirectory()&&!t.isDirectory()?-1:!e.isDirectory()&&t.isDirectory()?1:e.name.localeCompare(t.name));for(let r=0;r<c.length;r++){let i=c[r],l=r===c.length-1,u=l?`└── `:`├── `,d=n+(l?` `:`│ `);o+=`${n}${u}${i.name}${i.isDirectory()?`/`:``}\n`,i.isDirectory()&&(o+=await a(s(e,i.name),t+1,d))}return o}return{content:[k(await a(n,1,``)||`(empty)`)],isError:!1}}catch(e){return{content:[k(`Error: ${e.message}`)],isError:!0}}}}}function Pe(e){return{def:{name:`bash`,description:`Execute a shell command. Returns stdout and stderr. Timeout after N seconds (default 120).`,parameters:{type:`object`,properties:{command:{type:`string`,description:`Shell command to run`},timeout:{type:`number`,description:`Timeout in seconds (default 120)`}},required:[`command`]}},async execute(t,n){let r=t.command,i=(Number(t.timeout)||120)*1e3;try{let t=w(`sh`,[`-c`,r],{cwd:e,stdio:[`ignore`,`pipe`,`pipe`]}),a=``,o=``;t.stdout.on(`data`,e=>{a+=e.toString()}),t.stderr.on(`data`,e=>{o+=e.toString()});let s=!1,c=setTimeout(()=>{s=!0,t.kill(`SIGKILL`),t.stdout.destroy(),t.stderr.destroy()},i),l=()=>{s=!0,t.kill(`SIGKILL`),t.stdout.destroy(),t.stderr.destroy()};n?.addEventListener(`abort`,l,{once:!0});let u;try{u=await new Promise((e,n)=>{t.on(`error`,n),t.on(`close`,t=>e(t??-1))})}finally{clearTimeout(c),n?.removeEventListener(`abort`,l)}let d=5e4,f=``;return a&&(f+=a.slice(0,d)),o&&(f&&(f+=`
|
|
87
87
|
`),f+=o.slice(0,d-f.length)),f.length>=d&&(f+=`
|
|
88
|
-
…truncated`),s&&(f+=`\n[timeout after ${i/1e3}s]`),f+=`\n[exit ${u}]`,{content:[
|
|
88
|
+
…truncated`),s&&(f+=`\n[timeout after ${i/1e3}s]`),f+=`\n[exit ${u}]`,{content:[k(f)],isError:u!==0||s}}catch(e){return{content:[k(`Error: ${e.message}`)],isError:!0}}}}}const Y=5e4;function X(e){let t=e.replace(/<!--[\s\S]*?-->/g,``).replace(/<script[\s\S]*?<\/script>/gi,``).replace(/<style[\s\S]*?<\/style>/gi,``).replace(/<a[^>]*href=["']?([^"'>\s]*)["']?[^>]*>([\s\S]*?)<\/a>/gi,`[$2]($1)`).replace(/<\/?(p|div|br|h[1-6]|li|tr|blockquote|pre|hr)[^>]*>/gi,`
|
|
89
89
|
`).replace(/<[^>]+>/g,``).replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`).replace(/"/g,`"`).replace(/"/g,`"`).replace(/'/g,`'`).replace(/'/g,`'`).replace(/'/g,`'`).replace(/“/g,`"`).replace(/”/g,`"`).replace(/‘/g,`'`).replace(/’/g,`'`).replace(/ /g,` `).replace(/[ \t]+/g,` `).replace(/\n{3,}/g,`
|
|
90
90
|
|
|
91
|
-
`).trim();return t.length>
|
|
91
|
+
`).trim();return t.length>Y&&(t=`${t.slice(0,Y)}\n…truncated`),t}function Fe(){return{def:{name:`web_search`,description:`Search the web using DuckDuckGo. Returns up to 10 results with titles, URLs, and snippets. Use this when you need information from the internet.`,parameters:{type:`object`,properties:{query:{type:`string`,description:`Search query`}},required:[`query`]}},async execute(e,t){let n=e.query;if(!n.trim())return{content:[k(`Error: empty search query`)],isError:!0};try{let e=`https://html.duckduckgo.com/html/?q=${encodeURIComponent(n)}`,r=await fetch(e,{signal:t??void 0,headers:{"User-Agent":`Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36`}});if(!r.ok)return{content:[k(`Search failed: HTTP ${r.status}`)],isError:!0};let i=await r.text(),a=[],o=/<div[^>]*class="[^"]*result__body[^"]*"[^>]*>/gi,s=[];for(let e of i.matchAll(o))e.index!==void 0&&s.push(e.index);if(s.length>0)for(let e=0;e<s.length;e++){let t=s[e],n=s[e+1]??i.length;a.push(i.slice(t,n))}let c=[],l=/<a[^>]*class="result__a"[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/i,u=/<a[^>]*class="result__snippet"[^>]*>([\s\S]*?)<\/a>/i;for(let e of a){if(c.length>=10)break;let t=l.exec(e);if(!t)continue;let n=t[1],r=X(t[2]),i=u.exec(e),a=i?X(i[1]):``,o=n;try{let e=n.startsWith(`//`)?`https:${n}`:n.startsWith(`/`)?`https://duckduckgo.com${n}`:n,t=new URL(e).searchParams.get(`uddg`);t&&(o=t)}catch{}c.push(`## ${r}\n${o}\n${a}`)}return c.length===0?{content:[k(`No results found.`)],isError:!1}:{content:[k(c.join(`
|
|
92
92
|
|
|
93
|
-
`))],isError:!1}}catch(e){let t=e.message;return t.includes(`abort`)?{content:[
|
|
93
|
+
`))],isError:!1}}catch(e){let t=e.message;return t.includes(`abort`)?{content:[k(`Search aborted.`)],isError:!0}:{content:[k(`Search error: ${t}`)],isError:!0}}}}}function Ie(){return{def:{name:`web_fetch`,description:`Fetch and read the content of a web page. Returns the page text with HTML tags stripped. Useful for reading documentation, articles, or API references.`,parameters:{type:`object`,properties:{url:{type:`string`,description:`URL to fetch`}},required:[`url`]}},async execute(e,t){let n=e.url;if(!n.trim())return{content:[k(`Error: empty URL`)],isError:!0};try{new URL(n)}catch{return{content:[k(`Error: invalid URL: ${n}`)],isError:!0}}try{let e=await fetch(n,{signal:t??void 0,headers:{"User-Agent":`Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36`,Accept:`text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8`},redirect:`follow`});if(!e.ok)return{content:[k(`Fetch failed: HTTP ${e.status} ${e.statusText}`)],isError:!0};let r=(e.headers.get(`content-type`)??``).toLowerCase(),i=await e.text();return r.includes(`text/html`)||r.includes(`application/xhtml+xml`)||i.trim().toLowerCase().startsWith(`<!doctype html`)||i.trim().toLowerCase().startsWith(`<html`)?{content:[k(X(i))],isError:!1}:{content:[k(i.length>Y?`${i.slice(0,Y)}\n…truncated`:i)],isError:!1}}catch(e){let t=e.message;return t.includes(`abort`)?{content:[k(`Fetch aborted.`)],isError:!0}:{content:[k(`Fetch error: ${t}`)],isError:!0}}}}}function Le(e){return[Ee(e),De(e),Oe(e),Pe(e),Ae(e),je(e),Me(e),Ne(e),ke(e),Fe(),Ie()]}const Re=n(ee(import.meta.url));let Z=null,Q=null;async function $(){if(Q)return Q;try{let e=await p(a(Re,`..`,`package.json`),`utf-8`);return Q=JSON.parse(e).version??`0.0.0`,Q}catch{return`0.0.0`}}async function ze(){if(Z)return Z;try{let e=w(`npm`,[`info`,`novacode`,`version`],{stdio:[`ignore`,`pipe`,`ignore`]}),t=await new Promise((t,n)=>{let r=``;e.stdout.on(`data`,e=>{r+=e.toString()}),e.on(`error`,n),e.on(`close`,()=>t(r.trim()))});if(t)return Z=t,t}catch{}return null}async function Be(){let e=await $(),t=await ze();return t?{hasUpdate:te.gt(t,e),current:e,latest:t}:null}async function Ve(e=!1){try{let t=w(`npm`,[`update`,`-g`,`novacode`],{stdio:e?`ignore`:`inherit`}),n=await new Promise((e,n)=>{t.on(`error`,n),t.on(`close`,t=>e(t??-1))});return n===0?(e||console.log(`✓ novacode updated to latest version successfully.`),!0):(e||(console.error(`Update failed (exit code ${n})`),process.exit(1)),!1)}catch(t){return e||(console.error(`Update failed: ${t.message}`),process.exit(1)),!1}}function He(){let{values:t,positionals:n}=e({options:{help:{type:`boolean`,short:`h`},version:{type:`boolean`,short:`v`},provider:{type:`string`},model:{type:`string`},"api-key":{type:`string`},session:{type:`string`,short:`s`}},strict:!0,allowPositionals:!0});return{flags:t,args:n}}function Ue(e,t){return P.find(n=>t?n.provider===t&&n.id===e:n.id===e)}async function We(){let{flags:e,args:n}=He();if(e.version){let e=await $();console.log(`nova ${e}`),process.exit(0)}if(e.help&&(console.log(`nova — open-source coding agent
|
|
94
94
|
|
|
95
95
|
Usage:
|
|
96
96
|
nova Interactive mode
|
|
@@ -104,7 +104,7 @@ Options:
|
|
|
104
104
|
--provider <id> Provider to use
|
|
105
105
|
--model <id> Model to use
|
|
106
106
|
--api-key <key> API key override
|
|
107
|
-
-s, --session <id> Resume session by ID`),process.exit(0)),n[0]===`session`){await ve(n.slice(1));return}if(n[0]===`update`){await
|
|
107
|
+
-s, --session <id> Resume session by ID`),process.exit(0)),n[0]===`session`){await ve(n.slice(1));return}if(n[0]===`update`){await Ve();return}n.length>0&&(console.error(t.yellow(`Unknown command: ${n.join(` `)}`)),console.error("Run `nova --help` for usage."),process.exit(1));let r=new AbortController,i=()=>{r.abort(),process.stderr.write(`
|
|
108
108
|
Aborted.
|
|
109
|
-
`),process.exit(130)};process.on(`SIGINT`,i),process.on(`SIGTERM`,i);let a=await(await be()?
|
|
109
|
+
`),process.exit(130)};process.on(`SIGINT`,i),process.on(`SIGTERM`,i);let a=await(await be()?V():we()),o=await H(),s=e.provider||a.provider,c=e.model||a.model,l=e[`api-key`]||o.apiKeys[s],u=F(s);u||(console.error(`Unknown provider: ${s}`),console.error(`Available: ${F(`glm`)?`glm, `:``}gemini, deepseek, openai`),process.exit(1)),l||(console.error(`No API key for ${u.name}. Set ${u.envKey} or run nova for onboarding.`),process.exit(1));let d=Ue(c,s);if(!d){console.error(`Unknown model: ${c}`),console.error(`Available models:`);for(let e of P.filter(e=>e.provider===s))console.error(` ${e.id} — ${e.name}`);process.exit(1)}let f=process.cwd(),p=Le(f),m=he(f,p),h=M(),g=e.session?h.get(e.session):h.create(f,d.id,s);e.session&&!g&&(console.error(`Session not found: ${e.session}`),process.exit(1));let _=g.id,v=h.messages(_),y=new me({api:u.api,model:d,apiKey:l,baseUrl:u.baseUrl,system:m,tools:p,messages:v});process.off(`SIGINT`,i),process.off(`SIGTERM`,i);let{interactive:b}=await import(`./app-QfQR2FN9.mjs`);await b(y,h,_)}process.on(`unhandledRejection`,e=>{console.error(`Unhandled rejection:`,e),process.exit(1)}),We().catch(e=>{console.error(`Fatal:`,e),process.exit(1)});export{q as a,V as c,P as d,N as f,D as g,ue as h,xe as i,G as l,O as m,$ as n,K as o,F as p,Ve as r,H as s,Be as t,W as u};
|
|
110
110
|
//# sourceMappingURL=main.mjs.map
|