novacode 0.5.5 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -10
- package/dist/app-CbJSUNmf.mjs +22 -0
- package/dist/app-CbJSUNmf.mjs.map +1 -0
- package/dist/main.mjs +51 -61
- package/dist/main.mjs.map +1 -1
- package/package.json +3 -5
- package/dist/app-QfQR2FN9.mjs +0 -21
- package/dist/app-QfQR2FN9.mjs.map +0 -1
- package/src/agent/agent.ts +0 -87
- package/src/agent/loop.ts +0 -237
- package/src/agent/prompt.ts +0 -50
- package/src/commands/compact.ts +0 -28
- package/src/commands/index.ts +0 -86
- package/src/commands/models.ts +0 -85
- package/src/commands/providers.ts +0 -213
- package/src/commands/session.ts +0 -40
- package/src/config/providers.ts +0 -207
- package/src/config/store.ts +0 -66
- package/src/main.ts +0 -175
- package/src/onboarding/wizard.ts +0 -54
- package/src/provider/gemini.ts +0 -261
- package/src/provider/openai.ts +0 -215
- package/src/provider/stream.ts +0 -138
- package/src/session/compact.ts +0 -126
- package/src/session/store.ts +0 -229
- package/src/tools/fs.ts +0 -189
- package/src/tools/git.ts +0 -99
- package/src/tools/index.ts +0 -33
- package/src/tools/search.ts +0 -274
- package/src/tools/shell.ts +0 -90
- package/src/tools/web.ts +0 -239
- package/src/tui/app.tsx +0 -364
- package/src/tui/components/liveArea.tsx +0 -73
- package/src/tui/components/message.tsx +0 -113
- package/src/tui/components/statusBar.tsx +0 -58
- package/src/tui/markdown.ts +0 -62
- package/src/tui/prompts.tsx +0 -205
- package/src/types.ts +0 -248
- package/src/update.ts +0 -89
- package/src/util.ts +0 -61
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "novacode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Open-source multi-provider coding agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/main.mjs",
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
"nova": "dist/main.mjs"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
-
"src",
|
|
12
11
|
"dist"
|
|
13
12
|
],
|
|
14
13
|
"scripts": {
|
|
@@ -21,7 +20,8 @@
|
|
|
21
20
|
"lint:fix": "biome check --write .",
|
|
22
21
|
"format": "biome format --write .",
|
|
23
22
|
"typecheck": "tsc --noEmit",
|
|
24
|
-
"check": "npm run build && npm run typecheck && npm run lint && npm test"
|
|
23
|
+
"check": "npm run build && npm run typecheck && npm run lint && npm test",
|
|
24
|
+
"benchmark": "node --import tsx/esm test/benchmark.ts"
|
|
25
25
|
},
|
|
26
26
|
"keywords": [
|
|
27
27
|
"coding-agent",
|
|
@@ -40,7 +40,6 @@
|
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@biomejs/biome": "^2.4.15",
|
|
43
|
-
"@types/better-sqlite3": "^7.6.13",
|
|
44
43
|
"@types/node": "^24.5.2",
|
|
45
44
|
"@types/react": "^19.2.14",
|
|
46
45
|
"@types/semver": "^7.7.1",
|
|
@@ -54,7 +53,6 @@
|
|
|
54
53
|
"node": ">=24"
|
|
55
54
|
},
|
|
56
55
|
"dependencies": {
|
|
57
|
-
"better-sqlite3": "^12.10.0",
|
|
58
56
|
"chalk": "^5.6.2",
|
|
59
57
|
"glob": "^13.0.6",
|
|
60
58
|
"ink": "^7.0.3",
|
package/dist/app-QfQR2FN9.mjs
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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":"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"}
|
package/src/agent/agent.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import type { EventStream } from "../provider/stream.ts"
|
|
2
|
-
import type { AgentEvent, ApiFormat, LoopCtx, LoopOpts, Model, Msg, Tool } from "../types.ts"
|
|
3
|
-
import { run } from "./loop.ts"
|
|
4
|
-
|
|
5
|
-
export class Agent {
|
|
6
|
-
#api: ApiFormat
|
|
7
|
-
#model: Model
|
|
8
|
-
#system: string
|
|
9
|
-
#messages: Msg[] = []
|
|
10
|
-
#tools: Tool[]
|
|
11
|
-
#apiKey: string
|
|
12
|
-
#baseUrl: string
|
|
13
|
-
|
|
14
|
-
constructor(opts: {
|
|
15
|
-
api: ApiFormat
|
|
16
|
-
model: Model
|
|
17
|
-
apiKey: string
|
|
18
|
-
baseUrl: string
|
|
19
|
-
system: string
|
|
20
|
-
tools: Tool[]
|
|
21
|
-
messages?: Msg[]
|
|
22
|
-
}) {
|
|
23
|
-
this.#api = opts.api
|
|
24
|
-
this.#model = opts.model
|
|
25
|
-
this.#apiKey = opts.apiKey
|
|
26
|
-
this.#baseUrl = opts.baseUrl
|
|
27
|
-
this.#system = opts.system
|
|
28
|
-
this.#tools = opts.tools
|
|
29
|
-
this.#messages = opts.messages ?? []
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
get model(): Model {
|
|
33
|
-
return this.#model
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
get messages(): Msg[] {
|
|
37
|
-
return this.#messages
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
get tools(): Tool[] {
|
|
41
|
-
return this.#tools
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
get apiKey(): string {
|
|
45
|
-
return this.#apiKey
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
get baseUrl(): string {
|
|
49
|
-
return this.#baseUrl
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
updateConfig(opts: { api: ApiFormat; model: Model; apiKey: string; baseUrl: string }): void {
|
|
53
|
-
this.#api = opts.api
|
|
54
|
-
this.#model = opts.model
|
|
55
|
-
this.#apiKey = opts.apiKey
|
|
56
|
-
this.#baseUrl = opts.baseUrl
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
setTools(tools: Tool[]): void {
|
|
60
|
-
this.#tools = tools
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
setMessages(msgs: Msg[]): void {
|
|
64
|
-
this.#messages = msgs
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
setModel(model: Model): void {
|
|
68
|
-
this.#model = model
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
prompt(input: string, signal?: AbortSignal): EventStream<AgentEvent, Msg[]> {
|
|
72
|
-
const ctx: LoopCtx = {
|
|
73
|
-
system: this.#system,
|
|
74
|
-
messages: this.#messages,
|
|
75
|
-
tools: this.#tools,
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const opts: LoopOpts = {
|
|
79
|
-
api: this.#api,
|
|
80
|
-
model: this.#model,
|
|
81
|
-
apiKey: this.#apiKey,
|
|
82
|
-
baseUrl: this.#baseUrl,
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return run(input, ctx, opts, signal)
|
|
86
|
-
}
|
|
87
|
-
}
|
package/src/agent/loop.ts
DELETED
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core agent loop that orchestrates model interaction and tool execution.
|
|
3
|
-
* Handles turns, tool routing, safety checks, and event streaming.
|
|
4
|
-
*/
|
|
5
|
-
import { EventStream, stream } from "../provider/stream.ts"
|
|
6
|
-
import type {
|
|
7
|
-
AgentEvent,
|
|
8
|
-
AssistantMsg,
|
|
9
|
-
LoopCtx,
|
|
10
|
-
LoopOpts,
|
|
11
|
-
Msg,
|
|
12
|
-
ToolCallPart,
|
|
13
|
-
ToolResultMsg,
|
|
14
|
-
} from "../types.ts"
|
|
15
|
-
import { estimateTokens, textPart } from "../util.ts"
|
|
16
|
-
|
|
17
|
-
// Safety cap so a misbehaving model can't loop forever
|
|
18
|
-
const MAX_TURNS = 50
|
|
19
|
-
|
|
20
|
-
const isToolCall = (c: unknown): c is ToolCallPart =>
|
|
21
|
-
typeof c === "object" && c !== null && (c as ToolCallPart).type === "tool_call"
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Start a long-running agent session that yields an EventStream of updates.
|
|
25
|
-
*/
|
|
26
|
-
export function run(
|
|
27
|
-
input: string,
|
|
28
|
-
ctx: LoopCtx,
|
|
29
|
-
opts: LoopOpts,
|
|
30
|
-
signal?: AbortSignal,
|
|
31
|
-
): EventStream<AgentEvent, Msg[]> {
|
|
32
|
-
const es = new EventStream<AgentEvent, Msg[]>()
|
|
33
|
-
const out: Msg[] = []
|
|
34
|
-
const maxTurns = opts.maxTurns ?? MAX_TURNS
|
|
35
|
-
|
|
36
|
-
const userMsg: Msg = { role: "user", content: input, ts: Date.now() }
|
|
37
|
-
let activeCtx: LoopCtx = { ...ctx, messages: [...ctx.messages, userMsg] }
|
|
38
|
-
out.push(userMsg)
|
|
39
|
-
|
|
40
|
-
const tick = async () => {
|
|
41
|
-
es.push({ type: "start" })
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
let turns = 0
|
|
45
|
-
while (turns < maxTurns) {
|
|
46
|
-
if (signal?.aborted) break
|
|
47
|
-
|
|
48
|
-
turns++
|
|
49
|
-
es.push({ type: "turn" })
|
|
50
|
-
|
|
51
|
-
// Warn before hitting the hard limit so the caller can compact/summarize
|
|
52
|
-
const approxTokens = estimateTokens(activeCtx.messages)
|
|
53
|
-
if (approxTokens > opts.model.contextWindow * 0.9) {
|
|
54
|
-
es.push({
|
|
55
|
-
type: "text_delta",
|
|
56
|
-
text: `[warning] Approaching context limit (~${Math.round(approxTokens / 1000)}k / ${Math.round(opts.model.contextWindow / 1000)}k tokens)`,
|
|
57
|
-
})
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const reply = await getReply(activeCtx, opts, es, signal)
|
|
61
|
-
out.push(reply)
|
|
62
|
-
activeCtx = { ...activeCtx, messages: [...activeCtx.messages, reply] }
|
|
63
|
-
es.push({ type: "assistant_msg", msg: reply })
|
|
64
|
-
|
|
65
|
-
if (reply.stop === "error" || reply.stop === "aborted") {
|
|
66
|
-
es.push({ type: "turn_end", msg: reply, results: [] })
|
|
67
|
-
break
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const calls = reply.content.filter(isToolCall)
|
|
71
|
-
if (calls.length === 0) {
|
|
72
|
-
es.push({ type: "turn_end", msg: reply, results: [] })
|
|
73
|
-
break
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Execute tool calls
|
|
77
|
-
const results: ToolResultMsg[] = []
|
|
78
|
-
for (const call of calls) {
|
|
79
|
-
if (signal?.aborted) break
|
|
80
|
-
|
|
81
|
-
const tool = activeCtx.tools.find((t) => t.def.name === call.name)
|
|
82
|
-
if (!tool) {
|
|
83
|
-
const errResult: ToolResultMsg = {
|
|
84
|
-
role: "tool_result",
|
|
85
|
-
callId: call.id,
|
|
86
|
-
tool: call.name,
|
|
87
|
-
content: [textPart(`Unknown tool: ${call.name}`)],
|
|
88
|
-
isError: true,
|
|
89
|
-
ts: Date.now(),
|
|
90
|
-
}
|
|
91
|
-
results.push(errResult)
|
|
92
|
-
activeCtx = { ...activeCtx, messages: [...activeCtx.messages, errResult] }
|
|
93
|
-
out.push(errResult)
|
|
94
|
-
continue
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// beforeTool lets callers block dangerous operations (e.g. rm -rf)
|
|
98
|
-
const blocked = await opts.beforeTool?.(call, call.args, activeCtx)
|
|
99
|
-
if (blocked?.block) {
|
|
100
|
-
const blockResult: ToolResultMsg = {
|
|
101
|
-
role: "tool_result",
|
|
102
|
-
callId: call.id,
|
|
103
|
-
tool: call.name,
|
|
104
|
-
content: [textPart(blocked.reason ?? "Blocked")],
|
|
105
|
-
isError: true,
|
|
106
|
-
ts: Date.now(),
|
|
107
|
-
}
|
|
108
|
-
results.push(blockResult)
|
|
109
|
-
activeCtx = { ...activeCtx, messages: [...activeCtx.messages, blockResult] }
|
|
110
|
-
out.push(blockResult)
|
|
111
|
-
continue
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Execute
|
|
115
|
-
const result = await tool.execute(call.args, signal)
|
|
116
|
-
const toolMsg: ToolResultMsg = {
|
|
117
|
-
role: "tool_result",
|
|
118
|
-
callId: call.id,
|
|
119
|
-
tool: call.name,
|
|
120
|
-
args: call.args,
|
|
121
|
-
content: result.content,
|
|
122
|
-
isError: result.isError,
|
|
123
|
-
ts: Date.now(),
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
results.push(toolMsg)
|
|
127
|
-
activeCtx = { ...activeCtx, messages: [...activeCtx.messages, toolMsg] }
|
|
128
|
-
out.push(toolMsg)
|
|
129
|
-
es.push({ type: "tool_result", callId: call.id, result: toolMsg, args: call.args })
|
|
130
|
-
|
|
131
|
-
await opts.afterTool?.(call, toolMsg, activeCtx)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
es.push({ type: "turn_end", msg: reply, results })
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (turns >= maxTurns) {
|
|
138
|
-
es.push({
|
|
139
|
-
type: "text_delta",
|
|
140
|
-
text: `[max turns reached (${maxTurns})]`,
|
|
141
|
-
})
|
|
142
|
-
}
|
|
143
|
-
} catch (e) {
|
|
144
|
-
if ((e as Error).name === "AbortError") {
|
|
145
|
-
es.finish(out)
|
|
146
|
-
return
|
|
147
|
-
}
|
|
148
|
-
throw e
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
es.finish(out)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
tick()
|
|
155
|
-
return es
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
async function getReply(
|
|
159
|
-
ctx: LoopCtx,
|
|
160
|
-
opts: LoopOpts,
|
|
161
|
-
es: EventStream<AgentEvent, Msg[]>,
|
|
162
|
-
signal?: AbortSignal,
|
|
163
|
-
): Promise<AssistantMsg> {
|
|
164
|
-
const providerStream = stream({
|
|
165
|
-
api: opts.api,
|
|
166
|
-
model: opts.model,
|
|
167
|
-
apiKey: opts.apiKey,
|
|
168
|
-
baseUrl: opts.baseUrl,
|
|
169
|
-
system: ctx.system,
|
|
170
|
-
messages: ctx.messages,
|
|
171
|
-
tools: ctx.tools.map((t) => t.def),
|
|
172
|
-
signal,
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
const content: AssistantMsg["content"] = []
|
|
176
|
-
let usage = { in: 0, out: 0 }
|
|
177
|
-
|
|
178
|
-
// Accumulate content and proxy events to the outer stream
|
|
179
|
-
for await (const ev of providerStream) {
|
|
180
|
-
if (ev.type === "text_delta" && ev.text) {
|
|
181
|
-
const last = content[content.length - 1]
|
|
182
|
-
if (last?.type === "text") {
|
|
183
|
-
last.text += ev.text
|
|
184
|
-
} else {
|
|
185
|
-
content.push(textPart(ev.text))
|
|
186
|
-
}
|
|
187
|
-
es.push({ type: "text_delta", text: ev.text })
|
|
188
|
-
} else if (ev.type === "thinking_delta" && ev.text) {
|
|
189
|
-
const last = content[content.length - 1]
|
|
190
|
-
if (last?.type === "thinking") {
|
|
191
|
-
last.text += ev.text
|
|
192
|
-
} else {
|
|
193
|
-
content.push({ type: "thinking", text: ev.text })
|
|
194
|
-
}
|
|
195
|
-
es.push({ type: "thinking_delta", text: ev.text })
|
|
196
|
-
} else if (ev.type === "tool_call" && ev.call) {
|
|
197
|
-
content.push(ev.call)
|
|
198
|
-
es.push({ type: "tool_call", call: ev.call })
|
|
199
|
-
} else if (ev.type === "usage" && ev.usage) {
|
|
200
|
-
usage = ev.usage
|
|
201
|
-
es.push({ type: "usage", usage })
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const rawContent =
|
|
206
|
-
providerStream.result?.content && providerStream.result.content.length > 0
|
|
207
|
-
? providerStream.result.content
|
|
208
|
-
: content
|
|
209
|
-
|
|
210
|
-
const hasTool = rawContent.some((p) => p.type === "tool_call")
|
|
211
|
-
const cleanedContent = hasTool
|
|
212
|
-
? rawContent.filter((p) => p.type !== "text" || p.text.trim().length > 0)
|
|
213
|
-
: rawContent
|
|
214
|
-
|
|
215
|
-
const res = providerStream.result
|
|
216
|
-
if (res) {
|
|
217
|
-
return {
|
|
218
|
-
role: "assistant",
|
|
219
|
-
content: cleanedContent,
|
|
220
|
-
model: opts.model.id,
|
|
221
|
-
provider: opts.model.provider,
|
|
222
|
-
usage: res.usage,
|
|
223
|
-
stop: res.stop,
|
|
224
|
-
ts: Date.now(),
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
return {
|
|
229
|
-
role: "assistant",
|
|
230
|
-
content: cleanedContent,
|
|
231
|
-
model: opts.model.id,
|
|
232
|
-
provider: opts.model.provider,
|
|
233
|
-
usage,
|
|
234
|
-
stop: cleanedContent.some((c) => c.type === "tool_call") ? "tool_use" : "stop",
|
|
235
|
-
ts: Date.now(),
|
|
236
|
-
}
|
|
237
|
-
}
|
package/src/agent/prompt.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Logic for constructing the foundational system instruction given the environment and tools.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import os from "node:os"
|
|
6
|
-
import type { Tool } from "../types.ts"
|
|
7
|
-
|
|
8
|
-
export function buildSystemPrompt(cwd: string, tools: Tool[]): string {
|
|
9
|
-
const toolList = tools.map((t) => `- ${t.def.name}: ${t.def.description}`).join("\n")
|
|
10
|
-
const platform = os.platform()
|
|
11
|
-
const arch = os.arch()
|
|
12
|
-
const release = os.release()
|
|
13
|
-
const shell = process.env.SHELL || "unknown"
|
|
14
|
-
|
|
15
|
-
return `You are Nova, an expert coding assistant. Help users with coding tasks using the tools available.
|
|
16
|
-
|
|
17
|
-
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.
|
|
18
|
-
|
|
19
|
-
# Tools
|
|
20
|
-
|
|
21
|
-
${toolList}
|
|
22
|
-
|
|
23
|
-
# Environment
|
|
24
|
-
|
|
25
|
-
- Working directory: ${cwd}
|
|
26
|
-
- Operating System: ${platform} (${release})
|
|
27
|
-
- Architecture: ${arch}
|
|
28
|
-
- Shell: ${shell}
|
|
29
|
-
- Date: ${new Date().toISOString().split("T")[0]}
|
|
30
|
-
|
|
31
|
-
# Guidelines
|
|
32
|
-
|
|
33
|
-
- Use tools to fulfill requests. Do not fabricate file contents.
|
|
34
|
-
- Explain what you are doing and why before each tool call.
|
|
35
|
-
- Use the "bash" tool for ls, git, and other shell operations.
|
|
36
|
-
- Always read a file before editing it.
|
|
37
|
-
- Prefer edit over write for existing files.
|
|
38
|
-
- Run relevant tests after making changes.
|
|
39
|
-
- If a command fails, read the error carefully before retrying.
|
|
40
|
-
- For multi-file changes, plan first, then execute.
|
|
41
|
-
- When done, briefly summarize what was changed.
|
|
42
|
-
- Be concise and direct.
|
|
43
|
-
|
|
44
|
-
# Safety
|
|
45
|
-
|
|
46
|
-
- Never delete files outside the working directory.
|
|
47
|
-
- Never run destructive commands unless the user explicitly confirms.
|
|
48
|
-
- If unsure, ask for clarification.
|
|
49
|
-
- Never expose API keys, tokens, or secrets.`
|
|
50
|
-
}
|
package/src/commands/compact.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk"
|
|
2
|
-
import type { Agent } from "../agent/agent.ts"
|
|
3
|
-
import { compact as runCompact } from "../session/compact.ts"
|
|
4
|
-
import type { SessionStore } from "../session/store.ts"
|
|
5
|
-
|
|
6
|
-
export async function handleCompact(
|
|
7
|
-
agent: Agent,
|
|
8
|
-
store: SessionStore,
|
|
9
|
-
sessionId: string,
|
|
10
|
-
): Promise<string> {
|
|
11
|
-
const res = await runCompact(
|
|
12
|
-
store,
|
|
13
|
-
sessionId,
|
|
14
|
-
agent.messages,
|
|
15
|
-
agent.model,
|
|
16
|
-
agent.apiKey,
|
|
17
|
-
agent.baseUrl,
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
if (res.compacted) {
|
|
21
|
-
// Update agent messages
|
|
22
|
-
const msgs = store.messages(sessionId)
|
|
23
|
-
agent.setMessages(msgs)
|
|
24
|
-
return chalk.green(`✓ Context compacted (${res.msgsRemoved} messages removed)`)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return chalk.yellow("Context is small enough, no compaction needed.")
|
|
28
|
-
}
|