From claude-skills
Documents bash patterns that trigger Claude Code permission guardrails and provides alternative forms to avoid prompts.
How this skill is triggered — by the user, by Claude, or both
Slash command
/claude-skills:bash-styleThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Some bash patterns trigger Claude Code permission guardrails that **cannot be silenced by allowlist rules**. They are static-analysis flags, not pattern matches. Use the alternative forms below so commands run without prompting.
Some bash patterns trigger Claude Code permission guardrails that cannot be silenced by allowlist rules. They are static-analysis flags, not pattern matches. Use the alternative forms below so commands run without prompting.
git -C <path> over cd <path> && git ...cd <path> && git <subcommand> is flagged: "changes directory before running git, which can execute untrusted hooks." Even with both subcommands allowed, the compound form prompts.
# Bad
cd /path/to/repo && git status
# Good
git -C /path/to/repo status
Bash(git -C:*) is auto-allowed.
$(...) in shell — use multiple -m or a tempfileAny bash with $(...), backticks, or heredocs is flagged "shell syntax that cannot be statically analyzed." Prompts every time, regardless of allowlist.
# Bad — heredoc + command substitution
git commit -m "$(cat <<'EOF'
Title
Body
EOF
)"
# Good — multiple -m flags, each becomes a paragraph
git commit -m "Title" -m "Body line 1" -m "Body line 2"
Fallback (tempfile, only when the message has markdown/code blocks/quoting that breaks inside -m):
Write to /tmp/claude-commit-msgs/<repo>-<unix-timestamp>.txt (collision-proof across parallel sessions), then git commit -F <path>. Write(/tmp/claude-commit-msgs/**) is globally allowlisted. Append -<pid> if you need sub-second uniqueness. The directory is OS-managed scratch; find -mtime +7 -delete for cleanup.
( ... ) in skill !dynamic-context`` lines — extract to a scriptA skill's ## Context block runs each !`command` line at load time. A parenthesized subshell is flagged "shell operators that require approval for safety" — same unanalyzable-syntax bucket as $(...). Allowlist rules cannot silence it, so the skill errors before it even starts.
Plain && / || chains are fine (test -d openspec && echo yes || echo no passes). Only the subshell group ( ... ) (and $(...), backticks, heredocs) trips it.
<!-- Bad — subshell to combine commands with a fallback -->
- Repo status: !`(git -C ~/repo status -s && echo "---" && git -C ~/repo log --oneline -3) || echo "(missing)"`
<!-- Good — extract to a tracked, allowlistable script -->
- Repo status: !`~/.claude/bin/repo-status.sh`
The script can use whatever bash it needs internally — only the command Claude invokes (the bare script path) is analyzed. Allowlist both the literal ~/-form and the absolute form, since the matcher does not tilde-expand.
If a task needs shell logic with variables, conditionals, or loops, put it in a script file under .claude/bin/ (project) or ~/.claude/bin/ (global) and invoke that file with a simple command form. Allowlist the script path once and it never prompts again.
# Bad — inline multi-line with expansions
SESSION_ID=$(cat ~/.claude/session-topics/pid-$PPID.map 2>/dev/null)
if [ -n "$SESSION_ID" ]; then
printf '%s' "$TOPIC" > ~/.claude/session-topics/${SESSION_ID}.txt
fi
# Good — helper script
~/.claude/bin/set-session-topic.sh "$TOPIC"
When Claude Code prompts for python3 -c "<inline code>", the "don't ask again" option offers to allowlist that exact code string. Don't take it:
git diff audit trail — the "script" lives in settings.json.# Bad
jq -r '.htmlBody' thread.json | python3 -c "import sys, html, re; t=sys.stdin.read(); ..."
# Good — helper script with stable allowlistable path
jq -r '.htmlBody' thread.json | ~/.claude/bin/html-to-text.sh
Same rule for node -e, ruby -e, perl -e. If the logic doesn't fit in a one-token verb, it belongs in a script file.
The "use a helper script" pattern works only if the path is in version control. git diff is what makes the trust auditable. Otherwise the rule passes regardless of script content — Claude can rewrite the script and the trust still binds.
/tmp/, working dirs)./trust-action skill enforces this).~/ for audit clarity.These verbs should always prompt — keep them in ask, never silence them:
rm — irreversible deletioncurl, wget — network egress; exfiltration vectorchown — ownership changesdd — block-level operationssudo — privilege escalationnc, ncat — arbitrary network socketsIf you want to silence one: either script the operation, version-control it, and allowlist the script path; or accept the per-call prompt.
git -C is broadly allowedBash(git -C:*) is broadly allowed because it's the workaround for the cd <repo> && git ... flag. So git -C <path> push --force and git -C <path> reset --hard are not caught by the git push --force ask rules. Live with this — if you routinely run destructive git -C operations, add specific patterns to ask.
Each unnecessary permission prompt breaks flow. These patterns eliminate prompts that don't carry information.
But: trivial-prompt fatigue makes you reflexively approve non-trivial prompts. A user worn down by 20 ls approvals hits Y on rm -rf <project> without reading it. These patterns are for eliminating prompts that genuinely don't carry information — not workarounds to silence prompts you should still review.
Creates bite-sized, testable implementation plans from specs or requirements, with file structure and task decomposition. Activates before coding multi-step tasks.
npx claudepluginhub anutron/ai