Drive Codex threads from your terminal — or from Claude Code, as a skill.
List and read your threads, start new tasks, and steer, queue, interrupt, or re-model a turn that is running inside the Codex desktop app.
$ clauodex active
ID STATUS OWNER AGE SOURCE TITLE CWD
-------- ------- --------- --- ------ -------------------------- -----------------------
019f7191 running codex-app 22m vscode Reach #3 in ICML challenge ~/Documents/Codex/…
$ clauodex steer 019f7191 "stop rewriting the parser, just fix the off-by-one"
steered 019f7191 (turn 019fac00)Zero dependencies. One Python file. Python 3.8+.
Codex has no supported API for "what is my desktop app doing, and can you tell it something?" There are two protocols involved, and using only the first one gets you a read-only tool:
-
The app-server protocol — the JSON-RPC interface
codex app-serverspeaks. Great for listing, reading, and running threads. But "loaded" and "running" are per-process memory, and the desktop app's app-server is a stdio child of the Electron process with no listening socket. Nothing you start yourself can steer a turn the app is running. -
The desktop app's IPC router — an undocumented message bus the Electron process listens on, which exists so a second Codex client can drive a thread the app owns. This is the part that makes steering work.
Clauodex speaks both and routes each command to whichever process actually owns
the thread. Full protocol notes, including everything needed to reimplement the
IPC side, are in reference/protocol.md.
git clone https://github.com/SSHdotCodes/clauodex.git ~/.claude/skills/clauodex
ln -s ~/.claude/skills/clauodex/scripts/clauodex.py /usr/local/bin/clauodexCloning to ~/.claude/skills/clauodex registers it as a Claude Code skill —
ask Claude "what are my Codex threads doing?" and it will use it. The symlink is
only needed if you also want it on your $PATH; skip it to use the skill alone.
Then check your setup:
clauodex doctoractive |
threads with a turn running right now, and who owns each |
threads |
browse threads — --search, --cwd, --live, --archived |
read <thread> |
print the conversation — --last N, -v for reasoning and full output |
new "<prompt>" |
start a task — --cwd, --model, --effort, --sandbox, --wait |
send <thread> "<msg>" |
send a message; queues automatically if a turn is running |
steer <thread> "<msg>" |
inject into the running turn |
queue <thread> "<msg>" |
run after the current turn finishes |
interrupt <thread> |
stop the running turn |
model <thread> <id> |
change model / --effort |
goal |
Codex goals (/goal) — list, show, set, edit, pause, resume, clear |
--from <thread> |
on new/send/steer/queue: send as another Codex thread, producing the app's "Sent by Codex from another chat" badge |
pin <thread> |
pin/unpin in the app sidebar |
wait <thread> |
block until the turn ends, print the reply |
tail <thread> -f |
live event stream |
approvals / approve <id> |
answer a blocked approval request |
open <thread> |
open it in the Codex desktop app |
release <thread> |
drop clauodex's hold so the app can own it |
doctor / status |
environment report / daemon state |
Plus models, name, fork, compact, archive, delete, and raw for
calling any app-server method directly. --json works on everything.
Threads accept a full id, any unique id prefix, or last.
A goal is an objective attached to a thread that Codex's server keeps working
toward, turn after turn, on its own. This is the same mechanism /goal uses in
the app — continuation lives in the Codex core, not in a loop clauodex runs.
clauodex new "start here" --goal "Get the test suite green" --goal-budget 2000000
clauodex goal list --active
clauodex goal edit <thread> "new objective"
clauodex goal pause <thread> # stops autonomous continuation
clauodex goal clear <thread>Codex sets the status itself as it works (active, paused, blocked,
usageLimited, budgetLimited, complete), so a goal going complete is the
agent reporting, not an error.
A goal-driven thread runs in the Codex app, with its goal pill and progress
visible there, exactly like one started with /goal. A goal and a live app view
are not a trade-off.
An active goal spends quota with nobody watching — --goal-budget is worth
setting. interrupt only ends the current turn; the goal will start another, so
use goal pause to actually stop an autonomous thread.
Clauodex runs threads in the background and never opens or focuses the Codex
app on its own. Making the app take over a thread requires a codex://
deeplink, which pulls the app to the front and switches the thread you are
looking at, so that is opt-in: new --open, or clauodex open <thread>.
Work still goes to the app whenever the app already holds the thread — that costs nothing and keeps its UI live. Otherwise clauodex runs it. The app-server enforces a per-thread writer lock, so opening a thread clauodex is running contends safely rather than corrupting it. Idle threads are released automatically, so opening one afterwards loads cleanly.
clauodex active labels every thread with where it actually runs:
| OWNER | Meaning | What works |
|---|---|---|
codex-app |
running in the app, because it already had the thread open | everything, and the app shows it live |
clauodex |
running under clauodex — the default for anything the app isn't already showing | everything, but not visible live until you open it |
app/cli |
running in another Codex client, e.g. a codex CLI session |
read only |
- |
idle | anything; it gets handed to the app |
--local forces the local path. clauodex release <thread> drops the hold
manually; idle local threads release themselves when they finish. A thread with
an active goal is handed to the app rather than merely dropped, so the goal
keeps running there — goal continuation happens in whichever app-server holds the
thread.
app/cli is genuinely read-only: a plain CLI session has no IPC router to route
through.
Codex Desktop's "one task manages other tasks" capability is a set of
host-injected codex_app__* tools — an external program cannot load them. What
it reproduces is their wire behaviour: the prompt is wrapped in a
codex_delegation envelope carrying the calling task's id and sent as ordinary
turn/start / turn/steer input.
clauodex send <target> "take over the migration" --from <source-thread>The app renders only the prompt, tags it "Sent by Codex from another chat",
and links back to the source task. clauodex builds the envelope byte-compatibly
with the app's own builder and decodes it on read.
A background daemon (started on first use) holds a codex app-server child
process and a client registration on the app's IPC router, so threads stay
loaded between CLI invocations and running turns can be steered. State lives in
~/.clauodex; threads themselves live in CODEX_HOME and are shared with every
other Codex client, which is why threads you create here show up in the app.
| Variable | Effect |
|---|---|
CODEX_HOME |
Codex data directory (default ~/.codex) — honoured like the Codex tools do |
CLAUODEX_HOME |
clauodex state directory (default ~/.clauodex) |
CLAUODEX_CODEX_BIN |
use a specific codex binary |
CLAUODEX_FORCE_TCP |
use the loopback-TCP daemon transport instead of a unix socket |
The codex binary is auto-detected, preferring the desktop app's bundled build
(usually a newer alpha, and always the build whose protocol version matches the
threads the app writes) and falling back to the standalone install layout and
then $PATH.
Platforms. macOS and Linux are supported. Everything works without the
desktop app installed — you just lose the codex-app routing, so app-owned
threads become read-only. On Windows the app-server features fall back to a
loopback-TCP daemon transport; driving the desktop app needs its unix-socket IPC
router and is not available there.
- Every turn spends your Codex quota. There is no dry-run.
- After a
steer, the in-flight turn is recorded asinterruptedand a fresh turn continues with your message injected. That is Codex's own steer semantics, not a failure. - On
codex-appthreads,waitandqueuedetect "turn finished" by watching the thread's rollout file go quiet (~8s lag), because the app does not expose its in-memory turn state. - Approvals for
codex-appthreads appear in the app's UI and are answered there;clauodex approvalscovers clauodex's own threads.
Built against Codex 0.146.0-alpha.3.1 (desktop app) and verified against
0.145.0 (Homebrew CLI). Both protocols are internal to Codex and unversioned
in any public sense — a Codex update can change them. clauodex doctor is the
first thing to run if something stops working, and
reference/protocol.md documents how to regenerate the app-server types and
re-extract the IPC method table from a new build.
MIT