7.5 KiB
Less Permission Prompts
Look through my transcripts' MCP and bash tool calls, and based on those, make a prioritized list of patterns that I should add to my permission allowlist to reduce permission prompts. Focus on read-only commands.
The format for permissions is: Bash(foo*), Bash(foo), Bash(foo bar *), mcp__slack__slack_read_thread, etc.
Then, add these to the project .claude/settings.json under permissions.allow.
Steps
-
Locate transcripts. Session transcripts live at
~/.claude/projects/<sanitized-cwd>/*.jsonl. Each line is a JSON object. Tool calls appear asassistantmessages withmessage.content[]entries oftype: "tool_use". Thenamefield identifies the tool (e.g."Bash","mcp__slack__slack_read_thread"); for Bash,input.commandis the shell string.Scan the recent transcripts across the user's projects dir — not just the current project — so the allowlist reflects their actual usage. Cap the scan at a reasonable number of recent sessions (e.g. 50 most-recently-modified JSONL files) so this stays fast.
-
Extract tool-call frequencies.
- For
Bashcalls: parseinput.command, take the leading command token (handlingsudo,timeout, pipes,&&, env-var prefixes). Record the command + first subcommand pair (e.g.git status,gh pr view,ls,cat). - For MCP calls: record the full tool name (e.g.
mcp__slack__slack_read_thread). - Count occurrences across the scanned transcripts.
- For
-
Filter to read-only. Keep only commands that don't mutate state. Examples of read-only:
ls,cat,pwd,git status,git log,git diff,git show,git branch,rg,grep,find,head,tail,wc,file,which,echo,date,gh pr view,gh pr list,gh pr diff,gh issue view,gh issue list,gh run list,gh run view,gh api(GET),bun run typecheck,bun run lint,bun run test(for tests that don't mutate),docker ps,docker logs,kubectl get,kubectl describe,ps,top,df,du,env,printenv, any MCP tool withread/get/list/search/viewin its name.Drop anything that writes, deletes, renames, pushes, merges, installs, or runs a build/test that has side effects. When in doubt, leave it out.
Never allowlist a pattern that grants arbitrary code execution. A wildcard rule for any of these (e.g.
Bash(python3:*)) is equivalent to allowing arbitrary code execution. This list is not exhaustive — apply the same rule to anything in the same category:- Interpreters:
python/python3,node,bun,deno,ruby,perl,php,lua, etc. - Shells:
bash,sh,zsh,fish,eval,exec,ssh, etc. - Package runners:
npx,bunx,uvx,uv run, etc. - Task-runner wildcards:
npm run *,yarn run *,pnpm run *,bun run *,make *,just *,cargo run *,go run *, etc. — an exactBash(bun run typecheck)is fine,Bash(bun run *)is not gh api *,docker run/exec,kubectl exec,sudo, and similar
- Interpreters:
-
Drop commands Claude Code already auto-allows. These don't need an allowlist entry — they never prompt. If you see any of these in the transcripts, skip them; don't suggest them to the user.
- Always auto-allowed (any args):
cal,uptime,cat,head,tail,wc,stat,strings,hexdump,od,nl,id,uname,free,df,du,locale,groups,nproc,basename,dirname,realpath,cut,paste,tr,column,tac,rev,fold,expand,unexpand,fmt,comm,cmp,numfmt,readlink,diff,true,false,sleep,which,type,expr,test,getconf,seq,tsort,pr,echo,printf,ls,cd,find. - Auto-allowed with zero args only:
pwd,whoami,alias. - Auto-allowed exact forms:
claude -h,claude --help,node -v,node --version,python --version,python3 --version,ip addr. - Auto-allowed with safe flags only (validated):
xargs,file,sed(read-only expressions),sort,man,help,netstat,ps,base64,grep,egrep,fgrep,sha256sum,sha1sum,md5sum,tree,date,hostname,info,lsof,pgrep,tput,ss,fd,fdfind,aki,rg,jq,uniq,history,arch,ifconfig,pyright. - All git read-only subcommands:
git status,git log,git diff,git show,git blame,git branch,git tag,git remote,git ls-files,git ls-remote,git config --get,git rev-parse,git describe,git stash list,git reflog,git shortlog,git cat-file,git for-each-ref,git worktree list, etc. - All gh read-only subcommands:
gh pr view,gh pr list,gh pr diff,gh pr checks,gh pr status,gh issue view,gh issue list,gh issue status,gh run view,gh run list,gh workflow list,gh workflow view,gh repo view,gh release view,gh release list,gh api(GET),gh auth status, etc. - Docker read-only subcommands:
docker ps,docker images,docker logs,docker inspect.
Source of truth:
src/tools/BashTool/readOnlyValidation.ts(READONLY_COMMANDS,READONLY_NOARGS,READONLY_EXACT,COMMAND_ALLOWLIST) andsrc/utils/shell/readOnlyCommandValidation.ts(GIT_READ_ONLY_COMMANDS,GH_READ_ONLY_COMMANDS,DOCKER_READ_ONLY_COMMANDS,RIPGREP_READ_ONLY_COMMANDS,PYRIGHT_READ_ONLY_COMMANDS). If the user is in this repo and you're unsure whether a command is covered, grep these files rather than guessing. - Always auto-allowed (any args):
-
Pick the pattern form. Use the narrowest pattern that still covers the observed usage:
- If the user runs many variants (
git log,git log --oneline,git log main..HEAD): useBash(git log *)— note the space before*, which is required for prefix matching to work correctly. - If a single exact invocation is common: use
Bash(foo)with no wildcard. - For MCP: use the full tool name verbatim (no wildcard needed; they're already specific).
- Never widen a pattern to the point that it conflicts with the rules above (no arbitrary code execution, no mutation/side effects).
- If the user runs many variants (
-
Prioritize. Rank by count descending. Drop anything that appeared fewer than ~3 times — not worth the allowlist entry. Cap the list at the top ~20 so the user can skim it.
-
Present the prioritized list to the user as a markdown table with columns: rank, pattern, count, one-line description. Example:
# Pattern Count Notes 1 Bash(git status *)142 repo status checks 2 Bash(gh pr view *)87 PR inspection 3 mcp__slack__slack_read_thread54 Slack thread reads -
Merge into
.claude/settings.jsonin the current project (not~/.claude/settings.json, not.claude/settings.local.json). Create the file if it doesn't exist. Preserve existing keys and existing entries inpermissions.allow; de-duplicate against what's already there; don't remove anything; don't reorder unrelated fields. -
Report back. Tell the user what you added (count + a few examples), what was already in the allowlist, and what you skipped and why (e.g. "dropped
rmandgit push— not read-only; droppedcat/ls/git status— already auto-allowed, no rule needed").
Do not add anything to permissions.deny or permissions.ask. Do not touch any other settings field.