Engineering

Codex CLI Integration#

Co-Vibe supports OpenAI Codex CLI in two ways:

  1. covibe-local setup writes an MCP entry into ~/.codex/config.toml (the config Codex actually reads) so Codex sessions can call the Co-Vibe tools.
  2. covibe-local watch passively syncs Codex usage from the local ~/.codex artifacts (see the watch notes in README.md).

What Setup Writes#

When setup detects Codex — a ~/.codex directory, or the directory named by CODEX_HOME (or COVIBE_CODEX_HOME) — it merges this block into ~/.codex/config.toml:

toml
[mcp_servers.covibe]
# Managed by covibe-local setup; the MCP token is passed from your shell via
# env_vars and is never stored in this file. PWD is forwarded so the stdio
# bridge knows the repo Codex was launched in, not the cwd pinned below.
command = "node"
args = ["<package>/bin/covibe-mcp.mjs"]
cwd = "/path/to/repo"
env = { COVIBE_BASE_URL = "http://localhost:3000", COVIBE_AGENT = "codex" }
env_vars = ["COVIBE_MCP_TOKEN", "PWD"]

Codex reads [mcp_servers] from ~/.codex/config.toml. Stdio servers accept command, args, cwd, env (literal values forwarded to the server), and env_vars (names of environment variables whitelisted through from the parent environment at spawn time). The validator also accepts the equivalent [mcp_servers.covibe.env] table form, which Codex itself may write. Source: the Codex config reference at developers.openai.com/codex/config-reference and codex-rs/config/src/mcp_types.rs plus codex-rs/rmcp-client/src/utils.rs in openai/codex.

Forwarding PWD makes the working directory deterministic across repos: the config is global, so its pinned cwd (and therefore the bridge's process.cwd()) is whatever repo last ran setup, while the shell-set PWD at Codex launch is the repo Codex actually operates on. When COVIBE_AGENT is codex and PWD is an absolute existing path, the bridge uses PWD for repo exclusion checks; configs written before PWD forwarding validate as stale and the doctor's setup hint migrates them.

If Codex is not installed, setup skips the file with an INFO line.

Token Safety#

The raw cv_ token is never written to disk. Codex does not interpolate ${VAR} placeholders in env values, so the companion uses the supported env_vars allowlist instead: Codex reads COVIBE_MCP_TOKEN from your shell when it spawns the server. covibe-local doctor fails if a raw token ever appears in ~/.codex/config.toml.

Exporting COVIBE_MCP_TOKEN is optional since the device-flow setup: the non-secret COVIBE_AGENT = "codex" env marker lets the stdio bridge look up the typed codex token from ~/.covibe/credentials.json when the shell variable is unset (see device-setup.md). An exported COVIBE_MCP_TOKEN keeps precedence. Configs written before this feature validate as stale and the doctor's setup hint migrates them.

Merge Behavior#

Setup parses the existing ~/.codex/config.toml, replaces only the managed [mcp_servers.covibe] table, and leaves every other line — comments, other MCP servers, model settings — untouched. Re-runs are idempotent; a stale managed block (for example after moving the repo) is rewritten in place.

Because the home config is global, the managed block pins cwd to the repo that ran setup; re-run setup from another repo to repoint it. Tool calls do not depend on that pin: the forwarded PWD identifies the launch repo, so running Codex in several repos — including an excluded one — resolves each call against the right repo without re-running setup.

What Watch Captures#

covibe-local watch (and the one-off covibe-local telemetry --codex) reads two on-disk lanes, both opened read-only via file:…?immutable=1 URIs so the WAL databases stay readable while Codex is closed:

  1. ~/.codex/logs_2.sqlite response.completed rows under both the codex_api::sse::responses and codex_api::endpoint::responses_websocket targets (websocket is the current default transport; thread ids are recovered from the session_loop{thread_id=…} span prefix when the column is null). Captures model, input/output/cached token splits, and total.
  2. ~/.codex/sessions/**/rollout-*.jsonl token_count events for codex exec (non-interactive) sessions, which never reach the logs DB. Captures the same splits plus reasoning_output_tokens in the payload. Override the directory with COVIBE_CODEX_SESSIONS.

Threads are scoped to the repo by the thread's recorded cwd — a Codex-runtime value, not the config pin — including subdirectories of the repo root, so the sync in one repo never picks up threads from another (or an excluded) repo. De-duplication state lives in .covibe/codex-usage-state.json (primed on first run so history is not back-billed).

Doctor Check#

covibe-local doctor prints one codex check:

  • INFO when Codex is not installed (nothing to do)
  • WARN with a setup hint when Codex is installed but ~/.codex/config.toml is missing the wired Co-Vibe block or it is stale
  • FAIL when ~/.codex/config.toml stores a raw token
  • PASS when the config is wired, plus the result of a real read-only open probe of the local Codex usage DB (state_5.sqlite) for watch sync

Verification#

bash
npx vitest run tests/unit/local-companion-codex-setup.test.ts \
  tests/unit/local-companion-codex-doctor.test.ts \
  tests/unit/local-companion-codex-usage.test.ts \
  tests/unit/local-companion-codex-rollout.test.ts

File-Overlap Visibility#

Codex has no session hooks, so conflict warnings cannot be pushed into a running Codex session the way the Claude Code hook injects additionalContext. Codex agents get the same information by pulling: covibe_team operation: "state" returns repo_snapshot_conflicts (teammates' or your own other session's uncommitted/unpushed edits on overlapping files). The covibe-local agent-rules snippet installed into AGENTS.md — which Codex reads natively at session start — instructs agents to check it before editing files and to coordinate via covibe_coordination operation: "ask_peer" when their planned files appear there.

Codex agents get the mid-run equivalent at task-check time: passing target_files on covibe_task operation: "check" returns file_conflicts (who else has those exact files in an open task or dirty in a snapshot), so a Codex session learns about file-level overlap the moment it registers work, without needing hooks.

View as .md