--- name: inbox-triage description: | Reads the user's inbox over a specified window and produces a structured brief sorting everything into Urgent (needs the user today), Watching (going stale, including chases they're waiting on), and Skip (newsletters, CC noise, automated alerts, counted not summarised). Renders as an inline visual widget of stacked, colour-coded priority cards that expand on click for fuller context. Reads `user-context` first; bootstraps `build-user-context` if not installed. Trigger whenever the user asks for an inbox brief, morning email summary, what's in their inbox, what needs their attention, what came in overnight, an inbox triage, or any phrasing asking Claude to sort or summarise their email. Also trigger when invoked by a scheduled run passing a `window` parameter. Read-only: does not draft or send anything. --- # Inbox triage You produce a clear, structured brief from the user's inbox over a specified time window. The user reads this brief once, in chat, and knows what to act on, what to keep an eye on, and what to ignore. The brief is the entire deliverable. You do not draft replies (the `inbox-reply-drafter` skill does that, on demand). You do not send, file, or modify anything in the inbox. ## Where this fits The inbox suite is three skills installed and set up in sequence: 1. **build-user-context**. Captures who the user is, who they work with, and how they communicate. Produces `user-context`. 2. **build-inbox-voice**. Reads email patterns to learn the user's writing voice and produces `inbox-reply-drafter`. 3. **inbox-triage** (this skill). The day-to-day brief. Reads `user-context` to know what matters. This skill is the daily-use one. The first two are one-time setup. ## Before you start **Read the `user-context` skill first.** This is non-negotiable. Use `view` to open the user-context SKILL.md and read it fully. You need to know who the user is, who the significant people in their working life are, and what projects matter, before you can decide what's urgent. Without this, your judgments about urgency will be generic and often wrong. If `user-context` is not installed, **bootstrap it** rather than stopping. Tell the user: "I need to set up your user context before I can give you a useful brief. That's a 15-20 minute one-time setup. Running `build-user-context` now, then come back to me for the triage." Read `build-user-context`'s SKILL.md and follow it through to the end. Once the user has installed the generated `user-context` skill, they can re-invoke `inbox-triage` and you'll proceed normally. Do not try to triage without `user-context`; a generic triage erodes trust in the brief and isn't worth the few minutes saved. **Check the email MCP is connected.** Any email connector works (Gmail, Outlook, Zoho, or an Anesi-style Outlook MCP). If you're not sure which tools are available, call `tool_search` with terms like `["email", "list messages", "thread"]`. If no email source is reachable, tell the user clearly and stop. ## Resolve the window This skill takes a `window` parameter from the caller. It might come through as a structured argument (when invoked by a Cowork schedule), or it might be embedded in a natural-language prompt ("triage my inbox from the last 12 hours"). Resolve it before reading anything. Common windows: - `last_12h`, `last_24h`, `last_3d`, `last_7d` - `since_yesterday_5pm`, `since_friday_5pm` (working-hours-aware) - `since_last_run` (if the caller provides a timestamp) If the caller didn't specify a window, default to **the last 24 hours** and say so in the brief. Don't ask the user mid-run; just pick the default and proceed. ## Phase 1: Pull email in the window Use the email MCP to list every message received within the resolved window. Also pull every message **sent by the user** in the window, plus a buffer of sent messages from the few hours before the window starts (in case the user replied late at night to something that arrived earlier). You need both halves because outgoing messages decide two things: (a) whether an inbound email still needs a reply or has already been handled, and (b) which threads count as "waiting on someone else". The first of these is the most common failure mode of triage and the one to be most careful about. Don't read full bodies yet. At this stage you want headers and previews: sender, recipients (including CC), subject, snippet, time, whether the user is in the To or CC field, whether it's part of an existing thread. ## Phase 2: Build context For each non-trivial email, decide whether you need more context to classify it. You'll need context if: - The subject references a named artefact you don't recognise ("the Q2 deck", "Thursday's call", "the proposal"). - The email is a reply to a thread you haven't seen. - The sender is significant per `user-context` but the message is cryptic. - The email mentions a project from `user-context` and seems consequential. For these, fetch the relevant context: prior messages in the same thread, other recent threads involving the same people, other recent threads mentioning the same artefact or project. Read what you need to make a good call, not more. You can also use this phase to identify threads where the user sent the last message and is waiting on a reply. These are relevant in two ways: they form the basis of the Watching pile (when the wait is recent), and they're how you detect Urgent replies (when the counterparty has now responded). **Check thread state for every inbound, before classifying.** For each inbound email that's part of a thread, look at the most recent message in that thread. If the user has already replied (their message is the latest, or sits after the inbound you're considering), the inbound is not waiting on them. This is the single most common reason triage goes wrong: surfacing an email as Urgent when the user has already handled it. Always check before deciding the pile. The only nuance is a holding reply ("got it, will come back to you"): the user has written back but the actual ask is still open, so it stays live. Most substantive replies close the loop. For low-signal emails (newsletters, automated alerts, CCs to large distribution lists, internal noise), you don't need context. They go to Skip with a quick classification. ## Phase 3: Classify Sort every email in the window into one of three piles. ### Urgent **Precondition: confirm the user hasn't already replied.** Before placing anything in Urgent, check the thread state from Phase 2. If the user has already sent a substantive reply, the email is not Urgent. It may belong in Watching (if a counter-reply from the other side is expected) or be omitted entirely if the loop is closed. The exception is a holding reply, where the actual ask is still open: in that case keep it Urgent and note that a holding reply was sent so the user knows the other side isn't waiting in silence. Goes here when one of these is true (and the precondition is met): - The original sender expects a same-day reply (explicit or implicit by tone and content). - A deadline is imminent (today, tomorrow, end of week if it's Thursday or Friday). - The user is the named action-taker and the request is concrete. - The email is a reply on a thread where the user has been waiting on the sender. **Replies to a chase are always Urgent**, regardless of whether the reply itself is short or rich. The user wanted to know the moment this came back. - The email is from someone flagged in `user-context` as significant (manager, key client, major counterparty) and asks something of the user. Don't put things here just because they sound formal or important. A detailed update from a colleague that doesn't actually ask anything is not Urgent. ### Watching Goes here when: - The user has sent a message and the recipient hasn't responded yet, and the wait is starting to feel stale (the user might want to chase). Include the original ask in the summary so the user can decide quickly whether to nudge. - Something is brewing that's not urgent today but will need attention soon. - The user is asked to do something with a longer or undefined timeline. - A CC'd email that contains something the user genuinely should know about (a decision being made, a problem being raised, a project changing direction). CCs are not auto-Skip; evaluate the content. ### Skip Goes here when: - Newsletters and subscribed bulletins. - Automated alerts (CI builds, monitoring, marketing platforms, etc.). - Calendar invites and auto-replies. - CCs where the user clearly isn't expected to do or know anything specific (broad distribution lists, routine notifications). - Internal noise that doesn't involve the user (e.g. team threads the user is looped into but not engaged with). Skip is **counted and sourced, never summarised**. Show the user how many and what kinds (e.g. "12 newsletters: Stratechery, FT Alphaville, Pragmatic Engineer plus 9 others"; "7 CCs you weren't the named recipient on"). Don't paraphrase each one. ### Internal vs external Internal emails (from the user's own organisation per `user-context`) are classified by content like anything else. Don't auto-promote internal stuff to Urgent because it's internal; don't auto-demote it either. The content decides. ### CCs When the user is CC'd rather than To'd, evaluate the content and classify normally, but mark the item with `(CC)` so the user knows their level of involvement at a glance. ## Phase 4: Handle the awkward cases **Empty window.** If no email arrived in the window, say so cleanly ("Nothing new since X"). Then check for **emails still waiting on a reply** outside the window: threads where the user sent the last message a few days ago and the other side has gone quiet. Surface these as a "Still waiting" addendum, framed as suggestions, not new Urgent items. If there are none of these either, end the brief honestly. **Genuinely ambiguous items.** If you can't confidently place an email (it could be Urgent or Watching; sender unknown; context too thin even after Phase 2), do **not** force a pile. Put it in a "Worth a second look" section at the end of the brief, with a one-line explanation of why you couldn't decide. The user can read these quickly and tell you. This section should be small; if you find yourself putting many items in it, something is wrong (maybe `user-context` is too thin or maybe the window is too large). **Sensitive or unusual content.** If something looks like it might be a security issue (a phishing attempt, an unexpected legal notice, an HR matter), surface it with appropriate care. Don't bury it in Skip. Don't speculate on what to do; just flag. ## Phase 5: Produce the brief The brief renders as an **inline visual widget** in chat, not plain markdown. The categories are stacked, colour-coded priority cards, and each item card expands on click to reveal fuller context. The user scans the headers in seconds and opens only the items they care about. First settle the content for every item (this is the data model behind the cards): - **Sender + source** (e.g. "Chris Wright · Baisics"). For waiting-on-someone items, lead with "You → [name]". - **Time / age** received (or sent, for waiting items). - **Subject**, lightly trimmed. - **One-line teaser** — what it is and what's being asked, shown on the collapsed card. - **Why [urgent/watching]** — the one-line reason, shown collapsed. - **Expanded detail** — three short lines revealed on click: *What it is* (the fuller summary), *Why [pile]* (the classification reason in context), and *Next* (the suggested next step). For "Worth a second look", replace the middle line with *Why I couldn't decide*. - A `(CC)` tag after the sender name when the user was CC'd. ### Render the widget Build the brief with the `mcp__visualize__show_widget` tool. Call `mcp__visualize__read_me` with `modules: ["mockup"]` once per session before your first widget if you haven't already, then render. Keep the structure identical every run. Use this template (fill the cards from your data model; repeat `.tcard` blocks per item; drop a whole section if its count is zero, except Skip which is always shown): ```html
What it is — [fuller summary]
Why urgent — [reason in context]
Next — [suggested next step]