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
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,16 @@ $cc:review --model opus --effort xhigh # explicitly raise opus effort

**Flags:** `--base <ref>`, `--scope <auto|working-tree|branch>`, `--wait`, `--background`, `--model <model>`, `--effort <low|medium|high|xhigh|max>`

**Defaults:** model `opus`, and no effort at all. After trimming surrounding whitespace, the friendly aliases `fable`, `opus`, `sonnet`, and `haiku` are matched case-insensitively and canonicalized to lowercase; every other `--model` value passes through unchanged for Claude Code to resolve, including full model IDs and provider-specific names. `--effort` is forwarded only when you pass it, so each model keeps whatever effort Claude Code defaults to. Claude Code owns which effort levels each model supports, so check `/model` rather than assuming a level applies everywhere.
**Defaults:** explicit `--model` takes precedence over `CC_PLUGIN_CODEX_DEFAULT_MODEL`, which falls back to `opus` when unset or blank. No effort is forced. After trimming surrounding whitespace, the friendly aliases `fable`, `opus`, `sonnet`, and `haiku` are matched case-insensitively and canonicalized to lowercase; every other `--model` value passes through unchanged for Claude Code to resolve, including full model IDs and provider-specific names. `--effort` is forwarded only when you pass it, so each model keeps whatever effort Claude Code defaults to. Claude Code owns which effort levels each model supports, so check `/model` rather than assuming a level applies everywhere.

To choose a default for review, adversarial review, and rescue, add the variable to the existing `[shell_environment_policy.set]` table in `~/.codex/config.toml` (or create the table if absent):

```toml
[shell_environment_policy.set]
CC_PLUGIN_CODEX_DEFAULT_MODEL = "sonnet"
```

Restart Codex to load the setting. The value may be any alias, full model ID, or provider-specific name accepted by your Claude Code CLI; surrounding whitespace is trimmed. Explicit `--model` still overrides it. This setting does not change the turn-end review gate, which continues to use Claude Code's own defaults.

**Model discovery:** run `/model` in Claude Code to see the models and effort levels available to your current account and provider, then pass the selected alias or full ID to this plugin. The plugin intentionally does not maintain a static model catalog, a per-model effort table, or force `[1m]`; Claude Code owns alias versions, supported effort levels, managed restrictions, provider routing, and extended-context eligibility.

