# 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.
