agent-orchestrator/scripts/claude-batch-spawn

190 lines
6.7 KiB
Bash
Executable File

#!/bin/bash
# Claude Batch Session Spawner (v2 - worktree-based)
# Delegates to session managers for worktree creation, symlinks, and MCP setup.
# Adds duplicate detection: skips issues that already have active sessions.
# Usage: ~/claude-batch-spawn <project> <issue1> [issue2] [issue3] ...
PROJECT="${1:-}"
shift
ISSUES=("$@")
if [ -z "$PROJECT" ] || [ ${#ISSUES[@]} -eq 0 ]; then
echo "Usage: ~/claude-batch-spawn <project> <issue1> [issue2] [issue3] ..."
echo ""
echo "Examples:"
echo " ~/claude-batch-spawn splitly 298 299 300 301"
echo " ~/claude-batch-spawn integrator INT-1020 INT-1021"
echo ""
echo "This creates worktree-based sessions for each issue."
echo "After creation, attach with: ~/claude-*-session attach <session-name>"
exit 1
fi
# Determine project settings
case "$PROJECT" in
integrator|i)
SESSION_PREFIX="integrator"
SESSION_MANAGER="$HOME/claude-integrator-session"
SESSION_DATA_DIR="$HOME/.integrator-sessions"
GITHUB_REPO=""
;;
splitly|s|safesplit)
SESSION_PREFIX="splitly"
SESSION_MANAGER="$HOME/claude-splitly-session"
SESSION_DATA_DIR="$HOME/.splitly-sessions"
GITHUB_REPO="UniverseOfThings/SafeSplit"
;;
agent-orchestrator|ao)
SESSION_PREFIX="ao"
SESSION_MANAGER="$HOME/claude-ao-session"
SESSION_DATA_DIR="$HOME/.ao-sessions"
GITHUB_REPO="ComposioHQ/agent-orchestrator"
;;
*)
echo "Unknown project: $PROJECT"
echo "Use 'integrator' (or 'i'), 'splitly' (or 's'), or 'agent-orchestrator' (or 'ao')"
exit 1
;;
esac
mkdir -p "$SESSION_DATA_DIR"
# Duplicate detection: check if an issue already has an active session
issue_has_session() {
local issue="$1"
local issue_lower=$(echo "$issue" | tr '[:upper:]' '[:lower:]')
for meta_file in "$SESSION_DATA_DIR"/*; do
[ -f "$meta_file" ] || continue
local session_name=$(basename "$meta_file")
# Check if tmux session is still active
tmux has-session -t "$session_name" 2>/dev/null || continue
# Check issue field in metadata
local meta_issue=$(grep "^issue=" "$meta_file" 2>/dev/null | cut -d= -f2-)
local meta_issue_lower=$(echo "$meta_issue" | tr '[:upper:]' '[:lower:]')
# Match by issue ID in the URL or direct match
if echo "$meta_issue_lower" | grep -qi "$issue_lower"; then
echo "$session_name"
return 0
fi
done
return 1
}
echo "╔══════════════════════════════════════════════════════════════════════════════╗"
echo "║ BATCH SESSION SPAWNER (v2) ║"
echo "╚══════════════════════════════════════════════════════════════════════════════╝"
echo ""
echo "Project: $PROJECT"
echo "Issues to spawn: ${ISSUES[*]}"
echo ""
CREATED=()
SKIPPED=()
FAILED=()
for ISSUE in "${ISSUES[@]}"; do
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# Duplicate detection
existing=$(issue_has_session "$ISSUE")
if [ $? -eq 0 ]; then
echo " ⏭️ Skipping $ISSUE — already has session: $existing"
SKIPPED+=("$ISSUE:$existing")
continue
fi
echo " Creating session for: $ISSUE"
# Delegate to the session manager
output=$("$SESSION_MANAGER" new "$ISSUE" 2>&1)
if [ $? -ne 0 ]; then
echo " ❌ Error creating session!"
echo "$output" | sed 's/^/ /'
FAILED+=("$ISSUE")
continue
fi
# Extract session name from session manager output
SESSION=$(echo "$output" | grep "^SESSION=" | cut -d= -f2)
if [ -z "$SESSION" ]; then
echo " ❌ Error: Could not determine session name!"
echo "$output" | sed 's/^/ /'
FAILED+=("$ISSUE")
continue
fi
# Update metadata with issue info
if [ "$SESSION_PREFIX" = "splitly" ]; then
ISSUE_NUM=$(echo "$ISSUE" | sed 's/#//' | grep -o '[0-9]*')
ISSUE_URL="https://github.com/$GITHUB_REPO/issues/$ISSUE_NUM"
else
ISSUE_URL="$ISSUE"
fi
# Append issue to metadata (session manager may have already set it)
grep -q "^issue=" "$SESSION_DATA_DIR/$SESSION" 2>/dev/null || \
echo "issue=$ISSUE_URL" >> "$SESSION_DATA_DIR/$SESSION"
# Wait for Claude to start, then send the work prompt
sleep 1
# Build initial prompt
if [ "$SESSION_PREFIX" = "splitly" ]; then
INITIAL_PROMPT="Please start working on GitHub issue #$ISSUE_NUM. First, fetch the issue details using: gh issue view $ISSUE_NUM --repo $GITHUB_REPO. Then implement the solution in the branch already prepared for this session (confirm with \`git branch --show-current\` in the worktree if needed)."
else
INITIAL_PROMPT="Please start working on $ISSUE, fetch ticket info, and implement the task in the branch already prepared for this session (confirm with \`git branch --show-current\` in the worktree if needed)."
fi
# Send prompt to the running Claude session
tmux send-keys -t "$SESSION" "$INITIAL_PROMPT" Enter
echo " ✅ Session $SESSION created and prompted"
CREATED+=("$SESSION:$ISSUE")
# Small delay between spawns
sleep 0.5
done
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "📊 SUMMARY"
echo " Created: ${#CREATED[@]} sessions"
echo " Skipped: ${#SKIPPED[@]} (duplicate)"
echo " Failed: ${#FAILED[@]}"
echo ""
if [ ${#CREATED[@]} -gt 0 ]; then
echo "Created sessions:"
for item in "${CREATED[@]}"; do
SESSION=$(echo "$item" | cut -d: -f1)
ISSUE=$(echo "$item" | cut -d: -f2-)
echo "$SESSION$ISSUE"
done
fi
if [ ${#SKIPPED[@]} -gt 0 ]; then
echo ""
echo "Skipped (already has session):"
for item in "${SKIPPED[@]}"; do
ISSUE=$(echo "$item" | cut -d: -f1)
EXISTING=$(echo "$item" | cut -d: -f2-)
echo "$ISSUE → existing session: $EXISTING"
done
fi
if [ ${#FAILED[@]} -gt 0 ]; then
echo ""
echo "Failed issues: ${FAILED[*]}"
fi
echo ""
echo "To see all sessions: ~/claude-status"
echo "To open all tabs: ~/claude-open-all $SESSION_PREFIX"