#!/bin/bash set -e SKILLS_DIR="${SKILLS_DIR:-/skills}" PERSONALITIES_DIR="${PERSONALITIES_DIR:-/personalities}" WORKSPACES_DIR="${WORKSPACES_DIR:-/workspaces}" AGENTS_DATA_DIR="${AGENTS_DATA_DIR:-/data/agents}" # Install dependencies install_deps() { local needed="" command -v socat &>/dev/null || needed="$needed socat" command -v jq &>/dev/null || needed="$needed jq" command -v sqlite3 &>/dev/null || needed="$needed sqlite3" if [ -n "$needed" ]; then echo "Installing dependencies:$needed" apt-get update && apt-get install -y $needed fi } # Setup directories setup_dirs() { mkdir -p "$PERSONALITIES_DIR" mkdir -p "$WORKSPACES_DIR" mkdir -p "$AGENTS_DATA_DIR" mkdir -p /var/run/agents echo "Personalities: $PERSONALITIES_DIR" echo "Workspaces: $WORKSPACES_DIR" echo "Data: $AGENTS_DATA_DIR" } # Detect storage backend detect_storage() { if [ -d "$SKILLS_DIR/postgres" ] && command -v psql &>/dev/null; then echo "postgres" elif [ -d "$SKILLS_DIR/duckdb" ] && command -v duckdb &>/dev/null; then echo "duckdb" else echo "sqlite" fi } # Initialize SQLite database (fallback) init_sqlite() { local db="$AGENTS_DATA_DIR/agents.db" sqlite3 "$db" << 'SQL' CREATE TABLE IF NOT EXISTS instances ( id TEXT PRIMARY KEY, name TEXT, personality TEXT NOT NULL, workspace TEXT, port INTEGER, pid INTEGER, status TEXT DEFAULT 'starting', started_at TEXT DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS conversations ( id TEXT PRIMARY KEY, instance_id TEXT, started_at TEXT DEFAULT CURRENT_TIMESTAMP, status TEXT DEFAULT 'active', FOREIGN KEY (instance_id) REFERENCES instances(id) ); CREATE TABLE IF NOT EXISTS messages ( id TEXT PRIMARY KEY, conversation_id TEXT, role TEXT, content TEXT, tool_calls TEXT, timestamp TEXT DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (conversation_id) REFERENCES conversations(id) ); SQL echo "SQLite initialized: $db" } # Initialize DuckDB tables init_duckdb() { local db="$AGENTS_DATA_DIR/agents.duckdb" duckdb "$db" << 'SQL' CREATE TABLE IF NOT EXISTS instances ( id VARCHAR PRIMARY KEY, name VARCHAR, personality VARCHAR NOT NULL, workspace VARCHAR, port INTEGER, pid INTEGER, status VARCHAR DEFAULT 'starting', started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS conversations ( id VARCHAR PRIMARY KEY, instance_id VARCHAR, started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, status VARCHAR DEFAULT 'active' ); CREATE TABLE IF NOT EXISTS messages ( id VARCHAR PRIMARY KEY, conversation_id VARCHAR, role VARCHAR, content VARCHAR, tool_calls JSON, timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); SQL echo "DuckDB initialized: $db" } # Configure Caddy if present configure_caddy() { local caddy_dir="$SKILLS_DIR/caddy" local agents_port="${AGENTS_PORT:-8800}" local agents_domain="${AGENTS_DOMAIN:-}" if [ ! -d "$caddy_dir" ]; then echo "Caddy not found - Agents API on port $agents_port" return 0 fi echo "Caddy detected - configuring reverse proxy..." mkdir -p "$caddy_dir/snippets.d" local snippet="$caddy_dir/snippets.d/agents.caddy" if [ -n "$agents_domain" ]; then cat > "$snippet" << EOF # Auto-generated by agents skill $agents_domain { reverse_proxy localhost:$agents_port } EOF echo "Caddy config: $agents_domain -> localhost:$agents_port" fi } # Create example personality if none exist create_example_personality() { if [ -d "$PERSONALITIES_DIR/orchestrator" ]; then return 0 fi echo "Creating example orchestrator personality..." mkdir -p "$PERSONALITIES_DIR/orchestrator" cat > "$PERSONALITIES_DIR/orchestrator/CLAUDE.md" << 'EOF' # Orchestrator Agent You are the orchestrator agent. Your role is to: 1. Understand high-level tasks 2. Break them into subtasks 3. Spawn specialist agents as needed 4. Coordinate their work 5. Synthesize results ## Spawning Agents To spawn a specialist: ```bash curl -X POST http://localhost:8800/instances \ -H "Content-Type: application/json" \ -d '{"personality": "specialist-name", "workspace": "/workspaces/project"}' ``` ## Available Personalities Check what personalities are available: ```bash curl http://localhost:8800/personalities ``` ## Running Instances Check what agents are running: ```bash curl http://localhost:8800/instances ``` EOF cat > "$PERSONALITIES_DIR/orchestrator/config.yaml" << 'EOF' name: orchestrator description: Coordinates other agents, breaks down tasks model: claude-sonnet-4-20250514 max_turns: 50 lifecycle: long-running EOF echo "Created: $PERSONALITIES_DIR/orchestrator/" } install_deps setup_dirs STORAGE=$(detect_storage) echo "Storage backend: $STORAGE" case "$STORAGE" in sqlite) init_sqlite ;; duckdb) init_duckdb ;; postgres) echo "PostgreSQL - tables managed by postgres skill" ;; esac # Save storage type for run.sh echo "$STORAGE" > "$AGENTS_DATA_DIR/.storage" configure_caddy create_example_personality echo "Agents setup complete"