Skip to content

task --help (and any unknown --flag) is swallowed into the prompt and dispatches a real thread, hijacking --resume-last #539

Description

@wrightbuilt

Environment

  • Plugin: codex-plugin-cc v1.0.6 (Claude Code marketplace openai-codex)
  • Codex CLI: codex-cli 0.144.4
  • Claude Code: 2.1.216
  • OS: macOS (cross-platform — the defect is in JS arg handling, not platform-specific)

Summary

codex-companion.mjs task --help (and any unknown --flag on a subcommand) is silently swallowed into the prompt and dispatches a real Codex thread. There is no help output for the task subcommand — --help becomes the literal prompt text "--help", Codex is asked to respond to it, and the resulting throwaway thread becomes the most-recent thread, so the next --resume-last resumes it instead of the intended prior thread.

This is closely related to #512 (prose --model/--write hijacked as real options) — same root cause in parseArgs — but is the inverse manifestation: an unrecognized flag leaking into the prompt rather than a prose word being consumed as a flag. A single fix to unknown-flag handling addresses both.

Reproduction

node scripts/codex-companion.mjs task --help --cwd /tmp --json

Expected: usage text for the task subcommand, exit 0, no thread dispatched.

Actual (v1.0.6): a real thread is created and Codex answers the prompt "--help":

{
  "status": 0,
  "threadId": "019f84d2-cb9d-7101-b315-16dd344b4bd0",
  "rawOutput": "I'm Codex, your coding and research collaborator.\n\nCommon requests:\n- \"Diagnose this test failure.\"\n...\n- `--help` — show this help\n...",
  "touchedFiles": [],
  "reasoningSummary": []
}

Note the model-level help is Codex answering the prompt — not the companion CLI's usage. The created threadId now shadows --resume-last.

Root cause

Two gaps compound:

  1. main() only handles help at the subcommand position (scripts/codex-companion.mjs):

    const [subcommand, ...argv] = process.argv.slice(2);
    if (!subcommand || subcommand === "help" || subcommand === "--help") { printUsage(); return; }

    Once task is the subcommand, a following --help is just an arg passed to handleTask.

  2. parseArgs pushes any unrecognized long flag into positionals (scripts/lib/args.mjs):

    if (token.startsWith("--")) {
      const [rawKey, inlineValue] = token.slice(2).split("=", 2);
      const key = aliasMap[rawKey] ?? rawKey;
      if (booleanOptions.has(key)) { /* ... */ continue; }
      if (valueOptions.has(key))   { /* ... */ continue; }
      positionals.push(token);   // <-- unknown --flag silently becomes a positional
      continue;
    }

    handleTaskreadTaskPrompt then does positionals.join(" "), so the unknown flag becomes the prompt; requireTaskRequest sees a non-empty prompt and dispatches.

Suggested fix

Two targeted changes, low blast radius:

  1. Handle --help/-h before dispatch, at any position. In main() (or per-handler), if the subcommand's args contain --help/-h, print that subcommand's usage and exit 0 without dispatching.

  2. Reject unknown flags instead of swallowing them. Either an opt-in strict/allowUnknownFlags: false mode in parseArgs, or a guard at the task boundary: any positional beginning with -- is an unknown flag → throw Unknown flag: <token> rather than feeding it to the prompt. This also fixes the dangerous general case where a typo'd flag (--backgroud, --modl) silently becomes prompt text and dispatches. Prefer guarding at the task boundary (or an opt-in strict mode) over making parseArgs globally strict, to avoid disturbing any handler that intentionally relies on passthrough.

Impact

Automated callers that pass explicit, known flags are unaffected. The failure hits interactive/agent exploration (task --help) and any typo'd flag, and — because the throwaway thread shadows --resume-last — silently corrupts a subsequent resume, which is hard to notice after the fact.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions