Skip to content

fadedlamp42/otop

Repository files navigation

otop

htop, but for opencode. bubbletea TUI that shows all your running sessions at a glance — status, tokens, uptime, what the model last said, etc.

purpose

built because i run like 5-10 opencode sessions simultaneously and needed a way to see what's happening without switching through all of them :>

especially nice when you have a second monitor connected, so you can just leave this on there and turn your head for a quick check of all your sessions/where you're "needed" as a "pull" signal rather than the overwhelming "push" approach of hooking up TTS/sounds/visual bouncing for agents which are waiting (which doesn't scale well past human working memory)

install

go install github.com/fadedlamp42/otop@latest

or from source:

git clone https://github.com/fadedlamp42/otop
cd otop && go build -o otop .

requires Go 1.23+ (uses modernc.org/sqlite for pure-Go sqlite, no CGo needed).

usage

just run otop in your terminal.

each session gets two rows — title + metrics on top, last model output + secondary info on bottom. status is color-coded: green = active (generating, tool use, busy), yellow = transitional (thinking, queued), white = idle.

press enter on any session to open a detail view with the session's message history.

keys

q         quit
enter     detail view for selected session
r         force refresh
j/k       scroll (arrow keys too)
>/<       cycle sort column
s         flip sort direction
/         filter (matches title, model, tty, status, etc.)
y         yank session ID to clipboard
a         toggle non-interactive sessions (commit-msg, subagents)
p         toggle background processes (LSPs, tool wrappers)
t         todo panel for selected session
m         MCP server config panel

detail view: esc to go back, j/k to scroll.

how it works

the hard part is figuring out which process is running which session — opencode doesn't write a PID file or expose this anywhere. we solve it with a three-tier correlation:

  1. explicit -s flag in the cmdline (if you ran opencode -s ses_xxx)
  2. log filename timestamps — opencode writes to ~/.local/share/opencode/log/<UTC-timestamp>.log. even after rotation deletes the file, lsof still sees the fd. we extract the start time and match it against message activity in the db
  3. fallback — most recently updated session for that working directory

when multiple processes share the same cwd, a two-pass claimed-set algorithm ensures each process gets a unique session match. older processes get first pick since they have more message history to correlate against.

status is inferred from the db's finish field on assistant messages, cross-referenced with CPU usage from ps as a secondary signal (catches mid-stream responses that haven't been flushed to the db yet).

menu bar (SwiftBar)

otop has a bar-status subcommand that outputs SwiftBar-formatted text, showing session counts by status (e.g. G3 I12) in the macOS menu bar. setup is in bar.go.

run via pm2: pm2 start ecosystem.config.cjs starts otop serve on :8390, then the SwiftBar plugin (~/Library/SwiftBar/otop-bar.3s.sh) calls otop bar-status -p 8390 every 3 seconds.

troubleshooting: SwiftBar menu item invisible (ses_2a415f107ffeDRb8kJLfSOQDc3)

SwiftBar has a known bug (#442, milestone 2.1.0) where it creates a directory instead of a file when syncing plugins to its internal folder. this silently hides the menu bar item — the process runs fine but nothing appears.

symptoms: SwiftBar running in Activity Monitor, defaults read com.ameba.SwiftBar shows "NSStatusItem Visible otop-bar.3s.sh" = 0, and the app keeps resetting it to 0 on launch.

fix:

killall SwiftBar
rm -rf ~/Library/Application\ Support/SwiftBar/Plugins/otop-bar.3s.sh
defaults delete com.ameba.SwiftBar
killall cfprefsd
open /Applications/SwiftBar.app
# pick ~/Library/SwiftBar as the plugin folder when prompted

platform

macOS only right now — i use this daily on mac and that's where it's tested. the process discovery layer (lsof, ps output parsing, cwd resolution) is all macOS-flavored. linux support is on the horizon, mostly just needs /proc/<pid>/cwd and /proc/<pid>/fd/ instead of lsof :]

reads from opencode's sqlite db read-only (WAL mode, safe to query while sessions are active). respects $XDG_DATA_HOME and $XDG_CONFIG_HOME if set.

tasks

my opencode sessions, for convenience

ses_376f0394bffeMw00t9awVuAbEp - publishing and latest changes ses_367b7fb8cffeLWmyGNDY3ltaVi - go rewrite ses_2fb35dfc7ffeB5zQP9uKo6t1L2 - bar status

todo

  • fix tmux pane scroll false generating positive (i.e. wiggling the TUI scroll buffer convinces the logic that generation is happening, somehow); hopefully opencode.db exposes something more reliable for in-progress generations
  • improve first-time experience of bar status (ses_2fb35dfc7ffeB5zQP9uKo6t1L2) via brew and perhaps pm2 service responsible for installing and starting SwiftBar
  • record a vhs demo gif — scroll through sessions, enter detail view, filter, back out. embed in README above the install section
  • stable Linux support (it kinda just works already lol)
    • replace lsof cwd resolution with /proc/<pid>/cwd symlink readlink
    • replace lsof log file discovery with /proc/<pid>/fd/ enumeration
    • handle linux TTY format (pts/3 vs macOS ttys005) in tmux pane mapping
    • replace pbcopy with xclip -selection clipboard / xsel / wl-copy (detect what's available)
    • fix pthread_setname_np call signature (linux takes two args: thread + name)
    • test ps axo pid,pcpu,rss,tty,etime,args output format on debian — may need minor parsing tweaks
  • remote opencode server support (probably in combination with local ones, special rendering/separate section like k9s namespace to delineate)

ongoing

active

passive

waiting

done

cancelled

About

htop, but for opencode

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors