A terminal dashboard for the things you keep checking, in one TUI you tab between:
- PRs: your open GitHub pull requests, with review requests as a section or as their own Reviews tab
- Sessions: your local agent sessions (Claude Code, Codex, Antigravity), with model and estimated cost for Claude
- Linear: your assigned Linear issues, plus an inbox and project tree
Every view has the same two-line rows, the same filter, and a markdown
preview beside the list (or floating over it). Network views cache their
last result under $XDG_CACHE_HOME/agenda, so the app paints at once and
refreshes behind you.
You need a Nerd Font in the terminal, the gh CLI logged in for the
PRs view, and a Linear API key for the Linear view.
Contents: Install · Configuration · Keys · Views · Cross-references · Releasing · Credit · License
go install github.com/sanity-labs/agenda@latestagenda opens on the first tab; agenda prs, agenda reviews,
agenda sessions, agenda linear open on that view. agenda help lists the
commands, agenda version prints the build, agenda update says whether a
newer release exists and how to get it for this install (it never replaces
the binary itself). The same check runs once at startup and shows in the tab
bar; update_check: false turns it off.
Shell completion, once per shell:
agenda completion zsh > "${fpath[1]}/_agenda"
agenda completion bash > /usr/local/etc/bash_completion.d/agenda
agenda completion fish > ~/.config/fish/completions/agenda.fishTo hack on it: make help lists the targets; make run, make test, and
make check (formatting, vet, tests: what CI runs). go run . works too
(the commands live in cli.go, so not main.go). AGENTS.md
and DESIGN.md hold the conventions and the interaction rules.
$XDG_CONFIG_HOME/agenda/config.yml (default ~/.config/agenda/config.yml),
optional. config.example.yml documents every key.
ctrl+s edits all of them in-app, grouped in tabs, and writes back to the
file without touching your comments; it also has a keybind editor. Options
marked (reload required) offer r in the overlay to reload agenda in place;
leaving without reloading warns once, and esc then reverts them.
The one thing that needs setup is Linear:
linear:
token: lin_api_xxx # linear.app → Settings → Security & access → API keysReview requests show as a section under your own PRs (w hides it). To
work through them on their own, give them a tab by enabling the reviews
view. The order of views is the tab order, so the first one is what
agenda opens on:
views: [reviews, prs, sessions, linear] # opens on Reviews
github:
review_filter: review-requested:@me is:open archived:false
hide_approved: true # someone already approved: the ball is with the author
hide_dependency_bots: true # leave Renovate and Dependabot out of the search
mark_reviewed: true # dim what you have already reviewed
notifications:
popup: desktop # a review request arrives: say soWith the Reviews tab on, it owns the review search; the PRs tab shows your
own PRs only and w says so.
Options people reach for most (all off unless noted)
| Key | Does |
|---|---|
hide_preview |
start with the list full width; v floats one row's detail |
github.diff_pane |
d shows the diff in the pane as a file list instead of paging it through less |
github.review_view |
what r opens beside the review popup: files (default) or unified |
github.hide_drafts |
leave draft PRs out of both sections; D toggles in-app |
grouping |
swimlanes under the active sort (status, repo, Today/Yesterday/…) |
remember_on_quit |
a q that quits saves the tab and filters; the next start restores them (off) |
hide_hidden |
leave rows hidden with i out of every list; off (default), they show dimmed and marked |
unread, unread_sync |
blue dot on rows that arrived since you last looked (on); keep it in step with GitHub notifications |
notifications.popup |
terminal toast, desktop notification or herdr toast (over the active Herdr pane, window or not) on new review requests and assignments; click opens a desktop one (macOS needs terminal-notifier); sound picks a bundled sound (Glass, Submarine…) or off |
refresh.every |
auto-refresh interval, with per-view overrides |
list_jump |
rows pgup/pgdn and shift+↑↓ move (5) |
mouse |
capture clicks and the wheel (on); off hands the mouse to the terminal so dragging selects and copies text, say from a CI log |
daemon |
keep a background agenda fetching and notifying with no window open; the TUI starts one and paints from its cache (see Daemon) |
github.merge |
m/M in the review popup merge or enable auto-merge, after confirming |
github.sort, linear.sort, sessions.sort |
the sort each view opens on |
theme.name |
catppuccin-mocha, tokyonight, gruvbox, dracula, nord, rose-pine, … |
toggles: persist |
keep d/c/t and an expanded description across rows instead of per row, and keep a ctrl+p tree up after leaving it |
footer: false |
hide the hotkey bar |
Every key binding is remappable per scope under keys:. Arrows, esc and
ctrl+c keep their meaning (you can add keys to those actions, not point
them elsewhere).
? shows the bindings for whatever has focus.
The defaults
| Scope | Key | Action |
|---|---|---|
| Global | tab / shift+tab, 1…9 |
switch view |
| Global | /, f |
filter the loaded rows (fuzzy on word starts, plus GitHub-style terms like -label:x is:draft); field-scoped filter popup. esc clears it |
| Global | cmd+k, ctrl+k |
quick search. PRs: a number finds that issue or PR in every loaded repo, plus open ones it prefixes; words offer a repo to narrow to (yours, or any in the same orgs), then search inside it. Linear: searches the whole workspace. Shows search_results hits (default 5) in the view's sort order; enter opens one like a cross-reference, past any filter |
| Global | → / ← |
give the keys to the pane / back to the list; in a float, ← steps a level out |
| Global | esc |
step back: focus, then the pane, then a float, then zoom. Never quits |
| Global | v, z |
show/hide (or float) the preview; zoom it |
| Global | l |
follow a cross-reference via a picker |
| Global | ctrl+s, ctrl+r, !, q |
settings, refresh, message log, quit (closes an open pane or float first; ctrl+c quits outright) |
| List | j/k, g/G, ctrl+u/ctrl+d, pgup/pgdn or shift+↑↓ |
move, top/bottom, half page, jump list_jump rows |
| PRs | enter, y, s/S |
open, copy URL, cycle sort / reverse |
| PRs | d, c, t, e |
diff (file list), comments, CI jobs, expand description |
| PRs | r |
review popup: approve, comment, request changes, view diff; m/M merge with github.merge |
| PRs | R, X, C, ]/[ |
reply to thread, resolve thread, new comment, next/previous thread |
| PRs | w, D, F |
toggle the review section; hide drafts; edit the section's GitHub search |
| File list | ↑/↓, +/→, -/←, space |
next file when on screen (else scroll a line), expand, collapse, mark reviewed and advance; jump keys scroll |
| Jobs pane | enter, →/←, ]/[, o, p, y, x |
open a job or a step's log, in/out of a job, next/previous failure, browser, less, copy URL, rerun |
| Sessions | enter, y, s/S |
resume in its directory, copy the transcript path, sort |
| Linear | enter, y, b, s/S |
open, copy URL, copy branch, sort |
| Linear | ctrl+p, c, m |
project tree (show and focus; again hides), comments, only-mine in a project or initiative |
| Linear inbox | space, v |
mark the row read without opening it; v opens the detail and marks it read in Linear too |
The mouse works: click tabs, rows, the tree and the hints in the preview;
double-click runs enter; the wheel scrolls the pane it is over. Selecting
text needs your terminal's bypass modifier (shift, or option in iTerm2),
or mouse: false (General › Mouse in ctrl+s), which hands the mouse back
to the terminal so a drag selects and copies as it normally would.
A pane that has the keys dims the list, so one cursor is lit. A float
(hide_preview) has levels: v is the description, c/d/t over it is
level two, ← steps down and esc closes the whole thing. The rules are
written down in DESIGN.md.
PRs fetches through gh api graphql, one page at a time
(github.page_size, 20) and the next as you reach the end. Rows carry state,
CI and review glyphs, diff size, comments, labels and age in fixed columns.
Each section's band names its search and count. Sorts: date, review, checks
(worst first), repo, size, author. d lists the changed files with their
counts and expands them inline, with review threads boxed under the lines
they anchor to; it is the one diff view that works past GitHub's 300-file
limit. t lists the head commit's check runs grouped by workflow, worst
first: open a job for its steps, a step for its log (cleaned, opened at the
first error), rerun from the pane, and it polls while anything runs. The
selected PR is re-read when the cursor settles, so a check that just
finished shows without waiting for the timer.
Sessions scans ~/.claude, ~/.codex and ~/.gemini/antigravity-cli,
parsing only files whose signature changed. Sorts: recent, cwd, tool, msgs,
cost. Sessions are dated by the last timestamp inside the transcript, not
the file's mtime. If history looks short, the files are gone: Claude Code
deletes transcripts after 30 days unless ~/.claude/settings.json says
otherwise ({ "cleanupPeriodDays": 3650 }); it only stops future sweeps.
Linear shows your assigned issues by default; the tree (ctrl+p) adds
Inbox, All Issues and pinned projects and initiatives, plus Triage (your teams' issues in
triage) with linear.triage, or one team's with linear.triage_team. Lists load a page at a time
(linear.filter.limit, 100) and the next as you reach the end; Linear
gives no total, so the status line reads 100 loaded · more below until the
last page is in, and fetching more… with the tab spinner while a page is on its way. Rows carry priority, state,
id · project · assignee, comment count and labels. Sorts: date, status
(in progress, todo, triage, backlog), project, priority.
The Inbox shows one row per issue, dim once read. A comment notification
opens (v) on the comments, scrolled to that comment; ← steps back to the
description and esc closes the float. Agenda reopens on the source you left
it on.
Views link to each other and l follows the link: a PR to the Linear issue
in its title, branch or body; an issue to its attached PRs (with live
status glyphs) and the sessions that mention it; a session to the issues
and PRs in its transcript. The picker always confirms, even for one target.
Targets loaded in another view jump there; the rest open in the browser,
marked ↗.
A small shared store (internal/store) is how views enrich each other
without importing one another: each publishes the facts it owns and reads
the others'. A view exposes links by implementing ui.Referencer and
becomes a destination with ui.RefTarget.
With daemon: true, agenda keeps a background copy of itself running: the
same views, fetch loops, cache writes and notifications, with no screen.
Review requests and assignments pop while no window is open, and the next
agenda paints from a cache that is seconds old instead of fetching first.
The TUI connects on start, starting a daemon when none answers, and then
leaves fetching to it: a view repaints when the daemon rewrites its cache,
ctrl+r asks the daemon to refetch, rows you look at are reported so their
unread marks stay cleared, and the footer says daemon while it is
connected (daemon gone if it drops, at which point the window fetches for
itself and keeps trying the socket). Desktop and herdr notifications come
from the daemon and need no window; terminal toasts are forwarded to the
windows that are open, and the ones raised while none was are handed to the
next window that connects, with each in its message log (!).
agenda status # client and daemon versions, pid, uptime, views, clients
agenda stop # the daemon quits; open windows fall back to fetching
agenda restart # stop, then start a fresh one (after an update, or a config change)
agenda daemon # run it in the foreground, for a service manager or a look at its outputThe socket and log live in $XDG_STATE_HOME/agenda (~/.local/state/agenda),
next to config-writes.log, one line per settings write (when, which
process, key and value, calling frames) for the day a setting seems to
change by itself.
The daemon reads the config when it starts. A search edited with F, or a
setting it acts on changed in the overlay, restarts it on the new file
(the window fetches for itself in the gap and says so with a toast); a
reload (r) restarts it along with the window; an edit to the file by hand
wants agenda restart. With refresh.every unset the daemon
refreshes every 5 minutes (a window with no daemon still fetches only on
ctrl+r, as before); set it to choose.
Merges to main accumulate into a draft release; publishing it tags
vX.Y.Z and GoReleaser builds the binaries. Labels drive the version and
the notes: the PR title's conventional prefix sets the type label (feat:
bumps minor, breaking major, the rest patch), changed paths add area
labels for triage. If the notes look wrong, check the PR's labels.
The PRs view and much of the design follow gh-dash by Dolev Hadar (MIT): the tabbed views, the two-line rows, fetching through GraphQL for check and review state, the glyph vocabulary, Glamour previews. The jobs pane follows its companion gh-enhance. Nothing is vendored; it was rebuilt from reading the source. If you live in one repo and want the full original, use gh-dash. agenda's reason to exist is PRs plus sessions plus Linear in one switcher.
Built with Bubble Tea v2, Lip Gloss and Glamour.
MIT. gh-dash's license covers the ideas this builds on.