Files
Gary Lobermier 813a6a185e feat: Daedalus — autonomous Claude Code agent framework
A framework for running Claude Code as a fully autonomous agent via cron,
with persistent memory, task management, and session handoff.

Core features:
- Checkpoint-based session handoff (save game system)
- File-based state: tasks, worklog, questions, lessons-learned
- Full autonomy with human-in-the-loop via questions + Pushover
- PID management with stale session detection
- 11-step autonomous workflow prompt
- Customizable for any project via CLAUDE.md + ADAPTING.md guide

Named for the myth: Daedalus, the master craftsman who built
autonomous machines that worked tirelessly without supervision.
2026-02-10 16:17:59 -06:00

92 lines
4.2 KiB
Bash
Executable File

#!/bin/bash
# Main cron wrapper — runs the daily prompt against Claude Code
set -uo pipefail
# ─── Environment ──────────────────────────────────────────────────────────────
# Cron doesn't source /etc/environment (PAM does that for interactive logins).
# Source it explicitly so env vars (notifications, API tokens, etc.) are available.
if [ -f /etc/environment ]; then
set -a
source /etc/environment
set +a
fi
# Ensure locally-installed tools (like claude) are on PATH
export PATH="$HOME/.local/bin:$PATH"
# ─── Config ───────────────────────────────────────────────────────────────────
# IMPORTANT: Update PROJECT_DIR to the absolute path of your Daedalus installation
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
PROMPT_FILE="$PROJECT_DIR/prompts/daily.md"
LOG_DIR="$PROJECT_DIR/logs"
LOG_FILE="$LOG_DIR/daily-$(date +%Y%m%d-%H%M%S).log"
MAX_TURNS="${MAX_TURNS:-200}" # Safety limit on agentic loops
PID_FILE="$PROJECT_DIR/.claude/cron.pid"
CHECKPOINT_FILE="$PROJECT_DIR/.claude/checkpoint.md"
# Maximum age (in minutes) of the checkpoint before we consider the session stale.
# If the checkpoint was updated within this window, the session is likely still working.
STALE_THRESHOLD_MINUTES="${STALE_THRESHOLD_MINUTES:-30}"
# ─── Previous Session Cleanup ────────────────────────────────────────────────
if [ -f "$PID_FILE" ]; then
OLD_PID=$(cat "$PID_FILE")
if kill -0 "$OLD_PID" 2>/dev/null; then
# Process is still alive — check if it's actually working
# by looking at how recently the checkpoint was updated
SESSION_ACTIVE=false
if [ -f "$CHECKPOINT_FILE" ]; then
CHECKPOINT_AGE=$(( ( $(date +%s) - $(stat -c %Y "$CHECKPOINT_FILE") ) / 60 ))
if [ "$CHECKPOINT_AGE" -lt "$STALE_THRESHOLD_MINUTES" ]; then
SESSION_ACTIVE=true
fi
fi
if [ "$SESSION_ACTIVE" = true ]; then
echo "[$(date)] Previous session (PID $OLD_PID) is still active (checkpoint updated ${CHECKPOINT_AGE}m ago). Skipping this run." >> "$LOG_DIR/cron-skip.log"
exit 0
else
echo "[$(date)] Previous session (PID $OLD_PID) appears stale. Killing." >> "$LOG_DIR/cron-cleanup.log"
kill "$OLD_PID" 2>/dev/null || true
sleep 5
# Force kill if still alive
if kill -0 "$OLD_PID" 2>/dev/null; then
kill -9 "$OLD_PID" 2>/dev/null || true
sleep 2
fi
fi
fi
rm -f "$PID_FILE"
fi
# ─── Setup ────────────────────────────────────────────────────────────────────
mkdir -p "$LOG_DIR"
cd "$PROJECT_DIR"
echo "=============================================" >> "$LOG_FILE"
echo "Daedalus Autonomous Session" >> "$LOG_FILE"
echo "Started: $(date)" >> "$LOG_FILE"
echo "Prompt: $PROMPT_FILE" >> "$LOG_FILE"
echo "Max turns: $MAX_TURNS" >> "$LOG_FILE"
echo "=============================================" >> "$LOG_FILE"
# ─── Run ──────────────────────────────────────────────────────────────────────
claude -p "$(cat "$PROMPT_FILE")" \
--dangerously-skip-permissions \
--max-turns "$MAX_TURNS" \
--output-format text \
>> "$LOG_FILE" 2>&1 &
CLAUDE_PID=$!
echo "$CLAUDE_PID" > "$PID_FILE"
wait "$CLAUDE_PID" && EXIT_CODE=0 || EXIT_CODE=$?
rm -f "$PID_FILE"
echo "=============================================" >> "$LOG_FILE"
echo "Finished: $(date)" >> "$LOG_FILE"
echo "Exit code: $EXIT_CODE" >> "$LOG_FILE"
echo "=============================================" >> "$LOG_FILE"
exit $EXIT_CODE