Files
blackroad/operator/memory/memory-planner.sh
Alexa Amundson 6777292f6e
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
sync: 2026-03-16 18:00 — 21 files from Alexandria
RoadChain-SHA2048: c316572452cf6246
RoadChain-Identity: alexa@sovereign
RoadChain-Full: c316572452cf6246294a9bf089ca177403d4187333252430e919cc2f55176f80722ae82807051dbc5772fa50a393bfc6ba5f1dc464d9ae0a435082d60da68a8f61c7521bc0fbc0509bf9e043035456b80763949d1e1e30cca278c3b264e78a417b62be8636feeda64580bd6eac8adede7fcb7c095eadc922a1057710484f98d08f347104bf31434f66ced3da243d480c58a4dfa0cc068ddfda54acadde593e3964f2a6ee69283eb62557638056c9e69be5cf70d8d9698b4c07f76c3f2d91e713cff5568eef41c51991648e8e28e2e0523cf04269cb5be393cae9250b285c968b97e330f5ddf91836d3f67555a147108c594274f1878fe4dc8f47ec4b2291b82f
2026-03-16 18:00:02 -05:00

445 lines
17 KiB
Bash
Executable File

#!/bin/bash
# BlackRoad Planner — Project planning, focus tracking, standups, wrap-ups
# Usage: memory-planner.sh <command> [args]
set -e
COLLAB_DB="$HOME/.blackroad/collaboration.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() { sqlite3 "$COLLAB_DB" "$@" 2>/dev/null; }
get_session() {
[[ -f "$SESSION_FILE" ]] && cat "$SESSION_FILE" || echo ""
}
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
}
# ── PLAN ──
cmd_plan() {
local project_id="$1"
[[ -z "$project_id" ]] && { echo -e "${RED}Usage: $0 plan <project-id>${NC}"; exit 1; }
local project_file="$PROJECTS_DIR/${project_id}.json"
if [[ ! -f "$project_file" ]]; then
echo -e "${RED}Project not found: $project_id${NC}"
echo -e "${YELLOW}Available:${NC}"
ls "$PROJECTS_DIR"/*.json 2>/dev/null | while read -r f; do
local pid
pid=$(jq -r '.project_id' "$f")
local title
title=$(jq -r '.title' "$f")
local prog
prog=$(jq -r '.progress' "$f")
echo -e " ${CYAN}$pid${NC}$title (${prog}%)"
done
exit 1
fi
local title cadence status progress
title=$(jq -r '.title' "$project_file")
cadence=$(jq -r '.timescale' "$project_file")
status=$(jq -r '.status' "$project_file")
progress=$(jq -r '.progress' "$project_file")
echo -e "${PINK}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${PINK}${NC} ${BOLD}$title${NC}"
echo -e "${PINK}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo -e " ${BOLD}ID:${NC} ${CYAN}${project_id}${NC} | ${BOLD}Cadence:${NC} ${cadence} | ${BOLD}Status:${NC} ${status}"
# Progress bar
local bar_len=40
local filled=$(( progress * bar_len / 100 ))
local empty=$(( bar_len - filled ))
local bar="${GREEN}"
for ((i=0; i<filled; i++)); do bar+="█"; done
bar+="${DIM}"
for ((i=0; i<empty; i++)); do bar+="░"; done
bar+="${NC}"
echo -e " ${BOLD}Progress:${NC} $bar ${progress}%"
echo ""
# Todos
local total_todos done_todos pending_todos
total_todos=$(jq '.todos | length' "$project_file")
done_todos=$(jq '[.todos[] | select(.status == "completed")] | length' "$project_file")
pending_todos=$(jq '[.todos[] | select(.status == "pending")] | length' "$project_file")
echo -e " ${BOLD}Todos:${NC} $done_todos/$total_todos done, $pending_todos pending"
echo ""
# Show completed
if [[ "$done_todos" -gt 0 ]]; then
echo -e " ${GREEN}Completed:${NC}"
jq -r '.todos[] | select(.status == "completed") | " ✅ \(.text)"' "$project_file"
echo ""
fi
# Show pending
if [[ "$pending_todos" -gt 0 ]]; then
echo -e " ${YELLOW}Pending:${NC}"
jq -r '.todos[] | select(.status == "pending") | " ⬜ [\(.todo_id)] \(.text)"' "$project_file"
echo ""
fi
}
# ── NEXT ──
cmd_next() {
echo -e "${PINK}[PLANNER]${NC} ${BOLD}Finding next action...${NC}"
echo ""
local best_project="" best_todo="" best_priority=999 best_progress=999
for f in "$PROJECTS_DIR"/*.json; do
[[ ! -f "$f" ]] && continue
local status
status=$(jq -r '.status' "$f")
[[ "$status" != "active" ]] && continue
local progress pending_count cadence pid title
progress=$(jq -r '.progress' "$f")
pending_count=$(jq '[.todos[] | select(.status == "pending")] | length' "$f")
[[ "$pending_count" -eq 0 ]] && continue
cadence=$(jq -r '.timescale' "$f")
pid=$(jq -r '.project_id' "$f")
title=$(jq -r '.title' "$f")
# Priority: weekly=1, monthly=2, forever=3
local priority=3
case "$cadence" in
weekly) priority=1 ;;
monthly) priority=2 ;;
esac
if [[ "$priority" -lt "$best_priority" ]] || \
[[ "$priority" -eq "$best_priority" && "$progress" -lt "$best_progress" ]]; then
best_priority=$priority
best_progress=$progress
best_project="$pid"
best_todo=$(jq -r '[.todos[] | select(.status == "pending")][0].text' "$f")
fi
done
if [[ -n "$best_project" ]]; then
echo -e " ${BOLD}Next up:${NC} ${CYAN}$best_project${NC} (${best_progress}%)"
echo -e " ${YELLOW}${NC} $best_todo"
echo ""
echo -e " ${DIM}Set focus: $0 focus $best_project${NC}"
else
echo -e " ${GREEN}All projects complete or no pending todos!${NC}"
fi
}
# ── FOCUS ──
cmd_focus() {
local project_id="$1"
[[ -z "$project_id" ]] && { echo -e "${RED}Usage: $0 focus <project-id>${NC}"; exit 1; }
local session
session=$(get_session)
if [[ -z "$session" ]]; then
echo -e "${RED}No active session. Run: memory-collaboration.sh register${NC}"
exit 1
fi
# Update session focus
if [[ -f "$COLLAB_DB" ]]; then
sql "UPDATE sessions SET focus='$project_id' WHERE session_id='$session';"
fi
# Get project title
local title=""
local pf="$PROJECTS_DIR/${project_id}.json"
[[ -f "$pf" ]] && title=$(jq -r '.title' "$pf")
echo -e "${GREEN}Focus set:${NC} ${CYAN}${project_id}${NC}${title:+ — $title}"
# Announce to Slack
"$COLLAB" announce "Focus: $project_id${title:+ — $title}" 2>/dev/null || true
}
# ── STANDUP ──
cmd_standup() {
echo -e "${PINK}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${PINK}${NC} ${BOLD}Standup Report${NC} ${PINK}${NC}"
echo -e "${PINK}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
# What was done (last handoff picked up by this session)
echo -e " ${BOLD}✅ What was done (last handoff):${NC}"
if [[ -f "$COLLAB_DB" ]]; then
local last_handoff
last_handoff=$(sql "SELECT message FROM handoffs WHERE picked_up_by != '' ORDER BY picked_up_at DESC LIMIT 1;")
if [[ -n "$last_handoff" ]]; then
echo -e " $last_handoff"
else
echo -e " ${DIM}No handoff found${NC}"
fi
fi
echo ""
# What's planned (next action)
echo -e " ${BOLD}🎯 What's planned:${NC}"
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 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"
done
echo ""
# Blockers
echo -e " ${BOLD}🚧 Blockers:${NC}"
echo -e " ${DIM}(add manually or check memory for known issues)${NC}"
echo ""
# Post to Slack
echo -e " ${DIM}Post to Slack: $0 standup --slack${NC}"
if [[ "$1" == "--slack" ]]; then
local msg="📋 *Standup Report*\n"
msg="${msg}✅ Done: $(sql "SELECT message FROM handoffs WHERE picked_up_by != '' ORDER BY picked_up_at DESC LIMIT 1;" 2>/dev/null || echo 'N/A')\n"
msg="${msg}🎯 Next: $(cmd_next 2>/dev/null | grep "→" | head -1 || echo 'checking...')"
post_to_slack "$msg"
echo -e " ${GREEN}Posted to Slack${NC}"
fi
}
# ── ROADMAP ──
cmd_roadmap() {
echo -e "${PINK}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${PINK}${NC} ${BOLD}BlackRoad Project Roadmap${NC} ${PINK}${NC}"
echo -e "${PINK}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
for cadence in weekly monthly forever; do
local label icon
case "$cadence" in
weekly) label="WEEKLY"; icon="📆" ;;
monthly) label="MONTHLY"; icon="📊" ;;
forever) label="FOREVER"; icon="♾️" ;;
esac
local found=0
for f in "$PROJECTS_DIR"/*.json; do
[[ ! -f "$f" ]] && continue
local fc
fc=$(jq -r '.timescale' "$f")
[[ "$fc" != "$cadence" ]] && continue
if [[ "$found" -eq 0 ]]; then
echo -e " ${BOLD}$icon $label${NC}"
found=1
fi
local pid title progress status pending
pid=$(jq -r '.project_id' "$f")
title=$(jq -r '.title' "$f")
progress=$(jq -r '.progress' "$f")
status=$(jq -r '.status' "$f")
pending=$(jq '[.todos[] | select(.status == "pending")] | length' "$f")
local color="$GREEN"
[[ "$progress" -lt 50 ]] && color="$YELLOW"
[[ "$progress" -lt 20 ]] && color="$RED"
[[ "$progress" -eq 100 ]] && color="$GREEN"
local bar=""
local filled=$(( progress / 10 ))
for ((i=0; i<filled; i++)); do bar+="█"; done
for ((i=filled; i<10; i++)); do bar+="░"; done
echo -e " ${color}${bar}${NC} ${progress}% ${CYAN}${pid}${NC}$title ($pending pending)"
done
[[ "$found" -gt 0 ]] && echo ""
done
}
# ── REVIEW ──
cmd_review() {
echo -e "${PINK}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${PINK}${NC} ${BOLD}Session Review${NC} ${PINK}${NC}"
echo -e "${PINK}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
local session
session=$(get_session)
echo -e " ${BOLD}Session:${NC} ${CYAN}${session:-unknown}${NC}"
echo ""
if [[ -f "$COLLAB_DB" && -n "$session" ]]; then
# Messages sent this session
echo -e " ${BOLD}Messages sent:${NC}"
sql "SELECT type, message, created_at FROM messages WHERE session_id='$session' ORDER BY created_at;" | while IFS='|' read -r type msg created; do
echo -e " [${created:11:8}] ${CYAN}${type}${NC}: ${msg:0:70}"
done
# Handoffs created
echo ""
echo -e " ${BOLD}Handoffs left:${NC}"
sql "SELECT message FROM handoffs WHERE from_session='$session';" | while read -r msg; do
echo -e " 🤝 $msg"
done
fi
# Recent journal entries
if [[ -f "$JOURNAL" ]]; then
echo ""
echo -e " ${BOLD}Recent journal activity:${NC}"
tail -10 "$JOURNAL" | while read -r line; do
local action entity ts
action=$(echo "$line" | jq -r '.action')
entity=$(echo "$line" | jq -r '.entity')
ts=$(echo "$line" | jq -r '.timestamp' | cut -dT -f2 | cut -d. -f1)
echo -e " [${ts}] ${action}${entity}"
done
fi
}
# ── SUGGEST ──
cmd_suggest() {
echo -e "${PINK}[PLANNER]${NC} ${BOLD}Analyzing projects...${NC}"
echo ""
local suggestions=()
for f in "$PROJECTS_DIR"/*.json; do
[[ ! -f "$f" ]] && continue
local status
status=$(jq -r '.status' "$f")
[[ "$status" != "active" ]] && continue
local pid progress pending cadence title
pid=$(jq -r '.project_id' "$f")
progress=$(jq -r '.progress' "$f")
pending=$(jq '[.todos[] | select(.status == "pending")] | length' "$f")
cadence=$(jq -r '.timescale' "$f")
title=$(jq -r '.title' "$f")
[[ "$pending" -eq 0 ]] && continue
local reason=""
if [[ "$cadence" == "weekly" && "$progress" -lt 100 ]]; then
reason="⚡ Weekly deadline — ${progress}% done"
elif [[ "$progress" -lt 20 ]]; then
reason="🔴 Barely started — needs attention"
elif [[ "$progress" -lt 50 ]]; then
reason="🟡 Under halfway — good opportunity"
fi
if [[ -n "$reason" ]]; then
echo -e " ${CYAN}$pid${NC}$title"
echo -e " $reason ($pending pending todos)"
echo ""
fi
done
}
# ── WRAP ──
cmd_wrap() {
local session
session=$(get_session)
if [[ -z "$session" ]]; then
echo -e "${RED}No active session to wrap${NC}"
exit 1
fi
echo -e "${PINK}[PLANNER]${NC} ${BOLD}Wrapping up session...${NC}"
# Gather session activity
local msgs=""
if [[ -f "$COLLAB_DB" ]]; then
msgs=$(sql "SELECT type, message FROM messages WHERE session_id='$session' ORDER BY created_at;" 2>/dev/null)
fi
# Auto-generate handoff
local handoff_msg="Session $session wrap-up:"
if [[ -n "$msgs" ]]; then
handoff_msg="$handoff_msg | Activity: $(echo "$msgs" | wc -l | tr -d ' ') messages"
local last_announce
last_announce=$(sql "SELECT message FROM messages WHERE session_id='$session' AND type='announce' ORDER BY created_at DESC LIMIT 1;" 2>/dev/null)
[[ -n "$last_announce" ]] && handoff_msg="$handoff_msg | Last: $last_announce"
fi
# Get current focus
local focus
focus=$(sql "SELECT focus FROM sessions WHERE session_id='$session';" 2>/dev/null)
[[ -n "$focus" ]] && handoff_msg="$handoff_msg | Focus was: $focus"
echo -e " ${BOLD}Handoff:${NC} $handoff_msg"
echo ""
# Create handoff
"$COLLAB" handoff "$handoff_msg" 2>/dev/null || true
# Mark session complete
"$COLLAB" complete 2>/dev/null || true
echo -e "${GREEN}Session wrapped up and handed off!${NC}"
}
# ── HELP ──
cmd_help() {
cat <<EOF
${PINK}╔════════════════════════════════════════════════════════════╗${NC}
${PINK}║${NC} ${BOLD}BlackRoad Planner${NC} ${PINK}║${NC}
${PINK}╚════════════════════════════════════════════════════════════╝${NC}
${BOLD}Planning:${NC}
${CYAN}plan <project-id>${NC} Show full project plan with todos
${CYAN}next${NC} What to work on next (auto-prioritized)
${CYAN}roadmap${NC} All projects grouped by cadence
${CYAN}suggest${NC} AI-suggested priorities
${BOLD}Session:${NC}
${CYAN}focus <project-id>${NC} Set session focus + announce
${CYAN}standup [--slack]${NC} Generate standup report
${CYAN}review${NC} Review this session's activity
${CYAN}wrap${NC} End session: auto-handoff + complete
EOF
}
case "${1:-help}" in
plan) shift; cmd_plan "$@" ;;
next) cmd_next ;;
focus) shift; cmd_focus "$@" ;;
standup) shift 2>/dev/null || true; cmd_standup "$@" ;;
roadmap|map) cmd_roadmap ;;
review|rev) cmd_review ;;
suggest|sug) cmd_suggest ;;
wrap|end) cmd_wrap ;;
help|--help|-h) cmd_help ;;
*)
echo -e "${RED}Unknown: $1${NC}"
cmd_help
exit 1
;;
esac