agentleFS
buildosaurusbuildosaurus/scripteratops/skills/shell-script/SKILL.md

shell-script

Skill0 starsChanged 3 months agoMITView on GitHub

Write safe, testable Bash command-line tools — one sourceable script with pure-logic/side-effect separation (external commands wrapped so tests can stub them), strict mode, colorized logging, and a shellcheck/shfmt/bats gate. Use as soon as the user wants a shell/bash script, CLI utility, automation, or "glue" code (orchestrating git, curl, file manipulation, pipelines) in shell, even for a quick one. Also triggers on adding tests to an existing shell script, making one safe/portable/testable, or hardening a fragile bash script.

  • Deletes or force-pushes
  • Installs packages

2 other projects use a version of this.

Adopted by

Most recently taken up by

  • Other versions2 projects use a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 2

    1. kevinaimonster/skill-hub★ 4same name
    2. Jinbae10/skills★ 0same name

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

  • In this repository7 other files it gives its agents.

The file

We split this skill into the rules it gives an agent

1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    No generator; use help2man from the script's --help:

    line 127

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Shell script

Standard for all shell command-line tools: one Bash script, written under strict mode, whose functions are pure/testable, with external commands isolated behind wrapper functions, verified by shellcheck + shfmt + bats. The counterpart to the python-script, swift-script, kotlin-script, typescript-script, go-script, and rust-script skills — but a deliberately different shape: shell has no type system, so shellcheck is the type gate, "boundaries" are command-wrapping functions (stubbed in tests, not interfaces), and the CLI parser is hand-rolled.

Three decisions are already settled. Don't second-guess them, apply them:

  1. Target → Bash 4+ (#!/usr/bin/env bash, set -euo pipefail). Not POSIX sh: arrays, [[ ]], local, and mapfile are worth it. macOS ships bash 3.2 — brew install bash.
  2. Tests → bats-core, sourcing the script (which is written to be sourceable) and stubbing boundary functions.
  3. Lint/format → shellcheck (the real quality gate) + shfmt.

This file carries the decisions; the code is a complete, gate-passing script in assets/template/. Copy and adapt it — don't retype it from here. Then delete what your tool doesn't earn.

1. Layout

tool/
├── tool               # the script (chmod +x) — all sections in one sourceable file
├── test/
│   ├── logic.bats     # transform + log level (pure)
│   ├── color.bats     # should_color
│   ├── config.bats    # precedence helpers
│   └── work.bats      # do_work with stubbed boundaries
├── .editorconfig      # shfmt: 2-space
└── README.md

One file, sectioned like the other skills (pure logic → color/log → boundaries → config → orchestration → CLI). It is sourceable: the last line guards main so bats can source the script to get the functions without executing it.

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
  main "$@"
fi

set -euo pipefail lives inside main, not at top level — sourcing the file for tests must not flip shell options in the bats harness.

2. Structure

The structure makes tests possible without a type system: pure functions are called directly; every external command sits behind a wrapper function that a test can redefine (bash's loose analog of dependency injection).

  • Boundaries are functions — run_cmd (subprocess), http_fetch (network), env_get (environment). A test redefines them: run_cmd() { printf 'a\nb\n'; }.
  • Pure logic (transform) — reads a string, echoes lines; no I/O. Tested with run transform … ; [ "${lines[0]}" = … ].
  • Config precedence is pure helpers: first_nonempty (CLI > env > file > default) and config_from_file (a key=value file parsed without source — never execute a config file).
  • Orchestration (do_work) assembles wrappers + pure logic: data positional, behaviour flags after, items to stdout, logs to stderr.
  • The CLI is thin: a while/case loop (no arg-parsing library exists for shell), then wire logging + config and call do_work.

2.1 Strict mode & safety

set -euo pipefail (errexit, nounset, pipefail) is the baseline: unset variables and failing commands abort instead of silently continuing. Quote every expansion ("$var", "$@") — shellcheck enforces this. Default optional vars with "${VAR-}" so nounset doesn't trip.

2.2 Subprocess boundary

run_cmd <timeout_secs> <cmd...>:

  • Never a shell string. Pass the command as separate arguments — no eval, no sh -c, so no injection.
  • Timeout via GNU timeout/gtimeout when present (a timeout exits 124). Note macOS has no timeout unless brew install coreutils — the wrapper degrades to no hard timeout and this is the honest portability caveat.

2.3 Config and precedence

Precedence (highest wins): CLI flags > environment > config file > defaults, resolved by first_nonempty. The config file is key=value, parsed line-by-line (comments stripped) — never source it (that executes arbitrary code). Secrets get no CLI flag (argv is world-readable via ps); a token comes from the environment or the file. Shell has no JSON — use key=value, or require jq only if real JSON is unavoidable.

2.4 HTTP requests

http_fetch <url> wraps curl -fsS --max-time N: -f makes a non-2xx status a non-zero exit, --max-time bounds it. Tests redefine the function to fake it.

2.5 Colorized logging

Logging goes to stderr (stdout is for program data). should_color is a pure decision (--color > NO_COLOR/FORCE_COLOR > TTY via [[ -t 2 ]]). Verbosity: count -v/-q, map the net with log_level_for_verbosity, gate messages against a LOG_THRESHOLD global.

2.6 Robustness & signals

  • Bound external calls with a timeout where a hang is possible (§2.2, §2.4).
  • Signals: bash gives the conventional exit codes for free — SIGINT → 130, SIGTERM → 143, plus 126 (not executable) / 127 (command not found). Add a trap only for real cleanup (e.g. removing a temp dir): trap 'rm -rf "$tmp"' EXIT.
  • Preflight external tools for glue scripts: command -v jq >/dev/null || { log_error "jq required"; exit 1; }.

2.7 Subcommands (if needed)

Dispatch on the first positional in the case loop to per-subcommand functions (cmd_build, cmd_deploy). There is no framework — keep it a small dispatch.

2.8 Man page (optional)

No generator; use help2man from the script's --help:

help2man --no-info --name "tool" "./tool" > tool.1
sudo install -m 644 tool.1 /usr/local/share/man/man1/tool.1

2.9 Distribution

The README carries the environment set-up, not just the gates: a Prerequisites section with per-platform install of bash 4+ and the dev tools (shellcheck/shfmt/bats), the external commands the script calls (curl, timeout), and the Windows note (Git Bash / WSL — bash isn't native). There's no dependency manifest, so no --dependencies flag — the prerequisites section is the dependency list.

The script is the deliverable — install -m 755 tool ~/.local/bin/tool. No compile, no runtime beyond bash. Keep the #!/usr/bin/env bash shebang so it finds a modern bash on PATH.

3. CLI contract

Hand-rolled (no library):

  • -h/--help prints usage; -V/--version prints the version — both wired by hand.
  • Verbosity: count -v/-q occurrences; map the net to a level. (Bundled -vv is not parsed by the hand-rolled loop — repeat -v -v, or add bundling if you need it.)
  • --color auto|always|never; --dry-run short-circuits before output.
  • Stream discipline: data → stdout (printf/echo), logs → stderr.

Exit codes

CodeMeaning
0Success (also --help/--version)
1Runtime error (a failed command under set -e, or explicit)
2Usage error (unknown flag, missing argument)
124A subprocess exceeded its timeout (GNU timeout)
126 / 127Command not executable / not found (bash)
130 / 143SIGINT / SIGTERM (bash defaults)

4. Tests with bats

Each test/*.bats sources the script in setup() and calls functions with run.

  • Never test subprocess or network for real — redefine the wrapper function (run_cmd/http_fetch) in the test before run do_work ….
  • Pure functions (transform, should_color, first_nonempty, log_level_for_verbosity) are tested by direct run + [ "$output" = … ] / ${lines[@]}.
  • Sourcing is safe because set -euo pipefail is inside main and main is guarded — each @test runs in a fresh subshell, so readonly VERSION is set once per shell.

5. Quality gates

shellcheck tool          # lint / static analysis — the de-facto type gate
shfmt -d tool            # formatting (2-space via .editorconfig)
bash -n tool             # parse-only syntax check
bats test/               # tests
  • shellcheck is the highest-value gate — it catches the classic shell footguns (unquoted expansions, unset vars, non-portable constructs). Treat every finding as an error; suppress only with a justified # shellcheck disable=SCxxxx.
  • Indentation is 2-space (.editorconfig; shfmt -w tool fixes layout). shfmt cannot format .bats files (the @test syntax isn't plain bash), so lint those with bats itself.

Summary of what to produce

Faced with a shell tool request, produce:

  1. One Bash script copied from assets/template/: strict mode inside a guarded main, pure functions, command wrappers as boundaries, key=value config with precedence, stderr logging, the §3 CLI + exit codes, and the robustness rules the job earns (shelling out earns the timeout + preflight; config/secrets earn precedence + no-CLI-flag). Delete what it doesn't earn.
  2. test/*.bats covering pure functions, config precedence, and orchestration (stubbing the boundary wrappers).
  3. In one line: how to run it (./tool <args>), and that the gates — shellcheck + shfmt + bats — are green (needs bash 4+ and the tools).

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.