Expand Down Expand Up @@ -174,7 +183,7 @@ $cc:rescue --model sonnet --effort medium investigate the flaky test
| `--resume-last` | Alias for `--resume` |
| `--fresh` | Force a new task (don't resume) |
| `--write` | Allow file edits (default) |
| `--model <model>` | Any Claude Code model alias, full model ID, or provider-specific name; defaults to `opus`. Run `/model` in Claude Code to discover options available to your account and provider. |
| `--model <model>` | Any Claude Code model alias, full model ID, or provider-specific name; defaults to `CC_PLUGIN_CODEX_DEFAULT_MODEL`, then `opus`. Run `/model` in Claude Code to discover options available to your account and provider. |
| `--effort <level>` | Reasoning effort: `low`, `medium`, `high`, `xhigh`, `max`. Unset by default, so the model keeps Claude Code's own effort default. Claude Code owns which levels each model supports. |
| `--prompt-file <path>` | Read task description from a file |

Expand Down
2 changes: 1 addition & 1 deletion internal-skills/cli-runtime/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Command selection:

Routing controls:
- Treat `--model`, `--effort`, `--resume`, `--resume-last`, `--fresh`, `--prompt-file`, `--view-state`, `--owner-session-id`, and `--job-id` as routing controls, not task text.
- Leave `--model` and `--effort` unset unless the user explicitly asks for a specific model or effort. The companion command defaults model to `opus` and forwards no `--effort` at all, so `fable`, `opus`, `sonnet`, and `haiku` each keep whatever effort Claude Code defaults to for them. Claude Code owns which effort levels each model supports; this plugin owns no per-model effort table.
- Leave `--model` and `--effort` unset unless the user explicitly asks for a specific model or effort. The companion command uses `CC_PLUGIN_CODEX_DEFAULT_MODEL` when configured, otherwise defaults model to `opus` and forwards no `--effort` at all, so `fable`, `opus`, `sonnet`, and `haiku` each keep whatever effort Claude Code defaults to for them. Claude Code owns which effort levels each model supports; this plugin owns no per-model effort table.
- Forward an explicit `--model` value unchanged to the companion. The companion trims surrounding whitespace, canonicalizes the friendly aliases `fable`, `opus`, `sonnet`, and `haiku` to lowercase, then forwards every other `--model` value unchanged to Claude Code. Claude Code owns alias resolution; `/model` is the authoritative picker for the current account and provider.
- `--view-state on-success` means the user will see this companion result in the current turn, so the companion may mark it viewed on success.
- `--view-state defer` means the parent is not waiting, so the companion must leave the result unread until the user explicitly checks it.
Expand Down
2 changes: 1 addition & 1 deletion scripts/claude-companion.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* Adapted from codex-companion.mjs:
* - Uses claude-cli.mjs instead of app-server/broker
* - Friendly model aliases canonicalize to lowercase; other names pass through
* - Default model when --model is unset: opus
* - Default model when --model is unset: CC_PLUGIN_CODEX_DEFAULT_MODEL, then opus
* - No effort default: --effort is forwarded only when the user passes it
* - Claude CLI effort values: low, medium, high, xhigh, max
* - Legacy effort aliases: none|minimal -> low
Expand Down
2 changes: 1 addition & 1 deletion scripts/lib/claude-cli.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ const FRIENDLY_ALIASES = new Set(["fable", "opus", "sonnet", "haiku"]);
// so `--effort` is forwarded only when the user asks for it.
export function resolveDefaultModel(model) {
if (model == null || String(model).trim() === "") {
return DEFAULT_MODEL;
return String(process.env.CC_PLUGIN_CODEX_DEFAULT_MODEL ?? "").trim() || DEFAULT_MODEL;
}
return model;
}
Expand Down
6 changes: 4 additions & 2 deletions skills/adversarial-review/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: adversarial-review
description: 'Run a design-challenging Claude Code review of local git changes in this repository. Args: --wait, --background, --base <ref>, --scope <auto|working-tree|branch>, --model <model>, --effort <low|medium|high|xhigh|max>, [focus text]. Defaults to opus with no forced effort. Use only when the user wants stronger scrutiny than a normal review, such as explicit tradeoff challenge, risky-change review, or custom focus text.'
description: 'Run a design-challenging Claude Code review of local git changes in this repository. Args: --wait, --background, --base <ref>, --scope <auto|working-tree|branch>, --model <model>, --effort <low|medium|high|xhigh|max>, [focus text]. Defaults to CC_PLUGIN_CODEX_DEFAULT_MODEL (otherwise opus) with no forced effort. Use only when the user wants stronger scrutiny than a normal review, such as explicit tradeoff challenge, risky-change review, or custom focus text.'
---

# Claude Code Adversarial Review
Expand All @@ -16,7 +16,9 @@ Unlike `$cc:review`, this skill accepts custom focus text after the flags. The m
Resolve `<plugin-root>` as two directories above this `SKILL.md` file. Always run the companion from that active plugin root:
`node "<plugin-root>/scripts/claude-companion.mjs" adversarial-review ...`

Supported arguments: `--wait`, `--background`, `--base <ref>`, `--scope auto|working-tree|branch`, `--model <model>`, `--effort <low|medium|high|xhigh|max>`, plus optional focus text after the flags (defaults: model=opus and no effort; `fable`, `opus`, `sonnet`, and `haiku` each keep Claude Code's own effort default, and Claude Code owns which effort levels each model supports)
Supported arguments: `--wait`, `--background`, `--base <ref>`, `--scope auto|working-tree|branch`, `--model <model>`, `--effort <low|medium|high|xhigh|max>`, plus optional focus text after the flags (defaults: model=CC_PLUGIN_CODEX_DEFAULT_MODEL (otherwise opus) and no effort; `fable`, `opus`, `sonnet`, and `haiku` each keep Claude Code's own effort default, and Claude Code owns which effort levels each model supports)

When the user omits `--model`, omit it from the companion command so the configured default can apply.

Forward `--model` unchanged to the companion. The companion trims surrounding whitespace, canonicalizes the friendly aliases `fable`, `opus`, `sonnet`, and `haiku` to lowercase, then forwards every other `--model` value unchanged to Claude Code. Claude Code owns alias resolution and supported effort levels; `/model` is the authoritative picker for the current account and provider.

Expand Down
6 changes: 4 additions & 2 deletions skills/rescue/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: rescue
description: 'Delegate a substantial diagnosis, implementation, or follow-up task to Claude Code through the tracked-job runtime. Args: --background, --wait, --resume, --resume-last, --fresh, --write, --model <model>, --effort <low|medium|high|xhigh|max>, --prompt-file <path>, [task text]. Defaults to opus with no forced effort. Use when Claude should investigate or change things, not when the user only wants review findings.'
description: 'Delegate a substantial diagnosis, implementation, or follow-up task to Claude Code through the tracked-job runtime. Args: --background, --wait, --resume, --resume-last, --fresh, --write, --model <model>, --effort <low|medium|high|xhigh|max>, --prompt-file <path>, [task text]. Defaults to CC_PLUGIN_CODEX_DEFAULT_MODEL (otherwise opus) with no forced effort. Use when Claude should investigate or change things, not when the user only wants review findings.'
---

# Claude Code Rescue
Expand All @@ -24,7 +24,9 @@ Raw slash-command arguments:

Supported arguments: `--background`, `--wait`, `--resume`, `--resume-last`, `--fresh`, `--write`, `--model <model>`, `--effort <low|medium|high|xhigh|max>`, `--prompt-file <path>`, plus free-text task text

Companion defaults: model=opus, and no effort. The companion forwards `--effort` only when the user passes it, so `fable`, `opus`, `sonnet`, and `haiku` each keep Claude Code's own effort default. Claude Code owns which effort levels each model supports.
Companion defaults: model=CC_PLUGIN_CODEX_DEFAULT_MODEL (otherwise opus), and no effort. The companion forwards `--effort` only when the user passes it, so `fable`, `opus`, `sonnet`, and `haiku` each keep Claude Code's own effort default. Claude Code owns which effort levels each model supports.

When the user omits `--model`, omit it from the companion command so the configured default can apply.
Comment on lines +27 to +29

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 Synchronize the rescue runtime's default-model contract

When CC_PLUGIN_CODEX_DEFAULT_MODEL is configured and rescue delegates to its forwarding worker, internal-skills/cli-runtime/runtime.md:27 still states that the companion defaults to opus. This leaves the internal execution contract inconsistent with both this public skill and the shared resolver, so update that runtime reference to describe the environment-variable fallback as part of this contract change.

AGENTS.md reference: AGENTS.md:L5-L5

Useful? React with 👍 / 👎.


Forward `--model` unchanged to the companion. The companion trims surrounding whitespace, canonicalizes the friendly aliases `fable`, `opus`, `sonnet`, and `haiku` to lowercase, then forwards every other `--model` value unchanged to Claude Code. Claude Code owns alias resolution and supported effort levels; `/model` is the authoritative picker for the current account and provider.

Expand Down
6 changes: 4 additions & 2 deletions skills/review/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: review
description: 'Run a standard Claude Code review of local git changes in this repository. Args: --wait, --background, --base <ref>, --scope <auto|working-tree|branch>, --model <model>, --effort <low|medium|high|xhigh|max>. Defaults to opus with no forced effort. Use as the default path for ordinary code-review requests when the user did not explicitly ask for stronger adversarial scrutiny or for Claude to own the implementation work.'
description: 'Run a standard Claude Code review of local git changes in this repository. Args: --wait, --background, --base <ref>, --scope <auto|working-tree|branch>, --model <model>, --effort <low|medium|high|xhigh|max>. Defaults to CC_PLUGIN_CODEX_DEFAULT_MODEL (otherwise opus) with no forced effort. Use as the default path for ordinary code-review requests when the user did not explicitly ask for stronger adversarial scrutiny or for Claude to own the implementation work.'
---

# Claude Code Review
Expand All @@ -16,7 +16,9 @@ If the overall request is "you review it too, also ask Claude to review in the b
Resolve `<plugin-root>` as two directories above this `SKILL.md` file. Always run the companion from that active plugin root:
`node "<plugin-root>/scripts/claude-companion.mjs" review ...`

Supported arguments: `--wait`, `--background`, `--base <ref>`, `--scope auto|working-tree|branch`, `--model <model>`, `--effort <low|medium|high|xhigh|max>` (defaults: model=opus and no effort; `fable`, `opus`, `sonnet`, and `haiku` each keep Claude Code's own effort default, and Claude Code owns which effort levels each model supports)
Supported arguments: `--wait`, `--background`, `--base <ref>`, `--scope auto|working-tree|branch`, `--model <model>`, `--effort <low|medium|high|xhigh|max>` (defaults: model=CC_PLUGIN_CODEX_DEFAULT_MODEL (otherwise opus) and no effort; `fable`, `opus`, `sonnet`, and `haiku` each keep Claude Code's own effort default, and Claude Code owns which effort levels each model supports)

When the user omits `--model`, omit it from the companion command so the configured default can apply.

Forward `--model` unchanged to the companion. The companion trims surrounding whitespace, canonicalizes the friendly aliases `fable`, `opus`, `sonnet`, and `haiku` to lowercase, then forwards every other `--model` value unchanged to Claude Code. Claude Code owns alias resolution and supported effort levels; `/model` is the authoritative picker for the current account and provider.

Expand Down
38 changes: 37 additions & 1 deletion tests/claude-cli.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright 2026 Sendbird, Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it } from "node:test";
import { afterEach, beforeEach, describe, it } from "node:test";
import assert from "node:assert/strict";

import * as claudeCli from "../scripts/lib/claude-cli.mjs";
Expand Down Expand Up @@ -467,6 +467,42 @@ describe("resolveModel", () => {
// ===========================================================================

describe("resolveDefaultModel", () => {
let savedDefaultModel;
beforeEach(() => {
savedDefaultModel = process.env.CC_PLUGIN_CODEX_DEFAULT_MODEL;
delete process.env.CC_PLUGIN_CODEX_DEFAULT_MODEL;
});
afterEach(() => {
if (savedDefaultModel === undefined) {
delete process.env.CC_PLUGIN_CODEX_DEFAULT_MODEL;
} else {
process.env.CC_PLUGIN_CODEX_DEFAULT_MODEL = savedDefaultModel;
}
});

it("uses the configured default for omitted or blank model arguments", () => {
process.env.CC_PLUGIN_CODEX_DEFAULT_MODEL = " provider-model ";
for (const model of [undefined, null, "", " "]) {
assert.equal(resolveDefaultModel(model), "provider-model");
}
assert.deepEqual(
buildArgs("test", { model: resolveDefaultModel() }),
["-p", "--output-format", "json", "--model", "provider-model"]
);
});

it("prefers an explicit model over the configured default", () => {
process.env.CC_PLUGIN_CODEX_DEFAULT_MODEL = "sonnet";
assert.equal(resolveDefaultModel("provider-model"), "provider-model");
});

it("falls back to opus when the configured default is empty or whitespace", () => {
for (const value of ["", " "]) {
process.env.CC_PLUGIN_CODEX_DEFAULT_MODEL = value;
assert.equal(resolveDefaultModel(), "opus");
}
});

it("returns 'opus' when model is null/undefined/empty", () => {
assert.equal(resolveDefaultModel(null), "opus");
assert.equal(resolveDefaultModel(undefined), "opus");
Expand Down