fix: pass --settings before the auth status subcommand (#1392)

`runCliAuthProbe` builds `["auth", "status", "--json"]` and, when the
session has a settings
override, appends `--settings <json>`. `--settings` is a global option
of the Claude Code CLI;
the `auth status` subcommand only accepts `--json` and `--text` (see
`claude auth status --help`).
So every probe with an override fails with `error: unknown option
'--settings'` and an empty
stdout. The catch branch logs `claude auth status failed: …` on every
call and returns
`undefined`. For sessions that pass `_meta.claudeCode.options.settings`,
the session-creation and
per-prompt refreshes therefore never update the auth status. Only the
initialize probe, which has
no override, still works.

This moves `--settings <json>` in front of the subcommand. Without an
override the argv is
unchanged.

**Reproduce** (no credentials needed; an empty config dir answers "not
logged in"):

```sh
d=$(mktemp -d)
env -i HOME="$d" PATH="$PATH" CLAUDE_CONFIG_DIR="$d/.claude" \
  claude auth status --json --settings '{}'
# error: unknown option '--settings'
env -i HOME="$d" PATH="$PATH" CLAUDE_CONFIG_DIR="$d/.claude" \
  claude --settings '{}' auth status --json
# {"loggedIn": false, "authMethod": "none", ...}
```

The option is applied in the global position, not just parsed:
`claude --settings '{"apiKeyHelper":"/bin/echo rt-fake-key"}' auth
status --json` reports
`"loggedIn": true, "authMethod": "api_key_helper"`.

Tested with 10 native Claude Code binaries (linux-x64), each in an empty
`HOME`:

| Claude Code | `auth status --json --settings '{}'` | `--settings '{}'
auth status --json` |
|---|---|---|
| 2.1.286 | unknown option | JSON |
| 2.1.294 | unknown option | JSON |
| 2.1.295, 2.1.296, 2.1.299 | unknown option | JSON |
| 2.1.300, 2.1.301, 2.1.302 | unknown option | JSON |
| 2.1.303 (the SDK's `claudeCodeVersion`) | unknown option | JSON |
| 2.1.304 | unknown option | JSON |

The existing test in `auth-status.test.ts` pinned the old order; it now
expects `--settings`
first. Its mock and `statusCalls()` matched probes by `args[1] ===
"status"`, which a leading
global option shifts, so both now locate the subcommand instead.

`npm run build`, `format:check`, `lint` and `test:run` pass, apart from
one test that fails
identically on `main` without this change.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
KlausUllrich
2026-10-26 11:24:11 +01:00
committed by GitHub
parent e369479f93
commit 6a33dedcbb
2 changed files with 12 additions and 5 deletions

View File

@@ -3430,7 +3430,9 @@ export class ClaudeAcpAgent {
try {
const cliPath = await claudeCliPath();
const args = ["auth", "status", "--json"];
if (context?.override) args.push("--settings", JSON.stringify(context.override));
// `--settings` is a global CLI option: after the `auth status` subcommand the CLI rejects
// it with "unknown option '--settings'", so it has to come first.
if (context?.override) args.unshift("--settings", JSON.stringify(context.override));
({ stdout } = await execFileAsync(cliPath, args, {
timeout: AUTH_STATUS_PROBE_TIMEOUT_MS,
cwd: context?.manager.getCwd() ?? process.cwd(),

View File

@@ -22,6 +22,11 @@ vi.mock("@anthropic-ai/claude-agent-sdk", async () => ({
/** Shared spy for both `claude auth status --json` and `claude auth logout`.
* Tests set `execFileResults` per invoked subcommand. */
const execFileSpy = vi.hoisted(() => vi.fn());
/** `auth status` probes, wherever global options such as `--settings` put the subcommand. */
function isStatusProbe(args: string[]): boolean {
const auth = args.indexOf("auth");
return auth >= 0 && args[auth + 1] === "status";
}
vi.mock("node:child_process", async () => {
const actual = await vi.importActual<typeof import("node:child_process")>("node:child_process");
@@ -409,7 +414,7 @@ describe("auth status over ACP", () => {
execFileSpy.mockImplementation((...invocation: unknown[]) => {
const args = invocation[1] as string[];
const cb = invocation[invocation.length - 1] as (...a: unknown[]) => void;
if (args[1] === "status") {
if (isStatusProbe(args)) {
const stdout = statusStdout;
const fails = statusFails;
const answer = () => {
@@ -451,7 +456,7 @@ describe("auth status over ACP", () => {
});
function statusCalls() {
return execFileSpy.mock.calls.filter((call) => (call[1] as string[])[1] === "status");
return execFileSpy.mock.calls.filter((call) => isStatusProbe(call[1] as string[]));
}
function updates() {
@@ -542,11 +547,11 @@ describe("auth status over ACP", () => {
env: { ANTHROPIC_BASE_URL: "https://example.invalid/override" },
});
expect(statusCalls().at(-1)?.[1]).toEqual([
"--settings",
JSON.stringify({ apiKeyHelper: "central proxy start" }),
"auth",
"status",
"--json",
"--settings",
JSON.stringify({ apiKeyHelper: "central proxy start" }),
]);
await promptOnce(sessionId);
await flushStatus();