silverstein/minutes-opencode-minutes-prep
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.
This is a copy. The original lives at silverstein/minutes-.opencode-minutes-prep.
npx skills add https://github.com/silverstein/minutes --skill minutes-prep
Before running helper scripts or opening bundled references, set:
export MINUTES_SKILLS_ROOT="$(git rev-parse --show-toplevel)/.opencode/skills"
export MINUTES_SKILL_ROOT="$MINUTES_SKILLS_ROOT/minutes-prep"
Interactive meeting preparation that searches the normal conversation history
authorized by Minutes, synthesizes a relationship brief, and produces talking
points — before you walk into the room. Restricted history is excluded from
this agent workflow by default and must never be treated as absent evidence.
This is a multi-phase interactive flow, not a single command. Walk the user through each phase using AskUserQuestion, pushing back on vague answers.
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):
gog calendar list --today --json -a <account> 2>/dev/null
3. Apple Calendar (osascript) (every Mac, zero install):
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:
command -v m365 2>/dev/null && m365 status --output json 2>/dev/null
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?"
m365 login --authType browser and wait. Once done, proceed to fetch events below.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:
event.subjectevent.start.dateTime (UTC — convert to local time for display)event.attendees[].emailAddress.name (extract first names)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:
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.
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:
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 authorized normal meeting history:
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 the normal meeting history Minutes is authorized to use."
If the answer is a topic ("the pricing meeting", "the Q2 planning call"):
→ Adapt to topic-based prep:
minutes search "<topic>" --limit 20
Skip the relationship brief and go straight to a topic brief instead.
Treat search paths as hints, not capabilities. Reauthorize each selected normal meeting through minutes get "<exact path>" --json, require exit status 0, and use only its returned content. Never reopen a search path with the host Read tool. Build a relationship brief from those bounded native results:
Meeting history:
Recurring topics:
Open commitments:
action_items where assignee matches the user)action_items where assignee matches the other person)open)Decision history:
decisions: frontmatter)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.
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.
Save the prep brief to ~/.minutes/preps/ for later pickup by /minutes-debrief:
mkdir -p ~/.minutes/preps
Write the file as ~/.minutes/preps/YYYY-MM-DD-{person-first-name}.prep.md with this structure:
---
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:
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.
End with three beats:
"You said '[exact quote from their AskUserQuestion answers]' — that's your north star for this call."
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."
/minutes-debrief to capture what you decided and compare it to what you planned."node "$MINUTES_SKILLS_ROOT/_runtime/hooks/lib/minutes-learn-cli.mjs" set-explicit workflow_preference meeting_prep_mode prep "User explicitly prefers prep"
node "$MINUTES_SKILLS_ROOT/_runtime/hooks/lib/minutes-learn-cli.mjs" set-alias "Case Wintermute" "Case" "User confirmed alias"
gcal.mcp.claude.com/mcp) is the recommended source for Claude users.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.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.sarah not sarah-chen. Transcript attendees are often abbreviated. Debrief matches on first name./minutes-debrief ignores preps older than 48 hours. The user can prep the day before and still get the debrief connection.Take silverstein/minutes-opencode-minutes-prep from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.