TUI for running frequently used commands from markdown files
Find a file
SW 2e29267432 fix(ui): auto-scroll lists when selection moves out of view
Use ratatui's ListState with render_stateful_widget in all list widgets
(groups, files, sections, custom lines, search results) so the viewport
follows the cursor instead of letting the selected item disappear behind
the border.

Bump version to 0.1.16.
2026-06-22 14:14:08 +02:00
.github/workflows Add cargo-deb metadata and GitHub Actions workflow for .deb releases 2026-06-17 19:50:16 +02:00
examples Ghostty: run commands directly via base64 to avoid Gatekeeper prompts; translate README; anonymize examples 2026-06-15 12:47:51 +02:00
src fix(ui): auto-scroll lists when selection moves out of view 2026-06-22 14:14:08 +02:00
tests Ghostty: run commands directly via base64 to avoid Gatekeeper prompts; translate README; anonymize examples 2026-06-15 12:47:51 +02:00
.gitignore Initial commit 2026-06-15 10:46:15 +02:00
Cargo.lock fix(ui): auto-scroll lists when selection moves out of view 2026-06-22 14:14:08 +02:00
Cargo.toml fix(ui): auto-scroll lists when selection moves out of view 2026-06-22 14:14:08 +02:00
README.md Add cargo-deb metadata and GitHub Actions workflow for .deb releases 2026-06-17 19:50:16 +02:00

lines

A TUI app for quickly running frequently used commands — ideal for SSH scripts, updates, and anything you run regularly from the terminal.

Installation

macOS (Homebrew)

brew tap janvete/tools
brew install lines

Debian / Ubuntu (.deb)

Download the latest .deb from the GitHub Releases page and install it:

sudo apt install ./lines_*.deb
# or
sudo dpkg -i lines_*.deb

From source

cargo install --git https://github.com/janvete/lines

Usage

lines              # opens the TUI
lines --dir /path  # use a different data directory

Data structure

Data is stored as plain .md files in ~/.lines. Groups are subdirectories.

~/.lines
├── Servers
│   └── web.md
└── Scripts
    └── update.md

.md format

Each section starts with # Command name. Command lines follow the heading.

# Web server
ssh admin@web.example.com

# Database
ssh admin@db.example.com

A section without command lines means "run all commands in this file".

# Run all

# Docker update
ssh admin@host1.example.com /opt/app/update.sh
ssh admin@host2.example.com /opt/app/update.sh

Configuration

Create ~/.lines/config.toml:

terminal = "Ghostty"   # options: Terminal, Ghostty, iTerm
shell = "zsh"          # options: any shell name, default is taken from $SHELL

The default terminal is Terminal (macOS Terminal.app). The default shell is detected from the $SHELL environment variable.

Controls

Key Action
↑/↓ or j/k move
←/→ or h/l switch panel
Enter run selected command / all commands in the current terminal
/ open global search across all commands
o open selected command / all commands in a new terminal window
c open custom command mode for the current file
e open current .md file in $EDITOR
r reload data
q / Esc quit

Custom command mode

Press c on a selected file to run a custom command against specific lines from that file.

For a file containing:

# PVE1
ssh root@host1.example.com

# PVE2
ssh root@host2.example.com

# PVE3
ssh root@host3.example.com

Press c, select lines with Space, type lsblk, and press Enter. lines will run:

ssh root@host2.example.com lsblk
ssh root@host3.example.com lsblk

Use {} as a placeholder if your lines are just addresses:

ssh {} lsblk

Press / to search across all commands, files, groups, and section names. Type your query, use ↑/↓ to select a result, and press Enter to run it.

History

Every command run through lines is appended to ~/.lines/history.log:

2026-06-15 08:30:15 [Servers/web.md] Main server: ssh admin@web.example.com
2026-06-15 08:31:02 [Scripts/update.md] Run all: ssh admin@host1.example.com /opt/app/update.sh; ssh admin@host2.example.com /opt/app/update.sh

Backup

The entire ~/.lines folder is plain text. Copy it to a disk, Git repo, or iCloud. On a new Mac, move it to the same location and lines works immediately.

License

MIT