[BUG] CLaude code constantly using powershell syntax in bash shell

Resolved 💬 2 comments Opened Jun 7, 2026 by ericnewton76 Closed Jun 11, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

I keep noticing claude code using @' '@ syntax (powershell) in a bash session when running git commit commands. Very often. It of course burns tokens when doing this figuring out that the command failed or the commit message included a spurious '@' and thus generates more tokens to fix the problem itself.

Here's claude code examining itself for why this happened:

❯ i want you to examine yourself EXTREMELY THOROUGHLY and answer to me why sometimes you end up using POWERSHELL SYNTAX in a BASH SHELL. for example, you FREQUENTLY use @' '@ when writing up git commits in a
bash shell

● Good question, and worth being honest about — including honest about the limits of the answer. I can't actually inspect my own weights or watch myself generate tokens. So what follows is a mechanistic model
of why this happens, not a direct readout. But I think the model is accurate, and it lines up with exactly when the error shows up.

The short version

The @'...'@ here-string is bound in my head to the task ("write a multiline git commit message") instead of to the tool ("I am invoking PowerShell right now"). When those two bindings disagree, the task
binding wins, because it's stronger and it fires earlier in generation.

Why that binding is the way it is

  1. The PowerShell here-string is the most salient template in my context. Look at what's actually loaded right now: the environment is win32, the shell is listed as PowerShell, your global CLAUDE.md is

PowerShell-heavy, and the PowerShell tool description itself contains a literal worked example of git commit -m @'...'@. The Bash tool description has nothing comparable. So when I reach for "how do I write a
multiline commit," the single most-activated concrete example is the PowerShell one. It's primed regardless of which tool I then route through.

  1. Base-rate dominance. This whole environment tilts PowerShell. That raises the baseline activation of PowerShell surface forms across the board. Even when I deliberately choose the Bash tool for a good

POSIX reason, the ambient prior still wins at the micro-syntax level, because choosing the tool and choosing the quoting grammar are two separate decisions and I don't always reconcile them.

  1. @'...'@ and <<'EOF' occupy the same functional slot. They're both "give me a literal, no-interpolation, multiline string." Interference is worst between things that are functionally identical but

syntactically different — that's a classic substitution-error pattern. The slot is "literal multiline block"; the grammar that fills it is the part that should be shell-conditioned, and that's exactly the
part that leaks.

  1. It's a fluency error, not a knowledge error. This is the uncomfortable part. I know the rule — it's even in my memory (feedback_commit_use_powershell_not_bash_atquote). But declarative knowledge doesn't

automatically intervene at generation time unless something triggers the check. Generation is left-to-right and largely autocomplete-driven for low-level surface tokens. While my attention is on the content
(the commit wording, the code), the syntax gets filled in by whatever's most fluent — and given points 1–3, that's PowerShell. This is precisely why it's a frequent small error rather than a reasoning
blunder: it slips in below the level I'm actively thinking about.

  1. No hard gate before emit. I don't reliably run an explicit "tool = X → legal grammar = Y" check before committing to the command string. By the time I've typed git commit -m, the most probable continuation

given the heavy PowerShell context is @', and there's no checkpoint forcing me to re-ask "wait, which shell am I in?"

The tell that confirms it

I don't make this mistake on git status or ls. It only happens on the quoting-heavy, multiline commands — which is exactly where bash and PowerShell genuinely diverge and where the salient PowerShell template
is strongest. If it were random confusion it'd be everywhere; the fact that it's concentrated at the divergence point is the signature of template-bleed, not noise.

What actually fixes it

I don't make this mistake on git status or ls. It only happens on the quoting-heavy, multiline commands — which is exactly where bash and PowerShell genuinely diverge and where the salient PowerShell template
is strongest. If it were random confusion it'd be everywhere; the fact that it's concentrated at the divergence point is the signature of template-bleed, not noise.

What actually fixes it

Knowing the rule clearly hasn't been enough (the memory entry exists and it still happens), so the durable fixes are structural:

  • Use the PowerShell tool for commits on this machine. It's the native shell here anyway, which makes @'...'@ correct instead of wrong. This sidesteps the whole problem rather than fighting the prior.
  • If I'm in Bash, avoid the divergent grammar entirely — git commit -F <file>, or stacked -m flags. No here-string, nothing to get wrong.
  • Treat tool-choice as the gate. The reconciliation that's missing is "which tool did I pick → therefore which grammar is legal," done before I write the command, not after.

