codex-threads is a companion CLI for inspecting and controlling Codex
app-server threads from a terminal or another agent.
Important
codex-threads only sees threads on the Codex app-server instance it
connects to. This tool is built around running a shared, long-lived
codex app-server --listen <socket> (Unix domain socket or WebSocket),
pointing codex-threads at that socket, and starting interactive Codex TUI
sessions against the same socket with codex --remote <socket>. Codex
sessions started without --remote are not on that shared server, so
codex-threads cannot list, inspect, or control them — an empty thread list
usually means this setup is missing, not that something is broken. See
Quickstart for the full setup.
It exists for workflows the Codex CLI does not currently cover well: asking
what threads were active recently, what happened in a repo, whether a thread is
still running, and sending a follow-up to an existing session. The Codex desktop
app may expose some of this interactively; codex-threads makes it available
through a focused command-line interface.
The main use cases are coordinating Codex work as a user or through another agent: list recent sessions/threads, retrieve relevant transcript slices, summarize status and where work left off, spawn new Codex threads for background work, and relay user requests or follow-ups across those threads. Search is available when you need it, but it is not optimized and can be very slow over large histories; prefer recent listing, targeted transcript retrieval, thread creation, and direct follow-ups when those fit the workflow.
It talks to Codex app-server, the local control server exposed by the Codex agent runtime. In Codex terminology, a thread is one session and a turn is one user request/assistant response cycle.
codex-threads is built for headless Codex control by users who already run
Codex in yolo-style environments where sandboxing and approval policy are handled
outside the Codex application. Yolo mode is opt-out: by default, thread
creation, resume-before-action recovery, and turn start requests force Codex
app-server to use approvalPolicy = "never" and full-access sandboxing
(sandbox = "danger-full-access" or
sandboxPolicy.type = "dangerFullAccess"). Pass global --no-yolo to use the
app-server's configured approval and sandbox defaults instead. Do not use this
CLI as a safety boundary.
Features
- TOML configuration for named app-server endpoints.
- Direct
--connect unix:///path/to.sockand--connect ws://host:portdebug targeting. - Deterministic target selection with
--server,CODEX_THREADS_SERVER, or a single configured server. - Thread list, search, detail, status, and flattened message history commands.
- Interactive TUI browser for listing, searching, viewing, annotating, refreshing, sending, steering, interrupting, and creating threads.
- Local thread annotations projected into list, search, and detail output.
- Thread creation with required
--cwd. - Prompted
newandsendcommands that wait by default, stream human output, and support JSON final output or newline-delimited JSON (NDJSON) streaming. - Model, reasoning effort, and service-tier settings where Codex app-server supports them.
- Thread naming, archive/unarchive, active-turn steer/interrupt, model listing, and goal get/set/clear.
Screenshot
Install
Download the latest archive for your platform from GitHub Releases:
https://github.com/kcosr/codex-threads/releases
Supported release platforms are currently:
linux-x86_64macos-arm64
Install the extracted codex-threads binary somewhere on your PATH, for
example ~/.local/bin:
mkdir -p ~/.local/bin install -m 755 codex-threads ~/.local/bin/codex-threads codex-threads help
For unsupported platforms or local development, build from source in the Development section near the end of this document.
Quickstart
Prerequisites:
- Install the Codex CLI/runtime separately and ensure the
codexexecutable is onPATH. - Start Codex app-server with a Unix domain socket (UDS) or WebSocket listener before using this CLI.
When asking another agent to use this CLI, point it at the included skill:
codex-threads talks to a running Codex app-server:
CODEX_SOCK=unix:///path/to/codex.sock
codex app-server --listen "$CODEX_SOCK"codex-threads opts into Codex app-server experimental APIs during its
JSON-RPC initialize request by sending capabilities.experimentalApi = true.
No separate Codex feature flag is required in codex-threads. If the running
Codex app-server is too old or rejects that capability, commands that depend on
experimental methods fail with an app-server capability error.
Interactive Codex CLI sessions that should be visible to codex-threads should
connect to the same app-server with --remote:
codex --remote "$CODEX_SOCK" --cd "$PWD"
Without --remote, an interactive Codex CLI session may not be using the same
app-server that codex-threads queries and controls.
Use a direct socket when you do not want to create a config file:
codex-threads --connect "$CODEX_SOCK" models --jsonFor the common one-server case, configure one server at
~/.config/codex-threads/config.toml:
model = "gpt-5.5" model_reasoning_effort = "high" [servers.main] endpoint = "unix:///path/to/codex.sock"
See config.example.toml for a complete starting point.
Then omit --server:
codex-threads servers ping codex-threads list codex-threads new --cwd "$PWD" "Run the tests"
Or configure named servers when you have multiple app-server sockets:
[servers.main] endpoint = "unix:///path/to/main/codex.sock" [servers.work] endpoint = "ws://127.0.0.1:8765" model = "gpt-5.5" model_reasoning_effort = "low"
Then run commands against a server:
codex-threads servers ping --server main codex-threads list --server main codex-threads new --server main --cwd "$PWD" "Run the tests"
Successful servers ping human output is tabular:
This project targets Unix-like systems. Replace /path/to/codex.sock and
127.0.0.1:8765 examples with the endpoint you choose for your app-server.
Common Workflows
The examples below assume codex-threads is installed on PATH and a server is
configured or selected with --connect.
Examples that pipe JSON use jq; install it separately or replace those
pipelines with your preferred JSON tooling.
Find recent candidate threads, then inspect the selected thread:
codex-threads list --since 24h --limit 20 --json
codex-threads search "release process" --limit 10 --json
codex-threads messages THREAD_ID --role user --last 10 --max-turns 100
codex-threads messages THREAD_ID --last 8 --max-turns 50Use messages for readable recent context. Use show --items summary|full
when you need turn IDs, exact turn structure, or cursor-based paging:
codex-threads show THREAD_ID --last 10 --items summary --json codex-threads show THREAD_ID --asc --items full --json
Launch the interactive browser with the same initial filters:
codex-threads tui --since 24h --cwd "$PWD" codex-threads tui --query "release process" --limit 20
By default, codex-threads tui opens all configured servers and shows a
SERVER column when more than one target is visible. Use --server ALIAS or
CODEX_THREADS_SERVER to narrow the TUI to one server. --connect remains a
single direct target.
Inside the TUI, use j/k, arrow keys, or mouse wheel scrolling to move in the
browser and detail transcript; use gg and G to jump to top and bottom. Use
? for keyboard help, / to search threads or loaded transcript messages,
Enter to open a thread, p to toggle the lazy recent-message preview pane,
[ and ] to page browser results, f for filters, s for sort, c for
visible columns and updated-time display, a to annotate, e to rename, A
to confirm archive or unarchive,
i to confirm interrupting the selected active thread, r to refresh, y to copy the active thread id
with OSC 52, o to confirm opening the active thread in Codex's own TUI, and
u to show Codex account usage. The usage modal shows plan, credits,
reset-credit availability, and rate-limit windows for the active server; when
banked reset credits are available, the Redeem reset... action opens a
confirmation dialog that defaults to Cancel before calling Codex app-server's
reset-credit redemption RPC. Use
l to explicitly load the selected or open thread, matching
status THREAD_ID --load, then refresh visible metadata and history.
In the browser, n creates a new session: pick the server when more than one
is configured, confirm the working directory (prefilled from the selected
thread, falling back to the TUI's launch directory), optionally name the
session up front, then type the first message. The thread is created when the
message is sent — Esc at any step cancels without creating anything — and
the new row appears selected at the top while the first turn streams into the
preview.
Compose uses Enter to submit and Ctrl-J to insert a newline. On active
threads, compose defaults to steering the active turn; Tab switches to a
normal new-turn send, and Tab switches back to steer while the thread remains
active. On inactive threads, Tab toggles stream/no-wait for new turns. Browser
compose streams into the preview while the thread remains selected, follows
queued turns on that thread, and detaches locally when selection moves away. If
the initial selected browser row is active, or if an active thread is opened in
detail, the TUI attaches to it automatically. Automatic attaching is governed
by the persisted auto-attach toggle (a in the c menu, on by default): with
it off, browsing shows content from history fetches only — no live stream is
opened per selection — and only your own sends stream. While a stream is attached it is the sole content transport: status
polling backs off entirely while turn notifications are flowing and only
resumes as a fallback after a few quiet seconds. An animated green live
indicator marks the attached stream in the detail header, the preview pane
title, and the browser STATUS column.
Use t to toggle real browser auto-refresh; the c menu adjusts the persisted
refresh interval from 5-300 seconds with - and +.
Search prompts use Enter to apply and Ctrl-D to clear. Annotation editing
uses Enter to save and Ctrl-D to clear. Rename editing uses Enter to save
and Ctrl-D to clear the draft; app-server does not expose a clear-name
operation. In detail, i confirms interrupt,
Enter or m composes a message or steer action based on whether the thread is
active, and q quits. Normal send uses Codex app-server's turn/start path;
steer uses turn/steer when the thread is active and the composer is in steer
mode. Attaching resumes the thread with turns included so the active-turn
snapshot appears before new stream notifications; deltas that arrive while the
snapshot is being fetched are trimmed against it, so attached transcripts
continue from the snapshot without replaying text it already contains.
Opening a thread loads a small recent turn window, orders it chronologically,
and starts at the bottom of the transcript. Scrolling up at the top loads the
next older chunk above the current transcript and preserves the current view. In
detail, gg and Home load through to the real transcript start before jumping
there; G and End load through to the real transcript end before jumping
there. Detail views refresh in place while open, and Esc returns to the
browser after unlinking the local detail view and detaching any local stream.
Local detach leaves remote turns running.
Opening in Codex temporarily returns terminal control to codex resume <thread-id> --remote <server-endpoint> --cd <thread-cwd>, adding
--dangerously-bypass-approvals-and-sandbox when the codex-threads TUI was
launched with yolo enabled, then redraws and refreshes the codex-threads TUI
after Codex exits.
Transcript rendering is markdown-aware for common headings, blockquotes, lists,
paragraph spacing, and fenced code blocks. Message headings show role and
timestamp without repeating turn IDs. Fenced code blocks gain syntax-highlighted
spans when the default-off tui-syntax-highlighting Cargo feature is enabled.
Set CODEX_THREADS_TUI_STREAM_LOG=/path/to/events.jsonl to append raw stream
events while debugging live transcript rendering.
Set CODEX_THREADS_TURN_POLL_QUIET_SECS (default 3, clamped to 1-300) to
adjust how long a watched turn must stay silent before the fallback status
poll runs while waiting on or attached to a turn.
Set CODEX_THREADS_RPC_LOG=/path/to/rpc.ndjson to additionally append every
JSON-RPC frame exchanged with app-servers — one NDJSON line per frame with a
millisecond timestamp, per-connection id, and direction (send/recv) — plus
attach-time snapshot seeding and replay reconciliation decisions. This is the
ground-truth capture for diagnosing streaming issues such as duplicated or
missing transcript text; it can grow large and contains full thread content,
so enable it only while reproducing a problem.
Configuration
Default config path:
~/.config/codex-threads/config.toml
Config path precedence:
--config PATHCODEX_THREADS_CONFIG~/.config/codex-threads/config.toml
Server target precedence for commands that target one app-server:
--connect unix:///path/to.sock,--connect ws://host:port, or--connect wss://host:port--server ALIASCODEX_THREADS_SERVER- The single configured server, only when exactly one server exists
- Error
--connect bypasses configured servers and reports the endpoint URI as the
server value in JSON output. It is mutually exclusive with --server and
CODEX_THREADS_SERVER.
The TUI is the exception to single-target defaulting: without --server,
CODEX_THREADS_SERVER, or --connect, it opens every configured server and
uses the server column to disambiguate rows. Browser paging cursors remain
per-server, so the merged all-server browser starts with one page from each
server; narrow with --server when you need cursor paging on one target.
Configured servers use a single endpoint string:
[servers.main] endpoint = "unix:///path/to/codex.sock" [servers.local_ws] endpoint = "ws://127.0.0.1:8765"
ws:// and wss:// endpoints must include an explicit port and must not
include a path, query, or fragment.
Legacy UDS config still works, but prints a deprecation warning:
[servers.main] type = "uds" path = "/path/to/codex.sock"
Replace it with:
[servers.main] endpoint = "unix:///path/to/codex.sock"
WebSocket endpoints may use bearer-token auth. Prefer env-var indirection:
[servers.remote] endpoint = "wss://example.com:443" auth_token_env = "CODEX_APP_SERVER_TOKEN"
Literal tokens are also supported for private configs:
[servers.local_ws] endpoint = "ws://127.0.0.1:8765" auth_token = "literal-token"
For direct connections, use --connect-auth-token-env ENV_VAR or
--connect-auth-token TOKEN. Prefer the env-var form on shared machines because
literal command-line tokens may be visible through process listings. Both send
Authorization: Bearer <token> during the WebSocket upgrade. Tokens are
accepted only for wss:// or loopback ws:// endpoints; non-loopback plain
ws:// with a token is rejected to avoid
sending credentials over cleartext.
When more than one server is configured, app-server commands require an explicit
target through --server or CODEX_THREADS_SERVER.
This avoids cursor merging and prevents accidentally sending work to the wrong
server. servers ping --all is the only aggregate command.
New-thread model defaults:
new --model MODELandnew --effort EFFORT- The selected server's
modelandmodel_reasoning_effort - Top-level
modelandmodel_reasoning_effort - Codex app-server defaults
Config model defaults are applied only when creating a thread with new.
Follow-up send commands keep the thread's existing app-server settings unless
--model or --effort is passed explicitly.
Commands
| Command | Purpose |
|---|---|
servers [--json] |
List configured server aliases without connecting. |
servers ping [--server ALIAS|--all] [--json] |
Connect, initialize, and report reachability. |
list |
List threads with --limit, --cursor, --since, --cwd, --archived, --sort, --asc, --desc. Defaults to --limit 50. |
search QUERY |
Search one server with --limit, --cursor, --since, and --archived. |
show THREAD_ID |
Show thread detail and turns with --last, --cursor, --asc, --desc, --items summary|full|none. Defaults to --last 20. |
tui |
Launch the interactive browser across all configured servers by default, or one server with --server; accepts --query, --since, --cwd, --archived, --limit, --sort, --asc, and --desc initial filters. |
messages THREAD_ID |
Flatten messages from recent turns with --last, --since, --role user|assistant, and --max-turns. |
new --cwd PATH [PROMPT] |
Create a thread and optionally start the first turn. Supports --model, --effort, --service-tier, --name, --json, --stream, --no-wait. |
send THREAD_ID PROMPT |
Start a follow-up turn. Supports --model, --effort, --service-tier, --json, --stream, --no-wait. |
settings show THREAD_ID |
Read model, effort, service tier, and cwd. This resumes the thread for inspection but does not force yolo permissions. |
settings set THREAD_ID |
Update --model, --effort, --service-tier, or --clear-service-tier; at least one setting flag is required. |
status [THREAD_ID] |
Show server loaded-thread status or one thread with active turn discovery. Use --load with a thread ID to resume/load before reporting. |
steer THREAD_ID TURN_ID PROMPT |
Send steering input to an active turn. |
interrupt THREAD_ID TURN_ID |
Interrupt an active turn. |
name THREAD_ID NAME |
Set a thread name. |
archive THREAD_ID / unarchive THREAD_ID |
Archive or restore a thread. |
models |
List available models from the app-server. |
usage |
Show account usage, rate-limit windows, plan, and credits from the app-server. |
goal get THREAD_ID |
Read the active goal. |
goal set THREAD_ID |
Set --objective, --status, or --token-budget; at least one flag is required. |
goal clear THREAD_ID |
Clear the active goal. |
annotate set THREAD_ID TEXT |
Set or replace a local annotation for a thread. |
annotate get THREAD_ID |
Read a local annotation. Missing annotations exit with code 2. |
annotate clear THREAD_ID |
Clear a local annotation. |
annotate list |
List local annotations for the selected server, optionally filtered with --query. |
annotate search QUERY |
Search local annotation text for the selected server. |
annotate prune [--dry-run] |
Remove annotations whose threads are no longer found by app-server. |
completion [SHELL] |
Print shell completion setup instructions for bash, zsh, or fish. |
Every app-server and annotation command accepts --server ALIAS and --json.
Global
--config PATH, --connect ENDPOINT, --connect-auth-token-env ENV_VAR, and
--connect-auth-token TOKEN may be placed before or after the subcommand
because they are global options.
Global --no-yolo disables the default permission override for action commands
that create, resume before action, or start Codex work. settings show is a
read path and does not force yolo permissions even though it resumes the thread
to inspect settings.
If send, steer, or settings set receives Codex app-server's unloaded
thread error, codex-threads resumes the target thread and retries the action
once. That resume uses the same permission mode as the action: yolo permissions
by default, or app-server defaults when global --no-yolo is passed.
Accepted --effort values are none, minimal, low, medium, high, and
xhigh. Accepted goal set --status values are active, paused, blocked,
usage-limited, budget-limited, and complete.
Shell Completion
Print setup instructions for the detected shell:
codex-threads completion codex-threads completion bash codex-threads completion zsh codex-threads completion fish
Enable completion only for the current shell:
source <(codex-threads completion script bash) source <(codex-threads completion script zsh) codex-threads completion script fish | source
For permanent bash setup, generate a static completion file and source it from
~/.bashrc:
mkdir -p ~/.local/share/codex-threads codex-threads completion script bash > ~/.local/share/codex-threads/completion.bash printf '\nsource ~/.local/share/codex-threads/completion.bash\n' >> ~/.bashrc
For permanent zsh setup:
mkdir -p ~/.local/share/codex-threads codex-threads completion script zsh > ~/.local/share/codex-threads/completion.zsh printf '\nsource ~/.local/share/codex-threads/completion.zsh\n' >> ~/.zshrc
For permanent fish setup:
mkdir -p ~/.config/fish/completions codex-threads completion script fish > ~/.config/fish/completions/codex-threads.fish
Regenerate the completion file after upgrading codex-threads.
Completions suggest command names, nested subcommands, option names, static
values such as --sort updated|created, --items summary|full|none,
--role user|assistant, --effort none|minimal|low|medium|high|xhigh, goal
status values, shell names for completion, and local configured server aliases
for --server. Completion does not connect to Codex app-server, so thread IDs,
turn IDs, and remote model IDs are not completed.
Output
Human output is the default and is intended for terminal use.
--json emits a single pretty-printed JSON object for read commands,
acknowledgement commands, --no-wait turn commands, and blocking turn
commands. Blocking new PROMPT --json and send --json include:
serverthreadIdturnIdstatusprogressassistantResponsesfinalAssistantText
Streamed assistant progress events include Codex itemId when available, and
assistantResponses contains one entry per assistant item so clients can keep
separate assistant messages distinct.
--json --stream is available for new PROMPT and send. It emits NDJSON:
one accepted event, zero or more progress events, and one terminal event.
Commands that create or start work always return enough follow-up identifiers:
server, threadId, and turnId where applicable. new --cwd PATH without a
prompt creates the thread and returns threadId; --stream and --no-wait are
invalid without a prompt.
Blocking new PROMPT and send commands wait up to one hour for the turn to
reach a terminal status. They consume realtime notifications when available and
poll recent turns as a fallback so callers still get a final JSON response if a
notification is missed.
If the local one-hour wait times out, the command exits with code 3; the
remote Codex turn may still be running.
status --json without a thread ID returns { server, reachable, loadedThreadIds, nextCursor }. status THREAD_ID --json returns the selected
thread, threadId, activeTurnId, and truncated. Plain status THREAD_ID
does not resume unloaded threads; status THREAD_ID --load explicitly calls
thread/resume with excludeTurns: true, unsubscribes the probing connection,
then reports status from the loaded app-server view.
usage --json returns { server, rateLimits, rateLimitsByLimitId, rateLimitResetCredits } from Codex app-server's account/rateLimits/read
response. Human output summarizes the server, plan, credits, reset-credit
count, rate-limit reached state, and primary/secondary windows for each limit
ID.
Annotations are local codex-threads state, not Codex app-server state. The
state file is resolved as:
$CODEX_THREADS_STATE/annotations.json$XDG_STATE_HOME/codex-threads/annotations.json~/.local/state/codex-threads/annotations.json
Annotations are keyed by selected server endpoint and thread ID. annotate
commands can set, get, clear, list, search, and prune those local records.
list --json, search --json, and show --json include an annotation object
on returned thread objects when one exists. Human list and search add an
ANNOTATION column only when displayed rows have annotations; human show
prints the annotation in the thread detail.
TUI preferences are local codex-threads state, separate from annotations:
$CODEX_THREADS_STATE/tui.json$XDG_STATE_HOME/codex-threads/tui.json~/.local/state/codex-threads/tui.json
The TUI persists disposable UI preferences such as visible columns,
auto-refresh, the 5-300 second refresh interval, preview pane, and default sort.
Corrupt or unsupported preference files are renamed to
tui.json.corrupt.<epoch> when possible and fall back to defaults instead of
blocking launch. In search mode, --cwd is a local refinement over the loaded
search page; sort controls are disabled until the app-server search API supports
server-side sorting.
Exit codes:
| Code | Meaning |
|---|---|
0 |
Command succeeded, or a blocking turn completed. |
1 |
A blocking new or send turn reached failed or interrupted. |
2 |
Usage, argument, validation, configuration, or local lookup error. |
3 |
App-server, connection, Unix socket, WebSocket, or capability error. |
130 |
Local Ctrl-C while waiting on a turn; the remote turn may still be running. |
list --since, search --since, and messages --since accept either an epoch
timestamp in seconds or a relative duration ending in s, m, h, or d,
such as 5m. List and search filtering is applied client-side to updatedAt.
When a filtered list or search does not fill --limit from the first server
page, the CLI keeps scanning server pages until the filtered limit is filled or
the server cursor is exhausted. Returned cursors are still raw Codex server
cursors from the last scanned page.
messages is a convenience projection over recent turn history. It does not
page exact whole-thread message history and it does not have --first. For the
beginning of a thread or older exact review, use show --asc and/or
show --cursor with the appropriate --items view.
Message selection order is:
- Fetch up to
--max-turnsrecent turns from Codex with full items. - Flatten those turns into user/assistant messages.
- Apply
--since, if present, using the turn timestamp. - Apply
--role user|assistant, if present. - Apply
--last N, if present, to the final filtered message list.
--max-turns defaults to 200 and is the recent turn scan window, not a final
display limit.
--last is the final message limit after flattening and filtering; it is not
an alias for --max-turns. Role filtering only sees messages inside the
scanned recent turns, so increase --max-turns when looking for sparse or older
messages such as --role assistant --last 3.
In human output, messages prints readable timestamped blocks. When no role
filter is set, each block header includes the role. With --role user or
--role assistant, the role is omitted from the header because every message
has the requested role. --json keeps the structured message array shape.
When the recent turn scan is truncated, human output prints a warning; increase
--max-turns or use show --cursor for older exact paging.
Long table cells and message previews may be shortened in human output to keep
terminal output readable; use --json when exact text is required.
Development
Build the CLI during development:
Build the optimized binary:
To use the local build like a release binary, install it somewhere on your
PATH, for example:
mkdir -p ~/.local/bin install -m 755 target/release/codex-threads ~/.local/bin/codex-threads
You can also install directly from the checkout:
cargo install --path . --root ~/.local
Required checks:
cargo fmt --check
cargo test
cargo clippy --all-targets --all-features
cargo build --releaseThe integration smoke tests in tests/mock_smoke.rs start mock UDS and TCP
WebSocket app-servers and exercise the compiled CLI binary end to end.
The opt-in PTY smoke tests in tests/tui_pty_smoke.rs drive the real
interactive TUI through a pseudo-terminal, including browser/detail navigation,
streaming sends, attach/detach, and CLI history/status validation against a
stateful mock app-server:
cargo test --test tui_pty_smoke -- --ignoredLive smoke checks are opt-in:
CODEX_ENDPOINT=unix:///path/to/codex.sock smoke/live_smoke.sh
Set RUN_CODEX_TURN=1 to run a real model turn through the live app-server.
Set RUN_ARCHIVE=1 to include live archive/unarchive checks.
Release
Releases are driven from Cargo.toml, Cargo.lock, and CHANGELOG.md.
Use patch, minor, major, or an explicit semantic version:
node scripts/release.mjs patch node scripts/release.mjs minor node scripts/release.mjs major node scripts/release.mjs 0.2.3
The script stamps the changelog, commits Release vX.Y.Z, creates and pushes a
matching git tag, creates a GitHub release with notes from the changelog,
then commits a fresh Unreleased section for the next cycle.
Release binaries are packaged separately after the platform binaries have been provided or built by the release operator. Supported release platforms currently use archive names like:
codex-threads-VERSION-linux-x86_64.tar.gz
codex-threads-VERSION-macos-arm64.tar.gz
Each archive should contain one top-level directory named
codex-threads-VERSION-PLATFORM with:
codex-threads- executable binary for that platformREADME.mdLICENSECHANGELOG.mdconfig.example.tomlskills/
Example packaging flow for one platform:
VERSION=0.2.0 PLATFORM=linux-x86_64 BINARY=/path/to/codex-threads STAGE="$(mktemp -d)" ROOT="codex-threads-${VERSION}-${PLATFORM}" mkdir -p "$STAGE/$ROOT" install -m 755 "$BINARY" "$STAGE/$ROOT/codex-threads" cp README.md LICENSE CHANGELOG.md config.example.toml "$STAGE/$ROOT/" cp -R skills "$STAGE/$ROOT/" tar -C "$STAGE" -czf "${ROOT}.tar.gz" "$ROOT" rm -rf "$STAGE"
Repeat that staging step for each platform, for example linux-x86_64 and
macos-arm64, using the correct binary for each target. After the GitHub
Release exists, upload the archives:
RELEASE_TAG="v${VERSION}" gh release upload "$RELEASE_TAG" \ "codex-threads-${VERSION}-linux-x86_64.tar.gz" \ "codex-threads-${VERSION}-macos-arm64.tar.gz"
Project Structure
src/bin/- binary entrypoints.src/lib.rs- shared library entrypoint.src/config.rs- TOML schema, validation, and target resolution.src/rpc.rs- Unix domain socket WebSocket JSON-RPC transport and handshake.src/cli.rs- command-line parser.src/app.rs- command orchestration and rendering.tests/- deterministic binary-level mock smoke coverage.scripts/- release automation.smoke/- opt-in live smoke harness.skills/- Codex skill guidance for using this CLI from other assistant sessions.
