Skip to content

yrstm/agentdash

Repository files navigation

 █████╗  ██████╗ ███████╗███╗   ██╗████████╗██████╗  █████╗ ███████╗██╗  ██╗
██╔══██╗██╔════╝ ██╔════╝████╗  ██║╚══██╔══╝██╔══██╗██╔══██╗██╔════╝██║  ██║
███████║██║  ███╗█████╗  ██╔██╗ ██║   ██║   ██║  ██║███████║███████╗███████║
██╔══██║██║   ██║██╔══╝  ██║╚██╗██║   ██║   ██║  ██║██╔══██║╚════██║██╔══██║
██║  ██║╚██████╔╝███████╗██║ ╚████║   ██║   ██████╔╝██║  ██║███████║██║  ██║
╚═╝  ╚═╝ ╚═════╝ ╚══════╝╚═╝  ╚═══╝   ╚═╝   ╚═════╝ ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝

agentdash

w, but for coding agents. agentdash prints a table of the agent processes on a Linux or macOS box: what each one is working on, what model it is on, how full its context is, and whether it is blocked waiting on you. -w turns the table into a small interactive TUI.

It is a single static binary (no cgo, no runtime services), reading the session files agent CLIs already write locally (Claude Code and Codex are supported; adding another agent is a small parser, see CONTRIBUTING.md) and the OS process table directly — /proc on Linux, ps/lsof on macOS. No daemon, no server, no API calls, no telemetry, no file watcher, and it never launches or manages sessions. Watch mode samples foreground state on the TUI refresh tick, like w or htop. I wrote it because I kept losing track of agents across tmux sessions; maybe it is useful to you too. Runs on Linux and macOS.

Install

curl -fsSL https://raw.githubusercontent.com/yrstm/agentdash/main/install.sh | sh

or grab the binary directly (linux-amd64, linux-arm64, darwin-amd64, darwin-arm64):

curl -fsSLo ~/.local/bin/agentdash https://github.com/yrstm/agentdash/releases/latest/download/agentdash-linux-amd64
chmod +x ~/.local/bin/agentdash

or with Homebrew (Linux and macOS), or go install:

brew install yrstm/agentdash/agentdash
go install github.com/yrstm/agentdash/cmd/agentdash@latest

It is a single static binary with no cgo and no runtime services. Optional at runtime: tmux (pane jumping, attachment glyphs), docker (sandbox section, skipped if absent), jq (only for the integrations). On macOS, lsof (shipped with the OS) is used to read process working directories, open session files, and listening ports — the roles /proc fills on Linux; the ports and sandbox sections degrade gracefully if it is unavailable. The auditable v1 bash version lives in legacy/ and keeps working (Linux only).

Dependency footprint (measured with go list -buildvcs=false; absolute package counts vary a little by Go toolchain): the default build is ~201 compiled packages, 15 non-stdlib, 6 third-party modules, against a 30-module resolution graph (go list -m all). Watch mode is a small raw-terminal loop — no Bubble Tea / Charm stack — so the only third-party code in the default binary is go-runewidth (Unicode column alignment) plus golang.org/x/term/x/sys. -tags=hermes adds the pure-Go modernc SQLite driver. Note: the default build does not compile modernc, but modernc.org/sqlite is a direct go.mod requirement, so it remains in the module resolution graph until Hermes is split into its own module.

Optional: Hermes (SQLite-backed agents)

Claude and Codex write JSONL transcripts and work out of the box. Hermes keeps its sessions in a SQLite state.db, so support for it is an opt-in build — the default binary stays small and links no database driver:

go build -tags hermes -o agentdash ./cmd/agentdash

