minutes-prep

$npx mdskill add silverstein/minutes/minutes-prep

Interactive meeting preparation synthesizes relationship and talking points

  • Identifies upcoming meetings in user's calendar to prepare for a specific one-on-one call.
  • Deploys Claude AI and Google Calendar API to gather relevant information from past interactions.
  • Synthesizes a brief overview of the user's relationship with the meeting participant and potential discussion points.
  • Delivers synthesized content directly to the user, enhancing their preparation for the upcoming call.

SKILL.md

.github/skills/minutes-prepView on GitHub ↗
---
name: minutes-prep
description: Interactive meeting preparation — builds a relationship brief and talking points before a call. Use when the user says "prep me for my call with", "I'm meeting with X", "prepare me for", "what should I bring up with", "meeting prep", "get ready for my call", or wants to review history with someone before a meeting.
---

## Skill Path

Before running helper scripts or opening bundled references, set:

```bash
export MINUTES_SKILLS_ROOT="$(git rev-parse --show-toplevel)/.agents/skills/minutes"
export MINUTES_SKILL_ROOT="$MINUTES_SKILLS_ROOT/minutes-prep"
```

# /minutes-prep

Interactive meeting preparation that searches your entire conversation history with someone, synthesizes a relationship brief, and produces talking points — before you walk into the room.

## How it works

This is a multi-phase interactive flow, not a single command. Walk the user through each phase using AskUserQuestion, pushing back on vague answers.

### Phase 0: Calendar auto-detect (optional, runs first)

Before asking who the user is meeting with, check if upcoming meetings are available from any calendar source. Try these in order — use the first that works:

**1. Google Calendar MCP** (best — most Minutes users have Claude + MCP):
If `mcp__claude_ai_Google_Calendar__gcal_list_events` is available, query today's remaining events:
```
gcal_list_events(
  timeMin: "<now ISO, e.g. 2026-03-19T14:00:00>",
  timeMax: "<end of day, e.g. 2026-03-19T23:59:59>",
  condenseEventDetails: false
)
```
Do NOT hardcode a timezone — omit the `timeZone` parameter so the MCP uses the user's calendar default. This returns attendees, event titles, and times. Parse the results to find the next upcoming meeting with other people (skip all-day events and events with no attendees).

**2. `gog` CLI** (if installed):
```bash
gog calendar list --today --json -a <account> 2>/dev/null
```

**3. Apple Calendar (osascript)** (every Mac, zero install):
```bash
osascript -e 'tell application "Calendar" to get {summary, start date} of (every event of every calendar whose start date >= (current date) and start date < ((current date) + 1 * days))'
```

**4. Microsoft 365 / Outlook (`m365` CLI)** (if installed):

First check if m365 is installed and whether it's logged in:
```bash
command -v m365 2>/dev/null && m365 status --output json 2>/dev/null
```

- **If not installed**: skip silently to the next source.
- **If installed but not logged in** (`status` returns `"Logged out"`): ask the user via AskUserQuestion:
  > "I found the Microsoft 365 CLI (`m365`) but you're not logged in. Want me to start the login flow so I can pull your Outlook calendar?"
  - If **yes**: run `m365 login --authType browser` and wait. Once done, proceed to fetch events below.
  - If **no**: skip to the next source silently.
- **If installed and logged in**: fetch today's remaining events:
```bash
m365 outlook event list \
  --userId "@meId" \
  --startDateTime "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  --endDateTime "$(date -u -d '+1 day' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+1d +%Y-%m-%dT%H:%M:%SZ)" \
  --output json 2>/dev/null
```
Only use the result if the command exits with code 0 and returns a non-empty JSON array. Parse the response:
- **Title**: `event.subject`
- **Start time**: `event.start.dateTime` (UTC — convert to local time for display)
- **Attendees**: `event.attendees[].emailAddress.name` (extract first names)
- **Skip if**: `event.isAllDay === true`, `event.isCancelled === true`, `event.attendees` is empty, or `event.start.dateTime` converted to **local** time is not today (the `now`..`now+24h` UTC window over-fetches so it never misses a local-today event near the UTC date boundary; this trims the next-day-local spillover)

