feat: add work-queue skill for Marcus and Steve
Some checks failed
ci / build-test-push (push) Failing after 29s

This commit is contained in:
Marcus A.
2026-04-11 14:48:08 -05:00
parent fbc88bb62b
commit c5be58c3c5
9 changed files with 344 additions and 0 deletions

47
skill/bin/wq Executable file
View File

@@ -0,0 +1,47 @@
#!/bin/bash
# wq — Work Queue CLI wrapper
# Usage: wq <command> [options]
set -e
API_URL="${WORK_QUEUE_API_URL:-}"
if [[ -z "$API_URL" ]]; then
if [[ -f ~/.config/work_queue_api_url ]]; then
API_URL=$(cat ~/.config/work_queue_api_url)
else
echo "Error: WORK_QUEUE_API_URL not set and no ~/.config/work_queue_api_url" >&2
exit 1
fi
fi
CMD="$1"
shift || { echo "Usage: wq <command> [args]" >&2; exit 1; }
case "$CMD" in
add)
wq_add "$@"
;;
dispatch)
wq_dispatch "$@"
;;
update)
wq_update "$@"
;;
list)
wq_list "$@"
;;
get)
wq_get "$@"
;;
my-queue)
wq_my_queue "$@"
;;
stale-check)
wq_stale_check "$@"
;;
*)
echo "Unknown command: $CMD" >&2
echo "Commands: add, dispatch, update, list, get, my-queue, stale-check" >&2
exit 1
;;
esac

39
skill/bin/wq_add Executable file
View File

@@ -0,0 +1,39 @@
#!/bin/bash
# wq_add — submit a new work item
wq_add() {
local type desc agent project_id priority payload_json
while [[ $# -gt 0 ]]; do
case "$1" in
--agent) agent="$2"; shift 2;;
--project-id) project_id="$2"; shift 2;;
--priority) priority="$2"; shift 2;;
--payload) payload_json="$2"; shift 2;;
--) shift; break;;
-*) echo "Unknown option: $1" >&2; exit 1;;
*)
if [[ -z "$type" ]]; then
type="$1"
elif [[ -z "$desc" ]]; then
desc="$1"
fi
shift;;
esac
done
if [[ -z "$type" ]] || [[ -z "$desc" ]]; then
echo "Usage: wq add <type> <description> [--agent <agent>] [--project-id <id>] [--priority 1-5>] [--payload <json>]" >&2
exit 1
fi
local body="{\"type\":\"$type\",\"description\":\"$desc\"}"
[[ -n "$agent" ]] && body=$(echo "$body" | jq ".assigned_agent=\"$agent\"")
[[ -n "$project_id" ]] && body=$(echo "$body" | jq ".project_id=\"$project_id\"")
[[ -n "$priority" ]] && body=$(echo "$body" | jq ".priority=$priority")
[[ -n "$payload_json" ]] && body=$(echo "$body" | jq ".payload=$payload_json")
curl -sf -X POST "$API_URL/work" \
-H "Content-Type: application/json" \
-d "$body" | jq .
}

27
skill/bin/wq_dispatch Executable file
View File

@@ -0,0 +1,27 @@
#!/bin/bash
# wq_dispatch — dispatch queued item to agent (queued→dispatched→in_progress atomically)
wq_dispatch() {
local work_id="$1" agent="$2"
if [[ -z "$work_id" ]] || [[ -z "$agent" ]]; then
echo "Usage: wq dispatch <work_item_id> <agent>" >&2
exit 1
fi
# Check if agent already has in_progress
local existing
existing=$(curl -sf "$API_URL/work?status=in_progress&agent=$agent" | jq 'length')
if [[ "$existing" -gt 0 ]]; then
echo "Error: $agent already has an in_progress item" >&2
exit 1
fi
curl -sf -X PATCH "$API_URL/work/$work_id" \
-H "Content-Type: application/json" \
-d "{\"status\":\"dispatched\",\"assigned_agent\":\"$agent\"}" | jq .
curl -sf -X PATCH "$API_URL/work/$work_id" \
-H "Content-Type: application/json" \
-d "{\"status\":\"in_progress\"}" | jq .
}

8
skill/bin/wq_get Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/bash
# wq_get — get single work item
wq_get() {
local work_id="$1"
[[ -z "$work_id" ]] && { echo "Usage: wq get <work_item_id>" >&2; exit 1; }
curl -sf "$API_URL/work/$work_id" | jq .
}

