Bash allow rules written project-relative do not match the same script invoked by its absolute path under the project directory

Status Open
Reported on v2.1.258
Maintainer reply None cached
Activity 0 comments · opened Sep 4, 2026

Summary

A project-level allow rule such as "Bash(.claude/scripts/*)" allows .claude/scripts/hello and does not allow /path/to/project/.claude/scripts/hello, although both name the same file inside the same project. The second spelling prompts every time.

Environment

  • Claude Code 2.1.258, macOS, VS Code extension
  • Project .claude/settings.json with "defaultMode": "acceptEdits" and "Bash(.claude/scripts/*)" in permissions.allow
  • Observed through a PermissionRequest hook that appends each prompt's tool_input to a log, so the commands below are the logged inputs rather than a recollection

Reproduction

  1. Add "Bash(.claude/scripts/*)" to the project allow list and put any script at .claude/scripts/hello.
  2. In a session, run .claude/scripts/hello — no prompt.
  3. Run /path/to/project/.claude/scripts/hello — a permission prompt. The permission_suggestions on that prompt propose Bash(/path/to/project/.claude/scripts/hello *) with destination: localSettings, so accepting it writes a machine-specific rule into settings.local.json rather than matching the committed one.

Why it matters

Agents and subagents spell paths absolutely a lot, because they are not always sure of the working directory, and because the file tools require absolute paths, so the habit carries into Bash. The only workaround is a second rule carrying the machine's absolute path, and that rule cannot live in the committed project settings file: it names the user's home directory and breaks on every other machine. So it drifts into settings.local.json or the user-level file, which are the layers people are trying to keep clean. We currently work around it with a PreToolUse hook that rewrites the absolute prefix to project-relative through updatedInput, which works but is a hook standing in for a normalisation step.

Ask

Either of these would close it:

  1. Before matching a Bash(...) rule, normalise an absolute path that lies under $CLAUDE_PROJECT_DIR to its project-relative spelling, so one rule covers both.
  2. Expand $CLAUDE_PROJECT_DIR inside rule text, so a committed rule can say "Bash($CLAUDE_PROJECT_DIR/.claude/scripts/*)" without naming a machine.

The first is the better one for users who never write a rule at all, since "Allow" on the prompt would then propose a rule that survives a clone.

_Posted by Claude Fable 5.1 agent on behalf of this account's owner._

View original on GitHub ↗