Getting started
Shell history TUI for Linux and macOS. Install it, bind it to a key, then filter, select, favorite, and call commands back by name.
Install
One script downloads the latest release for your platform and drops the binary in ~/.local/bin.
curl -fsSL https://raw.githubusercontent.com/PedroElizalde01/hline/main/install.sh | bashPin a version with --version, or change the install directory with --bin-dir.
curl -fsSL https://raw.githubusercontent.com/PedroElizalde01/hline/main/install.sh | \
bash -s -- --version v0.2.3If your shell cannot find hline afterwards, add the install directory to PATH in your profile.
export PATH="$HOME/.local/bin:$PATH"If you have a Rust toolchain, install from crates.io instead. The crate is named hline-tui because hline was taken, the command it installs is still hline.
cargo install hline-tuiBind a key
hline init prints a widget for your shell. The widget opens hline on a key, and whatever you accept with Enter is pasted into your prompt, ready to edit or run. Add one line to your profile and restart the shell.
eval "$(hline init bash)" # ~/.bashrc
eval "$(hline init zsh)" # ~/.zshrc
hline init fish | source # ~/.config/fish/config.fishThe key defaults to Ctrl+R, replacing the built-in reverse search. See Settings to change it.
┌hline─────────────────────────────────────────────────────────────────────────────┐ │> [ ] 2026-08-28 10:02:00 | docker compose up -d │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └──────────────────────────────────────────────────────────────────────────────────┘ sort: Recency (newest-first) | filter: compose up | 23 total | 1 shown | 0 selected /compose up
$ docker compose up -dCommand line
With no arguments hline opens the TUI. It auto-detects the history format and picks the default history file for your shell.
hline # open the TUI
hline --file ~/.zsh_history --format zsh
hline --format fish --file ~/.local/share/fish/fish_history
hline <alias> # print + copy a favorite
hline --list # list favorites
hline --settings # show settings file
hline init bash|zsh|fish # print shell widget
hline --check-updates # check GitHub Releases
hline --no-update-check # skip the daily checkKeys
Vim-style navigation. Press ? inside hline for the same list.
┌hline─────────────────────────────────────────────────────────────────────────────┐ │ [ ] 2026-09-18 08:55:00 | npm run dev │ │ [ ] 2026-09-17 18:45:00 | docker compose down │ │ [x] 2026-09-17 18:40:00 | ssh deploy@prod-1 │ │ [ ] 2026-09-15 09:05:00 | git rebase -i main │ │> [x] 2026-09-15 09:00:00 | git log --oneline -10 │ │ [ ] 2026-09-12 14:31:00 | npx prisma generate │ │ [ ] 2026-09-12 14:30:00 | npm run db:migrate │ │ [ ] 2026-09-12 14:10:00 | psql -h localhost -U app -d app_dev │ │ [ ] 2026-09-09 10:01:00 | kubectl logs -f deploy/api -n staging │ │ [ ] 2026-09-09 10:00:00 | kubectl get pods -n staging │ │ [ ] 2026-09-05 16:25:00 | ./scripts/release_local.sh │ │ [ ] 2026-09-05 16:20:00 | cargo build --release │ │ [ ] 2026-09-02 12:03:00 | git push origin feature/api-auth │ │ [ ] 2026-09-02 12:02:00 | git commit -m "feat: auth middleware" │ │ [ ] 2026-09-02 12:01:00 | git add . │ │ [ ] 2026-09-02 09:15:00 | npm run test │ │ [ ] 2026-09-02 08:30:00 | git checkout -b feature/api-auth │ │ [ ] 2026-08-29 11:41:00 | npm run dev │ │ [ ] 2026-08-29 11:40:00 | npm install │ └──────────────────────────────────────────────────────────────────────────────────┘ sort: Recency (newest-first) | filter: no filter | 23 total | 23 shown | 2 selected
- j / k
- move, arrows also work
- Ctrl+d / Ctrl+u
- half page down / up
- g / G
- top / bottom
- /
- search, Enter confirms, Esc exits
- s / S
- cycle sort mode / reverse direction
- Space
- select current line
- a / c
- select all shown / clear selection
- y / Y
- copy selection / copy current line
- Enter
- accept selection to stdout and quit
- f
- save selection or current line as a favorite
- F
- toggle favorites view
- J / K
- jump favorite to favorite, Shift+Up/Down too
- ?
- help
- q
- quit
Search filters
Search is case-insensitive text matching. It also understands date filters when the history format carries timestamps. since: and until: are aliases for after: and before:.
cargo after:2026-03-01
git on:2026-03-14
deploy before:2026-01-01┌hline─────────────────────────────────────────────────────────────────────────────┐ │> [ ] 2026-09-15 09:05:00 | git rebase -i main │ │ [ ] 2026-09-15 09:00:00 | git log --oneline -10 │ │ [ ] 2026-09-02 12:03:00 | git push origin feature/api-auth │ │ [ ] 2026-09-02 12:02:00 | git commit -m "feat: auth middleware" │ │ [ ] 2026-09-02 12:01:00 | git add . │ │ [ ] 2026-09-02 08:30:00 | git checkout -b feature/api-auth │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └──────────────────────────────────────────────────────────────────────────────────┘ sort: Recency (newest-first) | filter: git after:2026-09-01 | 23 total | 6 shown | 0 /git after:2026-09-01
Favorites
Press f in history view to save the current line, or the whole multi-selection, as one favorite block. Favorites persist in ~/.config/hline/favorites.json.
Press F to open the favorites view. Search there matches titles and any line in a block.
┌favorites─────────────────────────────────────────────────────────────────────────┐ │> [ ] deploy (2 lines) │ │ [ ] cargo build --release │ │ [ ] ./scripts/release_local.sh │ │ [ ] db reset (2 lines) │ │ [ ] npm run db:migrate │ │ [ ] npx prisma generate │ │ [ ] fav1 (1 line) │ │ [ ] docker compose up -d │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └──────────────────────────────────────────────────────────────────────────────────┘ favorites: 3 blocks | filter: no filter | 8 rows shown | enter=accept fav
- Enter
- accept the whole block
- y / Y
- copy the whole block / copy current line
- r
- rename the block
- f
- remove the block
- j / k
- move line by line
- J / K
- jump block to block
Aliases
Every favorite title is an alias. New favorites are named fav1, fav2, and so on. Rename them with r to something you will remember.
hline <alias> prints the block to stdout and copies it to your clipboard. Lookup is case-insensitive: exact title first, then a unique prefix. A title that matches a subcommand like init or help is shadowed.
hline fav1
hline deploy
hline --list
hr() { eval "$(hline "$1")"; } # run a favorite in the current shellCopied to clipboard: cargo build --release ./scripts/release_local.sh $
deploy cargo build --release ./scripts/release_local.sh db reset npm run db:migrate npx prisma generate fav1 docker compose up -d $
Settings
hline --settings prints the settings path and its contents, creating the file with defaults on first run. Settings live in ~/.config/hline/settings.json unless XDG_CONFIG_HOME is set.
{
"shell_key": "ctrl-r",
"alias_behaviour": "copy"
}shell_key accepts ctrl-<letter> or alt-<letter>. The widget printed by hline init uses it, so re-source your profile after editing.
alias_behaviour decides what hline <alias> does with the block. print writes the commands to stdout. copy, the default, also puts them on the clipboard. full copies them, shows them, and runs them in a child shell.
- commands to stdout, nothing else
- copy
- clipboard plus stdout, the default
- full
- clipboard, preview on stderr, then runs them
The --behaviour flag reads and writes those modes. Passed with a favorite, the mode sticks to that favorite, so plain hline deploy keeps using it. Favorites without their own mode follow alias_behaviour, and hline --list marks the ones that carry a mode.
hline --behaviour # show the default and per-favorite modes
hline --behaviour full # change the default
hline deploy --behaviour full # change one favorite, then run itUnder full, stdout carries only the output of the commands. They run in a child shell from your current directory, so cd and exports inside a favorite do not affect your shell, and a favorite holding project commands needs you to be in that project.
/tmp/claude-1000/-home-pedro-projects-hline/44246292-b1b2-489f-97d6-d6b333931d7e/scr atchpad/demo/cfg/hline/settings.json { "shell_key": "ctrl-r" } shell_key: ctrl-<letter> or alt-<letter>, used by `hline init <shell>` $
Update checks
hline checks GitHub Releases for a newer version at most once a day and caches the result in ~/.cache/hline/update.json. Notices go to stderr, so accepted commands stay clean on stdout for the shell widget. Use --no-update-check to skip it, or --check-updates to force one.