meeting-assist
Live meeting transcription and translation in the terminal, with a Claude Code skill that suggests what to say next.
meeting-assist listen captures the audio of a meeting playing on your Mac,
streams it to AssemblyAI for transcription with speaker labels, translates
each finished turn with Claude or ChatGPT, shows both in the terminal, and
saves a transcript. meeting-assist summarize writes minutes afterwards.
The bundled Claude Code skill follows the live transcript and suggests a
reply after each turn.
Requirements
- macOS. Audio capture uses a macOS virtual audio device; there is no Linux or Windows support.
- Python 3.14.
- An AssemblyAI API key.
- An Anthropic or OpenAI API key, depending on the model you pick.
- BlackHole 2ch:
brew install --cask blackhole-2ch.
Install
uv tool install meeting-assist
or pipx install meeting-assist. To work on the source instead:
git clone https://github.com/xy13m/meeting-assist
cd meeting-assist
uv sync --all-groups
uv run meeting-assist --help
One-time audio setup
The tool reads whatever is played into BlackHole. Route the meeting audio there while still hearing it yourself:
- Open Audio MIDI Setup (Applications > Utilities).
- Click
+at the bottom left and choose "Create Multi-Output Device". - Tick your real output (speakers or headphones) and "BlackHole 2ch". Tick "Drift Correction" for BlackHole.
- Either set this Multi-Output Device as the system output (menu bar sound icon), or pick it as the speaker in your meeting app's audio settings.
While a Multi-Output Device is the system output, the keyboard volume keys do nothing. Selecting it only inside the meeting app avoids that.
The microphone is not captured; your own speech does not appear.
Configuration
Settings come from four places. Later ones override earlier ones.
-
~/.config/meeting-assist/config.toml:[keys] assemblyai = "..." anthropic = "..." openai = "..." [defaults] model = "claude-haiku-4-5" target = "zh-TW" device = "BlackHole" out = "~/meetings"
-
A
.envfile in the current directory (keys only, same variable names as below). -
Environment variables:
ASSEMBLYAI_API_KEY,ANTHROPIC_API_KEY,OPENAI_API_KEY,MEETING_ASSIST_MODEL,MEETING_ASSIST_TARGET,MEETING_ASSIST_DEVICE,MEETING_ASSIST_OUT. -
Command-line flags:
--model,--target,--device,--out.
Built-in defaults: model claude-haiku-4-5, target zh-TW, device
BlackHole, output directory meetings/ under the current directory.
MEETING_ASSIST_CONFIG points at a different config file.
The provider is chosen from the model name: claude-* uses Anthropic;
gpt-*, chatgpt-* and the o1/o3/o4 series use OpenAI. For any other
name write anthropic/<model> or openai/<model>.
The target language takes an IETF-style code such as zh-TW, ja, or en.
When the target is Traditional Chinese, Mandarin recognised in Simplified
characters is converted to Traditional (Taiwan vocabulary), and turns spoken
mostly in Chinese are not sent to the translator. Other targets translate
every turn.
Listen
meeting-assist listen
meeting-assist listen --context meeting.md
meeting-assist listen --context meeting.md --languages en,zh --target zh-TW
Write a context file before the meeting: agenda, participants, product
names, acronyms, anything that helps. The whole file goes into the
translator's system prompt, and its first heading is passed to the
recogniser as a hint. An optional ## Keyterms bullet list is sent to the
recogniser as-is; without one, the LLM derives up to 100 keyterms from the
text (skip that with --no-keyterms). Without --context there are no
keyterms and the translator works from the transcript alone.
Options:
| Flag | Meaning |
|---|---|
--context FILE |
Meeting context Markdown file. Copied into the meeting directory as meeting.md. |
--languages CODES |
Comma-separated AssemblyAI language codes to recognise. Default en. |
--wav FILE |
Replay a mono 16-bit WAV instead of a device, at real-time pace. |
--device NAME |
Input device name substring. |
--out DIR |
Directory that meeting folders are created in. |
--model NAME |
LLM for translation and keyterm derivation. |
--target CODE |
Translation target language. |
--no-keyterms |
Do not derive keyterms from the context file. |
Recognition is English-only by default. For a meeting where people also
speak Mandarin, pass --languages en,zh: the recogniser then switches
between the two, even inside one sentence. A single code forces a
monolingual session, which is why the default is en alone. Whether a turn
skips translation is decided from its text: the recogniser's per-turn
language label is stored but not trusted, because in testing it marked
plain English turns as zh.
Speaker labels shown live are provisional. AssemblyAI sends corrected
labels when the session ends; transcript.md is rewritten with them on
exit, and transcript.jsonl keeps the corrections as revision records.
Press Ctrl-C to stop. The exit code is 0 normally, 1 for a configuration or setup problem, and 2 when the connection to AssemblyAI could not be recovered (the transcript so far is still saved).
Output files
Each run creates <out>/<YYYYMMDD-HHMMSS>/ containing:
transcript.jsonl: one record per line, flushed immediately. The first line is ametarecord with the target language and recognised languages; thenturn,translation, andrevisionrecords in the order they happened. See CLAUDE.md for the exact shapes.transcript.md: readable transcript, one block per turn with its translation.meeting.md: a copy of the context file, when one was given.meeting-assist.log: warnings from the run (reconnects, turns whose formatted text never arrived, translation failures).
Summarize
meeting-assist summarize meetings/20260915-100000
Reads transcript.jsonl and meeting.md, asks the LLM for minutes, and
writes summary.md with a ## Summary section and an ## Action items
section in the target language. It only runs when you ask; listen never
calls it. Pass --force to replace an existing summary.md, --model to
use a different model than the default, --target for a different
language.
Reply suggestions with Claude Code
The package ships a Claude Code skill. Install it once:
meeting-assist install-skill
This copies SKILL.md to ~/.claude/skills/meeting-assist/. Pass
--dest .claude/skills to install it into one project instead, and
--force to replace an installed copy.
During a meeting, start Claude Code in the directory where you run
meeting-assist listen (so meetings/ is next to it) and run
/meeting-assist. It picks the newest meeting directory, or pass one. It
reads meeting.md for context, watches transcript.jsonl, and prints a
one-line summary plus a suggested reply for each complete turn. Fragments
of a turn still in progress get a one-line placeholder. /meeting-assist stop ends the watch.
Every turn wakes Claude Code once, so an hour-long meeting is a few hundred requests against your Claude Code quota. Suggestions appear about ten seconds after the speaker stops. The skill cannot hear you: the CLI records system audio only.
Cost
AssemblyAI bills connection time: about $0.52 per hour with speaker labels and keyterms at the time of writing. Translation with Claude Haiku is roughly $0.3 to $0.6 per hour. Start the tool when the meeting starts and stop it when it ends.
Roadmap
Not in this version:
- Automatic context generation from calendars, Confluence, Jira, or local
folders. Write
meeting.mdby hand. - A web UI.
- Reply suggestions inside the CLI. They stay in the Claude Code skill.
- Audio capture on Linux or Windows.
Development
uv sync --all-groups
uv run pytest # unit tests, no network
uv run pytest -m integration # one real run on a generated WAV; needs both keys
uv run ruff check . && uv run ruff format --check . && uv run mypy
uv build
CLAUDE.md lists the behaviours a change must not break.
Releasing
Bump version in pyproject.toml, commit, then push a tag with the same
number prefixed by v:
git tag v0.2.0
git push origin v0.2.0
The Release workflow checks the tag against pyproject.toml, runs the
checks above, uploads to PyPI, and creates a GitHub Release with the built
files. Tags that do not match the declared version fail before anything is
published.
License
MIT.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file meeting_assist-0.1.0.tar.gz.
File metadata
- Download URL: meeting_assist-0.1.0.tar.gz
- Upload date:
- Size: 47.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
638857fe802c4cda2c33788d534294ea428cb0c7f9da331bc3e8f0a92c55cad9
|
|
| MD5 |
58eb7ea84f64255e8bf44765668588ca
|
|
| BLAKE2b-256 |
4bb1b05f3e89226803dab8b85a10f264a53ec7cee81bb6fcd56a083649a09601
|
Provenance
The following attestation bundles were made for meeting_assist-0.1.0.tar.gz:
Publisher:
release.yml on xy13m/meeting-assist
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
meeting_assist-0.1.0.tar.gz -
Subject digest:
638857fe802c4cda2c33788d534294ea428cb0c7f9da331bc3e8f0a92c55cad9 - Sigstore transparency entry: 2833676218
- Sigstore integration time:
-
Permalink:
xy13m/meeting-assist@48ba0a6e7411e7213a998757694f7854a4668397 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/xy13m
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@48ba0a6e7411e7213a998757694f7854a4668397 -
Trigger Event:
push
-
Statement type:
File details
Details for the file meeting_assist-0.1.0-py3-none-any.whl.
File metadata
- Download URL: meeting_assist-0.1.0-py3-none-any.whl
- Upload date:
- Size: 35.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bc34d4965514e964daa4ea7bd83420a34ac48655d1e58edac34d993bf91061de
|
|
| MD5 |
8c98682c89cf458fb94f60c53612ec00
|
|
| BLAKE2b-256 |
1f89f0df05a182d447a989c4bd994cc44acd7654252ba380ac335747f9f35190
|
Provenance
The following attestation bundles were made for meeting_assist-0.1.0-py3-none-any.whl:
Publisher:
release.yml on xy13m/meeting-assist
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
meeting_assist-0.1.0-py3-none-any.whl -
Subject digest:
bc34d4965514e964daa4ea7bd83420a34ac48655d1e58edac34d993bf91061de - Sigstore transparency entry: 2833676237
- Sigstore integration time:
-
Permalink:
xy13m/meeting-assist@48ba0a6e7411e7213a998757694f7854a4668397 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/xy13m
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@48ba0a6e7411e7213a998757694f7854a4668397 -
Trigger Event:
push
-
Statement type: