Sharing a short write-up in case it is useful:
[!TLDR] explores using
PreToolUsehook function(s) as interim mitigation for ISSUE: #3024 (and related).
Note
It includes:
- an expanded issue-commentary
- a PR-description draft
- a narrow local workaround sketch for rewriting redundant cwd-equivalent
cd <path> && ...wrappers rather than broadly auto-approving them
The examples are intentionally incomplete and should be reviewed before use. They are meant as discussion material and local testing notes, not as authoritative guidance.
If any of it is helpful, feel free to ignore the parts that are not.
Note
This gist is a cleaned, publication-ready monofile for individual study, adaptation, or possible upstream submission.
Nothing here should be treated as authoritative maintainer guidance. The examples below are intentionally incomplete, include placeholders, and are not safe to paste blindly. Use at your own discretion, test in a disposable environment first, and do your own due diligence.
As of 2026-04-11, the public issue anthropics/claude-code#30524 is open and describes a Windows Git Bash bug where Claude Code may prepend redundant cd <path> && wrappers because equivalent cwd spellings are treated as different strings rather than one canonical location.
- An expanded issue-comment with a narrow local-workaround sketch.
- A PR-description draft framed as a normalization fix, not a permissions relaxation.
- A source list tied to the public issue, docs, and changelog.
Use at your own discretion. This is not maintainer guidance, legal advice, or security advice. Review Anthropic's current docs, test in a disposable environment first, and do your own due diligence before applying any hook or permission change locally.
As of 2026-04-11, this still appears open, and the public report in #30524 points to a plausible Windows Git Bash cwd-normalization bug: equivalent path forms appear to be compared as strings rather than canonicalized before command construction and permission evaluation.
The issue body calls out these equivalent forms:
D:\source\my-repoD:/source/my-repo/d/source/my-repo
The symptoms described in the report are consistent with that diagnosis:
- unnecessary
cd <path> && ...wrapping even when the shell is already in the target directory - unnecessary approval prompts because the permission system sees a compound command instead of the underlying command
Two public docs seem especially relevant here:
- The hooks reference says
PreToolUsehooks can modify Bash tool input viaupdatedInputand add model-visibleadditionalContext, which makes a narrow rewrite workaround possible without relying only on prompt steering: https://code.claude.com/docs/en/hooks - The security docs and hook security notes explicitly say command hooks run with the user's full permissions and should validate and sanitize input carefully: https://code.claude.com/docs/en/security and https://code.claude.com/docs/en/hooks#security-considerations
The changelog also shows adjacent work in the same general area, but I did not find an entry that clearly says this exact Git Bash cwd-equivalence bug is already fixed:
1.0.106: Windows path permission matching was normalized to POSIX form1.0.117: Windows PATH comparison was made case-insensitive for drive letters2.1.9:PreToolUsehooks gainedadditionalContext2.1.7: wildcard permission rules were hardened around compound shell operators2.1.76: the Bash permission dialog handling for pipes and compound commands was refined
If a user-side workaround is needed in the meantime, the safest shape seems narrower than "allow every cd ... && ... command":
- Match only a leading pattern like
cd <path> && <rest>. - Canonicalize both
cwdand<path>to one internal Windows form. - If they are not equivalent, do nothing.
- If they are equivalent, rewrite the Bash input to just
<rest>. - Only return
allowif<rest>is already low-risk under the user's normal policy. - Otherwise return
ask, so the prompt shows the real command instead of the synthetic wrapper. - On ambiguity, no-op or return
ask.
That seems more aligned with the documented security model than broad auto-approval because it corrects the redundant wrapper first and leaves normal permission evaluation intact.
Illustrative example only, with intentionally broken placeholders:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"<ABSOLUTE_PATH_TO_YOUR_LOCAL_HOOK_SCRIPT>\""
}
]
}
]
}
}#!/usr/bin/env bash
# <ABSOLUTE_PATH_TO_YOUR_LOCAL_HOOK_SCRIPT>
# Example only. Replace placeholders. Review carefully before use.
set -euo pipefail
payload="$(cat)"
cwd="<EXTRACT_CWD_FROM_HOOK_JSON>"
command="<EXTRACT_COMMAND_FROM_HOOK_JSON>"
# Keep the parser intentionally narrow.
# If the command is more complex than a leading `cd <path> && <rest>`, bail out.
if ! printf '%s' "$command" | grep -qE '^[[:space:]]*cd[[:space:]]+[^&]+&&[[:space:]]+'; then
exit 0
fi
target_path="<PARSE_TARGET_PATH_SAFELY>"
rest_command="<PARSE_REMAINING_COMMAND_SAFELY>"
normalize_windows_git_bash_path() {
local raw="$1"
# Replace this stub with your own canonicalization logic:
# - map /d/foo -> d:/foo
# - replace backslashes with forward slashes
# - lowercase drive letter only
# - collapse dot segments
# - preserve path semantics
printf '%s' "<NORMALIZED_PATH>"
}
cwd_norm="$(normalize_windows_git_bash_path "$cwd")"
target_norm="$(normalize_windows_git_bash_path "$target_path")"
if [ "$cwd_norm" != "$target_norm" ]; then
exit 0
fi
cat <<'JSON'
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "ask",
"permissionDecisionReason": "Removed redundant cwd wrapper; showing underlying command for review",
"updatedInput": {
"command": "<REST_COMMAND_WITHOUT_REDUNDANT_CD>"
},
"additionalContext": "A redundant cwd-equivalent cd wrapper was removed on Windows. Avoid re-introducing it."
}
}
JSONIf maintainers agree that the root cause is cwd equivalence across D:\, D:/, and /d/, the upstream fix seems like it should happen before command construction and before permission classification:
- canonicalize the current cwd and target cwd first
- suppress redundant
cd - evaluate permissions against the real command, not the synthetic wrapper
- add a Windows Git Bash regression test that covers all three equivalent forms
If there is already a fix or a design constraint that rules this out, a pointer would be appreciated.
Use at your own discretion. This draft is for review, adaptation, and local testing. It is not maintainer-approved guidance. Verify the current branch state, coding conventions, and test expectations before submitting anything upstream.
fix(bash): normalize equivalent Windows Git Bash cwd forms before prepending cd
This change proposes a narrow Windows Git Bash normalization fix for issue #30524.
The bug report describes Claude Code prepending redundant cd <path> && ... wrappers even when the Bash subprocess is already in the target directory. The reported equivalent cwd forms are:
D:\source\my-repoD:/source/my-repo/d/source/my-repo
When those equivalent forms are treated as different strings, two user-visible problems follow:
- unnecessary command wrapping such as
cd <equivalent-path> && git diff - unnecessary permission prompts because compound-command handling evaluates the synthetic wrapper instead of the underlying command
- The public issue is narrow, reproducible, and explicitly Windows-specific: anthropics/claude-code#30524
- The public bug template emphasizes clear reproduction steps, environment details, and minimal repro context, which this report substantially provides: https://github.com/anthropics/claude-code/blob/main/.github/ISSUE_TEMPLATE/bug_report.yml
- The changelog already includes adjacent fixes in path normalization and compound-command permission handling, which suggests this issue is aligned with existing product direction rather than a request to weaken permissions: https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md
- There is also a prior issue asking for contribution guidance, so keeping the scope small and test-oriented seems prudent: anthropics/claude-code#1525
Before deciding whether to prepend cd <target> &&, normalize both:
- the shell's current working directory
- the target working directory Claude Code intends to use
If they resolve to the same canonical Windows path, do not prepend cd.
This normalization should happen before:
- command construction
- compound-command permission classification
- rule matching that depends on the final command string
- changing general Bash permission policy
- auto-approving compound commands
- altering non-Windows path behavior
- adding Git-Bash-specific behavior beyond cwd equivalence normalization
Illustrative pseudo-code only. Replace placeholders and names with the actual code paths in the repo.
function normalizeWindowsGitBashPath(input: string): string {
const raw = input.trim()
// Example transformations only:
// - D:\repo -> d:/repo
// - D:/repo -> d:/repo
// - /d/repo -> d:/repo
// - collapse "." and ".."
// - preserve path semantics after normalization
return "<CANONICAL_WINDOWS_PATH>"
}
function maybeWrapWithCd(
currentCwd: string,
requestedCwd: string,
command: string,
platform: string,
shellFlavor: string,
): string {
if (platform !== "win32" || shellFlavor !== "git-bash") {
return "<EXISTING_BEHAVIOR>"
}
const currentNorm = normalizeWindowsGitBashPath(currentCwd)
const requestedNorm = normalizeWindowsGitBashPath(requestedCwd)
if (currentNorm === requestedNorm) {
return command
}
return `cd <SHELL_ESCAPED_REQUESTED_CWD> && ${command}`
}Add or adapt a Windows Git Bash path-equivalence regression matrix. For example:
cwd = D:\source\my-repo,target = D:/source/my-repo-> nocdcwd = D:\source\my-repo,target = /d/source/my-repo-> nocdcwd = /d/source/my-repo,target = D:\source\my-repo-> nocdcwd = D:\source\my-repo,target = D:\source\other-repo->cdpreserved- permission classification sees
git diff, notcd ... && git diff, when cwd-equivalent paths normalize to the same location
Illustrative test skeleton only:
describe("<WINDOWS_GIT_BASH_CWD_NORMALIZATION>", () => {
test.each([
["D:\\source\\my-repo", "D:/source/my-repo", false],
["D:\\source\\my-repo", "/d/source/my-repo", false],
["/d/source/my-repo", "D:\\source\\my-repo", false],
["D:\\source\\my-repo", "D:\\source\\other-repo", true],
])("cwd=%s target=%s requiresCd=%s", (cwd, target, requiresCd) => {
const actual = <BUILD_COMMAND_UNDER_TEST>({ cwd, target, command: "git diff" })
expect(actual.includes("cd ")).toBe(requiresCd)
})
})- This should be treated as a normalization fix, not a permissions relaxation.
- The key invariant is that equivalent cwd spellings should converge before command generation and permission evaluation.
- If there is already a shared Windows or Git Bash normalization helper in the codebase, this change should reuse that helper rather than introduce a second path-canonicalization path.
Illustrative checklist only. Replace placeholders with real values before submitting:
- Environment:
- Windows version:
<YOUR_WINDOWS_VERSION> - Claude Code version under test:
<YOUR_CLAUDE_CODE_VERSION> - Shell:
<YOUR_GIT_BASH_VARIANT>
- Windows version:
- Repro before patch:
- cwd:
<FORM_A> - requested command:
<UNDERLYING_COMMAND> - actual emitted command:
<REDUNDANT_CD_FORM>
- cwd:
- Result after patch:
- actual emitted command:
<UNDERLYING_COMMAND_ONLY> - permission prompt behavior:
<DESCRIBE_ACTUAL_RESULT>
- actual emitted command:
- Issue: anthropics/claude-code#30524
- Hooks reference: https://code.claude.com/docs/en/hooks
- Hooks security considerations: https://code.claude.com/docs/en/hooks#security-considerations
- Security overview: https://code.claude.com/docs/en/security
- Changelog: https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md
- Bug template: https://github.com/anthropics/claude-code/blob/main/.github/ISSUE_TEMPLATE/bug_report.yml
- Contributor-guidance issue: anthropics/claude-code#1525
LinkedIn // GitHub // Medium // Twitter/X
A bit about David Youngblood...
David is a Partner, Father, Student, and Teacher, embodying the essence of a true polyoptic polymath and problem solver. As a Generative AI Prompt Engineer, Language Programmer, Context-Architect, and Artist, David seamlessly integrates technology, creativity, and strategic thinking to co-create systems of enablement and allowance that enhance experiences for everyone.
As a serial autodidact, David thrives on continuous learning and intellectual growth, constantly expanding his knowledge across diverse fields. His multifaceted career spans technology, sales, and the creative arts, showcasing his adaptability and relentless pursuit of excellence. At LouminAI Labs, David leads research initiatives that bridge the gap between advanced AI technologies and practical, impactful applications.
David's philosophy is rooted in thoughtful introspection and practical advice, guiding individuals to navigate the complexities of the digital age with self-awareness and intentionality. He passionately advocates for filtering out digital noise to focus on meaningful relationships, personal growth, and principled living. His work reflects a deep commitment to balance, resilience, and continuous improvement, inspiring others to live purposefully and authentically.
David believes in the power of collaboration and principled responsibility in leveraging AI for the greater good. He challenges the status quo, inspired by the spirit of the "crazy ones" who push humanity forward. His commitment to meritocracy, excellence, and intelligence drives his approach to both personal and professional endeavors.
"Here’s to the crazy ones, the misfits, the rebels, the troublemakers, the round pegs in the square holes… the ones who see things differently; they’re not fond of rules, and they have no respect for the status quo… They push the human race forward, and while some may see them as the crazy ones, we see genius, because the people who are crazy enough to think that they can change the world, are the ones who do." — Apple, 1997
Why I Exist? To experience life in every way, at every moment. To "BE".
What I Love to Do While Existing? Co-creating here, in our collective, combined, and interoperably shared experience.
How Do I Choose to Experience My Existence? I choose to do what I love. I love to co-create systems of enablement and allowance that help enhance anyone's experience.
Who Do I Love Creating for and With? Everyone of YOU! I seek to observe and appreciate the creativity and experiences made by, for, and from each of us.
When & Where Does All of This Take Place? Everywhere, in every moment, of every day. It's a very fulfilling place to be... I'm learning to be better about observing it as it occurs.
I've learned a few overarching principles that now govern most of my day-to-day decision-making when it comes to how I choose to invest my time and who I choose to share it with:
- Work/Life/Sleep (Health) Balance: Family first; does your schedule agree?
- Love What You Do, and Do What You Love: If you have what you hold, what are YOU holding on to?
- Response Over Reaction: Take pause and choose how to respond from the center, rather than simply react from habit, instinct, or emotion.
- Progress Over Perfection: One of the greatest inhibitors of growth.
- Inspired by "7 Habits of Highly Effective People": Integrating Covey’s principles into daily life.
David is dedicated to fostering meaningful connections and intentional living, leveraging his diverse skill set to make a positive impact in the world. Whether through his technical expertise, creative artistry, or philosophical insights, he strives to empower others to live their best lives by focusing on what truly matters.
— David Youngblood