Skip to content
capydev42Public

About

Turn your shell history into a searchable command palette

Topics

Resources

Stars

17 stars

Watchers

0 watching

Forks

Repository files navigation

ctrlr

CI GitHub release crates.io License Platform

Turn your shell history into a searchable command palette
Stop googling commands you already used.

ctrlr demo


Install

curl -fsSL https://github.com/capydev42/ctrlr/releases/latest/download/install.sh -o install.sh && chmod +x install.sh && ./install.sh

The script asks where to install, downloads the binary for your platform and verifies it against the release checksums.

Windows, in PowerShell:

irm https://github.com/capydev42/ctrlr/releases/latest/download/install.ps1 | iex

Verifies the download against the release checksums, then adds itself to your user PATH — which applies to new shells, not the one you ran it in.

Homebrew (macOS and Linux):

brew install capydev42/ctrlr/ctrlr

Cargo — builds from source, needs Rust 1.88 or newer and a C compiler:

cargo install ctrlr
Other ways to install

A specific version:

curl -fsSL https://github.com/capydev42/ctrlr/releases/download/v0.11.4/install.sh -o install.sh && chmod +x install.sh && ./install.sh

A fixed directory, no prompt:

INSTALL_DIR=/usr/local/bin ./install.sh

From a release archive: download from the releases page, then

tar -xzf ctrlr-x86_64-unknown-linux-gnu.tar.gz
mv ctrlr ~/.local/bin/   # or /usr/local/bin/

From source (needs Rust 1.88 or newer):

git clone https://github.com/capydev42/ctrlr.git
cd ctrlr
cargo build --release
mv target/release/ctrlr ~/.local/bin/

Quick start

Open the command palette:

ctrlr

Type to search, Enter to put the command on your prompt line. ? shows every shortcut.

Then bind it to Ctrl+R, replacing your shell's own reverse search:

ctrlr init

Restart your shell and Ctrl+R opens ctrlr. The same integration is what lets ctrlr know where your commands ran — see How it works. ctrlr also offers to install it for you the first time you run it, and backs your config up to <config>.ctrlr.bak before touching it.

Works with bash, zsh and fish on Linux and macOS, and with PowerShell on Windows. PowerShell Core on Linux and macOS works too.

Git Bash and MSYS on Windows are not supported: ctrlr is a native Windows process there while the shell thinks in /c/... paths, so the two never agree on a directory.


Features

Search that knows what you meant

  • Fuzzy search over your whole history — gp o finds git push origin main
  • Tags are searched too, so a command you tagged deploy shows up under deploy
  • Results are ranked, not just filtered: favorites first, then commands you have run in this directory, then recent ones, then frequent ones

Directory-aware history

  • ctrlr records which directory each command ran in, and how it exited
  • . scopes the list to the current directory — the rest of your history steps aside while you work in a repo
  • The details pane shows how often a command ran, how often it ran here, its last exit code, and the directories it runs in most

Organize what you keep

  • f favorites a command, t tags it, c puts it in a collection
  • Collections are named groups you create, rename and delete from the TUI (3)
  • Commands added to a collection stick around even after they age out of your shell history

Made to live in

  • 4 Catppuccin flavors (Latte, Frappé, Macchiato, Mocha) with a live-preview picker on Ctrl+T, remembered across launches
  • Panes resize with < / > or by dragging their border, and the width is remembered; d hides the details pane entirely
  • Full mouse support — click, double-click to run, right-click for a context menu, wheel to scroll
  • Keyboard-first everywhere; vim keys where you expect them

Yours to keep

  • Everything is local SQLite — no account, no network, no telemetry
  • Import and export as JSON from the CLI or from the TUI (Ctrl+E / Ctrl+O), with a preview before anything is written

How it works

ctrlr never executes anything itself. It reads your shell's history file, shows you a picker, and hands the command you chose back to the shell, which puts it on your prompt line. Nothing runs until you press Enter yourself. That holds for edited commands too. Ctrl+x opens the selected command in $VISUAL / $EDITOR and puts whatever you save on the prompt line. e instead opens a one-line editor inside ctrlr, and Ctrl+x from there is the same detour through $EDITOR, coming back to the line so you can look before you commit. Either way, nothing runs on its own.

