Skip to content

feat: add /codex:attach command for live log streaming#291

Open
mreza0100 wants to merge 1 commit intoopenai:mainfrom
mreza0100:feat/codex-attach
Open

feat: add /codex:attach command for live log streaming#291
mreza0100 wants to merge 1 commit intoopenai:mainfrom
mreza0100:feat/codex-attach

Conversation

@mreza0100
Copy link
Copy Markdown

Problem

Background Codex jobs (/codex:rescue --background) are a black box. Once fired, there's no way to observe what Codex is doing until the job completes. The only options are polling /codex:status (which shows a one-line phase summary) or waiting for /codex:result (which shows the final output only).

Meanwhile, the background worker already writes every event — thread start, turn start, command executions, assistant messages, completion — to a structured log file on disk. That data just goes unobserved.

Solution

Add /codex:attach [job-id] — a new command that tails the job's log file in real time and exits cleanly when the job reaches a terminal status.

/codex:attach                    # attaches to the most recent active job
/codex:attach task-abc123        # attaches to a specific job by ID
/codex:attach task-abc123 --poll-interval-ms 250   # faster polling

Example output:

[attach] Job task-moozar3f-3ly2x1 · status: running
[attach] Task: Security audit of freudche-be/ and freudche-cortex/
[attach] Log: ~/.claude/plugins/data/.../jobs/task-moozar3f-3ly2x1.log
---
[2026-05-03T00:12:01Z] Starting Codex Task.
[2026-05-03T00:12:01Z] Queued for background execution.
[2026-05-03T00:12:02Z] Starting Codex task thread.
[2026-05-03T00:12:04Z] Turn started (019dead8-...).
[2026-05-03T00:12:04Z] Running command: rg -n "api_key|secret" freudche-be/
...

--- Job task-moozar3f-3ly2x1 completed ---

Implementation

plugins/codex/scripts/codex-companion.mjs — new handleAttach() function + attach case in the dispatch switch:

  • Resolves the job by ID or falls back to the most recent active job via listJobs() + sortJobsNewestFirst()
  • Reads log file path from job.logFile (falls back to resolveJobLogFile())
  • Polls for new log content every 500ms (configurable via --poll-interval-ms)
  • Simultaneously polls job state for terminal status (completed, failed, cancelled)
  • Final log flush + closing line on exit — no content is missed

plugins/codex/commands/attach.md — command definition following the same pattern as status.md and cancel.md.

What this does NOT change

  • No existing commands or behaviours are modified
  • No new dependencies
  • Background mode, foreground mode, review commands — all untouched
  • Works entirely with the existing state.mjs / job-control.mjs infrastructure

Context: why not MCP streaming?

We investigated whether codex mcp-server streaming notifications could solve this. The Codex MCP server does emit events as MCP notifications during execution, but Claude Code silently drops them (the MCP client does not forward notifications/progress or notifications/message to the model — tracked in anthropics/claude-code#4157, closed "not planned"). The log file approach is the reliable path and requires no protocol changes on either side.

🤖 Generated with Claude Code

Adds a new `attach` subcommand to codex-companion.mjs and a matching
`/codex:attach` command definition.

**Problem:** Background Codex jobs (`/codex:rescue --background`) produce
no live output. The only way to observe progress is to poll `/codex:status`
or wait for `/codex:result` after completion — a blind black-box experience.

**Solution:** `/codex:attach [job-id]` tails the job's on-disk log file in
real time and exits cleanly when the job reaches a terminal status
(completed, failed, cancelled). If no job ID is given, it automatically
attaches to the most recent active job.

Implementation details:
- New `handleAttach()` async function in codex-companion.mjs
- Reads the job log file path from `job.logFile` (falls back to
  `resolveJobLogFile` if missing)
- Polls for new log content every 500ms (configurable via
  `--poll-interval-ms`)
- Simultaneously polls job state via `listJobs()` for terminal status
- Final log flush + closing line on exit
- No new dependencies; uses existing state/job-control infrastructure
- Does not modify any existing commands or behaviour

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mreza0100 mreza0100 requested a review from a team May 2, 2026 23:45
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c310f05758

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

job = snapshot.job;
} else {
const jobs = sortJobsNewestFirst(listJobs(workspaceRoot));
job = jobs.find((j) => isActiveJobStatus(j.status)) ?? jobs[0] ?? null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Select only active jobs when no ID is provided

When attach is run without a job ID, this fallback picks jobs[0] whenever no queued/running job exists, so a previously completed/failed job is treated as attachable instead of reporting that there is no active job. In repositories with old job history, users will unexpectedly stream stale logs and immediately exit rather than getting the intended guidance to start /codex:rescue, which contradicts the command contract in plugins/codex/commands/attach.md.

Useful? React with 👍 / 👎.

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.

1 participant