Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/Morrison-Lab/ai-config --skill permission-check명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
| name | permission-check |
| description | Diagnose tool prompting behavior. |
| user-invocable | true |
| allowed-tools | ["Bash","Read"] |
A read-only diagnostic: given a tool/action pattern (e.g. Bash(rm:*),
Edit, WebFetch(domain:example.com)), walk every settings layer that can
carry a permissions block, find every rule that matches the pattern, and
report which one actually wins — and why.
This never edits a settings file. For changing permission config (adding an
allow entry, moving a rule to a different layer), hand off to update-config.
<tool pattern>", "why did that just prompt when it
didn't last time"This is the part worth getting exactly right, because it's counterintuitive:
rule type beats layer. All layers' matching rules are pooled into one
set, then resolved by type — deny, then ask, then allow — the first
match in that order wins, and neither pattern specificity nor which layer a
rule came from changes that order. A deny in any layer blocks a matching
allow in any other layer, including a higher-precedence one (a
project-shared deny on Bash(aws *) still blocks a project-local allow
on Bash(aws s3 ls)). The same holds one level down: a matching ask in any
layer forces a prompt even when a more specific allow in a different layer
also matches — a broad user-level ask can override a narrow
project-local allow for the same call, which is easy to miss if you
assume "more local wins."
Layer precedence still matters, but for a narrower thing than resolving individual rule conflicts — it decides which layers' rules are even in the pool at all:
deny rules, and (if
set) allowManagedPermissionRulesOnly: true, which excludes every other
layer's allow/ask/deny rules from the pool entirely (not just
overridden — genuinely not considered). File locations:
/Library/Application Support/ClaudeCode/managed-settings.json (+
managed-settings.d/) on macOS, /etc/claude-code/managed-settings.json
(+ managed-settings.d/) on Linux/WSL,
C:\Program Files\ClaudeCode\managed-settings.json (+
managed-settings.d\) on Windows.--allowedTools,
--disallowedTools, --permission-mode) — temporary session overrides..claude/settings.local.json (gitignored,
personal)..claude/settings.json (checked in).~/.claude/settings.json.Beyond the managed-settings special cases above, once a layer's rules are in
the pool, its rank in this list does not break ties among rules of the
same type — two matching allow rules from different layers both just
mean "allowed;" there's no conflict to resolve. The list matters for step 2
below (only a layer that's actually present contributes rules) and for
knowing when managed settings might be excluding a layer altogether.
Hooks (PreToolUse) run before the permission prompt but don't bypass
this chain: a matching deny/ask rule still applies regardless of what a
hook itself returns. A hook that exits with code 2 is a separate, stronger
mechanism — it blocks the call before permission rules are even evaluated,
so it can stop a call an allow rule would otherwise have let through.
Exit 2 is not the only way to deny, and it is not the way this repo's own
blocking hooks do it: they print a permissionDecision: "deny" JSON object to
stdout and exit 0.
That matters when testing one, since an exit-code check then reads a denial as
an allow --- see
memories/claude-code.md's "A PreToolUse
hook denies on stdout and still exits 0".
Source: Claude Code permissions docs (rule evaluation order, hook interaction) and settings docs (layer list and paths). If either page has since changed its ordering or paths, that page wins over this skill's restatement — re-fetch it rather than trust this file blindly on a stale point.
For the matcher's own syntax and matching mechanics --- how a compound Bash command is checked segment by segment, what a bare tool name matches, the // absolute-path prefix, and where Tool(param:value) rules apply --- see memories/claude-code-permissions.md, a sibling satellite covering the matcher rather than the scope/evaluation-order question this skill answers.
Ask for (or infer from context) the specific tool-call pattern — the same
shape a rule would use: Bash(git push:*), Edit, WebFetch(domain:x.com).
A vague description ("git stuff") isn't enough to match rule syntax
precisely; ask the user to narrow it if it's ambiguous.
# 1. Managed policy (host-global — see privacy gate below before reading)
# macOS: /Library/Application Support/ClaudeCode/managed-settings.json
# Linux: /etc/claude-code/managed-settings.json
# Windows: C:\Program Files\ClaudeCode\managed-settings.json
# (each may also have a managed-settings.d/ directory of fragments)
# 2. CLI args for this session — not a file; note if the invocation used
# --allowedTools / --disallowedTools / --permission-mode
# 3. Project-local (gitignored, personal)
cat .claude/settings.local.json 2>/dev/null
# 4. Project-shared (checked in)
cat .claude/settings.json 2>/dev/null
# 5. User
cat ~/.claude/settings.json 2>/dev/null
Privacy gate on layer 1. The managed-policy file is host-global, outside
this project and outside the user's own home-relative config — reading it
can surface content unrelated to this session (org-wide policy meant for a
different audience). Only read it after telling the user you're about to,
and skip it if they'd rather not. The user-settings file
(~/.claude/settings.json) is personal but not host-global; read it without
asking, same as the project layers.
Preflight: check for allowManagedPermissionRulesOnly first. If the
managed-policy file (layer 1) sets "allowManagedPermissionRulesOnly": true,
every other layer's allow/ask/deny rules are excluded from the pool
entirely — not overridden, genuinely not considered. When this is set, skip
layers 2–5 for this step and apply step 4 only to managed-policy rules; note
in the report that the flag is active and why non-managed rules were
excluded.
Otherwise, for each layer that exists, pull its permissions.allow /
permissions.ask / permissions.deny arrays and check each entry against
the target pattern — an exact match, a wildcard match (Bash(git *) matches
Bash(git push:*)), or a bare tool name (Edit matches any Edit call).
Record every hit with its layer and rule type; don't stop at the first one
found; the analysis needs the full set to apply the resolution model.
deny hit, from any layer → blocked. Report that layer + rule as
the reason, and note that no allow/ask anywhere can override it.ask hit, from any layer → prompts (with the layer + rule),
even if a more specific allow also matched in a different layer.allow hit, from any layer → auto-allowed (with the layer +
rule).If a step finds more than one matching rule of the winning type (e.g. two
allow rules across different layers both match), name any one of them as
the reason — they agree on the outcome, so there's nothing to break a tie on
— and list the others as also-matching.
Report format — note the counterintuitive result: a broader user-level ask
overrides a narrower project-local allow, because type beats layer:
Pattern: Bash(git push:*)
| Layer | Rule type | Rule | Matches? |
|------------------|-----------|----------------------|----------|
| Managed policy | — | (not present) | — |
| CLI args | — | (none passed) | — |
| Project-local | allow | Bash(git push:*) | yes |
| Project-shared | — | (no match) | — |
| User | ask | Bash(git *) | yes |
Verdict: prompts — the user-level `ask` on `Bash(git *)` wins over
project-local's more specific `Bash(git push:*)` allow rule, because ask
beats allow regardless of layer or specificity. If the intent was to make
this auto-allowed, the fix is to narrow or remove the user-level `ask` rule
(via `update-config`), not to add a more specific project-local allow rule —
that's already there and it isn't enough.
If the reported verdict doesn't match what the user actually saw happen
(e.g. it should auto-allow per the rules but still prompted), say so plainly
rather than forcing a fit — flag the mismatch and suggest checking for a
hook (PreToolUse) that may be intercepting the call before rule evaluation,
or a session using --permission-mode plan/bypassPermissions that changes
the baseline. Don't guess at a hook's behavior; read hooks config in the
same settings files if one is suspected.
update-config — the mutating counterpart. Once this skill identifies
which layer and rule should change, hand off there to actually edit it;
this skill never writes.fewer-permission-prompts — scans transcripts for repeated prompts
and proposes an allowlist; this skill diagnoses a single pattern's current
resolution on demand. Run fewer-permission-prompts when the question is
"what should I allowlist", and permission-check when it's "why did/didn't
this one thing prompt."update-config's
job; this skill only diagnoses.