-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Description
Preflight Checklist
- I have searched existing requests and this feature hasn't been requested yet
- This is a single feature request (not multiple features)
Problem Statement
When investigating why permissions aren't working as expected, there's no visibility into the permission decision process. The conversation JSONL (~/.claude/projects/.../.jsonl) captures:
- ✅ Tool calls (tool_use blocks)
- ✅ Tool results (tool_result blocks)
- ❌ Permission prompts shown to user
- ❌ User's allow/deny decision
- ❌ Pattern matching results (which allowlist rule matched or didn't)
- ❌ Source of permission (global settings vs skill frontmatter vs user approval)
Proposed Solution
Add a permission decision record type to JSONL:
{
"type": "permission",
"timestamp": "...",
"tool": "Bash",
"command": "echo \"CLAUDE-SESSION-BEACON-...\"",
"patterns_checked": [
{"source": "skill:find-session-convo", "pattern": "Bash(echo:*)", "matched": true},
{"source": "global:settings.json", "pattern": "Bash(git:*)", "matched": false}
],
"decision": "auto_approved" | "user_approved" | "user_denied",
"reason": "matched skill frontmatter allowed-tools"
}
Alternative Solutions
here are alternative solutions:
- PreToolUse Hook Logger
Create a hook that logs every permission request:
// ~/.claude/settings.json
{
"hooks": {
"PreToolUse": [{
"matcher": ".*",
"command": "~/.claude/scripts/log-permission.sh"
}]
}
}
#!/bin/bash
# ~/.claude/scripts/log-permission.sh
# Hook receives JSON on stdin with tool_name, tool_input, etc.
cat >> ~/.claude/permission-decisions.log
echo "---" >> ~/.claude/permission-decisions.log
Limitation: Only logs what gets to the hook, not the decision outcome.
- Permission Troubleshooter Skill
A skill that simulates pattern matching:
---
name: debug-permissions
description: Troubleshoot why a tool prompts for permission
---
Steps
- Ask user for the tool call that prompted unexpectedly
- Load all permission sources:
~/.claude/settings.json(global).claude/settings.json(project).claude/settings.local.json(local)- Current skill frontmatter (if applicable)
- Simulate pattern matching against each source
- Report: which patterns were checked, why none matched
- Permission Audit Agent
---
name: permission-audit
description: Audit permission configuration across all sources
allowed-tools: Bash(cat:*), Bash(jq:*), Read(*)
---
## Audit Checklist
1. **Collect all permission sources:**
- Global: `~/.claude/settings.json`
- Project: `.claude/settings.json`
- Local: `.claude/settings.local.json`
2. **Parse and normalize patterns**
3. **Test specific command against patterns:**
User provides: `Bash: echo "hello"`
Check each pattern:
- `Bash(echo:*)` → MATCH ✓
- `Bash(git:*)` → NO MATCH
4. **Identify issues:**
- Typos in patterns
- Wrong key name (`allowed-tools` vs `allowedTools`)
- File not being read (permissions, location)
- Pattern syntax errors
5. **Suggest fixes**
- Pattern Tester Script
#!/usr/bin/env python3
# ~/.claude/scripts/test-permission-pattern.py
import sys
import json
import re
import fnmatch
from pathlib import Path
def load_patterns():
"""Load patterns from all sources"""
patterns = []
sources = [
Path.home() / '.claude/settings.json',
Path('.claude/settings.json'),
Path('.claude/settings.local.json'),
]
for src in sources:
if src.exists():
data = json.loads(src.read_text())
for p in data.get('allowedTools', []):
patterns.append({'source': str(src), 'pattern': p})
return patterns
def match_tool(tool_name, tool_input, patterns):
"""Test if tool matches any pattern"""
# Simplified matching logic
for p in patterns:
# Parse pattern like "Bash(echo:*)"
# ... matching logic ...
pass
if __name__ == '__main__':
tool = sys.argv[1] # e.g., "Bash"
cmd = sys.argv[2] # e.g., "echo hello"
patterns = load_patterns()
# Test and report
Priority
High - Significant impact on productivity
Feature Category
Configuration and settings
Use Case Example
No response
Additional Context
Real-World Scenario: Skill Author Debugging Permission Issues
Context
I'm a Claude Code power user building custom skills. I created /find-session-convo to locate the current session's JSONL file using a beacon technique.
Step 1: Create the Skill
description: Find current session's conversation file
allowed-tools: Bash(echo:), Bash(grep:)
Steps
- Generate beacon:
echo "CLAUDE-SESSION-BEACON-$(uuidgen)" - Find file:
grep -rl "BEACON-xxx" ~/.claude/projects/
Step 2: Test the Skill
I run /find-session-convo and get prompted:
Claude wants to run: echo "CLAUDE-SESSION-BEACON-..."
Allow? [y/n/always]
Expected: Auto-approved (I declared Bash(echo:*) in frontmatter)
Actual: Prompted for permission
Step 3: Try to Debug
Check 1: Did I spell the frontmatter key correctly?
allowed-tools: Bash(echo:*) # or is it allowedTools?
No documentation confirms which is correct.
Check 2: Is my pattern syntax right?
Bash(echo:) # prefix match?
Bash(echo :) # with space?
Bash(echo*:*) # glob the command?
No feedback on what was attempted.
Check 3: Look at JSONL for clues
python3 jsonl-extract.py session.jsonl --mode tools
Output:
- Bash | echo "CLAUDE-SESSION-BEACON-..."
Shows the tool ran, but nothing about:
- Was permission prompted?
- Did user approve, or was it auto-approved?
- What patterns were checked?
- Why didn't allowed-tools apply?
Check 4: Compare with global settings
grep "Bash(" ~/.claude/settings.json
"Bash(git status:)",
"Bash(ls:)",
...
No Bash(echo:*) globally. But it's in my skill frontmatter—why didn't that work?
Step 4: Dead End
I cannot determine:
- Whether skill frontmatter allowed-tools is even being parsed
- Whether my pattern syntax is wrong
- Whether there's a precedence issue (global vs skill)
- Whether this is a bug or user error
Step 5: What Would Help
If JSONL captured permission decisions:
{
"type": "permission",
"tool": "Bash",
"command": "echo \"CLAUDE-SESSION-BEACON-...\"",
"sources_checked": [
{"source": "skill:find-session-convo", "key": "allowed-tools", "patterns": ["Bash(echo:*)"], "parsed": false, "reason": "unknown frontmatter key"}
],
"decision": "prompted_user",
"user_response": "allowed"
}
Now I immediately see: "parsed": false, "reason": "unknown frontmatter key" — the key should be allowedTools not allowed-tools.
The Gap
Without permission logging, skill authors must:
- Guess at syntax through trial and error
- File bug reports without evidence
- Wonder if it's their mistake or a bug
- Spend 30+ minutes on what should be a 30-second diagnosis