Meeting Prep
Orchestrates existing skills (slack-summary, source-reader, work-context
patterns) to build a cross-source prep brief for a meeting. Classifies
meeting type to determine prep depth, and triangulates context across Slack,
Gemini notes, and running docs. Output is an interactive Canvas with
prioritized talking points.
Skill Composition
This skill composes rather than reimplements. Each data-gathering step delegates to an existing skill's logic:
| Step | Delegates to | What it provides |
|---|---|---|
| Slack DMs | slack-summary Steps 2-5 |
Thread grouping, talking point priority (high/medium/low), accuracy rules |
| Meeting note docs | source-reader |
Google Doc extraction from URLs found in calendar events |
| MCP patterns | work-context |
Server names, cloud ID, read-only constraint, graceful degradation |
| Agent transcripts | Local filesystem | Search past Cursor agent conversations for undocumented work context related to the person |
Meeting Classification
Before gathering data, classify the meeting to determine prep depth:
| Type | Signals | Prep Depth |
|---|---|---|
| 1:1 | 2 attendees, "1:1", "Name / Name" | Deep -- DMs, shared topics, open loops, Gemini notes |
| Decision | "review", "approve", "prioritize", "strategy" | Deep -- options, stakeholder positions |
| Presentation | "readout", "demo", "showcase", "walkthrough" | Medium -- content preview |
| Standup | "standup", "sync", "check-in", recurring daily | Quick -- blockers only |
| Large | 10+ attendees, "all hands", "town hall" | Quick -- agenda only |
| Standard | Everything else | Medium -- recent discussion context |
For Quick meetings: skip Steps 4a (Slack DMs), 4d (Second Brain), and 4e (Agent transcripts). Only pull calendar context and Jira blockers.
For Deep meetings: run all steps plus Context Triangulation (below).
Context Triangulation
For Deep-prep meetings, don't look at each source in isolation. Connect conversations across Slack, Gemini meeting notes, and running docs using timestamps and participants.
Running Meeting Notes Docs (1:1 docs, team syncs)
- Fetch the doc and scan for entries from the last 4 weeks
- Extract topics and action items from the most recent entries
- For each topic found, search Slack for related messages from the same
time period (use
after:andbefore:date modifiers) - Present topics as continuous threads: "On Jun 18 you discussed X in the 1:1 notes, and on Jun 20 there was a Slack thread about the same topic"
Gemini Meeting Notes (auto-generated from Google Meet)
These arrive as emails from gemini-notes@google.com or
meetings-noreply@google.com.
- Search Gmail:
gmail_list_messages( query = "subject:\"<meeting title>\" (from:gemini-notes OR from:meetings-noreply)", max_results = 5 ) - Fetch the last 3-4 instances within the past month
- Extract: what was discussed, decisions made, action items with owners
- Cross-reference action items with Slack DMs and channel messages from the same time period
Recency Rules
- Regular contacts (weekly meetings, team members): last month of context
- Infrequent contacts: last interaction matters regardless of age
- Weight recent items higher: last week > two weeks ago > a month ago
- If a topic appears across multiple sources in the same week, flag it as an active thread
Read-Only Constraint
This skill MUST NEVER write, create, update, or modify data in any external
system. All MCP tool calls must be read-only operations. See work-context
for the full allowed operations list.
Autonomy Principle
This skill runs fully autonomously after Step 1. No user prompts during data gathering (Steps 2-6). If a query fails, log the error silently and continue with remaining sources.
The only acceptable user prompts are in:
- Step 1 — identifying the person
- Step 8 — presenting the Canvas
Prerequisites
Same as work-context. Additionally:
- The person must appear as a calendar attendee, Slack contact, or Jira user
Workflow
Step 1: Identify the meeting and person
Determine what the user is asking about:
| User says | Action |
|---|---|
| "prep for my 1:1" | Person = |
| "meeting prep for " | Person = |
| "what should I talk about with Megan" | Person = Megan |
| "what meeting am I in" | Run date, match current time against today's calendar |
| "prep me for my next meeting" | Find the next upcoming event on today's calendar |
| "prep for my next 1:1" | Ask the user who the meeting is with |
Read the personal-context rule (or Me.md in the workspace root) for
the user's name, email, timezone, Jira cloud ID, and MCP server names.
After identifying the meeting, classify it using the Meeting Classification table above. This determines which data sources to query.
Step 2: Resolve person across systems
Given the person's name, resolve their identity in each system. Fire these in parallel:
2a. Calendar — find email and meeting history
Server: user-google-workspace
calendar_get_events(
query = "<person name>",
time_min = "<30 days ago>",
time_max = "<7 days from now>",
max_results = 25
)
From the results:
- Match attendee names to find their email address (e.g.,
<YOUR_MANAGER_EMAIL>) - Find the last completed meeting with this person =
LAST_MEETING - Find the next upcoming meeting with this person =
NEXT_MEETING - The lookback window is:
LAST_MEETINGto now, with a floor of 7 days even whenLAST_MEETINGwas more recent than that. A 1:1 cadence and a team meeting cadence rarely line up -- a team sync or all-hands from a few days before the last 1:1 is still context the other person will assume you have. Without the floor, a 1:1 two days ago silently cuts off a same-week team meeting that happened the day before it.
If no past meeting is found, default the lookback window to 14 days.
2b. Jira — find account ID
Server: user-atlassian
lookupJiraAccountId(
cloudId = "<YOUR_ATLASSIAN_CLOUD_ID>",
searchString = "<person name or email>"
)
Store as THEIR_JIRA_ID.
2c. Slack — determine display name
The person's Slack display name is usually their first name or a variation. Use the email from Step 2a to search:
search_messages(
query = "from:<first name>",
limit = 5
)
Verify the sender matches by checking the results. Store as
THEIR_SLACK_NAME.
Step 3: Extract doc links from calendar events
Scan the description field of all calendar events found in Step 2a for
Google Doc/Slides/Drive URLs. Use source-reader's pattern matching:
| Pattern | Source type |
|---|---|
docs.google.com/document/d/<id> |
Google Doc |
docs.google.com/presentation/d/<id> |
Google Slides |
drive.google.com/file/d/<id> |
Google Drive file |
Collect all unique URLs for extraction in Step 5.
Step 4: Fire parallel data queries
Launch all of these in a single parallel batch. Do not wait for one to finish before starting another.
4a. Slack DMs — follow slack-summary logic
Server: <YOUR_SLACK_MCP_SERVER>
Follow slack-summary Steps 2-5 verbatim, scoped to this person:
Search both sides of the DM conversation:
search_messages(query = "from:<their name>", sort = "timestamp", limit = 100) search_messages(query = "to:<their name>", sort = "timestamp", limit = 100)Group messages into threads by topic — apply
slack-summaryStep 4 accuracy rules:- Every bullet must correspond to specific messages
- Use names, terms, and details exactly as they appear
- If a message is ambiguous, preserve the ambiguity
- Do not merge details from different messages unless clearly the same topic
Generate talking points — apply
slack-summaryStep 5 priority logic:- High: items they explicitly asked about or are waiting on
- Medium: topics discussed but without clear resolution
- Low: context items worth mentioning but not urgent
4b. Jira — shared tickets
Server: user-atlassian
searchJiraIssuesUsingJql(
cloudId = "<YOUR_ATLASSIAN_CLOUD_ID>",
jql = "(assignee = '{THEIR_JIRA_ID}' OR reporter = '{THEIR_JIRA_ID}') AND updated >= '{LOOKBACK_START}' ORDER BY updated DESC",
fields = ["summary", "status", "issuetype", "updated", "priority", "project"],
maxResults = 25
)
If THEIR_JIRA_ID was not resolved, skip this step.
4c. Drive — shared docs
Server: user-google-workspace
drive_search(
query = "'<their email>' in writers OR fullText contains '<person name>'",
max_results = 15
)
4d. Second Brain — wiki query
Delegate to the second-brain-wiki skill with the person's name as the
query (instead of manually scanning log.md) so this reuses the tag index
and gets a synthesized answer rather than a raw log scan.
This step must actually run, every time, even Deep-prep meetings you've already been working near in this same conversation. "I already have enough context on this person from earlier in this session" is exactly the failure mode that drops same-day or same-week meetings from a prep -- whatever you happened to read earlier was for a different purpose and was never checked against the lookback window. Run the query for real.
Tell second-brain-wiki the lookback window explicitly (not just the bare
person name), and treat its normal "cap at 5 pages" as a cap on
background material only -- every page dated inside the lookback window
must be included regardless of the cap, even if that means reading more
than 5 pages for someone with heavy tag volume. A page from today or
yesterday is never the one that gets trimmed.
4e. Agent transcripts — undocumented work context
Search past Cursor agent conversations for context related to this person that may not have made it to Jira, Slack, or Drive yet.
Transcript location:
~/.cursor/projects/<YOUR_WORKSPACE_PROJECT_ID>/agent-transcripts/
Each chat is a directory containing a <uuid>.jsonl file with the full
conversation (one JSON object per line, with role and message fields).
Search strategy:
List transcript directories sorted by modification time to find recent conversations (within the lookback window):
ls -lt ~/.cursor/projects/<YOUR_WORKSPACE_PROJECT_ID>/agent-transcripts/ | head -15Use ripgrep to search across all recent transcripts for the person's name:
rg -l "<person name>" ~/.cursor/projects/<YOUR_WORKSPACE_PROJECT_ID>/agent-transcripts/For each matching transcript, read the first 3-5 lines to extract the user's initial query and the assistant's approach. This gives the topic of the conversation without reading the entire file.
What to extract:
- What the user was working on related to this person
- Decisions made or approaches taken that haven't been documented elsewhere
- Open questions or unfinished work that could become talking points
Cap: Read at most 5 matching transcripts to avoid runaway context.
Step 5: Extract linked doc content
For each unique doc URL found in Step 3 (up to 3 docs), delegate to
source-reader for extraction:
- Follow
source-reader's source detection table to determine the type - Use
source-reader's extraction instructions for that type - Capture the extracted text as "last meeting context"
Cap at 3 docs to avoid runaway context gathering.
Step 6: Merge talking points
Combine talking points from all sources into a unified priority list:
- Start with Slack-derived talking points (from Step 4a)
- Add Jira open items as Medium priority:
- Tickets in progress that both people touch
- Tickets with status changes in the window
- Add doc open questions from extracted meeting notes as Low priority
- Deduplicate: if a Slack thread references a Jira ticket, merge them into a single talking point
- Sort: High first, then Medium, then Low
Step 7: Build Canvas
Read the Canvas skill and follow its instructions to create a .canvas.tsx
file at:
/Users/<user>/.cursor/projects/<workspace>/canvases/meeting-prep.canvas.tsx
Embed all gathered data inline.
Canvas sections:
- Header — "Prep: Evan / <Person>", next meeting time, lookback window
- Talking points — unified priority cards (high/medium/low) with source attribution and detail text. This is the primary content.
- Slack threads — grouped by topic (reuse
slack-summarythread grouping). Each thread card shows the theme, date range, and key points. - Shared Jira tickets — table with key, summary, status, last updated
- Meeting note context — extracted content from linked docs
(via
source-reader), summarized as key decisions and open questions - Agent context — summaries of recent Cursor agent conversations related to this person (undocumented work, decisions in progress)
- Last meeting — date and title of previous meeting with this person
- Coverage footer — which sources contributed data
Canvas rules:
- Import only from
cursor/canvas. No npm packages, no fetch calls. - Use
useHostTheme()tokens for all colors. No hardcoded hex. - No emojis, no gradients, no box-shadows.
- Default-export the top-level component.
- Omit sections with no data.
Step 8: Summarize in chat
Alongside the canvas, provide a brief text summary:
- Top 3 talking points with one-line descriptions
- Next meeting time
- A link to the canvas for the full prep brief
- Which sources were skipped, if any
Fallback Behavior
| Source | Primary tool | Fallback |
|---|---|---|
| Calendar | calendar_get_events |
Default to 14-day lookback if no past meeting found |
| Slack | search_messages |
Skip if person can't be found in Slack |
| Jira | searchJiraIssuesUsingJql |
Skip if lookupJiraAccountId fails |
| Drive | drive_search |
Skip; note in coverage footer |
| Second Brain | second-brain-wiki skill |
Skip; note in coverage footer |
| Agent transcripts | Filesystem read + ripgrep | Skip; note in coverage footer |
| Linked docs | source-reader extraction |
Skip individual docs that fail; note in footer |
Always report which sources were unavailable:
Sources not available for this prep: {list}. The canvas reflects only the sources that were reachable.
Common Mistakes
| Problem | Fix |
|---|---|
| Reimplementing Slack thread grouping | Follow slack-summary Steps 2-5 verbatim. Do not invent a new grouping algorithm. |
| Reimplementing doc extraction | Delegate to source-reader. Do not write custom Google Doc parsing. |
| Fixed lookback window | Use time since last meeting with this person, with a 7-day floor. Only default to 14 days if no past meeting is found. |
| Skipping Step 4d's wiki query because "I already have context on this person" | Run it anyway. Earlier context in the conversation was gathered for a different purpose and was never checked against this meeting's specific lookback window -- that's exactly how a same-day team meeting gets missed from a prep. |
Letting second-brain-wiki's cap-at-5 trim same-day or in-window pages |
The cap only applies to background material outside the lookback window. Pass the window explicitly and read every in-window page regardless of how many that is. |
| Asking the user during Steps 2-6 | Gather all data autonomously. Only prompt in Step 1 and Step 8. |
| Adding context from training data | Apply slack-summary accuracy rules: every claim must trace to a retrieved message or extracted doc. |
| Fetching too many linked docs | Cap at 3 doc extractions. Users can drill into specifics later. |
| Using wrong MCP server names | Jira/Confluence: user-atlassian. Slack: <YOUR_SLACK_MCP_SERVER>. Google: user-google-workspace. |