summaryrefslogblamecommitdiff
path: root/.makima/orchestrate.sh
blob: 15095bd9c2176760b9c06d14ffb2ee9136c742ad (plain) (tree)













































































































































































































                                                                                                                              
#!/bin/bash
# Makima Orchestrator Helper Script
# Usage: ./orchestrate.sh <command> [args...]

API_URL="${MAKIMA_API_URL:-http://localhost:8080}"
API_KEY="${MAKIMA_API_KEY}"
TASK_ID="${MAKIMA_TASK_ID}"

if [ -z "$API_KEY" ]; then
    echo "Error: MAKIMA_API_KEY not set" >&2
    exit 1
fi

if [ -z "$TASK_ID" ]; then
    echo "Error: MAKIMA_TASK_ID not set" >&2
    exit 1
fi

# Helper function to make API calls and check for errors
api_call() {
    local method="$1"
    local url="$2"
    local data="$3"
    local response
    local http_code

    if [ -n "$data" ]; then
        response=$(curl -s -w "\n%{http_code}" -X "$method" \
            -H "X-Makima-Tool-Key: $API_KEY" \
            -H "Content-Type: application/json" \
            -d "$data" \
            "$url")
    else
        response=$(curl -s -w "\n%{http_code}" -X "$method" \
            -H "X-Makima-Tool-Key: $API_KEY" \
            "$url")
    fi

    # Extract HTTP code (last line) and body (everything else)
    http_code=$(echo "$response" | tail -n1)
    body=$(echo "$response" | sed '$d')

    # Check for curl errors or non-2xx status
    if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
        echo "Error: API request failed with HTTP $http_code" >&2
        echo "URL: $url" >&2
        echo "Response: $body" >&2
        echo "$body"
        return 1
    fi

    echo "$body"
    return 0
}

