| name | meeting-memo |
| description | Create structured meeting memos from transcription files (.vtt, .txt, .srt) and save them to the second-brain knowledge system. Use when the user asks to create a memo from a meeting, process a meeting transcription, document a check-in or 1:1, or write meeting notes. Triggers on phrases like "create a memo", "meeting memo", "memo from meeting", "process transcription", "meeting notes from", "check-in with". Optionally enriches context by fetching linked Notion project pages. Output is an Obsidian-compatible markdown file with frontmatter, structured sections, action items, and knowledge graph connections. |
| args | [{"name":"vault_path","description":"Absolute path to Obsidian vault root directory","required":true,"example":"/Users/<<user>>/Documents/second-brain"},{"name":"meetings_dir","description":"Relative path from vault root to meetings directory","required":true,"default":"2-Areas/Meetings"},{"name":"daily_notes_dir","description":"Relative path from vault root to daily notes directory","required":true,"default":"DailyNotes"}] |
Meeting Memo
Transform meeting transcriptions into structured, insight-rich memos integrated into the second-brain knowledge system.
Skill Invocation
Use the Skill tool with structured parameters:
/meeting-memo \
--vault-path "/Users/<<user>>/Documents/second-brain" \
--meetings-dir "2-Areas/Meetings" \
--daily-notes-dir "DailyNotes"
Or use the shorthand invocation:
Create a memo from this meeting
When using shorthand, Claude will prompt for any missing required parameters or use defaults.
Workflow
Step 0: Parse and Validate Skill Arguments (MANDATORY)
MANDATORY FIRST STEP: Extract and validate all skill parameters before proceeding:
import sys
from pathlib import Path
skills_path = Path(__file__).parent.parent
sys.path.insert(0, str(skills_path / "_shared"))
from validation import (
validate_vault_config,
sanitize_participant_name,
sanitize_meeting_title,
sanitize_meeting_metadata,
normalize_unicode,
validate_no_script_mixing,
validate_notion_url,
validate_file_size,
validate_directory_safe,
safe_yaml_frontmatter,
sanitize_wikilink,
atomic_append_to_file,
ValidationError,
SecurityError
)
vault_path = Path(args.vault_path).expanduser()
meetings_dir = args.meetings_dir
daily_notes_dir = args.daily_notes_dir
if not vault_path.exists():
raise ValidationError(f"Vault path does not exist: {vault_path}")
if not vault_path.is_dir():
raise ValidationError(f"Vault path is not a directory: {vault_path}")
meetings_full = vault_path / meetings_dir
daily_notes_full = vault_path / daily_notes_dir
validate_directory_safe(meetings_full, vault_path)
validate_directory_safe(daily_notes_full, vault_path)
CRITICAL SECURITY STEP: All inputs MUST be sanitized before processing to prevent path traversal, YAML injection, and other attacks. NEVER skip this step, even if inputs appear to come from trusted sources. Defense in depth is essential.
Step 1: Gather and Validate Inputs
-
Transcription file. Read the .vtt, .srt, or .txt file. If the user has a file open in the IDE, use that. Otherwise ask for the path.
- SECURITY: Validate file size before reading:
validate_file_size(file_path)
-
Project reference (optional). If the user provides a Notion URL, validate and fetch it:
if notion_url:
safe_url = validate_notion_url(notion_url)
-
Meeting folder. Determine and validate the output path using validated skill args:
safe_person = sanitize_participant_name(person_name)
safe_date = date
safe_title = sanitize_meeting_title(title)
target_dir = vault_path / meetings_dir / safe_person
validate_directory_safe(target_dir, vault_path)
target_dir.mkdir(parents=True, exist_ok=True)
filename = f"{safe_date} {safe_title}.md"
output_path = target_dir / filename
Run steps 1 and 2 in parallel when both inputs are available.
Step 2: Analyze and Sanitize Transcription
Extract from the raw transcription and sanitize all extracted data:
-
Participants -- identify speakers from <v> tags or context
raw_participants = extract_participants(transcription)
participants = [sanitize_participant_name(p) for p in raw_participants]
-
Topics discussed -- group by theme, not chronology
-
Decisions made -- explicit commitments or conclusions
-
Action items -- who committed to what, by when
-
Risks and blockers -- anything flagged as a concern
-
Strategic discussions -- substantive debates, divergent viewpoints, unresolved questions
-
Numbers and specifics -- costs, timelines, percentages, thresholds
-
Case studies or examples -- client situations, parallel cases discussed
-
Cross-references -- mentions of people, projects, tools, or documents
raw_wikilinks = extract_wikilinks(content)
safe_wikilinks = [sanitize_wikilink(link) for link in raw_wikilinks]
Handle bilingual transcriptions (Polish/English mix is common). Transcription quality may be low -- infer meaning from context, do not reproduce garbled text.
Step 3: Enrich with Project Context
When a Notion project page is available:
- Compare discussed status against the project's documented scope and milestones
- Note discrepancies between the project plan and the conversation
- Use the project's deliverable list as a checklist for the status table
Step 4: Write the Memo with Secure Frontmatter
Follow the format in references/memo-format.md.
SECURITY: Generate frontmatter using the safe YAML function:
frontmatter = safe_yaml_frontmatter(
title=f"{person_name} / {other_person} - {topic}",
description=meeting_description,
person_name=person_name,
date=date,
project=project_name if project_name else None,
tags=['meeting']
)
memo_content = frontmatter + "\n\n" + body_content
Key principles:
- Decisions over discussion. Prioritize what was decided and committed.
- Risks and blockers first. Surface anything that could delay progress.
- Quantify. Capture numbers -- costs, timelines, thresholds.
- Preserve strategic tension. Document disagreements faithfully; do not flatten into consensus.
- Action items with owners. Every commitment needs a name and deadline.
- Connect to the knowledge graph. Link to people, projects, organizations via
[[wikilinks]] (all sanitized).
- Polish diacritics. Always use ą, ć, ę, ł, ń, ó, ś, ź, ż when writing Polish.
Include only sections that have substance. Do not pad with empty sections.
Step 5: Save, Journal Entry, and Report
-
Write the memo to the appropriate path in the second-brain:
output_path.write_text(memo_content, encoding='utf-8')
-
Add a journal entry to the daily note with file locking to prevent race conditions (CVE-3):
daily_note_path = vault_path / daily_notes_dir / year / month / f"{date}.md"
journal_entry = f"- [[{person_name}]] -- [[{filename}|{topic}]]\n"
journal_entry += " - Key takeaway 1\n"
journal_entry += " - Key takeaway 2\n"
journal_entry += " - Key takeaway 3\n"
atomic_append_to_file(daily_note_path, journal_entry)
The entry must include:
- The meeting link line (person wikilink + memo link + short topic)
- A nested bullet list of key takeaways (3-5 bullets): decisions made, risks surfaced, and critical action items. Keep each bullet to one concise sentence.
-
Report a concise summary to the user: key takeaways, number of action items, any risks surfaced.
Tone
Crisp, authoritative, consulting-grade. No filler, no meta-commentary. Professional clarity.