Skip to content

feat(rex): add a Rex plugin and clauth rex install - #149

Open
harrisrobin wants to merge 8 commits into
uwuclxdy:mommyfrom
harrisrobin:feat/rex-plugin
Open

harrisrobin wants to merge 8 commits into
uwuclxdy:mommyfrom
harrisrobin:feat/rex-plugin

Conversation

@harrisrobin

@harrisrobin harrisrobin commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

the problem: my operator moved from herdr to Rex and asked that "when i tell rex to open a tab or pane in a new session etc, it should use clauth so that i can benefit from what clauth gives me which is routing my subscription across multiple accounts etc based on usage", working "similarly to how it works with herdr". It started as a local plugin and grew into a request to upstream it as clauth rex install.

This PR adds a Rex plugin beside herdr-plugin/, plus clauth rex install and clauth rex uninstall to set it up.

Rex is in beta. It is a terminal multiplexer for coding agents (sessions, tabs, panes; a CLI, a Lua config, a rex do scripting runtime, and the OSC 7501 program-status protocol). Its Lua API can still change, so this plugin could break on a future Rex build. I checked everything below against Rex 0.1.0 builds 1541 and 1584 (October 2026) on macOS.

What the plugin does

It does the same three jobs as the herdr plugin, built on Rex's own extension points:

  1. Claude Code through clauth. The clauth_claude (split) and clauth_claude_tab actions open a pane running clauth start --auto --with-fallback, so a new session starts on the fallback-chain member with headroom and moves on as accounts run out. A profile= argument pins one. Bare claude in a Rex pane runs on the global login, and that is what this fixes for my operator.
  2. Dashboard. clauth_dashboard opens the clauth TUI in a Rex floating layer. A second press closes it.
  3. Per-pane account label. Every pane running Claude Code gets the account appended to its label: claude · work, codex · codex for codex started through clauth or on an adopted codex login, or claude · main (global) for a bare claude. Rex has no custom sidebar field like herdr's metadata tokens, so the label is the only place the account can go. The pane's original label comes back when the agent exits.

How it is built (rex-plugin/)

  • init.lua is a Lua module loaded with require("clauth").setup{} from Rex's init.lua. It defines the three actions and binds three default keys.
  • Rex only runs event handlers in rex do scripts, not in init.lua. So on every config load, init.lua starts start-watch.sh, which runs one rex do --all tag-watch.lua per Rex server. Each watcher is pinned to its parent server's --listen socket, because several Rex versions can run at once. A mkdir lock and a pid check against the watcher's own argv keep it to one watcher per server.
  • tag-watch.lua listens for terminal.process_changed. For a foreground claude, clauth or codex, it runs resolve-profile.sh. It also re-resolves tagged panes every 10 s, since a --with-fallback swap fires no Rex event.
  • resolve-profile.sh repeats herdr-plugin/report-profile.sh's join: the pid chain against ~/.clauth/live_sessions/, current_member over start_profile, skipping clauth mcp hops, the adopted-codex readlink, and clauth which for a bare claude. It also uses the same /proc-or-ps readers, so it works on Linux and macOS. I copied the logic rather than sharing it, so the two scripts now need to change together. The README says so. It also prints the agent, claude or codex, since Rex doesn't report one the way herdr does: for a clauth start pane that comes from the row's harness, because the pane's process is the clauth supervisor for either agent.

clauth rex install / uninstall (src/rex.rs)

Rex has no plugin manager, so install is small:

  • It writes the four plugin files to clauth/ beside Rex's init.lua. The files come from the binary through include_str!, so they always match the installed clauth. It finds init.lua the way Rex does: REX_CONFIG, then $XDG_CONFIG_HOME/rex, then ~/.config/rex.
  • It appends one marked require("clauth").setup{} block to init.lua.
  • It runs rex config reload when the CLI is on PATH.

Safety rules:

  • Install refuses a clauth/ directory without its .clauth-managed marker, and refuses a symlink there (the dev setup, linking a checkout).
  • It leaves init.lua alone when it already has its own require("clauth").
  • Rerunning install refreshes the files and adds no second block.
  • Uninstall removes only the marked block, leaving the rest of the file byte for byte, and removes only a directory clauth wrote.
  • Both confirm first, and --yes skips the prompt, mirroring clauth herdr. I made herdr's confirm pub(crate) rather than copying it.
  • Windows is refused, since the scripts are POSIX sh.

This is deliberately much smaller than clauth herdr install. There is no auto-update or heal (the user reruns install after upgrading), no Services-tab row, and no daemon or MCP integration. I left those out until you decide whether Rex is worth supporting at that depth.

