summaryrefslogtreecommitdiff
path: root/.makima/orchestrate.sh
blob: 15095bd9c2176760b9c06d14ffb2ee9136c742ad (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
#!/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