Follow-up to Building a 100% local meeting transcription app for macOS — Scripta already records dual-channel audio and summarizes with a local LLM. v3.4.7 adds something I kept forgetting to do myself: hit Record before the meeting starts.
Scripta works great when I remember to open it and tap Start Recording. I do not always remember.
Recurring standups, interviews, and “just a quick sync” calls all have a start time on the calendar — but my muscle memory does not. Cloud assistants solve this by running in someone else’s datacenter. Scripta’s whole pitch is nothing leaves the Mac, so “schedule a bot in the cloud” was never an option.
What I wanted instead:
2026-09-25_16-50-00)
That shipped in Scripta v3.4.7.
Open Scheduled Recordings from the toolbar calendar icon (or the menu bar):
Create a meeting — title, window, language, optional notification if the app is not open at start time:
Upcoming items show status (Pending, Recording now, Waiting to start, etc.) with Edit, Cancel, or Stop recording when relevant:
When a scheduled session finishes, the export folder’s session.json includes a title and optional scheduledRecordingId, so Meeting History is searchable by name.
These are intentional — not missing features for v3.5:
| Rule | Why |
|---|---|
| No overlapping schedules | Saves fail validation if windows intersect |
| No auto-summary after scheduled stop | Manual recordings still offer the summary sheet when Ollama is ready; scheduled runs set suppressAutoSummary so you are not interrupted |
| No takeover without consent | If you are already recording at start time, Scripta asks whether to stop the current session and start the scheduled one |
| App must be running for auto-capture | Phase 1 is in-process scheduling only — no launchd daemon silently recording in the background |
| If the app is closed at start time | A local notification nudges you to open Scripta; it does not pretend recording happened |
Calendar import, multi-device sync, and “record with zero UI process” are explicitly later phases.
Persistence is a single JSON file:
~/Library/Application Support/Scripta/schedules.json
A ScheduleCoordinator ticks about once per second while the app runs:
startRecording() if permissions are already grantedstopRecording() for that schedule’s session
Before auto-start, ScreenRecordingAccess checks mic, speech, and ScreenCaptureKit — scheduled paths avoid hammering the system permission dialog every second if you forgot to grant Screen Recording.
Recording still uses the same pipeline as manual mode: whisper.cpp on the mic, SFSpeechRecognizer on system audio via ScreenCaptureKit (architecture write-up).
Optional Awake (bottom bar, default on): while recording or finishing transcription, Scripta holds an NSProcessInfo activity to prevent idle system and display sleep — not a guarantee against lid-close or manual sleep, but enough for long calls at your desk.
The scheduler did not create new TCC categories — it surfaced the same ones as manual record:
Run Permissions Setup once before the meeting window. If auto-start fails, the list shows Waiting to start with Retry start after you fix settings and return to the app.
If you develop from source, install to /Applications/Scripta.app ( scripts/install-local.sh) so TCC matches what users run in production. swift run binaries are a different story.
@Published language on a background threadMeetingRecorder actually enters .recording`` ContentView lifecycle modifiers into smaller views fixed macOS 15 CI.
Install (macOS 14+, Apple Silicon or Intel):
curl -fsSL https://raw.githubusercontent.com/thehwang/Scripta/main/scripts/install.sh | bash
Repo: github.com/thehwang/Scripta
After install: complete Permissions Setup, open Scheduled Recordings, add a meeting a few minutes ahead, leave Scripta open, and watch it start on its own.
If this fits your workflow, a GitHub star helps others find it. Issues and PRs welcome — especially around calendar import and safer wake-from-sleep behavior.
Scripta is MIT-licensed. Scheduled recording is local-only: your schedules live in schedules.json on disk, not on our servers — because there are no servers.