An edited command is not written to ctrlr's database. The original keeps its favorites, tags and run count, because the original is not what ran. Your edited version shows up in the list on the next launch, the same way every other command does — once your shell has actually run it.

Metadata — favorites, tags, collections, run counts — lives in a local SQLite database. Your history file stays the source of truth for the command text.

Where commands ran. No shell writes the working directory into its history file, so it cannot be recovered after the fact. The integration installed by ctrlr init appends one line per command to runs.log in ctrlr's data directory (see below) using a shell builtin — no process is spawned per prompt — and ctrlr drains that log on launch. This only covers commands run after the integration is installed; existing history has no directories attached to it.

On zsh and fish the directory recorded is the one the command was typed in. On bash the same holds when bash-preexec is loaded — starship and atuin both bring it. Without it, ctrlr falls back to logging at prompt time, so a cd is recorded against the directory it moved to.

PowerShell is exact through PSReadLine's AddToHistoryHandler, which runs before the command does. Very old PSReadLine versions (1.x, shipped with the original Windows 10) have no such hook and fall back to prompt time, the same way bash does. Commands PSReadLine judges to hold a secret are kept out of its history file, and ctrlr does not log those either.

If ctrlr tells you the installed integration is outdated, re-run ctrlr init: the old block still binds Ctrl+R, but features it has no hooks for stay silently empty.


Keybindings

Press ? (or F1) inside ctrlr for a searchable version of this table.

Navigation

Key Action
j / ↓ Move down
k / ↑ Move up
Ctrl+d / PageDown Page down (~half a screen)
Ctrl+u / PageUp Page up (~half a screen)
gg Jump to the top
G Jump to the bottom

Actions

Key Action
Enter Put the selected command on the prompt line
e Edit the command before running it
Ctrl+x Open the command in $VISUAL / $EDITOR and run what you save
f Toggle favorite
y Copy to clipboard
t Edit tags
c Add to a collection
d Show / hide the details pane
/ Focus the search bar
Ctrl+u Clear the search (when the search bar has focus)
Ctrl+t Theme picker
Ctrl+e Export popup
Ctrl+o Import popup
? / F1 Help
Ctrl+g Rebind keys
Esc / Ctrl+C Clear / close / exit

Rebinding

Press Ctrl+G. The list shows every action with the keys that reach it; type to filter, then on a row:

Key Does
Enter Replace the keys with the one you press next
Ctrl+a Add another key, keeping the ones it has
Ctrl+d Remove a key — press the one you want gone
Ctrl+r Put every binding back to its default

Replace, add and remove all name the key by having you press it, so an action holding several keys needs no extra picking. A key another action already owns is not taken silently: the first press says what it would displace, the second confirms.

Changes take effect at once and are written to ~/.config/ctrlr/config.toml when you close the popup — the file and its directory are created for you.

Two things the popup cannot do. Esc cancels a recording, so Esc itself has to be rebound in the file. Two-key sequences like g g are shown but marked (file only), because recording reads a single press. If ctrlr overwrites a config you had written by hand, your version is kept as config.toml.ctrlr.bak.

Or edit the file directly. Start from the defaults:

mkdir -p ~/.config/ctrlr
ctrlr config --print > ~/.config/ctrlr/config.toml
[keys.history]
toggle_favorite = "v"
copy_to_clipboard = ["y", "ctrl+y"]   # an action can hold several keys

[keys.global]
go_to_top = "g g"                     # a space makes a two-key sequence
show_help = []                        # an empty list unbinds

Listing an action replaces its default keys, so you can drop one you dislike. Anything you leave out keeps its default. Contexts are global, search, history, collections_list, collection_items, help, tag_input, collection_input, import_export, theme_popup, context_menu, integration_popup, edit_command and keybind_popup; ctrlr config --print lists them all with every action name.

An action may hold as many keys as you like — give it a list. The first one is what the footer and the help popup advertise, so put the one you think of as primary first.

