agentscan is a standalone replacement stack for tmux agent discovery.
The current shell workflow in ~/.dotfiles does too much work at TUI launch time:
- full tmux pane scans
- shell-heavy parsing
- optional process inspection
- pane capture heuristics for activity state
This project starts from a simpler baseline:
- a Rust binary
- tmux metadata as the primary source of truth
- no
psscan in the steady-state path - no fast/full mode split
- plug-and-play detection as a core product invariant
- no provider log, transcript, or session-store scanning in the default detection path
Common agent panes should be discoverable without asking users to install provider hooks, extensions, launch wrappers, or shell integration. Those integrations may eventually enrich labels, session ids, or state, but they are deep-roadmap additions behind source analysis, local probing, and conservative plug-and-play detection.
The shell scripts in ~/.dotfiles are reference material, not the target design.
They are useful for understanding current user-visible behavior and edge cases, but
they do not define a requirement to preserve the same implementation strategy,
flags, heuristics, interactive flow, or output shape.
This repository is the central source for the product. If tmux helpers or shell
integration are still needed while the product matures, they should live here
rather than being developed inside host-specific dotfiles. The host workflow can
remain unchanged until agentscan is ready to replace it.
docs/index.md: map of the repo's progressively disclosed documentationAGENTS.md: repo-local agent guardrails and conventionsROADMAP.md: durable product direction, boundaries, and decision logCHANGELOG.md: unreleased user-facing changes and migration notesdocs/architecture.md: runtime model, daemon/socket contract, command families, and guardrailsdocs/integration.md: wrapper metadata, daemon-backed automation surfaces, picker/live stream contracts, shell boundary, and migration posturedocs/daemon-operations.md: daemon auto-start, status, telemetry, and troubleshootingdocs/desktop.md: desktop app operation, local/SSH profiles, picker behavior, and debug logdocs/desktop-client-contract.md: local/SSH desktop command contract and failure surfacesdocs/harness-engineering.md: progressively disclosed harness engineering approach for the repodocs/macos-release-signing.md: local and GitHub Actions Developer ID signing/notarization workflowdocs/desktop-release-smoke.md: macOS desktop build, signing, install, and smoke workflowdocs/desktop-platform-posture.md: desktop platform posture and future adapter seams
Active milestone sequencing lives in Linear. The repo docs are intentionally for stable engineering guidance and operator-facing contracts, not live task tracking.
Current local baseline:
cargo fmt --all --checkcargo clippy --all-targets --all-features -- -D warningscargo clippy --all-targets --all-features -- -D warnings -W clippy::cognitive_complexity -W clippy::too_many_argumentscargo test
Desktop shell checks:
cd desktop && npm run buildcargo test --manifest-path desktop/src-tauri/Cargo.tomlcd desktop && npm run tauri devscripts/check-desktop-version.sh
Current test coverage also includes committed file-based fixtures for representative tmux title snapshots and snapshot envelopes, plus property tests for parser and normalization invariants, so parser and schema regressions can be checked against both stable examples and generated inputs.
Daemon reliability is also covered by isolated integration tests that start a temporary tmux server, run the daemon, and assert state behavior for title changes, pane/window add-remove events, session add/remove and rename events, window rename events, attached-session removal, wrapper-metadata helper flows, and tmux server disappearance.
Current performance tooling:
cargo bench --bench core_paths -- --noplot
The benchmark target covers snapshot row parsing, row-to-pane conversion, snapshot deserialization, and interactive row rendering against committed fixtures.
The core architecture is a daemon-required, socket-backed model:
- the daemon is the single source of live pane state
- normal consumers auto-start the daemon unless explicitly opted out; on macOS, detached auto-start runs only after parent-side executable trust preflight succeeds
- consumers read full
SnapshotEnvelopeframes over a Unix socket - the cache file is removed as an IPC boundary
- the interactive command is
agentscan tui - the
agentscan cachecommand family is removed; useagentscan snapshotfor raw snapshot envelopes agentscan scanand refresh-capable command flags remain direct tmux recovery paths that do not start or require the daemon
Active future sequencing lives in Linear.
The current branch centers on:
- direct tmux snapshots from
tmux list-panes -a -F ... - a control-mode daemon that serves socket snapshots
- repo-local tmux helpers that stay thin and call the CLI
- a Mac-first Tauri desktop shell in
desktop/that talks to the installedagentscanCLI through a narrow IPC/preflight boundary - local and SSH desktop profiles that consume the same CLI command contract
- a runtime split by concern under
src/app/rather than a single monolithic application file
It can:
- run the daemon with tmux control mode and auto-start it for normal consumers, including signed/trusted macOS binaries
- fail fast when the daemon loses tmux, leaving restart policy to an external supervisor
- preserve raw tmux
session_idandwindow_idvalues in the canonical pane model for socket consumers and local daemon updates - refresh individual panes on daemon title and metadata updates, refresh affected windows or sessions when tmux emits stable ids for those scopes, and keep a periodic full reconcile as a safety net
- preserve helper-published metadata across unrelated daemon writes
- bypass daemon-backed state with a fresh direct tmux snapshot for refresh-capable one-shot commands using
-f/--refresh - list panes through the default
listflow - inspect a pane by
pane_id, including provider source, status source, classification reasons, and targeted/procfallback decisions - focus a pane by
pane_id, with attached-client fallback when no explicit tty is provided and tested multi-client selection of the most recent attached client - open an interactive
agentscan tuiUI directly from the Rust binary - bootstrap
agentscan tuifrom a live daemon socket subscription, show connection state, preserve the last snapshot while reconnecting, and avoid cache/direct-tmux discovery fallback in the TUI - page TUI rows when more panes exist than can fit the current key budget or viewport
- redraw the TUI immediately on terminal resize and keep keys stable for rows that remain visible on the current page
- infer likely agent panes from tmux metadata
- normalize noisy provider prefixes and wrapper/script suffixes out of display labels for title-driven panes
- populate
display.activity_labelfor meaningful title-driven panes and authoritative wrapper labels, including non-generic Codex wrapper titles - keep labels conservative: show what tmux metadata actually tells us and avoid inventing richer task names from weak signals
- use targeted live process evidence, including pane TTY foreground process groups, only for unresolved ambiguous panes
- use tightly scoped provider-specific pane output parsing as a final status
fallback for already-identified supported providers. When this path wins, JSON
reports
status.source="pane_output". - treat Cursor CLI as metadata-first: command detection is enough to identify the provider, but generic tmux titles fall back to conservative pane labels until wrappers publish stronger metadata
- infer Cursor CLI busy/idle status from the current Cursor footer only after provider identity is already established
- infer GitHub Copilot busy/idle status from current Copilot prompt, footer, thinking, and trust-prompt shapes only after provider identity is already established
- classify Factory Droid CLI panes from the exact
droidcommand or explicit metadata aliases, treat⛬ ...titles as display labels only after provider identity is known, and infer busy/idle from the current Droid prompt/footer only after identity is established - classify Grok and Hermes panes from provider-specific command/title/metadata evidence while keeping pane-output status fallback provider-scoped
- resolve unresolved Claude Code launcher panes from targeted process evidence, including Claude Code CLI paths and tmux teammate-spawn argv/env markers
- classify Antigravity CLI panes from the exact native
agycommand while keeping status unknown until wrapper metadata or a future provider-scoped output fallback supplies direct state - classify Pi coding agent panes from upstream-observed Greek terminal titles,
Linux
PI_CODING_AGENT=trueprocess evidence, and targeted package or shim path evidence while keeping barepicommands conservative - classify opencode panes from upstream-observed
OpenCode/OC | ...terminal titles, targeted package or shim path evidence, and LinuxOPENCODEprocess evidence while keeping default opencode status unknown unless explicit metadata publishes state - publish, clear, and consume explicit wrapper metadata via pane-local
@agent.*tmux options - emit canonical snapshot JSON
Automation contract:
agentscan tuiis interactive-only and is not a supported machine-readable surfaceagentscan popuphas been removed rather than kept as a compatibility alias- local unsupported flags on the interactive command should remain normal parse errors,
and root-level
--formatrouted to it should fail with migration guidance; do not add TUI-specific compatibility shims to intercept or emulate legacy formatting agentscan list --format jsonis the supported machine-readable command for downstream consumers in normal automation flowsagentscan list --all --format jsonis the supported way to include non-agent panes in that machine-readable outputagentscan snapshot --format jsonexposes the raw snapshot envelope when a consumer explicitly needs envelope details rather than the normallistviewagentscan subscribe --format jsonexposes live JSON Lines daemon events for terminal-adjacent tools and desktop clientsagentscan providers --format jsonexposes supported provider names, display markers for all icon modes, marker codepoints, and matching aliasesagentscan hotkeys --format jsonexposes the shared picker row modelagentscan hotkey <key>activates a shared picker key through the same focus path- TUI-shaped TSV or JSON output is not a supported long-term contract
Operational commands:
agentscanagentscan scanagentscan listagentscan inspect <pane_id>agentscan focus <pane_id>agentscan daemon startagentscan daemon runagentscan daemon statusagentscan daemon stopagentscan daemon restartagentscan snapshotagentscan subscribeagentscan providersagentscan hotkeysagentscan hotkey <key>agentscan tuiagentscan tmux set-metadataagentscan tmux clear-metadata
agentscan without a subcommand runs the default daemon-backed list flow.
For local ad-hoc macOS builds or debugging detached-start failures, run the
daemon in the foreground:
agentscan daemon runFor repo-local tmux display-popup testing without installing the binary on
PATH, use tmux display-popup -E "$PWD/target/debug/agentscan" tui after
building once.
agentscan reads optional user configuration from:
# ${XDG_CONFIG_HOME:-~/.config}/agentscan/config.toml
icons = "emoji"Supported icon modes:
emoji: default provider icons for terminals without Nerd Font coveragenerd-font: current Nerd Font provider iconsnerd-font-patched: reserved for a future custom patched Nerd Font; it currently falls back to thenerd-fontvalues
Icon mode precedence is CLI, then environment, then config file, then default:
agentscan list --icons nerd-font
AGENTSCAN_ICONS=nerd-font agentscan tuiagentscan providers previews the active text icon mode, and
agentscan providers --format json exposes every icon mode and codepoint for
scripts or font tweaking.
Machine-readable consumers should not call agentscan tui. The legacy
agentscan popup command has been removed and is not a compatibility path.
Use:
agentscan list --format jsonfor the supported JSON automation surfaceagentscan list --all --format jsonif the consumer previously depended on interactive--allagentscan snapshot --format jsononly when the consumer intentionally needs the raw snapshot envelopeagentscan subscribe --format jsonfor live JSON Lines daemon eventsagentscan daemon status --format jsonfor daemon lifecycle and readiness checksagentscan providers --format jsonfor supported provider names, display markers for all icon modes, marker codepoints, and aliasesagentscan hotkeys --format jsonfor shared picker rowsagentscan hotkey <key>for simple picker-key activationagentscan scanor supported--refreshflags when a script intentionally needs direct tmux state instead of daemon state
Keep agentscan tui in tmux key bindings and other human-facing launch paths.
Do not call it from scripts that parse stdout, and do not depend on terminal
rendering, row ordering, key labels, or error frame text as a data contract.
The removed agentscan cache command family and AGENTSCAN_CACHE_PATH are not
compatibility paths. Use daemon socket snapshots through the documented command
surfaces instead.
If an automation consumer cannot migrate because required fields are missing from
the documented JSON surfaces, treat that as an API gap to close in list or
snapshot JSON. Do not add --format back to the interactive command, including
hidden or compatibility-only parser paths.
The existing shell stack is still relevant as an input to design work. It shows the kinds of things users currently rely on:
- pane discovery across tmux
- stable pane targeting for navigation and focus
- provider inference and title normalization
- interactive pane selection and targeting
- rough busy/idle detection for some providers
But those scripts should be treated as a source of examples and migration context,
not as a contract for the Rust implementation. agentscan is free to adopt a
different internal model and different external commands as long as the new design
is faster, clearer, and intentionally documented.
The useful design inputs are mostly at the data-model level:
- wrapper-aware provider classification
- separation between raw pane metadata and cleaned display labels
- explicit
unknownstatus when a fast answer is better than an expensive guess - explicit status provenance.
status.sourcecan betmux_title,pane_metadata,pane_output, ornot_checked;pane_outputmeans a provider-scoped current prompt/footer pattern supplied the status after stronger metadata/title sources were unavailable. - stable pane identity for downstream consumers such as TUIs or focus commands
The CLI centers on:
agentscan daemonas the primary runtimeagentscan scanfor direct tmux snapshotsagentscan listfor normal human output and the supported JSON automation surfaceagentscan inspectfor one-pane diagnosticsagentscan focusfor pane targetingagentscan snapshotfor raw snapshot-envelope outputagentscan subscribefor live JSON Lines daemon eventsagentscan providersfor supported provider/icon metadataagentscan hotkeysfor the shared picker row modelagentscan hotkeyfor picker-key activationagentscan tuifor interactive pane selection onlyagentscan tmuxfor tmux-facing integration helpers
Shell remains the right place for aliases, launch wrappers, tmux binds, and
TUI entrypoints. agentscan owns pane discovery, provider classification,
metadata consumption, daemon lifecycle policy, and the documented JSON surfaces
those shell entrypoints can call.