From agenticat
Defensive Bash for scripts that mutate live systems. Use when writing, hardening, or reviewing scripts (deploy/apply, systemd oneshot+timer units, firewall/sshd/sudoers edits, ssh remote-exec).
How this skill is triggered — by the user, by Claude, or both
Slash command
/agenticat:bash-defensive-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Defensive idioms for Bash that changes real systems. Pick each guard by intent and keep its reason next to the code.
Defensive idioms for Bash that changes real systems. Pick each guard by intent and keep its reason next to the code.
set Flags by IntentComment the intent beside the flags so nobody cargo-cults the wrong one.
set -euo pipefail # orchestration/deploy: any failed step aborts the run
set -uo pipefail # drop -e: steps may fail without aborting (optional installs, fail-open heartbeats)
set -u # long-running/interactive loop: -e would kill on a benign non-zero
A thin wrapper sets no flags and ends with exec so the wrapped tool's exit code reaches the supervisor verbatim:
exec tool "$@" # no set flags; propagate the real exit code to systemd
End-of-options guard against argument injection:
cp -- "$src" "$dst"
Validate at the boundary; parse into a checked value so no call site re-tests a raw string.
HOST="${1:?host ip required}" # required positional arg
: "${DB_URL:?set DB_URL in $CONF}" # required env/config var
case "$MIN" in *[!0-9]*|'') echo "MIN must be integer" >&2; exit 2;; esac
safe="${HOST//[^0-9A-Za-z]/_}" # sanitize untrusted string used as a filename
Every mktemp gets an EXIT trap. Manual per-path rm leaks the moment someone adds a new early exit.
TMP="$(mktemp)"; trap 'rm -f "$TMP"' EXIT
Check-then-act so a re-run is a no-op.
have(){ docker inspect "$1" >/dev/null 2>&1; }
have web || docker run --name web "$image"
iptables -C FORWARD -i eth0 -j ACCEPT 2>/dev/null || iptables -I FORWARD -i eth0 -j ACCEPT
Validate with the format's own checker before overwriting a live file:
tmp="$(mktemp)"; trap 'rm -f "$tmp"' EXIT
build_config > "$tmp"
visudo -cf "$tmp" || { echo "sudoers invalid" >&2; exit 1; }
nft -c -f "$tmp" || { echo "nftables invalid" >&2; exit 1; }
install -m 0440 "$tmp" /etc/sudoers.d/app # not-world-writable is what sudo enforces; 0440 is just the safe conventional mode
Arm an auto-rollback before a connectivity-affecting change; cancel it only after a brand-new connection proves access survived:
systemd-run --on-active=300 --unit=rollback bash -c 'nft -f /etc/nftables.prev'
apply_firewall_change
ssh -o BatchMode=yes -o ConnectTimeout=8 "$HOST" true \
&& systemctl stop rollback.timer \
|| echo "no fresh connection; letting rollback fire" >&2
Prove a new connection rather than trusting the session you already hold. Defer any session-dropping restart to the last step:
edit_sshd_config # do not restart yet
# ... every other step ...
systemctl restart sshd # LAST: a dropped session cannot skip earlier steps
wait_for(){ local d=$((SECONDS+${2:-30})); until "$1"; do ((SECONDS<d)) || return 1; sleep 1; done; }
up(){ timeout 1 bash -c "exec 3<>/dev/tcp/$HOST/22"; }
wait_for up 60 || exit 1
curl -fsS -m 10 "$HEARTBEAT" || true # fail-open best-effort op
systemctl is-active --quiet svc || exit 0 # health-gated: stay silent so a watchdog fires
lock=/run/app.lock # debounce via mtime
[ -f "$lock" ] && [ $(( $(date +%s) - $(stat -c %Y "$lock") )) -lt 300 ] && exit 0
touch "$lock"
exec >>"$LOG" 2>&1 # capture all output of a long run
Single-quote the heredoc delimiter so the local shell expands nothing before it reaches the remote:
ssh "$HOST" bash -s <<'REMOTE'
set -euo pipefail
systemctl restart app
REMOTE
curl | sh floor is --proto '=https' --tlsv1.2; checksum-pin the payload when you can.npx claudepluginhub uwuclxdy/agenticat --plugin skillsGuides collaborative design exploration before implementation: explores context, asks clarifying questions, proposes approaches, and writes a design doc for user approval.
Creates structured, bite-sized implementation plans from specs or requirements before writing code. Useful for breaking down multi-step tasks into testable steps with file structure and task boundaries.
Resolves in-progress git merge or rebase conflicts by analyzing history, understanding intent, and preserving both changes where possible. Runs automated checks after resolution.