Modifiers are ctrl, alt and shift. Named keys are enter, esc, tab, space, backspace, delete, insert, home, end, pageup, pagedown, up, down, left, right and f1–f12. Anything else one character long is that character, and case matters — g and G are different keys.

One thing to know: a plain character always types when the search bar has focus. That is why d and ? do nothing special there, and why a letter you bind only fires from the other panes. Use a modifier if you want it everywhere.

A key that another action already owns in the same context is taken from it. A line ctrlr cannot read — a bad key name, an action that does not exist, broken TOML — keeps its default and is listed under Config at the top of the help popup. ctrlr always starts.

Views

Key Action
1 / 2 / 3 History / Favorites / Collections
. Scope the list to the current directory
Alt+1 Alt+2 Alt+3 Alt+. The same, from any pane

Panes

Key Action
Tab Cycle panes
Ctrl+h/j/k/l Focus the pane left / below / above / right
< / > Narrow / widen the details pane, or the collections pane when it has focus
Alt+< / Alt+> The same, from any pane

Collections view

Key Action
n New collection
e Rename collection
d Delete collection
a Search commands to add
r Remove command from collection

1, 2, 3, ., <, > and ? type into the search bar when it has focus — that is what the Alt+ variants are for.

Mouse

Input Action
Click Select a row, switch tab, focus the search bar
Double-click Run the command
Right-click a command Context menu: Run, Copy, Favorite, Tag, Add to / Remove from collection
Right-click the collections pane Open, Rename, Delete, New collection
Drag a pane border Resize it; drag past the minimum to hide the details pane
Wheel Scroll the list under the pointer
Click outside a popup Close it
Shift + drag Your terminal's own text selection

Both context menus are keyboard-driven too (j / k, Enter, Esc). ctrlr holds the mouse for as long as it runs, which is why selecting text needs Shift held.


Import & export

From inside the TUI: Ctrl+E opens the export popup, Ctrl+O the import popup — type a path, and import shows you a preview (new commands, new collections, duplicates) before writing anything.

From the CLI:

ctrlr export                        # print JSON to stdout
ctrlr export backup.json            # write to a file

ctrlr import backup.json            # merge (default)
ctrlr import backup.json --dry-run  # preview, change nothing
ctrlr import backup.json --replace  # wipe everything first (asks y/N)
  • Merge adds new commands, skips duplicates, and merges tags and collections
  • Replace deletes all existing data before importing
  • Dry run prints what would happen and exits

The format is versioned JSON, so an export from an older ctrlr still imports.


Storage

Local SQLite. Command metadata (favorites, use counts), tags, collections, run records and your theme.

  • Linux: ~/.local/share/ctrlr/ctrlr.db
  • macOS: ~/Library/Application Support/ctrlr/ctrlr.db
  • Windows: %LOCALAPPDATA%\ctrlr\ctrlr.db — Local, not Roaming, so a domain profile does not copy your history to a network share at logoff

The run log lives next to it as runs.log. If the database cannot be opened, ctrlr still runs — read-only, straight off your shell history.


Why ctrlr?

Default shell history search is linear, hard to navigate, and impossible to organize. You either remember the command or you go and google it again.

ctrlr gives you fuzzy search over everything you have ever run, ranked by what you actually use and where you are standing, plus somewhere to put the commands worth keeping.

I wasted a lot of time re-googling commands I had already used.


Roadmap

  • Fuzzy search
  • Favorites & tags
  • Collections
  • Import/export (CLI)
  • Import/export (TUI)
  • Better ranking (recency + frequency)
  • Directory-aware history
  • Mouse support & resizable panes
  • Improved collections UX
  • Richer command preview
  • Vim-style navigation
  • Themes with live preview
  • Edit a command before running it
  • Rebindable keys — config.toml and an in-TUI editor (Ctrl+G)
  • Windows and PowerShell support
  • Git Bash and MSYS on Windows

Contributing

Ideas, feedback, and UX suggestions are very welcome.


Built with ❤️

Colors from Catppuccin.

About

Turn your shell history into a searchable command palette

Topics

Resources

Stars

17 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages