[FEATURE] Serialize interactive prompts as structured output in `-p` (non-interactive) mode

Resolved 💬 3 comments Opened Feb 10, 2026 by efhsg Closed Feb 13, 2026

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

When running Claude Code in non-interactive mode (-p), if the model decides it needs clarification from the user, it triggers an interactive multi-select prompt (the Ink-based TUI widget with numbered options and arrow-key navigation). Since there is no TTY in -p mode, this prompt cannot render, and the CLI either hangs indefinitely or silently returns no output.

This is a silent failure. The caller has no way to:

  1. Know that a clarification question was triggered
  2. Read the question and its options
  3. Provide an answer programmatically

This makes -p mode unreliable for automation, CI/CD pipelines, and scripting, because you cannot predict when the model will decide to ask a follow-up question. Adding prompt instructions like "don't ask questions" is a workaround, but it's fragile — there's no guarantee the model will comply.

Reproduction

  1. Create a complex prompt that involves ambiguous design decisions (e.g., a multi-phase code analysis task)
  2. Run it with claude -p "your complex prompt"
  3. If the model decides it needs clarification → no output, process hangs or exits

Use cases affected

  • CI/CD pipelines: A build step using claude -p that needs to handle ambiguous tasks
  • Wrapper scripts: Tools that orchestrate multiple claude -p calls and need to handle clarification loops
  • Batch processing: Running the same prompt against multiple codebases where some trigger questions and others don't

Related issues:

  • #17603 — Model doesn't know it's in -p mode
  • #581 — Non-interactive mode doesn't respect configured tool permissions
  • #6009 — Feature request for piping input to interactive sessions

Proposed Solution

When running in -p mode and the model triggers an interactive prompt (multi-select, single-select, confirmation, or free-text input), serialize it as structured output to stdout instead of attempting to render a TUI widget.

Proposed output format (JSON, when using --output-format json)

{
  "type": "input_required",
  "input_type": "single_select",
  "question": "How should the existing project be matched for deletion before loading?",
  "options": [
    {
      "index": 1,
      "label": "Name + user_id",
      "description": "Match on project name and user. Works when IDs differ between machines."
    },
    {
      "index": 2,
      "label": "Same project ID",
      "description": "Assume the project ID in the dump matches the local ID."
    },
    {
      "index": 3,
      "label": "User chooses",
      "description": "User manually selects which local project to replace."
    }
  ],
  "session_id": "abc123"
}

Re-invocation with answer

The caller can then re-invoke Claude with the answer using --resume:

claude -p "2" --resume "abc123" --output-format json

Plain text fallback (without --output-format json)

[INPUT REQUIRED] How should the existing project be matched for deletion before loading?

1. Name + user_id — Match on project name and user. Works when IDs differ between machines.
2. Same project ID — Assume the project ID in the dump matches the local ID.
3. User chooses — User manually selects which local project to replace.

Session ID: abc123
Resume with: claude -p "<your choice>" --resume "abc123"

Alternatives considered

  1. Adding "don't ask questions" to the prompt — Fragile. The model may still decide to ask. And sometimes the questions are genuinely important and shouldn't be auto-resolved.
  2. --dangerously-skip-permissions — Only covers tool permission prompts, not model-initiated clarification questions.
  3. Piping yes or echo "1" to stdin — Doesn't work because the Ink TUI doesn't read from stdin in the same way.
  4. A --no-interactive flag that auto-resolves — Less useful than serializing, because the caller loses visibility into what was decided.

Alternative Solutions

  1. Adding "don't ask questions" to the prompt — Works sometimes but is fragile. The model may still decide to ask, and sometimes the clarification questions are genuinely important.
  2. --dangerously-skip-permissions — Only covers tool permission prompts, not model-initiated clarification questions.
  3. Piping yes or echo "1" to stdin — Doesn't work because the Ink TUI doesn't read from stdin in -p mode.
  4. A --no-interactive flag that auto-resolves — Less useful than serializing, because the caller loses visibility into what the model decided and why.

Priority

High - Significant impact on productivity

Feature Category

CLI commands and flags

Use Case Example

Example scenario:

  1. I have a complex multi-phase prompt that runs a domain analysis followed by a technical architecture phase for a Yii2/PHP codebase
  2. I run claude -p "$(cat my-prompt.md)" --output-format json in a wrapper script
  3. The model encounters an ambiguous design decision and triggers a multi-select prompt asking me to choose between 3 matching strategies
  4. In interactive mode, this renders a nice TUI with arrow-key selection — works great
  5. In -p mode: silent failure — no output, no error, process hangs or exits with empty result
  6. My wrapper script has no way to detect this happened, capture the question, or provide an answer

With this feature, step 5 would instead return a JSON object with "type": "input_required", and my wrapper script could:

  • Parse the question and options
  • Present them to the user via its own UI
  • Re-invoke with claude -p "2" --resume "session_id"
  • Or auto-select based on predefined rules

Additional Context

This is a significant gap in the -p mode contract. Currently, -p mode promises non-interactive execution, but the model can unilaterally break that contract by deciding to ask a clarification question. The caller has no way to handle this gracefully — it's a silent failure with no error code, no stderr output, and no way to detect or recover.

The --output-format stream-json mode would be a natural fit for emitting these input-required events as part of the JSON stream.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