meeting-scribe
Skill0 starsChanged 4 months agoMITView on GitHub
Use when the user asks to process new recordings or voice memos into meeting minutes/notes — e.g. "process my recordings", "transcribe the new audio", "check my voice memos", "处理录音", "扫一下录音", "转录音频", "处理语音备忘录" — or reports a fresh local recording without giving a path ("本地有录音", "刚录了个会", "I just recorded a meeting"), or when audio files are waiting in the recording inbox, or on first use when ~/.meeting-scribe/config.yaml does not exist yet.
- Installs packages
4 other projects use a version of this.
Adopted by
Most recently taken up by
- legioncodeinc/littlebird-skillstheir own version, first seen Oct 20, 2026
- skills-agents-co/skills-and-agents-librarytheir own version, first seen Oct 15, 2026
- Alpha-Park/genpark-skill-meeting-scribetheir own version, first seen Apr 26, 2026
- alphaparkinc/genpark-skill-meeting-scribetheir own version, first seen Apr 26, 2026
Other versions4 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 4
- alphaparkinc/genpark-skill-meeting-scribe★ 9same name
- Alpha-Park/genpark-skill-meeting-scribe★ 8same name
- legioncodeinc/littlebird-skills★ 8same name
- skills-agents-co/skills-and-agents-library★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
The file
We split this skill into the rules it gives an agent
11 rules. 0 are common, 6 are distinctive, and 5 are very specific to this project.
0
Common
Most of the 4 edited versions we read keep these.
6
Distinctive
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 85
Do NOT add immutable=1 (it ignores the WAL, where the newest recordings live).
- Distinctive: line 99
When the user reports a fresh local recording that isn't in any inbox ("本地有录音", "I just recorded a meeting"), locate it — never ask them …
- Very specific: line 116
After the run, offer to add that folder to inbox_dirs (or enable the Voice Memos source) so next time it's hands-off.
- Very specific: line 128
All transcription/analysis happens in $W on plain local disk.
- Very specific: line 176
If the transcript opening reads like multiple people (meeting/discussion), use <skill>/bin/transcribe_speakers.sh "<audio>" [lang] …
- Very specific: line 241
every (存疑)/(uncertain) mark in the minutes,
- Distinctive: line 243
every SPEAKER→real-name guess not yet confirmed by a namemap.
- Distinctive: line 270
Ask once: should any of these minutes be sent to someone?
- Distinctive: line 275
Create a DRAFT only:
- Distinctive: line 276
If the session has a working email tool the user has set up (MCP/CLI), use it to save into the drafts folder.
- Distinctive: line 324
Never keep processing on top of a corrupted batch.
“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
meeting-scribe
Batch-process raw audio recordings into speaker-attributed transcripts and structured, context-aware meeting minutes (summary / key points / decisions / todos / mind map), then archive everything to the right folder and optionally draft the minutes for delivery. Transcription is 100% local (whisper.cpp); analysis is done by you, informed by the user's glossary, project cards, and scene cards. Recordings come from the user's own devices — computer or phone recordings dropped into the inbox folder(s), the Apple Voice Memos library (optional), or located on demand when the user just says they recorded something (see Step 1-alt).
State lives in $MEETING_SCRIBE_HOME (default ~/.meeting-scribe/), never in this
skill directory. Below, $SCRIBE means that state directory and <skill> means the
directory containing this SKILL.md.
First-run setup (no $SCRIBE/config.yaml → do this instead of the pipeline)
Interview the user conversationally (their language), then generate their state. Ask:
- Inbox: which folder(s) will hold incoming recordings? (offer to create one)
- Identity: one line — name, role, company — so analysis knows who "I" is and who todos default to.
- Languages: main recording language (whisper code, or
auto); minutes language. - Routes: where should minutes be archived? Offer to
lstheir documents folder and propose route keys per client/project/team.personalandfallbackare required. - Context hint: one sentence describing what recordings usually are (feeds the whisper initial prompt).
- People: frequent collaborators (name + role) to seed the glossary.
- Voice Memos (macOS only): also scan the Apple Voice Memos library? If yes, tell them it may require Full Disk Access for the AI assistant's app.
- Timezone: does the device clock differ from the team's timezone? If so record a
timezone_note.
Then: mkdir -p $SCRIBE/{projects,scenes,models,_work,logs}; write config.yaml
(follow <skill>/config.example.yaml); instantiate <skill>/prompts/glossary.template.md
→ $SCRIBE/glossary.md filled with their answers (replacing the <skill_dir> placeholder
in its maintenance-rule line with this skill's real path); copy <skill>/prompts/projects/_INDEX.md,
all of <skill>/prompts/scenes/, and the right output_template.*.md →
$SCRIBE/output_template.md; offer to draft their first real project card from a short
description of a current project; run python3 <skill>/bin/gen_whisper_prompt.py;
finally check the environment (below) and offer <skill>/bin/download-model.sh
large-v3-turbo (~1.6GB; --mirror flag for mainland-China networks). Verify each
written file exists and is non-empty before declaring setup done.
Environment check (start of every run)
command -v whisper-cli && command -v ffmpeg && ls "$SCRIBE"/models/*.binAnything missing → stop and tell the user how to install
(brew install whisper-cpp ffmpeg; <skill>/bin/download-model.sh large-v3-turbo).
Always transcribe through <skill>/bin/transcribe.sh — never hand-roll a
whisper-cli command. Its model choice, beam size, and thread count are deliberate
power/speed tuning (saturating every core once melted a laptop's battery budget for
zero quality gain). One-off audio outside the inbox goes through it too.
Main pipeline
Report progress briefly after each step. The pipeline pauses for the user exactly three times — Step 3 (scenes & materials), Step 6 (uncertain terms, auto-skipped when empty), Step 8 (delivery) — everything else runs autonomously.
Step 1 — Scan for new audio
Sources:
- A. Inbox dirs from config:
find <inbox> -type f \( -iname "*.m4a" -o -iname "*.mp3" -o -iname "*.wav" -o -iname "*.aac" -o -iname "*.m4v" \) -not -path "*/.processed/*" - B. Apple Voice Memos library (only if
voice_memos_source: true):~/Library/Group Containers/group.com.apple.VoiceMemos.shared/Recordings/— scan*.m4aAND*.qta(the newer container; mp4 family, ffmpeg reads it).Operation not permitted→ skip source B, continue, and in the final report tell the user: System Settings → Privacy & Security → Full Disk Access → add their AI assistant's app. Never interrupt the run for this.- Titles live in
CloudRecordings.db, not filenames:sqlite3 "file:$VM_DIR/CloudRecordings.db?mode=ro" "SELECT ZCUSTOMLABEL, CAST(ZDURATION AS INT) FROM ZCLOUDRECORDING WHERE ZPATH='<file>'". Do NOT addimmutable=1(it ignores the WAL, where the newest recordings live).ZDATEis a Core Data timestamp: add 978307200 for Unix time. AZCUSTOMLABELthat is an ISO datetime string means the user never renamed it — not a real title. Any DB error → proceed without titles. - IRON RULE: source B is read-only. Only
cpout of it — never mv/rm/write. Deleting recordings is the user's job in the app; a corrupted sync library is unrepairable.
Dedup (both sources): key = basename + byte size (stat -f %z). Skip keys
already in $SCRIBE/logs/processed.jsonl. Nothing new → report "no new recordings",
done.
Step 1-alt — "I just recorded something" (no path given)
When the user reports a fresh local recording that isn't in any inbox ("本地有录音", "I just recorded a meeting"), locate it — never ask them for a path first:
- Newest files in inbox_dirs and (if enabled) the Voice Memos library.
- Spotlight, whole disk, last 48h (syntax verified):
mdfind 'kMDItemContentTypeTree == "public.audio" && kMDItemFSContentChangeDate >= $time.now(-172800)'Widen the window if the user says it was earlier. Spotlight skips most of~/Library— the Voice Memos check above covers that gap. This also finds audio received via chat apps ("someone sent me the recording"). - Spotlight off/empty →
find ~/Downloads ~/Desktop ~/Documents -maxdepth 3for audio extensions with-mtime -2, plus meeting apps' local recording dirs when present (~/Documents/Zoom,~/Documents/TencentMeeting). - Drop already-processed keys and obvious noise (app resources, system sounds,
clips under ~20s unless that's what the user described). Get durations cheaply via
mdls -name kMDItemDurationSeconds(ffprobe as fallback). Present candidates newest-first — name, duration, parent folder — and let the user pick; exactly one plausible candidate → state which file you're taking and proceed. - After the run, offer to add that folder to
inbox_dirs(or enable the Voice Memos source) so next time it's hands-off. Config growing out of real usage beats asking users to predict their folders upfront.
Then continue with the normal pipeline below.
Copy every to-do file into the local work dir $SCRIBE/_work/<YYYYMMDD>/:
cp "<src>" "$W/" && [ "$(stat -f %z "<src>")" = "$(stat -f %z "$W/<name>")" ] || echo "COPY FAILED, retry"All transcription/analysis happens in $W on plain local disk. Cloud-synced folders
(OneDrive/iCloud/Dropbox) are touched only at the two ends — scan and final archive —
and /tmp is never used: sync jitter and tmp-cleaning have both caused files to get
silently swapped mid-run. Quote all paths (spaces are common). Recordings of 3h+ take
15-30 min of GPU time — warn the user and suggest running in the background.
Step 2 — Transcribe (+ hallucination rescue ladder)
<skill>/bin/transcribe.sh "$W/<file>" # inbox files: config transcribe_language
<skill>/bin/transcribe.sh "$W/<file>" auto # Voice Memos files: language unpredictablestdout is the .txt path. Exit code 6 = degenerate output (empty/near-empty or
loop-repeated) — the run silently failed; retry once, then use the rescue ladder. Other
non-zero exits: report stderr, skip the file. After success, confirm with an
independent command (wc -c) — don't trust the same command's output.
Transcripts largely in a foreign language stay untranslated (archival value; terms
stay exact). Translation happens only in the minutes layer. Gibberish from an inbox
file often means a mixed-language meeting forced to one language — retranscribe with
auto.
Hallucination check (always):
awk '{if($0==prev)c++; else{if(c>max)max=c; c=0; prev=$0}} END{if(c>max)max=c; print max}' <txt>max repeat > 50 → whisper looped (common on far-field/noisy audio). Rescue ladder,
in order:
- whisperX (if installed — check
$SCRIBE/.venv-whisperx/): VAD pre-segmentation sidesteps the loop.--compute_type int8 --batch_size 8 --vad_method silero --no_align, output overwrites the bad.txt(expected). Don't pipe the command (| taileats the real exit code). Skip whisperX for 1h+ files — its memory peak gets SIGKILLed on long audio and it truncates the output file first. - Slice-and-retranscribe (big files): split with ffmpeg into 10-min segments, transcribe each (fresh decoder per segment — a bad segment can't infect the rest), concatenate. Re-transcribing the whole file with the same settings is useless: decoding is deterministic, you get the identical output.
- Dedup-degrade (last resort): collapse consecutive identical lines; if ≥80% of
real content survives, mark the transcript
degradedand continue to analysis.
Still hopeless → move audio to the fallback route dir under _transcribe-failed/, skip.
Step 2.5 — Speaker diarization (multi-speaker recordings; optional module)
If the transcript opening reads like multiple people (meeting/discussion), use
<skill>/bin/transcribe_speakers.sh "<audio>" [lang] [known_speaker_count] instead —
it produces <name>.speakers.txt ([SPEAKER hh:mm] text). Missing venv or any
failure auto-degrades to plain transcription without blocking. Pass the speaker count
when known — steadier. Single-voice notes skip this.
- Capability honesty: long solo passages attribute well; rapid interjections and crosstalk blur — attribute by large blocks, and mark decision-attribution as uncertain rather than guessing.
- First run yields
SPEAKER_00/01…: infer real names from content + glossary people + who-spoke-when; the guesses go into the Step 6 review for user confirmation, and confirmed mappings are saved as<name>.namemap.jsonnext to the audio so recurring voices auto-map next time.
Step 3 — Scene choice + supporting materials (ONE batched exchange, before analysis)
Present a compact list — per file: name, duration, a one-line gist from the transcript
opening, and a suggested scene (match $SCRIBE/scenes/ trigger signals). Built-in
scenes: sales (client-facing sales meeting), team_weekly (internal team weekly),
bd (business development / negotiation), personal_thinking (solo voice notes) —
plus any cards the user has added, plus standard (minutes only, no extra
deliverable).
Then ask, in one message:
- Scene per file — confirm or override the suggestions ("all as suggested" is a perfectly good one-word answer).
- Supporting materials — do they have an agenda, chat log, prior minutes, slides, or attendee list for any of these meetings? Paste or drag files (txt/md/docx/pdf), or skip. Materials sharpen term corrections, speaker naming, and todo attribution.
Copy any provided material into $W, note which recording it belongs to. This
exchange also serves as the batch gate: if there are more than 5 files, get an
explicit go before continuing. If the user has said to run fully automatically (now
or in their standing instructions), skip the questions: scenes = auto-match,
no materials.
Step 4 — Analysis (context-aware, parallel when ≥3 files)
- < 3 files: analyze in the main session — Read the transcript
(
.speakers.txtpreferred), then<skill>/prompts/classify.mdand follow it (it loads$SCRIBE/glossary.md, matches$SCRIBE/projects/cards, applies the assigned scene, applies corrections, outputs the JSON). Read any supporting material for the file before analyzing. - ≥ 3 files: dispatch one general-purpose subagent per file in a single message
(max ~10 concurrent). Each prompt must be self-contained: fill the template in
<skill>/prompts/subagent_analysis.md(it has slots for the assigned scene and supporting materials). No subagent capability in this harness → process the files sequentially with the same per-file procedure instead.
Validate every JSON: category/title/routing_hint required; strip /\:*?"<>|
from title, cap 30 chars; empty date → infer from transcript or filename timestamps.
Honor timezone_note from config when dating minutes.
Step 5 — Minutes document
Subagent path: already written — skip ahead. Main-session path: fill
$SCRIBE/output_template.md ({{title}}, {{date}}, {{category}}, {{routing_hint}},
{{routing_reason}}, {{summary}}, {{participants}} joined, {{key_points}}/{{decisions}}
as - lists, {{todos_table}} rows, {{mindmap_markdown}}, {{transcript}} — condense
very long ones, {{audio_filename}}).
Step 6 — Uncertain-term review with the user (the human half of the flywheel)
Collect across ALL files of the batch:
- every
(存疑)/(uncertain)mark in the minutes, - every
new_correctionscandidate, - every SPEAKER→real-name guess not yet confirmed by a namemap.
Nothing collected → skip this step silently. Otherwise present ONE numbered list — each item: the transcribed form, your best guess, one line of context — and let the user confirm / fix / dismiss everything in a single reply. Then apply:
- Fix the affected minutes files in place (they exist already — edit, don't regenerate).
- User-confirmed corrections go straight into the glossary tables — human sign-off replaces the usual 100%-certainty bar. Note the month. This is how the personal glossary compounds: every review makes the next transcription better.
- Confirmed speaker names →
<name>.namemap.json. - Dismissed items: remove the uncertainty mark if the user says the transcription was right; leave the mark if genuinely unresolvable.
Step 7 — Archive (always the main session's job)
target_dir = route from config for the routing_hint, plus /<date>_<title>/.
Copy in: transcript.txt, the audio, .srt if present, any supporting materials the
user provided for that meeting. The minutes .md is already there — never overwrite it.
Write-then-verify (mandatory): afterwards run an independent ls -la — every file
present and >0 bytes. Cloud-synced targets can fail silently or ENOENT when
unhydrated; on failure, land the whole bundle in the fallback route dir instead and
tell the user to enable "always keep on this device" for the target.
Step 8 — Delivery (optional; ask AFTER archiving)
Ask once: should any of these minutes be sent to someone? For each file the user wants delivered, ask which channel:
- Email draft: collect recipients (and cc) and a subject (default: minutes title +
date). Body = the minutes minus the raw transcript section, simply formatted.
Create a DRAFT only:
- If the session has a working email tool the user has set up (MCP/CLI), use it to save into the drafts folder.
- Otherwise on macOS, open a Mail.app draft window via AppleScript:
osascript -e 'tell application "Mail" to make new outgoing message with properties {subject:"...", content:"...", visible:true}'(add recipients viamake new to recipient). Nothing is sent by this — it opens a compose window. - IRON RULE: never trigger the send action, never ask for email passwords or credentials. Tell the user: the draft is ready — review it and hit send yourself.
- Chat snippet (WeChat / Feishu / Slack …): produce a SHORT plain-text version to copy-paste: no markdown syntax, single line breaks, plain numbered lists, ≤10 lines total — one-line conclusion first, then 3-5 key points, then todos with owners. Print it in the conversation for copy-paste; do not automate the chat app.
"No" ends the step immediately. Close the loop by telling the user where each draft landed (drafts folder / compose window / snippet above).
Step 9 — Finish
Per successfully processed file:
- Audio →
processed_archive/YYYY-MM/(month from the recording's date). Inbox originals:mv, then verify with an independentfind(cloud-sync can error on mv yet still have moved it — blind retries create duplicates). Voice Memos originals:cpfrom_work(library untouched, forever). .txt/.srtalso copied there.- Append to
$SCRIBE/logs/processed.jsonl:{"key":"<name>+<size>","audio":"...","category":"...","target":"...","title":"...","at":"<ISO8601>"} - Whole batch archived → delete
$SCRIBE/_work/<batch>/.
Correction flywheel wrap-up: user-confirmed entries were already merged in Step 6.
Remaining auto-candidates (high bar: the wrong form can't legitimately occur AND the
right form is 100% certain) may also be merged — grep first to avoid duplicates. New
high-frequency terms join the Priority terms section. Any glossary change →
python3 <skill>/bin/gen_whisper_prompt.py — without this, the transcription layer
never improves.
Step 10 — Report
One markdown table: # | file | scene | title | route | archive path | delivery |
status. Prefix fallback rows with ⚠️ and ask the user to file them manually —
never guess.
Failure signals — stop and re-verify ground truth
Same path exists-then-doesn't; cp/mv errored but the file is there; two different
recordings produce identical transcript openings. Any of these → the filesystem
state is lying (usually cloud-sync jitter or a machine fresh out of sleep). Stop,
verify with independent find + byte counts + content spot-checks before continuing.
Never keep processing on top of a corrupted batch.
Interaction rules
- Exactly three pause points (Steps 3, 6, 8); each is one batched exchange with a one-word fast path. Everything else runs without asking.
- User says "fully automatic" / "don't ask" (now or in standing instructions) → scenes auto-match, uncertain terms go into the final report table instead of a review, delivery is skipped.
- User interrupts mid-run →
processed.jsonlalready has finished files; next run won't repeat them. - Minutes/summaries/todos are written in
output_language; transcripts keep their original language. - Scene-card iron rules outrank everything — especially: operational numbers in
scene deliverables (weekly reports etc.) are
[待填]/[TBD]placeholders, never copied from speech — transcribed digits are the least reliable thing in the file. - Email drafts are drafts: the skill never sends anything, on any channel.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

