Keybindings Skill
Create or modify ~/.claude/keybindings.json to customize keyboard shortcuts.
CRITICAL: Read Before Write
Always read ~/.claude/keybindings.json first (it may not exist yet). Merge changes with existing bindings — never replace the entire file.
- Use Edit tool for modifications to existing files
- Use Write tool only if the file does not exist yet
File Format
{
"$schema": "https://www.schemastore.org/claude-code-keybindings.json",
"$docs": "https://code.claude.com/docs/en/keybindings",
"bindings": [
{
"context": "Chat",
"bindings": {
"ctrl+e": "chat:externalEditor"
}
}
]
}
Always include the $schema and $docs fields.
Keystroke Syntax
Modifiers (combine with +):
ctrl (alias: control)
alt (aliases: opt, option) — note: alt and meta are identical in terminals
shift
meta — same key as alt in terminals (Option key on macOS)
cmd (aliases: command, super, win) — Command key on macOS, Windows key on Windows, Super key on Linux; not the same as meta. Most terminals never send it (only ones that report the Super modifier, such as through the Kitty keyboard protocol or xterm modifyOtherKeys), so prefer ctrl for bindings that should work everywhere
Special keys: escape/esc, enter/return, tab, space, backspace, delete, up, down, left, right
Chords: Space-separated keystrokes, e.g. ctrl+k ctrl+s (3-second timeout between keystrokes)
Examples: ctrl+shift+p, alt+enter, ctrl+k ctrl+n
Unbinding Default Shortcuts
Set a key to null to remove its default binding:
{
"context": "Chat",
"bindings": {
"ctrl+s": null
}
}
How User Bindings Interact with Defaults
- User bindings are additive — they are appended after the default bindings
- To move a binding to a different key: unbind the old key (
null) AND add the new binding
- A context only needs to appear in the user's file if they want to change something in that context
Common Patterns
Rebind a key
To change the external editor shortcut from ctrl+g to ctrl+e:
{
"context": "Chat",
"bindings": {
"ctrl+g": null,
"ctrl+e": "chat:externalEditor"
}
}
Add a chord binding
{
"context": "Global",
"bindings": {
"ctrl+k ctrl+t": "app:toggleTodos"
}
}
Behavioral Rules
- Only include contexts the user wants to change (minimal overrides)
- Validate that actions and contexts are from the known lists below
- Warn the user proactively if they choose a key that conflicts with reserved shortcuts or common tools like tmux (
ctrl+b) and screen (ctrl+a)
- When adding a new binding for an existing action, the new binding is additive (existing default still works unless explicitly unbound)
- To fully replace a default binding, unbind the old key AND add the new one
Validation
Claude Code validates ~/.claude/keybindings.json when it loads; warnings go to the debug log. After editing the file, re-check it against the rules below and fix anything that matches.
Common Issues and Fixes
| Issue | Cause | Fix |
|---|
keybindings.json must have a "bindings" array | Missing wrapper object | Wrap bindings in { "bindings": [...] } |
"bindings" must be an array | bindings is not an array | Set "bindings" to an array: [{ context: ..., bindings: ... }] |
Unknown context "X" | Typo or invalid context name | Use exact context names from the Available Contexts table |
"X" is not a modifier, so "Y" ... applies to "Z" instead | Error: X comes before the key in Y but is not a modifier (a typo such as ctl for ctrl, or two keys joined with + instead of a space), so it is dropped and the binding applies to Z | Correct the modifier using the Keystroke Syntax list (the message suggests the corrected keystroke when it can), or put a space between the keystrokes of a chord |
Duplicate key "X" in Y bindings | Same key defined twice in one context | Remove the duplicate; JSON uses only the last value |
"X" may not work: ... | Key conflicts with terminal/OS reserved shortcut | Choose a different key (see Reserved Shortcuts section) |
Invalid action for "X" | Action value is not a string or null | Actions must be strings like "app:help" or null to unbind |
Example validation warnings (debug log)
[keybindings] Found 2 validation issue(s)
[keybindings] [error] Unknown context "chat" — Valid contexts: Global, Chat, Autocomplete, ...
[keybindings] [warning] "ctrl+c" may not work: Terminal interrupt (SIGINT)
Errors prevent bindings from working and must be fixed. Warnings indicate potential conflicts but the binding may still work.
Reserved Shortcuts
Non-rebindable (errors)
ctrl+c — Cannot be rebound - used for interrupt/exit (hardcoded)
ctrl+d — Cannot be rebound - used for exit (hardcoded)
ctrl+m — Cannot be rebound - identical to Enter in terminals (both send CR)
ctrl+[ — Cannot be rebound - identical to Escape in terminals
ctrl+i — Cannot be rebound - identical to Tab in terminals
ctrl+h — Cannot be rebound - identical to Backspace in terminals
capslock — Caps Lock is not delivered to terminal applications
Terminal reserved (errors/warnings)
ctrl+z — Unix process suspend (SIGTSTP) (may conflict)
ctrl+\ — Terminal quit signal (SIGQUIT) (will not work)
macOS reserved (errors)
cmd+c — macOS system copy
cmd+v — macOS system paste
cmd+x — macOS system cut
cmd+q — macOS quit application
cmd+w — macOS close window/tab
cmd+tab — macOS app switcher
cmd+space — macOS Spotlight
Available Contexts
| Context | Description |
|---|
Global | Active everywhere, regardless of focus |
Chat | When the chat input is focused |
Autocomplete | When autocomplete menu is visible |
Confirmation | When a confirmation/permission dialog is shown |
Help | When the help overlay is open |
Transcript | When viewing the transcript |
HistorySearch | When searching command history (ctrl+r) |
Task | When a task/agent is running in the foreground |
ThemePicker | When the theme picker is open |
Settings | When the settings menu is open |
Tabs | When tab navigation is active |
Attachments | When navigating image attachments in a select dialog |
Footer | When footer indicators are focused |
AbovePrompt | When a plugin's panel above the prompt, or a button in it, has keyboard focus |
AbovePromptInput | When a plugin's input field above the prompt has keyboard focus |
AbovePromptSelect | When a plugin's select above the prompt has keyboard focus |
Pane | When a plugin's pane has keyboard focus |
PaneField | When an input field or select in a plugin's pane has keyboard focus |
MessageSelector | When the message selector (rewind) is open |
DiffDialog | When the diff dialog is open |
DiffPanel | When the diff sidebar panel is open |
ModelPicker | When the model picker is open |
EffortSlider | When the effort slider is open |
Select | When a select/list component is focused |
Plugin | When the plugin dialog is open |
Scroll | When a scrollable view is focused (fullscreen layout) |
Agents | When the agents view (claude agents) is open |
Available Actions
| Action | Default Key(s) | Context |
|---|
app:interrupt | ctrl+c | Global |
app:exit | ctrl+d | Global |
app:toggleTodos | ctrl+t | Global |
app:toggleTranscript | ctrl+o | Global |
app:toggleBrief | ctrl+shift+b | Global |
app:toggleReplTab | (none) | Global |
app:toggleDiffNoiseFilter | (none) | Global |
app:diffFileListUp | ctrl+up, meta+up | Global |
app:diffFileListDown | ctrl+down, meta+down | Global |
app:toggleDiffPreSession | (none) | Global |
app:cycleDiffBase | ctrl+x b | DiffPanel |
app:toggleTerminal | (none) | Global |
app:redraw | (none) | Global |
app:openArtifact | ctrl+] | Global |
history:search | ctrl+r | Global |
history:previous | up | Chat |
history:next | down | Chat |
chat:cancel | escape | Chat |
chat:killAgents | ctrl+x ctrl+k | Chat |
chat:cycleMode | shift+tab | Chat |
chat:modelPicker | meta+p | Chat |
chat:fastMode | meta+o | Chat |
chat:defaultToNewerModel | ctrl+y | Chat |
chat:thinkingToggle | meta+t | Chat |
chat:workflowKeywordToggle | meta+w | Chat |
chat:submit | enter | Chat |
chat:queueSubmit | ctrl+x enter | Chat |
chat:sendNow | ctrl+x ctrl+s, ctrl+enter | Chat |
chat:newline | ctrl+j | Chat |
chat:undo | ctrl+_, ctrl+-, ctrl+shift+-, ctrl+shift+_ | Chat |
chat:externalEditor | ctrl+x ctrl+e, ctrl+g | Chat |
chat:stash | ctrl+s | Chat |
chat:imagePaste | ctrl+v | Chat |
chat:clearInput | ctrl+l | Chat |
chat:clearScreen | cmd+k | Chat |
autocomplete:accept | tab | Autocomplete |
autocomplete:dismiss | escape | Autocomplete |
autocomplete:previous | up | Autocomplete |
autocomplete:next | down | Autocomplete |
confirm:yes | enter | Confirmation |
confirm:no | escape, escape | Settings |
confirm:previous | up | Confirmation |
confirm:next | down | Confirmation |
confirm:nextField | tab | Confirmation |
confirm:previousField | (none) | Confirmation |
confirm:cycleMode | shift+tab | Confirmation |
confirm:toggle | space | Confirmation |
tabs:next | tab, right | Tabs |
tabs:previous | shift+tab, left | Tabs |
transcript:toggleShowAll | ctrl+e | Transcript |
transcript:exit | ctrl+c, escape, q | Transcript |
historySearch:next | ctrl+r | HistorySearch |
historySearch:accept | escape, tab | HistorySearch |
historySearch:cancel | ctrl+c | HistorySearch |
historySearch:execute | enter | HistorySearch |
historySearch:cycleScope | ctrl+s | HistorySearch |
task:background | ctrl+x ctrl+b, ctrl+b | Task |
theme:toggleSyntaxHighlighting | ctrl+t | ThemePicker |
theme:editCustom | ctrl+e | ThemePicker |
help:dismiss | escape | Help |
attachments:next | right | Attachments |
attachments:previous | left | Attachments |
attachments:remove | backspace, delete | Attachments |
attachments:exit | down, escape | Attachments |
footer:up | up, ctrl+p | Footer |
footer:down | down, ctrl+n | Footer |
footer:next | right | Footer |
footer:previous | left | Footer |
footer:openSelected | enter | Footer |
footer:clearSelection | escape | Footer |
footer:close | x | Footer |
footer:dismiss | (none) | Footer |
abovePrompt:toggle | ctrl+x ctrl+a | Chat |
abovePrompt:focus | ctrl+x tab | Chat |
abovePrompt:next | tab, right, tab, down, tab, tab | AbovePrompt |
abovePrompt:previous | shift+tab, left, shift+tab, up, shift+tab, shift+tab | AbovePrompt |
abovePrompt:press | enter, space, enter, enter, enter | AbovePrompt |
abovePrompt:leave | escape, escape, escape, escape | AbovePrompt |
abovePrompt:highlightNext | down | AbovePromptSelect |
abovePrompt:highlightPrevious | up | AbovePromptSelect |
pane:scrollUp | up, up | AbovePrompt |
pane:scrollDown | down, down | AbovePrompt |
pane:pageUp | pageup, pageup | AbovePrompt |
pane:pageDown | pagedown, pagedown | AbovePrompt |
pane:top | home, home | AbovePrompt |
pane:bottom | end, end | AbovePrompt |
pane:grow | ctrl+x left, ctrl+x up | Pane |
pane:shrink | ctrl+x right, ctrl+x down | Pane |
pane:close | ctrl+x x, ctrl+x x | Pane |
pane:next | (none) | Pane |
pane:previous | (none) | Pane |
diff:dismiss | escape | DiffDialog |
diff:previousSource | left | DiffDialog |
diff:nextSource | right | DiffDialog |
diff:back | (none) | DiffDialog |
diff:previousFile | up, k | DiffDialog |
diff:nextFile | down, j | DiffDialog |
modelPicker:decreaseEffort | left | ModelPicker |
modelPicker:increaseEffort | right | ModelPicker |
modelPicker:thisSessionOnly | s | ModelPicker |
effortSlider:decreaseEffort | left | EffortSlider |
effortSlider:increaseEffort | right | EffortSlider |
effortSlider:toggleUltracode | tab | EffortSlider |
effortSlider:thisSessionOnly | s | EffortSlider |
select:next | down, j, ctrl+n, down, j, ctrl+n | Settings |
select:previous | up, k, ctrl+p, up, k, ctrl+p | Settings |
select:pageUp | pageup, pageup | Settings |
select:pageDown | pagedown, pagedown | Settings |
select:first | home, home | Settings |
select:last | end, end | Settings |
select:accept | space, enter, enter | Settings |
select:cancel | escape | Select |
plugin:toggle | space | Plugin |
plugin:install | i | Plugin |
plugin:favorite | f | Plugin |
plugin:cycleMarketplace | ctrl+s | Plugin |
permission:toggleDebug | (none) | Confirmation |
settings:search | / | Settings |
settings:retry | r | Settings |
settings:periodDay | d | Settings |
settings:periodWeek | w | Settings |
settings:sortByTokens | t | Settings |
voice:pushToTalk | space | Chat |
scroll:pageUp | pageup, pageup | Scroll |
scroll:pageDown | pagedown, pagedown | Scroll |
scroll:lineUp | ctrl+p, k, up, wheelup | Transcript |
scroll:lineDown | ctrl+n, j, down, wheeldown | Transcript |
scroll:top | g, home, ctrl+home, g, home | Transcript |
scroll:bottom | shift+g, end, ctrl+end, shift+g, end | Transcript |
scroll:halfPageUp | ctrl+u, ctrl+u | Settings |
scroll:halfPageDown | ctrl+d, ctrl+d | Settings |
scroll:fullPageUp | ctrl+b, b, shift+space, b | Transcript |
scroll:fullPageDown | ctrl+f, space, space | Transcript |
selection:copy | ctrl+shift+c, cmd+c | Scroll |
selection:clear | (none) | Unknown |
selection:extendLeft | shift+left | Scroll |
selection:extendRight | shift+right | Scroll |
selection:extendUp | shift+up | Scroll |
selection:extendDown | shift+down | Scroll |
selection:extendLineStart | shift+home | Scroll |
selection:extendLineEnd | shift+end | Scroll |
agents:switchView | ctrl+s | Agents |
agents:togglePin | ctrl+t | Agents |
agents:find | ctrl+f | Agents |
agents:nextGroup | ctrl+down, meta+down | Agents |
agents:previousGroup | ctrl+up, meta+up | Agents |
agents:rename | ctrl+r | Agents |