Files
archy/For Others/setup.sh
T
DorianandClaude Opus 4.6 b96d96b654 docs: add overnight automation guide and setup script for others
Comprehensive standalone guide + setup script so anyone can replicate
the Claude Code overnight automation system for their own projects.
Includes loop.sh, hook templates, plan/prompt templates, and an
interactive setup script. No project-specific content included.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 08:31:41 +00:00

251 lines
9.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# ============================================================================
# Claude Code Overnight Automation — Setup Script
# ============================================================================
# Creates the loop directory, hook scripts, and configures settings.json.
# Run this from your project root:
# bash path/to/setup.sh
# ============================================================================
set -euo pipefail
# ── Colors ───────────────────────────────────────────────────────────────────
BOLD='\033[1m'
DIM='\033[2m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
CYAN='\033[0;36m'
RED='\033[0;31m'
NC='\033[0m'
ok() { echo -e " ${GREEN}+${NC} $1"; }
warn() { echo -e " ${YELLOW}!${NC} $1"; }
err() { echo -e " ${RED}x${NC} $1"; }
info() { echo -e " ${DIM}$1${NC}"; }
# ── Header ───────────────────────────────────────────────────────────────────
echo ""
echo -e "${BOLD} Claude Code Overnight Automation${NC}"
echo -e " ${DIM}────────────────────────────────────${NC}"
echo ""
# ── Find template directory ──────────────────────────────────────────────────
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
TEMPLATE_DIR="$SCRIPT_DIR/templates"
if [[ ! -d "$TEMPLATE_DIR" ]]; then
err "Templates directory not found at: $TEMPLATE_DIR"
err "Make sure setup.sh is in the same folder as the templates/ directory."
exit 1
fi
# ── Check prerequisites ─────────────────────────────────────────────────────
echo -e " ${BOLD}Checking prerequisites...${NC}"
echo ""
MISSING=0
if command -v claude &>/dev/null; then
ok "Claude CLI found: $(which claude)"
else
err "Claude CLI not found. Install from: https://docs.anthropic.com/en/docs/claude-code"
MISSING=1
fi
if command -v git &>/dev/null; then
ok "Git found: $(which git)"
else
err "Git not found. Install git first."
MISSING=1
fi
if [[ "$(uname)" == "Darwin" ]]; then
ok "macOS detected (caffeinate + launchd available)"
else
warn "Not macOS — caffeinate/launchd hooks will need manual Linux equivalents"
fi
if git rev-parse --is-inside-work-tree &>/dev/null; then
PROJECT_DIR="$(git rev-parse --show-toplevel)"
ok "Git repo found: $PROJECT_DIR"
else
PROJECT_DIR="$(pwd)"
warn "Not in a git repo. Using current directory: $PROJECT_DIR"
fi
if [[ "$MISSING" -eq 1 ]]; then
echo ""
err "Missing prerequisites. Fix the above and re-run."
exit 1
fi
echo ""
# ── Create loop directory ────────────────────────────────────────────────────
echo -e " ${BOLD}Setting up loop directory...${NC}"
echo ""
LOOP_DIR="$PROJECT_DIR/loop"
mkdir -p "$LOOP_DIR"
# Copy loop.sh
if [[ -f "$LOOP_DIR/loop.sh" ]]; then
warn "loop/loop.sh already exists — skipping (won't overwrite)"
else
cp "$TEMPLATE_DIR/loop.sh" "$LOOP_DIR/loop.sh"
chmod +x "$LOOP_DIR/loop.sh"
ok "Created loop/loop.sh"
fi
# Copy prompt.md
if [[ -f "$LOOP_DIR/prompt.md" ]]; then
warn "loop/prompt.md already exists — skipping"
else
cp "$TEMPLATE_DIR/prompt.md" "$LOOP_DIR/prompt.md"
ok "Created loop/prompt.md (edit this with your project rules)"
fi
# Copy plan.md
if [[ -f "$LOOP_DIR/plan.md" ]]; then
warn "loop/plan.md already exists — skipping"
else
cp "$TEMPLATE_DIR/plan.md" "$LOOP_DIR/plan.md"
ok "Created loop/plan.md (edit this with your tasks)"
fi
echo ""
# ── Install hooks ────────────────────────────────────────────────────────────
echo -e " ${BOLD}Installing hooks...${NC}"
echo ""
HOOKS_DIR="$HOME/.claude/hooks"
mkdir -p "$HOOKS_DIR"
for hook in prevent-sleep.sh stop-hook-autonomous.sh allow-sleep.sh; do
if [[ -f "$HOOKS_DIR/$hook" ]]; then
warn "$hook already exists — skipping (won't overwrite)"
else
cp "$TEMPLATE_DIR/hooks/$hook" "$HOOKS_DIR/$hook"
chmod +x "$HOOKS_DIR/$hook"
ok "Installed ~/.claude/hooks/$hook"
fi
done
echo ""
# ── Update settings.json ────────────────────────────────────────────────────
echo -e " ${BOLD}Configuring Claude settings...${NC}"
echo ""
SETTINGS_FILE="$HOME/.claude/settings.json"
# Backup existing settings
if [[ -f "$SETTINGS_FILE" ]]; then
cp "$SETTINGS_FILE" "${SETTINGS_FILE}.backup.$(date +%s)"
info "Backed up existing settings.json"
fi
# Check if hooks are already registered
if [[ -f "$SETTINGS_FILE" ]] && grep -q "stop-hook-autonomous" "$SETTINGS_FILE" 2>/dev/null; then
ok "Hooks already registered in settings.json"
else
# Create or update settings.json with hook registrations
if command -v python3 &>/dev/null; then
python3 << 'PYEOF'
import json, os
settings_path = os.path.expanduser("~/.claude/settings.json")
hooks_dir = os.path.expanduser("~/.claude/hooks")
# Load existing or start fresh
if os.path.exists(settings_path):
with open(settings_path) as f:
settings = json.load(f)
else:
settings = {}
# Ensure hooks section exists
if "hooks" not in settings:
settings["hooks"] = {}
hooks = settings["hooks"]
# Add UserPromptSubmit hook (prevent sleep)
if "UserPromptSubmit" not in hooks:
hooks["UserPromptSubmit"] = []
prevent_sleep = {"matcher": "", "hooks": [{"type": "command", "command": f"{hooks_dir}/prevent-sleep.sh"}]}
if not any("prevent-sleep" in json.dumps(h) for h in hooks["UserPromptSubmit"]):
hooks["UserPromptSubmit"].append(prevent_sleep)
# Add Stop hook (autonomous block)
if "Stop" not in hooks:
hooks["Stop"] = []
stop_hook = {"matcher": "", "hooks": [{"type": "command", "command": f"{hooks_dir}/stop-hook-autonomous.sh"}]}
if not any("stop-hook-autonomous" in json.dumps(h) for h in hooks["Stop"]):
hooks["Stop"].append(stop_hook)
# Add SessionEnd hook (allow sleep)
if "SessionEnd" not in hooks:
hooks["SessionEnd"] = []
session_end = {"matcher": "", "hooks": [{"type": "command", "command": f"{hooks_dir}/allow-sleep.sh"}]}
if not any("allow-sleep" in json.dumps(h) for h in hooks["SessionEnd"]):
hooks["SessionEnd"].append(session_end)
with open(settings_path, "w") as f:
json.dump(settings, f, indent=2)
f.write("\n")
print("done")
PYEOF
ok "Registered hooks in ~/.claude/settings.json"
else
warn "python3 not found — you'll need to manually add hooks to ~/.claude/settings.json"
info "See the README for the required settings.json format"
fi
fi
echo ""
# ── Summary ──────────────────────────────────────────────────────────────────
echo -e " ${BOLD}${GREEN}Setup complete!${NC}"
echo ""
echo -e " ${DIM}────────────────────────────────────────────────────────${NC}"
echo ""
echo -e " ${BOLD}How to use:${NC}"
echo ""
echo -e " ${CYAN}1.${NC} Edit your task list:"
echo -e " ${DIM}vim loop/plan.md${NC}"
echo ""
echo -e " ${CYAN}2.${NC} Edit your prompt (add your project's rules and specs):"
echo -e " ${DIM}vim loop/prompt.md${NC}"
echo ""
echo -e " Replace the {{PLACEHOLDERS}} with your project's:"
echo -e " - Spec file path (detailed task descriptions)"
echo -e " - Project rules file path (coding conventions)"
echo -e " - Inline project rules (style, frameworks, constraints)"
echo ""
echo -e " ${CYAN}3.${NC} Start the overnight run:"
echo -e " ${DIM}./loop/loop.sh${NC}"
echo ""
echo -e " Or with caffeinate (belt-and-suspenders sleep prevention):"
echo -e " ${DIM}caffeinate -i ./loop/loop.sh${NC}"
echo ""
echo -e " ${CYAN}4.${NC} Monitor progress:"
echo -e " ${DIM}tail -f loop/loop.log${NC}"
echo -e " ${DIM}grep -c '\\[x\\]' loop/plan.md${NC} # completed tasks"
echo -e " ${DIM}grep -c '\\[ \\]' loop/plan.md${NC} # remaining tasks"
echo ""
echo -e " ${BOLD}Configuration:${NC}"
echo ""
echo -e " ${DIM}CLAUDE_AUTONOMOUS=1${NC} Stop hook active (default)"
echo -e " ${DIM}CLAUDE_AUTONOMOUS=0${NC} Claude can stop freely"
echo -e " ${DIM}ITERATION_COUNT=20${NC} Run 20 iterations"
echo -e " ${DIM}ITERATION_DELAY=60${NC} 60s between iterations"
echo ""
echo -e " ${BOLD}Quick test:${NC}"
echo -e " ${DIM}CLAUDE_AUTONOMOUS=0 ITERATION_COUNT=1 ./loop/loop.sh${NC}"
echo ""
echo -e " ${DIM}────────────────────────────────────────────────────────${NC}"
echo -e " ${DIM}Full documentation: README.md${NC}"
echo ""