**5. None available** — skip to Phase 1 and ask manually.

**If upcoming meetings are found:**
Present via AskUserQuestion: "I see you have these meetings coming up today:
- [time] — [title] with [attendees]
- [time] — [title] with [attendees]

Which one are you prepping for?"

Options: list each meeting + "None of these — I want to prep for something else"

If the user picks a meeting, auto-populate the person name and skip Phase 1. Pull attendee names from the calendar event directly.

**If no upcoming meetings or calendar unavailable:**
Silently skip to Phase 1. Don't error or apologize — just ask manually.

### Phase 1: Who are you meeting with?

If Phase 0 already identified the person, skip this phase.

Otherwise, ask via AskUserQuestion: "Who are you meeting with?"

**If the answer is specific** (a name like "Alex" or "Case"):
→ Check for learned aliases first:
```bash
node "$MINUTES_SKILLS_ROOT/_runtime/hooks/lib/minutes-learn-cli.mjs" aliases "<name>" 2>/dev/null
```
If aliases exist, search across every returned variant and merge the hits before deciding there is no history.

→ Search all past meetings:
```bash
minutes search "<name>" --limit 50
```
Also search common variations — first name, last name, nickname.

**If the answer is vague** ("the team", "everyone", "my usual meeting"):
→ Push back: "Be specific. Name one person who'll be in the room. I'll search everything you've discussed with them."

**If the answer is a topic** ("the pricing meeting", "the Q2 planning call"):
→ Adapt to topic-based prep:
```bash
minutes search "<topic>" --limit 20
```
Skip the relationship brief and go straight to a topic brief instead.

### Phase 2: Relationship brief

Read each matching meeting file with `Read`. Build a relationship brief:

**Meeting history:**
- Total meetings with this person, first and most recent dates
- Meeting frequency trend (increasing/decreasing/stable)

**Recurring topics:**
- Topics that come up across multiple meetings
- Topic trending: which topics are appearing MORE recently (↑) vs fading (↓)
- Flag any topic that appeared 3+ times in the last 2 weeks — "Something's on their mind"

**Open commitments:**
- Items YOU owe THEM (from `action_items` where assignee matches the user)
- Items THEY owe YOU (from `action_items` where assignee matches the other person)
- Flag any overdue items (due date in the past, status still `open`)

**Decision history:**
- Recent decisions involving this person (from `decisions:` frontmatter)
- Any volatile decisions (changed 2+ times) — flag as "still in flux"

Present the relationship brief to the user. Don't ask for approval — just show it and move to Phase 3.

**If there are zero past meetings:**
Say: "I don't have any recorded meetings with [name]. This will be your first meeting on record. What's the context — how do you know them?"

Then skip the relationship brief and go straight to Phase 3 with whatever context the user provides.

### Phase 3: What do you want to accomplish?

Ask via AskUserQuestion: "What's the one thing you'd regret not discussing in this meeting?"

**If the answer is specific** ("finalize the pricing at monthly billing", "get a commitment on the hire"):
→ Frame talking points around that goal. Connect it to relationship data — e.g., "Alex's mentioned pricing 3 times recently. She's ready for this conversation."

**If the answer is vague** ("just catch up", "the usual"):
→ Push back with evidence: "Based on your last 3 meetings with Alex, these topics are active: [list]. Which one matters most today?"

**If the user skips** ("I don't know yet" / "nothing specific"):
→ Accept it. Frame as an open-ended catch-up. Still produce talking points based on open items and recent topics.

### Phase 4: Save prep file

Save the prep brief to `~/.minutes/preps/` for later pickup by `/minutes-debrief`:

```bash
mkdir -p ~/.minutes/preps
```

Write the file as `~/.minutes/preps/YYYY-MM-DD-{person-first-name}.prep.md` with this structure:

```markdown
---
person: {full name}
date: {today ISO}
goal: {what they want to accomplish}
meeting_count: {total past meetings}
---

## Relationship Brief
{the brief from Phase 2}

## Talking Points
{the talking points}

## Goal
{their stated goal, quoted}
```

Set permissions to 0600:
```bash
chmod 600 ~/.minutes/preps/YYYY-MM-DD-{slug}.prep.md
```

Use the person's first name (lowercase) as the slug — e.g., `sarah`, not `sarah-chen` — because transcript attendee names are often abbreviated.

### Phase 5: Closing ritual

End with three beats:

1. **Signal reflection** — Quote a specific thing the user said during the session.
   "You said '[exact quote from their AskUserQuestion answers]' — that's your north star for this call."

2. **Assignment** — One concrete real-world action before the meeting. Not "go build it" — something specific.
   Examples: "Text Alex before your call that you want to finalize pricing."
   "Review the competitor grid Case sent you — it's still in your action items."

3. **Next skill nudge** — "After your call, run `/minutes-debrief` to capture what you decided and compare it to what you planned."

## Gotchas

- **Record explicit workflow preferences when the user states them.** If the user says "default to prep", "I want the deep version by default", or "always do prep instead of brief", persist it:

```bash
node "$MINUTES_SKILLS_ROOT/_runtime/hooks/lib/minutes-learn-cli.mjs" set-explicit workflow_preference meeting_prep_mode prep "User explicitly prefers prep"
```

- **Record explicit aliases when the user clarifies a person mismatch.** If the user says "Sarah Chen is just Sarah" or "Case and Case Wintermute are the same person", persist it before searching again:

```bash
node "$MINUTES_SKILLS_ROOT/_runtime/hooks/lib/minutes-learn-cli.mjs" set-alias "Case Wintermute" "Case" "User confirmed alias"
```

- **Calendar auto-detect is best-effort** — If no calendar source is available, silently fall back to asking manually. Never error or nag the user about calendar setup. The Google Calendar MCP (`gcal.mcp.claude.com/mcp`) is the recommended source for Claude users.
- **m365 login is non-blocking** — Only prompt for `m365 login` if m365 is installed but the user is not yet authenticated. It's a one-time browser flow. If the user declines, fall through silently.
- **m365 event window is timezone-safe via over-fetch + local filter** — the fetch window is `now`..`now+24h` in UTC, which always spans the rest of the user's local day regardless of offset (max UTC offset is under 14h). Times come back in UTC, so convert `start.dateTime` to local both for the local-today skip check and for display. Use `date -d` (Linux) or `date -jf` / `date -r` (macOS) for conversion, or show the raw UTC time with a `(UTC)` suffix if conversion is unavailable.
- **Calendar attendee names may differ from meeting transcript names** — Calendar says "Alex Chen ([email protected])" but transcripts say "Alex" or "SPEAKER_0". Match on first name.
- **Skip all-day events and solo events** — Only show events with 2+ attendees as prep candidates. All-day events and events where the user is the only attendee aren't meetings.
- **Push back on vague answers** — This is the most important pattern. Vague prep = useless prep. "Everyone" → "Name one person." "Catch up" → "What would you regret not discussing?"
- **First-name slug for prep files** — Use `sarah` not `sarah-chen`. Transcript attendees are often abbreviated. Debrief matches on first name.
- **Zero past meetings is not an error** — It's a first meeting. Adjust the flow, don't apologize.
- **Don't hallucinate meeting history** — If you searched and found nothing, say so. Never invent meetings or conversations that don't appear in the search results.
- **Prep files are sensitive** — They contain relationship intelligence. Always 0600 permissions.
- **Multiple people in the meeting** — If the user names 2-3 people, search for each and combine the briefs. For >3, suggest picking the most important person to focus on.
- **Stale prep files** — `/minutes-debrief` ignores preps older than 48 hours. The user can prep the day before and still get the debrief connection.

