Speech.framework wake-word hook daemon for macOS 26.
31
stars
30
commits
Swift
primary language
Sep 4, 2026
updated

Speech.framework wake-word hook daemon for macOS 26.
swabble is a Swift 6.2, macOS 26-only rewrite of the brabble voice daemon. It listens on your mic, gates on a wake word, transcribes locally using Apple's new SpeechAnalyzer + SpeechTranscriber, then fires a shell hook with the transcript. No cloud calls, no Whisper binaries.
clawd (aliases claude), optional --no-wake bypass.# Install deps
brew install swiftformat swiftlint
# Build
swift build
# Inspect commands and options
swift run swabble --help
# Write default config (~/.config/swabble/config.json)
swift run swabble setup
# Run foreground daemon
swift run swabble serve
# Test your hook
swift run swabble test-hook "hello world"
# Transcribe a file to SRT
swift run swabble transcribe /path/to/audio.m4a --format srt --output out.srt
Add swabble as a SwiftPM dependency and import the Swabble product to reuse the Speech pipeline, config loader, hook runner, and transcript store in your own app:
// Package.swift
dependencies: [
.package(url: "https://github.com/openclaw/swabble.git", branch: "main"),
],
targets: [
.target(name: "MyApp", dependencies: [.product(name: "Swabble", package: "swabble")]),
]
serve — foreground loop (mic → wake → hook)transcribe <file> — offline transcription (txt|srt)test-hook "text" — invoke configured hookmic list|set <index> — enumerate inputs and save a preferred device index (live selection not wired yet)setup — write default config JSONdoctor — check Speech auth & device availabilityhealth — prints oktail-log — last 10 transcriptsstatus — show wake state + recent transcriptsservice install|uninstall|status — user launchd plist (stub: prints launchctl commands)start|stop|restart — placeholders until full launchd wiringUse swabble <command> --help for command options. health and status support --json/--json-output; commands that read config support --config where listed.
~/.config/swabble/config.json (auto-created by setup):
{
"audio": {"deviceName": "", "deviceIndex": -1, "sampleRate": 16000, "channels": 1},
"wake": {"enabled": true, "word": "clawd", "aliases": ["claude"]},
"hook": {
"command": "",
"args": [],
"prefix": "Voice swabble from ${hostname}: ",
"cooldownSeconds": 1,
"minCharacters": 24,
"timeoutSeconds": 5,
"env": {}
},
"logging": {"level": "info", "format": "text"},
"transcripts": {"enabled": true, "maxEntries": 50},
"speech": {"localeIdentifier": "en_US", "etiquetteReplacements": false}
}
--config /path/to/config.json on relevant commands.setup refuses to replace an existing config unless passed --force.~/Library/Application Support/swabble/transcripts.log.When a wake-gated transcript passes min_chars & cooldown, swabble runs:
<command> <args...> "<prefix><text>"
Environment variables:
SWABBLE_TEXT — stripped transcript (wake word removed)SWABBLE_PREFIX — rendered prefix (hostname substituted)hook.env key/valuesAVAudioEngine tap → BufferConverter → AnalyzerInput → SpeechAnalyzer with a SpeechTranscriber module../scripts/format.sh (uses ../peekaboo/.swiftformat if present)./scripts/lint.sh (uses ../peekaboo/.swiftlint.yml if present)swift test (uses swift-testing package)Swift
52.3%
JavaScript
47.4%
Speech.framework wake-word hook daemon for macOS 26.
31
stars
30
commits
Swift
primary language
Sep 4, 2026
updated

Speech.framework wake-word hook daemon for macOS 26.
swabble is a Swift 6.2, macOS 26-only rewrite of the brabble voice daemon. It listens on your mic, gates on a wake word, transcribes locally using Apple's new SpeechAnalyzer + SpeechTranscriber, then fires a shell hook with the transcript. No cloud calls, no Whisper binaries.
clawd (aliases claude), optional --no-wake bypass.# Install deps
brew install swiftformat swiftlint
# Build
swift build
# Inspect commands and options
swift run swabble --help
# Write default config (~/.config/swabble/config.json)
swift run swabble setup
# Run foreground daemon
swift run swabble serve
# Test your hook
swift run swabble test-hook "hello world"
# Transcribe a file to SRT
swift run swabble transcribe /path/to/audio.m4a --format srt --output out.srt
Add swabble as a SwiftPM dependency and import the Swabble product to reuse the Speech pipeline, config loader, hook runner, and transcript store in your own app:
// Package.swift
dependencies: [
.package(url: "https://github.com/openclaw/swabble.git", branch: "main"),
],
targets: [
.target(name: "MyApp", dependencies: [.product(name: "Swabble", package: "swabble")]),
]
serve — foreground loop (mic → wake → hook)transcribe <file> — offline transcription (txt|srt)test-hook "text" — invoke configured hookmic list|set <index> — enumerate inputs and save a preferred device index (live selection not wired yet)setup — write default config JSONdoctor — check Speech auth & device availabilityhealth — prints oktail-log — last 10 transcriptsstatus — show wake state + recent transcriptsservice install|uninstall|status — user launchd plist (stub: prints launchctl commands)start|stop|restart — placeholders until full launchd wiringUse swabble <command> --help for command options. health and status support --json/--json-output; commands that read config support --config where listed.
~/.config/swabble/config.json (auto-created by setup):
{
"audio": {"deviceName": "", "deviceIndex": -1, "sampleRate": 16000, "channels": 1},
"wake": {"enabled": true, "word": "clawd", "aliases": ["claude"]},
"hook": {
"command": "",
"args": [],
"prefix": "Voice swabble from ${hostname}: ",
"cooldownSeconds": 1,
"minCharacters": 24,
"timeoutSeconds": 5,
"env": {}
},
"logging": {"level": "info", "format": "text"},
"transcripts": {"enabled": true, "maxEntries": 50},
"speech": {"localeIdentifier": "en_US", "etiquetteReplacements": false}
}
--config /path/to/config.json on relevant commands.setup refuses to replace an existing config unless passed --force.~/Library/Application Support/swabble/transcripts.log.When a wake-gated transcript passes min_chars & cooldown, swabble runs:
<command> <args...> "<prefix><text>"
Environment variables:
SWABBLE_TEXT — stripped transcript (wake word removed)SWABBLE_PREFIX — rendered prefix (hostname substituted)hook.env key/valuesAVAudioEngine tap → BufferConverter → AnalyzerInput → SpeechAnalyzer with a SpeechTranscriber module../scripts/format.sh (uses ../peekaboo/.swiftformat if present)./scripts/lint.sh (uses ../peekaboo/.swiftlint.yml if present)swift test (uses swift-testing package)Swift
52.3%
JavaScript
47.4%