See what your coding agents are still doing after they stop talking.
Rex shows whether an agent is working,
blocked, done, or idle. It cannot tell you that a "done" Claude Code session
is really waiting on a background shell, tailing a build with a monitor, or will
wake itself up at 14:30 to check CI again. These hooks can.
They publish that work as extra records beside the agent's own program status, on the agent's own pane:
record state title
(the agent's own) done
rsact/bg/bglint01 working Run the test suite
rsact/mon/bgwatch2 working Watch the build log
rsact/next/1793794440.loop idle Check CI
It tracks, per agent session:
- Background shells started with
run_in_background - Monitors watching a stream or log
- Scheduled checks: session crons (
CronCreate,/loop) and dynamicScheduleWakeuploops, with the next fire time - Subagents of agents that do not report their own (Codex). Claude Code already reports its subagents to Rex, so they are left to it
It works standalone: there is no daemon and nothing else to install. Any Rex client can read the records (see CONTRACT.md). A Stream Deck plugin or a dashboard may use them to show more, and is optional in both directions: neither needs the other.
Verified against Rex API revision 10, with hook events recorded from Claude Code
2.1.318. Codex 0.166.0 hooks are only known from codex exec; its interactive mode
has not been run.
Free to use. MIT. Not an official Rex or Elgato product.
Requires Node.js 20+ on macOS or Linux, and an agent running in a Rex pane. Linux is tested without a Rex server.
npx github:Resilient-Software/rex-agent-activity installor from a clone:
node bin/cli.mjs installRestart running agents afterwards so they pick up the hooks.
Codex asks you to review the new hooks once per account on next launch; until then they do not run.
| Path | What |
|---|---|
settings.json in every Claude Code home (~/.claude, ~/.claude-N, $CLAUDE_CONFIG_DIR) |
Hooks, next to your existing ones |
hooks.json in every Codex home (~/.codex, ~/.codex-N, $CODEX_HOME) |
Hooks, next to your existing ones |
<file>.bak-rex-agent-activity |
A copy of each file above as it was before the first edit. Made once, and only if the file existed |
${XDG_CONFIG_HOME:-~/.config}/rex-agent-activity/hook.sh |
The command the hooks run |
${XDG_DATA_HOME:-~/.local/share}/rex-agent-activity |
A copy of this package's runtime |
${XDG_STATE_HOME:-~/.local/state}/rex-agent-activity |
One small JSON file per active session and pane, and a size-capped log. Readable only by you. What a killed agent leaves goes after 30 days |
--dry-run prints the files it would edit and changes nothing. --home <dir>
limits it to that Claude Code or Codex home, and can be repeated. Both work with
install and uninstall. A settings file that is a symlink is refused rather
than replaced, and --dry-run reports that too.
npx github:Resilient-Software/rex-agent-activity doctoror node bin/cli.mjs doctor from a clone. doctor lists each agent home and its
hook count, whether the shim and runtime are in place, and the last lines of the
log.
npx github:Resilient-Software/rex-agent-activity uninstallor node bin/cli.mjs uninstall. This removes the hooks, the shim, the runtime and
the state. A settings file you have not otherwise changed is restored byte for
byte and its backup removed. If you changed it since, only the hooks are taken out
and the backup stays. A file install created goes with its hooks, unless it has
since gained something else. A file without these hooks is not touched, and neither
are hooks belonging to anything else.
Uninstall cannot reach into a running pane: records already published stay until that agent exits cleanly. An agent that is still running may keep calling the removed hook until you restart it, and those calls fail.
Claude Code hooks (PostToolUse for background-capable tools, SubagentStart,
SubagentStop, Stop, UserPromptSubmit, SessionStart, SessionEnd) feed a
small reducer per session. Stop and SubagentStop carry Claude Code's own
snapshot of running background tasks and session crons, which the reducer treats
as authoritative, so missed events self-correct at the end of every turn.
Task-completion notifications end tasks between turns.
The hook finds the agent's pty by walking up its parent processes, and writes
OSC 7501 reports to
it in writes of at most 512 bytes, never splitting a report. It never calls the
rex CLI. A write stuck for 1.5 seconds kills the hook, never the agent, and later
events finish what it could not. Hooks always exit 0 without writing to stdout. On
Claude Code they run asynchronously and add no latency. On Codex they are not marked
asynchronous and take about 200 ms per event on a Mac.
Stop, UserPromptSubmit and SessionStart rewrite every record, because Rex can
drop one with no hook knowing. What has been published is tracked per pane, so a
/clear, which starts a new session id, still clears the old session's records.
On a clean exit Claude Code removes every record itself. An agent killed with
SIGKILL can leave records behind; the next agent to start in that pane clears them
if it runs these hooks.
The hook stays out of the way when REX_BLOCK is not set, in panes where another
terminal multiplexer publishes its own activity, and in an agent started by another
agent in the same pane, so a nested agent cannot clear its parent's records. A
shell script named like the agent, such as a wrapper at ~/bin/claude, is not
counted as another agent. An agent on a pty of its own, such as a worker under tmux
or script, inherits REX_BLOCK but counts as a separate pane: it writes to that
pty, keeps its own list of what it published, and cannot touch the session of the
pane it came from.
Codex exposes fewer hooks: subagents are tracked (SubagentStart /
SubagentStop); background shells and schedules are not visible to Codex hooks
today. If you run Codex through its shared app-server daemon, its hooks have no
pane to report to and do nothing; launch Codex with --no-daemon for per-pane
activity. Not yet run under Rex.
There is no daemon, so a recurring check's next time refreshes on the next hook event.
Reports reach Rex through the pty, the same way as the agent's own output. While a program floods the terminal with large writes, the kernel can split one of them, and a report can land inside one of its escape sequences and garble a line. In measurements on macOS this took about 1 MB/s or more of sustained output, and hit a few lines per hundred report writes. Hooks write little, mostly when a turn starts or ends, and only for sessions with background work.
npm test
npm run e2enpm test needs nothing but Node and is what CI runs. npm run e2e runs locally,
because it needs a Rex binary and hosted CI cannot get one: it starts a throwaway
Rex server on its own socket, runs a fake agent in one of its panes, fires
bin/hook.mjs from inside that pane, and reads the records back through
rex block call. HOME and the XDG directories point at a temp directory. It finds
rex through REX_PATH, PATH or the macOS install location, and puts its files
under E2E_TMP (default /tmp), which must be short enough for a unix socket path.
MIT © Resilient Software