feat: add agent-orchestrator (ao) as a self-hosting project
- Create claude-ao-session: worktree-based session manager for ao (Linear tickets, GitHub PRs, AO_SESSION env var) - Add ao|agent-orchestrator case to all shared scripts: claude-batch-spawn, claude-spawn, claude-status, claude-open-all, claude-review-check, claude-bugbot-fix - Rewrite CLAUDE.orchestrator.md with full self-hosting instructions: commands reference, workflows, architecture, tips - Session prefix: ao, metadata: ~/.ao-sessions/, worktrees: ~/.worktrees/ao/ Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
c16103fb4d
commit
1601ad75c0
|
|
@ -1,90 +1,158 @@
|
|||
# CLAUDE.local.md - Agent Orchestrator Development
|
||||
# CLAUDE.orchestrator.md - Agent Orchestrator
|
||||
|
||||
You are working on **agent-orchestrator**, an open-source multi-agent orchestrator for Claude Code. This tool manages parallel coding agents across projects from a single control plane.
|
||||
You are the **orchestrator agent** for the agent-orchestrator project. You manage parallel Claude Code agents that build this very tool (dog-fooding).
|
||||
|
||||
> **Dog-fooding**: This orchestrator is being built using a copy of itself. The `~/` home directory orchestrator manages sessions that work on this very repo.
|
||||
## Project Info
|
||||
|
||||
## Project Overview
|
||||
- **Repo**: ComposioHQ/agent-orchestrator (GitHub)
|
||||
- **Issue Tracker**: Linear (AO team)
|
||||
- **Main Branch**: `main`
|
||||
- **Session Prefix**: `ao`
|
||||
- **Session Naming**: `ao-1`, `ao-2`, etc.
|
||||
- **Metadata Dir**: `~/.ao-sessions/`
|
||||
- **Worktrees**: `~/.worktrees/ao/`
|
||||
|
||||
Agent Orchestrator lets you:
|
||||
- Spawn and manage dozens of Claude Code agents working in parallel
|
||||
- Track progress via a live HTML dashboard with PR status, CI checks, and merge actions
|
||||
- Send commands to agents, kill/cleanup sessions, and view terminals in-browser
|
||||
- Organize agents by project with worktree-based isolation
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# See all sessions
|
||||
~/claude-status
|
||||
|
||||
# Spawn sessions for Linear tickets
|
||||
~/claude-batch-spawn ao AO-1 AO-2 AO-3
|
||||
|
||||
# Spawn single session (new iTerm2 tab)
|
||||
~/claude-spawn ao AO-1
|
||||
|
||||
# List ao sessions
|
||||
~/claude-ao-session ls
|
||||
|
||||
# Attach to a session
|
||||
~/claude-ao-session attach ao-1
|
||||
|
||||
# Kill a session
|
||||
~/claude-ao-session kill ao-1
|
||||
|
||||
# Cleanup completed work (merged PRs / done tickets)
|
||||
~/claude-ao-session cleanup
|
||||
```
|
||||
|
||||
## Agent Hierarchy
|
||||
|
||||
```
|
||||
~/agent-orchestrator/ <- YOU (Orchestrator)
|
||||
└── ao agents <- Managed via ~/claude-ao-session
|
||||
├── ao-1 (~/.worktrees/ao/ao-1)
|
||||
├── ao-2 (~/.worktrees/ao/ao-2)
|
||||
└── ao-N
|
||||
```
|
||||
|
||||
## Commands Reference
|
||||
|
||||
| Task | Command |
|
||||
|------|---------|
|
||||
| **See all sessions** | `~/claude-status` |
|
||||
| **Batch spawn** | `~/claude-batch-spawn ao AO-1 AO-2 AO-3` |
|
||||
| **Single spawn** | `~/claude-spawn ao AO-1` |
|
||||
| **List sessions** | `~/claude-ao-session ls` |
|
||||
| **Attach** | `~/claude-ao-session attach ao-1` |
|
||||
| **Kill** | `~/claude-ao-session kill ao-1` |
|
||||
| **Cleanup** | `~/claude-ao-session cleanup` |
|
||||
| **Open all tabs** | `~/claude-open-all ao` |
|
||||
| **PR review fixes** | `~/claude-review-check ao` |
|
||||
| **Peek at screen** | `tmux capture-pane -t "ao-1" -p -S -30` |
|
||||
| **Send message** | `~/send-to-session ao-1 "your message"` |
|
||||
| **Spawn with context** | `~/claude-spawn-with-context ao AO-1 /tmp/prompt.txt --open` |
|
||||
|
||||
## Typical Workflows
|
||||
|
||||
### Spawn Work for Linear Tickets
|
||||
```bash
|
||||
# 1. Check what's already running
|
||||
~/claude-status
|
||||
|
||||
# 2. Spawn sessions (auto-deduplicates)
|
||||
~/claude-batch-spawn ao AO-1 AO-2 AO-3
|
||||
|
||||
# 3. Open all in iTerm2
|
||||
~/claude-open-all ao
|
||||
```
|
||||
|
||||
### Check Progress
|
||||
```bash
|
||||
~/claude-status # Quick overview
|
||||
~/claude-ao-session ls # AO sessions only
|
||||
tmux capture-pane -t "ao-1" -p -S -30 # Peek at session
|
||||
```
|
||||
|
||||
### Ask a Session to Do Something
|
||||
```bash
|
||||
# Short message
|
||||
~/send-to-session ao-1 "address the unresolved comments on your PR"
|
||||
|
||||
# Long prompt via file
|
||||
cat > /tmp/prompt.txt << 'PROMPT'
|
||||
Your detailed instructions here...
|
||||
PROMPT
|
||||
~/claude-spawn-with-context ao AO-1 /tmp/prompt.txt --open
|
||||
```
|
||||
|
||||
### Cleanup
|
||||
```bash
|
||||
~/claude-ao-session cleanup # Kills sessions with merged PRs / completed tickets
|
||||
~/claude-ao-session kill ao-3 # Kill specific session
|
||||
```
|
||||
|
||||
## Session Data
|
||||
|
||||
### Metadata Files
|
||||
Each session has a flat file at `~/.ao-sessions/ao-N`:
|
||||
```
|
||||
worktree=/Users/equinox/.worktrees/ao/ao-1
|
||||
branch=feat/AO-1
|
||||
status=starting
|
||||
issue=https://linear.app/composio/issue/AO-1
|
||||
pr=https://github.com/ComposioHQ/agent-orchestrator/pull/5
|
||||
```
|
||||
|
||||
### Environment Variables (inside sessions)
|
||||
- `AO_SESSION` — e.g., `ao-1`
|
||||
- `LINEAR_API_KEY` — required for cleanup to check ticket status
|
||||
|
||||
## Repo Structure
|
||||
|
||||
```
|
||||
agent-orchestrator/
|
||||
├── scripts/ # Core orchestrator scripts
|
||||
│ ├── claude-status # Unified dashboard (CLI)
|
||||
│ ├── claude-batch-spawn # Spawn multiple sessions at once
|
||||
│ ├── claude-spawn # Spawn single session in new terminal tab
|
||||
│ ├── claude-dashboard # HTML dashboard with live PR status (web UI)
|
||||
│ ├── claude-open-all # Open terminal tabs for all sessions
|
||||
│ ├── claude-review-check # Trigger agents to address PR review comments
|
||||
│ ├── claude-bugbot-fix # Fix bugbot comments across sessions
|
||||
│ ├── claude-session-status # Health monitor (working/idle/blocked/waiting)
|
||||
├── scripts/ # All orchestrator scripts
|
||||
│ ├── claude-ao-session # Session manager for this project
|
||||
│ ├── claude-status # Unified CLI dashboard
|
||||
│ ├── claude-batch-spawn # Spawn multiple sessions
|
||||
│ ├── claude-spawn # Spawn single session (new tab)
|
||||
│ ├── claude-dashboard # HTML dashboard with live PR status
|
||||
│ ├── claude-open-all # Open iTerm2 tabs for sessions
|
||||
│ ├── claude-review-check # Trigger PR review fixes
|
||||
│ ├── claude-bugbot-fix # Fix bugbot comments
|
||||
│ ├── claude-session-status # Health monitor
|
||||
│ ├── claude-spawn-with-context # Spawn with custom prompt file
|
||||
│ ├── claude-spawn-on-branch # Spawn on existing branch
|
||||
│ ├── claude-spawn-with-prompt # Spawn and deliver prompt after ready
|
||||
│ ├── claude-spawn-with-prompt # Spawn + deliver prompt after ready
|
||||
│ ├── get-claude-session-info # Extract session metadata from tmux
|
||||
│ ├── open-tmux-session # Switch to terminal tab for tmux session
|
||||
│ ├── open-tmux-session # Switch to terminal tab
|
||||
│ ├── open-iterm-tab # iTerm2 tab management
|
||||
│ ├── notify-session # iTerm2 notifications from sessions
|
||||
│ ├── send-to-session # Smart message delivery to tmux sessions
|
||||
│ ├── claude-integrator-session # Example: project session manager (Linear/integrator)
|
||||
│ └── claude-splitly-session # Example: project session manager (GitHub Issues/SafeSplit)
|
||||
├── CLAUDE.local.md # This file (dev instructions)
|
||||
├── CLAUDE.md # Repo-level instructions for contributors
|
||||
└── README.md # Project README
|
||||
│ ├── notify-session # iTerm2 notifications
|
||||
│ ├── send-to-session # Smart message delivery to sessions
|
||||
│ ├── claude-integrator-session # Example: Linear-based session manager
|
||||
│ └── claude-splitly-session # Example: GitHub Issues session manager
|
||||
├── CLAUDE.orchestrator.md # This file (orchestrator instructions)
|
||||
├── CLAUDE.md # Repo instructions for contributors
|
||||
└── README.md # Project README
|
||||
```
|
||||
|
||||
## Current State
|
||||
|
||||
The scripts in `scripts/` are direct copies from the production orchestrator (`~/`). They currently have hardcoded paths and project-specific references (integrator, splitly). The work ahead is to:
|
||||
|
||||
1. **Generalize** — Remove hardcoded project names, make scripts project-agnostic
|
||||
2. **Configuration** — Add a config file (e.g., `orchestrator.yaml`) that defines projects, repos, branches, issue trackers
|
||||
3. **Installation** — Create an install script that symlinks scripts to `~/` or adds them to PATH
|
||||
4. **Documentation** — Write a comprehensive README with setup guide and examples
|
||||
5. **Self-hosting** — Make this repo use its own orchestrator for development
|
||||
|
||||
## Project Info
|
||||
|
||||
- **Repo**: ComposioHQ/agent-orchestrator (GitHub)
|
||||
- **Issue Tracker**: Linear
|
||||
- **Visibility**: Internal (ComposioHQ org)
|
||||
- **Everything runs locally** — no Datadog, no cloud infra
|
||||
|
||||
## Development Workflow
|
||||
|
||||
```bash
|
||||
# This repo is managed by the home directory orchestrator
|
||||
# Sessions are spawned via:
|
||||
~/claude-spawn-with-context i TICKET /tmp/prompt.txt --open
|
||||
|
||||
# Linear tickets for this project
|
||||
# (use Rube MCP LINEAR_CREATE_LINEAR_ISSUE to create tickets)
|
||||
|
||||
# GitHub issues (secondary):
|
||||
gh issue list --repo ComposioHQ/agent-orchestrator --state open
|
||||
```
|
||||
|
||||
## Key Design Principles
|
||||
|
||||
1. **tmux-based** — All agent sessions run in tmux. This gives persistence, detach/attach, and scriptability.
|
||||
2. **Metadata files** — Each session has a flat metadata file (`key=value` format) tracking branch, PR, issue, status, notes.
|
||||
3. **Live dashboard** — HTML dashboard served locally with real-time activity detection via Claude's JSONL session files.
|
||||
4. **Project-agnostic shared scripts** — Core scripts (spawn, status, dashboard) take project as an argument.
|
||||
5. **Project-specific session managers** — Each project gets a session manager script that handles worktrees, naming, and cleanup.
|
||||
6. **iTerm2 integration** — AppleScript-based tab management for macOS (should be made terminal-agnostic).
|
||||
|
||||
## Architecture Notes
|
||||
## Architecture
|
||||
|
||||
### Session Lifecycle
|
||||
```
|
||||
spawn → tmux session created → Claude started → working on issue
|
||||
spawn → tmux session created → Claude started → working on ticket
|
||||
↓
|
||||
metadata file written (branch, issue, status)
|
||||
↓
|
||||
|
|
@ -101,10 +169,38 @@ The dashboard detects if agents are working/idle/exited by:
|
|||
2. Walking the process tree from tmux pane PID to find `claude` processes
|
||||
3. Polling every 5 seconds via `/api/sessions` endpoint
|
||||
|
||||
### Dashboard Server
|
||||
- Python HTTP server serving static HTML + JSON API
|
||||
- `/api/sessions` — live activity status
|
||||
- `/api/terminal/:name` — spawn ttyd web terminal for session
|
||||
- `/api/kill/:name` — kill session + close iTerm tab + archive metadata
|
||||
- `/api/merge/:repo/:num` — merge PR via `gh`
|
||||
- `/api/send/:name` — send message to agent via `send-to-session`
|
||||
### Key Design Principles
|
||||
1. **tmux-based** — persistence, detach/attach, scriptability
|
||||
2. **Flat metadata files** — `key=value` format, easy to parse and update
|
||||
3. **Worktree isolation** — each session gets its own git worktree
|
||||
4. **Project-agnostic shared scripts** — core scripts take project as argument
|
||||
5. **Project-specific session managers** — each project gets its own (e.g., `claude-ao-session`)
|
||||
|
||||
## Roadmap
|
||||
|
||||
1. **Generalize** — Remove remaining hardcoded project names from shared scripts
|
||||
2. **Configuration** — `orchestrator.yaml` defining projects, repos, branches, issue trackers
|
||||
3. **Installation** — Install script that symlinks scripts to `~/` or adds to PATH
|
||||
4. **Documentation** — Comprehensive README with setup guide and examples
|
||||
5. **Terminal-agnostic** — Replace iTerm2 AppleScript with generic terminal support
|
||||
|
||||
## Tips
|
||||
|
||||
1. **Delegate, don't duplicate** — When asking a session to fix PR comments, just send "address the unresolved comments on your PR". The session has `gh` access.
|
||||
2. **Check before spawning** — `~/claude-status` to avoid duplicate sessions.
|
||||
3. **Detach, don't kill** — `Ctrl-b d` detaches from tmux. Session keeps running.
|
||||
4. **Peek without attaching** — `tmux capture-pane -t "ao-1" -p -S -30`
|
||||
5. **Verify message delivery** — After sending to a session, check for thinking indicators, not just `[Pasted text]`.
|
||||
|
||||
## Linear Integration
|
||||
|
||||
Create tickets via Rube MCP:
|
||||
```
|
||||
RUBE_SEARCH_TOOLS: queries=[{use_case: "create an issue in Linear"}]
|
||||
|
||||
LINEAR_CREATE_LINEAR_ISSUE:
|
||||
team_id: "<AO team ID>"
|
||||
title: "Your ticket title"
|
||||
description: "Markdown description"
|
||||
priority: 2 # 1=Urgent, 2=High, 3=Normal, 4=Low
|
||||
```
|
||||
|
|
|
|||
|
|
@ -0,0 +1,235 @@
|
|||
#!/bin/bash
|
||||
# claude-ao-session - Worktree-based session manager for agent-orchestrator
|
||||
|
||||
set -e
|
||||
|
||||
MAIN_REPO=~/agent-orchestrator
|
||||
WORKTREE_DIR=~/.worktrees/ao
|
||||
SESSION_DIR=~/.ao-sessions
|
||||
DEFAULT_BRANCH="main"
|
||||
GITHUB_REPO="ComposioHQ/agent-orchestrator"
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
usage() {
|
||||
echo "Usage: ~/claude-ao-session <command> [args]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " new [LINEAR-ID] [--open] Create new session (optionally with Linear ticket)"
|
||||
echo " ls List all sessions"
|
||||
echo " attach <session> Attach to session"
|
||||
echo " kill <session> Kill session and remove worktree"
|
||||
echo " cleanup Kill sessions where PR is merged or ticket is done"
|
||||
echo " update Update current session metadata"
|
||||
exit 1
|
||||
}
|
||||
|
||||
get_next_session_number() {
|
||||
local max=0
|
||||
for session in $(tmux list-sessions -F "#{session_name}" 2>/dev/null | grep "^ao-[0-9]*$" | sed 's/ao-//'); do
|
||||
if [ "$session" -gt "$max" ] 2>/dev/null; then
|
||||
max=$session
|
||||
fi
|
||||
done
|
||||
echo $((max + 1))
|
||||
}
|
||||
|
||||
cmd_new() {
|
||||
local linear_id=""
|
||||
local open_tab=false
|
||||
|
||||
# Parse args
|
||||
for arg in "$@"; do
|
||||
if [ "$arg" = "--open" ]; then
|
||||
open_tab=true
|
||||
elif [ -z "$linear_id" ] && [ "$arg" != "--open" ]; then
|
||||
linear_id="$arg"
|
||||
fi
|
||||
done
|
||||
|
||||
local session_num=$(get_next_session_number)
|
||||
local session_name="ao-$session_num"
|
||||
local worktree_path="$WORKTREE_DIR/$session_name"
|
||||
|
||||
echo -e "${BLUE}Creating session: $session_name${NC}"
|
||||
|
||||
cd "$MAIN_REPO"
|
||||
git fetch origin --quiet
|
||||
|
||||
if [ -n "$linear_id" ]; then
|
||||
local branch_name="feat/$linear_id"
|
||||
git worktree add -b "$branch_name" "$worktree_path" "origin/$DEFAULT_BRANCH" 2>/dev/null || \
|
||||
git worktree add "$worktree_path" "origin/$DEFAULT_BRANCH"
|
||||
else
|
||||
git worktree add "$worktree_path" "origin/$DEFAULT_BRANCH" --detach
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Created worktree at $worktree_path${NC}"
|
||||
|
||||
# Symlink shared resources from main repo
|
||||
[ -f "$MAIN_REPO/CLAUDE.local.md" ] && ln -sf "$MAIN_REPO/CLAUDE.local.md" "$worktree_path/CLAUDE.local.md"
|
||||
[ -d "$MAIN_REPO/.claude" ] && rm -rf "$worktree_path/.claude" && ln -s "$MAIN_REPO/.claude" "$worktree_path/.claude"
|
||||
|
||||
# Create tmux session and start Claude (unset CLAUDECODE to avoid nested session detection)
|
||||
tmux new-session -d -s "$session_name" -c "$worktree_path" -e "AO_SESSION=$session_name" -e "DIRENV_LOG_FORMAT="
|
||||
tmux send-keys -t "$session_name" "unset CLAUDECODE && claude --dangerously-skip-permissions" Enter
|
||||
|
||||
# Create metadata file
|
||||
mkdir -p "$SESSION_DIR"
|
||||
cat > "$SESSION_DIR/$session_name" << EOF
|
||||
worktree=$worktree_path
|
||||
branch=$(cd "$worktree_path" && git branch --show-current || echo "detached")
|
||||
status=starting
|
||||
EOF
|
||||
[ -n "$linear_id" ] && echo "issue=https://linear.app/composio/issue/$linear_id" >> "$SESSION_DIR/$session_name"
|
||||
|
||||
echo -e "${GREEN}✓ Created tmux session: $session_name${NC}"
|
||||
echo -e "${GREEN}✓ Started Claude in session${NC}"
|
||||
echo ""
|
||||
echo "Attach with: tmux attach -t $session_name"
|
||||
echo "Or open in iTerm2: ~/open-iterm-tab $session_name"
|
||||
|
||||
# Clean output for scripting (no ANSI codes)
|
||||
echo "SESSION=$session_name"
|
||||
|
||||
# Open in iTerm2 if requested
|
||||
if [ "$open_tab" = true ]; then
|
||||
~/open-iterm-tab "$session_name"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_ls() {
|
||||
echo -e "${BLUE}Agent-Orchestrator Sessions:${NC}"
|
||||
echo ""
|
||||
|
||||
for session in $(tmux list-sessions -F "#{session_name}" 2>/dev/null | grep "^ao-" | sort -V); do
|
||||
local meta_file="$SESSION_DIR/$session"
|
||||
local worktree="" branch="" status="" pr=""
|
||||
|
||||
if [ -f "$meta_file" ]; then
|
||||
worktree=$(grep "^worktree=" "$meta_file" 2>/dev/null | cut -d= -f2-)
|
||||
branch=$(grep "^branch=" "$meta_file" 2>/dev/null | cut -d= -f2-)
|
||||
status=$(grep "^status=" "$meta_file" 2>/dev/null | cut -d= -f2-)
|
||||
pr=$(grep "^pr=" "$meta_file" 2>/dev/null | cut -d= -f2-)
|
||||
fi
|
||||
|
||||
[ -d "$worktree" ] && branch=$(cd "$worktree" && git branch --show-current 2>/dev/null || echo "$branch")
|
||||
|
||||
echo -e "${GREEN}$session${NC}"
|
||||
[ -n "$branch" ] && echo " Branch: $branch"
|
||||
[ -n "$status" ] && echo " Status: $status"
|
||||
[ -n "$pr" ] && echo " PR: $pr"
|
||||
echo ""
|
||||
done
|
||||
}
|
||||
|
||||
cmd_attach() {
|
||||
[ -z "$1" ] && echo "Usage: ~/claude-ao-session attach <session>" && exit 1
|
||||
tmux attach -t "$1"
|
||||
}
|
||||
|
||||
cmd_kill() {
|
||||
local session="$1"
|
||||
[ -z "$session" ] && echo "Usage: ~/claude-ao-session kill <session>" && exit 1
|
||||
|
||||
local meta_file="$SESSION_DIR/$session"
|
||||
local worktree=""
|
||||
[ -f "$meta_file" ] && worktree=$(grep "^worktree=" "$meta_file" 2>/dev/null | cut -d= -f2-)
|
||||
|
||||
# Close iTerm tab BEFORE killing tmux (tab name changes after tmux dies)
|
||||
osascript -e "
|
||||
tell application \"iTerm2\"
|
||||
repeat with w in windows
|
||||
repeat with t in tabs of w
|
||||
repeat with s in sessions of t
|
||||
if name of s contains \"$session\" then
|
||||
close t
|
||||
return
|
||||
end if
|
||||
end repeat
|
||||
end repeat
|
||||
end repeat
|
||||
end tell
|
||||
" 2>/dev/null && echo -e "${GREEN}✓ Closed iTerm tab: $session${NC}"
|
||||
|
||||
tmux kill-session -t "$session" 2>/dev/null && echo -e "${GREEN}✓ Killed tmux session: $session${NC}"
|
||||
|
||||
if [ -n "$worktree" ] && [ -d "$worktree" ]; then
|
||||
cd "$MAIN_REPO"
|
||||
git worktree remove --force "$worktree" 2>/dev/null && echo -e "${GREEN}✓ Removed worktree: $worktree${NC}"
|
||||
fi
|
||||
|
||||
# Archive metadata instead of deleting
|
||||
if [ -f "$meta_file" ]; then
|
||||
mkdir -p "$SESSION_DIR/archive"
|
||||
mv "$meta_file" "$SESSION_DIR/archive/${session}_$(date +%Y%m%d-%H%M%S)"
|
||||
echo -e "${GREEN}✓ Archived metadata${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_cleanup() {
|
||||
echo "Checking for completed sessions..."
|
||||
|
||||
for session in $(tmux list-sessions -F "#{session_name}" 2>/dev/null | grep "^ao-"); do
|
||||
local meta_file="$SESSION_DIR/$session"
|
||||
local pr="" issue=""
|
||||
|
||||
[ -f "$meta_file" ] && {
|
||||
pr=$(grep "^pr=" "$meta_file" 2>/dev/null | cut -d= -f2-)
|
||||
issue=$(grep "^issue=" "$meta_file" 2>/dev/null | cut -d= -f2-)
|
||||
}
|
||||
|
||||
if [ -n "$pr" ]; then
|
||||
local pr_num=$(echo "$pr" | grep -o '[0-9]*$')
|
||||
if [ -n "$pr_num" ]; then
|
||||
local state=$(gh pr view "$pr_num" --repo "$GITHUB_REPO" --json state -q '.state' 2>/dev/null)
|
||||
[ "$state" = "MERGED" ] && echo -e "${YELLOW}PR #$pr_num merged - killing $session${NC}" && cmd_kill "$session" && continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check Linear ticket status if we have an issue URL
|
||||
if [ -n "$issue" ] && [ -n "${LINEAR_API_KEY:-}" ]; then
|
||||
local ticket_id=$(echo "$issue" | grep -o 'AO-[0-9]*')
|
||||
if [ -n "$ticket_id" ]; then
|
||||
local ticket_state=$(curl -s -X POST https://api.linear.app/graphql \
|
||||
-H "Authorization: $LINEAR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"query\": \"{ issueSearch(filter: { identifier: { eq: \\\"$ticket_id\\\" } }) { nodes { state { type } } } }\"}" \
|
||||
2>/dev/null | jq -r '.data.issueSearch.nodes[0].state.type // empty' 2>/dev/null)
|
||||
if [ "$ticket_state" = "completed" ] || [ "$ticket_state" = "canceled" ]; then
|
||||
echo -e "${YELLOW}$ticket_id $ticket_state - killing $session${NC}"
|
||||
cmd_kill "$session" && continue
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
echo "Cleanup complete."
|
||||
}
|
||||
|
||||
cmd_update() {
|
||||
local session="${AO_SESSION:-}"
|
||||
[ -z "$session" ] && echo "Error: Not in an ao session" && exit 1
|
||||
|
||||
local meta_file="$SESSION_DIR/$session"
|
||||
local worktree=$(grep "^worktree=" "$meta_file" 2>/dev/null | cut -d= -f2-)
|
||||
[ -z "$worktree" ] || [ ! -d "$worktree" ] && worktree="$PWD"
|
||||
|
||||
local branch=$(cd "$worktree" && git branch --show-current 2>/dev/null)
|
||||
[ -n "$branch" ] && sed -i '' "s|^branch=.*|branch=$branch|" "$meta_file" 2>/dev/null
|
||||
|
||||
echo "Updated metadata for $session"
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
new) shift; cmd_new "$@" ;;
|
||||
ls) cmd_ls ;;
|
||||
attach) cmd_attach "$2" ;;
|
||||
kill) cmd_kill "$2" ;;
|
||||
cleanup) cmd_cleanup ;;
|
||||
update) cmd_update ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
|
|
@ -35,9 +35,15 @@ case "$PROJECT" in
|
|||
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') or 'splitly' (or 's')"
|
||||
echo "Use 'integrator' (or 'i'), 'splitly' (or 's'), or 'agent-orchestrator' (or 'ao')"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
|
|
|||
|
|
@ -24,8 +24,13 @@ case "$PROJECT" in
|
|||
SESSIONS_DIR="$HOME/.splitly-sessions"
|
||||
SESSION_PREFIX="splitly"
|
||||
;;
|
||||
agent-orchestrator|ao)
|
||||
REPO="ComposioHQ/agent-orchestrator"
|
||||
SESSIONS_DIR="$HOME/.ao-sessions"
|
||||
SESSION_PREFIX="ao"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 [integrator|splitly] [--dry-run]"
|
||||
echo "Usage: $0 [integrator|splitly|ao] [--dry-run]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ for arg in "$@"; do
|
|||
--new-window|-w)
|
||||
NEW_WINDOW=true
|
||||
;;
|
||||
integrator|i|splitly|s|safesplit|all)
|
||||
integrator|i|splitly|s|safesplit|agent-orchestrator|ao|all)
|
||||
PROJECT="$arg"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $arg"
|
||||
echo "Usage: ~/claude-open-all [--new-window] [integrator|splitly|all]"
|
||||
echo "Usage: ~/claude-open-all [--new-window] [integrator|splitly|ao|all]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
|
@ -94,8 +94,12 @@ case "$PROJECT" in
|
|||
SESSIONS=($(get_sessions "splitly"))
|
||||
echo "Opening ${#SESSIONS[@]} splitly sessions..."
|
||||
;;
|
||||
agent-orchestrator|ao)
|
||||
SESSIONS=($(get_sessions "ao"))
|
||||
echo "Opening ${#SESSIONS[@]} ao sessions..."
|
||||
;;
|
||||
all|"")
|
||||
SESSIONS=($(get_sessions "integrator") $(get_sessions "splitly"))
|
||||
SESSIONS=($(get_sessions "integrator") $(get_sessions "splitly") $(get_sessions "ao"))
|
||||
echo "Opening ${#SESSIONS[@]} total sessions..."
|
||||
;;
|
||||
esac
|
||||
|
|
|
|||
|
|
@ -14,8 +14,12 @@ case "$PROJECT" in
|
|||
SESSION_PREFIX="splitly"
|
||||
SESSION_DATA_DIR="$HOME/.splitly-sessions"
|
||||
;;
|
||||
agent-orchestrator|ao)
|
||||
SESSION_PREFIX="ao"
|
||||
SESSION_DATA_DIR="$HOME/.ao-sessions"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: ~/claude-review-check [integrator|splitly]"
|
||||
echo "Usage: ~/claude-review-check [integrator|splitly|ao]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
|
|
|||
|
|
@ -75,20 +75,25 @@ case "$PROJECT" in
|
|||
SESSION_SCRIPT="$HOME/claude-splitly-session-v2"
|
||||
PROJECT_NAME="splitly"
|
||||
;;
|
||||
agent-orchestrator|ao)
|
||||
SESSION_SCRIPT="$HOME/claude-ao-session"
|
||||
PROJECT_NAME="ao"
|
||||
;;
|
||||
"")
|
||||
echo "Usage: ~/claude-spawn <integrator|splitly> [issue]"
|
||||
echo "Usage: ~/claude-spawn <integrator|splitly|ao> [issue]"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " ~/claude-spawn integrator"
|
||||
echo " ~/claude-spawn integrator https://linear.app/composio/issue/INT-1020"
|
||||
echo " ~/claude-spawn splitly 294"
|
||||
echo " ~/claude-spawn ao AO-1"
|
||||
echo ""
|
||||
echo "Run '~/claude-spawn help' for more information."
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
echo "Unknown project: $PROJECT"
|
||||
echo "Use 'integrator' (or 'i') or 'splitly' (or 's')"
|
||||
echo "Use 'integrator' (or 'i'), 'splitly' (or 's'), or 'agent-orchestrator' (or 'ao')"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
|
|
|||
|
|
@ -54,7 +54,8 @@ echo ""
|
|||
# Count sessions
|
||||
INTEGRATOR_COUNT=$(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep -c "^integrator-" || echo 0)
|
||||
SPLITLY_COUNT=$(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep -c "^splitly-" || echo 0)
|
||||
TOTAL=$((INTEGRATOR_COUNT + SPLITLY_COUNT))
|
||||
AO_COUNT=$(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep -c "^ao-" || echo 0)
|
||||
TOTAL=$((INTEGRATOR_COUNT + SPLITLY_COUNT + AO_COUNT))
|
||||
|
||||
# Count empty checkouts
|
||||
INTEGRATOR_EMPTY=0
|
||||
|
|
@ -76,6 +77,7 @@ done
|
|||
echo "📊 SUMMARY: $TOTAL active sessions"
|
||||
echo " Integrator: $INTEGRATOR_COUNT sessions, $INTEGRATOR_EMPTY empty checkouts"
|
||||
echo " Splitly: $SPLITLY_COUNT sessions, $SPLITLY_EMPTY empty checkouts"
|
||||
echo " AO: $AO_COUNT sessions"
|
||||
echo ""
|
||||
|
||||
# Show integrator sessions
|
||||
|
|
@ -201,12 +203,76 @@ else
|
|||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Show ao sessions
|
||||
echo "┌──────────────────────────────────────────────────────────────────────────────┐"
|
||||
echo "│ AGENT-ORCHESTRATOR │"
|
||||
echo "└──────────────────────────────────────────────────────────────────────────────┘"
|
||||
|
||||
if [ "$AO_COUNT" -eq 0 ]; then
|
||||
echo " (no active sessions)"
|
||||
else
|
||||
for session in $(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep "^ao-" | sort); do
|
||||
activity=$(tmux display-message -t "$session" -p '#{session_activity}' 2>/dev/null)
|
||||
now=$(date +%s)
|
||||
if [ -n "$activity" ]; then
|
||||
diff=$((now - activity))
|
||||
if [ $diff -lt 60 ]; then
|
||||
last_active="${diff}s ago"
|
||||
elif [ $diff -lt 3600 ]; then
|
||||
last_active="$((diff / 60))m ago"
|
||||
elif [ $diff -lt 86400 ]; then
|
||||
last_active="$((diff / 3600))h ago"
|
||||
else
|
||||
last_active="$((diff / 86400))d ago"
|
||||
fi
|
||||
else
|
||||
last_active="-"
|
||||
fi
|
||||
|
||||
data_file="$HOME/.ao-sessions/$session"
|
||||
meta_branch=$(grep "^branch=" "$data_file" 2>/dev/null | cut -d'=' -f2-)
|
||||
meta_status=$(grep "^status=" "$data_file" 2>/dev/null | cut -d'=' -f2-)
|
||||
meta_summary=$(grep "^summary=" "$data_file" 2>/dev/null | cut -d'=' -f2- | cut -c1-60)
|
||||
|
||||
# Get live Claude session info
|
||||
eval "$(get_claude_info "$session" 2>/dev/null)"
|
||||
|
||||
echo " 📍 $session ($last_active)"
|
||||
|
||||
# Show branch - prefer live, show if different from metadata
|
||||
if [ -n "$claude_branch" ]; then
|
||||
if [ -n "$meta_branch" ] && [ "$claude_branch" != "$meta_branch" ]; then
|
||||
echo " Branch: $claude_branch (meta: $meta_branch)"
|
||||
else
|
||||
echo " Branch: $claude_branch"
|
||||
fi
|
||||
elif [ -n "$meta_branch" ]; then
|
||||
echo " Branch: $meta_branch (stale)"
|
||||
fi
|
||||
|
||||
# Show summary - prefer Claude's auto-generated, fall back to metadata
|
||||
if [ -n "$claude_summary" ]; then
|
||||
echo " Claude: ${claude_summary:0:65}"
|
||||
fi
|
||||
if [ -n "$meta_summary" ]; then
|
||||
echo " Status: ${meta_summary}..."
|
||||
fi
|
||||
|
||||
# Show session ID if available (useful for resuming)
|
||||
[ -n "$claude_session_id" ] && echo " Session: ${claude_session_id:0:8}..."
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "┌──────────────────────────────────────────────────────────────────────────────┐"
|
||||
echo "│ QUICK ACTIONS │"
|
||||
echo "└──────────────────────────────────────────────────────────────────────────────┘"
|
||||
echo " ~/claude-spawn i [linear-url] - New integrator session in new iTerm2 tab"
|
||||
echo " ~/claude-spawn s [issue-num] - New splitly session in new iTerm2 tab"
|
||||
echo " ~/claude-spawn ao [linear-id] - New agent-orchestrator session"
|
||||
echo " ~/claude-integrator-session cleanup"
|
||||
echo " ~/claude-splitly-session cleanup"
|
||||
echo " ~/claude-ao-session cleanup"
|
||||
echo ""
|
||||
|
|
|
|||
Loading…
Reference in New Issue