| name | setup-permissions |
| description | Scan the current repository's stack, predict the scoped Claude Code permission rules the user will likely need (test/lint/build/git/package-mgmt/read paths), and propose them for per-rule authorization before writing the approved ones to a settings.json file. Proactive project-setup companion. Global and project-agnostic. Trigger when the user says "set up permissions", "setup-permissions", "configure permissions for this repo", "what permissions do I need", or "allowlist commands for this project". |
Set up Claude Code permissions for a repository
Project-agnostic, global skill. It scans the repo's stack, predicts the scoped
permission rules the user will likely need, presents them for per-rule authorization,
and writes only the approved ones to the chosen settings.json. It is proactive -
it runs at project-setup time, before the user has hit a single permission prompt.
It complements two existing skills and must not duplicate them:
fewer-permission-prompts is reactive (mines past transcripts for commands already
prompted). This skill predicts from the stack instead.
update-config owns the settings.json rule syntax and the write mechanism. Reuse
its conventions; let it (or the same careful write) perform the actual file edit.
The one rule that matters: scope narrowly
Permissions are a security boundary. The risk is not building this - it is proposing
broad rules the user rubber-stamps. Every proposed rule must be as narrow as possible.
- GOOD:
Bash(npm run test:*), Bash(pytest:*), Bash(ruff check:*),
Bash(go build:*), Read(./src/**).
- BAD (never propose):
Bash(git:*) (allows git push --force, git reset --hard),
Bash(:*) (everything), unscoped Edit(**), Bash(rm:*), Bash(sudo:*).
Dangerous-by-nature operations - force-push, rm, sudo, deploy/release, piping a
download to a shell (curl ... | sh), credential or .env reads - must be listed under
"Deliberately NOT proposed", never offered as allows. The user can always add those
manually with full awareness.
Rule syntax reference (ground every proposal in this)
Tool(specifier); Bash uses prefix matching with :* (e.g. Bash(npm run build:*)
matches npm run build and anything after). Read/Edit use gitignore-style globs
rooted at the project (Read(./config/**)).
- Categories the harness supports include
allow, ask, and deny. Prefer allow
for safe, frequent commands; suggest ask (not silent allow) for medium-risk ones;
reserve deny for things that should be blocked outright.
- Per-rule, prefer the tightest specifier that still covers normal use.
Procedure
1. Choose the target file (ask once, up front)
.claude/settings.local.json (DEFAULT) - personal, git-ignored; does not force
choices on teammates.
.claude/settings.json - shared/versioned; use only if the user wants the repo
to carry the allowlist for everyone.
~/.claude/settings.json - global; only for genuinely stack-agnostic rules.
Read whichever file already exists first, so you merge into its permissions.allow
rather than clobbering it, and never re-propose a rule that is already present.
2. Scan the stack
- Manifests/configs:
package.json (scripts!), pyproject.toml, setup.py,
Makefile/justfile, go.mod, Cargo.toml, *.csproj, pom.xml,
build.gradle, CI workflows, lint/format configs (.eslintrc, ruff.toml, etc.).
- Detect: test runner, linter, formatter, type checker, build tool, package manager,
and whether
gh/docker/git are in use.
- For each, derive the scoped command(s) the user will actually run.
- For a large/unfamiliar repo, delegate the sweep to the
Explore agent and work from
its summary.
3. Propose for per-rule authorization
Group proposals by category (test / lint+format / build / type-check / package-mgmt /
git (scoped, safe ops only) / read paths). Present them so the user authorizes each rule
individually - use AskUserQuestion with multiSelect per category so a whole
category is cleared in one screen rather than one tedious prompt per rule. For each rule
show: the rule string + a one-line "allows X; does NOT allow Y" note.
Then a "Deliberately NOT proposed" section listing the dangerous operations you
withheld and why.
4. Write only the approved rules
- Merge the approved rules into the chosen file's
permissions.allow (dedupe; preserve
existing entries and formatting). Create the file/array if absent.
- Echo back exactly what was added and to which file.
- Remind the user that permission changes take effect immediately, but to restart if a
rule does not seem to apply.
Guardrails
- Propose in step 3; write only after per-rule approval in step 4. Never auto-allow.
- Every rule must be narrowly scoped; never propose wildcard or destructive allows.
- Always include the "Deliberately NOT proposed" section - withhold force-push,
rm,
sudo, deploy, curl|sh, and secret/.env reads.
- Merge, never clobber; do not duplicate rules already in the file.
- Default to
.claude/settings.local.json unless the user chose otherwise.
- Never fabricate stack facts - base every rule on a manifest/config you actually saw.