writing-shell · git:20260928.3135f26 · 2026-09-28 · sha256 302e8c05023be1bc
writing-shell git:20260928.3135f26B
Immutable. This exact content never changes and is served at /api/v1/blob/302e8c05023be1bc.
--- description: Idiomatic shell development for POSIX sh, Bash, Zsh, Fish, hooks, CI shell steps, and scriptable CLI glue. Use when writing or changing `.sh`, `.bash`, `.zsh`, `.fish`, `.bats`, shell functions, shell pipelines, CI `run:` shell bodies, or command-runner recipes. Emphasizes portability, quoting, safe filesystem/process handling, non-TUI CLI tools, ShellCheck, shfmt, Bats, and ShellSpec. NOT for Python, Rust, TypeScript, Go, web code, or GitHub Actions workflow/job/permissions semantics; use operating-infra. name: writing-shell --- # Shell Development In GitHub Actions, this skill owns the shell inside `run:` blocks; operating-infra owns the workflow YAML around it (jobs, permissions, actions, secrets, caching). Mixed changes use both. ## Shell Choice - Follow the existing shebang and tooling. New portable scripts: POSIX `sh` for simple logic; Bash when you need arrays, `pipefail`, `[[ ]]`, or regex. - Zsh and Fish only for existing files, interactive config, or explicit requests. - Never rely on the agent's own shell. Name the shell in the shebang, and invoke it explicitly in tests. - Shell is glue. Move data modeling, business logic, or CPU-heavy work to the project's real language. ## Safety and Portability - Bash: `set -euo pipefail`, knowing it does not fire inside conditionals or `&&`/`||` lists. POSIX sh: `set -eu` and explicit pipeline checks. - Build commands with arrays, never strings plus `eval`. Put `--` before user-controlled operands. - NUL- or newline-safe loops for filenames; never parse `ls` or use `for x in $(cmd)`. - `mktemp` plus a cleanup `trap` for temp files, locks, and partial outputs. - No `curl | sh`: download, verify, then run. - Destructive actions list their targets first and require confirmation unless the script runs non-interactively with explicit inputs. - macOS/BSD vs GNU: avoid `sed -i`, `date -d`, `readlink -f`, and GNU-only `grep` flags unless the dependency is documented. Prefer `printf` to `echo`. - Every `shellcheck disable` carries a short reason. ## CLI Tools - Use non-interactive, pipe-friendly tools with stable stdout and exit codes: no TUI, pager, color, or prompts when output is consumed. - Parse structured data with `jq`, `yq`, `mlr`, or `dasel`, not `grep`/`sed` scraping. Search with `rg` and `fd`. - Preview replacements before applying them (`sd -p`, `rg --replace`). - Check for non-standard tools and fail with a clear message. Never install them silently. - Look up exact flags with looking-up-docs instead of guessing. ## References - [testing.md](references/testing.md): gate commands (shfmt, ShellCheck, checkbashisms, Bats, ShellSpec) and test design; read when adding tests or choosing checks. Done when the relevant build/test/lint checks pass on what you changed, or you name each check that did not run and why.