How to Extract Text from Claude Code JSON Stream Output
2026-01-09 01:55 (2 months ago)
Background / Problem
When you use Claude Code’s --output-format=stream-json option, you get streaming output in JSON Lines format like this:
{"type":"stream_event","event":{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"容を"}},...}
{"type":"stream_event","event":{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"レビューして"}},...}
{"type":"stream_event","event":{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"問"}},...}
From this stream, you want to extract only the text content in the .event.delta.text field and display it in real time in the terminal.
Requirements
- Output the text fragments continuously (no newline per fragment)
- Only print a newline when
\nappears in the text - Support real-time streaming
Attempt 1: The problem with jq -r
The first method tried:
claude code ... | jq -r '.event.delta.text? // empty'
Issues:
- The
-r(--raw-output) option appends a newline after each output result - Fragments like “容を”, “レビューして”, “問” end up each on their own line
- As a result, text that should be continuous gets broken up unnaturally
Solution: The jq -j option
Final solution:
claude code の ${HOME}/.claude/local/claude \
--verbose --permission-mode delegate \
--print --output-format=stream-json --include-partial-messages "/push-with-review" \
| jq -j '.event.delta.text? // empty'
Characteristics of jq -j (--join-output)
- Does not automatically add newlines when printing output
- Concatenates text as-is
- Any
\ninside the text is displayed as an actual newline - Ideal for streaming processing
When you need to handle multiple message types
| jq -j '
(.event.delta.text? // empty),
(.message.content[]?.text? // empty)
'
This lets you continuously output text from both stream_event and assistant messages.
jq filter details
jq features used
-
Optional operator (
?)- Using
.event.delta.text?prevents errors even if the field doesn’t exist
- Using
-
Alternative operator (
//)// emptyoutputs nothing when the value is null or undefined
-
Strict filtering with
select()(alternative)| jq -j 'select(.event.delta.text != null) | .event.delta.text'
Reference
jq option comparison
| Option | Behavior | Use case |
|---|---|---|
-r (--raw-output) |
Adds a newline after each result | When you want to process line by line |
-j (--join-output) |
Does not add newlines | When you want continuous text output |
-c (--compact-output) |
Compresses JSON into one line | Keep JSON structure while compacting |
Reference URLs
- jq Manual: https://jqlang.org/manual/
- JSON Lines format: https://jsonlines.org/
Conclusion
To extract only the text from Claude Code’s streaming output, jq’s -j option is the best fit.
- Simple
- Fast
- Works with real-time streaming
- No need to build a dedicated tool in Rust, etc.
Practical examples
# Basic form
claude -p "タスク内容" | jq -j '.event.delta.text? // empty'
# Filter only stream_event
claude -p "タスク内容" | jq -j 'select(.type == "stream_event") | .event.delta.text? // empty'
With this, you can display Claude Code output in a human-readable format in real time.
Please rate this article
Currently unrated
The author runs the application development company Cyberneura.
We look forward to discussing your development needs.
We look forward to discussing your development needs.