diff --git a/README.md b/README.md index e47b098..6e3cec1 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,16 @@ $cc:review --model opus --effort xhigh # explicitly raise opus effort **Flags:** `--base `, `--scope `, `--wait`, `--background`, `--model `, `--effort ` -**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. @@ -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 ` | 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 ` | 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 ` | 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 ` | Read task description from a file | diff --git a/internal-skills/cli-runtime/runtime.md b/internal-skills/cli-runtime/runtime.md index 1171187..9e52b79 100644 --- a/internal-skills/cli-runtime/runtime.md +++ b/internal-skills/cli-runtime/runtime.md @@ -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. diff --git a/scripts/claude-companion.mjs b/scripts/claude-companion.mjs index 2a16c9e..c69b7f7 100644 --- a/scripts/claude-companion.mjs +++ b/scripts/claude-companion.mjs @@ -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 diff --git a/scripts/lib/claude-cli.mjs b/scripts/lib/claude-cli.mjs index 68ffdbb..c95a13a 100644 --- a/scripts/lib/claude-cli.mjs +++ b/scripts/lib/claude-cli.mjs @@ -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; } diff --git a/skills/adversarial-review/SKILL.md b/skills/adversarial-review/SKILL.md index 07878e3..ba5c962 100644 --- a/skills/adversarial-review/SKILL.md +++ b/skills/adversarial-review/SKILL.md @@ -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 , --scope , --model , --effort , [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 , --scope , --model , --effort , [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 @@ -16,7 +16,9 @@ Unlike `$cc:review`, this skill accepts custom focus text after the flags. The m Resolve `` as two directories above this `SKILL.md` file. Always run the companion from that active plugin root: `node "/scripts/claude-companion.mjs" adversarial-review ...` -Supported arguments: `--wait`, `--background`, `--base `, `--scope auto|working-tree|branch`, `--model `, `--effort `, 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 `, `--scope auto|working-tree|branch`, `--model `, `--effort `, 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. diff --git a/skills/rescue/SKILL.md b/skills/rescue/SKILL.md index 037e6cd..5241ca1 100644 --- a/skills/rescue/SKILL.md +++ b/skills/rescue/SKILL.md @@ -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 , --effort , --prompt-file , [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 , --effort , --prompt-file , [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 @@ -24,7 +24,9 @@ Raw slash-command arguments: Supported arguments: `--background`, `--wait`, `--resume`, `--resume-last`, `--fresh`, `--write`, `--model `, `--effort `, `--prompt-file `, 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. 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. diff --git a/skills/review/SKILL.md b/skills/review/SKILL.md index 37e2c2c..eb44f68 100644 --- a/skills/review/SKILL.md +++ b/skills/review/SKILL.md @@ -1,6 +1,6 @@ --- name: review -description: 'Run a standard Claude Code review of local git changes in this repository. Args: --wait, --background, --base , --scope , --model , --effort . 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 , --scope , --model , --effort . 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 @@ -16,7 +16,9 @@ If the overall request is "you review it too, also ask Claude to review in the b Resolve `` as two directories above this `SKILL.md` file. Always run the companion from that active plugin root: `node "/scripts/claude-companion.mjs" review ...` -Supported arguments: `--wait`, `--background`, `--base `, `--scope auto|working-tree|branch`, `--model `, `--effort ` (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 `, `--scope auto|working-tree|branch`, `--model `, `--effort ` (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. diff --git a/tests/claude-cli.test.mjs b/tests/claude-cli.test.mjs index 5c37212..efcc23a 100644 --- a/tests/claude-cli.test.mjs +++ b/tests/claude-cli.test.mjs @@ -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"; @@ -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");