macOS meeting transcription app — Granola-style. Captures both sides of a video call (mic + system audio), transcribes live with OpenAI Realtime, takes notes alongside, and produces an AI summary on stop.
BYO OpenAI key. Local-first storage. Multi-calendar via EventKit. Auto-detects Zoom / Teams / Webex / Meet / BlueJeans / Slack on launch.
All seven phases of the implementation plan are scaffolded:
- Phase 1 — Audio capture (mic via VoiceProcessingIO + AEC, system via ScreenCaptureKit), 24 kHz PCM mono
- Phase 2 — Two-stream live transcription with server-VAD continuous mode, merged segment list
- Phase 3 — Notes editor + Markdown / JSON persistence to a security-scoped output folder
- Phase 4 — AI summary via OpenAI Chat Completions (
response_format: json_schema) - Phase 5 — EventKit-backed today's-events list (iCloud + Gmail + Fastmail + any other CalDAV/Exchange) with one-click "start recording from this event"
- Phase 6 — App-launch detection (Zoom / Teams / Webex / Meet / BlueJeans / Slack) with notification banners
- Phase 7 — Makefile, GitHub Actions release workflow, Homebrew cask template
The Xcode project (Convene.xcodeproj) is generated from project.yml and committed for convenience.
Convene/
├── Audio/ Mic + system audio capture, WAV writer
├── Transcription/ OpenAI Realtime API client (transcription mode)
├── Calendar/ EventKit wrapper (multi-account)
├── Detection/ NSWorkspace meeting detector + UN delegate
├── Storage/ Markdown / JSON persistence with security-scoped bookmarks
├── Summary/ Chat Completions structured-output summary service
├── Models/ MeetingStore, Meeting, TranscriptSegment
├── UI/ SwiftUI views: meeting window, menu, settings
├── Util/ Logger
├── Hotkeys/ KeyboardShortcuts setup
├── Keychain/ OpenAI API key storage
├── Info.plist
└── Convene.entitlements
.github/workflows/release.yml Build → notarize → DMG → GitHub Release
homebrew/convene.rb Cask template (publish to mblode/homebrew-tap)
Makefile build / install / archive / dmg / notarize
The Xcode project is generated from project.yml by XcodeGen.
brew install xcodegen # one-time
xcodegen generate # refreshes Convene.xcodeproj
open Convene.xcodeproj # or `make install` to build + relaunchRe-run xcodegen generate whenever you add a Swift file or change project.yml. SPM resolves the KeyboardShortcuts dependency automatically on first build.
Convene will prompt for these:
- Microphone — to capture your side
- Screen Recording — required by ScreenCaptureKit even for audio-only capture; no video is recorded
- Calendar — to attach meetings to events from all configured macOS calendars
- Notifications — for the "meeting detected" banner
make build # Release build
make install # Debug build → /Applications/Convene.app → relaunch
make dmg # Build + sign + create DMG
make notarize # Build + DMG + notarize (requires APPLE_TEAM_ID, NOTARIZE_APPLE_ID, NOTARIZE_PASSWORD)
make clean # Remove /tmp/convene-buildBits of code adapted from sibling projects:
Transcription/RealtimeTranscriptionClient.swift— verbatim from~/Code/mblode/commandment/. Used for one-shot commit/transcribe; not currently wired (kept available for future use).Transcription/LiveTranscriptionStream.swift— new for Convene; continuous server-VAD transcription.Keychain/KeychainManager.swift,Util/Logger.swift,Hotkeys/HotkeyManager.swift— adapted from~/Code/mblode/commandment/(service id / log filename swapped).Audio/MicCapture.swift— slimmed-down adaptation of~/Code/mblode/rubber-duck/apps/macos/AudioManager.swift. Kept the 24 kHz PCM mono pipeline + VoiceProcessingIO hardware AEC + noise gate. Dropped the playback-coupled software AEC and startup planner (not relevant for meeting transcription).- Everything in
Audio/SystemAudioCapture.swift,Audio/AudioCaptureCoordinator.swift,Audio/WAVFileWriter.swift,Calendar/,Detection/,Storage/,Summary/, and most ofModels/andUI/— new.
Tag-driven via GitHub Actions:
git tag v0.1.0 && git push origin main --tagsThe release.yml workflow builds → signs (Developer ID) → notarizes → creates a GitHub Release with the DMG attached → signs and publishes the Sparkle appcast → updates the Homebrew tap.
Required secrets: DEVELOPER_ID_CERT_P12, DEVELOPER_ID_CERT_PASSWORD, APPLE_TEAM_ID, NOTARIZE_APPLE_ID, NOTARIZE_PASSWORD, SPARKLE_PRIVATE_ED_KEY, and HOMEBREW_TAP_TOKEN.
Sparkle checks https://raw.githubusercontent.com/mblode/convene/main/appcast.xml, and the generated Homebrew cask is published to mblode/homebrew-tap.