novacode 0.12.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app-C-3NXlux.mjs +58 -0
- package/dist/app-C-3NXlux.mjs.map +1 -0
- package/dist/main.mjs +25 -23
- package/dist/main.mjs.map +1 -1
- package/dist/{reset-Bq0e9TIp.mjs → reset-CWIz7872.mjs} +2 -2
- package/dist/{reset-Bq0e9TIp.mjs.map → reset-CWIz7872.mjs.map} +1 -1
- package/package.json +2 -1
- package/dist/app-DSSkuU-s.mjs +0 -56
- package/dist/app-DSSkuU-s.mjs.map +0 -1
package/dist/main.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{parseArgs as e}from"node:util";import t from"chalk";import{pruneMessages as n,stepCountIs as r,streamText as i,tool as a}from"ai";import{createAnthropic as o}from"@ai-sdk/anthropic";import{createGoogleGenerativeAI as s}from"@ai-sdk/google";import{createOpenAI as c}from"@ai-sdk/openai";import l,{homedir as u}from"node:os";import{chmod as d,mkdir as f,readFile as p,readdir as m,stat as h,writeFile as g}from"node:fs/promises";import{basename as _,dirname as v,extname as
|
|
3
|
-
`)}:e.content.some(e=>e.type===`image`)?{type:`content`,value:e.content.map(e=>e.type===`image`?{type:`media`,data:e.data,mediaType:e.mime}:{type:`text`,text:e.text})}:{type:`text`,value:e.content.map(
|
|
4
|
-
`)}}function
|
|
5
|
-
`),isError:!1};default:return{text:``,isError:!1}}}function
|
|
2
|
+
import{parseArgs as e}from"node:util";import t from"chalk";import{pruneMessages as n,stepCountIs as r,streamText as i,tool as a}from"ai";import{createAnthropic as o}from"@ai-sdk/anthropic";import{createGoogleGenerativeAI as s}from"@ai-sdk/google";import{createOpenAI as c}from"@ai-sdk/openai";import l,{homedir as u}from"node:os";import{chmod as d,mkdir as f,readFile as p,readdir as m,stat as h,writeFile as g}from"node:fs/promises";import{basename as _,dirname as v,extname as ee,isAbsolute as y,join as b,relative as x,resolve as S,sep as te}from"node:path";import{DatabaseSync as C}from"node:sqlite";import{mkdirSync as ne}from"node:fs";import{Box as w,Text as T,render as re,useInput as E,useWindowSize as ie}from"ink";import{createContext as ae,useContext as oe,useMemo as se,useState as D}from"react";import{jsx as O,jsxs as k}from"react/jsx-runtime";import{z as A}from"zod";import{spawn as j}from"node:child_process";import{glob as ce}from"glob";import{fileURLToPath as le}from"node:url";import ue from"semver";const M={glm:`glm`,gemini:`gemini`,deepseek:`deepseek`,openai:`openai`,anthropic:`anthropic`},N=[{id:M.glm,name:`GLM (Z.AI)`,envKey:`GLM_API_KEY`,models:[{id:`glm-5.2`,contextWindow:1e6,reasoning:!0,default:!0},{id:`glm-5.1`,contextWindow:204800,reasoning:!0},{id:`glm-5-turbo`,contextWindow:202800,reasoning:!0},{id:`glm-5`,contextWindow:202800,reasoning:!0},{id:`glm-4.7`,contextWindow:204800,reasoning:!0},{id:`glm-4.6`,contextWindow:204800,reasoning:!0}]},{id:M.gemini,name:`Gemini (Google)`,envKey:`GEMINI_API_KEY`,models:[{id:`gemini-3.5-flash`,contextWindow:1e6,reasoning:!0,default:!0},{id:`gemini-3.1-pro-preview`,contextWindow:1e6,reasoning:!0},{id:`gemini-3.1-flash-lite`,contextWindow:1e6,reasoning:!0},{id:`gemini-2.5-pro`,contextWindow:1e6,reasoning:!1},{id:`gemini-2.5-flash`,contextWindow:1e6,reasoning:!1},{id:`gemini-2.5-flash-lite`,contextWindow:1e6,reasoning:!1}]},{id:M.deepseek,name:`DeepSeek`,envKey:`DEEPSEEK_API_KEY`,models:[{id:`deepseek-v4-flash`,contextWindow:1e6,reasoning:!0,default:!0},{id:`deepseek-v4-pro`,contextWindow:1e6,reasoning:!0}]},{id:M.openai,name:`OpenAI`,envKey:`OPENAI_API_KEY`,models:[{id:`gpt-5.5`,contextWindow:1e6,reasoning:!0,default:!0},{id:`gpt-5.5-pro`,contextWindow:1e6,reasoning:!0},{id:`gpt-5.4`,contextWindow:1e6,reasoning:!0},{id:`gpt-5.4-pro`,contextWindow:1e6,reasoning:!0},{id:`gpt-5.4-mini`,contextWindow:4e5,reasoning:!0},{id:`gpt-5.4-nano`,contextWindow:4e5,reasoning:!0},{id:`gpt-5.3-codex`,contextWindow:4e5,reasoning:!0},{id:`gpt-5.2`,contextWindow:4e5,reasoning:!0},{id:`gpt-5.2-pro`,contextWindow:4e5,reasoning:!0},{id:`gpt-5.2-codex`,contextWindow:4e5,reasoning:!0},{id:`gpt-5.1-codex`,contextWindow:4e5,reasoning:!0},{id:`gpt-5.1-codex-max`,contextWindow:4e5,reasoning:!0},{id:`gpt-5.1-codex-mini`,contextWindow:4e5,reasoning:!0},{id:`gpt-5-codex`,contextWindow:4e5,reasoning:!0},{id:`gpt-5`,contextWindow:4e5,reasoning:!0},{id:`gpt-5-mini`,contextWindow:4e5,reasoning:!0},{id:`gpt-5-nano`,contextWindow:4e5,reasoning:!0}]},{id:M.anthropic,name:`Anthropic`,envKey:`ANTHROPIC_API_KEY`,models:[{id:`claude-fable-5`,contextWindow:1e6,reasoning:!0},{id:`claude-opus-4-8`,contextWindow:1e6,reasoning:!0,default:!0},{id:`claude-opus-4-7`,contextWindow:1e6,reasoning:!0},{id:`claude-sonnet-4-6`,contextWindow:1e6,reasoning:!0},{id:`claude-opus-4-6`,contextWindow:1e6,reasoning:!0},{id:`claude-haiku-4-5`,contextWindow:2e5,reasoning:!1},{id:`claude-sonnet-4-5`,contextWindow:1e6,reasoning:!1},{id:`claude-opus-4-5`,contextWindow:2e5,reasoning:!0},{id:`claude-opus-4-1`,contextWindow:2e5,reasoning:!1}]}],de={[M.glm]:`https://api.z.ai/api/coding/paas/v4`,[M.deepseek]:`https://api.deepseek.com`};function fe(e,t,n){switch(e){case M.anthropic:return o({apiKey:n})(t);case M.gemini:return s({apiKey:n})(t);case M.openai:return c({apiKey:n})(t);case M.glm:case M.deepseek:return c({apiKey:n,baseURL:de[e],name:e}).chat(t);default:throw Error(`Unknown provider: ${e}`)}}function pe(e){switch(e){case M.anthropic:return{anthropic:{effort:`high`}};case M.gemini:return{google:{thinkingConfig:{thinkingLevel:`high`}}};case M.openai:case M.glm:case M.deepseek:return{openai:{reasoningEffort:`high`}};default:return{}}}function me(e){return{type:`text`,text:e}}function he(e){return e.type===`text`?e.text:``}function P(e){return e.isError?{type:`error-text`,value:e.content.map(he).join(`
|
|
3
|
+
`)}:e.content.some(e=>e.type===`image`)?{type:`content`,value:e.content.map(e=>e.type===`image`?{type:`media`,data:e.data,mediaType:e.mime}:{type:`text`,text:e.text})}:{type:`text`,value:e.content.map(he).join(`
|
|
4
|
+
`)}}function ge(e){switch(e.type){case`text`:return{text:e.value,isError:!1};case`error-text`:return{text:e.value,isError:!0};case`error-json`:return{text:JSON.stringify(e.value),isError:!0};case`execution-denied`:return{text:e.reason??`execution denied`,isError:!0};case`json`:return{text:JSON.stringify(e.value),isError:!1};case`content`:return{text:e.value.map(e=>e.type===`text`?e.text:e.type===`media`?`[image]`:``).join(`
|
|
5
|
+
`),isError:!1};default:return{text:``,isError:!1}}}function _e(e,t){if(!t)return e;let n={};for(let[r,i]of Object.entries(e)){let e=i.execute;if(!e){n[r]=i;continue}n[r]={...i,execute:async(n,i)=>{let a=await t.check({name:r,args:n});return a.allow?e(n,i):{content:[me(a.reason??`Blocked`)],isError:!0}}}}return n}function ve(e,t,n=[],r){let i=Object.entries(t).map(([e,t])=>`- ${e}: ${t.description}`).join(`
|
|
6
6
|
`),a=l.platform(),o=l.arch();return`You are Nova, an expert coding assistant. Help users with coding tasks using the tools available.
|
|
7
7
|
|
|
8
8
|
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.
|
|
@@ -52,8 +52,8 @@ ${n.length>0?n.map(e=>`- ${e.name}: ${e.description} (path: ${e.path}/SKILL.md)`
|
|
|
52
52
|
|
|
53
53
|
**IMPORTANT:** Before responding to a task that matches any skill above, you MUST first read the skill's SKILL.md file using the read tool with the full absolute path, then follow its instructions exactly. Do not skip this step.
|
|
54
54
|
|
|
55
|
-
${r?`\n<project_context>\nProject-specific instructions and guidelines:\n\n<project_instructions path="AGENTS.md">\n${r}\n</project_instructions>\n</project_context>`:``}`}function
|
|
56
|
-
`)){let t=e.match(/^name:\s*(.+)$/);t&&(r=t[1].trim());let n=e.match(/^description:\s*(.+)$/);n&&(i=n[1].trim())}return r?{name:r,description:i}:null}async function
|
|
55
|
+
${r?`\n<project_context>\nProject-specific instructions and guidelines:\n\n<project_instructions path="AGENTS.md">\n${r}\n</project_instructions>\n</project_context>`:``}`}function ye(e,t){let r=t[0],i=r&&r.role===`user`&&typeof r.content==`string`&&r.content.startsWith(`[Prior context summary]`);return{instructions:i?`${e}\n\n${r.content}`:e,messages:n({messages:i?t.slice(1):t,reasoning:`before-last-message`,toolCalls:`before-last-message`,emptyMessages:`remove`})}}var be=class{#e;#t;#n;#r=[];#i;#a;#o;constructor(e){this.#e=e.provider,this.#t=e.model,this.#a=e.apiKey,this.#n=e.system,this.#i=e.tools,this.#r=e.messages??[],this.#o=e.policy??null}get model(){return this.#t}get messages(){return this.#r}get tools(){return this.#i}get apiKey(){return this.#a}get policy(){return this.#o}get system(){return this.#n}updateConfig(e){this.#e=e.provider,this.#t=e.model,this.#a=e.apiKey}setTools(e){this.#i=e}setMessages(e){this.#r=e}appendMessages(e){this.#r=[...this.#r,...e]}setModel(e){this.#t=e}async prompt(e,t){let{instructions:n,messages:a}=ye(this.#n,this.#r);return i({model:fe(this.#e,this.#t.id,this.#a),system:n,tools:_e(this.#i,this.#o),stopWhen:r(50),providerOptions:this.#t.reasoning?pe(this.#e):void 0,messages:a,abortSignal:e,onStepFinish:t,onError:()=>void 0})}};const xe=/^[a-z0-9]+(-[a-z0-9]+)*$/;function Se(e){return e.length>64?{valid:!1,warning:`Skill name exceeds 64 characters: "${e}"`}:xe.test(e)?{valid:!0}:{valid:!1,warning:`Skill name contains invalid characters (use lowercase, numbers, hyphens): "${e}"`}}function Ce(e){let t=e.match(/^---\s*\n([\s\S]*?)\n---/);if(!t)return null;let n=t[1],r,i;for(let e of n.split(`
|
|
56
|
+
`)){let t=e.match(/^name:\s*(.+)$/);t&&(r=t[1].trim());let n=e.match(/^description:\s*(.+)$/);n&&(i=n[1].trim())}return r?{name:r,description:i}:null}async function we(e,t){let n=b(e,`SKILL.md`);try{let{readFile:r}=await import(`node:fs/promises`),i=Ce(await r(n,`utf-8`));return i?.name?i.description?{name:i.name,description:i.description,path:e,source:t}:(console.warn(`Skill missing description, skipping: ${e}`),null):null}catch{return null}}async function Te(e,t){let n=[];try{let r=await m(e,{withFileTypes:!0});for(let i of r){if(!i.isDirectory())continue;let r=await we(b(e,i.name),t);r&&n.push(r)}}catch{}return n}async function Ee(e){let t=[{dir:S(e,`.novacode`,`skills`),source:`project`},{dir:S(e,`.agents`,`skills`),source:`project`},{dir:b(u(),`.novacode`,`skills`),source:`global`},{dir:b(u(),`.agents`,`skills`),source:`global`}],n=[];for(let{dir:e,source:r}of t)n.push(...await Te(e,r));let r=[];for(let e of n){let t=Se(e.name);t.valid||console.warn(t.warning),e.description.length>1024&&console.warn(`Skill description exceeds 1024 characters: "${e.name}"`),r.push({name:e.name,description:e.description,path:e.path,source:e.source})}return r}function De(e){return(e.source===`project`?0:2)+ +!e.path.includes(`.novacode/skills`)}function Oe(e){let t=new Map;for(let n of e){let e=t.get(n.name);e?e.push(n):t.set(n.name,[n])}return[...t.values()].map(e=>e.sort((e,t)=>De(e)-De(t)))}function ke(e){return Oe(e).map(e=>e[0])}async function Ae(e){let[t,n]=await Promise.all([Ee(e),je(e)]);return{skills:t,agentsMd:n}}async function je(e){try{return await p(b(e,`AGENTS.md`),`utf-8`)}catch{return null}}function F(e,t){return t===e||t.startsWith(`${e}/`)?x(e,t)||`.`:t}function Me(e){if(typeof e!=`string`)return e;let t=e,n=``;if(e.startsWith(`file://`)&&(t=e.slice(7),n=`file://`),y(t)){let e=process.cwd();return n+F(e,t)}return e}function Ne(e){let t=process.cwd();if(e===t||e.startsWith(`${t}/`))return x(t,e)||`.`;let n=u();return e===n||e.startsWith(`${n}/`)?`~${e.slice(n.length)}`:e}function Pe(e,n=!1){return e?Object.entries(e).map(([e,r])=>{let i=typeof r==`string`?Me(r):JSON.stringify(r),a=i.length>40?`${i.slice(0,40)}…`:i;return`${n?t.dim(`${e}:`):`${e}:`} ${a}`}).join(` `):``}function Fe(e){let t=Date.now()-e,n=Math.floor(t/1e3),r=Math.floor(n/60),i=Math.floor(r/60);return n<60?`just now`:r<60?`${r}m ago`:i<24?`${i}h ago`:new Date(e).toLocaleDateString()}function Ie(e){return e===0?`-`:e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}k`:String(e)}async function Le(e,t,n={}){let[r,i]=t;if(r===`list`||r===`ls`){let t=n.limit??10,r=await e.list(t);if(r.length===0){console.log(`No sessions found.`);return}console.log(`ID`.padEnd(25),`TITLE / UPDATED`.padEnd(35),`TOKENS`),console.log(`-`.repeat(75));for(let e of r){let t=Fe(e.updated),n=e.title?`"${e.title}" (${t})`:t,r=Ie(e.inputTokens+e.outputTokens);console.log(e.id.padEnd(25),n.padEnd(35),r)}return}if(r===`delete`||r===`rm`){if(n.all){await e.deleteAll(),console.log(`All sessions deleted.`);return}i||(console.error(`Usage: nova --sessions rm <id> or --sessions rm --all`),process.exit(1)),await e.delete(i)?console.log(`Deleted session: ${i}`):(console.error(`Session not found: ${i}`),process.exit(1));return}console.error(`Unknown session subcommand.`),process.exit(1)}const I=()=>b(process.env.HOME??`~`,`.novacode`),L=()=>b(I(),`config.json`),R=()=>b(I(),`auth.json`),z={provider:``,model:``},Re={apiKeys:{}};async function ze(){try{return await h(L()),!0}catch{return!1}}async function Be(){try{let e=JSON.parse(await p(L(),`utf-8`));return{provider:e.provider??z.provider,model:e.model??z.model}}catch{return{...z}}}async function Ve(){try{let e=JSON.parse(await p(R(),`utf-8`));return{...Re,...e}}catch{return{...Re}}}async function He(){await f(I(),{recursive:!0})}async function Ue(e){await He(),await g(L(),JSON.stringify(e,null,2))}async function We(e){await He(),await g(R(),JSON.stringify(e,null,2));try{await d(R(),384)}catch{}}function Ge(){return I()}let B=null;function Ke(e){if(B)return B;let t=e??b(process.env.HOME??`~`,`.novacode`,`state.db`);return ne(b(t,`..`),{recursive:!0}),B=new C(t,{enableForeignKeyConstraints:!0}),B.exec(`PRAGMA journal_mode = WAL`),B.exec(`
|
|
57
57
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
58
58
|
id TEXT PRIMARY KEY,
|
|
59
59
|
cwd TEXT NOT NULL,
|
|
@@ -80,31 +80,33 @@ CREATE TABLE IF NOT EXISTS messages (
|
|
|
80
80
|
CREATE INDEX IF NOT EXISTS idx_sessions_updated ON sessions(updated DESC);
|
|
81
81
|
CREATE INDEX IF NOT EXISTS idx_sessions_parent ON sessions(parent_session_id);
|
|
82
82
|
CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id, seq);
|
|
83
|
-
`),
|
|
84
|
-
VALUES (?, ?, ?, ?, ?, ?, NULL, ?, ?, 0, 0, 0)`).run(e,t.cwd,t.model,t.provider,t.title,t.parentSessionId,t.created,t.created),this.#t.delete(e))}async create(e,t,n){let r=
|
|
83
|
+
`),B}function qe(){B&&=(B.close(),null)}function Je(){B=null}function Ye(){return`${Date.now().toString(36)}-${crypto.randomUUID().slice(0,8)}`}function V(e){return{id:e.id,cwd:e.cwd,model:e.model,provider:e.provider,title:e.title??null,parentSessionId:e.parent_session_id??null,endReason:e.end_reason??null,created:e.created,updated:e.updated,inputTokens:e.input_tokens??0,outputTokens:e.output_tokens??0,messageCount:e.message_count??0}}var Xe=class{#e;#t=new Map;constructor(e){e instanceof C?this.#e=e:this.#e=Ke(e)}#n(e){let t=this.#t.get(e);t&&(this.#e.prepare(`INSERT OR IGNORE INTO sessions (id, cwd, model, provider, title, parent_session_id, end_reason, created, updated, input_tokens, output_tokens, message_count)
|
|
84
|
+
VALUES (?, ?, ?, ?, ?, ?, NULL, ?, ?, 0, 0, 0)`).run(e,t.cwd,t.model,t.provider,t.title,t.parentSessionId,t.created,t.created),this.#t.delete(e))}async create(e,t,n){let r=Ye(),i=Date.now();return this.#t.set(r,{cwd:e,model:t,provider:n,title:null,parentSessionId:null,created:i}),{id:r,cwd:e,model:t,provider:n,title:null,parentSessionId:null,endReason:null,created:i,updated:i,inputTokens:0,outputTokens:0,messageCount:0}}async get(e){let t=this.#e.prepare(`SELECT * FROM sessions WHERE id = ?`).get(e);if(t)return V(t);let n=this.#t.get(e);return n?{id:e,cwd:n.cwd,model:n.model,provider:n.provider,title:n.title,parentSessionId:n.parentSessionId,endReason:null,created:n.created,updated:n.created,inputTokens:0,outputTokens:0,messageCount:0}:null}async list(e=10){return this.#e.prepare(`SELECT * FROM sessions WHERE end_reason IS NULL ORDER BY updated DESC LIMIT ?`).all(e).map(V)}async latest(){let e=this.#e.prepare(`SELECT * FROM sessions WHERE end_reason IS NULL ORDER BY updated DESC LIMIT 1`).get();return e?V(e):null}async delete(e){let t=this.#t.delete(e),n=this.#e.prepare(`DELETE FROM sessions WHERE id = ?`).run(e);return t||n.changes>0}async deleteAll(){this.#t.clear(),this.#e.exec(`DELETE FROM messages; DELETE FROM sessions`)}async append(e,t){this.#n(e);let n=Date.now(),r=this.#e.prepare(`SELECT COALESCE(MAX(seq), 0) + 1 AS next_seq FROM messages WHERE session_id = ?`).get(e)?.next_seq;this.#i(e,r,t,n),this.#e.prepare(`UPDATE sessions SET message_count = message_count + 1, updated = ? WHERE id = ?`).run(n,e)}async addUsage(e,t,n){this.#n(e),this.#e.prepare(`UPDATE sessions SET input_tokens = input_tokens + ?, output_tokens = output_tokens + ?, updated = ? WHERE id = ?`).run(t,n,Date.now(),e)}async messages(e){return this.#e.prepare(`SELECT message FROM messages WHERE session_id = ? ORDER BY seq`).all(e).map(e=>JSON.parse(e.message))}async history(e){let t=this.#r(e);if(t.length<=1)return this.messages(e);let n=t.map((e,t)=>`WHEN '${e}' THEN ${t}`).join(` `);return this.#e.prepare(`SELECT message FROM messages m
|
|
85
85
|
WHERE m.session_id IN (${t.map(()=>`?`).join(`,`)})
|
|
86
|
-
ORDER BY CASE m.session_id ${n} END ASC, m.seq ASC`).all(...t).map(e=>JSON.parse(e.message))}async messageCount(e){return this.#e.prepare(`SELECT message_count FROM sessions WHERE id = ?`).get(e)?.message_count??0}async setTitle(e,t){this.#n(e),this.#e.prepare(`UPDATE sessions SET title = ?, updated = ? WHERE id = ?`).run(t,Date.now(),e)}async endSession(e,t){this.#n(e),this.#e.prepare(`UPDATE sessions SET end_reason = ?, updated = ? WHERE id = ?`).run(t,Date.now(),e)}async createContinuation(e,t,n,r){let i=
|
|
86
|
+
ORDER BY CASE m.session_id ${n} END ASC, m.seq ASC`).all(...t).map(e=>JSON.parse(e.message))}async messageCount(e){return this.#e.prepare(`SELECT message_count FROM sessions WHERE id = ?`).get(e)?.message_count??0}async setTitle(e,t){this.#n(e),this.#e.prepare(`UPDATE sessions SET title = ?, updated = ? WHERE id = ?`).run(t,Date.now(),e)}async endSession(e,t){this.#n(e),this.#e.prepare(`UPDATE sessions SET end_reason = ?, updated = ? WHERE id = ?`).run(t,Date.now(),e)}async createContinuation(e,t,n,r){let i=Ye(),a=Date.now();return this.#t.set(i,{cwd:t,model:n,provider:r,title:null,parentSessionId:e,created:a}),{id:i,cwd:t,model:n,provider:r,title:null,parentSessionId:e,endReason:null,created:a,updated:a,inputTokens:0,outputTokens:0,messageCount:0}}#r(e){let t=[],n=e,r=new Set;for(;n&&!r.has(n);){t.push(n),r.add(n);let e=this.#e.prepare(`SELECT parent_session_id FROM sessions WHERE id = ?`).get(n),i=null;if(i=e?e.parent_session_id:this.#t.get(n)?.parentSessionId??null,i){let e=this.#e.prepare(`SELECT end_reason FROM sessions WHERE id = ?`).get(i);n=e&&e.end_reason===`compacted`?``:i}else n=``}return t.reverse(),t}#i(e,t,n,r){this.#e.prepare(`INSERT INTO messages (session_id, seq, message, ts) VALUES (?, ?, ?, ?)`).run(e,t,JSON.stringify(n),r)}async prune(){try{this.#e.exec(`DELETE FROM sessions WHERE message_count = 0`)}catch{}}close(){qe()}};let H=null;async function Ze(e){return H||(H=new Xe(e?`${e}/state.db`:void 0),H)}function U(e){return N.find(t=>t.id===e)}function W(e){let t=U(e);return t?t.models.map(e=>({...e,provider:t.id})):[]}function G(e,t){return W(e).find(e=>e.id===t)}function Qe(e){for(let t of N){let n=G(t.id,e);if(n)return n}}function $e(e){let t=W(e);return t.find(e=>e.default)??t[0]}const K={name:`default`,palette:{bg:`#171717`,fg:`#e5e5e5`,muted:`#a3a3a3`,primary:`#60a5fa`,secondary:`#c084fc`,success:`#4ade80`,warning:`#facc15`,error:`#f87171`}},et=ae(K);function q({children:e,theme:t=K}){return O(et.Provider,{value:t,children:e})}function J(){return oe(et)}function Y({children:e}){return O(w,{flexDirection:`column`,paddingX:1,paddingY:1,backgroundColor:J().palette.bg,children:e})}function tt({yesLabel:e,noLabel:t,selected:n}){let r=J();return k(w,{flexDirection:`row`,children:[k(T,{bold:n===`yes`,color:n===`yes`?r.palette.bg:r.palette.fg,backgroundColor:n===`yes`?r.palette.primary:void 0,children:[n===`yes`?`❯ `:` `,e]}),O(T,{color:r.palette.muted,children:` `}),k(T,{bold:n===`no`,color:n===`no`?r.palette.bg:r.palette.fg,backgroundColor:n===`no`?r.palette.primary:void 0,children:[n===`no`?`❯ `:` `,t]})]})}function nt({req:e,onResolve:t}){let n=J(),[r,i]=D(!0);return E((e,n)=>{if(n.escape){t(null);return}if(n.upArrow||n.downArrow||n.leftArrow||n.rightArrow||n.tab){i(e=>!e);return}n.return&&t(r)}),k(Y,{children:[e.warning&&O(w,{marginBottom:1,children:O(T,{bold:!0,color:n.palette.warning,children:e.warning})}),k(w,{flexDirection:`row`,marginBottom:1,children:[k(T,{bold:!0,color:n.palette.warning,children:[`Approve?`,` `]}),O(T,{color:n.palette.muted,children:e.summary})]}),O(tt,{yesLabel:`Allow once`,noLabel:`Deny`,selected:r?`yes`:`no`}),O(w,{marginTop:1,children:O(T,{color:n.palette.muted,children:`←→ toggle · Enter confirm · Esc deny`})})]})}function rt({message:e}){return O(w,{marginBottom:1,children:O(T,{bold:!0,color:J().palette.muted,children:e})})}function it({message:e,onConfirm:t}){let[n,r]=D(!0);return E((e,i)=>{if(i.escape){t(null);return}if(i.leftArrow||i.rightArrow||i.tab){r(e=>!e);return}i.return&&t(n)}),k(Y,{children:[O(rt,{message:e}),O(tt,{yesLabel:`Yes`,noLabel:`No`,selected:n?`yes`:`no`}),O(w,{marginTop:1,children:O(T,{color:J().palette.muted,children:`←→ toggle · Enter confirm · Esc cancel`})})]})}function at({message:e,validate:t,onSubmit:n}){let r=J(),[i,a]=D(``),[o,s]=D(``);return E((e,r)=>{if(r.escape){n(null);return}if(r.return){let e=t?.(i);if(e){s(e);return}n(i);return}if(r.backspace||r.delete){a(e=>e.slice(0,-1)),s(``);return}e&&(a(t=>t+e),s(``))}),k(Y,{children:[O(w,{marginBottom:1,children:O(T,{bold:!0,color:r.palette.muted,children:e})}),k(w,{flexDirection:`row`,children:[O(T,{color:r.palette.muted,children:`│ `}),O(T,{bold:!0,color:r.palette.fg,children:`*`.repeat(i.length)}),O(T,{color:r.palette.muted,children:`│`})]}),o&&O(w,{marginTop:1,children:k(T,{bold:!0,color:r.palette.error,children:[`✗ `,o]})}),O(w,{marginTop:1,children:O(T,{color:r.palette.muted,children:`Enter submit · Esc cancel`})})]})}function ot({items:e,selectedIndex:t,visibleCount:n,renderItem:r,keyExtractor:i,emptyMessage:a}){let o=J();if(e.length===0)return O(w,{children:O(T,{color:o.palette.muted,children:a??`No items`})});let s=Math.max(0,e.length-n),c=Math.max(0,Math.min(t,s)),l=e.slice(c,c+n),u=e.length>n,d=Math.round(c/(e.length-n)*(n-1));return k(w,{flexDirection:`row`,width:`100%`,children:[O(w,{flexDirection:`column`,flexGrow:1,children:l.map((e,n)=>{let a=c+n;return O(w,{children:r(e,a,a===t)},i(e,a))})}),u&&k(w,{flexDirection:`column`,marginLeft:1,children:[O(T,{color:o.palette.muted,children:d>0?`░
|
|
87
|
+
`.repeat(d).slice(0,-1):``}),O(T,{color:o.palette.primary,children:`█`}),O(T,{color:o.palette.muted,children:n-d-1>0?`░
|
|
88
|
+
`.repeat(n-d-1).slice(0,-1):``})]})]})}function st({options:e,selectedIdx:t}){let n=J(),{rows:r}=ie(),i=r||24;return O(ot,{items:e,selectedIndex:t,visibleCount:Math.max(3,Math.min(e.length,i-6)),keyExtractor:e=>e.value,renderItem:(e,t,r)=>k(w,{flexDirection:`row`,children:[k(T,{bold:r,color:r?n.palette.bg:n.palette.fg,backgroundColor:r?n.palette.primary:void 0,children:[r?`❯ `:` `,e.label]}),e.hint&&r&&k(T,{color:n.palette.muted,children:[` `,e.hint]})]})})}function ct({message:e,options:t,query:n,selectedIdx:r,header:i,footer:a}){let o=J(),s=se(()=>{let e=n.trim().toLowerCase();return e?t.filter(t=>t.label.toLowerCase().includes(e)):t},[t,n]),c=Math.min(r,Math.max(0,s.length-1));return k(Y,{children:[i&&O(w,{marginBottom:1,children:O(T,{color:o.palette.muted,children:i})}),O(w,{marginBottom:1,children:O(T,{bold:!0,color:o.palette.muted,children:e})}),s.length===0?O(w,{children:O(T,{color:o.palette.muted,children:`No matches`})}):O(st,{options:s,selectedIdx:c}),O(w,{marginTop:1,children:O(T,{color:o.palette.muted,children:`type to filter · ↑↓ navigate · Enter select · Esc cancel`})}),a&&O(w,{marginTop:1,children:O(T,{color:o.palette.muted,children:a})})]})}function lt({message:e,options:t,header:n,footer:r,onSelect:i}){let a=J(),[o,s]=D(0);return E((e,n)=>{if(n.escape){i(null);return}if(n.upArrow){s(e=>(e-1+t.length)%t.length);return}if(n.downArrow){s(e=>(e+1)%t.length);return}n.return&&i(t[o]?.value??null)}),k(Y,{children:[n&&O(w,{marginBottom:1,children:O(T,{color:a.palette.muted,children:n})}),O(w,{marginBottom:1,children:O(T,{bold:!0,color:a.palette.muted,children:e})}),O(st,{options:t,selectedIdx:o}),O(w,{marginTop:1,children:O(T,{color:a.palette.muted,children:`↑↓ navigate · Enter select · Esc cancel`})}),r&&O(w,{marginTop:1,children:O(T,{color:a.palette.muted,children:r})})]})}function ut({mode:e,searchQuery:t,searchSelectedIdx:n,onResolve:r}){switch(e.type){case`select`:return O(lt,{message:e.message,options:e.options,header:e.header,footer:e.footer,onSelect:r});case`searchSelect`:return O(ct,{message:e.message,options:e.options,query:t??``,selectedIdx:n??0,header:e.header,footer:e.footer});case`password`:return O(at,{message:e.message,validate:e.validate,onSubmit:r});case`confirm`:return O(it,{message:e.message,onConfirm:r});case`approval`:return O(nt,{req:e.req,onResolve:r});default:return null}}function X(e,t,n,r){return new Promise(i=>{let{unmount:a}=re(O(q,{theme:K,children:O(lt,{message:e,options:t,header:n,footer:r,onSelect:e=>{a(),i(e)}})}))})}function dt(e,t){return new Promise(n=>{let{unmount:r}=re(O(q,{theme:K,children:O(at,{message:e,validate:t,onSubmit:e=>{r(),n(e)}})}))})}async function ft(){console.log(t.bold.cyan(`
|
|
87
89
|
⚡ Nova — your coding companion
|
|
88
|
-
`));let e=await
|
|
90
|
+
`));let e=await X(`Pick a provider`,[...N].sort((e,t)=>e.name.localeCompare(t.name)).map(e=>({value:e.id,label:e.name})));e||(console.log(`Cancelled`),process.exit(0));let n=U(e);n||(console.log(t.red(`Unknown provider`)),process.exit(1));let r=await dt(`Enter ${n.name} API key`);r||(console.log(`Cancelled`),process.exit(0));let i=await X(`Pick a default model`,W(e).map(e=>({value:e.id,label:`${e.id} (${(e.contextWindow/1e3).toFixed(0)}k ctx)`})));i||(console.log(`Cancelled`),process.exit(0));let a={provider:e,model:i};return await Ue(a),await We({apiKeys:{[e]:r}}),console.log(t.green(`
|
|
89
91
|
✓ Ready. Type your prompt or /help for commands
|
|
90
|
-
`)),a}const
|
|
92
|
+
`)),a}const pt=new Set([`.env.example`,`.env.sample`,`.env.template`]),mt=/^\.env(\..*)?$/i,ht=new Set([`.env`,`.npmrc`,`.pypirc`,`.netrc`,`.git-credentials`,`credentials`,`credentials.json`,`id_rsa`,`id_dsa`,`id_ecdsa`,`id_ed25519`,`.htpasswd`]),gt=new Set([`.pem`,`.key`,`.p12`,`.pfx`,`.keystore`,`.jks`,`.kdbx`]),_t=new Set([`.ssh`,`.aws`,`.gnupg`,`secrets`,`.secrets`]);function vt(e){let t=_(e).toLowerCase();if(!t)return!1;if(mt.test(t))return!pt.has(t);if(ht.has(t))return!0;let n=t.lastIndexOf(`.`);return n>0&>.has(t.slice(n))?!0:e.toLowerCase().split(te).some(e=>_t.has(e))}function yt(e){switch(e.name){case`read`:case`ls`:case`glob`:case`grep`:case`tree`:return`safe`;case`write`:case`edit`:return`write`;case`git`:{let t=e.args.action??``;return t===`add`||t===`commit`?`write`:`safe`}case`bash`:return`execution`;case`web_search`:case`web_fetch`:return`network`;default:return`execution`}}function bt(e){let t=e.args;switch(e.name){case`bash`:return String(t.command??``);case`read`:return`read ${t.path??``}`;case`write`:return`write ${t.path??``}`;case`edit`:return`edit ${t.path??``}`;case`git`:return`git ${t.action??``} ${(t.args??[]).join(` `)}`.trim();case`web_fetch`:return`fetch ${t.url??``}`;case`web_search`:return`search "${t.query??``}"`;case`glob`:return`glob ${t.pattern??``}`;case`grep`:return`grep "${t.pattern??``}"${t.path?` in ${t.path}`:``}`;case`ls`:return`ls ${t.path??``}`;case`tree`:return`tree ${t.path??``}`;default:return e.name}}function xt(e){if(!e)return``;let t=[];/(^|[^.a-z])\.env($|[^.a-z])/i.test(e)&&t.push(`.env`);for(let n of ht)n!==`.env`&&e.includes(n)&&t.push(n);for(let n of gt)if(e.includes(n)){t.push(`*${n}`);break}return t.length?`⚠ command may read secret file (${t.join(`, `)})`:``}var St=class{#e;#t;#n=null;constructor(e,t){this.#e=e,this.#t=t}get mode(){return this.#e}setMode(e){this.#e=e}setApprover(e){this.#n=e}async check(e){if(this.#e===`unrestricted`)return{allow:!0};let t=this.#r(e);if(t)return{allow:!1,reason:`Blocked: "${t}" is a secret file and cannot be read in restricted mode.`};if(yt(e)===`safe`)return{allow:!0};if(!this.#n)return{allow:!1,reason:`Blocked: restricted mode requires interactive approval, but no approver is connected.`};let n=bt(e),r={tool:e.name,risk:yt(e),summary:n,warning:e.name===`bash`?xt(n):void 0};return await this.#n.request(r)?{allow:!0}:{allow:!1,reason:`Denied: ${e.name} was not approved by the user.`}}#r(e){if(e.name!==`read`&&e.name!==`grep`)return null;let t=e.args.path;return typeof t!=`string`||!t.trim()?null:vt(S(this.#t,t))?t:null}};const Ct=new Set([`.jpg`,`.jpeg`,`.png`,`.gif`,`.webp`]),wt=e=>a({description:`Read file contents. Supports text and images (jpg, png, gif, webp). Text output is truncated to 2000 lines.`,inputSchema:A.object({path:A.string().describe(`Path to file (relative or absolute)`),offset:A.number().optional().describe(`Start line (1-based, default 1)`),limit:A.number().optional().describe(`Max lines to read (default 2000)`)}),execute:async t=>{try{let n=S(e,t.path),r=ee(n).toLowerCase();if(Ct.has(r))return{content:[{type:`image`,data:(await p(n)).toString(`base64`),mime:r===`.jpg`?`image/jpeg`:`image/${r.slice(1)}`}],isError:!1};let i=(await p(n,`utf-8`)).split(`
|
|
91
93
|
`),a=Math.max(0,(t.offset??1)-1),o=t.limit??2e3,s=i.slice(a,a+o),c=a+o<i.length;return{content:[{type:`text`,text:s.join(`
|
|
92
|
-
`)+(c?`\n…${i.length-a-o} more lines`:``)}],isError:!1}}catch(e){return{content:[{type:`text`,text:`Error reading file: ${e.message}`}],isError:!0}}},toModelOutput:({output:e})=>
|
|
94
|
+
`)+(c?`\n…${i.length-a-o} more lines`:``)}],isError:!1}}catch(e){return{content:[{type:`text`,text:`Error reading file: ${e.message}`}],isError:!0}}},toModelOutput:({output:e})=>P(e)}),Tt=e=>a({description:`Write content to a file. Creates the file and parent directories if needed.`,inputSchema:A.object({path:A.string().describe(`Path to file`),content:A.string().describe(`Content to write`)}),execute:async t=>{try{let n=S(e,t.path);await f(v(n),{recursive:!0}),await g(n,t.content);let r=F(e,n);return{content:[{type:`text`,text:`Wrote ${t.content.length} bytes → ${r}`}],isError:!1}}catch(e){return{content:[{type:`text`,text:`Error writing file: ${e.message}`}],isError:!0}}},toModelOutput:({output:e})=>P(e)}),Et=e=>a({description:`Edit a file using exact text replacement. Each edit's oldText must be unique in the file.`,inputSchema:A.object({path:A.string().describe(`Path to file`),edits:A.array(A.object({oldText:A.string().describe(`Exact text to find (must be unique)`),newText:A.string().describe(`Replacement text`)})).describe(`Array of {oldText, newText} replacements. oldText must be unique. Non-overlapping.`)}),execute:async t=>{try{let n=S(e,t.path),r;try{r=await p(n,`utf-8`)}catch{return{content:[{type:`text`,text:`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:[{type:`text`,text:`oldText not found: "${e.oldText.slice(0,80)}…"`}],isError:!0};if(t>1)return{content:[{type:`text`,text:`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:[{type:`text`,text:`Edited ${F(e,n)} (${i.length} replacement${i.length>1?`s`:``})`}],isError:!1}}catch(e){return{content:[{type:`text`,text:`Error editing file: ${e.message}`}],isError:!0}}},toModelOutput:({output:e})=>P(e)}),Dt=e=>a({description:`Execute safe, non-interactive git commands (status, diff, log, add, commit) in the repository.`,inputSchema:A.object({action:A.enum([`status`,`diff`,`log`,`add`,`commit`]).describe(`The git action to execute`),args:A.array(A.string()).optional().describe(`Optional additional arguments or file paths`)}),execute:async(t,{abortSignal:n})=>{let r=t.action,i=t.args??[];if(!new Set([`status`,`diff`,`log`,`add`,`commit`]).has(r))return{content:[{type:`text`,text:`Error: Git action '${r}' is not supported.`}],isError:!0};try{let t=[`git`,r,...i],a=j(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+=`
|
|
93
95
|
`),d+=s.slice(0,u-d.length)),d.length>=u&&(d+=`
|
|
94
|
-
…truncated`),{content:[{type:`text`,text:d||`(no output)`}],isError:l!==0}}catch(e){return{content:[{type:`text`,text:`Error running git: ${e.message}`}],isError:!0}}},toModelOutput:({output:e})=>
|
|
95
|
-
`):`No files found`}],isError:!1}}catch(e){return{content:[{type:`text`,text:`Error: ${e.message}`}],isError:!0}}},toModelOutput:({output:e})=>
|
|
96
|
+
…truncated`),{content:[{type:`text`,text:d||`(no output)`}],isError:l!==0}}catch(e){return{content:[{type:`text`,text:`Error running git: ${e.message}`}],isError:!0}}},toModelOutput:({output:e})=>P(e)}),Ot=e=>a({description:`Find files by glob pattern (e.g. **/*.ts, src/**/*.test.ts).`,inputSchema:A.object({pattern:A.string().describe(`Glob pattern (e.g. **/*.ts)`),path:A.string().optional().describe(`Directory to search in (default .)`),nocase:A.boolean().optional().describe(`Case-insensitive search (default false)`)}),execute:async t=>{try{let n=S(e,t.path??`.`),r=(await ce(t.pattern,{cwd:n,nocase:t.nocase??!1})).slice(0,500),i=x(e,n),a=i?`${i}/`:``,o=r.map(e=>a+e);return{content:[{type:`text`,text:o.length>0?o.join(`
|
|
97
|
+
`):`No files found`}],isError:!1}}catch(e){return{content:[{type:`text`,text:`Error: ${e.message}`}],isError:!0}}},toModelOutput:({output:e})=>P(e)}),kt=e=>a({description:`Search file contents with a regex pattern. Returns matching lines with file paths and line numbers.`,inputSchema:A.object({pattern:A.string().describe(`Regex pattern to search for`),path:A.string().optional().describe(`Directory or file to search in (default .)`),glob:A.string().optional().describe(`File filter glob (e.g. *.ts)`)}),execute:async(t,{abortSignal:n})=>{try{let r=S(e,t.path??`.`),i=t.glob,a=x(e,r)||`.`;try{let r=[`rg`,`--line-number`,`--max-count`,`200`];i&&r.push(`--glob=${i}`),r.push(`--`,t.pattern,a);let o=j(r[0],r.slice(1),{cwd:e,stdio:[`ignore`,`pipe`,`pipe`]}),s=()=>{o.kill(),o.stdout.destroy(),o.stderr.destroy()};n?.addEventListener(`abort`,s,{once:!0});let c=``;o.stdout.on(`data`,e=>{c+=e.toString()});let l;try{l=await new Promise((e,t)=>{o.on(`error`,t),o.on(`close`,t=>e(t??-1))})}finally{n?.removeEventListener(`abort`,s)}if(l===0)return{content:[{type:`text`,text:c.split(`
|
|
96
98
|
`).slice(0,200).join(`
|
|
97
|
-
`)||`No matches`}],isError:!1}}catch{}let o=await
|
|
99
|
+
`)||`No matches`}],isError:!1}}catch{}let o=await ce(i||`**/*`,{cwd:r,ignore:[`**/node_modules/**`,`**/.git/**`]}),s=a===`.`?``:`${a}/`,c=new RegExp(t.pattern,`i`),l=[];for(let e of o.slice(0,500)){if(n?.aborted)break;try{let t=(await p(S(r,e),`utf-8`)).split(`
|
|
98
100
|
`);for(let n=0;n<t.length&&l.length<200;n++){let r=t[n];r&&c.test(r)&&l.push(`${s}${e}:${n+1}:${r}`)}}catch{}}return{content:[{type:`text`,text:l.join(`
|
|
99
|
-
`)||`No matches`}],isError:!1}}catch(e){return{content:[{type:`text`,text:`Error: ${e.message}`}],isError:!0}}},toModelOutput:({output:e})=>
|
|
100
|
-
`)||`(empty)`}],isError:!1}}catch(e){return{content:[{type:`text`,text:`Error: ${e.message}`}],isError:!0}}},toModelOutput:({output:e})=>
|
|
101
|
+
`)||`No matches`}],isError:!1}}catch(e){return{content:[{type:`text`,text:`Error: ${e.message}`}],isError:!0}}},toModelOutput:({output:e})=>P(e)}),At=e=>a({description:`List directory contents.`,inputSchema:A.object({path:A.string().optional().describe(`Directory to list (default .)`)}),execute:async t=>{try{return{content:[{type:`text`,text:(await m(S(e,t.path??`.`),{withFileTypes:!0})).map(e=>{let t=e.isDirectory()?`/`:e.isSymbolicLink()?`@`:``;return`${e.name}${t}`}).join(`
|
|
102
|
+
`)||`(empty)`}],isError:!1}}catch(e){return{content:[{type:`text`,text:`Error: ${e.message}`}],isError:!0}}},toModelOutput:({output:e})=>P(e)}),jt=e=>a({description:`Print a visual directory tree structure, ignoring common ignored folders like node_modules and .git.`,inputSchema:A.object({path:A.string().optional().describe(`Directory to start tree from (default .)`),depth:A.number().optional().describe(`Maximum depth to traverse (default 3)`)}),execute:async t=>{try{let n=S(e,t.path??`.`),r=t.depth??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=``,s=(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<s.length;r++){let i=s[r],c=r===s.length-1,l=c?`└── `:`├── `,u=n+(c?` `:`│ `);o+=`${n}${l}${i.name}${i.isDirectory()?`/`:``}\n`,i.isDirectory()&&(o+=await a(S(e,i.name),t+1,u))}return o}return{content:[{type:`text`,text:await a(n,1,``)||`(empty)`}],isError:!1}}catch(e){return{content:[{type:`text`,text:`Error: ${e.message}`}],isError:!0}}},toModelOutput:({output:e})=>P(e)}),Mt=e=>a({description:`Execute a shell command. Returns stdout and stderr. Timeout after N seconds (default 120).`,inputSchema:A.object({command:A.string().describe(`Shell command to run`),timeout:A.number().optional().describe(`Timeout in seconds (default 120)`)}),execute:async(t,{abortSignal:n})=>{let r=t.command,i=(t.timeout??120)*1e3;try{let t=j(`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+=`
|
|
101
103
|
`),f+=o.slice(0,d-f.length)),f.length>=d&&(f+=`
|
|
102
|
-
…truncated`),s&&(f+=`\n[timeout after ${i/1e3}s]`),f+=`\n[exit ${u}]`,{content:[{type:`text`,text:f}],isError:u!==0||s}}catch(e){return{content:[{type:`text`,text:`Error: ${e.message}`}],isError:!0}}},toModelOutput:({output:e})=>
|
|
104
|
+
…truncated`),s&&(f+=`\n[timeout after ${i/1e3}s]`),f+=`\n[exit ${u}]`,{content:[{type:`text`,text:f}],isError:u!==0||s}}catch(e){return{content:[{type:`text`,text:`Error: ${e.message}`}],isError:!0}}},toModelOutput:({output:e})=>P(e)}),Z=5e4;function Q(e,t,n){let r;do r=e,e=e.replace(t,n);while(e!==r);return e}function Nt(e){let t=e;return t=Q(t,/<!--[\s\S]*?-->/g,``),t=Q(t,/<script[\s\S]*?<\/script[^>]*>/gi,``),t=Q(t,/<style[\s\S]*?<\/style[^>]*>/gi,``),t=t.replace(/<a[^>]*href=["']?([^"'>\s]*)["']?[^>]*>([\s\S]*?)<\/a>/gi,`[$2]($1)`).replace(/<\/?(p|div|br|h[1-6]|li|tr|blockquote|pre|hr)[^>]*>/gi,`
|
|
103
105
|
`).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,`
|
|
104
106
|
|
|
105
|
-
`).trim(),t.length>
|
|
107
|
+
`).trim(),t.length>Z&&(t=`${t.slice(0,Z)}\n…truncated`),t}const Pt=`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`,Ft=()=>a({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.`,inputSchema:A.object({query:A.string().describe(`Search query`)}),execute:async(e,{abortSignal:t})=>{let n=e.query;if(!n.trim())return{content:[{type:`text`,text:`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":Pt}});if(!r.ok)return{content:[{type:`text`,text:`Search failed: HTTP ${r.status}`}],isError:!0};let i=await r.text(),a=/<div[^>]*class="[^"]*result__body[^"]*"[^>]*>/gi,o=[];for(let e of i.matchAll(a))e.index!==void 0&&o.push(e.index);let s=[];if(o.length>0)for(let e=0;e<o.length;e++){let t=o[e],n=o[e+1]??i.length;s.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 s){if(c.length>=10)break;let t=l.exec(e);if(!t)continue;let n=t[1],r=Nt(t[2]),i=u.exec(e),a=i?Nt(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:[{type:`text`,text:`No results found.`}],isError:!1}:{content:[{type:`text`,text:c.join(`
|
|
106
108
|
|
|
107
|
-
`)}],isError:!1}}catch(e){let t=e.message;return t.includes(`abort`)?{content:[{type:`text`,text:`Search aborted.`}],isError:!0}:{content:[{type:`text`,text:`Search error: ${t}`}],isError:!0}}},toModelOutput:({output:e})=>
|
|
109
|
+
`)}],isError:!1}}catch(e){let t=e.message;return t.includes(`abort`)?{content:[{type:`text`,text:`Search aborted.`}],isError:!0}:{content:[{type:`text`,text:`Search error: ${t}`}],isError:!0}}},toModelOutput:({output:e})=>P(e)}),It=()=>a({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.`,inputSchema:A.object({url:A.string().describe(`URL to fetch`)}),execute:async(e,{abortSignal:t})=>{let n=e.url;if(!n.trim())return{content:[{type:`text`,text:`Error: empty URL`}],isError:!0};try{new URL(n)}catch{return{content:[{type:`text`,text:`Error: invalid URL: ${n}`}],isError:!0}}try{let e=await fetch(n,{signal:t??void 0,headers:{"User-Agent":Pt,Accept:`text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8`},redirect:`follow`});if(!e.ok)return{content:[{type:`text`,text:`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:[{type:`text`,text:Nt(i)}],isError:!1}:{content:[{type:`text`,text:i.length>Z?`${i.slice(0,Z)}\n…truncated`:i}],isError:!1}}catch(e){let t=e.message;return t.includes(`abort`)?{content:[{type:`text`,text:`Fetch aborted.`}],isError:!0}:{content:[{type:`text`,text:`Fetch error: ${t}`}],isError:!0}}},toModelOutput:({output:e})=>P(e)});function Lt(e){return{read:wt(e),write:Tt(e),edit:Et(e),bash:Mt(e),glob:Ot(e),grep:kt(e),ls:At(e),tree:jt(e),git:Dt(e),web_search:Ft(),web_fetch:It()}}const Rt=v(le(import.meta.url));let zt=null,$=null;async function Bt(){if($)return $;try{let e=await p(b(Rt,`..`,`package.json`),`utf-8`);return $=JSON.parse(e).version??`0.0.0`,$}catch{return`0.0.0`}}async function Vt(){if(zt)return zt;try{let e=j(`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 zt=t,t}catch{}return null}async function Ht(){let e=await Bt(),t=await Vt();return t?{hasUpdate:ue.gt(t,e),current:e,latest:t}:null}async function Ut(e=!1){try{let t=j(`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 Wt(){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`},sessions:{type:`string`,short:`s`},resume:{type:`boolean`,short:`r`},all:{type:`boolean`},restricted:{type:`boolean`},unrestricted:{type:`boolean`}},strict:!1,allowPositionals:!0});return{flags:t,args:n}}function Gt(e,t){return t?G(t,e):Qe(e)}async function Kt(){let e=Number(process.versions.node.split(`.`)[0]);(!e||e<24)&&(console.error(`novacode requires Node.js >= 24. You have ${process.version}.`),console.error(`Upgrade: https://nodejs.org/`),process.exit(1));let{flags:n,args:r}=Wt();if(n.version){let e=await Bt();console.log(`nova ${e}`),process.exit(0)}if(n.help&&(console.log(`nova — open-source coding agent
|
|
108
110
|
|
|
109
111
|
Usage:
|
|
110
112
|
nova Interactive mode
|
|
@@ -125,11 +127,11 @@ Options:
|
|
|
125
127
|
-s, --sessions <id> Resume/manage sessions
|
|
126
128
|
-r, --resume Resume the most recent session
|
|
127
129
|
--restricted Start in restricted mode (approve every action)
|
|
128
|
-
--unrestricted Start in unrestricted mode (no approvals)`),process.exit(0)),r[0]===`update`){await
|
|
130
|
+
--unrestricted Start in unrestricted mode (no approvals)`),process.exit(0)),r[0]===`update`){await Ut();return}if(r[0]===`reset`){let{handleCliReset:e}=await import(`./reset-CWIz7872.mjs`).then(e=>e.n);await e();return}r.length>0&&!n.sessions&&(console.error(t.yellow(`Unknown command: ${r.join(` `)}`)),console.error("Run `nova --help` for usage."),process.exit(1));let i=new AbortController,a=()=>{i.abort(),process.stderr.write(`
|
|
129
131
|
Aborted.
|
|
130
|
-
`),process.exit(130)};process.on(`SIGINT`,a),process.on(`SIGTERM`,a);let o=await(await
|
|
132
|
+
`),process.exit(130)};process.on(`SIGINT`,a),process.on(`SIGTERM`,a);let o=await(await ze()?Be():ft()),s=await Ve(),c=await Ze();if(await c.prune(),n.sessions){let e=n.sessions;if(e===`ls`||e===`list`){let e=r[0]?parseInt(r[0],10):10;await Le(c,[`ls`],{limit:Number.isNaN(e)?10:e});return}if(e===`rm`||e===`delete`){let e=r[0],t=!!n.all;await Le(c,[`rm`,e??``],{all:t});return}}let l=null;n.resume?(l=await c.latest(),l||(console.error(`No recent session found to resume.`),process.exit(1))):n.sessions&&(l=await c.get(n.sessions),l||(console.error(`Session not found: ${n.sessions}`),process.exit(1)));let u=n.provider||l?.provider||o.provider,d=n.model||l?.model||o.model,f=n[`api-key`]||s.apiKeys[u],p=U(u);p||(console.error(`Unknown provider: ${u}`),console.error(`Available: ${N.map(e=>e.id).join(`, `)}`),process.exit(1)),f||(console.error(`No API key for ${p.name}. Set ${p.envKey} or run nova for onboarding.`),process.exit(1));let m=Gt(d,u);if(!m){console.error(`Unknown model: ${d}`),console.error(`Available models:`);for(let e of W(u))console.error(` ${e.id}`);process.exit(1)}let h=process.cwd(),g=await qt(n);if(!g)return;let _=new St(g,h),v=Lt(h),{skills:ee,agentsMd:y}=await Ae(h),b=ve(h,v,ke(ee),y??void 0);l||=await c.create(h,m.id,u);let x=l.id,S=await c.messages(x),te=new be({provider:p.id,model:m,apiKey:f,system:b,tools:v,messages:S,policy:_});process.off(`SIGINT`,a),process.off(`SIGTERM`,a);let{interactive:C}=await import(`./app-C-3NXlux.mjs`);await C(te,c,x,ee,!!y,_)}async function qt(e){if(e.restricted)return`restricted`;if(e.unrestricted)return`unrestricted`;let t=await X(`Choose a permission mode`,[{value:`restricted`,label:`Restricted — ask permission before each action`},{value:`unrestricted`,label:`Unrestricted — run without approval (may be dangerous)`}],void 0,`Use /permission to switch later, or --restricted/--unrestricted to skip this prompt.`);return t!==`restricted`&&t!==`unrestricted`?(console.log(`Cancelled`),null):t}function Jt(e,n){console.error(t.red(`\n✗ ${e}:`),n),console.error(t.yellow(`
|
|
131
133
|
Nova hit an unexpected error and must exit.
|
|
132
134
|
• Try starting `+t.bold.cyan(`nova`)+` again.
|
|
133
135
|
• If it keeps failing, run `+t.bold.cyan(`nova reset`)+` — local data may be corrupted or out of date.
|
|
134
|
-
`)),process.exit(1)}process.on(`unhandledRejection`,e=>{
|
|
136
|
+
`)),process.exit(1)}process.on(`unhandledRejection`,e=>{Jt(`Unhandled rejection`,e)}),process.on(`uncaughtException`,e=>{Jt(`Uncaught exception`,e)}),Kt().catch(e=>{Jt(`Fatal error`,e)});export{Oe as C,N as D,pe as E,Ne as S,fe as T,Be as _,ot as a,Fe as b,$e as c,W as d,U as f,Ve as g,Ge as h,ut as i,G as l,Je as m,Bt as n,q as o,qe as p,Ut as r,J as s,Ht as t,Qe as u,We as v,ge as w,Pe as x,Ue as y};
|
|
135
137
|
//# sourceMappingURL=main.mjs.map
|