From quill-deep-analysis
Research a question across MANY of your own past meetings / transcripts (the Quill corpus) over a long time span, and consolidate the evidence into one cited, defensible report saved as a drill-down folder tree. This is the skill for researching YOUR OWN meetings — distinct from the built-in `deep-research`, which researches the web; when the source is your meetings/transcripts/conversations, use THIS one. USE IT when the ask spans weeks or months and many meetings, or weighs multiple facets/perspectives into one write-up — including casual and hypothesis-testing phrasings: "research / look into / dig into X across our meetings", "do some research on …", "use Quill to look into …", "find evidence (for or against) that …", "look for evidence that customers care about X", "how did our thinking on X evolve", "write up / synthesize / give me an overview of the last quarter of …", "a balanced picture of someone's last year for a performance review", "pull together a defensible explanation of each of our pillars from past conversations". Do NOT use for a single-meeting recap or a single-fact lookup ("what did we decide yesterday?", "when is the Acme call?") — query meetings directly for those. The only required input is the question; the skill plans the decomposition itself.
How this skill is triggered — by the user, by Claude, or both
Slash command
/quill-deep-analysis:meeting-transcript-researchThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Deep research over your own meetings. The **source is the Quill transcript corpus**, the **map dimension is time**, and the **output is a folder tree** you can drill into. (The engine underneath is a map/reduce — that's the *mechanism*, not the trigger; reach for this on intent, see below.) Distinct from the built-in web `deep-research` skill: that one reads the web, this one reads *your meetin...
Deep research over your own meetings. The source is the Quill transcript corpus, the map dimension is time, and the output is a folder tree you can drill into. (The engine underneath is a map/reduce — that's the mechanism, not the trigger; reach for this on intent, see below.) Distinct from the built-in web deep-research skill: that one reads the web, this one reads your meetings.
Reach for this when the answer must be assembled from many of your own conversations across a long span, or must weigh multiple facets/perspectives into one defensible write-up. Don't key off the mechanism ("map/reduce") — key off what the person wants (a researched report over your meetings vs. a quick lookup) and the source (their own meetings/transcripts, not the web).
USE IT when the ask sounds like:
deep-research)The only input is the question; the skill decides the decomposition (what the dimensions are, how to shard time). Same skill, different asks:
DON'T use it for a single meeting or a single fact — "what did we decide in yesterday's standup?", "when is the Acme call?". That's a direct search_meetings / get_transcript lookup, not a report.
MAP (by time window) → REDUCE (by dimension) → SYNTHESIZE (top level) → RECURSE (on generated questions)
00-SUMMARY.md, and generates the next round of questions.<outDir>/ e.g. research/pillars/2026-06-02/
00-SUMMARY.md top-level answer + links down the tree (read first)
_next-questions.md generated questions w/ open|answered status
<dimension>/report.md one per dimension (the reduce output)
_raw/window-NN-<range>.md per-window evidence (the map leaves; cited, linked)
questions/<slug>.md round-2 deep dives
Drill path: 00-SUMMARY → <dimension>/report → _raw/window-NN. Every claim traces to a meeting link.
Agents load tools first: ToolSearch("select:mcp__quill__search_meetings,mcp__quill__get_transcript,mcp__quill__get_meeting,mcp__quill__search_minutes").
search_meetings({ filter: { after, before }, ... }). This is what makes them parallel + focused. Pre-compute the windows and pass them in — workflow scripts can't do Date math.ranking.scope: 'deep-content' to weight transcripts, then get_transcript on the most relevant meetings (paginate with start_minutes/duration_minutes for very long ones). Only fall back to get_minutes/search_minutes to locate candidates.ranking.freshness: 'off' — you've already constrained time with the filter; you don't want recency decay reordering within the slice.filter.participants_any is a HARD cutoff (drops everything without those people). If a person was in most but not all relevant meetings, do NOT hard-filter on them — search by topic within the window and note who spoke. Only hard-filter when you truly want only their meetings.limit ≤ 30 per call. Page with offset if a window is dense.1737e957-8a05-410f-9b77-23c46338ba7a); a quill://meeting/<uuid> (or note) deep link only resolves if the entire id is intact. Never truncate, abbreviate, or shorten an id when capturing it or carrying it forward — propagate the complete UUID through map → reduce → synthesize. A partial id is worthless.query search returns empty, fall back to listing. Observed in practice: hybrid query search can come back empty for a window even when relevant meetings exist. Don't conclude "nothing happened" — re-run search_meetings with only filter:{after,before} (no query) to list the window's meetings, then open the promising ones by title/participants and read transcripts directly. Map agents must do this fallback before reporting a window as empty.The map and recurse agents read full transcripts — high token volume, but the task is extraction, which a cheaper model does well. The reduce, synthesize, and finalize agents do the cross-cutting judgment and final assembly (weighing evidence, narrating how the view evolved, writing the report, and folding in the follow-up answers), which is worth the more expensive model. Defaults:
| Agent | Task | Model |
|---|---|---|
| Map | read transcripts, extract per-dimension evidence | sonnet |
| Recurse | targeted follow-up research (also reads transcripts) | sonnet |
| Reduce | consolidate one dimension across all windows | opus |
| Synthesize | top-level report + generate questions | opus |
| Finalize | assemble the final report — fold round-2 answers into 00-SUMMARY.md, write the questions dashboard | opus |
Override per run with args.models, e.g. models: { map: 'haiku' } for a cheap dry run, or { reduce: 'sonnet' } to economize further. The transcript-reading agents dominate token spend, so keeping them off the most expensive model is the main lever.
The engine's agent prompts already encode this; it's restated here for anyone extending them. Give each agent the question, not your conclusion; name the tools; demand direct quotes with meeting IDs + URLs + speaker; ask for ONE thing that would surprise you; frame adversarially (look for evidence the claim is weak); grant permission to report "few/no relevant meetings in this window." Map agents capture decisions, disagreements (who disagreed with whom), and open questions — not just supporting quotes.
The engine ships with this plugin at workflows/map-reduce-research.mjs. Run the map/reduce through it via the Workflow tool rather than doing the map/reduce yourself in the main thread (the engine applies the per-stage model tiering).
Steps:
${CLAUDE_PLUGIN_ROOT}/workflows/map-reduce-research.mjs. If that variable isn't resolved in your context, find the installed copy (e.g. ls ~/.claude/plugins/**/workflows/map-reduce-research.mjs) or copy it into the project's .claude/workflows/. Pass the resolved absolute path below.mkdir -p the output tree, and copy this skill's assets/report-viewer.html into outDir.Workflow({ scriptPath: "<absolute path to map-reduce-research.mjs>", args: {
question, dimensions, windows, participantsHint, outDir, recursion: { rounds, topK }
}})
The engine sets the per-stage models itself (see Model strategy); you don't need to pass models unless overriding.
args shape:
question — the top-level question (string).dimensions — [{ key, name, definition, probes:[...] }]. Put any scoring rubric in a dimension's definition (e.g. "rate 1–5 as a buying pillar") — the engine is rubric-agnostic and won't invent a scale otherwise.windows — [{ start, end, label }] ISO date-times, pre-sharded.participantsHint — names usually present (e.g. ["Jordan"]); given as context, not a hard filter.outDir — the run folder. Default: use ./research/<topic>-<date>/, creating ./research/ in the project if it doesn't exist yet — unless the user has specified another location, in which case use that.recursion — { rounds, topK }, an enable-switch + cap, not a loop count. { rounds: 0 } disables the follow-up pass; { rounds: 1, topK: 4 } allows at most one round. The engine then runs a cautious in-workflow gate (an "evaluate and decide" step) that skips the round unless a follow-up would materially change the answer — so even rounds: 1 often does nothing, by design.
{ rounds: 0 }.{ rounds: 1, topK: 4 } (the gate still decides whether to spend it).1 and let the gate prevent wasted rounds; only exceed it for genuinely sprawling research, deliberately.models (optional) — per-agent model overrides; defaults to { map: 'sonnet', reduce: 'opus', synthesize: 'opus', recurse: 'sonnet', finalize: 'opus' }. See Model strategy above.After it returns:
( python3 -m http.server <port> --directory <outDir> >/tmp/qda-report.log 2>&1 & )
open "http://localhost:<port>/report-viewer.html?doc=00-SUMMARY.md" # macOS; use xdg-open on Linux
Pick an unused port, and always also print the URL in case the browser didn't pop.00-SUMMARY.md yourself and give the user the headline. The generated follow-up questions live in _next-questions.md.Agents only ever write markdown — that stays diffable, Obsidian-native, and cheap. The presentation layer is one self-contained file, assets/report-viewer.html, copied into each run folder. It renders any .md with reader-view typography, a light/dark/auto toggle, inline SVG, and — crucially — it rewrites relative .md links so you can click from 00-SUMMARY down through the pillar reports and _raw/ windows inside the styled reader.
Open it by serving the run folder (relative links + fetch need http):
cd <outDir> && python3 -m http.server 8000
# then open http://localhost:8000/report-viewer.html?doc=00-SUMMARY.md
No server handy? Open report-viewer.html directly and drag a .md onto it (single-file mode; cross-doc links won't resolve). To share externally, serve the run folder over any static host.
Marp (slides) — deferred, not built. Turning a report into a slide deck is a separate pipeline (marp-cli over a slides-formatted markdown). If wanted later, add a --slides variant of the synthesize prompt that emits Marp-fenced markdown and render with marp-cli; keep it out of the default article path.
The agents WRITE files — each map / reduce / synthesis agent saves its report into the run folder. Run interactively via the Workflow tool, those writes are prompted normally. But background or headless subagents can't answer a permission prompt, so a restrictive profile blocks their writes (they fall back to returning text). For a clean install — headless/automated runs, or sharing with teammates — grant Write/Edit in project settings, scoped to the working tree:
// .claude/settings.json
{ "permissions": { "allow": ["Write(<project-root>/**)", "Edit(<project-root>/**)"] } }
Skill frontmatter can't grant settings permissions (it only restricts what a skill may call), so ship this snippet with the package or include it in the install steps. Without it the engine still works — agents return their full report text and the caller persists it — just with an extra step.
recursion.rounds; each round re-runs targeted research on the still-open questions.Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub quillmeetings/claude-plugins --plugin quill-deep-analysis