Markitdown
ImCa0/just-laws
Convert files and office documents to Markdown. An agent skill from ImCa0/just-laws.
Summarize any content (YouTube video, article, whitepaper/PDF, podcast episode, book chapter, etc.) into a rich Obsidian note with section-by-section breakdowns, wikilinks to all technical concepts…
$ npx skills add reysu/ai-life-skills --skill summarize -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install reysu/ai-life-skills summarize --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/reysu/ai-life-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/summarize .claude/skills/summarize && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "summarize" agent skill from https://github.com/reysu/ai-life-skills/tree/main/summarize into .claude/skills/summarize/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "summarize", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/reysu/ai-life-skills/tree/main/summarizeType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add reysu/ai-life-skills --skill summarize -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install reysu/ai-life-skills summarize --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/reysu/ai-life-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/summarize .agents/skills/summarize && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "summarize" agent skill from https://github.com/reysu/ai-life-skills/tree/main/summarize into .agents/skills/summarize/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "summarize", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add reysu/ai-life-skills --skill summarize -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install reysu/ai-life-skills summarize --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/reysu/ai-life-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/summarize .cursor/skills/summarize && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "summarize" agent skill from https://github.com/reysu/ai-life-skills/tree/main/summarize into .cursor/skills/summarize/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "summarize", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/reysu/ai-life-skills.git --path summarize--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add reysu/ai-life-skills --skill summarize -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install reysu/ai-life-skills summarize --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/reysu/ai-life-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/summarize .gemini/skills/summarize && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "summarize" agent skill from https://github.com/reysu/ai-life-skills/tree/main/summarize into .gemini/skills/summarize/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "summarize", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install reysu/ai-life-skills summarizeInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add reysu/ai-life-skills --skill summarize -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/reysu/ai-life-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/summarize .github/skills/summarize && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "summarize" agent skill from https://github.com/reysu/ai-life-skills/tree/main/summarize into .github/skills/summarize/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "summarize", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add reysu/ai-life-skills --skill summarize -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install reysu/ai-life-skills summarize --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/reysu/ai-life-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/summarize .opencode/skills/summarize && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "summarize" agent skill from https://github.com/reysu/ai-life-skills/tree/main/summarize into .opencode/skills/summarize/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "summarize", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
summarizeSummarize any content (YouTube video, article, whitepaper/PDF, podcast episode, book chapter, etc.) into a rich Obsidian note with section-by-section breakdowns, wikilinks to all technical concepts…
Summarize is an agent skill from reysu/ai-life-skills. Summarize any content (YouTube video, article, whitepaper/PDF, podcast episode, book chapter, etc.) into a rich Obsidian note with section-by-section breakdowns, wikilinks to all technical concepts and people, and reference notes for every linked term. Use when the user provides a URL, file, or content to summarize and document in the vault.
Its SKILL.md is about 7.7k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Documents & Office, covering Report writing, Note-taking and Podcasting. It works with YouTube, Obsidian and Homebrew. The licence is MIT.
9 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit cd3e454. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
brewpandocyt-dlppipnpmpdftotextpython3From the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
github.comFrom URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
ELEVENLABS_API_KEYFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Summarize loads about 7.7k tokens when it runs. Until then it costs about 88 tokens; SKILL.md has 3,429 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from reysu/ai-life-skills at commit cd3e454, republished under its MIT licence (© reysu). 3,429 words, ~7,695 tokens.
.claude/skills/summarize/SKILL.md (or your agent's skills folder).Universal content summarizer. Takes any input — YouTube video, web article, whitepaper/PDF, epub book, podcast, lecture — and produces a rich, interlinked Obsidian summary note with reference notes for every concept mentioned.
Vault structure — the skill expects these folders inside your Obsidian vault. Folder names are the defaults; override them in the Configuration block below if your vault uses different names.
| Folder | Purpose |
|---|---|
08 Summaries/ | Where summary notes land |
07 References/ | Concept / company / product / place notes |
04 People/ | Person notes (creators, guests, mentioned people) |
02 Daily/YYYY/MM/ | Daily notes, named MM-DD-YY ddd.md (e.g. 03-29-26 Sun.md) |
_Templates/ | Note templates — skill installs new person template.md here on first run |
_Bases/ (optional) | Obsidian Bases — only needed if you use the Bases plugin |
CLI tools — install these before first use, or let Step 0 walk you through it:
| Tool | Purpose | Install |
|---|---|---|
yt-dlp | YouTube/podcast download + metadata + subs | brew install yt-dlp or pip install yt-dlp |
defuddle | Web article extraction | npm install -g defuddle |
pdftotext | PDF text extraction | brew install poppler |
pandoc | EPUB / DOCX → markdown | brew install pandoc |
mlx_whisper (optional) | Local audio transcription fallback | pip install mlx-whisper |
Alternative to mlx_whisper: set ELEVENLABS_API_KEY to use ElevenLabs Scribe for transcription.
The skill reads these variables at runtime. Override any of them via environment variables, or edit the defaults here:
VAULT_ROOT = $VAULT_ROOT # auto-detected if not set (see Step 0a)
SUMMARIES_DIR = 08 Summaries
REFERENCES_DIR = 07 References
PEOPLE_DIR = 04 People
DAILY_DIR = 02 Daily
TEMPLATES_DIR = _Templates
BASES_DIR = _BasesAll paths below are relative to $VAULT_ROOT.
When the user provides content to summarize: a URL (YouTube, article, blog), a PDF/file path, pasted text, or a reference to content already in the vault.
Before doing any work, verify the environment is ready. Skip any check that already passes — only prompt the user when something is actually missing. Do not re-run Step 0 on subsequent invocations if the initial setup succeeded; you can tell it already ran if $VAULT_ROOT resolves and the required folders + tools are present.
vault=""
if [ -n "$VAULT_ROOT" ]; then
vault="$VAULT_ROOT"
else
dir="$PWD"
while [ "$dir" != "/" ]; do
if [ -d "$dir/.obsidian" ]; then vault="$dir"; break; fi
dir="$(dirname "$dir")"
done
fi
echo "Vault: ${vault:-NOT FOUND}"If no vault is found, ask the user:
What's the absolute path to your Obsidian vault? Recommended: use a new, dedicated Obsidian vault for this skill — not your existing personal vault. The skill creates and modifies many notes and folders, and a clean vault avoids polluting your existing notes. If you don't have one yet, create an empty folder, open it in Obsidian (File → Open vault as folder), and paste that path here.
After they answer, validate that <answer>/.obsidian/ exists before using it — if not, warn that the path doesn't look like an Obsidian vault (they may need to open it in Obsidian first) and ask them to confirm or re-enter. Use the validated answer as $VAULT_ROOT for the session (and suggest they set it permanently in their shell profile).
for d in "$SUMMARIES_DIR" "$REFERENCES_DIR" "$PEOPLE_DIR" "$DAILY_DIR" "$TEMPLATES_DIR"; do
[ -d "$VAULT_ROOT/$d" ] || echo "MISSING: $d"
doneFor each missing folder, ask the user: "Create <folder> in your vault? [y/N]" — if yes, mkdir -p "$VAULT_ROOT/<folder>".
for tool in yt-dlp defuddle pdftotext pandoc; do
command -v "$tool" >/dev/null 2>&1 || echo "MISSING: $tool"
doneFor each missing tool, tell the user what's missing and ask before installing — installs touch the user's system. Use the install commands from the Requirements table above. If the user declines, note which tools are missing and warn that the corresponding content types (YouTube, web articles, PDFs, EPUBs) will fail until installed.
The skill ships two person templates in the repo's templates/ folder (shared with summarize-call):
new person template.md — full version with Dataview callouts (current age, total hours talked) and Obsidian Bases embeds (posts.base, books.base, meetings.base). Requires the Dataview plugin and Obsidian Bases.new person template (minimal).md — stripped version. Just frontmatter, a > [!info] summary callout, and an ## updates section. Works in any vault.If $VAULT_ROOT/$TEMPLATES_DIR/new person template.md already exists, leave it alone — the user may have their own customized version.
Otherwise, ask the user which version to install:
Install person template — which version?
- Minimal (default, works in any vault)
- Full (requires Dataview plugin + Obsidian Bases)
Then copy the chosen template into the user's _Templates/ folder:
skill_dir="$(dirname "$0")" # or wherever this SKILL.md lives
target="$VAULT_ROOT/$TEMPLATES_DIR/new person template.md"
if [ ! -f "$target" ]; then
# Use the user's choice — default to minimal
src="$skill_dir/../templates/new person template (minimal).md"
# if user picked full: src="$skill_dir/../templates/new person template.md"
cp "$src" "$target"
fiNote: whichever version gets installed lands at _Templates/new person template.md (no (minimal) suffix) so the skill's later references work uniformly.
Once Step 0 passes, proceed to Step 0.5.
Before extraction, establish which depth the user wants:
minimal, fast, quick, --minimal, -m → minimal modedetailed, deep, full, --detailed, -d → detailed modeDepth?
- Detailed (best results) — full reference notes for every wikilinked concept, person notes for every mentioned person, parallel highest-available-model subagents per section, base updates
- Minimal (fast) — summary note only, wikilinks left dangling, person notes for creators/guests only, Sonnet summary
This keeps interactive runs explicit while letting scheduled tasks / cron / /loop pass the mode in the invocation (e.g. /summarize <url> minimal) without blocking on input.
The chosen mode determines which steps run:
| Step | Detailed | Minimal |
|---|---|---|
| 1 Extract text | ✓ | ✓ |
| 1b Save transcript | ✓ | ✓ |
| 2 Output structure | ✓ | ✓ |
| 3a Depth from word count | ✓ | ✓ |
| 3b Parallel subagents | ✓ (>3000 words, highest available model) | ✓ (>3000 words, Sonnet) |
| 4 Assemble summary | ✓ | ✓ (skip ## People Mentioned section) |
| 5 Reference notes (concepts) | ✓ | ✗ — wikilinks left dangling |
| 5 Person notes | ✓ (all mentioned) | ✓ (creators/guests only — those in people frontmatter) |
| 5c Dangling-link audit | ✓ | ✗ |
| 6 Bases update | ✓ (if bases exist) | ✗ |
| 7 Daily note | ✓ | ✓ |
For book chapter-by-chapter depth (Step 1 book section), detailed mode gets the full 300-600 words per chapter; minimal mode gets a flatter single summary regardless of chapter count.
# Get metadata
yt-dlp --cookies-from-browser chrome \
--print "%(id)s|%(title)s|%(duration)s|%(upload_date)s|%(view_count)s|%(channel)s|%(channel_id)s" \
--no-download "<URL>"
# Try auto-subtitles first (fastest, free)
yt-dlp --cookies-from-browser chrome \
--write-auto-sub --sub-lang en --sub-format json3 \
--skip-download -o "/tmp/summarize/%(id)s" "<URL>"If auto-subs exist, extract text from the JSON3 file. If not, or if quality is poor:
youtube-transcribe skill — ask user: local mlx_whisper or ElevenLabs Scribe)defuddle parse "<URL>" --md -o /tmp/summarize/article.mdIf defuddle is not installed: npm install -g defuddle
Extract title, author, date, domain from defuddle metadata:
defuddle parse "<URL>" -p title
defuddle parse "<URL>" -p domainpdftotext "<path>" /tmp/summarize/paper.txtIf pdftotext is not available: brew install poppler
# Extract full text as markdown (preserves chapter structure)
pandoc "<path>" -t markdown --wrap=none -o /tmp/summarize/book.md
# If you need chapter boundaries, extract the TOC:
pandoc "<path>" -t json | python3 -c "
import json, sys
doc = json.load(sys.stdin)
for block in doc['blocks']:
if block['t'] == 'Header':
level = block['c'][0]
text = ''.join(
item['c'] if item['t'] == 'Str' else ' ' if item['t'] == 'Space' else ''
for item in block['c'][2]
)
print(f'L{level}: {text}')
"Chapter splitting strategy for books:
pandoc → markdown#/## headers)For very long books (>30 chapters): batch chapters into groups of ~5 per subagent to keep the number of parallel agents manageable. Each subagent summarizes its batch and returns section summaries.
CRITICAL — Book summary depth requirement:
## Chapter N: Title section with a substantial summary (300-600 words per chapter depending on chapter length)Output structure for books:
08 Summaries/<Book Title>.md (or 08 Summaries/<Author>/<Book Title>.md if summarizing multiple books by one author)bookcreator (author wikilink), published (year), isbn (if known), source (wikilink to the epub file if it's in the vault, e.g. "[[Book Title.epub]]")## Chapter N: Title section in the summary## Chapter Navigation callout at the top if the book has many chaptersFor .docx: pandoc "<path>" -t markdown --wrap=none -o /tmp/summarize/doc.md
For plain text: read directly.
Read directly from user message or vault path.
For any content that has audio — YouTube videos, podcast episodes, lectures/talks with recordings — save the extracted transcript as a permanent vault note.
When to create a transcript note:
Do NOT create transcript notes for: articles, blog posts, PDFs, books, pasted text — these are already text.
Location: Same folder as the summary note, with Transcript appended to the filename.
Format:
---
date: YYYY-MM-DD
duration: <seconds>
recording: "<source URL>"
meeting: "[[<Summary Note Title>]]"
unread: true
---
[Full timestamped transcript text, one line per segment]Link from summary: Add transcript: "[[<Title> Transcript]]" to the summary note's frontmatter.
This step happens immediately after text extraction (Step 1) and before output structure planning (Step 2). The transcript is the raw source material — always preserve it.
If the user has the Media Extended Obsidian plugin installed (assume YES unless proven otherwise — it's a common companion plugin for this workflow), move the downloaded source audio into the vault and wire up click-to-play timestamps throughout the summary.
Copy (or move) the downloaded mp3/wav/mp4 into $VAULT_ROOT/_Attachments/ with a descriptive, human-scannable filename that includes the date and — if cropped — the segment range.
<Creator> x <Guest> <YYYY-MM-DD>.mp3
<Creator> x <Guest> <YYYY-MM-DD> (HhMMm-HhMMm).mp3 # if croppedAdd audio: "[[<filename>.mp3]]" to the summary note's frontmatter so the attachment is a first-class property on the note (parallel to transcript:, source:, etc.).
Place a single full-length audio/video embed at the top of the summary, just above the > [!tldr] callout:
> [!abstract] Audio — full interview (cropped H:MM:SS – H:MM:SS of the VOD)
> ![[<filename>.mp3]]Do not scatter multiple ![[audio.mp3#t=...]] embeds through the note — every embed spawns a fresh player. Media Extended's pattern is one pinned player + many text-link jump-points.
Do NOT add:
For every > [!quote] callout that embeds a transcript line (![[...Transcript#^block-id]]), add a sibling line inside the same callout:
> [!quote] Who — what they said
> ![[<Transcript Note>#^block-id]]
> ▶ [[<filename>.mp3#t=<seconds>|jump player to H:MM:SS]]#t=<seconds> fragment is audio-local seconds, not wall-clock VOD time. If the audio was cropped (e.g. starting at VOD 1:17:00), subtract the crop offset from the VOD timestamp before emitting.|jump player to H:MM:SS alias is what the user reads — format it H:MM:SS when ≥1 hour, else M:SS.▶ (U+25B6) is a visual cue — keep it.! prefix), not embeds. Media Extended routes the click to the pinned player instead of creating a new one.audio_sec = (vod_h * 3600 + vod_m * 60 + vod_s) - crop_start_secIf the transcript already uses block IDs of the form ^p1-H-MM-SS (absolute VOD timestamps), this regex transformation converts every quote-embed into one with an audio-local jump link appended:
import re
AUDIO = "<filename>.mp3"
CROP_OFFSET_SEC = <crop start seconds> # 0 if audio starts at beginning of the source
pattern = re.compile(
r'^(> !\[\[[^\]]*Transcript#\^p1-(\d+)-(\d+)-(\d+)(?:-\d+)?\]\])$',
re.MULTILINE,
)
def repl(m):
block_line = m.group(1)
h, mm, ss = int(m.group(2)), int(m.group(3)), int(m.group(4))
audio_sec = (h*3600 + mm*60 + ss) - CROP_OFFSET_SEC
if audio_sec < 0:
return block_line
hh = audio_sec // 3600
mm2 = (audio_sec % 3600) // 60
ss2 = audio_sec % 60
label = f"{hh}:{mm2:02d}:{ss2:02d}" if hh else f"{mm2}:{ss2:02d}"
return f"{block_line}\n> ▶ [[{AUDIO}#t={audio_sec}|jump player to {label}]]"
text = pattern.sub(repl, text)Run this after Step 4 assembles the summary — it's a pure string transform.
Fall back to native Obsidian syntax: one top-of-note ![[audio.mp3]] embed only. Do not scatter ![[audio.mp3#t=N]] embeds inline — they each spawn a separate player, which clutters the note. Inline timestamp references in that case should just be the VOD timestamp as plain text.
Based on content type, choose the appropriate format:
| Content type | Location | Frontmatter tags | Extra fields |
|---|---|---|---|
| YouTube video | 08 Summaries/<Channel>/Summaries/<Title>.md | youtube | recording, audio (wikilink to vault mp3 if archived per Step 1c), views, creator, people, guest, hosts, guests, duration, uploaded, transcript |
| Article / blog | 08 Summaries/<Title>.md | article | creator, source (URL), published |
| Whitepaper / PDF | 08 Summaries/<Title>.md | paper | authors, affiliations, source (wikilink to PDF if in vault, or URL), published |
| EPUB / book | 08 Summaries/<Title>.md | book | creator (author wikilink), published (year), isbn, source (wikilink to epub if in vault) |
| Podcast episode | 08 Summaries/<Show>/Summaries/<Title>.md | podcast | recording, audio (wikilink to vault mp3 if archived per Step 1c), segment (e.g. "1:17:00 – 2:50:50" if cropped), people, guest, hosts, guests, duration, transcript |
| Lecture / talk | 08 Summaries/<Title>.md | lecture | creator, recording (if URL), audio (wikilink to vault mp3 if archived per Step 1c), transcript |
All notes get: created, updated, date, summary, categories: ["[[posts.base]]"], unread: true
summary field length — HARD LIMIT: ≤70 characters. One tight line, no wikilinks, no paragraph-length blurbs. The > [!tldr] callout at the top of the body is where the long-form overview lives. The frontmatter summary is just a scannable hint for base views — think newspaper subhead, not abstract. Examples that are the right size:
"Ledger interviews Cobie — 3h 51m UpOnly career retrospective" (60 chars)"Cobie on ThreadGuy — first interview since joining Coinbase" (59 chars)"Lex x Karpathy — state of AI, RLHF, self-driving, education" (60 chars)If it's longer than 70 characters, cut it. Do not paste the tldr into the summary field.
If a channel/show folder is needed, check if it already exists before creating.
Read the full extracted text. Identify the natural sections/chapters/topics.
Summary length must be proportional to the source material. A 10-minute video and a 3-hour documentary should not produce the same size summary. Use the source word count to determine the target summary word count:
| Source word count | Source examples | Target summary words | Sections | TLDR |
|---|---|---|---|---|
| <1,500 | 5-min video, short article | 200–400 | 1–2 | 2 sentences |
| 1,500–5,000 | 10–20 min video, blog post, short paper | 500–1,200 | 3–5 | 3 sentences |
| 5,000–15,000 | 30–60 min video, long article, whitepaper | 1,500–3,000 | 5–8 | 3–4 sentences |
| 15,000–40,000 | 1–3 hr video/podcast, long paper | 3,000–6,000 | 8–15 | 4–5 sentences |
| 40,000–80,000 | Short book, multi-hour series | 5,000–10,000 | 15–25 | 5 sentences |
| 80,000+ | Full book (200+ pages) | 8,000–15,000 | 20–40 | 5 sentences |
The ratio is roughly 1:5 to 1:10 — a 10,000-word source should produce ~1,500–2,500 words of summary. Denser/more technical content skews toward the higher end; conversational/repetitive content skews lower.
For videos/podcasts, estimate source words from duration: ~150 words/minute for conversational, ~120 words/minute for interviews with pauses, ~170 words/minute for scripted/narrated content. Or just use the actual transcript word count.
Per-section depth: each section's word budget should be proportional to its share of the source material. A section covering 20% of the transcript gets ~20% of the summary word budget. Adjust up for particularly dense/important sections, down for filler/repetitive ones.
For long content (>3000 source words): dispatch parallel subagents (see Model usage table for which model) — one per section — to summarize simultaneously. Each subagent gets:
[[wikilinks]] for every technical concept, person, place, company, and notable nounFor short content (<3000 source words): summarize directly without subagents.
Model choice: detailed mode uses the highest available model (Opus if the user has access, else Sonnet); minimal mode always uses Sonnet. Never Haiku.
---
[frontmatter per Step 2]
---
[embed if applicable: ![[file.pdf]], ```vid URL```, etc.]
> [!tldr]
> [Overview — sentence count per Step 3a depth table. What is it about, who made it, what are the key takeaways?]
## [Section 1 Title]
[Summary paragraphs with [[wikilinks]] to all concepts, people, places, companies, products]
## [Section 2 Title]
[...]
## People Mentioned
- [[Person Name]] — brief context of who they are and their role in this content# Title heading — filename is the title> [!tldr] for the overview, not ## Summary> [!quote] callouts for notable quotes (with speaker wikilink and source location if available)[[Cobie|Jordan Fish]], [[Bob Laksiv|King BTC]] — not two siblings like [[Cobie]] / [[Jordan Fish]] or [[Bob Laksiv]] / [[King BTC]]. The canonical note is whichever name already exists (or will exist) in 04 People/; everything else is a display alias pointing at it. Same for companies/products with renames — [[Facebook|Meta]], [[X|Twitter]]. When it's natural to mention both, write it as prose: [[Cobie]] (real name Jordan Fish), [[Bob Laksiv]] (a.k.a. King BTC). Rule of thumb: one entity = one link target, always.people field: only people who created/appeared in the content. Mentioned people go in ## People Mentioned> [!quote] callout that embeds a transcript block should also carry a > ▶ [[<audio>.mp3#t=<sec>|jump player to H:MM:SS]] text link (one pinned top-of-note player, many text-link jumps). See Step 1c for the full pattern and the regex transform.This is the most important step. Every wikilink MUST resolve to a note. No dangling links.
After the summary note is fully assembled, extract every unique wikilink programmatically:
The regex excludes | (alias), # (heading ref), and ^ (block ref) so [[Target|Alias]], [[Page#Heading]], and [[Page^block]] all resolve to the canonical note name (Target / Page):
grep -oE '\[\[[^]|#^]+' "<summary_note_path>" | sed 's/\[\[//' | sort -uThen check which ones are missing:
for term in <each extracted term>; do
found=$(find "$VAULT_ROOT" -name "$term.md" \
-not -path "*/.Trash/*" -not -path "*/Clippings/*" 2>/dev/null | head -1)
if [ -z "$found" ]; then echo "MISSING: $term"; fi
doneDo NOT skip this step. Do NOT estimate from memory which notes exist. Always run the audit.
Create in 07 References/<Term>.md:
---
created: YYYY-MM-DDT00:00
updated: YYYY-MM-DDT00:00
type: reference
unread: true
---
[2-4 sentence plain-language explanation. Use [[wikilinks]] to cross-reference related concepts.]Create in $PEOPLE_DIR/<Full Name>.md using the person template at $VAULT_ROOT/$TEMPLATES_DIR/new person template.md (installed by Step 0d). Conventions:
> [!info] callout should be a substantive snapshot — life story, mission, current focus — not a stub.> [!note] current age callout (from template): keep it if birthday is known or can be estimated. If estimated, append (estimated) to the callout text — but birthday in frontmatter must stay a pure YAML date (e.g. 2001-01-01), never text.> [!abstract] total hours talked callout (from template): ONLY keep this if the person has had real 1-on-1 calls/meetings with the vault owner (i.e. they appear in meeting notes). Delete the callout for people discovered through summarizing videos, articles, books, or podcasts — those people will never have meeting entries, so the callout would always show 0h.# Title heading — Obsidian shows the filename as the title.unread: true in frontmatter on every new or modified note.For large numbers of missing notes (>10), use parallel subagents (highest available model) in batches of ~20-25 notes each. Each subagent creates the notes and returns confirmation.
After all notes are created, re-run the audit from 5a to confirm zero missing notes. If any remain (e.g. a subagent failed or skipped one), create them manually. The summary is not done until this verification passes.
This step only applies if $VAULT_ROOT/$BASES_DIR/posts.base exists. If it doesn't, skip Step 6 entirely.
[ -f "$VAULT_ROOT/$BASES_DIR/posts.base" ] || echo "No posts.base — skipping Step 6"If it does exist:
posts.base: if new people appeared as creators/guests, add named views for them using the YAML block below, then embed them in their person notes (in a ## episodes or ## videos section) via ![[posts.base#Person Name]].posts.base the same way.Named view YAML block to append under the views: list:
- type: table
name: "Person Name"
filters:
and:
- recording != null
- people.contains(link("Person Name"))
order:
- date
- views
- file.name
- summary
sort:
- property: date
direction: DESCUpdate $VAULT_ROOT/$DAILY_DIR/YYYY/MM/MM-DD-YY ddd.md (e.g. 02 Daily/2026/04/04-11-26 Sat.md). Create the YYYY/MM/ subdirectories if they don't exist. No # Title heading — the filename is the title. Set unread: true in frontmatter.
## content summary
- summarized [[Note Title]] — [1-line description of what it is]
- created reference notes: [[Term 1]], [[Term 2]], ...
- created person notes: [[Person 1]], [[Person 2]], ...| Task | Detailed | Minimal |
|---|---|---|
| Content extraction | Scripts (defuddle, pdftotext, yt-dlp) | Scripts |
| Section summarization | Highest available (Opus if accessible, else Sonnet) | Sonnet |
| Reference note creation | Highest available | (skipped) |
| Person note creation | Highest available | Sonnet (creators/guests only) |
| NEVER | Haiku | Haiku |
[[wikilink]]# Title headings — Obsidian shows filename as titleunread: true on every note created or modified> [!tldr] is mandatory — every summary starts with a concise overview callout## updates links to the content note, NEVER the daily note_Attachments/, embed ONE pinned player at the top, and add ▶ [[audio.mp3#t=<sec>|jump player to H:MM:SS]] text links inside every quote callout. Never scatter multiple ![[audio.mp3#t=N]] embeds (they each spawn a separate player). See Step 1c.© reysu, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in summarize of reysu/ai-life-skills.
Open the folder on GitHubat commit cd3e454
Summarize next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Summarize this skillreysu/ai-life-skills | 270 | — | ~7.7k | Automated safety check: Pass | MIT | |
| MarkitdownImCa0/just-laws | 781 | 14 repos | ~3.2k | Automated safety check: Notes | MIT | |
| Markitdownjimmc414/Kosmos | 594 | 2 repos | ~1.7k | Automated safety check: Pass | None | |
| Graphic EbookVarnan-Tech/opendirectory | 674 | — | ~5k | Automated safety check: Pass | MIT | |
| Ky Markdown RebuilderKyrieCheungYep/ky-markdown-rebuilder | 117 | — | ~5.7k | Automated safety check: Pass | None | |
| Obsidian Paper VaultAperivue/medsci-skills | 329 | — | ~1.6k | Automated safety check: Pass | MIT |
ImCa0/just-laws
Convert files and office documents to Markdown. An agent skill from ImCa0/just-laws.
jimmc414/Kosmos
Convert various file formats (PDF, Office documents, images, audio, web content, structured data) to Markdown optimized for LLM processing.
Varnan-Tech/opendirectory
Creates professionally designed B2B SaaS e-books in HTML + CSS, exported as print-ready PDF.
KyrieCheungYep/ky-markdown-rebuilder
Rebuild visual documents into reliable Markdown by combining text extraction with page or screenshot alignment.
Aperivue/medsci-skills
A skill your agent uses when turning a folder of research PDFs into Obsidian notes, even if Obsidian is not named.
Nebutra/MinerU-Skill
An AI-Native skill for parsing PDF / Office / image files into Markdown with MinerU — a fast, zero-config document parser for AI agents.
reysu/ai-life-skills
Transcribe a call recording with speaker diarization, summarize it, and create Obsidian vault notes (call note, transcript, person notes for participants).
Categories
Summarize any content (YouTube video, article, whitepaper/PDF, podcast episode, book chapter, etc.) into a rich Obsidian note with section-by-section breakdowns, wikilinks to all technical concepts…. Summarize is an agent skill from reysu/ai-life-skills.) into a rich Obsidian note with section-by-section breakdowns, wikilinks to all technical concepts and people, and reference notes for every linked term.
Summarize fits situations like: the user provides a URL; content to summarize and document in the vault.
Run `npx skills add reysu/ai-life-skills --skill summarize -a claude-code`. Or copy the skill folder (summarize in reysu/ai-life-skills) into .claude/skills/summarize in your project. Claude Code loads it when a task matches its description.
Run `npx skills add reysu/ai-life-skills --skill summarize -a codex`. Or copy the skill folder (summarize in reysu/ai-life-skills) into .agents/skills/summarize in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add reysu/ai-life-skills --skill summarize -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/summarize, .gemini/skills/summarize, .github/skills/summarize and .opencode/skills/summarize in your project.
Going by SKILL.md and its folder, Summarize needs the command-line tools its instructions call (brew, pandoc, yt-dlp, pip, npm and pdftotext) and credentials named ELEVENLABS_API_KEY. Our summary lists: Python 3; Node.js; A credential in ELEVENLABS_API_KEY.
SKILL.md names 1 domain. As links in the text: github.com. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Summarize is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 7.7k tokens (SKILL.md is roughly 31k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Summarize: Markitdown (ImCa0/just-laws, 781 stars), Markitdown (jimmc414/Kosmos, 594 stars), Graphic Ebook (Varnan-Tech/opendirectory, 674 stars) and Ky Markdown Rebuilder (KyrieCheungYep/ky-markdown-rebuilder, 117 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
reysu (a GitHub user) maintains it in reysu/ai-life-skills, which has 270 GitHub stars. The repository holds 2 skills in this directory. The repository was last updated on August 24, 2026.
Source: reysu/ai-life-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.