Native macOS meeting automation for OpenClaw: calendar/window detection, prompt-before-recording, ScreenCaptureKit system audio + microphone recording, local...
---
name: meeting-assistant
description: "Native macOS meeting automation for OpenClaw: calendar/window detection, prompt-before-recording, ScreenCaptureKit system audio + microphone recording, local whisper-cli transcription, and agent-generated meeting notes. No BlackHole or virtual audio device required. Use when you need automatic meeting detection, recording, transcription, and final summaries delivered by an OpenClaw agent."
---
# Meeting Assistant
Native macOS meeting automation for OpenClaw: calendar/window detection → prompt before recording → system audio + microphone recording → local transcription → agent-generated meeting notes.
## Architecture
```text
Google Calendar / Window Detector
↓
schedule.json → scheduler_daemon.py
↓
meeting_daemon.py ask_record
↓
notify.py prompt: Start recording?
↓
record_audio.py → AudioDaemon.app (Unix socket)
↓
ScreenCaptureKit system audio + AVFoundation microphone
↓
WAV → transcribe.py → whisper-cli
↓
transcript.txt + summary_request queue
↓
OpenClaw heartbeat agent → final summary.md → user notification
```
## Key Features
- **No BlackHole required** — captures system audio directly with ScreenCaptureKit.
- **Native AudioDaemon** — a signed macOS app bundle for stable TCC privacy permissions.
- **System audio + microphone** — records remote speaker audio and local microphone input.
- **Half-duplex mixing** — prioritize system audio while others speak; fade to microphone during system silence.
- **Meeting detection** — detects Zoom, Tencent Meeting, Google Meet, Feishu/Lark, WeChat calls, and browser-based meetings.
- **Local transcription** — uses `whisper-cli` from whisper.cpp.
- **OpenClaw agent summaries** — transcription writes a `summary_request` into the agent queue; the OpenClaw agent generates the final meeting notes and notifies the user.
## Install
Clone the full repository first. `setup.sh` needs repository files and should not be run via `curl | bash`.
```bash
git clone https://github.com/Nowhitestar/meeting-assistant.git
cd meeting-assistant
bash meeting-assistant/scripts/setup.sh
```
If you are already inside the skill folder that contains `SKILL.md` and `scripts/`, you can run:
```bash
bash scripts/setup.sh
```
The installer will:
1. Check macOS, Homebrew, and Python
2. Install `sox`, `ffmpeg`, `whisper-cpp`, `terminal-notifier`, `gogcli`, and `cloudflared`
3. Create `~/.config/meeting-assistant/config.json`
4. Compile the window scanner and guide Accessibility permission setup
5. Build and sign `AudioDaemon.app`
6. Guide Microphone and Screen & System Audio Recording permissions
7. Optionally configure Google Calendar
8. Install LaunchAgent background services
9. Run basic verification tests
## macOS Permissions
| Component | Permission | Purpose |
|---|---|---|
| `AudioDaemon.app` | Microphone | Record local microphone audio |
| `AudioDaemon.app` | Screen & System Audio Recording | Capture system audio via ScreenCaptureKit |
| `window_scanner` | Accessibility | Read window titles to detect meetings/calls |
If system audio is missing, open:
System Settings → Privacy & Security → **Screen & System Audio Recording** → enable `AudioDaemon.app`.
## Google Calendar and Privacy
Calendar authorization uses `gog`; refresh tokens are stored in the system Keychain. The repository and skill package must never contain real `client_secret*.json`, refresh tokens, API keys, or personal calendar IDs.
Recommended setup:
```bash
# 1. In Google Cloud Console, enable Calendar API and create a Desktop OAuth client.
# 2. Download the JSON locally.
# 3. Import credentials and authorize Calendar access.
gog auth credentials ~/Downloads/client_secret_xxx.json
gog auth add your.email@example.com --services calendar
gog auth list
# 4. After auth succeeds, delete the Downloads copy of client_secret_xxx.json.
```
Then enable Google Calendar in `~/.config/meeting-assistant/config.json`:
```json
{
"calendars": [
{
"type": "google",
"enabled": true,
"gog_account": "your.email@example.com",
"watch_calendars": ["primary"]
}
]
}
```
If credentials were ever posted in chat or committed publicly: delete the OAuth client, revoke the token, create a fresh client, and authorize again.
## Configuration
Config path: `~/.config/meeting-assistant/config.json`
Key fields:
```json
{
"audio": {
"backend": "daemon",
"output_dir": "/path/to/meeting-assistant/meeting-recordings",
"silence_threshold": 0.01,
"silence_duration_sec": 300,
"half_duplex": true
},
"transcription": {
"whisper_cli": "whisper-cli",
"local_model_path": "~/Models/whisper/ggml-medium.bin",
"language": "zh"
},
"llm": {
"enabled": true
}
}
```
Notes:
- `audio.backend=daemon` is the recommended default and does not require BlackHole.
- `mic_device` / `system_audio_device` are only for the legacy SoX fallback path.
- If no external LLM command is configured, summaries are delegated to the OpenClaw agent queue.
## Scripts
All scripts live under `scripts/`:
| Script | Purpose |
|---|---|
| `setup.sh` | Interactive installer |
| `AudioDaemon.app/` | Native signed audio daemon app |
| `audio_daemon.swift` | ScreenCaptureKit + AVFoundation + Unix socket |
| `build_audio_daemon.sh` | Build and sign AudioDaemon |
| `record_audio.py` | Recording control (`start` / `stop` / `status`) |
| `meeting_daemon.py` | Recording workflow control |
| `scheduler_daemon.py` | Meeting scheduler daemon |
| `meeting_detector.py` | Meeting window detector |
| `window_scanner.swift` | Accessibility window scanner |
| `recorder_status.swift` | Floating recording status window |
| `transcribe.py` | whisper transcription + agent queue summary request |
| `agent_notify.py` | OpenClaw agent queue writer |
| `notify.py` | macOS notifications/prompts |
| `check_meetings.py` | Calendar queries |
| `send_summary.py` | Optional Telegram/Zulip/Notion output |
| `webhook_server.py` | Deprecated fallback Google Calendar webhook server |
| `run_calendar_services.py` | Google Calendar webhook + cloudflared tunnel service |
| `summary_template.md` | Meeting notes template |
| `config.example.json` | Example config |
| `com.meetingassistant.*.plist` | LaunchAgent definitions |
## Manual Commands
```bash
# AudioDaemon status
echo '{"action":"status"}' | nc -w 2 -U ~/.config/meeting-assistant/audio_daemon.sock
# Manual recording
python3 scripts/record_audio.py start "Test Meeting"
python3 scripts/record_audio.py stop
# Prompt-based recording flow
python3 scripts/meeting_daemon.py ask_record "Test Meeting" "manual-test"
# Transcribe WAV
python3 scripts/transcribe.py /path/to/meeting-recordings/xxx.wav
# Calendar
python3 scripts/check_meetings.py today
python3 scripts/check_meetings.py upcoming
python3 scripts/check_meetings.py week --json
# Window detection
python3 scripts/meeting_detector.py once
python3 scripts/meeting_detector.py daemon
```
## Troubleshooting
### AudioDaemon has no system audio
```bash
echo '{"action":"status"}' | nc -w 2 -U ~/.config/meeting-assistant/audio_daemon.sock
```
If `sysReady=false`:
1. Open System Settings → Privacy & Security → Screen & System Audio Recording
2. Enable `AudioDaemon.app`
3. Restart AudioDaemon:
```bash
pkill -f audio_daemon
open scripts/AudioDaemon.app
```
### WAV exists but is silent
Usually this is a TCC permission issue or the daemon is not ready. Newer versions refuse to start recording when `sysReady` / `micReady` is false to avoid fake silent WAVs.
### Summary is still a draft
Check the queue:
```bash
cat ~/.config/meeting-assistant/agent-queue.json
```
If there is a `summary_request`, the OpenClaw heartbeat agent will read the transcript and template, overwrite the final summary, and notify the user.
---
# 中文说明
这是一个 macOS 原生会议助手:日历/窗口检测 → 弹窗询问 → 录制系统音频 + 麦克风 → 本地转录 → OpenClaw agent 生成会议纪要。
核心特点:不需要 BlackHole;通过 ScreenCaptureKit 直接捕获系统音频;用 whisper-cli 本地转写;通过 OpenClaw agent queue 生成最终纪要。
快速安装:
```bash
git clone https://github.com/Nowhitestar/meeting-assistant.git
cd meeting-assistant
bash meeting-assistant/scripts/setup.sh
```
Google Calendar 授权使用 `gog`,refresh token 存在系统 Keychain。不要把 `client_secret*.json`、refresh token、API key 或个人日历 ID 提交到公开仓库。
don't have the plugin yet? install it then click "run inline in claude" again.
extracted intent, inputs with auth/permissions detail, procedure as ten explicit steps with inputs/outputs, decision points for six major branches (calendar enabled, window detected, user decline, missing permissions, audio issues, offline), output contract with file locations and data formats, outcome signal for verification.
automate meeting capture on macOS by detecting active calls (Zoom, Google Meet, Feishu, WeChat, etc.), prompting the user to start recording, capturing system audio and microphone input via ScreenCaptureKit, transcribing locally with whisper-cli, and queueing final summaries to an OpenClaw agent. use this when you need hands-off meeting documentation without virtual audio devices or cloud transcription services.
macOS environment:
external connections:
gog CLIgog auth credentials ~/Downloads/client_secret_xxx.json && gog auth add your.email@example.com --services calendar after Google Cloud Console OAuth setupgog_account string in ~/.config/meeting-assistant/config.json)https://www.googleapis.com/auth/calendar.readonlysystem permissions required:
local files/config:
~/.config/meeting-assistant/config.json (created by setup.sh)~/.config/meeting-assistant/audio_daemon.sock (Unix socket for AudioDaemon)./meeting-recordings/)~/Models/whisper/ggml-medium.bin)optional integrations:
~/.config/meeting-assistant/agent-queue.json) for summary generationinstall and configure
git clone https://github.com/Nowhitestar/meeting-assistant.git && cd meeting-assistant && bash scripts/setup.shscheduler daemon starts
~/.config/meeting-assistant/config.json with calendar enabledscheduler_daemon.py queries Google Calendar (or local calendar) every 60 secondsschedule.json written with upcoming meetings in next 8 hoursmeeting detector monitors windows
meeting_daemon.py if window title matches known meeting appsuser is prompted to record
meeting_daemon.py ask_record calls notify.py which displays native macOS notification with "Start Recording?" promptrecording starts (if approved)
record_audio.py start sends command via Unix socket to AudioDaemon.appoutput_dir/TIMESTAMP_meeting-title.wavrecording ends (manual or auto-silence)
record_audio.py stop) or 5-minute silence detectedtranscription runs locally
transcribe.py spawns whisper-cli (local binary, no cloud upload)local_model_path and outputs text to TIMESTAMP_meeting-title.txtsummary request queued for OpenClaw agent
transcribe.py writes entry to ~/.config/meeting-assistant/agent-queue.json with transcript path and templatesummary_request actionOpenClaw heartbeat agent processes summary
TIMESTAMP_meeting-title-summary.md written to meeting-recordings directoryuser notified of completion
agent_notify.py or send_summary.py sends macOS notification and optionally posts to Telegram/Zulip/Notionif Google Calendar is enabled:
scheduler_daemon.py queries Calendar API every 60 secondsif meeting window is detected:
if user declines recording prompt:
meeting_daemon.py logs decision and stopsif system audio permission is missing:
sysReady=false in statusrecord_audio.py refuses to start recording; notifies user to enable Screen & System Audio Recording in System Settingsif microphone permission is missing:
micReady=false in statusif Accessibility permission is not granted:
if WAV file is created but is silent:
echo '{"action":"status"}' | nc -w 2 -U ~/.config/meeting-assistant/audio_daemon.sock to verify AudioDaemon readinessif whisper-cli transcription fails (timeout, model missing, out of memory):
transcribe.py catches exception, logs error, and still queues summary_request with empty transcriptif OpenClaw agent is not running or agent-queue is stale:
python3 scripts/send_summary.py to finalize summary or check queue with cat ~/.config/meeting-assistant/agent-queue.jsonif Google Calendar refresh token expires:
scheduler_daemon.py fails to query Calendar; logs errorgog auth add your.email@example.com --services calendarif network is offline (Keychain sync, cloudflared tunnel for webhooks):
success state:
{output_dir}/{TIMESTAMP}_{meeting_title}.wav (stereo, 16-bit PCM, sample rate per config){output_dir}/{TIMESTAMP}_{meeting_title}.txt (plain text, one line per utterance or paragraph per speaker, depending on whisper output)~/.config/meeting-assistant/agent-queue.json with schema:{
"id": "uuid",
"action": "summary_request",
"timestamp": "2024-01-15T10:30:00Z",
"transcript_path": "/path/to/transcript.txt",
"template_path": "/path/to/summary_template.md",
"meeting_title": "Project Sync",
"status": "pending"
}
{output_dir}/{TIMESTAMP}_{meeting_title}-summary.md (Markdown, generated by OpenClaw agent)file locations:
{config.audio.output_dir}/ (default: ./meeting-recordings/)~/.config/meeting-assistant/config.json~/.config/meeting-assistant/agent-queue.json~/.config/meeting-assistant/audio_daemon.socklog stream --predicate 'process == "AudioDaemon"'data format:
record_audio.py status or AudioDaemon socket query shows recording=false and lastMeeting={title} after recording stopsls -la ~/.config/meeting-assistant/meeting-recordings/ shows recent .wav, .txt, and -summary.md files with current timestampscredits: original skill by Nowhitestar (GitHub: nowhitestar/meeting-assistant). enriched for Implexa standards.