Streaming output in `--verbose --print`

Status Fixed / completed
Maintainer reply ✓ Yes — ashwin-ant
Activity 9 comments · opened Apr 7, 2025 · closed Apr 9, 2025
💡 Likely answer: A maintainer (ashwin-ant, collaborator) responded on this thread — see the highlighted reply below.

Followup from https://github.com/anthropics/claude-code/issues/365#issuecomment-2784136143

When using --verbose with --print, output is only available as json:

Error: When using --print, --verbose must be used together with --json
    at sg1.<anonymous> (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1616:246)
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
    at async sg1.parseAsync (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:424:4184)
    at async Or5 (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1619:4348)
    at async dr5 (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1615:41755)
filipesilva@m4 ~/r/sandbox [1]> claude --verbose --json --print "say hello!"
[
  {
    "role": "user",
    "content": "say hello!"
  },
  {
    "id": "msg_01Xx3rAZ93AyDpMALs8nCJ9n",
    "type": "message",
    "role": "assistant",
    "model": "claude-3-7-sonnet-20250219",
    "content": [
      {
        "type": "text",
        "text": "Hello!"
      }
    ],
    "stop_reason": "end_turn",
    "stop_sequence": null,
    "usage": {
      "input_tokens": 4,
      "cache_creation_input_tokens": 26313,
      "cache_read_input_tokens": 0,
      "output_tokens": 5
    }
  },
  {
    "role": "system",
    "cost_usd": 0.09876075000000001,
    "duration_ms": 3365,
    "duration_api_ms": 3366,
    "result": "Hello!"
  }
]

It would be much nicer for CI and debugging scenarios if it would stream the output like in the non-print call then exited:

filipesilva@m4 ~/r/sandbox> claude "say hello!"
╭────────────────────────────────────────────╮
│ ✻ Welcome to Claude Code research preview! │
│                                            │
│   /help for help                           │
│                                            │
│   cwd: /Users/filipesilva/repos/sandbox    │
╰────────────────────────────────────────────╯

 Tips for getting started:

 1. Run /init to create a CLAUDE.md file with instructions for Claude
 2. Run /terminal-setup to set up terminal integration
 3. Use Claude to help with file analysis, editing, bash commands and git
 4. Be as specific as you would with another engineer for the best results

> say hello!

⏺ Hello!

╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ > Try "how does <filepath> work?"                                                                                                                                                                                                                                                                                                                                                                                                        │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
  Press Ctrl-C again to exit



Total cost:            $0.0080
Total duration (API):  1.7s
Total duration (wall): 11.2s
Total code changes:    0 lines added, 0 lines removed

View original on GitHub ↗

9 Comments

amaruy · 1 year ago

Streaming + json would be nice too. Currently I have no way of extracting the partial script of the command stops before completion.

ashwin-ant collaborator · 1 year ago

We've added this in 0.2.66! Try running:

claude -p --output-format stream-json "your prompt here"

It'll log individual JSON objects, so if you want to get a full JSON array at the end while also streaming, try:

claude -p --output-format stream-json "your prompt here" | tee /dev/tty | jq -s '.'

Regular (non-streaming) JSON is still available via --json (deprecated) or via --output-format json.

maschwenk · 1 year ago

For anyone wondering how to get this to work in Github Actions, where you need to force a TTY, you are welcome in advance.

https://github.com/actions/runner/issues/241#issuecomment-924327172

ashwin-ant collaborator · 1 year ago

^ Another option in GitHub Actions is to do it in two steps:

# Run Claude and tee the output to stdout while saving to output.txt
claude -p --output-format stream-json "your prompt here" | tee output.txt

# Parse the output into JSON
jq -s '.' output.txt > output.json
filipesilva · 1 year ago

@ashwin-ant the --output-format stream-json is pretty nice, but the issue was originally about more of a stream-text format instead: just print what claude would have printed otherwise.

Consider the different for the what's the current directory prompt below:

filipesilva@m4 ~/r/w/metabase (claude-code)> claude --print "whats the current directory"
/Users/filipesilva/repos/work/metabase⏎
filipesilva@m4 ~/r/w/metabase (claude-code)> claude "whats the current directory"
╭────────────────────────────────────────────────╮
│ ✻ Welcome to Claude Code research preview!     │
│                                                │
│   /help for help                               │
│                                                │
│   cwd: /Users/filipesilva/repos/work/metabase  │
╰────────────────────────────────────────────────╯

 Tips for getting started:

 1. Run /terminal-setup to set up terminal integration
 2. Use Claude to help with file analysis, editing, bash commands and git
 3. Be as specific as you would with another engineer for the best results
 4. ✔ Run /init to create a CLAUDE.md file with instructions for Claude

