Skip to content

feat(cli): add vim-style j/k navigation to list selectors#2673

Merged
cpacker merged 1 commit into
letta-ai:mainfrom
sanidhyasin:feat/vim-jk-selector-navigation
Jun 7, 2026
Merged

feat(cli): add vim-style j/k navigation to list selectors#2673
cpacker merged 1 commit into
letta-ai:mainfrom
sanidhyasin:feat/vim-jk-selector-navigation

Conversation

@sanidhyasin

Copy link
Copy Markdown
Contributor

Summary

Closes #2063.

Adds vim-style j/k as aliases for the down/up arrow keys in CLI list selectors, so users with vim muscle memory (or set -o vi shells) can navigate without reaching for the arrow keys.

This follows the convention already present in the codebase — PinDialog already binds j/k to down/up (input === "j" || key.downArrow). This change extends the same pattern to the shared pickers and the /resume conversation selector.

Changes

  • SingleSelectPicker — the shared single-select base picker (used by PersonalitySelector, CompactionSelector, WorktreeDiffSelector, SystemPromptSelector). Also updated its default footer hint to ↑↓/jk navigate.
  • MultiSelectPicker — the shared multi-select base picker.
  • ConversationSelector — the /resume conversation picker (explicitly called out in the issue).

In all three, k moves the cursor up and j moves it down, matching vim semantics.

Scope / why these components

The bindings are added only to pure-list selectors that have no inline text field, so a bare j/k keypress can never clash with typing.

Deliberately not changed:

  • Filter-based selectors (AgentSelector, ProviderSelector, ModelSelector, SleeptimeSelector, etc.) accept free-text search input. Binding bare letters there would swallow legitimate typing — agent-selector-shortcuts.test.ts documents this exact constraint (delete is Shift+D precisely so lowercase d stays typeable).
  • Autocomplete dropdowns (useAutocompleteNavigation) are active while the user is typing in the prompt, so j/k must remain literal characters there.

This keeps the change small, safe, and free of typing regressions. The remaining typing-free selectors can be migrated in follow-ups using the same pattern.

Tests

Added src/cli/components/vim-navigation.test.ts, which asserts the j/k → down/up bindings are wired in the three pure-list pickers (source-assertion style, matching the existing agent-selector-shortcuts.test.ts).

bun test src/cli/components/vim-navigation.test.ts   # 3 pass

Also verified locally: bun run typecheck, biome lint, layer-boundary / exported-function / filename-casing checks, and the existing conversation-selector + agent-selector-shortcuts suites all pass.

Bind "j"/"k" as aliases for the down/up arrows in the shared list
pickers (SingleSelectPicker, MultiSelectPicker) and the conversation
resume picker (ConversationSelector), matching the convention already
used in PinDialog.

These are pure-list selectors with no inline text field, so binding
bare letters cannot clash with typing. Filter-based selectors such as
AgentSelector and ProviderSelector are intentionally left untouched —
they accept free-text search where "j"/"k" must remain typeable (see
agent-selector-shortcuts.test.ts), and the typing-while-open
autocomplete dropdowns are excluded for the same reason.

Closes letta-ai#2063
@cpacker cpacker merged commit 14f7411 into letta-ai:main Jun 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: vim-style j/k navigation in CLI selectors

2 participants