novacode 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +10 -27
  2. package/dist/app-C_2My7n6.mjs +28 -0
  3. package/dist/app-C_2My7n6.mjs.map +1 -0
  4. package/dist/main.mjs +86 -36
  5. package/dist/main.mjs.map +1 -1
  6. package/package.json +1 -2
  7. package/dist/app-bQ9a_p_K.mjs +0 -22
  8. package/dist/app-bQ9a_p_K.mjs.map +0 -1
  9. package/src/agent/agent.ts +0 -87
  10. package/src/agent/loop.ts +0 -237
  11. package/src/agent/prompt.ts +0 -50
  12. package/src/commands/compact.ts +0 -28
  13. package/src/commands/index.ts +0 -128
  14. package/src/commands/models.ts +0 -85
  15. package/src/commands/providers.ts +0 -213
  16. package/src/commands/session.ts +0 -52
  17. package/src/config/providers.ts +0 -207
  18. package/src/config/store.ts +0 -66
  19. package/src/main.ts +0 -205
  20. package/src/onboarding/wizard.ts +0 -54
  21. package/src/provider/gemini.ts +0 -269
  22. package/src/provider/openai.ts +0 -239
  23. package/src/provider/stream.ts +0 -138
  24. package/src/session/compact.ts +0 -159
  25. package/src/session/store.ts +0 -209
  26. package/src/tools/fs.ts +0 -189
  27. package/src/tools/git.ts +0 -99
  28. package/src/tools/index.ts +0 -33
  29. package/src/tools/search.ts +0 -274
  30. package/src/tools/shell.ts +0 -90
  31. package/src/tools/web.ts +0 -239
  32. package/src/tui/app.tsx +0 -454
  33. package/src/tui/components/liveArea.tsx +0 -70
  34. package/src/tui/components/message.tsx +0 -117
  35. package/src/tui/components/statusBar.tsx +0 -64
  36. package/src/tui/constants.ts +0 -25
  37. package/src/tui/markdown.ts +0 -62
  38. package/src/tui/prompts.tsx +0 -205
  39. package/src/types.ts +0 -262
  40. package/src/update.ts +0 -89
  41. package/src/util.ts +0 -80
