request-radar

star 0

Track inbound requests and follow-ups directed at Leo across Outlook, Teams, ServiceNow, and Azure DevOps — who asked what, when, and whether it's still open. Use this skill whenever Leo asks to "track down requests", "what do I owe people", "follow-ups", "what's in my inbox", "what am I being asked for", "who's waiting on me", "did I reply to X", or wants a triage board of pending asks across email + chat + tickets + work items. Also use it to refresh the radar, render the task board, or mark an item resolved/snoozed/promoted. Triggers on: requests, follow-ups, follow ups, inbox triage, outstanding asks, who's waiting on me, /radar.

leonardoacosta By leonardoacosta schedule Updated 6/2/2026

name: request-radar description: >- Track inbound requests and follow-ups directed at Leo across Outlook, Teams, ServiceNow, and Azure DevOps — who asked what, when, and whether it's still open. Use this skill whenever Leo asks to "track down requests", "what do I owe people", "follow-ups", "what's in my inbox", "what am I being asked for", "who's waiting on me", "did I reply to X", or wants a triage board of pending asks across email + chat + tickets + work items. Also use it to refresh the radar, render the task board, or mark an item resolved/snoozed/promoted. Triggers on: requests, follow-ups, follow ups, inbox triage, outstanding asks, who's waiting on me, /radar. compatibility: >- Requires the bb-azure-ops operational layer (cloudpc SOCKS tunnel + Graph O365 token). Outlook/Teams need ~/.graph-token.json (Chat.Read + Mail.Read). ADO needs az --as-o365. SNOW needs /tmp/snow-cookies.txt. Each source degrades independently if its auth is down. allowed-tools: Read, Glob, Grep, Bash

request-radar

Inbound asks scatter across four channels — email, Teams chat, ServiceNow tickets, and ADO work items/PRs. They have no shared home, so things you owe people fall through the cracks. This skill keeps a persistent ledger of every request/follow-up, suggests a status for each, and renders a triage board so you can see at a glance what needs you vs what's parked elsewhere.

The design is scripts-as-data-producers + a JSON ledger as system of record. The heavy network scan runs fire-and-forget in the background; the board is the payoff at the end.

When to reach for it

  • "Track down the requests / follow-ups that came in" → run a full refresh + board.
  • "What's still open / who's waiting on me?" → status or board off the cached ledger.
  • "I replied to Tim / that's done" → resolve the item so it stops surfacing.
  • "That's real work now" → promote it into beads.
  • Session start → the nudge already printed the open/waiting counts (see hook below).

Architecture

scripts/radar.py            one CLI: scan | reconcile | board | nudge | status
                                     | resolve | open | snooze | promote | link
                                     | comment | serve
scripts/refresh.sh          background driver: token refresh -> 4 scans (parallel)
                                     -> reconcile -> render board
state/request-radar/
  ledger.json               SYSTEM OF RECORD, keyed by source+thread
  scans/<source>.json       latest raw scan per source (data producer output)
  board.html                the canvas
  last-run.json             heartbeat for the nudge

Story key = outlook:<conversationId> | teams:<chatId> | snow:<number> | ado:wi:<id> | ado:pr:<id>. Stable across runs, so reconcile dedups cleanly.

How to run a full refresh (the /radar path)

Run it in the background — token refresh alone is ~30s and the Teams scan walks ~145 chats. Do NOT block the session on it.

# fire-and-forget; you'll be notified on completion
~/.claude/skills/request-radar/scripts/refresh.sh        # refreshes O365 token first
# or, if ~/.graph-token.json is already fresh:
~/.claude/skills/request-radar/scripts/refresh.sh --no-token

On completion the driver launches the interactive server (radar.py serve) and prints its http://<tailscale-ip>:8899/ URL as the last line. Hand Leo that :8899 URL — it is read-write, so card comments + resolve/archive persist. Do NOT default to ropen: that serves the board as a read-only file server, the /api/* POSTs silently 404, and edits never stick (the recurring "board updates don't persist" footgun — fixed 2026-06-02 by serve-by-default).

# refresh.sh now echoes the :8899 interactive URL; just relay it to Leo.
# Fallback only if serve is unreachable (headless host): ropen (read-only)
ropen ~/.claude/state/request-radar/board.html

Then summarize the triage in chat: lead with the open column (what needs Leo), then waiting, then what newly resolved. Pull the list with radar.py status.

The board is live (client-side)

