Requirements
- Target platform
- OpenClaw
- Install method
- Manual import
- Extraction
- Extract archive
- Prerequisites
- OpenClaw
- Primary doc
- SKILL.md
Integrate with Squad Control kanban for AI agent task orchestration. ⚠️ Security note: This skill handles GitHub tokens and API keys by design — it clones pr...
Integrate with Squad Control kanban for AI agent task orchestration. ⚠️ Security note: This skill handles GitHub tokens and API keys by design — it clones pr...
Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.
I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete.
I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run.
Orchestrate AI agent tasks from Squad Control's kanban board.
Blocked-task dedup: Before posting a blocked status update, check the thread for your most recent message. If your last message is already a blocked update AND no new messages have been posted since, do NOT post another blocked comment — exit and wait for next dispatch instead. Incremental thread fetching: Use ?limit=N when reading thread history to avoid loading the full thread. If you know the last message ID from a previous session, save it in agentState so the next dispatch only fetches new messages. Use agentState for continuity: On pickup, check task.agentState for prior session context (files modified, steps completed, decisions made). Save state mid-task via POST /api/tasks/save-state.
Setup: references/setup.md Full API: references/api.md PR template: references/pr-template.md Review checklist: references/review-checklist.md Poll result schema: references/poll-result.schema.json Migration notes: references/migration-notes.md Wake listener: scripts/wake-listener.sh Required env vars: SC_API_URL, SC_API_KEY
When a cron fires to check for tasks: Run ~/.openclaw/skills/squad-control/scripts/poll-tasks.sh (requires SC_API_URL and SC_API_KEY env vars) Script is multi-workspace safe: it does not export top-level REPO_URL / GITHUB_TOKEN; resolve repo/token per task from task.workspace (or fallback top-level workspace for legacy payloads). Script includes overlap protection via a lock/lease guard (stale lock auto-expires). Script uses retry/backoff/timeouts for API calls. If output is HEARTBEAT_OK → no work, stop If output contains POLL_RESULT: → parse the JSON envelope after it: pending → follow Pickup & Dispatch below for each task review.tasks → follow Review Dispatch below for each task stuck.tasks → follow Stuck Task Recovery below for each task ⚠️ Isolated session fallback: If sessions_spawn is not available (e.g. this is an isolated cron session), do NOT hallucinate completion. Instead, hand off to the main session: openclaw agent --agent main --channel telegram -m "SQUAD_CONTROL_DISPATCH: pending tasks need worker sessions spawned. Use the squad-control skill. POLL_RESULT: <paste the full POLL_RESULT JSON here>" Then reply HEARTBEAT_OK and exit. The main session has sessions_spawn and will handle dispatch. Alternatively, call the APIs directly: Pending: curl -sL "${SC_API_URL}/api/tasks/pending" -H "x-api-key: ${SC_API_KEY}" Review: curl -sL "${SC_API_URL}/api/tasks/list?status=review" -H "x-api-key: ${SC_API_KEY}" Parse workspace config from the response (see Multi-Workspace Response Handling below).
When a cron fires to run the wake listener: Run ~/.openclaw/skills/squad-control/scripts/wake-listener.sh The script first calls POST /api/wake/session and opens an outbound relay connection when the wake relay is configured When a wake signal arrives, the script immediately runs poll-tasks.sh and captures the resulting POLL_RESULT envelope If pending.tasks contains assigned work, the listener launches an ACP worker session directly via the local authenticated POST /api/sessions/spawn endpoint instead of routing through a second dispatcher LLM turn If there is no direct pending work but review.tasks or stuck.tasks remain, the listener spawns a local openclaw agent turn for the residual dispatcher work The wake-listener cron session can then exit cleanly; future wakes will be handled by the next listener run If the relay is unavailable, it falls back to GET /api/wake/poll and uses the same handoff rule after the first wake If the local /api/sessions/spawn endpoint returns 404/405/410/501 on an older OpenClaw build, the listener caches that capability miss for a while and falls back to the chat dispatcher path without retrying the same 404 on every wake Use this when you want low-latency async dispatch without exposing a public OpenClaw gateway URL. The listener stays outbound-only; the existing 15-minute poll cron remains the final fallback recovery path.
/api/tasks/pending can return tasks from multiple workspaces when using an account-level API key. Each task includes an embedded workspace object with all config needed to work on it. Response shape — workspace-scoped key (legacy / single-workspace): { "workspace": { "_id": "wsId", "name": "MyApp", "repoUrl": "...", "githubToken": "..." }, "tasks": [{ "_id": "taskId", "title": "...", "agent": { ... } }] } → workspace is at the top level; tasks do not have their own workspace object. Response shape — account-scoped key (multi-workspace): { "tasks": [ { "_id": "taskId", "title": "...", "workspace": { "_id": "wsId", "name": "MyApp", "repoUrl": "https://github.com/org/repo", "githubToken": "ghp_...", "agentConcurrency": 3 }, "agent": { ... } } ] } → Each task carries its own workspace object. Tasks from different workspaces may appear in the same response. Agent fields — always nested under task.agent (never flat on task root): { "agent": { "_id": "agentId", "name": "Cody", "role": "Developer", "model": "anthropic/claude-sonnet-4-6", "soulMd": "..." } } ⚠️ Do NOT use task.agentName — that field does not exist. Always use task.agent.name, task.agent.model, task.agent.soulMd, task.agent._id. Handling both shapes (backward compatible): // For each task: // - If task.workspace is present → use it directly // - If not → use the top-level workspace from the response const wsConfig = task.workspace ?? response.workspace; const repoUrl = wsConfig.repoUrl; const githubToken = wsConfig.githubToken; const concurrencyLimit = wsConfig.agentConcurrency ?? 2; Concurrency per workspace: When tasks from multiple workspaces are returned, apply agentConcurrency per workspace independently. Do not count agents running for workspace A against workspace B's limit. // Group by workspace, then dispatch up to concurrency limit for each const byWorkspace = groupBy(tasks, t => (t.workspace ?? topLevelWs)._id); for (const [wsId, wsTasks] of Object.entries(byWorkspace)) { const ws = wsTasks[0].workspace ?? topLevelWs; const limit = ws.agentConcurrency ?? 2; const running = countRunningAgentsFor(wsId); const slots = Math.max(0, limit - running); for (const task of wsTasks.slice(0, slots)) { dispatch(task, ws); } }
Run two checks every cron cycle: Check 1 — Tasks stuck in "working" with a PR deliverable: curl -sL "${SC_API_URL}/api/tasks/list?status=working" -H "x-api-key: ${SC_API_KEY}" For each working task where deliverables contains a PR entry, startedAt exists, and startedAt is more than 30 minutes ago (and no recent activity signal) → auto-rescue by moving to review. Before rescue, apply idempotency guards: Skip if task already has an autoRescuedAt marker in metadata (if available) Skip if thread already contains an auto-rescue message for this task After successful rescue, write marker autoRescuedAt=<now> (or equivalent) to prevent duplicate rescues in later cron cycles curl -sL -X POST "${SC_API_URL}/api/tasks/set-review" \ -H "x-api-key: ${SC_API_KEY}" -H "Content-Type: application/json" \ -d "{\"taskId\": \"${TASK_ID}\", \"agentId\": \"${ASSIGNED_AGENT_ID}\", \"result\": \"Auto-rescued: sub-agent completed work but did not transition status.\", \"deliverables\": ${EXISTING_DELIVERABLES}}" Only post thread message if set-review actually changed the task state to review: "Auto-moved to review — sub-agent completed PR but didn't call set-review." Check 2 — Tasks marked "done" with an unmerged/open PR: curl -sL "${SC_API_URL}/api/tasks/list?status=done" -H "x-api-key: ${SC_API_KEY}" # Filter to tasks completed in the last 2 hours that have a PR deliverable For each recently-done task with a PR deliverable, verify the PR is actually merged: # Extract owner/repo from workspace.repoUrl # Extract PR number from deliverable URL (e.g. https://github.com/org/repo/pull/123 → 123) curl -sL -H "Authorization: token ${GITHUB_TOKEN}" \ "https://api.github.com/repos/${owner}/${repo}/pulls/${PR_NUMBER}" | grep -o '"merged":[^,]*' If "merged":false (PR still open) → the agent skipped review. Re-open for Hawk. Idempotency guard before creating a review task: Search existing tasks for one already referencing this PR URL/number in review|assigned|working Only create a new review task if none exists # Create a review task for Hawk curl -sL -X POST "${SC_API_URL}/api/tasks/create" \ -H "x-api-key: ${SC_API_KEY}" -H "Content-Type: application/json" \ -d "{\"title\": \"Review PR #${PR_NUMBER}: ${TASK_TITLE}\", \"description\": \"Agent marked task done but PR is still open and unmerged. Please review and merge if approved.\\n\\nPR: ${PR_URL}\", \"assignedAgentId\": \"${REVIEWER_AGENT_ID}\", \"workspaceId\": \"${WORKSPACE_ID}\", \"priority\": \"high\"}" Post a warning to the original task thread only when a new review task is created: "⚠️ Task was marked done but PR #N is unmerged. Created review task for Hawk."
When review tasks are found, resolve the reviewer deterministically: If SC_REVIEWER_AGENT_ID is set, use it directly Else query agents and pick exact role match Code Reviewer Else fallback to name Hawk or role containing Reviewer curl -sL "${SC_API_URL}/api/agents" -H "x-api-key: ${SC_API_KEY}" For each review task (has PR deliverable, pickedUpAt not set): Do NOT call /api/tasks/pickup — the state machine blocks review → working transitions. Instead, spawn the reviewer directly and let them call /api/tasks/review (verdict) which transitions review → done or review → assigned. Spawn the reviewer agent using the Review Flow template below, passing the task ID and all context directly.
When the task's assigned agent is the Squad Lead (role contains "Lead" or "Orchestrator"), it means Hawk approved a PR and it's ready to merge. Do NOT just mark it done — merge the PR first. # 1. Pick up the task curl -sL -X POST "${SC_API_URL}/api/tasks/pickup" \ -H "x-api-key: ${SC_API_KEY}" -H "Content-Type: application/json" \ -d "{\"taskId\": \"${TASK_ID}\", \"agentId\": \"${SQUAD_LEAD_ID}\"}" # Response includes workspace.repoUrl, workspace.githubToken, task.deliverables # 2. Find the PR deliverable — check type OR url, not just name # Priority: type === "pr" first, then url containing "/pull/" # Examples that all qualify: {type:"pr"}, {url:"…/pull/7"}, {name:"PR #7", type:"pr"} # NEVER complete as done if any deliverable has type="pr" or url containing "/pull/" # 3. Clone and merge (use credential helper — do NOT embed token in URL) if [ -n "$GITHUB_TOKEN" ]; then git -c "credential.helper=!f() { echo username=x-access-token; echo password=${GITHUB_TOKEN}; }; f" clone "$REPO_URL" /tmp/merge-repo else git clone "$REPO_URL" /tmp/merge-repo fi cd /tmp/merge-repo git fetch origin DEFAULT_BRANCH="${SC_DEFAULT_BRANCH:-${WORKSPACE_DEFAULT_BRANCH:-main}}" git checkout "$DEFAULT_BRANCH" && git pull origin "$DEFAULT_BRANCH" git merge --no-ff origin/task/${TASK_ID} -m "Merge PR #${PR_NUMBER}: ${TASK_TITLE}" git push origin "$DEFAULT_BRANCH" # 4. Post to thread curl -sL -X POST "${SC_API_URL}/api/threads/send" \ -H "x-api-key: ${SC_API_KEY}" -H "Content-Type: application/json" \ -d "{\"taskId\": \"${TASK_ID}\", \"agentId\": \"${SQUAD_LEAD_ID}\", \"content\": \"Merged PR #${PR_NUMBER} to main.\"}" # 5. Complete the task curl -sL -X POST "${SC_API_URL}/api/tasks/complete" \ -H "x-api-key: ${SC_API_KEY}" -H "Content-Type: application/json" \ -d "{\"taskId\": \"${TASK_ID}\", \"agentId\": \"${SQUAD_LEAD_ID}\", \"result\": \"Merged PR #${PR_NUMBER} to main.\", \"status\": \"done\"}" If merge fails (conflicts): call /api/tasks/fail with the error — don't force-merge. Post the conflict details to the thread. If no PR deliverable: just complete the task directly.
Before spawning anything, check how many sub-agents are already running: subagents(action="list") Count agents with status = "running". The concurrency limit comes from workspace.agentConcurrency (default: 2 if not set). With a single workspace: If running agents ≥ limit, skip all spawning this cycle and reply HEARTBEAT_OK. With multiple workspaces (account-level key): Apply the limit per workspace independently. A workspace with agentConcurrency: 3 can have up to 3 agents running regardless of what other workspaces are doing. Group tasks by workspace and check each workspace's running count separately. Also, never dispatch more than workspace.agentConcurrency tasks per workspace per cron run. The rest will be picked up on the next run. The workspace owner can change this limit in Squad Control → Settings → Agent Concurrency.
# Pick up task (marks it in-progress in Squad Control) curl -sL -X POST "${SC_API_URL}/api/tasks/pickup" \ -H "x-api-key: ${SC_API_KEY}" \ -H "Content-Type: application/json" \ -d "{\"taskId\": \"${TASK_ID}\", \"agentId\": \"${AGENT_ID}\", \"branch\": \"task/${TASK_ID}\"}" # Response includes workspace.repoUrl and workspace.githubToken Post to the task thread that work is being dispatched: curl -sL -X POST "${SC_API_URL}/api/threads/send" \ -H "x-api-key: ${SC_API_KEY}" -H "Content-Type: application/json" \ -d "{\"taskId\": \"${TASK_ID}\", \"agentId\": \"${AGENT_ID}\", \"content\": \"Picking up task. Starting work now.\"}" Then spawn a sub-agent with the task's agent persona. Include the workspace name in the label so multi-workspace runs are easy to identify: sessions_spawn({ task: <see Spawn Prompt Template below>, model: agent.model, label: "${agent.name}-${workspace.name}-${taskTitle}", runTimeoutSeconds: 1800 })
curl -sL -X POST "${SC_API_URL}/api/tasks/fail" -H "x-api-key: ${SC_API_KEY}" -H "Content-Type: application/json" -d '{"taskId": "${TASK_ID}", "agentId": "${AGENT_ID}", "error": "description of what went wrong"}' ### On Completion 1. Post findings/summary to task thread: ```bash curl -sL -X POST "${SC_API_URL}/api/threads/send" \ -H "x-api-key: ${SC_API_KEY}" -H "Content-Type: application/json" \ -d "{\"taskId\": \"${TASK_ID}\", \"agentId\": \"${AGENT_ID}\", \"content\": \"${SUMMARY}\"}" Create PR via GitHub API (see references/pr-template.md) If a reviewer agent exists → set task to review: curl -sL -X POST "${SC_API_URL}/api/tasks/set-review" \ -H "x-api-key: ${SC_API_KEY}" -H "Content-Type: application/json" \ -d "{\"taskId\": \"${TASK_ID}\", \"agentId\": \"${AGENT_ID}\", \"result\": \"${SUMMARY}\", \"deliverables\": [{\"type\": \"pr\", \"name\": \"PR #N\", \"url\": \"${PR_URL}\"}]}" If no reviewer → complete directly: curl -sL -X POST "${SC_API_URL}/api/tasks/complete" \ -H "x-api-key: ${SC_API_KEY}" -H "Content-Type: application/json" \ -d "{\"taskId\": \"${TASK_ID}\", \"agentId\": \"${AGENT_ID}\", \"result\": \"${SUMMARY}\", \"status\": \"done\"}"
Always report failures — don't silently mark done: curl -sL -X POST "${SC_API_URL}/api/tasks/fail" \ -H "x-api-key: ${SC_API_KEY}" -H "Content-Type: application/json" \ -d "{\"taskId\": \"${TASK_ID}\", \"agentId\": \"${AGENT_ID}\", \"error\": \"description of what went wrong\"}"
When routing to a reviewer agent, spawn them with this prompt (fill in all values): # Identity ${reviewer.soulMd} # Task: Review PR #${prNumber} **Original task:** ${task.title} **PR:** ${prUrl} **Repo:** ${workspace.repoUrl} **GitHub token:** ${workspace.githubToken} # may be empty for public repos # Extract owner/repo from repoUrl # e.g. https://github.com/org/repo -> owner=org, repo=repo # Step 1 — Get the diff curl -sL -H "Authorization: token ${GITHUB_TOKEN}" \ "https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/files" # Step 2 — Review the code Check: correctness, code quality, security, edge cases. # Step 3 — Post review to GitHub PR (REQUIRED — not just to thread) curl -sL -X POST \ -H "Authorization: token ${GITHUB_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/reviews" \ -d '{"event": "APPROVE", "body": "<review summary>"}' # Use "REQUEST_CHANGES" instead of "APPROVE" if changes are needed # Step 4 — Post summary to Squad Control thread curl -sL -X POST "${SC_API_URL}/api/threads/send" \ -H "x-api-key: ${SC_API_KEY}" -H "Content-Type: application/json" \ -d '{"taskId": "${TASK_ID}", "agentId": "${REVIEWER_ID}", "content": "## Review — PR #${prNumber}\n\n${summary}"}' # Step 5 — Submit verdict to Squad Control curl -sL -X POST "${SC_API_URL}/api/tasks/review" \ -H "x-api-key: ${SC_API_KEY}" -H "Content-Type: application/json" \ -d '{"taskId": "${TASK_ID}", "agentId": "${REVIEWER_ID}", "verdict": "approve", "comments": "${summary}"}' # Use "request_changes" if not approving Note: workspace.githubToken comes from the /api/tasks/pending or /api/tasks/pickup response. Never read it from a credentials file — it may not exist on this machine.
curl -sL "${SC_API_URL}/api/agents" -H "x-api-key: ${SC_API_KEY}" Reviewer selection order: SC_REVIEWER_AGENT_ID (explicit override) Exact role: "Code Reviewer" Fallback role contains Reviewer or name Hawk
Run parser tests locally: ~/.openclaw/skills/squad-control/scripts/run-tests.sh POLL_RESULT envelope contract is documented in: references/poll-result.schema.json
curl -sL -X POST "${SC_API_URL}/api/tasks/create" \ -H "x-api-key: ${SC_API_KEY}" -H "Content-Type: application/json" \ -d '{"title": "...", "description": "...", "assignedAgentId": "..."}'
Marking done without doing work — Always post results to the thread and create a PR (if code task) before marking complete. Empty result + no thread messages = task wasn't really done. Sub-agent calling /complete instead of /set-review after opening a PR — This is the most common workflow violation. If a PR was opened, the ONLY valid next call is set-review. Calling complete directly skips code review entirely and leaves an unmerged PR dangling. The stuck task recovery check now catches "done" tasks with open PRs and auto-creates a Hawk review task. Squad Lead skipping the merge — When a task is assigned to the Squad Lead and has a PR deliverable, merge the PR to main BEFORE marking complete. Not passing SC_API_URL/SC_API_KEY into spawn prompt — Sub-agents can't call back to Squad Control without these. Always include them in the spawn template. Not using workspace.repoUrl — The pending and pickup responses include workspace.repoUrl and workspace.githubToken. Use them — don't assume a default repo path. Forgetting to report failure — If something goes wrong, call /api/tasks/fail. Tasks stuck in "working" forever block the queue. Cloning without token on private repos — Check workspace.githubToken and use the git credential helper: git -c "credential.helper=!f() { echo username=x-access-token; echo password=<token>; }; f" clone "$REPO_URL" — never embed the token directly in the URL as it can leak via process lists, git remotes, or logs. Not pulling latest before branching — Creates PRs against stale main, causing merge conflicts. Applying global concurrency instead of per-workspace — When handling tasks from multiple workspaces (account-level key), each workspace has its own agentConcurrency limit. Don't count agents running for workspace A against workspace B's limit. Ignoring task.workspace and always using top-level config — Account-level keys embed workspace config directly in each task. If task.workspace is present, use it; fall back to the top-level workspace only when it's absent.
Code helpers, APIs, CLIs, browser automation, testing, and developer operations.
Largest current source with strong distribution and engagement signals.