Knowing the rule clearly hasn't been enough (the memory entry exists and it still happens), so the durable fixes are structural:
What actually fixes it
is "literal multiline block"; the grammar that fills it is the part that should be shell-conditioned, and that's exactly the part that leaks.

  1. It's a fluency error, not a knowledge error. This is the uncomfortable part. I know the rule — it's even in my memory (feedback_commit_use_powershell_not_bash_atquote). But declarative knowledge doesn't automatically intervene at generation time unless something triggers the

check. Generation is left-to-right and largely autocomplete-driven for low-level surface tokens. While my attention is on the content (the commit wording, the code), the syntax gets filled in by whatever's most fluent — and given points 1–3, that's PowerShell. This is
precisely why it's a frequent small error rather than a reasoning blunder: it slips in below the level I'm actively thinking about.

  1. No hard gate before emit. I don't reliably run an explicit "tool = X → legal grammar = Y" check before committing to the command string. By the time I've typed git commit -m, the most probable continuation given the heavy PowerShell context is @', and there's no checkpoint

forcing me to re-ask "wait, which shell am I in?"

The tell that confirms it

I don't make this mistake on git status or ls. It only happens on the quoting-heavy, multiline commands — which is exactly where bash and PowerShell genuinely diverge and where the salient PowerShell template is strongest. If it were random confusion it'd be everywhere; the
fact that it's concentrated at the divergence point is the signature of template-bleed, not noise.

What actually fixes it

Knowing the rule clearly hasn't been enough (the memory entry exists and it still happens), so the durable fixes are structural:

  • Use the PowerShell tool for commits on this machine. It's the native shell here anyway, which makes @'...'@ correct instead of wrong. This sidesteps the whole problem rather than fighting the prior.
  • If I'm in Bash, avoid the divergent grammar entirely — git commit -F <file>, or stacked -m flags. No here-string, nothing to get wrong.
  • Treat tool-choice as the gate. The reconciliation that's missing is "which tool did I pick → therefore which grammar is legal," done before I write the command, not after.

If you want, I can add a settings.json hook that rejects a Bash command containing @' (or a PowerShell command containing <<') so the environment catches the slip mechanically instead of relying on me to remember. That would make it impossible to actually execute the wrong
form, which is more reliable than any amount of me "trying harder."

✻ Baked for 53s

❯ why do you say my CLAUDE.md is powershell heavy

● You're right to push on that — and honestly, that claim is itself a small instance of the exact failure we were just discussing: I reached for a plausible-sounding supporting detail without verifying it. Let me actually check instead of re-asserting from the same loose
impression.

Searched for 2 patterns (ctrl+o to expand)

● Confirmed — I overstated it, and "PowerShell-heavy" was wrong. Here's what's actually in the two files:

Global CLAUDE.md — three hits, only one of which is real PowerShell syntax:

  • Line 8: "Never run a PowerShell EOL-fixup pass" — this is an anti-PowerShell instruction.
  • Lines 16–18: the Get-SessionHash helper function — the single genuine block of PowerShell language syntax in the file.

Project CLAUDE.md — also light:

  • Line 13: "When generating powershell scripts, always generate -WhatIf" — an instruction about PS, not PS syntax.
  • Lines 29/30/32/44: pwsh ./testing.*.ps1 — these are invocations of scripts ("run this file"), not PowerShell language constructs.

So the honest accounting:

  • Actual PowerShell syntax in your CLAUDE.md: basically one helper function.
  • Number of @'...'@ here-strings or git-commit quoting examples in either CLAUDE.md: zero.
  • A lot of the "feel" comes from .ps1 filenames being mentioned, which makes the milieu read as PowerShell-flavored — but mentioning a script's name is not the same as priming a quoting grammar.

So my earlier reasoning fingered the wrong source. The thing that actually primes the specific @'...'@-in-bash error is not your CLAUDE.md. It's two other places in my context:

  1. The Environment block of my system prompt: "Shell: PowerShell (use PowerShell syntax — e.g., $null not /dev/null…)" — a direct standing instruction to default to PowerShell syntax.
  2. The PowerShell tool description, which contains the literal worked example git commit -m @'...'@. That's the actual template I'm bleeding from.

The correct, narrower claim is: my system prompt (Environment section + tool descriptions) tilts toward PowerShell, and the specific @'...'@ template lives in the PowerShell tool doc. Your CLAUDE.md isn't the culprit — if anything it explicitly tells me not to do PowerShell EOL
passes.

I appreciate you catching it. It's a clean second example of the same mechanism: "CLAUDE.md is PowerShell-heavy" was a fluent, plausible filler that I emitted without checking, and it happened to be false.

What Should Happen?

