M A N D A L I V I A
Obsidian Lab All Notes

Meeting Prep That Actually Knows Who You're Talking To

You have a call in twenty minutes with someone you haven’t spoken to in four months. You know you talked about something important last time. You know they changed jobs at some point. You can’t remember the details. So you open their LinkedIn, scroll through your email, check your notes, and cobble together enough context to not sound like you forgot everything. It works, barely, and you spend the first five minutes of the call re-establishing ground you’ve already covered.

I built a skill in Claude Code that does this lookup in about thirty seconds. I say “prep me for my call with David,” and it pulls up his file from my vault, searches both my Gmail inboxes for recent threads, fetches his latest LinkedIn posts and role, and hands me a brief. Not a script, not a list of suggested questions — just the facts, with dates, so my brain can make the connections.

Why Pointers, Not a Script

Early versions of this skill tried to be helpful in the way AI usually tries to be helpful: it would synthesize the research into talking points, suggest questions to ask, infer how the person might be feeling about a recent job change. Every time, it was wrong in ways I could tell but it couldn’t.

The problem is asymmetric context. Claude has whatever’s in the file plus whatever’s on the public web. I have years of actual relationship history — tone, subtext, shared jokes, things said off the record. When Claude tries to connect dots, it’s guessing from a subset. When it hands me the dots and I connect them, my brain fills in everything it knows.

So the brief has three sections and nothing else:

  • What’s new — recent LinkedIn activity, role changes, company news. Each item is one line with a date and source. Framed as “what the record says,” not “what’s true” — because LinkedIn lags reality, and a clean public profile can hide a messy private story.
  • Recent email — one line per thread. Date, topic, last move. Did they reply? Did I leave something hanging?
  • Open threads — pulled from my own log entries. Intros promised, asks made, things left unresolved.

No “questions worth asking” section. No emotional framing. No “he may be feeling vindicated about his promotion.” The user’s brain does that work better than any model can.

The CRM Layer

Each person in my vault has a markdown file with frontmatter and a dated log. The frontmatter captures slow-moving facts: where they work, how we know each other, their LinkedIn handle, when we last spoke.

last_contact: 2026-04-27
cadence: 90
circle:
  - work
Company: "Acme Corp"
LinkedIn: david-profilename

The log is a running diary — voice-transcribed notes from meetings, summaries of email exchanges, things they mentioned in passing. This is richer than any CRM product because it’s editorialized. It’s not a transcript; it’s what I thought was worth remembering.

When the skill runs, it reads the last couple of log entries for quick context and checks the circle field to decide how much research to do. For work contacts, it runs web searches and LinkedIn lookups. For close friends and family, it skips the public web entirely — the file is the signal, and searching for your college roommate’s press coverage is a waste of time.

LinkedIn Enrichment

If the person’s file has a LinkedIn handle, the skill fetches their current profile and recent posts via an Apify scraper. This catches role changes the file hasn’t been updated to reflect, and surfaces recent posts that might be worth mentioning.

The output is deliberately restrained. If LinkedIn says “VP Engineering at NewCo” but the file still says “Senior Manager at OldCo,” the brief flags the discrepancy rather than auto-updating: “LinkedIn now shows VP Engineering at NewCo; file still says OldCo — unverified.” I decide whether to trust it.

Recent posts get one-line summaries with dates and engagement numbers if notable. Most people don’t post often, so this section is usually empty — and “no recent activity” is a valid output. No padding.

Email Adds the Real Context

The vault captures meetings and big moments. Email captures everything in between — the intro that went through, the link you sent that got no reply, the thread where they asked for a consultant recommendation. Searching Gmail fills gaps the vault doesn’t cover.

The skill searches both my personal and work inboxes (more on multi-account setup below) and summarizes the recent threads: date, topic, last move. This is often where the most useful context lives — not “they changed their LinkedIn headline” but “you promised to send them that article and never did.”

New Contacts

Not everyone has a file. Sometimes someone cold-emails through an introduction, or you get a calendar invite from a name you half-recognize. When the skill can’t find a match in the vault, it offers to create a stub with basic frontmatter and optionally enrich it from LinkedIn. One run and you have a working file for future reference — you don’t have to stop and do data entry.