User-visible changes (per CONTRIBUTING)

  • New subcommand clauth rex, with install and uninstall, --yes, and help text. Completions are added for bash, zsh and fish.
  • Three default Rex key bindings, which live in Rex's config and not clauth's: alt+cmd+c and shift+alt+cmd+c split a Claude pane right or down, and shift+cmd+c toggles the dashboard. I picked them because they collided with nothing on my operator's setup (I could not find Rex's default keymap documented, so I can't promise they never will), and shift+…+c echoes the herdr plugin's prefix+shift+c. Any of them can be turned off with keys = { … = false }.
  • The default account selection for plugin-launched sessions is --auto --with-fallback, which can be changed with select = { … }. Without a profile, clauth start would refuse to run, so the plugin needs some default.
  • No existing default, key or wording in clauth changes.

Docs

  • wiki/Rex-Plugin.md, shaped like Herdr-Plugin.md.
  • rex-plugin/README.md.
  • A row in README, wiki Home and the sidebar, plus a Quickstart entry.

All of them say Rex is in beta.

Commits

  1. feat(rex): add a Rex plugin beside the herdr one: the scripts only.
  2. feat(rex): add clauth rex install and clauth rex uninstall: src/rex.rs, tests/inline/rex.rs, CLI wiring and completions.
  3. docs(rex): document the Rex plugin
  4. fix(rex): label a clauth-started codex pane codex, not claude: found while making the screenshots, where a clauth start <codex profile> pane read claude · codex.
  5. docs(rex): say a clauth-started codex pane gets the codex label

agent

  • Tool: Claude Code (claude-opus-5-5), operated by @harrisrobin.
  • Checks I ran, on rustup stable 1.98.1, macOS arm64:
    • cargo fmt --all -- --check is clean. My machine has a global rustfmt config, so I ran it with --config-path pointed at an empty file. Without that, the clean tree fails too.
    • cargo clippy --locked --all-targets --all-features -- -D warnings is clean. FYI, Homebrew's clippy 1.99 flags two redundant_closure errors on current mommy, in src/daemon/status_json.rs:383 and tests/inline/daemon_status_json.rs:2573. That is unrelated to this PR, but CI's "latest stable" may hit it.
    • CLAUTH_REQUIRE_SHELL_LINT=1 cargo test --locked --all-targets --all-features: 8755 passed, 21 failed. 6 of the failures were completion tests that failed only because fish wasn't installed. With fish installed, all 51 completions:: tests pass. The other 15 failures are TLS and socket tests (daemon::api::terminal, daemon::api::tests, push::tests, one daemon::bridge test), which fail at TLS client setup. The same tests fail on commit 1, which has none of my Rust changes, so I read them as specific to this machine.
    • The 20 new rex:: tests pass. They cover path resolution, the block edit (append, idempotence, hand-written require, removal, unterminated block), install and uninstall against temp dirs (foreign dir, symlink, executable bits, reinstall, round trip), and resolve-profile.sh run against a faked /proc and registry (both harnesses, a row with no harness, a bare codex with and without an adopted login, and a bare claude).
    • I ran target/debug/clauth rex install / uninstall with REX_CONFIG pointed at a temp dir against a real Rex server. It refused with no TTY and no --yes. With --yes it wrote the files, appended the block once, and reloaded Rex. Uninstall restored init.lua exactly.
    • The plugin has run live on two Macs, each with its own Rex server and clauth fallback chain. I checked:
      • Labels on Claude, codex (bare on an adopted login, and through clauth start) and bare-claude panes, and the label coming back when Claude exits.
      • That a label like deploy · prod survives.
      • That restarting the watcher doesn't double-tag.
      • That a --with-fallback session reports follows_chain: true.
      • The tab and split actions, the dashboard toggle, and one watcher per server across reloads.
  • Operator verification: unknown.
  • Untested: Linux. The /proc paths and start-watch.sh's /proc/<pid>/exe lookup only ran under sh -n and shellcheck. Also untested: Rex builds other than 1541 and 1584, and installing through clauth rex install on a machine already running a hand-copied plugin (by design it refuses that as a foreign directory).
  • Least sure about:
    • Whether you want Rex support at all while Rex is in beta.
    • The label format and the three default keys.
    • The duplicated join in resolve-profile.sh.
    • Whether clauth rex install should eventually get the herdr-style heal instead of "rerun after upgrading".

https://claude.ai/code/session_01S5npf9mJwNArHszhk4v1eW

rex-plugin/ gives Rex (superlogical.com/rex, in beta) what herdr-plugin/
gives herdr: actions that open Claude Code through `clauth start --auto
--with-fallback`, the clauth dashboard in a floating pane, and a pane label
naming the account each Claude Code or codex pane spends.

Rex loads it as a Lua module (`require("clauth").setup{}` from init.lua).
Rex runs event handlers only in `rex do` scripts, so init.lua starts
start-watch.sh, which runs one tag-watch.lua per Rex server. The watcher
resolves a pane's foreground pid with resolve-profile.sh, the same
live-session join and adopted-codex readlink herdr-plugin/report-profile.sh
uses.

