[FEATURE] Show the permission rule that will be saved, verbatim, in the approval prompt
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
When I approve a Bash command with "don't ask again", Claude Code writes a pattern into settings.json — but the approval prompt only shows a human-readable sentence. I never see the pattern. The gap between "the command I just looked at" and "the rule that will match forever after" is invisible at the only moment I can act on it.
Auditing my own project settings recently, I found these standing allow rules:
"Bash(rm -rf *)" // unconditional recursive force-delete, any argument
"Bash(python -c ' *)" // arbitrary code execution — a strict superset of the above
"Bash(powershell.exe *)"
I could not reconstruct which approval produced each of them, and I am not claiming a specific dialog generated a specific rule — I did not verify that causally. The narrower point is what matters:
- Rules of this breadth can end up in settings.
- Nothing in the approval flow shows breadth before the click.
- Nothing in
/permissionsdistinguishes them after —Bash(rm -rf *)andBash(git diff *)render identically.
Bash(python -c ' *) is the one that worries me most: it grants arbitrary code execution, but reads like a narrow, tool-specific rule. Someone scanning their allow-list for dangerous entries would stop at rm -rf and never flag it.
This is a safety property rather than a convenience one — an over-broad rule is silent, permanent, and only discovered by manually reading the JSON.
Proposed Solution
Show the exact rule text that will be written to settings, inline in the approval prompt, next to the "don't ask again" option:
Run shell command
$ rm -rf ./build
[y] Yes, once
[a] Yes, don't ask again → will save: Bash(rm -rf *)
[n] No
The single line will save: Bash(rm -rf *) is the whole ask. It costs one line of output and lets me notice that a one-off cleanup is about to become an unconditional grant — which is exactly the moment I can still choose "yes, once" instead.
Same string should be shown when the rule is created any other way (e.g. the /permissions add flow), so what I read is always the thing that gets matched.
Alternative Solutions
Things I actually do today, none of which work well:
- Read
settings.json/.claude/settings.local.jsonby hand afterwards. This is how I foundBash(rm -rf *)— weeks after the fact, by accident. - Always choose "yes, once" and never persist anything. Safe, but then every command re-prompts and the feature stops being useful.
- Pre-write the allow-list by hand and never use the interactive approval. Works, but requires knowing the pattern syntax up front, and the prompts still appear for anything not predicted.
Adjacent changes I considered proposing instead (deliberately left out to keep this a single request, but they compose well with it):
- Make approval-list rows distinguishable — I recently had a
/permissionslist where four separate rows all readRun shell command, though one was a localfindand another wrote a file into a container on a remote host. Showing the saved rule would incidentally fix this, since the patterns differ. - Warn when a wildcard lands on a destructive verb (
rm,dd,mkfs,chmod -R, or interpreter escapes likepython -c/node -e/sh -c). This needs a maintained list and is more debatable, which is why I'm not asking for it here.
Priority
High - Significant impact on productivity
Feature Category
Configuration and settings
Use Case Example
- I ask Claude to clean a build directory. It proposes
rm -rf ./build. - The prompt shows
Run shell commandplus the command. Looks obviously fine, so I pick "don't ask again" — I expect to be approving build-directory cleanups. - Today: nothing else is displayed.
Bash(rm -rf *)is written to.claude/settings.local.json. Every futurerm -rfwith any argument is now pre-approved, in this project, permanently. - With this feature: the prompt reads
will save: Bash(rm -rf *). I see the argument was generalized away, pick "yes, once" instead, and move on.
Step 4 takes no extra time and no extra knowledge — the pattern is right there next to the choice it belongs to.
Additional Context
Related but distinct issues (checked before filing):
- #83406 and #86151 report the opposite failure mode — suggested patterns being too narrow, causing repeat prompts. The two asks are in tension: any fix that widens generalization makes displaying the resulting pattern more important, not less. Shipping this one first would make that work safer to do.
- #85023 asks for the requesting agent + reasoning in the prompt. Complementary; it does not cover the saved-pattern text.
Note on scope: this is a display-only change — no change to how patterns are generated, matched, or stored. That is deliberate: getting generalization "right" is a hard, contested problem, whereas showing the user what was generalized is cheap and helps regardless of which direction generalization moves.
Environment: Claude Code 2.1.232, Linux (WSL2), defaultMode: auto.