Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,58 @@ Ask Codex to redesign the database connection to be more resilient.
- if you say `spark`, the plugin maps that to `gpt-5.3-codex-spark`
- follow-up rescue requests can continue the latest Codex task in the repo

#### Carrying the Claude conversation into the rescue

By default a rescue only receives the task text you typed. Codex still reads the same repository,
but it does not know what you and Claude already discussed, ruled out, or tried and abandoned.

Add `--session-context` to attach the current Claude session as a handoff brief:

```bash
/codex:rescue --session-context fix the failing login test
/codex:rescue --session-context # no task text needed, continues the thread of work
/codex:rescue --session-context --session-context-mode full --background investigate the regression
```

The brief is assembled by the companion script straight from the Claude transcript, so Claude does
not have to read or summarize the conversation itself. It contains the compact summary written by
`/compact`, the turns recorded after that summary, and the branch and uncommitted file list at
handoff time. Tool calls, thinking blocks, and subagent chatter are excluded.

This mirrors how Claude Code survives its own compaction: the compact summary carries the earlier
session and the post-boundary turns close the gap. Running `/compact` before a rescue therefore
produces a denser, cheaper brief than a raw transcript, and it frees your own Claude context at the
same time.

Modes:

| Mode | What it attaches |
| --- | --- |
| `--session-context` | compact summary plus the turns recorded after it (the `auto` default) |
| `--session-context-mode summary` | compact summary only |
| `--session-context-mode recent` | recent turns only, no summary |
| `--session-context-mode full` | every turn in the transcript, ignoring the recent-turn cap |
| `--session-context-mode none` | nothing; identical to omitting the flag |

`--session-context-mode` implies `--session-context`, so you never need both.

**Notes:**

- `--session-turns <count>` caps how many recent turns are attached (default 30).
- `--session-max-chars <count>` caps the assembled brief (default 49152). Oldest turns are dropped first and the brief says how many were omitted.
- with `--resume`, the `auto` mode narrows to `recent`, because the resumed Codex thread already contains the earlier brief.
- if the compact summary is more than an hour old the plugin says so, so you can run `/compact` for a fresher brief.
- the brief is framed as third-party background, and instructs Codex to verify it against the repository rather than trust it.
- `--source <claude-jsonl>` overrides transcript discovery, the same way `/codex:transfer` does.

### `/codex:transfer`

Creates a persistent Codex thread from the current Claude Code session and prints a `codex resume <session-id>` command.

Use it when you started a debugging or implementation conversation in Claude Code and want to continue that same context directly in Codex.

