apps/swabble/docs/spec.md
Goal: brabble-style always-on voice hook for macOS 26 using Apple Speech.framework (SpeechAnalyzer + SpeechTranscriber) instead of whisper.cpp. Local-only, wake word gated, dispatches a shell hook with the transcript. Shared wake-gate utilities live in SwabbleKit for reuse by other apps (iOS/macOS).
--no-wake.SwabbleKit target (multi-platform) providing wake-word gating helpers that can use speech segment timing to require a post-trigger gap.~/.config/swabble/config.json (JSON, Codable) — no TOML.steipete/Commander); core types are available via the SwiftPM library product Swabble for embedding.serve; later launchd helper for start/stop/restart.swabble with subcommands serve, transcribe, test-hook, mic list|set, doctor, health, tail-log. Runtime flags from Commander (-v/--verbose, --json-output, --log-level). Custom --config path applies everywhere.SwabbleConfig Codable. Fields: audio device name/index, wake (enabled/word/aliases/sensitivity placeholder), hook (command/args/prefix/cooldown/min_chars/timeout/env), logging (level, format), transcripts (enabled, max kept), speech (locale, enableEtiquetteReplacements flag). Stored JSON; default written by setup.SpeechPipeline wraps AVAudioEngine input → SpeechAnalyzer with SpeechTranscriber module. Emits partial/final transcripts via async stream. Requests .audioTimeRange when transcripts enabled. Handles Speech permission and asset download prompts ahead of capture.SwabbleKit gate can enforce a minimum pause between the wake word and the next token when speech segments are available. --no-wake disables gating.HookExecutor spawns Process with configured args, prefix substitution ${hostname}. Enforces cooldown + timeout; injects env SWABBLE_TEXT, SWABBLE_PREFIX plus user env map.~/Library/Application Support/swabble/transcripts.log.SwabbleKit; CLI still text-only until segment timing is plumbed through).status/health (currently planned as stdin/out direct calls).