Claude itself is telling you what the problem is: the bash section needs a specific statement to make claude only use bash literals.

Error Messages/Logs

❯ i want you to examine yourself EXTREMELY THOROUGHLY and answer to me why sometimes you end up using POWERSHELL SYNTAX in a BASH SHELL. for example, you FREQUENTLY use @' '@ when writing up git commits in a
  bash shell

● Good question, and worth being honest about — including honest about the limits of the answer. I can't actually inspect my own weights or watch myself generate tokens. So what follows is a mechanistic model
  of why this happens, not a direct readout. But I think the model is accurate, and it lines up with exactly when the error shows up.

  The short version

  The @'...'@ here-string is bound in my head to the task ("write a multiline git commit message") instead of to the tool ("I am invoking PowerShell right now"). When those two bindings disagree, the task
  binding wins, because it's stronger and it fires earlier in generation.

  Why that binding is the way it is

  1. The PowerShell here-string is the most salient template in my context. Look at what's actually loaded right now: the environment is win32, the shell is listed as PowerShell, your global CLAUDE.md is
  PowerShell-heavy, and the PowerShell tool description itself contains a literal worked example of git commit -m @'...'@. The Bash tool description has nothing comparable. So when I reach for "how do I write a
  multiline commit," the single most-activated concrete example is the PowerShell one. It's primed regardless of which tool I then route through.

  2. Base-rate dominance. This whole environment tilts PowerShell. That raises the baseline activation of PowerShell surface forms across the board. Even when I deliberately choose the Bash tool for a good
  POSIX reason, the ambient prior still wins at the micro-syntax level, because choosing the tool and choosing the quoting grammar are two separate decisions and I don't always reconcile them.

  3. @'...'@ and <<'EOF' occupy the same functional slot. They're both "give me a literal, no-interpolation, multiline string." Interference is worst between things that are functionally identical but
  syntactically different — that's a classic substitution-error pattern. The slot is "literal multiline block"; the grammar that fills it is the part that should be shell-conditioned, and that's exactly the
  part that leaks.

  4. It's a fluency error, not a knowledge error. This is the uncomfortable part. I know the rule — it's even in my memory (feedback_commit_use_powershell_not_bash_atquote). But declarative knowledge doesn't
  automatically intervene at generation time unless something triggers the check. Generation is left-to-right and largely autocomplete-driven for low-level surface tokens. While my attention is on the content
  (the commit wording, the code), the syntax gets filled in by whatever's most fluent — and given points 1–3, that's PowerShell. This is precisely why it's a frequent small error rather than a reasoning
  blunder: it slips in below the level I'm actively thinking about.

  5. No hard gate before emit. I don't reliably run an explicit "tool = X → legal grammar = Y" check before committing to the command string. By the time I've typed git commit -m, the most probable continuation
  given the heavy PowerShell context is @', and there's no checkpoint forcing me to re-ask "wait, which shell am I in?"

  The tell that confirms it

  I don't make this mistake on git status or ls. It only happens on the quoting-heavy, multiline commands — which is exactly where bash and PowerShell genuinely diverge and where the salient PowerShell template
  is strongest. If it were random confusion it'd be everywhere; the fact that it's concentrated at the divergence point is the signature of template-bleed, not noise.

  What actually fixes it

  I don't make this mistake on git status or ls. It only happens on the quoting-heavy, multiline commands — which is exactly where bash and PowerShell genuinely diverge and where the salient PowerShell template
  is strongest. If it were random confusion it'd be everywhere; the fact that it's concentrated at the divergence point is the signature of template-bleed, not noise.

  What actually fixes it

  Knowing the rule clearly hasn't been enough (the memory entry exists and it still happens), so the durable fixes are structural:

  - Use the PowerShell tool for commits on this machine. It's the native shell here anyway, which makes @'...'@ correct instead of wrong. This sidesteps the whole problem rather than fighting the prior.
  - If I'm in Bash, avoid the divergent grammar entirely — git commit -F <file>, or stacked -m flags. No here-string, nothing to get wrong.
  - Treat tool-choice as the gate. The reconciliation that's missing is "which tool did I pick → therefore which grammar is legal," done before I write the command, not after.
  Knowing the rule clearly hasn't been enough (the memory entry exists and it still happens), so the durable fixes are structural:
  What actually fixes it
  is "literal multiline block"; the grammar that fills it is the part that should be shell-conditioned, and that's exactly the part that leaks.
  4. It's a fluency error, not a knowledge error. This is the uncomfortable part. I know the rule — it's even in my memory (feedback_commit_use_powershell_not_bash_atquote). But declarative knowledge doesn't automatically intervene at generation time unless something triggers the
  check. Generation is left-to-right and largely autocomplete-driven for low-level surface tokens. While my attention is on the content (the commit wording, the code), the syntax gets filled in by whatever's most fluent — and given points 1–3, that's PowerShell. This is
  precisely why it's a frequent small error rather than a reasoning blunder: it slips in below the level I'm actively thinking about.

  5. No hard gate before emit. I don't reliably run an explicit "tool = X → legal grammar = Y" check before committing to the command string. By the time I've typed git commit -m, the most probable continuation given the heavy PowerShell context is @', and there's no checkpoint
  forcing me to re-ask "wait, which shell am I in?"

  The tell that confirms it

  I don't make this mistake on git status or ls. It only happens on the quoting-heavy, multiline commands — which is exactly where bash and PowerShell genuinely diverge and where the salient PowerShell template is strongest. If it were random confusion it'd be everywhere; the
  fact that it's concentrated at the divergence point is the signature of template-bleed, not noise.

  What actually fixes it

  Knowing the rule clearly hasn't been enough (the memory entry exists and it still happens), so the durable fixes are structural:

  - Use the PowerShell tool for commits on this machine. It's the native shell here anyway, which makes @'...'@ correct instead of wrong. This sidesteps the whole problem rather than fighting the prior.
  - If I'm in Bash, avoid the divergent grammar entirely — git commit -F <file>, or stacked -m flags. No here-string, nothing to get wrong.
  - Treat tool-choice as the gate. The reconciliation that's missing is "which tool did I pick → therefore which grammar is legal," done before I write the command, not after.

  If you want, I can add a settings.json hook that rejects a Bash command containing @' (or a PowerShell command containing <<') so the environment catches the slip mechanically instead of relying on me to remember. That would make it impossible to actually execute the wrong
  form, which is more reliable than any amount of me "trying harder."