25
skill/bin/wq_list Executable file
View File

@@ -0,0 +1,25 @@
#!/bin/bash
# wq_list — list work items with optional filters
wq_list() {
local status agent project_id since qs="?"
while [[ $# -gt 0 ]]; do
case "$1" in
--status) status="$2"; shift 2;;
--agent) agent="$2"; shift 2;;
--project-id) project_id="$2"; shift 2;;
--since) since="$2"; shift 2;;
--) shift; break;;
-*) echo "Unknown option: $1" >&2; exit 1;;
*) echo "Unknown arg: $1" >&2; exit 1;;
esac
done
[[ -n "$status" ]] && qs="${qs}status=$status&"
[[ -n "$agent" ]] && qs="${qs}agent=$agent&"
[[ -n "$project_id" ]] && qs="${qs}project_id=$project_id&"
[[ -n "$since" ]] && qs="${qs}since=$since&"
curl -sf "$API_URL/work${qs}" | jq .
}

8
skill/bin/wq_my_queue Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/bash
# wq_my_queue — list dispatched items for a given agent (what Steve polls)
wq_my_queue() {
local agent="$1"
[[ -z "$agent" ]] && { echo "Usage: wq my-queue <agent>" >&2; exit 1; }
curl -sf "$API_URL/work?status=dispatched&agent=$agent" | jq .
}

35
skill/bin/wq_stale_check Executable file
View File

@@ -0,0 +1,35 @@
#!/bin/bash
# wq_stale_check — find in_progress items older than timeout, mark blocked
wq_stale_check() {
local timeout_minutes="${1:-30}"
local items
items=$(curl -sf "$API_URL/work?status=in_progress" | jq -r '.[] | @json' 2>/dev/null || echo "")
if [[ -z "$items" ]]; then
echo "No in_progress items found."
return 0
fi
local count=0
now_ts=$(date -u +%s)
while IFS= read -r item; do
[[ -z "$item" ]] && continue
work_id=$(echo "$item" | jq -r '.id')
updated_at=$(echo "$item" | jq -r '.updated_at')
agent=$(echo "$item" | jq -r '.assigned_agent')
age_minutes=$(( (now_ts - $(date -u -d "$updated_at" +%s 2>/dev/null || echo "$now_ts")) / 60 ))
if [[ "$age_minutes" -gt "$timeout_minutes" ]]; then
echo "Stale: $work_id ($agent, ${age_minutes}m old) — marking blocked"
curl -sf -X PATCH "$API_URL/work/$work_id" \
-H "Content-Type: application/json" \
-d "{\"status\":\"blocked\",\"notes\":\"Auto-blocked: stale for ${age_minutes} minutes (>$timeout_minutes)\"}" | jq -r '.id' > /dev/null
count=$((count + 1))
fi
done <<< "$items"
echo "Stale check complete: $count items marked blocked."
}

40
skill/bin/wq_update Executable file
View File

@@ -0,0 +1,40 @@
#!/bin/bash
# wq_update — update status, outcome, or notes
wq_update() {
local work_id status outcome notes
while [[ $# -gt 0 ]]; do
case "$1" in
--status) status="$2"; shift 2;;
--outcome) outcome="$2"; shift 2;;
--notes) notes="$2"; shift 2;;
--) shift; break;;
-*) echo "Unknown option: $1" >&2; exit 1;;
*)
if [[ -z "$work_id" ]]; then
work_id="$1"
fi
shift;;
esac
done
if [[ -z "$work_id" ]]; then
echo "Usage: wq update <work_item_id> [--status <status>] [--outcome <success|failed|cancelled>] [--notes <text>]" >&2
exit 1
fi
if [[ -z "$status" ]] && [[ -z "$outcome" ]] && [[ -z "$notes" ]]; then
echo "Error: at least one of --status, --outcome, or --notes required" >&2
exit 1
fi
local body="{}"
[[ -n "$status" ]] && body=$(echo "$body" | jq ".status=\"$status\"")
[[ -n "$outcome" ]] && body=$(echo "$body" | jq ".outcome=\"$outcome\"")
[[ -n "$notes" ]] && body=$(echo "$body" | jq ".notes=\"$notes\"")
curl -sf -X PATCH "$API_URL/work/$work_id" \
-H "Content-Type: application/json" \
-d "$body" | jq .
}