harness/plugins/common/claude/skills/shell-modify/SKILL.md
Safe shell script modifications — pre-edit analysis of strict modes, resource cleanup patterns, portability checks, and shellcheck validation. Use for any non-trivial shell script edits (.sh, .bash). Skip for one-line fixes.
npx skillsauth add popoffvg/dotfiles shell-modifyInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Systematic workflow for safely modifying shell scripts. Prevents the most common agent mistakes: missing cleanup traps, violating strict mode contracts, and non-portable constructs.
For patterns, idioms, and a full script template, see references/best-practices.md.
Use when:
set -e / set -eu / set -o pipefailSkip when:
plan front matter, friction logs, YAML/Markdown snippets) with no explicit shell script edit requestOnly apply this skill when the task clearly targets .sh/.bash code or shell behavior changes.
If text includes other skill docs/front matter (for example name: plan) treat it as context noise unless the user also names a shell script path or asks for shell script modifications.
Before making any changes, confirm the target script path. If no script path is provided, ask for it instead of inferring from unrelated pasted text. Then read the ENTIRE script and fill this checklist:
#!/bin/bash, #!/bin/sh, #!/usr/bin/env bash? This determines which features are safe (see references/best-practices.md § Portability).set -e (errexit), set -u (nounset), set -o pipefail? Note the exact combination and line number.trap commands? What signals? What cleanup? New traps must compose with existing ones, not replace them.|| true, || :, if ! cmd; then, explicit return codes? Match the style.set -e (errexit):
trap ... EXIT insteadmktemp without trap = leaked temp files on any failureif conditions, ||, && are exempt from errexitlocal var=$(cmd) masks the exit code of cmd — split into two lines: local var; var=$(cmd)set -u (nounset):
${var:-default} for optional variables (see references/best-practices.md § Default Values)${arr[@]} fails if array is empty in bash < 4.4 — use ${arr[@]+"${arr[@]}"}set -o pipefail:
cmd | grep pattern fails if cmd fails, even if grep succeedscmd | grep pattern || true if grep finding nothing (rc=1) is expectedIf your change creates resources, verify cleanup:
| Resource | Cleanup pattern |
|----------|----------------|
| mktemp / mktemp -d | trap 'rm -rf "$tmpdir"' EXIT immediately after creation |
| Background process (&) | trap 'kill "$pid" 2>/dev/null' EXIT |
| flock / lock files | trap 'rm -f "$lockfile"' EXIT |
| Mounted filesystems | trap 'umount "$mnt"' EXIT |
| Changed directory (cd) | Use subshell ( cd dir && ... ) or pushd/popd |
| Modified env/shell opts | Save and restore: old_opts=$(set +o); ... ; eval "$old_opts" |
Rule: one trap handler per script. If a trap already exists, extend it — don't add a second trap ... EXIT (it replaces the first). Pattern:
cleanup() {
rm -rf "$tmpdir"
# add more cleanup here
}
trap cleanup EXIT
See references/best-practices.md § Trap for Cleanup and § Temporary Files for full patterns.
jq, curl, realpath exist. Use command -v to guard (see references/best-practices.md § Check Command Existence).bash 3.x (macOS default) lacks associative arrays, readarray, ${var,,}."$var", not $var — especially in file paths and command arguments"$(cmd)", not $(cmd)references/best-practices.md § Complete Script Template# WRONG — cleanup never runs if cmd fails
tmpfile=$(mktemp)
cmd > "$tmpfile"
rm -f "$tmpfile"
# RIGHT — trap ensures cleanup on any exit
tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXIT
cmd > "$tmpfile"
# WRONG — local masks exit code
local result=$(failing_cmd)
# RIGHT — split declaration and assignment
local result
result=$(failing_cmd)
# WRONG — grep returns 1 when no match, pipefail kills the pipeline
data | grep "pattern" | while read -r line; do ...
# RIGHT — handle grep's no-match exit code
data | { grep "pattern" || true; } | while read -r line; do ...
For loops, arrays, conditionals, and argument parsing patterns, see references/best-practices.md.
After all edits, before staging:
# Run shellcheck — errors are mandatory fixes
shellcheck -S error "$script_path"
# Also run with warnings for advisory checks
shellcheck "$script_path"
Shellcheck is not optional. If shellcheck is not installed, note it in the worklog and flag for user attention.
| Code | Issue | Severity |
|------|-------|----------|
| SC2086 | Unquoted variable | Error — almost always a bug |
| SC2046 | Unquoted command substitution | Error |
| SC2155 | local var=$(cmd) masks return code | Warning — error in errexit scripts |
| SC2164 | cd without || exit | Warning — error in errexit scripts |
| SC2034 | Unused variable | Info |
| SC2029 | Expanding on client side in ssh | Warning |
bash -n "$script_path" # syntax check only, no execution
Shell tests vary by project. Check for:
bats test files (*.bats)test-*.sh, *_test.sh)If no test framework exists and the change is non-trivial, suggest adding a test in the worklog.
Eval checklist:
Test inputs:
Can change: pre-edit analysis steps, portability checks, shellcheck integration, best-practices references Cannot change: strict mode preservation, cleanup trap requirement, skip-when criteria for trivial fixes Min sessions before eval: 5 Runs per experiment: 3
testing
Use when the user asks to create test sets, enumerate scenarios, generate edge cases, or draft a coverage matrix before implementation.
testing
Use when the user asks to review, audit, score, or validate test sets for missed cases before execution or merge.
tools
Test harness plugins in isolation using tmux panes. Runs MCP servers, unit tests, typecheck, and Claude plugin loading. Use when user says "test plugin", "check plugin", "run plugin tests", "validate plugin", or names a specific plugin to test.
development
Guide for designing integration and e2e tests using BDD (Behavior-Driven Development) methodology with Cucumber-style Given/When/Then scenarios. Use when writing or reviewing tests for any service, API, or component. Language-agnostic — covers scenario structure, step notation, assertion principles, async patterns, and common anti-patterns.