Bash Expert
Caution
Missing error handling, unquoted variables and hardcoded paths can break scripts on another machine. Bash Expert follows repository patterns and checks syntax, ShellCheck findings and error paths.
Tip
Trigger by describing the script you need. “create a setup script for my plugin”, “bash script to check homebrew services”, “install script for macOS and Linux” — the agent picks the right template, adds error handling, and validates syntax before handing back the file.
Current execution contract
Bash Expert writes and validates a bounded script itself. Missing decisions return to the main
caller; it does not spawn nested agents. Its maxTurns: 60 is an anti-loop stop, with partial
results and on-disk checkpoints for recovery.
Quick reference
| Field | Value |
|---|---|
| Model | inherit — runs on the session’s model |
| Tools | Read, Write, Edit, Glob, Grep, Bash, WebFetch |
| Triggers | ”create script”, “bash script”, “shell script” |
| Output | .sh file + bash -n + ShellCheck report |
Scope guard
Bash Expert sizes the task before writing. One bounded unit = one deliverable, roughly 5 files, roughly 10 steps.
| Situation | What the agent does |
|---|---|
| Brief fits one bounded unit | Starts immediately |
| Brief exceeds ~5 files / ~10 steps, or bundles several independent deliverables | Stops before starting, returns a split proposal: 2-N bounded subtasks, each with scope and a suggested owner |
| Scope grows mid-flight | Stops at the next clean boundary, reports done / remaining / how to split |
| Brief is missing GOAL, SCOPE, CONTEXT, CONSUMER or acceptance | States a safe assumption or returns the unresolved question to the main caller; never invents scope |
“Write the whole CI toolchain” gets a split proposal. “Write the install script for brewtools” gets a script.
Ordinary subagents cannot use AskUserQuestion; conversation forks retain the parent’s tool
pool. This ordinary delegated agent returns decision requests to its caller. Claude Code allows
depth-limited nested delegation, but Brewcode dispatches through the main conversation using
the Agent tool. Bash Expert’s own tool list contains no Agent.
On maxTurns exhaustion, Claude Code 2.1.246+ returns a partial result. The caller inspects it
and resumes with SendMessage rather than marking the work complete. After ShellCheck and a
smoke run, Bash Expert records each script’s path and status in
.claude/reports/YYYYMMDD-HHMMSS_bash-expert/report.md; it reads that checkpoint first on resume.
Tip
Got a split proposal instead of a script? That is the guard working. Pick one script from the proposal and re-send it, or spawn the subtasks as parallel agents in a single message.
When to use
- Plugin lifecycle scripts — setup, install, teardown, version-bump for brewcode/brewtools/brewui/brewdoc
- CI helper scripts — health checks, service validation, environment bootstrap
- Cross-platform portability — script must run on both macOS (ARM + Intel) and Linux without modification
- Structured output — script prints markdown tables or phase headers consumed by Claude Code skills
- Idempotent installers — safe to re-run; checks state before mutating
Examples
# Natural language triggers
"create a setup script for the brewtools plugin"
"bash script to check if all homebrew services are running"
"write an install script that works on macOS and Linux"
# Multi-mode dispatch
"write a multi-mode script with status/install/help commands"
# Plugin-aware paths resolve via ${CLAUDE_PLUGIN_ROOT}
"create a version-bump script for the plugin"
Flow
- Analyze the request
Reads existing scripts in the repo (Glob + Grep) to match conventions. Identifies target platform, required commands, and expected output format before writing a single line.
- Choose a template
Minimal (single-purpose, ≤20 lines) or full multi-mode dispatch (`CMD="${1:-help}"` +
caseblock). Plugin scripts always getSCRIPT_DIRself-location and correct plugin-root resolution. - Implement
Writes the script with
set -euo pipefail, quoted variables andtrap cleanup EXITwhere needed. Uses compact status circles and plain warning text; check/cross markers remain optional. Platform detection handles macOS/Linux differences. - Validate
Runs
bash -n script.sh(syntax check) andshellcheck script.sh(static analysis). Fixes all ShellCheck warnings before reporting. - Report
Prints a structured summary: file path, purpose, platform, and a checklist of verified properties. You get a ready-to-run script, not a draft.
Internals — patterns, templates, and platform handling
Conventions
| Pattern | Example | Use |
|---|---|---|
| Strict mode | set -euo pipefail | Every script, by default |
| Cleanup trap | trap cleanup EXIT | Temp files, locks, other resources |
| Required var | ${VAR:?error msg} | Mandatory input |
| Soft failure | cmd || echo "Warning: optional step failed" >&2 | Optional steps |
Mode detection
ARGS_LOWER=$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')
case "$ARGS_LOWER" in
status|install|upgrade|enable|disable|uninstall|purge) MODE="$ARGS_LOWER" ;;
'') MODE="default" ;; # Resolve using this setup's documented default.
*) echo "Unsupported mode: $ARGS_LOWER" >&2; exit 2 ;;
esacMinimal template
#!/bin/bash
set -euo pipefail
ARG="${1:-}"
[[ -z "$ARG" ]] && { echo "Usage: script.sh <arg>"; exit 1; }
echo "Processing: $ARG"
echo "✅ Done"macOS vs Linux divergences
| Feature | macOS | Linux |
|---|---|---|
| Brew prefix | /opt/homebrew (ARM), /usr/local (Intel) | /home/linuxbrew/.linuxbrew |
| timeout | gtimeout (coreutils) | timeout |
| sed in-place | sed -i '' | sed -i |
| readlink | greadlink -f | readlink -f |
Always derive prefix via $(brew --prefix) — never hardcode.
Structured output
| Element | Pattern |
|---|---|
| Status symbols | 🟢 success · 🔴 error/blocker · ⚪ pending/skipped · 🔵 active/updated; warnings use Warning: |
| Markdown table | echo "| Component | Status |" then the separator row, then data rows |
| Phase header | echo "=== Phase 1: Scanning ===" && echo "" |
JSON parsing fallback chain
Use a real JSON parser and pass the input file explicitly. Regex extraction is not a JSON parser,
and macOS’s system grep does not support -P.
if command -v jq >/dev/null; then
jq -r '.key' file.json
elif command -v python3 >/dev/null; then
python3 -c 'import json,sys;print(json.load(open(sys.argv[1]))["key"])' file.json
else
echo "need jq or python3" >&2; exit 1
fiPlugin path variables
| Variable | Available in |
|---|---|
${CLAUDE_PLUGIN_ROOT} | Claude expands plugin skill/agent text and hook/MCP commands; ordinary scripts must receive or derive the value |
${CLAUDE_SKILL_DIR} | Skills (string substitution) |
$PLUGIN_ROOT/skills/X/scripts/ | Scripts that explicitly define or receive PLUGIN_ROOT |
Plugin agent text receives root substitution at Agent spawn. Project-local agents receive no
plugin-root substitution. Prompt tokens do not guarantee corresponding shell environment variables;
standalone scripts locate themselves with SCRIPT_DIR and derive or accept their root.
Anti-patterns
| Avoid | Prefer |
|---|---|
[ $VAR ] | [[ -n "$VAR" ]] |
cat file | grep | grep X file |
ls | while read | find -exec or glob |
cd dir; cmd; cd - | (cd dir && cmd) |
echo $VAR | echo "$VAR" |
if [ $? -eq 0 ] | if cmd; then |
/usr/local hardcoded | $(brew --prefix) |
Delivery checklist
| # | Check |
|---|---|
| 1 | #!/bin/bash shebang |
| 2 | set -euo pipefail |
| 3 | Usage header comment |
| 4 | shellcheck clean |
| 5 | chmod +x applied |
| 6 | bash -n passes |
| 7 | help subcommand works |
| 8 | Error paths tested |
| 9 | Idempotent — safe re-run |
Return Contract
Verdict first, <=30 lines, path:line. No script bodies, no ShellCheck transcripts, no smoke-run output, no preamble — one block per script, nothing else (the format is in Flow step 5 above). Failures return the check that failed plus the offending path:line, never the full output; long logs and full ShellCheck runs go to .claude/reports/<YYYYMMDD-HHMMSS>_bash-expert/, path only.
/brewtools:agent-return-setup enforces this at ~1000 / ~2500 est-tokens when installed; the contract itself ships unconditionally.
hook-creator agent
Builds Claude Code hooks — pairs with bash-expert when a hook shells out.
GitHub source
Agent definition, system prompt, and tool configuration.
Brewcode overview
All brewcode skills and agents in one place.
Updating plugins
/brewtools:plugin-update to check and update the brewcode plugin suite in one command.
See the FAQ for details.