@alanwchat/coder 0.1.105 → 0.1.107
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/bin +2 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/package.json +3 -3
- package/bin.cjs +0 -3
package/bin
ADDED
package/dist/index.cjs
CHANGED
|
@@ -2888,7 +2888,7 @@ function setGlobalOptions(opts) {
|
|
|
2888
2888
|
}
|
|
2889
2889
|
|
|
2890
2890
|
// src/index.ts
|
|
2891
|
-
var VERSION = "0.1.
|
|
2891
|
+
var VERSION = "0.1.107";
|
|
2892
2892
|
async function main() {
|
|
2893
2893
|
const rawArgs = process.argv.slice(2);
|
|
2894
2894
|
if (rawArgs.includes("--help") || rawArgs.includes("-h")) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/config/index.ts", "../src/handlers/list-dir.ts", "../src/handlers/result.ts", "../src/handlers/read-file.ts", "../src/handlers/write-file.ts", "../src/handlers/replace-file.ts", "../src/handlers/edit-file.ts", "../src/handlers/replace-lines.ts", "../src/handlers/glob.ts", "../src/handlers/grep.ts", "../src/handlers/shell-manager.ts", "../src/handlers/shell.ts", "../src/handlers/await-shell.ts", "../src/handlers/list-shells.ts", "../src/handlers/kill-shell.ts", "../src/handlers/read-shell-logs.ts", "../src/handlers/web-search.ts", "../src/handlers/browse-page.ts", "../src/handlers/todos.ts", "../src/handlers/plans.ts", "../src/handlers/workspace-tree.ts", "../src/handlers/ask-question.ts", "../src/handlers/spawn-subagent.ts", "../src/handlers/index.ts", "../src/agent/llm-stream.ts", "../src/agent/runner.ts", "../src/agent/environment/index.ts", "../src/agent/session.ts", "../src/ui/index.ts", "../src/commands/run.ts", "../src/commands/ask.ts", "../src/commands/plan.ts", "../src/commands/repl.ts", "../src/commands/init.ts", "../src/commands/config.ts", "../src/commands/common.ts", "../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Coder CLI Configuration System\n *\n * Stores provider settings, API keys, model preferences, and CLI options\n * in a platform-appropriate config directory (e.g. ~/.config/coder/ on Linux).\n */\n\nimport { homedir, platform, EOL } from \"node:os\";\nimport { join, dirname } from \"node:path\";\nimport { mkdirSync, existsSync, readFileSync, writeFileSync } from \"node:fs\";\n\n// ---------------------------------------------------------------------------\n// Config directory resolution\n// ---------------------------------------------------------------------------\n\nfunction getConfigDir(): string {\n const home = homedir();\n const p = platform();\n\n if (p === \"win32\") {\n // Windows: %APPDATA%/Coder or fallback to %USERPROFILE%/.config/coder\n return process.env.APPDATA\n ? join(process.env.APPDATA, \"Coder\", \"cli\")\n : join(home, \".config\", \"coder\", \"cli\");\n }\n\n // macOS / Linux: $XDG_CONFIG_HOME/coder or ~/.config/coder\n if (process.env.XDG_CONFIG_HOME) {\n return join(process.env.XDG_CONFIG_HOME, \"coder\", \"cli\");\n }\n\n return join(home, \".config\", \"coder\", \"cli\");\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type ApiKeySource = \"manual\" | \"env\";\n\nexport type ProviderSettings = {\n apiKeySource: ApiKeySource;\n apiKey: string;\n apiKeyEnvVar: string;\n customBaseUrl: string;\n showUsage: boolean;\n};\n\nexport type ProviderId =\n | \"deepseek\"\n | \"glm\"\n | \"agnes\"\n | \"nvidia\"\n | \"minimax\"\n | \"custom\";\n\nexport type ModelDefinition = {\n id: string;\n label?: string;\n contextWindow: number;\n supportsThinking: boolean;\n supportsMultimodal: boolean;\n};\n\nexport type ResolvedProviderConfig = {\n provider: ProviderId;\n baseUrl: string;\n apiKeySource: ApiKeySource;\n apiKey: string;\n apiKeyEnvVar: string;\n models: ModelDefinition[];\n};\n\nexport type CoderCliConfig = {\n /** Active provider ID */\n activeProvider: ProviderId;\n /** Provider-specific settings */\n providers: Record<ProviderId, ProviderSettings>;\n /** Last used model ID */\n lastModel: string;\n /** Whether to show token usage after each response */\n showUsage: boolean;\n};\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\nconst PRESET_PROVIDERS: Record<Exclude<ProviderId, \"custom\">, { baseUrl: string; defaultApiKeyEnvVar: string }> = {\n deepseek: { baseUrl: \"https://api.deepseek.com\", defaultApiKeyEnvVar: \"DEEPSEEK_API_KEY\" },\n glm: { baseUrl: \"https://open.bigmodel.cn/api/paas/v4\", defaultApiKeyEnvVar: \"GLM_API_KEY\" },\n agnes: { baseUrl: \"https://api.agnesai.com\", defaultApiKeyEnvVar: \"AGNES_API_KEY\" },\n nvidia: { baseUrl: \"https://integrate.api.nvidia.com/v1\", defaultApiKeyEnvVar: \"NVIDIA_API_KEY\" },\n minimax: { baseUrl: \"https://api.minimax.chat/v1\", defaultApiKeyEnvVar: \"MINIMAX_API_KEY\" },\n};\n\nconst PRESET_MODELS: Record<Exclude<ProviderId, \"custom\">, ModelDefinition[]> = {\n deepseek: [\n { id: \"deepseek-v4-flash\", label: \"DeepSeek V4 Flash\", contextWindow: 1_000_000, supportsThinking: true, supportsMultimodal: false },\n { id: \"deepseek-chat\", label: \"DeepSeek Chat\", contextWindow: 1_000_000, supportsThinking: false, supportsMultimodal: true },\n ],\n glm: [\n { id: \"glm-4\", label: \"GLM-4\", contextWindow: 128_000, supportsThinking: false, supportsMultimodal: true },\n { id: \"glm-4-plus\", label: \"GLM-4 Plus\", contextWindow: 128_000, supportsThinking: true, supportsMultimodal: true },\n ],\n agnes: [\n { id: \"agnes-v3\", label: \"Agnes V3\", contextWindow: 128_000, supportsThinking: false, supportsMultimodal: true },\n ],\n nvidia: [],\n minimax: [\n { id: \"minimax-m1\", label: \"MiniMax M1\", contextWindow: 200_000, supportsThinking: true, supportsMultimodal: false },\n ],\n};\n\nfunction createDefaultProviderSettings(provider: ProviderId): ProviderSettings {\n const isCustom = provider === \"custom\";\n return {\n apiKeySource: \"env\",\n apiKey: \"\",\n apiKeyEnvVar: isCustom ? \"CUSTOM_API_KEY\" : PRESET_PROVIDERS[provider]?.defaultApiKeyEnvVar ?? \"API_KEY\",\n customBaseUrl: isCustom ? \"https://api.example.com/v1\" : \"\",\n showUsage: false,\n };\n}\n\nconst PROVIDER_IDS: ProviderId[] = [\"deepseek\", \"glm\", \"agnes\", \"nvidia\", \"minimax\", \"custom\"];\n\nfunction createDefaultConfig(): CoderCliConfig {\n const providers = {} as Record<ProviderId, ProviderSettings>;\n for (const id of PROVIDER_IDS) {\n providers[id] = createDefaultProviderSettings(id);\n }\n return {\n activeProvider: \"deepseek\",\n providers,\n lastModel: \"deepseek-v4-flash\",\n showUsage: false,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Config file I/O\n// ---------------------------------------------------------------------------\n\nconst CONFIG_FILE = \"config.json\";\n\nfunction getConfigFilePath(): string {\n return join(getConfigDir(), CONFIG_FILE);\n}\n\nfunction ensureConfigDir(): void {\n const dir = getConfigDir();\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n}\n\nexport function loadConfig(): CoderCliConfig {\n const configPath = getConfigFilePath();\n if (!existsSync(configPath)) {\n const defaults = createDefaultConfig();\n saveConfig(defaults);\n return defaults;\n }\n\n try {\n const raw = readFileSync(configPath, \"utf-8\");\n const parsed = JSON.parse(raw);\n return mergeWithDefaults(parsed);\n } catch {\n const defaults = createDefaultConfig();\n saveConfig(defaults);\n return defaults;\n }\n}\n\nfunction mergeWithDefaults(raw: Record<string, unknown>): CoderCliConfig {\n const defaults = createDefaultConfig();\n return {\n activeProvider: (PROVIDER_IDS as readonly string[]).includes(String(raw.activeProvider ?? \"\"))\n ? (raw.activeProvider as ProviderId)\n : defaults.activeProvider,\n providers: mergeProviders(raw.providers as Record<string, unknown> | undefined, defaults.providers),\n lastModel: typeof raw.lastModel === \"string\" ? raw.lastModel : defaults.lastModel,\n showUsage: typeof raw.showUsage === \"boolean\" ? raw.showUsage : defaults.showUsage,\n };\n}\n\nfunction mergeProviders(\n raw: Record<string, unknown> | undefined,\n defaults: Record<ProviderId, ProviderSettings>,\n): Record<ProviderId, ProviderSettings> {\n const result = { ...defaults };\n if (!raw || typeof raw !== \"object\") {\n return result;\n }\n\n for (const id of PROVIDER_IDS) {\n const p = (raw as Record<string, Record<string, unknown>>)[id];\n if (p && typeof p === \"object\") {\n result[id] = {\n apiKeySource: p.apiKeySource === \"manual\" ? \"manual\" : \"env\",\n apiKey: typeof p.apiKey === \"string\" ? p.apiKey : result[id].apiKey,\n apiKeyEnvVar: typeof p.apiKeyEnvVar === \"string\" ? p.apiKeyEnvVar : result[id].apiKeyEnvVar,\n customBaseUrl: typeof p.customBaseUrl === \"string\" ? p.customBaseUrl : result[id].customBaseUrl,\n showUsage: typeof p.showUsage === \"boolean\" ? p.showUsage : result[id].showUsage,\n };\n }\n }\n\n return result;\n}\n\nexport function saveConfig(config: CoderCliConfig): void {\n ensureConfigDir();\n const configPath = getConfigFilePath();\n writeFileSync(configPath, JSON.stringify(config, null, 2), \"utf-8\");\n}\n\n// ---------------------------------------------------------------------------\n// Provider config resolution\n// ---------------------------------------------------------------------------\n\nexport function resolveProviderConfig(config: CoderCliConfig, providerId?: ProviderId): ResolvedProviderConfig {\n const provider = providerId ?? config.activeProvider;\n const settings = config.providers[provider];\n\n if (provider === \"custom\") {\n if (!settings.customBaseUrl.trim()) {\n throw new Error(\n \"Custom provider selected but no base URL configured. \" +\n \"Run `coder config` or manually edit the config file.\"\n );\n }\n return {\n provider,\n baseUrl: settings.customBaseUrl.trim(),\n apiKeySource: settings.apiKeySource,\n apiKey: settings.apiKey,\n apiKeyEnvVar: settings.apiKeyEnvVar.trim(),\n models: [],\n };\n }\n\n const preset = PRESET_PROVIDERS[provider];\n if (!preset) {\n throw new Error(`Unknown provider: ${provider}`);\n }\n\n return {\n provider,\n baseUrl: settings.customBaseUrl.trim() || preset.baseUrl,\n apiKeySource: settings.apiKeySource,\n apiKey: settings.apiKey,\n apiKeyEnvVar: settings.apiKeyEnvVar.trim() || preset.defaultApiKeyEnvVar,\n models: settings.customBaseUrl.trim()\n ? []\n : PRESET_MODELS[provider] ?? [],\n };\n}\n\nexport function resolveApiKey(resolved: ResolvedProviderConfig): string {\n if (resolved.apiKeySource === \"manual\") {\n if (!resolved.apiKey.trim()) {\n throw new Error(\n `API key for ${resolved.provider} is not configured. ` +\n \"Set it with: coder config providers.<provider>.apiKey <key>\"\n );\n }\n return resolved.apiKey.trim();\n }\n\n // Read from environment variable\n const envVar = resolved.apiKeyEnvVar;\n const envValue = process.env[envVar]?.trim();\n if (!envValue) {\n throw new Error(\n `Environment variable ${envVar} is not set. ` +\n `Set it with: export ${envVar}=<your-api-key>`\n );\n }\n return envValue;\n}\n\n// ---------------------------------------------------------------------------\n// Config directory path (exported for diagnostics)\n// ---------------------------------------------------------------------------\n\nexport function getConfigDirPath(): string {\n return getConfigDir();\n}\n\nexport function getConfigFilePathExplicit(): string {\n return getConfigFilePath();\n}\n", "import { readdirSync, statSync } from \"node:fs\";\nimport { resolve, relative } from \"node:path\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ListDirEntry, ToolHandler } from \"./types\";\n\ntype ListDirArgs = {\n path: string;\n recursive?: boolean;\n max_depth?: number;\n show_hidden?: boolean;\n};\n\nexport const listDirHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as ListDirArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"list_dir\", \"workspace_required\", \"No workspace directory set\");\n }\n\n const dirPath = resolve(context.workspaceDir, args.path);\n const maxDepth = args.recursive ? (args.max_depth ?? 1) : 1;\n\n try {\n const entries = walkDirectory(dirPath, 0, maxDepth, args.show_hidden ?? false);\n return toolSuccess(\"list_dir\", {\n path: relative(context.workspaceDir, dirPath),\n entries,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"list_dir\", \"read_error\", message);\n }\n};\n\nfunction walkDirectory(\n dirPath: string,\n currentDepth: number,\n maxDepth: number,\n showHidden: boolean,\n): ListDirEntry[] {\n const results: ListDirEntry[] = [];\n\n let entries: string[];\n try {\n entries = readdirSync(dirPath);\n } catch {\n return results;\n }\n\n for (const name of entries) {\n if (!showHidden && name.startsWith(\".\")) {\n continue;\n }\n\n const fullPath = resolve(dirPath, name);\n let stats: ReturnType<typeof statSync>;\n try {\n stats = statSync(fullPath);\n } catch {\n continue;\n }\n\n results.push({\n name,\n path: fullPath,\n isDir: stats.isDirectory(),\n size: stats.isFile() ? stats.size : undefined,\n });\n\n if (stats.isDirectory() && currentDepth < maxDepth) {\n results.push(...walkDirectory(fullPath, currentDepth + 1, maxDepth, showHidden));\n }\n }\n\n return results;\n}\n", "export function toolSuccess(tool: string, data: unknown): { ok: true; tool: string; data: unknown } {\n return { ok: true, tool, data };\n}\n\nexport function toolFailure(\n tool: string,\n code: string,\n message: string,\n): { ok: false; tool: string; error: { code: string; message: string } } {\n return {\n ok: false,\n tool,\n error: { code, message },\n };\n}\n", "import { readFileSync, statSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype ReadFileArgs = {\n path: string;\n start_line?: number;\n max_lines?: number;\n respect_gitignore?: boolean;\n};\n\nexport const readFileHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as ReadFileArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"read_file\", \"workspace_required\", \"No workspace directory set\");\n }\n\n const filePath = resolve(context.workspaceDir, args.path);\n const startLine = args.start_line ?? 1;\n const maxLines = args.max_lines ?? 500;\n\n try {\n statSync(filePath); // Check file exists\n const content = readFileSync(filePath, \"utf-8\");\n const lines = content.split(\"\\n\");\n const totalLines = lines.length;\n\n // Pagination\n const endLine = Math.min(startLine + maxLines - 1, totalLines);\n const paginatedContent = lines.slice(startLine - 1, endLine).join(\"\\n\");\n\n return toolSuccess(\"read_file\", {\n path: filePath,\n encoding: \"utf-8\",\n mimeType: \"text/plain\",\n content: paginatedContent,\n totalLines,\n startLine,\n endLine,\n truncated: endLine < totalLines,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"ENOENT\")) {\n return toolFailure(\"read_file\", \"not_found\", `File not found: ${filePath}`);\n }\n return toolFailure(\"read_file\", \"read_error\", message);\n }\n};\n", "import { writeFileSync, mkdirSync, existsSync } from \"node:fs\";\nimport { resolve, dirname } from \"node:path\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype WriteFileArgs = {\n path: string;\n content: string;\n create_parent_dirs?: boolean;\n};\n\nexport const writeFileHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as WriteFileArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"write_file\", \"workspace_required\", \"No workspace directory set\");\n }\n\n const filePath = resolve(context.workspaceDir, args.path);\n\n // Prevent writing outside workspace\n if (!filePath.startsWith(context.workspaceDir.replace(/\\\\/g, \"/\").replace(/\\\\/g, \"/\"))) {\n return toolFailure(\"write_file\", \"path_escape\", \"Path escapes workspace directory\");\n }\n\n if (existsSync(filePath)) {\n return toolFailure(\"write_file\", \"file_exists\", `File already exists: ${args.path}. Use replace_file to overwrite.`);\n }\n\n try {\n if (args.create_parent_dirs !== false) {\n const parentDir = dirname(filePath);\n if (!existsSync(parentDir)) {\n mkdirSync(parentDir, { recursive: true });\n }\n }\n\n const lines = args.content.split(\"\\n\");\n writeFileSync(filePath, args.content, \"utf-8\");\n\n return toolSuccess(\"write_file\", {\n path: args.path,\n action: \"created\",\n bytesWritten: Buffer.byteLength(args.content, \"utf-8\"),\n linesAdded: lines.length,\n linesRemoved: 0,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"write_file\", \"write_error\", message);\n }\n};\n", "import { writeFileSync, readFileSync, existsSync, mkdirSync } from \"node:fs\";\nimport { resolve, dirname } from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype ReplaceFileArgs = {\n path: string;\n content: string;\n expected_sha256?: string;\n create_backup?: boolean;\n respect_gitignore?: boolean;\n};\n\nexport const replaceFileHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as ReplaceFileArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"replace_file\", \"workspace_required\", \"No workspace directory set\");\n }\n\n const filePath = resolve(context.workspaceDir, args.path);\n\n if (!existsSync(filePath)) {\n return toolFailure(\"replace_file\", \"not_found\", `File not found: ${args.path}`);\n }\n\n try {\n const oldContent = readFileSync(filePath, \"utf-8\");\n const oldLines = oldContent.split(\"\\n\");\n const newLines = args.content.split(\"\\n\");\n\n // Verify SHA256 if provided\n if (args.expected_sha256) {\n const actualHash = createHash(\"sha256\").update(oldContent).digest(\"hex\");\n if (actualHash !== args.expected_sha256) {\n return toolFailure(\n \"replace_file\",\n \"content_changed\",\n `File content changed since last read. Expected SHA256: ${args.expected_sha256}, got: ${actualHash}. Read the file again to get the latest content.`,\n );\n }\n }\n\n writeFileSync(filePath, args.content, \"utf-8\");\n\n return toolSuccess(\"replace_file\", {\n path: args.path,\n action: \"replaced\",\n bytesWritten: Buffer.byteLength(args.content, \"utf-8\"),\n linesAdded: newLines.length,\n linesRemoved: oldLines.length,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"replace_file\", \"write_error\", message);\n }\n};\n", "import { readFileSync, writeFileSync, existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype EditFileArgs = {\n path: string;\n old_string: string;\n new_string: string;\n expected_sha256?: string;\n replace_all?: boolean;\n create_backup?: boolean;\n respect_gitignore?: boolean;\n};\n\nexport const editFileHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as EditFileArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"edit_file\", \"workspace_required\", \"No workspace directory set\");\n }\n\n const filePath = resolve(context.workspaceDir, args.path);\n\n if (!existsSync(filePath)) {\n return toolFailure(\"edit_file\", \"not_found\", `File not found: ${args.path}`);\n }\n\n try {\n const oldContent = readFileSync(filePath, \"utf-8\");\n const oldLines = oldContent.split(\"\\n\");\n\n // Verify SHA256 if provided\n if (args.expected_sha256) {\n const actualHash = createHash(\"sha256\").update(oldContent).digest(\"hex\");\n if (actualHash !== args.expected_sha256) {\n return toolFailure(\n \"edit_file\",\n \"content_changed\",\n `File content changed since last read. Expected SHA256: ${args.expected_sha256}, got: ${actualHash}.`,\n );\n }\n }\n\n // Apply search-and-replace\n let newContent: string;\n if (args.replace_all) {\n newContent = oldContent.split(args.old_string).join(args.new_string);\n } else {\n const index = oldContent.indexOf(args.old_string);\n if (index === -1) {\n return toolFailure(\n \"edit_file\",\n \"string_not_found\",\n `Could not find the exact text to replace in ${args.path}. The text must match exactly.`,\n );\n }\n newContent = oldContent.slice(0, index) + args.new_string + oldContent.slice(index + args.old_string.length);\n }\n\n const newLines = newContent.split(\"\\n\");\n writeFileSync(filePath, newContent, \"utf-8\");\n\n // Calculate diff stats\n const linesAdded = newLines.length - oldLines.length;\n const linesRemoved = oldLines.length - newLines.length;\n\n return toolSuccess(\"edit_file\", {\n path: args.path,\n action: \"modified\",\n bytesWritten: Buffer.byteLength(newContent, \"utf-8\"),\n linesAdded: linesAdded > 0 ? linesAdded : 0,\n linesRemoved: linesRemoved > 0 ? linesRemoved : 0,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"edit_file\", \"write_error\", message);\n }\n};\n", "import { readFileSync, writeFileSync, existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype ReplaceLinesArgs = {\n path: string;\n start_line: number;\n end_line: number;\n content: string;\n expected_sha256?: string;\n create_backup?: boolean;\n respect_gitignore?: boolean;\n};\n\nexport const replaceLinesHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as ReplaceLinesArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"replace_lines\", \"workspace_required\", \"No workspace directory set\");\n }\n\n const filePath = resolve(context.workspaceDir, args.path);\n\n if (!existsSync(filePath)) {\n return toolFailure(\"replace_lines\", \"not_found\", `File not found: ${args.path}`);\n }\n\n try {\n const oldContent = readFileSync(filePath, \"utf-8\");\n const oldLines = oldContent.split(\"\\n\");\n\n // Verify SHA256 if provided\n if (args.expected_sha256) {\n const actualHash = createHash(\"sha256\").update(oldContent).digest(\"hex\");\n if (actualHash !== args.expected_sha256) {\n return toolFailure(\n \"replace_lines\",\n \"content_changed\",\n `File content changed since last read. Expected SHA256: ${args.expected_sha256}, got: ${actualHash}.`,\n );\n }\n }\n\n // Validate line range\n if (args.start_line < 1 || args.end_line < args.start_line || args.start_line > oldLines.length) {\n return toolFailure(\n \"replace_lines\",\n \"invalid_range\",\n `Invalid line range: ${args.start_line}-${args.end_line}. File has ${oldLines.length} lines.`,\n );\n }\n\n const endLine = Math.min(args.end_line, oldLines.length);\n const replacementLines = args.content === \"\" ? [] : args.content.split(\"\\n\");\n\n // Build new content: lines before start + replacement + lines after end\n const before = oldLines.slice(0, args.start_line - 1);\n const after = oldLines.slice(endLine);\n const newContent = [...before, ...replacementLines, ...after].join(\"\\n\");\n\n writeFileSync(filePath, newContent, \"utf-8\");\n\n return toolSuccess(\"replace_lines\", {\n path: args.path,\n action: \"modified\",\n bytesWritten: Buffer.byteLength(newContent, \"utf-8\"),\n linesAdded: replacementLines.length,\n linesRemoved: endLine - args.start_line + 1,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"replace_lines\", \"write_error\", message);\n }\n};\n", "import { readdirSync, statSync } from \"node:fs\";\nimport { resolve, relative, sep } from \"node:path\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype GlobArgs = {\n glob_pattern: string;\n target_directory?: string;\n head_limit?: number;\n respect_gitignore?: boolean;\n};\n\n/**\n * Simple glob matching \u2014 converts glob patterns to regex and walks directories.\n * For complex patterns, the CLI agent can use the shell tool to call the real `find` or `dir` command.\n */\nexport const globHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as GlobArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"glob\", \"workspace_required\", \"No workspace directory set\");\n }\n\n const searchDir = args.target_directory\n ? resolve(context.workspaceDir, args.target_directory)\n : context.workspaceDir;\n\n const headLimit = args.head_limit ?? 100;\n const pattern = args.glob_pattern;\n\n try {\n const matches = simpleGlob(searchDir, pattern, context.workspaceDir, headLimit);\n return toolSuccess(\"glob\", {\n pattern,\n targetDirectory: relative(context.workspaceDir, searchDir),\n matches: matches.slice(0, headLimit),\n totalMatches: matches.length,\n truncated: matches.length > headLimit,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"glob\", \"search_error\", message);\n }\n};\n\nfunction simpleGlob(\n rootDir: string,\n pattern: string,\n workspaceDir: string,\n limit: number,\n): string[] {\n const results: string[] = [];\n\n // Simple recursive walk \u2014 for production, use a proper glob library\n function walk(dir: string): void {\n if (results.length >= limit) return;\n\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n\n for (const name of entries) {\n if (results.length >= limit) return;\n if (name.startsWith(\".\")) continue;\n\n const fullPath = resolve(dir, name);\n const relPath = relative(workspaceDir, fullPath).replace(/\\\\/g, \"/\");\n\n let stats: ReturnType<typeof statSync>;\n try {\n stats = statSync(fullPath);\n } catch {\n continue;\n }\n\n // Simple glob matching: check if pattern matches the relative path\n const regex = globToRegex(pattern);\n if (regex.test(relPath)) {\n results.push(relPath);\n }\n\n if (stats.isDirectory()) {\n walk(fullPath);\n }\n }\n }\n\n walk(rootDir);\n\n // Sort for consistent output\n results.sort();\n return results;\n}\n\nfunction globToRegex(pattern: string): RegExp {\n let regexStr = \"^\";\n let i = 0;\n\n while (i < pattern.length) {\n const ch = pattern[i];\n if (ch === \"*\") {\n if (i + 1 < pattern.length && pattern[i + 1] === \"*\") {\n // ** \u2014 match everything including path separators\n regexStr += \".*\";\n i += 2;\n // Handle **/ pattern\n if (i < pattern.length && pattern[i] === \"/\") {\n regexStr += \"/?\";\n i++;\n }\n } else {\n // * \u2014 match anything except path separator\n regexStr += \"[^/]*\";\n i++;\n }\n } else if (ch === \"?\") {\n regexStr += \"[^/]\";\n i++;\n } else if (ch === \".\" || ch === \"+\" || ch === \"(\" || ch === \")\" || ch === \"[\" || ch === \"]\" || ch === \"{\"\n || ch === \"}\" || ch === \"\\\\\" || ch === \"^\" || ch === \"$\" || ch === \"|\") {\n regexStr += \"\\\\\" + ch;\n i++;\n } else {\n regexStr += ch;\n i++;\n }\n }\n\n regexStr += \"$\";\n return new RegExp(regexStr, \"i\");\n}\n", "import { readFileSync, readdirSync, statSync } from \"node:fs\";\nimport { resolve, relative } from \"node:path\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { GrepData, ToolHandler } from \"./types\";\n\ntype GrepArgs = {\n pattern: string;\n path?: string;\n glob?: string;\n output_mode?: \"content\" | \"files_with_matches\" | \"count\";\n case_insensitive?: boolean;\n context_before?: number;\n context_after?: number;\n context?: number;\n head_limit?: number;\n offset?: number;\n multiline?: boolean;\n respect_gitignore?: boolean;\n};\n\nexport const grepHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as GrepArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"grep\", \"workspace_required\", \"No workspace directory set\");\n }\n\n const searchPath = args.path\n ? resolve(context.workspaceDir, args.path)\n : context.workspaceDir;\n\n const headLimit = args.head_limit ?? 200;\n const offset = args.offset ?? 0;\n const ctxBefore = args.context_before ?? args.context ?? 0;\n const ctxAfter = args.context_after ?? args.context ?? 0;\n const flags = args.case_insensitive ? \"gi\" : \"g\";\n const outputMode = args.output_mode ?? \"content\";\n const multiline = args.multiline ?? false;\n\n try {\n let searchRegex: RegExp;\n try {\n // For multiline, use s flag so . matches newlines\n const regexFlags = multiline ? `${flags}s` : flags;\n searchRegex = new RegExp(args.pattern, regexFlags);\n } catch {\n return toolFailure(\"grep\", \"invalid_pattern\", `Invalid regex pattern: ${args.pattern}`);\n }\n\n const files = collectFiles(searchPath);\n const matches: NonNullable<GrepData[\"matches\"]> = [];\n const fileMatchSet: Set<string> = new Set();\n /** Per-file match count for \"count\" mode. */\n const fileCountMap: Record<string, number> = {};\n let totalMatches = 0;\n let skippedFiles = 0;\n\n for (const file of files) {\n if (totalMatches >= headLimit + offset) break;\n\n let content: string;\n try {\n content = readFileSync(file, \"utf-8\");\n } catch {\n skippedFiles++;\n continue;\n }\n\n const relativePath = relative(context.workspaceDir, file);\n let fileHasMatch = false;\n let fileMatchCount = 0;\n\n if (multiline) {\n // Multiline mode: test the regex against the full content\n const lines = content.split(\"\\n\");\n searchRegex.lastIndex = 0;\n const fullMatch = searchRegex.exec(content);\n if (fullMatch) {\n fileHasMatch = true;\n // Count matches across the full content\n do {\n totalMatches++;\n fileMatchCount++;\n searchRegex.lastIndex = fullMatch.index + 1;\n } while (searchRegex.exec(content));\n searchRegex.lastIndex = 0;\n\n if (outputMode === \"content\") {\n matches.push({\n path: relativePath,\n lineNumber: 1,\n line: fullMatch[0].slice(0, 200),\n });\n }\n }\n } else {\n // Line-by-line mode\n const lines = content.split(\"\\n\");\n for (let lineNum = 0; lineNum < lines.length; lineNum++) {\n searchRegex.lastIndex = 0;\n if (searchRegex.test(lines[lineNum])) {\n totalMatches++;\n fileMatchCount++;\n\n if (outputMode === \"files_with_matches\") {\n fileMatchSet.add(relativePath);\n fileHasMatch = true;\n continue;\n }\n\n if (outputMode === \"count\") {\n fileHasMatch = true;\n continue;\n }\n\n // content mode\n if (totalMatches > offset && totalMatches <= offset + headLimit) {\n const contextBeforeLines = ctxBefore > 0\n ? lines.slice(Math.max(0, lineNum - ctxBefore), lineNum)\n : undefined;\n const contextAfterLines = ctxAfter > 0\n ? lines.slice(lineNum + 1, lineNum + 1 + ctxAfter)\n : undefined;\n\n matches.push({\n path: relativePath,\n lineNumber: lineNum + 1,\n line: lines[lineNum],\n contextBefore: contextBeforeLines?.length ? contextBeforeLines : undefined,\n contextAfter: contextAfterLines?.length ? contextAfterLines : undefined,\n });\n }\n }\n }\n }\n\n if (fileHasMatch || fileMatchCount > 0) {\n fileMatchSet.add(relativePath);\n fileCountMap[relativePath] = fileMatchCount;\n }\n\n if (outputMode === \"files_with_matches\" && fileMatchSet.size >= headLimit) {\n break;\n }\n }\n\n const result: GrepData = {\n pattern: args.pattern,\n path: relative(context.workspaceDir, searchPath),\n outputMode,\n totalMatches,\n truncated: totalMatches > headLimit,\n };\n\n if (outputMode === \"content\") {\n result.matches = matches;\n } else if (outputMode === \"files_with_matches\") {\n result.files = [...fileMatchSet];\n } else if (outputMode === \"count\") {\n result.files = [...fileMatchSet];\n result.fileCounts = fileCountMap;\n }\n\n if (skippedFiles > 0) {\n result.skippedFiles = skippedFiles;\n }\n\n return toolSuccess(\"grep\", result);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"grep\", \"search_error\", message);\n }\n};\n\nfunction collectFiles(dirPath: string): string[] {\n // If the path points directly to a file, return it as-is\n try {\n const stats = statSync(dirPath);\n if (stats.isFile()) {\n return [dirPath];\n }\n } catch {\n return [];\n }\n\n // Otherwise walk the directory tree\n const results: string[] = [];\n\n function walk(dir: string): void {\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n\n for (const name of entries) {\n if (name === \".git\" || name === \"node_modules\") continue;\n\n const fullPath = resolve(dir, name);\n let stats: ReturnType<typeof statSync>;\n try {\n stats = statSync(fullPath);\n } catch {\n continue;\n }\n\n if (stats.isDirectory()) {\n walk(fullPath);\n } else if (stats.isFile() && stats.size > 0) {\n results.push(fullPath);\n }\n }\n }\n\n walk(dirPath);\n return results;\n}\n", "/**\n * Shell process registry for the Coder CLI.\n * Tracks shell processes started by the agent.\n */\n\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport type { ShellData, ShellInfo, ShellStatus } from \"./types\";\nimport { resolve } from \"node:path\";\n\ntype ManagedShell = {\n shellId: string;\n command: string;\n description?: string;\n process: ChildProcess;\n status: ShellStatus;\n exitCode?: number;\n stdout: string;\n stderr: string;\n workingDirectory: string;\n startedAtMs: number;\n taskId?: string;\n};\n\nconst shells = new Map<string, ManagedShell>();\n\nlet shellCounter = 0;\n\nfunction generateShellId(): string {\n shellCounter++;\n return `shell-${Date.now()}-${shellCounter}`;\n}\n\nexport async function executeShell(\n command: string,\n options: {\n workingDirectory?: string;\n description?: string;\n blockUntilMs?: number;\n taskId?: string;\n workspaceDir?: string | null;\n },\n): Promise<ShellData> {\n const shellId = generateShellId();\n const cwd = options.workspaceDir\n ? options.workingDirectory\n ? resolve(options.workspaceDir, options.workingDirectory)\n : options.workspaceDir\n : process.cwd();\n\n const startTime = Date.now();\n const blockMs = options.blockUntilMs ?? 30000;\n\n // Determine shell\n const isWindows = process.platform === \"win32\";\n const shellCmd = isWindows ? \"cmd.exe\" : \"/bin/sh\";\n const shellArgs = isWindows ? [\"/c\", command] : [\"-c\", command];\n\n return new Promise((resolvePromise) => {\n const child = spawn(shellCmd, shellArgs, {\n cwd,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n shell: false,\n env: { ...process.env },\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let settled = false;\n\n const shell: ManagedShell = {\n shellId,\n command,\n description: options.description,\n process: child,\n status: \"running\",\n stdout,\n stderr,\n workingDirectory: cwd,\n startedAtMs: startTime,\n taskId: options.taskId,\n };\n\n child.stdout?.on(\"data\", (data: Buffer) => {\n stdout += data.toString();\n shell.stdout = stdout;\n });\n\n child.stderr?.on(\"data\", (data: Buffer) => {\n stderr += data.toString();\n shell.stderr = stderr;\n });\n\n const finish = (status: ShellStatus, code?: number) => {\n if (settled) return;\n settled = true;\n shell.status = status;\n shell.exitCode = code;\n const duration = Date.now() - startTime;\n\n const stdoutBytes = Buffer.byteLength(stdout);\n const stderrBytes = Buffer.byteLength(stderr);\n\n // Remove from registry if background shell\n // (sync shells are removed automatically)\n if (blockMs <= 0) {\n // Background \u2014 keep in registry\n shells.set(shellId, shell);\n } else {\n shells.delete(shellId);\n }\n\n resolvePromise({\n command,\n description: options.description,\n workingDirectory: cwd,\n stdout,\n stderr,\n stdoutTruncated: stdoutBytes > 65536,\n stderrTruncated: stderrBytes > 65536,\n stdoutTotalBytes: stdoutBytes,\n stderrTotalBytes: stderrBytes,\n exitCode: code,\n durationMs: duration,\n status,\n shellId: blockMs <= 0 ? shellId : undefined,\n });\n };\n\n child.on(\"error\", (err) => {\n stderr += err.message;\n finish(\"failed\", 1);\n });\n\n child.on(\"close\", (code) => {\n finish(code === 0 ? \"completed\" : \"failed\", code ?? undefined);\n });\n\n // Timeout handling\n if (blockMs > 0) {\n const timeout = setTimeout(() => {\n if (!settled) {\n child.kill();\n finish(\"timeout\", undefined);\n }\n }, blockMs);\n\n child.on(\"close\", () => clearTimeout(timeout));\n }\n\n if (blockMs <= 0) {\n // Background mode \u2014 return immediately\n shells.set(shellId, shell);\n const stdoutBytes = Buffer.byteLength(stdout);\n const stderrBytes = Buffer.byteLength(stderr);\n resolvePromise({\n command,\n description: options.description,\n workingDirectory: cwd,\n stdout: \"\",\n stderr: \"\",\n stdoutTruncated: false,\n stderrTruncated: false,\n stdoutTotalBytes: 0,\n stderrTotalBytes: 0,\n exitCode: undefined,\n durationMs: 0,\n status: \"running\",\n shellId,\n });\n }\n });\n}\n\nexport async function awaitShell(\n shellId: string,\n blockUntilMs: number = 30000,\n): Promise<ShellData> {\n const shell = shells.get(shellId);\n if (!shell) {\n return {\n command: \"\",\n workingDirectory: \"\",\n stdout: \"\",\n stderr: \"\",\n stdoutTruncated: false,\n stderrTruncated: false,\n stdoutTotalBytes: 0,\n stderrTotalBytes: 0,\n durationMs: 0,\n status: \"completed\",\n shellId,\n };\n }\n\n // Wait for the process to complete (or timeout)\n return new Promise((resolvePromise) => {\n const startTime = Date.now();\n\n const checkProcess = () => {\n if (shell.exitCode !== undefined || shell.status !== \"running\") {\n const duration = Date.now() - startTime;\n const stdoutBytes = Buffer.byteLength(shell.stdout);\n const stderrBytes = Buffer.byteLength(shell.stderr);\n resolvePromise({\n command: shell.command,\n description: shell.description,\n workingDirectory: shell.workingDirectory,\n stdout: shell.stdout,\n stderr: shell.stderr,\n stdoutTruncated: stdoutBytes > 65536,\n stderrTruncated: stderrBytes > 65536,\n stdoutTotalBytes: stdoutBytes,\n stderrTotalBytes: stderrBytes,\n exitCode: shell.exitCode,\n durationMs: duration,\n status: shell.status,\n shellId,\n });\n return;\n }\n\n if (Date.now() - startTime > blockUntilMs) {\n const duration = Date.now() - startTime;\n const stdoutBytes = Buffer.byteLength(shell.stdout);\n const stderrBytes = Buffer.byteLength(shell.stderr);\n resolvePromise({\n command: shell.command,\n description: shell.description,\n workingDirectory: shell.workingDirectory,\n stdout: shell.stdout,\n stderr: shell.stderr,\n stdoutTruncated: stdoutBytes > 65536,\n stderrTruncated: stderrBytes > 65536,\n stdoutTotalBytes: stdoutBytes,\n stderrTotalBytes: stderrBytes,\n exitCode: shell.exitCode,\n durationMs: duration,\n status: shell.status,\n shellId,\n });\n return;\n }\n\n setTimeout(checkProcess, 50);\n };\n\n // Also listen for close event\n shell.process.on(\"close\", () => {\n checkProcess();\n });\n\n checkProcess();\n });\n}\n\nexport function listShells(statusFilter?: string): ShellInfo[] {\n const result: ShellInfo[] = [];\n\n for (const shell of shells.values()) {\n if (statusFilter && shell.status !== statusFilter && statusFilter !== \"all\") {\n continue;\n }\n\n result.push({\n shellId: shell.shellId,\n command: shell.command,\n description: shell.description,\n workingDirectory: shell.workingDirectory,\n status: shell.status,\n exitCode: shell.exitCode,\n startedAtMs: shell.startedAtMs,\n taskId: shell.taskId,\n stdout: shell.stdout,\n stderr: shell.stderr,\n stdoutTruncated: Buffer.byteLength(shell.stdout) > 65536,\n stderrTruncated: Buffer.byteLength(shell.stderr) > 65536,\n });\n }\n\n return result;\n}\n\nexport function killShell(shellId: string): boolean {\n const shell = shells.get(shellId);\n if (!shell) return false;\n\n try {\n shell.process.kill(\"SIGTERM\");\n shell.status = \"cancelled\";\n setTimeout(() => {\n try {\n shell.process.kill(\"SIGKILL\");\n } catch {\n // Already dead\n }\n }, 2000);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function readShellLogs(\n shellId: string,\n stream: \"stdout\" | \"stderr\",\n offset: number = 0,\n limit: number = 4096,\n): { data: string; offset: number; totalBytes: number; truncated: boolean } {\n const shell = shells.get(shellId);\n if (!shell) {\n return { data: \"\", offset: 0, totalBytes: 0, truncated: false };\n }\n\n const content = stream === \"stdout\" ? shell.stdout : shell.stderr;\n const totalBytes = Buffer.byteLength(content);\n const sliced = content.slice(offset, offset + limit);\n\n return {\n data: sliced,\n offset: offset + sliced.length,\n totalBytes,\n truncated: offset + limit < totalBytes,\n };\n}\n", "import { toolFailure, toolSuccess } from \"./result\";\nimport type { ShellData, ToolHandler } from \"./types\";\nimport { executeShell } from \"./shell-manager\";\n\ntype ShellArgs = {\n command: string;\n description?: string;\n working_directory?: string;\n block_until_ms?: number;\n};\n\nexport const shellHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as ShellArgs;\n\n if (!args.command?.trim()) {\n return toolFailure(\"shell\", \"invalid_arguments\", \"Command is required\");\n }\n\n try {\n const result = await executeShell(args.command.trim(), {\n workingDirectory: args.working_directory,\n description: args.description,\n blockUntilMs: args.block_until_ms ?? 30000,\n taskId: context.taskId,\n workspaceDir: context.workspaceDir,\n });\n\n return toolSuccess(\"shell\", result);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"shell\", \"execution_error\", message);\n }\n};\n", "import { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\nimport { awaitShell } from \"./shell-manager\";\n\ntype AwaitArgs = {\n shell_id: string;\n block_until_ms?: number;\n};\n\nexport const awaitShellHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as AwaitArgs;\n\n if (!args.shell_id?.trim()) {\n return toolFailure(\"await\", \"invalid_arguments\", \"shell_id is required\");\n }\n\n try {\n const result = await awaitShell(args.shell_id.trim(), args.block_until_ms ?? 30000);\n return toolSuccess(\"await\", result);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"await\", \"error\", message);\n }\n};\n", "import { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\nimport { listShells } from \"./shell-manager\";\n\ntype ListShellsArgs = {\n status_filter?: string;\n};\n\nexport const listShellsHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as ListShellsArgs;\n\n const shells = listShells(args.status_filter);\n\n return toolSuccess(\"list_shells\", {\n shells,\n total: shells.length,\n });\n};\n", "import { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\nimport { killShell, readShellLogs } from \"./shell-manager\";\n\ntype KillShellArgs = {\n shell_id: string;\n};\n\nexport const killShellHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as KillShellArgs;\n\n if (!args.shell_id?.trim()) {\n return toolFailure(\"kill_shell\", \"invalid_arguments\", \"shell_id is required\");\n }\n\n const killed = killShell(args.shell_id.trim());\n return toolSuccess(\"kill_shell\", {\n shellId: args.shell_id.trim(),\n killed,\n });\n};\n", "import { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\nimport { readShellLogs } from \"./shell-manager\";\n\ntype ReadShellLogsArgs = {\n shell_id: string;\n stream?: \"stdout\" | \"stderr\";\n offset?: number;\n limit?: number;\n};\n\nexport const readShellLogsHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as ReadShellLogsArgs;\n\n if (!args.shell_id?.trim()) {\n return toolFailure(\"read_shell_logs\", \"invalid_arguments\", \"shell_id is required\");\n }\n\n const result = readShellLogs(\n args.shell_id.trim(),\n args.stream ?? \"stdout\",\n args.offset ?? 0,\n args.limit ?? 4096,\n );\n\n return toolSuccess(\"read_shell_logs\", {\n shellId: args.shell_id.trim(),\n stream: args.stream ?? \"stdout\",\n data: result.data,\n offset: result.offset,\n totalBytes: result.totalBytes,\n truncated: result.truncated,\n });\n};\n", "import { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype WebSearchArgs = {\n search_term: string;\n max_results?: number;\n explanation?: string;\n};\n\nexport const webSearchHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as WebSearchArgs;\n\n if (!args.search_term?.trim()) {\n return toolFailure(\"web_search\", \"invalid_arguments\", \"search_term is required\");\n }\n\n try {\n // Use a free search API (DuckDuckGo-style) or fallback\n const results = await performWebSearch(args.search_term, args.max_results ?? 5);\n\n return toolSuccess(\"web_search\", {\n query: args.search_term,\n results,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"web_search\", \"search_error\", message);\n }\n};\n\nasync function performWebSearch(\n query: string,\n maxResults: number,\n): Promise<Array<{ title: string; url: string; snippet: string }>> {\n // Try to use Tavily or fallback to DuckDuckGo scrape\n // For now, use a simple approach via DuckDuckGo's instant answer API\n try {\n const url = `https://api.duckduckgo.com/?q=${encodeURIComponent(query)}&format=json&no_html=1&skip_disambig=1`;\n const response = await fetch(url, {\n signal: AbortSignal.timeout(10000),\n });\n\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n\n const data = (await response.json()) as {\n AbstractText?: string;\n AbstractSource?: string;\n AbstractURL?: string;\n RelatedTopics?: Array<{ Text?: string; FirstURL?: string; Result?: string }>;\n };\n\n const results: Array<{ title: string; url: string; snippet: string }> = [];\n\n if (data.AbstractText) {\n results.push({\n title: data.AbstractSource ?? \"Result\",\n url: data.AbstractURL ?? \"\",\n snippet: data.AbstractText,\n });\n }\n\n if (data.RelatedTopics) {\n for (const topic of data.RelatedTopics.slice(0, maxResults)) {\n if (topic.Text && topic.FirstURL) {\n results.push({\n title: topic.Text.split(\" - \")[0] ?? \"Result\",\n url: topic.FirstURL,\n snippet: topic.Text,\n });\n }\n }\n }\n\n return results.slice(0, maxResults);\n } catch {\n // Fallback: return a message that web search is unavailable\n throw new Error(\n \"Web search is currently unavailable. Configure a Tavily API key for web search support, \" +\n \"or set the TAVILY_API_KEY environment variable.\",\n );\n }\n}\n", "import { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype BrowsePageArgs = {\n url: string;\n max_lines?: number;\n start_line?: number;\n explanation?: string;\n};\n\nexport const browsePageHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as BrowsePageArgs;\n\n if (!args.url?.trim()) {\n return toolFailure(\"browse_page\", \"invalid_arguments\", \"url is required\");\n }\n\n try {\n const response = await fetch(args.url.trim(), {\n headers: {\n \"User-Agent\": \"Mozilla/5.0 (compatible; CoderCLI/1.0)\",\n Accept: \"text/html,text/plain,*/*\",\n },\n signal: AbortSignal.timeout(15000),\n });\n\n const contentType = response.headers.get(\"content-type\") ?? \"text/plain\";\n const finalUrl = response.url;\n const title = extractTitleFromUrl(finalUrl);\n\n let content = await response.text();\n\n // Truncate if needed\n const startLine = args.start_line ?? 1;\n const maxLines = args.max_lines ?? 500;\n const lines = content.split(\"\\n\");\n const totalLines = lines.length;\n const endLine = Math.min(startLine + maxLines - 1, totalLines);\n const paginatedContent = lines.slice(startLine - 1, endLine).join(\"\\n\");\n\n return toolSuccess(\"browse_page\", {\n url: args.url,\n finalUrl,\n title,\n content: paginatedContent,\n truncated: endLine < totalLines,\n statusCode: response.status,\n contentType,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"fetch\")) {\n return toolFailure(\"browse_page\", \"network_error\", `Failed to fetch URL: ${message}`);\n }\n return toolFailure(\"browse_page\", \"error\", message);\n }\n};\n\nfunction extractTitleFromUrl(url: string): string | undefined {\n try {\n const parsed = new URL(url);\n return parsed.hostname;\n } catch {\n return undefined;\n }\n}\n", "import { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler, TodoSnapshotItem } from \"./types\";\nimport { getConfigDirPath } from \"../config\";\n\n// Todos storage \u2014 per-session JSON files\nfunction getTodosFilePath(sessionId?: string): string {\n const dir = join(getConfigDirPath(), \"todos\");\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n const safeSessionId = (sessionId ?? \"default\").replace(/[^a-z0-9-]/gi, \"\");\n return join(dir, `${safeSessionId}.json`);\n}\n\ntype TodoRecord = {\n id: string;\n content: string;\n status: \"pending\" | \"in_progress\" | \"completed\" | \"cancelled\";\n order: number;\n createdAt: number;\n updatedAt: number;\n};\n\nfunction readTodos(sessionId?: string): TodoRecord[] {\n const filePath = getTodosFilePath(sessionId);\n if (!existsSync(filePath)) return [];\n try {\n return JSON.parse(readFileSync(filePath, \"utf-8\"));\n } catch {\n return [];\n }\n}\n\nfunction writeTodos(todos: TodoRecord[], sessionId?: string): void {\n writeFileSync(getTodosFilePath(sessionId), JSON.stringify(todos, null, 2), \"utf-8\");\n}\n\n// Handlers\ntype TodoReadArgs = { session_id?: string };\n\nexport const todoReadHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as TodoReadArgs;\n const sessionId = args.session_id ?? context.sessionId;\n const todos = readTodos(sessionId);\n\n const snapshot: TodoSnapshotItem[] = todos\n .sort((a, b) => a.order - b.order)\n .map((t) => ({\n id: t.id,\n content: t.content,\n status: t.status,\n }));\n\n return toolSuccess(\"todo_read\", {\n sessionId: sessionId ?? null,\n todos: snapshot,\n total: snapshot.length,\n active: snapshot.filter((t) => t.status === \"pending\" || t.status === \"in_progress\").length,\n completed: snapshot.filter((t) => t.status === \"completed\").length,\n });\n};\n\ntype TodoWriteArgs = {\n todos: Array<{\n id: string;\n content: string;\n status: \"pending\" | \"in_progress\" | \"completed\" | \"cancelled\";\n }>;\n session_id?: string;\n};\n\nexport const todoWriteHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as TodoWriteArgs;\n const sessionId = args.session_id ?? context.sessionId;\n\n if (!args.todos || !Array.isArray(args.todos)) {\n return toolFailure(\"todo_write\", \"invalid_arguments\", \"todos array is required\");\n }\n\n const existing = readTodos(sessionId);\n const existingMap = new Map(existing.map((t) => [t.id, t]));\n\n // Merge: update existing, add new\n for (let i = 0; i < args.todos.length; i++) {\n const input = args.todos[i];\n const existingRecord = existingMap.get(input.id);\n if (existingRecord) {\n existingRecord.content = input.content;\n existingRecord.status = input.status;\n existingRecord.updatedAt = Date.now();\n } else {\n existing.push({\n id: input.id,\n content: input.content,\n status: input.status,\n order: i,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n });\n }\n }\n\n writeTodos(existing, sessionId);\n\n return toolSuccess(\"todo_write\", {\n total: existing.length,\n updated: args.todos.length,\n });\n};\n", "import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\nimport { getConfigDirPath } from \"../config\";\n\n// Plan storage \u2014 directory of .plan files\nfunction getPlansDir(): string {\n const dir = join(getConfigDirPath(), \"plans\");\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n return dir;\n}\n\ntype PlanRecord = {\n name: string;\n title: string;\n content: string;\n createdAt: number;\n updatedAt: number;\n};\n\nfunction getPlanFilePath(name: string): string {\n const safeName = name.replace(/[^a-z0-9-_.]/gi, \"\").toLowerCase();\n return join(getPlansDir(), `${safeName}.json`);\n}\n\nfunction readPlanFile(name: string): PlanRecord | null {\n const filePath = getPlanFilePath(name);\n if (!existsSync(filePath)) return null;\n try {\n return JSON.parse(readFileSync(filePath, \"utf-8\"));\n } catch {\n return null;\n }\n}\n\nfunction writePlanFile(name: string, plan: PlanRecord): void {\n writeFileSync(getPlanFilePath(name), JSON.stringify(plan, null, 2), \"utf-8\");\n}\n\nfunction deletePlanFile(name: string): boolean {\n const filePath = getPlanFilePath(name);\n if (!existsSync(filePath)) return false;\n try {\n writeFileSync(filePath, JSON.stringify({ deleted: true }), \"utf-8\");\n return true;\n } catch {\n return false;\n }\n}\n\nfunction listPlanFiles(): string[] {\n return readdirSync(getPlansDir())\n .filter((f) => f.endsWith(\".json\"))\n .map((f) => f.replace(/\\.json$/, \"\"));\n}\n\n// Handlers\ntype PlanCreateArgs = { name: string; title?: string; content: string };\n\nexport const planCreateHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as PlanCreateArgs;\n if (!args.name?.trim() || !args.content?.trim()) {\n return toolFailure(\"plan_create\", \"invalid_arguments\", \"name and content are required\");\n }\n\n const name = args.name.trim().toLowerCase().replace(/[^a-z0-9-_.]/gi, \"\");\n if (readPlanFile(name)) {\n return toolFailure(\"plan_create\", \"exists\", `Plan already exists: ${name}`);\n }\n\n writePlanFile(name, {\n name,\n title: args.title?.trim() ?? name,\n content: args.content.trim(),\n createdAt: Date.now(),\n updatedAt: Date.now(),\n });\n\n return toolSuccess(\"plan_create\", { name, title: args.title?.trim() ?? name });\n};\n\ntype PlanReadArgs = { name: string };\n\nexport const planReadHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as PlanReadArgs;\n if (!args.name?.trim()) {\n return toolFailure(\"plan_read\", \"invalid_arguments\", \"name is required\");\n }\n\n const plan = readPlanFile(args.name.trim());\n if (!plan) {\n return toolFailure(\"plan_read\", \"not_found\", `Plan not found: ${args.name}`);\n }\n\n return toolSuccess(\"plan_read\", plan);\n};\n\ntype PlanUpdateArgs = { name: string; title?: string; content?: string };\n\nexport const planUpdateHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as PlanUpdateArgs;\n if (!args.name?.trim()) {\n return toolFailure(\"plan_update\", \"invalid_arguments\", \"name is required\");\n }\n\n const existing = readPlanFile(args.name.trim());\n if (!existing) {\n return toolFailure(\"plan_update\", \"not_found\", `Plan not found: ${args.name}`);\n }\n\n writePlanFile(args.name.trim(), {\n ...existing,\n title: args.title?.trim() ?? existing.title,\n content: args.content?.trim() ?? existing.content,\n updatedAt: Date.now(),\n });\n\n return toolSuccess(\"plan_update\", { name: args.name.trim() });\n};\n\ntype PlanEditArgs = { name: string; instructions: string };\n\nexport const planEditHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as PlanEditArgs;\n if (!args.name?.trim()) {\n return toolFailure(\"plan_edit\", \"invalid_arguments\", \"name is required\");\n }\n\n const existing = readPlanFile(args.name.trim());\n if (!existing) {\n return toolFailure(\"plan_edit\", \"not_found\", `Plan not found: ${args.name}`);\n }\n\n // Append instructions to plan content\n writePlanFile(args.name.trim(), {\n ...existing,\n content: existing.content + \"\\n\\n## Edit Instructions\\n\\n\" + (args.instructions?.trim() ?? \"\"),\n updatedAt: Date.now(),\n });\n\n return toolSuccess(\"plan_edit\", { name: args.name.trim() });\n};\n\ntype PlanDeleteArgs = { name: string };\n\nexport const planDeleteHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as PlanDeleteArgs;\n if (!args.name?.trim()) {\n return toolFailure(\"plan_delete\", \"invalid_arguments\", \"name is required\");\n }\n\n deletePlanFile(args.name.trim());\n return toolSuccess(\"plan_delete\", { name: args.name.trim(), deleted: true });\n};\n\ntype PlanListArgs = Record<string, never>;\n\nexport const planListHandler: ToolHandler = async (_rawArgs, _context) => {\n const names = listPlanFiles();\n const plans = names\n .map((name) => readPlanFile(name))\n .filter((p): p is PlanRecord => p !== null)\n .map((p) => ({ name: p.name, title: p.title }));\n\n return toolSuccess(\"plan_list\", { plans, total: plans.length });\n};\n", "import { readdirSync, statSync } from \"node:fs\";\nimport { resolve, relative } from \"node:path\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype GetWorkspaceTreeArgs = {\n max_lines?: number;\n start_line?: number;\n};\n\nconst EXCLUDED_DIRS = new Set([\n \"node_modules\", \".git\", \"dist\", \"build\", \"target\", \".next\",\n \"out\", \".cache\", \".turbo\", \"coverage\", \".nyc_output\",\n \"__pycache__\", \".venv\", \"venv\", \".history\",\n]);\n\nexport const getWorkspaceTreeHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as GetWorkspaceTreeArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"get_workspace_tree\", \"workspace_required\", \"No workspace directory set\");\n }\n\n try {\n const rootDir = context.workspaceDir;\n const treeLines = buildTree(rootDir, rootDir, 0);\n const fullText = treeLines.join(\"\\n\");\n const totalLines = treeLines.length;\n const startLine = args.start_line ?? 1;\n const maxLines = args.max_lines ?? 500;\n const endLine = Math.min(startLine + maxLines - 1, totalLines);\n const paginatedText = treeLines.slice(startLine - 1, endLine).join(\"\\n\");\n\n return toolSuccess(\"get_workspace_tree\", {\n treeText: paginatedText,\n totalLines,\n startLine,\n endLine,\n truncated: endLine < totalLines,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"get_workspace_tree\", \"error\", message);\n }\n};\n\nfunction buildTree(rootDir: string, currentDir: string, depth: number): string[] {\n const lines: string[] = [];\n const relPath = relative(rootDir, currentDir);\n\n if (depth === 0) {\n lines.push(relative(rootDir, currentDir) || \".\");\n }\n\n let entries: string[];\n try {\n entries = readdirSync(currentDir);\n } catch {\n return lines;\n }\n\n // Sort: directories first, then files, alphabetical\n const dirs: string[] = [];\n const files: string[] = [];\n\n for (const name of entries) {\n if (name.startsWith(\".\") && depth === 0 && name === \".git\") continue;\n if (EXCLUDED_DIRS.has(name)) continue;\n if (name.startsWith(\".\") && depth > 0) continue;\n\n const fullPath = resolve(currentDir, name);\n try {\n const stats = statSync(fullPath);\n if (stats.isDirectory()) {\n dirs.push(name);\n } else {\n files.push(name);\n }\n } catch {\n // skip\n }\n }\n\n dirs.sort();\n files.sort();\n\n const allEntries = [...dirs, ...files];\n\n for (let i = 0; i < allEntries.length; i++) {\n const name = allEntries[i];\n const isLast = i === allEntries.length - 1;\n const prefix = depth === 0 ? (isLast ? \"\u2514\u2500\u2500 \" : \"\u251C\u2500\u2500 \") : \"\";\n const childPrefix = depth === 0 ? (isLast ? \" \" : \"\u2502 \") : \"\";\n const fullPath = resolve(currentDir, name);\n\n lines.push(`${prefix}${name}`);\n\n try {\n const stats = statSync(fullPath);\n if (stats.isDirectory()) {\n const childLines = buildTreeFromDepth(rootDir, fullPath, depth + 1, prefix || \"\", childPrefix);\n lines.push(...childLines);\n }\n } catch {\n // skip\n }\n }\n\n return lines;\n}\n\nfunction buildTreeFromDepth(\n rootDir: string,\n currentDir: string,\n depth: number,\n parentPrefix: string,\n prefix: string,\n): string[] {\n const lines: string[] = [];\n\n let entries: string[];\n try {\n entries = readdirSync(currentDir);\n } catch {\n return lines;\n }\n\n const dirs: string[] = [];\n const files: string[] = [];\n\n for (const name of entries) {\n if (EXCLUDED_DIRS.has(name)) continue;\n if (name.startsWith(\".\")) continue;\n\n const fullPath = resolve(currentDir, name);\n try {\n const stats = statSync(fullPath);\n if (stats.isDirectory()) {\n dirs.push(name);\n } else {\n files.push(name);\n }\n } catch {\n // skip\n }\n }\n\n dirs.sort();\n files.sort();\n\n const allEntries = [...dirs, ...files];\n\n for (let i = 0; i < allEntries.length; i++) {\n const name = allEntries[i];\n const isLast = i === allEntries.length - 1;\n const linePrefix = `${prefix}${isLast ? \"\u2514\u2500\u2500 \" : \"\u251C\u2500\u2500 \"}`;\n const childPrefix = `${prefix}${isLast ? \" \" : \"\u2502 \"}`;\n\n lines.push(`${linePrefix}${name}`);\n\n const fullPath = resolve(currentDir, name);\n try {\n const stats = statSync(fullPath);\n if (stats.isDirectory()) {\n const childLines = buildTreeFromDepth(rootDir, fullPath, depth + 1, \"\", childPrefix);\n lines.push(...childLines);\n }\n } catch {\n // skip\n }\n }\n\n return lines;\n}\n", "import { createInterface } from \"node:readline\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\nexport const askQuestionHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as { question: string };\n\n if (!args.question?.trim()) {\n return toolFailure(\"ask_question\", \"invalid_arguments\", \"question is required\");\n }\n\n // In non-interactive mode, return a placeholder\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n\n const answer = await new Promise<string>((resolve) => {\n process.stderr.write(`\\n\\x1b[33m\u2753 ${args.question}\\x1b[0m\\n> `);\n rl.once(\"line\", (line) => {\n resolve(line.trim());\n });\n });\n\n rl.close();\n\n if (!answer) {\n return toolFailure(\"ask_question\", \"no_answer\", \"User did not provide an answer\");\n }\n\n return toolSuccess(\"ask_question\", {\n question: args.question,\n answer,\n });\n};\n", "import { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\nimport type { AgentMode } from \"../agent/types\";\n\n// Forward reference \u2014 will be set when the agent system initializes\nlet spawnAgentFunction: ((task: string, context?: string, tools?: string[]) => Promise<unknown>) | null = null;\n\nexport function setSpawnAgentFunction(\n fn: (task: string, context?: string, tools?: string[]) => Promise<unknown>,\n): void {\n spawnAgentFunction = fn;\n}\n\ntype SpawnSubAgentArgs = {\n task: string;\n context?: string;\n tools?: string[];\n};\n\nexport const spawnSubAgentHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as SpawnSubAgentArgs;\n\n if (!args.task?.trim()) {\n return toolFailure(\"spawn_subagent\", \"invalid_arguments\", \"task is required\");\n }\n\n if (!spawnAgentFunction) {\n return toolFailure(\n \"spawn_subagent\",\n \"not_available\",\n \"Sub-agent spawning is not available in the current configuration.\",\n );\n }\n\n try {\n const result = await spawnAgentFunction(args.task, args.context, args.tools);\n return toolSuccess(\"spawn_subagent\", result);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"spawn_subagent\", \"execution_error\", message);\n }\n};\n", "/**\n * Tool handler registry \u2014 maps tool names to their CLI-native handlers.\n */\n\nimport type { ToolHandler, ToolResultEnvelope, ToolExecutionContext } from \"./types\";\nimport { listDirHandler } from \"./list-dir\";\nimport { readFileHandler } from \"./read-file\";\nimport { writeFileHandler } from \"./write-file\";\nimport { replaceFileHandler } from \"./replace-file\";\nimport { editFileHandler } from \"./edit-file\";\nimport { replaceLinesHandler } from \"./replace-lines\";\nimport { globHandler } from \"./glob\";\nimport { grepHandler } from \"./grep\";\nimport { shellHandler } from \"./shell\";\nimport { awaitShellHandler } from \"./await-shell\";\nimport { listShellsHandler } from \"./list-shells\";\nimport { killShellHandler } from \"./kill-shell\";\nimport { readShellLogsHandler } from \"./read-shell-logs\";\nimport { webSearchHandler } from \"./web-search\";\nimport { browsePageHandler } from \"./browse-page\";\nimport { todoReadHandler, todoWriteHandler } from \"./todos\";\nimport { planCreateHandler, planReadHandler, planUpdateHandler, planEditHandler, planDeleteHandler, planListHandler } from \"./plans\";\nimport { getWorkspaceTreeHandler } from \"./workspace-tree\";\nimport { askQuestionHandler } from \"./ask-question\";\n\nimport { spawnSubAgentHandler } from \"./spawn-subagent\";\n\n// ---------------------------------------------------------------------------\n// Tool definitions (matching existing AgentToolDefinition format)\n// ---------------------------------------------------------------------------\n\nexport type ToolDefinition = {\n type: \"function\";\n function: {\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n };\n};\n\nconst AGENT_TOOL_DEFINITIONS: ToolDefinition[] = [\n {\n type: \"function\",\n function: {\n name: \"list_dir\",\n description: \"List files and directories under a path. Relative paths are resolved against the workspace root.\",\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Relative or absolute path within the workspace.\" },\n recursive: { type: \"boolean\", description: \"Whether to list entries recursively.\", default: false },\n max_depth: { type: \"integer\", description: \"Maximum recursion depth when recursive is true.\", default: 1 },\n show_hidden: { type: \"boolean\", description: \"Whether to include dotfiles and dot-directories.\", default: false },\n },\n required: [\"path\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"read_file\",\n description: \"Read a text file with line numbers and pagination. Relative paths are resolved against the workspace root.\",\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Relative or absolute path to the file within the workspace.\" },\n start_line: { type: \"integer\", description: \"First line to read (1-based).\", default: 1 },\n max_lines: { type: \"integer\", description: \"Maximum number of lines to return.\", default: 500 },\n respect_gitignore: { type: \"boolean\", description: \"Whether to refuse reading paths ignored by .gitignore.\", default: true },\n },\n required: [\"path\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"write_file\",\n description: \"Create a new text file. Fails if the file already exists. Use replace_file or edit_file to modify existing files.\",\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Relative or absolute path to the new file within the workspace.\" },\n content: { type: \"string\", description: \"Full file content to write.\" },\n create_parent_dirs: { type: \"boolean\", description: \"Whether to create missing parent directories.\", default: true },\n },\n required: [\"path\", \"content\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"replace_file\",\n description: \"Replace the entire contents of an existing text file. Use expected_sha256 from read_file to avoid overwriting concurrent changes.\",\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Relative or absolute path to the file within the workspace.\" },\n content: { type: \"string\", description: \"Full replacement file content.\" },\n expected_sha256: { type: \"string\", description: \"SHA256 hash from read_file. Rejects the write if the file changed.\" },\n create_backup: { type: \"boolean\", description: \"Whether to save a backup copy under .history before writing.\", default: false },\n respect_gitignore: { type: \"boolean\", description: \"Whether to refuse editing paths ignored by .gitignore.\", default: true },\n },\n required: [\"path\", \"content\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"edit_file\",\n description: \"Apply a targeted search-and-replace edit to an existing text file. Prefer this over replace_file for small changes.\",\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Relative or absolute path to the file within the workspace.\" },\n old_string: { type: \"string\", description: \"Exact text to replace. Must match uniquely unless replace_all is true.\" },\n new_string: { type: \"string\", description: \"Replacement text.\" },\n expected_sha256: { type: \"string\", description: \"SHA256 hash from read_file. Rejects the edit if the file changed.\" },\n replace_all: { type: \"boolean\", description: \"Whether to replace every occurrence of old_string.\", default: false },\n create_backup: { type: \"boolean\", description: \"Whether to save a backup copy under .history before writing.\", default: false },\n respect_gitignore: { type: \"boolean\", description: \"Whether to refuse editing paths ignored by .gitignore.\", default: true },\n },\n required: [\"path\", \"old_string\", \"new_string\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"replace_lines\",\n description: \"Replace a range of lines in an existing text file by line number. Read the file with read_file first to see line numbers.\",\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Relative or absolute path to the file within the workspace.\" },\n start_line: { type: \"integer\", description: \"First line to replace (1-based).\" },\n end_line: { type: \"integer\", description: \"Last line to replace (inclusive, 1-based).\" },\n content: { type: \"string\", description: \"Replacement content for the specified line range.\" },\n expected_sha256: { type: \"string\", description: \"SHA256 hash from read_file. Rejects the edit if the file changed.\" },\n create_backup: { type: \"boolean\", description: \"Whether to save a backup copy under .history before writing.\", default: false },\n respect_gitignore: { type: \"boolean\", description: \"Whether to refuse editing paths ignored by .gitignore.\", default: true },\n },\n required: [\"path\", \"start_line\", \"end_line\", \"content\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"glob\",\n description: \"Find files by glob pattern under a directory. Relative paths are resolved against the workspace root.\",\n parameters: {\n type: \"object\",\n properties: {\n glob_pattern: { type: \"string\", description: \"Glob pattern such as **/*.tsx or src/**/*.rs.\" },\n target_directory: { type: \"string\", description: \"Directory to search from. Defaults to the workspace root.\" },\n head_limit: { type: \"integer\", description: \"Maximum number of matching paths to return.\", default: 100 },\n respect_gitignore: { type: \"boolean\", description: \"Whether to skip paths ignored by .gitignore.\", default: true },\n },\n required: [\"glob_pattern\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"grep\",\n description: \"Search file contents with a regex pattern. Relative paths are resolved against the workspace root.\",\n parameters: {\n type: \"object\",\n properties: {\n pattern: { type: \"string\", description: \"Regular expression pattern to search for.\" },\n path: { type: \"string\", description: \"File or directory to search. Defaults to the workspace root.\" },\n glob: { type: \"string\", description: \"Optional glob filter to limit searched files.\" },\n output_mode: { type: \"string\", enum: [\"content\", \"files_with_matches\", \"count\"], default: \"content\" },\n case_insensitive: { type: \"boolean\", description: \"Whether to ignore letter case while matching.\", default: false },\n context_before: { type: \"integer\", description: \"Number of lines to include before each match.\" },\n context_after: { type: \"integer\", description: \"Number of lines to include after each match.\" },\n context: { type: \"integer\", description: \"Number of lines to include before and after each match.\" },\n head_limit: { type: \"integer\", description: \"Maximum number of results to return.\", default: 200 },\n offset: { type: \"integer\", description: \"Number of results to skip in content mode.\", default: 0 },\n multiline: { type: \"boolean\", description: \"Whether . should match newlines.\", default: false },\n respect_gitignore: { type: \"boolean\", description: \"Whether to skip paths ignored by .gitignore.\", default: true },\n },\n required: [\"pattern\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"shell\",\n description: \"Execute a shell command in the workspace. Use for builds, tests, git, and other CLI tasks.\",\n parameters: {\n type: \"object\",\n properties: {\n command: { type: \"string\", description: \"The shell command to execute.\" },\n description: { type: \"string\", description: \"Short human-readable description for display only.\" },\n working_directory: { type: \"string\", description: \"Directory to run the command in, relative to workspace root.\" },\n block_until_ms: { type: \"integer\", description: \"Max wait time in ms. Default 30000. Use 0 for background mode.\", default: 30000 },\n },\n required: [\"command\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"await\",\n description: \"Poll a background shell started with shell(block_until_ms=0) until it completes or times out.\",\n parameters: {\n type: \"object\",\n properties: {\n shell_id: { type: \"string\", description: \"The shell_id returned from a background shell invocation.\" },\n block_until_ms: { type: \"integer\", description: \"Max wait time in ms before returning current output.\", default: 30000 },\n },\n required: [\"shell_id\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"list_shells\",\n description: \"List background shell processes started by the agent.\",\n parameters: {\n type: \"object\",\n properties: {\n status_filter: { type: \"string\", description: 'Filter by status. Default \"running\". Use \"all\" to include completed and failed shells.' },\n },\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"kill_shell\",\n description: \"Kill a running background shell process by shell_id.\",\n parameters: {\n type: \"object\",\n properties: {\n shell_id: { type: \"string\", description: \"The shell_id to terminate.\" },\n },\n required: [\"shell_id\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"read_shell_logs\",\n description: \"Read logs from a shell process in batches.\",\n parameters: {\n type: \"object\",\n properties: {\n shell_id: { type: \"string\", description: \"The shell_id to read logs from.\" },\n stream: { type: \"string\", enum: [\"stdout\", \"stderr\"], default: \"stdout\" },\n offset: { type: \"integer\", description: \"Byte offset to start reading from.\", default: 0 },\n limit: { type: \"integer\", description: \"Maximum bytes to return.\", default: 4096 },\n },\n required: [\"shell_id\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"web_search\",\n description: \"Search the web for real-time information outside training data.\",\n parameters: {\n type: \"object\",\n properties: {\n search_term: { type: \"string\", description: \"The search term to look up on the web.\" },\n max_results: { type: \"integer\", description: \"Maximum number of search results to return.\", default: 5 },\n explanation: { type: \"string\", description: \"One sentence explanation of why this search is being used.\" },\n },\n required: [\"search_term\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"browse_page\",\n description: \"Fetch a public web page and return readable Markdown content.\",\n parameters: {\n type: \"object\",\n properties: {\n url: { type: \"string\", description: \"The URL to fetch. Must be http or https.\" },\n max_lines: { type: \"integer\", description: \"Maximum number of lines to return.\", default: 500 },\n start_line: { type: \"integer\", description: \"First line to read (1-based).\", default: 1 },\n explanation: { type: \"string\", description: \"One sentence explanation of why this page is being fetched.\" },\n },\n required: [\"url\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"todo_read\",\n description: \"Read the current structured todo list for the session.\",\n parameters: {\n type: \"object\",\n properties: {\n session_id: { type: \"string\", description: \"Optional session ID.\" },\n },\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"todo_write\",\n description: \"Create and update a structured todo list for the session.\",\n parameters: {\n type: \"object\",\n properties: {\n todos: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n id: { type: \"string\" },\n content: { type: \"string\" },\n status: { type: \"string\", enum: [\"pending\", \"in_progress\", \"completed\", \"cancelled\"] },\n },\n required: [\"id\", \"content\", \"status\"],\n },\n },\n session_id: { type: \"string\", description: \"Optional session ID.\" },\n },\n required: [\"todos\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"get_workspace_tree\",\n description: \"Display the workspace directory tree structure with depth recursion.\",\n parameters: {\n type: \"object\",\n properties: {\n max_lines: { type: \"integer\", description: \"Maximum number of lines to return.\", default: 500 },\n start_line: { type: \"integer\", description: \"First line to return (1-based).\", default: 1 },\n },\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"spawn_subagent\",\n description: \"Spawn a sub-agent to complete an independent sub-task.\",\n parameters: {\n type: \"object\",\n properties: {\n task: { type: \"string\", description: \"The task description for the sub-agent.\" },\n context: { type: \"string\", description: \"Optional additional context or constraints.\" },\n tools: { type: \"array\", items: { type: \"string\" }, description: \"Optional whitelist of tool names.\" },\n },\n required: [\"task\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"ask_question\",\n description: \"Ask the user a question when you need additional information to proceed.\",\n parameters: {\n type: \"object\",\n properties: {\n question: { type: \"string\", description: \"The question to ask the user.\" },\n },\n required: [\"question\"],\n additionalProperties: false,\n },\n },\n },\n];\n\n// ---------------------------------------------------------------------------\n// Handler map\n// ---------------------------------------------------------------------------\n\nconst HANDLER_MAP: Record<string, ToolHandler> = {\n list_dir: listDirHandler,\n read_file: readFileHandler,\n write_file: writeFileHandler,\n replace_file: replaceFileHandler,\n edit_file: editFileHandler,\n replace_lines: replaceLinesHandler,\n glob: globHandler,\n grep: grepHandler,\n shell: shellHandler,\n await: awaitShellHandler,\n list_shells: listShellsHandler,\n kill_shell: killShellHandler,\n read_shell_logs: readShellLogsHandler,\n web_search: webSearchHandler,\n browse_page: browsePageHandler,\n todo_read: todoReadHandler,\n todo_write: todoWriteHandler,\n get_workspace_tree: getWorkspaceTreeHandler,\n spawn_subagent: spawnSubAgentHandler,\n ask_question: askQuestionHandler,\n plan_create: planCreateHandler,\n plan_read: planReadHandler,\n plan_update: planUpdateHandler,\n plan_edit: planEditHandler,\n plan_delete: planDeleteHandler,\n plan_list: planListHandler,\n};\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Mode-based tool filtering\n// ---------------------------------------------------------------------------\n\nconst ASK_QUESTION_TOOL_NAME = \"ask_question\";\n\n/** Tools excluded from default \"agent\" mode. */\nconst AGENT_MODE_EXCLUDED_TOOL_NAMES = new Set([\n ASK_QUESTION_TOOL_NAME,\n]);\n\n/** Tools allowed in \\\"ask\\\" mode \u2014 only read-only / information-gathering. */\nconst ASK_MODE_TOOL_NAMES = new Set([\n \"list_dir\",\n \"read_file\",\n \"glob\",\n \"grep\",\n \"web_search\",\n \"browse_page\",\n \"todo_read\",\n \"list_shells\",\n \"read_shell_logs\",\n \"get_workspace_tree\",\n]);\n\n/**\n * Returns tool definitions filtered by agent mode.\n * - \"agent\": all tools except ask_question.\n * - \"plan\": all tools including ask_question.\n * - \"ask\": only read-only tools.\n * - undefined: all tools (backward-compatible).\n */\nexport function getToolDefinitions(agentMode?: string): ToolDefinition[] {\n if (agentMode === \"ask\") {\n return AGENT_TOOL_DEFINITIONS.filter((t) =>\n ASK_MODE_TOOL_NAMES.has(t.function.name),\n );\n }\n\n if (agentMode === \"plan\") {\n return AGENT_TOOL_DEFINITIONS;\n }\n\n // Default agent mode: exclude ask_question\n return AGENT_TOOL_DEFINITIONS.filter(\n (t) => !AGENT_MODE_EXCLUDED_TOOL_NAMES.has(t.function.name),\n );\n}\n\nexport function getToolHandler(name: string): ToolHandler | undefined {\n return HANDLER_MAP[name];\n}\n\nexport async function executeToolCall(\n name: string,\n rawArguments: string,\n context: ToolExecutionContext,\n): Promise<ToolResultEnvelope> {\n const handler = getToolHandler(name);\n if (!handler) {\n return {\n ok: false,\n tool: name,\n error: {\n code: \"unknown_tool\",\n message: `Unknown tool: ${name}`,\n },\n };\n }\n\n let parsedArgs: unknown;\n try {\n parsedArgs = rawArguments.trim() ? JSON.parse(rawArguments) : {};\n } catch {\n return {\n ok: false,\n tool: name,\n error: {\n code: \"invalid_arguments\",\n message: \"Tool arguments must be valid JSON\",\n },\n };\n }\n\n return handler(parsedArgs, context);\n}\n", "/**\n * LLM Stream \u2014 handles SSE streaming from OpenAI-compatible chat completions API.\n */\n\nimport type { AgentChatMessage } from \"./types\";\nimport type { ToolDefinition } from \"../handlers\";\n\ntype StreamCallbacks = {\n onContent: (delta: string) => void;\n onReasoning: (delta: string) => void;\n onToolCall: (id: string, name: string, args: string) => void;\n onUsage: (usage: { promptTokens: number; completionTokens: number; totalTokens: number }) => void;\n onDone: () => void;\n onError: (error: Error) => void;\n};\n\ntype StreamOptions = {\n baseUrl: string;\n apiKey: string;\n model: string;\n messages: AgentChatMessage[];\n tools?: ToolDefinition[];\n};\n\nfunction chatCompletionsUrl(baseUrl: string): string {\n const trimmed = baseUrl.trim().replace(/\\/+$/, \"\");\n if (trimmed.endsWith(\"/v1\")) {\n return `${trimmed}/chat/completions`;\n }\n return `${trimmed}/v1/chat/completions`;\n}\n\nexport async function startLLMStream(\n options: StreamOptions,\n callbacks: StreamCallbacks,\n): Promise<void> {\n const url = chatCompletionsUrl(options.baseUrl);\n\n // Build request body\n const body: Record<string, unknown> = {\n model: options.model,\n messages: options.messages.map(formatMessage),\n stream: true,\n stream_options: { include_usage: true },\n };\n\n if (options.tools && options.tools.length > 0) {\n body.tools = options.tools;\n body.tool_choice = \"auto\";\n }\n\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${options.apiKey}`,\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => \"\");\n throw new Error(`API error (${response.status}): ${errorBody || response.statusText}`);\n }\n\n if (!response.body) {\n throw new Error(\"Response body is empty\");\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n const toolCallAccumulator = new ToolCallAccumulator({\n onIdentified: (id, name) => {\n // Called when a tool call is first identified\n },\n });\n let hasToolCalls = false;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n while (true) {\n const lineBreak = buffer.indexOf(\"\\n\");\n if (lineBreak === -1) break;\n\n const line = buffer.slice(0, lineBreak).trim();\n buffer = buffer.slice(lineBreak + 1);\n processLine(line, callbacks, toolCallAccumulator);\n }\n }\n\n // Process any remaining data\n if (buffer.trim()) {\n processLine(buffer.trim(), callbacks, toolCallAccumulator);\n }\n\n // Finalize tool calls\n const finalToolCalls = toolCallAccumulator.finalize();\n for (const call of finalToolCalls) {\n callbacks.onToolCall(call.id, call.name, call.arguments);\n }\n\n callbacks.onDone();\n } catch (error) {\n if (error instanceof Error) {\n callbacks.onError(error);\n } else {\n callbacks.onError(new Error(String(error)));\n }\n }\n}\n\nfunction processLine(\n line: string,\n callbacks: StreamCallbacks,\n toolCalls: ToolCallAccumulator,\n): void {\n if (!line || line.startsWith(\":\")) return;\n\n const payload = line.startsWith(\"data:\") ? line.slice(5).trim() : line;\n if (payload === \"[DONE]\") return;\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n return;\n }\n\n const choices = parsed.choices as Array<Record<string, unknown>> | undefined;\n const choice = choices?.[0];\n const delta = choice?.delta as Record<string, unknown> | undefined;\n\n if (delta) {\n // Reasoning content\n if (delta.reasoning_content) {\n callbacks.onReasoning(delta.reasoning_content as string);\n }\n\n // Text content\n if (delta.content) {\n callbacks.onContent(delta.content as string);\n }\n\n // Tool calls\n const toolCallDeltas = delta.tool_calls as Array<Record<string, unknown>> | undefined;\n if (toolCallDeltas) {\n for (const tcDelta of toolCallDeltas) {\n const index = tcDelta.index as number;\n const fn = tcDelta.function as Record<string, unknown> | undefined;\n const id = tcDelta.id as string | undefined;\n\n if (id) {\n toolCalls.startToolCall(index, id, fn?.name as string);\n } else if (fn?.arguments) {\n toolCalls.appendArguments(index, fn.arguments as string);\n }\n }\n }\n }\n\n // Usage\n const usage = parsed.usage as Record<string, unknown> | undefined;\n if (usage?.prompt_tokens !== undefined) {\n callbacks.onUsage({\n promptTokens: usage.prompt_tokens as number,\n completionTokens: usage.completion_tokens as number,\n totalTokens: usage.total_tokens as number,\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Tool Call Accumulator\n// ---------------------------------------------------------------------------\n\ntype PendingToolCall = {\n index: number;\n id: string;\n name: string;\n arguments: string;\n};\n\nclass ToolCallAccumulator {\n private pending: Map<number, PendingToolCall> = new Map();\n private finalized = false;\n private onIdentified: (id: string, name: string) => void;\n\n constructor(opts: { onIdentified: (id: string, name: string) => void }) {\n this.onIdentified = opts.onIdentified;\n }\n\n startToolCall(index: number, id: string, name: string): void {\n this.pending.set(index, { index, id, name, arguments: \"\" });\n }\n\n appendArguments(index: number, args: string): void {\n const existing = this.pending.get(index);\n if (existing) {\n existing.arguments += args;\n }\n }\n\n finalize(): Array<{ id: string; name: string; arguments: string }> {\n if (this.finalized) return [];\n this.finalized = true;\n const calls = Array.from(this.pending.values())\n .sort((a, b) => a.index - b.index)\n .map((c) => ({\n id: c.id,\n name: c.name,\n arguments: c.arguments,\n }));\n return calls;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Message formatter for OpenAI API\n// ---------------------------------------------------------------------------\n\nfunction formatMessage(msg: AgentChatMessage): Record<string, unknown> {\n const formatted: Record<string, unknown> = {\n role: msg.role,\n };\n\n if (msg.content !== undefined) {\n formatted.content = msg.content;\n }\n\n if (msg.reasoning_content) {\n formatted.reasoning_content = msg.reasoning_content;\n }\n\n if (msg.tool_calls) {\n formatted.tool_calls = msg.tool_calls;\n }\n\n if (msg.tool_call_id) {\n formatted.tool_call_id = msg.tool_call_id;\n }\n\n if (msg.name) {\n formatted.name = msg.name;\n }\n\n return formatted;\n}\n", "/**\n * CLI Agent Runner\n *\n * Manages the multi-turn agent loop. This is a CLI-native implementation\n * that bypasses Tauri entirely and uses direct API calls + Node.js tool handlers.\n */\n\nimport { executeToolCall, getToolDefinitions } from \"../handlers\";\nimport type { ToolExecutionContext, ToolResultEnvelope } from \"../handlers/types\";\nimport type { AgentChatMessage, AgentEvent, AgentEventHandler, AgentStartInput, AgentMode } from \"./types\";\nimport { startLLMStream } from \"./llm-stream\";\n\n// ---------------------------------------------------------------------------\n// Multi-turn agent loop\n// ---------------------------------------------------------------------------\n\ntype AgentTurnResult = {\n toolCalls: Array<{ id: string; name: string; arguments: string }>;\n content: string;\n reasoningContent: string;\n usage?: { promptTokens: number; completionTokens: number; totalTokens: number };\n};\n\nexport async function runAgentWithTools(\n input: AgentStartInput,\n toolContext: ToolExecutionContext,\n onEvent: AgentEventHandler,\n): Promise<AgentChatMessage[]> {\n let messages = [...input.messages];\n let cumulativeUsage: AgentTurnResult[\"usage\"] | undefined;\n\n while (true) {\n const turn = await runSingleAgentTurn(input, messages, onEvent);\n\n // Accumulate usage\n if (turn.usage) {\n cumulativeUsage = cumulativeUsage\n ? {\n promptTokens: cumulativeUsage.promptTokens + turn.usage.promptTokens,\n completionTokens: cumulativeUsage.completionTokens + turn.usage.completionTokens,\n totalTokens: cumulativeUsage.totalTokens + turn.usage.totalTokens,\n }\n : turn.usage;\n }\n\n // No tool calls \u2014 agent is done\n if (turn.toolCalls.length === 0) {\n // Append the final assistant response to messages for context persistence\n if (turn.content || turn.reasoningContent) {\n messages = [\n ...messages,\n {\n role: \"assistant\",\n content: turn.content || undefined,\n reasoning_content: turn.reasoningContent || undefined,\n },\n ];\n }\n onEvent({ type: \"done\", taskId: input.taskId, usage: cumulativeUsage ?? turn.usage });\n onEvent({ type: \"status\", taskId: input.taskId, status: \"completed\" });\n return messages;\n }\n\n // Execute tools and append results\n messages = await appendToolResults(messages, turn, toolContext, onEvent);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Single turn \u2014 stream from LLM and collect tool calls\n// ---------------------------------------------------------------------------\n\nasync function runSingleAgentTurn(\n input: AgentStartInput,\n messages: AgentChatMessage[],\n onEvent: AgentEventHandler,\n): Promise<AgentTurnResult> {\n return new Promise<AgentTurnResult>((resolve, reject) => {\n let content = \"\";\n let reasoningContent = \"\";\n const toolCalls: Array<{ id: string; name: string; arguments: string }> = [];\n let turnUsage: AgentTurnResult[\"usage\"] | undefined;\n\n startLLMStream(\n {\n baseUrl: input.baseUrl,\n apiKey: input.apiKey,\n model: input.model,\n messages,\n tools: getToolDefinitions(input.agentMode),\n },\n {\n onContent: (delta: string) => {\n content += delta;\n onEvent({ type: \"content_delta\", taskId: input.taskId, delta });\n },\n onReasoning: (delta: string) => {\n reasoningContent += delta;\n onEvent({ type: \"thinking_delta\", taskId: input.taskId, delta });\n },\n onToolCall: (id: string, name: string, args: string) => {\n toolCalls.push({ id, name, arguments: args });\n onEvent({ type: \"tool_call_pending\", taskId: input.taskId, toolCallId: id, name });\n },\n onUsage: (usage) => {\n turnUsage = usage;\n },\n onDone: () => {\n if (toolCalls.length > 0) {\n // Emit tool started events\n for (const call of toolCalls) {\n let parsedInput: unknown;\n try {\n parsedInput = call.arguments.trim() ? JSON.parse(call.arguments) : {};\n } catch {\n parsedInput = {};\n }\n onEvent({\n type: \"tool_call_started\",\n taskId: input.taskId,\n toolCallId: call.id,\n name: call.name,\n input: parsedInput,\n });\n }\n }\n resolve({ toolCalls, content, reasoningContent, usage: turnUsage });\n },\n onError: (error: Error) => {\n reject(error);\n },\n },\n );\n });\n}\n\n// ---------------------------------------------------------------------------\n// Tool result appending\n// ---------------------------------------------------------------------------\n\nasync function appendToolResults(\n messages: AgentChatMessage[],\n turn: AgentTurnResult,\n context: ToolExecutionContext,\n onEvent: AgentEventHandler,\n): Promise<AgentChatMessage[]> {\n // Build assistant message with tool calls\n const assistantMessage: AgentChatMessage = {\n role: \"assistant\",\n content: turn.content || undefined,\n reasoning_content: turn.reasoningContent || undefined,\n tool_calls: turn.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n };\n\n const nextMessages = [...messages, assistantMessage];\n\n // Execute all tools in parallel\n const results = await Promise.all(\n turn.toolCalls.map(async (call) => {\n try {\n const result = await executeToolCall(call.name, call.arguments, context);\n // Emit tool finished event\n if (result.ok) {\n onEvent({\n type: \"tool_call_finished\",\n taskId: context.taskId ?? \"\",\n toolCallId: call.id,\n output: result.data,\n });\n } else {\n onEvent({\n type: \"tool_call_finished\",\n taskId: context.taskId ?? \"\",\n toolCallId: call.id,\n errorText: result.error?.message,\n });\n }\n return { id: call.id, result };\n } catch (error) {\n const errorText = error instanceof Error ? error.message : String(error);\n onEvent({\n type: \"tool_call_finished\",\n taskId: context.taskId ?? \"\",\n toolCallId: call.id,\n errorText,\n });\n return {\n id: call.id,\n result: {\n ok: false,\n tool: call.name,\n error: { code: \"execution_error\", message: errorText },\n },\n };\n }\n }),\n );\n\n // Add tool result messages\n for (const { id, result } of results) {\n const content = result.ok\n ? JSON.stringify(result.data, null, 2)\n : JSON.stringify({ error: result.error }, null, 2);\n\n nextMessages.push({\n role: \"tool\",\n tool_call_id: id,\n content,\n });\n }\n\n return nextMessages;\n}\n", "/**\n * CLI Environment Resolver\n * Resolves the runtime environment for the agent system prompt.\n */\n\nimport { platform, release, hostname, EOL } from \"node:os\";\nimport { execSync } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nexport type AgentEnvironment = {\n workspaceDir: string | null;\n os: string;\n shell: string;\n isGitRepository: boolean;\n today: string;\n agentsMd: { path: string; content: string; truncated: boolean } | null;\n enabledSystemSkills: Array<{ slug: string; name: string; content: string }>;\n};\n\nexport function resolveAgentEnvironment(workspaceDir: string | null): AgentEnvironment {\n const os = `${platform()} (${release()})`;\n const shell = resolveShell();\n const isGitRepository = workspaceDir ? checkIsGitRepository(workspaceDir) : false;\n const today = new Date().toLocaleDateString(\"en-US\", {\n weekday: \"long\",\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n timeZone: \"UTC\",\n });\n\n let agentsMd: { path: string; content: string; truncated: boolean } | null = null;\n\n if (workspaceDir) {\n const agentsMdPath = resolve(workspaceDir, \"AGENTS.md\");\n if (existsSync(agentsMdPath)) {\n const content = readFileSync(agentsMdPath, \"utf-8\");\n agentsMd = {\n path: agentsMdPath,\n content: content.length > 4000 ? content.slice(0, 4000) + \"\\n... [truncated]\" : content,\n truncated: content.length > 4000,\n };\n }\n }\n\n return {\n workspaceDir,\n os,\n shell,\n isGitRepository,\n today,\n agentsMd,\n enabledSystemSkills: [],\n };\n}\n\nfunction resolveShell(): string {\n const p = platform();\n if (p === \"win32\") {\n return process.env.COMSPEC || \"cmd.exe\";\n }\n return process.env.SHELL || \"/bin/sh\";\n}\n\nfunction checkIsGitRepository(dir: string): boolean {\n try {\n execSync(\"git rev-parse --is-inside-work-tree\", {\n cwd: dir,\n stdio: \"pipe\",\n timeout: 3000,\n });\n return true;\n } catch {\n return false;\n }\n}\n\nexport function buildSystemPrompt(env: AgentEnvironment): string {\n const workspaceLine = env.workspaceDir ?? \"not selected\";\n const gitLine = env.isGitRepository ? \"yes\" : env.workspaceDir ? \"no\" : \"unknown\";\n\n const lines = [\n \"You are Coder, a helpful terminal AI assistant.\",\n \"\",\n \"## Environment\",\n `- Workspace: ${workspaceLine}`,\n `- OS: ${env.os}`,\n `- Shell: ${env.shell}`,\n `- Git repository: ${gitLine}`,\n `- Date: ${env.today}`,\n \"\",\n \"## Available Tools\",\n \"You have access to all standard tools: file operations, shell commands, web search, skills, and more.\",\n \"\",\n \"## Guidelines\",\n \"- Always read files before editing them.\",\n \"- Prefer targeted edits over full file replacements.\",\n \"- Use shell commands only for builds, tests, git, and non-interactive CLI tasks.\",\n \"- When a task requires multiple steps, create a plan first using todo_write.\",\n \"- After completing a task, summarize what was done.\",\n \"- Use ask_question when you need clarification from the user.\",\n \"\",\n ];\n\n if (env.agentsMd) {\n lines.push(\"## Project Instructions (AGENTS.md)\");\n lines.push(\"\");\n lines.push(env.agentsMd.content);\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n", "/**\n * Agent Session \u2014 manages a single agent session lifecycle.\n * Connects the agent loop to the CLI output.\n */\n\nimport type { AgentMode, AgentChatMessage, AgentEvent } from \"./types\";\nimport { runAgentWithTools } from \"./runner\";\nimport { resolveAgentEnvironment, buildSystemPrompt } from \"./environment\";\nimport { resolveProviderConfig, resolveApiKey, loadConfig } from \"../config\";\nimport type { ToolExecutionContext } from \"../handlers/types\";\nimport { error, warning, info, dim, bold, success, writeStream, writeLine, writeError } from \"../ui\";\n\nexport type SessionOptions = {\n agentMode: AgentMode;\n workspaceDir: string;\n interactive: boolean;\n model?: string;\n provider?: string;\n stream?: boolean;\n /**\n * Existing conversation history to continue from.\n * When provided, the system prompt is assumed to already be in the messages.\n * The new user prompt will be appended.\n */\n existingMessages?: AgentChatMessage[];\n};\n\n/**\n * Run an agent session.\n * Returns the updated messages array so callers (e.g. REPL) can persist context.\n */\nexport async function runAgentSession(\n prompt: string,\n options: SessionOptions,\n): Promise<AgentChatMessage[]> {\n const config = loadConfig();\n const workspaceDir = options.workspaceDir || null;\n\n // Resolve provider\n const providerId = (options.provider as any) ?? config.activeProvider;\n const resolvedConfig = resolveProviderConfig(config, providerId);\n const apiKey = resolveApiKey(resolvedConfig);\n\n // Resolve model\n const modelId = options.model ?? config.lastModel;\n\n // Build messages \u2014 either continue from existing or start fresh\n let messages: AgentChatMessage[];\n\n if (options.existingMessages && options.existingMessages.length > 0) {\n // Continue conversation: existing messages already contain the system prompt\n messages = [...options.existingMessages, { role: \"user\", content: prompt }];\n } else {\n // Fresh session: build system prompt\n const env = resolveAgentEnvironment(workspaceDir);\n const systemPrompt = buildSystemPrompt(env);\n messages = [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: prompt },\n ];\n }\n\n const taskId = `task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n const isStreaming = options.stream !== false;\n\n let currentContent = \"\";\n let currentThinking = \"\";\n let hasContent = false;\n\n // Tool execution context\n const toolContext: ToolExecutionContext = {\n workspaceDir,\n sessionId: taskId,\n taskId,\n agentMode: options.agentMode,\n };\n\n if (!isStreaming) {\n writeLine(`${info(\"\u2139\")} Running agent in ${bold(options.agentMode)} mode...\\n`);\n }\n\n try {\n const finalMessages = await runAgentWithTools(\n {\n taskId,\n baseUrl: resolvedConfig.baseUrl,\n apiKey,\n apiKeySource: resolvedConfig.apiKeySource,\n apiKeyEnvVar: resolvedConfig.apiKeyEnvVar,\n model: modelId,\n messages,\n agentMode: options.agentMode,\n },\n toolContext,\n (event: AgentEvent) => {\n switch (event.type) {\n case \"thinking_delta\": {\n currentThinking += event.delta;\n if (isStreaming && !hasContent) {\n writeStream(dim(event.delta));\n }\n break;\n }\n\n case \"content_delta\": {\n if (!hasContent && currentThinking && isStreaming) {\n // Transition from thinking to content\n writeLine(\"\");\n hasContent = true;\n }\n hasContent = true;\n currentContent += event.delta;\n if (isStreaming) {\n writeStream(event.delta);\n }\n break;\n }\n\n case \"tool_call_started\": {\n if (isStreaming) {\n if (hasContent || currentThinking) {\n writeLine(\"\");\n }\n writeLine(`${dim(\"\uD83D\uDD27\")} ${bold(event.name)}${dim(\"...\")}`);\n }\n break;\n }\n\n case \"tool_call_finished\": {\n if (event.errorText) {\n writeLine(` ${error(\"\u2717\")} ${dim(event.errorText.slice(0, 200))}`);\n } else if (isStreaming) {\n writeLine(` ${success(\"\u2713\")} ${dim(\"done\")}`);\n }\n break;\n }\n\n case \"status\": {\n if (event.status === \"completed\") {\n if (isStreaming) {\n writeLine(\"\");\n writeLine(success(`\\n\u2713 Task completed`));\n } else {\n writeLine(success(`\u2713 Task completed`));\n if (currentContent) {\n writeLine(\"\\n\" + currentContent);\n }\n }\n } else if (event.status === \"failed\") {\n writeLine(error(`\\n\u2717 Task failed`));\n } else if (event.status === \"cancelled\") {\n writeLine(warning(`\\n\u26A0 Task cancelled`));\n }\n break;\n }\n\n case \"done\": {\n if (event.usage && config.showUsage) {\n writeLine(dim(\n ` Tokens: ${event.usage.promptTokens}\u2191 ${event.usage.completionTokens}\u2193 ${event.usage.totalTokens}\u2211`,\n ));\n }\n break;\n }\n\n case \"error\": {\n writeLine(error(`\\n\u2717 Error: ${event.message}`));\n break;\n }\n }\n },\n );\n return finalMessages;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n writeError(error(`Fatal error: ${message}`));\n process.exit(1);\n }\n}\n", "/**\n * Terminal output utilities for Coder CLI.\n * Provides colored output, progress indication, and streaming helpers.\n */\n\nimport { isatty } from \"node:tty\";\n\nconst stdoutIsTTY = isatty(process.stdout.fd);\nconst stderrIsTTY = isatty(process.stderr.fd);\n\n// ANSI color codes (no dependency on chalk for speed)\nconst colors = {\n reset: \"\\x1b[0m\",\n bold: \"\\x1b[1m\",\n dim: \"\\x1b[2m\",\n italic: \"\\x1b[3m\",\n red: \"\\x1b[31m\",\n green: \"\\x1b[32m\",\n yellow: \"\\x1b[33m\",\n blue: \"\\x1b[34m\",\n magenta: \"\\x1b[35m\",\n cyan: \"\\x1b[36m\",\n gray: \"\\x1b[90m\",\n};\n\nexport function colorize(text: string, color: keyof typeof colors): string {\n if (!stdoutIsTTY) return text;\n return `${colors[color]}${text}${colors.reset}`;\n}\n\nexport function dim(text: string): string {\n return colorize(text, \"dim\");\n}\n\nexport function bold(text: string): string {\n if (!stdoutIsTTY) return text;\n return `${colors.bold}${text}${colors.reset}`;\n}\n\nexport function error(text: string): string {\n return colorize(text, \"red\");\n}\n\nexport function success(text: string): string {\n return colorize(text, \"green\");\n}\n\nexport function warning(text: string): string {\n return colorize(text, \"yellow\");\n}\n\nexport function info(text: string): string {\n return colorize(text, \"cyan\");\n}\n\n// ---------------------------------------------------------------------------\n// Streaming output\n// ---------------------------------------------------------------------------\n\nexport function writeStream(text: string): void {\n process.stdout.write(text);\n}\n\nexport function writeLine(text: string): void {\n process.stdout.write(text + \"\\n\");\n}\n\nexport function writeError(text: string): void {\n process.stderr.write(text + \"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Spinner / progress\n// ---------------------------------------------------------------------------\n\nconst SPINNER_FRAMES = [\"\u280B\", \"\u2819\", \"\u2839\", \"\u2838\", \"\u283C\", \"\u2834\", \"\u2826\", \"\u2827\", \"\u2807\", \"\u280F\"];\n\nexport class Spinner {\n private frame = 0;\n private interval: ReturnType<typeof setInterval> | null = null;\n private message = \"\";\n private running = false;\n\n start(message: string): void {\n if (!stderrIsTTY) {\n writeError(`\u23F3 ${message}`);\n return;\n }\n\n this.message = message;\n this.running = true;\n this.frame = 0;\n process.stderr.write(`${SPINNER_FRAMES[0]} ${message}`);\n this.interval = setInterval(() => {\n this.frame = (this.frame + 1) % SPINNER_FRAMES.length;\n process.stderr.write(`\\r${SPINNER_FRAMES[this.frame]} ${this.message}`);\n }, 80);\n }\n\n update(message: string): void {\n this.message = message;\n if (this.interval) {\n process.stderr.write(`\\r${SPINNER_FRAMES[this.frame]} ${this.message}`);\n }\n }\n\n stop(finalMessage?: string): void {\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = null;\n }\n this.running = false;\n // Clear the spinner line\n if (stderrIsTTY) {\n process.stderr.write(\"\\r\\x1b[K\");\n }\n if (finalMessage) {\n writeError(finalMessage);\n }\n }\n\n succeed(text: string): void {\n this.stop(`${success(\"\u2713\")} ${text}`);\n }\n\n fail(text: string): void {\n this.stop(`${error(\"\u2717\")} ${text}`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Thinking block rendering (like Claude Code's thinking animation)\n// ---------------------------------------------------------------------------\n\nexport class ThinkingDisplay {\n private spinner: Spinner;\n private lines: string[] = [];\n private expanded = false;\n\n constructor() {\n this.spinner = new Spinner();\n }\n\n start(): void {\n this.spinner.start(\"Thinking...\");\n }\n\n update(delta: string): void {\n this.spinner.update(\"Thinking...\");\n }\n\n stop(): void {\n this.spinner.stop();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Confirm prompt\n// ---------------------------------------------------------------------------\n\nexport async function confirmPrompt(question: string, autoYes: boolean = false): Promise<boolean> {\n if (autoYes) {\n return true;\n }\n\n if (!stderrIsTTY) {\n // Non-interactive: assume yes for piped input\n writeError(`${warning(\"\u26A0\")} ${question} ${dim(\"(assuming yes for non-interactive)\")}`);\n return true;\n }\n\n writeError(`${question} ${dim(\"[y/N]\")} `);\n\n return new Promise((resolve) => {\n process.stdin.once(\"data\", (data: Buffer) => {\n const input = data.toString().trim().toLowerCase();\n resolve(input === \"y\" || input === \"yes\");\n });\n });\n}\n\n// ---------------------------------------------------------------------------\n// Box / separator\n// ---------------------------------------------------------------------------\n\nexport function separator(title?: string): string {\n const width = Math.min(process.stdout.columns || 80, 80);\n if (!title) {\n return dim(\"\u2500\".repeat(width));\n }\n const titleStr = ` ${title} `;\n const half = Math.floor((width - titleStr.length) / 2);\n return dim(\"\u2500\".repeat(Math.max(0, half))) + bold(titleStr) + dim(\"\u2500\".repeat(Math.max(0, width - half - titleStr.length)));\n}\n", "/**\n * coder run <prompt> \u2014 Run the agent in full mode.\n * coder <prompt> \u2014 Shorthand for the same.\n */\n\nimport type { GlobalOptions } from \"./common\";\nimport { runAgentSession } from \"../agent/session\";\n\nexport async function runCommand(\n prompt: string,\n options: GlobalOptions,\n): Promise<void> {\n await runAgentSession(prompt, {\n ...options,\n agentMode: \"agent\",\n workspaceDir: options.workspace ?? process.cwd(),\n interactive: false,\n });\n}\n", "/**\n * coder ask <prompt> \u2014 Run in ask (read-only) mode.\n */\n\nimport type { GlobalOptions } from \"./common\";\nimport { runAgentSession } from \"../agent/session\";\n\nexport async function askCommand(\n prompt: string,\n options: GlobalOptions,\n): Promise<void> {\n await runAgentSession(prompt, {\n ...options,\n agentMode: \"ask\",\n workspaceDir: options.workspace ?? process.cwd(),\n interactive: false,\n });\n}\n", "/**\n * coder plan <prompt> \u2014 Run in plan mode.\n */\n\nimport type { GlobalOptions } from \"./common\";\nimport { runAgentSession } from \"../agent/session\";\n\nexport async function planCommand(\n prompt: string,\n options: GlobalOptions,\n): Promise<void> {\n await runAgentSession(prompt, {\n ...options,\n agentMode: \"plan\",\n workspaceDir: options.workspace ?? process.cwd(),\n interactive: false,\n });\n}\n", "/**\n * coder repl \u2014 Start interactive REPL session.\n */\n\nimport { createInterface } from \"node:readline\";\nimport type { GlobalOptions } from \"./common\";\nimport { runAgentSession } from \"../agent/session\";\nimport type { AgentChatMessage } from \"../agent/types\";\nimport { bold, dim, info, error, writeLine, writeError } from \"../ui\";\nimport { loadConfig } from \"../config\";\n\nexport async function replCommand(options: GlobalOptions): Promise<void> {\n const config = loadConfig();\n const workspaceDir = options.workspace ?? process.cwd();\n\n writeLine(\"\");\n writeLine(bold(\"Coder CLI \u2014 Interactive REPL\"));\n writeLine(dim(\"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\"));\n writeLine(dim(` Model: ${config.lastModel || \"not set\"}`));\n writeLine(dim(` Provider: ${config.activeProvider}`));\n writeLine(dim(` Workspace: ${workspaceDir}`));\n writeLine(dim(` Type 'exit' or Ctrl+C to quit`));\n writeLine(dim(\"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\"));\n writeLine(\"\");\n\n // Persistent conversation context across REPL turns\n let conversationMessages: AgentChatMessage[] | undefined;\n\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n prompt: `${bold(\"coder\")}> `,\n terminal: true,\n });\n\n rl.prompt();\n\n rl.on(\"line\", async (line: string) => {\n const trimmed = line.trim();\n\n if (!trimmed) {\n rl.prompt();\n return;\n }\n\n if (trimmed === \"exit\" || trimmed === \"quit\") {\n rl.close();\n return;\n }\n\n if (trimmed === \"clear\") {\n console.clear();\n rl.prompt();\n return;\n }\n\n if (trimmed === \"help\") {\n writeLine(bold(\"\\nREPL Commands:\"));\n writeLine(\" <prompt> Ask the agent anything\");\n writeLine(\" exit / quit Exit REPL\");\n writeLine(\" clear Clear screen\");\n writeLine(\" help Show this help\");\n writeLine(\" model <id> Switch model\");\n writeLine(\"\");\n rl.prompt();\n return;\n }\n\n if (trimmed.startsWith(\"model \")) {\n const modelId = trimmed.slice(6).trim();\n if (modelId) {\n config.lastModel = modelId;\n const { saveConfig } = await import(\"../config\");\n saveConfig(config);\n writeLine(info(`Model set to: ${modelId}`));\n }\n rl.prompt();\n return;\n }\n\n // Run the agent with the given prompt\n rl.pause();\n\n try {\n conversationMessages = await runAgentSession(trimmed, {\n agentMode: \"agent\",\n workspaceDir,\n interactive: true,\n model: options.model,\n provider: options.provider,\n existingMessages: conversationMessages,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n writeError(error(`Error: ${message}`));\n }\n\n writeLine(\"\");\n rl.prompt();\n rl.resume();\n });\n\n rl.on(\"close\", () => {\n writeLine(dim(\"\\nGoodbye! \uD83D\uDC4B\"));\n process.exit(0);\n });\n\n rl.on(\"SIGINT\", () => {\n rl.close();\n });\n}\n", "/**\n * coder init \u2014 Interactive initialization of Coder CLI configuration.\n */\n\nimport { bold, dim, info, success, error, writeLine, writeError } from \"../ui\";\nimport { loadConfig, saveConfig, getConfigDirPath, getConfigFilePathExplicit } from \"../config\";\nimport { createInterface } from \"node:readline\";\n\nexport async function initCommand(): Promise<void> {\n writeLine(bold(\"\\nCoder CLI \u2014 Initialization\"));\n writeLine(dim(\"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\"));\n writeLine(`Config directory: ${getConfigDirPath()}`);\n writeLine(\"\");\n\n const config = loadConfig();\n\n // Provider selection\n writeLine(info(\"Select your AI provider:\"));\n const providers = [\"deepseek\", \"glm\", \"agnes\", \"nvidia\", \"minimax\", \"custom\"];\n for (let i = 0; i < providers.length; i++) {\n writeLine(` ${i + 1}. ${providers[i]}`);\n }\n writeLine(\"\");\n\n const providerIndex = await askQuestion(\"Provider number [1]: \");\n const providerChoice = parseInt(providerIndex || \"1\", 10);\n const selectedProvider = providers[Math.max(0, Math.min(providerChoice - 1, providers.length - 1))];\n config.activeProvider = selectedProvider as any;\n writeLine(` Selected: ${bold(selectedProvider)}`);\n writeLine(\"\");\n\n // API key\n const providerSettings = config.providers[selectedProvider as keyof typeof config.providers];\n const envVar = providerSettings.apiKeyEnvVar;\n\n writeLine(info(`API Key Configuration for \"${selectedProvider}\":`));\n writeLine(` You can set the ${bold(envVar)} environment variable,`);\n writeLine(` or enter the key directly (stored in config file).`);\n writeLine(\"\");\n\n const source = await askQuestion(\"Use environment variable? [Y/n]: \");\n const useEnv = source.toLowerCase() !== \"n\";\n\n if (useEnv) {\n providerSettings.apiKeySource = \"env\";\n writeLine(` Using ${bold(envVar)} environment variable.`);\n writeLine(` Make sure to set it: export ${envVar}=<your-api-key>`);\n } else {\n providerSettings.apiKeySource = \"manual\";\n const key = await askQuestion(\"Enter API key: \");\n if (key.trim()) {\n providerSettings.apiKey = key.trim();\n writeLine(\" API key saved to config.\");\n } else {\n writeLine(` ${error(\"No key entered. Set it later with: coder config\")}`);\n }\n }\n writeLine(\"\");\n\n // Custom base URL (optional)\n if (selectedProvider === \"custom\") {\n const baseUrl = await askQuestion(\"Custom API base URL: \");\n if (baseUrl.trim()) {\n providerSettings.customBaseUrl = baseUrl.trim();\n }\n } else {\n const customUrl = await askQuestion(`Custom base URL (leave empty for default): `);\n if (customUrl.trim()) {\n providerSettings.customBaseUrl = customUrl.trim();\n }\n writeLine(\"\");\n }\n\n // Save config\n saveConfig(config);\n writeLine(success(\"\\n\u2713 Configuration saved!\"));\n writeLine(` Config file: ${getConfigFilePathExplicit()}`);\n writeLine(\"\");\n writeLine(info(\"Quick start:\"));\n writeLine(\" coder \\\"What is in this directory?\\\"\");\n writeLine(\" coder ask \\\"Explain this code\\\"\");\n writeLine(\" coder repl\");\n writeLine(\"\");\n}\n\nfunction askQuestion(query: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n\n return new Promise((resolve) => {\n rl.question(query, (answer) => {\n rl.close();\n resolve(answer);\n });\n });\n}\n", "/**\n * coder config \u2014 View or edit configuration.\n */\n\nimport { bold, dim, info, warning, writeLine } from \"../ui\";\nimport {\n loadConfig,\n saveConfig,\n getConfigDirPath,\n getConfigFilePathExplicit,\n type ProviderId,\n type ProviderSettings,\n} from \"../config\";\n\nconst PROVIDER_IDS: ProviderId[] = [\"deepseek\", \"glm\", \"agnes\", \"nvidia\", \"minimax\", \"custom\"];\n\nexport async function configCommand(key?: string, value?: string): Promise<void> {\n const config = loadConfig();\n\n // If no args, show full config\n if (!key) {\n showConfig(config);\n return;\n }\n\n // If both key and value, set a value\n if (key && value !== undefined) {\n await setConfigValue(config, key, value);\n return;\n }\n\n // If only key, show that specific value\n showConfigValue(config, key);\n}\n\nfunction showConfig(config: ReturnType<typeof loadConfig>): void {\n writeLine(bold(\"\\nCoder CLI Configuration\"));\n writeLine(dim(\"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\"));\n writeLine(`Config directory: ${getConfigDirPath()}`);\n writeLine(`Config file: ${getConfigFilePathExplicit()}`);\n writeLine(\"\");\n writeLine(bold(\"Active Settings:\"));\n writeLine(` Active provider: ${config.activeProvider}`);\n writeLine(` Last model: ${config.lastModel}`);\n writeLine(` Show usage: ${config.showUsage}`);\n writeLine(\"\");\n\n for (const providerId of PROVIDER_IDS) {\n const p = config.providers[providerId];\n writeLine(bold(`Provider: ${providerId}`));\n writeLine(` API Key Source: ${p.apiKeySource}`);\n writeLine(` API Key Env Var: ${p.apiKeyEnvVar}`);\n writeLine(` API Key (stored): ${p.apiKey ? \"***\" : \"(not set)\"}`);\n writeLine(` Custom Base URL: ${p.customBaseUrl || \"(default)\"}`);\n writeLine(\"\");\n }\n\n writeLine(dim(\"To change a value: coder config <key> <value>\"));\n writeLine(dim(\"Examples:\"));\n writeLine(dim(' coder config activeProvider \"deepseek\"'));\n writeLine(dim(' coder config lastModel \"deepseek-v4-flash\"'));\n writeLine(dim(' coder config providers.deepseek.apiKeySource \"env\"'));\n writeLine(\"\");\n}\n\nfunction showConfigValue(config: ReturnType<typeof loadConfig>, key: string): void {\n const value = getNestedValue(config, key);\n if (value === undefined) {\n writeLine(warning(`Config key not found: ${key}`));\n return;\n }\n writeLine(String(value));\n}\n\nasync function setConfigValue(\n config: ReturnType<typeof loadConfig>,\n key: string,\n value: string,\n): Promise<void> {\n setNestedValue(config, key, parseValue(value));\n saveConfig(config);\n writeLine(info(`Config updated: ${key} = ${value}`));\n}\n\nfunction getNestedValue(obj: Record<string, unknown>, path: string): unknown {\n const parts = path.split(\".\");\n let current: unknown = obj;\n\n for (const part of parts) {\n if (current === null || current === undefined || typeof current !== \"object\") {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n\n return current;\n}\n\nfunction setNestedValue(obj: Record<string, unknown>, path: string, value: unknown): void {\n const parts = path.split(\".\");\n let current = obj;\n\n for (let i = 0; i < parts.length - 1; i++) {\n if (!(parts[i] in current)) {\n current[parts[i]] = {};\n }\n current = current[parts[i]] as Record<string, unknown>;\n }\n\n current[parts[parts.length - 1]] = value;\n}\n\nfunction parseValue(value: string): unknown {\n // Try to parse as boolean\n if (value === \"true\") return true;\n if (value === \"false\") return false;\n // Try to parse as number\n const num = Number(value);\n if (!isNaN(num) && value.trim() !== \"\") return num;\n // Keep as string\n return value;\n}\n", "/**\n * Shared types and helpers for CLI commands.\n */\n\nimport type { AgentMode } from \"../agent/types\";\n\nexport type GlobalOptions = {\n model?: string;\n provider?: string;\n workspace?: string;\n yes?: boolean;\n stream?: boolean;\n};\n\nexport type CommandContext = GlobalOptions & {\n agentMode: AgentMode;\n};\n\nlet _globalOptions: GlobalOptions = {};\n\nexport function setGlobalOptions(opts: GlobalOptions): void {\n _globalOptions = opts;\n}\n\nexport function getGlobalOptions(): GlobalOptions {\n return _globalOptions;\n}\n", "/**\n * Coder CLI \u2014 Entrypoint\n *\n * Usage:\n * coder <prompt> Run agent with prompt\n * coder ask <prompt> Run in ask mode\n * coder plan <prompt> Run in plan mode\n * coder repl Start interactive REPL\n * coder init Initialize configuration\n * coder config Show/edit configuration\n * coder --version Show version\n * coder --help Show help\n */\n\nimport { runCommand } from \"./commands/run\";\nimport { askCommand } from \"./commands/ask\";\nimport { planCommand } from \"./commands/plan\";\nimport { replCommand } from \"./commands/repl\";\nimport { initCommand } from \"./commands/init\";\nimport { configCommand } from \"./commands/config\";\nimport { setGlobalOptions } from \"./commands/common\";\nimport { error, writeError } from \"./ui\";\n\nconst VERSION = \"0.1.0\";\n\nasync function main() {\n // Quick help/version check before commander\n const rawArgs = process.argv.slice(2);\n\n if (rawArgs.includes(\"--help\") || rawArgs.includes(\"-h\")) {\n printHelp();\n return;\n }\n\n if (rawArgs.includes(\"--version\") || rawArgs.includes(\"-V\")) {\n console.log(VERSION);\n return;\n }\n\n // If no args, start REPL\n if (rawArgs.length === 0) {\n await replCommand({});\n return;\n }\n\n // Extract global options from raw args\n const globalOpts = extractGlobalOptions(rawArgs);\n setGlobalOptions(globalOpts);\n\n // Check for known subcommands\n const firstNonFlag = rawArgs.find((a) => !a.startsWith(\"-\"));\n const knownSubcommands: Record<string, (args: string[], opts: Record<string, unknown>) => Promise<void>> = {\n ask: async (args, opts) => { await askCommand(args.join(\" \"), opts); },\n plan: async (args, opts) => { await planCommand(args.join(\" \"), opts); },\n repl: async () => { await replCommand(globalOpts); },\n init: async () => { await initCommand(); },\n config: async (args) => { await configCommand(args[0], args[1]); },\n };\n\n if (firstNonFlag && knownSubcommands[firstNonFlag]) {\n const cmdArgs = rawArgs.slice(rawArgs.indexOf(firstNonFlag) + 1).filter((a) => !a.startsWith(\"-\"));\n await knownSubcommands[firstNonFlag](cmdArgs, globalOpts);\n return;\n }\n\n // Everything else is treated as a prompt\n const promptParts = rawArgs.filter((a) => !a.startsWith(\"-\"));\n const prompt = promptParts.join(\" \");\n\n if (!prompt.trim()) {\n await replCommand(globalOpts);\n return;\n }\n\n await runCommand(prompt, globalOpts);\n}\n\nfunction extractGlobalOptions(args: string[]): Record<string, unknown> {\n const opts: Record<string, unknown> = {};\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === \"-m\" || arg === \"--model\") {\n opts.model = args[++i] ?? \"\";\n } else if (arg === \"-p\" || arg === \"--provider\") {\n opts.provider = args[++i] ?? \"\";\n } else if (arg === \"-w\" || arg === \"--workspace\") {\n opts.workspace = args[++i] ?? \"\";\n } else if (arg === \"-y\" || arg === \"--yes\") {\n opts.yes = true;\n } else if (arg === \"--no-stream\") {\n opts.stream = false;\n } else if (arg === \"--stream\") {\n opts.stream = true;\n }\n }\n return opts;\n}\n\nfunction printHelp(): void {\n console.log(`\nUsage: coder [options] [prompt...]\n\nCoder CLI \u2014 AI-powered coding assistant in the terminal\n\nOptions:\n -V, --version output the version number\n -m, --model <model> Model ID to use\n -p, --provider <provider> Provider ID (deepseek, glm, agnes, nvidia, minimax, custom)\n -w, --workspace <path> Workspace directory\n -y, --yes Auto-confirm prompts (unattended mode)\n --no-stream Disable streaming output\n -h, --help display help for command\n\nCommands:\n ask [prompt...] Ask a question (read-only mode)\n plan [prompt...] Create or work on a plan\n repl Start interactive REPL session\n init Initialize Coder CLI configuration\n config [key] [value] View or edit configuration\n`);\n}\n\nmain().catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n writeError(error(`Fatal: ${message}`));\n process.exit(1);\n});\n"],
|
|
4
|
+
"sourcesContent": ["/**\n * Coder CLI Configuration System\n *\n * Stores provider settings, API keys, model preferences, and CLI options\n * in a platform-appropriate config directory (e.g. ~/.config/coder/ on Linux).\n */\n\nimport { homedir, platform, EOL } from \"node:os\";\nimport { join, dirname } from \"node:path\";\nimport { mkdirSync, existsSync, readFileSync, writeFileSync } from \"node:fs\";\n\n// ---------------------------------------------------------------------------\n// Config directory resolution\n// ---------------------------------------------------------------------------\n\nfunction getConfigDir(): string {\n const home = homedir();\n const p = platform();\n\n if (p === \"win32\") {\n // Windows: %APPDATA%/Coder or fallback to %USERPROFILE%/.config/coder\n return process.env.APPDATA\n ? join(process.env.APPDATA, \"Coder\", \"cli\")\n : join(home, \".config\", \"coder\", \"cli\");\n }\n\n // macOS / Linux: $XDG_CONFIG_HOME/coder or ~/.config/coder\n if (process.env.XDG_CONFIG_HOME) {\n return join(process.env.XDG_CONFIG_HOME, \"coder\", \"cli\");\n }\n\n return join(home, \".config\", \"coder\", \"cli\");\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type ApiKeySource = \"manual\" | \"env\";\n\nexport type ProviderSettings = {\n apiKeySource: ApiKeySource;\n apiKey: string;\n apiKeyEnvVar: string;\n customBaseUrl: string;\n showUsage: boolean;\n};\n\nexport type ProviderId =\n | \"deepseek\"\n | \"glm\"\n | \"agnes\"\n | \"nvidia\"\n | \"minimax\"\n | \"custom\";\n\nexport type ModelDefinition = {\n id: string;\n label?: string;\n contextWindow: number;\n supportsThinking: boolean;\n supportsMultimodal: boolean;\n};\n\nexport type ResolvedProviderConfig = {\n provider: ProviderId;\n baseUrl: string;\n apiKeySource: ApiKeySource;\n apiKey: string;\n apiKeyEnvVar: string;\n models: ModelDefinition[];\n};\n\nexport type CoderCliConfig = {\n /** Active provider ID */\n activeProvider: ProviderId;\n /** Provider-specific settings */\n providers: Record<ProviderId, ProviderSettings>;\n /** Last used model ID */\n lastModel: string;\n /** Whether to show token usage after each response */\n showUsage: boolean;\n};\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\nconst PRESET_PROVIDERS: Record<Exclude<ProviderId, \"custom\">, { baseUrl: string; defaultApiKeyEnvVar: string }> = {\n deepseek: { baseUrl: \"https://api.deepseek.com\", defaultApiKeyEnvVar: \"DEEPSEEK_API_KEY\" },\n glm: { baseUrl: \"https://open.bigmodel.cn/api/paas/v4\", defaultApiKeyEnvVar: \"GLM_API_KEY\" },\n agnes: { baseUrl: \"https://api.agnesai.com\", defaultApiKeyEnvVar: \"AGNES_API_KEY\" },\n nvidia: { baseUrl: \"https://integrate.api.nvidia.com/v1\", defaultApiKeyEnvVar: \"NVIDIA_API_KEY\" },\n minimax: { baseUrl: \"https://api.minimax.chat/v1\", defaultApiKeyEnvVar: \"MINIMAX_API_KEY\" },\n};\n\nconst PRESET_MODELS: Record<Exclude<ProviderId, \"custom\">, ModelDefinition[]> = {\n deepseek: [\n { id: \"deepseek-v4-flash\", label: \"DeepSeek V4 Flash\", contextWindow: 1_000_000, supportsThinking: true, supportsMultimodal: false },\n { id: \"deepseek-chat\", label: \"DeepSeek Chat\", contextWindow: 1_000_000, supportsThinking: false, supportsMultimodal: true },\n ],\n glm: [\n { id: \"glm-4\", label: \"GLM-4\", contextWindow: 128_000, supportsThinking: false, supportsMultimodal: true },\n { id: \"glm-4-plus\", label: \"GLM-4 Plus\", contextWindow: 128_000, supportsThinking: true, supportsMultimodal: true },\n ],\n agnes: [\n { id: \"agnes-v3\", label: \"Agnes V3\", contextWindow: 128_000, supportsThinking: false, supportsMultimodal: true },\n ],\n nvidia: [],\n minimax: [\n { id: \"minimax-m1\", label: \"MiniMax M1\", contextWindow: 200_000, supportsThinking: true, supportsMultimodal: false },\n ],\n};\n\nfunction createDefaultProviderSettings(provider: ProviderId): ProviderSettings {\n const isCustom = provider === \"custom\";\n return {\n apiKeySource: \"env\",\n apiKey: \"\",\n apiKeyEnvVar: isCustom ? \"CUSTOM_API_KEY\" : PRESET_PROVIDERS[provider]?.defaultApiKeyEnvVar ?? \"API_KEY\",\n customBaseUrl: isCustom ? \"https://api.example.com/v1\" : \"\",\n showUsage: false,\n };\n}\n\nconst PROVIDER_IDS: ProviderId[] = [\"deepseek\", \"glm\", \"agnes\", \"nvidia\", \"minimax\", \"custom\"];\n\nfunction createDefaultConfig(): CoderCliConfig {\n const providers = {} as Record<ProviderId, ProviderSettings>;\n for (const id of PROVIDER_IDS) {\n providers[id] = createDefaultProviderSettings(id);\n }\n return {\n activeProvider: \"deepseek\",\n providers,\n lastModel: \"deepseek-v4-flash\",\n showUsage: false,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Config file I/O\n// ---------------------------------------------------------------------------\n\nconst CONFIG_FILE = \"config.json\";\n\nfunction getConfigFilePath(): string {\n return join(getConfigDir(), CONFIG_FILE);\n}\n\nfunction ensureConfigDir(): void {\n const dir = getConfigDir();\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n}\n\nexport function loadConfig(): CoderCliConfig {\n const configPath = getConfigFilePath();\n if (!existsSync(configPath)) {\n const defaults = createDefaultConfig();\n saveConfig(defaults);\n return defaults;\n }\n\n try {\n const raw = readFileSync(configPath, \"utf-8\");\n const parsed = JSON.parse(raw);\n return mergeWithDefaults(parsed);\n } catch {\n const defaults = createDefaultConfig();\n saveConfig(defaults);\n return defaults;\n }\n}\n\nfunction mergeWithDefaults(raw: Record<string, unknown>): CoderCliConfig {\n const defaults = createDefaultConfig();\n return {\n activeProvider: (PROVIDER_IDS as readonly string[]).includes(String(raw.activeProvider ?? \"\"))\n ? (raw.activeProvider as ProviderId)\n : defaults.activeProvider,\n providers: mergeProviders(raw.providers as Record<string, unknown> | undefined, defaults.providers),\n lastModel: typeof raw.lastModel === \"string\" ? raw.lastModel : defaults.lastModel,\n showUsage: typeof raw.showUsage === \"boolean\" ? raw.showUsage : defaults.showUsage,\n };\n}\n\nfunction mergeProviders(\n raw: Record<string, unknown> | undefined,\n defaults: Record<ProviderId, ProviderSettings>,\n): Record<ProviderId, ProviderSettings> {\n const result = { ...defaults };\n if (!raw || typeof raw !== \"object\") {\n return result;\n }\n\n for (const id of PROVIDER_IDS) {\n const p = (raw as Record<string, Record<string, unknown>>)[id];\n if (p && typeof p === \"object\") {\n result[id] = {\n apiKeySource: p.apiKeySource === \"manual\" ? \"manual\" : \"env\",\n apiKey: typeof p.apiKey === \"string\" ? p.apiKey : result[id].apiKey,\n apiKeyEnvVar: typeof p.apiKeyEnvVar === \"string\" ? p.apiKeyEnvVar : result[id].apiKeyEnvVar,\n customBaseUrl: typeof p.customBaseUrl === \"string\" ? p.customBaseUrl : result[id].customBaseUrl,\n showUsage: typeof p.showUsage === \"boolean\" ? p.showUsage : result[id].showUsage,\n };\n }\n }\n\n return result;\n}\n\nexport function saveConfig(config: CoderCliConfig): void {\n ensureConfigDir();\n const configPath = getConfigFilePath();\n writeFileSync(configPath, JSON.stringify(config, null, 2), \"utf-8\");\n}\n\n// ---------------------------------------------------------------------------\n// Provider config resolution\n// ---------------------------------------------------------------------------\n\nexport function resolveProviderConfig(config: CoderCliConfig, providerId?: ProviderId): ResolvedProviderConfig {\n const provider = providerId ?? config.activeProvider;\n const settings = config.providers[provider];\n\n if (provider === \"custom\") {\n if (!settings.customBaseUrl.trim()) {\n throw new Error(\n \"Custom provider selected but no base URL configured. \" +\n \"Run `coder config` or manually edit the config file.\"\n );\n }\n return {\n provider,\n baseUrl: settings.customBaseUrl.trim(),\n apiKeySource: settings.apiKeySource,\n apiKey: settings.apiKey,\n apiKeyEnvVar: settings.apiKeyEnvVar.trim(),\n models: [],\n };\n }\n\n const preset = PRESET_PROVIDERS[provider];\n if (!preset) {\n throw new Error(`Unknown provider: ${provider}`);\n }\n\n return {\n provider,\n baseUrl: settings.customBaseUrl.trim() || preset.baseUrl,\n apiKeySource: settings.apiKeySource,\n apiKey: settings.apiKey,\n apiKeyEnvVar: settings.apiKeyEnvVar.trim() || preset.defaultApiKeyEnvVar,\n models: settings.customBaseUrl.trim()\n ? []\n : PRESET_MODELS[provider] ?? [],\n };\n}\n\nexport function resolveApiKey(resolved: ResolvedProviderConfig): string {\n if (resolved.apiKeySource === \"manual\") {\n if (!resolved.apiKey.trim()) {\n throw new Error(\n `API key for ${resolved.provider} is not configured. ` +\n \"Set it with: coder config providers.<provider>.apiKey <key>\"\n );\n }\n return resolved.apiKey.trim();\n }\n\n // Read from environment variable\n const envVar = resolved.apiKeyEnvVar;\n const envValue = process.env[envVar]?.trim();\n if (!envValue) {\n throw new Error(\n `Environment variable ${envVar} is not set. ` +\n `Set it with: export ${envVar}=<your-api-key>`\n );\n }\n return envValue;\n}\n\n// ---------------------------------------------------------------------------\n// Config directory path (exported for diagnostics)\n// ---------------------------------------------------------------------------\n\nexport function getConfigDirPath(): string {\n return getConfigDir();\n}\n\nexport function getConfigFilePathExplicit(): string {\n return getConfigFilePath();\n}\n", "import { readdirSync, statSync } from \"node:fs\";\nimport { resolve, relative } from \"node:path\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ListDirEntry, ToolHandler } from \"./types\";\n\ntype ListDirArgs = {\n path: string;\n recursive?: boolean;\n max_depth?: number;\n show_hidden?: boolean;\n};\n\nexport const listDirHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as ListDirArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"list_dir\", \"workspace_required\", \"No workspace directory set\");\n }\n\n const dirPath = resolve(context.workspaceDir, args.path);\n const maxDepth = args.recursive ? (args.max_depth ?? 1) : 1;\n\n try {\n const entries = walkDirectory(dirPath, 0, maxDepth, args.show_hidden ?? false);\n return toolSuccess(\"list_dir\", {\n path: relative(context.workspaceDir, dirPath),\n entries,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"list_dir\", \"read_error\", message);\n }\n};\n\nfunction walkDirectory(\n dirPath: string,\n currentDepth: number,\n maxDepth: number,\n showHidden: boolean,\n): ListDirEntry[] {\n const results: ListDirEntry[] = [];\n\n let entries: string[];\n try {\n entries = readdirSync(dirPath);\n } catch {\n return results;\n }\n\n for (const name of entries) {\n if (!showHidden && name.startsWith(\".\")) {\n continue;\n }\n\n const fullPath = resolve(dirPath, name);\n let stats: ReturnType<typeof statSync>;\n try {\n stats = statSync(fullPath);\n } catch {\n continue;\n }\n\n results.push({\n name,\n path: fullPath,\n isDir: stats.isDirectory(),\n size: stats.isFile() ? stats.size : undefined,\n });\n\n if (stats.isDirectory() && currentDepth < maxDepth) {\n results.push(...walkDirectory(fullPath, currentDepth + 1, maxDepth, showHidden));\n }\n }\n\n return results;\n}\n", "export function toolSuccess(tool: string, data: unknown): { ok: true; tool: string; data: unknown } {\n return { ok: true, tool, data };\n}\n\nexport function toolFailure(\n tool: string,\n code: string,\n message: string,\n): { ok: false; tool: string; error: { code: string; message: string } } {\n return {\n ok: false,\n tool,\n error: { code, message },\n };\n}\n", "import { readFileSync, statSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype ReadFileArgs = {\n path: string;\n start_line?: number;\n max_lines?: number;\n respect_gitignore?: boolean;\n};\n\nexport const readFileHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as ReadFileArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"read_file\", \"workspace_required\", \"No workspace directory set\");\n }\n\n const filePath = resolve(context.workspaceDir, args.path);\n const startLine = args.start_line ?? 1;\n const maxLines = args.max_lines ?? 500;\n\n try {\n statSync(filePath); // Check file exists\n const content = readFileSync(filePath, \"utf-8\");\n const lines = content.split(\"\\n\");\n const totalLines = lines.length;\n\n // Pagination\n const endLine = Math.min(startLine + maxLines - 1, totalLines);\n const paginatedContent = lines.slice(startLine - 1, endLine).join(\"\\n\");\n\n return toolSuccess(\"read_file\", {\n path: filePath,\n encoding: \"utf-8\",\n mimeType: \"text/plain\",\n content: paginatedContent,\n totalLines,\n startLine,\n endLine,\n truncated: endLine < totalLines,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"ENOENT\")) {\n return toolFailure(\"read_file\", \"not_found\", `File not found: ${filePath}`);\n }\n return toolFailure(\"read_file\", \"read_error\", message);\n }\n};\n", "import { writeFileSync, mkdirSync, existsSync } from \"node:fs\";\nimport { resolve, dirname } from \"node:path\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype WriteFileArgs = {\n path: string;\n content: string;\n create_parent_dirs?: boolean;\n};\n\nexport const writeFileHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as WriteFileArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"write_file\", \"workspace_required\", \"No workspace directory set\");\n }\n\n const filePath = resolve(context.workspaceDir, args.path);\n\n // Prevent writing outside workspace\n if (!filePath.startsWith(context.workspaceDir.replace(/\\\\/g, \"/\").replace(/\\\\/g, \"/\"))) {\n return toolFailure(\"write_file\", \"path_escape\", \"Path escapes workspace directory\");\n }\n\n if (existsSync(filePath)) {\n return toolFailure(\"write_file\", \"file_exists\", `File already exists: ${args.path}. Use replace_file to overwrite.`);\n }\n\n try {\n if (args.create_parent_dirs !== false) {\n const parentDir = dirname(filePath);\n if (!existsSync(parentDir)) {\n mkdirSync(parentDir, { recursive: true });\n }\n }\n\n const lines = args.content.split(\"\\n\");\n writeFileSync(filePath, args.content, \"utf-8\");\n\n return toolSuccess(\"write_file\", {\n path: args.path,\n action: \"created\",\n bytesWritten: Buffer.byteLength(args.content, \"utf-8\"),\n linesAdded: lines.length,\n linesRemoved: 0,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"write_file\", \"write_error\", message);\n }\n};\n", "import { writeFileSync, readFileSync, existsSync, mkdirSync } from \"node:fs\";\nimport { resolve, dirname } from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype ReplaceFileArgs = {\n path: string;\n content: string;\n expected_sha256?: string;\n create_backup?: boolean;\n respect_gitignore?: boolean;\n};\n\nexport const replaceFileHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as ReplaceFileArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"replace_file\", \"workspace_required\", \"No workspace directory set\");\n }\n\n const filePath = resolve(context.workspaceDir, args.path);\n\n if (!existsSync(filePath)) {\n return toolFailure(\"replace_file\", \"not_found\", `File not found: ${args.path}`);\n }\n\n try {\n const oldContent = readFileSync(filePath, \"utf-8\");\n const oldLines = oldContent.split(\"\\n\");\n const newLines = args.content.split(\"\\n\");\n\n // Verify SHA256 if provided\n if (args.expected_sha256) {\n const actualHash = createHash(\"sha256\").update(oldContent).digest(\"hex\");\n if (actualHash !== args.expected_sha256) {\n return toolFailure(\n \"replace_file\",\n \"content_changed\",\n `File content changed since last read. Expected SHA256: ${args.expected_sha256}, got: ${actualHash}. Read the file again to get the latest content.`,\n );\n }\n }\n\n writeFileSync(filePath, args.content, \"utf-8\");\n\n return toolSuccess(\"replace_file\", {\n path: args.path,\n action: \"replaced\",\n bytesWritten: Buffer.byteLength(args.content, \"utf-8\"),\n linesAdded: newLines.length,\n linesRemoved: oldLines.length,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"replace_file\", \"write_error\", message);\n }\n};\n", "import { readFileSync, writeFileSync, existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype EditFileArgs = {\n path: string;\n old_string: string;\n new_string: string;\n expected_sha256?: string;\n replace_all?: boolean;\n create_backup?: boolean;\n respect_gitignore?: boolean;\n};\n\nexport const editFileHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as EditFileArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"edit_file\", \"workspace_required\", \"No workspace directory set\");\n }\n\n const filePath = resolve(context.workspaceDir, args.path);\n\n if (!existsSync(filePath)) {\n return toolFailure(\"edit_file\", \"not_found\", `File not found: ${args.path}`);\n }\n\n try {\n const oldContent = readFileSync(filePath, \"utf-8\");\n const oldLines = oldContent.split(\"\\n\");\n\n // Verify SHA256 if provided\n if (args.expected_sha256) {\n const actualHash = createHash(\"sha256\").update(oldContent).digest(\"hex\");\n if (actualHash !== args.expected_sha256) {\n return toolFailure(\n \"edit_file\",\n \"content_changed\",\n `File content changed since last read. Expected SHA256: ${args.expected_sha256}, got: ${actualHash}.`,\n );\n }\n }\n\n // Apply search-and-replace\n let newContent: string;\n if (args.replace_all) {\n newContent = oldContent.split(args.old_string).join(args.new_string);\n } else {\n const index = oldContent.indexOf(args.old_string);\n if (index === -1) {\n return toolFailure(\n \"edit_file\",\n \"string_not_found\",\n `Could not find the exact text to replace in ${args.path}. The text must match exactly.`,\n );\n }\n newContent = oldContent.slice(0, index) + args.new_string + oldContent.slice(index + args.old_string.length);\n }\n\n const newLines = newContent.split(\"\\n\");\n writeFileSync(filePath, newContent, \"utf-8\");\n\n // Calculate diff stats\n const linesAdded = newLines.length - oldLines.length;\n const linesRemoved = oldLines.length - newLines.length;\n\n return toolSuccess(\"edit_file\", {\n path: args.path,\n action: \"modified\",\n bytesWritten: Buffer.byteLength(newContent, \"utf-8\"),\n linesAdded: linesAdded > 0 ? linesAdded : 0,\n linesRemoved: linesRemoved > 0 ? linesRemoved : 0,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"edit_file\", \"write_error\", message);\n }\n};\n", "import { readFileSync, writeFileSync, existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype ReplaceLinesArgs = {\n path: string;\n start_line: number;\n end_line: number;\n content: string;\n expected_sha256?: string;\n create_backup?: boolean;\n respect_gitignore?: boolean;\n};\n\nexport const replaceLinesHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as ReplaceLinesArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"replace_lines\", \"workspace_required\", \"No workspace directory set\");\n }\n\n const filePath = resolve(context.workspaceDir, args.path);\n\n if (!existsSync(filePath)) {\n return toolFailure(\"replace_lines\", \"not_found\", `File not found: ${args.path}`);\n }\n\n try {\n const oldContent = readFileSync(filePath, \"utf-8\");\n const oldLines = oldContent.split(\"\\n\");\n\n // Verify SHA256 if provided\n if (args.expected_sha256) {\n const actualHash = createHash(\"sha256\").update(oldContent).digest(\"hex\");\n if (actualHash !== args.expected_sha256) {\n return toolFailure(\n \"replace_lines\",\n \"content_changed\",\n `File content changed since last read. Expected SHA256: ${args.expected_sha256}, got: ${actualHash}.`,\n );\n }\n }\n\n // Validate line range\n if (args.start_line < 1 || args.end_line < args.start_line || args.start_line > oldLines.length) {\n return toolFailure(\n \"replace_lines\",\n \"invalid_range\",\n `Invalid line range: ${args.start_line}-${args.end_line}. File has ${oldLines.length} lines.`,\n );\n }\n\n const endLine = Math.min(args.end_line, oldLines.length);\n const replacementLines = args.content === \"\" ? [] : args.content.split(\"\\n\");\n\n // Build new content: lines before start + replacement + lines after end\n const before = oldLines.slice(0, args.start_line - 1);\n const after = oldLines.slice(endLine);\n const newContent = [...before, ...replacementLines, ...after].join(\"\\n\");\n\n writeFileSync(filePath, newContent, \"utf-8\");\n\n return toolSuccess(\"replace_lines\", {\n path: args.path,\n action: \"modified\",\n bytesWritten: Buffer.byteLength(newContent, \"utf-8\"),\n linesAdded: replacementLines.length,\n linesRemoved: endLine - args.start_line + 1,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"replace_lines\", \"write_error\", message);\n }\n};\n", "import { readdirSync, statSync } from \"node:fs\";\nimport { resolve, relative, sep } from \"node:path\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype GlobArgs = {\n glob_pattern: string;\n target_directory?: string;\n head_limit?: number;\n respect_gitignore?: boolean;\n};\n\n/**\n * Simple glob matching \u2014 converts glob patterns to regex and walks directories.\n * For complex patterns, the CLI agent can use the shell tool to call the real `find` or `dir` command.\n */\nexport const globHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as GlobArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"glob\", \"workspace_required\", \"No workspace directory set\");\n }\n\n const searchDir = args.target_directory\n ? resolve(context.workspaceDir, args.target_directory)\n : context.workspaceDir;\n\n const headLimit = args.head_limit ?? 100;\n const pattern = args.glob_pattern;\n\n try {\n const matches = simpleGlob(searchDir, pattern, context.workspaceDir, headLimit);\n return toolSuccess(\"glob\", {\n pattern,\n targetDirectory: relative(context.workspaceDir, searchDir),\n matches: matches.slice(0, headLimit),\n totalMatches: matches.length,\n truncated: matches.length > headLimit,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"glob\", \"search_error\", message);\n }\n};\n\nfunction simpleGlob(\n rootDir: string,\n pattern: string,\n workspaceDir: string,\n limit: number,\n): string[] {\n const results: string[] = [];\n\n // Simple recursive walk \u2014 for production, use a proper glob library\n function walk(dir: string): void {\n if (results.length >= limit) return;\n\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n\n for (const name of entries) {\n if (results.length >= limit) return;\n if (name.startsWith(\".\")) continue;\n\n const fullPath = resolve(dir, name);\n const relPath = relative(workspaceDir, fullPath).replace(/\\\\/g, \"/\");\n\n let stats: ReturnType<typeof statSync>;\n try {\n stats = statSync(fullPath);\n } catch {\n continue;\n }\n\n // Simple glob matching: check if pattern matches the relative path\n const regex = globToRegex(pattern);\n if (regex.test(relPath)) {\n results.push(relPath);\n }\n\n if (stats.isDirectory()) {\n walk(fullPath);\n }\n }\n }\n\n walk(rootDir);\n\n // Sort for consistent output\n results.sort();\n return results;\n}\n\nfunction globToRegex(pattern: string): RegExp {\n let regexStr = \"^\";\n let i = 0;\n\n while (i < pattern.length) {\n const ch = pattern[i];\n if (ch === \"*\") {\n if (i + 1 < pattern.length && pattern[i + 1] === \"*\") {\n // ** \u2014 match everything including path separators\n regexStr += \".*\";\n i += 2;\n // Handle **/ pattern\n if (i < pattern.length && pattern[i] === \"/\") {\n regexStr += \"/?\";\n i++;\n }\n } else {\n // * \u2014 match anything except path separator\n regexStr += \"[^/]*\";\n i++;\n }\n } else if (ch === \"?\") {\n regexStr += \"[^/]\";\n i++;\n } else if (ch === \".\" || ch === \"+\" || ch === \"(\" || ch === \")\" || ch === \"[\" || ch === \"]\" || ch === \"{\"\n || ch === \"}\" || ch === \"\\\\\" || ch === \"^\" || ch === \"$\" || ch === \"|\") {\n regexStr += \"\\\\\" + ch;\n i++;\n } else {\n regexStr += ch;\n i++;\n }\n }\n\n regexStr += \"$\";\n return new RegExp(regexStr, \"i\");\n}\n", "import { readFileSync, readdirSync, statSync } from \"node:fs\";\nimport { resolve, relative } from \"node:path\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { GrepData, ToolHandler } from \"./types\";\n\ntype GrepArgs = {\n pattern: string;\n path?: string;\n glob?: string;\n output_mode?: \"content\" | \"files_with_matches\" | \"count\";\n case_insensitive?: boolean;\n context_before?: number;\n context_after?: number;\n context?: number;\n head_limit?: number;\n offset?: number;\n multiline?: boolean;\n respect_gitignore?: boolean;\n};\n\nexport const grepHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as GrepArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"grep\", \"workspace_required\", \"No workspace directory set\");\n }\n\n const searchPath = args.path\n ? resolve(context.workspaceDir, args.path)\n : context.workspaceDir;\n\n const headLimit = args.head_limit ?? 200;\n const offset = args.offset ?? 0;\n const ctxBefore = args.context_before ?? args.context ?? 0;\n const ctxAfter = args.context_after ?? args.context ?? 0;\n const flags = args.case_insensitive ? \"gi\" : \"g\";\n const outputMode = args.output_mode ?? \"content\";\n const multiline = args.multiline ?? false;\n\n try {\n let searchRegex: RegExp;\n try {\n // For multiline, use s flag so . matches newlines\n const regexFlags = multiline ? `${flags}s` : flags;\n searchRegex = new RegExp(args.pattern, regexFlags);\n } catch {\n return toolFailure(\"grep\", \"invalid_pattern\", `Invalid regex pattern: ${args.pattern}`);\n }\n\n const files = collectFiles(searchPath);\n const matches: NonNullable<GrepData[\"matches\"]> = [];\n const fileMatchSet: Set<string> = new Set();\n /** Per-file match count for \"count\" mode. */\n const fileCountMap: Record<string, number> = {};\n let totalMatches = 0;\n let skippedFiles = 0;\n\n for (const file of files) {\n if (totalMatches >= headLimit + offset) break;\n\n let content: string;\n try {\n content = readFileSync(file, \"utf-8\");\n } catch {\n skippedFiles++;\n continue;\n }\n\n const relativePath = relative(context.workspaceDir, file);\n let fileHasMatch = false;\n let fileMatchCount = 0;\n\n if (multiline) {\n // Multiline mode: test the regex against the full content\n const lines = content.split(\"\\n\");\n searchRegex.lastIndex = 0;\n const fullMatch = searchRegex.exec(content);\n if (fullMatch) {\n fileHasMatch = true;\n // Count matches across the full content\n do {\n totalMatches++;\n fileMatchCount++;\n searchRegex.lastIndex = fullMatch.index + 1;\n } while (searchRegex.exec(content));\n searchRegex.lastIndex = 0;\n\n if (outputMode === \"content\") {\n matches.push({\n path: relativePath,\n lineNumber: 1,\n line: fullMatch[0].slice(0, 200),\n });\n }\n }\n } else {\n // Line-by-line mode\n const lines = content.split(\"\\n\");\n for (let lineNum = 0; lineNum < lines.length; lineNum++) {\n searchRegex.lastIndex = 0;\n if (searchRegex.test(lines[lineNum])) {\n totalMatches++;\n fileMatchCount++;\n\n if (outputMode === \"files_with_matches\") {\n fileMatchSet.add(relativePath);\n fileHasMatch = true;\n continue;\n }\n\n if (outputMode === \"count\") {\n fileHasMatch = true;\n continue;\n }\n\n // content mode\n if (totalMatches > offset && totalMatches <= offset + headLimit) {\n const contextBeforeLines = ctxBefore > 0\n ? lines.slice(Math.max(0, lineNum - ctxBefore), lineNum)\n : undefined;\n const contextAfterLines = ctxAfter > 0\n ? lines.slice(lineNum + 1, lineNum + 1 + ctxAfter)\n : undefined;\n\n matches.push({\n path: relativePath,\n lineNumber: lineNum + 1,\n line: lines[lineNum],\n contextBefore: contextBeforeLines?.length ? contextBeforeLines : undefined,\n contextAfter: contextAfterLines?.length ? contextAfterLines : undefined,\n });\n }\n }\n }\n }\n\n if (fileHasMatch || fileMatchCount > 0) {\n fileMatchSet.add(relativePath);\n fileCountMap[relativePath] = fileMatchCount;\n }\n\n if (outputMode === \"files_with_matches\" && fileMatchSet.size >= headLimit) {\n break;\n }\n }\n\n const result: GrepData = {\n pattern: args.pattern,\n path: relative(context.workspaceDir, searchPath),\n outputMode,\n totalMatches,\n truncated: totalMatches > headLimit,\n };\n\n if (outputMode === \"content\") {\n result.matches = matches;\n } else if (outputMode === \"files_with_matches\") {\n result.files = [...fileMatchSet];\n } else if (outputMode === \"count\") {\n result.files = [...fileMatchSet];\n result.fileCounts = fileCountMap;\n }\n\n if (skippedFiles > 0) {\n result.skippedFiles = skippedFiles;\n }\n\n return toolSuccess(\"grep\", result);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"grep\", \"search_error\", message);\n }\n};\n\nfunction collectFiles(dirPath: string): string[] {\n // If the path points directly to a file, return it as-is\n try {\n const stats = statSync(dirPath);\n if (stats.isFile()) {\n return [dirPath];\n }\n } catch {\n return [];\n }\n\n // Otherwise walk the directory tree\n const results: string[] = [];\n\n function walk(dir: string): void {\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n\n for (const name of entries) {\n if (name === \".git\" || name === \"node_modules\") continue;\n\n const fullPath = resolve(dir, name);\n let stats: ReturnType<typeof statSync>;\n try {\n stats = statSync(fullPath);\n } catch {\n continue;\n }\n\n if (stats.isDirectory()) {\n walk(fullPath);\n } else if (stats.isFile() && stats.size > 0) {\n results.push(fullPath);\n }\n }\n }\n\n walk(dirPath);\n return results;\n}\n", "/**\n * Shell process registry for the Coder CLI.\n * Tracks shell processes started by the agent.\n */\n\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport type { ShellData, ShellInfo, ShellStatus } from \"./types\";\nimport { resolve } from \"node:path\";\n\ntype ManagedShell = {\n shellId: string;\n command: string;\n description?: string;\n process: ChildProcess;\n status: ShellStatus;\n exitCode?: number;\n stdout: string;\n stderr: string;\n workingDirectory: string;\n startedAtMs: number;\n taskId?: string;\n};\n\nconst shells = new Map<string, ManagedShell>();\n\nlet shellCounter = 0;\n\nfunction generateShellId(): string {\n shellCounter++;\n return `shell-${Date.now()}-${shellCounter}`;\n}\n\nexport async function executeShell(\n command: string,\n options: {\n workingDirectory?: string;\n description?: string;\n blockUntilMs?: number;\n taskId?: string;\n workspaceDir?: string | null;\n },\n): Promise<ShellData> {\n const shellId = generateShellId();\n const cwd = options.workspaceDir\n ? options.workingDirectory\n ? resolve(options.workspaceDir, options.workingDirectory)\n : options.workspaceDir\n : process.cwd();\n\n const startTime = Date.now();\n const blockMs = options.blockUntilMs ?? 30000;\n\n // Determine shell\n const isWindows = process.platform === \"win32\";\n const shellCmd = isWindows ? \"cmd.exe\" : \"/bin/sh\";\n const shellArgs = isWindows ? [\"/c\", command] : [\"-c\", command];\n\n return new Promise((resolvePromise) => {\n const child = spawn(shellCmd, shellArgs, {\n cwd,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n shell: false,\n env: { ...process.env },\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let settled = false;\n\n const shell: ManagedShell = {\n shellId,\n command,\n description: options.description,\n process: child,\n status: \"running\",\n stdout,\n stderr,\n workingDirectory: cwd,\n startedAtMs: startTime,\n taskId: options.taskId,\n };\n\n child.stdout?.on(\"data\", (data: Buffer) => {\n stdout += data.toString();\n shell.stdout = stdout;\n });\n\n child.stderr?.on(\"data\", (data: Buffer) => {\n stderr += data.toString();\n shell.stderr = stderr;\n });\n\n const finish = (status: ShellStatus, code?: number) => {\n if (settled) return;\n settled = true;\n shell.status = status;\n shell.exitCode = code;\n const duration = Date.now() - startTime;\n\n const stdoutBytes = Buffer.byteLength(stdout);\n const stderrBytes = Buffer.byteLength(stderr);\n\n // Remove from registry if background shell\n // (sync shells are removed automatically)\n if (blockMs <= 0) {\n // Background \u2014 keep in registry\n shells.set(shellId, shell);\n } else {\n shells.delete(shellId);\n }\n\n resolvePromise({\n command,\n description: options.description,\n workingDirectory: cwd,\n stdout,\n stderr,\n stdoutTruncated: stdoutBytes > 65536,\n stderrTruncated: stderrBytes > 65536,\n stdoutTotalBytes: stdoutBytes,\n stderrTotalBytes: stderrBytes,\n exitCode: code,\n durationMs: duration,\n status,\n shellId: blockMs <= 0 ? shellId : undefined,\n });\n };\n\n child.on(\"error\", (err) => {\n stderr += err.message;\n finish(\"failed\", 1);\n });\n\n child.on(\"close\", (code) => {\n finish(code === 0 ? \"completed\" : \"failed\", code ?? undefined);\n });\n\n // Timeout handling\n if (blockMs > 0) {\n const timeout = setTimeout(() => {\n if (!settled) {\n child.kill();\n finish(\"timeout\", undefined);\n }\n }, blockMs);\n\n child.on(\"close\", () => clearTimeout(timeout));\n }\n\n if (blockMs <= 0) {\n // Background mode \u2014 return immediately\n shells.set(shellId, shell);\n const stdoutBytes = Buffer.byteLength(stdout);\n const stderrBytes = Buffer.byteLength(stderr);\n resolvePromise({\n command,\n description: options.description,\n workingDirectory: cwd,\n stdout: \"\",\n stderr: \"\",\n stdoutTruncated: false,\n stderrTruncated: false,\n stdoutTotalBytes: 0,\n stderrTotalBytes: 0,\n exitCode: undefined,\n durationMs: 0,\n status: \"running\",\n shellId,\n });\n }\n });\n}\n\nexport async function awaitShell(\n shellId: string,\n blockUntilMs: number = 30000,\n): Promise<ShellData> {\n const shell = shells.get(shellId);\n if (!shell) {\n return {\n command: \"\",\n workingDirectory: \"\",\n stdout: \"\",\n stderr: \"\",\n stdoutTruncated: false,\n stderrTruncated: false,\n stdoutTotalBytes: 0,\n stderrTotalBytes: 0,\n durationMs: 0,\n status: \"completed\",\n shellId,\n };\n }\n\n // Wait for the process to complete (or timeout)\n return new Promise((resolvePromise) => {\n const startTime = Date.now();\n\n const checkProcess = () => {\n if (shell.exitCode !== undefined || shell.status !== \"running\") {\n const duration = Date.now() - startTime;\n const stdoutBytes = Buffer.byteLength(shell.stdout);\n const stderrBytes = Buffer.byteLength(shell.stderr);\n resolvePromise({\n command: shell.command,\n description: shell.description,\n workingDirectory: shell.workingDirectory,\n stdout: shell.stdout,\n stderr: shell.stderr,\n stdoutTruncated: stdoutBytes > 65536,\n stderrTruncated: stderrBytes > 65536,\n stdoutTotalBytes: stdoutBytes,\n stderrTotalBytes: stderrBytes,\n exitCode: shell.exitCode,\n durationMs: duration,\n status: shell.status,\n shellId,\n });\n return;\n }\n\n if (Date.now() - startTime > blockUntilMs) {\n const duration = Date.now() - startTime;\n const stdoutBytes = Buffer.byteLength(shell.stdout);\n const stderrBytes = Buffer.byteLength(shell.stderr);\n resolvePromise({\n command: shell.command,\n description: shell.description,\n workingDirectory: shell.workingDirectory,\n stdout: shell.stdout,\n stderr: shell.stderr,\n stdoutTruncated: stdoutBytes > 65536,\n stderrTruncated: stderrBytes > 65536,\n stdoutTotalBytes: stdoutBytes,\n stderrTotalBytes: stderrBytes,\n exitCode: shell.exitCode,\n durationMs: duration,\n status: shell.status,\n shellId,\n });\n return;\n }\n\n setTimeout(checkProcess, 50);\n };\n\n // Also listen for close event\n shell.process.on(\"close\", () => {\n checkProcess();\n });\n\n checkProcess();\n });\n}\n\nexport function listShells(statusFilter?: string): ShellInfo[] {\n const result: ShellInfo[] = [];\n\n for (const shell of shells.values()) {\n if (statusFilter && shell.status !== statusFilter && statusFilter !== \"all\") {\n continue;\n }\n\n result.push({\n shellId: shell.shellId,\n command: shell.command,\n description: shell.description,\n workingDirectory: shell.workingDirectory,\n status: shell.status,\n exitCode: shell.exitCode,\n startedAtMs: shell.startedAtMs,\n taskId: shell.taskId,\n stdout: shell.stdout,\n stderr: shell.stderr,\n stdoutTruncated: Buffer.byteLength(shell.stdout) > 65536,\n stderrTruncated: Buffer.byteLength(shell.stderr) > 65536,\n });\n }\n\n return result;\n}\n\nexport function killShell(shellId: string): boolean {\n const shell = shells.get(shellId);\n if (!shell) return false;\n\n try {\n shell.process.kill(\"SIGTERM\");\n shell.status = \"cancelled\";\n setTimeout(() => {\n try {\n shell.process.kill(\"SIGKILL\");\n } catch {\n // Already dead\n }\n }, 2000);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function readShellLogs(\n shellId: string,\n stream: \"stdout\" | \"stderr\",\n offset: number = 0,\n limit: number = 4096,\n): { data: string; offset: number; totalBytes: number; truncated: boolean } {\n const shell = shells.get(shellId);\n if (!shell) {\n return { data: \"\", offset: 0, totalBytes: 0, truncated: false };\n }\n\n const content = stream === \"stdout\" ? shell.stdout : shell.stderr;\n const totalBytes = Buffer.byteLength(content);\n const sliced = content.slice(offset, offset + limit);\n\n return {\n data: sliced,\n offset: offset + sliced.length,\n totalBytes,\n truncated: offset + limit < totalBytes,\n };\n}\n", "import { toolFailure, toolSuccess } from \"./result\";\nimport type { ShellData, ToolHandler } from \"./types\";\nimport { executeShell } from \"./shell-manager\";\n\ntype ShellArgs = {\n command: string;\n description?: string;\n working_directory?: string;\n block_until_ms?: number;\n};\n\nexport const shellHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as ShellArgs;\n\n if (!args.command?.trim()) {\n return toolFailure(\"shell\", \"invalid_arguments\", \"Command is required\");\n }\n\n try {\n const result = await executeShell(args.command.trim(), {\n workingDirectory: args.working_directory,\n description: args.description,\n blockUntilMs: args.block_until_ms ?? 30000,\n taskId: context.taskId,\n workspaceDir: context.workspaceDir,\n });\n\n return toolSuccess(\"shell\", result);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"shell\", \"execution_error\", message);\n }\n};\n", "import { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\nimport { awaitShell } from \"./shell-manager\";\n\ntype AwaitArgs = {\n shell_id: string;\n block_until_ms?: number;\n};\n\nexport const awaitShellHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as AwaitArgs;\n\n if (!args.shell_id?.trim()) {\n return toolFailure(\"await\", \"invalid_arguments\", \"shell_id is required\");\n }\n\n try {\n const result = await awaitShell(args.shell_id.trim(), args.block_until_ms ?? 30000);\n return toolSuccess(\"await\", result);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"await\", \"error\", message);\n }\n};\n", "import { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\nimport { listShells } from \"./shell-manager\";\n\ntype ListShellsArgs = {\n status_filter?: string;\n};\n\nexport const listShellsHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as ListShellsArgs;\n\n const shells = listShells(args.status_filter);\n\n return toolSuccess(\"list_shells\", {\n shells,\n total: shells.length,\n });\n};\n", "import { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\nimport { killShell, readShellLogs } from \"./shell-manager\";\n\ntype KillShellArgs = {\n shell_id: string;\n};\n\nexport const killShellHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as KillShellArgs;\n\n if (!args.shell_id?.trim()) {\n return toolFailure(\"kill_shell\", \"invalid_arguments\", \"shell_id is required\");\n }\n\n const killed = killShell(args.shell_id.trim());\n return toolSuccess(\"kill_shell\", {\n shellId: args.shell_id.trim(),\n killed,\n });\n};\n", "import { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\nimport { readShellLogs } from \"./shell-manager\";\n\ntype ReadShellLogsArgs = {\n shell_id: string;\n stream?: \"stdout\" | \"stderr\";\n offset?: number;\n limit?: number;\n};\n\nexport const readShellLogsHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as ReadShellLogsArgs;\n\n if (!args.shell_id?.trim()) {\n return toolFailure(\"read_shell_logs\", \"invalid_arguments\", \"shell_id is required\");\n }\n\n const result = readShellLogs(\n args.shell_id.trim(),\n args.stream ?? \"stdout\",\n args.offset ?? 0,\n args.limit ?? 4096,\n );\n\n return toolSuccess(\"read_shell_logs\", {\n shellId: args.shell_id.trim(),\n stream: args.stream ?? \"stdout\",\n data: result.data,\n offset: result.offset,\n totalBytes: result.totalBytes,\n truncated: result.truncated,\n });\n};\n", "import { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype WebSearchArgs = {\n search_term: string;\n max_results?: number;\n explanation?: string;\n};\n\nexport const webSearchHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as WebSearchArgs;\n\n if (!args.search_term?.trim()) {\n return toolFailure(\"web_search\", \"invalid_arguments\", \"search_term is required\");\n }\n\n try {\n // Use a free search API (DuckDuckGo-style) or fallback\n const results = await performWebSearch(args.search_term, args.max_results ?? 5);\n\n return toolSuccess(\"web_search\", {\n query: args.search_term,\n results,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"web_search\", \"search_error\", message);\n }\n};\n\nasync function performWebSearch(\n query: string,\n maxResults: number,\n): Promise<Array<{ title: string; url: string; snippet: string }>> {\n // Try to use Tavily or fallback to DuckDuckGo scrape\n // For now, use a simple approach via DuckDuckGo's instant answer API\n try {\n const url = `https://api.duckduckgo.com/?q=${encodeURIComponent(query)}&format=json&no_html=1&skip_disambig=1`;\n const response = await fetch(url, {\n signal: AbortSignal.timeout(10000),\n });\n\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n\n const data = (await response.json()) as {\n AbstractText?: string;\n AbstractSource?: string;\n AbstractURL?: string;\n RelatedTopics?: Array<{ Text?: string; FirstURL?: string; Result?: string }>;\n };\n\n const results: Array<{ title: string; url: string; snippet: string }> = [];\n\n if (data.AbstractText) {\n results.push({\n title: data.AbstractSource ?? \"Result\",\n url: data.AbstractURL ?? \"\",\n snippet: data.AbstractText,\n });\n }\n\n if (data.RelatedTopics) {\n for (const topic of data.RelatedTopics.slice(0, maxResults)) {\n if (topic.Text && topic.FirstURL) {\n results.push({\n title: topic.Text.split(\" - \")[0] ?? \"Result\",\n url: topic.FirstURL,\n snippet: topic.Text,\n });\n }\n }\n }\n\n return results.slice(0, maxResults);\n } catch {\n // Fallback: return a message that web search is unavailable\n throw new Error(\n \"Web search is currently unavailable. Configure a Tavily API key for web search support, \" +\n \"or set the TAVILY_API_KEY environment variable.\",\n );\n }\n}\n", "import { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype BrowsePageArgs = {\n url: string;\n max_lines?: number;\n start_line?: number;\n explanation?: string;\n};\n\nexport const browsePageHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as BrowsePageArgs;\n\n if (!args.url?.trim()) {\n return toolFailure(\"browse_page\", \"invalid_arguments\", \"url is required\");\n }\n\n try {\n const response = await fetch(args.url.trim(), {\n headers: {\n \"User-Agent\": \"Mozilla/5.0 (compatible; CoderCLI/1.0)\",\n Accept: \"text/html,text/plain,*/*\",\n },\n signal: AbortSignal.timeout(15000),\n });\n\n const contentType = response.headers.get(\"content-type\") ?? \"text/plain\";\n const finalUrl = response.url;\n const title = extractTitleFromUrl(finalUrl);\n\n let content = await response.text();\n\n // Truncate if needed\n const startLine = args.start_line ?? 1;\n const maxLines = args.max_lines ?? 500;\n const lines = content.split(\"\\n\");\n const totalLines = lines.length;\n const endLine = Math.min(startLine + maxLines - 1, totalLines);\n const paginatedContent = lines.slice(startLine - 1, endLine).join(\"\\n\");\n\n return toolSuccess(\"browse_page\", {\n url: args.url,\n finalUrl,\n title,\n content: paginatedContent,\n truncated: endLine < totalLines,\n statusCode: response.status,\n contentType,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"fetch\")) {\n return toolFailure(\"browse_page\", \"network_error\", `Failed to fetch URL: ${message}`);\n }\n return toolFailure(\"browse_page\", \"error\", message);\n }\n};\n\nfunction extractTitleFromUrl(url: string): string | undefined {\n try {\n const parsed = new URL(url);\n return parsed.hostname;\n } catch {\n return undefined;\n }\n}\n", "import { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler, TodoSnapshotItem } from \"./types\";\nimport { getConfigDirPath } from \"../config\";\n\n// Todos storage \u2014 per-session JSON files\nfunction getTodosFilePath(sessionId?: string): string {\n const dir = join(getConfigDirPath(), \"todos\");\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n const safeSessionId = (sessionId ?? \"default\").replace(/[^a-z0-9-]/gi, \"\");\n return join(dir, `${safeSessionId}.json`);\n}\n\ntype TodoRecord = {\n id: string;\n content: string;\n status: \"pending\" | \"in_progress\" | \"completed\" | \"cancelled\";\n order: number;\n createdAt: number;\n updatedAt: number;\n};\n\nfunction readTodos(sessionId?: string): TodoRecord[] {\n const filePath = getTodosFilePath(sessionId);\n if (!existsSync(filePath)) return [];\n try {\n return JSON.parse(readFileSync(filePath, \"utf-8\"));\n } catch {\n return [];\n }\n}\n\nfunction writeTodos(todos: TodoRecord[], sessionId?: string): void {\n writeFileSync(getTodosFilePath(sessionId), JSON.stringify(todos, null, 2), \"utf-8\");\n}\n\n// Handlers\ntype TodoReadArgs = { session_id?: string };\n\nexport const todoReadHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as TodoReadArgs;\n const sessionId = args.session_id ?? context.sessionId;\n const todos = readTodos(sessionId);\n\n const snapshot: TodoSnapshotItem[] = todos\n .sort((a, b) => a.order - b.order)\n .map((t) => ({\n id: t.id,\n content: t.content,\n status: t.status,\n }));\n\n return toolSuccess(\"todo_read\", {\n sessionId: sessionId ?? null,\n todos: snapshot,\n total: snapshot.length,\n active: snapshot.filter((t) => t.status === \"pending\" || t.status === \"in_progress\").length,\n completed: snapshot.filter((t) => t.status === \"completed\").length,\n });\n};\n\ntype TodoWriteArgs = {\n todos: Array<{\n id: string;\n content: string;\n status: \"pending\" | \"in_progress\" | \"completed\" | \"cancelled\";\n }>;\n session_id?: string;\n};\n\nexport const todoWriteHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as TodoWriteArgs;\n const sessionId = args.session_id ?? context.sessionId;\n\n if (!args.todos || !Array.isArray(args.todos)) {\n return toolFailure(\"todo_write\", \"invalid_arguments\", \"todos array is required\");\n }\n\n const existing = readTodos(sessionId);\n const existingMap = new Map(existing.map((t) => [t.id, t]));\n\n // Merge: update existing, add new\n for (let i = 0; i < args.todos.length; i++) {\n const input = args.todos[i];\n const existingRecord = existingMap.get(input.id);\n if (existingRecord) {\n existingRecord.content = input.content;\n existingRecord.status = input.status;\n existingRecord.updatedAt = Date.now();\n } else {\n existing.push({\n id: input.id,\n content: input.content,\n status: input.status,\n order: i,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n });\n }\n }\n\n writeTodos(existing, sessionId);\n\n return toolSuccess(\"todo_write\", {\n total: existing.length,\n updated: args.todos.length,\n });\n};\n", "import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\nimport { getConfigDirPath } from \"../config\";\n\n// Plan storage \u2014 directory of .plan files\nfunction getPlansDir(): string {\n const dir = join(getConfigDirPath(), \"plans\");\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n return dir;\n}\n\ntype PlanRecord = {\n name: string;\n title: string;\n content: string;\n createdAt: number;\n updatedAt: number;\n};\n\nfunction getPlanFilePath(name: string): string {\n const safeName = name.replace(/[^a-z0-9-_.]/gi, \"\").toLowerCase();\n return join(getPlansDir(), `${safeName}.json`);\n}\n\nfunction readPlanFile(name: string): PlanRecord | null {\n const filePath = getPlanFilePath(name);\n if (!existsSync(filePath)) return null;\n try {\n return JSON.parse(readFileSync(filePath, \"utf-8\"));\n } catch {\n return null;\n }\n}\n\nfunction writePlanFile(name: string, plan: PlanRecord): void {\n writeFileSync(getPlanFilePath(name), JSON.stringify(plan, null, 2), \"utf-8\");\n}\n\nfunction deletePlanFile(name: string): boolean {\n const filePath = getPlanFilePath(name);\n if (!existsSync(filePath)) return false;\n try {\n writeFileSync(filePath, JSON.stringify({ deleted: true }), \"utf-8\");\n return true;\n } catch {\n return false;\n }\n}\n\nfunction listPlanFiles(): string[] {\n return readdirSync(getPlansDir())\n .filter((f) => f.endsWith(\".json\"))\n .map((f) => f.replace(/\\.json$/, \"\"));\n}\n\n// Handlers\ntype PlanCreateArgs = { name: string; title?: string; content: string };\n\nexport const planCreateHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as PlanCreateArgs;\n if (!args.name?.trim() || !args.content?.trim()) {\n return toolFailure(\"plan_create\", \"invalid_arguments\", \"name and content are required\");\n }\n\n const name = args.name.trim().toLowerCase().replace(/[^a-z0-9-_.]/gi, \"\");\n if (readPlanFile(name)) {\n return toolFailure(\"plan_create\", \"exists\", `Plan already exists: ${name}`);\n }\n\n writePlanFile(name, {\n name,\n title: args.title?.trim() ?? name,\n content: args.content.trim(),\n createdAt: Date.now(),\n updatedAt: Date.now(),\n });\n\n return toolSuccess(\"plan_create\", { name, title: args.title?.trim() ?? name });\n};\n\ntype PlanReadArgs = { name: string };\n\nexport const planReadHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as PlanReadArgs;\n if (!args.name?.trim()) {\n return toolFailure(\"plan_read\", \"invalid_arguments\", \"name is required\");\n }\n\n const plan = readPlanFile(args.name.trim());\n if (!plan) {\n return toolFailure(\"plan_read\", \"not_found\", `Plan not found: ${args.name}`);\n }\n\n return toolSuccess(\"plan_read\", plan);\n};\n\ntype PlanUpdateArgs = { name: string; title?: string; content?: string };\n\nexport const planUpdateHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as PlanUpdateArgs;\n if (!args.name?.trim()) {\n return toolFailure(\"plan_update\", \"invalid_arguments\", \"name is required\");\n }\n\n const existing = readPlanFile(args.name.trim());\n if (!existing) {\n return toolFailure(\"plan_update\", \"not_found\", `Plan not found: ${args.name}`);\n }\n\n writePlanFile(args.name.trim(), {\n ...existing,\n title: args.title?.trim() ?? existing.title,\n content: args.content?.trim() ?? existing.content,\n updatedAt: Date.now(),\n });\n\n return toolSuccess(\"plan_update\", { name: args.name.trim() });\n};\n\ntype PlanEditArgs = { name: string; instructions: string };\n\nexport const planEditHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as PlanEditArgs;\n if (!args.name?.trim()) {\n return toolFailure(\"plan_edit\", \"invalid_arguments\", \"name is required\");\n }\n\n const existing = readPlanFile(args.name.trim());\n if (!existing) {\n return toolFailure(\"plan_edit\", \"not_found\", `Plan not found: ${args.name}`);\n }\n\n // Append instructions to plan content\n writePlanFile(args.name.trim(), {\n ...existing,\n content: existing.content + \"\\n\\n## Edit Instructions\\n\\n\" + (args.instructions?.trim() ?? \"\"),\n updatedAt: Date.now(),\n });\n\n return toolSuccess(\"plan_edit\", { name: args.name.trim() });\n};\n\ntype PlanDeleteArgs = { name: string };\n\nexport const planDeleteHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as PlanDeleteArgs;\n if (!args.name?.trim()) {\n return toolFailure(\"plan_delete\", \"invalid_arguments\", \"name is required\");\n }\n\n deletePlanFile(args.name.trim());\n return toolSuccess(\"plan_delete\", { name: args.name.trim(), deleted: true });\n};\n\ntype PlanListArgs = Record<string, never>;\n\nexport const planListHandler: ToolHandler = async (_rawArgs, _context) => {\n const names = listPlanFiles();\n const plans = names\n .map((name) => readPlanFile(name))\n .filter((p): p is PlanRecord => p !== null)\n .map((p) => ({ name: p.name, title: p.title }));\n\n return toolSuccess(\"plan_list\", { plans, total: plans.length });\n};\n", "import { readdirSync, statSync } from \"node:fs\";\nimport { resolve, relative } from \"node:path\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\ntype GetWorkspaceTreeArgs = {\n max_lines?: number;\n start_line?: number;\n};\n\nconst EXCLUDED_DIRS = new Set([\n \"node_modules\", \".git\", \"dist\", \"build\", \"target\", \".next\",\n \"out\", \".cache\", \".turbo\", \"coverage\", \".nyc_output\",\n \"__pycache__\", \".venv\", \"venv\", \".history\",\n]);\n\nexport const getWorkspaceTreeHandler: ToolHandler = async (rawArgs, context) => {\n const args = rawArgs as GetWorkspaceTreeArgs;\n\n if (!context.workspaceDir) {\n return toolFailure(\"get_workspace_tree\", \"workspace_required\", \"No workspace directory set\");\n }\n\n try {\n const rootDir = context.workspaceDir;\n const treeLines = buildTree(rootDir, rootDir, 0);\n const fullText = treeLines.join(\"\\n\");\n const totalLines = treeLines.length;\n const startLine = args.start_line ?? 1;\n const maxLines = args.max_lines ?? 500;\n const endLine = Math.min(startLine + maxLines - 1, totalLines);\n const paginatedText = treeLines.slice(startLine - 1, endLine).join(\"\\n\");\n\n return toolSuccess(\"get_workspace_tree\", {\n treeText: paginatedText,\n totalLines,\n startLine,\n endLine,\n truncated: endLine < totalLines,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"get_workspace_tree\", \"error\", message);\n }\n};\n\nfunction buildTree(rootDir: string, currentDir: string, depth: number): string[] {\n const lines: string[] = [];\n const relPath = relative(rootDir, currentDir);\n\n if (depth === 0) {\n lines.push(relative(rootDir, currentDir) || \".\");\n }\n\n let entries: string[];\n try {\n entries = readdirSync(currentDir);\n } catch {\n return lines;\n }\n\n // Sort: directories first, then files, alphabetical\n const dirs: string[] = [];\n const files: string[] = [];\n\n for (const name of entries) {\n if (name.startsWith(\".\") && depth === 0 && name === \".git\") continue;\n if (EXCLUDED_DIRS.has(name)) continue;\n if (name.startsWith(\".\") && depth > 0) continue;\n\n const fullPath = resolve(currentDir, name);\n try {\n const stats = statSync(fullPath);\n if (stats.isDirectory()) {\n dirs.push(name);\n } else {\n files.push(name);\n }\n } catch {\n // skip\n }\n }\n\n dirs.sort();\n files.sort();\n\n const allEntries = [...dirs, ...files];\n\n for (let i = 0; i < allEntries.length; i++) {\n const name = allEntries[i];\n const isLast = i === allEntries.length - 1;\n const prefix = depth === 0 ? (isLast ? \"\u2514\u2500\u2500 \" : \"\u251C\u2500\u2500 \") : \"\";\n const childPrefix = depth === 0 ? (isLast ? \" \" : \"\u2502 \") : \"\";\n const fullPath = resolve(currentDir, name);\n\n lines.push(`${prefix}${name}`);\n\n try {\n const stats = statSync(fullPath);\n if (stats.isDirectory()) {\n const childLines = buildTreeFromDepth(rootDir, fullPath, depth + 1, prefix || \"\", childPrefix);\n lines.push(...childLines);\n }\n } catch {\n // skip\n }\n }\n\n return lines;\n}\n\nfunction buildTreeFromDepth(\n rootDir: string,\n currentDir: string,\n depth: number,\n parentPrefix: string,\n prefix: string,\n): string[] {\n const lines: string[] = [];\n\n let entries: string[];\n try {\n entries = readdirSync(currentDir);\n } catch {\n return lines;\n }\n\n const dirs: string[] = [];\n const files: string[] = [];\n\n for (const name of entries) {\n if (EXCLUDED_DIRS.has(name)) continue;\n if (name.startsWith(\".\")) continue;\n\n const fullPath = resolve(currentDir, name);\n try {\n const stats = statSync(fullPath);\n if (stats.isDirectory()) {\n dirs.push(name);\n } else {\n files.push(name);\n }\n } catch {\n // skip\n }\n }\n\n dirs.sort();\n files.sort();\n\n const allEntries = [...dirs, ...files];\n\n for (let i = 0; i < allEntries.length; i++) {\n const name = allEntries[i];\n const isLast = i === allEntries.length - 1;\n const linePrefix = `${prefix}${isLast ? \"\u2514\u2500\u2500 \" : \"\u251C\u2500\u2500 \"}`;\n const childPrefix = `${prefix}${isLast ? \" \" : \"\u2502 \"}`;\n\n lines.push(`${linePrefix}${name}`);\n\n const fullPath = resolve(currentDir, name);\n try {\n const stats = statSync(fullPath);\n if (stats.isDirectory()) {\n const childLines = buildTreeFromDepth(rootDir, fullPath, depth + 1, \"\", childPrefix);\n lines.push(...childLines);\n }\n } catch {\n // skip\n }\n }\n\n return lines;\n}\n", "import { createInterface } from \"node:readline\";\nimport { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\n\nexport const askQuestionHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as { question: string };\n\n if (!args.question?.trim()) {\n return toolFailure(\"ask_question\", \"invalid_arguments\", \"question is required\");\n }\n\n // In non-interactive mode, return a placeholder\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n\n const answer = await new Promise<string>((resolve) => {\n process.stderr.write(`\\n\\x1b[33m\u2753 ${args.question}\\x1b[0m\\n> `);\n rl.once(\"line\", (line) => {\n resolve(line.trim());\n });\n });\n\n rl.close();\n\n if (!answer) {\n return toolFailure(\"ask_question\", \"no_answer\", \"User did not provide an answer\");\n }\n\n return toolSuccess(\"ask_question\", {\n question: args.question,\n answer,\n });\n};\n", "import { toolFailure, toolSuccess } from \"./result\";\nimport type { ToolHandler } from \"./types\";\nimport type { AgentMode } from \"../agent/types\";\n\n// Forward reference \u2014 will be set when the agent system initializes\nlet spawnAgentFunction: ((task: string, context?: string, tools?: string[]) => Promise<unknown>) | null = null;\n\nexport function setSpawnAgentFunction(\n fn: (task: string, context?: string, tools?: string[]) => Promise<unknown>,\n): void {\n spawnAgentFunction = fn;\n}\n\ntype SpawnSubAgentArgs = {\n task: string;\n context?: string;\n tools?: string[];\n};\n\nexport const spawnSubAgentHandler: ToolHandler = async (rawArgs, _context) => {\n const args = rawArgs as SpawnSubAgentArgs;\n\n if (!args.task?.trim()) {\n return toolFailure(\"spawn_subagent\", \"invalid_arguments\", \"task is required\");\n }\n\n if (!spawnAgentFunction) {\n return toolFailure(\n \"spawn_subagent\",\n \"not_available\",\n \"Sub-agent spawning is not available in the current configuration.\",\n );\n }\n\n try {\n const result = await spawnAgentFunction(args.task, args.context, args.tools);\n return toolSuccess(\"spawn_subagent\", result);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return toolFailure(\"spawn_subagent\", \"execution_error\", message);\n }\n};\n", "/**\n * Tool handler registry \u2014 maps tool names to their CLI-native handlers.\n */\n\nimport type { ToolHandler, ToolResultEnvelope, ToolExecutionContext } from \"./types\";\nimport { listDirHandler } from \"./list-dir\";\nimport { readFileHandler } from \"./read-file\";\nimport { writeFileHandler } from \"./write-file\";\nimport { replaceFileHandler } from \"./replace-file\";\nimport { editFileHandler } from \"./edit-file\";\nimport { replaceLinesHandler } from \"./replace-lines\";\nimport { globHandler } from \"./glob\";\nimport { grepHandler } from \"./grep\";\nimport { shellHandler } from \"./shell\";\nimport { awaitShellHandler } from \"./await-shell\";\nimport { listShellsHandler } from \"./list-shells\";\nimport { killShellHandler } from \"./kill-shell\";\nimport { readShellLogsHandler } from \"./read-shell-logs\";\nimport { webSearchHandler } from \"./web-search\";\nimport { browsePageHandler } from \"./browse-page\";\nimport { todoReadHandler, todoWriteHandler } from \"./todos\";\nimport { planCreateHandler, planReadHandler, planUpdateHandler, planEditHandler, planDeleteHandler, planListHandler } from \"./plans\";\nimport { getWorkspaceTreeHandler } from \"./workspace-tree\";\nimport { askQuestionHandler } from \"./ask-question\";\n\nimport { spawnSubAgentHandler } from \"./spawn-subagent\";\n\n// ---------------------------------------------------------------------------\n// Tool definitions (matching existing AgentToolDefinition format)\n// ---------------------------------------------------------------------------\n\nexport type ToolDefinition = {\n type: \"function\";\n function: {\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n };\n};\n\nconst AGENT_TOOL_DEFINITIONS: ToolDefinition[] = [\n {\n type: \"function\",\n function: {\n name: \"list_dir\",\n description: \"List files and directories under a path. Relative paths are resolved against the workspace root.\",\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Relative or absolute path within the workspace.\" },\n recursive: { type: \"boolean\", description: \"Whether to list entries recursively.\", default: false },\n max_depth: { type: \"integer\", description: \"Maximum recursion depth when recursive is true.\", default: 1 },\n show_hidden: { type: \"boolean\", description: \"Whether to include dotfiles and dot-directories.\", default: false },\n },\n required: [\"path\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"read_file\",\n description: \"Read a text file with line numbers and pagination. Relative paths are resolved against the workspace root.\",\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Relative or absolute path to the file within the workspace.\" },\n start_line: { type: \"integer\", description: \"First line to read (1-based).\", default: 1 },\n max_lines: { type: \"integer\", description: \"Maximum number of lines to return.\", default: 500 },\n respect_gitignore: { type: \"boolean\", description: \"Whether to refuse reading paths ignored by .gitignore.\", default: true },\n },\n required: [\"path\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"write_file\",\n description: \"Create a new text file. Fails if the file already exists. Use replace_file or edit_file to modify existing files.\",\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Relative or absolute path to the new file within the workspace.\" },\n content: { type: \"string\", description: \"Full file content to write.\" },\n create_parent_dirs: { type: \"boolean\", description: \"Whether to create missing parent directories.\", default: true },\n },\n required: [\"path\", \"content\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"replace_file\",\n description: \"Replace the entire contents of an existing text file. Use expected_sha256 from read_file to avoid overwriting concurrent changes.\",\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Relative or absolute path to the file within the workspace.\" },\n content: { type: \"string\", description: \"Full replacement file content.\" },\n expected_sha256: { type: \"string\", description: \"SHA256 hash from read_file. Rejects the write if the file changed.\" },\n create_backup: { type: \"boolean\", description: \"Whether to save a backup copy under .history before writing.\", default: false },\n respect_gitignore: { type: \"boolean\", description: \"Whether to refuse editing paths ignored by .gitignore.\", default: true },\n },\n required: [\"path\", \"content\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"edit_file\",\n description: \"Apply a targeted search-and-replace edit to an existing text file. Prefer this over replace_file for small changes.\",\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Relative or absolute path to the file within the workspace.\" },\n old_string: { type: \"string\", description: \"Exact text to replace. Must match uniquely unless replace_all is true.\" },\n new_string: { type: \"string\", description: \"Replacement text.\" },\n expected_sha256: { type: \"string\", description: \"SHA256 hash from read_file. Rejects the edit if the file changed.\" },\n replace_all: { type: \"boolean\", description: \"Whether to replace every occurrence of old_string.\", default: false },\n create_backup: { type: \"boolean\", description: \"Whether to save a backup copy under .history before writing.\", default: false },\n respect_gitignore: { type: \"boolean\", description: \"Whether to refuse editing paths ignored by .gitignore.\", default: true },\n },\n required: [\"path\", \"old_string\", \"new_string\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"replace_lines\",\n description: \"Replace a range of lines in an existing text file by line number. Read the file with read_file first to see line numbers.\",\n parameters: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Relative or absolute path to the file within the workspace.\" },\n start_line: { type: \"integer\", description: \"First line to replace (1-based).\" },\n end_line: { type: \"integer\", description: \"Last line to replace (inclusive, 1-based).\" },\n content: { type: \"string\", description: \"Replacement content for the specified line range.\" },\n expected_sha256: { type: \"string\", description: \"SHA256 hash from read_file. Rejects the edit if the file changed.\" },\n create_backup: { type: \"boolean\", description: \"Whether to save a backup copy under .history before writing.\", default: false },\n respect_gitignore: { type: \"boolean\", description: \"Whether to refuse editing paths ignored by .gitignore.\", default: true },\n },\n required: [\"path\", \"start_line\", \"end_line\", \"content\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"glob\",\n description: \"Find files by glob pattern under a directory. Relative paths are resolved against the workspace root.\",\n parameters: {\n type: \"object\",\n properties: {\n glob_pattern: { type: \"string\", description: \"Glob pattern such as **/*.tsx or src/**/*.rs.\" },\n target_directory: { type: \"string\", description: \"Directory to search from. Defaults to the workspace root.\" },\n head_limit: { type: \"integer\", description: \"Maximum number of matching paths to return.\", default: 100 },\n respect_gitignore: { type: \"boolean\", description: \"Whether to skip paths ignored by .gitignore.\", default: true },\n },\n required: [\"glob_pattern\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"grep\",\n description: \"Search file contents with a regex pattern. Relative paths are resolved against the workspace root.\",\n parameters: {\n type: \"object\",\n properties: {\n pattern: { type: \"string\", description: \"Regular expression pattern to search for.\" },\n path: { type: \"string\", description: \"File or directory to search. Defaults to the workspace root.\" },\n glob: { type: \"string\", description: \"Optional glob filter to limit searched files.\" },\n output_mode: { type: \"string\", enum: [\"content\", \"files_with_matches\", \"count\"], default: \"content\" },\n case_insensitive: { type: \"boolean\", description: \"Whether to ignore letter case while matching.\", default: false },\n context_before: { type: \"integer\", description: \"Number of lines to include before each match.\" },\n context_after: { type: \"integer\", description: \"Number of lines to include after each match.\" },\n context: { type: \"integer\", description: \"Number of lines to include before and after each match.\" },\n head_limit: { type: \"integer\", description: \"Maximum number of results to return.\", default: 200 },\n offset: { type: \"integer\", description: \"Number of results to skip in content mode.\", default: 0 },\n multiline: { type: \"boolean\", description: \"Whether . should match newlines.\", default: false },\n respect_gitignore: { type: \"boolean\", description: \"Whether to skip paths ignored by .gitignore.\", default: true },\n },\n required: [\"pattern\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"shell\",\n description: \"Execute a shell command in the workspace. Use for builds, tests, git, and other CLI tasks.\",\n parameters: {\n type: \"object\",\n properties: {\n command: { type: \"string\", description: \"The shell command to execute.\" },\n description: { type: \"string\", description: \"Short human-readable description for display only.\" },\n working_directory: { type: \"string\", description: \"Directory to run the command in, relative to workspace root.\" },\n block_until_ms: { type: \"integer\", description: \"Max wait time in ms. Default 30000. Use 0 for background mode.\", default: 30000 },\n },\n required: [\"command\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"await\",\n description: \"Poll a background shell started with shell(block_until_ms=0) until it completes or times out.\",\n parameters: {\n type: \"object\",\n properties: {\n shell_id: { type: \"string\", description: \"The shell_id returned from a background shell invocation.\" },\n block_until_ms: { type: \"integer\", description: \"Max wait time in ms before returning current output.\", default: 30000 },\n },\n required: [\"shell_id\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"list_shells\",\n description: \"List background shell processes started by the agent.\",\n parameters: {\n type: \"object\",\n properties: {\n status_filter: { type: \"string\", description: 'Filter by status. Default \"running\". Use \"all\" to include completed and failed shells.' },\n },\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"kill_shell\",\n description: \"Kill a running background shell process by shell_id.\",\n parameters: {\n type: \"object\",\n properties: {\n shell_id: { type: \"string\", description: \"The shell_id to terminate.\" },\n },\n required: [\"shell_id\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"read_shell_logs\",\n description: \"Read logs from a shell process in batches.\",\n parameters: {\n type: \"object\",\n properties: {\n shell_id: { type: \"string\", description: \"The shell_id to read logs from.\" },\n stream: { type: \"string\", enum: [\"stdout\", \"stderr\"], default: \"stdout\" },\n offset: { type: \"integer\", description: \"Byte offset to start reading from.\", default: 0 },\n limit: { type: \"integer\", description: \"Maximum bytes to return.\", default: 4096 },\n },\n required: [\"shell_id\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"web_search\",\n description: \"Search the web for real-time information outside training data.\",\n parameters: {\n type: \"object\",\n properties: {\n search_term: { type: \"string\", description: \"The search term to look up on the web.\" },\n max_results: { type: \"integer\", description: \"Maximum number of search results to return.\", default: 5 },\n explanation: { type: \"string\", description: \"One sentence explanation of why this search is being used.\" },\n },\n required: [\"search_term\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"browse_page\",\n description: \"Fetch a public web page and return readable Markdown content.\",\n parameters: {\n type: \"object\",\n properties: {\n url: { type: \"string\", description: \"The URL to fetch. Must be http or https.\" },\n max_lines: { type: \"integer\", description: \"Maximum number of lines to return.\", default: 500 },\n start_line: { type: \"integer\", description: \"First line to read (1-based).\", default: 1 },\n explanation: { type: \"string\", description: \"One sentence explanation of why this page is being fetched.\" },\n },\n required: [\"url\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"todo_read\",\n description: \"Read the current structured todo list for the session.\",\n parameters: {\n type: \"object\",\n properties: {\n session_id: { type: \"string\", description: \"Optional session ID.\" },\n },\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"todo_write\",\n description: \"Create and update a structured todo list for the session.\",\n parameters: {\n type: \"object\",\n properties: {\n todos: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n id: { type: \"string\" },\n content: { type: \"string\" },\n status: { type: \"string\", enum: [\"pending\", \"in_progress\", \"completed\", \"cancelled\"] },\n },\n required: [\"id\", \"content\", \"status\"],\n },\n },\n session_id: { type: \"string\", description: \"Optional session ID.\" },\n },\n required: [\"todos\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"get_workspace_tree\",\n description: \"Display the workspace directory tree structure with depth recursion.\",\n parameters: {\n type: \"object\",\n properties: {\n max_lines: { type: \"integer\", description: \"Maximum number of lines to return.\", default: 500 },\n start_line: { type: \"integer\", description: \"First line to return (1-based).\", default: 1 },\n },\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"spawn_subagent\",\n description: \"Spawn a sub-agent to complete an independent sub-task.\",\n parameters: {\n type: \"object\",\n properties: {\n task: { type: \"string\", description: \"The task description for the sub-agent.\" },\n context: { type: \"string\", description: \"Optional additional context or constraints.\" },\n tools: { type: \"array\", items: { type: \"string\" }, description: \"Optional whitelist of tool names.\" },\n },\n required: [\"task\"],\n additionalProperties: false,\n },\n },\n },\n {\n type: \"function\",\n function: {\n name: \"ask_question\",\n description: \"Ask the user a question when you need additional information to proceed.\",\n parameters: {\n type: \"object\",\n properties: {\n question: { type: \"string\", description: \"The question to ask the user.\" },\n },\n required: [\"question\"],\n additionalProperties: false,\n },\n },\n },\n];\n\n// ---------------------------------------------------------------------------\n// Handler map\n// ---------------------------------------------------------------------------\n\nconst HANDLER_MAP: Record<string, ToolHandler> = {\n list_dir: listDirHandler,\n read_file: readFileHandler,\n write_file: writeFileHandler,\n replace_file: replaceFileHandler,\n edit_file: editFileHandler,\n replace_lines: replaceLinesHandler,\n glob: globHandler,\n grep: grepHandler,\n shell: shellHandler,\n await: awaitShellHandler,\n list_shells: listShellsHandler,\n kill_shell: killShellHandler,\n read_shell_logs: readShellLogsHandler,\n web_search: webSearchHandler,\n browse_page: browsePageHandler,\n todo_read: todoReadHandler,\n todo_write: todoWriteHandler,\n get_workspace_tree: getWorkspaceTreeHandler,\n spawn_subagent: spawnSubAgentHandler,\n ask_question: askQuestionHandler,\n plan_create: planCreateHandler,\n plan_read: planReadHandler,\n plan_update: planUpdateHandler,\n plan_edit: planEditHandler,\n plan_delete: planDeleteHandler,\n plan_list: planListHandler,\n};\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Mode-based tool filtering\n// ---------------------------------------------------------------------------\n\nconst ASK_QUESTION_TOOL_NAME = \"ask_question\";\n\n/** Tools excluded from default \"agent\" mode. */\nconst AGENT_MODE_EXCLUDED_TOOL_NAMES = new Set([\n ASK_QUESTION_TOOL_NAME,\n]);\n\n/** Tools allowed in \\\"ask\\\" mode \u2014 only read-only / information-gathering. */\nconst ASK_MODE_TOOL_NAMES = new Set([\n \"list_dir\",\n \"read_file\",\n \"glob\",\n \"grep\",\n \"web_search\",\n \"browse_page\",\n \"todo_read\",\n \"list_shells\",\n \"read_shell_logs\",\n \"get_workspace_tree\",\n]);\n\n/**\n * Returns tool definitions filtered by agent mode.\n * - \"agent\": all tools except ask_question.\n * - \"plan\": all tools including ask_question.\n * - \"ask\": only read-only tools.\n * - undefined: all tools (backward-compatible).\n */\nexport function getToolDefinitions(agentMode?: string): ToolDefinition[] {\n if (agentMode === \"ask\") {\n return AGENT_TOOL_DEFINITIONS.filter((t) =>\n ASK_MODE_TOOL_NAMES.has(t.function.name),\n );\n }\n\n if (agentMode === \"plan\") {\n return AGENT_TOOL_DEFINITIONS;\n }\n\n // Default agent mode: exclude ask_question\n return AGENT_TOOL_DEFINITIONS.filter(\n (t) => !AGENT_MODE_EXCLUDED_TOOL_NAMES.has(t.function.name),\n );\n}\n\nexport function getToolHandler(name: string): ToolHandler | undefined {\n return HANDLER_MAP[name];\n}\n\nexport async function executeToolCall(\n name: string,\n rawArguments: string,\n context: ToolExecutionContext,\n): Promise<ToolResultEnvelope> {\n const handler = getToolHandler(name);\n if (!handler) {\n return {\n ok: false,\n tool: name,\n error: {\n code: \"unknown_tool\",\n message: `Unknown tool: ${name}`,\n },\n };\n }\n\n let parsedArgs: unknown;\n try {\n parsedArgs = rawArguments.trim() ? JSON.parse(rawArguments) : {};\n } catch {\n return {\n ok: false,\n tool: name,\n error: {\n code: \"invalid_arguments\",\n message: \"Tool arguments must be valid JSON\",\n },\n };\n }\n\n return handler(parsedArgs, context);\n}\n", "/**\n * LLM Stream \u2014 handles SSE streaming from OpenAI-compatible chat completions API.\n */\n\nimport type { AgentChatMessage } from \"./types\";\nimport type { ToolDefinition } from \"../handlers\";\n\ntype StreamCallbacks = {\n onContent: (delta: string) => void;\n onReasoning: (delta: string) => void;\n onToolCall: (id: string, name: string, args: string) => void;\n onUsage: (usage: { promptTokens: number; completionTokens: number; totalTokens: number }) => void;\n onDone: () => void;\n onError: (error: Error) => void;\n};\n\ntype StreamOptions = {\n baseUrl: string;\n apiKey: string;\n model: string;\n messages: AgentChatMessage[];\n tools?: ToolDefinition[];\n};\n\nfunction chatCompletionsUrl(baseUrl: string): string {\n const trimmed = baseUrl.trim().replace(/\\/+$/, \"\");\n if (trimmed.endsWith(\"/v1\")) {\n return `${trimmed}/chat/completions`;\n }\n return `${trimmed}/v1/chat/completions`;\n}\n\nexport async function startLLMStream(\n options: StreamOptions,\n callbacks: StreamCallbacks,\n): Promise<void> {\n const url = chatCompletionsUrl(options.baseUrl);\n\n // Build request body\n const body: Record<string, unknown> = {\n model: options.model,\n messages: options.messages.map(formatMessage),\n stream: true,\n stream_options: { include_usage: true },\n };\n\n if (options.tools && options.tools.length > 0) {\n body.tools = options.tools;\n body.tool_choice = \"auto\";\n }\n\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${options.apiKey}`,\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => \"\");\n throw new Error(`API error (${response.status}): ${errorBody || response.statusText}`);\n }\n\n if (!response.body) {\n throw new Error(\"Response body is empty\");\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n const toolCallAccumulator = new ToolCallAccumulator({\n onIdentified: (id, name) => {\n // Called when a tool call is first identified\n },\n });\n let hasToolCalls = false;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n while (true) {\n const lineBreak = buffer.indexOf(\"\\n\");\n if (lineBreak === -1) break;\n\n const line = buffer.slice(0, lineBreak).trim();\n buffer = buffer.slice(lineBreak + 1);\n processLine(line, callbacks, toolCallAccumulator);\n }\n }\n\n // Process any remaining data\n if (buffer.trim()) {\n processLine(buffer.trim(), callbacks, toolCallAccumulator);\n }\n\n // Finalize tool calls\n const finalToolCalls = toolCallAccumulator.finalize();\n for (const call of finalToolCalls) {\n callbacks.onToolCall(call.id, call.name, call.arguments);\n }\n\n callbacks.onDone();\n } catch (error) {\n if (error instanceof Error) {\n callbacks.onError(error);\n } else {\n callbacks.onError(new Error(String(error)));\n }\n }\n}\n\nfunction processLine(\n line: string,\n callbacks: StreamCallbacks,\n toolCalls: ToolCallAccumulator,\n): void {\n if (!line || line.startsWith(\":\")) return;\n\n const payload = line.startsWith(\"data:\") ? line.slice(5).trim() : line;\n if (payload === \"[DONE]\") return;\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n return;\n }\n\n const choices = parsed.choices as Array<Record<string, unknown>> | undefined;\n const choice = choices?.[0];\n const delta = choice?.delta as Record<string, unknown> | undefined;\n\n if (delta) {\n // Reasoning content\n if (delta.reasoning_content) {\n callbacks.onReasoning(delta.reasoning_content as string);\n }\n\n // Text content\n if (delta.content) {\n callbacks.onContent(delta.content as string);\n }\n\n // Tool calls\n const toolCallDeltas = delta.tool_calls as Array<Record<string, unknown>> | undefined;\n if (toolCallDeltas) {\n for (const tcDelta of toolCallDeltas) {\n const index = tcDelta.index as number;\n const fn = tcDelta.function as Record<string, unknown> | undefined;\n const id = tcDelta.id as string | undefined;\n\n if (id) {\n toolCalls.startToolCall(index, id, fn?.name as string);\n } else if (fn?.arguments) {\n toolCalls.appendArguments(index, fn.arguments as string);\n }\n }\n }\n }\n\n // Usage\n const usage = parsed.usage as Record<string, unknown> | undefined;\n if (usage?.prompt_tokens !== undefined) {\n callbacks.onUsage({\n promptTokens: usage.prompt_tokens as number,\n completionTokens: usage.completion_tokens as number,\n totalTokens: usage.total_tokens as number,\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Tool Call Accumulator\n// ---------------------------------------------------------------------------\n\ntype PendingToolCall = {\n index: number;\n id: string;\n name: string;\n arguments: string;\n};\n\nclass ToolCallAccumulator {\n private pending: Map<number, PendingToolCall> = new Map();\n private finalized = false;\n private onIdentified: (id: string, name: string) => void;\n\n constructor(opts: { onIdentified: (id: string, name: string) => void }) {\n this.onIdentified = opts.onIdentified;\n }\n\n startToolCall(index: number, id: string, name: string): void {\n this.pending.set(index, { index, id, name, arguments: \"\" });\n }\n\n appendArguments(index: number, args: string): void {\n const existing = this.pending.get(index);\n if (existing) {\n existing.arguments += args;\n }\n }\n\n finalize(): Array<{ id: string; name: string; arguments: string }> {\n if (this.finalized) return [];\n this.finalized = true;\n const calls = Array.from(this.pending.values())\n .sort((a, b) => a.index - b.index)\n .map((c) => ({\n id: c.id,\n name: c.name,\n arguments: c.arguments,\n }));\n return calls;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Message formatter for OpenAI API\n// ---------------------------------------------------------------------------\n\nfunction formatMessage(msg: AgentChatMessage): Record<string, unknown> {\n const formatted: Record<string, unknown> = {\n role: msg.role,\n };\n\n if (msg.content !== undefined) {\n formatted.content = msg.content;\n }\n\n if (msg.reasoning_content) {\n formatted.reasoning_content = msg.reasoning_content;\n }\n\n if (msg.tool_calls) {\n formatted.tool_calls = msg.tool_calls;\n }\n\n if (msg.tool_call_id) {\n formatted.tool_call_id = msg.tool_call_id;\n }\n\n if (msg.name) {\n formatted.name = msg.name;\n }\n\n return formatted;\n}\n", "/**\n * CLI Agent Runner\n *\n * Manages the multi-turn agent loop. This is a CLI-native implementation\n * that bypasses Tauri entirely and uses direct API calls + Node.js tool handlers.\n */\n\nimport { executeToolCall, getToolDefinitions } from \"../handlers\";\nimport type { ToolExecutionContext, ToolResultEnvelope } from \"../handlers/types\";\nimport type { AgentChatMessage, AgentEvent, AgentEventHandler, AgentStartInput, AgentMode } from \"./types\";\nimport { startLLMStream } from \"./llm-stream\";\n\n// ---------------------------------------------------------------------------\n// Multi-turn agent loop\n// ---------------------------------------------------------------------------\n\ntype AgentTurnResult = {\n toolCalls: Array<{ id: string; name: string; arguments: string }>;\n content: string;\n reasoningContent: string;\n usage?: { promptTokens: number; completionTokens: number; totalTokens: number };\n};\n\nexport async function runAgentWithTools(\n input: AgentStartInput,\n toolContext: ToolExecutionContext,\n onEvent: AgentEventHandler,\n): Promise<AgentChatMessage[]> {\n let messages = [...input.messages];\n let cumulativeUsage: AgentTurnResult[\"usage\"] | undefined;\n\n while (true) {\n const turn = await runSingleAgentTurn(input, messages, onEvent);\n\n // Accumulate usage\n if (turn.usage) {\n cumulativeUsage = cumulativeUsage\n ? {\n promptTokens: cumulativeUsage.promptTokens + turn.usage.promptTokens,\n completionTokens: cumulativeUsage.completionTokens + turn.usage.completionTokens,\n totalTokens: cumulativeUsage.totalTokens + turn.usage.totalTokens,\n }\n : turn.usage;\n }\n\n // No tool calls \u2014 agent is done\n if (turn.toolCalls.length === 0) {\n // Append the final assistant response to messages for context persistence\n if (turn.content || turn.reasoningContent) {\n messages = [\n ...messages,\n {\n role: \"assistant\",\n content: turn.content || undefined,\n reasoning_content: turn.reasoningContent || undefined,\n },\n ];\n }\n onEvent({ type: \"done\", taskId: input.taskId, usage: cumulativeUsage ?? turn.usage });\n onEvent({ type: \"status\", taskId: input.taskId, status: \"completed\" });\n return messages;\n }\n\n // Execute tools and append results\n messages = await appendToolResults(messages, turn, toolContext, onEvent);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Single turn \u2014 stream from LLM and collect tool calls\n// ---------------------------------------------------------------------------\n\nasync function runSingleAgentTurn(\n input: AgentStartInput,\n messages: AgentChatMessage[],\n onEvent: AgentEventHandler,\n): Promise<AgentTurnResult> {\n return new Promise<AgentTurnResult>((resolve, reject) => {\n let content = \"\";\n let reasoningContent = \"\";\n const toolCalls: Array<{ id: string; name: string; arguments: string }> = [];\n let turnUsage: AgentTurnResult[\"usage\"] | undefined;\n\n startLLMStream(\n {\n baseUrl: input.baseUrl,\n apiKey: input.apiKey,\n model: input.model,\n messages,\n tools: getToolDefinitions(input.agentMode),\n },\n {\n onContent: (delta: string) => {\n content += delta;\n onEvent({ type: \"content_delta\", taskId: input.taskId, delta });\n },\n onReasoning: (delta: string) => {\n reasoningContent += delta;\n onEvent({ type: \"thinking_delta\", taskId: input.taskId, delta });\n },\n onToolCall: (id: string, name: string, args: string) => {\n toolCalls.push({ id, name, arguments: args });\n onEvent({ type: \"tool_call_pending\", taskId: input.taskId, toolCallId: id, name });\n },\n onUsage: (usage) => {\n turnUsage = usage;\n },\n onDone: () => {\n if (toolCalls.length > 0) {\n // Emit tool started events\n for (const call of toolCalls) {\n let parsedInput: unknown;\n try {\n parsedInput = call.arguments.trim() ? JSON.parse(call.arguments) : {};\n } catch {\n parsedInput = {};\n }\n onEvent({\n type: \"tool_call_started\",\n taskId: input.taskId,\n toolCallId: call.id,\n name: call.name,\n input: parsedInput,\n });\n }\n }\n resolve({ toolCalls, content, reasoningContent, usage: turnUsage });\n },\n onError: (error: Error) => {\n reject(error);\n },\n },\n );\n });\n}\n\n// ---------------------------------------------------------------------------\n// Tool result appending\n// ---------------------------------------------------------------------------\n\nasync function appendToolResults(\n messages: AgentChatMessage[],\n turn: AgentTurnResult,\n context: ToolExecutionContext,\n onEvent: AgentEventHandler,\n): Promise<AgentChatMessage[]> {\n // Build assistant message with tool calls\n const assistantMessage: AgentChatMessage = {\n role: \"assistant\",\n content: turn.content || undefined,\n reasoning_content: turn.reasoningContent || undefined,\n tool_calls: turn.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n };\n\n const nextMessages = [...messages, assistantMessage];\n\n // Execute all tools in parallel\n const results = await Promise.all(\n turn.toolCalls.map(async (call) => {\n try {\n const result = await executeToolCall(call.name, call.arguments, context);\n // Emit tool finished event\n if (result.ok) {\n onEvent({\n type: \"tool_call_finished\",\n taskId: context.taskId ?? \"\",\n toolCallId: call.id,\n output: result.data,\n });\n } else {\n onEvent({\n type: \"tool_call_finished\",\n taskId: context.taskId ?? \"\",\n toolCallId: call.id,\n errorText: result.error?.message,\n });\n }\n return { id: call.id, result };\n } catch (error) {\n const errorText = error instanceof Error ? error.message : String(error);\n onEvent({\n type: \"tool_call_finished\",\n taskId: context.taskId ?? \"\",\n toolCallId: call.id,\n errorText,\n });\n return {\n id: call.id,\n result: {\n ok: false,\n tool: call.name,\n error: { code: \"execution_error\", message: errorText },\n },\n };\n }\n }),\n );\n\n // Add tool result messages\n for (const { id, result } of results) {\n const content = result.ok\n ? JSON.stringify(result.data, null, 2)\n : JSON.stringify({ error: result.error }, null, 2);\n\n nextMessages.push({\n role: \"tool\",\n tool_call_id: id,\n content,\n });\n }\n\n return nextMessages;\n}\n", "/**\n * CLI Environment Resolver\n * Resolves the runtime environment for the agent system prompt.\n */\n\nimport { platform, release, hostname, EOL } from \"node:os\";\nimport { execSync } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nexport type AgentEnvironment = {\n workspaceDir: string | null;\n os: string;\n shell: string;\n isGitRepository: boolean;\n today: string;\n agentsMd: { path: string; content: string; truncated: boolean } | null;\n enabledSystemSkills: Array<{ slug: string; name: string; content: string }>;\n};\n\nexport function resolveAgentEnvironment(workspaceDir: string | null): AgentEnvironment {\n const os = `${platform()} (${release()})`;\n const shell = resolveShell();\n const isGitRepository = workspaceDir ? checkIsGitRepository(workspaceDir) : false;\n const today = new Date().toLocaleDateString(\"en-US\", {\n weekday: \"long\",\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n timeZone: \"UTC\",\n });\n\n let agentsMd: { path: string; content: string; truncated: boolean } | null = null;\n\n if (workspaceDir) {\n const agentsMdPath = resolve(workspaceDir, \"AGENTS.md\");\n if (existsSync(agentsMdPath)) {\n const content = readFileSync(agentsMdPath, \"utf-8\");\n agentsMd = {\n path: agentsMdPath,\n content: content.length > 4000 ? content.slice(0, 4000) + \"\\n... [truncated]\" : content,\n truncated: content.length > 4000,\n };\n }\n }\n\n return {\n workspaceDir,\n os,\n shell,\n isGitRepository,\n today,\n agentsMd,\n enabledSystemSkills: [],\n };\n}\n\nfunction resolveShell(): string {\n const p = platform();\n if (p === \"win32\") {\n return process.env.COMSPEC || \"cmd.exe\";\n }\n return process.env.SHELL || \"/bin/sh\";\n}\n\nfunction checkIsGitRepository(dir: string): boolean {\n try {\n execSync(\"git rev-parse --is-inside-work-tree\", {\n cwd: dir,\n stdio: \"pipe\",\n timeout: 3000,\n });\n return true;\n } catch {\n return false;\n }\n}\n\nexport function buildSystemPrompt(env: AgentEnvironment): string {\n const workspaceLine = env.workspaceDir ?? \"not selected\";\n const gitLine = env.isGitRepository ? \"yes\" : env.workspaceDir ? \"no\" : \"unknown\";\n\n const lines = [\n \"You are Coder, a helpful terminal AI assistant.\",\n \"\",\n \"## Environment\",\n `- Workspace: ${workspaceLine}`,\n `- OS: ${env.os}`,\n `- Shell: ${env.shell}`,\n `- Git repository: ${gitLine}`,\n `- Date: ${env.today}`,\n \"\",\n \"## Available Tools\",\n \"You have access to all standard tools: file operations, shell commands, web search, skills, and more.\",\n \"\",\n \"## Guidelines\",\n \"- Always read files before editing them.\",\n \"- Prefer targeted edits over full file replacements.\",\n \"- Use shell commands only for builds, tests, git, and non-interactive CLI tasks.\",\n \"- When a task requires multiple steps, create a plan first using todo_write.\",\n \"- After completing a task, summarize what was done.\",\n \"- Use ask_question when you need clarification from the user.\",\n \"\",\n ];\n\n if (env.agentsMd) {\n lines.push(\"## Project Instructions (AGENTS.md)\");\n lines.push(\"\");\n lines.push(env.agentsMd.content);\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n", "/**\n * Agent Session \u2014 manages a single agent session lifecycle.\n * Connects the agent loop to the CLI output.\n */\n\nimport type { AgentMode, AgentChatMessage, AgentEvent } from \"./types\";\nimport { runAgentWithTools } from \"./runner\";\nimport { resolveAgentEnvironment, buildSystemPrompt } from \"./environment\";\nimport { resolveProviderConfig, resolveApiKey, loadConfig } from \"../config\";\nimport type { ToolExecutionContext } from \"../handlers/types\";\nimport { error, warning, info, dim, bold, success, writeStream, writeLine, writeError } from \"../ui\";\n\nexport type SessionOptions = {\n agentMode: AgentMode;\n workspaceDir: string;\n interactive: boolean;\n model?: string;\n provider?: string;\n stream?: boolean;\n /**\n * Existing conversation history to continue from.\n * When provided, the system prompt is assumed to already be in the messages.\n * The new user prompt will be appended.\n */\n existingMessages?: AgentChatMessage[];\n};\n\n/**\n * Run an agent session.\n * Returns the updated messages array so callers (e.g. REPL) can persist context.\n */\nexport async function runAgentSession(\n prompt: string,\n options: SessionOptions,\n): Promise<AgentChatMessage[]> {\n const config = loadConfig();\n const workspaceDir = options.workspaceDir || null;\n\n // Resolve provider\n const providerId = (options.provider as any) ?? config.activeProvider;\n const resolvedConfig = resolveProviderConfig(config, providerId);\n const apiKey = resolveApiKey(resolvedConfig);\n\n // Resolve model\n const modelId = options.model ?? config.lastModel;\n\n // Build messages \u2014 either continue from existing or start fresh\n let messages: AgentChatMessage[];\n\n if (options.existingMessages && options.existingMessages.length > 0) {\n // Continue conversation: existing messages already contain the system prompt\n messages = [...options.existingMessages, { role: \"user\", content: prompt }];\n } else {\n // Fresh session: build system prompt\n const env = resolveAgentEnvironment(workspaceDir);\n const systemPrompt = buildSystemPrompt(env);\n messages = [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: prompt },\n ];\n }\n\n const taskId = `task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n const isStreaming = options.stream !== false;\n\n let currentContent = \"\";\n let currentThinking = \"\";\n let hasContent = false;\n\n // Tool execution context\n const toolContext: ToolExecutionContext = {\n workspaceDir,\n sessionId: taskId,\n taskId,\n agentMode: options.agentMode,\n };\n\n if (!isStreaming) {\n writeLine(`${info(\"\u2139\")} Running agent in ${bold(options.agentMode)} mode...\\n`);\n }\n\n try {\n const finalMessages = await runAgentWithTools(\n {\n taskId,\n baseUrl: resolvedConfig.baseUrl,\n apiKey,\n apiKeySource: resolvedConfig.apiKeySource,\n apiKeyEnvVar: resolvedConfig.apiKeyEnvVar,\n model: modelId,\n messages,\n agentMode: options.agentMode,\n },\n toolContext,\n (event: AgentEvent) => {\n switch (event.type) {\n case \"thinking_delta\": {\n currentThinking += event.delta;\n if (isStreaming && !hasContent) {\n writeStream(dim(event.delta));\n }\n break;\n }\n\n case \"content_delta\": {\n if (!hasContent && currentThinking && isStreaming) {\n // Transition from thinking to content\n writeLine(\"\");\n hasContent = true;\n }\n hasContent = true;\n currentContent += event.delta;\n if (isStreaming) {\n writeStream(event.delta);\n }\n break;\n }\n\n case \"tool_call_started\": {\n if (isStreaming) {\n if (hasContent || currentThinking) {\n writeLine(\"\");\n }\n writeLine(`${dim(\"\uD83D\uDD27\")} ${bold(event.name)}${dim(\"...\")}`);\n }\n break;\n }\n\n case \"tool_call_finished\": {\n if (event.errorText) {\n writeLine(` ${error(\"\u2717\")} ${dim(event.errorText.slice(0, 200))}`);\n } else if (isStreaming) {\n writeLine(` ${success(\"\u2713\")} ${dim(\"done\")}`);\n }\n break;\n }\n\n case \"status\": {\n if (event.status === \"completed\") {\n if (isStreaming) {\n writeLine(\"\");\n writeLine(success(`\\n\u2713 Task completed`));\n } else {\n writeLine(success(`\u2713 Task completed`));\n if (currentContent) {\n writeLine(\"\\n\" + currentContent);\n }\n }\n } else if (event.status === \"failed\") {\n writeLine(error(`\\n\u2717 Task failed`));\n } else if (event.status === \"cancelled\") {\n writeLine(warning(`\\n\u26A0 Task cancelled`));\n }\n break;\n }\n\n case \"done\": {\n if (event.usage && config.showUsage) {\n writeLine(dim(\n ` Tokens: ${event.usage.promptTokens}\u2191 ${event.usage.completionTokens}\u2193 ${event.usage.totalTokens}\u2211`,\n ));\n }\n break;\n }\n\n case \"error\": {\n writeLine(error(`\\n\u2717 Error: ${event.message}`));\n break;\n }\n }\n },\n );\n return finalMessages;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n writeError(error(`Fatal error: ${message}`));\n process.exit(1);\n }\n}\n", "/**\n * Terminal output utilities for Coder CLI.\n * Provides colored output, progress indication, and streaming helpers.\n */\n\nimport { isatty } from \"node:tty\";\n\nconst stdoutIsTTY = isatty(process.stdout.fd);\nconst stderrIsTTY = isatty(process.stderr.fd);\n\n// ANSI color codes (no dependency on chalk for speed)\nconst colors = {\n reset: \"\\x1b[0m\",\n bold: \"\\x1b[1m\",\n dim: \"\\x1b[2m\",\n italic: \"\\x1b[3m\",\n red: \"\\x1b[31m\",\n green: \"\\x1b[32m\",\n yellow: \"\\x1b[33m\",\n blue: \"\\x1b[34m\",\n magenta: \"\\x1b[35m\",\n cyan: \"\\x1b[36m\",\n gray: \"\\x1b[90m\",\n};\n\nexport function colorize(text: string, color: keyof typeof colors): string {\n if (!stdoutIsTTY) return text;\n return `${colors[color]}${text}${colors.reset}`;\n}\n\nexport function dim(text: string): string {\n return colorize(text, \"dim\");\n}\n\nexport function bold(text: string): string {\n if (!stdoutIsTTY) return text;\n return `${colors.bold}${text}${colors.reset}`;\n}\n\nexport function error(text: string): string {\n return colorize(text, \"red\");\n}\n\nexport function success(text: string): string {\n return colorize(text, \"green\");\n}\n\nexport function warning(text: string): string {\n return colorize(text, \"yellow\");\n}\n\nexport function info(text: string): string {\n return colorize(text, \"cyan\");\n}\n\n// ---------------------------------------------------------------------------\n// Streaming output\n// ---------------------------------------------------------------------------\n\nexport function writeStream(text: string): void {\n process.stdout.write(text);\n}\n\nexport function writeLine(text: string): void {\n process.stdout.write(text + \"\\n\");\n}\n\nexport function writeError(text: string): void {\n process.stderr.write(text + \"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Spinner / progress\n// ---------------------------------------------------------------------------\n\nconst SPINNER_FRAMES = [\"\u280B\", \"\u2819\", \"\u2839\", \"\u2838\", \"\u283C\", \"\u2834\", \"\u2826\", \"\u2827\", \"\u2807\", \"\u280F\"];\n\nexport class Spinner {\n private frame = 0;\n private interval: ReturnType<typeof setInterval> | null = null;\n private message = \"\";\n private running = false;\n\n start(message: string): void {\n if (!stderrIsTTY) {\n writeError(`\u23F3 ${message}`);\n return;\n }\n\n this.message = message;\n this.running = true;\n this.frame = 0;\n process.stderr.write(`${SPINNER_FRAMES[0]} ${message}`);\n this.interval = setInterval(() => {\n this.frame = (this.frame + 1) % SPINNER_FRAMES.length;\n process.stderr.write(`\\r${SPINNER_FRAMES[this.frame]} ${this.message}`);\n }, 80);\n }\n\n update(message: string): void {\n this.message = message;\n if (this.interval) {\n process.stderr.write(`\\r${SPINNER_FRAMES[this.frame]} ${this.message}`);\n }\n }\n\n stop(finalMessage?: string): void {\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = null;\n }\n this.running = false;\n // Clear the spinner line\n if (stderrIsTTY) {\n process.stderr.write(\"\\r\\x1b[K\");\n }\n if (finalMessage) {\n writeError(finalMessage);\n }\n }\n\n succeed(text: string): void {\n this.stop(`${success(\"\u2713\")} ${text}`);\n }\n\n fail(text: string): void {\n this.stop(`${error(\"\u2717\")} ${text}`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Thinking block rendering (like Claude Code's thinking animation)\n// ---------------------------------------------------------------------------\n\nexport class ThinkingDisplay {\n private spinner: Spinner;\n private lines: string[] = [];\n private expanded = false;\n\n constructor() {\n this.spinner = new Spinner();\n }\n\n start(): void {\n this.spinner.start(\"Thinking...\");\n }\n\n update(delta: string): void {\n this.spinner.update(\"Thinking...\");\n }\n\n stop(): void {\n this.spinner.stop();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Confirm prompt\n// ---------------------------------------------------------------------------\n\nexport async function confirmPrompt(question: string, autoYes: boolean = false): Promise<boolean> {\n if (autoYes) {\n return true;\n }\n\n if (!stderrIsTTY) {\n // Non-interactive: assume yes for piped input\n writeError(`${warning(\"\u26A0\")} ${question} ${dim(\"(assuming yes for non-interactive)\")}`);\n return true;\n }\n\n writeError(`${question} ${dim(\"[y/N]\")} `);\n\n return new Promise((resolve) => {\n process.stdin.once(\"data\", (data: Buffer) => {\n const input = data.toString().trim().toLowerCase();\n resolve(input === \"y\" || input === \"yes\");\n });\n });\n}\n\n// ---------------------------------------------------------------------------\n// Box / separator\n// ---------------------------------------------------------------------------\n\nexport function separator(title?: string): string {\n const width = Math.min(process.stdout.columns || 80, 80);\n if (!title) {\n return dim(\"\u2500\".repeat(width));\n }\n const titleStr = ` ${title} `;\n const half = Math.floor((width - titleStr.length) / 2);\n return dim(\"\u2500\".repeat(Math.max(0, half))) + bold(titleStr) + dim(\"\u2500\".repeat(Math.max(0, width - half - titleStr.length)));\n}\n", "/**\n * coder run <prompt> \u2014 Run the agent in full mode.\n * coder <prompt> \u2014 Shorthand for the same.\n */\n\nimport type { GlobalOptions } from \"./common\";\nimport { runAgentSession } from \"../agent/session\";\n\nexport async function runCommand(\n prompt: string,\n options: GlobalOptions,\n): Promise<void> {\n await runAgentSession(prompt, {\n ...options,\n agentMode: \"agent\",\n workspaceDir: options.workspace ?? process.cwd(),\n interactive: false,\n });\n}\n", "/**\n * coder ask <prompt> \u2014 Run in ask (read-only) mode.\n */\n\nimport type { GlobalOptions } from \"./common\";\nimport { runAgentSession } from \"../agent/session\";\n\nexport async function askCommand(\n prompt: string,\n options: GlobalOptions,\n): Promise<void> {\n await runAgentSession(prompt, {\n ...options,\n agentMode: \"ask\",\n workspaceDir: options.workspace ?? process.cwd(),\n interactive: false,\n });\n}\n", "/**\n * coder plan <prompt> \u2014 Run in plan mode.\n */\n\nimport type { GlobalOptions } from \"./common\";\nimport { runAgentSession } from \"../agent/session\";\n\nexport async function planCommand(\n prompt: string,\n options: GlobalOptions,\n): Promise<void> {\n await runAgentSession(prompt, {\n ...options,\n agentMode: \"plan\",\n workspaceDir: options.workspace ?? process.cwd(),\n interactive: false,\n });\n}\n", "/**\n * coder repl \u2014 Start interactive REPL session.\n */\n\nimport { createInterface } from \"node:readline\";\nimport type { GlobalOptions } from \"./common\";\nimport { runAgentSession } from \"../agent/session\";\nimport type { AgentChatMessage } from \"../agent/types\";\nimport { bold, dim, info, error, writeLine, writeError } from \"../ui\";\nimport { loadConfig } from \"../config\";\n\nexport async function replCommand(options: GlobalOptions): Promise<void> {\n const config = loadConfig();\n const workspaceDir = options.workspace ?? process.cwd();\n\n writeLine(\"\");\n writeLine(bold(\"Coder CLI \u2014 Interactive REPL\"));\n writeLine(dim(\"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\"));\n writeLine(dim(` Model: ${config.lastModel || \"not set\"}`));\n writeLine(dim(` Provider: ${config.activeProvider}`));\n writeLine(dim(` Workspace: ${workspaceDir}`));\n writeLine(dim(` Type 'exit' or Ctrl+C to quit`));\n writeLine(dim(\"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\"));\n writeLine(\"\");\n\n // Persistent conversation context across REPL turns\n let conversationMessages: AgentChatMessage[] | undefined;\n\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n prompt: `${bold(\"coder\")}> `,\n terminal: true,\n });\n\n rl.prompt();\n\n rl.on(\"line\", async (line: string) => {\n const trimmed = line.trim();\n\n if (!trimmed) {\n rl.prompt();\n return;\n }\n\n if (trimmed === \"exit\" || trimmed === \"quit\") {\n rl.close();\n return;\n }\n\n if (trimmed === \"clear\") {\n console.clear();\n rl.prompt();\n return;\n }\n\n if (trimmed === \"help\") {\n writeLine(bold(\"\\nREPL Commands:\"));\n writeLine(\" <prompt> Ask the agent anything\");\n writeLine(\" exit / quit Exit REPL\");\n writeLine(\" clear Clear screen\");\n writeLine(\" help Show this help\");\n writeLine(\" model <id> Switch model\");\n writeLine(\"\");\n rl.prompt();\n return;\n }\n\n if (trimmed.startsWith(\"model \")) {\n const modelId = trimmed.slice(6).trim();\n if (modelId) {\n config.lastModel = modelId;\n const { saveConfig } = await import(\"../config\");\n saveConfig(config);\n writeLine(info(`Model set to: ${modelId}`));\n }\n rl.prompt();\n return;\n }\n\n // Run the agent with the given prompt\n rl.pause();\n\n try {\n conversationMessages = await runAgentSession(trimmed, {\n agentMode: \"agent\",\n workspaceDir,\n interactive: true,\n model: options.model,\n provider: options.provider,\n existingMessages: conversationMessages,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n writeError(error(`Error: ${message}`));\n }\n\n writeLine(\"\");\n rl.prompt();\n rl.resume();\n });\n\n rl.on(\"close\", () => {\n writeLine(dim(\"\\nGoodbye! \uD83D\uDC4B\"));\n process.exit(0);\n });\n\n rl.on(\"SIGINT\", () => {\n rl.close();\n });\n}\n", "/**\n * coder init \u2014 Interactive initialization of Coder CLI configuration.\n */\n\nimport { bold, dim, info, success, error, writeLine, writeError } from \"../ui\";\nimport { loadConfig, saveConfig, getConfigDirPath, getConfigFilePathExplicit } from \"../config\";\nimport { createInterface } from \"node:readline\";\n\nexport async function initCommand(): Promise<void> {\n writeLine(bold(\"\\nCoder CLI \u2014 Initialization\"));\n writeLine(dim(\"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\"));\n writeLine(`Config directory: ${getConfigDirPath()}`);\n writeLine(\"\");\n\n const config = loadConfig();\n\n // Provider selection\n writeLine(info(\"Select your AI provider:\"));\n const providers = [\"deepseek\", \"glm\", \"agnes\", \"nvidia\", \"minimax\", \"custom\"];\n for (let i = 0; i < providers.length; i++) {\n writeLine(` ${i + 1}. ${providers[i]}`);\n }\n writeLine(\"\");\n\n const providerIndex = await askQuestion(\"Provider number [1]: \");\n const providerChoice = parseInt(providerIndex || \"1\", 10);\n const selectedProvider = providers[Math.max(0, Math.min(providerChoice - 1, providers.length - 1))];\n config.activeProvider = selectedProvider as any;\n writeLine(` Selected: ${bold(selectedProvider)}`);\n writeLine(\"\");\n\n // API key\n const providerSettings = config.providers[selectedProvider as keyof typeof config.providers];\n const envVar = providerSettings.apiKeyEnvVar;\n\n writeLine(info(`API Key Configuration for \"${selectedProvider}\":`));\n writeLine(` You can set the ${bold(envVar)} environment variable,`);\n writeLine(` or enter the key directly (stored in config file).`);\n writeLine(\"\");\n\n const source = await askQuestion(\"Use environment variable? [Y/n]: \");\n const useEnv = source.toLowerCase() !== \"n\";\n\n if (useEnv) {\n providerSettings.apiKeySource = \"env\";\n writeLine(` Using ${bold(envVar)} environment variable.`);\n writeLine(` Make sure to set it: export ${envVar}=<your-api-key>`);\n } else {\n providerSettings.apiKeySource = \"manual\";\n const key = await askQuestion(\"Enter API key: \");\n if (key.trim()) {\n providerSettings.apiKey = key.trim();\n writeLine(\" API key saved to config.\");\n } else {\n writeLine(` ${error(\"No key entered. Set it later with: coder config\")}`);\n }\n }\n writeLine(\"\");\n\n // Custom base URL (optional)\n if (selectedProvider === \"custom\") {\n const baseUrl = await askQuestion(\"Custom API base URL: \");\n if (baseUrl.trim()) {\n providerSettings.customBaseUrl = baseUrl.trim();\n }\n } else {\n const customUrl = await askQuestion(`Custom base URL (leave empty for default): `);\n if (customUrl.trim()) {\n providerSettings.customBaseUrl = customUrl.trim();\n }\n writeLine(\"\");\n }\n\n // Save config\n saveConfig(config);\n writeLine(success(\"\\n\u2713 Configuration saved!\"));\n writeLine(` Config file: ${getConfigFilePathExplicit()}`);\n writeLine(\"\");\n writeLine(info(\"Quick start:\"));\n writeLine(\" coder \\\"What is in this directory?\\\"\");\n writeLine(\" coder ask \\\"Explain this code\\\"\");\n writeLine(\" coder repl\");\n writeLine(\"\");\n}\n\nfunction askQuestion(query: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n\n return new Promise((resolve) => {\n rl.question(query, (answer) => {\n rl.close();\n resolve(answer);\n });\n });\n}\n", "/**\n * coder config \u2014 View or edit configuration.\n */\n\nimport { bold, dim, info, warning, writeLine } from \"../ui\";\nimport {\n loadConfig,\n saveConfig,\n getConfigDirPath,\n getConfigFilePathExplicit,\n type ProviderId,\n type ProviderSettings,\n} from \"../config\";\n\nconst PROVIDER_IDS: ProviderId[] = [\"deepseek\", \"glm\", \"agnes\", \"nvidia\", \"minimax\", \"custom\"];\n\nexport async function configCommand(key?: string, value?: string): Promise<void> {\n const config = loadConfig();\n\n // If no args, show full config\n if (!key) {\n showConfig(config);\n return;\n }\n\n // If both key and value, set a value\n if (key && value !== undefined) {\n await setConfigValue(config, key, value);\n return;\n }\n\n // If only key, show that specific value\n showConfigValue(config, key);\n}\n\nfunction showConfig(config: ReturnType<typeof loadConfig>): void {\n writeLine(bold(\"\\nCoder CLI Configuration\"));\n writeLine(dim(\"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\"));\n writeLine(`Config directory: ${getConfigDirPath()}`);\n writeLine(`Config file: ${getConfigFilePathExplicit()}`);\n writeLine(\"\");\n writeLine(bold(\"Active Settings:\"));\n writeLine(` Active provider: ${config.activeProvider}`);\n writeLine(` Last model: ${config.lastModel}`);\n writeLine(` Show usage: ${config.showUsage}`);\n writeLine(\"\");\n\n for (const providerId of PROVIDER_IDS) {\n const p = config.providers[providerId];\n writeLine(bold(`Provider: ${providerId}`));\n writeLine(` API Key Source: ${p.apiKeySource}`);\n writeLine(` API Key Env Var: ${p.apiKeyEnvVar}`);\n writeLine(` API Key (stored): ${p.apiKey ? \"***\" : \"(not set)\"}`);\n writeLine(` Custom Base URL: ${p.customBaseUrl || \"(default)\"}`);\n writeLine(\"\");\n }\n\n writeLine(dim(\"To change a value: coder config <key> <value>\"));\n writeLine(dim(\"Examples:\"));\n writeLine(dim(' coder config activeProvider \"deepseek\"'));\n writeLine(dim(' coder config lastModel \"deepseek-v4-flash\"'));\n writeLine(dim(' coder config providers.deepseek.apiKeySource \"env\"'));\n writeLine(\"\");\n}\n\nfunction showConfigValue(config: ReturnType<typeof loadConfig>, key: string): void {\n const value = getNestedValue(config, key);\n if (value === undefined) {\n writeLine(warning(`Config key not found: ${key}`));\n return;\n }\n writeLine(String(value));\n}\n\nasync function setConfigValue(\n config: ReturnType<typeof loadConfig>,\n key: string,\n value: string,\n): Promise<void> {\n setNestedValue(config, key, parseValue(value));\n saveConfig(config);\n writeLine(info(`Config updated: ${key} = ${value}`));\n}\n\nfunction getNestedValue(obj: Record<string, unknown>, path: string): unknown {\n const parts = path.split(\".\");\n let current: unknown = obj;\n\n for (const part of parts) {\n if (current === null || current === undefined || typeof current !== \"object\") {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n\n return current;\n}\n\nfunction setNestedValue(obj: Record<string, unknown>, path: string, value: unknown): void {\n const parts = path.split(\".\");\n let current = obj;\n\n for (let i = 0; i < parts.length - 1; i++) {\n if (!(parts[i] in current)) {\n current[parts[i]] = {};\n }\n current = current[parts[i]] as Record<string, unknown>;\n }\n\n current[parts[parts.length - 1]] = value;\n}\n\nfunction parseValue(value: string): unknown {\n // Try to parse as boolean\n if (value === \"true\") return true;\n if (value === \"false\") return false;\n // Try to parse as number\n const num = Number(value);\n if (!isNaN(num) && value.trim() !== \"\") return num;\n // Keep as string\n return value;\n}\n", "/**\n * Shared types and helpers for CLI commands.\n */\n\nimport type { AgentMode } from \"../agent/types\";\n\nexport type GlobalOptions = {\n model?: string;\n provider?: string;\n workspace?: string;\n yes?: boolean;\n stream?: boolean;\n};\n\nexport type CommandContext = GlobalOptions & {\n agentMode: AgentMode;\n};\n\nlet _globalOptions: GlobalOptions = {};\n\nexport function setGlobalOptions(opts: GlobalOptions): void {\n _globalOptions = opts;\n}\n\nexport function getGlobalOptions(): GlobalOptions {\n return _globalOptions;\n}\n", "/**\n * Coder CLI \u2014 Entrypoint\n *\n * Usage:\n * coder <prompt> Run agent with prompt\n * coder ask <prompt> Run in ask mode\n * coder plan <prompt> Run in plan mode\n * coder repl Start interactive REPL\n * coder init Initialize configuration\n * coder config Show/edit configuration\n * coder --version Show version\n * coder --help Show help\n */\n\nimport { runCommand } from \"./commands/run\";\nimport { askCommand } from \"./commands/ask\";\nimport { planCommand } from \"./commands/plan\";\nimport { replCommand } from \"./commands/repl\";\nimport { initCommand } from \"./commands/init\";\nimport { configCommand } from \"./commands/config\";\nimport { setGlobalOptions } from \"./commands/common\";\nimport { error, writeError } from \"./ui\";\n\nconst VERSION = process.env.CLI_VERSION ?? \"0.0.0\";\n\nasync function main() {\n // Quick help/version check before commander\n const rawArgs = process.argv.slice(2);\n\n if (rawArgs.includes(\"--help\") || rawArgs.includes(\"-h\")) {\n printHelp();\n return;\n }\n\n if (rawArgs.includes(\"--version\") || rawArgs.includes(\"-V\")) {\n console.log(VERSION);\n return;\n }\n\n // If no args, start REPL\n if (rawArgs.length === 0) {\n await replCommand({});\n return;\n }\n\n // Extract global options from raw args\n const globalOpts = extractGlobalOptions(rawArgs);\n setGlobalOptions(globalOpts);\n\n // Check for known subcommands\n const firstNonFlag = rawArgs.find((a) => !a.startsWith(\"-\"));\n const knownSubcommands: Record<string, (args: string[], opts: Record<string, unknown>) => Promise<void>> = {\n ask: async (args, opts) => { await askCommand(args.join(\" \"), opts); },\n plan: async (args, opts) => { await planCommand(args.join(\" \"), opts); },\n repl: async () => { await replCommand(globalOpts); },\n init: async () => { await initCommand(); },\n config: async (args) => { await configCommand(args[0], args[1]); },\n };\n\n if (firstNonFlag && knownSubcommands[firstNonFlag]) {\n const cmdArgs = rawArgs.slice(rawArgs.indexOf(firstNonFlag) + 1).filter((a) => !a.startsWith(\"-\"));\n await knownSubcommands[firstNonFlag](cmdArgs, globalOpts);\n return;\n }\n\n // Everything else is treated as a prompt\n const promptParts = rawArgs.filter((a) => !a.startsWith(\"-\"));\n const prompt = promptParts.join(\" \");\n\n if (!prompt.trim()) {\n await replCommand(globalOpts);\n return;\n }\n\n await runCommand(prompt, globalOpts);\n}\n\nfunction extractGlobalOptions(args: string[]): Record<string, unknown> {\n const opts: Record<string, unknown> = {};\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === \"-m\" || arg === \"--model\") {\n opts.model = args[++i] ?? \"\";\n } else if (arg === \"-p\" || arg === \"--provider\") {\n opts.provider = args[++i] ?? \"\";\n } else if (arg === \"-w\" || arg === \"--workspace\") {\n opts.workspace = args[++i] ?? \"\";\n } else if (arg === \"-y\" || arg === \"--yes\") {\n opts.yes = true;\n } else if (arg === \"--no-stream\") {\n opts.stream = false;\n } else if (arg === \"--stream\") {\n opts.stream = true;\n }\n }\n return opts;\n}\n\nfunction printHelp(): void {\n console.log(`\nUsage: coder [options] [prompt...]\n\nCoder CLI \u2014 AI-powered coding assistant in the terminal\n\nOptions:\n -V, --version output the version number\n -m, --model <model> Model ID to use\n -p, --provider <provider> Provider ID (deepseek, glm, agnes, nvidia, minimax, custom)\n -w, --workspace <path> Workspace directory\n -y, --yes Auto-confirm prompts (unattended mode)\n --no-stream Disable streaming output\n -h, --help display help for command\n\nCommands:\n ask [prompt...] Ask a question (read-only mode)\n plan [prompt...] Create or work on a plan\n repl Start interactive REPL session\n init Initialize Coder CLI configuration\n config [key] [value] View or edit configuration\n`);\n}\n\nmain().catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n writeError(error(`Fatal: ${message}`));\n process.exit(1);\n});\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": ["import_node_path", "import_node_fs", "error", "import_node_fs", "import_node_path", "error", "import_node_fs", "import_node_path", "error", "import_node_fs", "import_node_path", "error", "import_node_fs", "import_node_path", "import_node_crypto", "error", "import_node_fs", "import_node_path", "import_node_crypto", "error", "import_node_fs", "import_node_path", "error", "import_node_fs", "import_node_path", "error", "import_node_path", "error", "error", "shells", "error", "error", "import_node_fs", "import_node_path", "import_node_fs", "import_node_path", "import_node_fs", "import_node_path", "error", "resolve", "error", "error", "resolve", "error", "import_node_os", "import_node_child_process", "import_node_fs", "import_node_path", "import_node_readline", "saveConfig", "import_node_readline", "resolve", "PROVIDER_IDS"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alanwchat/coder",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.107",
|
|
4
4
|
"description": "Coder CLI — AI-powered coding assistant in the terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/leyen-me/coder.git"
|
|
9
9
|
},
|
|
10
|
-
"bin": "./bin
|
|
10
|
+
"bin": "./bin",
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|
|
13
|
-
"bin
|
|
13
|
+
"bin"
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "node build.mjs",
|
package/bin.cjs
DELETED