More from silverstein/minutes

SkillDescription
minutes-briefFast non-interactive briefing before any meeting — auto-detects your next calendar event, pulls relationship history, surfaces open commitments, and produces a one-page brief in under 30 seconds. Use this whenever the user says "brief me", "give me a quick brief", "what's coming up", "background on my next call", "who am I meeting next", "brief me on Sarah", "I have a call in 10 min", "quick rundown", or right before walking into a meeting. Different from /minutes-prep — brief is the fast hook-fireable version that doesn't ask questions and doesn't set goals. Use brief when speed matters; use prep when the user wants to think hard about goals first.
minutes-cleanupManage old recordings — find large files, archive old meetings, delete processed originals. Use when the user says "clean up recordings", "how much space are meetings using", "delete old recordings", "archive meetings", "manage meeting storage", or asks about disk space from minutes.
minutes-copilotStart and control Minutes Coach, the separate real-time copilot HUD, with an explicit meeting goal. Use only for explicit Coach or HUD lifecycle requests such as "start Minutes Coach", "open the Coach HUD", "pause Minutes Coach", "resume Minutes Coach", "Minutes Coach status", or "stop Minutes Coach". Do not use for requests that explicitly ask the current terminal agent to watch or strategize; those belong to minutes-live-sidekick. An ambiguous request such as "coach me live" requires one short surface clarification and must not automatically start Coach.
minutes-debriefPost-meeting debrief — analyzes what happened, compares outcomes to your prep intentions, tracks decision evolution. Use when the user says "debrief", "what just happened in that meeting", "what did we decide", "debrief that call", "post-meeting", "what changed", or right after stopping a recording.
minutes-graphCross-meeting entity graph — query who/what/when across all your meetings as structured data, with co-occurrence and cross-entity queries that text search can't answer. Use whenever the user says "show me everyone who mentioned X", "all mentions of Y across meetings", "who knows about Z", "graph", "across all meetings", "entity search", "first time we talked about", "trend for X over time", "who's been mentioned alongside", or wants to query meetings as an index rather than full-text search. Builds a JSON entity index on first run (one-time slow), then answers queries instantly. Surface this skill for relationship intelligence, due diligence, or any "across all my history" question that text search alone can't answer.
minutes-ideasSurface recent voice memos and ideas captured from any device. Use when the user asks "what ideas did I have?", "what were my recent memos?", "what did I record while walking?", or wants to recall a captured thought.
minutes-ingestExtract facts from meetings and update your knowledge base — person profiles, chronological log, and index. Use when the user asks "ingest my meetings", "update my knowledge base", "extract facts from meetings", "sync meetings to wiki", "backfill knowledge", or wants their PARA/Obsidian/wiki profiles updated from conversation data.
minutes-lintHealth-check your meeting knowledge for contradictions, stale commitments, and decision conflicts. Use when the user asks "any conflicts in my meetings", "check for stale action items", "lint my meetings", "consistency check", "are there contradictions", or wants to audit their decision history.
minutes-listList recent meetings and voice memos. Use when the user asks "what meetings did I have", "show my recent recordings", "any meetings today", "list my voice memos", or wants an overview of their meeting history. Also use when they need to find a specific meeting by browsing rather than searching.
minutes-live-sidekickAct as the user's live meeting sidekick inside the current terminal agent session. Use when the user explicitly asks you, the terminal agent, to watch a meeting, follow the live transcript, answer during the call, offer strategist thoughts, silently watch for risks, or track decisions. Do not use this skill to start or control the separate Minutes Coach HUD; explicit Coach or HUD lifecycle requests belong to minutes-copilot, and an ambiguous request such as "coach me live" requires one short surface clarification.