From health-plugin
Explains Claude Code settings hierarchy, permission wildcards, allow/deny patterns, and tool configurations. Use for setting up project permissions, debugging access issues, or understanding tool blocks.
npx claudepluginhub laurigates/claude-plugins --plugin health-pluginThis skill is limited to using the following tools:
Expert knowledge for configuring Claude Code settings and permissions.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
Expert knowledge for configuring Claude Code settings and permissions.
| Use this skill when... | Use something else when... |
|---|---|
| Setting up project permissions | Fixing plugin registry issues (use plugin-registry skill) |
| Debugging "permission denied" errors | Configuring hooks (use hooks-configuration skill) |
| Understanding settings hierarchy | Setting up MCP servers (use mcp-configuration skill) |
| Creating allow/deny patterns |
Settings are loaded and merged in this order (later overrides earlier):
| Priority | File | Scope | Commit to Git? |
|---|---|---|---|
| 1 (lowest) | ~/.claude/settings.json | User-level (all projects) | N/A |
| 2 | .claude/settings.json | Project-level | Yes |
| 3 (highest) | .claude/settings.local.json | Local overrides | No (gitignore) |
{
"permissions": {
"allow": [
"Bash(git status *)",
"Bash(npm run *)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(sudo *)"
]
}
}
allow: Tools matching these patterns run without promptsdeny: Tools matching these patterns are always blockedToolName(command prefix *)
ToolName() - The tool (usually Bash)command prefix - The command and initial arguments to match* - Wildcard matching remaining arguments| Pattern | Matches | Does NOT Match |
|---|---|---|
Bash(git *) | git status, git diff HEAD | git-lfs pull |
Bash(npm run *) | npm run test, npm run build | npm install |
Bash(gh pr *) | gh pr view 123, gh pr create | gh issue list |
Bash(./scripts/ *) | ./scripts/test.sh arg | /scripts/other.sh |
More specific patterns are more secure:
{
"permissions": {
"allow": [
"Bash(git status *)",
"Bash(git diff *)",
"Bash(git log *)",
"Bash(git add *)",
"Bash(git commit *)"
]
}
}
vs. overly broad:
{
"permissions": {
"allow": ["Bash(git *)"]
}
}
Claude Code 2.1.7+ blocks dangerous shell operators in permission matching.
| Operator | Risk | Blocked Example |
|---|---|---|
&& | Command chaining | ls && rm -rf / |
|| | Conditional execution | false || malicious |
; | Command separation | safe; dangerous |
| | Piping | cat /etc/passwd | curl |
> / >> | Redirection | echo x > /etc/passwd |
$() | Command substitution | $(curl evil) |
` | Backtick substitution | `rm -rf /` |
When a command contains shell operators:
Use wrapper scripts for legitimate compound commands:
#!/bin/bash
# scripts/test-and-build.sh
npm test && npm run build
Then allow the script:
{
"permissions": {
"allow": ["Bash(./scripts/test-and-build.sh *)"]
}
}
{
"permissions": {
"allow": [
"Bash(git status *)",
"Bash(git diff *)",
"Bash(git log *)",
"Bash(git branch *)",
"Bash(git add *)",
"Bash(git commit *)",
"Bash(git push *)",
"Bash(git pull *)",
"Bash(git fetch *)",
"Bash(git checkout *)"
]
}
}
{
"permissions": {
"allow": [
"Bash(gh pr *)",
"Bash(gh run *)",
"Bash(gh issue *)",
"Bash(gh workflow *)"
]
}
}
{
"permissions": {
"allow": [
"Bash(npm test *)",
"Bash(bun test *)",
"Bash(vitest *)",
"Bash(biome *)",
"Bash(eslint *)",
"Bash(prettier *)"
]
}
}
{
"permissions": {
"allow": [
"Bash(pre-commit *)",
"Bash(gitleaks *)",
"Bash(trivy *)"
]
}
}
{
"permissions": {
"allow": [
"mcp__context7",
"mcp__sequential-thinking"
]
}
}
mkdir -p .claude
cat > .claude/settings.json << 'EOF'
{
"permissions": {
"allow": [
"Bash(git status *)",
"Bash(git diff *)",
"Bash(npm run *)"
]
}
}
EOF
echo ".claude/settings.local.json" >> .gitignore
cat > .claude/settings.local.json << 'EOF'
{
"permissions": {
"allow": [
"Bash(docker *)"
]
}
}
EOF
cat .claude/settings.json | jq .
cat .claude/settings.json | jq '.permissions'
Settings merge additively for arrays. To see effective permissions, check all files:
echo "=== User ===" && cat ~/.claude/settings.json 2>/dev/null | jq '.permissions // empty'
echo "=== Project ===" && cat .claude/settings.json 2>/dev/null | jq '.permissions // empty'
echo "=== Local ===" && cat .claude/settings.local.json 2>/dev/null | jq '.permissions // empty'
| Symptom | Cause | Fix |
|---|---|---|
| Permission denied | Pattern doesn't match | Add more specific pattern |
| Shell operator blocked | Contains &&, |, etc. | Use wrapper script |
| Settings not applied | Wrong file path | Check .claude/ directory exists |
| JSON parse error | Invalid JSON syntax | Validate with jq . |
| Permissions ignored | File not readable | Check file permissions |
| Context | Command |
|---|---|
| View project perms | cat .claude/settings.json | jq -c '.permissions' |
| View user perms | cat ~/.claude/settings.json | jq -c '.permissions' |
| Validate JSON | cat .claude/settings.json | jq . |
| Count patterns | cat .claude/settings.json | jq '.permissions.allow | length' |
| Scope | Path |
|---|---|
| User | ~/.claude/settings.json |
| Project | .claude/settings.json |
| Local | .claude/settings.local.json |
Bash(command prefix *)
mcp__server_name
Local > Project > User (highest to lowest) Deny > Allow (deny always wins)