What I Learned

The file is the moat. LinkedIn and Gmail are available to anyone with API access. The dated log of what I actually discussed with someone, what they were thinking about, what I promised to follow up on — that’s irreplaceable context that no scraper can produce. Every meeting where I capture notes makes the next prep better.

“No new info” saves time too. Early versions felt like failures when they had nothing to report. But knowing there’s no public signal since your last conversation is itself useful information — it means the conversation doesn’t need to start with “so what’s new at work?” You can pick up where you left off.

Circle-aware research prevents awkward briefs. Running a full web search on a close friend produces either nothing or irrelevant noise. The circle field lets the skill match its effort to the relationship type.


Technical Setup

The section above describes the workflow. Below is how to build it. If you’re handing this URL to Claude Code, the information below is what it needs.

Prerequisites

  • gws (Google Workspace CLI) — for searching Gmail threads. See Gmail to Vault to Calendar for installation and OAuth setup. For multi-account Gmail (personal + work), see Replying to Email Without Leaving the Terminal.
  • An Obsidian vault with a Personal CRM/ folder. Each person file needs frontmatter fields: last_contact, cadence (days), circle (friend/work/vc), LinkedIn (profile handle), email.
  • Apify account (free tier, $5/month credit) — for LinkedIn enrichment.

LinkedIn Enrichment via Apify

The skill uses HarvestAPI’s LinkedIn scrapers on the Apify platform. Two actors:

  • Profile scraper — current headline, about section, position history. ~$4 per 1,000 profiles.
  • Posts scraper — recent posts with engagement stats. ~$1.50 per 1,000 posts.

A wrapper script simplifies the calls:

# Fetch profile
apify-linkedin profile <linkedin-username> | jq '{headline, currentPosition, about: (.about[:200])}'
 
# Fetch recent posts
apify-linkedin posts <linkedin-username>

Cost per prep run is about $0.006 — effectively free for personal use.

The script needs an APIFY_TOKEN environment variable. Get it from Apify Console → Settings → Integrations.

CRM Schema

The minimum viable person file:

---
tags:
  - Type/person/known
last_contact: 2026-04-27
cadence: 90
circle:
  - work
Company: "Acme Corp"
LinkedIn: profile-handle
email:
  - [email protected]
---
 
## Background
- How you met, what makes them tick, role history.
 
## Log
### [2026-04-27](/obsidian/2026-04-27/)
- What you discussed. Key takeaways. Open threads.

last_contact uses underscores, not hyphens — Obsidian Bases formulas parse last-contact as subtraction.

cadence is numeric (30, 90, 180) — days between ideal check-ins. A blank cadence means “don’t track.” This powers a Bases view that surfaces who’s overdue for a reach-out.

circle controls research depth: work/vc gets full web + LinkedIn search. friend/family defaults to file-only (web is opt-in). Mixed tags like [friend, work] get a light LinkedIn check only.

The Prep-Meeting Skill

The skill file lives at .claude/skills/prep-meeting/SKILL.md. Key sections:

  1. Find the person — glob Personal CRM/<Name>.md, read frontmatter and recent log entries
  2. Check circle — decide whether web research makes sense for this relationship type
  3. LinkedIn enrichment — fetch profile and posts if the LinkedIn field exists. Compare headline to file, flag discrepancies
  4. Web search — targeted queries scoped to a 3-month window or since last_contact, whichever is longer
  5. Email search — search both Gmail accounts for recent threads. One-line summaries, not transcripts
  6. Write the brief — three sections (What’s new, Recent email, Open threads), terse bullets, one line each. No commentary, no questions, no emotional inference

The brief is ephemeral — it lives in the chat, never auto-written to the person’s file. Only post-meeting debriefs go into the log.

Search Window Logic

The search window is the larger of 3 months or the time since last_contact. A 3-month floor catches public signal even if you spoke recently — people discuss kids and weather over coffee, not press releases. If last contact was two years ago, the window is two years.

Items within the window are all fair game — don’t auto-skip hits older than last_contact. The user may have talked to them without ever covering that news.

Always verify article dates against the URL. Search result summaries sometimes frame old pieces as current-year news. If the URL contains /2024/, it’s from 2024.

Keep Exploring