shell-tdd
Shell-based TDD test harness patterns — pass/fail counters, common assertions, set -e gotchas, and exit code conventions for bash test scripts
Best use case
shell-tdd is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Shell-based TDD test harness patterns — pass/fail counters, common assertions, set -e gotchas, and exit code conventions for bash test scripts
Teams using shell-tdd should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/shell-tdd/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How shell-tdd Compares
| Feature / Agent | shell-tdd | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Shell-based TDD test harness patterns — pass/fail counters, common assertions, set -e gotchas, and exit code conventions for bash test scripts
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Shell TDD Test Harness
## Quick Start
Shell TDD scripts follow a simple pattern: counter functions, if-block assertions,
and `exit $FAIL` for CI integration. The critical rule is **never use `(( var++ ))`
with `set -e`** — use `var=$((var + 1))` instead.
## Template
```bash
#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(git -C "$(dirname "$0")" rev-parse --show-toplevel)"
PASS=0
FAIL=0
pass() { PASS=$((PASS + 1)); echo " PASS: $1"; }
fail() { FAIL=$((FAIL + 1)); echo " FAIL: $1"; }
echo "=== Suite: <name> ==="
# --- AC1: <description> ---
echo ""
echo "--- AC1: <description> ---"
if <check>; then
pass "<what passed>"
else
fail "<what failed>"
fi
# --- Summary ---
echo ""
echo "=== Results: $PASS passed, $FAIL failed ==="
exit $FAIL
```
## Common Assertions
```bash
# File exists
if [ -f "$path" ]; then pass "file exists"; else fail "file missing"; fi
# Directory exists
if [ -d "$path" ]; then pass "dir exists"; else fail "dir missing"; fi
# File contains text
if grep -q "pattern" "$file"; then pass "pattern found"; else fail "pattern missing"; fi
# Git ignores file
if git check-ignore -q "$path"; then pass "ignored"; else fail "not ignored"; fi
# Command succeeds
if command_here; then pass "command ok"; else fail "command failed"; fi
# Command output matches expected value
if [ "$(command)" = "expected" ]; then pass "output matches"; else fail "output mismatch"; fi
# File is not empty
if [ -s "$path" ]; then pass "file has content"; else fail "file empty"; fi
# String variable is non-empty
if [ -n "$var" ]; then pass "var set"; else fail "var empty"; fi
```
## Gotchas
### 1. Counter increment with `set -e` (CRITICAL)
```bash
# BROKEN — exits script when PASS=0 because (( 0 )) returns exit code 1
(( PASS++ ))
# CORRECT — arithmetic substitution always succeeds
PASS=$((PASS + 1))
```
Why: `(( expr ))` returns exit code 1 when the expression evaluates to 0.
When `PASS=0`, `(( PASS++ ))` evaluates `0` (the pre-increment value), which is
falsy, so `set -e` kills the script on the very first pass.
### 2. `set -e` and conditionals
`set -e` does **not** exit when a failing command is inside an `if` condition,
`while` condition, or the left side of `&&`/`||`. Always wrap checks that might
fail in `if` blocks rather than running them bare.
```bash
# BROKEN — exits script if grep finds nothing
grep -q "pattern" "$file"
# CORRECT — if block suppresses set -e for the condition
if grep -q "pattern" "$file"; then pass "found"; else fail "missing"; fi
```
### 3. `set -u` and optional variables
Use `${var:-default}` for variables that may be unset. Bare `$var` with `set -u`
exits immediately if `var` is not defined.
### 4. `set -o pipefail` and pipes
The pipe exit code is the rightmost non-zero exit. A `grep | head` where grep
finds nothing will fail the whole pipe. Wrap in `if` when failure is expected.
## Exit Code Convention
`exit $FAIL` — zero means all tests passed, non-zero equals the failure count.
This integrates directly with CI: any non-zero exit fails the build.Related Skills
github-actions-trigger-and-shell-gotchas
Prevent false verification gaps in GitHub Actions by checking push path filters, shell compatibility, and shared CI environment failures before concluding a workflow fix worked or failed.
shell-script-hardening-patterns
Harden Bash automation scripts with TDD-first static and behavioral checks, safe Python invocation via uv, locking, persistent state, and review-driven correction loops.
cli-productivity-8-shell-aliases-and-functions
Sub-skill of cli-productivity: 8. Shell Aliases and Functions.
cli-productivity-1-complete-shell-configuration
Sub-skill of cli-productivity: 1. Complete Shell Configuration (+1).
test-oversized-skill
A test fixture skill that exceeds 200 lines with multiple H2/H3 sections for split testing.
interactive-report-generator
Generate interactive HTML reports with Plotly visualizations from data analysis results. Supports dashboards, charts, and professional styling.
data-validation-reporter
Generate interactive validation reports with quality scoring, missing data analysis, and type checking. Combines Pandas validation, Plotly visualization, and YAML configuration for comprehensive data quality reporting.
agent-os-framework
Generate standardized .agent-os directory structure with product documentation, mission, tech-stack, roadmap, and decision records. Enables AI-native workflows.
OrcaFlex Specialist Skill
```yaml
repo-ecosystem-hygiene
Interpret the daily read-only repo ecosystem hygiene audit and route remediation through approved workflows.
domain-knowledge-sweep
Systematic multi-source research of an engineering domain. Spawns parent issue → 6 research subissues (Standards, Academic, Industry, LinkedIn-marketing, Code-audit, Synthesis) → gap implementation subissues. Replaces LinkedIn-only extraction with defensible comprehensive sourcing.
subagent-write-verification
Independently verify subagent-claimed file writes with filesystem and git checks before treating the artifact as real, before committing it, and before referencing the path in downstream prompts.