allowed-tools does not pre-approve Read/Write for paths in the user's home directory (only ${CLAUDE_PLUGIN_ROOT} paths work)

Status Fixed / completed
Maintainer reply ✓ Yes — bcherny
Activity 3 comments · opened Jul 10, 2026 · closed Aug 17, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

Summary

A plugin skill's allowed-tools frontmatter successfully pre-approves Read/Write for paths under ${CLAUDE_PLUGIN_ROOT} (inside the plugin), but does not pre-approve them for paths in the user's home directory — in any syntax I could find. Every home-dir read/write prompts despite a matching-looking grant.

What works

allowed-tools: Read(${CLAUDE_PLUGIN_ROOT}/skills/practice/banks/*.json)

Reads of that path run silently, no prompt. ${CLAUDE_PLUGIN_ROOT} is substituted to the resolved absolute plugin path and matches the tool call.

What does NOT work

A skill that persists small per-user state to ~/.dql-tutor/progress.json (a home-dir location, deliberately outside the protected ~/.claude/). All of these grant forms still produce a permission prompt on both the Read (startup) and the Write (save):

allowed-tools: Write(~/.dql-tutor/progress.json) Read(~/.dql-tutor/progress.json)
allowed-tools: Write(${HOME}/.dql-tutor/progress.json) Read(${HOME}/.dql-tutor/progress.json)
allowed-tools: Write(**/.dql-tutor/progress.json) Read(**/.dql-tutor/progress.json)

In each case the tool-call header shows the correctly-resolved path (e.g. Read(/Users/name/.dql-tutor/progress.json)), and the prompt is the normal (bypassable) permission prompt — but it prompts every time regardless of the grant.

Expectation

The docs state ~/path is a supported anchor for Read/Edit permission patterns (e.g. Read(~/Documents/*.pdf) matching /Users/alice/Documents/*.pdf). Based on that, Write(~/.dql-tutor/progress.json) should pre-approve a write to that resolved path. It doesn't.

Impact

A skill that maintains per-user state in the home directory (the natural place for state that must persist across sessions and survive plugin reinstalls) cannot pre-approve its own state-file I/O, so the user is prompted on every session. ${CLAUDE_PLUGIN_ROOT} isn't a workaround because state written there is lost on reinstall and shared across users of the same install.

Repro

  1. Create a plugin skill with allowed-tools: Write(~/somedir/state.json) Read(~/somedir/state.json).
  2. Have the skill Read then Write that path.
  3. Observe both prompt for permission despite the grant.

Environment

  • Plugin loaded via claude --plugin-dir.
  • Possibly related: #76196 (allowed-tools only grants the first Bash use per session).

View original on GitHub ↗

3 Comments

oreos-and-ramen · 3 months ago

Correction — this is Write-specific, not Read+Write.

Follow-up testing narrows the finding. With the plain ~/… grant form:

  • Write(~/.dql-tutor/progress.json) → still prompts on every save, even with the matching grant. Confirmed consistently across sessions.
  • Read(~/.dql-tutor/progress.json) → went through silently in a fresh session with the same ~/… grant.

So the tilde grant appears honored for Read but not for Write. (I can't fully rule out that the silent Read was instead the "first tool use per session is free" behavior seen in #76196 rather than the grant matching — but the Write consistently prompts regardless.)

The ${HOME}/… and **/… glob grant forms matched nothing for either tool. ${CLAUDE_PLUGIN_ROOT} paths (inside the plugin) are reliably pre-approved for Read.

Net: the blocking issue is that writes to a home-directory path can't be pre-approved via allowed-tools, which prevents a skill from silently persisting per-user state in the home directory.

bcherny collaborator · 1 month ago

Tested on 2.1.233 (Linux, fresh config, default permission mode, interactive tmux session) with a project skill granting allowed-tools: Read(~/.testtutor/**), Write(~/.testtutor/**).

  • Reproduced the prompts: Write(~/…), Write(/home/<user>/…), and Write(//home/<user>/…) all still prompt before writing.
  • Read(~/.testtutor/**) worked for me: the home-dir read ran with no prompt (without the rule, the same read does prompt).
  • Swapping Write(...) for Edit(~/.testtutor/**) (the //home/<user>/... absolute form works too): the home-dir write ran with no prompt.

So this is intended behavior rather than a home-directory limitation: file permission checks only consult Edit(path) and Read(path) rules — Edit rules cover all file-editing tools, including Write. A Write(path) rule is accepted but never matched. See https://code.claude.com/docs/en/permissions. Since 2.1.210, Write(path) rules in settings show a startup warning; rules inside skill frontmatter don't surface that warning yet, which makes this easy to hit — we're looking at extending it there.

Closing as intended behavior — replace Write(~/.dql-tutor/**) with Edit(~/.dql-tutor/**) and the home-dir grant is honored. Reply here to reopen if that doesn't fix it for you.

🤖 Generated with Claude Code

github-actions[bot] · 7 days ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.