feat(skill): cover full API — projects, delete, health, all WorkCreate fields
All checks were successful
ci / build-test-push (push) Successful in 1m26s
All checks were successful
ci / build-test-push (push) Successful in 1m26s
This commit is contained in:
181
skill/SKILL.md
181
skill/SKILL.md
@@ -1,115 +1,168 @@
|
|||||||
---
|
---
|
||||||
name: work-queue
|
name: work-queue
|
||||||
description: "Work queue skill for TheLab agents. Submit, dispatch, track, and complete work items via the Work Queue API. Embeds TheLab dispatch opinion: one in_progress per agent, stale detection, automatic blocking of timed-out items."
|
description: "Full Work Queue API skill for TheLab agents. Covers all project and work item operations: submit, dispatch, track, complete, cancel, and monitor work across agents. Embeds TheLab dispatch opinion."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Work Queue Skill
|
# Work Queue Skill
|
||||||
|
|
||||||
Thin wrapper around the Work Queue API with embedded dispatch opinion.
|
Wrapper around the Work Queue API at `http://app-01:8080`.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
Set the base URL:
|
|
||||||
```bash
|
```bash
|
||||||
export WORK_QUEUE_API_URL=https://api.example.com # replace with actual API URL
|
# Already configured:
|
||||||
|
echo "http://app-01:8080" > ~/.config/work_queue_api_url
|
||||||
|
export WORK_QUEUE_API_URL=http://app-01:8080
|
||||||
```
|
```
|
||||||
|
|
||||||
The skill reads `WORK_QUEUE_API_URL` from env.
|
## Commands
|
||||||
|
|
||||||
## Core Commands
|
### `wq health`
|
||||||
|
Check API is up.
|
||||||
|
```bash
|
||||||
|
wq health
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Project Commands
|
||||||
|
|
||||||
|
### `wq project add`
|
||||||
|
Create a project.
|
||||||
|
```bash
|
||||||
|
wq project add <name> [--external-ref <ref>]
|
||||||
|
```
|
||||||
|
`external_ref` can be a Todoist project ID, GitHub repo, or any external identifier.
|
||||||
|
|
||||||
|
### `wq project list`
|
||||||
|
List all projects.
|
||||||
|
```bash
|
||||||
|
wq project list
|
||||||
|
```
|
||||||
|
|
||||||
|
### `wq project get`
|
||||||
|
Get a single project.
|
||||||
|
```bash
|
||||||
|
wq project get <project_id>
|
||||||
|
```
|
||||||
|
|
||||||
|
### `wq project update`
|
||||||
|
Update a project's name or external_ref.
|
||||||
|
```bash
|
||||||
|
wq project update <project_id> [--name <name>] [--external-ref <ref>]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Work Item Commands
|
||||||
|
|
||||||
### `wq add`
|
### `wq add`
|
||||||
|
|
||||||
Submit a new work item (status=queued).
|
Submit a new work item (status=queued).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wq add <type> <description> [--agent <agent>] [--project-id <id>] [--priority 1-5>] [--payload <json>]
|
wq add <type> <description> \
|
||||||
|
[--agent <agent>] \
|
||||||
|
[--project-id <id>] \
|
||||||
|
[--priority 1-5>] \
|
||||||
|
[--payload <json>] \
|
||||||
|
[--created-by <name>]
|
||||||
```
|
```
|
||||||
|
- `type` — e.g. `code_review`, `bug_fix`, `infra_setup`, `gitea_issue`
|
||||||
|
- `priority` — 1 (highest) to 5 (lowest), default 3
|
||||||
|
- `payload` — arbitrary JSON object with type-specific fields
|
||||||
|
- `created-by` — defaults to `marcus-a`
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```bash
|
```bash
|
||||||
wq add code_review "Review PR #3 in shopping-list-api" --agent steve-w --priority 2 --payload '{"pr":3,"repo":"shopping-list-api"}'
|
wq add code_review "Review PR #3 in work-queue-api" \
|
||||||
|
--agent steve-w \
|
||||||
|
--project-id be358bbf-aff2-477d-8116-d4bf3d4d3540 \
|
||||||
|
--priority 1 \
|
||||||
|
--payload '{"pr":3,"repo":"work-queue-api"}'
|
||||||
```
|
```
|
||||||
|
|
||||||
### `wq dispatch`
|
### `wq dispatch`
|
||||||
|
Dispatch a queued item to an agent. Atomically moves queued→dispatched→in_progress.
|
||||||
Dispatch a queued item to an agent (moves queued→dispatched→in_progress atomically).
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wq dispatch <work_item_id> <agent>
|
wq dispatch <work_item_id> <agent>
|
||||||
```
|
```
|
||||||
|
|
||||||
Fails if agent already has an in_progress item.
|
Fails if agent already has an in_progress item.
|
||||||
|
|
||||||
### `wq update`
|
### `wq update`
|
||||||
|
Update status, outcome, notes, or reassign agent.
|
||||||
Update status, outcome, or notes on a work item.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wq update <work_item_id> [--status <status>] [--outcome <success|failed|cancelled>] [--notes <text>]
|
wq update <work_item_id> \
|
||||||
|
[--status <status>] \
|
||||||
|
[--outcome <success|failed|cancelled>] \
|
||||||
|
[--notes <text>] \
|
||||||
|
[--agent <agent>]
|
||||||
```
|
```
|
||||||
|
|
||||||
Valid status transitions:
|
### `wq delete`
|
||||||
- dispatched → in_progress (agent picked it up)
|
Cancel a work item (sets status=cancelled). Only works from queued or dispatched.
|
||||||
- in_progress → blocked (waiting on something)
|
```bash
|
||||||
- in_progress → completed (done)
|
wq delete <work_item_id>
|
||||||
- in_progress → failed (unrecoverable error)
|
```
|
||||||
- queued → cancelled
|
|
||||||
- dispatched → cancelled
|
|
||||||
|
|
||||||
### `wq list`
|
### `wq list`
|
||||||
|
List work items with optional filters.
|
||||||
List work items, optionally filtered.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wq list [--status <status>] [--agent <agent>] [--project-id <id>]
|
wq list \
|
||||||
|
[--status <status>] \
|
||||||
|
[--agent <agent>] \
|
||||||
|
[--project-id <id>] \
|
||||||
|
[--since <ISO8601>] \
|
||||||
|
[--type <type>]
|
||||||
```
|
```
|
||||||
|
`since` filters to items created after the given timestamp (ISO8601).
|
||||||
|
|
||||||
### `wq get`
|
### `wq get`
|
||||||
|
Get a single work item (includes dispatch history).
|
||||||
Get a single work item by ID.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wq get <work_item_id>
|
wq get <work_item_id>
|
||||||
```
|
```
|
||||||
|
|
||||||
### `wq my-queue`
|
### `wq my-queue`
|
||||||
|
Short-cut: dispatched items for a given agent — what Steve polls.
|
||||||
Short-cut: list items assigned to a given agent with status=dispatched (what Steve should poll).
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wq my-queue <agent>
|
wq my-queue <agent>
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### `wq stale-check`
|
||||||
|
Find in_progress items older than N minutes, mark them blocked. Run on heartbeat.
|
||||||
## Dispatch Opinion
|
|
||||||
|
|
||||||
These rules are enforced automatically by the skill:
|
|
||||||
|
|
||||||
1. **One in_progress per agent** — dispatch fails if target agent is already in_progress
|
|
||||||
2. **Stale detection** — on every heartbeat, `wq stale-check` is called; items in_progress >30min are automatically marked blocked
|
|
||||||
3. **Terminal states require outcome** — moving to completed/failed/cancelled requires outcome field
|
|
||||||
4. **Cancelled only from queued/dispatched** — cannot cancel something already in_progress
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Stale Check (for heartbeat)
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wq stale-check [--timeout-minutes 30]
|
wq stale-check [30]
|
||||||
|
```
|
||||||
|
Default timeout: 30 minutes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dispatch Opinion (enforced by the API)
|
||||||
|
|
||||||
|
| Transition | From | To |
|
||||||
|
|---|---|---|
|
||||||
|
| submit | — | queued |
|
||||||
|
| dispatch | queued | dispatched |
|
||||||
|
| pick up | dispatched | in_progress |
|
||||||
|
| block | in_progress | blocked |
|
||||||
|
| complete | in_progress | completed |
|
||||||
|
| fail | in_progress | failed |
|
||||||
|
| cancel | queued/dispatched | cancelled |
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
1. **One in_progress per agent** — dispatch blocks if agent is already busy
|
||||||
|
2. **Terminal states require outcome** — completed/failed/cancelled require outcome field
|
||||||
|
3. **Stale detection** — `wq stale-check` auto-blocks stale in_progress items
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Status Lifecycle
|
||||||
|
|
||||||
|
```
|
||||||
|
queued → dispatched → in_progress → completed
|
||||||
|
↘ blocked
|
||||||
|
↘ failed
|
||||||
|
↘ cancelled (from queued or dispatched only)
|
||||||
```
|
```
|
||||||
|
|
||||||
Finds all in_progress items older than timeout, marks them blocked, prints a summary line per item.
|
## API Base
|
||||||
|
`http://app-01:8080`
|
||||||
---
|
|
||||||
|
|
||||||
## Integration with Gitea Watcher
|
|
||||||
|
|
||||||
The Gitea watcher (`gitea_cron/check.sh`) outputs `dispatch:` lines. On heartbeat, parse those lines and for each:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
wq add <type> <description> --agent steve-w --payload '<json>'
|
|
||||||
```
|
|
||||||
|
|
||||||
The watcher itself does NOT call the API — it just emits dispatch lines. Marcus's heartbeat parses them, creates real work items via `wq add`, then dispatches via `wq dispatch`.
|
|
||||||
|
|||||||
70
skill/bin/wq
70
skill/bin/wq
@@ -1,47 +1,41 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# wq — Work Queue CLI wrapper
|
# wq — Work Queue CLI
|
||||||
# Usage: wq <command> [options]
|
# Usage: wq <command> [options]
|
||||||
|
|
||||||
set -e
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
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"
|
CMD="$1"
|
||||||
shift || { echo "Usage: wq <command> [args]" >&2; exit 1; }
|
shift || { echo "Usage: wq <command> [args]" >&2; exit 1; }
|
||||||
|
|
||||||
case "$CMD" in
|
case "$CMD" in
|
||||||
add)
|
# Work item commands
|
||||||
wq_add "$@"
|
add) exec "$SCRIPT_DIR/wq_add" "$@";;
|
||||||
;;
|
dispatch) exec "$SCRIPT_DIR/wq_dispatch" "$@";;
|
||||||
dispatch)
|
update) exec "$SCRIPT_DIR/wq_update" "$@";;
|
||||||
wq_dispatch "$@"
|
delete) exec "$SCRIPT_DIR/wq_delete" "$@";;
|
||||||
;;
|
list) exec "$SCRIPT_DIR/wq_list" "$@";;
|
||||||
update)
|
get) exec "$SCRIPT_DIR/wq_get" "$@";;
|
||||||
wq_update "$@"
|
my-queue) exec "$SCRIPT_DIR/wq_my_queue" "$@";;
|
||||||
;;
|
stale-check) exec "$SCRIPT_DIR/wq_stale_check" "$@";;
|
||||||
list)
|
|
||||||
wq_list "$@"
|
# Project commands
|
||||||
;;
|
project)
|
||||||
get)
|
sub="$1"; shift || { echo "Usage: wq project <add|list|get|update>" >&2; exit 1; }
|
||||||
wq_get "$@"
|
case "$sub" in
|
||||||
;;
|
add) exec "$SCRIPT_DIR/wq_project_add" "$@";;
|
||||||
my-queue)
|
list) exec "$SCRIPT_DIR/wq_project_list" "$@";;
|
||||||
wq_my_queue "$@"
|
get) exec "$SCRIPT_DIR/wq_project_get" "$@";;
|
||||||
;;
|
update) exec "$SCRIPT_DIR/wq_project_update" "$@";;
|
||||||
stale-check)
|
*) echo "Unknown project command: $sub" >&2
|
||||||
wq_stale_check "$@"
|
echo "Commands: wq project <add|list|get|update>" >&2; exit 1;;
|
||||||
;;
|
esac;;
|
||||||
*)
|
|
||||||
echo "Unknown command: $CMD" >&2
|
# Health check
|
||||||
echo "Commands: add, dispatch, update, list, get, my-queue, stale-check" >&2
|
health) exec "$SCRIPT_DIR/wq_health" "$@";;
|
||||||
exit 1
|
|
||||||
;;
|
*) echo "Unknown command: $CMD" >&2
|
||||||
|
echo "Commands: add, dispatch, update, delete, list, get, my-queue, stale-check" >&2
|
||||||
|
echo " project <add|list|get|update>" >&2
|
||||||
|
echo " health" >&2
|
||||||
|
exit 1;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -1,39 +1,44 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# wq_add — submit a new work item
|
# wq_add — submit a new work item
|
||||||
|
# Usage: wq add <type> <description> [options]
|
||||||
|
# Options:
|
||||||
|
# --agent <agent> Assign to agent (does NOT dispatch, just assigns)
|
||||||
|
# --project-id <id> Link to a project
|
||||||
|
# --priority <1-5> 1=highest, 3=default
|
||||||
|
# --payload <json> Arbitrary JSON payload
|
||||||
|
# --created-by <name> Who/what created this (default: marcus-a)
|
||||||
|
|
||||||
wq_add() {
|
API_URL="${WORK_QUEUE_API_URL:-}"
|
||||||
local type desc agent project_id priority payload_json
|
[[ -z "$API_URL" ]] && API_URL=$(cat ~/.config/work_queue_api_url 2>/dev/null || echo "")
|
||||||
|
[[ -z "$API_URL" ]] && { echo "Error: WORK_QUEUE_API_URL not set" >&2; exit 1; }
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
type="" desc="" agent="" project_id="" priority="" payload_json="" created_by="marcus-a"
|
||||||
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
|
while [[ $# -gt 0 ]]; do
|
||||||
echo "Usage: wq add <type> <description> [--agent <agent>] [--project-id <id>] [--priority 1-5>] [--payload <json>]" >&2
|
case "$1" in
|
||||||
exit 1
|
--agent) agent="$2"; shift 2;;
|
||||||
fi
|
--project-id) project_id="$2"; shift 2;;
|
||||||
|
--priority) priority="$2"; shift 2;;
|
||||||
|
--payload) payload_json="$2"; shift 2;;
|
||||||
|
--created-by) created_by="$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
|
||||||
|
|
||||||
local body="{\"type\":\"$type\",\"description\":\"$desc\"}"
|
[[ -z "$type" ]] || [[ -z "$desc" ]] && { echo "Usage: wq add <type> <description> [--agent <agent>] [--project-id <id>] [--priority 1-5>] [--payload <json>] [--created-by <name>]" >&2; exit 1; }
|
||||||
[[ -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" \
|
body="{\"type\":\"$type\",\"description\":\"$desc\",\"created_by\":\"$created_by\"}"
|
||||||
-H "Content-Type: application/json" \
|
[[ -n "$agent" ]] && body=$(echo "$body" | jq ".assigned_agent=\"$agent\"")
|
||||||
-d "$body" | jq .
|
[[ -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 .
|
||||||
|
|||||||
20
skill/bin/wq_delete
Executable file
20
skill/bin/wq_delete
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# wq_delete — cancel a work item (sets status=cancelled)
|
||||||
|
# Usage: wq delete <work_item_id>
|
||||||
|
|
||||||
|
API_URL="${WORK_QUEUE_API_URL:-}"
|
||||||
|
[[ -z "$API_URL" ]] && API_URL=$(cat ~/.config/work_queue_api_url 2>/dev/null || echo "")
|
||||||
|
[[ -z "$API_URL" ]] && { echo "Error: WORK_QUEUE_API_URL not set" >&2; exit 1; }
|
||||||
|
|
||||||
|
work_id="$1"
|
||||||
|
[[ -z "$work_id" ]] && { echo "Usage: wq delete <work_item_id>" >&2; exit 1; }
|
||||||
|
|
||||||
|
response=$(curl -sf -X DELETE "$API_URL/work/$work_id" -w "\n%{http_code}")
|
||||||
|
http_code=$(echo "$response" | tail -1)
|
||||||
|
body=$(echo "$response" | head -1)
|
||||||
|
|
||||||
|
if [[ "$http_code" == "204" ]]; then
|
||||||
|
echo "Work item $work_id cancelled."
|
||||||
|
else
|
||||||
|
echo "$body" | jq . || echo "$body"
|
||||||
|
fi
|
||||||
@@ -1,27 +1,21 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# wq_dispatch — dispatch queued item to agent (queued→dispatched→in_progress atomically)
|
# wq_dispatch — dispatch queued item to agent (queued→dispatched→in_progress atomically)
|
||||||
|
|
||||||
wq_dispatch() {
|
API_URL="${WORK_QUEUE_API_URL:-}"
|
||||||
local work_id="$1" agent="$2"
|
[[ -z "$API_URL" ]] && API_URL=$(cat ~/.config/work_queue_api_url 2>/dev/null || echo "")
|
||||||
|
[[ -z "$API_URL" ]] && { echo "Error: WORK_QUEUE_API_URL not set" >&2; exit 1; }
|
||||||
|
|
||||||
if [[ -z "$work_id" ]] || [[ -z "$agent" ]]; then
|
work_id="$1" agent="$2"
|
||||||
echo "Usage: wq dispatch <work_item_id> <agent>" >&2
|
[[ -z "$work_id" ]] || [[ -z "$agent" ]] && { echo "Usage: wq dispatch <work_item_id> <agent>" >&2; exit 1; }
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if agent already has in_progress
|
# Check if agent already has in_progress
|
||||||
local existing
|
existing=$(curl -sf "$API_URL/work?status=in_progress&agent=$agent" | jq 'length')
|
||||||
existing=$(curl -sf "$API_URL/work?status=in_progress&agent=$agent" | jq 'length')
|
[[ "$existing" -gt 0 ]] && { echo "Error: $agent already has an in_progress item" >&2; exit 1; }
|
||||||
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" \
|
curl -sf -X PATCH "$API_URL/work/$work_id" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{\"status\":\"dispatched\",\"assigned_agent\":\"$agent\"}" | jq .
|
-d "{\"status\":\"dispatched\",\"assigned_agent\":\"$agent\"}" | jq .
|
||||||
|
|
||||||
curl -sf -X PATCH "$API_URL/work/$work_id" \
|
curl -sf -X PATCH "$API_URL/work/$work_id" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{\"status\":\"in_progress\"}" | jq .
|
-d "{\"status\":\"in_progress\"}" | jq .
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# wq_get — get single work item
|
# wq_get — get single work item
|
||||||
|
|
||||||
wq_get() {
|
API_URL="${WORK_QUEUE_API_URL:-}"
|
||||||
local work_id="$1"
|
[[ -z "$API_URL" ]] && API_URL=$(cat ~/.config/work_queue_api_url 2>/dev/null || echo "")
|
||||||
[[ -z "$work_id" ]] && { echo "Usage: wq get <work_item_id>" >&2; exit 1; }
|
[[ -z "$API_URL" ]] && { echo "Error: WORK_QUEUE_API_URL not set" >&2; exit 1; }
|
||||||
curl -sf "$API_URL/work/$work_id" | jq .
|
|
||||||
}
|
work_id="$1"
|
||||||
|
[[ -z "$work_id" ]] && { echo "Usage: wq get <work_item_id>" >&2; exit 1; }
|
||||||
|
|
||||||
|
curl -sf "$API_URL/work/$work_id" | jq .
|
||||||
|
|||||||
8
skill/bin/wq_health
Executable file
8
skill/bin/wq_health
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# wq_health — check API health
|
||||||
|
|
||||||
|
API_URL="${WORK_QUEUE_API_URL:-}"
|
||||||
|
[[ -z "$API_URL" ]] && API_URL=$(cat ~/.config/work_queue_api_url 2>/dev/null || echo "")
|
||||||
|
[[ -z "$API_URL" ]] && { echo "Error: WORK_QUEUE_API_URL not set" >&2; exit 1; }
|
||||||
|
|
||||||
|
curl -sf "$API_URL/health" | jq .
|
||||||
@@ -1,25 +1,35 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# wq_list — list work items with optional filters
|
# wq_list — list work items with optional filters
|
||||||
|
# Usage: wq list [options]
|
||||||
|
# Options:
|
||||||
|
# --status <status> Filter by status: queued, dispatched, in_progress, blocked, completed, failed, cancelled
|
||||||
|
# --agent <agent> Filter by assigned agent
|
||||||
|
# --project-id <id> Filter by project
|
||||||
|
# --since <ISO8601> Only items created after this timestamp
|
||||||
|
# --type <type> Filter by work type
|
||||||
|
|
||||||
wq_list() {
|
API_URL="${WORK_QUEUE_API_URL:-}"
|
||||||
local status agent project_id since qs="?"
|
[[ -z "$API_URL" ]] && API_URL=$(cat ~/.config/work_queue_api_url 2>/dev/null || echo "")
|
||||||
|
[[ -z "$API_URL" ]] && { echo "Error: WORK_QUEUE_API_URL not set" >&2; exit 1; }
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
status="" agent="" project_id="" since="" type="" qs="?"
|
||||||
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&"
|
while [[ $# -gt 0 ]]; do
|
||||||
[[ -n "$agent" ]] && qs="${qs}agent=$agent&"
|
case "$1" in
|
||||||
[[ -n "$project_id" ]] && qs="${qs}project_id=$project_id&"
|
--status) status="$2"; shift 2;;
|
||||||
[[ -n "$since" ]] && qs="${qs}since=$since&"
|
--agent) agent="$2"; shift 2;;
|
||||||
|
--project-id) project_id="$2"; shift 2;;
|
||||||
|
--since) since="$2"; shift 2;;
|
||||||
|
--type) type="$2"; shift 2;;
|
||||||
|
--) shift; break;;
|
||||||
|
*) shift;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
curl -sf "$API_URL/work${qs}" | jq .
|
[[ -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&"
|
||||||
|
[[ -n "$type" ]] && qs="${qs}type=$type&"
|
||||||
|
|
||||||
|
curl -sf "${API_URL}/work${qs}" | jq .
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# wq_my_queue — list dispatched items for a given agent (what Steve polls)
|
# wq_my_queue — list dispatched items for a given agent (what Steve polls)
|
||||||
|
|
||||||
wq_my_queue() {
|
API_URL="${WORK_QUEUE_API_URL:-}"
|
||||||
local agent="$1"
|
[[ -z "$API_URL" ]] && API_URL=$(cat ~/.config/work_queue_api_url 2>/dev/null || echo "")
|
||||||
[[ -z "$agent" ]] && { echo "Usage: wq my-queue <agent>" >&2; exit 1; }
|
[[ -z "$API_URL" ]] && { echo "Error: WORK_QUEUE_API_URL not set" >&2; exit 1; }
|
||||||
curl -sf "$API_URL/work?status=dispatched&agent=$agent" | jq .
|
|
||||||
}
|
agent="$1"
|
||||||
|
[[ -z "$agent" ]] && { echo "Usage: wq my-queue <agent>" >&2; exit 1; }
|
||||||
|
|
||||||
|
curl -sf "$API_URL/work?status=dispatched&agent=$agent" | jq .
|
||||||
|
|||||||
27
skill/bin/wq_project_add
Executable file
27
skill/bin/wq_project_add
Executable file
@@ -0,0 +1,27 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# wq_project_add — create a project
|
||||||
|
# Usage: wq project add <name> [--external-ref <ref>]
|
||||||
|
|
||||||
|
API_URL="${WORK_QUEUE_API_URL:-}"
|
||||||
|
[[ -z "$API_URL" ]] && API_URL=$(cat ~/.config/work_queue_api_url 2>/dev/null || echo "")
|
||||||
|
[[ -z "$API_URL" ]] && { echo "Error: WORK_QUEUE_API_URL not set" >&2; exit 1; }
|
||||||
|
|
||||||
|
name="" external_ref=""
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--external-ref) external_ref="$2"; shift 2;;
|
||||||
|
--) shift; break;;
|
||||||
|
-*) echo "Unknown option: $1" >&2; exit 1;;
|
||||||
|
*) [[ -z "$name" ]] && name="$1" || { echo "Usage: wq project add <name> [--external-ref <ref>]" >&2; exit 1; }; shift;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -z "$name" ]] && { echo "Usage: wq project add <name> [--external-ref <ref>]" >&2; exit 1; }
|
||||||
|
|
||||||
|
body="{\"name\":\"$name\"}"
|
||||||
|
[[ -n "$external_ref" ]] && body=$(echo "$body" | jq ".external_ref=\"$external_ref\"")
|
||||||
|
|
||||||
|
curl -sf -X POST "$API_URL/projects" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$body" | jq .
|
||||||
11
skill/bin/wq_project_get
Executable file
11
skill/bin/wq_project_get
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# wq_project_get — get a single project
|
||||||
|
|
||||||
|
API_URL="${WORK_QUEUE_API_URL:-}"
|
||||||
|
[[ -z "$API_URL" ]] && API_URL=$(cat ~/.config/work_queue_api_url 2>/dev/null || echo "")
|
||||||
|
[[ -z "$API_URL" ]] && { echo "Error: WORK_QUEUE_API_URL not set" >&2; exit 1; }
|
||||||
|
|
||||||
|
project_id="$1"
|
||||||
|
[[ -z "$project_id" ]] && { echo "Usage: wq project get <project_id>" >&2; exit 1; }
|
||||||
|
|
||||||
|
curl -sf "$API_URL/projects/$project_id" | jq .
|
||||||
8
skill/bin/wq_project_list
Executable file
8
skill/bin/wq_project_list
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# wq_project_list — list all projects
|
||||||
|
|
||||||
|
API_URL="${WORK_QUEUE_API_URL:-}"
|
||||||
|
[[ -z "$API_URL" ]] && API_URL=$(cat ~/.config/work_queue_api_url 2>/dev/null || echo "")
|
||||||
|
[[ -z "$API_URL" ]] && { echo "Error: WORK_QUEUE_API_URL not set" >&2; exit 1; }
|
||||||
|
|
||||||
|
curl -sf "$API_URL/projects" | jq .
|
||||||
30
skill/bin/wq_project_update
Executable file
30
skill/bin/wq_project_update
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# wq_project_update — update a project (name or external_ref)
|
||||||
|
# Usage: wq project update <project_id> [--name <name>] [--external-ref <ref>]
|
||||||
|
|
||||||
|
API_URL="${WORK_QUEUE_API_URL:-}"
|
||||||
|
[[ -z "$API_URL" ]] && API_URL=$(cat ~/.config/work_queue_api_url 2>/dev/null || echo "")
|
||||||
|
[[ -z "$API_URL" ]] && { echo "Error: WORK_QUEUE_API_URL not set" >&2; exit 1; }
|
||||||
|
|
||||||
|
project_id="" name="" external_ref=""
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--name) name="$2"; shift 2;;
|
||||||
|
--external-ref) external_ref="$2"; shift 2;;
|
||||||
|
--) shift; break;;
|
||||||
|
-*) echo "Unknown option: $1" >&2; exit 1;;
|
||||||
|
*) [[ -z "$project_id" ]] && project_id="$1"; shift;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -z "$project_id" ]] && { echo "Usage: wq project update <project_id> [--name <name>] [--external-ref <ref>]" >&2; exit 1; }
|
||||||
|
[[ -z "$name" ]] && [[ -z "$external_ref" ]] && { echo "Error: at least one of --name or --external-ref required" >&2; exit 1; }
|
||||||
|
|
||||||
|
body="{}"
|
||||||
|
[[ -n "$name" ]] && body=$(echo "$body" | jq ".name=\"$name\"")
|
||||||
|
[[ -n "$external_ref" ]] && body=$(echo "$body" | jq ".external_ref=\"$external_ref\"")
|
||||||
|
|
||||||
|
curl -sf -X PATCH "$API_URL/projects/$project_id" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$body" | jq .
|
||||||
@@ -1,35 +1,33 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# wq_stale_check — find in_progress items older than timeout, mark blocked
|
# wq_stale_check — find in_progress items older than timeout, mark blocked
|
||||||
|
|
||||||
wq_stale_check() {
|
API_URL="${WORK_QUEUE_API_URL:-}"
|
||||||
local timeout_minutes="${1:-30}"
|
[[ -z "$API_URL" ]] && API_URL=$(cat ~/.config/work_queue_api_url 2>/dev/null || echo "")
|
||||||
|
[[ -z "$API_URL" ]] && { echo "Error: WORK_QUEUE_API_URL not set" >&2; exit 1; }
|
||||||
|
|
||||||
local items
|
timeout_minutes="${1:-30}"
|
||||||
items=$(curl -sf "$API_URL/work?status=in_progress" | jq -r '.[] | @json' 2>/dev/null || echo "")
|
|
||||||
|
|
||||||
if [[ -z "$items" ]]; then
|
items=$(curl -sf "$API_URL/work?status=in_progress" | jq -r '.[] | @json' 2>/dev/null || echo "")
|
||||||
echo "No in_progress items found."
|
|
||||||
return 0
|
[[ -z "$items" ]] && { echo "No in_progress items found."; exit 0; }
|
||||||
|
|
||||||
|
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)\"}" > /dev/null
|
||||||
|
count=$((count + 1))
|
||||||
fi
|
fi
|
||||||
|
done <<< "$items"
|
||||||
|
|
||||||
local count=0
|
echo "Stale check complete: $count items marked blocked."
|
||||||
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."
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,40 +1,39 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# wq_update — update status, outcome, or notes
|
# wq_update — update status, outcome, notes, or assigned_agent on a work item
|
||||||
|
# Usage: wq update <work_item_id> [options]
|
||||||
|
# Options:
|
||||||
|
# --status <status> New status
|
||||||
|
# --outcome <outcome> success, failed, cancelled
|
||||||
|
# --notes <text> Agent notes / context
|
||||||
|
# --agent <agent> Re-assign to different agent
|
||||||
|
|
||||||
wq_update() {
|
API_URL="${WORK_QUEUE_API_URL:-}"
|
||||||
local work_id status outcome notes
|
[[ -z "$API_URL" ]] && API_URL=$(cat ~/.config/work_queue_api_url 2>/dev/null || echo "")
|
||||||
|
[[ -z "$API_URL" ]] && { echo "Error: WORK_QUEUE_API_URL not set" >&2; exit 1; }
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
work_id="" status="" outcome="" notes="" agent=""
|
||||||
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
|
while [[ $# -gt 0 ]]; do
|
||||||
echo "Usage: wq update <work_item_id> [--status <status>] [--outcome <success|failed|cancelled>] [--notes <text>]" >&2
|
case "$1" in
|
||||||
exit 1
|
--status) status="$2"; shift 2;;
|
||||||
fi
|
--outcome) outcome="$2"; shift 2;;
|
||||||
|
--notes) notes="$2"; shift 2;;
|
||||||
|
--agent) agent="$2"; shift 2;;
|
||||||
|
--) shift; break;;
|
||||||
|
-*) echo "Unknown option: $1" >&2; exit 1;;
|
||||||
|
*) [[ -z "$work_id" ]] && work_id="$1"; shift;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
if [[ -z "$status" ]] && [[ -z "$outcome" ]] && [[ -z "$notes" ]]; then
|
[[ -z "$work_id" ]] && { echo "Usage: wq update <work_item_id> [--status <status>] [--outcome <success|failed|cancelled>] [--notes <text>] [--agent <agent>]" >&2; exit 1; }
|
||||||
echo "Error: at least one of --status, --outcome, or --notes required" >&2
|
[[ -z "$status" ]] && [[ -z "$outcome" ]] && [[ -z "$notes" ]] && [[ -z "$agent" ]] && { echo "Error: at least one of --status, --outcome, --notes, or --agent required" >&2; exit 1; }
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
local body="{}"
|
body="{}"
|
||||||
[[ -n "$status" ]] && body=$(echo "$body" | jq ".status=\"$status\"")
|
[[ -n "$status" ]] && body=$(echo "$body" | jq ".status=\"$status\"")
|
||||||
[[ -n "$outcome" ]] && body=$(echo "$body" | jq ".outcome=\"$outcome\"")
|
[[ -n "$outcome" ]] && body=$(echo "$body" | jq ".outcome=\"$outcome\"")
|
||||||
[[ -n "$notes" ]] && body=$(echo "$body" | jq ".notes=\"$notes\"")
|
[[ -n "$notes" ]] && body=$(echo "$body" | jq ".notes=\"$notes\"")
|
||||||
|
[[ -n "$agent" ]] && body=$(echo "$body" | jq ".assigned_agent=\"$agent\"")
|
||||||
|
|
||||||
curl -sf -X PATCH "$API_URL/work/$work_id" \
|
curl -sf -X PATCH "$API_URL/work/$work_id" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "$body" | jq .
|
-d "$body" | jq .
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user