A TUI client for Linear.app — manage issues, projects, and cycles from your terminal.
Built with ratatui and the Linear GraphQL API.
- Sidebar navigation, like Linear's — My Issues, your Favorites (in Linear's order, with folders), and the current team's Issues / Cycles / Projects / Views under a team switcher
- Favorites — favorite projects, cycles, issues, and views open right in the terminal; favorites of kinds the TUI has no page for (documents, labels, …) open on linear.app
- Saved views — issue and project views, on the workspace Views page and each team's own, with Linear's Issues / Projects tabs; Linear evaluates each view's filter, so it shows exactly what it shows on linear.app
- Grouped lists — issues stacked under collapsible status headers (or by assignee, priority, project), sub-issues nested under their parent, and Linear's Active / Backlog / All issues presets
- Rich rows — priority and status glyphs in the workspace's own colours, label chips, project, estimate, assignee avatar, and date, dropping columns gracefully as the terminal narrows
- Issue detail like the web app — rendered Markdown (bold, code, lists, headings, quotes, code blocks) with proper Japanese line breaking, threaded comment cards, sub-issues, and a properties panel with status, priority, assignee, creator, estimate, due date, cycle, labels, project and milestone; step to the next/previous issue with
J/K - Mouse support — click sidebar entries, preset chips, group headers, popup entries; click a row to select it and again to open it; scroll with the wheel
- Issue management — create issues, change status, priority, and assignee, and comment
- Never blocks — Every API call runs off the UI thread, so navigation and input stay responsive while data loads
- Search — Filter as you type locally, or
Ctrl+Gto search all of Linear - Open & copy — Jump to the issue in your browser, or copy its identifier, URL, or suggested branch name
- Linear's own keybindings —
cto create,s/p/a/ito update,g+key to go places, plus vim-stylej/k - One-command sign-in —
linear-tui auth loginopens the browser; no application to register, no client secret, or use a personal API key instead - Theme support — Default (dark), Light, and Ocean color schemes
- Pagination — Cursor-based infinite scrolling across every list
cargo install linear-tuiPre-built binaries are available for Linux, macOS (Intel/Apple Silicon), and Windows on the Releases page.
git clone https://github.com/k1-c/linear-tui.git
cd linear-tui
cargo install --path .Building needs only a Rust toolchain — see Development for the mise setup.
Just run it:
linear-tuiOn first launch it asks how you want to connect, and nothing else has to be set up beforehand.
linear-tui auth loginThis opens Linear in your browser, and the authorization screen appears as k1-c/tui — the application linear-tui is registered as. (Linear does not allow "Linear" in an application's name, which is why it is not called linear-tui there.)
Approving it hands a token back to a local callback on port 53681, 53682, or
53683. Tokens are stored in ~/.config/linear-tui/tokens.json with 0600
permissions and refreshed automatically.
No client secret is involved: Linear's PKCE flow makes one optional, so linear-tui ships as a public OAuth client.
Useful when the browser flow cannot reach your terminal — over SSH, for example, where the callback would land on the wrong machine.
Create a key under Settings > Account > Security & access, then:
linear-tui auth token <your-api-key>The key is verified before it is saved.
linear-tui auth status # which credentials are in use, and who they belong to
linear-tui auth logout # forget the OAuth token
linear-tui auth logout --all # forget the API key in config.toml as wellSome workspaces require third-party applications to be approved by an admin. If
that blocks you — or you would simply rather authorize against your own — register
one at Linear Settings > API with
http://localhost:53681/callback (plus 53682 and 53683) as its redirect URIs:
linear-tui auth set-oauth <client-id> [client-secret]LINEAR_CLIENT_ID and LINEAR_CLIENT_SECRET override the config file.
Shortcuts follow Linear's own keyboard shortcuts wherever a terminal allows it, so muscle memory carries over from the web app.
| Key | Action |
|---|---|
j / k, ↓ / ↑ |
Move cursor down / up |
g g / G |
Jump to first / last item |
Enter, Space |
Open (Linear: peek) |
Esc |
Back / close |
J / K |
Next / previous issue, in the detail view |
Tab |
Move focus between the sidebar and the content |
Ctrl+b |
Show / hide the sidebar |
h / l |
Fold / unfold a Favorites folder (while the sidebar has focus) |
t |
Switch team (or Enter / click on the team row in the sidebar) |
g a / g b / g e |
Active / Backlog / All issues of the current team |
g m / g v / g p / g c |
Go to My Issues / Views / Projects / Cycles |
1-5 |
Team issues / My Issues / Projects / Cycles / Views |
Ctrl+d / Ctrl+u |
Half page down / up |
PgDn / PgUp |
Full page down / up |
| Key | Action |
|---|---|
Shift+Tab |
Next preset: Active → Backlog → All issues (on a Views page: Issues ⇄ Projects tab) |
D |
Group by status → assignee → priority → project → none |
z / Z |
Fold the group under the cursor / fold or unfold every group |
| Action | Effect |
|---|---|
| Click a row | Select it; click it again to open it |
| Click a sidebar entry | Go there; a folder folds, the team row opens the team switcher |
| Click a preset chip, tab, or group header | Switch preset or tab / fold the group |
| Click a popup entry | Choose it; click outside to close |
| Wheel | Scroll whatever is under the pointer |
| Key | Action |
|---|---|
c |
Create a new issue (Ctrl+Enter to save) |
s |
Change status |
p |
Change priority |
Shift+1 … Shift+4, Shift+0 |
Set priority directly (Urgent → Low, None) |
a |
Assign to someone |
i |
Assign to me |
m |
Add a comment (Ctrl+Enter to send) |
| Key | Action |
|---|---|
Ctrl+. or y |
Copy the issue ID |
Ctrl+Shift+, or Y |
Copy the issue URL |
Ctrl+Shift+. or b |
Copy the suggested git branch name |
o |
Open the issue (or project) on linear.app |
| Key | Action |
|---|---|
/ |
Filter the visible list as you type |
Ctrl+G |
Search all of Linear for the current query |
f / Shift+F |
Filter by status & priority / clear filters |
t |
Switch team |
Ctrl+r, F5 |
Refresh the current view |
? |
Show all shortcuts |
q, Ctrl+c |
Quit |
Text fields (search, comments, the new-issue form) accept readline-style
editing: Ctrl+w, Ctrl+u, Ctrl+k, Ctrl+a, Ctrl+e, and arrow keys.
A terminal cannot deliver every shortcut the web app uses, and a few of Linear's actions have no meaning here. Where they differ:
| Linear | linear-tui | Why |
|---|---|---|
Ctrl+., Ctrl+Shift+., Ctrl+Shift+,, Ctrl+M |
also y, b, Y, m |
Legacy terminals cannot distinguish Ctrl+punctuation, and Ctrl+M is Enter. The originals work in terminals supporting the kitty keyboard protocol (kitty, Ghostty, WezTerm, foot, Alacritty), which is enabled automatically when available. |
r — rename issue |
(unbound) | Renaming isn't supported yet; refreshing uses the terminal's Ctrl+r instead. |
j / k — next / previous issue in the issue view |
J / K |
In the detail view j/k scroll the text, which a terminal cannot do with a trackpad. |
| Display options menu (grouping, collapsing) | D, z, Z, Shift+Tab |
Linear keeps these behind a menu with no shortcut; the keys are ones Linear leaves free. |
| Double-click to open | click the selected row again | Terminals do not report double-clicks. |
Ctrl+d — set due date |
half page down | The scrolling convention wins in a terminal. |
| — | o, t, q, Tab, Ctrl+b, 1-5 |
Open in browser, switch team, quit, and sidebar focus have no web-app equivalent. |
Copying uses the OSC 52 terminal escape, so it works over SSH. If nothing
lands on your clipboard, enable it in your terminal — under tmux that means
set -g set-clipboard on.
Config file: ~/.config/linear-tui/config.toml
[auth]
# OAuth tokens are managed automatically via `linear-tui auth login`
# To use a personal API key instead:
# api_key = "lin_api_xxxxx"
# To authorize against your own Linear application:
# oauth_client_id = "..."
# oauth_client_secret = "..." # optional — PKCE does not require one
[ui]
default_team = "Core" # Auto-select this team on startup
items_per_page = 50 # Issues per page (pagination)
theme = "default" # "default" | "light" | "ocean"
sidebar = true # Show the sidebar (it hides itself below 100 columns)
sidebar_width = 26 # Sidebar width in columns (18-48)
group_by = "status" # "status" | "assignee" | "priority" | "project" | "none"| Theme | Description |
|---|---|
default |
Dark theme with cyan accents |
light |
Light background with blue accents |
ocean |
Dark blue palette with soft colors |
The Rust toolchain is pinned in mise.toml, so mise
is all you need to install:
git clone https://github.com/k1-c/linear-tui.git
cd linear-tui
mise install # fetch the pinned toolchain
mise run dev # run the TUI from source
mise run verify # format, lint, test, buildmise run fmt, lint, test, and build run the steps individually. Without
mise, any stable rustup toolchain works — cargo run, cargo test, and the
rest behave the same.
There is nothing else to install: TLS goes through rustls, so no system OpenSSL
or pkg-config is involved.
The GIF above is scripted with VHS, so it can be re-recorded after a UI change. Sign linear-tui in to a throwaway workspace, fill it with demo data once, then record:
python3 demo/seed.py # issues, projects, cycles, views
LINEAR_DEMO_TEAM=<team name> demo/record.sh # writes assets/demo.gifrecord.sh needs vhs, ttyd, and ffmpeg, and runs linear-tui with a
throwaway config so your own settings stay out of the recording.