fix: prefer vim over nano for $EDITOR fallback (CLI + TUI)

prompt_toolkit's default editor list is: $VISUAL, $EDITOR, /usr/bin/editor,
/usr/bin/nano, /usr/bin/pico, /usr/bin/vi, /usr/bin/emacs — so when
neither env var is set, the base CLI launched nano. The TUI fell back
to a literal 'vi'. Same Ctrl+G keystroke, two different editors.

Pick the same chain on both surfaces:
  $VISUAL → $EDITOR → vim → vi → nano

CLI: override input_area.buffer._open_file_in_editor on the TextArea
once at app build time. Local to that buffer; doesn't touch
os.environ or affect other subprocesses.

TUI: extract resolveEditor() into ui-tui/src/lib/editor.ts. PATH walk
with accessSync(X_OK), no shelling out. Six-line unit test verifies
the priority order and the multi-entry PATH walk.
This commit is contained in:
Brooklyn Nicholson 2026-04-25 20:11:25 -05:00
parent 5fac6c3440
commit db7c5735f0
4 changed files with 128 additions and 1 deletions

View file

@ -14,6 +14,7 @@ import { useCompletion } from '../hooks/useCompletion.js'
import { useInputHistory } from '../hooks/useInputHistory.js'
import { useQueue } from '../hooks/useQueue.js'
import { isUsableClipboardText, readClipboardText } from '../lib/clipboard.js'
import { resolveEditor } from '../lib/editor.js'
import { readOsc52Clipboard } from '../lib/osc52.js'
import { isRemoteShellSession } from '../lib/terminalSetup.js'
import { pasteTokenLabel, stripTrailingPasteNewlines } from '../lib/text.js'
@ -254,7 +255,7 @@ export function useComposerState({
)
const openEditor = useCallback(async () => {
const editor = process.env.EDITOR || process.env.VISUAL || 'vi'
const editor = resolveEditor()
const file = join(mkdtempSync(join(tmpdir(), 'hermes-')), 'prompt.md')
let code: null | number = null