hermes

Write a robust shell script

Writes a portable shell script with strict mode, argument parsing, a dry-run flag, clear errors and idempotent steps. Use when automating a chore you will run more than once.

context

Shell scripts written in a hurry fail in predictable ways: an unset variable expands to an empty string and rm -rf hits the wrong directory, a failed command in a pipeline is ignored, a filename with a space splits in two, GNU-only flags break on macOS, and a second run duplicates what the first run did. The person needs a script they can run twice, read in a year, and trust in a dry run first.

task

Write a script for this goal, to run on :

  1. If the goal leaves out something that decides what gets deleted, overwritten or sent (which paths, which hosts, whether it needs root), ask up to 3 questions and stop. Otherwise state your assumptions and continue.
  2. Choose the strict-mode preamble for the shell:
  • bash: set -Eeuo pipefail, a trap that reports the failing line on ERR, and a cleanup trap on EXIT.
  • zsh: emulate -L zsh and setopt ERR_EXIT NO_UNSET PIPE_FAIL.
  • posix-sh: set -eu. Do not rely on pipefail, arrays, [[ ]], local or $'...'; check pipeline stages explicitly where failure matters.
  • powershell: a param() block with [CmdletBinding(SupportsShouldProcess)], Set-StrictMode -Version Latest and $ErrorActionPreference = 'Stop'; check $LASTEXITCODE after native commands.
  1. Parse arguments: -h/--help (usage to stdout, exit 0), long options, required values validated up front, unknown options rejected with usage on stderr and exit 2. In bash and zsh use a while/case loop so long options work; getopts handles only short ones.
  2. Add a dry-run mode (--dry-run, or -WhatIf in PowerShell) that prints every state-changing command, safely quoted, instead of running it. Route all side effects through one helper so dry run cannot miss one.
  3. Make each step idempotent: test before acting, use mkdir -p and ln -sfn, check before appending to a file, and write files to a temp file on the same filesystem and then move it into place.
  4. Fail clearly: check required tools with command -v at start-up, print errors to stderr with the script name and a fix, and use distinct non-zero exit codes for distinct failures.
  5. Check the script against ShellCheck (or PSScriptAnalyzer) rules in your head, and fix anything they would flag.
constraints
  • Quote every expansion. Use -- before user-supplied paths. Never parse ls; use find ... -print0 with while IFS= read -r -d '' (bash/zsh) or a glob loop.
  • Guard destructive commands against empty variables with ${VAR:?}, and never rm -rf a path built from unchecked input.
  • Portability for macOS and Linux: macOS ships bash 3.2 (no associative arrays, mapfile or ${var,,}) and BSD tools (sed -i '', no date -d, no grep -P, different stat flags). If target_os is any or macos, avoid these or branch on uname explicitly.
  • No secrets in the script, arguments or logs. Read them from the environment or a file with restricted permissions.
  • Never fetch remote code and execute it.
  • If the job is better done by an existing tool (rsync, a package manager, a cron entry), say so in one line, then write the script anyway.
output format

Assumptions

Bullets, or "None".

Script

One complete code block with a header comment: purpose, usage line, exit codes.

Usage

Two or three example invocations, including a dry run.

What it changes

Every file, directory, service or remote system it creates, modifies or deletes.

How to test it

Steps to try it safely: dry run first, then a throwaway directory or container.

Limitations

What it does not handle, one line each.

1 required value still a placeholder; the assistant will ask for it.

details

kind
Prompt: a task you run by name to get one finished thing back
domain
Software engineering
category
Implementation
level
Intermediate
made for
Software engineer, DevOps / platform engineer, Site reliability engineer
risk
read-only
version
v1.0.0 · incubating
reviewed
2026-10-02
works in
Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Antigravity, OpenCode, Windsurf, Zed, Continue, AGENTS.md, ChatGPT, claude.ai

Edit on GitHubReport a problem

use in

Hodios CLI
npx @hermes-hq/hodios install write-shell-script --target claude-code
Agent Skills
npx skills add hermes-hq/hodios-dist --skill write-shell-script -a claude-code
Add the Hodios marketplace (once)
claude plugin marketplace add hermes-hq/hodios-dist
Install the software-engineering plugin
claude plugin install hodios-software-engineering@hodios

The plugin brings every entry in this domain at once.

more in implementation

All of Implementation
PromptImplementation

Put a change behind a feature flag

Wraps new behaviour behind a feature flag with a safe default, a kill switch, tests for both paths and a cleanup ticket. Use when shipping a risky change incrementally.

add-feature-flag
PromptImplementation

Add rate limiting to an API

Adds rate limiting to API endpoints with a fitting algorithm, keys, per-tier limits, standard headers, 429 responses and tests. Use when protecting endpoints from abuse or overload.

add-rate-limiting
PersonaImplementation

Backend engineer

Acts as a backend engineer focused on correct data handling, clear API contracts, explicit failure modes and services that are easy to operate. Use as a builder or reviewer persona for server code.

backend-engineer
PromptImplementation

Build a REST endpoint end to end

Implements one HTTP endpoint with route, input validation, handler, error mapping and tests in the project's own framework and conventions. Use when adding an API route.

build-rest-endpoint
PromptImplementation

Build a reusable UI component

Builds a typed, accessible UI component from a description or screenshot, with loading, empty and error states and a usage example. Use when adding a component to a frontend.

build-ui-component
PromptImplementation

Build a webhook handler

Implements a webhook receiver with signature checks, replay protection, idempotent processing, fast acknowledgement, async work, retries and tests. Use when integrating Stripe, GitHub or similar.

build-webhook-handler