To keep working inside Claude Code and hand Codex a single task with that same context instead of moving over entirely, use [`/codex:rescue --session-context`](#carrying-the-claude-conversation-into-the-rescue).

Examples:

```bash
Expand Down
5 changes: 5 additions & 0 deletions plugins/codex/agents/codex-rescue.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ Forwarding rules:
- If the user asks for `spark`, map that to `--model gpt-5.3-codex-spark`.
- If the user asks for a concrete model name such as `gpt-5.4-mini`, pass it through with `--model`.
- Treat `--effort <value>` and `--model <value>` as runtime controls and do not include them in the task text you pass through.
- Treat `--session-context`, `--session-context-mode <value>`, `--session-turns <value>`, and `--session-max-chars <value>` as context controls and do not include them in the task text you pass through.
- `--session-context` is a boolean flag and takes no value. Never consume the following word as its value.
- If the user names a mode such as `summary`, `recent`, or `full`, forward it as `--session-context-mode <value>`.
- Never assemble session context yourself. The companion script reads the Claude transcript and builds the brief. Do not read the transcript, summarize the conversation, or paste history into the task text.
- When `--session-context` is present, an empty task text is valid. Forward the `task` call without a prompt rather than inventing one.
- Default to a write-capable Codex run by adding `--write` unless the user explicitly asks for read-only behavior or only wants review, diagnosis, or research without edits.
- Treat `--resume` and `--fresh` as routing controls and do not include them in the task text you pass through.
- `--resume` means add `--resume-last`.
Expand Down
5 changes: 4 additions & 1 deletion plugins/codex/commands/rescue.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
description: Delegate investigation, an explicit fix request, or follow-up rescue work to the Codex rescue subagent
argument-hint: "[--background|--wait] [--resume|--fresh] [--model <model|spark>] [--effort <none|minimal|low|medium|high|xhigh>] [what Codex should investigate, solve, or continue]"
argument-hint: "[--background|--wait] [--resume|--fresh] [--session-context] [--session-context-mode <auto|summary|recent|full>] [--model <model|spark>] [--effort <none|minimal|low|medium|high|xhigh>] [what Codex should investigate, solve, or continue]"
allowed-tools: Bash(node:*), AskUserQuestion, Agent
---

Expand All @@ -18,6 +18,9 @@ Execution mode:
- If neither flag is present, default to foreground.
- `--background` and `--wait` are execution flags for Claude Code. Do not forward them to `task`, and do not treat them as part of the natural-language task text.
- `--model` and `--effort` are runtime-selection flags. Preserve them for the forwarded `task` call, but do not treat them as part of the natural-language task text.
- `--session-context`, `--session-context-mode`, `--session-turns`, and `--session-max-chars` are context-selection flags. Preserve them for the forwarded `task` call, but do not treat them as part of the natural-language task text.
- `--session-context` attaches this Claude session's compact summary and recent turns to the Codex prompt so Codex starts with the same background. The companion script assembles it directly from the transcript, so do not read, summarize, or paste the conversation yourself.
- When `--session-context` is present the task text is optional. If the user only says something like "continue" or "take it from here", forward `--session-context` with no task text and let the companion supply the continuation request.
- If the request includes `--resume`, do not ask whether to continue. The user already chose.
- If the request includes `--fresh`, do not ask whether to continue. The user already chose.
- Otherwise, before starting Codex, check for a resumable rescue thread from this Claude session by running:
Expand Down
39 changes: 34 additions & 5 deletions plugins/codex/scripts/codex-companion.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
runAppServerReview,
runAppServerTurn
} from "./lib/codex.mjs";
import { buildClaudeContextBrief, composeTaskPromptWithBrief, normalizeContextMode } from "./lib/claude-context-brief.mjs";
import { resolveClaudeSessionPath } from "./lib/claude-session-transfer.mjs";
import { readStdinIfPiped } from "./lib/fs.mjs";
import { collectReviewContext, ensureGitRepository, resolveReviewTarget } from "./lib/git.mjs";
Expand Down Expand Up @@ -54,6 +55,7 @@ import {
} from "./lib/tracked-jobs.mjs";
import { resolveWorkspaceRoot } from "./lib/workspace.mjs";
import {
renderContextBriefNotice,
renderNativeReviewResult,
renderReviewResult,
renderStoredJobResult,
Expand All @@ -79,7 +81,7 @@ function printUsage() {
" node scripts/codex-companion.mjs setup [--enable-review-gate|--disable-review-gate] [--json]",
" node scripts/codex-companion.mjs review [--wait|--background] [--base <ref>] [--scope <auto|working-tree|branch>]",
" node scripts/codex-companion.mjs adversarial-review [--wait|--background] [--base <ref>] [--scope <auto|working-tree|branch>] [focus text]",
" node scripts/codex-companion.mjs task [--background] [--write] [--resume-last|--resume|--fresh] [--model <model|spark>] [--effort <none|minimal|low|medium|high|xhigh>] [prompt]",
" node scripts/codex-companion.mjs task [--background] [--write] [--resume-last|--resume|--fresh] [--model <model|spark>] [--effort <none|minimal|low|medium|high|xhigh>] [--session-context] [--session-context-mode <auto|summary|recent|full|none>] [--session-turns <count>] [--session-max-chars <count>] [--source <claude-jsonl>] [prompt]",
" node scripts/codex-companion.mjs transfer [--source <claude-jsonl>] [--json]",
" node scripts/codex-companion.mjs status [job-id] [--all] [--json]",
" node scripts/codex-companion.mjs result [job-id] [--json]",
Expand Down Expand Up @@ -761,8 +763,17 @@ async function handleReview(argv) {

async function handleTask(argv) {
const { options, positionals } = parseCommandInput(argv, {
valueOptions: ["model", "effort", "cwd", "prompt-file"],
booleanOptions: ["json", "write", "resume-last", "resume", "fresh", "background"],
valueOptions: [
"model",
"effort",
"cwd",
"prompt-file",
"session-context-mode",
"session-turns",
"session-max-chars",
"source"
],
booleanOptions: ["json", "write", "resume-last", "resume", "fresh", "background", "session-context"],
aliasMap: {
m: "model"
}
Expand All @@ -772,16 +783,34 @@ async function handleTask(argv) {
const workspaceRoot = resolveCommandWorkspace(options);
const model = normalizeRequestedModel(options.model);
const effort = normalizeReasoningEffort(options.effort);
const prompt = readTaskPrompt(cwd, options, positionals);
const requestedPrompt = readTaskPrompt(cwd, options, positionals);

const resumeLast = Boolean(options["resume-last"] || options.resume);
const fresh = Boolean(options.fresh);
if (resumeLast && fresh) {
throw new Error("Choose either --resume/--resume-last or --fresh.");
}

// A resumed thread already carries the earlier brief, so only the newer turns are worth resending.
const requestedContextMode = normalizeContextMode(
options["session-context-mode"] ?? (options["session-context"] ? "auto" : undefined)
);
const contextMode = resumeLast && requestedContextMode === "auto" ? "recent" : requestedContextMode;
const brief = buildClaudeContextBrief(cwd, {
mode: contextMode,
source: options.source,
recentTurns: options["session-turns"],
maxChars: options["session-max-chars"],
now: Date.now()
});
if (brief && !options.json) {
process.stderr.write(`${renderContextBriefNotice(brief.stats)}\n`);
}

const prompt = brief ? composeTaskPromptWithBrief(brief, requestedPrompt) : requestedPrompt;
const write = Boolean(options.write);
const taskMetadata = buildTaskRunMetadata({
prompt,
prompt: requestedPrompt || (brief ? "Continue from Claude Code handoff brief" : requestedPrompt),
resumeLast
});

Expand Down
Loading