edge-pi-cli 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA2IH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,iBAsLxC","sourcesContent":["/**\n * Main entry point for the edge-pi CLI.\n *\n * Handles argument parsing, model creation, skill loading,\n * session management, auth, and mode dispatch.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport chalk from \"chalk\";\nimport type { CodingAgentConfig, Skill as PromptSkill } from \"edge-pi\";\nimport { CodingAgent, SessionManager } from \"edge-pi\";\nimport {\n\tAuthStorage,\n\tanthropicOAuthProvider,\n\tgithubCopilotOAuthProvider,\n\topenaiCodexOAuthProvider,\n} from \"./auth/index.js\";\nimport { parseArgs, printHelp, printModels } from \"./cli/args.js\";\nimport { loadContextFiles } from \"./context.js\";\nimport { createModel } from \"./model-factory.js\";\nimport { runInteractiveMode } from \"./modes/interactive-mode.js\";\nimport { runPrintMode } from \"./modes/print-mode.js\";\nimport { loadPrompts } from \"./prompts.js\";\nimport { buildProviderOptions } from \"./provider-options.js\";\nimport { SettingsManager } from \"./settings.js\";\nimport { loadSkills, type Skill } from \"./skills.js\";\nimport { findFd } from \"./utils/find-fd.js\";\n\nconst VERSION = \"0.1.0\";\nconst CONFIG_DIR_NAME = \".pi\";\n\nfunction getAgentDir(): string {\n\tconst envDir = process.env.PI_CODING_AGENT_DIR;\n\tif (envDir) {\n\t\tif (envDir === \"~\") return homedir();\n\t\tif (envDir.startsWith(\"~/\")) return homedir() + envDir.slice(1);\n\t\treturn envDir;\n\t}\n\treturn join(homedir(), CONFIG_DIR_NAME, \"agent\");\n}\n\nfunction getSessionsDir(): string {\n\treturn join(getAgentDir(), \"sessions\");\n}\n\nfunction getAuthPath(): string {\n\treturn join(getAgentDir(), \"auth.json\");\n}\n\n/**\n * Read all content from piped stdin.\n * Returns undefined if stdin is a TTY.\n */\nasync function readPipedStdin(): Promise<string | undefined> {\n\tif (process.stdin.isTTY) {\n\t\treturn undefined;\n\t}\n\treturn new Promise((resolve) => {\n\t\tlet data = \"\";\n\t\tprocess.stdin.setEncoding(\"utf8\");\n\t\tprocess.stdin.on(\"data\", (chunk) => {\n\t\t\tdata += chunk;\n\t\t});\n\t\tprocess.stdin.on(\"end\", () => {\n\t\t\tresolve(data.trim() || undefined);\n\t\t});\n\t\tprocess.stdin.resume();\n\t});\n}\n\n/**\n * Process @file arguments into text content.\n */\nfunction processFileArgs(fileArgs: string[]): string {\n\tconst parts: string[] = [];\n\tfor (const filePath of fileArgs) {\n\t\tconst resolved = resolve(filePath);\n\t\tif (!existsSync(resolved)) {\n\t\t\tconsole.error(chalk.yellow(`Warning: File not found: ${resolved}`));\n\t\t\tcontinue;\n\t\t}\n\t\ttry {\n\t\t\tconst content = readFileSync(resolved, \"utf-8\");\n\t\t\tparts.push(`<file path=\"${resolved}\">\\n${content}\\n</file>`);\n\t\t} catch (error) {\n\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${resolved}: ${(error as Error).message}`));\n\t\t}\n\t}\n\treturn parts.length > 0 ? `${parts.join(\"\\n\\n\")}\\n\\n` : \"\";\n}\n\n/**\n * Create a session directory path based on the current working directory.\n */\nfunction getProjectSessionDir(cwd: string): string {\n\tconst sanitized = cwd.replace(/\\//g, \"--\").replace(/^--/, \"\");\n\treturn join(getSessionsDir(), sanitized);\n}\n\n/**\n * Find the most recent session file in a directory.\n */\nfunction findRecentSession(sessionDir: string): string | undefined {\n\tif (!existsSync(sessionDir)) return undefined;\n\ttry {\n\t\tconst files = readdirSync(sessionDir)\n\t\t\t.filter((f: string) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f: string) => ({\n\t\t\t\tname: f,\n\t\t\t\tpath: join(sessionDir, f),\n\t\t\t\tmtime: statSync(join(sessionDir, f)).mtime.getTime(),\n\t\t\t}))\n\t\t\t.sort((a: { mtime: number }, b: { mtime: number }) => b.mtime - a.mtime);\n\t\treturn files[0]?.path;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Create and configure AuthStorage with built-in OAuth providers.\n */\nfunction createAuthStorage(): AuthStorage {\n\tconst authStorage = new AuthStorage(getAuthPath());\n\tauthStorage.registerProvider(anthropicOAuthProvider);\n\tauthStorage.registerProvider(githubCopilotOAuthProvider);\n\tauthStorage.registerProvider(openaiCodexOAuthProvider);\n\treturn authStorage;\n}\n\nfunction toSkillMap(skills: Skill[]): Record<string, PromptSkill> {\n\tconst map: Record<string, PromptSkill> = {};\n\tfor (const skill of skills) {\n\t\tmap[skill.name] = {\n\t\t\tdescription: skill.description,\n\t\t\tfilePath: skill.filePath,\n\t\t\tdisableModelInvocation: skill.disableModelInvocation,\n\t\t};\n\t}\n\treturn map;\n}\n\nexport async function main(args: string[]) {\n\tconst parsed = parseArgs(args);\n\n\tif (parsed.version) {\n\t\tconsole.log(VERSION);\n\t\treturn;\n\t}\n\n\tif (parsed.help) {\n\t\tprintHelp();\n\t\treturn;\n\t}\n\n\tif (parsed.listModels) {\n\t\tprintModels();\n\t\treturn;\n\t}\n\n\t// Read piped stdin\n\tconst stdinContent = await readPipedStdin();\n\tif (stdinContent !== undefined) {\n\t\tparsed.print = true;\n\t\tparsed.messages.unshift(stdinContent);\n\t}\n\n\t// Process @file arguments\n\tlet initialMessage: string | undefined;\n\tif (parsed.fileArgs.length > 0) {\n\t\tconst fileContent = processFileArgs(parsed.fileArgs);\n\t\tif (parsed.messages.length > 0) {\n\t\t\tinitialMessage = fileContent + parsed.messages.shift();\n\t\t} else {\n\t\t\tinitialMessage = fileContent;\n\t\t}\n\t}\n\n\tconst cwd = process.cwd();\n\tconst isInteractive = !parsed.print && parsed.mode === undefined;\n\tconst mode = parsed.mode || \"text\";\n\n\t// Set up auth storage\n\tconst authStorage = createAuthStorage();\n\n\t// Set up settings persistence\n\tconst settingsManager = SettingsManager.create(getAgentDir());\n\n\t// Apply CLI --api-key override\n\tif (parsed.apiKey && parsed.provider) {\n\t\tauthStorage.setRuntimeApiKey(parsed.provider, parsed.apiKey);\n\t}\n\n\t// Create model (async - may resolve OAuth tokens)\n\t// Fall back to saved defaults when CLI args are not specified\n\tconst { model, provider, modelId } = await createModel({\n\t\tprovider: parsed.provider ?? settingsManager.getDefaultProvider(),\n\t\tmodel: parsed.model ?? settingsManager.getDefaultModel(),\n\t\tapiKey: parsed.apiKey,\n\t\tauthStorage,\n\t});\n\n\t// Load skills\n\tlet skills: Skill[] = [];\n\tif (!parsed.noSkills) {\n\t\tconst skillResult = loadSkills({\n\t\t\tcwd,\n\t\t\tskillPaths: parsed.skills,\n\t\t\tincludeDefaults: true,\n\t\t});\n\t\tskills = skillResult.skills;\n\n\t\tif (parsed.verbose && skillResult.diagnostics.length > 0) {\n\t\t\tfor (const d of skillResult.diagnostics) {\n\t\t\t\tconsole.error(chalk.yellow(`Skill warning: ${d.message} (${d.path})`));\n\t\t\t}\n\t\t}\n\t}\n\n\t// Load context files (AGENTS.md)\n\tconst contextFiles = loadContextFiles(cwd);\n\tif (parsed.verbose && contextFiles.length > 0) {\n\t\tconsole.log(chalk.dim(`Loaded ${contextFiles.length} context file(s).`));\n\t}\n\n\t// Load prompt templates\n\tconst promptsResult = loadPrompts({ cwd });\n\tconst prompts = promptsResult.prompts;\n\tif (parsed.verbose && promptsResult.diagnostics.length > 0) {\n\t\tfor (const d of promptsResult.diagnostics) {\n\t\t\tconsole.error(chalk.yellow(`Prompt warning: ${d.message} (${d.path})`));\n\t\t}\n\t}\n\n\t// Find fd binary for @ file autocomplete\n\tconst fdPath = findFd();\n\n\t// Set up session manager\n\tconst sessionDir = parsed.sessionDir ?? getProjectSessionDir(cwd);\n\tlet sessionManager: SessionManager | undefined;\n\tif (parsed.noSession) {\n\t\tsessionManager = SessionManager.inMemory(cwd);\n\t} else if (parsed.session) {\n\t\tsessionManager = SessionManager.open(parsed.session, parsed.sessionDir);\n\t} else if (parsed.resume) {\n\t\t// Start a new session; the TUI will show the session picker on startup\n\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t} else if (parsed.continue) {\n\t\tconst recentFile = findRecentSession(sessionDir);\n\t\tif (recentFile) {\n\t\t\tsessionManager = SessionManager.open(recentFile, sessionDir);\n\t\t} else {\n\t\t\tif (parsed.verbose) {\n\t\t\t\tconsole.log(chalk.dim(\"No previous session found, starting new.\"));\n\t\t\t}\n\t\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t\t}\n\t} else {\n\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t}\n\n\t// Create agent config\n\tconst agentConfig: CodingAgentConfig = {\n\t\tmodel,\n\t\tcwd,\n\t\ttoolSet: parsed.toolSet ?? \"coding\",\n\t\tproviderOptions: buildProviderOptions(provider, parsed.thinking),\n\t\tsessionManager,\n\t};\n\n\tagentConfig.systemPromptOptions = {\n\t\tappendSystemPrompt: parsed.appendSystemPrompt,\n\t\tcontextFiles,\n\t\tskills: toSkillMap(skills),\n\t};\n\tif (parsed.systemPrompt) {\n\t\tagentConfig.systemPromptOptions.customPrompt = parsed.systemPrompt;\n\t}\n\n\t// Create agent (session messages are auto-restored from sessionManager)\n\tconst agent = new CodingAgent(agentConfig);\n\n\tif (parsed.verbose && agent.messages.length > 0) {\n\t\tconsole.log(chalk.dim(`Restored ${agent.messages.length} messages from session.`));\n\t}\n\n\t// Dispatch to mode\n\tif (isInteractive) {\n\t\tawait runInteractiveMode(agent, {\n\t\t\tinitialMessage,\n\t\t\tinitialMessages: parsed.messages,\n\t\t\tsessionDir,\n\t\t\tagentConfig,\n\t\t\tresumeOnStart: parsed.resume,\n\t\t\tskills,\n\t\t\tcontextFiles,\n\t\t\tprompts,\n\t\t\tverbose: parsed.verbose,\n\t\t\tprovider,\n\t\t\tmodelId,\n\t\t\tauthStorage,\n\t\t\tsettingsManager,\n\t\t\tfdPath,\n\t\t\tonModelChange: async (newProvider: string, newModelId: string) => {\n\t\t\t\tconst { model: newModel } = await createModel({\n\t\t\t\t\tprovider: newProvider,\n\t\t\t\t\tmodel: newModelId,\n\t\t\t\t\tauthStorage,\n\t\t\t\t});\n\t\t\t\treturn new CodingAgent({\n\t\t\t\t\t...agentConfig,\n\t\t\t\t\tmodel: newModel,\n\t\t\t\t\tproviderOptions: buildProviderOptions(newProvider, parsed.thinking),\n\t\t\t\t});\n\t\t\t},\n\t\t});\n\t} else {\n\t\tawait runPrintMode(agent, {\n\t\t\tmode,\n\t\t\tmessages: parsed.messages,\n\t\t\tinitialMessage,\n\t\t});\n\t\tprocess.exit(0);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA6IH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,iBAuLxC","sourcesContent":["/**\n * Main entry point for the edge-pi CLI.\n *\n * Handles argument parsing, model creation, skill loading,\n * session management, auth, and mode dispatch.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport chalk from \"chalk\";\nimport type { CodingAgentConfig, Skill as PromptSkill } from \"edge-pi\";\nimport { CodingAgent } from \"edge-pi\";\nimport { createNodeRuntime } from \"edge-pi/node\";\nimport { SessionManager } from \"edge-pi/session\";\nimport {\n\tAuthStorage,\n\tanthropicOAuthProvider,\n\tgithubCopilotOAuthProvider,\n\topenaiCodexOAuthProvider,\n} from \"./auth/index.js\";\nimport { parseArgs, printHelp, printModels } from \"./cli/args.js\";\nimport { loadContextFiles } from \"./context.js\";\nimport { createModel } from \"./model-factory.js\";\nimport { runInteractiveMode } from \"./modes/interactive-mode.js\";\nimport { runPrintMode } from \"./modes/print-mode.js\";\nimport { loadPrompts } from \"./prompts.js\";\nimport { buildProviderOptions } from \"./provider-options.js\";\nimport { SettingsManager } from \"./settings.js\";\nimport { loadSkills, type Skill } from \"./skills.js\";\nimport { findFd } from \"./utils/find-fd.js\";\n\nconst VERSION = \"0.1.0\";\nconst CONFIG_DIR_NAME = \".pi\";\n\nfunction getAgentDir(): string {\n\tconst envDir = process.env.PI_CODING_AGENT_DIR;\n\tif (envDir) {\n\t\tif (envDir === \"~\") return homedir();\n\t\tif (envDir.startsWith(\"~/\")) return homedir() + envDir.slice(1);\n\t\treturn envDir;\n\t}\n\treturn join(homedir(), CONFIG_DIR_NAME, \"agent\");\n}\n\nfunction getSessionsDir(): string {\n\treturn join(getAgentDir(), \"sessions\");\n}\n\nfunction getAuthPath(): string {\n\treturn join(getAgentDir(), \"auth.json\");\n}\n\n/**\n * Read all content from piped stdin.\n * Returns undefined if stdin is a TTY.\n */\nasync function readPipedStdin(): Promise<string | undefined> {\n\tif (process.stdin.isTTY) {\n\t\treturn undefined;\n\t}\n\treturn new Promise((resolve) => {\n\t\tlet data = \"\";\n\t\tprocess.stdin.setEncoding(\"utf8\");\n\t\tprocess.stdin.on(\"data\", (chunk) => {\n\t\t\tdata += chunk;\n\t\t});\n\t\tprocess.stdin.on(\"end\", () => {\n\t\t\tresolve(data.trim() || undefined);\n\t\t});\n\t\tprocess.stdin.resume();\n\t});\n}\n\n/**\n * Process @file arguments into text content.\n */\nfunction processFileArgs(fileArgs: string[]): string {\n\tconst parts: string[] = [];\n\tfor (const filePath of fileArgs) {\n\t\tconst resolved = resolve(filePath);\n\t\tif (!existsSync(resolved)) {\n\t\t\tconsole.error(chalk.yellow(`Warning: File not found: ${resolved}`));\n\t\t\tcontinue;\n\t\t}\n\t\ttry {\n\t\t\tconst content = readFileSync(resolved, \"utf-8\");\n\t\t\tparts.push(`<file path=\"${resolved}\">\\n${content}\\n</file>`);\n\t\t} catch (error) {\n\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${resolved}: ${(error as Error).message}`));\n\t\t}\n\t}\n\treturn parts.length > 0 ? `${parts.join(\"\\n\\n\")}\\n\\n` : \"\";\n}\n\n/**\n * Create a session directory path based on the current working directory.\n */\nfunction getProjectSessionDir(cwd: string): string {\n\tconst sanitized = cwd.replace(/\\//g, \"--\").replace(/^--/, \"\");\n\treturn join(getSessionsDir(), sanitized);\n}\n\n/**\n * Find the most recent session file in a directory.\n */\nfunction findRecentSession(sessionDir: string): string | undefined {\n\tif (!existsSync(sessionDir)) return undefined;\n\ttry {\n\t\tconst files = readdirSync(sessionDir)\n\t\t\t.filter((f: string) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f: string) => ({\n\t\t\t\tname: f,\n\t\t\t\tpath: join(sessionDir, f),\n\t\t\t\tmtime: statSync(join(sessionDir, f)).mtime.getTime(),\n\t\t\t}))\n\t\t\t.sort((a: { mtime: number }, b: { mtime: number }) => b.mtime - a.mtime);\n\t\treturn files[0]?.path;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Create and configure AuthStorage with built-in OAuth providers.\n */\nfunction createAuthStorage(): AuthStorage {\n\tconst authStorage = new AuthStorage(getAuthPath());\n\tauthStorage.registerProvider(anthropicOAuthProvider);\n\tauthStorage.registerProvider(githubCopilotOAuthProvider);\n\tauthStorage.registerProvider(openaiCodexOAuthProvider);\n\treturn authStorage;\n}\n\nfunction toSkillMap(skills: Skill[]): Record<string, PromptSkill> {\n\tconst map: Record<string, PromptSkill> = {};\n\tfor (const skill of skills) {\n\t\tmap[skill.name] = {\n\t\t\tdescription: skill.description,\n\t\t\tfilePath: skill.filePath,\n\t\t\tdisableModelInvocation: skill.disableModelInvocation,\n\t\t};\n\t}\n\treturn map;\n}\n\nexport async function main(args: string[]) {\n\tconst parsed = parseArgs(args);\n\n\tif (parsed.version) {\n\t\tconsole.log(VERSION);\n\t\treturn;\n\t}\n\n\tif (parsed.help) {\n\t\tprintHelp();\n\t\treturn;\n\t}\n\n\tif (parsed.listModels) {\n\t\tprintModels();\n\t\treturn;\n\t}\n\n\t// Read piped stdin\n\tconst stdinContent = await readPipedStdin();\n\tif (stdinContent !== undefined) {\n\t\tparsed.print = true;\n\t\tparsed.messages.unshift(stdinContent);\n\t}\n\n\t// Process @file arguments\n\tlet initialMessage: string | undefined;\n\tif (parsed.fileArgs.length > 0) {\n\t\tconst fileContent = processFileArgs(parsed.fileArgs);\n\t\tif (parsed.messages.length > 0) {\n\t\t\tinitialMessage = fileContent + parsed.messages.shift();\n\t\t} else {\n\t\t\tinitialMessage = fileContent;\n\t\t}\n\t}\n\n\tconst cwd = process.cwd();\n\tconst isInteractive = !parsed.print && parsed.mode === undefined;\n\tconst mode = parsed.mode || \"text\";\n\n\t// Set up auth storage\n\tconst authStorage = createAuthStorage();\n\n\t// Set up settings persistence\n\tconst settingsManager = SettingsManager.create(getAgentDir());\n\n\t// Apply CLI --api-key override\n\tif (parsed.apiKey && parsed.provider) {\n\t\tauthStorage.setRuntimeApiKey(parsed.provider, parsed.apiKey);\n\t}\n\n\t// Create model (async - may resolve OAuth tokens)\n\t// Fall back to saved defaults when CLI args are not specified\n\tconst { model, provider, modelId } = await createModel({\n\t\tprovider: parsed.provider ?? settingsManager.getDefaultProvider(),\n\t\tmodel: parsed.model ?? settingsManager.getDefaultModel(),\n\t\tapiKey: parsed.apiKey,\n\t\tauthStorage,\n\t});\n\n\t// Load skills\n\tlet skills: Skill[] = [];\n\tif (!parsed.noSkills) {\n\t\tconst skillResult = loadSkills({\n\t\t\tcwd,\n\t\t\tskillPaths: parsed.skills,\n\t\t\tincludeDefaults: true,\n\t\t});\n\t\tskills = skillResult.skills;\n\n\t\tif (parsed.verbose && skillResult.diagnostics.length > 0) {\n\t\t\tfor (const d of skillResult.diagnostics) {\n\t\t\t\tconsole.error(chalk.yellow(`Skill warning: ${d.message} (${d.path})`));\n\t\t\t}\n\t\t}\n\t}\n\n\t// Load context files (AGENTS.md)\n\tconst contextFiles = loadContextFiles(cwd);\n\tif (parsed.verbose && contextFiles.length > 0) {\n\t\tconsole.log(chalk.dim(`Loaded ${contextFiles.length} context file(s).`));\n\t}\n\n\t// Load prompt templates\n\tconst promptsResult = loadPrompts({ cwd });\n\tconst prompts = promptsResult.prompts;\n\tif (parsed.verbose && promptsResult.diagnostics.length > 0) {\n\t\tfor (const d of promptsResult.diagnostics) {\n\t\t\tconsole.error(chalk.yellow(`Prompt warning: ${d.message} (${d.path})`));\n\t\t}\n\t}\n\n\t// Find fd binary for @ file autocomplete\n\tconst fdPath = findFd();\n\n\t// Set up session manager\n\tconst sessionDir = parsed.sessionDir ?? getProjectSessionDir(cwd);\n\tlet sessionManager: SessionManager | undefined;\n\tif (parsed.noSession) {\n\t\tsessionManager = SessionManager.inMemory(cwd);\n\t} else if (parsed.session) {\n\t\tsessionManager = SessionManager.open(parsed.session, parsed.sessionDir);\n\t} else if (parsed.resume) {\n\t\t// Start a new session; the TUI will show the session picker on startup\n\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t} else if (parsed.continue) {\n\t\tconst recentFile = findRecentSession(sessionDir);\n\t\tif (recentFile) {\n\t\t\tsessionManager = SessionManager.open(recentFile, sessionDir);\n\t\t} else {\n\t\t\tif (parsed.verbose) {\n\t\t\t\tconsole.log(chalk.dim(\"No previous session found, starting new.\"));\n\t\t\t}\n\t\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t\t}\n\t} else {\n\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t}\n\n\t// Create agent config\n\tconst agentConfig: CodingAgentConfig = {\n\t\tmodel,\n\t\truntime: createNodeRuntime(),\n\t\tcwd,\n\t\ttoolSet: parsed.toolSet ?? \"coding\",\n\t\tproviderOptions: buildProviderOptions(provider, parsed.thinking),\n\t\tsessionManager,\n\t};\n\n\tagentConfig.systemPromptOptions = {\n\t\tappendSystemPrompt: parsed.appendSystemPrompt,\n\t\tcontextFiles,\n\t\tskills: toSkillMap(skills),\n\t};\n\tif (parsed.systemPrompt) {\n\t\tagentConfig.systemPromptOptions.customPrompt = parsed.systemPrompt;\n\t}\n\n\t// Create agent (session messages are auto-restored from sessionManager)\n\tconst agent = new CodingAgent(agentConfig);\n\n\tif (parsed.verbose && agent.messages.length > 0) {\n\t\tconsole.log(chalk.dim(`Restored ${agent.messages.length} messages from session.`));\n\t}\n\n\t// Dispatch to mode\n\tif (isInteractive) {\n\t\tawait runInteractiveMode(agent, {\n\t\t\tinitialMessage,\n\t\t\tinitialMessages: parsed.messages,\n\t\t\tsessionDir,\n\t\t\tagentConfig,\n\t\t\tresumeOnStart: parsed.resume,\n\t\t\tskills,\n\t\t\tcontextFiles,\n\t\t\tprompts,\n\t\t\tverbose: parsed.verbose,\n\t\t\tprovider,\n\t\t\tmodelId,\n\t\t\tauthStorage,\n\t\t\tsettingsManager,\n\t\t\tfdPath,\n\t\t\tonModelChange: async (newProvider: string, newModelId: string) => {\n\t\t\t\tconst { model: newModel } = await createModel({\n\t\t\t\t\tprovider: newProvider,\n\t\t\t\t\tmodel: newModelId,\n\t\t\t\t\tauthStorage,\n\t\t\t\t});\n\t\t\t\treturn new CodingAgent({\n\t\t\t\t\t...agentConfig,\n\t\t\t\t\tmodel: newModel,\n\t\t\t\t\tproviderOptions: buildProviderOptions(newProvider, parsed.thinking),\n\t\t\t\t});\n\t\t\t},\n\t\t});\n\t} else {\n\t\tawait runPrintMode(agent, {\n\t\t\tmode,\n\t\t\tmessages: parsed.messages,\n\t\t\tinitialMessage,\n\t\t});\n\t\tprocess.exit(0);\n\t}\n}\n"]}
|
package/dist/main.js
CHANGED
|
@@ -8,7 +8,9 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import { join, resolve } from "node:path";
|
|
10
10
|
import chalk from "chalk";
|
|
11
|
-
import { CodingAgent
|
|
11
|
+
import { CodingAgent } from "edge-pi";
|
|
12
|
+
import { createNodeRuntime } from "edge-pi/node";
|
|
13
|
+
import { SessionManager } from "edge-pi/session";
|
|
12
14
|
import { AuthStorage, anthropicOAuthProvider, githubCopilotOAuthProvider, openaiCodexOAuthProvider, } from "./auth/index.js";
|
|
13
15
|
import { parseArgs, printHelp, printModels } from "./cli/args.js";
|
|
14
16
|
import { loadContextFiles } from "./context.js";
|
|
@@ -240,6 +242,7 @@ export async function main(args) {
|
|
|
240
242
|
// Create agent config
|
|
241
243
|
const agentConfig = {
|
|
242
244
|
model,
|
|
245
|
+
runtime: createNodeRuntime(),
|
|
243
246
|
cwd,
|
|
244
247
|
toolSet: parsed.toolSet ?? "coding",
|
|
245
248
|
providerOptions: buildProviderOptions(provider, parsed.thinking),
|
package/dist/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Main entry point for the edge-pi CLI.\n *\n * Handles argument parsing, model creation, skill loading,\n * session management, auth, and mode dispatch.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport chalk from \"chalk\";\nimport type { CodingAgentConfig, Skill as PromptSkill } from \"edge-pi\";\nimport { CodingAgent, SessionManager } from \"edge-pi\";\nimport {\n\tAuthStorage,\n\tanthropicOAuthProvider,\n\tgithubCopilotOAuthProvider,\n\topenaiCodexOAuthProvider,\n} from \"./auth/index.js\";\nimport { parseArgs, printHelp, printModels } from \"./cli/args.js\";\nimport { loadContextFiles } from \"./context.js\";\nimport { createModel } from \"./model-factory.js\";\nimport { runInteractiveMode } from \"./modes/interactive-mode.js\";\nimport { runPrintMode } from \"./modes/print-mode.js\";\nimport { loadPrompts } from \"./prompts.js\";\nimport { buildProviderOptions } from \"./provider-options.js\";\nimport { SettingsManager } from \"./settings.js\";\nimport { loadSkills, type Skill } from \"./skills.js\";\nimport { findFd } from \"./utils/find-fd.js\";\n\nconst VERSION = \"0.1.0\";\nconst CONFIG_DIR_NAME = \".pi\";\n\nfunction getAgentDir(): string {\n\tconst envDir = process.env.PI_CODING_AGENT_DIR;\n\tif (envDir) {\n\t\tif (envDir === \"~\") return homedir();\n\t\tif (envDir.startsWith(\"~/\")) return homedir() + envDir.slice(1);\n\t\treturn envDir;\n\t}\n\treturn join(homedir(), CONFIG_DIR_NAME, \"agent\");\n}\n\nfunction getSessionsDir(): string {\n\treturn join(getAgentDir(), \"sessions\");\n}\n\nfunction getAuthPath(): string {\n\treturn join(getAgentDir(), \"auth.json\");\n}\n\n/**\n * Read all content from piped stdin.\n * Returns undefined if stdin is a TTY.\n */\nasync function readPipedStdin(): Promise<string | undefined> {\n\tif (process.stdin.isTTY) {\n\t\treturn undefined;\n\t}\n\treturn new Promise((resolve) => {\n\t\tlet data = \"\";\n\t\tprocess.stdin.setEncoding(\"utf8\");\n\t\tprocess.stdin.on(\"data\", (chunk) => {\n\t\t\tdata += chunk;\n\t\t});\n\t\tprocess.stdin.on(\"end\", () => {\n\t\t\tresolve(data.trim() || undefined);\n\t\t});\n\t\tprocess.stdin.resume();\n\t});\n}\n\n/**\n * Process @file arguments into text content.\n */\nfunction processFileArgs(fileArgs: string[]): string {\n\tconst parts: string[] = [];\n\tfor (const filePath of fileArgs) {\n\t\tconst resolved = resolve(filePath);\n\t\tif (!existsSync(resolved)) {\n\t\t\tconsole.error(chalk.yellow(`Warning: File not found: ${resolved}`));\n\t\t\tcontinue;\n\t\t}\n\t\ttry {\n\t\t\tconst content = readFileSync(resolved, \"utf-8\");\n\t\t\tparts.push(`<file path=\"${resolved}\">\\n${content}\\n</file>`);\n\t\t} catch (error) {\n\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${resolved}: ${(error as Error).message}`));\n\t\t}\n\t}\n\treturn parts.length > 0 ? `${parts.join(\"\\n\\n\")}\\n\\n` : \"\";\n}\n\n/**\n * Create a session directory path based on the current working directory.\n */\nfunction getProjectSessionDir(cwd: string): string {\n\tconst sanitized = cwd.replace(/\\//g, \"--\").replace(/^--/, \"\");\n\treturn join(getSessionsDir(), sanitized);\n}\n\n/**\n * Find the most recent session file in a directory.\n */\nfunction findRecentSession(sessionDir: string): string | undefined {\n\tif (!existsSync(sessionDir)) return undefined;\n\ttry {\n\t\tconst files = readdirSync(sessionDir)\n\t\t\t.filter((f: string) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f: string) => ({\n\t\t\t\tname: f,\n\t\t\t\tpath: join(sessionDir, f),\n\t\t\t\tmtime: statSync(join(sessionDir, f)).mtime.getTime(),\n\t\t\t}))\n\t\t\t.sort((a: { mtime: number }, b: { mtime: number }) => b.mtime - a.mtime);\n\t\treturn files[0]?.path;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Create and configure AuthStorage with built-in OAuth providers.\n */\nfunction createAuthStorage(): AuthStorage {\n\tconst authStorage = new AuthStorage(getAuthPath());\n\tauthStorage.registerProvider(anthropicOAuthProvider);\n\tauthStorage.registerProvider(githubCopilotOAuthProvider);\n\tauthStorage.registerProvider(openaiCodexOAuthProvider);\n\treturn authStorage;\n}\n\nfunction toSkillMap(skills: Skill[]): Record<string, PromptSkill> {\n\tconst map: Record<string, PromptSkill> = {};\n\tfor (const skill of skills) {\n\t\tmap[skill.name] = {\n\t\t\tdescription: skill.description,\n\t\t\tfilePath: skill.filePath,\n\t\t\tdisableModelInvocation: skill.disableModelInvocation,\n\t\t};\n\t}\n\treturn map;\n}\n\nexport async function main(args: string[]) {\n\tconst parsed = parseArgs(args);\n\n\tif (parsed.version) {\n\t\tconsole.log(VERSION);\n\t\treturn;\n\t}\n\n\tif (parsed.help) {\n\t\tprintHelp();\n\t\treturn;\n\t}\n\n\tif (parsed.listModels) {\n\t\tprintModels();\n\t\treturn;\n\t}\n\n\t// Read piped stdin\n\tconst stdinContent = await readPipedStdin();\n\tif (stdinContent !== undefined) {\n\t\tparsed.print = true;\n\t\tparsed.messages.unshift(stdinContent);\n\t}\n\n\t// Process @file arguments\n\tlet initialMessage: string | undefined;\n\tif (parsed.fileArgs.length > 0) {\n\t\tconst fileContent = processFileArgs(parsed.fileArgs);\n\t\tif (parsed.messages.length > 0) {\n\t\t\tinitialMessage = fileContent + parsed.messages.shift();\n\t\t} else {\n\t\t\tinitialMessage = fileContent;\n\t\t}\n\t}\n\n\tconst cwd = process.cwd();\n\tconst isInteractive = !parsed.print && parsed.mode === undefined;\n\tconst mode = parsed.mode || \"text\";\n\n\t// Set up auth storage\n\tconst authStorage = createAuthStorage();\n\n\t// Set up settings persistence\n\tconst settingsManager = SettingsManager.create(getAgentDir());\n\n\t// Apply CLI --api-key override\n\tif (parsed.apiKey && parsed.provider) {\n\t\tauthStorage.setRuntimeApiKey(parsed.provider, parsed.apiKey);\n\t}\n\n\t// Create model (async - may resolve OAuth tokens)\n\t// Fall back to saved defaults when CLI args are not specified\n\tconst { model, provider, modelId } = await createModel({\n\t\tprovider: parsed.provider ?? settingsManager.getDefaultProvider(),\n\t\tmodel: parsed.model ?? settingsManager.getDefaultModel(),\n\t\tapiKey: parsed.apiKey,\n\t\tauthStorage,\n\t});\n\n\t// Load skills\n\tlet skills: Skill[] = [];\n\tif (!parsed.noSkills) {\n\t\tconst skillResult = loadSkills({\n\t\t\tcwd,\n\t\t\tskillPaths: parsed.skills,\n\t\t\tincludeDefaults: true,\n\t\t});\n\t\tskills = skillResult.skills;\n\n\t\tif (parsed.verbose && skillResult.diagnostics.length > 0) {\n\t\t\tfor (const d of skillResult.diagnostics) {\n\t\t\t\tconsole.error(chalk.yellow(`Skill warning: ${d.message} (${d.path})`));\n\t\t\t}\n\t\t}\n\t}\n\n\t// Load context files (AGENTS.md)\n\tconst contextFiles = loadContextFiles(cwd);\n\tif (parsed.verbose && contextFiles.length > 0) {\n\t\tconsole.log(chalk.dim(`Loaded ${contextFiles.length} context file(s).`));\n\t}\n\n\t// Load prompt templates\n\tconst promptsResult = loadPrompts({ cwd });\n\tconst prompts = promptsResult.prompts;\n\tif (parsed.verbose && promptsResult.diagnostics.length > 0) {\n\t\tfor (const d of promptsResult.diagnostics) {\n\t\t\tconsole.error(chalk.yellow(`Prompt warning: ${d.message} (${d.path})`));\n\t\t}\n\t}\n\n\t// Find fd binary for @ file autocomplete\n\tconst fdPath = findFd();\n\n\t// Set up session manager\n\tconst sessionDir = parsed.sessionDir ?? getProjectSessionDir(cwd);\n\tlet sessionManager: SessionManager | undefined;\n\tif (parsed.noSession) {\n\t\tsessionManager = SessionManager.inMemory(cwd);\n\t} else if (parsed.session) {\n\t\tsessionManager = SessionManager.open(parsed.session, parsed.sessionDir);\n\t} else if (parsed.resume) {\n\t\t// Start a new session; the TUI will show the session picker on startup\n\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t} else if (parsed.continue) {\n\t\tconst recentFile = findRecentSession(sessionDir);\n\t\tif (recentFile) {\n\t\t\tsessionManager = SessionManager.open(recentFile, sessionDir);\n\t\t} else {\n\t\t\tif (parsed.verbose) {\n\t\t\t\tconsole.log(chalk.dim(\"No previous session found, starting new.\"));\n\t\t\t}\n\t\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t\t}\n\t} else {\n\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t}\n\n\t// Create agent config\n\tconst agentConfig: CodingAgentConfig = {\n\t\tmodel,\n\t\tcwd,\n\t\ttoolSet: parsed.toolSet ?? \"coding\",\n\t\tproviderOptions: buildProviderOptions(provider, parsed.thinking),\n\t\tsessionManager,\n\t};\n\n\tagentConfig.systemPromptOptions = {\n\t\tappendSystemPrompt: parsed.appendSystemPrompt,\n\t\tcontextFiles,\n\t\tskills: toSkillMap(skills),\n\t};\n\tif (parsed.systemPrompt) {\n\t\tagentConfig.systemPromptOptions.customPrompt = parsed.systemPrompt;\n\t}\n\n\t// Create agent (session messages are auto-restored from sessionManager)\n\tconst agent = new CodingAgent(agentConfig);\n\n\tif (parsed.verbose && agent.messages.length > 0) {\n\t\tconsole.log(chalk.dim(`Restored ${agent.messages.length} messages from session.`));\n\t}\n\n\t// Dispatch to mode\n\tif (isInteractive) {\n\t\tawait runInteractiveMode(agent, {\n\t\t\tinitialMessage,\n\t\t\tinitialMessages: parsed.messages,\n\t\t\tsessionDir,\n\t\t\tagentConfig,\n\t\t\tresumeOnStart: parsed.resume,\n\t\t\tskills,\n\t\t\tcontextFiles,\n\t\t\tprompts,\n\t\t\tverbose: parsed.verbose,\n\t\t\tprovider,\n\t\t\tmodelId,\n\t\t\tauthStorage,\n\t\t\tsettingsManager,\n\t\t\tfdPath,\n\t\t\tonModelChange: async (newProvider: string, newModelId: string) => {\n\t\t\t\tconst { model: newModel } = await createModel({\n\t\t\t\t\tprovider: newProvider,\n\t\t\t\t\tmodel: newModelId,\n\t\t\t\t\tauthStorage,\n\t\t\t\t});\n\t\t\t\treturn new CodingAgent({\n\t\t\t\t\t...agentConfig,\n\t\t\t\t\tmodel: newModel,\n\t\t\t\t\tproviderOptions: buildProviderOptions(newProvider, parsed.thinking),\n\t\t\t\t});\n\t\t\t},\n\t\t});\n\t} else {\n\t\tawait runPrintMode(agent, {\n\t\t\tmode,\n\t\t\tmessages: parsed.messages,\n\t\t\tinitialMessage,\n\t\t});\n\t\tprocess.exit(0);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EACN,WAAW,EACX,sBAAsB,EACtB,0BAA0B,EAC1B,wBAAwB,GACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,UAAU,EAAc,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,OAAO,GAAG,OAAO,CAAC;AACxB,MAAM,eAAe,GAAG,KAAK,CAAC;AAE9B,SAAS,WAAW,GAAW;IAC9B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;IAC/C,IAAI,MAAM,EAAE,CAAC;QACZ,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,OAAO,EAAE,CAAC;QACrC,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,OAAO,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAChE,OAAO,MAAM,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,OAAO,CAAC,CAAC;AAAA,CACjD;AAED,SAAS,cAAc,GAAW;IACjC,OAAO,IAAI,CAAC,WAAW,EAAE,EAAE,UAAU,CAAC,CAAC;AAAA,CACvC;AAED,SAAS,WAAW,GAAW;IAC9B,OAAO,IAAI,CAAC,WAAW,EAAE,EAAE,WAAW,CAAC,CAAC;AAAA,CACxC;AAED;;;GAGG;AACH,KAAK,UAAU,cAAc,GAAgC;IAC5D,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QAC/B,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC;YACnC,IAAI,IAAI,KAAK,CAAC;QAAA,CACd,CAAC,CAAC;QACH,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC;YAC7B,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,SAAS,CAAC,CAAC;QAAA,CAClC,CAAC,CAAC;QACH,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;IAAA,CACvB,CAAC,CAAC;AAAA,CACH;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,QAAkB,EAAU;IACpD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,QAAQ,IAAI,QAAQ,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,4BAA4B,QAAQ,EAAE,CAAC,CAAC,CAAC;YACpE,SAAS;QACV,CAAC;QACD,IAAI,CAAC;YACJ,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAChD,KAAK,CAAC,IAAI,CAAC,eAAe,QAAQ,OAAO,OAAO,WAAW,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,2BAA2B,QAAQ,KAAM,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACjG,CAAC;IACF,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;AAAA,CAC3D;AAED;;GAEG;AACH,SAAS,oBAAoB,CAAC,GAAW,EAAU;IAClD,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC9D,OAAO,IAAI,CAAC,cAAc,EAAE,EAAE,SAAS,CAAC,CAAC;AAAA,CACzC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,UAAkB,EAAsB;IAClE,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9C,IAAI,CAAC;QACJ,MAAM,KAAK,GAAG,WAAW,CAAC,UAAU,CAAC;aACnC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;aAC3C,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC;YACpB,IAAI,EAAE,CAAC;YACP,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;YACzB,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE;SACpD,CAAC,CAAC;aACF,IAAI,CAAC,CAAC,CAAoB,EAAE,CAAoB,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAC1E,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;AAAA,CACD;AAED;;GAEG;AACH,SAAS,iBAAiB,GAAgB;IACzC,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;IACnD,WAAW,CAAC,gBAAgB,CAAC,sBAAsB,CAAC,CAAC;IACrD,WAAW,CAAC,gBAAgB,CAAC,0BAA0B,CAAC,CAAC;IACzD,WAAW,CAAC,gBAAgB,CAAC,wBAAwB,CAAC,CAAC;IACvD,OAAO,WAAW,CAAC;AAAA,CACnB;AAED,SAAS,UAAU,CAAC,MAAe,EAA+B;IACjE,MAAM,GAAG,GAAgC,EAAE,CAAC;IAC5C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG;YACjB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,sBAAsB,EAAE,KAAK,CAAC,sBAAsB;SACpD,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AAAA,CACX;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAc,EAAE;IAC1C,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAE/B,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrB,OAAO;IACR,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QACjB,SAAS,EAAE,CAAC;QACZ,OAAO;IACR,CAAC;IAED,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACvB,WAAW,EAAE,CAAC;QACd,OAAO;IACR,CAAC;IAED,mBAAmB;IACnB,MAAM,YAAY,GAAG,MAAM,cAAc,EAAE,CAAC;IAC5C,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACvC,CAAC;IAED,0BAA0B;IAC1B,IAAI,cAAkC,CAAC;IACvC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,MAAM,WAAW,GAAG,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACrD,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChC,cAAc,GAAG,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACxD,CAAC;aAAM,CAAC;YACP,cAAc,GAAG,WAAW,CAAC;QAC9B,CAAC;IACF,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,aAAa,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC;IACjE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC;IAEnC,sBAAsB;IACtB,MAAM,WAAW,GAAG,iBAAiB,EAAE,CAAC;IAExC,8BAA8B;IAC9B,MAAM,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IAE9D,+BAA+B;IAC/B,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACtC,WAAW,CAAC,gBAAgB,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9D,CAAC;IAED,kDAAkD;IAClD,8DAA8D;IAC9D,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,WAAW,CAAC;QACtD,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,eAAe,CAAC,kBAAkB,EAAE;QACjE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,eAAe,CAAC,eAAe,EAAE;QACxD,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,WAAW;KACX,CAAC,CAAC;IAEH,cAAc;IACd,IAAI,MAAM,GAAY,EAAE,CAAC;IACzB,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QACtB,MAAM,WAAW,GAAG,UAAU,CAAC;YAC9B,GAAG;YACH,UAAU,EAAE,MAAM,CAAC,MAAM;YACzB,eAAe,EAAE,IAAI;SACrB,CAAC,CAAC;QACH,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC;QAE5B,IAAI,MAAM,CAAC,OAAO,IAAI,WAAW,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1D,KAAK,MAAM,CAAC,IAAI,WAAW,CAAC,WAAW,EAAE,CAAC;gBACzC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;YACxE,CAAC;QACF,CAAC;IACF,CAAC;IAED,iCAAiC;IACjC,MAAM,YAAY,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAC3C,IAAI,MAAM,CAAC,OAAO,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,YAAY,CAAC,MAAM,mBAAmB,CAAC,CAAC,CAAC;IAC1E,CAAC;IAED,wBAAwB;IACxB,MAAM,aAAa,GAAG,WAAW,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC;IACtC,IAAI,MAAM,CAAC,OAAO,IAAI,aAAa,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5D,KAAK,MAAM,CAAC,IAAI,aAAa,CAAC,WAAW,EAAE,CAAC;YAC3C,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QACzE,CAAC;IACF,CAAC;IAED,yCAAyC;IACzC,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IAExB,yBAAyB;IACzB,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAClE,IAAI,cAA0C,CAAC;IAC/C,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,cAAc,GAAG,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC/C,CAAC;SAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QAC3B,cAAc,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IACzE,CAAC;SAAM,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAC1B,uEAAuE;QACvE,cAAc,GAAG,cAAc,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACzD,CAAC;SAAM,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QAC5B,MAAM,UAAU,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC;QACjD,IAAI,UAAU,EAAE,CAAC;YAChB,cAAc,GAAG,cAAc,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QAC9D,CAAC;aAAM,CAAC;YACP,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC,CAAC;YACpE,CAAC;YACD,cAAc,GAAG,cAAc,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QACzD,CAAC;IACF,CAAC;SAAM,CAAC;QACP,cAAc,GAAG,cAAc,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACzD,CAAC;IAED,sBAAsB;IACtB,MAAM,WAAW,GAAsB;QACtC,KAAK;QACL,OAAO,EAAE,iBAAiB,EAAE;QAC5B,GAAG;QACH,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,QAAQ;QACnC,eAAe,EAAE,oBAAoB,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;QAChE,cAAc;KACd,CAAC;IAEF,WAAW,CAAC,mBAAmB,GAAG;QACjC,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;QAC7C,YAAY;QACZ,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC;KAC1B,CAAC;IACF,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACzB,WAAW,CAAC,mBAAmB,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;IACpE,CAAC;IAED,wEAAwE;IACxE,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,WAAW,CAAC,CAAC;IAE3C,IAAI,MAAM,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,KAAK,CAAC,QAAQ,CAAC,MAAM,yBAAyB,CAAC,CAAC,CAAC;IACpF,CAAC;IAED,mBAAmB;IACnB,IAAI,aAAa,EAAE,CAAC;QACnB,MAAM,kBAAkB,CAAC,KAAK,EAAE;YAC/B,cAAc;YACd,eAAe,EAAE,MAAM,CAAC,QAAQ;YAChC,UAAU;YACV,WAAW;YACX,aAAa,EAAE,MAAM,CAAC,MAAM;YAC5B,MAAM;YACN,YAAY;YACZ,OAAO;YACP,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,QAAQ;YACR,OAAO;YACP,WAAW;YACX,eAAe;YACf,MAAM;YACN,aAAa,EAAE,KAAK,EAAE,WAAmB,EAAE,UAAkB,EAAE,EAAE,CAAC;gBACjE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,WAAW,CAAC;oBAC7C,QAAQ,EAAE,WAAW;oBACrB,KAAK,EAAE,UAAU;oBACjB,WAAW;iBACX,CAAC,CAAC;gBACH,OAAO,IAAI,WAAW,CAAC;oBACtB,GAAG,WAAW;oBACd,KAAK,EAAE,QAAQ;oBACf,eAAe,EAAE,oBAAoB,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC;iBACnE,CAAC,CAAC;YAAA,CACH;SACD,CAAC,CAAC;IACJ,CAAC;SAAM,CAAC;QACP,MAAM,YAAY,CAAC,KAAK,EAAE;YACzB,IAAI;YACJ,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,cAAc;SACd,CAAC,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;AAAA,CACD","sourcesContent":["/**\n * Main entry point for the edge-pi CLI.\n *\n * Handles argument parsing, model creation, skill loading,\n * session management, auth, and mode dispatch.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport chalk from \"chalk\";\nimport type { CodingAgentConfig, Skill as PromptSkill } from \"edge-pi\";\nimport { CodingAgent } from \"edge-pi\";\nimport { createNodeRuntime } from \"edge-pi/node\";\nimport { SessionManager } from \"edge-pi/session\";\nimport {\n\tAuthStorage,\n\tanthropicOAuthProvider,\n\tgithubCopilotOAuthProvider,\n\topenaiCodexOAuthProvider,\n} from \"./auth/index.js\";\nimport { parseArgs, printHelp, printModels } from \"./cli/args.js\";\nimport { loadContextFiles } from \"./context.js\";\nimport { createModel } from \"./model-factory.js\";\nimport { runInteractiveMode } from \"./modes/interactive-mode.js\";\nimport { runPrintMode } from \"./modes/print-mode.js\";\nimport { loadPrompts } from \"./prompts.js\";\nimport { buildProviderOptions } from \"./provider-options.js\";\nimport { SettingsManager } from \"./settings.js\";\nimport { loadSkills, type Skill } from \"./skills.js\";\nimport { findFd } from \"./utils/find-fd.js\";\n\nconst VERSION = \"0.1.0\";\nconst CONFIG_DIR_NAME = \".pi\";\n\nfunction getAgentDir(): string {\n\tconst envDir = process.env.PI_CODING_AGENT_DIR;\n\tif (envDir) {\n\t\tif (envDir === \"~\") return homedir();\n\t\tif (envDir.startsWith(\"~/\")) return homedir() + envDir.slice(1);\n\t\treturn envDir;\n\t}\n\treturn join(homedir(), CONFIG_DIR_NAME, \"agent\");\n}\n\nfunction getSessionsDir(): string {\n\treturn join(getAgentDir(), \"sessions\");\n}\n\nfunction getAuthPath(): string {\n\treturn join(getAgentDir(), \"auth.json\");\n}\n\n/**\n * Read all content from piped stdin.\n * Returns undefined if stdin is a TTY.\n */\nasync function readPipedStdin(): Promise<string | undefined> {\n\tif (process.stdin.isTTY) {\n\t\treturn undefined;\n\t}\n\treturn new Promise((resolve) => {\n\t\tlet data = \"\";\n\t\tprocess.stdin.setEncoding(\"utf8\");\n\t\tprocess.stdin.on(\"data\", (chunk) => {\n\t\t\tdata += chunk;\n\t\t});\n\t\tprocess.stdin.on(\"end\", () => {\n\t\t\tresolve(data.trim() || undefined);\n\t\t});\n\t\tprocess.stdin.resume();\n\t});\n}\n\n/**\n * Process @file arguments into text content.\n */\nfunction processFileArgs(fileArgs: string[]): string {\n\tconst parts: string[] = [];\n\tfor (const filePath of fileArgs) {\n\t\tconst resolved = resolve(filePath);\n\t\tif (!existsSync(resolved)) {\n\t\t\tconsole.error(chalk.yellow(`Warning: File not found: ${resolved}`));\n\t\t\tcontinue;\n\t\t}\n\t\ttry {\n\t\t\tconst content = readFileSync(resolved, \"utf-8\");\n\t\t\tparts.push(`<file path=\"${resolved}\">\\n${content}\\n</file>`);\n\t\t} catch (error) {\n\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${resolved}: ${(error as Error).message}`));\n\t\t}\n\t}\n\treturn parts.length > 0 ? `${parts.join(\"\\n\\n\")}\\n\\n` : \"\";\n}\n\n/**\n * Create a session directory path based on the current working directory.\n */\nfunction getProjectSessionDir(cwd: string): string {\n\tconst sanitized = cwd.replace(/\\//g, \"--\").replace(/^--/, \"\");\n\treturn join(getSessionsDir(), sanitized);\n}\n\n/**\n * Find the most recent session file in a directory.\n */\nfunction findRecentSession(sessionDir: string): string | undefined {\n\tif (!existsSync(sessionDir)) return undefined;\n\ttry {\n\t\tconst files = readdirSync(sessionDir)\n\t\t\t.filter((f: string) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f: string) => ({\n\t\t\t\tname: f,\n\t\t\t\tpath: join(sessionDir, f),\n\t\t\t\tmtime: statSync(join(sessionDir, f)).mtime.getTime(),\n\t\t\t}))\n\t\t\t.sort((a: { mtime: number }, b: { mtime: number }) => b.mtime - a.mtime);\n\t\treturn files[0]?.path;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Create and configure AuthStorage with built-in OAuth providers.\n */\nfunction createAuthStorage(): AuthStorage {\n\tconst authStorage = new AuthStorage(getAuthPath());\n\tauthStorage.registerProvider(anthropicOAuthProvider);\n\tauthStorage.registerProvider(githubCopilotOAuthProvider);\n\tauthStorage.registerProvider(openaiCodexOAuthProvider);\n\treturn authStorage;\n}\n\nfunction toSkillMap(skills: Skill[]): Record<string, PromptSkill> {\n\tconst map: Record<string, PromptSkill> = {};\n\tfor (const skill of skills) {\n\t\tmap[skill.name] = {\n\t\t\tdescription: skill.description,\n\t\t\tfilePath: skill.filePath,\n\t\t\tdisableModelInvocation: skill.disableModelInvocation,\n\t\t};\n\t}\n\treturn map;\n}\n\nexport async function main(args: string[]) {\n\tconst parsed = parseArgs(args);\n\n\tif (parsed.version) {\n\t\tconsole.log(VERSION);\n\t\treturn;\n\t}\n\n\tif (parsed.help) {\n\t\tprintHelp();\n\t\treturn;\n\t}\n\n\tif (parsed.listModels) {\n\t\tprintModels();\n\t\treturn;\n\t}\n\n\t// Read piped stdin\n\tconst stdinContent = await readPipedStdin();\n\tif (stdinContent !== undefined) {\n\t\tparsed.print = true;\n\t\tparsed.messages.unshift(stdinContent);\n\t}\n\n\t// Process @file arguments\n\tlet initialMessage: string | undefined;\n\tif (parsed.fileArgs.length > 0) {\n\t\tconst fileContent = processFileArgs(parsed.fileArgs);\n\t\tif (parsed.messages.length > 0) {\n\t\t\tinitialMessage = fileContent + parsed.messages.shift();\n\t\t} else {\n\t\t\tinitialMessage = fileContent;\n\t\t}\n\t}\n\n\tconst cwd = process.cwd();\n\tconst isInteractive = !parsed.print && parsed.mode === undefined;\n\tconst mode = parsed.mode || \"text\";\n\n\t// Set up auth storage\n\tconst authStorage = createAuthStorage();\n\n\t// Set up settings persistence\n\tconst settingsManager = SettingsManager.create(getAgentDir());\n\n\t// Apply CLI --api-key override\n\tif (parsed.apiKey && parsed.provider) {\n\t\tauthStorage.setRuntimeApiKey(parsed.provider, parsed.apiKey);\n\t}\n\n\t// Create model (async - may resolve OAuth tokens)\n\t// Fall back to saved defaults when CLI args are not specified\n\tconst { model, provider, modelId } = await createModel({\n\t\tprovider: parsed.provider ?? settingsManager.getDefaultProvider(),\n\t\tmodel: parsed.model ?? settingsManager.getDefaultModel(),\n\t\tapiKey: parsed.apiKey,\n\t\tauthStorage,\n\t});\n\n\t// Load skills\n\tlet skills: Skill[] = [];\n\tif (!parsed.noSkills) {\n\t\tconst skillResult = loadSkills({\n\t\t\tcwd,\n\t\t\tskillPaths: parsed.skills,\n\t\t\tincludeDefaults: true,\n\t\t});\n\t\tskills = skillResult.skills;\n\n\t\tif (parsed.verbose && skillResult.diagnostics.length > 0) {\n\t\t\tfor (const d of skillResult.diagnostics) {\n\t\t\t\tconsole.error(chalk.yellow(`Skill warning: ${d.message} (${d.path})`));\n\t\t\t}\n\t\t}\n\t}\n\n\t// Load context files (AGENTS.md)\n\tconst contextFiles = loadContextFiles(cwd);\n\tif (parsed.verbose && contextFiles.length > 0) {\n\t\tconsole.log(chalk.dim(`Loaded ${contextFiles.length} context file(s).`));\n\t}\n\n\t// Load prompt templates\n\tconst promptsResult = loadPrompts({ cwd });\n\tconst prompts = promptsResult.prompts;\n\tif (parsed.verbose && promptsResult.diagnostics.length > 0) {\n\t\tfor (const d of promptsResult.diagnostics) {\n\t\t\tconsole.error(chalk.yellow(`Prompt warning: ${d.message} (${d.path})`));\n\t\t}\n\t}\n\n\t// Find fd binary for @ file autocomplete\n\tconst fdPath = findFd();\n\n\t// Set up session manager\n\tconst sessionDir = parsed.sessionDir ?? getProjectSessionDir(cwd);\n\tlet sessionManager: SessionManager | undefined;\n\tif (parsed.noSession) {\n\t\tsessionManager = SessionManager.inMemory(cwd);\n\t} else if (parsed.session) {\n\t\tsessionManager = SessionManager.open(parsed.session, parsed.sessionDir);\n\t} else if (parsed.resume) {\n\t\t// Start a new session; the TUI will show the session picker on startup\n\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t} else if (parsed.continue) {\n\t\tconst recentFile = findRecentSession(sessionDir);\n\t\tif (recentFile) {\n\t\t\tsessionManager = SessionManager.open(recentFile, sessionDir);\n\t\t} else {\n\t\t\tif (parsed.verbose) {\n\t\t\t\tconsole.log(chalk.dim(\"No previous session found, starting new.\"));\n\t\t\t}\n\t\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t\t}\n\t} else {\n\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t}\n\n\t// Create agent config\n\tconst agentConfig: CodingAgentConfig = {\n\t\tmodel,\n\t\truntime: createNodeRuntime(),\n\t\tcwd,\n\t\ttoolSet: parsed.toolSet ?? \"coding\",\n\t\tproviderOptions: buildProviderOptions(provider, parsed.thinking),\n\t\tsessionManager,\n\t};\n\n\tagentConfig.systemPromptOptions = {\n\t\tappendSystemPrompt: parsed.appendSystemPrompt,\n\t\tcontextFiles,\n\t\tskills: toSkillMap(skills),\n\t};\n\tif (parsed.systemPrompt) {\n\t\tagentConfig.systemPromptOptions.customPrompt = parsed.systemPrompt;\n\t}\n\n\t// Create agent (session messages are auto-restored from sessionManager)\n\tconst agent = new CodingAgent(agentConfig);\n\n\tif (parsed.verbose && agent.messages.length > 0) {\n\t\tconsole.log(chalk.dim(`Restored ${agent.messages.length} messages from session.`));\n\t}\n\n\t// Dispatch to mode\n\tif (isInteractive) {\n\t\tawait runInteractiveMode(agent, {\n\t\t\tinitialMessage,\n\t\t\tinitialMessages: parsed.messages,\n\t\t\tsessionDir,\n\t\t\tagentConfig,\n\t\t\tresumeOnStart: parsed.resume,\n\t\t\tskills,\n\t\t\tcontextFiles,\n\t\t\tprompts,\n\t\t\tverbose: parsed.verbose,\n\t\t\tprovider,\n\t\t\tmodelId,\n\t\t\tauthStorage,\n\t\t\tsettingsManager,\n\t\t\tfdPath,\n\t\t\tonModelChange: async (newProvider: string, newModelId: string) => {\n\t\t\t\tconst { model: newModel } = await createModel({\n\t\t\t\t\tprovider: newProvider,\n\t\t\t\t\tmodel: newModelId,\n\t\t\t\t\tauthStorage,\n\t\t\t\t});\n\t\t\t\treturn new CodingAgent({\n\t\t\t\t\t...agentConfig,\n\t\t\t\t\tmodel: newModel,\n\t\t\t\t\tproviderOptions: buildProviderOptions(newProvider, parsed.thinking),\n\t\t\t\t});\n\t\t\t},\n\t\t});\n\t} else {\n\t\tawait runPrintMode(agent, {\n\t\t\tmode,\n\t\t\tmessages: parsed.messages,\n\t\t\tinitialMessage,\n\t\t});\n\t\tprocess.exit(0);\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive-mode.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/interactive-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAqBH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAwC7C,MAAM,WAAW,sBAAsB;IACtC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,sFAAsF;IACtF,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6FAA6F;IAC7F,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC5E,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,iEAAiE;IACjE,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG3G","sourcesContent":["/**\n * Interactive mode using @mariozechner/pi-tui.\n *\n * Replaces the old readline-based REPL with a proper TUI that matches\n * the UX patterns from @mariozechner/pi-coding-agent:\n * - Editor component for input with submit/escape handling\n * - Markdown rendering for assistant responses\n * - Tool execution components with collapsible output\n * - Footer with model/provider info and token stats\n * - Container-based layout (header → chat → pending → editor → footer)\n * - Context compaction (manual /compact + auto mode)\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n\tCombinedAutocompleteProvider,\n\tContainer,\n\tEditor,\n\tKey,\n\tLoader,\n\tmatchesKey,\n\tProcessTerminal,\n\ttype SelectItem,\n\tSelectList,\n\ttype SlashCommand,\n\tSpacer,\n\tText,\n\tTUI,\n} from \"@mariozechner/pi-tui\";\nimport type { ImagePart, ModelMessage } from \"ai\";\nimport chalk from \"chalk\";\nimport type { CodingAgent, CodingAgentConfig } from \"edge-pi\";\nimport { type CompactionResult, estimateContextTokens, SessionManager as SessionManagerClass } from \"edge-pi\";\nimport type { AuthStorage } from \"../../auth/auth-storage.js\";\nimport type { ContextFile } from \"../../context.js\";\nimport { getLatestModels } from \"../../model-factory.js\";\nimport type { PromptTemplate } from \"../../prompts.js\";\nimport { expandPromptTemplate } from \"../../prompts.js\";\nimport type { SettingsManager } from \"../../settings.js\";\nimport type { Skill } from \"../../skills.js\";\nimport { executeBashCommand } from \"../../utils/bash-executor.js\";\nimport { type ClipboardImage, extensionForImageMimeType, readClipboardImage } from \"../../utils/clipboard-image.js\";\nimport { formatAIError } from \"../../utils/format-ai-error.js\";\nimport { formatPendingMessages, parseBashInput } from \"./bash-helpers.js\";\nimport { AssistantMessageComponent } from \"./components/assistant-message.js\";\nimport { BashExecutionComponent } from \"./components/bash-execution.js\";\nimport { CompactionSummaryComponent } from \"./components/compaction-summary.js\";\nimport { FooterComponent } from \"./components/footer.js\";\nimport { ToolExecutionComponent, type ToolOutput } from \"./components/tool-execution.js\";\nimport { UserMessageComponent } from \"./components/user-message.js\";\nimport { getEditorTheme, getMarkdownTheme, getSelectListTheme } from \"./theme.js\";\n\n/** Default context window size (used when model doesn't report one). */\nconst DEFAULT_CONTEXT_WINDOW = 200_000;\nconst DEFAULT_COMPACTION_SETTINGS = {\n\treserveTokens: 16384,\n\tkeepRecentTokens: 20000,\n} as const;\n\ntype CompactionSettings = {\n\treserveTokens: number;\n\tkeepRecentTokens: number;\n};\n\n/** Extract display-friendly output from a tool result. Handles both plain strings and structured objects with text/image fields. */\nfunction extractToolOutput(output: unknown): ToolOutput {\n\tif (typeof output === \"string\") {\n\t\treturn { text: output };\n\t}\n\tif (typeof output === \"object\" && output !== null && \"text\" in output && typeof (output as any).text === \"string\") {\n\t\tconst obj = output as any;\n\t\treturn {\n\t\t\ttext: obj.text,\n\t\t\t...(obj.image && { image: obj.image }),\n\t\t};\n\t}\n\treturn { text: JSON.stringify(output) };\n}\n\nexport interface InteractiveModeOptions {\n\tinitialMessage?: string;\n\tinitialMessages?: string[];\n\tskills?: Skill[];\n\tcontextFiles?: ContextFile[];\n\tprompts?: PromptTemplate[];\n\tverbose?: boolean;\n\tprovider: string;\n\tmodelId: string;\n\tauthStorage?: AuthStorage;\n\t/** Settings manager for persisting user preferences (provider, model, compaction). */\n\tsettingsManager?: SettingsManager;\n\t/** Path to the `fd` binary for @ file autocomplete, or undefined if unavailable. */\n\tfdPath?: string;\n\t/** Called when the user switches model via Ctrl+L. Returns a new agent for the new model. */\n\tonModelChange?: (provider: string, modelId: string) => Promise<CodingAgent>;\n\t/** Context window size for the model. Defaults to 200k. */\n\tcontextWindow?: number;\n\t/** Directory where session files are stored. Required for /resume. */\n\tsessionDir?: string;\n\t/** Agent config used to recreate agents when resuming sessions. */\n\tagentConfig?: CodingAgentConfig;\n\t/** When true, show the session picker immediately on startup. */\n\tresumeOnStart?: boolean;\n}\n\n/**\n * Run the interactive TUI mode with streaming output.\n */\nexport async function runInteractiveMode(agent: CodingAgent, options: InteractiveModeOptions): Promise<void> {\n\tconst mode = new InteractiveMode(agent, options);\n\tawait mode.run();\n}\n\n// ============================================================================\n// InteractiveMode class\n// ============================================================================\n\nclass InteractiveMode {\n\tprivate agent: CodingAgent;\n\tprivate options: InteractiveModeOptions;\n\tprivate currentProvider: string;\n\tprivate currentModelId: string;\n\n\tprivate ui!: TUI;\n\tprivate headerContainer!: Container;\n\tprivate chatContainer!: Container;\n\tprivate pendingContainer!: Container;\n\tprivate pendingMessagesContainer!: Container;\n\tprivate editorContainer!: Container;\n\tprivate editor!: Editor;\n\tprivate editorTheme!: import(\"@mariozechner/pi-tui\").EditorTheme;\n\n\t// Message queues\n\tprivate steeringMessages: string[] = [];\n\tprivate followUpMessages: string[] = [];\n\tprivate isStreaming = false;\n\n\t// Inline bash state\n\tprivate isBashMode = false;\n\tprivate isBashRunning = false;\n\tprivate bashAbortController: AbortController | null = null;\n\tprivate bashComponent: import(\"./components/bash-execution.js\").BashExecutionComponent | undefined = undefined;\n\tprivate footer!: FooterComponent;\n\n\t// Loading animation during agent processing\n\tprivate loadingAnimation: Loader | undefined = undefined;\n\n\t// Streaming state\n\tprivate streamingComponent: AssistantMessageComponent | undefined = undefined;\n\tprivate streamingText = \"\";\n\tprivate hadToolResults = false;\n\n\t// Tool execution tracking: toolCallId → component\n\tprivate pendingTools = new Map<string, ToolExecutionComponent>();\n\n\t// Tool output expansion state\n\tprivate toolOutputExpanded = false;\n\n\t// Callback for resolving user input promise\n\tprivate onInputCallback?: (text: string) => void;\n\n\t// Pending clipboard images to attach to the next message\n\tprivate pendingImages: ClipboardImage[] = [];\n\n\t// Compaction state\n\tprivate contextWindow: number;\n\tprivate compactionSettings: CompactionSettings;\n\tprivate autoCompaction = true;\n\tprivate isCompacting = false;\n\tprivate compactionLoader: Loader | null = null;\n\n\tconstructor(agent: CodingAgent, options: InteractiveModeOptions) {\n\t\tthis.agent = agent;\n\t\tthis.options = options;\n\t\tthis.currentProvider = options.provider;\n\t\tthis.currentModelId = options.modelId;\n\t\tthis.contextWindow = options.contextWindow ?? DEFAULT_CONTEXT_WINDOW;\n\n\t\t// Initialize compaction settings from persisted settings if available\n\t\tconst savedCompaction = options.settingsManager?.getCompaction();\n\t\tthis.compactionSettings = {\n\t\t\t...DEFAULT_COMPACTION_SETTINGS,\n\t\t\t...(savedCompaction?.reserveTokens !== undefined && { reserveTokens: savedCompaction.reserveTokens }),\n\t\t\t...(savedCompaction?.keepRecentTokens !== undefined && { keepRecentTokens: savedCompaction.keepRecentTokens }),\n\t\t};\n\t\tthis.autoCompaction = options.settingsManager?.getCompactionEnabled() ?? true;\n\n\t\tthis.configureAgentCompaction();\n\t}\n\n\tasync run(): Promise<void> {\n\t\tthis.initUI();\n\t\tthis.updateFooterTokens();\n\n\t\t// Show session picker immediately if --resume was passed\n\t\tif (this.options.resumeOnStart) {\n\t\t\tawait this.handleResume();\n\t\t}\n\n\t\t// Process initial messages\n\t\tconst { initialMessage, initialMessages = [] } = this.options;\n\n\t\tconst allInitial: string[] = [];\n\t\tif (initialMessage) allInitial.push(initialMessage);\n\t\tallInitial.push(...initialMessages);\n\n\t\tfor (const msg of allInitial) {\n\t\t\tthis.chatContainer.addChild(new UserMessageComponent(msg, getMarkdownTheme()));\n\t\t\tthis.ui.requestRender();\n\t\t\tawait this.streamPrompt(msg);\n\t\t}\n\n\t\t// Main interactive loop\n\t\twhile (true) {\n\t\t\tconst userInput = await this.getUserInput();\n\t\t\tawait this.handleUserInput(userInput);\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// UI Setup\n\t// ========================================================================\n\n\tprivate initUI(): void {\n\t\tconst { provider, modelId, skills = [], contextFiles = [], prompts = [], verbose } = this.options;\n\n\t\tthis.ui = new TUI(new ProcessTerminal());\n\n\t\t// Header\n\t\tthis.headerContainer = new Container();\n\t\tconst logo = chalk.bold(\"epi\") + chalk.dim(` - ${provider}/${modelId}`);\n\n\t\tconst hints = [\n\t\t\t`${chalk.dim(\"Escape\")} to abort`,\n\t\t\t`${chalk.dim(\"!\")} inline bash`,\n\t\t\t`${chalk.dim(\"Alt+Enter\")} follow-up while streaming`,\n\t\t\t`${chalk.dim(\"Ctrl+C\")} to exit`,\n\t\t\t`${chalk.dim(\"Ctrl+E\")} to expand tools`,\n\t\t\t`${chalk.dim(\"Ctrl+L\")} to switch model`,\n\t\t\t`${chalk.dim(\"Ctrl+V\")} to paste image`,\n\t\t\t`${chalk.dim(\"↑/↓\")} to browse history`,\n\t\t\t`${chalk.dim(\"@\")} for file references`,\n\t\t\t`${chalk.dim(\"/\")} for commands`,\n\t\t].join(\"\\n\");\n\n\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\tthis.headerContainer.addChild(new Text(`${logo}\\n${hints}`, 1, 0));\n\t\tthis.headerContainer.addChild(new Spacer(1));\n\n\t\tif (verbose && this.agent.sessionManager?.getSessionFile()) {\n\t\t\tthis.headerContainer.addChild(\n\t\t\t\tnew Text(chalk.dim(`Session: ${this.agent.sessionManager.getSessionFile()}`), 1, 0),\n\t\t\t);\n\t\t}\n\n\t\t// Show loaded context, skills, and prompts at startup\n\t\tthis.showLoadedResources(contextFiles, skills, prompts);\n\n\t\t// Chat area\n\t\tthis.chatContainer = new Container();\n\n\t\t// Pending messages (loading animations, status)\n\t\tthis.pendingContainer = new Container();\n\n\t\t// Pending steering/follow-up messages\n\t\tthis.pendingMessagesContainer = new Container();\n\n\t\t// Editor with slash command autocomplete\n\t\tthis.editorTheme = getEditorTheme();\n\t\tthis.editor = new Editor(this.ui, this.editorTheme);\n\t\tthis.editor.setAutocompleteProvider(this.buildAutocompleteProvider());\n\t\tthis.editorContainer = new Container();\n\t\tthis.editorContainer.addChild(this.editor);\n\n\t\t// Footer\n\t\tthis.footer = new FooterComponent(this.currentProvider, this.currentModelId);\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\n\t\t// Assemble layout\n\t\tthis.ui.addChild(this.headerContainer);\n\t\tthis.ui.addChild(this.chatContainer);\n\t\tthis.ui.addChild(this.pendingMessagesContainer);\n\t\tthis.ui.addChild(this.pendingContainer);\n\t\tthis.ui.addChild(this.editorContainer);\n\t\tthis.ui.addChild(this.footer);\n\n\t\tthis.ui.setFocus(this.editor);\n\t\tthis.setupKeyHandlers();\n\n\t\tthis.ui.start();\n\t}\n\n\t// ========================================================================\n\t// Key Handlers\n\t// ========================================================================\n\n\tprivate setupKeyHandlers(): void {\n\t\tthis.editor.onChange = (text: string) => {\n\t\t\tconst wasBashMode = this.isBashMode;\n\t\t\tthis.isBashMode = text.trimStart().startsWith(\"!\");\n\t\t\tif (wasBashMode !== this.isBashMode) {\n\t\t\t\tthis.updateEditorBorderColor();\n\t\t\t}\n\t\t};\n\t\tthis.editor.onSubmit = (text: string) => {\n\t\t\ttext = text.trim();\n\t\t\tif (!text) return;\n\n\t\t\t// If agent is streaming, Enter becomes a steering message\n\t\t\tif (this.isStreaming) {\n\t\t\t\tthis.agent.steer({ role: \"user\", content: [{ type: \"text\", text }] });\n\t\t\t\tthis.steeringMessages.push(text);\n\t\t\t\tthis.editor.setText(\"\");\n\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.editor.addToHistory(text);\n\t\t\tthis.editor.setText(\"\");\n\n\t\t\tif (this.onInputCallback) {\n\t\t\t\tthis.onInputCallback(text);\n\t\t\t}\n\t\t};\n\n\t\tconst origHandleInput = this.editor.handleInput.bind(this.editor);\n\t\tthis.editor.handleInput = (data: string) => {\n\t\t\t// Escape: abort if agent is running or compacting\n\t\t\tif (matchesKey(data, Key.escape)) {\n\t\t\t\tif (this.isBashRunning && this.bashAbortController) {\n\t\t\t\t\tthis.bashAbortController.abort();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.isCompacting) {\n\t\t\t\t\tthis.agent.abort();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.loadingAnimation) {\n\t\t\t\t\tthis.agent.abort();\n\t\t\t\t\tthis.stopLoading();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Ctrl+C: exit\n\t\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\t\tthis.shutdown();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+D: exit if editor is empty\n\t\t\tif (matchesKey(data, Key.ctrl(\"d\"))) {\n\t\t\t\tif (this.editor.getText().length === 0) {\n\t\t\t\t\tthis.shutdown();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Ctrl+E: toggle tool output expansion\n\t\t\tif (matchesKey(data, Key.ctrl(\"e\"))) {\n\t\t\t\tthis.toggleToolExpansion();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+L: select model\n\t\t\tif (matchesKey(data, Key.ctrl(\"l\"))) {\n\t\t\t\tthis.handleModelSelect();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+V: paste image from clipboard\n\t\t\tif (matchesKey(data, Key.ctrl(\"v\"))) {\n\t\t\t\tthis.handleClipboardImagePaste();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Alt+Enter (Option+Enter on Mac): follow-up while streaming (or submit normally when idle)\n\t\t\tif (matchesKey(data, Key.alt(\"enter\"))) {\n\t\t\t\tconst text = this.editor.getText().trim();\n\t\t\t\tif (!text) return;\n\n\t\t\t\tif (this.isStreaming) {\n\t\t\t\t\tthis.followUpMessages.push(text);\n\t\t\t\t\tthis.editor.setText(\"\");\n\t\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Not streaming: treat like regular submit\n\t\t\t\tthis.editor.onSubmit?.(text);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Alt+Up (Option+Up on Mac): dequeue all queued messages back into the editor\n\t\t\tif (matchesKey(data, Key.alt(\"up\"))) {\n\t\t\t\tconst restored = this.clearAllQueues();\n\t\t\t\tif (restored.length > 0) {\n\t\t\t\t\tthis.editor.setText(restored.join(\"\\n\\n\"));\n\t\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\torigHandleInput(data);\n\t\t};\n\t}\n\n\t// ========================================================================\n\t// User Input\n\t// ========================================================================\n\n\tprivate getUserInput(): Promise<string> {\n\t\treturn new Promise((resolve) => {\n\t\t\tthis.onInputCallback = (text: string) => {\n\t\t\t\tthis.onInputCallback = undefined;\n\t\t\t\tresolve(text);\n\t\t\t};\n\t\t});\n\t}\n\n\tprivate async handleUserInput(input: string): Promise<void> {\n\t\t// Handle commands\n\t\tif (input === \"/help\") {\n\t\t\tthis.showHelp();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/skills\") {\n\t\t\tthis.showSkills();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/quit\" || input === \"/exit\") {\n\t\t\tthis.shutdown();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/model\") {\n\t\t\tawait this.handleModelSelect();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/login\") {\n\t\t\tawait this.handleLogin();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/logout\") {\n\t\t\tawait this.handleLogout();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/compact\" || input.startsWith(\"/compact \")) {\n\t\t\tconst customInstructions = input.startsWith(\"/compact \") ? input.slice(9).trim() : undefined;\n\t\t\tawait this.handleCompactCommand(customInstructions);\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/auto-compact\") {\n\t\t\tthis.toggleAutoCompaction();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/resume\") {\n\t\t\tawait this.handleResume();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input.startsWith(\"/skill:\")) {\n\t\t\tconst skillName = input.slice(\"/skill:\".length).trim();\n\t\t\tawait this.handleSkillInvocation(skillName);\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle bash commands (! for normal, !! for excluded from context)\n\t\tconst bashParsed = parseBashInput(input);\n\t\tif (bashParsed) {\n\t\t\tif (this.isBashRunning) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"A bash command is already running. Press Escape to cancel it first.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait this.handleBashCommand(bashParsed.command, bashParsed.excludeFromContext);\n\t\t\tthis.isBashMode = false;\n\t\t\tthis.updateEditorBorderColor();\n\t\t\treturn;\n\t\t}\n\n\t\t// Try expanding prompt templates\n\t\tconst { prompts = [] } = this.options;\n\t\tconst expanded = expandPromptTemplate(input, prompts);\n\n\t\t// Capture and clear pending images\n\t\tconst images = this.pendingImages.length > 0 ? [...this.pendingImages] : undefined;\n\t\tthis.pendingImages = [];\n\n\t\t// Regular message (use expanded text if a prompt template was matched)\n\t\tconst imageLabel = images ? chalk.dim(` (${images.length} image${images.length > 1 ? \"s\" : \"\"})`) : \"\";\n\t\tthis.chatContainer.addChild(new UserMessageComponent(`${expanded}${imageLabel}`, getMarkdownTheme()));\n\t\tthis.ui.requestRender();\n\t\tawait this.streamPrompt(expanded, images);\n\t}\n\n\tprivate async handleBashCommand(command: string, excludeFromContext: boolean): Promise<void> {\n\t\tthis.bashAbortController = new AbortController();\n\t\tthis.isBashRunning = true;\n\n\t\tthis.bashComponent = new BashExecutionComponent(command, this.ui, excludeFromContext);\n\t\tif (this.toolOutputExpanded) {\n\t\t\tthis.bashComponent.setExpanded(true);\n\t\t}\n\t\tthis.chatContainer.addChild(this.bashComponent);\n\t\tthis.ui.requestRender();\n\n\t\ttry {\n\t\t\tconst result = await executeBashCommand(command, {\n\t\t\t\tsignal: this.bashAbortController.signal,\n\t\t\t\tonChunk: (chunk) => {\n\t\t\t\t\tthis.bashComponent?.appendOutput(chunk);\n\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.bashComponent.setComplete(result.exitCode, result.cancelled, result.truncated, result.fullOutputPath);\n\t\t\tthis.ui.requestRender();\n\n\t\t\tif (!excludeFromContext) {\n\t\t\t\tconst msgText = `Ran \\`${command}\\`\\n\\n\\`\\`\\`\\n${result.output.trimEnd()}\\n\\`\\`\\``;\n\t\t\t\tconst userMsg: ModelMessage = { role: \"user\", content: [{ type: \"text\", text: msgText }] };\n\t\t\t\tthis.agent.setMessages([...this.agent.messages, userMsg]);\n\t\t\t\tthis.agent.sessionManager?.appendMessage(userMsg);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.isBashRunning = false;\n\t\t\tthis.bashAbortController = null;\n\t\t\tthis.bashComponent = undefined;\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Autocomplete\n\t// ========================================================================\n\n\tprivate buildAutocompleteProvider(): CombinedAutocompleteProvider {\n\t\tconst { skills = [], prompts = [], fdPath } = this.options;\n\n\t\tconst commands: SlashCommand[] = [\n\t\t\t{ name: \"help\", description: \"Show available commands\" },\n\t\t\t{ name: \"resume\", description: \"Resume a previous session\" },\n\t\t\t{ name: \"compact\", description: \"Manually compact the session context\" },\n\t\t\t{ name: \"auto-compact\", description: \"Toggle automatic context compaction\" },\n\t\t\t{ name: \"login\", description: \"Login to an OAuth provider\" },\n\t\t\t{ name: \"logout\", description: \"Logout from an OAuth provider\" },\n\t\t\t{ name: \"skills\", description: \"List loaded skills\" },\n\t\t\t{ name: \"model\", description: \"Switch model (Ctrl+L)\" },\n\t\t\t{ name: \"quit\", description: \"Exit the CLI\" },\n\t\t\t{ name: \"exit\", description: \"Exit the CLI\" },\n\t\t];\n\n\t\tfor (const skill of skills) {\n\t\t\tcommands.push({\n\t\t\t\tname: `skill:${skill.name}`,\n\t\t\t\tdescription: skill.description,\n\t\t\t});\n\t\t}\n\n\t\t// Add prompt templates as slash commands\n\t\tfor (const prompt of prompts) {\n\t\t\tcommands.push({\n\t\t\t\tname: prompt.name,\n\t\t\t\tdescription: prompt.description,\n\t\t\t});\n\t\t}\n\n\t\treturn new CombinedAutocompleteProvider(commands, process.cwd(), fdPath ?? null);\n\t}\n\n\t// ========================================================================\n\t// Model Selection\n\t// ========================================================================\n\n\tprivate async handleModelSelect(): Promise<void> {\n\t\tconst latestModels = getLatestModels();\n\t\tconst modelOptions: { provider: string; modelId: string; label: string }[] = [];\n\t\tfor (const [provider, models] of Object.entries(latestModels)) {\n\t\t\tfor (const modelId of models) {\n\t\t\t\tmodelOptions.push({ provider, modelId, label: `${provider}/${modelId}` });\n\t\t\t}\n\t\t}\n\n\t\tconst items: SelectItem[] = modelOptions.map((m) => {\n\t\t\tconst current = m.provider === this.currentProvider && m.modelId === this.currentModelId;\n\t\t\treturn {\n\t\t\t\tvalue: `${m.provider}/${m.modelId}`,\n\t\t\t\tlabel: current ? `${m.label} (current)` : m.label,\n\t\t\t};\n\t\t});\n\n\t\tconst selected = await this.showSelectList(\"Switch model\", items);\n\t\tif (!selected) return;\n\n\t\tconst [newProvider, ...modelParts] = selected.split(\"/\");\n\t\tconst newModelId = modelParts.join(\"/\");\n\n\t\tif (newProvider === this.currentProvider && newModelId === this.currentModelId) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.showStatus(chalk.dim(`Switching to ${newProvider}/${newModelId}...`));\n\n\t\tif (!this.options.onModelChange) {\n\t\t\tthis.showStatus(chalk.yellow(\"Model switching is not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst newAgent = await this.options.onModelChange(newProvider, newModelId);\n\t\t\t// Preserve conversation history\n\t\t\tnewAgent.setMessages([...this.agent.messages]);\n\t\t\tthis.agent = newAgent;\n\t\t\tthis.configureAgentCompaction();\n\n\t\t\tthis.currentProvider = newProvider;\n\t\t\tthis.currentModelId = newModelId;\n\t\t\tthis.updateFooter();\n\n\t\t\t// Persist the choice for next startup\n\t\t\tthis.options.settingsManager?.setDefaults(newProvider, newModelId);\n\n\t\t\tthis.showStatus(chalk.green(`Switched to ${newProvider}/${newModelId}`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tthis.showStatus(chalk.red(`Failed to switch model: ${msg}`));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Streaming\n\t// ========================================================================\n\n\tprivate async streamPrompt(prompt: string, images?: ClipboardImage[]): Promise<void> {\n\t\tthis.isStreaming = true;\n\t\tthis.updatePendingMessagesDisplay();\n\n\t\t// Build image parts from clipboard images\n\t\tconst imageParts: ImagePart[] = (images ?? []).map((img) => ({\n\t\t\ttype: \"image\" as const,\n\t\t\timage: Buffer.from(img.bytes).toString(\"base64\"),\n\t\t\tmediaType: img.mimeType,\n\t\t}));\n\n\t\t// Start loading animation\n\t\tthis.startLoading();\n\n\t\tthis.streamingComponent = undefined;\n\t\tthis.streamingText = \"\";\n\t\tthis.hadToolResults = false;\n\n\t\tlet errorDisplayed = false;\n\t\tlet streamFailed = false;\n\t\ttry {\n\t\t\tconst result =\n\t\t\t\timageParts.length > 0\n\t\t\t\t\t? await this.agent.stream({\n\t\t\t\t\t\t\tmessages: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\trole: \"user\" as const,\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: prompt }, ...imageParts],\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: await this.agent.stream({ prompt });\n\n\t\t\tfor await (const part of result.fullStream) {\n\t\t\t\tswitch (part.type) {\n\t\t\t\t\tcase \"text-delta\":\n\t\t\t\t\t\t// After tool results, or for the very first text part, start a new assistant message component\n\t\t\t\t\t\t// so each agent step gets its own message bubble\n\t\t\t\t\t\tif (this.hadToolResults || !this.streamingComponent) {\n\t\t\t\t\t\t\tthis.streamingComponent = new AssistantMessageComponent(getMarkdownTheme());\n\t\t\t\t\t\t\tthis.streamingText = \"\";\n\t\t\t\t\t\t\tthis.hadToolResults = false;\n\t\t\t\t\t\t\tthis.chatContainer.addChild(this.streamingComponent);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.streamingText += part.text;\n\t\t\t\t\t\tthis.streamingComponent!.updateText(this.streamingText);\n\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"tool-call\": {\n\t\t\t\t\t\tconst args =\n\t\t\t\t\t\t\ttypeof part.input === \"object\" && part.input !== null\n\t\t\t\t\t\t\t\t? (part.input as Record<string, unknown>)\n\t\t\t\t\t\t\t\t: {};\n\t\t\t\t\t\tconst toolComponent = new ToolExecutionComponent(part.toolName, args);\n\n\t\t\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\t\t\ttoolComponent.setExpanded(true);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.pendingTools.set(part.toolCallId, toolComponent);\n\t\t\t\t\t\tthis.chatContainer.addChild(toolComponent);\n\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"tool-result\": {\n\t\t\t\t\t\tconst toolComponent = this.pendingTools.get(part.toolCallId);\n\t\t\t\t\t\tif (toolComponent) {\n\t\t\t\t\t\t\tconst toolOutput = extractToolOutput(part.output);\n\t\t\t\t\t\t\ttoolComponent.updateResult(toolOutput, /* isError */ false, /* isPartial */ false);\n\t\t\t\t\t\t\tthis.pendingTools.delete(part.toolCallId);\n\t\t\t\t\t\t\tthis.hadToolResults = true;\n\t\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"error\": {\n\t\t\t\t\t\tconst errorMessage = formatAIError(part.error);\n\t\t\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\t\t\tthis.streamingComponent.setError(errorMessage);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.showStatus(chalk.red(`Error: ${errorMessage}`));\n\t\t\t\t\t\t}\n\t\t\t\t\t\terrorDisplayed = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (errorDisplayed) return;\n\n\t\t\t// Wait for stream to complete — the agent auto-updates messages and persists to session\n\t\t\tawait result.response;\n\n\t\t\t// Update footer token stats\n\t\t\tthis.updateFooterTokens();\n\t\t} catch (error) {\n\t\t\tif (errorDisplayed) {\n\t\t\t\tstreamFailed = true;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tstreamFailed = true;\n\t\t\tif ((error as Error).name === \"AbortError\") {\n\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\tthis.streamingComponent.setAborted();\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.dim(\"[aborted]\"));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst msg =\n\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t? error.message\n\t\t\t\t\t\t: typeof error === \"object\" && error !== null\n\t\t\t\t\t\t\t? JSON.stringify(error)\n\t\t\t\t\t\t\t: String(error);\n\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\tthis.streamingComponent.setError(msg);\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.red(`Error: ${msg}`));\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.stopLoading();\n\t\t\tthis.streamingComponent = undefined;\n\t\t\tthis.streamingText = \"\";\n\t\t\tthis.hadToolResults = false;\n\t\t\tthis.pendingTools.clear();\n\t\t\tthis.isStreaming = false;\n\t\t\tthis.steeringMessages = [];\n\t\t\tif (streamFailed) {\n\t\t\t\tthis.followUpMessages = [];\n\t\t\t}\n\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\tthis.ui.requestRender();\n\t\t}\n\n\t\t// Process queued follow-ups (skipped if stream failed/aborted)\n\t\twhile (this.followUpMessages.length > 0) {\n\t\t\tconst next = this.followUpMessages.shift();\n\t\t\tif (!next) break;\n\n\t\t\tthis.updatePendingMessagesDisplay();\n\n\t\t\tthis.chatContainer.addChild(new UserMessageComponent(next, getMarkdownTheme()));\n\t\t\tthis.ui.requestRender();\n\t\t\tawait this.streamPrompt(next);\n\t\t}\n\t}\n\n\tprivate updatePendingMessagesDisplay(): void {\n\t\tthis.pendingMessagesContainer.clear();\n\n\t\t// If no agent is running, clear pending messages (they've been consumed)\n\t\tif (!this.isStreaming && this.followUpMessages.length === 0 && this.steeringMessages.length === 0) {\n\t\t\tthis.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines = formatPendingMessages(this.steeringMessages, this.followUpMessages);\n\n\t\tif (lines.length === 0) {\n\t\t\tthis.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.pendingMessagesContainer.addChild(new Spacer(1));\n\t\tthis.pendingMessagesContainer.addChild(new Text(lines.map((l) => chalk.dim(l)).join(\"\\n\"), 1, 0));\n\t\tthis.pendingMessagesContainer.addChild(new Text(chalk.dim(\"↳ Alt+Up to edit queued messages\"), 1, 0));\n\t\tthis.pendingMessagesContainer.addChild(new Spacer(1));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate clearAllQueues(): string[] {\n\t\tconst restored = [...this.steeringMessages, ...this.followUpMessages];\n\t\tthis.steeringMessages = [];\n\t\tthis.followUpMessages = [];\n\t\treturn restored;\n\t}\n\n\t// ========================================================================\n\t// Loading Animation\n\t// ========================================================================\n\n\tprivate startLoading(): void {\n\t\tthis.stopLoading();\n\t\tthis.loadingAnimation = new Loader(\n\t\t\tthis.ui,\n\t\t\t(s: string) => chalk.cyan(s),\n\t\t\t(s: string) => chalk.dim(s),\n\t\t\t\"Working...\",\n\t\t);\n\t\tthis.loadingAnimation.start();\n\t\tthis.pendingContainer.addChild(new Spacer(1));\n\t\tthis.pendingContainer.addChild(this.loadingAnimation);\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate stopLoading(): void {\n\t\tif (this.loadingAnimation) {\n\t\t\tthis.loadingAnimation.stop();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.loadingAnimation = undefined;\n\t\t\tthis.ui.requestRender();\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Tool Expansion\n\t// ========================================================================\n\n\tprivate toggleToolExpansion(): void {\n\t\tthis.toolOutputExpanded = !this.toolOutputExpanded;\n\n\t\t// Update all tool components and compaction components in the chat\n\t\tfor (const child of this.chatContainer.children) {\n\t\t\tif (child instanceof ToolExecutionComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t} else if (child instanceof CompactionSummaryComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t} else if (child instanceof BashExecutionComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t}\n\t\t}\n\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Clipboard Image Paste\n\t// ========================================================================\n\n\tprivate handleClipboardImagePaste(): void {\n\t\ttry {\n\t\t\tconst image = readClipboardImage();\n\t\t\tif (!image) return;\n\t\t\tthis.pendingImages.push(image);\n\t\t\tconst ext = extensionForImageMimeType(image.mimeType) ?? \"image\";\n\t\t\tconst label = `[image ${this.pendingImages.length}: ${ext}]`;\n\t\t\tthis.editor.insertTextAtCursor(label);\n\t\t\tthis.ui.requestRender();\n\t\t} catch {\n\t\t\t// Silently ignore clipboard errors (may not have permission, etc.)\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Compaction\n\t// ========================================================================\n\n\t/**\n\t * Handle the /compact command.\n\t */\n\tprivate async handleCompactCommand(_customInstructions?: string): Promise<void> {\n\t\ttry {\n\t\t\tconst result = await this.agent.compact();\n\t\t\tif (!result) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (already compacted or insufficient history).\"));\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Compaction callbacks already report errors in interactive mode.\n\t\t\t// Catch here to avoid unwinding the main TUI loop on abort/provider errors.\n\t\t\tif (!this.agent.compaction?.onCompactionError) {\n\t\t\t\tconst compactionError = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tif (compactionError.name === \"AbortError\" || compactionError.message === \"Compaction cancelled\") {\n\t\t\t\t\tthis.showStatus(chalk.dim(\"Compaction cancelled.\"));\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.red(`Compaction failed: ${compactionError.message}`));\n\t\t\t\t}\n\t\t\t\tthis.ui.requestRender();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Toggle auto-compaction on/off.\n\t */\n\tprivate toggleAutoCompaction(): void {\n\t\tthis.autoCompaction = !this.autoCompaction;\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.options.settingsManager?.setCompactionEnabled(this.autoCompaction);\n\t\tif (this.agent.compaction) {\n\t\t\tthis.agent.setCompaction({\n\t\t\t\t...this.agent.compaction,\n\t\t\t\tmode: this.autoCompaction ? \"auto\" : \"manual\",\n\t\t\t});\n\t\t}\n\t\tthis.showStatus(\n\t\t\tthis.autoCompaction ? chalk.green(\"Auto-compaction enabled\") : chalk.dim(\"Auto-compaction disabled\"),\n\t\t);\n\t\tthis.ui.requestRender();\n\t}\n\n\t/**\n\t * Configure agent-level compaction and callbacks for UI updates.\n\t */\n\tprivate configureAgentCompaction(): void {\n\t\tconst mode = this.autoCompaction ? \"auto\" : \"manual\";\n\t\tthis.agent.setCompaction({\n\t\t\tcontextWindow: this.contextWindow,\n\t\t\tmode,\n\t\t\tsettings: {\n\t\t\t\treserveTokens: this.compactionSettings.reserveTokens,\n\t\t\t\tkeepRecentTokens: this.compactionSettings.keepRecentTokens,\n\t\t\t},\n\t\t\tonCompactionStart: () => {\n\t\t\t\tthis.isCompacting = true;\n\t\t\t\tthis.compactionLoader?.stop();\n\t\t\t\tthis.compactionLoader = new Loader(\n\t\t\t\t\tthis.ui,\n\t\t\t\t\t(s: string) => chalk.cyan(s),\n\t\t\t\t\t(s: string) => chalk.dim(s),\n\t\t\t\t\tthis.autoCompaction\n\t\t\t\t\t\t? \"Auto-compacting context... (Escape to cancel)\"\n\t\t\t\t\t\t: \"Compacting context... (Escape to cancel)\",\n\t\t\t\t);\n\t\t\t\tthis.compactionLoader.start();\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.pendingContainer.addChild(new Spacer(1));\n\t\t\t\tthis.pendingContainer.addChild(this.compactionLoader);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t},\n\t\t\tonCompactionComplete: (result: CompactionResult) => {\n\t\t\t\tthis.compactionLoader?.stop();\n\t\t\t\tthis.compactionLoader = null;\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.isCompacting = false;\n\n\t\t\t\tthis.rebuildChatFromSession();\n\t\t\t\tconst summaryComponent = new CompactionSummaryComponent(result.tokensBefore, result.summary);\n\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\tsummaryComponent.setExpanded(true);\n\t\t\t\t}\n\t\t\t\tthis.chatContainer.addChild(summaryComponent);\n\n\t\t\t\tthis.updateFooterTokens();\n\t\t\t\tif (this.options.verbose) {\n\t\t\t\t\tconst tokensAfter = estimateContextTokens([...this.agent.messages]);\n\t\t\t\t\tthis.showStatus(\n\t\t\t\t\t\tchalk.dim(\n\t\t\t\t\t\t\t`Compacted: ${result.tokensBefore.toLocaleString()} -> ${tokensAfter.toLocaleString()} tokens`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthis.ui.requestRender();\n\t\t\t},\n\t\t\tonCompactionError: (error: Error) => {\n\t\t\t\tthis.compactionLoader?.stop();\n\t\t\t\tthis.compactionLoader = null;\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.isCompacting = false;\n\n\t\t\t\tif (error.name === \"AbortError\" || error.message === \"Compaction cancelled\") {\n\t\t\t\t\tthis.showStatus(chalk.dim(\"Compaction cancelled.\"));\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.red(`Compaction failed: ${error.message}`));\n\t\t\t\t}\n\t\t\t\tthis.ui.requestRender();\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Rebuild the chat UI from session context after compaction.\n\t */\n\tprivate rebuildChatFromSession(): void {\n\t\tthis.chatContainer.clear();\n\n\t\tconst messages = this.agent.messages;\n\t\tfor (const msg of messages) {\n\t\t\tif (msg.role === \"user\") {\n\t\t\t\t// Check if this is a compaction summary\n\t\t\t\tconst content = msg.content;\n\t\t\t\tif (Array.isArray(content) && content.length > 0) {\n\t\t\t\t\tconst textBlock = content[0] as { type: string; text?: string };\n\t\t\t\t\tif (textBlock.type === \"text\" && textBlock.text?.startsWith('<summary type=\"compaction\"')) {\n\t\t\t\t\t\t// Skip compaction summaries in rebuild (they are injected by buildSessionContext)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (textBlock.type === \"text\" && textBlock.text?.startsWith('<summary type=\"branch\"')) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst text = extractTextFromMessage(msg);\n\t\t\t\tif (text) {\n\t\t\t\t\tthis.chatContainer.addChild(new UserMessageComponent(text, getMarkdownTheme()));\n\t\t\t\t}\n\t\t\t} else if (msg.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = msg as import(\"edge-pi\").AssistantModelMessage;\n\t\t\t\tconst textParts: string[] = [];\n\t\t\t\tfor (const block of assistantMsg.content) {\n\t\t\t\t\tconst b = block as {\n\t\t\t\t\t\ttype: string;\n\t\t\t\t\t\ttext?: string;\n\t\t\t\t\t\ttoolName?: string;\n\t\t\t\t\t\tinput?: unknown;\n\t\t\t\t\t\ttoolCallId?: string;\n\t\t\t\t\t};\n\t\t\t\t\tif (b.type === \"text\" && b.text) {\n\t\t\t\t\t\ttextParts.push(b.text);\n\t\t\t\t\t} else if (b.type === \"tool-call\" && b.toolName) {\n\t\t\t\t\t\tconst args =\n\t\t\t\t\t\t\ttypeof b.input === \"object\" && b.input !== null ? (b.input as Record<string, unknown>) : {};\n\t\t\t\t\t\tconst toolComp = new ToolExecutionComponent(b.toolName, args);\n\t\t\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\t\t\ttoolComp.setExpanded(true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Mark as completed (we don't have the result here, just show collapsed)\n\t\t\t\t\t\ttoolComp.updateResult({ text: \"(from history)\" }, false, false);\n\t\t\t\t\t\tthis.chatContainer.addChild(toolComp);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (textParts.length > 0) {\n\t\t\t\t\tconst comp = new AssistantMessageComponent(getMarkdownTheme());\n\t\t\t\t\tcomp.updateText(textParts.join(\"\"));\n\t\t\t\t\tthis.chatContainer.addChild(comp);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Skip tool messages in UI rebuild - they are consumed by tool-call components\n\t\t}\n\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Footer Token Tracking\n\t// ========================================================================\n\n\t/**\n\t * Update the footer with current token count information.\n\t */\n\tprivate updateFooterTokens(): void {\n\t\tconst contextTokens = estimateContextTokens([...this.agent.messages]);\n\t\tthis.footer.setTokenInfo(contextTokens, this.contextWindow);\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.ui?.requestRender();\n\t}\n\n\t/**\n\t * Check if the current provider is using an OAuth subscription credential.\n\t */\n\tprivate isSubscriptionProvider(): boolean {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) return false;\n\t\tconst cred = authStorage.get(this.currentProvider);\n\t\treturn cred?.type === \"oauth\";\n\t}\n\n\t/**\n\t * Replace the footer component and update token info.\n\t */\n\tprivate updateFooter(): void {\n\t\tthis.footer = new FooterComponent(this.currentProvider, this.currentModelId);\n\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\t\tthis.updateFooterTokens();\n\n\t\t// Replace footer in UI\n\t\tconst children = this.ui.children;\n\t\tchildren[children.length - 1] = this.footer;\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Startup Resource Display\n\t// ========================================================================\n\n\tprivate formatDisplayPath(p: string): string {\n\t\tconst home = process.env.HOME || process.env.USERPROFILE || \"\";\n\t\tif (home && p.startsWith(home)) {\n\t\t\treturn `~${p.slice(home.length)}`;\n\t\t}\n\t\treturn p;\n\t}\n\n\tprivate showLoadedResources(contextFiles: ContextFile[], skills: Skill[], prompts: PromptTemplate[]): void {\n\t\tconst sectionHeader = (name: string) => chalk.cyan(`[${name}]`);\n\n\t\tif (contextFiles.length > 0) {\n\t\t\tconst contextList = contextFiles.map((f) => chalk.dim(` ${this.formatDisplayPath(f.path)}`)).join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Context\")}\\n${contextList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (skills.length > 0) {\n\t\t\tconst skillList = skills.map((s) => chalk.dim(` ${this.formatDisplayPath(s.filePath)}`)).join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Skills\")}\\n${skillList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (prompts.length > 0) {\n\t\t\tconst promptList = prompts\n\t\t\t\t.map((p) => {\n\t\t\t\t\tconst sourceLabel = chalk.cyan(p.source);\n\t\t\t\t\treturn chalk.dim(` ${sourceLabel} /${p.name}`);\n\t\t\t\t})\n\t\t\t\t.join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Prompts\")}\\n${promptList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Commands\n\t// ========================================================================\n\n\tprivate showHelp(): void {\n\t\tconst helpText = [\n\t\t\tchalk.bold(\"Commands:\"),\n\t\t\t\" !<command> Run inline bash and include output in context\",\n\t\t\t\" !!<command> Run inline bash but exclude output from context\",\n\t\t\t\" /resume Resume a previous session\",\n\t\t\t\" /compact [text] Compact the session context (optional instructions)\",\n\t\t\t\" /auto-compact Toggle automatic context compaction\",\n\t\t\t\" /model Switch model (Ctrl+L)\",\n\t\t\t\" /login Login to an OAuth provider\",\n\t\t\t\" /logout Logout from an OAuth provider\",\n\t\t\t\" /skills List loaded skills\",\n\t\t\t\" /skill:<name> Invoke a skill by name\",\n\t\t\t\" /quit, /exit Exit the CLI\",\n\t\t].join(\"\\n\");\n\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(helpText, 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate showSkills(): void {\n\t\tconst { skills = [] } = this.options;\n\n\t\tif (skills.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No skills loaded.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines: string[] = [];\n\t\tfor (const skill of skills) {\n\t\t\tconst hidden = skill.disableModelInvocation ? chalk.dim(\" (hidden from model)\") : \"\";\n\t\t\tlines.push(` ${chalk.bold(skill.name)}${hidden}`);\n\t\t\tlines.push(chalk.dim(` ${skill.description}`));\n\t\t\tlines.push(chalk.dim(` ${skill.filePath}`));\n\t\t}\n\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(lines.join(\"\\n\"), 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate async handleSkillInvocation(skillName: string): Promise<void> {\n\t\tconst { skills = [] } = this.options;\n\t\tconst skill = skills.find((s) => s.name === skillName);\n\n\t\tif (!skill) {\n\t\t\tthis.showStatus(chalk.red(`Skill \"${skillName}\" not found.`));\n\t\t\treturn;\n\t\t}\n\n\t\tconst skillPrompt = `Please read and follow the instructions in the skill file: ${skill.filePath}`;\n\t\tthis.chatContainer.addChild(new UserMessageComponent(skillPrompt, getMarkdownTheme()));\n\t\tthis.ui.requestRender();\n\t\tawait this.streamPrompt(skillPrompt);\n\t}\n\n\t// ========================================================================\n\t// OAuth Login/Logout\n\t// ========================================================================\n\n\tprivate async handleLogin(): Promise<void> {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) {\n\t\t\tthis.showStatus(chalk.red(\"Auth storage not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst providers = authStorage.getProviders();\n\t\tif (providers.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No OAuth providers registered.\"));\n\t\t\treturn;\n\t\t}\n\n\t\t// Use SelectList overlay for provider selection\n\t\tconst items: SelectItem[] = providers.map((p) => {\n\t\t\tconst loggedIn = authStorage.get(p.id)?.type === \"oauth\" ? \" (logged in)\" : \"\";\n\t\t\treturn { value: p.id, label: `${p.name}${loggedIn}` };\n\t\t});\n\n\t\tconst selected = await this.showSelectList(\"Login to OAuth provider\", items);\n\t\tif (!selected) return;\n\n\t\tconst provider = providers.find((p) => p.id === selected);\n\t\tif (!provider) return;\n\n\t\tthis.showStatus(chalk.dim(`Logging in to ${provider.name}...`));\n\n\t\ttry {\n\t\t\tawait authStorage.login(provider.id, {\n\t\t\t\tonAuth: (info) => {\n\t\t\t\t\tconst lines = [chalk.bold(\"Open this URL in your browser:\"), chalk.cyan(info.url)];\n\t\t\t\t\tif (info.instructions) {\n\t\t\t\t\t\tlines.push(chalk.dim(info.instructions));\n\t\t\t\t\t}\n\t\t\t\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\t\t\t\tthis.chatContainer.addChild(new Text(lines.join(\"\\n\"), 1, 0));\n\t\t\t\t\tthis.ui.requestRender();\n\n\t\t\t\t\t// Try to open browser\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst { execSync } = require(\"node:child_process\") as typeof import(\"node:child_process\");\n\t\t\t\t\t\tconst platform = process.platform;\n\t\t\t\t\t\tif (platform === \"darwin\") {\n\t\t\t\t\t\t\texecSync(`open \"${info.url}\"`, { stdio: \"ignore\" });\n\t\t\t\t\t\t} else if (platform === \"linux\") {\n\t\t\t\t\t\t\texecSync(`xdg-open \"${info.url}\" 2>/dev/null || sensible-browser \"${info.url}\" 2>/dev/null`, {\n\t\t\t\t\t\t\t\tstdio: \"ignore\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else if (platform === \"win32\") {\n\t\t\t\t\t\t\texecSync(`start \"\" \"${info.url}\"`, { stdio: \"ignore\" });\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Silently fail - user can open manually\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tonPrompt: async (promptInfo) => {\n\t\t\t\t\t// Show prompt message and wait for user input\n\t\t\t\t\tthis.showStatus(chalk.dim(promptInfo.message));\n\t\t\t\t\tconst answer = await this.getUserInput();\n\t\t\t\t\treturn answer.trim();\n\t\t\t\t},\n\t\t\t\tonProgress: (message) => {\n\t\t\t\t\tthis.showStatus(chalk.dim(message));\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\t\t\tthis.ui.requestRender();\n\t\t\tthis.showStatus(chalk.green(`Logged in to ${provider.name}. Credentials saved.`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tif (msg !== \"Login cancelled\") {\n\t\t\t\tthis.showStatus(chalk.red(`Login failed: ${msg}`));\n\t\t\t} else {\n\t\t\t\tthis.showStatus(chalk.dim(\"Login cancelled.\"));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async handleLogout(): Promise<void> {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) {\n\t\t\tthis.showStatus(chalk.red(\"Auth storage not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst loggedIn = authStorage\n\t\t\t.list()\n\t\t\t.filter((id) => authStorage.get(id)?.type === \"oauth\")\n\t\t\t.map((id) => {\n\t\t\t\tconst provider = authStorage.getProvider(id);\n\t\t\t\treturn { id, name: provider?.name ?? id };\n\t\t\t});\n\n\t\tif (loggedIn.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No OAuth providers logged in. Use /login first.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst items: SelectItem[] = loggedIn.map((p) => ({\n\t\t\tvalue: p.id,\n\t\t\tlabel: p.name,\n\t\t}));\n\n\t\tconst selected = await this.showSelectList(\"Logout from OAuth provider\", items);\n\t\tif (!selected) return;\n\n\t\tconst entry = loggedIn.find((p) => p.id === selected);\n\t\tif (!entry) return;\n\n\t\tauthStorage.logout(entry.id);\n\t\tthis.showStatus(chalk.green(`Logged out of ${entry.name}.`));\n\t}\n\n\t// ========================================================================\n\t// Resume Session\n\t// ========================================================================\n\n\t/**\n\t * List session files from the session directory, sorted by modification time (newest first).\n\t * Returns metadata for each session including the first user message as a preview.\n\t */\n\tprivate listAvailableSessions(): { path: string; mtime: number; preview: string; timestamp: string }[] {\n\t\tconst { sessionDir } = this.options;\n\t\tif (!sessionDir || !existsSync(sessionDir)) return [];\n\n\t\ttry {\n\t\t\tconst files = readdirSync(sessionDir)\n\t\t\t\t.filter((f: string) => f.endsWith(\".jsonl\"))\n\t\t\t\t.map((f: string) => {\n\t\t\t\t\tconst filePath = join(sessionDir, f);\n\t\t\t\t\tconst mtime = statSync(filePath).mtime.getTime();\n\t\t\t\t\treturn { name: f, path: filePath, mtime };\n\t\t\t\t})\n\t\t\t\t.sort((a, b) => b.mtime - a.mtime);\n\n\t\t\tconst sessions: { path: string; mtime: number; preview: string; timestamp: string }[] = [];\n\t\t\tfor (const file of files) {\n\t\t\t\t// Skip the current session file\n\t\t\t\tif (this.agent.sessionManager?.getSessionFile() === file.path) continue;\n\n\t\t\t\tconst preview = this.getSessionPreview(file.path);\n\t\t\t\tconst timestamp = new Date(file.mtime).toLocaleString();\n\t\t\t\tsessions.push({ path: file.path, mtime: file.mtime, preview, timestamp });\n\t\t\t}\n\t\t\treturn sessions;\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Extract the first user message from a session file for preview.\n\t */\n\tprivate getSessionPreview(filePath: string): string {\n\t\ttry {\n\t\t\tconst content = readFileSync(filePath, \"utf-8\");\n\t\t\tconst lines = content.trim().split(\"\\n\");\n\t\t\tfor (const line of lines) {\n\t\t\t\tif (!line.trim()) continue;\n\t\t\t\ttry {\n\t\t\t\t\tconst entry = JSON.parse(line);\n\t\t\t\t\tif (entry.type === \"message\" && entry.message?.role === \"user\") {\n\t\t\t\t\t\tconst msg = entry.message;\n\t\t\t\t\t\tlet text = \"\";\n\t\t\t\t\t\tif (typeof msg.content === \"string\") {\n\t\t\t\t\t\t\ttext = msg.content;\n\t\t\t\t\t\t} else if (Array.isArray(msg.content)) {\n\t\t\t\t\t\t\tfor (const block of msg.content) {\n\t\t\t\t\t\t\t\tif (block.type === \"text\" && block.text) {\n\t\t\t\t\t\t\t\t\ttext = block.text;\n\t\t\t\t\t\t\t\t\tbreak;\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\t// Truncate and clean up for display\n\t\t\t\t\t\ttext = text.replace(/\\n/g, \" \").trim();\n\t\t\t\t\t\tif (text.length > 80) {\n\t\t\t\t\t\t\ttext = `${text.slice(0, 77)}...`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn text || \"(empty message)\";\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Skip malformed lines\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn \"(no messages)\";\n\t\t} catch {\n\t\t\treturn \"(unreadable)\";\n\t\t}\n\t}\n\n\t/**\n\t * Format a relative time string (e.g. \"2 hours ago\", \"3 days ago\").\n\t */\n\tprivate formatRelativeTime(mtime: number): string {\n\t\tconst now = Date.now();\n\t\tconst diffMs = now - mtime;\n\t\tconst diffSec = Math.floor(diffMs / 1000);\n\t\tconst diffMin = Math.floor(diffSec / 60);\n\t\tconst diffHour = Math.floor(diffMin / 60);\n\t\tconst diffDay = Math.floor(diffHour / 24);\n\n\t\tif (diffMin < 1) return \"just now\";\n\t\tif (diffMin < 60) return `${diffMin}m ago`;\n\t\tif (diffHour < 24) return `${diffHour}h ago`;\n\t\tif (diffDay < 30) return `${diffDay}d ago`;\n\t\treturn new Date(mtime).toLocaleDateString();\n\t}\n\n\t/**\n\t * Handle the /resume command: show a list of previous sessions and load the selected one.\n\t */\n\tprivate async handleResume(): Promise<void> {\n\t\tconst sessions = this.listAvailableSessions();\n\t\tif (sessions.length === 0) {\n\t\t\tthis.showStatus(chalk.yellow(\"No previous sessions found.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst items: SelectItem[] = sessions.map((s) => ({\n\t\t\tvalue: s.path,\n\t\t\tlabel: `${chalk.dim(this.formatRelativeTime(s.mtime))} ${s.preview}`,\n\t\t}));\n\n\t\tconst selected = await this.showSelectList(\"Resume session\", items);\n\t\tif (!selected) return;\n\n\t\tconst session = sessions.find((s) => s.path === selected);\n\t\tif (!session) return;\n\n\t\ttry {\n\t\t\t// Open the selected session and set on agent (auto-restores messages)\n\t\t\tconst sessionDir = this.options.sessionDir!;\n\t\t\tconst newSessionManager = SessionManagerClass.open(selected, sessionDir);\n\t\t\tthis.agent.sessionManager = newSessionManager;\n\n\t\t\t// Rebuild the chat UI\n\t\t\tthis.chatContainer.clear();\n\t\t\tthis.rebuildChatFromSession();\n\n\t\t\t// Update footer tokens\n\t\t\tthis.updateFooterTokens();\n\n\t\t\tconst msgCount = this.agent.messages.length;\n\t\t\tthis.showStatus(chalk.green(`Resumed session (${msgCount} messages)`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tthis.showStatus(chalk.red(`Failed to resume session: ${msg}`));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Select List (overlay pattern from pi-coding-agent)\n\t// ========================================================================\n\n\tprivate showSelectList(title: string, items: SelectItem[]): Promise<string | null> {\n\t\treturn new Promise((resolve) => {\n\t\t\tconst container = new Container();\n\n\t\t\tcontainer.addChild(new Spacer(1));\n\t\t\tcontainer.addChild(new Text(chalk.bold.cyan(title), 1, 0));\n\n\t\t\tconst selectList = new SelectList(items, Math.min(items.length, 10), getSelectListTheme());\n\t\t\tselectList.onSelect = (item) => {\n\t\t\t\t// Restore normal UI\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.editorContainer.addChild(this.editor);\n\t\t\t\tthis.ui.setFocus(this.editor);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\tresolve(item.value);\n\t\t\t};\n\t\t\tselectList.onCancel = () => {\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.editorContainer.addChild(this.editor);\n\t\t\t\tthis.ui.setFocus(this.editor);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\tresolve(null);\n\t\t\t};\n\t\t\tcontainer.addChild(selectList);\n\t\t\tcontainer.addChild(new Text(chalk.dim(\"↑↓ navigate • enter select • esc cancel\"), 1, 0));\n\t\t\tcontainer.addChild(new Spacer(1));\n\n\t\t\t// Replace editor area with select list\n\t\t\tthis.editorContainer.clear();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.pendingContainer.addChild(container);\n\t\t\tthis.ui.setFocus(selectList);\n\t\t\tthis.ui.requestRender();\n\t\t});\n\t}\n\n\t// ========================================================================\n\t// Status & Utilities\n\t// ========================================================================\n\n\tprivate showStatus(text: string): void {\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(text, 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate updateEditorBorderColor(): void {\n\t\tthis.editorTheme.borderColor = this.isBashMode ? (s: string) => chalk.yellow(s) : (s: string) => chalk.gray(s);\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate shutdown(): void {\n\t\tthis.ui.stop();\n\t\tconsole.log(chalk.dim(\"\\nGoodbye.\"));\n\t\tprocess.exit(0);\n\t}\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction extractTextFromMessage(msg: ModelMessage): string {\n\tif (msg.role === \"user\") {\n\t\tconst content = (msg as import(\"edge-pi\").UserModelMessage).content;\n\t\tif (typeof content === \"string\") return content;\n\t\tif (Array.isArray(content)) {\n\t\t\treturn content\n\t\t\t\t.filter((c): c is { type: \"text\"; text: string } => (c as { type: string }).type === \"text\")\n\t\t\t\t.map((c) => c.text)\n\t\t\t\t.join(\"\");\n\t\t}\n\t}\n\treturn \"\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"interactive-mode.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/interactive-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAqBH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAG9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAwC7C,MAAM,WAAW,sBAAsB;IACtC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,sFAAsF;IACtF,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6FAA6F;IAC7F,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC5E,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,iEAAiE;IACjE,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG3G","sourcesContent":["/**\n * Interactive mode using @mariozechner/pi-tui.\n *\n * Replaces the old readline-based REPL with a proper TUI that matches\n * the UX patterns from @mariozechner/pi-coding-agent:\n * - Editor component for input with submit/escape handling\n * - Markdown rendering for assistant responses\n * - Tool execution components with collapsible output\n * - Footer with model/provider info and token stats\n * - Container-based layout (header → chat → pending → editor → footer)\n * - Context compaction (manual /compact + auto mode)\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n\tCombinedAutocompleteProvider,\n\tContainer,\n\tEditor,\n\tKey,\n\tLoader,\n\tmatchesKey,\n\tProcessTerminal,\n\ttype SelectItem,\n\tSelectList,\n\ttype SlashCommand,\n\tSpacer,\n\tText,\n\tTUI,\n} from \"@mariozechner/pi-tui\";\nimport type { ImagePart, ModelMessage } from \"ai\";\nimport chalk from \"chalk\";\nimport type { CodingAgent, CodingAgentConfig } from \"edge-pi\";\nimport { type CompactionResult, estimateContextTokens } from \"edge-pi\";\nimport { SessionManager as SessionManagerClass } from \"edge-pi/session\";\nimport type { AuthStorage } from \"../../auth/auth-storage.js\";\nimport type { ContextFile } from \"../../context.js\";\nimport { getLatestModels } from \"../../model-factory.js\";\nimport type { PromptTemplate } from \"../../prompts.js\";\nimport { expandPromptTemplate } from \"../../prompts.js\";\nimport type { SettingsManager } from \"../../settings.js\";\nimport type { Skill } from \"../../skills.js\";\nimport { executeBashCommand } from \"../../utils/bash-executor.js\";\nimport { type ClipboardImage, extensionForImageMimeType, readClipboardImage } from \"../../utils/clipboard-image.js\";\nimport { formatAIError } from \"../../utils/format-ai-error.js\";\nimport { formatPendingMessages, parseBashInput } from \"./bash-helpers.js\";\nimport { AssistantMessageComponent } from \"./components/assistant-message.js\";\nimport { BashExecutionComponent } from \"./components/bash-execution.js\";\nimport { CompactionSummaryComponent } from \"./components/compaction-summary.js\";\nimport { FooterComponent } from \"./components/footer.js\";\nimport { ToolExecutionComponent, type ToolOutput } from \"./components/tool-execution.js\";\nimport { UserMessageComponent } from \"./components/user-message.js\";\nimport { getEditorTheme, getMarkdownTheme, getSelectListTheme } from \"./theme.js\";\n\n/** Default context window size (used when model doesn't report one). */\nconst DEFAULT_CONTEXT_WINDOW = 200_000;\nconst DEFAULT_COMPACTION_SETTINGS = {\n\treserveTokens: 16384,\n\tkeepRecentTokens: 20000,\n} as const;\n\ntype CompactionSettings = {\n\treserveTokens: number;\n\tkeepRecentTokens: number;\n};\n\n/** Extract display-friendly output from a tool result. Handles both plain strings and structured objects with text/image fields. */\nfunction extractToolOutput(output: unknown): ToolOutput {\n\tif (typeof output === \"string\") {\n\t\treturn { text: output };\n\t}\n\tif (typeof output === \"object\" && output !== null && \"text\" in output && typeof (output as any).text === \"string\") {\n\t\tconst obj = output as any;\n\t\treturn {\n\t\t\ttext: obj.text,\n\t\t\t...(obj.image && { image: obj.image }),\n\t\t};\n\t}\n\treturn { text: JSON.stringify(output) };\n}\n\nexport interface InteractiveModeOptions {\n\tinitialMessage?: string;\n\tinitialMessages?: string[];\n\tskills?: Skill[];\n\tcontextFiles?: ContextFile[];\n\tprompts?: PromptTemplate[];\n\tverbose?: boolean;\n\tprovider: string;\n\tmodelId: string;\n\tauthStorage?: AuthStorage;\n\t/** Settings manager for persisting user preferences (provider, model, compaction). */\n\tsettingsManager?: SettingsManager;\n\t/** Path to the `fd` binary for @ file autocomplete, or undefined if unavailable. */\n\tfdPath?: string;\n\t/** Called when the user switches model via Ctrl+L. Returns a new agent for the new model. */\n\tonModelChange?: (provider: string, modelId: string) => Promise<CodingAgent>;\n\t/** Context window size for the model. Defaults to 200k. */\n\tcontextWindow?: number;\n\t/** Directory where session files are stored. Required for /resume. */\n\tsessionDir?: string;\n\t/** Agent config used to recreate agents when resuming sessions. */\n\tagentConfig?: CodingAgentConfig;\n\t/** When true, show the session picker immediately on startup. */\n\tresumeOnStart?: boolean;\n}\n\n/**\n * Run the interactive TUI mode with streaming output.\n */\nexport async function runInteractiveMode(agent: CodingAgent, options: InteractiveModeOptions): Promise<void> {\n\tconst mode = new InteractiveMode(agent, options);\n\tawait mode.run();\n}\n\n// ============================================================================\n// InteractiveMode class\n// ============================================================================\n\nclass InteractiveMode {\n\tprivate agent: CodingAgent;\n\tprivate options: InteractiveModeOptions;\n\tprivate currentProvider: string;\n\tprivate currentModelId: string;\n\n\tprivate ui!: TUI;\n\tprivate headerContainer!: Container;\n\tprivate chatContainer!: Container;\n\tprivate pendingContainer!: Container;\n\tprivate pendingMessagesContainer!: Container;\n\tprivate editorContainer!: Container;\n\tprivate editor!: Editor;\n\tprivate editorTheme!: import(\"@mariozechner/pi-tui\").EditorTheme;\n\n\t// Message queues\n\tprivate steeringMessages: string[] = [];\n\tprivate followUpMessages: string[] = [];\n\tprivate isStreaming = false;\n\n\t// Inline bash state\n\tprivate isBashMode = false;\n\tprivate isBashRunning = false;\n\tprivate bashAbortController: AbortController | null = null;\n\tprivate bashComponent: import(\"./components/bash-execution.js\").BashExecutionComponent | undefined = undefined;\n\tprivate footer!: FooterComponent;\n\n\t// Loading animation during agent processing\n\tprivate loadingAnimation: Loader | undefined = undefined;\n\n\t// Streaming state\n\tprivate streamingComponent: AssistantMessageComponent | undefined = undefined;\n\tprivate streamingText = \"\";\n\tprivate hadToolResults = false;\n\n\t// Tool execution tracking: toolCallId → component\n\tprivate pendingTools = new Map<string, ToolExecutionComponent>();\n\n\t// Tool output expansion state\n\tprivate toolOutputExpanded = false;\n\n\t// Callback for resolving user input promise\n\tprivate onInputCallback?: (text: string) => void;\n\n\t// Pending clipboard images to attach to the next message\n\tprivate pendingImages: ClipboardImage[] = [];\n\n\t// Compaction state\n\tprivate contextWindow: number;\n\tprivate compactionSettings: CompactionSettings;\n\tprivate autoCompaction = true;\n\tprivate isCompacting = false;\n\tprivate compactionLoader: Loader | null = null;\n\n\tconstructor(agent: CodingAgent, options: InteractiveModeOptions) {\n\t\tthis.agent = agent;\n\t\tthis.options = options;\n\t\tthis.currentProvider = options.provider;\n\t\tthis.currentModelId = options.modelId;\n\t\tthis.contextWindow = options.contextWindow ?? DEFAULT_CONTEXT_WINDOW;\n\n\t\t// Initialize compaction settings from persisted settings if available\n\t\tconst savedCompaction = options.settingsManager?.getCompaction();\n\t\tthis.compactionSettings = {\n\t\t\t...DEFAULT_COMPACTION_SETTINGS,\n\t\t\t...(savedCompaction?.reserveTokens !== undefined && { reserveTokens: savedCompaction.reserveTokens }),\n\t\t\t...(savedCompaction?.keepRecentTokens !== undefined && { keepRecentTokens: savedCompaction.keepRecentTokens }),\n\t\t};\n\t\tthis.autoCompaction = options.settingsManager?.getCompactionEnabled() ?? true;\n\n\t\tthis.configureAgentCompaction();\n\t}\n\n\tasync run(): Promise<void> {\n\t\tthis.initUI();\n\t\tthis.updateFooterTokens();\n\n\t\t// Show session picker immediately if --resume was passed\n\t\tif (this.options.resumeOnStart) {\n\t\t\tawait this.handleResume();\n\t\t}\n\n\t\t// Process initial messages\n\t\tconst { initialMessage, initialMessages = [] } = this.options;\n\n\t\tconst allInitial: string[] = [];\n\t\tif (initialMessage) allInitial.push(initialMessage);\n\t\tallInitial.push(...initialMessages);\n\n\t\tfor (const msg of allInitial) {\n\t\t\tthis.chatContainer.addChild(new UserMessageComponent(msg, getMarkdownTheme()));\n\t\t\tthis.ui.requestRender();\n\t\t\tawait this.streamPrompt(msg);\n\t\t}\n\n\t\t// Main interactive loop\n\t\twhile (true) {\n\t\t\tconst userInput = await this.getUserInput();\n\t\t\tawait this.handleUserInput(userInput);\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// UI Setup\n\t// ========================================================================\n\n\tprivate initUI(): void {\n\t\tconst { provider, modelId, skills = [], contextFiles = [], prompts = [], verbose } = this.options;\n\n\t\tthis.ui = new TUI(new ProcessTerminal());\n\n\t\t// Header\n\t\tthis.headerContainer = new Container();\n\t\tconst logo = chalk.bold(\"epi\") + chalk.dim(` - ${provider}/${modelId}`);\n\n\t\tconst hints = [\n\t\t\t`${chalk.dim(\"Escape\")} to abort`,\n\t\t\t`${chalk.dim(\"!\")} inline bash`,\n\t\t\t`${chalk.dim(\"Alt+Enter\")} follow-up while streaming`,\n\t\t\t`${chalk.dim(\"Ctrl+C\")} to exit`,\n\t\t\t`${chalk.dim(\"Ctrl+E\")} to expand tools`,\n\t\t\t`${chalk.dim(\"Ctrl+L\")} to switch model`,\n\t\t\t`${chalk.dim(\"Ctrl+V\")} to paste image`,\n\t\t\t`${chalk.dim(\"↑/↓\")} to browse history`,\n\t\t\t`${chalk.dim(\"@\")} for file references`,\n\t\t\t`${chalk.dim(\"/\")} for commands`,\n\t\t].join(\"\\n\");\n\n\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\tthis.headerContainer.addChild(new Text(`${logo}\\n${hints}`, 1, 0));\n\t\tthis.headerContainer.addChild(new Spacer(1));\n\n\t\tif (verbose && this.agent.sessionManager?.getSessionFile()) {\n\t\t\tthis.headerContainer.addChild(\n\t\t\t\tnew Text(chalk.dim(`Session: ${this.agent.sessionManager.getSessionFile()}`), 1, 0),\n\t\t\t);\n\t\t}\n\n\t\t// Show loaded context, skills, and prompts at startup\n\t\tthis.showLoadedResources(contextFiles, skills, prompts);\n\n\t\t// Chat area\n\t\tthis.chatContainer = new Container();\n\n\t\t// Pending messages (loading animations, status)\n\t\tthis.pendingContainer = new Container();\n\n\t\t// Pending steering/follow-up messages\n\t\tthis.pendingMessagesContainer = new Container();\n\n\t\t// Editor with slash command autocomplete\n\t\tthis.editorTheme = getEditorTheme();\n\t\tthis.editor = new Editor(this.ui, this.editorTheme);\n\t\tthis.editor.setAutocompleteProvider(this.buildAutocompleteProvider());\n\t\tthis.editorContainer = new Container();\n\t\tthis.editorContainer.addChild(this.editor);\n\n\t\t// Footer\n\t\tthis.footer = new FooterComponent(this.currentProvider, this.currentModelId);\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\n\t\t// Assemble layout\n\t\tthis.ui.addChild(this.headerContainer);\n\t\tthis.ui.addChild(this.chatContainer);\n\t\tthis.ui.addChild(this.pendingMessagesContainer);\n\t\tthis.ui.addChild(this.pendingContainer);\n\t\tthis.ui.addChild(this.editorContainer);\n\t\tthis.ui.addChild(this.footer);\n\n\t\tthis.ui.setFocus(this.editor);\n\t\tthis.setupKeyHandlers();\n\n\t\tthis.ui.start();\n\t}\n\n\t// ========================================================================\n\t// Key Handlers\n\t// ========================================================================\n\n\tprivate setupKeyHandlers(): void {\n\t\tthis.editor.onChange = (text: string) => {\n\t\t\tconst wasBashMode = this.isBashMode;\n\t\t\tthis.isBashMode = text.trimStart().startsWith(\"!\");\n\t\t\tif (wasBashMode !== this.isBashMode) {\n\t\t\t\tthis.updateEditorBorderColor();\n\t\t\t}\n\t\t};\n\t\tthis.editor.onSubmit = (text: string) => {\n\t\t\ttext = text.trim();\n\t\t\tif (!text) return;\n\n\t\t\t// If agent is streaming, Enter becomes a steering message\n\t\t\tif (this.isStreaming) {\n\t\t\t\tthis.agent.steer({ role: \"user\", content: [{ type: \"text\", text }] });\n\t\t\t\tthis.steeringMessages.push(text);\n\t\t\t\tthis.editor.setText(\"\");\n\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.editor.addToHistory(text);\n\t\t\tthis.editor.setText(\"\");\n\n\t\t\tif (this.onInputCallback) {\n\t\t\t\tthis.onInputCallback(text);\n\t\t\t}\n\t\t};\n\n\t\tconst origHandleInput = this.editor.handleInput.bind(this.editor);\n\t\tthis.editor.handleInput = (data: string) => {\n\t\t\t// Escape: abort if agent is running or compacting\n\t\t\tif (matchesKey(data, Key.escape)) {\n\t\t\t\tif (this.isBashRunning && this.bashAbortController) {\n\t\t\t\t\tthis.bashAbortController.abort();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.isCompacting) {\n\t\t\t\t\tthis.agent.abort();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.loadingAnimation) {\n\t\t\t\t\tthis.agent.abort();\n\t\t\t\t\tthis.stopLoading();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Ctrl+C: exit\n\t\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\t\tthis.shutdown();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+D: exit if editor is empty\n\t\t\tif (matchesKey(data, Key.ctrl(\"d\"))) {\n\t\t\t\tif (this.editor.getText().length === 0) {\n\t\t\t\t\tthis.shutdown();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Ctrl+E: toggle tool output expansion\n\t\t\tif (matchesKey(data, Key.ctrl(\"e\"))) {\n\t\t\t\tthis.toggleToolExpansion();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+L: select model\n\t\t\tif (matchesKey(data, Key.ctrl(\"l\"))) {\n\t\t\t\tthis.handleModelSelect();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+V: paste image from clipboard\n\t\t\tif (matchesKey(data, Key.ctrl(\"v\"))) {\n\t\t\t\tthis.handleClipboardImagePaste();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Alt+Enter (Option+Enter on Mac): follow-up while streaming (or submit normally when idle)\n\t\t\tif (matchesKey(data, Key.alt(\"enter\"))) {\n\t\t\t\tconst text = this.editor.getText().trim();\n\t\t\t\tif (!text) return;\n\n\t\t\t\tif (this.isStreaming) {\n\t\t\t\t\tthis.followUpMessages.push(text);\n\t\t\t\t\tthis.editor.setText(\"\");\n\t\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Not streaming: treat like regular submit\n\t\t\t\tthis.editor.onSubmit?.(text);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Alt+Up (Option+Up on Mac): dequeue all queued messages back into the editor\n\t\t\tif (matchesKey(data, Key.alt(\"up\"))) {\n\t\t\t\tconst restored = this.clearAllQueues();\n\t\t\t\tif (restored.length > 0) {\n\t\t\t\t\tthis.editor.setText(restored.join(\"\\n\\n\"));\n\t\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\torigHandleInput(data);\n\t\t};\n\t}\n\n\t// ========================================================================\n\t// User Input\n\t// ========================================================================\n\n\tprivate getUserInput(): Promise<string> {\n\t\treturn new Promise((resolve) => {\n\t\t\tthis.onInputCallback = (text: string) => {\n\t\t\t\tthis.onInputCallback = undefined;\n\t\t\t\tresolve(text);\n\t\t\t};\n\t\t});\n\t}\n\n\tprivate async handleUserInput(input: string): Promise<void> {\n\t\t// Handle commands\n\t\tif (input === \"/help\") {\n\t\t\tthis.showHelp();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/skills\") {\n\t\t\tthis.showSkills();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/quit\" || input === \"/exit\") {\n\t\t\tthis.shutdown();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/model\") {\n\t\t\tawait this.handleModelSelect();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/login\") {\n\t\t\tawait this.handleLogin();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/logout\") {\n\t\t\tawait this.handleLogout();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/compact\" || input.startsWith(\"/compact \")) {\n\t\t\tconst customInstructions = input.startsWith(\"/compact \") ? input.slice(9).trim() : undefined;\n\t\t\tawait this.handleCompactCommand(customInstructions);\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/auto-compact\") {\n\t\t\tthis.toggleAutoCompaction();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/resume\") {\n\t\t\tawait this.handleResume();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input.startsWith(\"/skill:\")) {\n\t\t\tconst skillName = input.slice(\"/skill:\".length).trim();\n\t\t\tawait this.handleSkillInvocation(skillName);\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle bash commands (! for normal, !! for excluded from context)\n\t\tconst bashParsed = parseBashInput(input);\n\t\tif (bashParsed) {\n\t\t\tif (this.isBashRunning) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"A bash command is already running. Press Escape to cancel it first.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait this.handleBashCommand(bashParsed.command, bashParsed.excludeFromContext);\n\t\t\tthis.isBashMode = false;\n\t\t\tthis.updateEditorBorderColor();\n\t\t\treturn;\n\t\t}\n\n\t\t// Try expanding prompt templates\n\t\tconst { prompts = [] } = this.options;\n\t\tconst expanded = expandPromptTemplate(input, prompts);\n\n\t\t// Capture and clear pending images\n\t\tconst images = this.pendingImages.length > 0 ? [...this.pendingImages] : undefined;\n\t\tthis.pendingImages = [];\n\n\t\t// Regular message (use expanded text if a prompt template was matched)\n\t\tconst imageLabel = images ? chalk.dim(` (${images.length} image${images.length > 1 ? \"s\" : \"\"})`) : \"\";\n\t\tthis.chatContainer.addChild(new UserMessageComponent(`${expanded}${imageLabel}`, getMarkdownTheme()));\n\t\tthis.ui.requestRender();\n\t\tawait this.streamPrompt(expanded, images);\n\t}\n\n\tprivate async handleBashCommand(command: string, excludeFromContext: boolean): Promise<void> {\n\t\tthis.bashAbortController = new AbortController();\n\t\tthis.isBashRunning = true;\n\n\t\tthis.bashComponent = new BashExecutionComponent(command, this.ui, excludeFromContext);\n\t\tif (this.toolOutputExpanded) {\n\t\t\tthis.bashComponent.setExpanded(true);\n\t\t}\n\t\tthis.chatContainer.addChild(this.bashComponent);\n\t\tthis.ui.requestRender();\n\n\t\ttry {\n\t\t\tconst result = await executeBashCommand(command, {\n\t\t\t\tsignal: this.bashAbortController.signal,\n\t\t\t\tonChunk: (chunk) => {\n\t\t\t\t\tthis.bashComponent?.appendOutput(chunk);\n\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.bashComponent.setComplete(result.exitCode, result.cancelled, result.truncated, result.fullOutputPath);\n\t\t\tthis.ui.requestRender();\n\n\t\t\tif (!excludeFromContext) {\n\t\t\t\tconst msgText = `Ran \\`${command}\\`\\n\\n\\`\\`\\`\\n${result.output.trimEnd()}\\n\\`\\`\\``;\n\t\t\t\tconst userMsg: ModelMessage = { role: \"user\", content: [{ type: \"text\", text: msgText }] };\n\t\t\t\tthis.agent.setMessages([...this.agent.messages, userMsg]);\n\t\t\t\tthis.agent.sessionManager?.appendMessage(userMsg);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.isBashRunning = false;\n\t\t\tthis.bashAbortController = null;\n\t\t\tthis.bashComponent = undefined;\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Autocomplete\n\t// ========================================================================\n\n\tprivate buildAutocompleteProvider(): CombinedAutocompleteProvider {\n\t\tconst { skills = [], prompts = [], fdPath } = this.options;\n\n\t\tconst commands: SlashCommand[] = [\n\t\t\t{ name: \"help\", description: \"Show available commands\" },\n\t\t\t{ name: \"resume\", description: \"Resume a previous session\" },\n\t\t\t{ name: \"compact\", description: \"Manually compact the session context\" },\n\t\t\t{ name: \"auto-compact\", description: \"Toggle automatic context compaction\" },\n\t\t\t{ name: \"login\", description: \"Login to an OAuth provider\" },\n\t\t\t{ name: \"logout\", description: \"Logout from an OAuth provider\" },\n\t\t\t{ name: \"skills\", description: \"List loaded skills\" },\n\t\t\t{ name: \"model\", description: \"Switch model (Ctrl+L)\" },\n\t\t\t{ name: \"quit\", description: \"Exit the CLI\" },\n\t\t\t{ name: \"exit\", description: \"Exit the CLI\" },\n\t\t];\n\n\t\tfor (const skill of skills) {\n\t\t\tcommands.push({\n\t\t\t\tname: `skill:${skill.name}`,\n\t\t\t\tdescription: skill.description,\n\t\t\t});\n\t\t}\n\n\t\t// Add prompt templates as slash commands\n\t\tfor (const prompt of prompts) {\n\t\t\tcommands.push({\n\t\t\t\tname: prompt.name,\n\t\t\t\tdescription: prompt.description,\n\t\t\t});\n\t\t}\n\n\t\treturn new CombinedAutocompleteProvider(commands, process.cwd(), fdPath ?? null);\n\t}\n\n\t// ========================================================================\n\t// Model Selection\n\t// ========================================================================\n\n\tprivate async handleModelSelect(): Promise<void> {\n\t\tconst latestModels = getLatestModels();\n\t\tconst modelOptions: { provider: string; modelId: string; label: string }[] = [];\n\t\tfor (const [provider, models] of Object.entries(latestModels)) {\n\t\t\tfor (const modelId of models) {\n\t\t\t\tmodelOptions.push({ provider, modelId, label: `${provider}/${modelId}` });\n\t\t\t}\n\t\t}\n\n\t\tconst items: SelectItem[] = modelOptions.map((m) => {\n\t\t\tconst current = m.provider === this.currentProvider && m.modelId === this.currentModelId;\n\t\t\treturn {\n\t\t\t\tvalue: `${m.provider}/${m.modelId}`,\n\t\t\t\tlabel: current ? `${m.label} (current)` : m.label,\n\t\t\t};\n\t\t});\n\n\t\tconst selected = await this.showSelectList(\"Switch model\", items);\n\t\tif (!selected) return;\n\n\t\tconst [newProvider, ...modelParts] = selected.split(\"/\");\n\t\tconst newModelId = modelParts.join(\"/\");\n\n\t\tif (newProvider === this.currentProvider && newModelId === this.currentModelId) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.showStatus(chalk.dim(`Switching to ${newProvider}/${newModelId}...`));\n\n\t\tif (!this.options.onModelChange) {\n\t\t\tthis.showStatus(chalk.yellow(\"Model switching is not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst newAgent = await this.options.onModelChange(newProvider, newModelId);\n\t\t\t// Preserve conversation history\n\t\t\tnewAgent.setMessages([...this.agent.messages]);\n\t\t\tthis.agent = newAgent;\n\t\t\tthis.configureAgentCompaction();\n\n\t\t\tthis.currentProvider = newProvider;\n\t\t\tthis.currentModelId = newModelId;\n\t\t\tthis.updateFooter();\n\n\t\t\t// Persist the choice for next startup\n\t\t\tthis.options.settingsManager?.setDefaults(newProvider, newModelId);\n\n\t\t\tthis.showStatus(chalk.green(`Switched to ${newProvider}/${newModelId}`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tthis.showStatus(chalk.red(`Failed to switch model: ${msg}`));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Streaming\n\t// ========================================================================\n\n\tprivate async streamPrompt(prompt: string, images?: ClipboardImage[]): Promise<void> {\n\t\tthis.isStreaming = true;\n\t\tthis.updatePendingMessagesDisplay();\n\n\t\t// Build image parts from clipboard images\n\t\tconst imageParts: ImagePart[] = (images ?? []).map((img) => ({\n\t\t\ttype: \"image\" as const,\n\t\t\timage: Buffer.from(img.bytes).toString(\"base64\"),\n\t\t\tmediaType: img.mimeType,\n\t\t}));\n\n\t\t// Start loading animation\n\t\tthis.startLoading();\n\n\t\tthis.streamingComponent = undefined;\n\t\tthis.streamingText = \"\";\n\t\tthis.hadToolResults = false;\n\n\t\tlet errorDisplayed = false;\n\t\tlet streamFailed = false;\n\t\ttry {\n\t\t\tconst result =\n\t\t\t\timageParts.length > 0\n\t\t\t\t\t? await this.agent.stream({\n\t\t\t\t\t\t\tmessages: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\trole: \"user\" as const,\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: prompt }, ...imageParts],\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: await this.agent.stream({ prompt });\n\n\t\t\tfor await (const part of result.fullStream) {\n\t\t\t\tswitch (part.type) {\n\t\t\t\t\tcase \"text-delta\":\n\t\t\t\t\t\t// After tool results, or for the very first text part, start a new assistant message component\n\t\t\t\t\t\t// so each agent step gets its own message bubble\n\t\t\t\t\t\tif (this.hadToolResults || !this.streamingComponent) {\n\t\t\t\t\t\t\tthis.streamingComponent = new AssistantMessageComponent(getMarkdownTheme());\n\t\t\t\t\t\t\tthis.streamingText = \"\";\n\t\t\t\t\t\t\tthis.hadToolResults = false;\n\t\t\t\t\t\t\tthis.chatContainer.addChild(this.streamingComponent);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.streamingText += part.text;\n\t\t\t\t\t\tthis.streamingComponent!.updateText(this.streamingText);\n\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"tool-call\": {\n\t\t\t\t\t\tconst args =\n\t\t\t\t\t\t\ttypeof part.input === \"object\" && part.input !== null\n\t\t\t\t\t\t\t\t? (part.input as Record<string, unknown>)\n\t\t\t\t\t\t\t\t: {};\n\t\t\t\t\t\tconst toolComponent = new ToolExecutionComponent(part.toolName, args);\n\n\t\t\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\t\t\ttoolComponent.setExpanded(true);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.pendingTools.set(part.toolCallId, toolComponent);\n\t\t\t\t\t\tthis.chatContainer.addChild(toolComponent);\n\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"tool-result\": {\n\t\t\t\t\t\tconst toolComponent = this.pendingTools.get(part.toolCallId);\n\t\t\t\t\t\tif (toolComponent) {\n\t\t\t\t\t\t\tconst toolOutput = extractToolOutput(part.output);\n\t\t\t\t\t\t\ttoolComponent.updateResult(toolOutput, /* isError */ false, /* isPartial */ false);\n\t\t\t\t\t\t\tthis.pendingTools.delete(part.toolCallId);\n\t\t\t\t\t\t\tthis.hadToolResults = true;\n\t\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"error\": {\n\t\t\t\t\t\tconst errorMessage = formatAIError(part.error);\n\t\t\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\t\t\tthis.streamingComponent.setError(errorMessage);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.showStatus(chalk.red(`Error: ${errorMessage}`));\n\t\t\t\t\t\t}\n\t\t\t\t\t\terrorDisplayed = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (errorDisplayed) return;\n\n\t\t\t// Wait for stream to complete — the agent auto-updates messages and persists to session\n\t\t\tawait result.response;\n\n\t\t\t// Update footer token stats\n\t\t\tthis.updateFooterTokens();\n\t\t} catch (error) {\n\t\t\tif (errorDisplayed) {\n\t\t\t\tstreamFailed = true;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tstreamFailed = true;\n\t\t\tif ((error as Error).name === \"AbortError\") {\n\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\tthis.streamingComponent.setAborted();\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.dim(\"[aborted]\"));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst msg =\n\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t? error.message\n\t\t\t\t\t\t: typeof error === \"object\" && error !== null\n\t\t\t\t\t\t\t? JSON.stringify(error)\n\t\t\t\t\t\t\t: String(error);\n\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\tthis.streamingComponent.setError(msg);\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.red(`Error: ${msg}`));\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.stopLoading();\n\t\t\tthis.streamingComponent = undefined;\n\t\t\tthis.streamingText = \"\";\n\t\t\tthis.hadToolResults = false;\n\t\t\tthis.pendingTools.clear();\n\t\t\tthis.isStreaming = false;\n\t\t\tthis.steeringMessages = [];\n\t\t\tif (streamFailed) {\n\t\t\t\tthis.followUpMessages = [];\n\t\t\t}\n\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\tthis.ui.requestRender();\n\t\t}\n\n\t\t// Process queued follow-ups (skipped if stream failed/aborted)\n\t\twhile (this.followUpMessages.length > 0) {\n\t\t\tconst next = this.followUpMessages.shift();\n\t\t\tif (!next) break;\n\n\t\t\tthis.updatePendingMessagesDisplay();\n\n\t\t\tthis.chatContainer.addChild(new UserMessageComponent(next, getMarkdownTheme()));\n\t\t\tthis.ui.requestRender();\n\t\t\tawait this.streamPrompt(next);\n\t\t}\n\t}\n\n\tprivate updatePendingMessagesDisplay(): void {\n\t\tthis.pendingMessagesContainer.clear();\n\n\t\t// If no agent is running, clear pending messages (they've been consumed)\n\t\tif (!this.isStreaming && this.followUpMessages.length === 0 && this.steeringMessages.length === 0) {\n\t\t\tthis.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines = formatPendingMessages(this.steeringMessages, this.followUpMessages);\n\n\t\tif (lines.length === 0) {\n\t\t\tthis.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.pendingMessagesContainer.addChild(new Spacer(1));\n\t\tthis.pendingMessagesContainer.addChild(new Text(lines.map((l) => chalk.dim(l)).join(\"\\n\"), 1, 0));\n\t\tthis.pendingMessagesContainer.addChild(new Text(chalk.dim(\"↳ Alt+Up to edit queued messages\"), 1, 0));\n\t\tthis.pendingMessagesContainer.addChild(new Spacer(1));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate clearAllQueues(): string[] {\n\t\tconst restored = [...this.steeringMessages, ...this.followUpMessages];\n\t\tthis.steeringMessages = [];\n\t\tthis.followUpMessages = [];\n\t\treturn restored;\n\t}\n\n\t// ========================================================================\n\t// Loading Animation\n\t// ========================================================================\n\n\tprivate startLoading(): void {\n\t\tthis.stopLoading();\n\t\tthis.loadingAnimation = new Loader(\n\t\t\tthis.ui,\n\t\t\t(s: string) => chalk.cyan(s),\n\t\t\t(s: string) => chalk.dim(s),\n\t\t\t\"Working...\",\n\t\t);\n\t\tthis.loadingAnimation.start();\n\t\tthis.pendingContainer.addChild(new Spacer(1));\n\t\tthis.pendingContainer.addChild(this.loadingAnimation);\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate stopLoading(): void {\n\t\tif (this.loadingAnimation) {\n\t\t\tthis.loadingAnimation.stop();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.loadingAnimation = undefined;\n\t\t\tthis.ui.requestRender();\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Tool Expansion\n\t// ========================================================================\n\n\tprivate toggleToolExpansion(): void {\n\t\tthis.toolOutputExpanded = !this.toolOutputExpanded;\n\n\t\t// Update all tool components and compaction components in the chat\n\t\tfor (const child of this.chatContainer.children) {\n\t\t\tif (child instanceof ToolExecutionComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t} else if (child instanceof CompactionSummaryComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t} else if (child instanceof BashExecutionComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t}\n\t\t}\n\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Clipboard Image Paste\n\t// ========================================================================\n\n\tprivate handleClipboardImagePaste(): void {\n\t\ttry {\n\t\t\tconst image = readClipboardImage();\n\t\t\tif (!image) return;\n\t\t\tthis.pendingImages.push(image);\n\t\t\tconst ext = extensionForImageMimeType(image.mimeType) ?? \"image\";\n\t\t\tconst label = `[image ${this.pendingImages.length}: ${ext}]`;\n\t\t\tthis.editor.insertTextAtCursor(label);\n\t\t\tthis.ui.requestRender();\n\t\t} catch {\n\t\t\t// Silently ignore clipboard errors (may not have permission, etc.)\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Compaction\n\t// ========================================================================\n\n\t/**\n\t * Handle the /compact command.\n\t */\n\tprivate async handleCompactCommand(_customInstructions?: string): Promise<void> {\n\t\ttry {\n\t\t\tconst result = await this.agent.compact();\n\t\t\tif (!result) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (already compacted or insufficient history).\"));\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Compaction callbacks already report errors in interactive mode.\n\t\t\t// Catch here to avoid unwinding the main TUI loop on abort/provider errors.\n\t\t\tif (!this.agent.compaction?.onCompactionError) {\n\t\t\t\tconst compactionError = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tif (compactionError.name === \"AbortError\" || compactionError.message === \"Compaction cancelled\") {\n\t\t\t\t\tthis.showStatus(chalk.dim(\"Compaction cancelled.\"));\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.red(`Compaction failed: ${compactionError.message}`));\n\t\t\t\t}\n\t\t\t\tthis.ui.requestRender();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Toggle auto-compaction on/off.\n\t */\n\tprivate toggleAutoCompaction(): void {\n\t\tthis.autoCompaction = !this.autoCompaction;\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.options.settingsManager?.setCompactionEnabled(this.autoCompaction);\n\t\tif (this.agent.compaction) {\n\t\t\tthis.agent.setCompaction({\n\t\t\t\t...this.agent.compaction,\n\t\t\t\tmode: this.autoCompaction ? \"auto\" : \"manual\",\n\t\t\t});\n\t\t}\n\t\tthis.showStatus(\n\t\t\tthis.autoCompaction ? chalk.green(\"Auto-compaction enabled\") : chalk.dim(\"Auto-compaction disabled\"),\n\t\t);\n\t\tthis.ui.requestRender();\n\t}\n\n\t/**\n\t * Configure agent-level compaction and callbacks for UI updates.\n\t */\n\tprivate configureAgentCompaction(): void {\n\t\tconst mode = this.autoCompaction ? \"auto\" : \"manual\";\n\t\tthis.agent.setCompaction({\n\t\t\tcontextWindow: this.contextWindow,\n\t\t\tmode,\n\t\t\tsettings: {\n\t\t\t\treserveTokens: this.compactionSettings.reserveTokens,\n\t\t\t\tkeepRecentTokens: this.compactionSettings.keepRecentTokens,\n\t\t\t},\n\t\t\tonCompactionStart: () => {\n\t\t\t\tthis.isCompacting = true;\n\t\t\t\tthis.compactionLoader?.stop();\n\t\t\t\tthis.compactionLoader = new Loader(\n\t\t\t\t\tthis.ui,\n\t\t\t\t\t(s: string) => chalk.cyan(s),\n\t\t\t\t\t(s: string) => chalk.dim(s),\n\t\t\t\t\tthis.autoCompaction\n\t\t\t\t\t\t? \"Auto-compacting context... (Escape to cancel)\"\n\t\t\t\t\t\t: \"Compacting context... (Escape to cancel)\",\n\t\t\t\t);\n\t\t\t\tthis.compactionLoader.start();\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.pendingContainer.addChild(new Spacer(1));\n\t\t\t\tthis.pendingContainer.addChild(this.compactionLoader);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t},\n\t\t\tonCompactionComplete: (result: CompactionResult) => {\n\t\t\t\tthis.compactionLoader?.stop();\n\t\t\t\tthis.compactionLoader = null;\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.isCompacting = false;\n\n\t\t\t\tthis.rebuildChatFromSession();\n\t\t\t\tconst summaryComponent = new CompactionSummaryComponent(result.tokensBefore, result.summary);\n\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\tsummaryComponent.setExpanded(true);\n\t\t\t\t}\n\t\t\t\tthis.chatContainer.addChild(summaryComponent);\n\n\t\t\t\tthis.updateFooterTokens();\n\t\t\t\tif (this.options.verbose) {\n\t\t\t\t\tconst tokensAfter = estimateContextTokens([...this.agent.messages]);\n\t\t\t\t\tthis.showStatus(\n\t\t\t\t\t\tchalk.dim(\n\t\t\t\t\t\t\t`Compacted: ${result.tokensBefore.toLocaleString()} -> ${tokensAfter.toLocaleString()} tokens`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthis.ui.requestRender();\n\t\t\t},\n\t\t\tonCompactionError: (error: Error) => {\n\t\t\t\tthis.compactionLoader?.stop();\n\t\t\t\tthis.compactionLoader = null;\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.isCompacting = false;\n\n\t\t\t\tif (error.name === \"AbortError\" || error.message === \"Compaction cancelled\") {\n\t\t\t\t\tthis.showStatus(chalk.dim(\"Compaction cancelled.\"));\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.red(`Compaction failed: ${error.message}`));\n\t\t\t\t}\n\t\t\t\tthis.ui.requestRender();\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Rebuild the chat UI from session context after compaction.\n\t */\n\tprivate rebuildChatFromSession(): void {\n\t\tthis.chatContainer.clear();\n\n\t\tconst messages = this.agent.messages;\n\t\tfor (const msg of messages) {\n\t\t\tif (msg.role === \"user\") {\n\t\t\t\t// Check if this is a compaction summary\n\t\t\t\tconst content = msg.content;\n\t\t\t\tif (Array.isArray(content) && content.length > 0) {\n\t\t\t\t\tconst textBlock = content[0] as { type: string; text?: string };\n\t\t\t\t\tif (textBlock.type === \"text\" && textBlock.text?.startsWith('<summary type=\"compaction\"')) {\n\t\t\t\t\t\t// Skip compaction summaries in rebuild (they are injected by buildSessionContext)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (textBlock.type === \"text\" && textBlock.text?.startsWith('<summary type=\"branch\"')) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst text = extractTextFromMessage(msg);\n\t\t\t\tif (text) {\n\t\t\t\t\tthis.chatContainer.addChild(new UserMessageComponent(text, getMarkdownTheme()));\n\t\t\t\t}\n\t\t\t} else if (msg.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = msg as import(\"edge-pi\").AssistantModelMessage;\n\t\t\t\tconst textParts: string[] = [];\n\t\t\t\tfor (const block of assistantMsg.content) {\n\t\t\t\t\tconst b = block as {\n\t\t\t\t\t\ttype: string;\n\t\t\t\t\t\ttext?: string;\n\t\t\t\t\t\ttoolName?: string;\n\t\t\t\t\t\tinput?: unknown;\n\t\t\t\t\t\ttoolCallId?: string;\n\t\t\t\t\t};\n\t\t\t\t\tif (b.type === \"text\" && b.text) {\n\t\t\t\t\t\ttextParts.push(b.text);\n\t\t\t\t\t} else if (b.type === \"tool-call\" && b.toolName) {\n\t\t\t\t\t\tconst args =\n\t\t\t\t\t\t\ttypeof b.input === \"object\" && b.input !== null ? (b.input as Record<string, unknown>) : {};\n\t\t\t\t\t\tconst toolComp = new ToolExecutionComponent(b.toolName, args);\n\t\t\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\t\t\ttoolComp.setExpanded(true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Mark as completed (we don't have the result here, just show collapsed)\n\t\t\t\t\t\ttoolComp.updateResult({ text: \"(from history)\" }, false, false);\n\t\t\t\t\t\tthis.chatContainer.addChild(toolComp);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (textParts.length > 0) {\n\t\t\t\t\tconst comp = new AssistantMessageComponent(getMarkdownTheme());\n\t\t\t\t\tcomp.updateText(textParts.join(\"\"));\n\t\t\t\t\tthis.chatContainer.addChild(comp);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Skip tool messages in UI rebuild - they are consumed by tool-call components\n\t\t}\n\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Footer Token Tracking\n\t// ========================================================================\n\n\t/**\n\t * Update the footer with current token count information.\n\t */\n\tprivate updateFooterTokens(): void {\n\t\tconst contextTokens = estimateContextTokens([...this.agent.messages]);\n\t\tthis.footer.setTokenInfo(contextTokens, this.contextWindow);\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.ui?.requestRender();\n\t}\n\n\t/**\n\t * Check if the current provider is using an OAuth subscription credential.\n\t */\n\tprivate isSubscriptionProvider(): boolean {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) return false;\n\t\tconst cred = authStorage.get(this.currentProvider);\n\t\treturn cred?.type === \"oauth\";\n\t}\n\n\t/**\n\t * Replace the footer component and update token info.\n\t */\n\tprivate updateFooter(): void {\n\t\tthis.footer = new FooterComponent(this.currentProvider, this.currentModelId);\n\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\t\tthis.updateFooterTokens();\n\n\t\t// Replace footer in UI\n\t\tconst children = this.ui.children;\n\t\tchildren[children.length - 1] = this.footer;\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Startup Resource Display\n\t// ========================================================================\n\n\tprivate formatDisplayPath(p: string): string {\n\t\tconst home = process.env.HOME || process.env.USERPROFILE || \"\";\n\t\tif (home && p.startsWith(home)) {\n\t\t\treturn `~${p.slice(home.length)}`;\n\t\t}\n\t\treturn p;\n\t}\n\n\tprivate showLoadedResources(contextFiles: ContextFile[], skills: Skill[], prompts: PromptTemplate[]): void {\n\t\tconst sectionHeader = (name: string) => chalk.cyan(`[${name}]`);\n\n\t\tif (contextFiles.length > 0) {\n\t\t\tconst contextList = contextFiles.map((f) => chalk.dim(` ${this.formatDisplayPath(f.path)}`)).join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Context\")}\\n${contextList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (skills.length > 0) {\n\t\t\tconst skillList = skills.map((s) => chalk.dim(` ${this.formatDisplayPath(s.filePath)}`)).join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Skills\")}\\n${skillList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (prompts.length > 0) {\n\t\t\tconst promptList = prompts\n\t\t\t\t.map((p) => {\n\t\t\t\t\tconst sourceLabel = chalk.cyan(p.source);\n\t\t\t\t\treturn chalk.dim(` ${sourceLabel} /${p.name}`);\n\t\t\t\t})\n\t\t\t\t.join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Prompts\")}\\n${promptList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Commands\n\t// ========================================================================\n\n\tprivate showHelp(): void {\n\t\tconst helpText = [\n\t\t\tchalk.bold(\"Commands:\"),\n\t\t\t\" !<command> Run inline bash and include output in context\",\n\t\t\t\" !!<command> Run inline bash but exclude output from context\",\n\t\t\t\" /resume Resume a previous session\",\n\t\t\t\" /compact [text] Compact the session context (optional instructions)\",\n\t\t\t\" /auto-compact Toggle automatic context compaction\",\n\t\t\t\" /model Switch model (Ctrl+L)\",\n\t\t\t\" /login Login to an OAuth provider\",\n\t\t\t\" /logout Logout from an OAuth provider\",\n\t\t\t\" /skills List loaded skills\",\n\t\t\t\" /skill:<name> Invoke a skill by name\",\n\t\t\t\" /quit, /exit Exit the CLI\",\n\t\t].join(\"\\n\");\n\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(helpText, 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate showSkills(): void {\n\t\tconst { skills = [] } = this.options;\n\n\t\tif (skills.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No skills loaded.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines: string[] = [];\n\t\tfor (const skill of skills) {\n\t\t\tconst hidden = skill.disableModelInvocation ? chalk.dim(\" (hidden from model)\") : \"\";\n\t\t\tlines.push(` ${chalk.bold(skill.name)}${hidden}`);\n\t\t\tlines.push(chalk.dim(` ${skill.description}`));\n\t\t\tlines.push(chalk.dim(` ${skill.filePath}`));\n\t\t}\n\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(lines.join(\"\\n\"), 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate async handleSkillInvocation(skillName: string): Promise<void> {\n\t\tconst { skills = [] } = this.options;\n\t\tconst skill = skills.find((s) => s.name === skillName);\n\n\t\tif (!skill) {\n\t\t\tthis.showStatus(chalk.red(`Skill \"${skillName}\" not found.`));\n\t\t\treturn;\n\t\t}\n\n\t\tconst skillPrompt = `Please read and follow the instructions in the skill file: ${skill.filePath}`;\n\t\tthis.chatContainer.addChild(new UserMessageComponent(skillPrompt, getMarkdownTheme()));\n\t\tthis.ui.requestRender();\n\t\tawait this.streamPrompt(skillPrompt);\n\t}\n\n\t// ========================================================================\n\t// OAuth Login/Logout\n\t// ========================================================================\n\n\tprivate async handleLogin(): Promise<void> {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) {\n\t\t\tthis.showStatus(chalk.red(\"Auth storage not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst providers = authStorage.getProviders();\n\t\tif (providers.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No OAuth providers registered.\"));\n\t\t\treturn;\n\t\t}\n\n\t\t// Use SelectList overlay for provider selection\n\t\tconst items: SelectItem[] = providers.map((p) => {\n\t\t\tconst loggedIn = authStorage.get(p.id)?.type === \"oauth\" ? \" (logged in)\" : \"\";\n\t\t\treturn { value: p.id, label: `${p.name}${loggedIn}` };\n\t\t});\n\n\t\tconst selected = await this.showSelectList(\"Login to OAuth provider\", items);\n\t\tif (!selected) return;\n\n\t\tconst provider = providers.find((p) => p.id === selected);\n\t\tif (!provider) return;\n\n\t\tthis.showStatus(chalk.dim(`Logging in to ${provider.name}...`));\n\n\t\ttry {\n\t\t\tawait authStorage.login(provider.id, {\n\t\t\t\tonAuth: (info) => {\n\t\t\t\t\tconst lines = [chalk.bold(\"Open this URL in your browser:\"), chalk.cyan(info.url)];\n\t\t\t\t\tif (info.instructions) {\n\t\t\t\t\t\tlines.push(chalk.dim(info.instructions));\n\t\t\t\t\t}\n\t\t\t\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\t\t\t\tthis.chatContainer.addChild(new Text(lines.join(\"\\n\"), 1, 0));\n\t\t\t\t\tthis.ui.requestRender();\n\n\t\t\t\t\t// Try to open browser\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst { execSync } = require(\"node:child_process\") as typeof import(\"node:child_process\");\n\t\t\t\t\t\tconst platform = process.platform;\n\t\t\t\t\t\tif (platform === \"darwin\") {\n\t\t\t\t\t\t\texecSync(`open \"${info.url}\"`, { stdio: \"ignore\" });\n\t\t\t\t\t\t} else if (platform === \"linux\") {\n\t\t\t\t\t\t\texecSync(`xdg-open \"${info.url}\" 2>/dev/null || sensible-browser \"${info.url}\" 2>/dev/null`, {\n\t\t\t\t\t\t\t\tstdio: \"ignore\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else if (platform === \"win32\") {\n\t\t\t\t\t\t\texecSync(`start \"\" \"${info.url}\"`, { stdio: \"ignore\" });\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Silently fail - user can open manually\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tonPrompt: async (promptInfo) => {\n\t\t\t\t\t// Show prompt message and wait for user input\n\t\t\t\t\tthis.showStatus(chalk.dim(promptInfo.message));\n\t\t\t\t\tconst answer = await this.getUserInput();\n\t\t\t\t\treturn answer.trim();\n\t\t\t\t},\n\t\t\t\tonProgress: (message) => {\n\t\t\t\t\tthis.showStatus(chalk.dim(message));\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\t\t\tthis.ui.requestRender();\n\t\t\tthis.showStatus(chalk.green(`Logged in to ${provider.name}. Credentials saved.`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tif (msg !== \"Login cancelled\") {\n\t\t\t\tthis.showStatus(chalk.red(`Login failed: ${msg}`));\n\t\t\t} else {\n\t\t\t\tthis.showStatus(chalk.dim(\"Login cancelled.\"));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async handleLogout(): Promise<void> {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) {\n\t\t\tthis.showStatus(chalk.red(\"Auth storage not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst loggedIn = authStorage\n\t\t\t.list()\n\t\t\t.filter((id) => authStorage.get(id)?.type === \"oauth\")\n\t\t\t.map((id) => {\n\t\t\t\tconst provider = authStorage.getProvider(id);\n\t\t\t\treturn { id, name: provider?.name ?? id };\n\t\t\t});\n\n\t\tif (loggedIn.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No OAuth providers logged in. Use /login first.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst items: SelectItem[] = loggedIn.map((p) => ({\n\t\t\tvalue: p.id,\n\t\t\tlabel: p.name,\n\t\t}));\n\n\t\tconst selected = await this.showSelectList(\"Logout from OAuth provider\", items);\n\t\tif (!selected) return;\n\n\t\tconst entry = loggedIn.find((p) => p.id === selected);\n\t\tif (!entry) return;\n\n\t\tauthStorage.logout(entry.id);\n\t\tthis.showStatus(chalk.green(`Logged out of ${entry.name}.`));\n\t}\n\n\t// ========================================================================\n\t// Resume Session\n\t// ========================================================================\n\n\t/**\n\t * List session files from the session directory, sorted by modification time (newest first).\n\t * Returns metadata for each session including the first user message as a preview.\n\t */\n\tprivate listAvailableSessions(): { path: string; mtime: number; preview: string; timestamp: string }[] {\n\t\tconst { sessionDir } = this.options;\n\t\tif (!sessionDir || !existsSync(sessionDir)) return [];\n\n\t\ttry {\n\t\t\tconst files = readdirSync(sessionDir)\n\t\t\t\t.filter((f: string) => f.endsWith(\".jsonl\"))\n\t\t\t\t.map((f: string) => {\n\t\t\t\t\tconst filePath = join(sessionDir, f);\n\t\t\t\t\tconst mtime = statSync(filePath).mtime.getTime();\n\t\t\t\t\treturn { name: f, path: filePath, mtime };\n\t\t\t\t})\n\t\t\t\t.sort((a, b) => b.mtime - a.mtime);\n\n\t\t\tconst sessions: { path: string; mtime: number; preview: string; timestamp: string }[] = [];\n\t\t\tfor (const file of files) {\n\t\t\t\t// Skip the current session file\n\t\t\t\tif (this.agent.sessionManager?.getSessionFile() === file.path) continue;\n\n\t\t\t\tconst preview = this.getSessionPreview(file.path);\n\t\t\t\tconst timestamp = new Date(file.mtime).toLocaleString();\n\t\t\t\tsessions.push({ path: file.path, mtime: file.mtime, preview, timestamp });\n\t\t\t}\n\t\t\treturn sessions;\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Extract the first user message from a session file for preview.\n\t */\n\tprivate getSessionPreview(filePath: string): string {\n\t\ttry {\n\t\t\tconst content = readFileSync(filePath, \"utf-8\");\n\t\t\tconst lines = content.trim().split(\"\\n\");\n\t\t\tfor (const line of lines) {\n\t\t\t\tif (!line.trim()) continue;\n\t\t\t\ttry {\n\t\t\t\t\tconst entry = JSON.parse(line);\n\t\t\t\t\tif (entry.type === \"message\" && entry.message?.role === \"user\") {\n\t\t\t\t\t\tconst msg = entry.message;\n\t\t\t\t\t\tlet text = \"\";\n\t\t\t\t\t\tif (typeof msg.content === \"string\") {\n\t\t\t\t\t\t\ttext = msg.content;\n\t\t\t\t\t\t} else if (Array.isArray(msg.content)) {\n\t\t\t\t\t\t\tfor (const block of msg.content) {\n\t\t\t\t\t\t\t\tif (block.type === \"text\" && block.text) {\n\t\t\t\t\t\t\t\t\ttext = block.text;\n\t\t\t\t\t\t\t\t\tbreak;\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\t// Truncate and clean up for display\n\t\t\t\t\t\ttext = text.replace(/\\n/g, \" \").trim();\n\t\t\t\t\t\tif (text.length > 80) {\n\t\t\t\t\t\t\ttext = `${text.slice(0, 77)}...`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn text || \"(empty message)\";\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Skip malformed lines\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn \"(no messages)\";\n\t\t} catch {\n\t\t\treturn \"(unreadable)\";\n\t\t}\n\t}\n\n\t/**\n\t * Format a relative time string (e.g. \"2 hours ago\", \"3 days ago\").\n\t */\n\tprivate formatRelativeTime(mtime: number): string {\n\t\tconst now = Date.now();\n\t\tconst diffMs = now - mtime;\n\t\tconst diffSec = Math.floor(diffMs / 1000);\n\t\tconst diffMin = Math.floor(diffSec / 60);\n\t\tconst diffHour = Math.floor(diffMin / 60);\n\t\tconst diffDay = Math.floor(diffHour / 24);\n\n\t\tif (diffMin < 1) return \"just now\";\n\t\tif (diffMin < 60) return `${diffMin}m ago`;\n\t\tif (diffHour < 24) return `${diffHour}h ago`;\n\t\tif (diffDay < 30) return `${diffDay}d ago`;\n\t\treturn new Date(mtime).toLocaleDateString();\n\t}\n\n\t/**\n\t * Handle the /resume command: show a list of previous sessions and load the selected one.\n\t */\n\tprivate async handleResume(): Promise<void> {\n\t\tconst sessions = this.listAvailableSessions();\n\t\tif (sessions.length === 0) {\n\t\t\tthis.showStatus(chalk.yellow(\"No previous sessions found.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst items: SelectItem[] = sessions.map((s) => ({\n\t\t\tvalue: s.path,\n\t\t\tlabel: `${chalk.dim(this.formatRelativeTime(s.mtime))} ${s.preview}`,\n\t\t}));\n\n\t\tconst selected = await this.showSelectList(\"Resume session\", items);\n\t\tif (!selected) return;\n\n\t\tconst session = sessions.find((s) => s.path === selected);\n\t\tif (!session) return;\n\n\t\ttry {\n\t\t\t// Open the selected session and set on agent (auto-restores messages)\n\t\t\tconst sessionDir = this.options.sessionDir!;\n\t\t\tconst newSessionManager = SessionManagerClass.open(selected, sessionDir);\n\t\t\tthis.agent.sessionManager = newSessionManager;\n\n\t\t\t// Rebuild the chat UI\n\t\t\tthis.chatContainer.clear();\n\t\t\tthis.rebuildChatFromSession();\n\n\t\t\t// Update footer tokens\n\t\t\tthis.updateFooterTokens();\n\n\t\t\tconst msgCount = this.agent.messages.length;\n\t\t\tthis.showStatus(chalk.green(`Resumed session (${msgCount} messages)`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tthis.showStatus(chalk.red(`Failed to resume session: ${msg}`));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Select List (overlay pattern from pi-coding-agent)\n\t// ========================================================================\n\n\tprivate showSelectList(title: string, items: SelectItem[]): Promise<string | null> {\n\t\treturn new Promise((resolve) => {\n\t\t\tconst container = new Container();\n\n\t\t\tcontainer.addChild(new Spacer(1));\n\t\t\tcontainer.addChild(new Text(chalk.bold.cyan(title), 1, 0));\n\n\t\t\tconst selectList = new SelectList(items, Math.min(items.length, 10), getSelectListTheme());\n\t\t\tselectList.onSelect = (item) => {\n\t\t\t\t// Restore normal UI\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.editorContainer.addChild(this.editor);\n\t\t\t\tthis.ui.setFocus(this.editor);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\tresolve(item.value);\n\t\t\t};\n\t\t\tselectList.onCancel = () => {\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.editorContainer.addChild(this.editor);\n\t\t\t\tthis.ui.setFocus(this.editor);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\tresolve(null);\n\t\t\t};\n\t\t\tcontainer.addChild(selectList);\n\t\t\tcontainer.addChild(new Text(chalk.dim(\"↑↓ navigate • enter select • esc cancel\"), 1, 0));\n\t\t\tcontainer.addChild(new Spacer(1));\n\n\t\t\t// Replace editor area with select list\n\t\t\tthis.editorContainer.clear();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.pendingContainer.addChild(container);\n\t\t\tthis.ui.setFocus(selectList);\n\t\t\tthis.ui.requestRender();\n\t\t});\n\t}\n\n\t// ========================================================================\n\t// Status & Utilities\n\t// ========================================================================\n\n\tprivate showStatus(text: string): void {\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(text, 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate updateEditorBorderColor(): void {\n\t\tthis.editorTheme.borderColor = this.isBashMode ? (s: string) => chalk.yellow(s) : (s: string) => chalk.gray(s);\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate shutdown(): void {\n\t\tthis.ui.stop();\n\t\tconsole.log(chalk.dim(\"\\nGoodbye.\"));\n\t\tprocess.exit(0);\n\t}\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction extractTextFromMessage(msg: ModelMessage): string {\n\tif (msg.role === \"user\") {\n\t\tconst content = (msg as import(\"edge-pi\").UserModelMessage).content;\n\t\tif (typeof content === \"string\") return content;\n\t\tif (Array.isArray(content)) {\n\t\t\treturn content\n\t\t\t\t.filter((c): c is { type: \"text\"; text: string } => (c as { type: string }).type === \"text\")\n\t\t\t\t.map((c) => c.text)\n\t\t\t\t.join(\"\");\n\t\t}\n\t}\n\treturn \"\";\n}\n"]}
|
|
@@ -14,7 +14,8 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
|
14
14
|
import { join } from "node:path";
|
|
15
15
|
import { CombinedAutocompleteProvider, Container, Editor, Key, Loader, matchesKey, ProcessTerminal, SelectList, Spacer, Text, TUI, } from "@mariozechner/pi-tui";
|
|
16
16
|
import chalk from "chalk";
|
|
17
|
-
import { estimateContextTokens
|
|
17
|
+
import { estimateContextTokens } from "edge-pi";
|
|
18
|
+
import { SessionManager as SessionManagerClass } from "edge-pi/session";
|
|
18
19
|
import { getLatestModels } from "../../model-factory.js";
|
|
19
20
|
import { expandPromptTemplate } from "../../prompts.js";
|
|
20
21
|
import { executeBashCommand } from "../../utils/bash-executor.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive-mode.js","sourceRoot":"","sources":["../../../src/modes/interactive/interactive-mode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Interactive mode using @mariozechner/pi-tui.\n *\n * Replaces the old readline-based REPL with a proper TUI that matches\n * the UX patterns from @mariozechner/pi-coding-agent:\n * - Editor component for input with submit/escape handling\n * - Markdown rendering for assistant responses\n * - Tool execution components with collapsible output\n * - Footer with model/provider info and token stats\n * - Container-based layout (header → chat → pending → editor → footer)\n * - Context compaction (manual /compact + auto mode)\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n\tCombinedAutocompleteProvider,\n\tContainer,\n\tEditor,\n\tKey,\n\tLoader,\n\tmatchesKey,\n\tProcessTerminal,\n\ttype SelectItem,\n\tSelectList,\n\ttype SlashCommand,\n\tSpacer,\n\tText,\n\tTUI,\n} from \"@mariozechner/pi-tui\";\nimport type { ImagePart, ModelMessage } from \"ai\";\nimport chalk from \"chalk\";\nimport type { CodingAgent, CodingAgentConfig } from \"edge-pi\";\nimport { type CompactionResult, estimateContextTokens, SessionManager as SessionManagerClass } from \"edge-pi\";\nimport type { AuthStorage } from \"../../auth/auth-storage.js\";\nimport type { ContextFile } from \"../../context.js\";\nimport { getLatestModels } from \"../../model-factory.js\";\nimport type { PromptTemplate } from \"../../prompts.js\";\nimport { expandPromptTemplate } from \"../../prompts.js\";\nimport type { SettingsManager } from \"../../settings.js\";\nimport type { Skill } from \"../../skills.js\";\nimport { executeBashCommand } from \"../../utils/bash-executor.js\";\nimport { type ClipboardImage, extensionForImageMimeType, readClipboardImage } from \"../../utils/clipboard-image.js\";\nimport { formatAIError } from \"../../utils/format-ai-error.js\";\nimport { formatPendingMessages, parseBashInput } from \"./bash-helpers.js\";\nimport { AssistantMessageComponent } from \"./components/assistant-message.js\";\nimport { BashExecutionComponent } from \"./components/bash-execution.js\";\nimport { CompactionSummaryComponent } from \"./components/compaction-summary.js\";\nimport { FooterComponent } from \"./components/footer.js\";\nimport { ToolExecutionComponent, type ToolOutput } from \"./components/tool-execution.js\";\nimport { UserMessageComponent } from \"./components/user-message.js\";\nimport { getEditorTheme, getMarkdownTheme, getSelectListTheme } from \"./theme.js\";\n\n/** Default context window size (used when model doesn't report one). */\nconst DEFAULT_CONTEXT_WINDOW = 200_000;\nconst DEFAULT_COMPACTION_SETTINGS = {\n\treserveTokens: 16384,\n\tkeepRecentTokens: 20000,\n} as const;\n\ntype CompactionSettings = {\n\treserveTokens: number;\n\tkeepRecentTokens: number;\n};\n\n/** Extract display-friendly output from a tool result. Handles both plain strings and structured objects with text/image fields. */\nfunction extractToolOutput(output: unknown): ToolOutput {\n\tif (typeof output === \"string\") {\n\t\treturn { text: output };\n\t}\n\tif (typeof output === \"object\" && output !== null && \"text\" in output && typeof (output as any).text === \"string\") {\n\t\tconst obj = output as any;\n\t\treturn {\n\t\t\ttext: obj.text,\n\t\t\t...(obj.image && { image: obj.image }),\n\t\t};\n\t}\n\treturn { text: JSON.stringify(output) };\n}\n\nexport interface InteractiveModeOptions {\n\tinitialMessage?: string;\n\tinitialMessages?: string[];\n\tskills?: Skill[];\n\tcontextFiles?: ContextFile[];\n\tprompts?: PromptTemplate[];\n\tverbose?: boolean;\n\tprovider: string;\n\tmodelId: string;\n\tauthStorage?: AuthStorage;\n\t/** Settings manager for persisting user preferences (provider, model, compaction). */\n\tsettingsManager?: SettingsManager;\n\t/** Path to the `fd` binary for @ file autocomplete, or undefined if unavailable. */\n\tfdPath?: string;\n\t/** Called when the user switches model via Ctrl+L. Returns a new agent for the new model. */\n\tonModelChange?: (provider: string, modelId: string) => Promise<CodingAgent>;\n\t/** Context window size for the model. Defaults to 200k. */\n\tcontextWindow?: number;\n\t/** Directory where session files are stored. Required for /resume. */\n\tsessionDir?: string;\n\t/** Agent config used to recreate agents when resuming sessions. */\n\tagentConfig?: CodingAgentConfig;\n\t/** When true, show the session picker immediately on startup. */\n\tresumeOnStart?: boolean;\n}\n\n/**\n * Run the interactive TUI mode with streaming output.\n */\nexport async function runInteractiveMode(agent: CodingAgent, options: InteractiveModeOptions): Promise<void> {\n\tconst mode = new InteractiveMode(agent, options);\n\tawait mode.run();\n}\n\n// ============================================================================\n// InteractiveMode class\n// ============================================================================\n\nclass InteractiveMode {\n\tprivate agent: CodingAgent;\n\tprivate options: InteractiveModeOptions;\n\tprivate currentProvider: string;\n\tprivate currentModelId: string;\n\n\tprivate ui!: TUI;\n\tprivate headerContainer!: Container;\n\tprivate chatContainer!: Container;\n\tprivate pendingContainer!: Container;\n\tprivate pendingMessagesContainer!: Container;\n\tprivate editorContainer!: Container;\n\tprivate editor!: Editor;\n\tprivate editorTheme!: import(\"@mariozechner/pi-tui\").EditorTheme;\n\n\t// Message queues\n\tprivate steeringMessages: string[] = [];\n\tprivate followUpMessages: string[] = [];\n\tprivate isStreaming = false;\n\n\t// Inline bash state\n\tprivate isBashMode = false;\n\tprivate isBashRunning = false;\n\tprivate bashAbortController: AbortController | null = null;\n\tprivate bashComponent: import(\"./components/bash-execution.js\").BashExecutionComponent | undefined = undefined;\n\tprivate footer!: FooterComponent;\n\n\t// Loading animation during agent processing\n\tprivate loadingAnimation: Loader | undefined = undefined;\n\n\t// Streaming state\n\tprivate streamingComponent: AssistantMessageComponent | undefined = undefined;\n\tprivate streamingText = \"\";\n\tprivate hadToolResults = false;\n\n\t// Tool execution tracking: toolCallId → component\n\tprivate pendingTools = new Map<string, ToolExecutionComponent>();\n\n\t// Tool output expansion state\n\tprivate toolOutputExpanded = false;\n\n\t// Callback for resolving user input promise\n\tprivate onInputCallback?: (text: string) => void;\n\n\t// Pending clipboard images to attach to the next message\n\tprivate pendingImages: ClipboardImage[] = [];\n\n\t// Compaction state\n\tprivate contextWindow: number;\n\tprivate compactionSettings: CompactionSettings;\n\tprivate autoCompaction = true;\n\tprivate isCompacting = false;\n\tprivate compactionLoader: Loader | null = null;\n\n\tconstructor(agent: CodingAgent, options: InteractiveModeOptions) {\n\t\tthis.agent = agent;\n\t\tthis.options = options;\n\t\tthis.currentProvider = options.provider;\n\t\tthis.currentModelId = options.modelId;\n\t\tthis.contextWindow = options.contextWindow ?? DEFAULT_CONTEXT_WINDOW;\n\n\t\t// Initialize compaction settings from persisted settings if available\n\t\tconst savedCompaction = options.settingsManager?.getCompaction();\n\t\tthis.compactionSettings = {\n\t\t\t...DEFAULT_COMPACTION_SETTINGS,\n\t\t\t...(savedCompaction?.reserveTokens !== undefined && { reserveTokens: savedCompaction.reserveTokens }),\n\t\t\t...(savedCompaction?.keepRecentTokens !== undefined && { keepRecentTokens: savedCompaction.keepRecentTokens }),\n\t\t};\n\t\tthis.autoCompaction = options.settingsManager?.getCompactionEnabled() ?? true;\n\n\t\tthis.configureAgentCompaction();\n\t}\n\n\tasync run(): Promise<void> {\n\t\tthis.initUI();\n\t\tthis.updateFooterTokens();\n\n\t\t// Show session picker immediately if --resume was passed\n\t\tif (this.options.resumeOnStart) {\n\t\t\tawait this.handleResume();\n\t\t}\n\n\t\t// Process initial messages\n\t\tconst { initialMessage, initialMessages = [] } = this.options;\n\n\t\tconst allInitial: string[] = [];\n\t\tif (initialMessage) allInitial.push(initialMessage);\n\t\tallInitial.push(...initialMessages);\n\n\t\tfor (const msg of allInitial) {\n\t\t\tthis.chatContainer.addChild(new UserMessageComponent(msg, getMarkdownTheme()));\n\t\t\tthis.ui.requestRender();\n\t\t\tawait this.streamPrompt(msg);\n\t\t}\n\n\t\t// Main interactive loop\n\t\twhile (true) {\n\t\t\tconst userInput = await this.getUserInput();\n\t\t\tawait this.handleUserInput(userInput);\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// UI Setup\n\t// ========================================================================\n\n\tprivate initUI(): void {\n\t\tconst { provider, modelId, skills = [], contextFiles = [], prompts = [], verbose } = this.options;\n\n\t\tthis.ui = new TUI(new ProcessTerminal());\n\n\t\t// Header\n\t\tthis.headerContainer = new Container();\n\t\tconst logo = chalk.bold(\"epi\") + chalk.dim(` - ${provider}/${modelId}`);\n\n\t\tconst hints = [\n\t\t\t`${chalk.dim(\"Escape\")} to abort`,\n\t\t\t`${chalk.dim(\"!\")} inline bash`,\n\t\t\t`${chalk.dim(\"Alt+Enter\")} follow-up while streaming`,\n\t\t\t`${chalk.dim(\"Ctrl+C\")} to exit`,\n\t\t\t`${chalk.dim(\"Ctrl+E\")} to expand tools`,\n\t\t\t`${chalk.dim(\"Ctrl+L\")} to switch model`,\n\t\t\t`${chalk.dim(\"Ctrl+V\")} to paste image`,\n\t\t\t`${chalk.dim(\"↑/↓\")} to browse history`,\n\t\t\t`${chalk.dim(\"@\")} for file references`,\n\t\t\t`${chalk.dim(\"/\")} for commands`,\n\t\t].join(\"\\n\");\n\n\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\tthis.headerContainer.addChild(new Text(`${logo}\\n${hints}`, 1, 0));\n\t\tthis.headerContainer.addChild(new Spacer(1));\n\n\t\tif (verbose && this.agent.sessionManager?.getSessionFile()) {\n\t\t\tthis.headerContainer.addChild(\n\t\t\t\tnew Text(chalk.dim(`Session: ${this.agent.sessionManager.getSessionFile()}`), 1, 0),\n\t\t\t);\n\t\t}\n\n\t\t// Show loaded context, skills, and prompts at startup\n\t\tthis.showLoadedResources(contextFiles, skills, prompts);\n\n\t\t// Chat area\n\t\tthis.chatContainer = new Container();\n\n\t\t// Pending messages (loading animations, status)\n\t\tthis.pendingContainer = new Container();\n\n\t\t// Pending steering/follow-up messages\n\t\tthis.pendingMessagesContainer = new Container();\n\n\t\t// Editor with slash command autocomplete\n\t\tthis.editorTheme = getEditorTheme();\n\t\tthis.editor = new Editor(this.ui, this.editorTheme);\n\t\tthis.editor.setAutocompleteProvider(this.buildAutocompleteProvider());\n\t\tthis.editorContainer = new Container();\n\t\tthis.editorContainer.addChild(this.editor);\n\n\t\t// Footer\n\t\tthis.footer = new FooterComponent(this.currentProvider, this.currentModelId);\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\n\t\t// Assemble layout\n\t\tthis.ui.addChild(this.headerContainer);\n\t\tthis.ui.addChild(this.chatContainer);\n\t\tthis.ui.addChild(this.pendingMessagesContainer);\n\t\tthis.ui.addChild(this.pendingContainer);\n\t\tthis.ui.addChild(this.editorContainer);\n\t\tthis.ui.addChild(this.footer);\n\n\t\tthis.ui.setFocus(this.editor);\n\t\tthis.setupKeyHandlers();\n\n\t\tthis.ui.start();\n\t}\n\n\t// ========================================================================\n\t// Key Handlers\n\t// ========================================================================\n\n\tprivate setupKeyHandlers(): void {\n\t\tthis.editor.onChange = (text: string) => {\n\t\t\tconst wasBashMode = this.isBashMode;\n\t\t\tthis.isBashMode = text.trimStart().startsWith(\"!\");\n\t\t\tif (wasBashMode !== this.isBashMode) {\n\t\t\t\tthis.updateEditorBorderColor();\n\t\t\t}\n\t\t};\n\t\tthis.editor.onSubmit = (text: string) => {\n\t\t\ttext = text.trim();\n\t\t\tif (!text) return;\n\n\t\t\t// If agent is streaming, Enter becomes a steering message\n\t\t\tif (this.isStreaming) {\n\t\t\t\tthis.agent.steer({ role: \"user\", content: [{ type: \"text\", text }] });\n\t\t\t\tthis.steeringMessages.push(text);\n\t\t\t\tthis.editor.setText(\"\");\n\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.editor.addToHistory(text);\n\t\t\tthis.editor.setText(\"\");\n\n\t\t\tif (this.onInputCallback) {\n\t\t\t\tthis.onInputCallback(text);\n\t\t\t}\n\t\t};\n\n\t\tconst origHandleInput = this.editor.handleInput.bind(this.editor);\n\t\tthis.editor.handleInput = (data: string) => {\n\t\t\t// Escape: abort if agent is running or compacting\n\t\t\tif (matchesKey(data, Key.escape)) {\n\t\t\t\tif (this.isBashRunning && this.bashAbortController) {\n\t\t\t\t\tthis.bashAbortController.abort();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.isCompacting) {\n\t\t\t\t\tthis.agent.abort();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.loadingAnimation) {\n\t\t\t\t\tthis.agent.abort();\n\t\t\t\t\tthis.stopLoading();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Ctrl+C: exit\n\t\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\t\tthis.shutdown();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+D: exit if editor is empty\n\t\t\tif (matchesKey(data, Key.ctrl(\"d\"))) {\n\t\t\t\tif (this.editor.getText().length === 0) {\n\t\t\t\t\tthis.shutdown();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Ctrl+E: toggle tool output expansion\n\t\t\tif (matchesKey(data, Key.ctrl(\"e\"))) {\n\t\t\t\tthis.toggleToolExpansion();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+L: select model\n\t\t\tif (matchesKey(data, Key.ctrl(\"l\"))) {\n\t\t\t\tthis.handleModelSelect();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+V: paste image from clipboard\n\t\t\tif (matchesKey(data, Key.ctrl(\"v\"))) {\n\t\t\t\tthis.handleClipboardImagePaste();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Alt+Enter (Option+Enter on Mac): follow-up while streaming (or submit normally when idle)\n\t\t\tif (matchesKey(data, Key.alt(\"enter\"))) {\n\t\t\t\tconst text = this.editor.getText().trim();\n\t\t\t\tif (!text) return;\n\n\t\t\t\tif (this.isStreaming) {\n\t\t\t\t\tthis.followUpMessages.push(text);\n\t\t\t\t\tthis.editor.setText(\"\");\n\t\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Not streaming: treat like regular submit\n\t\t\t\tthis.editor.onSubmit?.(text);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Alt+Up (Option+Up on Mac): dequeue all queued messages back into the editor\n\t\t\tif (matchesKey(data, Key.alt(\"up\"))) {\n\t\t\t\tconst restored = this.clearAllQueues();\n\t\t\t\tif (restored.length > 0) {\n\t\t\t\t\tthis.editor.setText(restored.join(\"\\n\\n\"));\n\t\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\torigHandleInput(data);\n\t\t};\n\t}\n\n\t// ========================================================================\n\t// User Input\n\t// ========================================================================\n\n\tprivate getUserInput(): Promise<string> {\n\t\treturn new Promise((resolve) => {\n\t\t\tthis.onInputCallback = (text: string) => {\n\t\t\t\tthis.onInputCallback = undefined;\n\t\t\t\tresolve(text);\n\t\t\t};\n\t\t});\n\t}\n\n\tprivate async handleUserInput(input: string): Promise<void> {\n\t\t// Handle commands\n\t\tif (input === \"/help\") {\n\t\t\tthis.showHelp();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/skills\") {\n\t\t\tthis.showSkills();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/quit\" || input === \"/exit\") {\n\t\t\tthis.shutdown();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/model\") {\n\t\t\tawait this.handleModelSelect();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/login\") {\n\t\t\tawait this.handleLogin();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/logout\") {\n\t\t\tawait this.handleLogout();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/compact\" || input.startsWith(\"/compact \")) {\n\t\t\tconst customInstructions = input.startsWith(\"/compact \") ? input.slice(9).trim() : undefined;\n\t\t\tawait this.handleCompactCommand(customInstructions);\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/auto-compact\") {\n\t\t\tthis.toggleAutoCompaction();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/resume\") {\n\t\t\tawait this.handleResume();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input.startsWith(\"/skill:\")) {\n\t\t\tconst skillName = input.slice(\"/skill:\".length).trim();\n\t\t\tawait this.handleSkillInvocation(skillName);\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle bash commands (! for normal, !! for excluded from context)\n\t\tconst bashParsed = parseBashInput(input);\n\t\tif (bashParsed) {\n\t\t\tif (this.isBashRunning) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"A bash command is already running. Press Escape to cancel it first.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait this.handleBashCommand(bashParsed.command, bashParsed.excludeFromContext);\n\t\t\tthis.isBashMode = false;\n\t\t\tthis.updateEditorBorderColor();\n\t\t\treturn;\n\t\t}\n\n\t\t// Try expanding prompt templates\n\t\tconst { prompts = [] } = this.options;\n\t\tconst expanded = expandPromptTemplate(input, prompts);\n\n\t\t// Capture and clear pending images\n\t\tconst images = this.pendingImages.length > 0 ? [...this.pendingImages] : undefined;\n\t\tthis.pendingImages = [];\n\n\t\t// Regular message (use expanded text if a prompt template was matched)\n\t\tconst imageLabel = images ? chalk.dim(` (${images.length} image${images.length > 1 ? \"s\" : \"\"})`) : \"\";\n\t\tthis.chatContainer.addChild(new UserMessageComponent(`${expanded}${imageLabel}`, getMarkdownTheme()));\n\t\tthis.ui.requestRender();\n\t\tawait this.streamPrompt(expanded, images);\n\t}\n\n\tprivate async handleBashCommand(command: string, excludeFromContext: boolean): Promise<void> {\n\t\tthis.bashAbortController = new AbortController();\n\t\tthis.isBashRunning = true;\n\n\t\tthis.bashComponent = new BashExecutionComponent(command, this.ui, excludeFromContext);\n\t\tif (this.toolOutputExpanded) {\n\t\t\tthis.bashComponent.setExpanded(true);\n\t\t}\n\t\tthis.chatContainer.addChild(this.bashComponent);\n\t\tthis.ui.requestRender();\n\n\t\ttry {\n\t\t\tconst result = await executeBashCommand(command, {\n\t\t\t\tsignal: this.bashAbortController.signal,\n\t\t\t\tonChunk: (chunk) => {\n\t\t\t\t\tthis.bashComponent?.appendOutput(chunk);\n\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.bashComponent.setComplete(result.exitCode, result.cancelled, result.truncated, result.fullOutputPath);\n\t\t\tthis.ui.requestRender();\n\n\t\t\tif (!excludeFromContext) {\n\t\t\t\tconst msgText = `Ran \\`${command}\\`\\n\\n\\`\\`\\`\\n${result.output.trimEnd()}\\n\\`\\`\\``;\n\t\t\t\tconst userMsg: ModelMessage = { role: \"user\", content: [{ type: \"text\", text: msgText }] };\n\t\t\t\tthis.agent.setMessages([...this.agent.messages, userMsg]);\n\t\t\t\tthis.agent.sessionManager?.appendMessage(userMsg);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.isBashRunning = false;\n\t\t\tthis.bashAbortController = null;\n\t\t\tthis.bashComponent = undefined;\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Autocomplete\n\t// ========================================================================\n\n\tprivate buildAutocompleteProvider(): CombinedAutocompleteProvider {\n\t\tconst { skills = [], prompts = [], fdPath } = this.options;\n\n\t\tconst commands: SlashCommand[] = [\n\t\t\t{ name: \"help\", description: \"Show available commands\" },\n\t\t\t{ name: \"resume\", description: \"Resume a previous session\" },\n\t\t\t{ name: \"compact\", description: \"Manually compact the session context\" },\n\t\t\t{ name: \"auto-compact\", description: \"Toggle automatic context compaction\" },\n\t\t\t{ name: \"login\", description: \"Login to an OAuth provider\" },\n\t\t\t{ name: \"logout\", description: \"Logout from an OAuth provider\" },\n\t\t\t{ name: \"skills\", description: \"List loaded skills\" },\n\t\t\t{ name: \"model\", description: \"Switch model (Ctrl+L)\" },\n\t\t\t{ name: \"quit\", description: \"Exit the CLI\" },\n\t\t\t{ name: \"exit\", description: \"Exit the CLI\" },\n\t\t];\n\n\t\tfor (const skill of skills) {\n\t\t\tcommands.push({\n\t\t\t\tname: `skill:${skill.name}`,\n\t\t\t\tdescription: skill.description,\n\t\t\t});\n\t\t}\n\n\t\t// Add prompt templates as slash commands\n\t\tfor (const prompt of prompts) {\n\t\t\tcommands.push({\n\t\t\t\tname: prompt.name,\n\t\t\t\tdescription: prompt.description,\n\t\t\t});\n\t\t}\n\n\t\treturn new CombinedAutocompleteProvider(commands, process.cwd(), fdPath ?? null);\n\t}\n\n\t// ========================================================================\n\t// Model Selection\n\t// ========================================================================\n\n\tprivate async handleModelSelect(): Promise<void> {\n\t\tconst latestModels = getLatestModels();\n\t\tconst modelOptions: { provider: string; modelId: string; label: string }[] = [];\n\t\tfor (const [provider, models] of Object.entries(latestModels)) {\n\t\t\tfor (const modelId of models) {\n\t\t\t\tmodelOptions.push({ provider, modelId, label: `${provider}/${modelId}` });\n\t\t\t}\n\t\t}\n\n\t\tconst items: SelectItem[] = modelOptions.map((m) => {\n\t\t\tconst current = m.provider === this.currentProvider && m.modelId === this.currentModelId;\n\t\t\treturn {\n\t\t\t\tvalue: `${m.provider}/${m.modelId}`,\n\t\t\t\tlabel: current ? `${m.label} (current)` : m.label,\n\t\t\t};\n\t\t});\n\n\t\tconst selected = await this.showSelectList(\"Switch model\", items);\n\t\tif (!selected) return;\n\n\t\tconst [newProvider, ...modelParts] = selected.split(\"/\");\n\t\tconst newModelId = modelParts.join(\"/\");\n\n\t\tif (newProvider === this.currentProvider && newModelId === this.currentModelId) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.showStatus(chalk.dim(`Switching to ${newProvider}/${newModelId}...`));\n\n\t\tif (!this.options.onModelChange) {\n\t\t\tthis.showStatus(chalk.yellow(\"Model switching is not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst newAgent = await this.options.onModelChange(newProvider, newModelId);\n\t\t\t// Preserve conversation history\n\t\t\tnewAgent.setMessages([...this.agent.messages]);\n\t\t\tthis.agent = newAgent;\n\t\t\tthis.configureAgentCompaction();\n\n\t\t\tthis.currentProvider = newProvider;\n\t\t\tthis.currentModelId = newModelId;\n\t\t\tthis.updateFooter();\n\n\t\t\t// Persist the choice for next startup\n\t\t\tthis.options.settingsManager?.setDefaults(newProvider, newModelId);\n\n\t\t\tthis.showStatus(chalk.green(`Switched to ${newProvider}/${newModelId}`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tthis.showStatus(chalk.red(`Failed to switch model: ${msg}`));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Streaming\n\t// ========================================================================\n\n\tprivate async streamPrompt(prompt: string, images?: ClipboardImage[]): Promise<void> {\n\t\tthis.isStreaming = true;\n\t\tthis.updatePendingMessagesDisplay();\n\n\t\t// Build image parts from clipboard images\n\t\tconst imageParts: ImagePart[] = (images ?? []).map((img) => ({\n\t\t\ttype: \"image\" as const,\n\t\t\timage: Buffer.from(img.bytes).toString(\"base64\"),\n\t\t\tmediaType: img.mimeType,\n\t\t}));\n\n\t\t// Start loading animation\n\t\tthis.startLoading();\n\n\t\tthis.streamingComponent = undefined;\n\t\tthis.streamingText = \"\";\n\t\tthis.hadToolResults = false;\n\n\t\tlet errorDisplayed = false;\n\t\tlet streamFailed = false;\n\t\ttry {\n\t\t\tconst result =\n\t\t\t\timageParts.length > 0\n\t\t\t\t\t? await this.agent.stream({\n\t\t\t\t\t\t\tmessages: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\trole: \"user\" as const,\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: prompt }, ...imageParts],\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: await this.agent.stream({ prompt });\n\n\t\t\tfor await (const part of result.fullStream) {\n\t\t\t\tswitch (part.type) {\n\t\t\t\t\tcase \"text-delta\":\n\t\t\t\t\t\t// After tool results, or for the very first text part, start a new assistant message component\n\t\t\t\t\t\t// so each agent step gets its own message bubble\n\t\t\t\t\t\tif (this.hadToolResults || !this.streamingComponent) {\n\t\t\t\t\t\t\tthis.streamingComponent = new AssistantMessageComponent(getMarkdownTheme());\n\t\t\t\t\t\t\tthis.streamingText = \"\";\n\t\t\t\t\t\t\tthis.hadToolResults = false;\n\t\t\t\t\t\t\tthis.chatContainer.addChild(this.streamingComponent);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.streamingText += part.text;\n\t\t\t\t\t\tthis.streamingComponent!.updateText(this.streamingText);\n\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"tool-call\": {\n\t\t\t\t\t\tconst args =\n\t\t\t\t\t\t\ttypeof part.input === \"object\" && part.input !== null\n\t\t\t\t\t\t\t\t? (part.input as Record<string, unknown>)\n\t\t\t\t\t\t\t\t: {};\n\t\t\t\t\t\tconst toolComponent = new ToolExecutionComponent(part.toolName, args);\n\n\t\t\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\t\t\ttoolComponent.setExpanded(true);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.pendingTools.set(part.toolCallId, toolComponent);\n\t\t\t\t\t\tthis.chatContainer.addChild(toolComponent);\n\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"tool-result\": {\n\t\t\t\t\t\tconst toolComponent = this.pendingTools.get(part.toolCallId);\n\t\t\t\t\t\tif (toolComponent) {\n\t\t\t\t\t\t\tconst toolOutput = extractToolOutput(part.output);\n\t\t\t\t\t\t\ttoolComponent.updateResult(toolOutput, /* isError */ false, /* isPartial */ false);\n\t\t\t\t\t\t\tthis.pendingTools.delete(part.toolCallId);\n\t\t\t\t\t\t\tthis.hadToolResults = true;\n\t\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"error\": {\n\t\t\t\t\t\tconst errorMessage = formatAIError(part.error);\n\t\t\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\t\t\tthis.streamingComponent.setError(errorMessage);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.showStatus(chalk.red(`Error: ${errorMessage}`));\n\t\t\t\t\t\t}\n\t\t\t\t\t\terrorDisplayed = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (errorDisplayed) return;\n\n\t\t\t// Wait for stream to complete — the agent auto-updates messages and persists to session\n\t\t\tawait result.response;\n\n\t\t\t// Update footer token stats\n\t\t\tthis.updateFooterTokens();\n\t\t} catch (error) {\n\t\t\tif (errorDisplayed) {\n\t\t\t\tstreamFailed = true;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tstreamFailed = true;\n\t\t\tif ((error as Error).name === \"AbortError\") {\n\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\tthis.streamingComponent.setAborted();\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.dim(\"[aborted]\"));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst msg =\n\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t? error.message\n\t\t\t\t\t\t: typeof error === \"object\" && error !== null\n\t\t\t\t\t\t\t? JSON.stringify(error)\n\t\t\t\t\t\t\t: String(error);\n\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\tthis.streamingComponent.setError(msg);\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.red(`Error: ${msg}`));\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.stopLoading();\n\t\t\tthis.streamingComponent = undefined;\n\t\t\tthis.streamingText = \"\";\n\t\t\tthis.hadToolResults = false;\n\t\t\tthis.pendingTools.clear();\n\t\t\tthis.isStreaming = false;\n\t\t\tthis.steeringMessages = [];\n\t\t\tif (streamFailed) {\n\t\t\t\tthis.followUpMessages = [];\n\t\t\t}\n\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\tthis.ui.requestRender();\n\t\t}\n\n\t\t// Process queued follow-ups (skipped if stream failed/aborted)\n\t\twhile (this.followUpMessages.length > 0) {\n\t\t\tconst next = this.followUpMessages.shift();\n\t\t\tif (!next) break;\n\n\t\t\tthis.updatePendingMessagesDisplay();\n\n\t\t\tthis.chatContainer.addChild(new UserMessageComponent(next, getMarkdownTheme()));\n\t\t\tthis.ui.requestRender();\n\t\t\tawait this.streamPrompt(next);\n\t\t}\n\t}\n\n\tprivate updatePendingMessagesDisplay(): void {\n\t\tthis.pendingMessagesContainer.clear();\n\n\t\t// If no agent is running, clear pending messages (they've been consumed)\n\t\tif (!this.isStreaming && this.followUpMessages.length === 0 && this.steeringMessages.length === 0) {\n\t\t\tthis.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines = formatPendingMessages(this.steeringMessages, this.followUpMessages);\n\n\t\tif (lines.length === 0) {\n\t\t\tthis.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.pendingMessagesContainer.addChild(new Spacer(1));\n\t\tthis.pendingMessagesContainer.addChild(new Text(lines.map((l) => chalk.dim(l)).join(\"\\n\"), 1, 0));\n\t\tthis.pendingMessagesContainer.addChild(new Text(chalk.dim(\"↳ Alt+Up to edit queued messages\"), 1, 0));\n\t\tthis.pendingMessagesContainer.addChild(new Spacer(1));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate clearAllQueues(): string[] {\n\t\tconst restored = [...this.steeringMessages, ...this.followUpMessages];\n\t\tthis.steeringMessages = [];\n\t\tthis.followUpMessages = [];\n\t\treturn restored;\n\t}\n\n\t// ========================================================================\n\t// Loading Animation\n\t// ========================================================================\n\n\tprivate startLoading(): void {\n\t\tthis.stopLoading();\n\t\tthis.loadingAnimation = new Loader(\n\t\t\tthis.ui,\n\t\t\t(s: string) => chalk.cyan(s),\n\t\t\t(s: string) => chalk.dim(s),\n\t\t\t\"Working...\",\n\t\t);\n\t\tthis.loadingAnimation.start();\n\t\tthis.pendingContainer.addChild(new Spacer(1));\n\t\tthis.pendingContainer.addChild(this.loadingAnimation);\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate stopLoading(): void {\n\t\tif (this.loadingAnimation) {\n\t\t\tthis.loadingAnimation.stop();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.loadingAnimation = undefined;\n\t\t\tthis.ui.requestRender();\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Tool Expansion\n\t// ========================================================================\n\n\tprivate toggleToolExpansion(): void {\n\t\tthis.toolOutputExpanded = !this.toolOutputExpanded;\n\n\t\t// Update all tool components and compaction components in the chat\n\t\tfor (const child of this.chatContainer.children) {\n\t\t\tif (child instanceof ToolExecutionComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t} else if (child instanceof CompactionSummaryComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t} else if (child instanceof BashExecutionComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t}\n\t\t}\n\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Clipboard Image Paste\n\t// ========================================================================\n\n\tprivate handleClipboardImagePaste(): void {\n\t\ttry {\n\t\t\tconst image = readClipboardImage();\n\t\t\tif (!image) return;\n\t\t\tthis.pendingImages.push(image);\n\t\t\tconst ext = extensionForImageMimeType(image.mimeType) ?? \"image\";\n\t\t\tconst label = `[image ${this.pendingImages.length}: ${ext}]`;\n\t\t\tthis.editor.insertTextAtCursor(label);\n\t\t\tthis.ui.requestRender();\n\t\t} catch {\n\t\t\t// Silently ignore clipboard errors (may not have permission, etc.)\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Compaction\n\t// ========================================================================\n\n\t/**\n\t * Handle the /compact command.\n\t */\n\tprivate async handleCompactCommand(_customInstructions?: string): Promise<void> {\n\t\ttry {\n\t\t\tconst result = await this.agent.compact();\n\t\t\tif (!result) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (already compacted or insufficient history).\"));\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Compaction callbacks already report errors in interactive mode.\n\t\t\t// Catch here to avoid unwinding the main TUI loop on abort/provider errors.\n\t\t\tif (!this.agent.compaction?.onCompactionError) {\n\t\t\t\tconst compactionError = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tif (compactionError.name === \"AbortError\" || compactionError.message === \"Compaction cancelled\") {\n\t\t\t\t\tthis.showStatus(chalk.dim(\"Compaction cancelled.\"));\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.red(`Compaction failed: ${compactionError.message}`));\n\t\t\t\t}\n\t\t\t\tthis.ui.requestRender();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Toggle auto-compaction on/off.\n\t */\n\tprivate toggleAutoCompaction(): void {\n\t\tthis.autoCompaction = !this.autoCompaction;\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.options.settingsManager?.setCompactionEnabled(this.autoCompaction);\n\t\tif (this.agent.compaction) {\n\t\t\tthis.agent.setCompaction({\n\t\t\t\t...this.agent.compaction,\n\t\t\t\tmode: this.autoCompaction ? \"auto\" : \"manual\",\n\t\t\t});\n\t\t}\n\t\tthis.showStatus(\n\t\t\tthis.autoCompaction ? chalk.green(\"Auto-compaction enabled\") : chalk.dim(\"Auto-compaction disabled\"),\n\t\t);\n\t\tthis.ui.requestRender();\n\t}\n\n\t/**\n\t * Configure agent-level compaction and callbacks for UI updates.\n\t */\n\tprivate configureAgentCompaction(): void {\n\t\tconst mode = this.autoCompaction ? \"auto\" : \"manual\";\n\t\tthis.agent.setCompaction({\n\t\t\tcontextWindow: this.contextWindow,\n\t\t\tmode,\n\t\t\tsettings: {\n\t\t\t\treserveTokens: this.compactionSettings.reserveTokens,\n\t\t\t\tkeepRecentTokens: this.compactionSettings.keepRecentTokens,\n\t\t\t},\n\t\t\tonCompactionStart: () => {\n\t\t\t\tthis.isCompacting = true;\n\t\t\t\tthis.compactionLoader?.stop();\n\t\t\t\tthis.compactionLoader = new Loader(\n\t\t\t\t\tthis.ui,\n\t\t\t\t\t(s: string) => chalk.cyan(s),\n\t\t\t\t\t(s: string) => chalk.dim(s),\n\t\t\t\t\tthis.autoCompaction\n\t\t\t\t\t\t? \"Auto-compacting context... (Escape to cancel)\"\n\t\t\t\t\t\t: \"Compacting context... (Escape to cancel)\",\n\t\t\t\t);\n\t\t\t\tthis.compactionLoader.start();\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.pendingContainer.addChild(new Spacer(1));\n\t\t\t\tthis.pendingContainer.addChild(this.compactionLoader);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t},\n\t\t\tonCompactionComplete: (result: CompactionResult) => {\n\t\t\t\tthis.compactionLoader?.stop();\n\t\t\t\tthis.compactionLoader = null;\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.isCompacting = false;\n\n\t\t\t\tthis.rebuildChatFromSession();\n\t\t\t\tconst summaryComponent = new CompactionSummaryComponent(result.tokensBefore, result.summary);\n\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\tsummaryComponent.setExpanded(true);\n\t\t\t\t}\n\t\t\t\tthis.chatContainer.addChild(summaryComponent);\n\n\t\t\t\tthis.updateFooterTokens();\n\t\t\t\tif (this.options.verbose) {\n\t\t\t\t\tconst tokensAfter = estimateContextTokens([...this.agent.messages]);\n\t\t\t\t\tthis.showStatus(\n\t\t\t\t\t\tchalk.dim(\n\t\t\t\t\t\t\t`Compacted: ${result.tokensBefore.toLocaleString()} -> ${tokensAfter.toLocaleString()} tokens`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthis.ui.requestRender();\n\t\t\t},\n\t\t\tonCompactionError: (error: Error) => {\n\t\t\t\tthis.compactionLoader?.stop();\n\t\t\t\tthis.compactionLoader = null;\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.isCompacting = false;\n\n\t\t\t\tif (error.name === \"AbortError\" || error.message === \"Compaction cancelled\") {\n\t\t\t\t\tthis.showStatus(chalk.dim(\"Compaction cancelled.\"));\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.red(`Compaction failed: ${error.message}`));\n\t\t\t\t}\n\t\t\t\tthis.ui.requestRender();\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Rebuild the chat UI from session context after compaction.\n\t */\n\tprivate rebuildChatFromSession(): void {\n\t\tthis.chatContainer.clear();\n\n\t\tconst messages = this.agent.messages;\n\t\tfor (const msg of messages) {\n\t\t\tif (msg.role === \"user\") {\n\t\t\t\t// Check if this is a compaction summary\n\t\t\t\tconst content = msg.content;\n\t\t\t\tif (Array.isArray(content) && content.length > 0) {\n\t\t\t\t\tconst textBlock = content[0] as { type: string; text?: string };\n\t\t\t\t\tif (textBlock.type === \"text\" && textBlock.text?.startsWith('<summary type=\"compaction\"')) {\n\t\t\t\t\t\t// Skip compaction summaries in rebuild (they are injected by buildSessionContext)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (textBlock.type === \"text\" && textBlock.text?.startsWith('<summary type=\"branch\"')) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst text = extractTextFromMessage(msg);\n\t\t\t\tif (text) {\n\t\t\t\t\tthis.chatContainer.addChild(new UserMessageComponent(text, getMarkdownTheme()));\n\t\t\t\t}\n\t\t\t} else if (msg.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = msg as import(\"edge-pi\").AssistantModelMessage;\n\t\t\t\tconst textParts: string[] = [];\n\t\t\t\tfor (const block of assistantMsg.content) {\n\t\t\t\t\tconst b = block as {\n\t\t\t\t\t\ttype: string;\n\t\t\t\t\t\ttext?: string;\n\t\t\t\t\t\ttoolName?: string;\n\t\t\t\t\t\tinput?: unknown;\n\t\t\t\t\t\ttoolCallId?: string;\n\t\t\t\t\t};\n\t\t\t\t\tif (b.type === \"text\" && b.text) {\n\t\t\t\t\t\ttextParts.push(b.text);\n\t\t\t\t\t} else if (b.type === \"tool-call\" && b.toolName) {\n\t\t\t\t\t\tconst args =\n\t\t\t\t\t\t\ttypeof b.input === \"object\" && b.input !== null ? (b.input as Record<string, unknown>) : {};\n\t\t\t\t\t\tconst toolComp = new ToolExecutionComponent(b.toolName, args);\n\t\t\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\t\t\ttoolComp.setExpanded(true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Mark as completed (we don't have the result here, just show collapsed)\n\t\t\t\t\t\ttoolComp.updateResult({ text: \"(from history)\" }, false, false);\n\t\t\t\t\t\tthis.chatContainer.addChild(toolComp);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (textParts.length > 0) {\n\t\t\t\t\tconst comp = new AssistantMessageComponent(getMarkdownTheme());\n\t\t\t\t\tcomp.updateText(textParts.join(\"\"));\n\t\t\t\t\tthis.chatContainer.addChild(comp);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Skip tool messages in UI rebuild - they are consumed by tool-call components\n\t\t}\n\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Footer Token Tracking\n\t// ========================================================================\n\n\t/**\n\t * Update the footer with current token count information.\n\t */\n\tprivate updateFooterTokens(): void {\n\t\tconst contextTokens = estimateContextTokens([...this.agent.messages]);\n\t\tthis.footer.setTokenInfo(contextTokens, this.contextWindow);\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.ui?.requestRender();\n\t}\n\n\t/**\n\t * Check if the current provider is using an OAuth subscription credential.\n\t */\n\tprivate isSubscriptionProvider(): boolean {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) return false;\n\t\tconst cred = authStorage.get(this.currentProvider);\n\t\treturn cred?.type === \"oauth\";\n\t}\n\n\t/**\n\t * Replace the footer component and update token info.\n\t */\n\tprivate updateFooter(): void {\n\t\tthis.footer = new FooterComponent(this.currentProvider, this.currentModelId);\n\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\t\tthis.updateFooterTokens();\n\n\t\t// Replace footer in UI\n\t\tconst children = this.ui.children;\n\t\tchildren[children.length - 1] = this.footer;\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Startup Resource Display\n\t// ========================================================================\n\n\tprivate formatDisplayPath(p: string): string {\n\t\tconst home = process.env.HOME || process.env.USERPROFILE || \"\";\n\t\tif (home && p.startsWith(home)) {\n\t\t\treturn `~${p.slice(home.length)}`;\n\t\t}\n\t\treturn p;\n\t}\n\n\tprivate showLoadedResources(contextFiles: ContextFile[], skills: Skill[], prompts: PromptTemplate[]): void {\n\t\tconst sectionHeader = (name: string) => chalk.cyan(`[${name}]`);\n\n\t\tif (contextFiles.length > 0) {\n\t\t\tconst contextList = contextFiles.map((f) => chalk.dim(` ${this.formatDisplayPath(f.path)}`)).join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Context\")}\\n${contextList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (skills.length > 0) {\n\t\t\tconst skillList = skills.map((s) => chalk.dim(` ${this.formatDisplayPath(s.filePath)}`)).join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Skills\")}\\n${skillList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (prompts.length > 0) {\n\t\t\tconst promptList = prompts\n\t\t\t\t.map((p) => {\n\t\t\t\t\tconst sourceLabel = chalk.cyan(p.source);\n\t\t\t\t\treturn chalk.dim(` ${sourceLabel} /${p.name}`);\n\t\t\t\t})\n\t\t\t\t.join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Prompts\")}\\n${promptList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Commands\n\t// ========================================================================\n\n\tprivate showHelp(): void {\n\t\tconst helpText = [\n\t\t\tchalk.bold(\"Commands:\"),\n\t\t\t\" !<command> Run inline bash and include output in context\",\n\t\t\t\" !!<command> Run inline bash but exclude output from context\",\n\t\t\t\" /resume Resume a previous session\",\n\t\t\t\" /compact [text] Compact the session context (optional instructions)\",\n\t\t\t\" /auto-compact Toggle automatic context compaction\",\n\t\t\t\" /model Switch model (Ctrl+L)\",\n\t\t\t\" /login Login to an OAuth provider\",\n\t\t\t\" /logout Logout from an OAuth provider\",\n\t\t\t\" /skills List loaded skills\",\n\t\t\t\" /skill:<name> Invoke a skill by name\",\n\t\t\t\" /quit, /exit Exit the CLI\",\n\t\t].join(\"\\n\");\n\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(helpText, 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate showSkills(): void {\n\t\tconst { skills = [] } = this.options;\n\n\t\tif (skills.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No skills loaded.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines: string[] = [];\n\t\tfor (const skill of skills) {\n\t\t\tconst hidden = skill.disableModelInvocation ? chalk.dim(\" (hidden from model)\") : \"\";\n\t\t\tlines.push(` ${chalk.bold(skill.name)}${hidden}`);\n\t\t\tlines.push(chalk.dim(` ${skill.description}`));\n\t\t\tlines.push(chalk.dim(` ${skill.filePath}`));\n\t\t}\n\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(lines.join(\"\\n\"), 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate async handleSkillInvocation(skillName: string): Promise<void> {\n\t\tconst { skills = [] } = this.options;\n\t\tconst skill = skills.find((s) => s.name === skillName);\n\n\t\tif (!skill) {\n\t\t\tthis.showStatus(chalk.red(`Skill \"${skillName}\" not found.`));\n\t\t\treturn;\n\t\t}\n\n\t\tconst skillPrompt = `Please read and follow the instructions in the skill file: ${skill.filePath}`;\n\t\tthis.chatContainer.addChild(new UserMessageComponent(skillPrompt, getMarkdownTheme()));\n\t\tthis.ui.requestRender();\n\t\tawait this.streamPrompt(skillPrompt);\n\t}\n\n\t// ========================================================================\n\t// OAuth Login/Logout\n\t// ========================================================================\n\n\tprivate async handleLogin(): Promise<void> {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) {\n\t\t\tthis.showStatus(chalk.red(\"Auth storage not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst providers = authStorage.getProviders();\n\t\tif (providers.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No OAuth providers registered.\"));\n\t\t\treturn;\n\t\t}\n\n\t\t// Use SelectList overlay for provider selection\n\t\tconst items: SelectItem[] = providers.map((p) => {\n\t\t\tconst loggedIn = authStorage.get(p.id)?.type === \"oauth\" ? \" (logged in)\" : \"\";\n\t\t\treturn { value: p.id, label: `${p.name}${loggedIn}` };\n\t\t});\n\n\t\tconst selected = await this.showSelectList(\"Login to OAuth provider\", items);\n\t\tif (!selected) return;\n\n\t\tconst provider = providers.find((p) => p.id === selected);\n\t\tif (!provider) return;\n\n\t\tthis.showStatus(chalk.dim(`Logging in to ${provider.name}...`));\n\n\t\ttry {\n\t\t\tawait authStorage.login(provider.id, {\n\t\t\t\tonAuth: (info) => {\n\t\t\t\t\tconst lines = [chalk.bold(\"Open this URL in your browser:\"), chalk.cyan(info.url)];\n\t\t\t\t\tif (info.instructions) {\n\t\t\t\t\t\tlines.push(chalk.dim(info.instructions));\n\t\t\t\t\t}\n\t\t\t\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\t\t\t\tthis.chatContainer.addChild(new Text(lines.join(\"\\n\"), 1, 0));\n\t\t\t\t\tthis.ui.requestRender();\n\n\t\t\t\t\t// Try to open browser\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst { execSync } = require(\"node:child_process\") as typeof import(\"node:child_process\");\n\t\t\t\t\t\tconst platform = process.platform;\n\t\t\t\t\t\tif (platform === \"darwin\") {\n\t\t\t\t\t\t\texecSync(`open \"${info.url}\"`, { stdio: \"ignore\" });\n\t\t\t\t\t\t} else if (platform === \"linux\") {\n\t\t\t\t\t\t\texecSync(`xdg-open \"${info.url}\" 2>/dev/null || sensible-browser \"${info.url}\" 2>/dev/null`, {\n\t\t\t\t\t\t\t\tstdio: \"ignore\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else if (platform === \"win32\") {\n\t\t\t\t\t\t\texecSync(`start \"\" \"${info.url}\"`, { stdio: \"ignore\" });\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Silently fail - user can open manually\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tonPrompt: async (promptInfo) => {\n\t\t\t\t\t// Show prompt message and wait for user input\n\t\t\t\t\tthis.showStatus(chalk.dim(promptInfo.message));\n\t\t\t\t\tconst answer = await this.getUserInput();\n\t\t\t\t\treturn answer.trim();\n\t\t\t\t},\n\t\t\t\tonProgress: (message) => {\n\t\t\t\t\tthis.showStatus(chalk.dim(message));\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\t\t\tthis.ui.requestRender();\n\t\t\tthis.showStatus(chalk.green(`Logged in to ${provider.name}. Credentials saved.`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tif (msg !== \"Login cancelled\") {\n\t\t\t\tthis.showStatus(chalk.red(`Login failed: ${msg}`));\n\t\t\t} else {\n\t\t\t\tthis.showStatus(chalk.dim(\"Login cancelled.\"));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async handleLogout(): Promise<void> {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) {\n\t\t\tthis.showStatus(chalk.red(\"Auth storage not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst loggedIn = authStorage\n\t\t\t.list()\n\t\t\t.filter((id) => authStorage.get(id)?.type === \"oauth\")\n\t\t\t.map((id) => {\n\t\t\t\tconst provider = authStorage.getProvider(id);\n\t\t\t\treturn { id, name: provider?.name ?? id };\n\t\t\t});\n\n\t\tif (loggedIn.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No OAuth providers logged in. Use /login first.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst items: SelectItem[] = loggedIn.map((p) => ({\n\t\t\tvalue: p.id,\n\t\t\tlabel: p.name,\n\t\t}));\n\n\t\tconst selected = await this.showSelectList(\"Logout from OAuth provider\", items);\n\t\tif (!selected) return;\n\n\t\tconst entry = loggedIn.find((p) => p.id === selected);\n\t\tif (!entry) return;\n\n\t\tauthStorage.logout(entry.id);\n\t\tthis.showStatus(chalk.green(`Logged out of ${entry.name}.`));\n\t}\n\n\t// ========================================================================\n\t// Resume Session\n\t// ========================================================================\n\n\t/**\n\t * List session files from the session directory, sorted by modification time (newest first).\n\t * Returns metadata for each session including the first user message as a preview.\n\t */\n\tprivate listAvailableSessions(): { path: string; mtime: number; preview: string; timestamp: string }[] {\n\t\tconst { sessionDir } = this.options;\n\t\tif (!sessionDir || !existsSync(sessionDir)) return [];\n\n\t\ttry {\n\t\t\tconst files = readdirSync(sessionDir)\n\t\t\t\t.filter((f: string) => f.endsWith(\".jsonl\"))\n\t\t\t\t.map((f: string) => {\n\t\t\t\t\tconst filePath = join(sessionDir, f);\n\t\t\t\t\tconst mtime = statSync(filePath).mtime.getTime();\n\t\t\t\t\treturn { name: f, path: filePath, mtime };\n\t\t\t\t})\n\t\t\t\t.sort((a, b) => b.mtime - a.mtime);\n\n\t\t\tconst sessions: { path: string; mtime: number; preview: string; timestamp: string }[] = [];\n\t\t\tfor (const file of files) {\n\t\t\t\t// Skip the current session file\n\t\t\t\tif (this.agent.sessionManager?.getSessionFile() === file.path) continue;\n\n\t\t\t\tconst preview = this.getSessionPreview(file.path);\n\t\t\t\tconst timestamp = new Date(file.mtime).toLocaleString();\n\t\t\t\tsessions.push({ path: file.path, mtime: file.mtime, preview, timestamp });\n\t\t\t}\n\t\t\treturn sessions;\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Extract the first user message from a session file for preview.\n\t */\n\tprivate getSessionPreview(filePath: string): string {\n\t\ttry {\n\t\t\tconst content = readFileSync(filePath, \"utf-8\");\n\t\t\tconst lines = content.trim().split(\"\\n\");\n\t\t\tfor (const line of lines) {\n\t\t\t\tif (!line.trim()) continue;\n\t\t\t\ttry {\n\t\t\t\t\tconst entry = JSON.parse(line);\n\t\t\t\t\tif (entry.type === \"message\" && entry.message?.role === \"user\") {\n\t\t\t\t\t\tconst msg = entry.message;\n\t\t\t\t\t\tlet text = \"\";\n\t\t\t\t\t\tif (typeof msg.content === \"string\") {\n\t\t\t\t\t\t\ttext = msg.content;\n\t\t\t\t\t\t} else if (Array.isArray(msg.content)) {\n\t\t\t\t\t\t\tfor (const block of msg.content) {\n\t\t\t\t\t\t\t\tif (block.type === \"text\" && block.text) {\n\t\t\t\t\t\t\t\t\ttext = block.text;\n\t\t\t\t\t\t\t\t\tbreak;\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\t// Truncate and clean up for display\n\t\t\t\t\t\ttext = text.replace(/\\n/g, \" \").trim();\n\t\t\t\t\t\tif (text.length > 80) {\n\t\t\t\t\t\t\ttext = `${text.slice(0, 77)}...`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn text || \"(empty message)\";\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Skip malformed lines\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn \"(no messages)\";\n\t\t} catch {\n\t\t\treturn \"(unreadable)\";\n\t\t}\n\t}\n\n\t/**\n\t * Format a relative time string (e.g. \"2 hours ago\", \"3 days ago\").\n\t */\n\tprivate formatRelativeTime(mtime: number): string {\n\t\tconst now = Date.now();\n\t\tconst diffMs = now - mtime;\n\t\tconst diffSec = Math.floor(diffMs / 1000);\n\t\tconst diffMin = Math.floor(diffSec / 60);\n\t\tconst diffHour = Math.floor(diffMin / 60);\n\t\tconst diffDay = Math.floor(diffHour / 24);\n\n\t\tif (diffMin < 1) return \"just now\";\n\t\tif (diffMin < 60) return `${diffMin}m ago`;\n\t\tif (diffHour < 24) return `${diffHour}h ago`;\n\t\tif (diffDay < 30) return `${diffDay}d ago`;\n\t\treturn new Date(mtime).toLocaleDateString();\n\t}\n\n\t/**\n\t * Handle the /resume command: show a list of previous sessions and load the selected one.\n\t */\n\tprivate async handleResume(): Promise<void> {\n\t\tconst sessions = this.listAvailableSessions();\n\t\tif (sessions.length === 0) {\n\t\t\tthis.showStatus(chalk.yellow(\"No previous sessions found.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst items: SelectItem[] = sessions.map((s) => ({\n\t\t\tvalue: s.path,\n\t\t\tlabel: `${chalk.dim(this.formatRelativeTime(s.mtime))} ${s.preview}`,\n\t\t}));\n\n\t\tconst selected = await this.showSelectList(\"Resume session\", items);\n\t\tif (!selected) return;\n\n\t\tconst session = sessions.find((s) => s.path === selected);\n\t\tif (!session) return;\n\n\t\ttry {\n\t\t\t// Open the selected session and set on agent (auto-restores messages)\n\t\t\tconst sessionDir = this.options.sessionDir!;\n\t\t\tconst newSessionManager = SessionManagerClass.open(selected, sessionDir);\n\t\t\tthis.agent.sessionManager = newSessionManager;\n\n\t\t\t// Rebuild the chat UI\n\t\t\tthis.chatContainer.clear();\n\t\t\tthis.rebuildChatFromSession();\n\n\t\t\t// Update footer tokens\n\t\t\tthis.updateFooterTokens();\n\n\t\t\tconst msgCount = this.agent.messages.length;\n\t\t\tthis.showStatus(chalk.green(`Resumed session (${msgCount} messages)`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tthis.showStatus(chalk.red(`Failed to resume session: ${msg}`));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Select List (overlay pattern from pi-coding-agent)\n\t// ========================================================================\n\n\tprivate showSelectList(title: string, items: SelectItem[]): Promise<string | null> {\n\t\treturn new Promise((resolve) => {\n\t\t\tconst container = new Container();\n\n\t\t\tcontainer.addChild(new Spacer(1));\n\t\t\tcontainer.addChild(new Text(chalk.bold.cyan(title), 1, 0));\n\n\t\t\tconst selectList = new SelectList(items, Math.min(items.length, 10), getSelectListTheme());\n\t\t\tselectList.onSelect = (item) => {\n\t\t\t\t// Restore normal UI\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.editorContainer.addChild(this.editor);\n\t\t\t\tthis.ui.setFocus(this.editor);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\tresolve(item.value);\n\t\t\t};\n\t\t\tselectList.onCancel = () => {\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.editorContainer.addChild(this.editor);\n\t\t\t\tthis.ui.setFocus(this.editor);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\tresolve(null);\n\t\t\t};\n\t\t\tcontainer.addChild(selectList);\n\t\t\tcontainer.addChild(new Text(chalk.dim(\"↑↓ navigate • enter select • esc cancel\"), 1, 0));\n\t\t\tcontainer.addChild(new Spacer(1));\n\n\t\t\t// Replace editor area with select list\n\t\t\tthis.editorContainer.clear();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.pendingContainer.addChild(container);\n\t\t\tthis.ui.setFocus(selectList);\n\t\t\tthis.ui.requestRender();\n\t\t});\n\t}\n\n\t// ========================================================================\n\t// Status & Utilities\n\t// ========================================================================\n\n\tprivate showStatus(text: string): void {\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(text, 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate updateEditorBorderColor(): void {\n\t\tthis.editorTheme.borderColor = this.isBashMode ? (s: string) => chalk.yellow(s) : (s: string) => chalk.gray(s);\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate shutdown(): void {\n\t\tthis.ui.stop();\n\t\tconsole.log(chalk.dim(\"\\nGoodbye.\"));\n\t\tprocess.exit(0);\n\t}\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction extractTextFromMessage(msg: ModelMessage): string {\n\tif (msg.role === \"user\") {\n\t\tconst content = (msg as import(\"edge-pi\").UserModelMessage).content;\n\t\tif (typeof content === \"string\") return content;\n\t\tif (Array.isArray(content)) {\n\t\t\treturn content\n\t\t\t\t.filter((c): c is { type: \"text\"; text: string } => (c as { type: string }).type === \"text\")\n\t\t\t\t.map((c) => c.text)\n\t\t\t\t.join(\"\");\n\t\t}\n\t}\n\treturn \"\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"interactive-mode.js","sourceRoot":"","sources":["../../../src/modes/interactive/interactive-mode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Interactive mode using @mariozechner/pi-tui.\n *\n * Replaces the old readline-based REPL with a proper TUI that matches\n * the UX patterns from @mariozechner/pi-coding-agent:\n * - Editor component for input with submit/escape handling\n * - Markdown rendering for assistant responses\n * - Tool execution components with collapsible output\n * - Footer with model/provider info and token stats\n * - Container-based layout (header → chat → pending → editor → footer)\n * - Context compaction (manual /compact + auto mode)\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n\tCombinedAutocompleteProvider,\n\tContainer,\n\tEditor,\n\tKey,\n\tLoader,\n\tmatchesKey,\n\tProcessTerminal,\n\ttype SelectItem,\n\tSelectList,\n\ttype SlashCommand,\n\tSpacer,\n\tText,\n\tTUI,\n} from \"@mariozechner/pi-tui\";\nimport type { ImagePart, ModelMessage } from \"ai\";\nimport chalk from \"chalk\";\nimport type { CodingAgent, CodingAgentConfig } from \"edge-pi\";\nimport { type CompactionResult, estimateContextTokens } from \"edge-pi\";\nimport { SessionManager as SessionManagerClass } from \"edge-pi/session\";\nimport type { AuthStorage } from \"../../auth/auth-storage.js\";\nimport type { ContextFile } from \"../../context.js\";\nimport { getLatestModels } from \"../../model-factory.js\";\nimport type { PromptTemplate } from \"../../prompts.js\";\nimport { expandPromptTemplate } from \"../../prompts.js\";\nimport type { SettingsManager } from \"../../settings.js\";\nimport type { Skill } from \"../../skills.js\";\nimport { executeBashCommand } from \"../../utils/bash-executor.js\";\nimport { type ClipboardImage, extensionForImageMimeType, readClipboardImage } from \"../../utils/clipboard-image.js\";\nimport { formatAIError } from \"../../utils/format-ai-error.js\";\nimport { formatPendingMessages, parseBashInput } from \"./bash-helpers.js\";\nimport { AssistantMessageComponent } from \"./components/assistant-message.js\";\nimport { BashExecutionComponent } from \"./components/bash-execution.js\";\nimport { CompactionSummaryComponent } from \"./components/compaction-summary.js\";\nimport { FooterComponent } from \"./components/footer.js\";\nimport { ToolExecutionComponent, type ToolOutput } from \"./components/tool-execution.js\";\nimport { UserMessageComponent } from \"./components/user-message.js\";\nimport { getEditorTheme, getMarkdownTheme, getSelectListTheme } from \"./theme.js\";\n\n/** Default context window size (used when model doesn't report one). */\nconst DEFAULT_CONTEXT_WINDOW = 200_000;\nconst DEFAULT_COMPACTION_SETTINGS = {\n\treserveTokens: 16384,\n\tkeepRecentTokens: 20000,\n} as const;\n\ntype CompactionSettings = {\n\treserveTokens: number;\n\tkeepRecentTokens: number;\n};\n\n/** Extract display-friendly output from a tool result. Handles both plain strings and structured objects with text/image fields. */\nfunction extractToolOutput(output: unknown): ToolOutput {\n\tif (typeof output === \"string\") {\n\t\treturn { text: output };\n\t}\n\tif (typeof output === \"object\" && output !== null && \"text\" in output && typeof (output as any).text === \"string\") {\n\t\tconst obj = output as any;\n\t\treturn {\n\t\t\ttext: obj.text,\n\t\t\t...(obj.image && { image: obj.image }),\n\t\t};\n\t}\n\treturn { text: JSON.stringify(output) };\n}\n\nexport interface InteractiveModeOptions {\n\tinitialMessage?: string;\n\tinitialMessages?: string[];\n\tskills?: Skill[];\n\tcontextFiles?: ContextFile[];\n\tprompts?: PromptTemplate[];\n\tverbose?: boolean;\n\tprovider: string;\n\tmodelId: string;\n\tauthStorage?: AuthStorage;\n\t/** Settings manager for persisting user preferences (provider, model, compaction). */\n\tsettingsManager?: SettingsManager;\n\t/** Path to the `fd` binary for @ file autocomplete, or undefined if unavailable. */\n\tfdPath?: string;\n\t/** Called when the user switches model via Ctrl+L. Returns a new agent for the new model. */\n\tonModelChange?: (provider: string, modelId: string) => Promise<CodingAgent>;\n\t/** Context window size for the model. Defaults to 200k. */\n\tcontextWindow?: number;\n\t/** Directory where session files are stored. Required for /resume. */\n\tsessionDir?: string;\n\t/** Agent config used to recreate agents when resuming sessions. */\n\tagentConfig?: CodingAgentConfig;\n\t/** When true, show the session picker immediately on startup. */\n\tresumeOnStart?: boolean;\n}\n\n/**\n * Run the interactive TUI mode with streaming output.\n */\nexport async function runInteractiveMode(agent: CodingAgent, options: InteractiveModeOptions): Promise<void> {\n\tconst mode = new InteractiveMode(agent, options);\n\tawait mode.run();\n}\n\n// ============================================================================\n// InteractiveMode class\n// ============================================================================\n\nclass InteractiveMode {\n\tprivate agent: CodingAgent;\n\tprivate options: InteractiveModeOptions;\n\tprivate currentProvider: string;\n\tprivate currentModelId: string;\n\n\tprivate ui!: TUI;\n\tprivate headerContainer!: Container;\n\tprivate chatContainer!: Container;\n\tprivate pendingContainer!: Container;\n\tprivate pendingMessagesContainer!: Container;\n\tprivate editorContainer!: Container;\n\tprivate editor!: Editor;\n\tprivate editorTheme!: import(\"@mariozechner/pi-tui\").EditorTheme;\n\n\t// Message queues\n\tprivate steeringMessages: string[] = [];\n\tprivate followUpMessages: string[] = [];\n\tprivate isStreaming = false;\n\n\t// Inline bash state\n\tprivate isBashMode = false;\n\tprivate isBashRunning = false;\n\tprivate bashAbortController: AbortController | null = null;\n\tprivate bashComponent: import(\"./components/bash-execution.js\").BashExecutionComponent | undefined = undefined;\n\tprivate footer!: FooterComponent;\n\n\t// Loading animation during agent processing\n\tprivate loadingAnimation: Loader | undefined = undefined;\n\n\t// Streaming state\n\tprivate streamingComponent: AssistantMessageComponent | undefined = undefined;\n\tprivate streamingText = \"\";\n\tprivate hadToolResults = false;\n\n\t// Tool execution tracking: toolCallId → component\n\tprivate pendingTools = new Map<string, ToolExecutionComponent>();\n\n\t// Tool output expansion state\n\tprivate toolOutputExpanded = false;\n\n\t// Callback for resolving user input promise\n\tprivate onInputCallback?: (text: string) => void;\n\n\t// Pending clipboard images to attach to the next message\n\tprivate pendingImages: ClipboardImage[] = [];\n\n\t// Compaction state\n\tprivate contextWindow: number;\n\tprivate compactionSettings: CompactionSettings;\n\tprivate autoCompaction = true;\n\tprivate isCompacting = false;\n\tprivate compactionLoader: Loader | null = null;\n\n\tconstructor(agent: CodingAgent, options: InteractiveModeOptions) {\n\t\tthis.agent = agent;\n\t\tthis.options = options;\n\t\tthis.currentProvider = options.provider;\n\t\tthis.currentModelId = options.modelId;\n\t\tthis.contextWindow = options.contextWindow ?? DEFAULT_CONTEXT_WINDOW;\n\n\t\t// Initialize compaction settings from persisted settings if available\n\t\tconst savedCompaction = options.settingsManager?.getCompaction();\n\t\tthis.compactionSettings = {\n\t\t\t...DEFAULT_COMPACTION_SETTINGS,\n\t\t\t...(savedCompaction?.reserveTokens !== undefined && { reserveTokens: savedCompaction.reserveTokens }),\n\t\t\t...(savedCompaction?.keepRecentTokens !== undefined && { keepRecentTokens: savedCompaction.keepRecentTokens }),\n\t\t};\n\t\tthis.autoCompaction = options.settingsManager?.getCompactionEnabled() ?? true;\n\n\t\tthis.configureAgentCompaction();\n\t}\n\n\tasync run(): Promise<void> {\n\t\tthis.initUI();\n\t\tthis.updateFooterTokens();\n\n\t\t// Show session picker immediately if --resume was passed\n\t\tif (this.options.resumeOnStart) {\n\t\t\tawait this.handleResume();\n\t\t}\n\n\t\t// Process initial messages\n\t\tconst { initialMessage, initialMessages = [] } = this.options;\n\n\t\tconst allInitial: string[] = [];\n\t\tif (initialMessage) allInitial.push(initialMessage);\n\t\tallInitial.push(...initialMessages);\n\n\t\tfor (const msg of allInitial) {\n\t\t\tthis.chatContainer.addChild(new UserMessageComponent(msg, getMarkdownTheme()));\n\t\t\tthis.ui.requestRender();\n\t\t\tawait this.streamPrompt(msg);\n\t\t}\n\n\t\t// Main interactive loop\n\t\twhile (true) {\n\t\t\tconst userInput = await this.getUserInput();\n\t\t\tawait this.handleUserInput(userInput);\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// UI Setup\n\t// ========================================================================\n\n\tprivate initUI(): void {\n\t\tconst { provider, modelId, skills = [], contextFiles = [], prompts = [], verbose } = this.options;\n\n\t\tthis.ui = new TUI(new ProcessTerminal());\n\n\t\t// Header\n\t\tthis.headerContainer = new Container();\n\t\tconst logo = chalk.bold(\"epi\") + chalk.dim(` - ${provider}/${modelId}`);\n\n\t\tconst hints = [\n\t\t\t`${chalk.dim(\"Escape\")} to abort`,\n\t\t\t`${chalk.dim(\"!\")} inline bash`,\n\t\t\t`${chalk.dim(\"Alt+Enter\")} follow-up while streaming`,\n\t\t\t`${chalk.dim(\"Ctrl+C\")} to exit`,\n\t\t\t`${chalk.dim(\"Ctrl+E\")} to expand tools`,\n\t\t\t`${chalk.dim(\"Ctrl+L\")} to switch model`,\n\t\t\t`${chalk.dim(\"Ctrl+V\")} to paste image`,\n\t\t\t`${chalk.dim(\"↑/↓\")} to browse history`,\n\t\t\t`${chalk.dim(\"@\")} for file references`,\n\t\t\t`${chalk.dim(\"/\")} for commands`,\n\t\t].join(\"\\n\");\n\n\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\tthis.headerContainer.addChild(new Text(`${logo}\\n${hints}`, 1, 0));\n\t\tthis.headerContainer.addChild(new Spacer(1));\n\n\t\tif (verbose && this.agent.sessionManager?.getSessionFile()) {\n\t\t\tthis.headerContainer.addChild(\n\t\t\t\tnew Text(chalk.dim(`Session: ${this.agent.sessionManager.getSessionFile()}`), 1, 0),\n\t\t\t);\n\t\t}\n\n\t\t// Show loaded context, skills, and prompts at startup\n\t\tthis.showLoadedResources(contextFiles, skills, prompts);\n\n\t\t// Chat area\n\t\tthis.chatContainer = new Container();\n\n\t\t// Pending messages (loading animations, status)\n\t\tthis.pendingContainer = new Container();\n\n\t\t// Pending steering/follow-up messages\n\t\tthis.pendingMessagesContainer = new Container();\n\n\t\t// Editor with slash command autocomplete\n\t\tthis.editorTheme = getEditorTheme();\n\t\tthis.editor = new Editor(this.ui, this.editorTheme);\n\t\tthis.editor.setAutocompleteProvider(this.buildAutocompleteProvider());\n\t\tthis.editorContainer = new Container();\n\t\tthis.editorContainer.addChild(this.editor);\n\n\t\t// Footer\n\t\tthis.footer = new FooterComponent(this.currentProvider, this.currentModelId);\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\n\t\t// Assemble layout\n\t\tthis.ui.addChild(this.headerContainer);\n\t\tthis.ui.addChild(this.chatContainer);\n\t\tthis.ui.addChild(this.pendingMessagesContainer);\n\t\tthis.ui.addChild(this.pendingContainer);\n\t\tthis.ui.addChild(this.editorContainer);\n\t\tthis.ui.addChild(this.footer);\n\n\t\tthis.ui.setFocus(this.editor);\n\t\tthis.setupKeyHandlers();\n\n\t\tthis.ui.start();\n\t}\n\n\t// ========================================================================\n\t// Key Handlers\n\t// ========================================================================\n\n\tprivate setupKeyHandlers(): void {\n\t\tthis.editor.onChange = (text: string) => {\n\t\t\tconst wasBashMode = this.isBashMode;\n\t\t\tthis.isBashMode = text.trimStart().startsWith(\"!\");\n\t\t\tif (wasBashMode !== this.isBashMode) {\n\t\t\t\tthis.updateEditorBorderColor();\n\t\t\t}\n\t\t};\n\t\tthis.editor.onSubmit = (text: string) => {\n\t\t\ttext = text.trim();\n\t\t\tif (!text) return;\n\n\t\t\t// If agent is streaming, Enter becomes a steering message\n\t\t\tif (this.isStreaming) {\n\t\t\t\tthis.agent.steer({ role: \"user\", content: [{ type: \"text\", text }] });\n\t\t\t\tthis.steeringMessages.push(text);\n\t\t\t\tthis.editor.setText(\"\");\n\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.editor.addToHistory(text);\n\t\t\tthis.editor.setText(\"\");\n\n\t\t\tif (this.onInputCallback) {\n\t\t\t\tthis.onInputCallback(text);\n\t\t\t}\n\t\t};\n\n\t\tconst origHandleInput = this.editor.handleInput.bind(this.editor);\n\t\tthis.editor.handleInput = (data: string) => {\n\t\t\t// Escape: abort if agent is running or compacting\n\t\t\tif (matchesKey(data, Key.escape)) {\n\t\t\t\tif (this.isBashRunning && this.bashAbortController) {\n\t\t\t\t\tthis.bashAbortController.abort();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.isCompacting) {\n\t\t\t\t\tthis.agent.abort();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.loadingAnimation) {\n\t\t\t\t\tthis.agent.abort();\n\t\t\t\t\tthis.stopLoading();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Ctrl+C: exit\n\t\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\t\tthis.shutdown();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+D: exit if editor is empty\n\t\t\tif (matchesKey(data, Key.ctrl(\"d\"))) {\n\t\t\t\tif (this.editor.getText().length === 0) {\n\t\t\t\t\tthis.shutdown();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Ctrl+E: toggle tool output expansion\n\t\t\tif (matchesKey(data, Key.ctrl(\"e\"))) {\n\t\t\t\tthis.toggleToolExpansion();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+L: select model\n\t\t\tif (matchesKey(data, Key.ctrl(\"l\"))) {\n\t\t\t\tthis.handleModelSelect();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+V: paste image from clipboard\n\t\t\tif (matchesKey(data, Key.ctrl(\"v\"))) {\n\t\t\t\tthis.handleClipboardImagePaste();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Alt+Enter (Option+Enter on Mac): follow-up while streaming (or submit normally when idle)\n\t\t\tif (matchesKey(data, Key.alt(\"enter\"))) {\n\t\t\t\tconst text = this.editor.getText().trim();\n\t\t\t\tif (!text) return;\n\n\t\t\t\tif (this.isStreaming) {\n\t\t\t\t\tthis.followUpMessages.push(text);\n\t\t\t\t\tthis.editor.setText(\"\");\n\t\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Not streaming: treat like regular submit\n\t\t\t\tthis.editor.onSubmit?.(text);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Alt+Up (Option+Up on Mac): dequeue all queued messages back into the editor\n\t\t\tif (matchesKey(data, Key.alt(\"up\"))) {\n\t\t\t\tconst restored = this.clearAllQueues();\n\t\t\t\tif (restored.length > 0) {\n\t\t\t\t\tthis.editor.setText(restored.join(\"\\n\\n\"));\n\t\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\torigHandleInput(data);\n\t\t};\n\t}\n\n\t// ========================================================================\n\t// User Input\n\t// ========================================================================\n\n\tprivate getUserInput(): Promise<string> {\n\t\treturn new Promise((resolve) => {\n\t\t\tthis.onInputCallback = (text: string) => {\n\t\t\t\tthis.onInputCallback = undefined;\n\t\t\t\tresolve(text);\n\t\t\t};\n\t\t});\n\t}\n\n\tprivate async handleUserInput(input: string): Promise<void> {\n\t\t// Handle commands\n\t\tif (input === \"/help\") {\n\t\t\tthis.showHelp();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/skills\") {\n\t\t\tthis.showSkills();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/quit\" || input === \"/exit\") {\n\t\t\tthis.shutdown();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/model\") {\n\t\t\tawait this.handleModelSelect();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/login\") {\n\t\t\tawait this.handleLogin();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/logout\") {\n\t\t\tawait this.handleLogout();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/compact\" || input.startsWith(\"/compact \")) {\n\t\t\tconst customInstructions = input.startsWith(\"/compact \") ? input.slice(9).trim() : undefined;\n\t\t\tawait this.handleCompactCommand(customInstructions);\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/auto-compact\") {\n\t\t\tthis.toggleAutoCompaction();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/resume\") {\n\t\t\tawait this.handleResume();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input.startsWith(\"/skill:\")) {\n\t\t\tconst skillName = input.slice(\"/skill:\".length).trim();\n\t\t\tawait this.handleSkillInvocation(skillName);\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle bash commands (! for normal, !! for excluded from context)\n\t\tconst bashParsed = parseBashInput(input);\n\t\tif (bashParsed) {\n\t\t\tif (this.isBashRunning) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"A bash command is already running. Press Escape to cancel it first.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait this.handleBashCommand(bashParsed.command, bashParsed.excludeFromContext);\n\t\t\tthis.isBashMode = false;\n\t\t\tthis.updateEditorBorderColor();\n\t\t\treturn;\n\t\t}\n\n\t\t// Try expanding prompt templates\n\t\tconst { prompts = [] } = this.options;\n\t\tconst expanded = expandPromptTemplate(input, prompts);\n\n\t\t// Capture and clear pending images\n\t\tconst images = this.pendingImages.length > 0 ? [...this.pendingImages] : undefined;\n\t\tthis.pendingImages = [];\n\n\t\t// Regular message (use expanded text if a prompt template was matched)\n\t\tconst imageLabel = images ? chalk.dim(` (${images.length} image${images.length > 1 ? \"s\" : \"\"})`) : \"\";\n\t\tthis.chatContainer.addChild(new UserMessageComponent(`${expanded}${imageLabel}`, getMarkdownTheme()));\n\t\tthis.ui.requestRender();\n\t\tawait this.streamPrompt(expanded, images);\n\t}\n\n\tprivate async handleBashCommand(command: string, excludeFromContext: boolean): Promise<void> {\n\t\tthis.bashAbortController = new AbortController();\n\t\tthis.isBashRunning = true;\n\n\t\tthis.bashComponent = new BashExecutionComponent(command, this.ui, excludeFromContext);\n\t\tif (this.toolOutputExpanded) {\n\t\t\tthis.bashComponent.setExpanded(true);\n\t\t}\n\t\tthis.chatContainer.addChild(this.bashComponent);\n\t\tthis.ui.requestRender();\n\n\t\ttry {\n\t\t\tconst result = await executeBashCommand(command, {\n\t\t\t\tsignal: this.bashAbortController.signal,\n\t\t\t\tonChunk: (chunk) => {\n\t\t\t\t\tthis.bashComponent?.appendOutput(chunk);\n\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.bashComponent.setComplete(result.exitCode, result.cancelled, result.truncated, result.fullOutputPath);\n\t\t\tthis.ui.requestRender();\n\n\t\t\tif (!excludeFromContext) {\n\t\t\t\tconst msgText = `Ran \\`${command}\\`\\n\\n\\`\\`\\`\\n${result.output.trimEnd()}\\n\\`\\`\\``;\n\t\t\t\tconst userMsg: ModelMessage = { role: \"user\", content: [{ type: \"text\", text: msgText }] };\n\t\t\t\tthis.agent.setMessages([...this.agent.messages, userMsg]);\n\t\t\t\tthis.agent.sessionManager?.appendMessage(userMsg);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.isBashRunning = false;\n\t\t\tthis.bashAbortController = null;\n\t\t\tthis.bashComponent = undefined;\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Autocomplete\n\t// ========================================================================\n\n\tprivate buildAutocompleteProvider(): CombinedAutocompleteProvider {\n\t\tconst { skills = [], prompts = [], fdPath } = this.options;\n\n\t\tconst commands: SlashCommand[] = [\n\t\t\t{ name: \"help\", description: \"Show available commands\" },\n\t\t\t{ name: \"resume\", description: \"Resume a previous session\" },\n\t\t\t{ name: \"compact\", description: \"Manually compact the session context\" },\n\t\t\t{ name: \"auto-compact\", description: \"Toggle automatic context compaction\" },\n\t\t\t{ name: \"login\", description: \"Login to an OAuth provider\" },\n\t\t\t{ name: \"logout\", description: \"Logout from an OAuth provider\" },\n\t\t\t{ name: \"skills\", description: \"List loaded skills\" },\n\t\t\t{ name: \"model\", description: \"Switch model (Ctrl+L)\" },\n\t\t\t{ name: \"quit\", description: \"Exit the CLI\" },\n\t\t\t{ name: \"exit\", description: \"Exit the CLI\" },\n\t\t];\n\n\t\tfor (const skill of skills) {\n\t\t\tcommands.push({\n\t\t\t\tname: `skill:${skill.name}`,\n\t\t\t\tdescription: skill.description,\n\t\t\t});\n\t\t}\n\n\t\t// Add prompt templates as slash commands\n\t\tfor (const prompt of prompts) {\n\t\t\tcommands.push({\n\t\t\t\tname: prompt.name,\n\t\t\t\tdescription: prompt.description,\n\t\t\t});\n\t\t}\n\n\t\treturn new CombinedAutocompleteProvider(commands, process.cwd(), fdPath ?? null);\n\t}\n\n\t// ========================================================================\n\t// Model Selection\n\t// ========================================================================\n\n\tprivate async handleModelSelect(): Promise<void> {\n\t\tconst latestModels = getLatestModels();\n\t\tconst modelOptions: { provider: string; modelId: string; label: string }[] = [];\n\t\tfor (const [provider, models] of Object.entries(latestModels)) {\n\t\t\tfor (const modelId of models) {\n\t\t\t\tmodelOptions.push({ provider, modelId, label: `${provider}/${modelId}` });\n\t\t\t}\n\t\t}\n\n\t\tconst items: SelectItem[] = modelOptions.map((m) => {\n\t\t\tconst current = m.provider === this.currentProvider && m.modelId === this.currentModelId;\n\t\t\treturn {\n\t\t\t\tvalue: `${m.provider}/${m.modelId}`,\n\t\t\t\tlabel: current ? `${m.label} (current)` : m.label,\n\t\t\t};\n\t\t});\n\n\t\tconst selected = await this.showSelectList(\"Switch model\", items);\n\t\tif (!selected) return;\n\n\t\tconst [newProvider, ...modelParts] = selected.split(\"/\");\n\t\tconst newModelId = modelParts.join(\"/\");\n\n\t\tif (newProvider === this.currentProvider && newModelId === this.currentModelId) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.showStatus(chalk.dim(`Switching to ${newProvider}/${newModelId}...`));\n\n\t\tif (!this.options.onModelChange) {\n\t\t\tthis.showStatus(chalk.yellow(\"Model switching is not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst newAgent = await this.options.onModelChange(newProvider, newModelId);\n\t\t\t// Preserve conversation history\n\t\t\tnewAgent.setMessages([...this.agent.messages]);\n\t\t\tthis.agent = newAgent;\n\t\t\tthis.configureAgentCompaction();\n\n\t\t\tthis.currentProvider = newProvider;\n\t\t\tthis.currentModelId = newModelId;\n\t\t\tthis.updateFooter();\n\n\t\t\t// Persist the choice for next startup\n\t\t\tthis.options.settingsManager?.setDefaults(newProvider, newModelId);\n\n\t\t\tthis.showStatus(chalk.green(`Switched to ${newProvider}/${newModelId}`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tthis.showStatus(chalk.red(`Failed to switch model: ${msg}`));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Streaming\n\t// ========================================================================\n\n\tprivate async streamPrompt(prompt: string, images?: ClipboardImage[]): Promise<void> {\n\t\tthis.isStreaming = true;\n\t\tthis.updatePendingMessagesDisplay();\n\n\t\t// Build image parts from clipboard images\n\t\tconst imageParts: ImagePart[] = (images ?? []).map((img) => ({\n\t\t\ttype: \"image\" as const,\n\t\t\timage: Buffer.from(img.bytes).toString(\"base64\"),\n\t\t\tmediaType: img.mimeType,\n\t\t}));\n\n\t\t// Start loading animation\n\t\tthis.startLoading();\n\n\t\tthis.streamingComponent = undefined;\n\t\tthis.streamingText = \"\";\n\t\tthis.hadToolResults = false;\n\n\t\tlet errorDisplayed = false;\n\t\tlet streamFailed = false;\n\t\ttry {\n\t\t\tconst result =\n\t\t\t\timageParts.length > 0\n\t\t\t\t\t? await this.agent.stream({\n\t\t\t\t\t\t\tmessages: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\trole: \"user\" as const,\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: prompt }, ...imageParts],\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: await this.agent.stream({ prompt });\n\n\t\t\tfor await (const part of result.fullStream) {\n\t\t\t\tswitch (part.type) {\n\t\t\t\t\tcase \"text-delta\":\n\t\t\t\t\t\t// After tool results, or for the very first text part, start a new assistant message component\n\t\t\t\t\t\t// so each agent step gets its own message bubble\n\t\t\t\t\t\tif (this.hadToolResults || !this.streamingComponent) {\n\t\t\t\t\t\t\tthis.streamingComponent = new AssistantMessageComponent(getMarkdownTheme());\n\t\t\t\t\t\t\tthis.streamingText = \"\";\n\t\t\t\t\t\t\tthis.hadToolResults = false;\n\t\t\t\t\t\t\tthis.chatContainer.addChild(this.streamingComponent);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.streamingText += part.text;\n\t\t\t\t\t\tthis.streamingComponent!.updateText(this.streamingText);\n\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"tool-call\": {\n\t\t\t\t\t\tconst args =\n\t\t\t\t\t\t\ttypeof part.input === \"object\" && part.input !== null\n\t\t\t\t\t\t\t\t? (part.input as Record<string, unknown>)\n\t\t\t\t\t\t\t\t: {};\n\t\t\t\t\t\tconst toolComponent = new ToolExecutionComponent(part.toolName, args);\n\n\t\t\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\t\t\ttoolComponent.setExpanded(true);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.pendingTools.set(part.toolCallId, toolComponent);\n\t\t\t\t\t\tthis.chatContainer.addChild(toolComponent);\n\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"tool-result\": {\n\t\t\t\t\t\tconst toolComponent = this.pendingTools.get(part.toolCallId);\n\t\t\t\t\t\tif (toolComponent) {\n\t\t\t\t\t\t\tconst toolOutput = extractToolOutput(part.output);\n\t\t\t\t\t\t\ttoolComponent.updateResult(toolOutput, /* isError */ false, /* isPartial */ false);\n\t\t\t\t\t\t\tthis.pendingTools.delete(part.toolCallId);\n\t\t\t\t\t\t\tthis.hadToolResults = true;\n\t\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"error\": {\n\t\t\t\t\t\tconst errorMessage = formatAIError(part.error);\n\t\t\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\t\t\tthis.streamingComponent.setError(errorMessage);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.showStatus(chalk.red(`Error: ${errorMessage}`));\n\t\t\t\t\t\t}\n\t\t\t\t\t\terrorDisplayed = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (errorDisplayed) return;\n\n\t\t\t// Wait for stream to complete — the agent auto-updates messages and persists to session\n\t\t\tawait result.response;\n\n\t\t\t// Update footer token stats\n\t\t\tthis.updateFooterTokens();\n\t\t} catch (error) {\n\t\t\tif (errorDisplayed) {\n\t\t\t\tstreamFailed = true;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tstreamFailed = true;\n\t\t\tif ((error as Error).name === \"AbortError\") {\n\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\tthis.streamingComponent.setAborted();\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.dim(\"[aborted]\"));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst msg =\n\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t? error.message\n\t\t\t\t\t\t: typeof error === \"object\" && error !== null\n\t\t\t\t\t\t\t? JSON.stringify(error)\n\t\t\t\t\t\t\t: String(error);\n\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\tthis.streamingComponent.setError(msg);\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.red(`Error: ${msg}`));\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.stopLoading();\n\t\t\tthis.streamingComponent = undefined;\n\t\t\tthis.streamingText = \"\";\n\t\t\tthis.hadToolResults = false;\n\t\t\tthis.pendingTools.clear();\n\t\t\tthis.isStreaming = false;\n\t\t\tthis.steeringMessages = [];\n\t\t\tif (streamFailed) {\n\t\t\t\tthis.followUpMessages = [];\n\t\t\t}\n\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\tthis.ui.requestRender();\n\t\t}\n\n\t\t// Process queued follow-ups (skipped if stream failed/aborted)\n\t\twhile (this.followUpMessages.length > 0) {\n\t\t\tconst next = this.followUpMessages.shift();\n\t\t\tif (!next) break;\n\n\t\t\tthis.updatePendingMessagesDisplay();\n\n\t\t\tthis.chatContainer.addChild(new UserMessageComponent(next, getMarkdownTheme()));\n\t\t\tthis.ui.requestRender();\n\t\t\tawait this.streamPrompt(next);\n\t\t}\n\t}\n\n\tprivate updatePendingMessagesDisplay(): void {\n\t\tthis.pendingMessagesContainer.clear();\n\n\t\t// If no agent is running, clear pending messages (they've been consumed)\n\t\tif (!this.isStreaming && this.followUpMessages.length === 0 && this.steeringMessages.length === 0) {\n\t\t\tthis.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines = formatPendingMessages(this.steeringMessages, this.followUpMessages);\n\n\t\tif (lines.length === 0) {\n\t\t\tthis.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.pendingMessagesContainer.addChild(new Spacer(1));\n\t\tthis.pendingMessagesContainer.addChild(new Text(lines.map((l) => chalk.dim(l)).join(\"\\n\"), 1, 0));\n\t\tthis.pendingMessagesContainer.addChild(new Text(chalk.dim(\"↳ Alt+Up to edit queued messages\"), 1, 0));\n\t\tthis.pendingMessagesContainer.addChild(new Spacer(1));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate clearAllQueues(): string[] {\n\t\tconst restored = [...this.steeringMessages, ...this.followUpMessages];\n\t\tthis.steeringMessages = [];\n\t\tthis.followUpMessages = [];\n\t\treturn restored;\n\t}\n\n\t// ========================================================================\n\t// Loading Animation\n\t// ========================================================================\n\n\tprivate startLoading(): void {\n\t\tthis.stopLoading();\n\t\tthis.loadingAnimation = new Loader(\n\t\t\tthis.ui,\n\t\t\t(s: string) => chalk.cyan(s),\n\t\t\t(s: string) => chalk.dim(s),\n\t\t\t\"Working...\",\n\t\t);\n\t\tthis.loadingAnimation.start();\n\t\tthis.pendingContainer.addChild(new Spacer(1));\n\t\tthis.pendingContainer.addChild(this.loadingAnimation);\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate stopLoading(): void {\n\t\tif (this.loadingAnimation) {\n\t\t\tthis.loadingAnimation.stop();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.loadingAnimation = undefined;\n\t\t\tthis.ui.requestRender();\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Tool Expansion\n\t// ========================================================================\n\n\tprivate toggleToolExpansion(): void {\n\t\tthis.toolOutputExpanded = !this.toolOutputExpanded;\n\n\t\t// Update all tool components and compaction components in the chat\n\t\tfor (const child of this.chatContainer.children) {\n\t\t\tif (child instanceof ToolExecutionComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t} else if (child instanceof CompactionSummaryComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t} else if (child instanceof BashExecutionComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t}\n\t\t}\n\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Clipboard Image Paste\n\t// ========================================================================\n\n\tprivate handleClipboardImagePaste(): void {\n\t\ttry {\n\t\t\tconst image = readClipboardImage();\n\t\t\tif (!image) return;\n\t\t\tthis.pendingImages.push(image);\n\t\t\tconst ext = extensionForImageMimeType(image.mimeType) ?? \"image\";\n\t\t\tconst label = `[image ${this.pendingImages.length}: ${ext}]`;\n\t\t\tthis.editor.insertTextAtCursor(label);\n\t\t\tthis.ui.requestRender();\n\t\t} catch {\n\t\t\t// Silently ignore clipboard errors (may not have permission, etc.)\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Compaction\n\t// ========================================================================\n\n\t/**\n\t * Handle the /compact command.\n\t */\n\tprivate async handleCompactCommand(_customInstructions?: string): Promise<void> {\n\t\ttry {\n\t\t\tconst result = await this.agent.compact();\n\t\t\tif (!result) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (already compacted or insufficient history).\"));\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Compaction callbacks already report errors in interactive mode.\n\t\t\t// Catch here to avoid unwinding the main TUI loop on abort/provider errors.\n\t\t\tif (!this.agent.compaction?.onCompactionError) {\n\t\t\t\tconst compactionError = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tif (compactionError.name === \"AbortError\" || compactionError.message === \"Compaction cancelled\") {\n\t\t\t\t\tthis.showStatus(chalk.dim(\"Compaction cancelled.\"));\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.red(`Compaction failed: ${compactionError.message}`));\n\t\t\t\t}\n\t\t\t\tthis.ui.requestRender();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Toggle auto-compaction on/off.\n\t */\n\tprivate toggleAutoCompaction(): void {\n\t\tthis.autoCompaction = !this.autoCompaction;\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.options.settingsManager?.setCompactionEnabled(this.autoCompaction);\n\t\tif (this.agent.compaction) {\n\t\t\tthis.agent.setCompaction({\n\t\t\t\t...this.agent.compaction,\n\t\t\t\tmode: this.autoCompaction ? \"auto\" : \"manual\",\n\t\t\t});\n\t\t}\n\t\tthis.showStatus(\n\t\t\tthis.autoCompaction ? chalk.green(\"Auto-compaction enabled\") : chalk.dim(\"Auto-compaction disabled\"),\n\t\t);\n\t\tthis.ui.requestRender();\n\t}\n\n\t/**\n\t * Configure agent-level compaction and callbacks for UI updates.\n\t */\n\tprivate configureAgentCompaction(): void {\n\t\tconst mode = this.autoCompaction ? \"auto\" : \"manual\";\n\t\tthis.agent.setCompaction({\n\t\t\tcontextWindow: this.contextWindow,\n\t\t\tmode,\n\t\t\tsettings: {\n\t\t\t\treserveTokens: this.compactionSettings.reserveTokens,\n\t\t\t\tkeepRecentTokens: this.compactionSettings.keepRecentTokens,\n\t\t\t},\n\t\t\tonCompactionStart: () => {\n\t\t\t\tthis.isCompacting = true;\n\t\t\t\tthis.compactionLoader?.stop();\n\t\t\t\tthis.compactionLoader = new Loader(\n\t\t\t\t\tthis.ui,\n\t\t\t\t\t(s: string) => chalk.cyan(s),\n\t\t\t\t\t(s: string) => chalk.dim(s),\n\t\t\t\t\tthis.autoCompaction\n\t\t\t\t\t\t? \"Auto-compacting context... (Escape to cancel)\"\n\t\t\t\t\t\t: \"Compacting context... (Escape to cancel)\",\n\t\t\t\t);\n\t\t\t\tthis.compactionLoader.start();\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.pendingContainer.addChild(new Spacer(1));\n\t\t\t\tthis.pendingContainer.addChild(this.compactionLoader);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t},\n\t\t\tonCompactionComplete: (result: CompactionResult) => {\n\t\t\t\tthis.compactionLoader?.stop();\n\t\t\t\tthis.compactionLoader = null;\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.isCompacting = false;\n\n\t\t\t\tthis.rebuildChatFromSession();\n\t\t\t\tconst summaryComponent = new CompactionSummaryComponent(result.tokensBefore, result.summary);\n\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\tsummaryComponent.setExpanded(true);\n\t\t\t\t}\n\t\t\t\tthis.chatContainer.addChild(summaryComponent);\n\n\t\t\t\tthis.updateFooterTokens();\n\t\t\t\tif (this.options.verbose) {\n\t\t\t\t\tconst tokensAfter = estimateContextTokens([...this.agent.messages]);\n\t\t\t\t\tthis.showStatus(\n\t\t\t\t\t\tchalk.dim(\n\t\t\t\t\t\t\t`Compacted: ${result.tokensBefore.toLocaleString()} -> ${tokensAfter.toLocaleString()} tokens`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthis.ui.requestRender();\n\t\t\t},\n\t\t\tonCompactionError: (error: Error) => {\n\t\t\t\tthis.compactionLoader?.stop();\n\t\t\t\tthis.compactionLoader = null;\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.isCompacting = false;\n\n\t\t\t\tif (error.name === \"AbortError\" || error.message === \"Compaction cancelled\") {\n\t\t\t\t\tthis.showStatus(chalk.dim(\"Compaction cancelled.\"));\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.red(`Compaction failed: ${error.message}`));\n\t\t\t\t}\n\t\t\t\tthis.ui.requestRender();\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Rebuild the chat UI from session context after compaction.\n\t */\n\tprivate rebuildChatFromSession(): void {\n\t\tthis.chatContainer.clear();\n\n\t\tconst messages = this.agent.messages;\n\t\tfor (const msg of messages) {\n\t\t\tif (msg.role === \"user\") {\n\t\t\t\t// Check if this is a compaction summary\n\t\t\t\tconst content = msg.content;\n\t\t\t\tif (Array.isArray(content) && content.length > 0) {\n\t\t\t\t\tconst textBlock = content[0] as { type: string; text?: string };\n\t\t\t\t\tif (textBlock.type === \"text\" && textBlock.text?.startsWith('<summary type=\"compaction\"')) {\n\t\t\t\t\t\t// Skip compaction summaries in rebuild (they are injected by buildSessionContext)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (textBlock.type === \"text\" && textBlock.text?.startsWith('<summary type=\"branch\"')) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst text = extractTextFromMessage(msg);\n\t\t\t\tif (text) {\n\t\t\t\t\tthis.chatContainer.addChild(new UserMessageComponent(text, getMarkdownTheme()));\n\t\t\t\t}\n\t\t\t} else if (msg.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = msg as import(\"edge-pi\").AssistantModelMessage;\n\t\t\t\tconst textParts: string[] = [];\n\t\t\t\tfor (const block of assistantMsg.content) {\n\t\t\t\t\tconst b = block as {\n\t\t\t\t\t\ttype: string;\n\t\t\t\t\t\ttext?: string;\n\t\t\t\t\t\ttoolName?: string;\n\t\t\t\t\t\tinput?: unknown;\n\t\t\t\t\t\ttoolCallId?: string;\n\t\t\t\t\t};\n\t\t\t\t\tif (b.type === \"text\" && b.text) {\n\t\t\t\t\t\ttextParts.push(b.text);\n\t\t\t\t\t} else if (b.type === \"tool-call\" && b.toolName) {\n\t\t\t\t\t\tconst args =\n\t\t\t\t\t\t\ttypeof b.input === \"object\" && b.input !== null ? (b.input as Record<string, unknown>) : {};\n\t\t\t\t\t\tconst toolComp = new ToolExecutionComponent(b.toolName, args);\n\t\t\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\t\t\ttoolComp.setExpanded(true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Mark as completed (we don't have the result here, just show collapsed)\n\t\t\t\t\t\ttoolComp.updateResult({ text: \"(from history)\" }, false, false);\n\t\t\t\t\t\tthis.chatContainer.addChild(toolComp);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (textParts.length > 0) {\n\t\t\t\t\tconst comp = new AssistantMessageComponent(getMarkdownTheme());\n\t\t\t\t\tcomp.updateText(textParts.join(\"\"));\n\t\t\t\t\tthis.chatContainer.addChild(comp);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Skip tool messages in UI rebuild - they are consumed by tool-call components\n\t\t}\n\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Footer Token Tracking\n\t// ========================================================================\n\n\t/**\n\t * Update the footer with current token count information.\n\t */\n\tprivate updateFooterTokens(): void {\n\t\tconst contextTokens = estimateContextTokens([...this.agent.messages]);\n\t\tthis.footer.setTokenInfo(contextTokens, this.contextWindow);\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.ui?.requestRender();\n\t}\n\n\t/**\n\t * Check if the current provider is using an OAuth subscription credential.\n\t */\n\tprivate isSubscriptionProvider(): boolean {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) return false;\n\t\tconst cred = authStorage.get(this.currentProvider);\n\t\treturn cred?.type === \"oauth\";\n\t}\n\n\t/**\n\t * Replace the footer component and update token info.\n\t */\n\tprivate updateFooter(): void {\n\t\tthis.footer = new FooterComponent(this.currentProvider, this.currentModelId);\n\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\t\tthis.updateFooterTokens();\n\n\t\t// Replace footer in UI\n\t\tconst children = this.ui.children;\n\t\tchildren[children.length - 1] = this.footer;\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Startup Resource Display\n\t// ========================================================================\n\n\tprivate formatDisplayPath(p: string): string {\n\t\tconst home = process.env.HOME || process.env.USERPROFILE || \"\";\n\t\tif (home && p.startsWith(home)) {\n\t\t\treturn `~${p.slice(home.length)}`;\n\t\t}\n\t\treturn p;\n\t}\n\n\tprivate showLoadedResources(contextFiles: ContextFile[], skills: Skill[], prompts: PromptTemplate[]): void {\n\t\tconst sectionHeader = (name: string) => chalk.cyan(`[${name}]`);\n\n\t\tif (contextFiles.length > 0) {\n\t\t\tconst contextList = contextFiles.map((f) => chalk.dim(` ${this.formatDisplayPath(f.path)}`)).join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Context\")}\\n${contextList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (skills.length > 0) {\n\t\t\tconst skillList = skills.map((s) => chalk.dim(` ${this.formatDisplayPath(s.filePath)}`)).join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Skills\")}\\n${skillList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (prompts.length > 0) {\n\t\t\tconst promptList = prompts\n\t\t\t\t.map((p) => {\n\t\t\t\t\tconst sourceLabel = chalk.cyan(p.source);\n\t\t\t\t\treturn chalk.dim(` ${sourceLabel} /${p.name}`);\n\t\t\t\t})\n\t\t\t\t.join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Prompts\")}\\n${promptList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Commands\n\t// ========================================================================\n\n\tprivate showHelp(): void {\n\t\tconst helpText = [\n\t\t\tchalk.bold(\"Commands:\"),\n\t\t\t\" !<command> Run inline bash and include output in context\",\n\t\t\t\" !!<command> Run inline bash but exclude output from context\",\n\t\t\t\" /resume Resume a previous session\",\n\t\t\t\" /compact [text] Compact the session context (optional instructions)\",\n\t\t\t\" /auto-compact Toggle automatic context compaction\",\n\t\t\t\" /model Switch model (Ctrl+L)\",\n\t\t\t\" /login Login to an OAuth provider\",\n\t\t\t\" /logout Logout from an OAuth provider\",\n\t\t\t\" /skills List loaded skills\",\n\t\t\t\" /skill:<name> Invoke a skill by name\",\n\t\t\t\" /quit, /exit Exit the CLI\",\n\t\t].join(\"\\n\");\n\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(helpText, 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate showSkills(): void {\n\t\tconst { skills = [] } = this.options;\n\n\t\tif (skills.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No skills loaded.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines: string[] = [];\n\t\tfor (const skill of skills) {\n\t\t\tconst hidden = skill.disableModelInvocation ? chalk.dim(\" (hidden from model)\") : \"\";\n\t\t\tlines.push(` ${chalk.bold(skill.name)}${hidden}`);\n\t\t\tlines.push(chalk.dim(` ${skill.description}`));\n\t\t\tlines.push(chalk.dim(` ${skill.filePath}`));\n\t\t}\n\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(lines.join(\"\\n\"), 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate async handleSkillInvocation(skillName: string): Promise<void> {\n\t\tconst { skills = [] } = this.options;\n\t\tconst skill = skills.find((s) => s.name === skillName);\n\n\t\tif (!skill) {\n\t\t\tthis.showStatus(chalk.red(`Skill \"${skillName}\" not found.`));\n\t\t\treturn;\n\t\t}\n\n\t\tconst skillPrompt = `Please read and follow the instructions in the skill file: ${skill.filePath}`;\n\t\tthis.chatContainer.addChild(new UserMessageComponent(skillPrompt, getMarkdownTheme()));\n\t\tthis.ui.requestRender();\n\t\tawait this.streamPrompt(skillPrompt);\n\t}\n\n\t// ========================================================================\n\t// OAuth Login/Logout\n\t// ========================================================================\n\n\tprivate async handleLogin(): Promise<void> {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) {\n\t\t\tthis.showStatus(chalk.red(\"Auth storage not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst providers = authStorage.getProviders();\n\t\tif (providers.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No OAuth providers registered.\"));\n\t\t\treturn;\n\t\t}\n\n\t\t// Use SelectList overlay for provider selection\n\t\tconst items: SelectItem[] = providers.map((p) => {\n\t\t\tconst loggedIn = authStorage.get(p.id)?.type === \"oauth\" ? \" (logged in)\" : \"\";\n\t\t\treturn { value: p.id, label: `${p.name}${loggedIn}` };\n\t\t});\n\n\t\tconst selected = await this.showSelectList(\"Login to OAuth provider\", items);\n\t\tif (!selected) return;\n\n\t\tconst provider = providers.find((p) => p.id === selected);\n\t\tif (!provider) return;\n\n\t\tthis.showStatus(chalk.dim(`Logging in to ${provider.name}...`));\n\n\t\ttry {\n\t\t\tawait authStorage.login(provider.id, {\n\t\t\t\tonAuth: (info) => {\n\t\t\t\t\tconst lines = [chalk.bold(\"Open this URL in your browser:\"), chalk.cyan(info.url)];\n\t\t\t\t\tif (info.instructions) {\n\t\t\t\t\t\tlines.push(chalk.dim(info.instructions));\n\t\t\t\t\t}\n\t\t\t\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\t\t\t\tthis.chatContainer.addChild(new Text(lines.join(\"\\n\"), 1, 0));\n\t\t\t\t\tthis.ui.requestRender();\n\n\t\t\t\t\t// Try to open browser\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst { execSync } = require(\"node:child_process\") as typeof import(\"node:child_process\");\n\t\t\t\t\t\tconst platform = process.platform;\n\t\t\t\t\t\tif (platform === \"darwin\") {\n\t\t\t\t\t\t\texecSync(`open \"${info.url}\"`, { stdio: \"ignore\" });\n\t\t\t\t\t\t} else if (platform === \"linux\") {\n\t\t\t\t\t\t\texecSync(`xdg-open \"${info.url}\" 2>/dev/null || sensible-browser \"${info.url}\" 2>/dev/null`, {\n\t\t\t\t\t\t\t\tstdio: \"ignore\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else if (platform === \"win32\") {\n\t\t\t\t\t\t\texecSync(`start \"\" \"${info.url}\"`, { stdio: \"ignore\" });\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Silently fail - user can open manually\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tonPrompt: async (promptInfo) => {\n\t\t\t\t\t// Show prompt message and wait for user input\n\t\t\t\t\tthis.showStatus(chalk.dim(promptInfo.message));\n\t\t\t\t\tconst answer = await this.getUserInput();\n\t\t\t\t\treturn answer.trim();\n\t\t\t\t},\n\t\t\t\tonProgress: (message) => {\n\t\t\t\t\tthis.showStatus(chalk.dim(message));\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\t\t\tthis.ui.requestRender();\n\t\t\tthis.showStatus(chalk.green(`Logged in to ${provider.name}. Credentials saved.`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tif (msg !== \"Login cancelled\") {\n\t\t\t\tthis.showStatus(chalk.red(`Login failed: ${msg}`));\n\t\t\t} else {\n\t\t\t\tthis.showStatus(chalk.dim(\"Login cancelled.\"));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async handleLogout(): Promise<void> {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) {\n\t\t\tthis.showStatus(chalk.red(\"Auth storage not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst loggedIn = authStorage\n\t\t\t.list()\n\t\t\t.filter((id) => authStorage.get(id)?.type === \"oauth\")\n\t\t\t.map((id) => {\n\t\t\t\tconst provider = authStorage.getProvider(id);\n\t\t\t\treturn { id, name: provider?.name ?? id };\n\t\t\t});\n\n\t\tif (loggedIn.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No OAuth providers logged in. Use /login first.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst items: SelectItem[] = loggedIn.map((p) => ({\n\t\t\tvalue: p.id,\n\t\t\tlabel: p.name,\n\t\t}));\n\n\t\tconst selected = await this.showSelectList(\"Logout from OAuth provider\", items);\n\t\tif (!selected) return;\n\n\t\tconst entry = loggedIn.find((p) => p.id === selected);\n\t\tif (!entry) return;\n\n\t\tauthStorage.logout(entry.id);\n\t\tthis.showStatus(chalk.green(`Logged out of ${entry.name}.`));\n\t}\n\n\t// ========================================================================\n\t// Resume Session\n\t// ========================================================================\n\n\t/**\n\t * List session files from the session directory, sorted by modification time (newest first).\n\t * Returns metadata for each session including the first user message as a preview.\n\t */\n\tprivate listAvailableSessions(): { path: string; mtime: number; preview: string; timestamp: string }[] {\n\t\tconst { sessionDir } = this.options;\n\t\tif (!sessionDir || !existsSync(sessionDir)) return [];\n\n\t\ttry {\n\t\t\tconst files = readdirSync(sessionDir)\n\t\t\t\t.filter((f: string) => f.endsWith(\".jsonl\"))\n\t\t\t\t.map((f: string) => {\n\t\t\t\t\tconst filePath = join(sessionDir, f);\n\t\t\t\t\tconst mtime = statSync(filePath).mtime.getTime();\n\t\t\t\t\treturn { name: f, path: filePath, mtime };\n\t\t\t\t})\n\t\t\t\t.sort((a, b) => b.mtime - a.mtime);\n\n\t\t\tconst sessions: { path: string; mtime: number; preview: string; timestamp: string }[] = [];\n\t\t\tfor (const file of files) {\n\t\t\t\t// Skip the current session file\n\t\t\t\tif (this.agent.sessionManager?.getSessionFile() === file.path) continue;\n\n\t\t\t\tconst preview = this.getSessionPreview(file.path);\n\t\t\t\tconst timestamp = new Date(file.mtime).toLocaleString();\n\t\t\t\tsessions.push({ path: file.path, mtime: file.mtime, preview, timestamp });\n\t\t\t}\n\t\t\treturn sessions;\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Extract the first user message from a session file for preview.\n\t */\n\tprivate getSessionPreview(filePath: string): string {\n\t\ttry {\n\t\t\tconst content = readFileSync(filePath, \"utf-8\");\n\t\t\tconst lines = content.trim().split(\"\\n\");\n\t\t\tfor (const line of lines) {\n\t\t\t\tif (!line.trim()) continue;\n\t\t\t\ttry {\n\t\t\t\t\tconst entry = JSON.parse(line);\n\t\t\t\t\tif (entry.type === \"message\" && entry.message?.role === \"user\") {\n\t\t\t\t\t\tconst msg = entry.message;\n\t\t\t\t\t\tlet text = \"\";\n\t\t\t\t\t\tif (typeof msg.content === \"string\") {\n\t\t\t\t\t\t\ttext = msg.content;\n\t\t\t\t\t\t} else if (Array.isArray(msg.content)) {\n\t\t\t\t\t\t\tfor (const block of msg.content) {\n\t\t\t\t\t\t\t\tif (block.type === \"text\" && block.text) {\n\t\t\t\t\t\t\t\t\ttext = block.text;\n\t\t\t\t\t\t\t\t\tbreak;\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\t// Truncate and clean up for display\n\t\t\t\t\t\ttext = text.replace(/\\n/g, \" \").trim();\n\t\t\t\t\t\tif (text.length > 80) {\n\t\t\t\t\t\t\ttext = `${text.slice(0, 77)}...`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn text || \"(empty message)\";\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Skip malformed lines\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn \"(no messages)\";\n\t\t} catch {\n\t\t\treturn \"(unreadable)\";\n\t\t}\n\t}\n\n\t/**\n\t * Format a relative time string (e.g. \"2 hours ago\", \"3 days ago\").\n\t */\n\tprivate formatRelativeTime(mtime: number): string {\n\t\tconst now = Date.now();\n\t\tconst diffMs = now - mtime;\n\t\tconst diffSec = Math.floor(diffMs / 1000);\n\t\tconst diffMin = Math.floor(diffSec / 60);\n\t\tconst diffHour = Math.floor(diffMin / 60);\n\t\tconst diffDay = Math.floor(diffHour / 24);\n\n\t\tif (diffMin < 1) return \"just now\";\n\t\tif (diffMin < 60) return `${diffMin}m ago`;\n\t\tif (diffHour < 24) return `${diffHour}h ago`;\n\t\tif (diffDay < 30) return `${diffDay}d ago`;\n\t\treturn new Date(mtime).toLocaleDateString();\n\t}\n\n\t/**\n\t * Handle the /resume command: show a list of previous sessions and load the selected one.\n\t */\n\tprivate async handleResume(): Promise<void> {\n\t\tconst sessions = this.listAvailableSessions();\n\t\tif (sessions.length === 0) {\n\t\t\tthis.showStatus(chalk.yellow(\"No previous sessions found.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst items: SelectItem[] = sessions.map((s) => ({\n\t\t\tvalue: s.path,\n\t\t\tlabel: `${chalk.dim(this.formatRelativeTime(s.mtime))} ${s.preview}`,\n\t\t}));\n\n\t\tconst selected = await this.showSelectList(\"Resume session\", items);\n\t\tif (!selected) return;\n\n\t\tconst session = sessions.find((s) => s.path === selected);\n\t\tif (!session) return;\n\n\t\ttry {\n\t\t\t// Open the selected session and set on agent (auto-restores messages)\n\t\t\tconst sessionDir = this.options.sessionDir!;\n\t\t\tconst newSessionManager = SessionManagerClass.open(selected, sessionDir);\n\t\t\tthis.agent.sessionManager = newSessionManager;\n\n\t\t\t// Rebuild the chat UI\n\t\t\tthis.chatContainer.clear();\n\t\t\tthis.rebuildChatFromSession();\n\n\t\t\t// Update footer tokens\n\t\t\tthis.updateFooterTokens();\n\n\t\t\tconst msgCount = this.agent.messages.length;\n\t\t\tthis.showStatus(chalk.green(`Resumed session (${msgCount} messages)`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tthis.showStatus(chalk.red(`Failed to resume session: ${msg}`));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Select List (overlay pattern from pi-coding-agent)\n\t// ========================================================================\n\n\tprivate showSelectList(title: string, items: SelectItem[]): Promise<string | null> {\n\t\treturn new Promise((resolve) => {\n\t\t\tconst container = new Container();\n\n\t\t\tcontainer.addChild(new Spacer(1));\n\t\t\tcontainer.addChild(new Text(chalk.bold.cyan(title), 1, 0));\n\n\t\t\tconst selectList = new SelectList(items, Math.min(items.length, 10), getSelectListTheme());\n\t\t\tselectList.onSelect = (item) => {\n\t\t\t\t// Restore normal UI\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.editorContainer.addChild(this.editor);\n\t\t\t\tthis.ui.setFocus(this.editor);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\tresolve(item.value);\n\t\t\t};\n\t\t\tselectList.onCancel = () => {\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.editorContainer.addChild(this.editor);\n\t\t\t\tthis.ui.setFocus(this.editor);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\tresolve(null);\n\t\t\t};\n\t\t\tcontainer.addChild(selectList);\n\t\t\tcontainer.addChild(new Text(chalk.dim(\"↑↓ navigate • enter select • esc cancel\"), 1, 0));\n\t\t\tcontainer.addChild(new Spacer(1));\n\n\t\t\t// Replace editor area with select list\n\t\t\tthis.editorContainer.clear();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.pendingContainer.addChild(container);\n\t\t\tthis.ui.setFocus(selectList);\n\t\t\tthis.ui.requestRender();\n\t\t});\n\t}\n\n\t// ========================================================================\n\t// Status & Utilities\n\t// ========================================================================\n\n\tprivate showStatus(text: string): void {\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(text, 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate updateEditorBorderColor(): void {\n\t\tthis.editorTheme.borderColor = this.isBashMode ? (s: string) => chalk.yellow(s) : (s: string) => chalk.gray(s);\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate shutdown(): void {\n\t\tthis.ui.stop();\n\t\tconsole.log(chalk.dim(\"\\nGoodbye.\"));\n\t\tprocess.exit(0);\n\t}\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction extractTextFromMessage(msg: ModelMessage): string {\n\tif (msg.role === \"user\") {\n\t\tconst content = (msg as import(\"edge-pi\").UserModelMessage).content;\n\t\tif (typeof content === \"string\") return content;\n\t\tif (Array.isArray(content)) {\n\t\t\treturn content\n\t\t\t\t.filter((c): c is { type: \"text\"; text: string } => (c as { type: string }).type === \"text\")\n\t\t\t\t.map((c) => c.text)\n\t\t\t\t.join(\"\");\n\t\t}\n\t}\n\treturn \"\";\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "edge-pi-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "CLI for the edge-pi coding agent SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"@ai-sdk/openai": "^3.0.26",
|
|
28
28
|
"@mariozechner/pi-tui": "^0.50.9",
|
|
29
29
|
"chalk": "^5.5.0",
|
|
30
|
-
"edge-pi": "^0.
|
|
30
|
+
"edge-pi": "^0.4.1",
|
|
31
31
|
"proper-lockfile": "^4.1.2",
|
|
32
32
|
"yaml": "^2.7.1"
|
|
33
33
|
},
|