The tagged build reads ~/.hermes/state.db (and profiles/*/state.db) read-only, pairing a live Hermes process by HERMES_SESSION_ID when it is exported, otherwise by cwd and start time. Nothing about the default build changes.

Usage

agentdash              print the table once
agentdash -w [secs]    watch mode (default 5s), keys below
agentdash -a           expand collapsed rows and healthy sections
agentdash -l           long view: adds PID, TTY, UP columns
agentdash -t           tree view: group agents under the wrapper that spawned them
agentdash --json       machine-readable output (schema_version 1)
agentdash --plain      no color, no glyphs; NO_COLOR is honored
agentdash --any-waiting   exit 0 if anything needs you

agentdash go [row|pid]     jump to that agent's tmux pane
agentdash show <row|pid>   drill-down with recent turns and resume command
agentdash why <row|pid>    where every value on the row came from
agentdash label <row|pid> "text"   pin a task label
agentdash resume <row|pid> print the resume command for a session
agentdash recap [4h]       what changed since you last looked
agentdash docs [repo|. | <file>]  agent-memory health, drift, and per-file change history (--json)
agentdash grep <pattern>   search past sessions of both agents (--json for tooling)
agentdash du               disk triage: agent file sizes by category (--json for tooling)
agentdash usage            local token-spend estimate: windows, burn, attribution
agentdash health           per-agent warning roll-up; exit 0 if nothing flagged
agentdash inspect          inventory config files (token cost, mtime, git-tracked)
agentdash audit            config + context-rot findings (--handoff for a fix pack)
agentdash context <row|pid>  the effective instruction stack for a live session
agentdash trail <commands|files|secrets>  forensics from transcripts (--json/--csv)

Trust trail (agentdash trail)

agentdash trail reconstructs, from transcripts only, what agents actually did on this box — read-only, and it never prints a secret in full.

agentdash trail commands --since 7d
agentdash trail files --blast <session-file>
agentdash trail secrets --json
  • trail commands — every shell command an agent ran (timestamp, session, cwd, command line). Codex rollouts also record per-turn approval and sandbox policy, so commands that ran with approvals off (approval_policy=never) or the sandbox disabled (danger-full-access) are flagged, and the count of them is the headline number.
  • trail files — every Edit/Write (and codex apply_patch): timestamp, session, path. --blast <session> shows the file set one session touched and marks which of those files currently differ in git status.
  • trail secrets — scans transcripts for high-confidence secret patterns (AWS keys, GitHub/Slack/OpenAI/Anthropic tokens, Google API keys, private-key headers, JWTs). It reports a masked value (first 4 chars + ), the pattern name, the session file, and the age — the full value is never printed or written anywhere. It offers no scrubber: rotate the credential, then trim history and delete the session file.

All three take --since and --project, and export --json and --csv.

Config inventory & context rot (inspect, audit, context)

agentdash inspect inventories every file that shapes agent behaviour for the current project — the CLAUDE.md / AGENTS.md chain, .cursor/rules, hooks, and slash commands — with an estimated token cost (~chars/4), last-modified date, and whether each file is git-tracked, and a footer with the total always-loaded instruction tokens. inspect why <file> explains why a file applies.

agentdash audit reports deterministic, evidence-backed findings — no model calls. Alongside the original drift checks (missing_rule, stale_rule) it now flags conflicting rules across files (same topic, opposite values — indentation, package manager, test runner), duplicate rules, dead hooks (a settings hook pointing at a missing or non-executable script), and a heavy context (the always-loaded chain over a token budget; default 4k, AGENTDASH_LINT_CTX_TOKENS overrides). Each finding has a severity, evidence lines, and a suggested fix. It never edits files; --handoff <file> writes an evidence pack plus a ready prompt you can feed to your own agent to apply the fixes — that file is the only LLM boundary, and agentdash calls nothing.

agentdash context <row|pid> shows the effective instruction stack for a live session: the memory-file chain for its cwd, active hooks, and configured MCP servers, each with an estimated token cost; the model's window and current CTX% (exact, from usage); and the session's compaction events with timestamps. The MCP tool-schema "context tax" is reported as not measurable from transcripts rather than guessed.

Search past sessions (agentdash grep)

Find the old conversation where something was already worked out. agentdash grep <pattern> searches the message text of every Claude and Codex transcript and prints one line per matching session, newest first:

agentdash grep "flaky checkout"
agentdash grep "rate limit" --role assistant --project api --since 7d -n 20
agentdash grep "AKIA" --tools --json

<pattern> is a Go regular expression (RE2). Each hit shows the session age, agent kind, project directory, a match count, the session title, the best matching snippet, and a paste-ready resume command. Flags:

  • --role user|assistant — search only that side of the conversation.
  • --project <dir> — keep sessions whose cwd or repo root contains <dir>.
  • --since 30m|4h|7d — skip sessions with no activity in the window.
  • -n <max> — stop after this many matching sessions (newest first), so a broad pattern on a large history returns quickly.
  • --tools — also search tool-call payloads (Bash commands, tool results), not just message text.
  • --json — schema_version 1 document for tooling.

By default only human/assistant message text is searched; tool payloads (and any secrets pasted into them) are searched only with --tools. Subagent transcripts fold under their parent session rather than showing as their own hit.

Disk triage (agentdash du)

agentdash du breaks down the disk the agent CLIs accumulate, largest category first, and for the transcript store lists the ten biggest sessions:

agentdash du
agentdash du --json

Each category shows its size, one sentence on what it is and whether deleting it is safe, the relevant retention knob where one exists (e.g. Claude Code's cleanupPeriodDays), and a suggested cleanup command. Categories covered: ~/.claude/projects, ~/.claude/file-history, ~/.claude/shell-snapshots, ~/.claude/todos, ~/.claude.json, ~/.codex/sessions, ~/.codex/log, and the MCP log cache (~/.cache/claude-cli-nodejs on Linux, ~/Library/Caches/claude-cli-nodejs on macOS); on macOS it also accounts for the desktop app's ~/Library/Application Support/Claude and ~/Library/Logs/Claude. du never deletes anything — the cleanup lines are suggestions for you to run.

Usage estimates (agentdash usage)

agentdash usage estimates token spend from the per-message usage blocks the transcripts already record. It reads no credentials and makes no API call, so everything it prints is a local estimate — it cannot see provider-side limits, spend on other machines, or anything the transcripts do not record, and it says so in its header.

agentdash usage
agentdash usage --limit 20000000
agentdash usage --json

It reports, per model, rolling 5-hour and 7-day token totals (input counts include cache tokens, matching the board); a burn rate in tokens/min over the last 30 minutes; a per-session attribution table for the 5h window (share %, agent, model, in/out, task — subagent transcripts are tagged); and a per-project cache-hit ratio over 7 days, flagging a project whose ratio dropped sharply in the last day (usually an always-loaded file changed and stopped hitting cache). With --limit N (or AGENTDASH_USAGE_LIMIT) it projects when the 5h window would reach that cap at the current burn rate; without one it stays silent about limits it cannot know.

Usage blocks are deduplicated by message id. Codex reports cumulative counts, so its per-turn delta (last_token_usage) is what lands in the windows; Codex does not split cache read from creation, so the cache-hit stats are Claude-only.

Health roll-up (agentdash health)

agentdash health prints one block per live agent with a small set of pass/flag signals, each carrying the evidence behind it. Like --any-waiting it composes with cron: it exits 0 when nothing is flagged and 1 when something is, so agentdash health || notify-me works. --json always exits 0 (read the flagged field).

agentdash health
agentdash health || echo "something needs attention"
agentdash health --json

The signals, and how each is derived:

  • stuck / respawn — the board's own status for the row (stuck?, or respawn ×N for a crash loop).
  • ctx_high — the session's context tokens are ≥85% of the model's window (AGENTDASH_* status thresholds and the context-window config feed this).
  • compaction — context-compaction (summary) entries per hour over the session's lifetime; a high rate means the agent keeps summarizing its memory away. Flagged at ≥2/hour.
  • api_errors / interrupts — share of the last 30 turns that are API-error turns (isApiErrorMessage) or user interrupts ([Request interrupted …]), flagged at ≥20%. Claude transcripts only — Codex has no equivalent marker.
  • waiting_today — minutes the session has spent in waiting since local midnight, reconstructed from the event log's status history (so it needs the event log enabled; it reads 0 otherwise). Flagged at ≥30 minutes.
  • zombie MCP (box-level) — MCP server processes reparented to init (their launching agent exited but the server kept running). Conservative: it keys on unmistakable MCP markers in the command line, so a server still owned by a live agent is never flagged.

Memory health & file history (agentdash docs)

In plain terms: agentdash quietly watches the memory files your agents rely on (CLAUDE.md, AGENTS.md) and tells you when a project's memory has gone stale or out of step with recent work — so you can spot an agent running on outdated notes before it bites you.

Agents accumulate durable memory in repo-root CLAUDE.md / AGENTS.md. Every normal agentdash run opportunistically samples those files for the projects that have a live session (an mtime/size check short-circuits before hashing, so steady-state cost is tiny) and appends to an append-only, never-pruned log at ~/.cache/agentdash/memory-log.jsonl — but only when the content hash actually changes, so a same-size edit is still recorded.

agentdash docs shows a cross-project board ranked by how far each project's memory trails its recent work (git commit time and dirty-tree state when available, else file mtime), flags stale and — when a memory change landed while two or more live sessions shared the project — ⚠concurrent (a mechanical risk signal, not proven authorship). agentdash docs <repo|.> prints that project's change log, newest last, each entry labelled baseline (the first time agentdash observed the file — it did not create it), grew, shrunk, or same-size-rewrite. --json emits the same data as a stable schema_version: 1 document (cross-project board, or per-project log).

Given a file instead of a repo, agentdash docs <file> prints that one file's change history: a git log --follow timeline (when, author, +/- lines, first diff hunk) when the file is tracked, or agentdash's own hash/size snapshots when it is not. Each change is attributed by correlating its time against Edit/Write tool calls in the transcripts — by <agent> session <task> at <t> when a matching edit is within five minutes, otherwise outside any recorded agent session (human or other tool). This --json document is schema_version: 2 (the command's one additive bump; the board and per-project log documents are unchanged at 1).

Exactly what it samples. Repo-root CLAUDE.md and AGENTS.md only — no recursive crawl, no other markdown, no subdirectories. It reads each file only to hash it; file contents are never stored. Each appended log row holds: project path, path, kind, bytes, sha256, mtime, sample ts, and the live-sessions count at sample time — metadata and a hash, nothing more.

Log location & retention. The log is a single append-only JSONL file at:

~/.cache/agentdash/memory-log.jsonl

It is never pruned — that is the point (long-term history) — so it grows by one line per real content change. It records no file contents, but project paths and names can themselves be sensitive; the file stays local and is yours to delete. (A future agentdash docs compact / --forget <repo> could trim it; not built yet.)

Local only, no network, read-only toward your files; the scope is deliberately tight (repo-root files, never a filesystem crawl).

Updating

agentdash has no self-update command — the binary never touches the network. Instead the plain board shows the running binary's build age (from its embedded VCS stamp) and, once it crosses AGENTDASH_STALE_DAYS (default 14), prints the reinstall command — carrying the build tags it was built with, so a Hermes build tells you to reinstall with -tags=hermes. You run it yourself:

go install -tags=hermes github.com/yrstm/agentdash/cmd/agentdash@main

Watch mode keys

Watch mode has a cursor (); keys act on the selected row. Panels open over the board and any key returns to it.

j/k or arrows   move the cursor
tab             switch between Agents and History
g               jump to the agent's tmux pane
s               drill-down panel: recent turns, session path, resume command
y               provenance panel: where each value on the row came from
L               edit the task label
r               show the resume command
/               filter rows across task, cwd, model, status (Esc clears)
o               cycle sort: urgency, last-write, tokens, uptime
History view: s details, r resume, i read/command disclosure
t               toggle tree view        l   toggle long view
a               toggle expanded view    ?   help
mouse           wheel scrolls, click selects (off under --plain)
q               quit

The board refreshes on foreground TUI ticks. Process starts and exits are picked up by a cheap /proc scan every second (AGENTDASH_PROC_TICK); the full board and History view resample on the watch interval.

Event hooks

Watch mode can run a command of yours when an agent changes state, so you can fire-and-forget an agent and be reached only when it actually needs you — a desktop toast, a phone push, a Slack message, a log line:

agentdash -w --on-needs-you <cmd>   run <cmd> when an agent enters a needs-you state
agentdash -w --on-stuck <cmd>       run <cmd> when an agent's status becomes stuck?

The command runs through sh -c, so quote it as one argument. It fires once on the transition into the state (not repeatedly while it lasts), and only with -w. agentdash itself opens no socket and makes no network call anywhere; your command is what reaches out.

Each invocation receives the agent as JSON on stdin — one object, byte-identical to an entry in the agents array of agentdash --json, wrapped in an envelope:

{"event":"needs_you","ts":1718566400,"attached":false,"agent":{"agent":"claude","pid":4123,"needs_you":true,"status":"waiting","cwd":"/home/dev/code/app-be","task":"rebase the feature branch",}}

attached is whether someone is on its tmux pane (so a notifier can stay quiet for agents you are already watching). For quick shell one-liners the headline fields are also in the environment: AGENTDASH_EVENT, AGENTDASH_PID, AGENTDASH_TASK, AGENTDASH_AGENT, AGENTDASH_CWD, and AGENTDASH_STATUS. A stuck? transition counts as needs-you too, so with both hooks set it fires both events; deduplicate on event if that matters. Hooks are edge-triggered (they fire on entry into a state, not while it persists) and debounced per session: the same event will not re-fire for the same agent within 60s, so a status that flickers does not spam you. Commands run non-blocking and are bounded by a 10s timeout, so a slow or wedged hook never stalls the board.

A minimal example notifier lives in integrations/notify-example.sh, or use your desktop/phone notifier directly:

agentdash -w --on-needs-you 'integrations/notify-example.sh'
agentdash -w --on-needs-you 'notify-send "agent $AGENTDASH_AGENT" "$AGENTDASH_STATUS: $AGENTDASH_TASK"'
agentdash -w --on-needs-you 'curl -fsS -d "$AGENTDASH_STATUS: $AGENTDASH_TASK" https://ntfy.sh/your-topic'

To let another agent read and triage the fleet through the same CLI (no MCP needed), see docs/agent-interface.md.

The table

devbox 14:02 · 2 need you · 3 working · 6 idle · 8m ctx held idle · load 0.27

  AGENT    LAST  MODEL      TOKENS     CTX        ACT      STATUS     CWD        TASK
▸ claude ○ 4m    opus-4-8   34m/359k   ▓▓▓░░  45%   ▅▂▁    waiting    ~/c/api    flaky checkout test
  claude ● 12s   fable-5    18m/285k   ▓▓░░░  35%   ▆█▇▅   working    ~          migrate the queue
  + 2 wrappers · 1 unmatched (-a to list)

  ok: tmux ×4 · logins ×2 · sandboxes ×3 · ports ×4 · no zombies

Rows sort by urgency and keep their order between refreshes. Healthy sections collapse into the ok: line and expand only when something is flagged; pass -a (or set AGENTDASH_EXPAND=1) to always expand everything. Defunct (zombie) processes and orphaned wrapper processes — a bash -c/nohup launcher still running with no controlling tty after its agent has exited — surface in a ZOMBIES & ORPHANS section when present. Glyphs: tmux attached, detached; a red means the agent is waiting and nobody is attached.

Columns: LAST is time since the session file was written. TOKENS is cumulative input/output, where input includes cache reads and writes (it measures context fill, not billing). CTX is the last request against the model's context window, yellow at 70%, red at 85%. ACT is bytes appended per refresh over the last 8 intervals. TASK is your label, else the session's summary, else the first prompt; a trailing ? means the process-to-session pairing was heuristic.

Colors carry one meaning each: green working, yellow worth a look, red needs you now, dim ignorable. A healthy board is almost colorless.

Tree view (-t, or t in watch mode) regroups the rows so an agent sits under the wrapper process that spawned it, found by walking the ppid chain. Urgency order still applies to the top level.

Design notes

agentdash started life as a bash script with an embedded stdlib-only Python parser: session files are JSONL with nested, escaped content (prompts hold quotes, unicode, pasted code), which bash regex cannot parse correctly, and jq would have been a lateral dependency swap that loses the stateful incremental scan. v1 proved the heuristics in bash+python; v2 compiles them. The genre's reference tools (w, htop) are compiled C for a reason: the Go version reads /proc and the session files directly, drops the procps/iproute2/python3 dependencies entirely, and keeps watch mode as a foreground sampler with no daemon, listener, or file watcher. The v1 script is preserved, working, in legacy/agentdash.sh; tools/parity.sh holds the two implementations to identical --json output. The only socket the tool will ever open is the local docker unix socket for the sandboxes section, and CI enforces that with strace.

The scanner is incremental by byte offset: each render stats every paired session file and parses only the bytes appended since the last look, folding them into a cached per-session entry. Only complete lines are consumed, since an agent may be mid-write; a partial tail line waits for the next refresh. That is why a board over gigabytes of session history costs milliseconds once the cache is warm.

Pairing a pid to a session file never guesses silently. A five-tier evidence chain runs from exact to heuristic, records which tier fired, and anything below the confident tiers is marked with a ? on the board. agentdash why <row> prints the recorded evidence for every value on the row.

Performance

Measured with the reproducible suite in bench/ (4-core container, 25 MB session corpus, methodology and caveats in docs/benchmarks/):

metric v1 (bash+python) v2 (Go)
one-shot render, warm cache 174 ms 15 ms
one-shot render, cold cache (25 MB) 562 ms 368 ms
idle watch CPU, 10 min avg 3.65 % 0.29 %
session write to screen ~2.3 s ~80 ms
peak memory (PSS), cold render 38 MB 17 MB
dependencies on a clean box bash python3 procps iproute2 none

How values are derived

Pairing a process to a session file walks an evidence chain: an open fd in /proc, then a unique session file in the project dir for the process cwd, then a first-entry timestamp within 5 minutes of process start, then a sticky previous guess, then the newest unclaimed recent file (the last two are heuristic and marked ?). Codex sessions pair on an open rollout fd first (exact, and the only thing that catches a codex resume, whose rollout filename keeps its original start time), otherwise on the cwd and start time recorded in the rollout file. agentdash why <row> prints which tier applied.

Status: file written under 60s ago is working; over 10 minutes quiet is idle; in between, waiting if the last entry is an assistant turn, else it is graded by how long it has been quiet on a user/tool entry: busy? (dim) under AGENTDASH_STUCK_SECS (default 90s) — likely a slow tool call, not alarming — and stuck? (red) past it. This grading replaces the old flat "silent over a minute = stuck?" false positive. respawn ×N means three or more fresh pids on one session file within 10 minutes. Thresholds are configurable via AGENTDASH_WORKING_SECS, AGENTDASH_STUCK_SECS and AGENTDASH_IDLE_SECS.

Context windows come from ~/.config/agentdash/context-windows.conf (<model-id-substring> <window-tokens>, first match wins), then built-in defaults, then self-correction: if observed context exceeds the assumed window, the larger tier is adopted and written back to the conf.

Port flags: NEW is first seen since the previous run, dup-cwd is two listeners in one project directory, no-agent is a tty-less listener in a project directory no agent or tmux pane is using.

Privacy

The TASK column shows prompt text and the cache at ~/.cache/agentdash/usage.json persists it (mode 0600). agentdash grep reads the prompt and reply text of your transcripts to search them; it prints matching snippets to your terminal (and --tools widens the search to tool payloads, which may include pasted secrets). agentdash usage reads prompt text too — it shows each session's task in the attribution table — but reports only token counts, never credentials, and makes no network call. Nothing leaves the machine and no new file is written by grep or usage. agentdash trail also reads transcript text (commands, file paths, and — for the secrets scan — the conversation body); trail secrets masks every match to its first 4 characters and never prints or persists the full value, and no trail subcommand writes a file. Mind screen-sharing and log shipping.

License

MIT.

About

No description, website, or topics provided.

Resources

License

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors