| name | batch-claude-loop |
| description | Orchestrate batch headless Claude Code calls - loop over inputs with clean context per iteration, collect structured results |
| allowed-tools | Bash, Read, Write, Edit |
| user-invocable | true |
| argument-hint | [description of what to process] |
| metadata | {"version":"1.0","created":"2026-03-26T00:00:00.000Z","author":"Ability.ai","changelog":["1.0: Initial version — orchestrate batch headless Claude Code calls, looping over inputs with clean context per iteration and structured result collection."]} |
Batch Claude Loop
ℹ️ First, set expectations: before anything else, print one short line with this skill's version and its most recent change — the top entry of metadata.changelog above — e.g. batch-claude-loop vX.Y — recent: <summary>. Then proceed.
Purpose
Orchestrate multiple headless Claude Code invocations in a loop — each with clean context, different input, and structured output collection. This skill builds and executes the batch script; individual worker calls run independently via claude -p.
Process
Step 1: Clarify the Batch Job
Determine from the user:
- What to iterate over — file list, array of prompts, lines from a file, API results, etc.
- What each worker should do — the prompt template with a
{{ITEM}} placeholder
- Which tools workers need — for
--allowedTools (e.g., Read,Bash,Edit)
- Worker directory — which directory should workers run in (default: current directory)
- Parallelism — serial (default) or parallel (with max concurrency)
- Output format — text, json, or stream-json
Step 2: Configure Worker Flags
Select flags for each headless invocation:
claude -p "${PROMPT}"
--output-format json
--allowedTools "Read,Bash"
--max-turns 5
--bare
--max-budget-usd 1.00
--no-session-persistence
--append-system-prompt "..."
Decision: --bare vs normal mode
- Use
--bare when workers are self-contained (just a prompt + tools)
- Omit
--bare when workers need project context (CLAUDE.md, skills, MCP servers)
Step 3: Generate the Batch Script
Create an output directory for this batch run:
BATCH_DIR="batch_$(date +%Y-%m-%d_%H%M%S)"
mkdir -p "$BATCH_DIR"
Serial execution template:
#!/bin/bash
set -euo pipefail
OUTPUT_DIR="{{BATCH_DIR}}"
RESULTS_FILE="$OUTPUT_DIR/results.jsonl"
LOG_FILE="$OUTPUT_DIR/batch.log"
ITEMS=({{ITEMS_ARRAY}})
echo "Starting batch: ${#ITEMS[@]} items" | tee "$LOG_FILE"
for i in "${!ITEMS[@]}"; do
item="${ITEMS[$i]}"
echo "[$((i+1))/${#ITEMS[@]}] Processing: $item" | tee -a "$LOG_FILE"
result=$(claude {{BARE_FLAG}} -p "{{PROMPT_TEMPLATE}}" \
--output-format json \
--allowedTools "{{TOOLS}}" \
--max-turns {{MAX_TURNS}} \
2>>"$LOG_FILE") || {
echo "FAILED: $item" | tee -a "$LOG_FILE"
echo "{\"item\":\"$item\",\"error\":true}" >> "$RESULTS_FILE"
continue
}
echo "$result" >> "$RESULTS_FILE"
echo " Done." | -a
| -a
Parallel execution template (with concurrency limit):
#!/bin/bash
set -euo pipefail
OUTPUT_DIR="{{BATCH_DIR}}"
MAX_PARALLEL={{MAX_PARALLEL:-3}}
mkdir -p "$OUTPUT_DIR/parts"
process_item() {
local idx=$1 item=$2
claude {{BARE_FLAG}} -p "{{PROMPT_TEMPLATE}}" \
--output-format json \
--allowedTools "{{TOOLS}}" \
--max-turns {{MAX_TURNS}} \
> "$OUTPUT_DIR/parts/result_${idx}.json" 2>"$OUTPUT_DIR/parts/log_${idx}.txt" || true
}
ITEMS=({{ITEMS_ARRAY}})
running=0
for i in "${!ITEMS[@]}"; do
process_item "$i" "${ITEMS[$i]}" &
running=$((running + 1))
if [ "$running" -ge "$MAX_PARALLEL" ]; then
wait -n
running=$((running - 1))
fi
done
wait
cat "$OUTPUT_DIR/parts/result_*.json" > "$OUTPUT_DIR/results.jsonl"
echo "Batch complete. Results: $OUTPUT_DIR/results.jsonl"
Step 4: Execute and Monitor
-
Make script executable and run it:
chmod +x "$OUTPUT_DIR/batch_run.sh"
bash "$OUTPUT_DIR/batch_run.sh"
-
For long-running batches, use run_in_background and notify on completion.
-
After completion, parse results:
jq -s '[.[] | select(.error != true)] | length' "$OUTPUT_DIR/results.jsonl"
jq -r '.result' "$OUTPUT_DIR/results.jsonl"
Step 5: Deliver Results
- Summarize outcomes (success/fail counts, highlights)
- Provide path to results file
- Open output folder:
open "$OUTPUT_DIR"
Outputs
batch_run.sh — Generated and executed script
results.jsonl — One JSON result per line
batch.log — Execution log
- Summary of outcomes presented to user
Quick Reference: Common Patterns
Process files in a directory:
ITEMS=($(ls src/**/*.py))
PROMPT="Review this file for bugs: $item"
TOOLS="Read"
Run prompts from a file (one per line):
mapfile -t ITEMS < prompts.txt
Process with different working directory:
cd /path/to/project && claude -p "..." --allowedTools "..."
Resume a failed batch (skip completed):