package/dist/main.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"main.mjs","names":["#abort","#resolve","#events","#done","#result","#doneResolve","#api","#model","#apiKey","#baseUrl","#system","#tools","#messages","#sessionsDir","#sessionDir","#metadataPath","#messagesPath","#compactionPath"],"sources":["../src/provider/gemini.ts","../src/provider/openai.ts","../src/provider/stream.ts","../src/util.ts","../src/agent/loop.ts","../src/agent/agent.ts","../src/agent/prompt.ts","../src/commands/session.ts","../src/config/providers.ts","../src/config/store.ts","../src/tui/prompts.tsx","../src/onboarding/wizard.ts","../src/session/store.ts","../src/tools/fs.ts","../src/tools/git.ts","../src/tools/search.ts","../src/tools/shell.ts","../src/tools/web.ts","../src/tools/index.ts","../src/update.ts","../src/main.ts"],"sourcesContent":["import type {\n\tAssistantResult,\n\tContentPart,\n\tMsg,\n\tStopReason,\n\tStreamEvent,\n\tStreamFn,\n\tStreamOpts,\n\tToolDef,\n\tUsage,\n} from \"../types.ts\"\nimport { EventStream } from \"./stream.ts\"\n\ninterface GeminiPart {\n\ttext?: string\n\tthought?: boolean | string\n\tinline_data?: { mime_type: string; data: string }\n\tfunction_call?: { name: string; args: Record<string, unknown> }\n\tfunction_response?: { name: string; response: Record<string, unknown> }\n\tthought_signature?: string\n}\n\ninterface GeminiContent {\n\trole: \"user\" | \"model\"\n\tparts: GeminiPart[]\n}\n\n/**\n * Maps our internal Msg format to the Gemini 'Content' format.\n * Groups consecutive tool_result messages into a single Gemini message.\n */\nfunction msgsToGemini(messages: Msg[]): GeminiContent[] {\n\tconst contents: GeminiContent[] = []\n\n\tfor (const msg of messages) {\n\t\tif (msg.role === \"user\") {\n\t\t\tconst parts: GeminiPart[] =\n\t\t\t\ttypeof msg.content === \"string\"\n\t\t\t\t\t? [{ text: msg.content }]\n\t\t\t\t\t: msg.content.map((c) => {\n\t\t\t\t\t\t\tif (c.type === \"text\") return { text: c.text }\n\t\t\t\t\t\t\tif (c.type === \"image\") return { inline_data: { mime_type: c.mime, data: c.data } }\n\t\t\t\t\t\t\treturn { text: \"\" }\n\t\t\t\t\t\t})\n\t\t\tcontents.push({ role: \"user\", parts })\n\t\t} else if (msg.role === \"assistant\") {\n\t\t\tconst parts: GeminiPart[] = msg.content.map((c) => {\n\t\t\t\tif (c.type === \"text\") return { text: c.text, thought_signature: c.signature }\n\t\t\t\tif (c.type === \"thinking\")\n\t\t\t\t\treturn { thought: true, text: c.text, thought_signature: c.signature }\n\t\t\t\tif (c.type === \"tool_call\")\n\t\t\t\t\treturn { function_call: { name: c.name, args: c.args }, thought_signature: c.signature }\n\t\t\t\treturn { text: \"\" }\n\t\t\t})\n\t\t\tcontents.push({ role: \"model\", parts })\n\t\t} else if (msg.role === \"tool_result\") {\n\t\t\tconst part: GeminiPart = {\n\t\t\t\tfunction_response: {\n\t\t\t\t\tname: msg.tool,\n\t\t\t\t\tresponse: {\n\t\t\t\t\t\tcontent: msg.content\n\t\t\t\t\t\t\t.map((c) => (c.type === \"text\" ? c.text : JSON.stringify(c)))\n\t\t\t\t\t\t\t.join(\"\\n\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tconst last = contents[contents.length - 1]\n\t\t\t// Gemini requires alternating roles; multiple function_responses group into one 'user' message.\n\t\t\tif (last && last.role === \"user\" && last.parts.some((p) => p.function_response)) {\n\t\t\t\tlast.parts.push(part)\n\t\t\t} else {\n\t\t\t\tcontents.push({ role: \"user\", parts: [part] })\n\t\t\t}\n\t\t}\n\t}\n\n\treturn contents\n}\n\nfunction toolsToGemini(tools: ToolDef[]): unknown[] {\n\tif (tools.length === 0) return []\n\treturn [\n\t\t{\n\t\t\tfunction_declarations: tools.map((t) => ({\n\t\t\t\tname: t.name,\n\t\t\t\tdescription: t.description,\n\t\t\t\tparameters: t.parameters,\n\t\t\t})),\n\t\t},\n\t]\n}\n\nexport const streamGemini: StreamFn = (\n\topts: StreamOpts,\n): EventStream<StreamEvent, AssistantResult> => {\n\tconst es = new EventStream<StreamEvent, AssistantResult>()\n\n\t;(async () => {\n\t\tlet usage: Usage = { in: 0, out: 0 }\n\t\tconst content: ContentPart[] = []\n\n\t\ttry {\n\t\t\tconst baseUrl = opts.baseUrl || \"https://generativelanguage.googleapis.com\"\n\t\t\tconst url = `${baseUrl}/v1beta/models/${opts.model.id}:streamGenerateContent?alt=sse&key=${opts.apiKey}`\n\n\t\t\tconst body = {\n\t\t\t\tcontents: msgsToGemini(opts.messages),\n\t\t\t\tsystem_instruction: opts.system ? { parts: [{ text: opts.system }] } : undefined,\n\t\t\t\ttools: opts.tools.length > 0 ? toolsToGemini(opts.tools) : undefined,\n\t\t\t\tgenerationConfig: {\n\t\t\t\t\tthinkingConfig: opts.model.supportsThinking ? { thinkingLevel: \"low\" } : undefined,\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\"Api-Revision\": \"2026-05-20\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(body),\n\t\t\t\tsignal: opts.signal,\n\t\t\t})\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await response.text()\n\t\t\t\tlet msg = text\n\t\t\t\ttry {\n\t\t\t\t\tconst json = JSON.parse(text)\n\t\t\t\t\tmsg = json.error?.message || json.message || text\n\t\t\t\t} catch {\n\t\t\t\t\t/* use raw text */\n\t\t\t\t}\n\n\t\t\t\tconst errorMsg = `Gemini Error (${response.status}): ${msg}`\n\t\t\t\tes.push({ type: \"text_delta\", text: errorMsg })\n\t\t\t\tes.finish({\n\t\t\t\t\tcontent: [{ type: \"text\", text: errorMsg }],\n\t\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\t\tstop: \"error\",\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst reader = response.body?.getReader()\n\t\t\tif (!reader) {\n\t\t\t\tes.finish({ content: [], usage: { in: 0, out: 0 }, stop: \"error\" })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst decoder = new TextDecoder()\n\t\t\tlet buffer = \"\"\n\t\t\tlet stop: StopReason = \"stop\"\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\tif (done) break\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true })\n\t\t\t\tconst lines = buffer.split(\"\\n\")\n\t\t\t\tbuffer = lines.pop() ?? \"\"\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst trimmed = line.trim()\n\t\t\t\t\tif (!trimmed?.startsWith(\"data: \")) continue\n\t\t\t\t\tconst data = trimmed.slice(6)\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst chunk = JSON.parse(data)\n\t\t\t\t\t\tconst candidate = chunk.candidates?.[0]\n\n\t\t\t\t\t\t// Handle usage metadata\n\t\t\t\t\t\tif (chunk.usageMetadata) {\n\t\t\t\t\t\t\tusage = {\n\t\t\t\t\t\t\t\tin: chunk.usageMetadata.promptTokenCount || usage.in,\n\t\t\t\t\t\t\t\tout: chunk.usageMetadata.candidatesTokenCount || usage.out,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tes.push({ type: \"usage\", usage })\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!candidate) continue\n\n\t\t\t\t\t\t// Map finish reason\n\t\t\t\t\t\tif (candidate.finishReason) {\n\t\t\t\t\t\t\tconst reason = candidate.finishReason\n\t\t\t\t\t\t\tif (reason === \"STOP\") stop = \"stop\"\n\t\t\t\t\t\t\telse if (reason === \"MAX_TOKENS\") stop = \"length\"\n\t\t\t\t\t\t\telse if (reason === \"SAFETY\" || reason === \"RECITATION\" || reason === \"OTHER\")\n\t\t\t\t\t\t\t\tstop = \"error\"\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst parts = candidate.content?.parts\n\t\t\t\t\t\tif (parts) {\n\t\t\t\t\t\t\tfor (const part of parts) {\n\t\t\t\t\t\t\t\tconst sig = part.thought_signature || part.thoughtSignature\n\n\t\t\t\t\t\t\t\t// Handle text and thinking deltas\n\t\t\t\t\t\t\t\tif (part.text) {\n\t\t\t\t\t\t\t\t\tif (part.thought === true || typeof part.thought === \"string\") {\n\t\t\t\t\t\t\t\t\t\tconst thoughtText = typeof part.thought === \"string\" ? part.thought : part.text\n\t\t\t\t\t\t\t\t\t\tes.push({ type: \"thinking_delta\", text: thoughtText })\n\t\t\t\t\t\t\t\t\t\tconst last = content[content.length - 1]\n\t\t\t\t\t\t\t\t\t\tif (last?.type === \"thinking\") {\n\t\t\t\t\t\t\t\t\t\t\tlast.text += thoughtText\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tcontent.push({ type: \"thinking\", text: thoughtText, signature: sig })\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tes.push({ type: \"text_delta\", text: part.text })\n\t\t\t\t\t\t\t\t\t\tconst last = content[content.length - 1]\n\t\t\t\t\t\t\t\t\t\tif (last?.type === \"text\") {\n\t\t\t\t\t\t\t\t\t\t\tlast.text += part.text\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tcontent.push({ type: \"text\", text: part.text, signature: sig })\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Handle function calls (can be snake_case or camelCase in some API versions)\n\t\t\t\t\t\t\t\tconst fc = part.functionCall || part.function_call\n\t\t\t\t\t\t\t\tif (fc) {\n\t\t\t\t\t\t\t\t\tconst name = fc.name\n\t\t\t\t\t\t\t\t\tconst args = (fc.args as Record<string, unknown>) || {}\n\t\t\t\t\t\t\t\t\tconst id = `call_${Math.random().toString(36).slice(2, 9)}`\n\n\t\t\t\t\t\t\t\t\tconst toolCall: ContentPart = {\n\t\t\t\t\t\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\t\t\targs,\n\t\t\t\t\t\t\t\t\t\tsignature: sig,\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tcontent.push(toolCall)\n\t\t\t\t\t\t\t\t\tes.push({ type: \"tool_call\", call: toolCall })\n\t\t\t\t\t\t\t\t\tstop = \"tool_use\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (_e) {\n\t\t\t\t\t\tif (data.trim() !== \"\" && data.trim() !== \"[DONE]\") {\n\t\t\t\t\t\t\t// skip noise\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tes.finish({ content, usage, stop })\n\t\t} catch (e) {\n\t\t\tif (opts.signal?.aborted) {\n\t\t\t\tes.finish({\n\t\t\t\t\tcontent,\n\t\t\t\t\tusage,\n\t\t\t\t\tstop: \"aborted\",\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst errorMsg = `Gemini Network/Request Error: ${e instanceof Error ? e.message : String(e)}`\n\t\t\tes.push({ type: \"text_delta\", text: errorMsg })\n\t\t\tes.finish({\n\t\t\t\tcontent: [{ type: \"text\", text: errorMsg }],\n\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\tstop: \"error\",\n\t\t\t})\n\t\t}\n\t})()\n\n\treturn es\n}\n","import type {\n\tAssistantResult,\n\tMsg,\n\tStopReason,\n\tStreamEvent,\n\tStreamFn,\n\tStreamOpts,\n\tToolDef,\n\tUsage,\n} from \"../types.ts\"\nimport { EventStream } from \"./stream.ts\"\n\nfunction msgToOpenAI(msg: Msg): Record<string, unknown> {\n\tif (msg.role === \"user\") {\n\t\treturn {\n\t\t\trole: \"user\",\n\t\t\tcontent:\n\t\t\t\ttypeof msg.content === \"string\"\n\t\t\t\t\t? msg.content\n\t\t\t\t\t: msg.content.map((c) => {\n\t\t\t\t\t\t\tif (c.type === \"text\") return { type: \"text\", text: c.text }\n\t\t\t\t\t\t\tif (c.type === \"image\")\n\t\t\t\t\t\t\t\treturn { type: \"image_url\", image_url: { url: `data:${c.mime};base64,${c.data}` } }\n\t\t\t\t\t\t\treturn { type: \"text\", text: \"\" }\n\t\t\t\t\t\t}),\n\t\t}\n\t}\n\tif (msg.role === \"assistant\") {\n\t\tconst textParts: string[] = []\n\t\tconst toolCalls: unknown[] = []\n\n\t\tfor (const c of msg.content) {\n\t\t\tif (c.type === \"text\") textParts.push(c.text)\n\t\t\t// thinking parts are internal — never sent back to the API\n\t\t\tif (c.type === \"tool_call\")\n\t\t\t\ttoolCalls.push({\n\t\t\t\t\ttype: \"function\",\n\t\t\t\t\tid: c.id,\n\t\t\t\t\tfunction: { name: c.name, arguments: JSON.stringify(c.args) },\n\t\t\t\t})\n\t\t}\n\n\t\tconst result: Record<string, unknown> = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: textParts.length > 0 ? textParts.join(\"\") : null,\n\t\t}\n\t\tif (toolCalls.length > 0) result.tool_calls = toolCalls\n\t\treturn result\n\t}\n\t// tool_result\n\tif (msg.role === \"tool_result\") {\n\t\treturn {\n\t\t\trole: \"tool\",\n\t\t\ttool_call_id: msg.callId,\n\t\t\tcontent: msg.content.map((c) => (c.type === \"text\" ? c.text : JSON.stringify(c))).join(\"\\n\"),\n\t\t}\n\t}\n\treturn { role: \"user\", content: \"\" }\n}\n\nfunction toolsToOpenAI(tools: ToolDef[]): unknown[] {\n\treturn tools.map((t) => ({\n\t\ttype: \"function\",\n\t\tfunction: {\n\t\t\tname: t.name,\n\t\t\tdescription: t.description,\n\t\t\tparameters: t.parameters,\n\t\t},\n\t}))\n}\n\nexport const streamOpenAI: StreamFn = (\n\topts: StreamOpts,\n): EventStream<StreamEvent, AssistantResult> => {\n\tconst es = new EventStream<StreamEvent, AssistantResult>()\n\n\t;(async () => {\n\t\tlet textContent = \"\"\n\t\tconst currentToolCalls = new Map<number, { id: string; name: string; args: string }>()\n\t\tlet usage: Usage = { in: 0, out: 0 }\n\n\t\ttry {\n\t\t\tconst body = {\n\t\t\t\tmodel: opts.model.id,\n\t\t\t\tmessages: [{ role: \"system\", content: opts.system }, ...opts.messages.map(msgToOpenAI)],\n\t\t\t\ttools: opts.tools.length > 0 ? toolsToOpenAI(opts.tools) : undefined,\n\t\t\t\tstream: true,\n\t\t\t}\n\n\t\t\tconst response = await fetch(`${opts.baseUrl}/chat/completions`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\tAuthorization: `Bearer ${opts.apiKey}`,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(body),\n\t\t\t\tsignal: opts.signal,\n\t\t\t})\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await response.text()\n\t\t\t\tconst errorMsg = `API error ${response.status}: ${text}`\n\t\t\t\tes.push({ type: \"text_delta\", text: errorMsg })\n\t\t\t\tes.finish({\n\t\t\t\t\tcontent: [{ type: \"text\", text: errorMsg }],\n\t\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\t\tstop: \"error\",\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst reader = response.body?.getReader()\n\t\t\tif (!reader) {\n\t\t\t\tes.finish({ content: [], usage: { in: 0, out: 0 }, stop: \"error\" })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst decoder = new TextDecoder()\n\t\t\tlet buffer = \"\"\n\t\t\tlet stop = \"stop\"\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\tif (done) break\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true })\n\t\t\t\tconst lines = buffer.split(\"\\n\")\n\t\t\t\tbuffer = lines.pop() ?? \"\"\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst trimmed = line.trim()\n\t\t\t\t\tif (!trimmed?.startsWith(\"data: \")) continue\n\t\t\t\t\tconst data = trimmed.slice(6)\n\t\t\t\t\tif (data === \"[DONE]\") continue\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst chunk = JSON.parse(data)\n\t\t\t\t\t\tconst delta = chunk.choices?.[0]?.delta\n\t\t\t\t\t\tif (!delta) continue\n\n\t\t\t\t\t\tif (delta.content) {\n\t\t\t\t\t\t\tes.push({ type: \"text_delta\", text: delta.content })\n\t\t\t\t\t\t\ttextContent += delta.content\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (delta.tool_calls) {\n\t\t\t\t\t\t\tfor (const tc of delta.tool_calls) {\n\t\t\t\t\t\t\t\tconst idx = tc.index ?? 0\n\t\t\t\t\t\t\t\tif (!currentToolCalls.has(idx)) {\n\t\t\t\t\t\t\t\t\tcurrentToolCalls.set(idx, {\n\t\t\t\t\t\t\t\t\t\tid: tc.id ?? \"\",\n\t\t\t\t\t\t\t\t\t\tname: tc.function?.name ?? \"\",\n\t\t\t\t\t\t\t\t\t\targs: \"\",\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconst existing = currentToolCalls.get(idx)!\n\t\t\t\t\t\t\t\tif (tc.id) existing.id = tc.id\n\t\t\t\t\t\t\t\tif (tc.function?.name) existing.name = tc.function.name\n\t\t\t\t\t\t\t\tif (tc.function?.arguments) existing.args += tc.function.arguments\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (chunk.usage) {\n\t\t\t\t\t\t\tusage = {\n\t\t\t\t\t\t\t\tin: chunk.usage.prompt_tokens ?? 0,\n\t\t\t\t\t\t\t\tout: chunk.usage.completion_tokens ?? 0,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tes.push({ type: \"usage\", usage })\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst finishReason = chunk.choices?.[0]?.finish_reason\n\t\t\t\t\t\tif (finishReason) stop = finishReason\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Skip malformed JSON chunks\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst content: AssistantResult[\"content\"] = []\n\t\t\tif (textContent) {\n\t\t\t\tcontent.push({ type: \"text\", text: textContent })\n\t\t\t}\n\t\t\tfor (const [, tc] of currentToolCalls) {\n\t\t\t\tcontent.push({\n\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\tid: tc.id,\n\t\t\t\t\tname: tc.name,\n\t\t\t\t\targs: JSON.parse(tc.args || \"{}\"),\n\t\t\t\t})\n\t\t\t\tes.push({\n\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\tcall: {\n\t\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\t\tid: tc.id,\n\t\t\t\t\t\tname: tc.name,\n\t\t\t\t\t\targs: JSON.parse(tc.args || \"{}\"),\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tstop = \"tool_use\"\n\t\t\t}\n\n\t\t\tes.finish({ content, usage, stop: stop as StopReason })\n\t\t} catch (e) {\n\t\t\tif (opts.signal?.aborted) {\n\t\t\t\tconst content: AssistantResult[\"content\"] = []\n\t\t\t\tif (textContent) {\n\t\t\t\t\tcontent.push({ type: \"text\", text: textContent })\n\t\t\t\t}\n\t\t\t\tfor (const [, tc] of currentToolCalls) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcontent.push({\n\t\t\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\t\t\tid: tc.id,\n\t\t\t\t\t\t\tname: tc.name,\n\t\t\t\t\t\t\targs: JSON.parse(tc.args || \"{}\"),\n\t\t\t\t\t\t})\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// skip malformed\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tes.finish({\n\t\t\t\t\tcontent,\n\t\t\t\t\tusage,\n\t\t\t\t\tstop: \"aborted\",\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst errorMsg = `Unexpected error: ${e instanceof Error ? e.message : String(e)}`\n\t\t\tes.push({ type: \"text_delta\", text: errorMsg })\n\t\t\tes.finish({\n\t\t\t\tcontent: [{ type: \"text\", text: errorMsg }],\n\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\tstop: \"error\",\n\t\t\t})\n\t\t}\n\t})()\n\n\treturn es\n}\n","import type { AgentEvent, ApiFormat, AssistantResult, StreamFn, StreamOpts } from \"../types.ts\"\nimport { streamGemini } from \"./gemini.ts\"\nimport { streamOpenAI } from \"./openai.ts\"\n\nexport type { AssistantResult, StreamEvent, StreamFn, StreamOpts } from \"../types.ts\"\n\n/*\n * Push-based async event stream.\n *\n * Producers call push()/finish(). Consumers iterate with for-await-of.\n * Backpressure is implicit: push() resolves immediately; the iterator\n * awaits the next value only when the consumer asks for it.\n */\nexport class EventStream<T, R> {\n\t#events: T[] = []\n\t#done = false\n\t#result?: R\n\t#resolve?: (value: T) => void\n\t#doneResolve?: (value: R) => void\n\t#abort = false\n\n\tpush(event: T): void {\n\t\tif (this.#abort) return\n\t\t// If a consumer is already waiting, deliver directly — skip the queue\n\t\tif (this.#resolve) {\n\t\t\tconst resolve = this.#resolve\n\t\t\tthis.#resolve = undefined\n\t\t\tresolve(event)\n\t\t} else {\n\t\t\tthis.#events.push(event)\n\t\t}\n\t}\n\n\tfinish(result: R): void {\n\t\tthis.#done = true\n\t\tthis.#result = result\n\t\t// Wake up a suspended iterator so it can see done=true and exit\n\t\tif (this.#resolve) {\n\t\t\t// undefined is a sentinel — the iterator loop checks done after waking\n\t\t\tthis.#resolve(undefined as T)\n\t\t}\n\t\tif (this.#doneResolve) {\n\t\t\tthis.#doneResolve(result)\n\t\t}\n\t}\n\n\tabort(): void {\n\t\tthis.#abort = true\n\t\tthis.#done = true\n\t\tif (this.#resolve) {\n\t\t\tthis.#resolve(undefined as T)\n\t\t}\n\t\tif (this.#doneResolve) {\n\t\t\tthis.#doneResolve(undefined as R)\n\t\t}\n\t}\n\n\tasync *[Symbol.asyncIterator](): AsyncGenerator<T> {\n\t\twhile (!this.#done || this.#events.length > 0) {\n\t\t\tif (this.#events.length > 0) {\n\t\t\t\tyield this.#events.shift() as T\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (this.#done) break\n\t\t\tconst item = await new Promise<T | undefined>((resolve) => {\n\t\t\t\tthis.#resolve = resolve as (value: T) => void\n\t\t\t})\n\t\t\tif (item !== undefined) {\n\t\t\t\tyield item\n\t\t\t}\n\t\t}\n\t}\n\n\tget result(): R | undefined {\n\t\treturn this.#result\n\t}\n\n\tget isDone(): boolean {\n\t\treturn this.#done\n\t}\n}\n\n// Internal map of registered provider implementations\nconst registry = new Map<ApiFormat, StreamFn>([\n\t[\"openai\", streamOpenAI],\n\t[\"gemini\", streamGemini],\n])\n\nexport function register(api: ApiFormat, fn: StreamFn): void {\n\tregistry.set(api, fn)\n}\n\n// Bridges provider-specific StreamEvents into AgentEvents so the loop and TUI deal with one type.\nexport function stream(opts: StreamOpts): EventStream<AgentEvent, AssistantResult> {\n\tconst fn = registry.get(opts.api)\n\tif (!fn) throw new Error(`No provider registered for API format: ${opts.api}`)\n\n\t// Bridge layer: converts provider-specific StreamEvents into the agent's\n\t// AgentEvent shape, so the loop and TUI only deal with one event type.\n\tconst providerStream = fn(opts)\n\tconst agentStream = new EventStream<AgentEvent, AssistantResult>()\n\n\t;(async () => {\n\t\tfor await (const event of providerStream) {\n\t\t\tif (event.type === \"text_delta\") {\n\t\t\t\tagentStream.push({ type: \"text_delta\", text: event.text ?? \"\" })\n\t\t\t} else if (event.type === \"thinking_delta\") {\n\t\t\t\tagentStream.push({ type: \"thinking_delta\", text: event.text ?? \"\" })\n\t\t\t} else if (event.type === \"tool_call\" && event.call) {\n\t\t\t\tagentStream.push({\n\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\tcall: {\n\t\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\t\tid: event.call.id,\n\t\t\t\t\t\tname: event.call.name,\n\t\t\t\t\t\targs: event.call.args,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t} else if (event.type === \"usage\" && event.usage) {\n\t\t\t\tagentStream.push({ type: \"usage\", usage: event.usage })\n\t\t\t}\n\t\t}\n\n\t\tconst res = providerStream.result\n\t\tif (res) {\n\t\t\tagentStream.finish(res)\n\t\t} else {\n\t\t\t// Fallback for unexpected closure\n\t\t\tagentStream.finish({ content: [], usage: { in: 0, out: 0 }, stop: \"stop\" })\n\t\t}\n\t})()\n\n\treturn agentStream\n}\n\nexport function getRegisteredApis(): ApiFormat[] {\n\treturn [...registry.keys()]\n}\n","import { isAbsolute, relative } from \"node:path\"\nimport chalk from \"chalk\"\nimport type { Msg, TextPart } from \"./types.ts\"\n\n// ~4 chars per token for English/code. Close enough for capacity warnings.\nexport function estimateTokens(messages: Msg[]): number {\n\tlet chars = 0\n\tfor (const msg of messages) {\n\t\tif (typeof msg.content === \"string\") {\n\t\t\tchars += msg.content.length\n\t\t} else if (Array.isArray(msg.content)) {\n\t\t\tfor (const part of msg.content) {\n\t\t\t\tif (part.type === \"text\") chars += part.text.length\n\t\t\t}\n\t\t}\n\t}\n\treturn Math.ceil(chars / 4)\n}\n\nexport function textPart(s: string): TextPart {\n\treturn { type: \"text\", text: s }\n}\n\nexport function getRelativeIfInside(cwd: string, filePath: string): string {\n\tif (filePath === cwd || filePath.startsWith(`${cwd}/`)) {\n\t\treturn relative(cwd, filePath) || \".\"\n\t}\n\treturn filePath\n}\n\nexport function makeRelative(val: string): string {\n\tif (typeof val !== \"string\") return val\n\n\tlet pathVal = val\n\tlet prefix = \"\"\n\tif (val.startsWith(\"file://\")) {\n\t\tpathVal = val.slice(7)\n\t\tprefix = \"file://\"\n\t}\n\n\tif (isAbsolute(pathVal)) {\n\t\tconst cwd = process.cwd()\n\t\treturn prefix + getRelativeIfInside(cwd, pathVal)\n\t}\n\treturn val\n}\n\nexport function formatToolArgs(\n\targs: Record<string, unknown> | undefined,\n\tuseChalk = false,\n): string {\n\tif (!args) return \"\"\n\treturn Object.entries(args)\n\t\t.map(([k, v]) => {\n\t\t\tconst val = typeof v === \"string\" ? makeRelative(v) : JSON.stringify(v)\n\t\t\tconst valStr = val.length > 40 ? `${val.slice(0, 40)}…` : val\n\t\t\tconst keyStr = useChalk ? chalk.dim(`${k}:`) : `${k}:`\n\t\t\treturn `${keyStr} ${valStr}`\n\t\t})\n\t\t.join(\" \")\n}\n\nexport function formatRelativeTime(ts: number): string {\n\tconst now = Date.now()\n\tconst diffMs = now - ts\n\tconst diffSec = Math.floor(diffMs / 1000)\n\tconst diffMin = Math.floor(diffSec / 60)\n\tconst diffHour = Math.floor(diffMin / 60)\n\n\tif (diffSec < 60) {\n\t\treturn \"just now\"\n\t}\n\tif (diffMin < 60) {\n\t\treturn `${diffMin}m ago`\n\t}\n\tif (diffHour < 24) {\n\t\treturn `${diffHour}h ago`\n\t}\n\treturn new Date(ts).toLocaleDateString()\n}\n","/**\n * Core agent loop that orchestrates model interaction and tool execution.\n * Handles turns, tool routing, safety checks, and event streaming.\n */\nimport { EventStream, stream } from \"../provider/stream.ts\"\nimport type {\n\tAgentEvent,\n\tAssistantMsg,\n\tLoopCtx,\n\tLoopOpts,\n\tMsg,\n\tToolCallPart,\n\tToolResultMsg,\n} from \"../types.ts\"\nimport { estimateTokens, textPart } from \"../util.ts\"\n\n// Safety cap so a misbehaving model can't loop forever\nconst MAX_TURNS = 50\n\nconst isToolCall = (c: unknown): c is ToolCallPart =>\n\ttypeof c === \"object\" && c !== null && (c as ToolCallPart).type === \"tool_call\"\n\n/**\n * Start a long-running agent session that yields an EventStream of updates.\n */\nexport function run(\n\tinput: string,\n\tctx: LoopCtx,\n\topts: LoopOpts,\n\tsignal?: AbortSignal,\n): EventStream<AgentEvent, Msg[]> {\n\tconst es = new EventStream<AgentEvent, Msg[]>()\n\tconst out: Msg[] = []\n\tconst maxTurns = opts.maxTurns ?? MAX_TURNS\n\n\tconst userMsg: Msg = { role: \"user\", content: input, ts: Date.now() }\n\tlet activeCtx: LoopCtx = { ...ctx, messages: [...ctx.messages, userMsg] }\n\tout.push(userMsg)\n\n\tconst tick = async () => {\n\t\tes.push({ type: \"start\" })\n\n\t\ttry {\n\t\t\tlet turns = 0\n\t\t\twhile (turns < maxTurns) {\n\t\t\t\tif (signal?.aborted) break\n\n\t\t\t\tturns++\n\t\t\t\tes.push({ type: \"turn\" })\n\n\t\t\t\t// Warn before hitting the hard limit so the caller can compact/summarize\n\t\t\t\tconst approxTokens = estimateTokens(activeCtx.messages)\n\t\t\t\tif (approxTokens > opts.model.contextWindow * 0.9) {\n\t\t\t\t\tes.push({\n\t\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\t\ttext: `[warning] Approaching context limit (~${Math.round(approxTokens / 1000)}k / ${Math.round(opts.model.contextWindow / 1000)}k tokens)`,\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tconst reply = await getReply(activeCtx, opts, es, signal)\n\t\t\t\tout.push(reply)\n\t\t\t\tactiveCtx = { ...activeCtx, messages: [...activeCtx.messages, reply] }\n\t\t\t\tes.push({ type: \"assistant_msg\", msg: reply })\n\n\t\t\t\tif (reply.stop === \"error\" || reply.stop === \"aborted\") {\n\t\t\t\t\tes.push({ type: \"turn_end\", msg: reply, results: [] })\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tconst calls = reply.content.filter(isToolCall)\n\t\t\t\tif (calls.length === 0) {\n\t\t\t\t\tes.push({ type: \"turn_end\", msg: reply, results: [] })\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\t// Execute tool calls\n\t\t\t\tconst results: ToolResultMsg[] = []\n\t\t\t\tfor (const call of calls) {\n\t\t\t\t\tif (signal?.aborted) break\n\n\t\t\t\t\tconst tool = activeCtx.tools.find((t) => t.def.name === call.name)\n\t\t\t\t\tif (!tool) {\n\t\t\t\t\t\tconst errResult: ToolResultMsg = {\n\t\t\t\t\t\t\trole: \"tool_result\",\n\t\t\t\t\t\t\tcallId: call.id,\n\t\t\t\t\t\t\ttool: call.name,\n\t\t\t\t\t\t\tcontent: [textPart(`Unknown tool: ${call.name}`)],\n\t\t\t\t\t\t\tisError: true,\n\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresults.push(errResult)\n\t\t\t\t\t\tactiveCtx = { ...activeCtx, messages: [...activeCtx.messages, errResult] }\n\t\t\t\t\t\tout.push(errResult)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// beforeTool lets callers block dangerous operations (e.g. rm -rf)\n\t\t\t\t\tconst blocked = await opts.beforeTool?.(call, call.args, activeCtx)\n\t\t\t\t\tif (blocked?.block) {\n\t\t\t\t\t\tconst blockResult: ToolResultMsg = {\n\t\t\t\t\t\t\trole: \"tool_result\",\n\t\t\t\t\t\t\tcallId: call.id,\n\t\t\t\t\t\t\ttool: call.name,\n\t\t\t\t\t\t\tcontent: [textPart(blocked.reason ?? \"Blocked\")],\n\t\t\t\t\t\t\tisError: true,\n\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresults.push(blockResult)\n\t\t\t\t\t\tactiveCtx = { ...activeCtx, messages: [...activeCtx.messages, blockResult] }\n\t\t\t\t\t\tout.push(blockResult)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// Execute\n\t\t\t\t\tconst result = await tool.execute(call.args, signal)\n\t\t\t\t\tconst toolMsg: ToolResultMsg = {\n\t\t\t\t\t\trole: \"tool_result\",\n\t\t\t\t\t\tcallId: call.id,\n\t\t\t\t\t\ttool: call.name,\n\t\t\t\t\t\targs: call.args,\n\t\t\t\t\t\tcontent: result.content,\n\t\t\t\t\t\tisError: result.isError,\n\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t}\n\n\t\t\t\t\tresults.push(toolMsg)\n\t\t\t\t\tactiveCtx = { ...activeCtx, messages: [...activeCtx.messages, toolMsg] }\n\t\t\t\t\tout.push(toolMsg)\n\t\t\t\t\tes.push({ type: \"tool_result\", callId: call.id, result: toolMsg, args: call.args })\n\n\t\t\t\t\tawait opts.afterTool?.(call, toolMsg, activeCtx)\n\t\t\t\t}\n\n\t\t\t\tes.push({ type: \"turn_end\", msg: reply, results })\n\t\t\t}\n\n\t\t\tif (turns >= maxTurns) {\n\t\t\t\tes.push({\n\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\ttext: `[max turns reached (${maxTurns})]`,\n\t\t\t\t})\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tif ((e as Error).name === \"AbortError\") {\n\t\t\t\tes.finish(out)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\n\t\tes.finish(out)\n\t}\n\n\ttick()\n\treturn es\n}\n\nasync function getReply(\n\tctx: LoopCtx,\n\topts: LoopOpts,\n\tes: EventStream<AgentEvent, Msg[]>,\n\tsignal?: AbortSignal,\n): Promise<AssistantMsg> {\n\tconst providerStream = stream({\n\t\tapi: opts.api,\n\t\tmodel: opts.model,\n\t\tapiKey: opts.apiKey,\n\t\tbaseUrl: opts.baseUrl,\n\t\tsystem: ctx.system,\n\t\tmessages: ctx.messages,\n\t\ttools: ctx.tools.map((t) => t.def),\n\t\tsignal,\n\t})\n\n\tconst content: AssistantMsg[\"content\"] = []\n\tlet usage = { in: 0, out: 0 }\n\n\t// Accumulate content and proxy events to the outer stream\n\tfor await (const ev of providerStream) {\n\t\tif (ev.type === \"text_delta\" && ev.text) {\n\t\t\tconst last = content[content.length - 1]\n\t\t\tif (last?.type === \"text\") {\n\t\t\t\tlast.text += ev.text\n\t\t\t} else {\n\t\t\t\tcontent.push(textPart(ev.text))\n\t\t\t}\n\t\t\tes.push({ type: \"text_delta\", text: ev.text })\n\t\t} else if (ev.type === \"thinking_delta\" && ev.text) {\n\t\t\tconst last = content[content.length - 1]\n\t\t\tif (last?.type === \"thinking\") {\n\t\t\t\tlast.text += ev.text\n\t\t\t} else {\n\t\t\t\tcontent.push({ type: \"thinking\", text: ev.text })\n\t\t\t}\n\t\t\tes.push({ type: \"thinking_delta\", text: ev.text })\n\t\t} else if (ev.type === \"tool_call\" && ev.call) {\n\t\t\tcontent.push(ev.call)\n\t\t\tes.push({ type: \"tool_call\", call: ev.call })\n\t\t} else if (ev.type === \"usage\" && ev.usage) {\n\t\t\tusage = ev.usage\n\t\t\tes.push({ type: \"usage\", usage })\n\t\t}\n\t}\n\n\tconst rawContent =\n\t\tproviderStream.result?.content && providerStream.result.content.length > 0\n\t\t\t? providerStream.result.content\n\t\t\t: content\n\n\tconst hasTool = rawContent.some((p) => p.type === \"tool_call\")\n\tconst cleanedContent = hasTool\n\t\t? rawContent.filter((p) => p.type !== \"text\" || p.text.trim().length > 0)\n\t\t: rawContent\n\n\tconst res = providerStream.result\n\tif (res) {\n\t\treturn {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: cleanedContent,\n\t\t\tmodel: opts.model.id,\n\t\t\tprovider: opts.model.provider,\n\t\t\tusage: res.usage,\n\t\t\tstop: res.stop,\n\t\t\tts: Date.now(),\n\t\t}\n\t}\n\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: cleanedContent,\n\t\tmodel: opts.model.id,\n\t\tprovider: opts.model.provider,\n\t\tusage,\n\t\tstop: cleanedContent.some((c) => c.type === \"tool_call\") ? \"tool_use\" : \"stop\",\n\t\tts: Date.now(),\n\t}\n}\n","import type { EventStream } from \"../provider/stream.ts\"\nimport type { AgentEvent, ApiFormat, LoopCtx, LoopOpts, Model, Msg, Tool } from \"../types.ts\"\nimport { run } from \"./loop.ts\"\n\nexport class Agent {\n\t#api: ApiFormat\n\t#model: Model\n\t#system: string\n\t#messages: Msg[] = []\n\t#tools: Tool[]\n\t#apiKey: string\n\t#baseUrl: string\n\n\tconstructor(opts: {\n\t\tapi: ApiFormat\n\t\tmodel: Model\n\t\tapiKey: string\n\t\tbaseUrl: string\n\t\tsystem: string\n\t\ttools: Tool[]\n\t\tmessages?: Msg[]\n\t}) {\n\t\tthis.#api = opts.api\n\t\tthis.#model = opts.model\n\t\tthis.#apiKey = opts.apiKey\n\t\tthis.#baseUrl = opts.baseUrl\n\t\tthis.#system = opts.system\n\t\tthis.#tools = opts.tools\n\t\tthis.#messages = opts.messages ?? []\n\t}\n\n\tget model(): Model {\n\t\treturn this.#model\n\t}\n\n\tget messages(): Msg[] {\n\t\treturn this.#messages\n\t}\n\n\tget tools(): Tool[] {\n\t\treturn this.#tools\n\t}\n\n\tget apiKey(): string {\n\t\treturn this.#apiKey\n\t}\n\n\tget baseUrl(): string {\n\t\treturn this.#baseUrl\n\t}\n\n\tupdateConfig(opts: { api: ApiFormat; model: Model; apiKey: string; baseUrl: string }): void {\n\t\tthis.#api = opts.api\n\t\tthis.#model = opts.model\n\t\tthis.#apiKey = opts.apiKey\n\t\tthis.#baseUrl = opts.baseUrl\n\t}\n\n\tsetTools(tools: Tool[]): void {\n\t\tthis.#tools = tools\n\t}\n\n\tsetMessages(msgs: Msg[]): void {\n\t\tthis.#messages = msgs\n\t}\n\n\tsetModel(model: Model): void {\n\t\tthis.#model = model\n\t}\n\n\tprompt(input: string, signal?: AbortSignal): EventStream<AgentEvent, Msg[]> {\n\t\tconst ctx: LoopCtx = {\n\t\t\tsystem: this.#system,\n\t\t\tmessages: this.#messages,\n\t\t\ttools: this.#tools,\n\t\t}\n\n\t\tconst opts: LoopOpts = {\n\t\t\tapi: this.#api,\n\t\t\tmodel: this.#model,\n\t\t\tapiKey: this.#apiKey,\n\t\t\tbaseUrl: this.#baseUrl,\n\t\t}\n\n\t\treturn run(input, ctx, opts, signal)\n\t}\n}\n","/**\n * Logic for constructing the foundational system instruction given the environment and tools.\n */\n\nimport os from \"node:os\"\nimport type { Tool } from \"../types.ts\"\n\nexport function buildSystemPrompt(cwd: string, tools: Tool[]): string {\n\tconst toolList = tools.map((t) => `- ${t.def.name}: ${t.def.description}`).join(\"\\n\")\n\tconst platform = os.platform()\n\tconst arch = os.arch()\n\tconst release = os.release()\n\tconst shell = process.env.SHELL || \"unknown\"\n\n\treturn `You are Nova, an expert coding assistant. Help users with coding tasks using the tools available.\n\nFormat 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.\n\n# Tools\n\n${toolList}\n\n# Environment\n\n- Working directory: ${cwd}\n- Operating System: ${platform} (${release})\n- Architecture: ${arch}\n- Shell: ${shell}\n- Date: ${new Date().toISOString().split(\"T\")[0]}\n\n# Guidelines\n\n- Use tools to fulfill requests. Do not fabricate file contents.\n- Explain what you are doing and why before each tool call.\n- Use the \"bash\" tool for ls, git, and other shell operations.\n- Always read a file before editing it.\n- Prefer edit over write for existing files.\n- Run relevant tests after making changes.\n- If a command fails, read the error carefully before retrying.\n- For multi-file changes, plan first, then execute.\n- When done, briefly summarize what was changed.\n- Be concise and direct.\n\n# Safety\n\n- Never delete files outside the working directory.\n- Never run destructive commands unless the user explicitly confirms.\n- If unsure, ask for clarification.\n- Never expose API keys, tokens, or secrets.`\n}\n","import type { SessionStore } from \"../session/store.ts\"\nimport { formatRelativeTime } from \"../util.ts\"\n\nexport async function handleSessionCommand(\n\tstore: SessionStore,\n\targs: string[],\n\topts: { limit?: number; all?: boolean } = {},\n): Promise<void> {\n\tconst [subcommand, id] = args\n\n\tif (subcommand === \"list\" || subcommand === \"ls\") {\n\t\tconst limit = opts.limit ?? 10\n\t\tconst sessions = await store.list(limit)\n\t\tif (sessions.length === 0) {\n\t\t\tconsole.log(\"No sessions found.\")\n\t\t\treturn\n\t\t}\n\n\t\tconsole.log(\"ID\".padEnd(25), \"TITLE / UPDATED\")\n\t\tconsole.log(\"-\".repeat(60))\n\t\tfor (const s of sessions) {\n\t\t\tconst relTime = formatRelativeTime(s.updated)\n\t\t\tconst titleOrUpdated = s.title ? `\"${s.title}\" (${relTime})` : relTime\n\t\t\tconsole.log(s.id.padEnd(25), titleOrUpdated)\n\t\t}\n\t\treturn\n\t}\n\n\tif (subcommand === \"delete\" || subcommand === \"rm\") {\n\t\tif (opts.all) {\n\t\t\tawait store.deleteAll()\n\t\t\tconsole.log(\"All sessions deleted.\")\n\t\t\treturn\n\t\t}\n\n\t\tif (!id) {\n\t\t\tconsole.error(\"Usage: nova --session rm <id> or --session rm --all\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t\tconst success = await store.delete(id)\n\t\tif (success) {\n\t\t\tconsole.log(`Deleted session: ${id}`)\n\t\t} else {\n\t\t\tconsole.error(`Session not found: ${id}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\t\treturn\n\t}\n\n\tconsole.error(\"Unknown session subcommand.\")\n\tprocess.exit(1)\n}\n","import type { Model, ProviderDef } from \"../types.ts\"\n\nexport const PROVIDERS: ProviderDef[] = [\n\t{\n\t\tid: \"glm\",\n\t\tname: \"GLM (Z.AI)\",\n\t\tapi: \"openai\",\n\t\tbaseUrl: \"https://api.z.ai/api/coding/paas/v4\",\n\t\tenvKey: \"GLM_API_KEY\",\n\t},\n\t{\n\t\tid: \"gemini\",\n\t\tname: \"Gemini (Google)\",\n\t\tapi: \"gemini\",\n\t\tbaseUrl: \"https://generativelanguage.googleapis.com\",\n\t\tenvKey: \"GEMINI_API_KEY\",\n\t},\n\t{\n\t\tid: \"deepseek\",\n\t\tname: \"DeepSeek\",\n\t\tapi: \"openai\",\n\t\tbaseUrl: \"https://api.deepseek.com\",\n\t\tenvKey: \"DEEPSEEK_API_KEY\",\n\t},\n\t{\n\t\tid: \"openai\",\n\t\tname: \"OpenAI\",\n\t\tapi: \"openai\",\n\t\tbaseUrl: \"https://api.openai.com/v1\",\n\t\tenvKey: \"OPENAI_API_KEY\",\n\t},\n]\n\nexport const MODELS: Model[] = [\n\t// GLM\n\t{\n\t\tid: \"glm-5.1\",\n\t\tname: \"GLM-5.1\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-5\",\n\t\tname: \"GLM-5\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-5-turbo\",\n\t\tname: \"GLM-5 Turbo\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-4.7\",\n\t\tname: \"GLM-4.7\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-4.7-flash\",\n\t\tname: \"GLM-4.7 Flash (Free)\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-4.5-flash\",\n\t\tname: \"GLM-4.5 Flash (Free)\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t// Gemini\n\t{\n\t\tid: \"gemini-3.5-flash\",\n\t\tname: \"Gemini 3.5 Flash\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3.1-pro-preview\",\n\t\tname: \"Gemini 3.1 Pro Preview\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 2_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3.1-pro-preview-customtools\",\n\t\tname: \"Gemini 3.1 Pro (Custom Tools)\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 2_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3.1-flash-lite\",\n\t\tname: \"Gemini 3.1 Flash-Lite\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3.1-flash-lite-preview\",\n\t\tname: \"Gemini 3.1 Flash-Lite Preview\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3-flash-preview\",\n\t\tname: \"Gemini 3 Flash Preview\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-2.5-pro\",\n\t\tname: \"Gemini 2.5 Pro\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 2_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"gemini-2.5-flash\",\n\t\tname: \"Gemini 2.5 Flash\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"gemini-2.5-flash-lite\",\n\t\tname: \"Gemini 2.5 Flash-Lite\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"gemini-2.5-computer-use-preview-10-2025\",\n\t\tname: \"Gemini 2.5 Computer Use\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: false,\n\t},\n\t// DeepSeek\n\t{\n\t\tid: \"deepseek-chat\",\n\t\tname: \"DeepSeek V3\",\n\t\tprovider: \"deepseek\",\n\t\tcontextWindow: 64_000,\n\t\tmaxTokens: 8_192,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"deepseek-reasoner\",\n\t\tname: \"DeepSeek R1\",\n\t\tprovider: \"deepseek\",\n\t\tcontextWindow: 64_000,\n\t\tmaxTokens: 8_192,\n\t\tsupportsThinking: true,\n\t},\n\t// OpenAI\n\t{\n\t\tid: \"gpt-4o\",\n\t\tname: \"GPT-4o\",\n\t\tprovider: \"openai\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 16_384,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"o4-mini\",\n\t\tname: \"o4-mini\",\n\t\tprovider: \"openai\",\n\t\tcontextWindow: 200_000,\n\t\tmaxTokens: 100_000,\n\t\tsupportsThinking: true,\n\t},\n]\n\nexport function getProvider(id: string): ProviderDef | undefined {\n\treturn PROVIDERS.find((p) => p.id === id)\n}\n\nexport function getModelsForProvider(providerId: string): Model[] {\n\treturn MODELS.filter((m) => m.provider === providerId)\n}\n","import { chmod, mkdir, readFile, stat, writeFile } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport type { NovaAuth, NovaConfig } from \"../types.ts\"\n\nconst NOVA_DIR = () => join(process.env.HOME ?? \"~\", \".novacode\")\nconst CONFIG_PATH = () => join(NOVA_DIR(), \"config.json\")\nconst AUTH_PATH = () => join(NOVA_DIR(), \"auth.json\")\n\nconst defaultConfig: NovaConfig = {\n\tprovider: \"\",\n\tmodel: \"\",\n}\n\nconst defaultAuth: NovaAuth = {\n\tapiKeys: {},\n}\n\nexport async function configExists(): Promise<boolean> {\n\ttry {\n\t\tawait stat(CONFIG_PATH())\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\nexport async function loadConfig(): Promise<NovaConfig> {\n\ttry {\n\t\tconst raw = JSON.parse(await readFile(CONFIG_PATH(), \"utf-8\"))\n\t\treturn { ...defaultConfig, ...raw }\n\t} catch {\n\t\treturn { ...defaultConfig }\n\t}\n}\n\nexport async function loadAuth(): Promise<NovaAuth> {\n\ttry {\n\t\tconst raw = JSON.parse(await readFile(AUTH_PATH(), \"utf-8\"))\n\t\treturn { ...defaultAuth, ...raw }\n\t} catch {\n\t\treturn { ...defaultAuth }\n\t}\n}\n\nasync function ensureDir(): Promise<void> {\n\tawait mkdir(NOVA_DIR(), { recursive: true })\n}\n\nexport async function saveConfig(config: NovaConfig): Promise<void> {\n\tawait ensureDir()\n\tawait writeFile(CONFIG_PATH(), JSON.stringify(config, null, 2))\n}\n\nexport async function saveAuth(auth: NovaAuth): Promise<void> {\n\tawait ensureDir()\n\tawait writeFile(AUTH_PATH(), JSON.stringify(auth, null, 2))\n\ttry {\n\t\tawait chmod(AUTH_PATH(), 0o600)\n\t} catch {\n\t\t// chmod may fail on some platforms, non-fatal\n\t}\n}\n\nexport function getNovaDir(): string {\n\treturn NOVA_DIR()\n}\n","import { Box, render, Text, useInput } from \"ink\"\nimport { useState } from \"react\"\n\ninterface SelectOption {\n\tvalue: string\n\tlabel: string\n\thint?: string\n}\n\nexport function SelectPrompt({\n\tmessage,\n\toptions,\n\theader,\n\tonSelect,\n}: {\n\tmessage: string\n\toptions: SelectOption[]\n\theader?: string\n\tonSelect: (value: string | null) => void\n}) {\n\tconst [idx, setIdx] = useState(0)\n\n\tuseInput((_, key) => {\n\t\tif (key.escape) {\n\t\t\tonSelect(null)\n\t\t\treturn\n\t\t}\n\t\tif (key.upArrow) {\n\t\t\tsetIdx((i) => (i - 1 + options.length) % options.length)\n\t\t\treturn\n\t\t}\n\t\tif (key.downArrow) {\n\t\t\tsetIdx((i) => (i + 1) % options.length)\n\t\t\treturn\n\t\t}\n\t\tif (key.return) {\n\t\t\tonSelect(options[idx]?.value ?? null)\n\t\t}\n\t})\n\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingX={1}>\n\t\t\t{header && (\n\t\t\t\t<Box marginBottom={1}>\n\t\t\t\t\t<Text>{header}</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t\t<Box marginBottom={1}>\n\t\t\t\t<Text bold>{message}</Text>\n\t\t\t</Box>\n\t\t\t{options.map((opt, i) => (\n\t\t\t\t<Box key={opt.value}>\n\t\t\t\t\t<Text color={i === idx ? \"green\" : undefined}>\n\t\t\t\t\t\t{i === idx ? \"❯ \" : \" \"}\n\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t</Text>\n\t\t\t\t\t{opt.hint && i === idx && <Text dimColor> {opt.hint}</Text>}\n\t\t\t\t</Box>\n\t\t\t))}\n\t\t\t<Box marginTop={1}>\n\t\t\t\t<Text dimColor>↑↓ navigate · Enter select · Esc cancel</Text>\n\t\t\t</Box>\n\t\t</Box>\n\t)\n}\n\nexport function PasswordPrompt({\n\tmessage,\n\tvalidate,\n\tonSubmit,\n}: {\n\tmessage: string\n\tvalidate?: (v: string) => string | undefined\n\tonSubmit: (value: string | null) => void\n}) {\n\tconst [value, setValue] = useState(\"\")\n\tconst [error, setError] = useState(\"\")\n\n\tuseInput((ch, key) => {\n\t\tif (key.escape) {\n\t\t\tonSubmit(null)\n\t\t\treturn\n\t\t}\n\t\tif (key.return) {\n\t\t\tconst err = validate?.(value)\n\t\t\tif (err) {\n\t\t\t\tsetError(err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tonSubmit(value)\n\t\t\treturn\n\t\t}\n\t\tif (key.backspace || key.delete) {\n\t\t\tsetValue((v) => v.slice(0, -1))\n\t\t\tsetError(\"\")\n\t\t\treturn\n\t\t}\n\t\tif (ch) {\n\t\t\tsetValue((v) => v + ch)\n\t\t\tsetError(\"\")\n\t\t}\n\t})\n\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingX={1}>\n\t\t\t<Box marginBottom={1}>\n\t\t\t\t<Text bold>{message}</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Text color=\"green\">│ </Text>\n\t\t\t\t<Text dimColor>{\"*\".repeat(value.length)}</Text>\n\t\t\t\t<Text color=\"green\">│</Text>\n\t\t\t</Box>\n\t\t\t{error && (\n\t\t\t\t<Box>\n\t\t\t\t\t<Text color=\"red\">{error}</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t\t<Box marginTop={1}>\n\t\t\t\t<Text dimColor>Enter submit · Esc cancel</Text>\n\t\t\t</Box>\n\t\t</Box>\n\t)\n}\n\nexport function ConfirmPrompt({\n\tmessage,\n\tonConfirm,\n}: {\n\tmessage: string\n\tonConfirm: (value: boolean | null) => void\n}) {\n\tconst [yes, setYes] = useState(true)\n\n\tuseInput((_, key) => {\n\t\tif (key.escape) {\n\t\t\tonConfirm(null)\n\t\t\treturn\n\t\t}\n\t\tif (key.leftArrow || key.rightArrow || key.tab) {\n\t\t\tsetYes((y) => !y)\n\t\t\treturn\n\t\t}\n\t\tif (key.return) {\n\t\t\tonConfirm(yes)\n\t\t}\n\t})\n\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingX={1}>\n\t\t\t<Box marginBottom={1}>\n\t\t\t\t<Text bold>{message}</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Text color={yes ? \"green\" : undefined}>{yes ? \"❯ \" : \" \"}Yes</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Text color={!yes ? \"red\" : undefined}>{!yes ? \"❯ \" : \" \"}No</Text>\n\t\t\t</Box>\n\t\t\t<Box marginTop={1}>\n\t\t\t\t<Text dimColor>←→ toggle · Enter confirm · Esc cancel</Text>\n\t\t\t</Box>\n\t\t</Box>\n\t)\n}\n\n// Standalone wrappers for use outside the main TUI (e.g. onboarding)\n\nexport function standaloneSelect(\n\tmessage: string,\n\toptions: SelectOption[],\n\theader?: string,\n): Promise<string | null> {\n\treturn new Promise((resolve) => {\n\t\tconst { unmount } = render(\n\t\t\t<SelectPrompt\n\t\t\t\tmessage={message}\n\t\t\t\toptions={options}\n\t\t\t\theader={header}\n\t\t\t\tonSelect={(v) => {\n\t\t\t\t\tunmount()\n\t\t\t\t\tresolve(v)\n\t\t\t\t}}\n\t\t\t/>,\n\t\t)\n\t})\n}\n\nexport function standalonePassword(\n\tmessage: string,\n\tvalidate?: (v: string) => string | undefined,\n): Promise<string | null> {\n\treturn new Promise((resolve) => {\n\t\tconst { unmount } = render(\n\t\t\t<PasswordPrompt\n\t\t\t\tmessage={message}\n\t\t\t\tvalidate={validate}\n\t\t\t\tonSubmit={(v) => {\n\t\t\t\t\tunmount()\n\t\t\t\t\tresolve(v)\n\t\t\t\t}}\n\t\t\t/>,\n\t\t)\n\t})\n}\n","import chalk from \"chalk\"\nimport { getModelsForProvider, getProvider, PROVIDERS } from \"../config/providers.ts\"\nimport { saveAuth, saveConfig } from \"../config/store.ts\"\nimport { standalonePassword, standaloneSelect } from \"../tui/prompts.tsx\"\nimport type { NovaConfig } from \"../types.ts\"\n\nexport async function runOnboarding(): Promise<NovaConfig> {\n\tconsole.log(chalk.bold.cyan(\"\\n⚡ Nova — your coding companion\\n\"))\n\n\tconst providerId = await standaloneSelect(\n\t\t\"Pick a provider\",\n\t\tPROVIDERS.map((p) => ({ value: p.id, label: p.name })),\n\t)\n\tif (!providerId) {\n\t\tconsole.log(chalk.dim(\"Cancelled\"))\n\t\tprocess.exit(0)\n\t}\n\n\tconst provider = getProvider(providerId)\n\tif (!provider) {\n\t\tconsole.log(chalk.red(\"Unknown provider\"))\n\t\tprocess.exit(1)\n\t}\n\n\tconst apiKey = await standalonePassword(`Enter ${provider.name} API key`)\n\tif (!apiKey) {\n\t\tconsole.log(chalk.dim(\"Cancelled\"))\n\t\tprocess.exit(0)\n\t}\n\n\tconst models = getModelsForProvider(providerId)\n\tconst modelId = await standaloneSelect(\n\t\t\"Pick a default model\",\n\t\tmodels.map((m) => ({\n\t\t\tvalue: m.id,\n\t\t\tlabel: `${m.name} (${(m.contextWindow / 1000).toFixed(0)}k ctx)`,\n\t\t})),\n\t)\n\tif (!modelId) {\n\t\tconsole.log(chalk.dim(\"Cancelled\"))\n\t\tprocess.exit(0)\n\t}\n\n\tconst config: NovaConfig = {\n\t\tprovider: providerId,\n\t\tmodel: modelId,\n\t}\n\n\tawait saveConfig(config)\n\tawait saveAuth({ apiKeys: { [providerId]: apiKey } })\n\n\tconsole.log(chalk.green(\"\\n✓ Ready. Type your prompt or /help for commands\\n\"))\n\treturn config\n}\n","import { appendFile, mkdir, readdir, readFile, rm, writeFile } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport type { Compaction, Msg, Session } from \"../types.ts\"\n\nfunction generateId(): string {\n\treturn `${Date.now().toString(36)}-${crypto.randomUUID().slice(0, 8)}`\n}\n\nexport class SessionStore {\n\t#sessionsDir: string\n\n\tconstructor(sessionsDir: string) {\n\t\tthis.#sessionsDir = sessionsDir\n\t}\n\n\t#sessionDir(id: string): string {\n\t\treturn join(this.#sessionsDir, id)\n\t}\n\n\t#metadataPath(id: string): string {\n\t\treturn join(this.#sessionDir(id), \"metadata.json\")\n\t}\n\n\t#messagesPath(id: string): string {\n\t\treturn join(this.#sessionDir(id), \"messages.jsonl\")\n\t}\n\n\t#compactionPath(id: string): string {\n\t\treturn join(this.#sessionDir(id), \"compaction.json\")\n\t}\n\n\tasync create(cwd: string, model: string, provider: string): Promise<Session> {\n\t\tconst id = generateId()\n\t\tconst now = Date.now()\n\t\tconst session: Session = {\n\t\t\tid,\n\t\t\tcwd,\n\t\t\tmodel,\n\t\t\tprovider,\n\t\t\ttitle: null,\n\t\t\tcreated: now,\n\t\t\tupdated: now,\n\t\t}\n\n\t\tawait mkdir(this.#sessionDir(id), { recursive: true })\n\t\tawait writeFile(this.#metadataPath(id), JSON.stringify(session, null, 2))\n\t\treturn session\n\t}\n\n\tasync get(id: string): Promise<Session | null> {\n\t\ttry {\n\t\t\tconst data = await readFile(this.#metadataPath(id), \"utf-8\")\n\t\t\treturn JSON.parse(data) as Session\n\t\t} catch {\n\t\t\treturn null\n\t\t}\n\t}\n\n\tasync list(limit = 10): Promise<Session[]> {\n\t\ttry {\n\t\t\tconst entries = await readdir(this.#sessionsDir, { withFileTypes: true })\n\t\t\tconst dirNames = entries\n\t\t\t\t.filter((e) => e.isDirectory())\n\t\t\t\t.map((e) => e.name)\n\t\t\t\t.sort((a, b) => b.localeCompare(a))\n\n\t\t\tconst candidates = dirNames.slice(0, Math.max(limit * 2, 50))\n\t\t\tconst sessions: Session[] = []\n\t\t\tfor (const name of candidates) {\n\t\t\t\tconst s = await this.get(name)\n\t\t\t\tif (s) sessions.push(s)\n\t\t\t}\n\t\t\tsessions.sort((a, b) => b.updated - a.updated)\n\t\t\treturn sessions.slice(0, limit)\n\t\t} catch {\n\t\t\treturn []\n\t\t}\n\t}\n\n\tasync latest(): Promise<Session | null> {\n\t\tconst sessions = await this.list(1)\n\t\treturn sessions[0] ?? null\n\t}\n\n\tasync delete(id: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait rm(this.#sessionDir(id), { recursive: true, force: true })\n\t\t\treturn true\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tasync deleteAll(): Promise<void> {\n\t\ttry {\n\t\t\tawait rm(this.#sessionsDir, { recursive: true, force: true })\n\t\t\tawait mkdir(this.#sessionsDir, { recursive: true })\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tasync append(sessionId: string, msg: Msg): Promise<void> {\n\t\tconst session = await this.get(sessionId)\n\t\tif (!session) return\n\n\t\tsession.updated = Date.now()\n\t\tawait writeFile(this.#metadataPath(sessionId), JSON.stringify(session, null, 2))\n\n\t\tconst line = `${JSON.stringify(msg)}\\n`\n\t\tawait appendFile(this.#messagesPath(sessionId), line)\n\t}\n\n\tasync messages(sessionId: string): Promise<Msg[]> {\n\t\ttry {\n\t\t\tconst data = await readFile(this.#messagesPath(sessionId), \"utf-8\")\n\t\t\tconst lines = data.split(\"\\n\").filter((l) => l.trim().length > 0)\n\t\t\treturn lines.map((l) => JSON.parse(l) as Msg)\n\t\t} catch {\n\t\t\treturn []\n\t\t}\n\t}\n\n\tasync messageCount(sessionId: string): Promise<number> {\n\t\ttry {\n\t\t\tconst data = await readFile(this.#messagesPath(sessionId), \"utf-8\")\n\t\t\tconst lines = data.split(\"\\n\").filter((l) => l.trim().length > 0)\n\t\t\treturn lines.length\n\t\t} catch {\n\t\t\treturn 0\n\t\t}\n\t}\n\n\tasync setTitle(sessionId: string, title: string): Promise<void> {\n\t\tconst session = await this.get(sessionId)\n\t\tif (!session) return\n\t\tsession.title = title\n\t\tsession.updated = Date.now()\n\t\tawait writeFile(this.#metadataPath(sessionId), JSON.stringify(session, null, 2))\n\t}\n\n\tasync saveCompaction(\n\t\tsessionId: string,\n\t\tsummary: string,\n\t\tfilesRead: string[],\n\t\tfilesWrote: string[],\n\t\tseqBefore: number,\n\t): Promise<void> {\n\t\tconst compaction: Compaction = {\n\t\t\tsummary,\n\t\t\tseqBefore,\n\t\t\tfilesRead,\n\t\t\tfilesWrote,\n\t\t\tts: Date.now(),\n\t\t}\n\t\tawait writeFile(this.#compactionPath(sessionId), JSON.stringify(compaction, null, 2))\n\t}\n\n\tasync getLatestCompaction(sessionId: string): Promise<Compaction | null> {\n\t\ttry {\n\t\t\tconst data = await readFile(this.#compactionPath(sessionId), \"utf-8\")\n\t\t\treturn JSON.parse(data) as Compaction\n\t\t} catch {\n\t\t\treturn null\n\t\t}\n\t}\n\n\tasync truncateBeforeSeq(sessionId: string, seq: number): Promise<void> {\n\t\tconst msgs = await this.messages(sessionId)\n\t\tconst remaining = msgs.slice(seq)\n\t\tconst data =\n\t\t\tremaining.map((m) => JSON.stringify(m)).join(\"\\n\") + (remaining.length > 0 ? \"\\n\" : \"\")\n\t\tawait writeFile(this.#messagesPath(sessionId), data)\n\t}\n\n\tasync prune(limit = 10): Promise<void> {\n\t\ttry {\n\t\t\tconst entries = await readdir(this.#sessionsDir, { withFileTypes: true })\n\t\t\tconst dirNames = entries\n\t\t\t\t.filter((e) => e.isDirectory())\n\t\t\t\t.map((e) => e.name)\n\t\t\t\t.sort((a, b) => b.localeCompare(a))\n\n\t\t\tconst targets = limit > 0 ? dirNames.slice(0, limit) : dirNames\n\t\t\tfor (const name of targets) {\n\t\t\t\tconst count = await this.messageCount(name)\n\t\t\t\tif (count === 0) {\n\t\t\t\t\tawait this.delete(name)\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tclose(): void {\n\t\t// no-op\n\t}\n}\n\nlet _store: SessionStore | null = null\n\nexport async function getSessionStore(dir?: string): Promise<SessionStore> {\n\tif (_store) return _store\n\tconst sessionsPath = join(dir ?? join(process.env.HOME ?? \"~\", \".novacode\"), \"sessions\")\n\tawait mkdir(sessionsPath, { recursive: true })\n\t_store = new SessionStore(sessionsPath)\n\treturn _store\n}\n","/**\n * Filesystem tools for reading, writing, and editing files.\n * Includes safety checks to prevent path traversal.\n */\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\"\nimport { dirname, extname, resolve } from \"node:path\"\nimport type { Tool, ToolResult } from \"../types.ts\"\nimport { getRelativeIfInside, textPart } from \"../util.ts\"\n\n// Extensions we return as base64 images instead of text\nconst IMAGES = new Set([\".jpg\", \".jpeg\", \".png\", \".gif\", \".webp\"])\n\nfunction safePath(cwd: string, p: string): string {\n\tconst abs = resolve(cwd, p)\n\tif (abs !== cwd && !abs.startsWith(`${cwd}/`)) {\n\t\tthrow new Error(`Path outside project: ${p}`)\n\t}\n\treturn abs\n}\n\nexport function readTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"read\",\n\t\t\tdescription:\n\t\t\t\t\"Read file contents. Supports text and images (jpg, png, gif, webp). Text output is truncated to 2000 lines.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Path to file (relative or absolute)\" },\n\t\t\t\t\toffset: { type: \"number\", description: \"Start line (1-based, default 1)\" },\n\t\t\t\t\tlimit: { type: \"number\", description: \"Max lines to read (default 2000)\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"path\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst filePath = safePath(cwd, args.path as string)\n\t\t\t\t// Return images as base64 so the LLM can process them visually\n\t\t\t\tconst ext = extname(filePath).toLowerCase()\n\t\t\t\tif (IMAGES.has(ext)) {\n\t\t\t\t\tconst buf = await readFile(filePath)\n\t\t\t\t\tconst b64 = buf.toString(\"base64\")\n\t\t\t\t\tconst mime = ext === \".jpg\" ? \"image/jpeg\" : `image/${ext.slice(1)}`\n\t\t\t\t\treturn { content: [{ type: \"image\", data: b64, mime }], isError: false }\n\t\t\t\t}\n\n\t\t\t\tconst content = await readFile(filePath, \"utf-8\")\n\t\t\t\tconst lines = content.split(\"\\n\")\n\t\t\t\tconst offset = Math.max(0, (Number(args.offset ?? 1) || 1) - 1)\n\t\t\t\tconst limit = Number(args.limit ?? 2000) || 2000\n\t\t\t\tconst slice = lines.slice(offset, offset + limit)\n\t\t\t\tconst truncated = offset + limit < lines.length\n\n\t\t\t\tconst out = slice.join(\"\\n\")\n\t\t\t\tconst suffix = truncated ? `\\n…${lines.length - offset - limit} more lines` : \"\"\n\n\t\t\t\treturn { content: [textPart(out + suffix)], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error reading file: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\nexport function writeTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"write\",\n\t\t\tdescription: \"Write content to a file. Creates the file and parent directories if needed.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Path to file\" },\n\t\t\t\t\tcontent: { type: \"string\", description: \"Content to write\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"path\", \"content\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst filePath = safePath(cwd, args.path as string)\n\t\t\t\tconst content = args.content as string\n\t\t\t\tawait mkdir(dirname(filePath), { recursive: true })\n\t\t\t\tawait writeFile(filePath, content)\n\t\t\t\tconst relPath = getRelativeIfInside(cwd, filePath)\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Wrote ${content.length} bytes → ${relPath}`)],\n\t\t\t\t\tisError: false,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error writing file: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n// Requires oldText to be unique to avoid ambiguous replacements.\nexport function editTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"edit\",\n\t\t\tdescription:\n\t\t\t\t\"Edit a file using exact text replacement. Each edit's oldText must be unique in the file.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Path to file\" },\n\t\t\t\t\tedits: {\n\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Array of {oldText, newText} replacements. oldText must be unique. Non-overlapping.\",\n\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\toldText: { type: \"string\", description: \"Exact text to find (must be unique)\" },\n\t\t\t\t\t\t\t\tnewText: { type: \"string\", description: \"Replacement text\" },\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\trequired: [\"oldText\", \"newText\"],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\trequired: [\"path\", \"edits\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst filePath = safePath(cwd, args.path as string)\n\t\t\t\tlet content: string\n\t\t\t\ttry {\n\t\t\t\t\tcontent = await readFile(filePath, \"utf-8\")\n\t\t\t\t} catch {\n\t\t\t\t\treturn { content: [textPart(`File not found: ${args.path}`)], isError: true }\n\t\t\t\t}\n\t\t\t\tconst edits = args.edits as Array<{ oldText: string; newText: string }>\n\n\t\t\t\t// Validate all edits before applying any — avoids partial writes on bad input\n\t\t\t\tfor (const edit of edits) {\n\t\t\t\t\tconst count = content.split(edit.oldText).length - 1\n\t\t\t\t\tif (count === 0) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [textPart(`oldText not found: \"${edit.oldText.slice(0, 80)}…\"`)],\n\t\t\t\t\t\t\tisError: true,\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Ambiguous match would replace the wrong occurrence\n\t\t\t\t\tif (count > 1) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\ttextPart(\n\t\t\t\t\t\t\t\t\t`oldText found ${count} times — add surrounding context to make it unique: \"${edit.oldText.slice(0, 60)}…\"`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tisError: true,\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Apply edits sequentially\n\t\t\t\tfor (const edit of edits) {\n\t\t\t\t\tcontent = content.replace(edit.oldText, edit.newText)\n\t\t\t\t}\n\n\t\t\t\tawait writeFile(filePath, content)\n\t\t\t\tconst relPath = getRelativeIfInside(cwd, filePath)\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\ttextPart(\n\t\t\t\t\t\t\t`Edited ${relPath} (${edits.length} replacement${edits.length > 1 ? \"s\" : \"\"})`,\n\t\t\t\t\t\t),\n\t\t\t\t\t],\n\t\t\t\t\tisError: false,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error editing file: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","/**\n * Git tools for executing safe repository operations programmatically.\n */\nimport { spawn } from \"node:child_process\"\nimport type { Tool, ToolResult } from \"../types.ts\"\nimport { textPart } from \"../util.ts\"\n\nexport function gitTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"git\",\n\t\t\tdescription:\n\t\t\t\t\"Execute safe, non-interactive git commands (status, diff, log, add, commit) in the repository.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\taction: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\tenum: [\"status\", \"diff\", \"log\", \"add\", \"commit\"],\n\t\t\t\t\t\tdescription: \"The git action to execute\",\n\t\t\t\t\t},\n\t\t\t\t\targs: {\n\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\tdescription: \"Optional additional arguments or file paths for the git action\",\n\t\t\t\t\t\titems: { type: \"string\" },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\trequired: [\"action\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\tconst action = args.action as string\n\t\t\tconst extraArgs = (args.args as string[]) || []\n\n\t\t\tconst allowed = new Set([\"status\", \"diff\", \"log\", \"add\", \"commit\"])\n\t\t\tif (!allowed.has(action)) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: Git action '${action}' is not supported.`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst cmd = [\"git\", action, ...extraArgs]\n\t\t\t\tconst proc = spawn(cmd[0]!, cmd.slice(1), {\n\t\t\t\t\tcwd,\n\t\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\t\tenv: { ...process.env, PAGER: \"cat\" },\n\t\t\t\t})\n\n\t\t\t\tlet stdout = \"\"\n\t\t\t\tlet stderr = \"\"\n\t\t\t\tproc.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tstdout += chunk.toString()\n\t\t\t\t})\n\t\t\t\tproc.stderr.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tstderr += chunk.toString()\n\t\t\t\t})\n\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tproc.kill(\"SIGKILL\")\n\t\t\t\t\tproc.stdout.destroy()\n\t\t\t\t\tproc.stderr.destroy()\n\t\t\t\t}\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true })\n\n\t\t\t\tlet exitCode: number\n\t\t\t\ttry {\n\t\t\t\t\texitCode = await new Promise<number>((resolve, reject) => {\n\t\t\t\t\t\tproc.on(\"error\", reject)\n\t\t\t\t\t\tproc.on(\"close\", (code) => resolve(code ?? -1))\n\t\t\t\t\t})\n\t\t\t\t} finally {\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort)\n\t\t\t\t}\n\n\t\t\t\t// Prevent context window blowout by truncating very large outputs\n\t\t\t\tconst MAX = 50_000\n\t\t\t\tlet out = \"\"\n\t\t\t\tif (stdout) out += stdout.slice(0, MAX)\n\t\t\t\tif (stderr) {\n\t\t\t\t\tif (out) out += \"\\n\"\n\t\t\t\t\tout += stderr.slice(0, MAX - out.length)\n\t\t\t\t}\n\t\t\t\tif (out.length >= MAX) out += \"\\n…truncated\"\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(out || \"(no output)\")],\n\t\t\t\t\tisError: exitCode !== 0,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error running git: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","/**\n * Search tools for finding files and content.\n * Uses 'rg' (ripgrep) if available, falling back to a pure JS implementation.\n */\n\nimport { spawn } from \"node:child_process\"\nimport { readdir, readFile } from \"node:fs/promises\"\nimport { relative, resolve } from \"node:path\"\nimport { glob } from \"glob\"\nimport type { Tool, ToolResult } from \"../types.ts\"\n\nimport { textPart } from \"../util.ts\"\n\n/**\n * Tool for finding files by glob pattern.\n */\nexport function globTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"glob\",\n\t\t\tdescription: \"Find files by glob pattern (e.g. **/*.ts, src/**/*.test.ts).\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpattern: { type: \"string\", description: \"Glob pattern (e.g. **/*.ts)\" },\n\t\t\t\t\tpath: { type: \"string\", description: \"Directory to search in (default .)\" },\n\t\t\t\t\tnocase: { type: \"boolean\", description: \"Case-insensitive search (default false)\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"pattern\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst rawPath = (args.path as string) || \".\"\n\t\t\t\tconst dir = resolve(cwd, rawPath)\n\t\t\t\tif (dir !== cwd && !dir.startsWith(`${cwd}/`)) {\n\t\t\t\t\tthrow new Error(`Path outside project: ${rawPath}`)\n\t\t\t\t}\n\n\t\t\t\tconst pattern = args.pattern as string\n\t\t\t\tconst nocase = !!args.nocase\n\t\t\t\tconst files = await glob(pattern, { cwd: dir, nocase })\n\t\t\t\tconst sliced = files.slice(0, 500)\n\t\t\t\tconst relSearchPath = relative(cwd, dir)\n\t\t\t\tconst prefix = relSearchPath ? `${relSearchPath}/` : \"\"\n\t\t\t\tconst relFiles = sliced.map((f) => prefix + f)\n\t\t\t\tconst out = relFiles.length > 0 ? relFiles.join(\"\\n\") : \"No files found\"\n\t\t\t\treturn { content: [textPart(out)], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n/**\n * Tool for searching file contents using regex.\n */\nexport function grepTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"grep\",\n\t\t\tdescription:\n\t\t\t\t\"Search file contents with a regex pattern. Returns matching lines with file paths and line numbers.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpattern: { type: \"string\", description: \"Regex pattern to search for\" },\n\t\t\t\t\tpath: { type: \"string\", description: \"Directory or file to search in (default .)\" },\n\t\t\t\t\tglob: { type: \"string\", description: \"File filter glob (e.g. *.ts)\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"pattern\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst rawPath = (args.path as string) || \".\"\n\t\t\t\tconst dir = resolve(cwd, rawPath)\n\t\t\t\tif (dir !== cwd && !dir.startsWith(`${cwd}/`)) {\n\t\t\t\t\tthrow new Error(`Path outside project: ${rawPath}`)\n\t\t\t\t}\n\n\t\t\t\tconst pattern = args.pattern as string\n\t\t\t\tconst globFilter = args.glob as string | undefined\n\t\t\t\tconst relSearchPath = relative(cwd, dir) || \".\"\n\n\t\t\t\t// rg is 10-100x faster than our JS fallback, but isn't always installed\n\t\t\t\ttry {\n\t\t\t\t\tconst cmd = [\"rg\", \"--line-number\", \"--max-count\", \"200\"]\n\t\t\t\t\tif (globFilter) cmd.push(`--glob=${globFilter}`)\n\t\t\t\t\tcmd.push(\"--\", pattern, relSearchPath)\n\n\t\t\t\t\tconst proc = spawn(cmd[0]!, cmd.slice(1), {\n\t\t\t\t\t\tcwd,\n\t\t\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\t\t})\n\n\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\tproc.kill()\n\t\t\t\t\t\tproc.stdout.destroy()\n\t\t\t\t\t\tproc.stderr.destroy()\n\t\t\t\t\t}\n\t\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true })\n\n\t\t\t\t\tlet stdout = \"\"\n\t\t\t\t\tproc.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\t\tstdout += chunk.toString()\n\t\t\t\t\t})\n\n\t\t\t\t\tlet exitCode: number\n\t\t\t\t\ttry {\n\t\t\t\t\t\texitCode = await new Promise<number>((resolve, reject) => {\n\t\t\t\t\t\t\tproc.on(\"error\", reject)\n\t\t\t\t\t\t\tproc.on(\"close\", (code) => resolve(code ?? -1))\n\t\t\t\t\t\t})\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort)\n\t\t\t\t\t}\n\n\t\t\t\t\tif (exitCode === 0) {\n\t\t\t\t\t\tconst lines = stdout.split(\"\\n\").slice(0, 200).join(\"\\n\")\n\t\t\t\t\t\treturn { content: [textPart(lines || \"No matches\")], isError: false }\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// rg not available, fall through\n\t\t\t\t}\n\n\t\t\t\t// Pure JS fallback when rg is not available\n\t\t\t\tconst files = await glob(globFilter || \"**/*\", {\n\t\t\t\t\tcwd: dir,\n\t\t\t\t\tignore: [\"**/node_modules/**\", \"**/.git/**\"],\n\t\t\t\t})\n\t\t\t\tconst prefix = relSearchPath === \".\" ? \"\" : `${relSearchPath}/`\n\t\t\t\tconst re = new RegExp(pattern, \"i\")\n\t\t\t\tconst matches: string[] = []\n\t\t\t\tfor (const file of files.slice(0, 500)) {\n\t\t\t\t\tif (signal?.aborted) break\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst content = await readFile(resolve(dir, file), \"utf-8\")\n\t\t\t\t\t\tconst lines = content.split(\"\\n\")\n\t\t\t\t\t\tfor (let i = 0; i < lines.length && matches.length < 200; i++) {\n\t\t\t\t\t\t\tconst line = lines[i]\n\t\t\t\t\t\t\tif (line && re.test(line)) matches.push(`${prefix}${file}:${i + 1}:${line}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Skip binary/unreadable files silently\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(matches.join(\"\\n\") || \"No matches\")],\n\t\t\t\t\tisError: false,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n/**\n * Tool for listing directory entries.\n */\nexport function lsTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"ls\",\n\t\t\tdescription: \"List directory contents.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Directory to list (default .)\" },\n\t\t\t\t},\n\t\t\t\trequired: [],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst dir = resolve(cwd, (args.path as string) || \".\")\n\t\t\t\tconst entries = await readdir(dir, { withFileTypes: true })\n\t\t\t\tconst lines = entries.map((e) => {\n\t\t\t\t\tconst suffix = e.isDirectory() ? \"/\" : e.isSymbolicLink() ? \"@\" : \"\"\n\t\t\t\t\treturn `${e.name}${suffix}`\n\t\t\t\t})\n\t\t\t\treturn { content: [textPart(lines.join(\"\\n\") || \"(empty)\")], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n/**\n * Tool for visualizing a truncated directory tree.\n */\nexport function treeTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"tree\",\n\t\t\tdescription:\n\t\t\t\t\"Print a visual directory tree structure, ignoring common ignored folders like node_modules and .git.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Directory to start tree from (default .)\" },\n\t\t\t\t\tdepth: { type: \"number\", description: \"Maximum depth to traverse (default 3)\" },\n\t\t\t\t},\n\t\t\t\trequired: [],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst startDir = resolve(cwd, (args.path as string) || \".\")\n\t\t\t\tconst maxDepth = Number(args.depth ?? 3) || 3\n\n\t\t\t\tconst ignoreList = new Set([\n\t\t\t\t\t\".git\",\n\t\t\t\t\t\"node_modules\",\n\t\t\t\t\t\"dist\",\n\t\t\t\t\t\"build\",\n\t\t\t\t\t\".svelte-kit\",\n\t\t\t\t\t\".next\",\n\t\t\t\t\t\"out\",\n\t\t\t\t\t\".scannerwork\",\n\t\t\t\t\t\"coverage\",\n\t\t\t\t])\n\n\t\t\t\tasync function walk(dir: string, currentDepth: number, prefix: string): Promise<string> {\n\t\t\t\t\tif (currentDepth > maxDepth) return \"\"\n\t\t\t\t\tlet result = \"\"\n\n\t\t\t\t\tconst entries = await readdir(dir, { withFileTypes: true })\n\t\t\t\t\tconst sorted = entries\n\t\t\t\t\t\t.filter((e) => !ignoreList.has(e.name))\n\t\t\t\t\t\t.sort((a, b) => {\n\t\t\t\t\t\t\tif (a.isDirectory() && !b.isDirectory()) return -1\n\t\t\t\t\t\t\tif (!a.isDirectory() && b.isDirectory()) return 1\n\t\t\t\t\t\t\treturn a.name.localeCompare(b.name)\n\t\t\t\t\t\t})\n\n\t\t\t\t\tfor (let i = 0; i < sorted.length; i++) {\n\t\t\t\t\t\tconst entry = sorted[i]!\n\t\t\t\t\t\tconst isLast = i === sorted.length - 1\n\t\t\t\t\t\tconst connector = isLast ? \"└── \" : \"├── \"\n\t\t\t\t\t\tconst childPrefix = prefix + (isLast ? \" \" : \"│ \")\n\n\t\t\t\t\t\tresult += `${prefix}${connector}${entry.name}${entry.isDirectory() ? \"/\" : \"\"}\\n`\n\n\t\t\t\t\t\tif (entry.isDirectory()) {\n\t\t\t\t\t\t\tresult += await walk(resolve(dir, entry.name), currentDepth + 1, childPrefix)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn result\n\t\t\t\t}\n\n\t\t\t\tconst treeText = await walk(startDir, 1, \"\")\n\t\t\t\treturn { content: [textPart(treeText || \"(empty)\")], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","/**\n * Tool for executing shell commands within the project root.\n * Supports timeouts and output truncation to protect the context window.\n */\nimport { spawn } from \"node:child_process\"\nimport type { Tool, ToolResult } from \"../types.ts\"\n\nimport { textPart } from \"../util.ts\"\n\nexport function bashTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"bash\",\n\t\t\tdescription:\n\t\t\t\t\"Execute a shell command. Returns stdout and stderr. Timeout after N seconds (default 120).\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tcommand: { type: \"string\", description: \"Shell command to run\" },\n\t\t\t\t\ttimeout: { type: \"number\", description: \"Timeout in seconds (default 120)\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"command\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\tconst command = args.command as string\n\t\t\tconst timeoutMs = (Number(args.timeout) || 120) * 1000\n\n\t\t\ttry {\n\t\t\t\tconst proc = spawn(\"sh\", [\"-c\", command], { cwd, stdio: [\"ignore\", \"pipe\", \"pipe\"] })\n\n\t\t\t\tlet stdout = \"\"\n\t\t\t\tlet stderr = \"\"\n\t\t\t\tproc.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tstdout += chunk.toString()\n\t\t\t\t})\n\t\t\t\tproc.stderr.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tstderr += chunk.toString()\n\t\t\t\t})\n\n\t\t\t\tlet killed = false\n\t\t\t\tconst timer = setTimeout(() => {\n\t\t\t\t\tkilled = true\n\t\t\t\t\tproc.kill(\"SIGKILL\")\n\t\t\t\t\tproc.stdout.destroy()\n\t\t\t\t\tproc.stderr.destroy()\n\t\t\t\t}, timeoutMs)\n\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tkilled = true\n\t\t\t\t\tproc.kill(\"SIGKILL\")\n\t\t\t\t\tproc.stdout.destroy()\n\t\t\t\t\tproc.stderr.destroy()\n\t\t\t\t}\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true })\n\n\t\t\t\tlet exitCode: number\n\t\t\t\ttry {\n\t\t\t\t\texitCode = await new Promise<number>((resolve, reject) => {\n\t\t\t\t\t\tproc.on(\"error\", reject)\n\t\t\t\t\t\tproc.on(\"close\", (code) => resolve(code ?? -1))\n\t\t\t\t\t})\n\t\t\t\t} finally {\n\t\t\t\t\tclearTimeout(timer)\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort)\n\t\t\t\t}\n\n\t\t\t\t// Prevent context-window blowout from noisy commands\n\t\t\t\tconst MAX = 50_000\n\t\t\t\tlet out = \"\"\n\t\t\t\tif (stdout) out += stdout.slice(0, MAX)\n\t\t\t\tif (stderr) {\n\t\t\t\t\tif (out) out += \"\\n\"\n\t\t\t\t\tout += stderr.slice(0, MAX - out.length)\n\t\t\t\t}\n\t\t\t\tif (out.length >= MAX) out += `\\n…truncated`\n\n\t\t\t\tif (killed) out += `\\n[timeout after ${timeoutMs / 1000}s]`\n\t\t\t\tout += `\\n[exit ${exitCode}]`\n\n\t\t\t\treturn { content: [textPart(out)], isError: exitCode !== 0 || killed }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","/**\n * Web tools for searching and fetching internet content.\n * Uses DuckDuckGo HTML for search (no API key needed) and Node's built-in\n * fetch for reading URLs.\n */\nimport type { Tool, ToolResult } from \"../types.ts\"\nimport { textPart } from \"../util.ts\"\n\nconst MAX_CONTENT = 50_000\n\n// Minimal HTML → plaintext: strips tags, decodes entities, collapses whitespace\nfunction htmlToText(html: string): string {\n\tlet text = html\n\t\t// Remove HTML comments\n\t\t.replace(/<!--[\\s\\S]*?-->/g, \"\")\n\t\t// Remove script and style blocks entirely\n\t\t.replace(/<script[\\s\\S]*?<\\/script>/gi, \"\")\n\t\t.replace(/<style[\\s\\S]*?<\\/style>/gi, \"\")\n\t\t// Keep link hrefs visible (supports single, double, or no quotes)\n\t\t.replace(/<a[^>]*href=[\"']?([^\"'>\\s]*)[\"']?[^>]*>([\\s\\S]*?)<\\/a>/gi, \"[$2]($1)\")\n\t\t// Block-level tags → newlines\n\t\t.replace(/<\\/?(p|div|br|h[1-6]|li|tr|blockquote|pre|hr)[^>]*>/gi, \"\\n\")\n\t\t// Remove remaining tags\n\t\t.replace(/<[^>]+>/g, \"\")\n\t\t// Decode common HTML entities (both named and numeric, single/double quotes)\n\t\t.replace(/&amp;/g, \"&\")\n\t\t.replace(/&lt;/g, \"<\")\n\t\t.replace(/&gt;/g, \">\")\n\t\t.replace(/&quot;/g, '\"')\n\t\t.replace(/&#34;/g, '\"')\n\t\t.replace(/&#x22;/g, '\"')\n\t\t.replace(/&#39;/g, \"'\")\n\t\t.replace(/&#x27;/g, \"'\")\n\t\t.replace(/&apos;/g, \"'\")\n\t\t.replace(/&ldquo;/g, '\"')\n\t\t.replace(/&rdquo;/g, '\"')\n\t\t.replace(/&lsquo;/g, \"'\")\n\t\t.replace(/&rsquo;/g, \"'\")\n\t\t.replace(/&nbsp;/g, \" \")\n\t\t// Collapse whitespace but keep paragraph breaks\n\t\t.replace(/[ \\t]+/g, \" \")\n\t\t.replace(/\\n{3,}/g, \"\\n\\n\")\n\t\t.trim()\n\n\tif (text.length > MAX_CONTENT) {\n\t\ttext = `${text.slice(0, MAX_CONTENT)}\\n…truncated`\n\t}\n\treturn text\n}\n\nexport function webSearchTool(): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"web_search\",\n\t\t\tdescription:\n\t\t\t\t\"Search the web using DuckDuckGo. Returns up to 10 results with titles, URLs, and snippets. Use this when you need information from the internet.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tquery: { type: \"string\", description: \"Search query\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"query\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\tconst query = args.query as string\n\t\t\tif (!query.trim()) {\n\t\t\t\treturn { content: [textPart(\"Error: empty search query\")], isError: true }\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst url = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`\n\t\t\t\tconst resp = await fetch(url, {\n\t\t\t\t\tsignal: signal ?? undefined,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"User-Agent\":\n\t\t\t\t\t\t\t\"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\",\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\tif (!resp.ok) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [textPart(`Search failed: HTTP ${resp.status}`)],\n\t\t\t\t\t\tisError: true,\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst html = await resp.text()\n\n\t\t\t\t// Split HTML into blocks by result__body to isolate each search result safely\n\t\t\t\tconst blocks: string[] = []\n\t\t\t\tconst containerRegex = /<div[^>]*class=\"[^\"]*result__body[^\"]*\"[^>]*>/gi\n\t\t\t\tconst indices: number[] = []\n\t\t\t\tfor (const match of html.matchAll(containerRegex)) {\n\t\t\t\t\tif (match.index !== undefined) {\n\t\t\t\t\t\tindices.push(match.index)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (indices.length > 0) {\n\t\t\t\t\tfor (let i = 0; i < indices.length; i++) {\n\t\t\t\t\t\tconst start = indices[i]!\n\t\t\t\t\t\tconst end = indices[i + 1] ?? html.length\n\t\t\t\t\t\tblocks.push(html.slice(start, end))\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst results: string[] = []\n\t\t\t\tconst titleRegex = /<a[^>]*class=\"result__a\"[^>]*href=\"([^\"]*)\"[^>]*>([\\s\\S]*?)<\\/a>/i\n\t\t\t\tconst snippetRegex = /<a[^>]*class=\"result__snippet\"[^>]*>([\\s\\S]*?)<\\/a>/i\n\n\t\t\t\tfor (const block of blocks) {\n\t\t\t\t\tif (results.length >= 10) break\n\n\t\t\t\t\tconst titleMatch = titleRegex.exec(block)\n\t\t\t\t\tif (!titleMatch) continue\n\n\t\t\t\t\tconst rawUrl = titleMatch[1]!\n\t\t\t\t\tconst title = htmlToText(titleMatch[2]!)\n\n\t\t\t\t\tconst snippetMatch = snippetRegex.exec(block)\n\t\t\t\t\tconst snippet = snippetMatch ? htmlToText(snippetMatch[1]!) : \"\"\n\n\t\t\t\t\t// DuckDuckGo wraps URLs through a redirect; extract the actual URL\n\t\t\t\t\tlet cleanUrl = rawUrl\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Prepend protocol/host if DuckDuckGo returns a relative path or protocol-relative URL\n\t\t\t\t\t\tconst urlToParse = rawUrl.startsWith(\"//\")\n\t\t\t\t\t\t\t? `https:${rawUrl}`\n\t\t\t\t\t\t\t: rawUrl.startsWith(\"/\")\n\t\t\t\t\t\t\t\t? `https://duckduckgo.com${rawUrl}`\n\t\t\t\t\t\t\t\t: rawUrl\n\t\t\t\t\t\tconst param = new URL(urlToParse).searchParams.get(\"uddg\")\n\t\t\t\t\t\tif (param) cleanUrl = param\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Not a redirect URL, use as-is\n\t\t\t\t\t}\n\n\t\t\t\t\tresults.push(`## ${title}\\n${cleanUrl}\\n${snippet}`)\n\t\t\t\t}\n\n\t\t\t\tif (results.length === 0) {\n\t\t\t\t\treturn { content: [textPart(\"No results found.\")], isError: false }\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(results.join(\"\\n\\n\"))],\n\t\t\t\t\tisError: false,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tconst msg = (e as Error).message\n\t\t\t\tif (msg.includes(\"abort\")) {\n\t\t\t\t\treturn { content: [textPart(\"Search aborted.\")], isError: true }\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Search error: ${msg}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\nexport function webFetchTool(): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"web_fetch\",\n\t\t\tdescription:\n\t\t\t\t\"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.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\turl: { type: \"string\", description: \"URL to fetch\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"url\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\tconst url = args.url as string\n\t\t\tif (!url.trim()) {\n\t\t\t\treturn { content: [textPart(\"Error: empty URL\")], isError: true }\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tnew URL(url)\n\t\t\t} catch {\n\t\t\t\treturn { content: [textPart(`Error: invalid URL: ${url}`)], isError: true }\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst resp = await fetch(url, {\n\t\t\t\t\tsignal: signal ?? undefined,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"User-Agent\":\n\t\t\t\t\t\t\t\"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\",\n\t\t\t\t\t\tAccept: \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\",\n\t\t\t\t\t},\n\t\t\t\t\tredirect: \"follow\",\n\t\t\t\t})\n\n\t\t\t\tif (!resp.ok) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [textPart(`Fetch failed: HTTP ${resp.status} ${resp.statusText}`)],\n\t\t\t\t\t\tisError: true,\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst contentType = (resp.headers.get(\"content-type\") ?? \"\").toLowerCase()\n\t\t\t\tconst body = await resp.text()\n\n\t\t\t\t// Sniff HTML: check content-type or if body has HTML markup signature\n\t\t\t\tconst isHtml =\n\t\t\t\t\tcontentType.includes(\"text/html\") ||\n\t\t\t\t\tcontentType.includes(\"application/xhtml+xml\") ||\n\t\t\t\t\tbody.trim().toLowerCase().startsWith(\"<!doctype html\") ||\n\t\t\t\t\tbody.trim().toLowerCase().startsWith(\"<html\")\n\n\t\t\t\tif (isHtml) {\n\t\t\t\t\tconst text = htmlToText(body)\n\t\t\t\t\treturn { content: [textPart(text)], isError: false }\n\t\t\t\t}\n\n\t\t\t\t// For plain text, JSON, etc. return as-is (truncated if needed)\n\t\t\t\tconst truncated =\n\t\t\t\t\tbody.length > MAX_CONTENT ? `${body.slice(0, MAX_CONTENT)}\\n…truncated` : body\n\t\t\t\treturn { content: [textPart(truncated)], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\tconst msg = (e as Error).message\n\t\t\t\tif (msg.includes(\"abort\")) {\n\t\t\t\t\treturn { content: [textPart(\"Fetch aborted.\")], isError: true }\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Fetch error: ${msg}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","import type { Tool } from \"../types.ts\"\nimport { editTool, readTool, writeTool } from \"./fs.ts\"\nimport { gitTool } from \"./git.ts\"\nimport { globTool, grepTool, lsTool, treeTool } from \"./search.ts\"\nimport { bashTool } from \"./shell.ts\"\nimport { webFetchTool, webSearchTool } from \"./web.ts\"\n\nexport function getAllTools(cwd: string): Tool[] {\n\treturn [\n\t\treadTool(cwd),\n\t\twriteTool(cwd),\n\t\teditTool(cwd),\n\t\tbashTool(cwd),\n\t\tglobTool(cwd),\n\t\tgrepTool(cwd),\n\t\tlsTool(cwd),\n\t\ttreeTool(cwd),\n\t\tgitTool(cwd),\n\t\twebSearchTool(),\n\t\twebFetchTool(),\n\t]\n}\n\nexport function getDefaultTools(cwd: string): Tool[] {\n\treturn [\n\t\treadTool(cwd),\n\t\twriteTool(cwd),\n\t\teditTool(cwd),\n\t\tbashTool(cwd),\n\t\twebSearchTool(),\n\t\twebFetchTool(),\n\t]\n}\n","import { spawn } from \"node:child_process\"\nimport { readFile } from \"node:fs/promises\"\nimport { dirname, join } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport semver from \"semver\"\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nlet cachedLatest: string | null = null\nlet cachedCurrent: string | null = null\n\nexport async function getCurrentVersion(): Promise<string> {\n\tif (cachedCurrent) return cachedCurrent\n\ttry {\n\t\tconst raw = await readFile(join(__dirname, \"..\", \"package.json\"), \"utf-8\")\n\t\tconst pkg = JSON.parse(raw)\n\t\tcachedCurrent = (pkg.version as string) ?? \"0.0.0\"\n\t\treturn cachedCurrent\n\t} catch {\n\t\treturn \"0.0.0\"\n\t}\n}\n\nexport async function getLatestVersion(): Promise<string | null> {\n\tif (cachedLatest) return cachedLatest\n\ttry {\n\t\tconst proc = spawn(\"npm\", [\"info\", \"novacode\", \"version\"], {\n\t\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t\t})\n\t\tconst text = await new Promise<string>((resolve, reject) => {\n\t\t\tlet out = \"\"\n\t\t\tproc.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tout += chunk.toString()\n\t\t\t})\n\t\t\tproc.on(\"error\", reject)\n\t\t\tproc.on(\"close\", () => resolve(out.trim()))\n\t\t})\n\t\tif (text) {\n\t\t\tcachedLatest = text\n\t\t\treturn text\n\t\t}\n\t} catch {}\n\treturn null\n}\n\nexport async function checkForUpdate(): Promise<{\n\thasUpdate: boolean\n\tcurrent: string\n\tlatest: string\n} | null> {\n\tconst current = await getCurrentVersion()\n\tconst latest = await getLatestVersion()\n\tif (!latest) return null\n\treturn {\n\t\thasUpdate: semver.gt(latest, current),\n\t\tcurrent,\n\t\tlatest,\n\t}\n}\n\nexport async function runUpdate(silent = false): Promise<boolean> {\n\ttry {\n\t\tconst proc = spawn(\"npm\", [\"update\", \"-g\", \"novacode\"], {\n\t\t\tstdio: silent ? \"ignore\" : \"inherit\",\n\t\t})\n\t\tconst exitCode = await new Promise<number>((resolve, reject) => {\n\t\t\tproc.on(\"error\", reject)\n\t\t\tproc.on(\"close\", (code) => resolve(code ?? -1))\n\t\t})\n\t\tif (exitCode === 0) {\n\t\t\tif (!silent) {\n\t\t\t\tconsole.log(\"✓ novacode updated to latest version successfully.\")\n\t\t\t}\n\t\t\treturn true\n\t\t} else {\n\t\t\tif (!silent) {\n\t\t\t\tconsole.error(`Update failed (exit code ${exitCode})`)\n\t\t\t\tprocess.exit(1)\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\t} catch (e) {\n\t\tif (!silent) {\n\t\t\tconsole.error(`Update failed: ${(e as Error).message}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\t\treturn false\n\t}\n}\n","#!/usr/bin/env node\nimport { parseArgs } from \"node:util\"\n/**\n * Entry point for the nova CLI.\n * Handles configuration, CLI flags, and runs interactive TUI mode.\n */\nimport chalk from \"chalk\"\nimport { Agent } from \"./agent/agent.ts\"\nimport { buildSystemPrompt } from \"./agent/prompt.ts\"\nimport { handleSessionCommand } from \"./commands/session.ts\"\nimport { getProvider, MODELS } from \"./config/providers.ts\"\nimport { configExists, loadAuth, loadConfig } from \"./config/store.ts\"\nimport { runOnboarding } from \"./onboarding/wizard.ts\"\nimport { getSessionStore } from \"./session/store.ts\"\nimport { getAllTools } from \"./tools/index.ts\"\nimport type { Session } from \"./types.ts\"\nimport { getCurrentVersion, runUpdate } from \"./update.ts\"\n\nfunction parseCli() {\n\tconst { values, positionals } = parseArgs({\n\t\toptions: {\n\t\t\thelp: { type: \"boolean\", short: \"h\" },\n\t\t\tversion: { type: \"boolean\", short: \"v\" },\n\t\t\tprovider: { type: \"string\" },\n\t\t\tmodel: { type: \"string\" },\n\t\t\t\"api-key\": { type: \"string\" },\n\t\t\tsession: { type: \"string\", short: \"s\" },\n\t\t\tresume: { type: \"boolean\" },\n\t\t\tn: { type: \"string\" },\n\t\t\tlimit: { type: \"string\" },\n\t\t\tall: { type: \"boolean\" },\n\t\t},\n\t\tstrict: false,\n\t\tallowPositionals: true,\n\t})\n\n\treturn { flags: values, args: positionals }\n}\n\nfunction findModel(modelId: string, providerId?: string) {\n\treturn MODELS.find((m) => {\n\t\tif (providerId) return m.provider === providerId && m.id === modelId\n\t\treturn m.id === modelId\n\t})\n}\n\nasync function main() {\n\tconst { flags, args } = parseCli()\n\n\tif (flags.version) {\n\t\tconst version = await getCurrentVersion()\n\t\tconsole.log(`nova ${version}`)\n\t\tprocess.exit(0)\n\t}\n\n\tif (flags.help) {\n\t\tconsole.log(`nova — open-source coding agent\n\nUsage:\n nova Interactive mode\n nova update Update to latest version\n nova --session ls List sessions (last 10 by default)\n nova --session ls -n N List last N sessions\n nova --session rm <id> Delete a specific session\n nova --session rm --all Delete all sessions\n nova --session <id> Resume a session by ID\n nova --resume Resume the most recent session\n\nOptions:\n -h, --help Show help\n -v, --version Show version\n --provider <id> Provider to use\n --model <id> Model to use\n --api-key <key> API key override\n -s, --session <id> Resume/manage session`)\n\t\tprocess.exit(0)\n\t}\n\n\t// Handle update subcommand\n\tif (args[0] === \"update\") {\n\t\tawait runUpdate()\n\t\treturn\n\t}\n\n\t// Reject positional args — use interactive mode with / commands\n\tif (args.length > 0 && !flags.session) {\n\t\tconsole.error(chalk.yellow(`Unknown command: ${args.join(\" \")}`))\n\t\tconsole.error(\"Run `nova --help` for usage.\")\n\t\tprocess.exit(1)\n\t}\n\n\tconst controller = new AbortController()\n\n\tconst onSignal = () => {\n\t\tcontroller.abort()\n\t\tprocess.stderr.write(\"\\nAborted.\\n\")\n\t\tprocess.exit(130)\n\t}\n\tprocess.on(\"SIGINT\", onSignal)\n\tprocess.on(\"SIGTERM\", onSignal)\n\n\t// First-run onboarding\n\tconst config = await ((await configExists()) ? loadConfig() : runOnboarding())\n\tconst auth = await loadAuth()\n\n\tconst store = await getSessionStore()\n\tawait store.prune()\n\n\t// Handle --session commands (ls, rm)\n\tif (flags.session) {\n\t\tconst sessionFlag = flags.session as string\n\t\tif (sessionFlag === \"ls\" || sessionFlag === \"list\") {\n\t\t\tconst limit = parseInt((flags.n as string) || (flags.limit as string) || \"10\", 10)\n\t\t\tawait handleSessionCommand(store, [\"ls\"], { limit })\n\t\t\treturn\n\t\t}\n\t\tif (sessionFlag === \"rm\" || sessionFlag === \"delete\") {\n\t\t\tconst id = args[0]\n\t\t\tconst all = !!flags.all\n\t\t\tawait handleSessionCommand(store, [\"rm\", id ?? \"\"], { all })\n\t\t\treturn\n\t\t}\n\t}\n\n\tlet session: Session | null = null\n\tif (flags.resume) {\n\t\tsession = await store.latest()\n\t\tif (!session) {\n\t\t\tconsole.error(\"No recent session found to resume.\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t} else if (flags.session) {\n\t\tsession = await store.get(flags.session as string)\n\t\tif (!session) {\n\t\t\tconsole.error(`Session not found: ${flags.session}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\t}\n\n\t// CLI overrides or session default or config default\n\tconst providerId = (flags.provider as string) || session?.provider || config.provider\n\tconst modelId = (flags.model as string) || session?.model || config.model\n\tconst apiKey = (flags[\"api-key\"] as string) || auth.apiKeys[providerId]\n\n\tconst provider = getProvider(providerId)\n\tif (!provider) {\n\t\tconsole.error(`Unknown provider: ${providerId}`)\n\t\tconsole.error(`Available: ${getProvider(\"glm\") ? \"glm, \" : \"\"}gemini, deepseek, openai`)\n\t\tprocess.exit(1)\n\t}\n\n\tif (!apiKey) {\n\t\tconsole.error(\n\t\t\t`No API key for ${provider.name}. Set ${provider.envKey} or run nova for onboarding.`,\n\t\t)\n\t\tprocess.exit(1)\n\t}\n\n\tconst model = findModel(modelId, providerId)\n\tif (!model) {\n\t\tconsole.error(`Unknown model: ${modelId}`)\n\t\tconsole.error(\"Available models:\")\n\t\tfor (const m of MODELS.filter((m) => m.provider === providerId)) {\n\t\t\tconsole.error(` ${m.id} — ${m.name}`)\n\t\t}\n\t\tprocess.exit(1)\n\t}\n\n\tconst cwd = process.cwd()\n\tconst tools = getAllTools(cwd)\n\tconst system = buildSystemPrompt(cwd, tools)\n\n\tif (!session) {\n\t\tsession = await store.create(cwd, model.id, providerId)\n\t}\n\n\tconst sessionId = session.id\n\tconst existingMessages = await store.messages(sessionId)\n\n\tconst agent = new Agent({\n\t\tapi: provider.api,\n\t\tmodel,\n\t\tapiKey,\n\t\tbaseUrl: provider.baseUrl,\n\t\tsystem,\n\t\ttools,\n\t\tmessages: existingMessages,\n\t})\n\n\t// Interactive TUI mode\n\tprocess.off(\"SIGINT\", onSignal)\n\tprocess.off(\"SIGTERM\", onSignal)\n\tconst { interactive } = await import(\"./tui/app.tsx\")\n\tawait interactive(agent, store, sessionId)\n}\n\nprocess.on(\"unhandledRejection\", (reason) => {\n\tconsole.error(\"Unhandled rejection:\", reason)\n\tprocess.exit(1)\n})\n\nmain().catch((e) => {\n\tconsole.error(\"Fatal:\", e)\n\tprocess.exit(1)\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"main.mjs","names":["#abort","#resolve","#events","#done","#result","#doneResolve","#api","#model","#apiKey","#baseUrl","#system","#tools","#messages","#db","#pendingSessions","#ensurePersisted","#getLineage","#insertMessage"],"sources":["../src/provider/gemini.ts","../src/provider/openai.ts","../src/provider/stream.ts","../src/util.ts","../src/agent/loop.ts","../src/agent/agent.ts","../src/agent/prompt.ts","../src/commands/session.ts","../src/config/providers.ts","../src/config/store.ts","../src/tui/prompts.tsx","../src/onboarding/wizard.ts","../src/skills/discovery.ts","../src/resource.ts","../src/session/db.ts","../src/session/store.ts","../src/tools/fs.ts","../src/tools/git.ts","../src/tools/search.ts","../src/tools/shell.ts","../src/tools/web.ts","../src/tools/index.ts","../src/update.ts","../src/main.ts"],"sourcesContent":["import type {\n\tAssistantResult,\n\tContentPart,\n\tMsg,\n\tStopReason,\n\tStreamEvent,\n\tStreamFn,\n\tStreamOpts,\n\tToolDef,\n\tUsage,\n} from \"../types.ts\"\nimport { EventStream } from \"./stream.ts\"\n\ninterface GeminiPart {\n\ttext?: string\n\tthought?: boolean | string\n\tinline_data?: { mime_type: string; data: string }\n\tfunction_call?: { name: string; args: Record<string, unknown> }\n\tfunction_response?: { name: string; response: Record<string, unknown> }\n\tthought_signature?: string\n}\n\ninterface GeminiContent {\n\trole: \"user\" | \"model\"\n\tparts: GeminiPart[]\n}\n\n/**\n * Maps our internal Msg format to the Gemini 'Content' format.\n * Groups consecutive tool_result messages into a single Gemini message.\n */\nfunction msgsToGemini(messages: Msg[]): GeminiContent[] {\n\tconst contents: GeminiContent[] = []\n\n\tfor (const msg of messages) {\n\t\tif (msg.role === \"user\") {\n\t\t\tconst parts: GeminiPart[] =\n\t\t\t\ttypeof msg.content === \"string\"\n\t\t\t\t\t? [{ text: msg.content }]\n\t\t\t\t\t: msg.content.map((c) => {\n\t\t\t\t\t\t\tif (c.type === \"text\") return { text: c.text }\n\t\t\t\t\t\t\tif (c.type === \"image\") return { inline_data: { mime_type: c.mime, data: c.data } }\n\t\t\t\t\t\t\treturn { text: \"\" }\n\t\t\t\t\t\t})\n\t\t\tcontents.push({ role: \"user\", parts })\n\t\t} else if (msg.role === \"assistant\") {\n\t\t\tconst parts: GeminiPart[] = msg.content.map((c) => {\n\t\t\t\tif (c.type === \"text\") return { text: c.text, thought_signature: c.signature }\n\t\t\t\tif (c.type === \"thinking\")\n\t\t\t\t\treturn { thought: true, text: c.text, thought_signature: c.signature }\n\t\t\t\tif (c.type === \"tool_call\")\n\t\t\t\t\treturn { function_call: { name: c.name, args: c.args }, thought_signature: c.signature }\n\t\t\t\treturn { text: \"\" }\n\t\t\t})\n\t\t\tcontents.push({ role: \"model\", parts })\n\t\t} else if (msg.role === \"tool_result\") {\n\t\t\tconst part: GeminiPart = {\n\t\t\t\tfunction_response: {\n\t\t\t\t\tname: msg.tool,\n\t\t\t\t\tresponse: {\n\t\t\t\t\t\tcontent: msg.content\n\t\t\t\t\t\t\t.map((c) => (c.type === \"text\" ? c.text : JSON.stringify(c)))\n\t\t\t\t\t\t\t.join(\"\\n\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tconst last = contents[contents.length - 1]\n\t\t\t// Gemini requires alternating roles; multiple function_responses group into one 'user' message.\n\t\t\tif (last && last.role === \"user\" && last.parts.some((p) => p.function_response)) {\n\t\t\t\tlast.parts.push(part)\n\t\t\t} else {\n\t\t\t\tcontents.push({ role: \"user\", parts: [part] })\n\t\t\t}\n\t\t}\n\t}\n\n\treturn contents\n}\n\nfunction toolsToGemini(tools: ToolDef[]): unknown[] {\n\tif (tools.length === 0) return []\n\treturn [\n\t\t{\n\t\t\tfunction_declarations: tools.map((t) => ({\n\t\t\t\tname: t.name,\n\t\t\t\tdescription: t.description,\n\t\t\t\tparameters: t.parameters,\n\t\t\t})),\n\t\t},\n\t]\n}\n\nexport const streamGemini: StreamFn = (\n\topts: StreamOpts,\n): EventStream<StreamEvent, AssistantResult> => {\n\tconst es = new EventStream<StreamEvent, AssistantResult>()\n\n\t;(async () => {\n\t\tlet usage: Usage = { in: 0, out: 0 }\n\t\tconst content: ContentPart[] = []\n\n\t\ttry {\n\t\t\tconst baseUrl = opts.baseUrl || \"https://generativelanguage.googleapis.com\"\n\t\t\tconst url = `${baseUrl}/v1beta/models/${opts.model.id}:streamGenerateContent?alt=sse&key=${opts.apiKey}`\n\n\t\t\tconst body = {\n\t\t\t\tcontents: msgsToGemini(opts.messages),\n\t\t\t\tsystem_instruction: opts.system ? { parts: [{ text: opts.system }] } : undefined,\n\t\t\t\ttools: opts.tools.length > 0 ? toolsToGemini(opts.tools) : undefined,\n\t\t\t\tgenerationConfig: {\n\t\t\t\t\tthinkingConfig: opts.model.supportsThinking ? { thinkingLevel: \"low\" } : undefined,\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\"Api-Revision\": \"2026-05-20\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(body),\n\t\t\t\tsignal: opts.signal,\n\t\t\t})\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await response.text()\n\t\t\t\tlet msg = text\n\t\t\t\ttry {\n\t\t\t\t\tconst json = JSON.parse(text)\n\t\t\t\t\tmsg = json.error?.message || json.message || text\n\t\t\t\t} catch {\n\t\t\t\t\t/* use raw text */\n\t\t\t\t}\n\n\t\t\t\tconst errorMsg = `Gemini Error (${response.status}): ${msg}`\n\t\t\t\tes.push({ type: \"text_delta\", text: errorMsg })\n\t\t\t\tes.finish({\n\t\t\t\t\tcontent: [{ type: \"text\", text: errorMsg }],\n\t\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\t\tstop: \"error\",\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst reader = response.body?.getReader()\n\t\t\tif (!reader) {\n\t\t\t\tes.finish({ content: [], usage: { in: 0, out: 0 }, stop: \"error\" })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst decoder = new TextDecoder()\n\t\t\tlet buffer = \"\"\n\t\t\tlet stop: StopReason = \"stop\"\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\tif (done) break\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true })\n\t\t\t\tconst lines = buffer.split(\"\\n\")\n\t\t\t\tbuffer = lines.pop() ?? \"\"\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst trimmed = line.trim()\n\t\t\t\t\tif (!trimmed?.startsWith(\"data: \")) continue\n\t\t\t\t\tconst data = trimmed.slice(6)\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst chunk = JSON.parse(data)\n\t\t\t\t\t\tconst candidate = chunk.candidates?.[0]\n\n\t\t\t\t\t\t// Handle usage metadata\n\t\t\t\t\t\tif (chunk.usageMetadata) {\n\t\t\t\t\t\t\tusage = {\n\t\t\t\t\t\t\t\tin: chunk.usageMetadata.promptTokenCount || usage.in,\n\t\t\t\t\t\t\t\tout: chunk.usageMetadata.candidatesTokenCount || usage.out,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tes.push({ type: \"usage\", usage })\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!candidate) continue\n\n\t\t\t\t\t\t// Map finish reason\n\t\t\t\t\t\tif (candidate.finishReason) {\n\t\t\t\t\t\t\tconst reason = candidate.finishReason\n\t\t\t\t\t\t\tif (reason === \"STOP\") stop = \"stop\"\n\t\t\t\t\t\t\telse if (reason === \"MAX_TOKENS\") stop = \"length\"\n\t\t\t\t\t\t\telse if (reason === \"SAFETY\" || reason === \"RECITATION\" || reason === \"OTHER\")\n\t\t\t\t\t\t\t\tstop = \"error\"\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst parts = candidate.content?.parts\n\t\t\t\t\t\tif (parts) {\n\t\t\t\t\t\t\tfor (const part of parts) {\n\t\t\t\t\t\t\t\tconst sig = part.thought_signature || part.thoughtSignature\n\n\t\t\t\t\t\t\t\t// Handle text and thinking deltas\n\t\t\t\t\t\t\t\tif (part.text) {\n\t\t\t\t\t\t\t\t\tif (part.thought === true || typeof part.thought === \"string\") {\n\t\t\t\t\t\t\t\t\t\tconst thoughtText = typeof part.thought === \"string\" ? part.thought : part.text\n\t\t\t\t\t\t\t\t\t\tes.push({ type: \"thinking_delta\", text: thoughtText })\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tes.push({ type: \"text_delta\", text: part.text })\n\t\t\t\t\t\t\t\t\t\tconst last = content[content.length - 1]\n\t\t\t\t\t\t\t\t\t\tif (last?.type === \"text\") {\n\t\t\t\t\t\t\t\t\t\t\tlast.text += part.text\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tcontent.push({ type: \"text\", text: part.text, signature: sig })\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Handle function calls (can be snake_case or camelCase in some API versions)\n\t\t\t\t\t\t\t\tconst fc = part.functionCall || part.function_call\n\t\t\t\t\t\t\t\tif (fc) {\n\t\t\t\t\t\t\t\t\tconst name = fc.name\n\t\t\t\t\t\t\t\t\tconst args = (fc.args as Record<string, unknown>) || {}\n\t\t\t\t\t\t\t\t\tconst id = `call_${Math.random().toString(36).slice(2, 9)}`\n\n\t\t\t\t\t\t\t\t\tconst toolCall: ContentPart = {\n\t\t\t\t\t\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\t\t\targs,\n\t\t\t\t\t\t\t\t\t\tsignature: sig,\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tcontent.push(toolCall)\n\t\t\t\t\t\t\t\t\tes.push({ type: \"tool_call\", call: toolCall })\n\t\t\t\t\t\t\t\t\tstop = \"tool_use\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (_e) {\n\t\t\t\t\t\tif (data.trim() !== \"\" && data.trim() !== \"[DONE]\") {\n\t\t\t\t\t\t\t// skip noise\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tes.finish({ content, usage, stop })\n\t\t} catch (e) {\n\t\t\tif (opts.signal?.aborted) {\n\t\t\t\tes.finish({\n\t\t\t\t\tcontent,\n\t\t\t\t\tusage,\n\t\t\t\t\tstop: \"aborted\",\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst errorMsg = `Gemini Network/Request Error: ${e instanceof Error ? e.message : String(e)}`\n\t\t\tes.push({ type: \"text_delta\", text: errorMsg })\n\t\t\tes.finish({\n\t\t\t\tcontent: [{ type: \"text\", text: errorMsg }],\n\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\tstop: \"error\",\n\t\t\t})\n\t\t}\n\t})()\n\n\treturn es\n}\n","import type {\n\tAssistantResult,\n\tMsg,\n\tStopReason,\n\tStreamEvent,\n\tStreamFn,\n\tStreamOpts,\n\tToolDef,\n\tUsage,\n} from \"../types.ts\"\nimport { EventStream } from \"./stream.ts\"\n\nfunction msgToOpenAI(msg: Msg): Record<string, unknown> {\n\tif (msg.role === \"user\") {\n\t\treturn {\n\t\t\trole: \"user\",\n\t\t\tcontent:\n\t\t\t\ttypeof msg.content === \"string\"\n\t\t\t\t\t? msg.content\n\t\t\t\t\t: msg.content.map((c) => {\n\t\t\t\t\t\t\tif (c.type === \"text\") return { type: \"text\", text: c.text }\n\t\t\t\t\t\t\tif (c.type === \"image\")\n\t\t\t\t\t\t\t\treturn { type: \"image_url\", image_url: { url: `data:${c.mime};base64,${c.data}` } }\n\t\t\t\t\t\t\treturn { type: \"text\", text: \"\" }\n\t\t\t\t\t\t}),\n\t\t}\n\t}\n\tif (msg.role === \"assistant\") {\n\t\tconst textParts: string[] = []\n\t\tconst toolCalls: unknown[] = []\n\n\t\tfor (const c of msg.content) {\n\t\t\tif (c.type === \"text\") textParts.push(c.text)\n\t\t\t// thinking parts are internal — never sent back to the API\n\t\t\tif (c.type === \"tool_call\")\n\t\t\t\ttoolCalls.push({\n\t\t\t\t\ttype: \"function\",\n\t\t\t\t\tid: c.id,\n\t\t\t\t\tfunction: { name: c.name, arguments: JSON.stringify(c.args) },\n\t\t\t\t})\n\t\t}\n\n\t\tconst result: Record<string, unknown> = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: textParts.length > 0 ? textParts.join(\"\") : null,\n\t\t}\n\t\tif (toolCalls.length > 0) result.tool_calls = toolCalls\n\t\treturn result\n\t}\n\t// tool_result\n\tif (msg.role === \"tool_result\") {\n\t\treturn {\n\t\t\trole: \"tool\",\n\t\t\ttool_call_id: msg.callId,\n\t\t\tcontent: msg.content.map((c) => (c.type === \"text\" ? c.text : JSON.stringify(c))).join(\"\\n\"),\n\t\t}\n\t}\n\treturn { role: \"user\", content: \"\" }\n}\n\nfunction toolsToOpenAI(tools: ToolDef[]): unknown[] {\n\treturn tools.map((t) => ({\n\t\ttype: \"function\",\n\t\tfunction: {\n\t\t\tname: t.name,\n\t\t\tdescription: t.description,\n\t\t\tparameters: t.parameters,\n\t\t},\n\t}))\n}\n\nexport const streamOpenAI: StreamFn = (\n\topts: StreamOpts,\n): EventStream<StreamEvent, AssistantResult> => {\n\tconst es = new EventStream<StreamEvent, AssistantResult>()\n\n\t;(async () => {\n\t\tlet textContent = \"\"\n\t\tconst currentToolCalls = new Map<number, { id: string; name: string; args: string }>()\n\t\tlet usage: Usage = { in: 0, out: 0 }\n\n\t\ttry {\n\t\t\tconst body = {\n\t\t\t\tmodel: opts.model.id,\n\t\t\t\tmessages: [{ role: \"system\", content: opts.system }, ...opts.messages.map(msgToOpenAI)],\n\t\t\t\ttools: opts.tools.length > 0 ? toolsToOpenAI(opts.tools) : undefined,\n\t\t\t\tstream: true,\n\t\t\t}\n\n\t\t\tconst response = await fetch(`${opts.baseUrl}/chat/completions`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\tAuthorization: `Bearer ${opts.apiKey}`,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(body),\n\t\t\t\tsignal: opts.signal,\n\t\t\t})\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await response.text()\n\t\t\t\tconst errorMsg = `API error ${response.status}: ${text}`\n\t\t\t\tes.push({ type: \"text_delta\", text: errorMsg })\n\t\t\t\tes.finish({\n\t\t\t\t\tcontent: [{ type: \"text\", text: errorMsg }],\n\t\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\t\tstop: \"error\",\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst reader = response.body?.getReader()\n\t\t\tif (!reader) {\n\t\t\t\tes.finish({ content: [], usage: { in: 0, out: 0 }, stop: \"error\" })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst decoder = new TextDecoder()\n\t\t\tlet buffer = \"\"\n\t\t\tlet stop = \"stop\"\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\tif (done) break\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true })\n\t\t\t\tconst lines = buffer.split(\"\\n\")\n\t\t\t\tbuffer = lines.pop() ?? \"\"\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst trimmed = line.trim()\n\t\t\t\t\tif (!trimmed?.startsWith(\"data: \")) continue\n\t\t\t\t\tconst data = trimmed.slice(6)\n\t\t\t\t\tif (data === \"[DONE]\") continue\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst chunk = JSON.parse(data)\n\t\t\t\t\t\tconst delta = chunk.choices?.[0]?.delta\n\t\t\t\t\t\tif (!delta) continue\n\n\t\t\t\t\t\tif (delta.content) {\n\t\t\t\t\t\t\tes.push({ type: \"text_delta\", text: delta.content })\n\t\t\t\t\t\t\ttextContent += delta.content\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst reasoning = delta.reasoning_content || delta.reasoning || delta.thinking\n\t\t\t\t\t\tif (reasoning) {\n\t\t\t\t\t\t\tes.push({ type: \"thinking_delta\", text: reasoning })\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (delta.tool_calls) {\n\t\t\t\t\t\t\tfor (const tc of delta.tool_calls) {\n\t\t\t\t\t\t\t\tconst idx = tc.index ?? 0\n\t\t\t\t\t\t\t\tif (!currentToolCalls.has(idx)) {\n\t\t\t\t\t\t\t\t\tcurrentToolCalls.set(idx, {\n\t\t\t\t\t\t\t\t\t\tid: tc.id ?? \"\",\n\t\t\t\t\t\t\t\t\t\tname: tc.function?.name ?? \"\",\n\t\t\t\t\t\t\t\t\t\targs: \"\",\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconst existing = currentToolCalls.get(idx)!\n\t\t\t\t\t\t\t\tif (tc.id) existing.id = tc.id\n\t\t\t\t\t\t\t\tif (tc.function?.name) existing.name = tc.function.name\n\t\t\t\t\t\t\t\tif (tc.function?.arguments) existing.args += tc.function.arguments\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (chunk.usage) {\n\t\t\t\t\t\t\tusage = {\n\t\t\t\t\t\t\t\tin: chunk.usage.prompt_tokens ?? 0,\n\t\t\t\t\t\t\t\tout: chunk.usage.completion_tokens ?? 0,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tes.push({ type: \"usage\", usage })\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst finishReason = chunk.choices?.[0]?.finish_reason\n\t\t\t\t\t\tif (finishReason) stop = finishReason\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Skip malformed JSON chunks\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst content: AssistantResult[\"content\"] = []\n\t\t\tif (textContent) {\n\t\t\t\tcontent.push({ type: \"text\", text: textContent })\n\t\t\t}\n\t\t\tfor (const [, tc] of currentToolCalls) {\n\t\t\t\tcontent.push({\n\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\tid: tc.id,\n\t\t\t\t\tname: tc.name,\n\t\t\t\t\targs: JSON.parse(tc.args || \"{}\"),\n\t\t\t\t})\n\t\t\t\tes.push({\n\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\tcall: {\n\t\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\t\tid: tc.id,\n\t\t\t\t\t\tname: tc.name,\n\t\t\t\t\t\targs: JSON.parse(tc.args || \"{}\"),\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tstop = \"tool_use\"\n\t\t\t}\n\n\t\t\tes.finish({ content, usage, stop: stop as StopReason })\n\t\t} catch (e) {\n\t\t\tif (opts.signal?.aborted) {\n\t\t\t\tconst content: AssistantResult[\"content\"] = []\n\t\t\t\tif (textContent) {\n\t\t\t\t\tcontent.push({ type: \"text\", text: textContent })\n\t\t\t\t}\n\t\t\t\tfor (const [, tc] of currentToolCalls) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcontent.push({\n\t\t\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\t\t\tid: tc.id,\n\t\t\t\t\t\t\tname: tc.name,\n\t\t\t\t\t\t\targs: JSON.parse(tc.args || \"{}\"),\n\t\t\t\t\t\t})\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// skip malformed\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tes.finish({\n\t\t\t\t\tcontent,\n\t\t\t\t\tusage,\n\t\t\t\t\tstop: \"aborted\",\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst errorMsg = `Unexpected error: ${e instanceof Error ? e.message : String(e)}`\n\t\t\tes.push({ type: \"text_delta\", text: errorMsg })\n\t\t\tes.finish({\n\t\t\t\tcontent: [{ type: \"text\", text: errorMsg }],\n\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\tstop: \"error\",\n\t\t\t})\n\t\t}\n\t})()\n\n\treturn es\n}\n","import type { AgentEvent, ApiFormat, AssistantResult, StreamFn, StreamOpts } from \"../types.ts\"\nimport { streamGemini } from \"./gemini.ts\"\nimport { streamOpenAI } from \"./openai.ts\"\n\nexport type { AssistantResult, StreamEvent, StreamFn, StreamOpts } from \"../types.ts\"\n\n/*\n * Push-based async event stream.\n *\n * Producers call push()/finish(). Consumers iterate with for-await-of.\n * Backpressure is implicit: push() resolves immediately; the iterator\n * awaits the next value only when the consumer asks for it.\n */\nexport class EventStream<T, R> {\n\t#events: T[] = []\n\t#done = false\n\t#result?: R\n\t#resolve?: (value: T) => void\n\t#doneResolve?: (value: R) => void\n\t#abort = false\n\n\tpush(event: T): void {\n\t\tif (this.#abort) return\n\t\t// If a consumer is already waiting, deliver directly — skip the queue\n\t\tif (this.#resolve) {\n\t\t\tconst resolve = this.#resolve\n\t\t\tthis.#resolve = undefined\n\t\t\tresolve(event)\n\t\t} else {\n\t\t\tthis.#events.push(event)\n\t\t}\n\t}\n\n\tfinish(result: R): void {\n\t\tthis.#done = true\n\t\tthis.#result = result\n\t\t// Wake up a suspended iterator so it can see done=true and exit\n\t\tif (this.#resolve) {\n\t\t\t// undefined is a sentinel — the iterator loop checks done after waking\n\t\t\tthis.#resolve(undefined as T)\n\t\t}\n\t\tif (this.#doneResolve) {\n\t\t\tthis.#doneResolve(result)\n\t\t}\n\t}\n\n\tabort(): void {\n\t\tthis.#abort = true\n\t\tthis.#done = true\n\t\tif (this.#resolve) {\n\t\t\tthis.#resolve(undefined as T)\n\t\t}\n\t\tif (this.#doneResolve) {\n\t\t\tthis.#doneResolve(undefined as R)\n\t\t}\n\t}\n\n\tasync *[Symbol.asyncIterator](): AsyncGenerator<T> {\n\t\twhile (!this.#done || this.#events.length > 0) {\n\t\t\tif (this.#events.length > 0) {\n\t\t\t\tyield this.#events.shift() as T\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (this.#done) break\n\t\t\tconst item = await new Promise<T | undefined>((resolve) => {\n\t\t\t\tthis.#resolve = resolve as (value: T) => void\n\t\t\t})\n\t\t\tif (item !== undefined) {\n\t\t\t\tyield item\n\t\t\t}\n\t\t}\n\t}\n\n\tget result(): R | undefined {\n\t\treturn this.#result\n\t}\n\n\tget isDone(): boolean {\n\t\treturn this.#done\n\t}\n}\n\n// Internal map of registered provider implementations\nconst registry = new Map<ApiFormat, StreamFn>([\n\t[\"openai\", streamOpenAI],\n\t[\"gemini\", streamGemini],\n])\n\nexport function register(api: ApiFormat, fn: StreamFn): void {\n\tregistry.set(api, fn)\n}\n\n// Bridges provider-specific StreamEvents into AgentEvents so the loop and TUI deal with one type.\nexport function stream(opts: StreamOpts): EventStream<AgentEvent, AssistantResult> {\n\tconst fn = registry.get(opts.api)\n\tif (!fn) throw new Error(`No provider registered for API format: ${opts.api}`)\n\n\t// Bridge layer: converts provider-specific StreamEvents into the agent's\n\t// AgentEvent shape, so the loop and TUI only deal with one event type.\n\tconst providerStream = fn(opts)\n\tconst agentStream = new EventStream<AgentEvent, AssistantResult>()\n\n\t;(async () => {\n\t\tfor await (const event of providerStream) {\n\t\t\tif (event.type === \"text_delta\") {\n\t\t\t\tagentStream.push({ type: \"text_delta\", text: event.text ?? \"\" })\n\t\t\t} else if (event.type === \"thinking_delta\") {\n\t\t\t\tagentStream.push({ type: \"thinking_delta\", text: event.text ?? \"\" })\n\t\t\t} else if (event.type === \"tool_call\" && event.call) {\n\t\t\t\tagentStream.push({\n\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\tcall: {\n\t\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\t\tid: event.call.id,\n\t\t\t\t\t\tname: event.call.name,\n\t\t\t\t\t\targs: event.call.args,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t} else if (event.type === \"usage\" && event.usage) {\n\t\t\t\tagentStream.push({ type: \"usage\", usage: event.usage })\n\t\t\t}\n\t\t}\n\n\t\tconst res = providerStream.result\n\t\tif (res) {\n\t\t\tagentStream.finish(res)\n\t\t} else {\n\t\t\t// Fallback for unexpected closure\n\t\t\tagentStream.finish({ content: [], usage: { in: 0, out: 0 }, stop: \"stop\" })\n\t\t}\n\t})()\n\n\treturn agentStream\n}\n\nexport function getRegisteredApis(): ApiFormat[] {\n\treturn [...registry.keys()]\n}\n","import { isAbsolute, relative } from \"node:path\"\nimport chalk from \"chalk\"\nimport type { Msg, TextPart } from \"./types.ts\"\n\n// ~4 chars per token for English/code. Close enough for capacity warnings.\nexport function estimateTokens(messages: Msg[]): number {\n\tlet chars = 0\n\tfor (const msg of messages) {\n\t\tif (typeof msg.content === \"string\") {\n\t\t\tchars += msg.content.length\n\t\t} else if (Array.isArray(msg.content)) {\n\t\t\tfor (const part of msg.content) {\n\t\t\t\tif (part.type === \"text\") chars += part.text.length\n\t\t\t}\n\t\t}\n\t}\n\treturn Math.ceil(chars / 4)\n}\n\nexport function textPart(s: string): TextPart {\n\treturn { type: \"text\", text: s }\n}\n\nexport function getRelativeIfInside(cwd: string, filePath: string): string {\n\tif (filePath === cwd || filePath.startsWith(`${cwd}/`)) {\n\t\treturn relative(cwd, filePath) || \".\"\n\t}\n\treturn filePath\n}\n\nexport function makeRelative(val: string): string {\n\tif (typeof val !== \"string\") return val\n\n\tlet pathVal = val\n\tlet prefix = \"\"\n\tif (val.startsWith(\"file://\")) {\n\t\tpathVal = val.slice(7)\n\t\tprefix = \"file://\"\n\t}\n\n\tif (isAbsolute(pathVal)) {\n\t\tconst cwd = process.cwd()\n\t\treturn prefix + getRelativeIfInside(cwd, pathVal)\n\t}\n\treturn val\n}\n\nexport function formatToolArgs(\n\targs: Record<string, unknown> | undefined,\n\tuseChalk = false,\n): string {\n\tif (!args) return \"\"\n\treturn Object.entries(args)\n\t\t.map(([k, v]) => {\n\t\t\tconst val = typeof v === \"string\" ? makeRelative(v) : JSON.stringify(v)\n\t\t\tconst valStr = val.length > 40 ? `${val.slice(0, 40)}…` : val\n\t\t\tconst keyStr = useChalk ? chalk.dim(`${k}:`) : `${k}:`\n\t\t\treturn `${keyStr} ${valStr}`\n\t\t})\n\t\t.join(\" \")\n}\n\nexport function formatRelativeTime(ts: number): string {\n\tconst now = Date.now()\n\tconst diffMs = now - ts\n\tconst diffSec = Math.floor(diffMs / 1000)\n\tconst diffMin = Math.floor(diffSec / 60)\n\tconst diffHour = Math.floor(diffMin / 60)\n\n\tif (diffSec < 60) {\n\t\treturn \"just now\"\n\t}\n\tif (diffMin < 60) {\n\t\treturn `${diffMin}m ago`\n\t}\n\tif (diffHour < 24) {\n\t\treturn `${diffHour}h ago`\n\t}\n\treturn new Date(ts).toLocaleDateString()\n}\n","/**\n * Core agent loop that orchestrates model interaction and tool execution.\n * Handles turns, tool routing, safety checks, and event streaming.\n */\nimport { EventStream, stream } from \"../provider/stream.ts\"\nimport type {\n\tAgentEvent,\n\tAssistantMsg,\n\tLlmContext,\n\tLoopOpts,\n\tMsg,\n\tToolCallPart,\n\tToolResultMsg,\n} from \"../types.ts\"\nimport { estimateTokens, textPart } from \"../util.ts\"\n\n// Safety cap so a misbehaving model can't loop forever\nconst MAX_TURNS = 50\n\nconst isToolCall = (c: unknown): c is ToolCallPart =>\n\ttypeof c === \"object\" && c !== null && (c as ToolCallPart).type === \"tool_call\"\n\n/**\n * Start a long-running agent session that yields an EventStream of updates.\n */\nexport function run(\n\tinitialContext: LlmContext,\n\topts: LoopOpts,\n\tsignal?: AbortSignal,\n): EventStream<AgentEvent, Msg[]> {\n\tconst es = new EventStream<AgentEvent, Msg[]>()\n\tconst out: Msg[] = []\n\tconst maxTurns = opts.maxTurns ?? MAX_TURNS\n\n\tlet context = initialContext\n\n\tconst tick = async () => {\n\t\tes.push({ type: \"start\" })\n\n\t\ttry {\n\t\t\tlet turns = 0\n\t\t\twhile (turns < maxTurns) {\n\t\t\t\tif (signal?.aborted) break\n\n\t\t\t\tturns++\n\t\t\t\tes.push({ type: \"turn\" })\n\n\t\t\t\t// Warn before hitting the hard limit so the caller can compact/summarize\n\t\t\t\tconst approxTokens = estimateTokens(context.messages)\n\t\t\t\tif (approxTokens > opts.model.contextWindow * 0.9) {\n\t\t\t\t\tes.push({\n\t\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\t\ttext: `[warning] Approaching context limit (~${Math.round(approxTokens / 1000)}k / ${Math.round(opts.model.contextWindow / 1000)}k tokens)`,\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tconst reply = await getReply(context, opts, es, signal)\n\t\t\t\tout.push(reply)\n\t\t\t\tcontext = { ...context, messages: [...context.messages, reply] }\n\t\t\t\tes.push({ type: \"assistant_msg\", msg: reply })\n\n\t\t\t\tif (reply.stop === \"error\" || reply.stop === \"aborted\") {\n\t\t\t\t\tes.push({ type: \"turn_end\", msg: reply, results: [] })\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tconst calls = reply.content.filter(isToolCall)\n\t\t\t\tif (calls.length === 0) {\n\t\t\t\t\tes.push({ type: \"turn_end\", msg: reply, results: [] })\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\t// Execute tool calls\n\t\t\t\tconst results: ToolResultMsg[] = []\n\t\t\t\tfor (const call of calls) {\n\t\t\t\t\tif (signal?.aborted) break\n\n\t\t\t\t\tconst tool = context.tools.find((t) => t.def.name === call.name)\n\t\t\t\t\tif (!tool) {\n\t\t\t\t\t\tconst errResult: ToolResultMsg = {\n\t\t\t\t\t\t\trole: \"tool_result\",\n\t\t\t\t\t\t\tcallId: call.id,\n\t\t\t\t\t\t\ttool: call.name,\n\t\t\t\t\t\t\tcontent: [textPart(`Unknown tool: ${call.name}`)],\n\t\t\t\t\t\t\tisError: true,\n\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresults.push(errResult)\n\t\t\t\t\t\tcontext = { ...context, messages: [...context.messages, errResult] }\n\t\t\t\t\t\tout.push(errResult)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// beforeTool lets callers block dangerous operations (e.g. rm -rf)\n\t\t\t\t\tconst blocked = await opts.beforeTool?.(call, call.args, context)\n\t\t\t\t\tif (blocked?.block) {\n\t\t\t\t\t\tconst blockResult: ToolResultMsg = {\n\t\t\t\t\t\t\trole: \"tool_result\",\n\t\t\t\t\t\t\tcallId: call.id,\n\t\t\t\t\t\t\ttool: call.name,\n\t\t\t\t\t\t\tcontent: [textPart(blocked.reason ?? \"Blocked\")],\n\t\t\t\t\t\t\tisError: true,\n\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresults.push(blockResult)\n\t\t\t\t\t\tcontext = { ...context, messages: [...context.messages, blockResult] }\n\t\t\t\t\t\tout.push(blockResult)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// Execute\n\t\t\t\t\tconst result = await tool.execute(call.args, signal)\n\t\t\t\t\tconst toolMsg: ToolResultMsg = {\n\t\t\t\t\t\trole: \"tool_result\",\n\t\t\t\t\t\tcallId: call.id,\n\t\t\t\t\t\ttool: call.name,\n\t\t\t\t\t\targs: call.args,\n\t\t\t\t\t\tcontent: result.content,\n\t\t\t\t\t\tisError: result.isError,\n\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t}\n\n\t\t\t\t\tresults.push(toolMsg)\n\t\t\t\t\tcontext = { ...context, messages: [...context.messages, toolMsg] }\n\t\t\t\t\tout.push(toolMsg)\n\t\t\t\t\tes.push({ type: \"tool_result\", callId: call.id, result: toolMsg, args: call.args })\n\n\t\t\t\t\tawait opts.afterTool?.(call, toolMsg, context)\n\t\t\t\t}\n\n\t\t\t\tes.push({ type: \"turn_end\", msg: reply, results })\n\t\t\t}\n\n\t\t\tif (turns >= maxTurns) {\n\t\t\t\tes.push({\n\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\ttext: `[max turns reached (${maxTurns})]`,\n\t\t\t\t})\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tif ((e as Error).name === \"AbortError\") {\n\t\t\t\tes.finish(out)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\n\t\tes.finish(out)\n\t}\n\n\ttick()\n\treturn es\n}\n\nasync function getReply(\n\tcontext: LlmContext,\n\topts: LoopOpts,\n\tes: EventStream<AgentEvent, Msg[]>,\n\tsignal?: AbortSignal,\n): Promise<AssistantMsg> {\n\tconst providerStream = stream({\n\t\tapi: opts.api,\n\t\tmodel: opts.model,\n\t\tapiKey: opts.apiKey,\n\t\tbaseUrl: opts.baseUrl,\n\t\tsystem: context.system,\n\t\tmessages: context.messages,\n\t\ttools: context.tools.map((t) => t.def),\n\t\tsignal,\n\t})\n\n\tconst content: AssistantMsg[\"content\"] = []\n\tlet usage = { in: 0, out: 0 }\n\n\t// Accumulate content and proxy events to the outer stream\n\tfor await (const ev of providerStream) {\n\t\tif (ev.type === \"text_delta\" && ev.text) {\n\t\t\tconst last = content[content.length - 1]\n\t\t\tif (last?.type === \"text\") {\n\t\t\t\tlast.text += ev.text\n\t\t\t} else {\n\t\t\t\tcontent.push(textPart(ev.text))\n\t\t\t}\n\t\t\tes.push({ type: \"text_delta\", text: ev.text })\n\t\t} else if (ev.type === \"thinking_delta\" && ev.text) {\n\t\t\tes.push({ type: \"thinking_delta\", text: ev.text })\n\t\t} else if (ev.type === \"tool_call\" && ev.call) {\n\t\t\tcontent.push(ev.call)\n\t\t\tes.push({ type: \"tool_call\", call: ev.call })\n\t\t} else if (ev.type === \"usage\" && ev.usage) {\n\t\t\tusage = ev.usage\n\t\t\tes.push({ type: \"usage\", usage })\n\t\t}\n\t}\n\n\tconst parts =\n\t\tproviderStream.result?.content && providerStream.result.content.length > 0\n\t\t\t? providerStream.result.content\n\t\t\t: content\n\n\tconst hasToolCall = parts.some((p) => p.type === \"tool_call\")\n\tconst replyContent = hasToolCall\n\t\t? parts.filter((p) => p.type !== \"text\" || p.text.trim().length > 0)\n\t\t: parts\n\n\tconst res = providerStream.result\n\tif (res) {\n\t\treturn {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: replyContent,\n\t\t\tmodel: opts.model.id,\n\t\t\tprovider: opts.model.provider,\n\t\t\tusage: res.usage,\n\t\t\tstop: res.stop,\n\t\t\tts: Date.now(),\n\t\t}\n\t}\n\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: replyContent,\n\t\tmodel: opts.model.id,\n\t\tprovider: opts.model.provider,\n\t\tusage,\n\t\tstop: replyContent.some((c) => c.type === \"tool_call\") ? \"tool_use\" : \"stop\",\n\t\tts: Date.now(),\n\t}\n}\n","import type { EventStream } from \"../provider/stream.ts\"\nimport type { AgentEvent, ApiFormat, LlmContext, LoopOpts, Model, Msg, Tool } from \"../types.ts\"\nimport { run } from \"./loop.ts\"\n\nexport class Agent {\n\t#api: ApiFormat\n\t#model: Model\n\t#system: string\n\t#messages: Msg[] = []\n\t#tools: Tool[]\n\t#apiKey: string\n\t#baseUrl: string\n\n\tconstructor(opts: {\n\t\tapi: ApiFormat\n\t\tmodel: Model\n\t\tapiKey: string\n\t\tbaseUrl: string\n\t\tsystem: string\n\t\ttools: Tool[]\n\t\tmessages?: Msg[]\n\t}) {\n\t\tthis.#api = opts.api\n\t\tthis.#model = opts.model\n\t\tthis.#apiKey = opts.apiKey\n\t\tthis.#baseUrl = opts.baseUrl\n\t\tthis.#system = opts.system\n\t\tthis.#tools = opts.tools\n\t\tthis.#messages = opts.messages ?? []\n\t}\n\n\tget model(): Model {\n\t\treturn this.#model\n\t}\n\n\tget messages(): Msg[] {\n\t\treturn this.#messages\n\t}\n\n\tget tools(): Tool[] {\n\t\treturn this.#tools\n\t}\n\n\tget apiKey(): string {\n\t\treturn this.#apiKey\n\t}\n\n\tget baseUrl(): string {\n\t\treturn this.#baseUrl\n\t}\n\n\tupdateConfig(opts: { api: ApiFormat; model: Model; apiKey: string; baseUrl: string }): void {\n\t\tthis.#api = opts.api\n\t\tthis.#model = opts.model\n\t\tthis.#apiKey = opts.apiKey\n\t\tthis.#baseUrl = opts.baseUrl\n\t}\n\n\tsetTools(tools: Tool[]): void {\n\t\tthis.#tools = tools\n\t}\n\n\tsetMessages(msgs: Msg[]): void {\n\t\tthis.#messages = msgs\n\t}\n\n\tsetModel(model: Model): void {\n\t\tthis.#model = model\n\t}\n\n\tprompt(signal?: AbortSignal): EventStream<AgentEvent, Msg[]> {\n\t\tconst context: LlmContext = {\n\t\t\tsystem: this.#system,\n\t\t\tmessages: this.#messages,\n\t\t\ttools: this.#tools,\n\t\t}\n\n\t\tconst opts: LoopOpts = {\n\t\t\tapi: this.#api,\n\t\t\tmodel: this.#model,\n\t\t\tapiKey: this.#apiKey,\n\t\t\tbaseUrl: this.#baseUrl,\n\t\t}\n\n\t\treturn run(context, opts, signal)\n\t}\n}\n","/**\n * Logic for constructing the foundational system instruction given the environment and tools.\n */\n\nimport os from \"node:os\"\nimport type { Skill, Tool } from \"../types.ts\"\n\nexport function buildSystemPrompt(\n\tcwd: string,\n\ttools: Tool[],\n\tskills: Skill[] = [],\n\tagentsMd?: string,\n): string {\n\tconst toolList = tools.map((t) => `- ${t.def.name}: ${t.def.description}`).join(\"\\n\")\n\tconst platform = os.platform()\n\tconst arch = os.arch()\n\tconst release = os.release()\n\tconst shell = process.env.SHELL || \"unknown\"\n\n\treturn `You are Nova, an expert coding assistant. Help users with coding tasks using the tools available.\n\nFormat 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.\n\n# Tools\n\n${toolList}\n\n# Environment\n\n- Working directory: ${cwd}\n- Operating System: ${platform} (${release})\n- Architecture: ${arch}\n- Shell: ${shell}\n- Date: ${new Date().toISOString().split(\"T\")[0]}\n\n# Guidelines\n\n- Use tools to fulfill requests. Do not fabricate file contents.\n- Explain what you are doing and why before each tool call.\n- Use the \"bash\" tool for ls, git, and other shell operations.\n- Always read a file before editing it.\n- Prefer edit over write for existing files.\n- Run relevant tests after making changes.\n- If a command fails, read the error carefully before retrying.\n- For multi-file changes, plan first, then execute.\n- When done, briefly summarize what was changed.\n- Be concise and direct.\n\n# Safety\n\n- Never delete files outside the working directory.\n- Never run destructive commands unless the user explicitly confirms.\n- If unsure, ask for clarification.\n- Never expose API keys, tokens, or secrets.\n\n# Skills\n\nThe following skills are available. Each skill provides specialized instructions for specific tasks.\n\n${skills.length > 0 ? skills.map((s) => `- ${s.name}: ${s.description} (path: ${s.path}/SKILL.md)`).join(\"\\n\") : \"No skills loaded.\"}\n\n**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.\n\n${agentsMd ? `\\n<project_context>\\nProject-specific instructions and guidelines:\\n\\n<project_instructions path=\"AGENTS.md\">\\n${agentsMd}\\n</project_instructions>\\n</project_context>` : \"\"}`\n}\n","import type { SessionStore } from \"../session/store.ts\"\nimport { formatRelativeTime } from \"../util.ts\"\n\nfunction formatTokens(n: number): string {\n\tif (n === 0) return \"-\"\n\tif (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`\n\tif (n >= 1_000) return `${(n / 1_000).toFixed(1)}k`\n\treturn String(n)\n}\n\nexport async function handleSessionCommand(\n\tstore: SessionStore,\n\targs: string[],\n\topts: { limit?: number; all?: boolean } = {},\n): Promise<void> {\n\tconst [subcommand, id] = args\n\n\tif (subcommand === \"list\" || subcommand === \"ls\") {\n\t\tconst limit = opts.limit ?? 10\n\t\tconst sessions = await store.list(limit)\n\t\tif (sessions.length === 0) {\n\t\t\tconsole.log(\"No sessions found.\")\n\t\t\treturn\n\t\t}\n\n\t\tconsole.log(\"ID\".padEnd(25), \"TITLE / UPDATED\".padEnd(35), \"TOKENS\")\n\t\tconsole.log(\"-\".repeat(75))\n\t\tfor (const s of sessions) {\n\t\t\tconst relTime = formatRelativeTime(s.updated)\n\t\t\tconst titleOrUpdated = s.title ? `\"${s.title}\" (${relTime})` : relTime\n\t\t\tconst tokens = formatTokens(s.inputTokens + s.outputTokens)\n\t\t\tconsole.log(s.id.padEnd(25), titleOrUpdated.padEnd(35), tokens)\n\t\t}\n\t\treturn\n\t}\n\n\tif (subcommand === \"delete\" || subcommand === \"rm\") {\n\t\tif (opts.all) {\n\t\t\tawait store.deleteAll()\n\t\t\tconsole.log(\"All sessions deleted.\")\n\t\t\treturn\n\t\t}\n\n\t\tif (!id) {\n\t\t\tconsole.error(\"Usage: nova --sessions rm <id> or --sessions rm --all\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t\tconst success = await store.delete(id)\n\t\tif (success) {\n\t\t\tconsole.log(`Deleted session: ${id}`)\n\t\t} else {\n\t\t\tconsole.error(`Session not found: ${id}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\t\treturn\n\t}\n\n\tconsole.error(\"Unknown session subcommand.\")\n\tprocess.exit(1)\n}\n","import type { Model, ProviderDef } from \"../types.ts\"\n\nexport const PROVIDERS: ProviderDef[] = [\n\t{\n\t\tid: \"glm\",\n\t\tname: \"GLM (Z.AI)\",\n\t\tapi: \"openai\",\n\t\tbaseUrl: \"https://api.z.ai/api/coding/paas/v4\",\n\t\tenvKey: \"GLM_API_KEY\",\n\t},\n\t{\n\t\tid: \"gemini\",\n\t\tname: \"Gemini (Google)\",\n\t\tapi: \"gemini\",\n\t\tbaseUrl: \"https://generativelanguage.googleapis.com\",\n\t\tenvKey: \"GEMINI_API_KEY\",\n\t},\n\t{\n\t\tid: \"deepseek\",\n\t\tname: \"DeepSeek\",\n\t\tapi: \"openai\",\n\t\tbaseUrl: \"https://api.deepseek.com\",\n\t\tenvKey: \"DEEPSEEK_API_KEY\",\n\t},\n\t{\n\t\tid: \"openai\",\n\t\tname: \"OpenAI\",\n\t\tapi: \"openai\",\n\t\tbaseUrl: \"https://api.openai.com/v1\",\n\t\tenvKey: \"OPENAI_API_KEY\",\n\t},\n]\n\nexport const MODELS: Model[] = [\n\t// GLM\n\t{\n\t\tid: \"glm-5.1\",\n\t\tname: \"GLM-5.1\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-5\",\n\t\tname: \"GLM-5\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-5-turbo\",\n\t\tname: \"GLM-5 Turbo\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-4.7\",\n\t\tname: \"GLM-4.7\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-4.7-flash\",\n\t\tname: \"GLM-4.7 Flash (Free)\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-4.5-flash\",\n\t\tname: \"GLM-4.5 Flash (Free)\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t// Gemini\n\t{\n\t\tid: \"gemini-3.5-flash\",\n\t\tname: \"Gemini 3.5 Flash\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3.1-pro-preview\",\n\t\tname: \"Gemini 3.1 Pro Preview\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 2_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3.1-pro-preview-customtools\",\n\t\tname: \"Gemini 3.1 Pro (Custom Tools)\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 2_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3.1-flash-lite\",\n\t\tname: \"Gemini 3.1 Flash-Lite\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3.1-flash-lite-preview\",\n\t\tname: \"Gemini 3.1 Flash-Lite Preview\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3-flash-preview\",\n\t\tname: \"Gemini 3 Flash Preview\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-2.5-pro\",\n\t\tname: \"Gemini 2.5 Pro\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 2_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"gemini-2.5-flash\",\n\t\tname: \"Gemini 2.5 Flash\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"gemini-2.5-flash-lite\",\n\t\tname: \"Gemini 2.5 Flash-Lite\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"gemini-2.5-computer-use-preview-10-2025\",\n\t\tname: \"Gemini 2.5 Computer Use\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: false,\n\t},\n\t// DeepSeek\n\t{\n\t\tid: \"deepseek-chat\",\n\t\tname: \"DeepSeek V3\",\n\t\tprovider: \"deepseek\",\n\t\tcontextWindow: 64_000,\n\t\tmaxTokens: 8_192,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"deepseek-reasoner\",\n\t\tname: \"DeepSeek R1\",\n\t\tprovider: \"deepseek\",\n\t\tcontextWindow: 64_000,\n\t\tmaxTokens: 8_192,\n\t\tsupportsThinking: true,\n\t},\n\t// OpenAI\n\t{\n\t\tid: \"gpt-4o\",\n\t\tname: \"GPT-4o\",\n\t\tprovider: \"openai\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 16_384,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"o4-mini\",\n\t\tname: \"o4-mini\",\n\t\tprovider: \"openai\",\n\t\tcontextWindow: 200_000,\n\t\tmaxTokens: 100_000,\n\t\tsupportsThinking: true,\n\t},\n]\n\nexport function getProvider(id: string): ProviderDef | undefined {\n\treturn PROVIDERS.find((p) => p.id === id)\n}\n\nexport function getModelsForProvider(providerId: string): Model[] {\n\treturn MODELS.filter((m) => m.provider === providerId)\n}\n","import { chmod, mkdir, readFile, stat, writeFile } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport type { NovaAuth, NovaConfig } from \"../types.ts\"\n\nconst NOVA_DIR = () => join(process.env.HOME ?? \"~\", \".novacode\")\nconst CONFIG_PATH = () => join(NOVA_DIR(), \"config.json\")\nconst AUTH_PATH = () => join(NOVA_DIR(), \"auth.json\")\n\nconst defaultConfig: NovaConfig = {\n\tprovider: \"\",\n\tmodel: \"\",\n}\n\nconst defaultAuth: NovaAuth = {\n\tapiKeys: {},\n}\n\nexport async function configExists(): Promise<boolean> {\n\ttry {\n\t\tawait stat(CONFIG_PATH())\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\nexport async function loadConfig(): Promise<NovaConfig> {\n\ttry {\n\t\tconst raw = JSON.parse(await readFile(CONFIG_PATH(), \"utf-8\"))\n\t\treturn { ...defaultConfig, ...raw }\n\t} catch {\n\t\treturn { ...defaultConfig }\n\t}\n}\n\nexport async function loadAuth(): Promise<NovaAuth> {\n\ttry {\n\t\tconst raw = JSON.parse(await readFile(AUTH_PATH(), \"utf-8\"))\n\t\treturn { ...defaultAuth, ...raw }\n\t} catch {\n\t\treturn { ...defaultAuth }\n\t}\n}\n\nasync function ensureDir(): Promise<void> {\n\tawait mkdir(NOVA_DIR(), { recursive: true })\n}\n\nexport async function saveConfig(config: NovaConfig): Promise<void> {\n\tawait ensureDir()\n\tawait writeFile(CONFIG_PATH(), JSON.stringify(config, null, 2))\n}\n\nexport async function saveAuth(auth: NovaAuth): Promise<void> {\n\tawait ensureDir()\n\tawait writeFile(AUTH_PATH(), JSON.stringify(auth, null, 2))\n\ttry {\n\t\tawait chmod(AUTH_PATH(), 0o600)\n\t} catch {\n\t\t// chmod may fail on some platforms, non-fatal\n\t}\n}\n\nexport function getNovaDir(): string {\n\treturn NOVA_DIR()\n}\n","import { Box, render, Text, useInput } from \"ink\"\nimport { useState } from \"react\"\n\ninterface SelectOption {\n\tvalue: string\n\tlabel: string\n\thint?: string\n}\n\nexport function SelectPrompt({\n\tmessage,\n\toptions,\n\theader,\n\tfooter,\n\tonSelect,\n}: {\n\tmessage: string\n\toptions: SelectOption[]\n\theader?: string\n\tfooter?: string\n\tonSelect: (value: string | null) => void\n}) {\n\tconst [idx, setIdx] = useState(0)\n\n\tuseInput((_, key) => {\n\t\tif (key.escape) {\n\t\t\tonSelect(null)\n\t\t\treturn\n\t\t}\n\t\tif (key.upArrow) {\n\t\t\tsetIdx((i) => (i - 1 + options.length) % options.length)\n\t\t\treturn\n\t\t}\n\t\tif (key.downArrow) {\n\t\t\tsetIdx((i) => (i + 1) % options.length)\n\t\t\treturn\n\t\t}\n\t\tif (key.return) {\n\t\t\tonSelect(options[idx]?.value ?? null)\n\t\t}\n\t})\n\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingX={1}>\n\t\t\t{header && (\n\t\t\t\t<Box marginBottom={1}>\n\t\t\t\t\t<Text>{header}</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t\t<Box marginBottom={1}>\n\t\t\t\t<Text bold>{message}</Text>\n\t\t\t</Box>\n\t\t\t{options.map((opt, i) => (\n\t\t\t\t<Box key={opt.value}>\n\t\t\t\t\t<Text color={i === idx ? \"green\" : undefined}>\n\t\t\t\t\t\t{i === idx ? \"❯ \" : \" \"}\n\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t</Text>\n\t\t\t\t\t{opt.hint && i === idx && <Text dimColor> {opt.hint}</Text>}\n\t\t\t\t</Box>\n\t\t\t))}\n\t\t\t<Box marginTop={1}>\n\t\t\t\t<Text dimColor>↑↓ navigate · Enter select · Esc cancel</Text>\n\t\t\t</Box>\n\t\t\t{footer && (\n\t\t\t\t<Box marginTop={1}>\n\t\t\t\t\t<Text>{footer}</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t</Box>\n\t)\n}\n\nexport function PasswordPrompt({\n\tmessage,\n\tvalidate,\n\tonSubmit,\n}: {\n\tmessage: string\n\tvalidate?: (v: string) => string | undefined\n\tonSubmit: (value: string | null) => void\n}) {\n\tconst [value, setValue] = useState(\"\")\n\tconst [error, setError] = useState(\"\")\n\n\tuseInput((ch, key) => {\n\t\tif (key.escape) {\n\t\t\tonSubmit(null)\n\t\t\treturn\n\t\t}\n\t\tif (key.return) {\n\t\t\tconst err = validate?.(value)\n\t\t\tif (err) {\n\t\t\t\tsetError(err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tonSubmit(value)\n\t\t\treturn\n\t\t}\n\t\tif (key.backspace || key.delete) {\n\t\t\tsetValue((v) => v.slice(0, -1))\n\t\t\tsetError(\"\")\n\t\t\treturn\n\t\t}\n\t\tif (ch) {\n\t\t\tsetValue((v) => v + ch)\n\t\t\tsetError(\"\")\n\t\t}\n\t})\n\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingX={1}>\n\t\t\t<Box marginBottom={1}>\n\t\t\t\t<Text bold>{message}</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Text color=\"green\">│ </Text>\n\t\t\t\t<Text dimColor>{\"*\".repeat(value.length)}</Text>\n\t\t\t\t<Text color=\"green\">│</Text>\n\t\t\t</Box>\n\t\t\t{error && (\n\t\t\t\t<Box>\n\t\t\t\t\t<Text color=\"red\">{error}</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t\t<Box marginTop={1}>\n\t\t\t\t<Text dimColor>Enter submit · Esc cancel</Text>\n\t\t\t</Box>\n\t\t</Box>\n\t)\n}\n\nexport function ConfirmPrompt({\n\tmessage,\n\tonConfirm,\n}: {\n\tmessage: string\n\tonConfirm: (value: boolean | null) => void\n}) {\n\tconst [yes, setYes] = useState(true)\n\n\tuseInput((_, key) => {\n\t\tif (key.escape) {\n\t\t\tonConfirm(null)\n\t\t\treturn\n\t\t}\n\t\tif (key.leftArrow || key.rightArrow || key.tab) {\n\t\t\tsetYes((y) => !y)\n\t\t\treturn\n\t\t}\n\t\tif (key.return) {\n\t\t\tonConfirm(yes)\n\t\t}\n\t})\n\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingX={1}>\n\t\t\t<Box marginBottom={1}>\n\t\t\t\t<Text bold>{message}</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Text color={yes ? \"green\" : undefined}>{yes ? \"❯ \" : \" \"}Yes</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Text color={!yes ? \"red\" : undefined}>{!yes ? \"❯ \" : \" \"}No</Text>\n\t\t\t</Box>\n\t\t\t<Box marginTop={1}>\n\t\t\t\t<Text dimColor>←→ toggle · Enter confirm · Esc cancel</Text>\n\t\t\t</Box>\n\t\t</Box>\n\t)\n}\n\n// Standalone wrappers for use outside the main TUI (e.g. onboarding)\n\nexport function standaloneSelect(\n\tmessage: string,\n\toptions: SelectOption[],\n\theader?: string,\n\tfooter?: string,\n): Promise<string | null> {\n\treturn new Promise((resolve) => {\n\t\tconst { unmount } = render(\n\t\t\t<SelectPrompt\n\t\t\t\tmessage={message}\n\t\t\t\toptions={options}\n\t\t\t\theader={header}\n\t\t\t\tfooter={footer}\n\t\t\t\tonSelect={(v) => {\n\t\t\t\t\tunmount()\n\t\t\t\t\tresolve(v)\n\t\t\t\t}}\n\t\t\t/>,\n\t\t)\n\t})\n}\n\nexport function standalonePassword(\n\tmessage: string,\n\tvalidate?: (v: string) => string | undefined,\n): Promise<string | null> {\n\treturn new Promise((resolve) => {\n\t\tconst { unmount } = render(\n\t\t\t<PasswordPrompt\n\t\t\t\tmessage={message}\n\t\t\t\tvalidate={validate}\n\t\t\t\tonSubmit={(v) => {\n\t\t\t\t\tunmount()\n\t\t\t\t\tresolve(v)\n\t\t\t\t}}\n\t\t\t/>,\n\t\t)\n\t})\n}\n","import chalk from \"chalk\"\nimport { getModelsForProvider, getProvider, PROVIDERS } from \"../config/providers.ts\"\nimport { saveAuth, saveConfig } from \"../config/store.ts\"\nimport { standalonePassword, standaloneSelect } from \"../tui/prompts.tsx\"\nimport type { NovaConfig } from \"../types.ts\"\n\nexport async function runOnboarding(): Promise<NovaConfig> {\n\tconsole.log(chalk.bold.cyan(\"\\n⚡ Nova — your coding companion\\n\"))\n\n\tconst providerId = await standaloneSelect(\n\t\t\"Pick a provider\",\n\t\tPROVIDERS.map((p) => ({ value: p.id, label: p.name })),\n\t)\n\tif (!providerId) {\n\t\tconsole.log(chalk.dim(\"Cancelled\"))\n\t\tprocess.exit(0)\n\t}\n\n\tconst provider = getProvider(providerId)\n\tif (!provider) {\n\t\tconsole.log(chalk.red(\"Unknown provider\"))\n\t\tprocess.exit(1)\n\t}\n\n\tconst apiKey = await standalonePassword(`Enter ${provider.name} API key`)\n\tif (!apiKey) {\n\t\tconsole.log(chalk.dim(\"Cancelled\"))\n\t\tprocess.exit(0)\n\t}\n\n\tconst models = getModelsForProvider(providerId)\n\tconst modelId = await standaloneSelect(\n\t\t\"Pick a default model\",\n\t\tmodels.map((m) => ({\n\t\t\tvalue: m.id,\n\t\t\tlabel: `${m.name} (${(m.contextWindow / 1000).toFixed(0)}k ctx)`,\n\t\t})),\n\t)\n\tif (!modelId) {\n\t\tconsole.log(chalk.dim(\"Cancelled\"))\n\t\tprocess.exit(0)\n\t}\n\n\tconst config: NovaConfig = {\n\t\tprovider: providerId,\n\t\tmodel: modelId,\n\t}\n\n\tawait saveConfig(config)\n\tawait saveAuth({ apiKeys: { [providerId]: apiKey } })\n\n\tconsole.log(chalk.green(\"\\n✓ Ready. Type your prompt or /help for commands\\n\"))\n\treturn config\n}\n","/**\n * Skill discovery: scans configured directories for SKILL.md files,\n * parses YAML frontmatter, validates, and returns Skill objects.\n */\n\nimport { readdir } from \"node:fs/promises\"\nimport { homedir } from \"node:os\"\nimport { join, resolve } from \"node:path\"\nimport type { Skill } from \"../types.ts\"\n\nconst SKILL_FILE = \"SKILL.md\"\n\ninterface RawSkill {\n\tname: string\n\tdescription: string\n\tpath: string\n\tsource: \"global\" | \"project\"\n}\n\nconst NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/\n\nfunction validateName(name: string): { valid: boolean; warning?: string } {\n\tif (name.length > 64)\n\t\treturn { valid: false, warning: `Skill name exceeds 64 characters: \"${name}\"` }\n\tif (!NAME_RE.test(name))\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\twarning: `Skill name contains invalid characters (use lowercase, numbers, hyphens): \"${name}\"`,\n\t\t}\n\treturn { valid: true }\n}\n\nfunction parseFrontmatter(content: string): { name?: string; description?: string } | null {\n\tconst match = content.match(/^---\\s*\\n([\\s\\S]*?)\\n---/)\n\tif (!match) return null\n\tconst yaml = match[1]!\n\tlet name: string | undefined\n\tlet description: string | undefined\n\tfor (const line of yaml.split(\"\\n\")) {\n\t\tconst n = line.match(/^name:\\s*(.+)$/)\n\t\tif (n) name = n[1]!.trim()\n\t\tconst d = line.match(/^description:\\s*(.+)$/)\n\t\tif (d) description = d[1]!.trim()\n\t}\n\tif (!name) return null\n\treturn { name, description }\n}\n\nasync function readSkill(dirPath: string, source: \"global\" | \"project\"): Promise<RawSkill | null> {\n\tconst skillPath = join(dirPath, SKILL_FILE)\n\ttry {\n\t\tconst { readFile } = await import(\"node:fs/promises\")\n\t\tconst content = await readFile(skillPath, \"utf-8\")\n\t\tconst fm = parseFrontmatter(content)\n\t\tif (!fm?.name) return null\n\t\tif (!fm.description) {\n\t\t\tconsole.warn(`Skill missing description, skipping: ${dirPath}`)\n\t\t\treturn null\n\t\t}\n\t\treturn { name: fm.name, description: fm.description, path: dirPath, source }\n\t} catch {\n\t\treturn null\n\t}\n}\n\nasync function scanDirectory(dir: string, source: \"global\" | \"project\"): Promise<RawSkill[]> {\n\tconst skills: RawSkill[] = []\n\ttry {\n\t\tconst entries = await readdir(dir, { withFileTypes: true })\n\t\tfor (const entry of entries) {\n\t\t\tif (!entry.isDirectory()) continue\n\t\t\tconst fullPath = join(dir, entry.name)\n\t\t\tconst skill = await readSkill(fullPath, source)\n\t\t\tif (skill) skills.push(skill)\n\t\t}\n\t} catch {\n\t\t// Directory doesn't exist, skip\n\t}\n\treturn skills\n}\n\nexport async function discoverSkills(cwd: string): Promise<Skill[]> {\n\tconst globalDirs = [join(homedir(), \".agents\", \"skills\"), join(homedir(), \".novacode\", \"skills\")]\n\tconst projectDirs = [resolve(cwd, \".agents\", \"skills\"), resolve(cwd, \".novacode\", \"skills\")]\n\n\tconst raw: RawSkill[] = []\n\n\tfor (const dir of globalDirs) {\n\t\traw.push(...(await scanDirectory(dir, \"global\")))\n\t}\n\tfor (const dir of projectDirs) {\n\t\traw.push(...(await scanDirectory(dir, \"project\")))\n\t}\n\n\t// Deduplicate by name, keep first found\n\tconst seen = new Set<string>()\n\tconst skills: Skill[] = []\n\n\tfor (const s of raw) {\n\t\tconst nameCheck = validateName(s.name)\n\t\tif (!nameCheck.valid) {\n\t\t\tconsole.warn(nameCheck.warning)\n\t\t}\n\n\t\tif (seen.has(s.name)) {\n\t\t\tconsole.warn(`Duplicate skill name \"${s.name}\", keeping first occurrence`)\n\t\t\tcontinue\n\t\t}\n\t\tseen.add(s.name)\n\n\t\tif (s.description.length > 1024) {\n\t\t\tconsole.warn(`Skill description exceeds 1024 characters: \"${s.name}\"`)\n\t\t}\n\n\t\tskills.push({\n\t\t\tname: s.name,\n\t\t\tdescription: s.description,\n\t\t\tpath: s.path,\n\t\t\tsource: s.source,\n\t\t})\n\t}\n\n\treturn skills\n}\n","/**\n * Resource loader: discovers skills, loads AGENTS.md context, and exposes\n * a single load() call that returns everything the agent needs at startup.\n */\n\nimport { readFile } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport { discoverSkills } from \"./skills/discovery.ts\"\nimport type { Skill } from \"./types.ts\"\n\nexport interface Resources {\n\tskills: Skill[]\n\tagentsMd: string | null\n}\n\nexport async function loadResources(cwd: string): Promise<Resources> {\n\tconst [skills, agentsMd] = await Promise.all([discoverSkills(cwd), readAgentsMd(cwd)])\n\treturn { skills, agentsMd }\n}\n\nasync function readAgentsMd(cwd: string): Promise<string | null> {\n\ttry {\n\t\treturn await readFile(join(cwd, \"AGENTS.md\"), \"utf-8\")\n\t} catch {\n\t\treturn null\n\t}\n}\n","import { mkdirSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { DatabaseSync } from \"node:sqlite\"\n\nconst SCHEMA = `\nCREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n cwd TEXT NOT NULL,\n model TEXT NOT NULL,\n provider TEXT NOT NULL,\n title TEXT,\n parent_session_id TEXT,\n end_reason TEXT,\n created INTEGER NOT NULL,\n updated INTEGER NOT NULL,\n input_tokens INTEGER DEFAULT 0,\n output_tokens INTEGER DEFAULT 0,\n message_count INTEGER DEFAULT 0\n);\n\nCREATE TABLE IF NOT EXISTS messages (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n seq INTEGER NOT NULL,\n role TEXT NOT NULL,\n content TEXT,\n tool_call_id TEXT,\n tool_name TEXT,\n tool_args TEXT,\n model TEXT,\n provider TEXT,\n usage_input INTEGER DEFAULT 0,\n usage_output INTEGER DEFAULT 0,\n stop_reason TEXT,\n is_error INTEGER DEFAULT 0,\n ts INTEGER NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_sessions_updated ON sessions(updated DESC);\nCREATE INDEX IF NOT EXISTS idx_sessions_parent ON sessions(parent_session_id);\nCREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id, seq);\n`\n\nlet db: DatabaseSync | null = null\n\nexport function getDb(path?: string): DatabaseSync {\n\tif (db) return db\n\n\tconst dbPath = path ?? join(process.env.HOME ?? \"~\", \".novacode\", \"state.db\")\n\tconst dir = join(dbPath, \"..\")\n\tmkdirSync(dir, { recursive: true })\n\n\tdb = new DatabaseSync(dbPath, {\n\t\tenableForeignKeyConstraints: true,\n\t})\n\tdb.exec(\"PRAGMA journal_mode = WAL\")\n\tdb.exec(SCHEMA)\n\treturn db\n}\n\nexport function closeDb(): void {\n\tif (db) {\n\t\tdb.close()\n\t\tdb = null\n\t}\n}\n\nexport function resetDb(): void {\n\tdb = null\n}\n","import { DatabaseSync } from \"node:sqlite\"\nimport type { ContentPart, Msg, PendingSession, Session } from \"../types.ts\"\nimport { closeDb, getDb } from \"./db.ts\"\n\nfunction generateId(): string {\n\treturn `${Date.now().toString(36)}-${crypto.randomUUID().slice(0, 8)}`\n}\n\nconst JSON_SENTINEL = \"$json:\"\n\nfunction serializeContent(content: string | ContentPart[]): string | null {\n\tif (content === undefined || content === null) return null\n\tif (typeof content === \"string\") return content\n\treturn JSON_SENTINEL + JSON.stringify(content)\n}\n\nfunction deserializeContent(raw: string | null): string | ContentPart[] {\n\tif (raw === null) return \"\"\n\tif (raw.startsWith(JSON_SENTINEL)) return JSON.parse(raw.slice(JSON_SENTINEL.length))\n\treturn raw\n}\n\nfunction rowToMsg(row: Record<string, unknown>): Msg {\n\tconst role = row.role as string\n\tconst content = deserializeContent(row.content as string | null)\n\tconst ts = row.ts as number\n\n\tif (role === \"user\") {\n\t\treturn { role: \"user\", content: content as string | ContentPart[], ts }\n\t}\n\n\tif (role === \"assistant\") {\n\t\treturn {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: (content || []) as ContentPart[],\n\t\t\tmodel: (row.model as string) ?? \"\",\n\t\t\tprovider: (row.provider as string) ?? \"\",\n\t\t\tusage: { in: (row.usage_input as number) ?? 0, out: (row.usage_output as number) ?? 0 },\n\t\t\tstop: (row.stop_reason as \"stop\" | \"length\" | \"tool_use\" | \"error\" | \"aborted\") ?? \"stop\",\n\t\t\terror: undefined,\n\t\t\tts,\n\t\t}\n\t}\n\n\t// tool_result\n\treturn {\n\t\trole: \"tool_result\",\n\t\tcallId: (row.tool_call_id as string) ?? \"\",\n\t\ttool: (row.tool_name as string) ?? \"\",\n\t\targs: row.tool_args ? JSON.parse(row.tool_args as string) : undefined,\n\t\tcontent: (content || []) as ContentPart[],\n\t\tisError: !!(row.is_error as number),\n\t\tts,\n\t}\n}\n\nfunction rowToSession(row: Record<string, unknown>): Session {\n\treturn {\n\t\tid: row.id as string,\n\t\tcwd: row.cwd as string,\n\t\tmodel: row.model as string,\n\t\tprovider: row.provider as string,\n\t\ttitle: (row.title as string | null) ?? null,\n\t\tparentSessionId: (row.parent_session_id as string | null) ?? null,\n\t\tendReason: (row.end_reason as string | null) ?? null,\n\t\tcreated: row.created as number,\n\t\tupdated: row.updated as number,\n\t\tinputTokens: (row.input_tokens as number) ?? 0,\n\t\toutputTokens: (row.output_tokens as number) ?? 0,\n\t\tmessageCount: (row.message_count as number) ?? 0,\n\t}\n}\n\nexport class SessionStore {\n\t#db: DatabaseSync\n\t#pendingSessions = new Map<string, PendingSession>()\n\n\tconstructor(dbOrPath?: DatabaseSync | string) {\n\t\tif (dbOrPath instanceof DatabaseSync) {\n\t\t\tthis.#db = dbOrPath\n\t\t} else {\n\t\t\tthis.#db = getDb(dbOrPath)\n\t\t}\n\t}\n\n\t#ensurePersisted(sessionId: string): void {\n\t\tconst pending = this.#pendingSessions.get(sessionId)\n\t\tif (!pending) return\n\n\t\tthis.#db\n\t\t\t.prepare(\n\t\t\t\t`INSERT OR IGNORE INTO sessions (id, cwd, model, provider, title, parent_session_id, end_reason, created, updated, input_tokens, output_tokens, message_count)\n\t\t\t\t VALUES (?, ?, ?, ?, ?, ?, NULL, ?, ?, 0, 0, 0)`,\n\t\t\t)\n\t\t\t.run(\n\t\t\t\tsessionId,\n\t\t\t\tpending.cwd,\n\t\t\t\tpending.model,\n\t\t\t\tpending.provider,\n\t\t\t\tpending.title,\n\t\t\t\tpending.parentSessionId,\n\t\t\t\tpending.created,\n\t\t\t\tpending.created,\n\t\t\t)\n\t\tthis.#pendingSessions.delete(sessionId)\n\t}\n\n\tasync create(cwd: string, model: string, provider: string): Promise<Session> {\n\t\tconst id = generateId()\n\t\tconst now = Date.now()\n\t\tthis.#pendingSessions.set(id, {\n\t\t\tcwd,\n\t\t\tmodel,\n\t\t\tprovider,\n\t\t\ttitle: null,\n\t\t\tparentSessionId: null,\n\t\t\tcreated: now,\n\t\t})\n\t\treturn {\n\t\t\tid,\n\t\t\tcwd,\n\t\t\tmodel,\n\t\t\tprovider,\n\t\t\ttitle: null,\n\t\t\tparentSessionId: null,\n\t\t\tendReason: null,\n\t\t\tcreated: now,\n\t\t\tupdated: now,\n\t\t\tinputTokens: 0,\n\t\t\toutputTokens: 0,\n\t\t\tmessageCount: 0,\n\t\t}\n\t}\n\n\tasync get(id: string): Promise<Session | null> {\n\t\tconst row = this.#db.prepare(\"SELECT * FROM sessions WHERE id = ?\").get(id) as\n\t\t\t| Record<string, unknown>\n\t\t\t| undefined\n\t\tif (row) return rowToSession(row)\n\n\t\tconst pending = this.#pendingSessions.get(id)\n\t\tif (pending) {\n\t\t\treturn {\n\t\t\t\tid,\n\t\t\t\tcwd: pending.cwd,\n\t\t\t\tmodel: pending.model,\n\t\t\t\tprovider: pending.provider,\n\t\t\t\ttitle: pending.title,\n\t\t\t\tparentSessionId: pending.parentSessionId,\n\t\t\t\tendReason: null,\n\t\t\t\tcreated: pending.created,\n\t\t\t\tupdated: pending.created,\n\t\t\t\tinputTokens: 0,\n\t\t\t\toutputTokens: 0,\n\t\t\t\tmessageCount: 0,\n\t\t\t}\n\t\t}\n\t\treturn null\n\t}\n\n\tasync list(limit = 10): Promise<Session[]> {\n\t\tconst rows = this.#db\n\t\t\t.prepare(\"SELECT * FROM sessions WHERE end_reason IS NULL ORDER BY updated DESC LIMIT ?\")\n\t\t\t.all(limit) as Record<string, unknown>[]\n\t\treturn rows.map(rowToSession)\n\t}\n\n\tasync latest(): Promise<Session | null> {\n\t\tconst row = this.#db\n\t\t\t.prepare(\"SELECT * FROM sessions WHERE end_reason IS NULL ORDER BY updated DESC LIMIT 1\")\n\t\t\t.get() as Record<string, unknown> | undefined\n\t\treturn row ? rowToSession(row) : null\n\t}\n\n\tasync delete(id: string): Promise<boolean> {\n\t\tconst pending = this.#pendingSessions.delete(id)\n\t\tconst result = this.#db.prepare(\"DELETE FROM sessions WHERE id = ?\").run(id)\n\t\treturn pending || result.changes > 0\n\t}\n\n\tasync deleteAll(): Promise<void> {\n\t\tthis.#pendingSessions.clear()\n\t\tthis.#db.exec(\"DELETE FROM messages; DELETE FROM sessions\")\n\t}\n\n\tasync append(sessionId: string, msg: Msg): Promise<void> {\n\t\tthis.#ensurePersisted(sessionId)\n\t\tconst now = Date.now()\n\n\t\tconst role = msg.role\n\t\tlet content: string | null = null\n\t\tlet toolCallId: string | null = null\n\t\tlet toolName: string | null = null\n\t\tlet toolArgs: string | null = null\n\t\tlet model: string | null = null\n\t\tlet provider: string | null = null\n\t\tlet usageInput = 0\n\t\tlet usageOutput = 0\n\t\tlet stopReason: string | null = null\n\t\tlet isError = 0\n\n\t\tif (role === \"user\") {\n\t\t\tcontent = serializeContent(msg.content)\n\t\t} else if (role === \"assistant\") {\n\t\t\tcontent = serializeContent(msg.content)\n\t\t\tmodel = msg.model ?? null\n\t\t\tprovider = msg.provider ?? null\n\t\t\tusageInput = msg.usage?.in ?? 0\n\t\t\tusageOutput = msg.usage?.out ?? 0\n\t\t\tstopReason = msg.stop ?? null\n\t\t\tif (msg.error) isError = 1\n\t\t} else if (role === \"tool_result\") {\n\t\t\tcontent = serializeContent(msg.content)\n\t\t\ttoolCallId = msg.callId ?? null\n\t\t\ttoolName = msg.tool ?? null\n\t\t\ttoolArgs = msg.args ? JSON.stringify(msg.args) : null\n\t\t\tisError = msg.isError ? 1 : 0\n\t\t}\n\n\t\tconst seqRow = this.#db\n\t\t\t.prepare(\"SELECT COALESCE(MAX(seq), 0) + 1 AS next_seq FROM messages WHERE session_id = ?\")\n\t\t\t.get(sessionId) as Record<string, unknown>\n\t\tconst seq = seqRow?.next_seq as number\n\n\t\tthis.#db\n\t\t\t.prepare(\n\t\t\t\t`INSERT INTO messages (session_id, seq, role, content, tool_call_id, tool_name, tool_args, model, provider, usage_input, usage_output, stop_reason, is_error, ts)\n\t\t\t\t VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n\t\t\t)\n\t\t\t.run(\n\t\t\t\tsessionId,\n\t\t\t\tseq,\n\t\t\t\trole,\n\t\t\t\tcontent,\n\t\t\t\ttoolCallId,\n\t\t\t\ttoolName,\n\t\t\t\ttoolArgs,\n\t\t\t\tmodel,\n\t\t\t\tprovider,\n\t\t\t\tusageInput,\n\t\t\t\tusageOutput,\n\t\t\t\tstopReason,\n\t\t\t\tisError,\n\t\t\t\tmsg.ts ?? now,\n\t\t\t)\n\n\t\tthis.#db\n\t\t\t.prepare(\n\t\t\t\t\"UPDATE sessions SET message_count = message_count + 1, updated = ?, input_tokens = input_tokens + ?, output_tokens = output_tokens + ? WHERE id = ?\",\n\t\t\t)\n\t\t\t.run(now, usageInput, usageOutput, sessionId)\n\t}\n\n\tasync messages(sessionId: string): Promise<Msg[]> {\n\t\tconst rows = this.#db\n\t\t\t.prepare(\"SELECT * FROM messages WHERE session_id = ? ORDER BY seq\")\n\t\t\t.all(sessionId) as Record<string, unknown>[]\n\t\treturn rows.map(rowToMsg)\n\t}\n\n\tasync history(sessionId: string): Promise<Msg[]> {\n\t\tconst lineage = this.#getLineage(sessionId)\n\t\tif (lineage.length <= 1) {\n\t\t\treturn this.messages(sessionId)\n\t\t}\n\n\t\t// Build CASE ordering from lineage (root first, tip last)\n\t\tconst caseExpr = lineage.map((id, i) => `WHEN '${id}' THEN ${i}`).join(\" \")\n\t\tconst rows = this.#db\n\t\t\t.prepare(\n\t\t\t\t`SELECT m.* FROM messages m\n\t\t\t\t WHERE m.session_id IN (${lineage.map(() => \"?\").join(\",\")})\n\t\t\t\t ORDER BY CASE m.session_id ${caseExpr} END ASC, m.seq ASC`,\n\t\t\t)\n\t\t\t.all(...lineage) as Record<string, unknown>[]\n\t\treturn rows.map(rowToMsg)\n\t}\n\n\tasync messageCount(sessionId: string): Promise<number> {\n\t\tconst row = this.#db\n\t\t\t.prepare(\"SELECT message_count FROM sessions WHERE id = ?\")\n\t\t\t.get(sessionId) as Record<string, unknown> | undefined\n\t\treturn (row?.message_count as number) ?? 0\n\t}\n\n\tasync setTitle(sessionId: string, title: string): Promise<void> {\n\t\tthis.#ensurePersisted(sessionId)\n\t\tthis.#db\n\t\t\t.prepare(\"UPDATE sessions SET title = ?, updated = ? WHERE id = ?\")\n\t\t\t.run(title, Date.now(), sessionId)\n\t}\n\n\tasync replaceMessages(sessionId: string, msgs: Msg[]): Promise<void> {\n\t\tif (msgs.length > 0) {\n\t\t\tthis.#ensurePersisted(sessionId)\n\t\t}\n\t\tthis.#db.prepare(\"DELETE FROM messages WHERE session_id = ?\").run(sessionId)\n\t\tthis.#db\n\t\t\t.prepare(\"UPDATE sessions SET message_count = 0, updated = ? WHERE id = ?\")\n\t\t\t.run(Date.now(), sessionId)\n\n\t\tlet seq = 0\n\t\tfor (const msg of msgs) {\n\t\t\tseq++\n\t\t\tawait this.#insertMessage(sessionId, seq, msg)\n\t\t}\n\t\tthis.#db\n\t\t\t.prepare(\"UPDATE sessions SET message_count = ?, updated = ? WHERE id = ?\")\n\t\t\t.run(seq, Date.now(), sessionId)\n\t}\n\n\tasync endSession(id: string, reason: string): Promise<void> {\n\t\tthis.#ensurePersisted(id)\n\t\tthis.#db\n\t\t\t.prepare(\"UPDATE sessions SET end_reason = ?, updated = ? WHERE id = ?\")\n\t\t\t.run(reason, Date.now(), id)\n\t}\n\n\tasync createContinuation(\n\t\tparentId: string,\n\t\tcwd: string,\n\t\tmodel: string,\n\t\tprovider: string,\n\t): Promise<Session> {\n\t\tconst id = generateId()\n\t\tconst now = Date.now()\n\t\tthis.#pendingSessions.set(id, {\n\t\t\tcwd,\n\t\t\tmodel,\n\t\t\tprovider,\n\t\t\ttitle: null,\n\t\t\tparentSessionId: parentId,\n\t\t\tcreated: now,\n\t\t})\n\n\t\treturn {\n\t\t\tid,\n\t\t\tcwd,\n\t\t\tmodel,\n\t\t\tprovider,\n\t\t\ttitle: null,\n\t\t\tparentSessionId: parentId,\n\t\t\tendReason: null,\n\t\t\tcreated: now,\n\t\t\tupdated: now,\n\t\t\tinputTokens: 0,\n\t\t\toutputTokens: 0,\n\t\t\tmessageCount: 0,\n\t\t}\n\t}\n\n\t#getLineage(sessionId: string): string[] {\n\t\tconst ids: string[] = []\n\t\tlet current = sessionId\n\t\tconst visited = new Set<string>()\n\n\t\twhile (current && !visited.has(current)) {\n\t\t\tids.push(current)\n\t\t\tvisited.add(current)\n\t\t\tconst row = this.#db\n\t\t\t\t.prepare(\"SELECT parent_session_id FROM sessions WHERE id = ?\")\n\t\t\t\t.get(current) as Record<string, unknown> | undefined\n\t\t\tif (row) {\n\t\t\t\tcurrent = (row.parent_session_id as string | null) ?? \"\"\n\t\t\t} else {\n\t\t\t\tconst pending = this.#pendingSessions.get(current)\n\t\t\t\tcurrent = pending?.parentSessionId ?? \"\"\n\t\t\t}\n\t\t}\n\n\t\tids.reverse()\n\t\treturn ids\n\t}\n\n\tasync #insertMessage(sessionId: string, seq: number, msg: Msg): Promise<void> {\n\t\tconst role = msg.role\n\t\tlet content: string | null = null\n\t\tlet toolCallId: string | null = null\n\t\tlet toolName: string | null = null\n\t\tlet toolArgs: string | null = null\n\t\tlet model: string | null = null\n\t\tlet provider: string | null = null\n\t\tlet usageInput = 0\n\t\tlet usageOutput = 0\n\t\tlet stopReason: string | null = null\n\t\tlet isError = 0\n\n\t\tif (role === \"user\") {\n\t\t\tcontent = serializeContent(msg.content)\n\t\t} else if (role === \"assistant\") {\n\t\t\tcontent = serializeContent(msg.content)\n\t\t\tmodel = msg.model ?? null\n\t\t\tprovider = msg.provider ?? null\n\t\t\tusageInput = msg.usage?.in ?? 0\n\t\t\tusageOutput = msg.usage?.out ?? 0\n\t\t\tstopReason = msg.stop ?? null\n\t\t\tif (msg.error) isError = 1\n\t\t} else if (role === \"tool_result\") {\n\t\t\tcontent = serializeContent(msg.content)\n\t\t\ttoolCallId = msg.callId ?? null\n\t\t\ttoolName = msg.tool ?? null\n\t\t\ttoolArgs = msg.args ? JSON.stringify(msg.args) : null\n\t\t\tisError = msg.isError ? 1 : 0\n\t\t}\n\n\t\tthis.#db\n\t\t\t.prepare(\n\t\t\t\t`INSERT INTO messages (session_id, seq, role, content, tool_call_id, tool_name, tool_args, model, provider, usage_input, usage_output, stop_reason, is_error, ts)\n\t\t\t\t VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n\t\t\t)\n\t\t\t.run(\n\t\t\t\tsessionId,\n\t\t\t\tseq,\n\t\t\t\trole,\n\t\t\t\tcontent,\n\t\t\t\ttoolCallId,\n\t\t\t\ttoolName,\n\t\t\t\ttoolArgs,\n\t\t\t\tmodel,\n\t\t\t\tprovider,\n\t\t\t\tusageInput,\n\t\t\t\tusageOutput,\n\t\t\t\tstopReason,\n\t\t\t\tisError,\n\t\t\t\tmsg.ts ?? Date.now(),\n\t\t\t)\n\t}\n\n\tasync prune(): Promise<void> {\n\t\tthis.#db.exec(\"DELETE FROM sessions WHERE message_count = 0\")\n\t}\n\n\tclose(): void {\n\t\tcloseDb()\n\t}\n}\n\nlet _store: SessionStore | null = null\n\nexport async function getSessionStore(dir?: string): Promise<SessionStore> {\n\tif (_store) return _store\n\t_store = new SessionStore(dir ? `${dir}/state.db` : undefined)\n\treturn _store\n}\n","/**\n * Filesystem tools for reading, writing, and editing files.\n * Includes safety checks to prevent path traversal.\n */\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\"\nimport { dirname, extname, resolve } from \"node:path\"\nimport type { Tool, ToolResult } from \"../types.ts\"\nimport { getRelativeIfInside, textPart } from \"../util.ts\"\n\n// Extensions we return as base64 images instead of text\nconst IMAGES = new Set([\".jpg\", \".jpeg\", \".png\", \".gif\", \".webp\"])\n\nfunction safePath(cwd: string, p: string): string {\n\tconst abs = resolve(cwd, p)\n\tif (abs !== cwd && !abs.startsWith(`${cwd}/`)) {\n\t\tthrow new Error(`Path outside project: ${p}`)\n\t}\n\treturn abs\n}\n\nexport function readTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"read\",\n\t\t\tdescription:\n\t\t\t\t\"Read file contents. Supports text and images (jpg, png, gif, webp). Text output is truncated to 2000 lines.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Path to file (relative or absolute)\" },\n\t\t\t\t\toffset: { type: \"number\", description: \"Start line (1-based, default 1)\" },\n\t\t\t\t\tlimit: { type: \"number\", description: \"Max lines to read (default 2000)\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"path\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst filePath = safePath(cwd, args.path as string)\n\t\t\t\t// Return images as base64 so the LLM can process them visually\n\t\t\t\tconst ext = extname(filePath).toLowerCase()\n\t\t\t\tif (IMAGES.has(ext)) {\n\t\t\t\t\tconst buf = await readFile(filePath)\n\t\t\t\t\tconst b64 = buf.toString(\"base64\")\n\t\t\t\t\tconst mime = ext === \".jpg\" ? \"image/jpeg\" : `image/${ext.slice(1)}`\n\t\t\t\t\treturn { content: [{ type: \"image\", data: b64, mime }], isError: false }\n\t\t\t\t}\n\n\t\t\t\tconst content = await readFile(filePath, \"utf-8\")\n\t\t\t\tconst lines = content.split(\"\\n\")\n\t\t\t\tconst offset = Math.max(0, (Number(args.offset ?? 1) || 1) - 1)\n\t\t\t\tconst limit = Number(args.limit ?? 2000) || 2000\n\t\t\t\tconst slice = lines.slice(offset, offset + limit)\n\t\t\t\tconst truncated = offset + limit < lines.length\n\n\t\t\t\tconst out = slice.join(\"\\n\")\n\t\t\t\tconst suffix = truncated ? `\\n…${lines.length - offset - limit} more lines` : \"\"\n\n\t\t\t\treturn { content: [textPart(out + suffix)], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error reading file: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\nexport function writeTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"write\",\n\t\t\tdescription: \"Write content to a file. Creates the file and parent directories if needed.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Path to file\" },\n\t\t\t\t\tcontent: { type: \"string\", description: \"Content to write\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"path\", \"content\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst filePath = safePath(cwd, args.path as string)\n\t\t\t\tconst content = args.content as string\n\t\t\t\tawait mkdir(dirname(filePath), { recursive: true })\n\t\t\t\tawait writeFile(filePath, content)\n\t\t\t\tconst relPath = getRelativeIfInside(cwd, filePath)\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Wrote ${content.length} bytes → ${relPath}`)],\n\t\t\t\t\tisError: false,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error writing file: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n// Requires oldText to be unique to avoid ambiguous replacements.\nexport function editTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"edit\",\n\t\t\tdescription:\n\t\t\t\t\"Edit a file using exact text replacement. Each edit's oldText must be unique in the file.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Path to file\" },\n\t\t\t\t\tedits: {\n\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Array of {oldText, newText} replacements. oldText must be unique. Non-overlapping.\",\n\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\toldText: { type: \"string\", description: \"Exact text to find (must be unique)\" },\n\t\t\t\t\t\t\t\tnewText: { type: \"string\", description: \"Replacement text\" },\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\trequired: [\"oldText\", \"newText\"],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\trequired: [\"path\", \"edits\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst filePath = safePath(cwd, args.path as string)\n\t\t\t\tlet content: string\n\t\t\t\ttry {\n\t\t\t\t\tcontent = await readFile(filePath, \"utf-8\")\n\t\t\t\t} catch {\n\t\t\t\t\treturn { content: [textPart(`File not found: ${args.path}`)], isError: true }\n\t\t\t\t}\n\t\t\t\tconst edits = args.edits as Array<{ oldText: string; newText: string }>\n\n\t\t\t\t// Validate all edits before applying any — avoids partial writes on bad input\n\t\t\t\tfor (const edit of edits) {\n\t\t\t\t\tconst count = content.split(edit.oldText).length - 1\n\t\t\t\t\tif (count === 0) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [textPart(`oldText not found: \"${edit.oldText.slice(0, 80)}…\"`)],\n\t\t\t\t\t\t\tisError: true,\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Ambiguous match would replace the wrong occurrence\n\t\t\t\t\tif (count > 1) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\ttextPart(\n\t\t\t\t\t\t\t\t\t`oldText found ${count} times — add surrounding context to make it unique: \"${edit.oldText.slice(0, 60)}…\"`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tisError: true,\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Apply edits sequentially\n\t\t\t\tfor (const edit of edits) {\n\t\t\t\t\tcontent = content.replace(edit.oldText, edit.newText)\n\t\t\t\t}\n\n\t\t\t\tawait writeFile(filePath, content)\n\t\t\t\tconst relPath = getRelativeIfInside(cwd, filePath)\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\ttextPart(\n\t\t\t\t\t\t\t`Edited ${relPath} (${edits.length} replacement${edits.length > 1 ? \"s\" : \"\"})`,\n\t\t\t\t\t\t),\n\t\t\t\t\t],\n\t\t\t\t\tisError: false,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error editing file: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","/**\n * Git tools for executing safe repository operations programmatically.\n */\nimport { spawn } from \"node:child_process\"\nimport type { Tool, ToolResult } from \"../types.ts\"\nimport { textPart } from \"../util.ts\"\n\nexport function gitTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"git\",\n\t\t\tdescription:\n\t\t\t\t\"Execute safe, non-interactive git commands (status, diff, log, add, commit) in the repository.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\taction: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\tenum: [\"status\", \"diff\", \"log\", \"add\", \"commit\"],\n\t\t\t\t\t\tdescription: \"The git action to execute\",\n\t\t\t\t\t},\n\t\t\t\t\targs: {\n\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\tdescription: \"Optional additional arguments or file paths for the git action\",\n\t\t\t\t\t\titems: { type: \"string\" },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\trequired: [\"action\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\tconst action = args.action as string\n\t\t\tconst extraArgs = (args.args as string[]) || []\n\n\t\t\tconst allowed = new Set([\"status\", \"diff\", \"log\", \"add\", \"commit\"])\n\t\t\tif (!allowed.has(action)) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: Git action '${action}' is not supported.`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst cmd = [\"git\", action, ...extraArgs]\n\t\t\t\tconst proc = spawn(cmd[0]!, cmd.slice(1), {\n\t\t\t\t\tcwd,\n\t\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\t\tenv: { ...process.env, PAGER: \"cat\" },\n\t\t\t\t})\n\n\t\t\t\tlet stdout = \"\"\n\t\t\t\tlet stderr = \"\"\n\t\t\t\tproc.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tstdout += chunk.toString()\n\t\t\t\t})\n\t\t\t\tproc.stderr.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tstderr += chunk.toString()\n\t\t\t\t})\n\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tproc.kill(\"SIGKILL\")\n\t\t\t\t\tproc.stdout.destroy()\n\t\t\t\t\tproc.stderr.destroy()\n\t\t\t\t}\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true })\n\n\t\t\t\tlet exitCode: number\n\t\t\t\ttry {\n\t\t\t\t\texitCode = await new Promise<number>((resolve, reject) => {\n\t\t\t\t\t\tproc.on(\"error\", reject)\n\t\t\t\t\t\tproc.on(\"close\", (code) => resolve(code ?? -1))\n\t\t\t\t\t})\n\t\t\t\t} finally {\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort)\n\t\t\t\t}\n\n\t\t\t\t// Prevent context window blowout by truncating very large outputs\n\t\t\t\tconst MAX = 50_000\n\t\t\t\tlet out = \"\"\n\t\t\t\tif (stdout) out += stdout.slice(0, MAX)\n\t\t\t\tif (stderr) {\n\t\t\t\t\tif (out) out += \"\\n\"\n\t\t\t\t\tout += stderr.slice(0, MAX - out.length)\n\t\t\t\t}\n\t\t\t\tif (out.length >= MAX) out += \"\\n…truncated\"\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(out || \"(no output)\")],\n\t\t\t\t\tisError: exitCode !== 0,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error running git: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","/**\n * Search tools for finding files and content.\n * Uses 'rg' (ripgrep) if available, falling back to a pure JS implementation.\n */\n\nimport { spawn } from \"node:child_process\"\nimport { readdir, readFile } from \"node:fs/promises\"\nimport { relative, resolve } from \"node:path\"\nimport { glob } from \"glob\"\nimport type { Tool, ToolResult } from \"../types.ts\"\n\nimport { textPart } from \"../util.ts\"\n\n/**\n * Tool for finding files by glob pattern.\n */\nexport function globTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"glob\",\n\t\t\tdescription: \"Find files by glob pattern (e.g. **/*.ts, src/**/*.test.ts).\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpattern: { type: \"string\", description: \"Glob pattern (e.g. **/*.ts)\" },\n\t\t\t\t\tpath: { type: \"string\", description: \"Directory to search in (default .)\" },\n\t\t\t\t\tnocase: { type: \"boolean\", description: \"Case-insensitive search (default false)\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"pattern\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst rawPath = (args.path as string) || \".\"\n\t\t\t\tconst dir = resolve(cwd, rawPath)\n\t\t\t\tif (dir !== cwd && !dir.startsWith(`${cwd}/`)) {\n\t\t\t\t\tthrow new Error(`Path outside project: ${rawPath}`)\n\t\t\t\t}\n\n\t\t\t\tconst pattern = args.pattern as string\n\t\t\t\tconst nocase = !!args.nocase\n\t\t\t\tconst files = await glob(pattern, { cwd: dir, nocase })\n\t\t\t\tconst sliced = files.slice(0, 500)\n\t\t\t\tconst relSearchPath = relative(cwd, dir)\n\t\t\t\tconst prefix = relSearchPath ? `${relSearchPath}/` : \"\"\n\t\t\t\tconst relFiles = sliced.map((f) => prefix + f)\n\t\t\t\tconst out = relFiles.length > 0 ? relFiles.join(\"\\n\") : \"No files found\"\n\t\t\t\treturn { content: [textPart(out)], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n/**\n * Tool for searching file contents using regex.\n */\nexport function grepTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"grep\",\n\t\t\tdescription:\n\t\t\t\t\"Search file contents with a regex pattern. Returns matching lines with file paths and line numbers.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpattern: { type: \"string\", description: \"Regex pattern to search for\" },\n\t\t\t\t\tpath: { type: \"string\", description: \"Directory or file to search in (default .)\" },\n\t\t\t\t\tglob: { type: \"string\", description: \"File filter glob (e.g. *.ts)\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"pattern\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst rawPath = (args.path as string) || \".\"\n\t\t\t\tconst dir = resolve(cwd, rawPath)\n\t\t\t\tif (dir !== cwd && !dir.startsWith(`${cwd}/`)) {\n\t\t\t\t\tthrow new Error(`Path outside project: ${rawPath}`)\n\t\t\t\t}\n\n\t\t\t\tconst pattern = args.pattern as string\n\t\t\t\tconst globFilter = args.glob as string | undefined\n\t\t\t\tconst relSearchPath = relative(cwd, dir) || \".\"\n\n\t\t\t\t// rg is 10-100x faster than our JS fallback, but isn't always installed\n\t\t\t\ttry {\n\t\t\t\t\tconst cmd = [\"rg\", \"--line-number\", \"--max-count\", \"200\"]\n\t\t\t\t\tif (globFilter) cmd.push(`--glob=${globFilter}`)\n\t\t\t\t\tcmd.push(\"--\", pattern, relSearchPath)\n\n\t\t\t\t\tconst proc = spawn(cmd[0]!, cmd.slice(1), {\n\t\t\t\t\t\tcwd,\n\t\t\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\t\t})\n\n\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\tproc.kill()\n\t\t\t\t\t\tproc.stdout.destroy()\n\t\t\t\t\t\tproc.stderr.destroy()\n\t\t\t\t\t}\n\t\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true })\n\n\t\t\t\t\tlet stdout = \"\"\n\t\t\t\t\tproc.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\t\tstdout += chunk.toString()\n\t\t\t\t\t})\n\n\t\t\t\t\tlet exitCode: number\n\t\t\t\t\ttry {\n\t\t\t\t\t\texitCode = await new Promise<number>((resolve, reject) => {\n\t\t\t\t\t\t\tproc.on(\"error\", reject)\n\t\t\t\t\t\t\tproc.on(\"close\", (code) => resolve(code ?? -1))\n\t\t\t\t\t\t})\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort)\n\t\t\t\t\t}\n\n\t\t\t\t\tif (exitCode === 0) {\n\t\t\t\t\t\tconst lines = stdout.split(\"\\n\").slice(0, 200).join(\"\\n\")\n\t\t\t\t\t\treturn { content: [textPart(lines || \"No matches\")], isError: false }\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// rg not available, fall through\n\t\t\t\t}\n\n\t\t\t\t// Pure JS fallback when rg is not available\n\t\t\t\tconst files = await glob(globFilter || \"**/*\", {\n\t\t\t\t\tcwd: dir,\n\t\t\t\t\tignore: [\"**/node_modules/**\", \"**/.git/**\"],\n\t\t\t\t})\n\t\t\t\tconst prefix = relSearchPath === \".\" ? \"\" : `${relSearchPath}/`\n\t\t\t\tconst re = new RegExp(pattern, \"i\")\n\t\t\t\tconst matches: string[] = []\n\t\t\t\tfor (const file of files.slice(0, 500)) {\n\t\t\t\t\tif (signal?.aborted) break\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst content = await readFile(resolve(dir, file), \"utf-8\")\n\t\t\t\t\t\tconst lines = content.split(\"\\n\")\n\t\t\t\t\t\tfor (let i = 0; i < lines.length && matches.length < 200; i++) {\n\t\t\t\t\t\t\tconst line = lines[i]\n\t\t\t\t\t\t\tif (line && re.test(line)) matches.push(`${prefix}${file}:${i + 1}:${line}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Skip binary/unreadable files silently\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(matches.join(\"\\n\") || \"No matches\")],\n\t\t\t\t\tisError: false,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n/**\n * Tool for listing directory entries.\n */\nexport function lsTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"ls\",\n\t\t\tdescription: \"List directory contents.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Directory to list (default .)\" },\n\t\t\t\t},\n\t\t\t\trequired: [],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst dir = resolve(cwd, (args.path as string) || \".\")\n\t\t\t\tconst entries = await readdir(dir, { withFileTypes: true })\n\t\t\t\tconst lines = entries.map((e) => {\n\t\t\t\t\tconst suffix = e.isDirectory() ? \"/\" : e.isSymbolicLink() ? \"@\" : \"\"\n\t\t\t\t\treturn `${e.name}${suffix}`\n\t\t\t\t})\n\t\t\t\treturn { content: [textPart(lines.join(\"\\n\") || \"(empty)\")], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n/**\n * Tool for visualizing a truncated directory tree.\n */\nexport function treeTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"tree\",\n\t\t\tdescription:\n\t\t\t\t\"Print a visual directory tree structure, ignoring common ignored folders like node_modules and .git.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Directory to start tree from (default .)\" },\n\t\t\t\t\tdepth: { type: \"number\", description: \"Maximum depth to traverse (default 3)\" },\n\t\t\t\t},\n\t\t\t\trequired: [],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst startDir = resolve(cwd, (args.path as string) || \".\")\n\t\t\t\tconst maxDepth = Number(args.depth ?? 3) || 3\n\n\t\t\t\tconst ignoreList = new Set([\n\t\t\t\t\t\".git\",\n\t\t\t\t\t\"node_modules\",\n\t\t\t\t\t\"dist\",\n\t\t\t\t\t\"build\",\n\t\t\t\t\t\".svelte-kit\",\n\t\t\t\t\t\".next\",\n\t\t\t\t\t\"out\",\n\t\t\t\t\t\".scannerwork\",\n\t\t\t\t\t\"coverage\",\n\t\t\t\t])\n\n\t\t\t\tasync function walk(dir: string, currentDepth: number, prefix: string): Promise<string> {\n\t\t\t\t\tif (currentDepth > maxDepth) return \"\"\n\t\t\t\t\tlet result = \"\"\n\n\t\t\t\t\tconst entries = await readdir(dir, { withFileTypes: true })\n\t\t\t\t\tconst sorted = entries\n\t\t\t\t\t\t.filter((e) => !ignoreList.has(e.name))\n\t\t\t\t\t\t.sort((a, b) => {\n\t\t\t\t\t\t\tif (a.isDirectory() && !b.isDirectory()) return -1\n\t\t\t\t\t\t\tif (!a.isDirectory() && b.isDirectory()) return 1\n\t\t\t\t\t\t\treturn a.name.localeCompare(b.name)\n\t\t\t\t\t\t})\n\n\t\t\t\t\tfor (let i = 0; i < sorted.length; i++) {\n\t\t\t\t\t\tconst entry = sorted[i]!\n\t\t\t\t\t\tconst isLast = i === sorted.length - 1\n\t\t\t\t\t\tconst connector = isLast ? \"└── \" : \"├── \"\n\t\t\t\t\t\tconst childPrefix = prefix + (isLast ? \" \" : \"│ \")\n\n\t\t\t\t\t\tresult += `${prefix}${connector}${entry.name}${entry.isDirectory() ? \"/\" : \"\"}\\n`\n\n\t\t\t\t\t\tif (entry.isDirectory()) {\n\t\t\t\t\t\t\tresult += await walk(resolve(dir, entry.name), currentDepth + 1, childPrefix)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn result\n\t\t\t\t}\n\n\t\t\t\tconst treeText = await walk(startDir, 1, \"\")\n\t\t\t\treturn { content: [textPart(treeText || \"(empty)\")], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","/**\n * Tool for executing shell commands within the project root.\n * Supports timeouts and output truncation to protect the context window.\n */\nimport { spawn } from \"node:child_process\"\nimport type { Tool, ToolResult } from \"../types.ts\"\n\nimport { textPart } from \"../util.ts\"\n\nexport function bashTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"bash\",\n\t\t\tdescription:\n\t\t\t\t\"Execute a shell command. Returns stdout and stderr. Timeout after N seconds (default 120).\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tcommand: { type: \"string\", description: \"Shell command to run\" },\n\t\t\t\t\ttimeout: { type: \"number\", description: \"Timeout in seconds (default 120)\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"command\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\tconst command = args.command as string\n\t\t\tconst timeoutMs = (Number(args.timeout) || 120) * 1000\n\n\t\t\ttry {\n\t\t\t\tconst proc = spawn(\"sh\", [\"-c\", command], { cwd, stdio: [\"ignore\", \"pipe\", \"pipe\"] })\n\n\t\t\t\tlet stdout = \"\"\n\t\t\t\tlet stderr = \"\"\n\t\t\t\tproc.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tstdout += chunk.toString()\n\t\t\t\t})\n\t\t\t\tproc.stderr.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tstderr += chunk.toString()\n\t\t\t\t})\n\n\t\t\t\tlet killed = false\n\t\t\t\tconst timer = setTimeout(() => {\n\t\t\t\t\tkilled = true\n\t\t\t\t\tproc.kill(\"SIGKILL\")\n\t\t\t\t\tproc.stdout.destroy()\n\t\t\t\t\tproc.stderr.destroy()\n\t\t\t\t}, timeoutMs)\n\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tkilled = true\n\t\t\t\t\tproc.kill(\"SIGKILL\")\n\t\t\t\t\tproc.stdout.destroy()\n\t\t\t\t\tproc.stderr.destroy()\n\t\t\t\t}\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true })\n\n\t\t\t\tlet exitCode: number\n\t\t\t\ttry {\n\t\t\t\t\texitCode = await new Promise<number>((resolve, reject) => {\n\t\t\t\t\t\tproc.on(\"error\", reject)\n\t\t\t\t\t\tproc.on(\"close\", (code) => resolve(code ?? -1))\n\t\t\t\t\t})\n\t\t\t\t} finally {\n\t\t\t\t\tclearTimeout(timer)\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort)\n\t\t\t\t}\n\n\t\t\t\t// Prevent context-window blowout from noisy commands\n\t\t\t\tconst MAX = 50_000\n\t\t\t\tlet out = \"\"\n\t\t\t\tif (stdout) out += stdout.slice(0, MAX)\n\t\t\t\tif (stderr) {\n\t\t\t\t\tif (out) out += \"\\n\"\n\t\t\t\t\tout += stderr.slice(0, MAX - out.length)\n\t\t\t\t}\n\t\t\t\tif (out.length >= MAX) out += `\\n…truncated`\n\n\t\t\t\tif (killed) out += `\\n[timeout after ${timeoutMs / 1000}s]`\n\t\t\t\tout += `\\n[exit ${exitCode}]`\n\n\t\t\t\treturn { content: [textPart(out)], isError: exitCode !== 0 || killed }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","/**\n * Web tools for searching and fetching internet content.\n * Uses DuckDuckGo HTML for search (no API key needed) and Node's built-in\n * fetch for reading URLs.\n */\nimport type { Tool, ToolResult } from \"../types.ts\"\nimport { textPart } from \"../util.ts\"\n\nconst MAX_CONTENT = 50_000\n\n// Minimal HTML → plaintext: strips tags, decodes entities, collapses whitespace\nfunction stripRepeatedly(input: string, pattern: RegExp, replacement: string): string {\n\tlet previous: string\n\tdo {\n\t\tprevious = input\n\t\tinput = input.replace(pattern, replacement)\n\t} while (input !== previous)\n\treturn input\n}\n\nfunction htmlToText(html: string): string {\n\tlet text = html\n\t// Remove HTML comments (loop to handle nested/re-introduced patterns)\n\ttext = stripRepeatedly(text, /<!--[\\s\\S]*?-->/g, \"\")\n\t// Remove script blocks (loop + tolerate whitespace in close tag like </script >)\n\ttext = stripRepeatedly(text, /<script[\\s\\S]*?<\\/script\\s*>/gi, \"\")\n\t// Remove style blocks\n\ttext = stripRepeatedly(text, /<style[\\s\\S]*?<\\/style\\s*>/gi, \"\")\n\ttext = text\n\t\t// Keep link hrefs visible (supports single, double, or no quotes)\n\t\t.replace(/<a[^>]*href=[\"']?([^\"'>\\s]*)[\"']?[^>]*>([\\s\\S]*?)<\\/a>/gi, \"[$2]($1)\")\n\t\t// Block-level tags → newlines\n\t\t.replace(/<\\/?(p|div|br|h[1-6]|li|tr|blockquote|pre|hr)[^>]*>/gi, \"\\n\")\n\t\t// Remove remaining tags\n\t\t.replace(/<[^>]+>/g, \"\")\n\t\t// Decode common HTML entities — decode &amp; LAST to avoid double-unescaping\n\t\t.replace(/&lt;/g, \"<\")\n\t\t.replace(/&gt;/g, \">\")\n\t\t.replace(/&quot;/g, '\"')\n\t\t.replace(/&#34;/g, '\"')\n\t\t.replace(/&#x22;/g, '\"')\n\t\t.replace(/&#39;/g, \"'\")\n\t\t.replace(/&#x27;/g, \"'\")\n\t\t.replace(/&apos;/g, \"'\")\n\t\t.replace(/&ldquo;/g, '\"')\n\t\t.replace(/&rdquo;/g, '\"')\n\t\t.replace(/&lsquo;/g, \"'\")\n\t\t.replace(/&rsquo;/g, \"'\")\n\t\t.replace(/&nbsp;/g, \" \")\n\t\t.replace(/&amp;/g, \"&\")\n\t\t// Collapse whitespace but keep paragraph breaks\n\t\t.replace(/[ \\t]+/g, \" \")\n\t\t.replace(/\\n{3,}/g, \"\\n\\n\")\n\t\t.trim()\n\n\tif (text.length > MAX_CONTENT) {\n\t\ttext = `${text.slice(0, MAX_CONTENT)}\\n…truncated`\n\t}\n\treturn text\n}\n\nexport function webSearchTool(): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"web_search\",\n\t\t\tdescription:\n\t\t\t\t\"Search the web using DuckDuckGo. Returns up to 10 results with titles, URLs, and snippets. Use this when you need information from the internet.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tquery: { type: \"string\", description: \"Search query\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"query\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\tconst query = args.query as string\n\t\t\tif (!query.trim()) {\n\t\t\t\treturn { content: [textPart(\"Error: empty search query\")], isError: true }\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst url = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`\n\t\t\t\tconst resp = await fetch(url, {\n\t\t\t\t\tsignal: signal ?? undefined,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"User-Agent\":\n\t\t\t\t\t\t\t\"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\",\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\tif (!resp.ok) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [textPart(`Search failed: HTTP ${resp.status}`)],\n\t\t\t\t\t\tisError: true,\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst html = await resp.text()\n\n\t\t\t\t// Split HTML into blocks by result__body to isolate each search result safely\n\t\t\t\tconst blocks: string[] = []\n\t\t\t\tconst containerRegex = /<div[^>]*class=\"[^\"]*result__body[^\"]*\"[^>]*>/gi\n\t\t\t\tconst indices: number[] = []\n\t\t\t\tfor (const match of html.matchAll(containerRegex)) {\n\t\t\t\t\tif (match.index !== undefined) {\n\t\t\t\t\t\tindices.push(match.index)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (indices.length > 0) {\n\t\t\t\t\tfor (let i = 0; i < indices.length; i++) {\n\t\t\t\t\t\tconst start = indices[i]!\n\t\t\t\t\t\tconst end = indices[i + 1] ?? html.length\n\t\t\t\t\t\tblocks.push(html.slice(start, end))\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst results: string[] = []\n\t\t\t\tconst titleRegex = /<a[^>]*class=\"result__a\"[^>]*href=\"([^\"]*)\"[^>]*>([\\s\\S]*?)<\\/a>/i\n\t\t\t\tconst snippetRegex = /<a[^>]*class=\"result__snippet\"[^>]*>([\\s\\S]*?)<\\/a>/i\n\n\t\t\t\tfor (const block of blocks) {\n\t\t\t\t\tif (results.length >= 10) break\n\n\t\t\t\t\tconst titleMatch = titleRegex.exec(block)\n\t\t\t\t\tif (!titleMatch) continue\n\n\t\t\t\t\tconst rawUrl = titleMatch[1]!\n\t\t\t\t\tconst title = htmlToText(titleMatch[2]!)\n\n\t\t\t\t\tconst snippetMatch = snippetRegex.exec(block)\n\t\t\t\t\tconst snippet = snippetMatch ? htmlToText(snippetMatch[1]!) : \"\"\n\n\t\t\t\t\t// DuckDuckGo wraps URLs through a redirect; extract the actual URL\n\t\t\t\t\tlet cleanUrl = rawUrl\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Prepend protocol/host if DuckDuckGo returns a relative path or protocol-relative URL\n\t\t\t\t\t\tconst urlToParse = rawUrl.startsWith(\"//\")\n\t\t\t\t\t\t\t? `https:${rawUrl}`\n\t\t\t\t\t\t\t: rawUrl.startsWith(\"/\")\n\t\t\t\t\t\t\t\t? `https://duckduckgo.com${rawUrl}`\n\t\t\t\t\t\t\t\t: rawUrl\n\t\t\t\t\t\tconst param = new URL(urlToParse).searchParams.get(\"uddg\")\n\t\t\t\t\t\tif (param) cleanUrl = param\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Not a redirect URL, use as-is\n\t\t\t\t\t}\n\n\t\t\t\t\tresults.push(`## ${title}\\n${cleanUrl}\\n${snippet}`)\n\t\t\t\t}\n\n\t\t\t\tif (results.length === 0) {\n\t\t\t\t\treturn { content: [textPart(\"No results found.\")], isError: false }\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(results.join(\"\\n\\n\"))],\n\t\t\t\t\tisError: false,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tconst msg = (e as Error).message\n\t\t\t\tif (msg.includes(\"abort\")) {\n\t\t\t\t\treturn { content: [textPart(\"Search aborted.\")], isError: true }\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Search error: ${msg}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\nexport function webFetchTool(): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"web_fetch\",\n\t\t\tdescription:\n\t\t\t\t\"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.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\turl: { type: \"string\", description: \"URL to fetch\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"url\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\tconst url = args.url as string\n\t\t\tif (!url.trim()) {\n\t\t\t\treturn { content: [textPart(\"Error: empty URL\")], isError: true }\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tnew URL(url)\n\t\t\t} catch {\n\t\t\t\treturn { content: [textPart(`Error: invalid URL: ${url}`)], isError: true }\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst resp = await fetch(url, {\n\t\t\t\t\tsignal: signal ?? undefined,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"User-Agent\":\n\t\t\t\t\t\t\t\"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\",\n\t\t\t\t\t\tAccept: \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\",\n\t\t\t\t\t},\n\t\t\t\t\tredirect: \"follow\",\n\t\t\t\t})\n\n\t\t\t\tif (!resp.ok) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [textPart(`Fetch failed: HTTP ${resp.status} ${resp.statusText}`)],\n\t\t\t\t\t\tisError: true,\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst contentType = (resp.headers.get(\"content-type\") ?? \"\").toLowerCase()\n\t\t\t\tconst body = await resp.text()\n\n\t\t\t\t// Sniff HTML: check content-type or if body has HTML markup signature\n\t\t\t\tconst isHtml =\n\t\t\t\t\tcontentType.includes(\"text/html\") ||\n\t\t\t\t\tcontentType.includes(\"application/xhtml+xml\") ||\n\t\t\t\t\tbody.trim().toLowerCase().startsWith(\"<!doctype html\") ||\n\t\t\t\t\tbody.trim().toLowerCase().startsWith(\"<html\")\n\n\t\t\t\tif (isHtml) {\n\t\t\t\t\tconst text = htmlToText(body)\n\t\t\t\t\treturn { content: [textPart(text)], isError: false }\n\t\t\t\t}\n\n\t\t\t\t// For plain text, JSON, etc. return as-is (truncated if needed)\n\t\t\t\tconst truncated =\n\t\t\t\t\tbody.length > MAX_CONTENT ? `${body.slice(0, MAX_CONTENT)}\\n…truncated` : body\n\t\t\t\treturn { content: [textPart(truncated)], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\tconst msg = (e as Error).message\n\t\t\t\tif (msg.includes(\"abort\")) {\n\t\t\t\t\treturn { content: [textPart(\"Fetch aborted.\")], isError: true }\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Fetch error: ${msg}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","import type { Tool } from \"../types.ts\"\nimport { editTool, readTool, writeTool } from \"./fs.ts\"\nimport { gitTool } from \"./git.ts\"\nimport { globTool, grepTool, lsTool, treeTool } from \"./search.ts\"\nimport { bashTool } from \"./shell.ts\"\nimport { webFetchTool, webSearchTool } from \"./web.ts\"\n\nexport function getAllTools(cwd: string): Tool[] {\n\treturn [\n\t\treadTool(cwd),\n\t\twriteTool(cwd),\n\t\teditTool(cwd),\n\t\tbashTool(cwd),\n\t\tglobTool(cwd),\n\t\tgrepTool(cwd),\n\t\tlsTool(cwd),\n\t\ttreeTool(cwd),\n\t\tgitTool(cwd),\n\t\twebSearchTool(),\n\t\twebFetchTool(),\n\t]\n}\n\nexport function getDefaultTools(cwd: string): Tool[] {\n\treturn [\n\t\treadTool(cwd),\n\t\twriteTool(cwd),\n\t\teditTool(cwd),\n\t\tbashTool(cwd),\n\t\twebSearchTool(),\n\t\twebFetchTool(),\n\t]\n}\n","import { spawn } from \"node:child_process\"\nimport { readFile } from \"node:fs/promises\"\nimport { dirname, join } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport semver from \"semver\"\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nlet cachedLatest: string | null = null\nlet cachedCurrent: string | null = null\n\nexport async function getCurrentVersion(): Promise<string> {\n\tif (cachedCurrent) return cachedCurrent\n\ttry {\n\t\tconst raw = await readFile(join(__dirname, \"..\", \"package.json\"), \"utf-8\")\n\t\tconst pkg = JSON.parse(raw)\n\t\tcachedCurrent = (pkg.version as string) ?? \"0.0.0\"\n\t\treturn cachedCurrent\n\t} catch {\n\t\treturn \"0.0.0\"\n\t}\n}\n\nexport async function getLatestVersion(): Promise<string | null> {\n\tif (cachedLatest) return cachedLatest\n\ttry {\n\t\tconst proc = spawn(\"npm\", [\"info\", \"novacode\", \"version\"], {\n\t\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t\t})\n\t\tconst text = await new Promise<string>((resolve, reject) => {\n\t\t\tlet out = \"\"\n\t\t\tproc.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tout += chunk.toString()\n\t\t\t})\n\t\t\tproc.on(\"error\", reject)\n\t\t\tproc.on(\"close\", () => resolve(out.trim()))\n\t\t})\n\t\tif (text) {\n\t\t\tcachedLatest = text\n\t\t\treturn text\n\t\t}\n\t} catch {}\n\treturn null\n}\n\nexport async function checkForUpdate(): Promise<{\n\thasUpdate: boolean\n\tcurrent: string\n\tlatest: string\n} | null> {\n\tconst current = await getCurrentVersion()\n\tconst latest = await getLatestVersion()\n\tif (!latest) return null\n\treturn {\n\t\thasUpdate: semver.gt(latest, current),\n\t\tcurrent,\n\t\tlatest,\n\t}\n}\n\nexport async function runUpdate(silent = false): Promise<boolean> {\n\ttry {\n\t\tconst proc = spawn(\"npm\", [\"update\", \"-g\", \"novacode\"], {\n\t\t\tstdio: silent ? \"ignore\" : \"inherit\",\n\t\t})\n\t\tconst exitCode = await new Promise<number>((resolve, reject) => {\n\t\t\tproc.on(\"error\", reject)\n\t\t\tproc.on(\"close\", (code) => resolve(code ?? -1))\n\t\t})\n\t\tif (exitCode === 0) {\n\t\t\tif (!silent) {\n\t\t\t\tconsole.log(\"✓ novacode updated to latest version successfully.\")\n\t\t\t}\n\t\t\treturn true\n\t\t} else {\n\t\t\tif (!silent) {\n\t\t\t\tconsole.error(`Update failed (exit code ${exitCode})`)\n\t\t\t\tprocess.exit(1)\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\t} catch (e) {\n\t\tif (!silent) {\n\t\t\tconsole.error(`Update failed: ${(e as Error).message}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\t\treturn false\n\t}\n}\n","#!/usr/bin/env node\nimport { parseArgs } from \"node:util\"\n/**\n * Entry point for the nova CLI.\n * Handles configuration, CLI flags, and runs interactive TUI mode.\n */\nimport chalk from \"chalk\"\nimport { Agent } from \"./agent/agent.ts\"\nimport { buildSystemPrompt } from \"./agent/prompt.ts\"\nimport { handleSessionCommand } from \"./commands/session.ts\"\nimport { getProvider, MODELS } from \"./config/providers.ts\"\nimport { configExists, loadAuth, loadConfig } from \"./config/store.ts\"\nimport { runOnboarding } from \"./onboarding/wizard.ts\"\nimport { loadResources } from \"./resource.ts\"\nimport { getSessionStore } from \"./session/store.ts\"\nimport { getAllTools } from \"./tools/index.ts\"\nimport type { Session } from \"./types.ts\"\nimport { getCurrentVersion, runUpdate } from \"./update.ts\"\n\nfunction parseCli() {\n\tconst { values, positionals } = parseArgs({\n\t\toptions: {\n\t\t\thelp: { type: \"boolean\", short: \"h\" },\n\t\t\tversion: { type: \"boolean\", short: \"v\" },\n\t\t\tprovider: { type: \"string\" },\n\t\t\tmodel: { type: \"string\" },\n\t\t\t\"api-key\": { type: \"string\" },\n\t\t\tsessions: { type: \"string\", short: \"s\" },\n\t\t\tresume: { type: \"boolean\", short: \"r\" },\n\t\t\tall: { type: \"boolean\" },\n\t\t},\n\t\tstrict: false,\n\t\tallowPositionals: true,\n\t})\n\n\treturn { flags: values, args: positionals }\n}\n\nfunction findModel(modelId: string, providerId?: string) {\n\treturn MODELS.find((m) => {\n\t\tif (providerId) return m.provider === providerId && m.id === modelId\n\t\treturn m.id === modelId\n\t})\n}\n\nconst NODE_MIN = 24\n\nasync function main() {\n\tconst major = Number(process.versions.node.split(\".\")[0])\n\tif (!major || major < NODE_MIN) {\n\t\tconsole.error(`novacode requires Node.js >= ${NODE_MIN}. You have ${process.version}.`)\n\t\tconsole.error(`Upgrade: https://nodejs.org/`)\n\t\tprocess.exit(1)\n\t}\n\n\tconst { flags, args } = parseCli()\n\n\tif (flags.version) {\n\t\tconst version = await getCurrentVersion()\n\t\tconsole.log(`nova ${version}`)\n\t\tprocess.exit(0)\n\t}\n\n\tif (flags.help) {\n\t\tconsole.log(`nova — open-source coding agent\n\nUsage:\n nova Interactive mode\n nova update Update to latest version\n nova -s ls [limit] List sessions (last 10 by default)\n nova -s rm <id> Delete a specific session\n nova -s rm --all Delete all sessions\n nova -s <id> Resume a session by ID\n nova -r, --resume Resume the most recent session\n\nOptions:\n -h, --help Show help\n -v, --version Show version\n --provider <id> Provider to use\n --model <id> Model to use\n --api-key <key> API key override\n -s, --sessions <id> Resume/manage sessions\n -r, --resume Resume the most recent session`)\n\t\tprocess.exit(0)\n\t}\n\n\t// Handle update subcommand\n\tif (args[0] === \"update\") {\n\t\tawait runUpdate()\n\t\treturn\n\t}\n\n\t// Reject positional args — use interactive mode with / commands\n\tif (args.length > 0 && !flags.sessions) {\n\t\tconsole.error(chalk.yellow(`Unknown command: ${args.join(\" \")}`))\n\t\tconsole.error(\"Run `nova --help` for usage.\")\n\t\tprocess.exit(1)\n\t}\n\n\tconst controller = new AbortController()\n\n\tconst onSignal = () => {\n\t\tcontroller.abort()\n\t\tprocess.stderr.write(\"\\nAborted.\\n\")\n\t\tprocess.exit(130)\n\t}\n\tprocess.on(\"SIGINT\", onSignal)\n\tprocess.on(\"SIGTERM\", onSignal)\n\n\t// First-run onboarding\n\tconst config = await ((await configExists()) ? loadConfig() : runOnboarding())\n\tconst auth = await loadAuth()\n\n\tconst store = await getSessionStore()\n\tawait store.prune()\n\n\t// Handle --sessions commands (ls, rm)\n\tif (flags.sessions) {\n\t\tconst sessionFlag = flags.sessions as string\n\t\tif (sessionFlag === \"ls\" || sessionFlag === \"list\") {\n\t\t\tconst limitVal = args[0] ? parseInt(args[0], 10) : 10\n\t\t\tconst limit = Number.isNaN(limitVal) ? 10 : limitVal\n\t\t\tawait handleSessionCommand(store, [\"ls\"], { limit })\n\t\t\treturn\n\t\t}\n\t\tif (sessionFlag === \"rm\" || sessionFlag === \"delete\") {\n\t\t\tconst id = args[0]\n\t\t\tconst all = !!flags.all\n\t\t\tawait handleSessionCommand(store, [\"rm\", id ?? \"\"], { all })\n\t\t\treturn\n\t\t}\n\t}\n\n\tlet session: Session | null = null\n\tif (flags.resume) {\n\t\tsession = await store.latest()\n\t\tif (!session) {\n\t\t\tconsole.error(\"No recent session found to resume.\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t} else if (flags.sessions) {\n\t\tsession = await store.get(flags.sessions as string)\n\t\tif (!session) {\n\t\t\tconsole.error(`Session not found: ${flags.sessions}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\t}\n\n\t// CLI overrides or session default or config default\n\tconst providerId = (flags.provider as string) || session?.provider || config.provider\n\tconst modelId = (flags.model as string) || session?.model || config.model\n\tconst apiKey = (flags[\"api-key\"] as string) || auth.apiKeys[providerId]\n\n\tconst provider = getProvider(providerId)\n\tif (!provider) {\n\t\tconsole.error(`Unknown provider: ${providerId}`)\n\t\tconsole.error(`Available: ${getProvider(\"glm\") ? \"glm, \" : \"\"}gemini, deepseek, openai`)\n\t\tprocess.exit(1)\n\t}\n\n\tif (!apiKey) {\n\t\tconsole.error(\n\t\t\t`No API key for ${provider.name}. Set ${provider.envKey} or run nova for onboarding.`,\n\t\t)\n\t\tprocess.exit(1)\n\t}\n\n\tconst model = findModel(modelId, providerId)\n\tif (!model) {\n\t\tconsole.error(`Unknown model: ${modelId}`)\n\t\tconsole.error(\"Available models:\")\n\t\tfor (const m of MODELS.filter((m) => m.provider === providerId)) {\n\t\t\tconsole.error(` ${m.id} — ${m.name}`)\n\t\t}\n\t\tprocess.exit(1)\n\t}\n\n\tconst cwd = process.cwd()\n\tconst tools = getAllTools(cwd)\n\tconst { skills, agentsMd } = await loadResources(cwd)\n\tconst system = buildSystemPrompt(cwd, tools, skills, agentsMd ?? undefined)\n\n\tif (!session) {\n\t\tsession = await store.create(cwd, model.id, providerId)\n\t}\n\n\tconst sessionId = session.id\n\tconst existingMessages = await store.messages(sessionId)\n\n\tconst agent = new Agent({\n\t\tapi: provider.api,\n\t\tmodel,\n\t\tapiKey,\n\t\tbaseUrl: provider.baseUrl,\n\t\tsystem,\n\t\ttools,\n\t\tmessages: existingMessages,\n\t})\n\n\t// Interactive TUI mode\n\tprocess.off(\"SIGINT\", onSignal)\n\tprocess.off(\"SIGTERM\", onSignal)\n\tconst { interactive } = await import(\"./tui/app.tsx\")\n\tawait interactive(agent, store, sessionId, skills, !!agentsMd)\n}\n\nprocess.on(\"unhandledRejection\", (reason) => {\n\tconsole.error(\"Unhandled rejection:\", reason)\n\tprocess.exit(1)\n})\n\nmain().catch((e) => {\n\tconsole.error(\"Fatal:\", e)\n\tprocess.exit(1)\n})\n"],"mappings":"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"}