Skip to content
sanity-labsPublic

About

Terminal dashboard unifying GitHub PRs, agent sessions, and Linear issues — one TUI you tab between. Inspired by gh-dash.

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

agenda

CI Release License: MIT

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

Install

go install github.com/sanity-labs/agenda@latest

agenda 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.fish

To 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.

Configuration

$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 keys

Review 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 so

With 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).

Keys

? 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.

Views

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.

Cross-references

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.

Daemon

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 output

The 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.

Releasing

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.

Credit

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.

License

MIT. gh-dash's license covers the ideas this builds on.

About

Terminal dashboard unifying GitHub PRs, agent sessions, and Linear issues — one TUI you tab between. Inspired by gh-dash.

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages