[FEATURE] Add Option to Always Show Claude's Thinking

Open 💬 86 comments Opened Sep 30, 2025 by janbam

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

Since v2.0.0 thinking is no longer shown in verbose mode.
I work on steering and improving Claude's thinking.
Now I have to press ctrl-o, ctrl-e, and scroll up all the time to see the thinking content, which is tedious.

Proposed Solution

Add a config option to always show thinking, like in verbose mode pre v2.0

Alternative Solutions

_No response_

Priority

High - Significant impact on productivity

Feature Category

Interactive mode (TUI)

Use Case Example

_No response_

Additional Context

_No response_

View original on GitHub ↗

86 Comments

github-actions[bot] · 9 months ago

Found 2 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/8371
  2. https://github.com/anthropics/claude-code/issues/8453

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

Emasoft · 9 months ago

Yes! I really can’t work without being able to follow the model train of thought! 🙏

damsolanke · 9 months ago

Please prioritize this!!!

trb · 9 months ago

To further provide a use case - I've had multiple instances where I needed to correct Claude Code's usage of internal libraries and I only found out because I checked the thought process with Ctrl+O.

The thinking tokens are so important in order to see if Claude Code is following instructions, it's a surprisingly ill-informed decision to hide them by default (and make them only accessible with the non-updating verbose mode), given that otherwise Claude Codes development has been pretty in-line with my use of it.

I agree with Emasoft, Claude Code is nearly unusable to me without having to check verbose mode all the time.

davidjson · 9 months ago

You can downgrade via npm i -g @anthropic-ai/claude-code@1.0.128 to display thinking if you don't rely on the new features.

fenixgx · 9 months ago

Plase## Critical Use Case: Real-time Code Review

I need to see Claude's thinking blocks in real-time to catch errors before they happen, not after.

### My Workflow (that v2.0 broke):

  1. I ask Claude to implement something
  2. I watch the thinking blocks as they appear
  3. If I see Claude going in the wrong direction → I stop it immediately
  4. This saves me hours of debugging bad implementations

### Why Ctrl+O, Ctrl+E doesn't work:

  • It's post-mortem debugging - I can only see thinking AFTER Claude finishes
  • By then, the wrong code is already written
  • I lose the ability to course-correct in real-time

### The Value:

> "Seeing Claude think is like pair programming with visibility into your partner's reasoning. Without it, it's like coding with someone who won't tell you their plan until after
they've written everything."

### What I Need:

A simple setting in ~/.claude/settings.json:

```json
{
"alwaysShowThinking": true
}

Or an environment variable:
CLAUDE_CODE_ALWAYS_SHOW_THINKING=true

Priority:

This isn't a "nice to have" - it's the difference between:

  • ✅ Proactive collaboration (v1.0.128)
  • ❌ Reactive debugging (v2.0+)

Please bring back the ability to see thinking blocks in real-time. The community is clearly asking for this.

---
Temporary workaround for others: Downgrade to npm i -g @anthropic-ai/claude-code@1.0.128 but this means losing all v2.0+ improvements.

ericksonc · 9 months ago

I'm concerned about the trajectory here. Looking at industry patterns:

  • OpenAI never exposes reasoning tokens
  • Gemini moved from raw thinking to "summaries"
  • Claude shows full thinking in web UI but obscures it via API
  • Now Claude Code v2.0 makes thinking harder to access

For power users working on prompt engineering, debugging, and understanding AI behavior, visibility into reasoning is incredibly valuable. This feels like a progressive degradation rather than a UX improvement.

I don't want to assume motives, but from a user perspective: making this harder to access, then pointing to "low usage" to justify removing it entirely would be frustrating. Can Anthropic commit that full reasoning visibility will remain accessible for users who want it, even if it's not the default?

tom-murray-dev · 9 months ago

I genuinely can't believe that this vibe coding feature was released as the default option with no fallback, a potentially worrying indication of where Claude Code is heading, but I hope i'm wrong.

aaronsb · 9 months ago

Claude code MUST show it's thinking in a more user friendly way. Italicized, highlighted some how as a thought bubble, I don't care. stop hiding it behind control-o reveal, because it's too slow. It should be a simple matter to configure claude to expose thinking if it's on, persistently as an option. I can't black box the thinking because it's shared with MY thinking.

Reading the thinking is like reading "body language" for a language model agent. If I can't read the body language, then I can't read the intent that's about to come next.

IV2KBMoFxYIA · 8 months ago

+1 for this one. I somehow enabled it magically on a session on Claude Code 2.0.32 and it is showing the thinking as it happens. I wish I knew how I did that, since apparently it _is_ possible.

<img width="1194" height="502" alt="Image" src="https://github.com/user-attachments/assets/e5a30fc6-ebfc-48f4-b6d6-3759396a9e10" />

IV2KBMoFxYIA · 8 months ago

nevermind. i asked Claude itself.

<img width="1230" height="1081" alt="Image" src="https://github.com/user-attachments/assets/7dee8875-89f2-44db-94f3-ff673fd7c2b4" />

anthrotype · 8 months ago

I believe the "alwaysThinkingEnabled" setting was meant to enable thinking mode by default on startup so you don't have to press Tab, not to toggle whether the thinking is made visible.

noughts · 8 months ago

I'm using v2.0.36 and confirmed that enabling verbose output does display Thinking logs as a workaround. However, this workaround has significant limitations:

  1. Excessive log noise: Verbose mode displays numerous other logs alongside Thinking output, making it difficult to read and understand the actual thinking process
  2. Degraded developer experience: Every time I need to review Thinking content, I have to filter through unrelated logs, which significantly hampers the development workflow

Adding an independent option to control Thinking log display (e.g., showThinkingLogs or verboseThinking) would provide substantial benefits:

  • Ability to focus on Thinking content without distraction
  • Easier debugging and understanding of the model's reasoning process
  • Reduced noise by separating Thinking logs from other debug output
  • Better workflow for developers who actively work on steering and improving Claude's thinking

This feature would be extremely valuable for users who want to understand and improve LLM reasoning processes. I'd greatly appreciate your consideration.

adamavenir · 7 months ago

Just joining here to say this is a real problem for building UX-friendly things on the Claude ~~Code~~ Agent SDK.

As I said in this SDK issue

I have a suspicion (wild guess based on my own opinions on how I'd design the harness) that the reason the thinking output was removed is that they're now using more sophisticated model routing during extended thinking phases and involving calling multiple parallel subagents and, assuming that it's not 'secret sauce' they would prefer not to expose, it represents a challenge to present the result in a legible way given the current design. And my _further_ guess is they would like to just go "turn thinking back on" at some point and what we get there will actually be (multi-staged?) summar(ies) of the multi-agent process rather than the actual under-the-hood agent activity, as this will actually be more meaningful and understandable _anyway_ but piecing that together is obviously a little challenging. I say I suspect all this because this is exactly how I'm architecting agent 'thinking' phases in my own systems lol
anthrotype · 7 months ago

The most frustrating thing is this would be trivial for Anthropic to solve: a config flag like showThinkingInline: true in settings.json, or a --show-thinking CLI flag. Instead, we have to resort to reverse-engineering minified JS (thanks @aleks-apostle for the service you provide to keep the patch working everytime CC gets updated!)

I suspect this is partly a case where the Claude Code team optimized for the median user experience (cleaner UI, less visual noise) without realizing how much they were degrading the experience for those users who care most deeply about the tool. The people patching JS to restore inline thinking are probably also the people filing the most useful bug reports and pushing the product's limits in productive ways.

numman-ali · 7 months ago

+1

tom-murray-dev · 7 months ago

Looks like they've added a native fix for this...

/config > Verbose Output > True

anthrotype · 7 months ago

Yes, it's true that with "Verbose Output" enabled the thinking summaries get shown, however _a lot_ of other things (like the full output of tool calls) also get displayed which makes it difficult to read the thinking interspersed with the normal output, as it used to be before v2.0. Only the patch from @aleks-apostle restores the latter behavior.

github-actions[bot] · 6 months ago

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

saeedesmaili · 6 months ago

Don't close it, still not fixed.

tremo1 · 6 months ago

Can you please prioritize this? following the reasoning process, helps in correcting the model and understanding how the model reached a certain conclusion

tremo1 · 6 months ago

I actually found that using claude --verbose serves what I was looking for, more output & more reasoning shown by default in main CC view.

anthrotype · 5 months ago

but that is TOO verbose, shows the full tools' output. we only want to have inline thinking blocks like we used to have before v2.0...

saeedesmaili · 5 months ago

Yeah agreed, that's not useful at all, we don't want to review the tool output reaults, we just want ti be able to review the thinking logic of the model.

toppsdown · 5 months ago

Something seems to have just changed. I have verbose output on and last week I was able to read along with it's decision making process. Now it just says "thinking" and I get long summary dump at the end.

Coletronix · 5 months ago

Agreed. There needs to be an option to show thinking, even if it isn't the default. I'm trying to debug a local model thinking for too long, and I need to be able to see what loop it's getting stuck in

emmahyde · 5 months ago

Whether or not the people in this thread would be pleased if this is something you're intentionally phasing out, it _would_ be nice to hear back from a dev simply if this is intended or not - you guys are amazingly fast at so many responses on here and I thank you for that, but this has gone unanswered for months and is still active and causing issues. Please let us know if this is intended or unintended behavior.

lost-RD · 5 months ago

I bet the devs would love to leave thinking exposed but the suits have forbidden it. The reasoning models are valuable IP and exposing their output risks giving details away to competitors.

JanChec · 4 months ago

With verbose configuration you can see the thinking without clicking anything. However, each thinking process' output shows up only after it completes. It's not streamed. Which means if it thinks for a while, you can't stop to correct or be simply caught up - you need to catch up afterwards (wait, read, wait...). That's the problem for me. UX here is essential.

TinDang97 · 4 months ago

.

wowitsjack · 4 months ago

There are two fix-halves for this that already exist but are poorly documented:

Claude Code must have Verbose Mode enabled in /config

1. Real-time thinking toggle (interactive TUI)

Press Alt+T (meta+t) during a session to toggle thinking block visibility on/off in the live TUI. This is the chat:thinkingToggle keybind.

2. Persist thinking summaries in transcript/verbose mode

Add this to ~/.claude/settings.json:

{
  "showThinkingSummaries": true
}

This was introduced around v2.1.69 but defaults to false/undefined, which causes the API to redact thinking text entirely (via a redact-thinking-2026-02-12 beta header). Setting it to true restores thinking content in Ctrl+O transcript view and verbose output.

---

macOS users: If Alt+T doesn't work, your terminal is probably sending Option+T as a special character (†) instead of Meta. Fix per terminal:

  • iTerm2: Preferences > Profiles > Keys > set Left/Right Option to Esc+
  • Kitty: set macos_option_as_alt yes in kitty.conf
  • Terminal.app: limited Meta support, consider switching terminals

Confirmed working on v2.1.71, Linux.

cc @trb @RokaCreativa @Emasoft @damsolanke @davidjson @catthedd @dandanmarcovici @Stoops0311 @lost-RD @adamavenir

anthrotype · 4 months ago
Press Alt+T (meta+t) during a session to toggle thinking block visibility on/off in the live TUI.

alt+T does not toggle thinking _visibility_, it enables or disables thinking mode (whether Claude will think before responding). Unfortunately, the thinking message are no longer visible inline in the live TUI (unless you run in --verbose mode or with ctrl+o transcript mode -- which is not "live") and they can't be enabled via any toggle (despite many people asking multiple times in here). I resort to patching the minified cli.js to make them visible in normal mode.

wowitsjack · 4 months ago
> Press Alt+T (meta+t) during a session to toggle thinking block visibility on/off in the live TUI. alt+T does not toggle thinking _visibility_, it enables or disables thinking mode (whether Claude will think before responding). Unfortunately, the thinking message are no longer visible inline in the live TUI (unless you run in --verbose mode or with ctrl+o transcript mode -- which is not "live") and they can't be enabled via any toggle (despite many people asking multiple times in here). I resort to patching the minified cli.js to make them visible in normal mode.

they BOTH have to be on and verbose mode on in settings.

Once enabled, and that flag set, full visibility works. In real time. With no need for ctrl-o.

wowitsjack · 4 months ago

<img width="2485" height="368" alt="Image" src="https://github.com/user-attachments/assets/4fb6f8d4-7e52-4ea8-9df6-9e02da3b5498" /> @anthrotype

caioariede · 4 months ago
<img alt="Image" width="2000" height="368" src="https://private-user-images.githubusercontent.com/18360463/563017947-4fb6f8d4-7e52-4ea8-9df6-9e02da3b5498.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NzM0MDc2OTQsIm5iZiI6MTc3MzQwNzM5NCwicGF0aCI6Ii8xODM2MDQ2My81NjMwMTc5NDctNGZiNmY4ZDQtN2U1Mi00ZWE4LTlkZjYtOWUwMmRhM2I1NDk4LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjAzMTMlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwMzEzVDEzMDk1NFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTcyOGE2ZDkyNWFlYmU4YWU5NjE3MTFkMzM1N2QyMDNiMmU0MTRlOWVjNDQwMTg2NWM4NjFhZTUyZTljNTM1YzgmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.12mvcoVkueK2X_c2eFFTUyJZLeKI4uZmw1QOz32YE5I"> @anthrotype

this didn't work for me. I'm on 2.1.74 (macOS)

anthrotype · 4 months ago

yeah, exactly what I said. You need to use --verbose, but I don't want --verbose! Verbose also prints other stuff like full tool outputs which is just noise for me, I want the pre-v2.0 behavior where thinking would just be printed inline in between regular messages and regular (non-verbose) tool calls. That's why my patch is still necessary (for me at least).

wowitsjack · 4 months ago
> <img alt="Image" width="2000" height="368" src="https://private-user-images.githubusercontent.com/18360463/563017947-4fb6f8d4-7e52-4ea8-9df6-9e02da3b5498.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NzM0MDc2OTQsIm5iZiI6MTc3MzQwNzM5NCwicGF0aCI6Ii8xODM2MDQ2My81NjMwMTc5NDctNGZiNmY4ZDQtN2U1Mi00ZWE4LTlkZjYtOWUwMmRhM2I1NDk4LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjAzMTMlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwMzEzVDEzMDk1NFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTcyOGE2ZDkyNWFlYmU4YWU5NjE3MTFkMzM1N2QyMDNiMmU0MTRlOWVjNDQwMTg2NWM4NjFhZTUyZTljNTM1YzgmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.12mvcoVkueK2X_c2eFFTUyJZLeKI4uZmw1QOz32YE5I"> @anthrotype this didn't work for me. I'm on 2.1.74 (macOS)

Verbose Mode on in /settings ?
Model set to Opus (High Thinking) ?
Tried using the 'ultrathink' keyword?
Restarted CC?
NPM or Native?

Do lmk, keen to figure this one out.

caioariede · 4 months ago

@wowitsjack thanks, it worked now!

so it seems verbose mode needs to be enabled!

caioariede · 4 months ago
yeah, exactly what I said. You need to use --verbose, but I don't want --verbose! Verbose also prints other stuff like full tool outputs which is just noise for me, I want the pre-v2.0 behavior where thinking would just be printed inline in between regular messages and regular (non-verbose) tool calls. That's why my patch is still necessary (for me at least).

@anthrotype mind sharing how to do your patch?

anthrotype · 4 months ago

Sure (I've shared several times in here). Claude patches it for me every time they publish a new release: https://github.com/aleks-apostle/claude-code-patches/pull/9

Simplereally · 4 months ago

This is still an issue. Using amazon bedrock, the thinking does not stream in, even with:
verbose as true in /config

and this set in settings.json:

{
  ...
  "showThinkingSummaries": true,
  "alwaysThinkingEnabled": true,
  ...
}
ducky-duke · 4 months ago

Vote

0x1ocean · 4 months ago

Show us thinking please

KrzaQ · 4 months ago

showThinkingSummaries does work for me (but it sucks it can't be set in the TUI)

NealMcConachieWork · 3 months ago

Request: Config option to always display thinking blocks (redact-thinking opt-out)

Between Claude Code v2.1.68 and v2.1.78, a server-side change began sending the redact-thinking API beta flag with requests. This strips thinking text from responses — content blocks arrive with thinking: "" and only the cryptographic signature retained. Even with ctrl+o transcript mode enabled, there's no thinking text to display.

This is a regression for users who rely on visible thinking as part of their active workflow. I use thinking blocks in two ways:

Live, during a session — when I'm planning with Claude or vibe-coding, I watch the thinking to catch misaligned reasoning before it becomes a wrong implementation. Spotting "I think the user wants X" in thinking when I actually want Y lets me course-correct immediately, saving significant time and tokens.

After the fact — I scroll back through thinking to understand:

  • Where Claude went off-track, so I can write better prompts next time
  • What information Claude spent time searching for, so I can provide that context upfront
  • How Claude interpreted ambiguous instructions, so I can be more precise

Visible thinking is one of the most valuable feedback loops for becoming a more effective Claude Code user. Without it, I can see what Claude did, but not why.

There's no official way to achieve this. The alwaysThinkingEnabled setting referenced in #13532 has never worked in my experience. Multiple issues have requested this capability: #8477, #9099, #15263, #30660 — indicating clear demand.

What I'd like: A setting (via /config or settings.json) that:

  1. Displays all thinking blocks inline as part of the normal conversation flow — always visible, not collapsed behind a keyboard shortcut
  2. Preserves thinking text in responses (opts out of the redact-thinking beta)
  3. Persists across sessions — set once, always on

This doesn't need to be the default — a power-user setting is fine. But the current state where thinking is both hidden in the UI and stripped at the API level makes it impossible to access, even for users who explicitly want it.

fabiogaliano · 3 months ago

this is so hidden, doesn't make sense. verbose should automatically show the thinking. but ye, thanks wowitsjack!

intellia1 · 3 months ago

This thread actually shows a pattern, and not a good one.

Back in v2.0, the issue was that thinking became harder to access (hidden behind ctrl+o / verbose). But as of v2.1.69+, it's no longer just hidden, it's often removed entirely.

Many of us are now seeing responses like:

  • thinking: ""
  • signature still present

Which strongly implies:
→ The model is still performing extended reasoning
→ The readable thinking is being intentionally stripped out

So the trajectory looks like:

  • v1.x → full visibility
  • v2.0 → reduced visibility (UI friction)
  • v2.1.69+ → effective removal of readable thinking

That's not an accident. That's a directional change.

And the concerning part isn't just the outcome, it's the lack of transparency:

  • No clear announcement
  • No explanation of the trade-offs
  • No opt-in / opt-out mechanism

For power users, this is a serious loss:
Thinking visibility is how you debug, steer, and understand model behavior. Without it, Claude Code becomes a black box.

There are also credible reports of redact-thinking-* behavior being applied server-side, which would explain why this is no longer recoverable even via transcript mode.

If that's the case, then this isn't a UI decision, it's a deliberate restriction of introspection.

So the real question is:
Is full thinking visibility being phased out without saying it?

Because from the outside, this looks like a gradual removal of a critical capability, done quietly.

MarioVilas · 3 months ago

Confirmed. I've intercepted the traffic with Burp Suite Pro, found this header:

Anthropic-Beta: oauth-2025-04-20,interleaved-thinking-2025-05-14,redact-thinking-2026-02-12,context-management-2025-06-27,prompt-caching-scope-2026-01-05

Long story short, I didn't find any official docs, but with some debugging and reversing found that adding this option to ~/.claude/settings.json seems to bring the thinking back:

{
  "showThinkingSummaries": true
}

If that fails at some point, a less subtle solution is to disable all betas completely:

CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1 claude

If at some point that doesn't work anymore, you can do what I first tried, when confirming the issue:

  1. Download your Burp certificate (or whatever proxy tool you're using - Zap, Fiddler, any will work).
  2. If the cert is in DER format (Burp does this) be sure to convert it to PEM: openssl x509 -inform DER -in cacert.der -out cacert.pem.
  3. Add this cert temporarily to Node's root certs with this environment variable: export NODE_EXTRA_CA_CERTS=cacert.pem (you may want to use an absolute path there). If that fails, try nuking all certificate validation completely: export NODE_TLS_REJECT_UNAUTHORIZED=0.
  4. Now run Claude Code by proxying it via your interception proxy: HTTPS_PROXY=http://127.0.0.1:8080 claude
  5. In Burp or equivalent, add a match-and-replace rule that substitutes redact-thinking-2026-02-12, (don't forget the trailing comma) for an empty string in all HTTP headers.

The last one is complicated as heck, but I doubt it'll ever stop working, no matter what the next patches bring. You may want to update the date in the "redact-thinking" part in later patches though. You can find it easily using the strings command: strings $(which claude) | grep --color redact-thinking.

EDIT: just saw an earlier comment saying this setting didn't work in Amazon Bedrock. For the record I tested this on the native client in Mac OS X. So, your mileage may vary.

If this is the direction things keep going in I might write an automatic patcher for the binary (until Anthropic decides to get that feature out of beta and into the main API, then there's not much to do from the client side).

intellia1 · 3 months ago

This is super useful, thanks @MarioVilas for taking the time to dig into it and share the workaround.

yurukusa · 3 months ago

Until Anthropic adds a native toggle, here are ways to make Claude's reasoning visible:
Workaround 1 — CLAUDE.md instruction:

<!-- CLAUDE.md -->
Always explain your reasoning step by step before taking any action.
Show your thought process as numbered steps in a code block labeled "Thinking".
Never skip the reasoning step, even for simple tasks.

This makes Claude externalize its thinking into visible output. It's not the same as the internal thinking blocks, but it surfaces the reasoning.
Workaround 2 — Keyboard shortcut for on-demand thinking:
Ctrl+O or Ctrl+E toggles extended thinking in the current session. If you find yourself toggling frequently, you can set it as default:

claude --thinking

Workaround 3 — Use verbose mode:

claude --verbose

Verbose mode shows more of the internal process (tool calls, API requests), though not the thinking blocks themselves.
Workaround 4 — Hook to log thinking content:
If you're on a plan that supports extended thinking, a PostToolUse hook can capture context:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "*",
        "hook": "bash -c 'INPUT=$(cat); echo \"[$(date +%H:%M:%S)] $(echo \"$INPUT\" | jq -r .tool_name)\" >> /tmp/claude-thinking.log'"
      }
    ]
  }
}

This logs tool usage (not thinking content directly, but helps trace the reasoning chain).
The core request — a persistent setting like "alwaysShowThinking": true in settings.json — would be the proper solution. The workarounds above approximate it from different angles.

CaptFaraday · 3 months ago

The problem isn't just about debugging — it's about knowing if the agent is alive.

I regularly have Claude Code sit in thinking phases for 30-45+ minutes with no output. Here's what an actual session looks like:

● Now I have everything I need. Let me write the implementation plan.
  ⎿  Request timed out

✻ Churned for 1h 43m 44s

❯ What happened
  ⎿  Request timed out

✻ Brewed for 56m 16s

Nearly 3 hours. Multiple timeouts. Zero output. And at no point during any of that could I tell whether the model was making progress, stuck in a loop, or effectively dead.

The only information I get during a long thinking phase is:

✶ Writing implementation plan… (45m 32s · ↓ 47 tokens · thinking with high effort)

47 tokens in 45 minutes. Is that progress? Is it frozen? There's no way to tell. My options are:

  • Keep waiting — but I've now watched it churn for hours and produce nothing but timeouts
  • Hit Escape — lose the entire thinking session and start over
  • Stare at a spinner and hope

showThinkingSummaries helps after the fact, but the core issue is real-time visibility during long thinking phases. Even streaming a few lines of reasoning would tell me whether to wait or bail. Instead I'm gambling hours of my time on a black box.

This isn't a nice-to-have. When the model is thinking for 5 seconds, sure, hide it. But when it's been 30+ minutes with no sign of life, users need something — even a heartbeat.

CaptFaraday · 3 months ago

Update: Got inline thinking working on Windows (standalone binary install)

Thanks to the patch from @aleks-apostle (claude-code-patches), I got real-time inline thinking visibility working — even on the Windows standalone binary install where cli.js isn't directly accessible.

The trick is that the JS is embedded as plain text in the binary, so you can patch it directly with a same-length byte replacement. Here's what I did on v2.1.96:

Step 1: Add to ~/.claude/settings.json:

{
  "showThinkingSummaries": true
}

Without this, the API strips thinking content server-side and no client patch can help.

Step 2: Find the pattern in the binary:

grep -oa 'case"thinking":{if(!D&&!A)return null' ~/.local/bin/claude.exe

(Your variable names may differ — the structure is case"thinking":{if(!VAR1&&!VAR2)return null)

Step 3: Patch the binary with a same-length replacement (critical — byte length must match or the binary corrupts):

  • if(!D&&!A)if(!1&&!A) — guard becomes always-false, thinking never hidden
  • isTranscriptMode:DisTranscriptMode:1 — always render content
  • hideInTranscript:khideInTranscript:0 — never hide thinking blocks

I used a Python script to do the binary find-and-replace. Back up your binary first:

cp ~/.local/bin/claude.exe ~/.local/bin/claude.exe.backup

Result: thinking blocks now stream inline in real-time during generation. Exactly what this thread has been asking for.

Credit to @aleks-apostle and @anthrotype (PR #9) for figuring out the two-layer gating and keeping the patch updated across versions.

youlikemodernart · 3 months ago

Confirming showThinkingSummaries: true in ~/.claude/settings.json works on macOS with Claude Code Max subscription. Thinking output is now visible after restarting the session. Thanks for the detective work.

anthrotype · 3 months ago

This just got worse with Opus 4.7 released yesterday.

Opus 4.7 changed the API default for thinking blocks from display: "summarized" to display: "omitted". The thinking blocks are still present in the response stream, but their thinking field is empty unless the caller explicitly passes display: "summarized".

This means that even if you've been patching cli.js to restore inline thinking display (as some of us have), there is now nothing to display — the API no longer returns the thinking content at all unless opted in.

See their migration guide: https://platform.claude.com/docs/en/about-claude/models/migration-guide#migrating-to-claude-opus-4-7

With the help of claude code, I found the following two workarounds, both undocumented:

  1. Via ~/.claude/settings.json:
{
  "env": {
    "CLAUDE_CODE_EXTRA_BODY": "{\"thinking\":{\"type\":\"adaptive\",\"display\":\"summarized\"}}"
  }
}
  1. Via hidden CLI flag: --thinking-display summarized

The CLAUDE_CODE_EXTRA_BODY env var approach also works for the VS Code extension (since it delegates to the CLI), so it covers both surfaces.

But the real fix remains what this issue has been asking for since September: a first-class thinkingDisplay setting in settings.json, alongside the existing thinkingEnabled and effortLevel. The gap between what the API supports and what Claude Code exposes as configuration keeps widening.

AndASM · 3 months ago

Claude hides it's thinking anyway, it was only summaries which still provided some help. But you the user is not trusted to know what is actually going on, it must be redacted and hidden from you.

bisbaldk · 2 months ago
2\. --thinking-display summarized

tested on wsl2 both workarounds didn't work in my case with opus 4.7. That said I appreciate these workaround anyways.
It just shows 'This one needs a moment' in my case.

I wonder, based on what rationale did Anthropic figured: ok, let's hide the thinking process silently that literally nobody ever wants

MarioVilas · 2 months ago

Patching the binary will fail on Mac, due to code signatures. I suppose it's possible to re-sign the binary using a developer certificate but this is getting complicated quickly.

anthrotype · 2 months ago
Patching the binary will fail on Mac, due to code signatures

ad-hoc sign with codesign --force --sign - {path}

MarioVilas · 2 months ago

BTW I tried that and got a Bun error. Not sure if a flaw in my patching script or an additional integrity check by Bun itself. I'll leave my script here in case it's useful to someone - but I'm changing approach and will try to verify the undocumented arguments approach.

#!/usr/bin/env python3
"""
Patch the Claude CLI binary to flip isTranscriptMode, verbose, and
hideInTranscript flags.

Usage:
    python patch_claude.py            # auto-locate claude on PATH
    python patch_claude.py /path/to/claude
"""

from __future__ import annotations

import os
import re
import shutil
import subprocess
import sys
from pathlib import Path


PATTERN = re.compile(
    rb',isTranscriptMode:[A-Za-z],verbose:[A-Za-z],hideInTranscript:[A-Za-z]\}'
)
REPLACEMENT = b',isTranscriptMode:1,verbose:1,hideInTranscript:0}'


def locate_claude(arg: str | None) -> Path:
    """Return the real path to the claude binary/script to patch."""
    if arg:
        candidate = Path(arg)
    else:
        name = 'claude.exe' if os.name == 'nt' else 'claude'
        found = shutil.which(name)
        if not found:
            sys.exit(f"Could not find {name} on PATH. Pass the path explicitly.")
        candidate = Path(found)

    if not candidate.exists():
        sys.exit(f"Path does not exist: {candidate}")

    resolved = candidate.resolve()

    if resolved.is_file() and resolved.stat().st_size < 8192:
        real = sniff_shim_target(resolved)
        if real is not None:
            print(f"Shim detected at {resolved}")
            print(f"Following to: {real}")
            resolved = real

    if not resolved.is_file():
        sys.exit(f"Resolved path is not a regular file: {resolved}")

    return resolved


def sniff_shim_target(shim: Path) -> Path | None:
    """Very small heuristic to extract the real script path from a shim."""
    try:
        text = shim.read_text(errors='replace')
    except OSError:
        return None

    for match in re.finditer(r'([A-Za-z]:)?[\\/][^\s"\']+\.m?js', text):
        candidate = Path(match.group(0))
        if candidate.is_file():
            return candidate.resolve()
    return None


def resign_macos(path: Path) -> None:
    """Ad-hoc re-sign a macOS binary after patching.

    macOS enforces code signatures at launch. Modifying the binary
    invalidates its signature and the kernel will kill the process
    with SIGKILL ("zsh: killed"). Ad-hoc signing with `-` replaces
    the signature with a local one the kernel accepts.
    """
    if sys.platform != 'darwin':
        return

    if shutil.which('codesign') is None:
        print("Warning: codesign not found; skipping re-sign step.")
        print("The binary may fail to launch with 'killed'.")
        return

    print("Re-signing binary (ad-hoc)...")
    result = subprocess.run(
        ['codesign', '--force', '--sign', '-', str(path)],
        capture_output=True,
        text=True,
    )
    if result.returncode != 0:
        print(f"Warning: codesign failed with exit code {result.returncode}")
        if result.stderr:
            print(f"  stderr: {result.stderr.strip()}")
        return

    # Clear the quarantine xattr if present — harmless if it isn't.
    subprocess.run(
        ['xattr', '-d', 'com.apple.quarantine', str(path)],
        capture_output=True,
    )

    # Verify the new signature is valid.
    verify = subprocess.run(
        ['codesign', '-v', str(path)],
        capture_output=True,
        text=True,
    )
    if verify.returncode == 0:
        print("Signature verified.")
    else:
        print(f"Warning: signature verification failed: {verify.stderr.strip()}")


def patch(path: Path) -> None:
    data = path.read_bytes()
    matches = list(PATTERN.finditer(data))

    if not matches:
        sys.exit("Pattern not found. The file may already be patched or the "
                 "bundle layout changed.")

    for m in matches:
        if len(m.group(0)) != len(REPLACEMENT):
            sys.exit(
                f"Length mismatch: match is {len(m.group(0))} bytes, "
                f"replacement is {len(REPLACEMENT)} bytes. Aborting."
            )

    backup = path.with_suffix(path.suffix + '.bak')
    if not backup.exists():
        shutil.copy2(path, backup)
        print(f"Backup written to {backup}")
    else:
        print(f"Backup already exists at {backup} (not overwriting)")

    new_data = PATTERN.sub(REPLACEMENT, data)
    assert len(new_data) == len(data), "size changed — refusing to write"

    original_mode = path.stat().st_mode

    tmp = path.with_suffix(path.suffix + '.tmp')
    tmp.write_bytes(new_data)
    os.replace(tmp, path)

    os.chmod(path, original_mode)

    print(f"Patched {len(matches)} occurrence(s) in {path}")

    resign_macos(path)


def main() -> None:
    arg = sys.argv[1] if len(sys.argv) > 1 else None
    target = locate_claude(arg)
    print(f"Target: {target}")
    patch(target)


if __name__ == '__main__':
    main()
anthrotype · 2 months ago

patching invalidates the embedded CMS signature; all page hashes in the CodeDirectory need to be recalculated so ad-hoc re-sign succeeds (Claude can figure out the rest)

MarioVilas · 2 months ago

Ok I asked Claude to reverse engineer itself and verify this very thread:

Here are the results. I verified each undocumented option mentioned in the issue comments against your binary (v2.1.87):

  Confirmed PRESENT in the binary

  ┌────────────────────────────────────────┬────────────────┬─────────────────────┬─────────────────────────────────────────────────────────────────────────┐   
  │                 Option                 │      Type      │      Location       │                                 Purpose                                 │
  ├────────────────────────────────────────┼────────────────┼─────────────────────┼─────────────────────────────────────────────────────────────────────────┤   
  │ showThinkingSummaries                  │ settings.json  │ line 212298         │ Prevents server-side thinking redaction. Listed alongside other         │ 
  │                                        │ key            │                     │ settings keys (plansDirectory, sshHost, etc.)                           │
  ├────────────────────────────────────────┼────────────────┼─────────────────────┼─────────────────────────────────────────────────────────────────────────┤   
  │ CLAUDE_CODE_EXTRA_BODY                 │ env var        │ lines 196424-196427 │ Injects extra fields into API request body. Has validation code: "must  │
  │                                        │                │                     │ be a JSON object", "Error parsing"                                      │   
  ├────────────────────────────────────────┼────────────────┼─────────────────────┼─────────────────────────────────────────────────────────────────────────┤ 
  │                                        │                │ lines 158325,       │ Strips beta headers including redact-thinking. Message at 196337:       │   
  │ CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS │ env var        │ 196337, 290896,     │ "Stripped from tool schemas: [...]                                      │   
  │                                        │                │ 290971              │ (CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1)"                             │
  ├────────────────────────────────────────┼────────────────┼─────────────────────┼─────────────────────────────────────────────────────────────────────────┤   
  │ alwaysThinkingEnabled                  │ settings.json  │ line 161774         │ Controls whether thinking mode is enabled by default (not whether it's  │ 
  │                                        │ key            │                     │ displayed inline)                                                       │
  ├────────────────────────────────────────┼────────────────┼─────────────────────┼─────────────────────────────────────────────────────────────────────────┤
  │ redact-thinking-2026-02-12             │ beta header    │ line 148526         │ The beta flag that strips thinking content server-side                  │
  ├────────────────────────────────────────┼────────────────┼─────────────────────┼─────────────────────────────────────────────────────────────────────────┤   
  │ isTranscriptMode / hideInTranscript    │ JS property    │ lines 175395-175396 │ The binary patching targets for forcing inline display                  │
  │                                        │ names          │                     │                                                                         │   
  └────────────────────────────────────────┴────────────────┴─────────────────────┴─────────────────────────────────────────────────────────────────────────┘ 
                                                                                                                                                                
  Confirmed NOT PRESENT                                                                                                                                       

  ┌───────────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  │            Option             │                                                         Status                                                          │
  ├───────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ --thinking-display            │ Does not exist - not found in strings or hex dump. Zero matches for thinking-display, thinkingDisplay, or               │
  │                               │ thinking_display.                                                                                                       │
  ├───────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤   
  │ thinkingDisplay (settings     │ Does not exist                                                                                                          │
  │ key)                          │                                                                                                                         │   
  └───────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 

  Analysis                                                                                                                                                      
   
  The conflicting reports in the issue make sense now:                                                                                                          
                                                                                                                                                              
  1. showThinkingSummaries: true works for people because it opts out of redact-thinking-2026-02-12, so the API actually returns thinking content. But it only  
  shows thinking in verbose/transcript mode, not inline.
  2. --thinking-display summarized (suggested by @anthrotype for Opus 4.7) does not exist in this binary. This explains why @bisbaldk reported it didn't work.  
  It was likely a hallucination by Claude when asked for a workaround, or it existed in a different version.                                                    
  3. CLAUDE_CODE_EXTRA_BODY with {"thinking":{"type":"adaptive","display":"summarized"}} is the most reliable workaround for the API-level redaction, since it
  directly injects the parameter into the API request body.                                                                                                     
  4. CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1 is the nuclear option - disables all beta features, which removes redact-thinking but also everything else.     
  5. For inline real-time display (what most commenters actually want), the only option remains binary patching isTranscriptMode/hideInTranscript - those       
  property names are present in the code.         
anthrotype · 2 months ago

No, the other Claude got it wrong. The --thinking-display CLI flag is actually present in v2.1.114 (current), it is just hidden in the --help. Try it yourself!

$ claude --thinking-display bogus -p x 2>&1 | head -1
error: option '--thinking-display <display>' argument 'bogus' is invalid. Allowed choices are summarized, omitted.
MarioVilas · 2 months ago

Ah, I have an older version (2.1.87). I should update then.

Here's what happened when I asked for a full RE of the bundled code though (again, 2.1.87):

  Complete data flow for isTranscriptMode and hideInTranscript
                                                                                                                                                                
  The thinking block renderer: BB_                                                                                                                            
                                                                                                                                                                
  function BB_(H) {                                                                                                                                             
    let {param:q, addMargin:$, isTranscriptMode:K, verbose:O, hideInTranscript:T} = H;
    let {thinking:z} = q;                                                                                                                                       
    let f = T === void 0 ? false : T;                                                                                                                         
                                                                                                                                                                
    if (!z) return null;           // no thinking content → skip                                                                                                
    if (f) return null;            // hideInTranscript → skip                                                                                                   
    if (!(K || O)) {               // neither transcript nor verbose?                                                                                           
      return <dim italic>"∴ Thinking" <spinner/></dim>;  // just show placeholder                                                                               
    }                                                                                                                                                           
    // ELSE: render actual thinking text inline                                                                                                                 
  }                                                                                                                                                             
                                                                                                                                                              
  The guard is if (!(K || O)) — thinking text is only rendered when isTranscriptMode OR verbose is true. Otherwise you get the "∴ Thinking" placeholder with a  
  spinner.                                                                                                                                                    
                                                                                                                                                                
  Where BB_ is called (in RB1, the content block switch):                                                                                                       
   
  case "thinking": {                                                                                                                                            
    if (!j && !T) return null;  // j=isTranscriptMode, T=verbose — hidden entirely if both false                                                                
    let Z = j && !(!X || P === X);  // hideInTranscript = isTranscriptMode AND (not the latest thinking block)                                                  
    return <BB_ addMargin:$ param:q isTranscriptMode:j verbose:T hideInTranscript:Z />;                                                                         
  }                                                                                                                                                             
                                                                                                                                                                
  So hideInTranscript is computed as: in transcript mode AND this is not the most recent thinking block (controlled by lastThinkingBlockId). This hides older   
  thinking blocks in transcript view to avoid clutter.                                                                                                        
                                                                                                                                                                
  Where isTranscriptMode comes from:                                                                                                                          

  In o4K (the message row component):                                                                                                                           
  let {screen: w, ...} = H;
  let R = w === "transcript";   // isTranscriptMode = screen === "transcript"                                                                                   
                                                                                                                                                                
  Where screen comes from:                                                                                                                                      
                                                                                                                                                                
  SH (screen) is a useState in the main REPL component (line 139645). It defaults to the main chat view ("prompt") and is set to "transcript" when the user     
  presses Ctrl+O. When in transcript mode, verbose:!0 (true) is hardcoded.                                                                                    
                                                                                                                                                                
  Where verbose comes from:                                                                                                                                   

  p = Y_((T_) => T_.verbose) — read from the zustand store (line 139634).                                                                                       
   
  The store's verbose field:                                                                                                                                    
  1. Defaults to false in k8H() (line 134526): verbose:!1                                                                                                     
  2. Set from CLI --verbose flag (line 145853): "Override verbose mode setting from config"                                                                     
  3. Synced from bridge/settings (line 139458): if(H.verbose!==_.verbose && z_().verbose!==H.verbose) { let K=H.verbose; u_((O)=>({...O, verbose:K})) } — this
  is how the /config toggle and settings.json "verbose": true propagate at runtime.                                                                             
                                                                                                                                                                
  The two gates preventing inline thinking:                                                                                                                     
                                                                                                                                                                
  Gate 1 (RB1):  if (!isTranscriptMode && !verbose) return null;                                                                                              
                  ↓ thinking blocks aren't even passed to BB_                                                                                                   
                                                                                                                                                                
  Gate 2 (BB_):   if (!(isTranscriptMode || verbose)) return <placeholder>;                                                                                     
                  ↓ only the "∴ Thinking" spinner shown, no text                                                                                                
                                                                                                                                                                
  Both require either verbose === true (from store/settings/CLI) or screen === "transcript" (Ctrl+O mode).                                                      
                                                                                                                                                                
  Summary                                                                                                                                                       
                                                                                                                                                              
  ┌──────────────────┬──────────────────────────────────────────────────────────────────────┬───────────────────────────────────────────────────────────────┐   
  │     Variable     │                                Source                                │                        How to control                         │ 
  ├──────────────────┼──────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
  │ verbose          │ Zustand store → settings.json verbose: true, CLI --verbose, or       │ User-controllable                                             │
  │                  │ /config toggle                                                       │                                                               │
  ├──────────────────┼──────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤   
  │ isTranscriptMode │ screen === "transcript" — a React useState toggled by Ctrl+O         │ Only via Ctrl+O at runtime                                    │   
  ├──────────────────┼──────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤   
  │ hideInTranscript │ Computed: isTranscriptMode && (blockId !== lastThinkingBlockId)      │ Not user-controllable — hides non-latest thinking blocks in   │   
  │                  │                                                                      │ transcript                                                    │ 
  └──────────────────┴──────────────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────────────────┘   
                                                                                                                                                              
  Bottom line: There is no dedicated "show thinking inline" toggle. The code has a hard gate: thinking text only renders when verbose || isTranscriptMode. The  
  showThinkingSummaries setting controls the API-side redaction (whether the server strips thinking content), but even with content present, the client-side
  rendering still requires passing through these two gates. This is why binary patchers change isTranscriptMode to 1 (always true) — it's the only way to bypass
   the client-side guard without enabling full verbose mode.          
MarioVilas · 2 months ago
Verification complete for v2.1.114

  New in this version

  --thinking-display <display> is now REAL. Values: summarized | omitted. Wired at line 147581:                                                                 
   
  if (kO.type !== "disabled" &&                                                                                                                                 
      (Y.thinkingDisplay === "summarized" || Y.thinkingDisplay === "omitted"))                                                                                  
    kO.display = Y.thinkingDisplay;                                                                                                                             
                                                                                                                                                                
  This sets display on the thinking config sent to the API — the direct opt-in for Opus 4.7's new display parameter (which defaults to "omitted" on 4.7, vs     
  "summarized" on 4.6). The telemetry field thinkingDisplay also exists (line 216244).                                                                        
                                                                                                                                                                
  Significant changes summary                                                                                                                                 

  ┌───────────────────────────────┬───────────────────────────┬────────────────────────────────────────────────────────────────────────────────────────────┐ 
  │            Aspect             │          v2.1.87          │                                          v2.1.114                                          │ 
  ├───────────────────────────────┼───────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤    
  │ --thinking-display CLI flag   │ ❌ Did not exist          │ ✅ Real, writes to API request thinking.display                                            │ 
  ├───────────────────────────────┼───────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤    
  │ showThinkingSummaries setting │ ✅ Exists, gates          │ ✅ Unchanged (same logic at line 134219: k8().showThinkingSummaries !== !0 → push v0_, the │    
  │                               │ redact-thinking beta      │  redact-thinking beta)                                                                     │    
  ├───────────────────────────────┼───────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤    
  │ redact-thinking-2026-02-12    │ ✅ Present                │ ✅ Still present (line 150777)                                                             │    
  ├───────────────────────────────┼───────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤  
  │                               │                           │ ✅ New UI-only state — triggers TE7 which renders the rotating messages "Hmm…", "This one  │    
  │ showThinkingHint              │ ❌                        │ needs a moment…", "Working through it…". This is NOT inline thinking — it's just a         │    
  │                               │                           │ liveness hint to address the "is it alive?" complaint. This explains @bisbaldk's "It just  │    
  │                               │                           │ shows 'This one needs a moment'" report.                                                   │    
  ├───────────────────────────────┼───────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤  
  │ Client-side render gate (BB_  │ if (!(isTranscriptMode || │ Unchanged. Same guard, same "∴ Thinking" placeholder.                                      │
  │ → renamed Yl_)                │  verbose)) → placeholder  │                                                                                            │
  └───────────────────────────────┴───────────────────────────┴────────────────────────────────────────────────────────────────────────────────────────────┘    
   
  What this means for your workflow                                                                                                                             
                                                                                                                                                              
  The new --thinking-display summarized only solves half the problem: it opts into the API returning thinking text on Opus 4.7. It does not unlock inline       
  real-time display.                                                                                                                                          
                                                                                                                                                                
  To actually see thinking inline on v2.1.114 + Opus 4.7, you still need all three:                                                                             
   
  1. --thinking-display summarized (or settings-equivalent, or CLAUDE_CODE_EXTRA_BODY) — API returns content                                                    
  2. showThinkingSummaries: true in ~/.claude/settings.json — prevents the client from sending redact-thinking beta that strips it again                      
  3. --verbose or /config → Verbose Output → True — unlocks the client-side rendering gate (unchanged from v2.1.87)                                             
                                                                                                                                                                
  The binary patch targeting isTranscriptMode/hideInTranscript is still the only way to get inline thinking without the noise of full verbose mode.
MarioVilas · 2 months ago

In v2.0.62 the streaming plumbing was different: partial assistant messages were re-rendered as content_block_delta arrived, so the message-switch (the same
case "thinking": we patched) got invoked repeatedly with growing content. Forcing isTranscriptMode:!0 there made each re-render visible → looked like
streaming.

In v2.1.114 streaming was decoupled. UxH now owns the delta pipeline, and its thinking_delta case only calls q(...) (the token buffer) — it never pushes into
the React state that drives the UI. The case "thinking": branch only fires when the full assistant message lands. That's why no amount of patching the
historical-render path will restore token-by-token streaming.

Rubenisme · 2 months ago

I suppose this is done to make it harder to distill from for other models, but it's valuable to be able to add this for developers. I used it a lot to see where it went wrong or what it infers from what I said. Sometimes that can be really useful to see that I should have written a prompt clearer or that it can be interpreted in multiple ways, now I find out in the end.

Update, I see there's also a bug issue, and not sure how much they overlap now, that one seems focused on just 4.7.

anthrotype · 2 months ago

It doesn't actually make distillation harder. Anyone motivated enough has the skills to work around the suppression, the same way the rest of us do by patching the binary (one can literally just ask Claude how). What it does do is lock legitimate, paying developers (the ones uncomfortable patching their CLI) out of seeing the model's summarized thoughts, learning how it reasons, and steering it while it's working.

If 90% of users don't care to read the thinking, fine. Nobody asked for visibility by default. Just for a toggle.

anthrotype · 2 months ago

Since 2.1.117, the "showThinkingSummaries": true option in settings.json not only prevents the redact-thinking header to be sent, but also sets the thinking display to "summarized" for models like Opus 4.7 (which defaults to "omitted").

So to reenable thinking summaries in Opus 4.7 (at least in transcript mode) one no longer needs that (hidden, undocumented) --thinking-display summary CLI flag I mentioned above, nor the CLAUDE_CODE_EXTRA_BODY hack (the latter env var gets sent unconditionally to all requests to Claude API and breaks Haiku 4.5 because it doesn't support adaptive thinking and returns error 400).

So it seems Anthropic are listening after all, at least in part.

One still hopes they gave us the option to bring back the interleaved, inline thinking messages in the regular (non-verbose) TUI view (instead of only in transcript mode)...

Shadetail · 2 months ago

Adding my voice. Visible extended thinking was an Anthropic differentiator when 3.7 launched - the visible extended thinking post explicitly contrasted it with ChatGPT's summary-only approach. The progression since (Claude 4 → summarized by default, Opus 4.7 → omitted by default, Claude Code → redact-thinking-2026-02-12 beta header) reverses that position.

Beyond the principle, concrete reasons to want real-time thinking visibility: catching wrong directions early so I can interrupt before a 10-minute agent run burns tokens on the wrong approach, understanding why the model made a questionable choice in a diff, and auditability for code that lands in a shipping product. Many times reading Claude's thinking would help me think about the problem alongside it. Please make a first-class, persistent "always show thinking when available" setting that actually works across models.

BadPackage · 2 months ago

I don't like seeing the same steps ClosedAI took >:(

anthrotype · 2 months ago

I need to qualify my previous claim:

Since 2.1.117, the "showThinkingSummaries": true option in settings.json ... also sets the thinking display to "summarized" for models like Opus 4.7 (which defaults to "omitted").

This is true, but only if claude is runninng in _interactive_ mode. When running non-interactively e.g. via claude agent SDK or in the Visual Studio Code extension or with -p flag, showThinkingSummary will not also turn the thinking display to "summarized". It justs stops the redact-thinking-2026-02-12 beta header, but the thinking stays omitted for Opus 4.7.

Here's the relevant piece of code from v2.1.118 (note the !S8() gate)

function S8(){ return !x_.isInteractive }
...
if (H5.type !== "disabled") {
  if (w.thinkingDisplay === "summarized" || w.thinkingDisplay === "omitted")
    H5.display = w.thinkingDisplay;
  else if (!S8() && u8().showThinkingSummaries === true)
    H5.display = "summarized";
}

Not sure why they decided to do that.

- else if(!S8()&&u8().showThinkingSummaries===!0)H5.display="summarized"
+ else if(true &&u8().showThinkingSummaries===!0)H5.display="summarized"
AndASM · 2 months ago

They appear to be trying to prevent others from using their model as a source for training. Since models are generally converging and plateauing, they appear to be using increasingly underhanded tactics to try and prevent their system from being understood.

Unfortunately this is the opposite of making a helpful coding tool. The whole "but they are distilling our models!" thing, as well as being insane from the perspective of how they gathered copyrighted data for their own pre-training, creates an incentive incompatible with their marketing and product differentiation. It seems they are choosing to destroy their own product defensively as a result.

jmac122 · 2 months ago
They appear to be trying to prevent others from using their model as a source for training. Since models are generally converging and plateauing, they appear to be using increasingly underhanded tactics to try and prevent their system from being understood. Unfortunately this is the opposite of making a helpful coding tool. The whole "but they are distilling our models!" thing, as well as being insane from the perspective of how they gathered copyrighted data for their own pre-training, creates an incentive incompatible with their marketing and product differentiation. It seems they are choosing to destroy their own product defensively as a result.

Very hard to believe this is why. I have often caught the model completely deviating down a rabbit hole in their thoughts and have had to catch them or add clarification before they got too deep into the thought process and burned thousands of tokens, just to find out they read the prompt wrong, or didn't read the full prompt, or truncated a code file and didn't read the part of it that prevented them rabbit holing.

Regardless of why, it sucks. Cursor's API has access to all of the thoughts, and if someone really wanted to distill the model, they wouldn't do it through a Claude Max subscription, they would do it through a middleman like OpenRouter or Cursor so that it is massaged into the rest of the API usage and doesn't create mountains and valleys with usage.

AndASM · 2 months ago

I mean, the timeline aligns with their public statements and actions about things like other vendors distilling against their API. It also aligns with profit motive. The thing it doesn't align with is good product design, user interests, or Anthropic's mission statement.

hifihedgehog · 2 months ago

Same bug on Windows / VS Code extension 2.1.x with Opus 4.7. Not macOS-specific.

Root cause is now public (credit to the workaround in #8477 (comment)): Opus 4.7 changed the API default for thinking blocks from display: "summarized" to display: "omitted". The blocks are still in the response stream, but the thinking field is empty unless the caller passes display: "summarized" explicitly. Documented in Anthropic's own migration guide: https://platform.claude.com/docs/en/about-claude/models/migration-guide#migrating-to-claude-opus-4-7

Net effect: showThinkingSummaries, alwaysThinkingEnabled, and viewMode: "verbose" have nothing to render, because Claude Code does not pass display: "summarized" when calling the Opus 4.7 API. showThinkingSummaries is the documented, official way to surface thinking in the VS Code extension, and it is silently broken on Opus 4.7. The settings surface never caught up to the API change.

Two undocumented workarounds (from the same comment):

  1. Add to ~/.claude/settings.json:

``json
{
"env": {
"CLAUDE_CODE_EXTRA_BODY": "{\"thinking\":{\"type\":\"adaptive\",\"display\":\"summarized\"}}"
}
}
``

  1. Hidden CLI flag: --thinking-display summarized

The env var approach works for the VS Code extension since it delegates to the CLI.

Why this is high-priority, not a cosmetic regression:

Visible real-time thinking was load-bearing for the steering loop. With it visible, you could stop the model going down a wrong branch before it spent N turns of token budget on a wrong premise; you could verify it had internalized constraints from CLAUDE.md / memory rather than hallucinated them; and you could audit assumptions before committing to a plan.

With it hidden behind an unexpandable pill, you cannot tell whether a wrong final answer came from a wrong premise or a wrong execution of a right premise. Silent assumption drift becomes invisible until after the wrong action is taken. "Thought for 376s" is a duration, not a signal. It tells you nothing actionable. This is a regression in the developer's ability to supervise the agent.

On the user side: this is a paid product. Pro at $20/month, Max at $100–$200/month. Paying customers should not have to monkey-patch cli.js, reverse-engineer an undocumented CLAUDE_CODE_EXTRA_BODY env var, or scrape issue threads to recover functionality that worked in the previous release of the same product. The workaround being undocumented is itself a bug. The gap between what the API supports and what Claude Code exposes as a first-class setting keeps widening with every model bump.

Related open issues, same root problem:

  • #49268 — harness doesn't set display: "summarized" for Opus 4.7 (same root cause, named explicitly)
  • #49757 — empty/unexpandable stub in VS Code extension (Opus 4.7)
  • #49902 — thinking summaries not rendered (VS Code extension 2.1.112)
  • #49322 — thinking summaries not rendered in VS Code extension (Opus 4.7)
  • #48065 — thinking summaries not displayed when showThinkingSummaries enabled
  • #49739 — toggle button inverted, no visual feedback (v2.1.112), likely a related UI-state bug
  • #30958 — earlier transcript/TUI variant of the same regression (v2.1.69)
  • #51131 — extended thinking dropdown no longer expandable in VS Code extension (Opus 4.7)
  • #33163 — "Bring back thinking" feature request, same underlying ask

What would actually resolve this:

  1. Pass display: "summarized" by default for Opus 4.7+ when showThinkingSummaries and alwaysThinkingEnabled are on. Or expose a first-class thinkingDisplay setting alongside the existing thinkingEnabled and effortLevel.
  2. Restore the expandable thinking block in the VS Code extension chat panel so showThinkingSummaries actually shows summaries when the data is present.
  3. Update the docs so they reflect current showThinkingSummaries semantics on Opus 4.7+ instead of describing behavior that no longer applies.
betovildoza · 2 months ago

Confirming the Haiku-breaking caveat is broader: CLAUDE_CODE_EXTRA_BODY also breaks subagent spawning on Opus 4.7.

Tested the env-var workaround from this thread on Windows / VS Code extension with Opus 4.7 set globally in ~/.claude/settings.json:

{
  "env": {
    "CLAUDE_CODE_EXTRA_BODY": "{\"thinking\":{\"type\":\"adaptive\",\"display\":\"summarized\"}}"
  }
}

When the orchestrator tried to spawn a subagent via the Agent tool, the API rejected the call:

400 invalid_request_error
"adaptive thinking is not supported on this model"

So the previously known caveat ("breaks Haiku 4.5") is actually wider than documented in the comments above:

  • The env var is inherited by every subprocess/subagent call made by the CLI, regardless of which model the subagent is configured to use.
  • Any subagent whose model doesn't support thinking.type=adaptive (Haiku 4.5, and apparently other configurations as well) fails to spawn outright with a 400, not a soft degradation.
  • There is no per-turn / per-subagent scoping mechanism for CLAUDE_CODE_EXTRA_BODY it's process-wide for the whole session.

Net effect for anyone using a multi-agent orchestrator pattern: this workaround is a non-starter, because turning on visible thinking breaks the ability to delegate work. You have to choose between seeing thinking or having functional subagents.

---

A no-config UI workaround that does work (VS Code extension, Opus 4.7):

Without touching settings.json or any env var, you can force thinking blocks to render by toggling the in-chat thinking control mid-turn:

  1. Send your message with the thinking toggle on the right set to off. The response header shows the usual "thinking 3s" pill but no expandable block.
  2. Wait for the model to finish responding.
  3. When it's your turn to reply, switch the toggle on the left to on before sending the next message.
  4. From that next turn onward, the thinking block renders inline and is expandable as it used to be on previous Opus releases.

This is fully reversible (just flip the toggle back), it doesn't clobber the request body, and critically it doesn't break subagent spawning, Haiku, /plugins, or anything else that the env var / claudeProcessWrapper workarounds break. It's a pure UI-state toggle in the extension. Not a fix, but the only workaround I've found so far that doesn't carry collateral damage.

Caveat on the UI workaround: it relies on a toggle whose UX is itself currently broken, I filed #49739 separately about the thinking toggle in VS Code extension v2.1.112 sliding to the wrong side, having no visual feedback (no color/icon/state indication), and not persisting the expanded/collapsed state of the thinking block between sessions. So while the toggle-mid-turn trick works, getting the toggle into the right state is harder than it should be. Fixing the toggle's visual feedback would make this workaround usable by anyone, not just users who happen to discover the inverted-toggle dance.

This reinforces the asks at the bottom of the previous comment: the fix has to live inside Claude Code (pass display: "summarized" selectively when the target model supports it), not be punted to users via a global env var that clobbers the request body for every model in the session.

jmac122 · 2 months ago

Got inline thinking working on Windows (standalone binary, v2.1.123, Opus 4.7, Max subscription)

Two layers to fix — API-side and UI-side.

---

Layer 1: API-side (get thinking content from the server)

Add to ~/.claude/settings.json:

{
  "showThinkingSummaries": true
}

On v2.1.117+, this setting handles everything — it removes the redact-thinking header AND sets display: "summarized" on the API request.

Important: Do NOT use the --thinking-display summarized CLI flag on v2.1.117+. It actually interferes with the setting-based behavior and results in empty thinking blocks. I spent hours debugging this; the flag overrides the setting's code path in a way that breaks things. The setting alone works.

Layer 2: UI-side (render thinking inline instead of behind Ctrl+O)

The JS is embedded as plain text in the Windows binary at ~/.local/bin/claude.exe, so you can patch it directly with same-length byte replacements.

Patch 1: Bypass the thinking guard:

Find:

case"thinking":{if(!VAR1&&!VAR2)return null

Replace if(!VAR1 with if(!1 - guard becomes always-false, thinking never hidden.

Patch 2: Force createElement props to render expanded:

Find the createElement call that passes isTranscriptMode and verbose props to the thinking component (search for isTranscriptMode: near createElement and hideInTranscript):

isTranscriptMode:VAR,verbose:VAR,hideInTranscript:

Replace both variable names with 1:

isTranscriptMode:1,verbose:1,hideInTranscript:

The variable names change every version (minification), so you need to search the binary to find the current names. Here's what I've seen:

| Version | Patch 1 guard | Patch 2 props |
|---------|--------------|---------------|
| v2.1.101 | if(!j&&!A) | isTranscriptMode:j,verbose:A |
| v2.1.104 | if(!j&&!A) | isTranscriptMode:j,verbose:A |
| v2.1.116 | if(!j&&!A) | isTranscriptMode:j,verbose:A |
| v2.1.123 | if(!j&&!f) | isTranscriptMode:j,verbose:f |

How to find patterns in the binary

$text = [System.Text.Encoding]::ASCII.GetString([System.IO.File]::ReadAllBytes("$HOME\.local\bin\claude.exe"))

# Patch 1: find the guard variables
[regex]::Match($text, 'case"thinking":\{if\!(\w+)&&!(\w+)\)return null')

# Patch 2: find the createElement props
# Search for 'isTranscriptMode:' near 'createElement' and 'hideInTranscript'

Critical rules

  1. Byte length must be preserved — replacements must be exact same length or the binary corrupts (Bun TypeError)
  2. Only patch the createElement call, NOT the variable declarations — isTranscriptMode:_ appears ~10+ times in destructuring patterns. Replacing _ with 1 in declarations breaks unrelated code.
  3. Back up before patching: copy claude.exe claude.exe.backup
  4. Must NOT use tui: fullscreen — the fullscreen TUI renderer has a separate code path that the patches don't reach
  5. Must re-patch after every update — set DISABLE_AUTOUPDATER: 1 in settings.json env to prevent surprise overwrites

Python patch script example (v2.1.123)

import shutil

binary = os.path.expanduser('~/.local/bin/claude.exe')
backup = binary + '.backup'
shutil.copy2(backup, binary)  # always start from clean backup

with open(binary, 'rb') as f:
    data = f.read()

orig_len = len(data)

# Patch 1: thinking guard
data = data.replace(b'if(!j&&!f)return null', b'if(!1&&!f)return null')

# Patch 2: createElement props
data = data.replace(
    b'isTranscriptMode:j,verbose:f,hideInTranscript:',
    b'isTranscriptMode:1,verbose:1,hideInTranscript:'
)

assert len(data) == orig_len, 'byte length changed!'

with open(binary, 'wb') as f:
    f.write(data)

Result: thinking blocks stream inline in real-time during generation. Tested on Windows, PowerShell 7, Opus 4.7, Max subscription.

Credit to @aleks-apostle and @anthrotype for the original research that made this possible. @phpmypython's macOS gist helped identify the createElement props approach for v2.1.113+.

a-connoisseur · 2 months ago

btw, I maintain a patched version ↓ that streams thinking live into the UI and auto-updates via Github Actions on each release
https://github.com/a-connoisseur/patch-claude-code

jmac122 · 2 months ago
btw, I maintain a patched version ↓ that streams thinking live into the UI and auto-updates via Github Actions on each release https://github.com/a-connoisseur/patch-claude-code

it looks like you only patch for linux / macOS though, no? A lot of us are on Windows because most corporate laptops are Windows based.

a-connoisseur · 2 months ago
it looks like you only patch for linux / macOS though, no? A lot of us are on Windows because most corporate laptops are Windows based.

added windows! although I don't have a windows machine so I can't test it
it just patches the bundled JS though, so shouldn't be any different

PwccaCode · 1 month ago

Insane that the community has to keep comming up with fixes like this for some bs that is vendor locked.
can't even use claude with my own harness have to be locked in claude code that does not show thinking

cvmocanu · 22 days ago

@Anthropic: seriously, if Claude is so good, why not asking it to fix this huge usability issue? Code is cheap, right? :D

MarioVilas · 16 days ago

They're scared of China distilling their model 😆 the entire business model of AI is broken, much like the old dot com bubble.

betovildoza · 16 days ago

It's been a little over a week since the "always active thinking blocks" feature started working.

ylluminate · 13 days ago

This is a very important feature to have in Claude Code. Sometimes we really need to understand what is going on - WHEN it's going on to follow along in certain areas, especially in scientific work.