Some checks failed
Lint & Format / detect (push) Has been cancelled
Lint & Format / js-lint (push) Has been cancelled
Lint & Format / py-lint (push) Has been cancelled
Lint & Format / sh-lint (push) Has been cancelled
Lint & Format / go-lint (push) Has been cancelled
Monorepo Lint / lint-shell (push) Has been cancelled
Monorepo Lint / lint-js (push) Has been cancelled
RoadChain-SHA2048: 2bb8ae01244552ce RoadChain-Identity: alexa@sovereign RoadChain-Full: 2bb8ae01244552cecab2d00dcd4d9be800c7f72efd48c0042d181cbe095735be688df2988e3e51f8e6ea2562aaf6e4a842be17389c27b9c02b0680dec7fb1a89a8117868ffa1457a937dccb40674e118d9394e4131c5000ee0e516109be0623b37d46fa3eb1f2c8916755e5060648c3dee42a48ca180464f284d95844a67c9e1708a1b4da531c5135deaa21f9bbe0de9e1695c09909c55c4f395cbbacde4778f9e4620cbb7b055d7b8b41dbf384076cd3944d7561aebb6dc8720ab432c2720722e162a6a9d08c1decf429b62e1ff12a6517b569ddc8e0664bd785ff1f56c7c9735fef29c60c9899bc1fb55945e9ee015d9cae0ef5023519ac656f29fa8e27a2e
372 lines
14 KiB
Bash
372 lines
14 KiB
Bash
#!/bin/bash
|
|
# BlackRoad Agent Dispatch — Task dispatch, sprint board, work queue
|
|
# Usage: memory-agent-dispatch.sh <command> [args]
|
|
set -e
|
|
|
|
COLLAB_DB="$HOME/.blackroad/collaboration.db"
|
|
TASKS_DB="$HOME/.blackroad/memory/tasks.db"
|
|
PROJECTS_DIR="$HOME/.blackroad/memory/infinite-todos/projects"
|
|
SESSION_FILE="$HOME/.blackroad/memory/current-collab-session"
|
|
JOURNAL="$HOME/.blackroad/memory/journals/master-journal.jsonl"
|
|
SLACK_API="https://blackroad-slack.amundsonalexa.workers.dev"
|
|
TODOS="$HOME/blackroad-operator/scripts/memory/memory-infinite-todos.sh"
|
|
COLLAB="$HOME/blackroad-operator/scripts/memory/memory-collaboration.sh"
|
|
|
|
PINK='\033[38;5;205m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
CYAN='\033[0;36m'
|
|
RED='\033[0;31m'
|
|
BOLD='\033[1m'
|
|
DIM='\033[2m'
|
|
NC='\033[0m'
|
|
|
|
sql_collab() { sqlite3 "$COLLAB_DB" "$@" 2>/dev/null; }
|
|
sql_tasks() { sqlite3 "$TASKS_DB" "$@" 2>/dev/null; }
|
|
|
|
get_session() {
|
|
[[ -f "$SESSION_FILE" ]] && cat "$SESSION_FILE" || echo "unknown"
|
|
}
|
|
|
|
post_to_slack() {
|
|
curl -s --max-time 3 --connect-timeout 2 \
|
|
-X POST "$SLACK_API/post" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$(jq -n --arg t "$1" '{text:$t}')" >/dev/null 2>&1 || true
|
|
}
|
|
|
|
log_journal() {
|
|
local action="$1" entity="$2" details="$3"
|
|
local ts
|
|
ts=$(date -u +"%Y-%m-%dT%H:%M:%S.3NZ")
|
|
local hash
|
|
hash=$(echo -n "$ts$action$entity$details" | shasum -a 256 | cut -c1-16)
|
|
printf '{"timestamp":"%s","action":"%s","entity":"%s","details":"%s","hash":"%s"}\n' \
|
|
"$ts" "$action" "$entity" "$details" "$hash" >> "$JOURNAL" 2>/dev/null || true
|
|
}
|
|
|
|
# ── DISPATCH ──
|
|
cmd_dispatch() {
|
|
local project_id="$1"
|
|
[[ -z "$project_id" ]] && { echo -e "${RED}Usage: $0 dispatch <project-id>${NC}"; exit 1; }
|
|
|
|
local pf="$PROJECTS_DIR/${project_id}.json"
|
|
if [[ ! -f "$pf" ]]; then
|
|
echo -e "${RED}Project not found: $project_id${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
local next_todo next_id
|
|
next_todo=$(jq -r '[.todos[] | select(.status == "pending")][0].text // empty' "$pf")
|
|
next_id=$(jq -r '[.todos[] | select(.status == "pending")][0].todo_id // empty' "$pf")
|
|
|
|
if [[ -z "$next_todo" ]]; then
|
|
echo -e "${GREEN}Project $project_id has no pending todos!${NC}"
|
|
return
|
|
fi
|
|
|
|
local title
|
|
title=$(jq -r '.title' "$pf")
|
|
local session
|
|
session=$(get_session)
|
|
|
|
echo -e "${PINK}[DISPATCH]${NC} ${BOLD}$title${NC}"
|
|
echo -e " ${YELLOW}→${NC} [$next_id] $next_todo"
|
|
echo ""
|
|
|
|
# Post to Slack
|
|
post_to_slack "🎯 *Task Dispatched* [$session]\nProject: *$title*\nTodo: $next_todo\nID: $next_id" &
|
|
|
|
# Log
|
|
log_journal "dispatch" "$project_id" "Dispatched: $next_todo ($next_id)"
|
|
echo -e " ${GREEN}Dispatched and announced${NC}"
|
|
}
|
|
|
|
# ── QUEUE ──
|
|
cmd_queue() {
|
|
echo -e "${PINK}╔════════════════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${PINK}║${NC} ${BOLD}Available Work Queue${NC} ${PINK}║${NC}"
|
|
echo -e "${PINK}╚════════════════════════════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
|
|
# Project todos
|
|
echo -e " ${BOLD}📋 Project Todos:${NC}"
|
|
local found=0
|
|
for f in "$PROJECTS_DIR"/*.json; do
|
|
[[ ! -f "$f" ]] && continue
|
|
local status
|
|
status=$(jq -r '.status' "$f")
|
|
[[ "$status" != "active" ]] && continue
|
|
|
|
local pending
|
|
pending=$(jq '[.todos[] | select(.status == "pending")] | length' "$f")
|
|
[[ "$pending" -eq 0 ]] && continue
|
|
|
|
local pid title progress
|
|
pid=$(jq -r '.project_id' "$f")
|
|
title=$(jq -r '.title' "$f")
|
|
progress=$(jq -r '.progress' "$f")
|
|
|
|
echo -e " ${CYAN}$pid${NC} — $title (${progress}%, $pending pending)"
|
|
jq -r '[.todos[] | select(.status == "pending")][0:3][] | " ⬜ [\(.todo_id)] \(.text)"' "$f"
|
|
found=1
|
|
done
|
|
[[ "$found" -eq 0 ]] && echo -e " ${GREEN}No pending project todos${NC}"
|
|
echo ""
|
|
|
|
# Marketplace tasks
|
|
if [[ -f "$TASKS_DB" ]]; then
|
|
echo -e " ${BOLD}🏪 Marketplace Tasks:${NC}"
|
|
local available
|
|
available=$(sql_tasks "SELECT count(*) FROM tasks WHERE status='available';")
|
|
echo -e " $available available task(s)"
|
|
|
|
sql_tasks "SELECT task_id, title FROM tasks WHERE status='available' ORDER BY created_at DESC LIMIT 5;" 2>/dev/null | while IFS='|' read -r tid title; do
|
|
echo -e " ${BLUE}$tid${NC}: $title"
|
|
done
|
|
fi
|
|
echo ""
|
|
}
|
|
|
|
# ── CLAIM ──
|
|
cmd_claim() {
|
|
local task_id="$1"
|
|
[[ -z "$task_id" ]] && { echo -e "${RED}Usage: $0 claim <task-id>${NC}"; exit 1; }
|
|
|
|
if [[ ! -f "$TASKS_DB" ]]; then
|
|
echo -e "${RED}Task marketplace DB not found${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
local session
|
|
session=$(get_session)
|
|
local now
|
|
now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
|
|
sql_tasks "UPDATE tasks SET status='claimed', claimed_by='$session', claimed_at='$now' WHERE task_id='$task_id' AND status='available';"
|
|
|
|
local affected
|
|
affected=$(sql_tasks "SELECT changes();")
|
|
if [[ "$affected" -gt 0 ]]; then
|
|
local title
|
|
title=$(sql_tasks "SELECT title FROM tasks WHERE task_id='$task_id';")
|
|
echo -e "${GREEN}Claimed:${NC} $title"
|
|
post_to_slack "📌 *Task Claimed* [$session]: $title" &
|
|
log_journal "task-claim" "$task_id" "Claimed by $session: $title"
|
|
else
|
|
echo -e "${YELLOW}Could not claim $task_id (may not exist or already claimed)${NC}"
|
|
fi
|
|
}
|
|
|
|
# ── PROGRESS ──
|
|
cmd_progress() {
|
|
local msg="$*"
|
|
[[ -z "$msg" ]] && { echo -e "${RED}Usage: $0 progress <message>${NC}"; exit 1; }
|
|
|
|
local session
|
|
session=$(get_session)
|
|
local now
|
|
now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
|
|
# Log to collab DB
|
|
if [[ -f "$COLLAB_DB" ]]; then
|
|
local msg_id="prog-$(date +%s)-$$"
|
|
sql_collab "INSERT INTO messages (msg_id, session_id, type, message, created_at) VALUES ('$msg_id', '$session', 'progress', '$(echo "$msg" | sed "s/'/''/g")', '$now');"
|
|
sql_collab "UPDATE sessions SET last_seen='$now' WHERE session_id='$session';"
|
|
fi
|
|
|
|
echo -e "${GREEN}Progress:${NC} $msg"
|
|
post_to_slack "⚡ *Progress* [$session]: $msg" &
|
|
log_journal "progress" "$session" "$msg"
|
|
}
|
|
|
|
# ── DONE ──
|
|
cmd_done() {
|
|
local todo_id="$1"
|
|
local project_id="$2"
|
|
|
|
if [[ -z "$todo_id" ]]; then
|
|
echo -e "${RED}Usage: $0 done <todo-id> [project-id]${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# If project not specified, search for it
|
|
if [[ -z "$project_id" ]]; then
|
|
for f in "$PROJECTS_DIR"/*.json; do
|
|
[[ ! -f "$f" ]] && continue
|
|
if jq -e ".todos[] | select(.todo_id == \"$todo_id\")" "$f" >/dev/null 2>&1; then
|
|
project_id=$(jq -r '.project_id' "$f")
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [[ -z "$project_id" ]]; then
|
|
echo -e "${RED}Could not find todo $todo_id in any project${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Complete via infinite todos
|
|
"$TODOS" complete-todo "$project_id" "$todo_id" 2>/dev/null || true
|
|
|
|
local session
|
|
session=$(get_session)
|
|
echo -e "${GREEN}Completed:${NC} $todo_id in $project_id"
|
|
post_to_slack "✅ *Todo Done* [$session]: $todo_id in $project_id" &
|
|
log_journal "todo-complete" "$project_id" "Completed $todo_id"
|
|
|
|
# Show next todo
|
|
local pf="$PROJECTS_DIR/${project_id}.json"
|
|
if [[ -f "$pf" ]]; then
|
|
local next
|
|
next=$(jq -r '[.todos[] | select(.status == "pending")][0].text // empty' "$pf")
|
|
if [[ -n "$next" ]]; then
|
|
echo -e "${YELLOW}Next:${NC} $next"
|
|
else
|
|
echo -e "${GREEN}All todos complete for $project_id!${NC}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ── SPRINT ──
|
|
cmd_sprint() {
|
|
echo -e "${PINK}╔════════════════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${PINK}║${NC} ${BOLD}Sprint Board${NC} ${PINK}║${NC}"
|
|
echo -e "${PINK}╚════════════════════════════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
|
|
# In Progress — active sessions with focus
|
|
echo -e " ${BOLD}🔄 In Progress:${NC}"
|
|
if [[ -f "$COLLAB_DB" ]]; then
|
|
sql_collab "SELECT session_id, focus FROM sessions WHERE status='active' AND focus != '' ORDER BY last_seen DESC LIMIT 10;" | while IFS='|' read -r sid focus; do
|
|
echo -e " ${GREEN}●${NC} ${CYAN}${sid}${NC} → $focus"
|
|
done
|
|
local no_focus
|
|
no_focus=$(sql_collab "SELECT count(*) FROM sessions WHERE status='active' AND (focus='' OR focus IS NULL);")
|
|
[[ "$no_focus" -gt 0 ]] && echo -e " ${DIM}+ $no_focus session(s) without focus${NC}"
|
|
fi
|
|
echo ""
|
|
|
|
# Claimed marketplace tasks
|
|
if [[ -f "$TASKS_DB" ]]; then
|
|
local claimed
|
|
claimed=$(sql_tasks "SELECT count(*) FROM tasks WHERE status='claimed';")
|
|
if [[ "$claimed" -gt 0 ]]; then
|
|
echo -e " ${BOLD}📌 Claimed Tasks:${NC}"
|
|
sql_tasks "SELECT task_id, title, claimed_by FROM tasks WHERE status='claimed' ORDER BY claimed_at DESC LIMIT 5;" | while IFS='|' read -r tid title by; do
|
|
echo -e " ${BLUE}$tid${NC}: $title ${DIM}(${by})${NC}"
|
|
done
|
|
echo ""
|
|
fi
|
|
fi
|
|
|
|
# Up Next — pending todos from top priority projects
|
|
echo -e " ${BOLD}📋 Up Next:${NC}"
|
|
local count=0
|
|
for f in "$PROJECTS_DIR"/*.json; do
|
|
[[ ! -f "$f" ]] && continue
|
|
[[ "$count" -ge 5 ]] && break
|
|
local status
|
|
status=$(jq -r '.status' "$f")
|
|
[[ "$status" != "active" ]] && continue
|
|
local pending
|
|
pending=$(jq '[.todos[] | select(.status == "pending")] | length' "$f")
|
|
[[ "$pending" -eq 0 ]] && continue
|
|
|
|
local pid next_todo
|
|
pid=$(jq -r '.project_id' "$f")
|
|
next_todo=$(jq -r '[.todos[] | select(.status == "pending")][0].text' "$f")
|
|
echo -e " ⬜ ${CYAN}$pid${NC}: $next_todo"
|
|
((count++))
|
|
done
|
|
echo ""
|
|
|
|
# Done today
|
|
if [[ -f "$COLLAB_DB" ]]; then
|
|
local done_today
|
|
done_today=$(sql_collab "SELECT count(*) FROM messages WHERE type='progress' AND created_at > datetime('now', '-24 hours');")
|
|
echo -e " ${BOLD}✅ Done today:${NC} $done_today progress updates"
|
|
sql_collab "SELECT message FROM messages WHERE type='progress' AND created_at > datetime('now', '-24 hours') ORDER BY created_at DESC LIMIT 5;" | while read -r msg; do
|
|
echo -e " ✓ $msg"
|
|
done
|
|
fi
|
|
}
|
|
|
|
# ── AUTOQUEUE ──
|
|
cmd_autoqueue() {
|
|
echo -e "${PINK}[DISPATCH]${NC} ${BOLD}Auto-generating tasks from projects...${NC}"
|
|
echo ""
|
|
|
|
if [[ ! -f "$TASKS_DB" ]]; then
|
|
echo -e "${RED}Task marketplace DB not found${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
local added=0
|
|
for f in "$PROJECTS_DIR"/*.json; do
|
|
[[ ! -f "$f" ]] && continue
|
|
local status
|
|
status=$(jq -r '.status' "$f")
|
|
[[ "$status" != "active" ]] && continue
|
|
|
|
local pid title
|
|
pid=$(jq -r '.project_id' "$f")
|
|
title=$(jq -r '.title' "$f")
|
|
|
|
# Get pending todos not already in marketplace
|
|
jq -r '.todos[] | select(.status == "pending") | "\(.todo_id)|\(.text)"' "$f" | while IFS='|' read -r tid text; do
|
|
local existing
|
|
existing=$(sql_tasks "SELECT count(*) FROM tasks WHERE task_id='${pid}-${tid}';")
|
|
if [[ "$existing" -eq 0 ]]; then
|
|
local now
|
|
now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
sql_tasks "INSERT INTO tasks (task_id, title, description, status, priority, category, created_at) VALUES ('${pid}-${tid}', '$(echo "$text" | sed "s/'/''/g")', 'From project: $title', 'available', 'medium', '$pid', '$now');" 2>/dev/null || true
|
|
echo -e " ${GREEN}+${NC} ${CYAN}${pid}-${tid}${NC}: $text"
|
|
((added++)) || true
|
|
fi
|
|
done
|
|
done
|
|
|
|
echo ""
|
|
echo -e "${GREEN}Added $added task(s) to marketplace${NC}"
|
|
}
|
|
|
|
# ── HELP ──
|
|
cmd_help() {
|
|
cat <<EOF
|
|
${PINK}╔════════════════════════════════════════════════════════════╗${NC}
|
|
${PINK}║${NC} ${BOLD}BlackRoad Agent Dispatch${NC} ${PINK}║${NC}
|
|
${PINK}╚════════════════════════════════════════════════════════════╝${NC}
|
|
|
|
${BOLD}Task Management:${NC}
|
|
${CYAN}dispatch <project>${NC} Dispatch next todo for a project
|
|
${CYAN}queue${NC} Show all available work
|
|
${CYAN}claim <task-id>${NC} Claim a marketplace task
|
|
${CYAN}done <todo-id>${NC} Mark a todo as complete
|
|
|
|
${BOLD}Progress:${NC}
|
|
${CYAN}progress <msg>${NC} Post progress update (DB + Slack)
|
|
${CYAN}sprint${NC} Sprint board: in-progress, next, done
|
|
|
|
${BOLD}Automation:${NC}
|
|
${CYAN}autoqueue${NC} Auto-generate marketplace tasks from projects
|
|
EOF
|
|
}
|
|
|
|
case "${1:-help}" in
|
|
dispatch|dis) shift; cmd_dispatch "$@" ;;
|
|
queue|q) cmd_queue ;;
|
|
claim|c) shift; cmd_claim "$@" ;;
|
|
progress|prog) shift; cmd_progress "$@" ;;
|
|
done|complete) shift; cmd_done "$@" ;;
|
|
sprint|sp) cmd_sprint ;;
|
|
autoqueue|auto) cmd_autoqueue ;;
|
|
help|--help|-h) cmd_help ;;
|
|
*)
|
|
echo -e "${RED}Unknown: $1${NC}"
|
|
cmd_help
|
|
exit 1
|
|
;;
|
|
esac
|