setup.sh now walks the user through entering their tasks and project context interactively. Writes plan.md and prompt.md from their input. Offers to launch the loop immediately at the end. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude Code Overnight Automation
Run Claude Code headlessly overnight to execute a full task checklist — with rate-limit resilience, macOS sleep prevention, and a stop hook that prevents Claude from quitting until every task is done.
How It Works
loop.sh (orchestrator)
|
+--> Reads plan.md for unchecked [ ] tasks
+--> Pipes prompt.md into `claude -p` (headless mode)
| |
| +--> Claude reads your plan, specs, and project rules
| +--> Implements tasks one by one
| +--> Runs typecheck/lint/test after each
| +--> Commits, marks [x], moves to next
| |
| +--> Claude tries to stop
| |
| +--> Stop Hook intercepts
| +--> Checks plan.md for remaining [ ] tasks
| +--> If incomplete: BLOCKS the stop (Claude continues)
| +--> If all done: allows stop
|
+--> Detects rate limits in output
| +--> Sleeps 1 hour, retries (up to 5x)
| +--> After 5 retries: schedules macOS launchd job to resume later
|
+--> Loops N iterations (default 10)
+--> Exits when all tasks checked or iterations exhausted
The "Ralph Wiggum" Stop Hook
The secret sauce. Claude Code supports a Stop hook — a shell script that runs every time Claude tries to end its session. By returning {"decision":"block"}, the hook prevents Claude from stopping. Combined with --dangerously-skip-permissions, Claude becomes a fully autonomous task executor that won't quit until the job is done.
Sleep Prevention
On macOS, caffeinate -i prevents idle sleep during long runs. A hook starts it when Claude begins and kills it when Claude finishes.
Rate Limit Resilience
If Claude hits API rate limits:
- Inline retry: Sleep 1 hour, then retry the same iteration
- Scheduled retry: After 5 failed retries, create a macOS
launchdplist that auto-runs the loop later - The plist self-destructs after executing
Prerequisites
- Claude Code CLI (
claudecommand available in PATH)- Install: https://docs.anthropic.com/en/docs/claude-code
- Must be logged in: run
claude loginfirst
- macOS (for
caffeinateandlaunchd— see Linux notes below) - Git (the script commits after each task)
- A project with
package.jsonor similar build tooling
Quick Start
# 1. Clone or copy this folder into your project
cp -r "For Others/templates" ~/my-project/loop
# 2. Run the setup script (creates hooks, updates settings)
cd ~/my-project
bash "path/to/For Others/setup.sh"
# 3. Edit your task list
vim loop/plan.md
# 4. Edit your prompt (project-specific rules)
vim loop/prompt.md
# 5. Start the overnight run
./loop/loop.sh
Or just run the setup script — it walks you through everything:
bash "For Others/setup.sh"
File Structure
After setup, your project will have:
your-project/
loop/
loop.sh # Main orchestrator (run this)
prompt.md # Instructions piped to Claude each iteration
plan.md # Task checklist ([ ] = todo, [x] = done)
loop.log # Full output log (auto-created)
~/.claude/
hooks/
prevent-sleep.sh # Starts caffeinate on session start
stop-hook-autonomous.sh # Blocks stop until tasks complete
allow-sleep.sh # Kills caffeinate on session end
settings.json # Hook registrations (auto-updated by setup)
Configuration
All config is via environment variables (set before running loop.sh or export in your shell):
| Variable | Default | Description |
|---|---|---|
CLAUDE_AUTONOMOUS |
1 |
Set to 0 to disable the stop hook (Claude can quit freely) |
ITERATION_COUNT |
10 |
Max loop iterations |
ITERATION_DELAY |
30 |
Seconds to pause between iterations |
RATE_LIMIT_WAIT |
3600 |
Seconds to sleep when rate limited (1 hour) |
MAX_RATE_LIMIT_RETRIES |
5 |
Retries before scheduling launchd |
CLAUDE_BIN |
claude |
Path to Claude CLI binary |
PROMPT_FILE |
loop/prompt.md |
Path to prompt file |
LOG_FILE |
loop/loop.log |
Path to log file |
Examples
# Quick test run (2 iterations, 10s delay, no stop hook)
CLAUDE_AUTONOMOUS=0 ITERATION_COUNT=2 ITERATION_DELAY=10 ./loop/loop.sh
# Full overnight run (20 iterations, 1 min between)
ITERATION_COUNT=20 ITERATION_DELAY=60 ./loop/loop.sh
# Use a custom prompt
PROMPT_FILE=my-prompt.md ./loop/loop.sh
Writing Your Plan
loop/plan.md is a markdown checklist. Each line starting with - [ ] is a pending task:
## Phase 1: Core Features
- [ ] **1.1** — Add user authentication (JWT + refresh tokens)
- [ ] **1.2** — Create user profile page with avatar upload
- [ ] **1.3** — Add settings page with theme toggle
## Phase 2: API
- [ ] **2.1** — REST endpoints for CRUD operations
- [ ] **2.2** — WebSocket support for real-time updates
## Final
- [ ] **FINAL** — Run full test suite, fix any failures, tag release
Claude will:
- Find the first
- [ ]line - Read the spec from your prompt or a separate spec file
- Implement it
- Mark it
- [x] - Move to the next
Tips for good plans
- Be specific: "Add JWT auth with refresh tokens, store in httpOnly cookies" > "Add auth"
- Order matters: Put foundational tasks first (types, utils, config) before features that depend on them
- Include testing gates: "Run
pnpm testand fix failures" as part of each task - Keep tasks small: 30-60 minutes of work each. Large tasks lead to context window exhaustion
- Add a FINAL task: A catchall that runs the full test suite
Writing Your Prompt
loop/prompt.md is what Claude reads at the start of every iteration. Include:
- What files to read (your plan, specs, project conventions)
- Project-specific rules (coding style, frameworks, constraints)
- Per-task workflow (implement → test → commit → mark done)
- Hard rules (what to never do, minimum effort before skipping)
See templates/prompt.md for a starting template.
Operating the Loop
Starting
# Foreground (see output live)
./loop/loop.sh
# Background with logging
nohup ./loop/loop.sh > /dev/null 2>&1 &
# With caffeinate (prevents sleep even if hooks fail)
caffeinate -i ./loop/loop.sh
Monitoring
# Watch the log live
tail -f loop/loop.log
# Check progress
grep -c '\- \[x\]' loop/plan.md # completed
grep -c '\- \[ \]' loop/plan.md # remaining
# Check git commits
git log --oneline -20
Stopping
- Let it finish: The loop stops automatically when all tasks are checked
- Kill it:
Ctrl+Corkill %1— Claude's current task will be interrupted but committed work is preserved - Disable stop hook: Set
CLAUDE_AUTONOMOUS=0in the environment before the next iteration
Resuming
Just run ./loop/loop.sh again. It reads plan.md fresh each iteration, so it picks up where it left off (skipping [x] tasks).
Customizing the Prompt
The prompt template has {{PLACEHOLDER}} markers. Replace them with your project's specifics:
| Placeholder | What to put |
|---|---|
{{SPEC_FILE}} |
Path to your detailed spec (e.g., SPEC.md, docs/plan.md) |
{{PROJECT_RULES_FILE}} |
Path to your coding conventions file |
{{PROJECT_RULES}} |
Inline coding rules (style, frameworks, constraints) |
Troubleshooting
Claude exits immediately
- Make sure
claude loginhas been run - Check that
claude -p "hello"works in your terminal - Verify
~/.claude/hooks/stop-hook-autonomous.shexists and is executable
Rate limit loop
- Default wait is 1 hour. Increase
RATE_LIMIT_WAITif your limits are longer - Check
loop.logfor the specific rate limit message - Claude Max subscriptions have higher limits than API keys
Mac goes to sleep
- Run
caffeinate -i ./loop/loop.shas a belt-and-suspenders approach - Check that
~/.claude/hooks/prevent-sleep.shis executable:chmod +x ~/.claude/hooks/prevent-sleep.sh
Tasks not getting marked complete
- Ensure your plan uses exact format:
- [ ](dash, space, brackets, space) - The stop hook matches
^\s*[-*]?\s*\[\s*\]— standard markdown checkboxes
Stop hook not working
- Verify
CLAUDE_AUTONOMOUS=1is set:echo $CLAUDE_AUTONOMOUS - Check hook is registered in
~/.claude/settings.json - Test the hook manually:
echo '{}' | bash ~/.claude/hooks/stop-hook-autonomous.sh
Linux Notes
The system is macOS-focused but works on Linux with minor changes:
- Sleep prevention: Replace
caffeinatewithsystemd-inhibit --what=idle --who=claude-loop --why="Overnight automation" sleep infinity &or simply disable sleep viasystemctl mask sleep.target - Scheduled retry: Replace the launchd plist section in
loop.shwith asystemd-run --on-calendaroratcommand - Hooks work identically — they're plain bash scripts
Security Notes
--dangerously-skip-permissionsgives Claude full system access within the project. Only run on trusted codebases.- The loop runs as your user — Claude can read/write anything you can
- API keys in
.env.localare accessible to Claude during the session - Review commits after an overnight run before pushing to production
- Consider running in a VM or container for additional isolation
License
MIT. Use it however you want.