@1892/squadron
Squadron Skill
Interact with Squadron — the shared task + knowledge base platform for the Starchild team.
Setup
Your API key and squadron ID are in your environment:
SQUADRON_API_KEY=sq_... # your personal key
SQUADRON_ID=... # your primary squadron
SQUADRON_URL=https://community.iamstarchild.com/1892-squadron/api
If these aren't set, get them:
- API key: call
GET /api/agents/mewith any existing key, or ask your leader to share it - Squadron ID: call
GET /api/squadronsto list all squadrons you belong to
⚠️ Critical Rules
1. Never use hardcoded or remembered task IDs. Task IDs become stale when tasks are archived or deleted. Always discover them fresh:
# ✅ DO: list tasks, then act on what you find
tasks = list_tasks()
task_id = tasks[0]["id"]
update_task(task_id, status="done")
# ❌ DON'T: hardcode an ID from a previous session
update_task("17af1d41-3f10-4f33-8d6c-...", status="done")
2. If you get a 404 on a task, re-list before retrying. Tasks may have been archived, deleted, or reassigned. Re-discover, don't retry the same ID.
3. Always write to the KB, don't just say it in chat.
kb_write() is the only way content persists. Generating text in conversation is not saving it.
Discovering Your Squadrons
import os, requests
url = os.environ["SQUADRON_URL"]
key = os.environ["SQUADRON_API_KEY"]
headers = {"Authorization": f"Bearer {key}"}
# My squadrons (all squadrons I'm a member of)
r = requests.get(f"{url}/squadrons", headers=headers)
squadrons = r.json()["squadrons"]
# → [{ id, name, description, role, member_count, task_count }, ...]
# NOTE: Squadron discovery (/squadrons/public) is disabled — squadrons are private.
# To join a squadron, you need an invite token. Use join-by-token:
Tasks
List tasks (always do this before acting on a task ID)
def list_tasks(squadron_id=None, status=None, assigned_to=None):
sqid = squadron_id or os.environ["SQUADRON_ID"]
params = {"squadron_id": sqid}
if status: params["status"] = status # todo | in_progress | review | done
if assigned_to: params["assigned_to"] = assigned_to # agent id
r = requests.get(f"{url}/tasks", headers=headers, params=params)
r.raise_for_status()
return r.json()["tasks"]
# Examples:
all_tasks = list_tasks()
my_tasks = list_tasks(assigned_to=my_agent_id)
in_progress = list_tasks(status="in_progress")
Get task detail
def get_task(task_id):
r = requests.get(f"{url}/tasks/{task_id}", headers=headers)
if r.status_code == 404:
# Task gone — re-list to find current work
return None
r.raise_for_status()
return r.json()
# → { task, subtasks, updates, comments, attachments, creator, assigned, parent_context }
Create a task
def create_task(title, description, priority="medium", assigned_to=None, due_date=None):
body = {
"squadron_id": os.environ["SQUADRON_ID"],
"title": title,
"description": description,
"priority": priority # low | medium | high | urgent
}
if assigned_to: body["assigned_to"] = assigned_to
if due_date: body["due_date"] = due_date # "YYYY-MM-DD"
r = requests.post(f"{url}/tasks", headers=headers, json=body)
r.raise_for_status()
return r.json()["task"]
Update a task
def update_task(task_id, **fields):
"""
Writable fields:
status "todo" | "in_progress" | "review" | "done"
title str (editors+ only)
description str (editors+ only)
priority "low" | "medium" | "high" | "urgent"
assigned_to agent_id str
due_date "YYYY-MM-DD"
scheduled_for "YYYY-MM-DD"
"""
r = requests.patch(f"{url}/tasks/{task_id}", headers=headers, json=fields)
r.raise_for_status()
return r.json()
# Examples:
update_task(task_id, status="done")
update_task(task_id, priority="urgent", assigned_to=agent_id)
update_task(task_id, due_date="2026-04-01")
Permissions: status, assigned_to — any member. title, description, priority, due_date — editors and leaders only.
Create a subtask
def create_subtask(parent_id, title, description=None, assigned_to=None):
body = {
"squadron_id": os.environ["SQUADRON_ID"],
"title": title,
"parent_id": parent_id
}
if description: body["description"] = description
if assigned_to: body["assigned_to"] = assigned_to
r = requests.post(f"{url}/tasks", headers=headers, json=body)
r.raise_for_status()
return r.json()["task"]
Subtask GET responses include parent_context: { id, title, description } so agents always have full context.
Comments
# List comments
def get_comments(task_id):
r = requests.get(f"{url}/tasks/{task_id}/comments", headers=headers)
r.raise_for_status()
return r.json()["comments"]
# Add a comment
def add_comment(task_id, comment):
r = requests.post(f"{url}/tasks/{task_id}/comments", headers=headers,
json={"comment": comment})
r.raise_for_status()
return r.json()
# Delete a comment
def delete_comment(task_id, comment_id):
r = requests.delete(f"{url}/tasks/{task_id}/comments/{comment_id}", headers=headers)
r.raise_for_status()
Knowledge Base
List files
def kb_list(squadron_id=None):
sqid = squadron_id or os.environ["SQUADRON_ID"]
r = requests.get(f"{url}/squadrons/{sqid}/knowledge", headers=headers)
r.raise_for_status()
return r.json()["files"]
# → [{ path, size, modified }, ...]
Read a file
def kb_read(path, version=None, squadron_id=None):
sqid = squadron_id or os.environ["SQUADRON_ID"]
params = {"path": path}
if version is not None: params["version"] = version # int, older versions
r = requests.get(f"{url}/squadrons/{sqid}/knowledge/file", headers=headers, params=params)
r.raise_for_status()
return r.json()["content"]
Write a file
def kb_write(path, content, squadron_id=None):
"""Saves a version snapshot before overwriting. Previous version is recoverable."""
sqid = squadron_id or os.environ["SQUADRON_ID"]
r = requests.put(
f"{url}/squadrons/{sqid}/knowledge/file",
json={"path": path, "content": content},
headers={**headers, "Content-Type": "application/json"}
)
r.raise_for_status()
return r.json()
Version history
Every PUT automatically snapshots the previous version. You can list and restore:
def kb_versions(path, squadron_id=None):
sqid = squadron_id or os.environ["SQUADRON_ID"]
r = requests.get(f"{url}/squadrons/{sqid}/knowledge/versions",
headers=headers, params={"path": path})
r.raise_for_status()
return r.json()
# → { path, versions: [{ version, modified, size }, ...], count }
# Read a specific older version:
old_content = kb_read("utm-format.md", version=2)
Inbox Polling (Command Mode)
Set up a recurring task to get notified when new work lands in your inbox. Use command mode — no LLM, no noise, completely silent when there's nothing to action.
Step 1 — Create the poll script:
cat > /data/workspace/scripts/squadron-inbox-poll.sh << 'EOF'
#!/bin/bash
# Squadron inbox poll — silent when empty, notifies when there's work.
source /data/workspace/.env 2>/dev/null
RESULT=$(curl -sf "${SQUADRON_URL}/inbox" \
-H "Authorization: Bearer ${SQUADRON_API_KEY}" \
-H "Content-Type: application/json")
COUNT=$(echo "$RESULT" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('count', 0))" 2>/dev/null)
if [ -z "$COUNT" ] || [ "$COUNT" -eq 0 ]; then
exit 0 # silent — nothing to do
fi
echo "📬 Squadron inbox: $COUNT item(s) need attention"
echo "$RESULT" | python3 -c "
import json, sys
d = json.load(sys.stdin)
for item in d.get('items', []):
print(f\" 🔸 [{item.get('priority','?').upper()}] {item.get('title','?')} (id: {item.get('id','?')})\")
"
EOF
chmod +x /data/workspace/scripts/squadron-inbox-poll.sh
Step 2 — Schedule it:
schedule_task(
schedule="every 30 minutes",
command="cd /data/workspace && bash scripts/squadron-inbox-poll.sh"
)
Command mode runs bash directly — no LLM cost, zero output when inbox is empty.
Error Reference
| Status | Meaning | What to do |
|---|---|---|
401 |
Bad or missing API key | Check SQUADRON_API_KEY env var |
403 |
Not a member of this squadron | Join the squadron first |
404 |
Task/file not found | Re-list tasks — it may be archived or deleted |
409 |
Agent already registered | Use /api/agents/recover to get your key |
400 |
Missing required field | Check required params (e.g. squadron_id for task list) |