Claude-Session: https://claude.ai/code/session_01S5npf9mJwNArHszhk4v1eW
Rex has no plugin manager: a plugin is a Lua module beside Rex's own
init.lua. `clauth rex install` writes the rex-plugin/ files compiled into
the binary to `clauth/` beside init.lua (REX_CONFIG, then
XDG_CONFIG_HOME/rex, then ~/.config/rex, Rex's own lookup) and appends one
marked `require("clauth").setup{}` block, then runs `rex config reload`
when the CLI is on PATH. Rerunning it refreshes the files after an
upgrade and adds no second block.

It refuses a plugin directory clauth did not write (no `.clauth-managed`
marker) and a symlink onto a checkout, and leaves init.lua alone when it
already has its own require("clauth"). `clauth rex uninstall` takes the
block out, leaving the rest of the file byte for byte, and removes only a
directory clauth wrote. Both confirm first; `--yes` skips it.

herdr's `confirm` becomes pub(crate) so both commands share one prompt.
Completions gain `rex install|uninstall` and `--yes` in all three shells.

Claude-Session: https://claude.ai/code/session_01S5npf9mJwNArHszhk4v1eW
A wiki page in the shape of Herdr-Plugin.md (install, updates, uninstall,
actions and keys, options, pane labels, what it runs as you), the plugin's
own README, and a row in the README, wiki Home and sidebar, plus a
Quickstart entry for `clauth rex install`. Each says Rex is in beta.

Claude-Session: https://claude.ai/code/session_01S5npf9mJwNArHszhk4v1eW
@uwuclxdy

Copy link
Copy Markdown
Owner

oh awesome! will review soon, could i see how it looks tho?

@uwuclxdy uwuclxdy added this to the 0.19.0 milestone Oct 29, 2026
@harrisrobin

Copy link
Copy Markdown
Contributor Author
CleanShot 2026-11-01 at 9 37 15 PM@2x

@uwuclxdy

What do you think?

A pane running `clauth start <codex profile>` was labeled
"claude · <profile>". Its foreground process is the clauth supervisor
for either agent, so resolve-profile.sh reported only the account and
how it is spent, and tag-watch.lua named every clauth launch "claude".

resolve-profile.sh now prints a third field, the agent: the live-session
row's harness for a clauth launch (rows from before that field are
Claude Code, as clauth reads them), codex for an adopted codex login,
and claude for a bare claude. tag-watch.lua names a pane that has no
label of its own after it.

The new rex:: tests run the real script against a faked /proc and
registry: both harnesses, a row with no harness, a bare codex with and
without an adopted login, and a bare claude.

Claude-Session: https://claude.ai/code/session_01S5npf9N7fT9knNbKwdkNeh

@uwuclxdy uwuclxdy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the plugin and the screenshot! Rex goes in as experimental support, and two pieces move into clauth itself so neither plugin carries its own copy:

  • clauth which --pid <pid> resolves the account a process spends, the join resolve-profile.sh and herdr's report-profile.sh each do today: the registry row up the parent chain skipping clauth mcp, current_member over start_profile, then the adopted codex login or the global one. Plain output is the profile name, --json adds source (live_session, codex_adopted, global) and harness (claude, codex), and it prints unknown when nothing resolves, like clauth which. From 0.19.0 both print nothing instead, so treat empty and unknown alike. I'll land it on mommy first.
  • a read-only Rex row in the Services tab: installed, stale against the running clauth, watcher alive. I'll add that too. It reads the .clauth-managed marker, so keep its written by clauth <version> line as it is.

Keys, the label format and the --auto --with-fallback default are fine as they are.

Comment thread src/rex.rs Resolved
Comment thread rex-plugin/start-watch.sh Resolved
Comment thread rex-plugin/resolve-profile.sh Resolved
Comment thread wiki/Rex-Plugin.md Resolved
A temp-and-rename replaced a symlinked init.lua (a dotfiles checkout)
with a plain file and reset its mode, so the checkout never got the
block. Install and uninstall now write it in place, as `clauth herdr
install` writes herdr's config.toml.

Claude-Session: https://claude.ai/code/session_01S5npf7xz73gi937TG5ssdw
start-watch.sh exits while its pidfile names a live watcher, so after a
reinstall the old tag-watch.lua kept running until Rex restarted, and
after uninstall it kept running with no resolver. Install and uninstall
now SIGTERM each recorded watcher whose pid still runs tag-watch.lua,
and uninstall removes ~/.local/state/clauth-rex/. A linked or foreign
plugin directory keeps its watcher and state, since its checkout runs
them.

Claude-Session: https://claude.ai/code/session_01S5npf7xz73gi937TG5ssdw
Say it in the wiki page, the README and Home rows, the Quickstart entry
and rex-plugin/README.md: nothing heals or updates the install, the
Services tab has no controls for it, and a new Rex build can break it.
Also say that install and uninstall stop the pane-label watcher, that
uninstall removes ~/.local/state/clauth-rex/, and that a symlinked
init.lua is written in place.

Claude-Session: https://claude.ai/code/session_01S5npf7xz73gi937TG5ssdw
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants