Files
Brit 410e6e774e Merge PR #16873: feat(coding-agent): open local:// files in external editor via /local and Alt+G (@Shadorain)
feat(coding-agent): open local:// files in external editor via /local and Alt+G
2026-10-26 10:05:34 +01:00

21 KiB
Raw Permalink Blame History

Keybindings

Run /hotkeys inside an omp session for the built-in shortcut reference. Application-action rows reflect remaps loaded from disk; navigation/editing examples use fixed chords. This is not an inventory of extension-registered shortcuts.

Base editor, input, and selector actions use tui.editor.*, tui.input.*, and tui.select.*; application actions use app.*. Definitions live in packages/tui/src/keybindings.ts and packages/tui/src/app-keybindings.ts.

Customize keybindings

User remaps live in ~/.omp/agent/keybindings.yml. The file is a YAML mapping whose keys are keybinding action IDs and whose values are either one chord string or an array of chord strings. It is not read from ~/.omp/agent/config.yml, and there is no nested keybindings object.

With a named profile, bindings from the default profile's agent directory are loaded first and the active profile's keybindings.yml overrides them action by action. The inherited file is read-only during that profile's startup.

app.model.cycleForward: Ctrl+P
app.model.selectTemporary: Alt+P
app.plan.toggle: Alt+Shift+P

Chord names are case-insensitive and use the same notation shown in the UI, such as Ctrl+P, Alt+Shift+P, Shift+Enter, and Ctrl+Backspace.

Set an action to an empty array to disable it:

app.history.search: []

Common action IDs

Action ID Default Meaning
app.model.cycleForward Ctrl+P Cycle role models forward
app.model.cycleBackward Shift+Ctrl+P Cycle role models backward
app.model.selectTemporary Alt+P Pick a model temporarily for this session
app.model.select Alt+M Open the model selector and set roles
app.plan.toggle Alt+Shift+P Toggle plan mode
app.history.search Ctrl+R Search prompt history
app.tools.expand Ctrl+O Toggle tool-output expansion
app.tools.toggleVisibility Ctrl+Shift+O Show or hide tool activity
app.ask.hide Alt+H Hide a pending ask question to one line (the Hide button in Tern); the same key, Enter, or Show brings it back
app.approval.approve Ctrl+Enter, Ctrl+Q Approve a pending tool call outright (the Approve button in Tern); Esc denies, and Enter takes the Approve/Deny choice highlighted with the arrows
app.thinking.toggle Ctrl+T Toggle thinking-block visibility
app.thinking.cycle Shift+Tab Cycle thinking level
app.editor.external Ctrl+G Edit the draft in $VISUAL / $EDITOR
app.local.open Alt+G Pick a session local:// file and edit it in $VISUAL / $EDITOR (same as /local)
app.message.followUp Ctrl+Q, Ctrl+Enter Queue a follow-up message
app.message.dequeue Alt+Up, Shift+Up Dequeue a queued message back into the editor
app.retry F5, Alt+R Retry the last failed assistant turn
app.display.reset Alt+L Reset terminal display
app.clipboard.copyLine Alt+Shift+L Copy the current line
app.clipboard.copyPrompt Alt+Shift+C Copy the whole prompt
app.clipboard.pasteTextRaw Ctrl+Shift+V, Alt+Shift+V Paste clipboard text without collapsing it
app.clipboard.pasteImage Linux: Ctrl+V; macOS: Ctrl+V, Cmd+V; Windows: Ctrl+V, Alt+V Paste from the clipboard (image preferred, text fallback)
app.stt.pushToTalk Space Hold to record and release to transcribe. Remap this action or set it to [] to disable push-to-talk without disabling speech-to-text.
app.stt.toggle Unbound Start or stop speech-to-text recording with each press; independent of push-to-talk.
app.live.toggle Ctrl+L Start or stop live voice mode (same as /live)
app.agents.hub Alt+A Open the Agent Hub
app.tree.previousBranch Unbound Jump to the previous visible branch in /tree (details)
app.tree.nextBranch Unbound Jump to the next visible branch in /tree (details)

To disable push-to-talk while keeping speech-to-text available through a separate toggle binding:

app.stt.pushToTalk: []
app.stt.toggle: Ctrl+Shift+S

While speech-to-text is enabled, non-printable keys and chords assigned to app.stt.pushToTalk are reserved: a tap is swallowed instead of running its normal editing or application action. A plain printable key tap still types normally. Text-assistance transformations from initial taps are preserved; only unchanged literal repeat text is removed when a hold is recognized. Each configured alternative is independent; alternating between alternatives does not combine them into one hold. Hold detection requires a terminal that delivers key auto-repeat.

Click to dictate in Tern

With stt.enabled on, the native composer shows a microphone beside Send. Click it to start recording without holding a key. While the speech model loads, the button shows Starting…; click it again to cancel. Once it changes to Recording · Stop, click it to stop, then wait for Transcribing… to finish.

Dictation started with this button stays in the draft, regardless of stt.submitTrigger. Review or edit the text, then press Enter or click Send. While dictation is recording or transcribing into the draft, Enter and Send do nothing.

For live English transcription, select Nemotron 0.6B (live streaming, English) in omp setup speech, or set modelRoles.dictation to local/nemotron-streaming-en-0.6b. Words appear in the draft while you speak. The first recording waits for the model to load before opening the microphone; start speaking when the button says Recording · Stop. Later recordings reuse the loaded model. See local speech models for download and language details.

Recover a cleared prompt

Press Ctrl+C to clear an unsent composer draft, then Up to recall it. Older drafts and submitted prompts share the existing Up/Down navigation. Recalled drafts remain editable and are never sent until you submit them.

Cleared drafts preserve whitespace, collapsed pastes, and image attachments in the current editor's bounded history (100 entries). They are not written to persistent prompt history and do not appear in Ctrl+R search. Closing the process discards these canceled drafts; the separate save-on-exit behavior still applies to text currently in the composer. This is not a per-agent stash: history follows the editor, including when Agent Hub changes focus.

The existing double-Ctrl+C exit behavior is unchanged. Empty clears do not add history entries.

Recovery is on by default. Turn off Recall Cleared Drafts in /settings under Interaction > Input, or set composer.recallClearedDrafts: false. This takes effect on the next clear without restarting; previously retained drafts remain in history until evicted or the editor closes.

On Windows Terminal, Ctrl+V may be handled by the terminal paste command before omp sees it; use the Alt+V fallback when clipboard image paste appears to do nothing. When the clipboard holds no image, app.clipboard.pasteImage pastes the clipboard text instead, so hosts that deliver only this chord (VS Code's integrated terminal when configured to forward Ctrl+V, Windows clipboard history via Win+V) work for both payload kinds. Windows Terminal also swallows Ctrl+Enter, so the app.message.followUp chord also binds Ctrl+Q — the same chord GitHub Copilot CLI uses — and the same chord submits the agent dashboard's new-agent description and hook-editor prompts. If your existing keybindings.yml already assigns Ctrl+Q to another action, that user remap wins and follow-up keeps Ctrl+Enter unless you explicitly bind app.message.followUp.

Terminals that implement OSC 5522 enhanced paste can send clipboard MIME data directly to omp; image pastes are attached as [Image #N], while text/plain paste events keep normal paste behavior. When OSC 5522 is unavailable, bracketed paste still handles text, and a pasted single image-file path is loaded as an image when the file is readable from the omp host.

Older unqualified action names are migrated when keybindings.yml is loaded, but new docs and new configs should use the namespaced action IDs above. Existing keybindings.json files are still accepted and migrated to keybindings.yml; keybindings.yaml is also accepted.

Vim editing mode

Off by default. Turn it on with Vim Editing Mode in /settings (Interaction → Input), or set it directly:

tui.vimMode: true

The prompt starts in Insert mode. Escape switches to Normal mode; the prompt border changes color to show the mode (bash/python mode colors take precedence). The software cursor underlines the current character in Insert mode and uses a block in Normal mode; at line end Insert uses a bar, and Visual mode highlights the selection. The terminal cursor shape is set via DECSCUSR to a bar in Insert and a block in Normal/Visual, and restored on exit. This is a useful subset of Vim, not a full implementation.

The status line also shows the mode, pending command, and multi-line Visual selection size. Vim Mode Indicator (tui.vimModeDisplay) selects text (default), icon, or none (hides the whole Vim status segment).

Mode Enter with Leave with
Insert i a I A o O Escape
Normal Escape from Insert any Insert-mode key
Visual v Escape, or an operator (y d c)
Visual line V Escape, or an operator (y d c)

Normal mode

Keys Meaning
h j k l Move by character and line (arrow keys work too)
0 ^ $ Line start / first non-blank / line end
w b e Next word, previous word, end of word
f F t T + char Find / till that character on the line (; repeats, , reverses)
gg G First line, last line (5gg and 5G jump to line 5)
1–9 prefix Repeat a motion or operator, e.g. 3w, 5j, 2dd
i a I A Insert before / after cursor, at line start / line end
o O Open a line below / above and insert
x D C Delete character, delete to line end (2D takes count lines), change to line end (2C likewise)
r + char Replace the character under the cursor (3rx replaces three)
R Replace mode: typed characters overwrite until Escape
% Jump to the matching () [] {} " ' on or after the cursor. Quotes stay on the line; brackets can cross lines. With a count, 50% goes to that percentage of the buffer.
{ } Previous / next paragraph (blank-line boundary)
J Join lines, dropping the indent of the following line (an empty line joins without a space)
>> << Indent / outdent by two spaces (3>> shifts three lines; >} shifts a motion). Empty lines are left alone.
d y c + motion Operate over a motion, e.g. dw, d$, yb, cw
dd yy cc Linewise delete / yank / change
d y c + text object Operate over a text object, e.g. diw, ca(, ci", dap
p P Put the last yank or delete after / before the cursor
u Undo (a whole Insert session, or a whole ., undoes in one step)
. Repeat the last change, including text typed in the Insert session it opened; a count replaces the original one (3.)

Text objects

A text object follows an operator (diw) or extends a Visual selection (viw). i takes the inside, a takes the surroundings; counts apply, e.g. d2aw.

Object Covers
iw aw Word; aw also takes the adjoining whitespace
iW aW Whitespace-delimited WORD
i" i' i` Inside the quotes on the current line (a" takes the quotes too)
i( i[ i{ i< Inside the innermost matching pair, nesting-aware and across lines
a( a[ a{ a< The same pair including its delimiters (b and B alias ( and {)
ip ap Paragraph — the run of non-blank (or blank) lines, linewise

Visual mode

v starts a character-wise selection and V a line-wise one; motions move the free end. y copies the selection to the system clipboard (and to the internal register, so p puts it back), d deletes it, and c deletes it and drops into Insert mode. x deletes like d, and s changes like c. o jumps to the other end of the selection. Escape cancels.

A selection that would cut through an attachment placeholder such as [Image #1, 800x600] or [Paste #2, +30 lines] takes the whole placeholder with it, so a delete can never leave a corrupt fragment behind.

Escape

Escape is shared with the app-level interrupt, so Vim mode takes it only when it has something to do:

  • Insert mode → switch to Normal mode (an open autocomplete popup gets the first Escape to dismiss it).
  • Visual mode, or a half-typed count or operator → cancel back to a quiet Normal mode.
  • Replace mode (R) → switch to Normal mode, stepping back onto the last overwritten character.
  • Normal mode with nothing pending → falls through to app behavior: dismiss autocomplete, abort active work, or run the configured double-Escape action with an empty editor. An idle main-session draft is preserved; bash/python and focused-agent views have their own Escape behavior.

Vim keys never shadow app chords: Ctrl-combinations, Enter, and Tab keep their normal behavior in every mode, so Enter still submits from Normal mode. Such a key also cancels a half-typed command (d, r, f) first, so the command never carries over into the next prompt. Prompt history stays on Up/Down in Insert mode only — in Normal mode those keys are k and j, so navigating a multi-line draft never loads a previous prompt.