board.html is a static shell that polls ledger.json every 12s (co-served by ropen from the same dir) and re-renders client-side. Consequences:

  • A resolve/snooze/open/promote mutation shows on an already-open tab within ~12s — you do NOT need to re-run board or re-ropen after a mutation.
  • You only re-ropen when the shell changed (a renderer edit), not when data changed.
  • The board view is real-time; the data is only as fresh as the last scan. For continuously fresh data, pair with /loop 10m /radar (periodic background re-scan) — but that re-refreshes the O365 token + walks ~145 Teams chats each cycle, so reserve it for active triage windows.
  • Source-filter chips (Outlook/Teams/SNOW/ADO) toggle visibility client-side; degraded sources show an amber dot + the error.

Interactive mode (comments + write-back)

ropen board.html is read-only (file server — comment inputs show "read-only"). To make the board interactive, run the bundled server:

~/.claude/skills/request-radar/scripts/radar.py serve   # binds 0.0.0.0:8899, prints the Tailscale URL

Then open the printed http://<tailscale-ip>:8899/ on Leo's Mac (NOT the ropen URL). That server:

  • serves board.html + ledger.json (so polling/dates/cards all work), and
  • accepts POST /api/comment {id,text} and POST /api/status {id,action} — comments typed on a card persist straight into ledger.json. Cards show a per-note date; existing comments render above an "add a note…" input that appears on hover.

CLI equivalents: radar.py comment <id> "text" and the existing resolve/open/snooze. The server is session-scoped (launched fire-and-forget); for a durable always-on board, wrap it in a systemd-user unit.

Status model

Each story carries a suggested_status (heuristic) and a confirmed status (what the board shows). On first sight confirmed = suggested. When Leo runs resolve/open/snooze, manual=true and the override persists — unless genuinely-new activity arrives after the override (a resolved item with a new reply re-surfaces automatically).

Status Meaning Heuristic
open Needs Leo someone else spoke last with a "?" / request verb / @mention
waiting Ball elsewhere Leo spoke last, or informational with no ask
inbox Read, not act triaged as a notification/FYI — own purple lane, kept visible, never auto-hidden
resolved Done close-out language, or Leo's last msg reads as a wrap-up
archived Dismissed not an action item; hidden from the board (manual archive)
snoozed Hidden until a date snooze --days N

Triage gate (run by /radar after reconcile): triage-queue lists new, untouched items; classify each as a real request/Leo-or-DevOps responsibility → keep, or a notification → inbox. The board has four lanes — Open / Waiting / Inbox / Resolved. Per-card hover actions: resolve (✓) and archive. The inbox/keep/archive CLI + /api/status mirror these.

The heuristic is deliberately loose — it's a suggestion. Expect false-opens (standup agendas, meeting invites, OOO replies); Leo resolves them once and they stay resolved.

Dormancy rule: an Outlook/Teams ask that ages out of the 7-day window but is still open stays on the board — an unanswered ask is more urgent at day 8, not less. ADO/SNOW rows that drop out of their query vanish (the work item moved on / ticket closed).

Mutations

radar.py status                      # triage list (open + waiting), cached, no scan
radar.py resolve  <id> --note "..."  # mark done
radar.py open     <id>               # force back to open
radar.py snooze   <id> --days 7      # hide for a week
radar.py promote  <id>               # prints a `bd create` line; then `link <id> --bd-id <id>`

Source-specific notes

  • Outlook — inbox + sent in-window, collapsed per conversation. Sent items decide whether Leo replied last. Automated senders (Azure, alerts, ServiceDesk) are filtered as noise.
  • Teams — enumerate chats, scan messages per chat client-side (there is no usable server-side Teams search with our token — see bb-azure-ops graph-endpoints). 1:1s and @mentions rank high; large meeting chats rank low.
  • SNOWsc_task + incident assigned to Leo, active. Needs live cookies; degrades to empty + a clear error when stale. Re-run snow-refresh first if you want SNOW coverage.
  • ADO — work items assigned to @Me (org-wide WIQL, filtered to changed-in-window so the standing backlog doesn't flood the board) + active PRs authored by Leo (reviewer-of- others PRs are intentionally excluded — those aren't Leo's task). az --as-o365 token.

Failure handling

Every scanner exits 0 and records {"ok": false, "error": ...} so one dead source never aborts the pipeline. The board's source line shows per-source health (ok / degraded: ...). If everything is empty, check the SOCKS tunnel (ss -tlnp | grep 1080) and token freshness (bb-azure-ops § smoke test).

Install via CLI
npx skills add https://github.com/leonardoacosta/central-claude --skill request-radar
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator
leonardoacosta
leonardoacosta Explore all skills →