> whats the current directory

⏺ Bash(pwd)…
  ⎿  /Users/filipesilva/repos/work/metabase

╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ > Try "edit settings.ts to..."                                                                                                                                                                                                                                                                                                                                                                                                           │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
  Press Ctrl-C again to exit
filipesilva@m4 ~/r/w/metabase (claude-code)> claude --print --output-format stream-json "whats the current directory"
{
  "id": "msg_01RK6k48tEj9HbGtSQt6GoTj",
  "type": "message",
  "role": "assistant",
  "model": "claude-3-7-sonnet-20250219",
  "content": [
    {
      "type": "tool_use",
      "id": "toolu_01CYP17MVewCD8JF8b58mNNR",
      "name": "Bash",
      "input": {
        "command": "pwd",
        "description": "Show current working directory"
      }
    }
  ],
  "stop_reason": "tool_use",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 4,
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 25484,
    "output_tokens": 72
  }
}
{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "content": "/Users/filipesilva/repos/work/metabase",
      "tool_use_id": "toolu_01CYP17MVewCD8JF8b58mNNR"
    }
  ]
}
{
  "id": "msg_01AeJ67WHy2rHNyzPnFNUNUv",
  "type": "message",
  "role": "assistant",
  "model": "claude-3-7-sonnet-20250219",
  "content": [
    {
      "type": "text",
      "text": "/Users/filipesilva/repos/work/metabase"
    }
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 6,
    "cache_creation_input_tokens": 98,
    "cache_read_input_tokens": 25580,
    "output_tokens": 19
  }
}
{
  "role": "system",
  "cost_usd": 0.0172697,
  "duration_ms": 5840,
  "duration_api_ms": 5840
}

The claude --print "whats the current directory" version does not show me that claude ran pwd, which is a very important thing. The --output-format stream-json does, but now you need to parse the json as its streamed, which is not that straightforward.

The CI scenario is the one I really want to highlight here... if I'm running claude in CI I'd very much like to see the output as it comes, like pretty much any other logging in CI, as if I ran claude <prompt> followed by exiting. One-shotting it, if you will, since CI is non-interactive.

I don't have permissions to reopen but would like to, because streaming json was not the original ask, but also understand if you want to close it as something you're not interested in tackling.

maschwenk · 1 year ago

I agree with @filipesilva but I can understand how a streaming text implementation could be a bit odd. Also one thing to consider is that the JSON can be somewhat hard to read for a human and does not include nice stuff like syntax highlighting for diffs, etc. Those things are nice when you are watching this stuff in CI.

ashwin-ant collaborator · 1 year ago

Understood. We'll potentially revisit this, but given the variety of use cases to support, keeping it very raw right now hopefully addresses the most needs. You can stream out just messages with something like:

claude -p --output-format stream-json "first say hi, then read the readme, then say bye" | 
  tee >(
    jq -r 'select(.content != null and (.content | type) == "array") | 
           .content[] | 
           select(.type == "text") | 
           .text'
  ) > output.txt

which outputs:

Hi!
Bye!

Or if you want tools as well, you can do:

claude -p --output-format stream-json "first say hi, then read the readme, then say bye" | tee >(jq -r '
  if .content != null and (.content | type) == "array" then
    # Extract text content
    (.content[] | select(.type == "text") | "TEXT: " + .text),
    # Extract tool use
    (.content[] | select(.type == "tool_use") | "TOOL: " + (.name) + " - " + (.input|tostring))
  else empty
  end' > /dev/tty) > output.txt

which outputs:

TEXT: Hi!
TOOL: View - {"file_path":"/path/to/readme"}
TEXT: Bye!

You could extract tool_result messages similarly.

It's definitely a bit more work, but it should be pretty flexible. Claude Code is quite good at writing out jq commands 😉

tino · 1 year ago

Some recent update changed the format to JSONL? At least this now works against 0.2.120:

jq -r '
    if .message.content != null and (.message.content | type) == "array" then
      # Extract text content
      (.message.content[] | select(.type == "text") | "TEXT: " + .text),
      # Extract tool use
      (.message.content[] | select(.type == "tool_use") | "TOOL: " + .name + " - " + (.input|tostring))
    else
      "NON-ARRAY-CONTENT: " + (.|tostring)
    end'
github-actions[bot] · 1 year ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.