case "$1" in
    list)
        api_call GET "$API_URL/api/v1/mesh/tasks/$TASK_ID/subtasks"
        ;;
    create)
        # Parse arguments: create "name" "plan" [--continue-from <task_id>] [--files "file1,file2"]
        if [ -z "$2" ] || [ -z "$3" ]; then
            echo "Usage: $0 create \"<name>\" \"<plan>\" [--continue-from <task_id>] [--files \"file1,file2\"]" >&2
            exit 1
        fi
        NAME="$2"
        PLAN="$3"
        CONTINUE_FROM=""
        COPY_FILES=""

        # Parse optional flags (can be in any order after name and plan)
        shift 3
        while [ $# -gt 0 ]; do
            case "$1" in
                --continue-from)
                    CONTINUE_FROM="$2"
                    shift 2
                    ;;
                --files)
                    COPY_FILES="$2"
                    shift 2
                    ;;
                *)
                    echo "Unknown option: $1" >&2
                    exit 1
                    ;;
            esac
        done

        # Escape quotes in name and plan for JSON
        NAME_ESCAPED=$(echo "$NAME" | sed 's/"/\\"/g' | sed 's/\\/\\\\/g')
        PLAN_ESCAPED=$(echo "$PLAN" | sed 's/"/\\"/g' | sed 's/\\/\\\\/g')

        # Build JSON body
        JSON_BODY="{\"name\":\"$NAME_ESCAPED\",\"plan\":\"$PLAN_ESCAPED\",\"parentTaskId\":\"$TASK_ID\""

        if [ -n "$CONTINUE_FROM" ]; then
            echo "Creating subtask: $NAME (continuing from $CONTINUE_FROM)..." >&2
            JSON_BODY="$JSON_BODY,\"continueFromTaskId\":\"$CONTINUE_FROM\""
        else
            echo "Creating subtask: $NAME..." >&2
        fi

        if [ -n "$COPY_FILES" ]; then
            # Convert comma-separated file list to JSON array
            FILES_JSON="["
            first=true
            IFS=',' read -ra FILE_ARRAY <<< "$COPY_FILES"
            for file in "${FILE_ARRAY[@]}"; do
                file=$(echo "$file" | xargs)  # trim whitespace
                if [ "$first" = true ]; then
                    FILES_JSON="$FILES_JSON\"$file\""
                    first=false
                else
                    FILES_JSON="$FILES_JSON,\"$file\""
                fi
            done
            FILES_JSON="$FILES_JSON]"
            JSON_BODY="$JSON_BODY,\"copyFiles\":$FILES_JSON"
            echo "  (copying files: $COPY_FILES)" >&2
        fi

        JSON_BODY="$JSON_BODY}"
        api_call POST "$API_URL/api/v1/mesh/tasks" "$JSON_BODY"
        ;;
    start)
        if [ -z "$2" ]; then
            echo "Usage: $0 start <subtask_id>" >&2
            exit 1
        fi
        echo "Starting subtask $2..." >&2
        api_call POST "$API_URL/api/v1/mesh/tasks/$2/start"
        ;;
    stop)
        if [ -z "$2" ]; then
            echo "Usage: $0 stop <subtask_id>" >&2
            exit 1
        fi
        api_call POST "$API_URL/api/v1/mesh/tasks/$2/stop"
        ;;
    status)
        if [ -z "$2" ]; then
            echo "Usage: $0 status <subtask_id>" >&2
            exit 1
        fi
        api_call GET "$API_URL/api/v1/mesh/tasks/$2"
        ;;
    output)
        if [ -z "$2" ]; then
            echo "Usage: $0 output <subtask_id>" >&2
            exit 1
        fi
        api_call GET "$API_URL/api/v1/mesh/tasks/$2/output"
        ;;
    worktree)
        if [ -z "$2" ]; then
            echo "Usage: $0 worktree <subtask_id>" >&2
            exit 1
        fi
        # Get the worktree path from the task's overlayPath field via API
        TASK_JSON=$(api_call GET "$API_URL/api/v1/mesh/tasks/$2")
        if [ $? -ne 0 ]; then
            echo "Error: Failed to get task info" >&2
            exit 1
        fi
        WORKTREE_PATH=$(echo "$TASK_JSON" | grep -o '"overlayPath":"[^"]*"' | cut -d'"' -f4)
        if [ -z "$WORKTREE_PATH" ]; then
            echo "Error: Task has no worktree path (may not have started yet)" >&2
            exit 1
        fi
        if [ -d "$WORKTREE_PATH" ]; then
            echo "$WORKTREE_PATH"
        else
            echo "Error: Worktree not found at $WORKTREE_PATH" >&2
            echo "The worktree may have been cleaned up." >&2
            exit 1
        fi
        ;;
    done)
        SUMMARY="${2:-Task completed}"
        api_call PUT "$API_URL/api/v1/mesh/tasks/$TASK_ID" "{\"status\":\"done\",\"progressSummary\":\"$SUMMARY\"}"
        ;;
    *)
        echo "Makima Orchestrator Helper"
        echo ""
        echo "Usage: $0 <command> [args...]"
        echo ""
        echo "Subtask Commands:"
        echo "  list                                              List all subtasks and their status"
        echo "  create \"<name>\" \"<plan>\"                          Create a new subtask"
        echo "  create \"...\" --continue-from ID                   Create subtask continuing from another task's worktree"
        echo "  create \"...\" --files \"file1,file2\"                Copy specific files from parent (orchestrator) worktree"
        echo "  start <subtask_id>                                Start a subtask"
        echo "  stop <subtask_id>                                 Stop a running subtask"
        echo "  status <subtask_id>                               Get detailed subtask status"
        echo "  output <subtask_id>                               Get subtask output history"
        echo "  worktree <subtask_id>                             Get path to subtask's worktree"
        echo ""
        echo "Completion:"
        echo "  done [summary]                                    Mark orchestrator as complete"
        echo ""
        echo "Examples:"
        echo "  create \"Fix bug\" \"Fix the null check bug\" --files \"PLAN.md\""
        echo "  create \"Step 2\" \"Continue work\" --continue-from abc123 --files \"shared.rs,types.rs\""
        ;;
esac