| name | bash-script |
| description | Generates production Bash scripts with a visual timeline UI, state-based interactive prompts, and strict error handling. Use when asked to write a bash script, create a shell script, or build a DevOps or CLI script with framed terminal output. |
Bash script
Generate production-ready Bash scripts for DevOps and CLI workflows. Enforce strict formatting with a visual timeline UI and state-based interactivity.
Load ${CLAUDE_SKILL_DIR}/references/patterns.md for the timeline lifecycle, logging, interactive prompt, help-screen, and full-script code templates. ${CLAUDE_SKILL_DIR} expands to this skill's own directory, so the path resolves from any project. Copy those definitions verbatim, keeping only the colors and functions the script actually uses.
Script setup
- Start with
#!/usr/bin/env bash, set -e, and set -o pipefail.
- Bash 4+ features (namerefs, associative arrays) are allowed.
- Implement a visual help screen via
show_help if the script accepts arguments.
- Do not rely on unset variables. Use
${VAR:-default}.
Visual timeline
- Maintain a vertical timeline (
│) from ┌ to └ throughout all output.
- Write all frame output (
┌, │, ├, └, log lines, prompts) to stderr via >&2. Write data (JSON, lists, piped values) to stdout. --help is the exception and prints to stdout.
- Open the timeline once at the start of
main() via open_timeline "Title", before any logic, prompts, or checks.
- Close the timeline via
trap close_timeline EXIT, registered immediately after open_timeline.
- On success, disable with
trap - EXIT, then print └\n and the success message manually.
- On cancellation and error, never print
└ manually. The trap owns those exits.
- Use state transitions for interactive prompts:
◆ active, ◇ inactive.
- Do not add diamonds (
◆ or ◇) to non-interactive log functions.
- On cancellation, show
◇ ... Cancelled, exit 1, and make no log_error call. Both ask() and select_option() handle escape cancellation identically.
- Guard interactive prompts against non-TTY stdin with
[ -t 0 ] and render a framed log_error when a TTY is required but absent. Never let read block silently on piped input.
Code style
- Decompose by responsibility: each function does one thing,
main() orchestrates only.
- Name functions verb-first:
validate_input, deploy_service, install_dependencies.
- Do not use global variables except exports from
ask().
- Do not define unused color variables.
- Do not include comments except the shebang line.
- Use sentence case for section headers and log messages. Proper nouns and product names retain their casing.
- Quote variables inside parameter expansions:
"${file#"$dir"/}" not "${file#$dir/}".
- Quote variables in test brackets:
[ "$i" -eq "$cur" ] not [ $i -eq $cur ].
- Guard commands that return non-zero on valid empty results:
grep ... || true, diff ... || true.
Output hygiene
- Show external tool output by default (git, npm, gh).
- Include context in error messages:
log_error "npm install failed: check package.json".
- Do not echo command names before running them. Output speaks for itself.
- Do not log "Starting..." and "Finished..." around every action.
- Do not log intermediate variable assignments.
- Use
log_add for item writes (files created, entries added, keys written).
- Use
log_info for status confirmations only ("up to date", "check passed").
- Use
log_warn for drift, skipped states, or recoverable issues.
- Never use
log_info for file or entry writes.
Icon usage
Interactive prompts only:
◆ (green): active user input required
◇ (grey): completed input, transition via \r\033[K to rewrite the ◆ line in place
❯ (green): selected option in a menu
- plain text (grey): unselected option in a menu
Non-interactive logs:
├ section branch (log_step)
✓ (green): success (log_info)
! (yellow): warning (log_warn)
✗ (red): error (log_error)
+ (green): add item (log_add)
- (red): remove item (log_rem)
Validation
Before responding, verify:
- File starts with
#!/usr/bin/env bash, set -e, set -o pipefail and uses exactly 2 spaces for indentation.
- Timeline opens via
open_timeline "Title", which writes ┌ and │ Title to stderr.
- Timeline closes via
trap close_timeline EXIT registered immediately after open_timeline. Success paths use trap - EXIT then manual └\n (to stderr) then the success message. Cancellation and error paths never print └ manually.
open_timeline and close_timeline are defined and write to stderr via >&2.
- All frame output (
│, ├, └, log lines, interactive prompts) writes to stderr via >&2. Stdout carries data only. --help is the exception.
- Interactive prompts (
ask, select_option) guard with [ -t 0 ] and call log_error if stdin is not a TTY.
- The timeline (
│) appears in all log functions, and interactive prompts use ◆ → ◇ transitions.
ask() uses \r\033[K to rewrite the ◆ line in place, with no \033[1A cursor-up sequences.
ask() drains trailing escape bytes with read -rsn2 -t 0.001 _ || true before cancelling.
- All log messages use sentence case (proper nouns and product names exempt).
- Only defined color variables are used in the script.
- Cancellation shows a single
◇ ... Cancelled line without a subsequent log_error.
- Escape key in
ask() triggers ◇ ... Cancelled and exits, consistent with select_option().
- Functions follow single responsibility: each does one thing,
main() delegates to helpers.
- Logging is concise: no "Starting.../Finished..." bloat, no intermediate variable logging.
log_add is used for all file, entry, and key writes.
log_info is used for status confirmations only, not writes.
- Every section header uses
log_step, including the first one after the title block.
- File ends with exactly one empty line.