diff --git a/bun.lock b/bun.lock index b2a1b15ff49a..cbc4e7f7e78f 100644 --- a/bun.lock +++ b/bun.lock @@ -29,7 +29,7 @@ }, "packages/app": { "name": "@opencode-ai/app", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/core": "workspace:*", @@ -85,7 +85,7 @@ }, "packages/cli": { "name": "@opencode-ai/cli", - "version": "1.17.0", + "version": "1.17.1", "bin": { "lildax": "./bin/lildax.cjs", }, @@ -110,7 +110,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -146,7 +146,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -173,7 +173,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@ai-sdk/anthropic": "3.0.82", "@ai-sdk/openai": "3.0.48", @@ -195,7 +195,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -219,7 +219,7 @@ }, "packages/console/support": { "name": "@opencode-ai/console-support", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@opencode-ai/console-core": "workspace:*", @@ -239,7 +239,7 @@ }, "packages/core": { "name": "@opencode-ai/core", - "version": "1.17.0", + "version": "1.17.1", "bin": { "opencode": "./bin/opencode", }, @@ -330,7 +330,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@zip.js/zip.js": "2.7.62", "effect": "catalog:", @@ -384,7 +384,7 @@ }, "packages/effect-drizzle-sqlite": { "name": "@opencode-ai/effect-drizzle-sqlite", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "drizzle-orm": "catalog:", "effect": "catalog:", @@ -398,7 +398,7 @@ }, "packages/effect-sqlite-node": { "name": "@opencode-ai/effect-sqlite-node", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "effect": "catalog:", }, @@ -410,7 +410,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@hono/standard-validator": "catalog:", "@opencode-ai/core": "workspace:*", @@ -441,7 +441,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "catalog:", @@ -457,7 +457,7 @@ }, "packages/http-recorder": { "name": "@opencode-ai/http-recorder", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@effect/platform-node": "4.0.0-beta.74", "@effect/platform-node-shared": "4.0.0-beta.74", @@ -476,7 +476,7 @@ }, "packages/llm": { "name": "@opencode-ai/llm", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@smithy/eventstream-codec": "4.2.14", "@smithy/util-utf8": "4.2.2", @@ -494,7 +494,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.17.0", + "version": "1.17.1", "bin": { "opencode": "./bin/opencode", }, @@ -622,7 +622,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@opencode-ai/sdk": "workspace:*", "effect": "catalog:", @@ -660,7 +660,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "cross-spawn": "catalog:", }, @@ -675,7 +675,7 @@ }, "packages/server": { "name": "@opencode-ai/server", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@opencode-ai/core": "workspace:*", "drizzle-orm": "catalog:", @@ -689,7 +689,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -702,7 +702,7 @@ }, "packages/stats/app": { "name": "@opencode-ai/stats-app", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@ibm/plex": "6.4.1", "@opencode-ai/stats-core": "workspace:*", @@ -735,7 +735,7 @@ }, "packages/stats/core": { "name": "@opencode-ai/stats-core", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@aws-sdk/client-athena": "3.933.0", "@planetscale/database": "1.19.0", @@ -754,7 +754,7 @@ }, "packages/stats/server": { "name": "@opencode-ai/stats-server", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@aws-sdk/client-firehose": "3.933.0", "@effect/platform-node": "catalog:", @@ -794,7 +794,7 @@ }, "packages/tui": { "name": "@opencode-ai/tui", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@opencode-ai/core": "workspace:*", "@opencode-ai/plugin": "workspace:*", @@ -822,7 +822,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/core": "workspace:*", @@ -871,7 +871,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.17.0", + "version": "1.17.1", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/tui/src/component/prompt/index.tsx b/packages/tui/src/component/prompt/index.tsx index 0b8311963261..5f48163b0bf1 100644 --- a/packages/tui/src/component/prompt/index.tsx +++ b/packages/tui/src/component/prompt/index.tsx @@ -437,6 +437,7 @@ export function Prompt(props: PromptProps) { const content = await openEditor({ renderer, value, + config: tuiConfig.editor, cwd: (project.instance.path().worktree === "/" ? undefined : project.instance.path().worktree) || project.instance.directory() || diff --git a/packages/tui/src/config/index.tsx b/packages/tui/src/config/index.tsx index df9239763a68..9fbbdfb9fb13 100644 --- a/packages/tui/src/config/index.tsx +++ b/packages/tui/src/config/index.tsx @@ -50,6 +50,15 @@ export const Prompt = Schema.Struct({ }), }).annotate({ description: "Prompt size settings" }) +export const EditorConfig = Schema.Struct({ + editor_path: Schema.optional(Schema.String).annotate({ + description: "Editor command (overrides VISUAL/EDITOR environment variables)", + }), + editor_temp_dir: Schema.optional(Schema.String).annotate({ + description: "Temporary directory for editor temp files (default: OS tmp dir)", + }), +}).annotate({ description: "Editor settings" }) + export const Info = Schema.Struct({ $schema: Schema.optional(Schema.String), theme: Schema.optional(Schema.String), @@ -58,6 +67,7 @@ export const Info = Schema.Struct({ plugin_enabled: Schema.optional(Schema.Record(Schema.String, Schema.Boolean)), leader_timeout: Schema.optional(LeaderTimeout), attention: Schema.optional(Attention), + editor: Schema.optional(EditorConfig), prompt: Schema.optional(Prompt), scroll_speed: Schema.optional(ScrollSpeed).annotate({ description: "TUI scroll speed" }), scroll_acceleration: Schema.optional(ScrollAcceleration), diff --git a/packages/tui/src/editor.ts b/packages/tui/src/editor.ts index 68afba6751b6..7665d699d69e 100644 --- a/packages/tui/src/editor.ts +++ b/packages/tui/src/editor.ts @@ -23,10 +23,21 @@ export function normalizePromptContent(content: string) { return content } -export async function openEditor(input: { value: string; renderer: CliRenderer; cwd?: string; stdin?: EditorStdio }) { - const editor = process.env.VISUAL || process.env.EDITOR +export type EditorConfig = { + editor_path?: string + editor_temp_dir?: string +} + +export async function openEditor(input: { + value: string + renderer: CliRenderer + cwd?: string + stdin?: EditorStdio + config?: EditorConfig +}) { + const editor = input.config?.editor_path || process.env.VISUAL || process.env.EDITOR if (!editor) return - const file = path.join(os.tmpdir(), `${Date.now()}.md`) + const file = path.join(input.config?.editor_temp_dir ?? os.tmpdir(), `${Date.now()}.md`) await writeFile(file, input.value) input.renderer.suspend() input.renderer.currentRenderBuffer.clear() diff --git a/packages/tui/src/routes/session/index.tsx b/packages/tui/src/routes/session/index.tsx index 7736eb75b0c3..a2f935823fe5 100644 --- a/packages/tui/src/routes/session/index.tsx +++ b/packages/tui/src/routes/session/index.tsx @@ -978,10 +978,11 @@ export function Session() { ) if (options.openWithoutSaving) { - // Just open in editor without saving + // Just open in editor without saving — use os.tmpdir() not user-configured temp dir await openEditor({ renderer, value: transcript, + config: { ...tuiConfig.editor, editor_temp_dir: undefined }, cwd: (project.instance.path().worktree === "/" ? undefined : project.instance.path().worktree) || project.instance.directory() || @@ -998,11 +999,17 @@ export function Session() { const result = await openEditor({ renderer, value: transcript, + config: tuiConfig.editor, cwd: (project.instance.path().worktree === "/" ? undefined : project.instance.path().worktree) || project.instance.directory() || paths.cwd, + }).catch((e) => { + // File was already saved above. Opening the editor afterward is + // best-effort — swallow errors so they don't reach the outer + // catch block, which would incorrectly show "Failed to export". }) + if (result !== undefined) { await writeExport(filepath, result) }