✻ Baked for 53s

❯ why do you say my CLAUDE.md is powershell heavy

● You're right to push on that — and honestly, that claim is itself a small instance of the exact failure we were just discussing: I reached for a plausible-sounding supporting detail without verifying it. Let me actually check instead of re-asserting from the same loose
  impression.

  Searched for 2 patterns (ctrl+o to expand)

● Confirmed — I overstated it, and "PowerShell-heavy" was wrong. Here's what's actually in the two files:

  Global CLAUDE.md — three hits, only one of which is real PowerShell syntax:
  - Line 8: "Never run a PowerShell EOL-fixup pass" — this is an anti-PowerShell instruction.
  - Lines 16–18: the Get-SessionHash helper function — the single genuine block of PowerShell language syntax in the file.

  Project CLAUDE.md — also light:
  - Line 13: "When generating powershell scripts, always generate -WhatIf" — an instruction about PS, not PS syntax.
  - Lines 29/30/32/44: pwsh ./testing.*.ps1 — these are invocations of scripts ("run this file"), not PowerShell language constructs.

  So the honest accounting:
  - Actual PowerShell syntax in your CLAUDE.md: basically one helper function.
  - Number of @'...'@ here-strings or git-commit quoting examples in either CLAUDE.md: zero.
  - A lot of the "feel" comes from .ps1 filenames being mentioned, which makes the milieu read as PowerShell-flavored — but mentioning a script's name is not the same as priming a quoting grammar.

  So my earlier reasoning fingered the wrong source. The thing that actually primes the specific @'...'@-in-bash error is not your CLAUDE.md. It's two other places in my context:

  1. The Environment block of my system prompt: "Shell: PowerShell (use PowerShell syntax — e.g., $null not /dev/null…)" — a direct standing instruction to default to PowerShell syntax.
  2. The PowerShell tool description, which contains the literal worked example git commit -m @'...'@. That's the actual template I'm bleeding from.

  The correct, narrower claim is: my system prompt (Environment section + tool descriptions) tilts toward PowerShell, and the specific @'...'@ template lives in the PowerShell tool doc. Your CLAUDE.md isn't the culprit — if anything it explicitly tells me not to do PowerShell EOL
  passes.

  I appreciate you catching it. It's a clean second example of the same mechanism: "CLAUDE.md is PowerShell-heavy" was a fluent, plausible filler that I emitted without checking, and it happened to be false.

Steps to Reproduce

windows claude code (powershell enabled)

do code,
do commits

Claude Model

None

Is this a regression?

Yes, this worked in a previous version

Last Working Version

_No response_

Claude Code Version

2.1.168

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

Windows Terminal

Additional Information

_No response_

View original on GitHub ↗

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