Skip to content

"Hey, Computer" from Star Trek. Talk to your agent. Run hooks after trigger comands. Runs locally, cause shit's scary.

License

Notifications You must be signed in to change notification settings

steipete/brabble

Repository files navigation

🎙️ Brabble — Open hailing frequencies… and run the command.

Always-on, local-only voice daemon for macOS. Hears your wake word (“clawd” by default), transcribes with whisper.cpp, then fires a configurable hook (user-defined, e.g., warelay heartbeat). Written in Go; ships with a daemon lifecycle, status socket, and launchd helper.

Quick start

  • Requirements: Go 1.25+, brew install portaudio pkg-config, a whisper.cpp model.
  • One-liner: pnpm brabble setup && pnpm start (downloads medium Q5_1, writes config, starts daemon).
  • Foreground run: go run ./cmd/brabble serve (mic + PortAudio required).

CLI surface

  • start | stop | restart — daemon lifecycle (PID + UNIX socket).
  • status [--json] — uptime + last transcripts; tail-log shows recent logs.
  • mic list|set [--index N] — enumerate or select microphone (aliases: mics, microphone).
  • models list|download|set — manage whisper.cpp models under ~/Library/Application Support/brabble/models.
  • setup — download default model and update config; doctor — check deps/model/hook/portaudio.
  • test-hook "text" — invoke hook manually; health — ping daemon; service install|uninstall|status — launchd helper (prints kickstart/bootout commands).
  • transcribe <wav> — run whisper on a WAV file; add --hook to send it through your configured hook (respects wake/min_chars unless --no-wake).
  • Hidden internal: serve runs the foreground daemon (used by start/launchd).
  • --metrics-addr enables Prometheus text endpoint; --no-wake bypasses wake word.

PNPM helpers (all build Go, no JS runtime)

  • pnpm brabble — build then start daemon (default); extra args pass through, e.g. pnpm brabble --help, pnpm brabble status.
  • pnpm start|stop|restart — lifecycle wrappers.
  • pnpm build — build to bin/brabble; pnpm lintgolangci-lint run; pnpm formatgofmt -w .; pnpm testgo test ./....
  • Lint deps: brew install golangci-lint; CI runs gofmt+golangci-lint+tests (see .github/workflows/ci.yml).

File-based testing

  • Transcribe without the daemon: pnpm brabble transcribe samples/clip.wav
  • Send through your hook (wake+min_chars enforced): pnpm brabble transcribe samples/clip.wav --hook
  • Ignore wake gating for a file: pnpm brabble transcribe samples/clip.wav --hook --no-wake
  • Input: any WAV; we downmix to mono and resample to 16 kHz internally.

Config (auto-created at ~/.config/brabble/config.toml)

[audio]
device_name = ""
device_index = -1
sample_rate = 16000
channels = 1
frame_ms = 20          # 10/20/30 only

[vad]
enabled = true
silence_ms = 1000      # end-of-speech detector
aggressiveness = 2
energy_threshold = -35.0  # dBFS gate; raise (e.g., -30) to suppress low-noise hallucinations
min_speech_ms = 300
max_segment_ms = 10000
partial_flush_ms = 4000  # emit partial segments (not sent to hook)

[asr]
 model_path = "~/Library/Application Support/brabble/models/ggml-large-v3-turbo-q8_0.bin"
language = "auto"
compute_type = "q5_1"
device = "auto"       # auto/metal/cpu

[wake]
enabled = true
word = "clawd"
aliases = ["claude"]
sensitivity = 0.6

[hook]
command = ""                       # REQUIRED: set to your warelay binary path
args = []                          # e.g., ["heartbeat", "--message"]
prefix = "Voice brabble from ${hostname}: "
cooldown_sec = 1
min_chars = 24
max_latency_ms = 5000
queue_size = 16
timeout_sec = 30
redact_pii = false
env = {}

[logging]
level = "info"   # debug|info|warn|error
format = "text"  # text|json
stdout = false   # also log to stdout (defaults to file-only)

[daemon]
stop_timeout_sec = 5     # wait for PID to clear on restart

[metrics]
enabled = false
addr = "127.0.0.1:9317"

[transcripts]
enabled = true

State & logs: ~/Library/Application Support/brabble/ (pid, socket, logs, transcripts, models).

Models

  • Registry: ggml-small-q5_1.bin, ggml-medium-q5_1.bin (default), ggml-large-v3-q5_0.bin.
  • brabble models download <name> fetches to the models dir; brabble models set <name|path> updates config.
  • brabble setup fetches the default model and writes asr.model_path; reruns doctor afterward.

Audio & wake

  • PortAudio capture → WebRTC VAD → partial segments every partial_flush_ms (suppressed from hook) → final segment; retries device open on failure.
  • Wake word (case-insensitive) is stripped before dispatch; disable with --no-wake or BRABBLE_WAKE_ENABLED=0. If wake word is “clawd”, “Claude” is also accepted.
  • Partial transcripts are logged with Partial=true and skipped by the hook; full segments respect hook.min_chars and cooldown.

Hook

  • Default hook: ../warelay send "<prefix><text>", prefix includes hostname.
  • Extra env: BRABBLE_TEXT, BRABBLE_PREFIX plus any hook.env; redaction toggle masks obvious emails/phones.
  • Queue + timeout + cooldown prevent flooding; test-hook is the dry-run.

Service (launchd)

  • brabble service install --env KEY=VAL writes ~/Library/LaunchAgents/com.brabble.agent.plist and prints:
    • launchctl load -w <plist>
    • launchctl kickstart gui/$(id -u)/com.brabble.agent
    • launchctl bootout gui/$(id -u)/com.brabble.agent
  • service status reports whether the plist exists; service uninstall removes the plist file.

Env overrides

BRABBLE_WAKE_ENABLED, BRABBLE_METRICS_ADDR, BRABBLE_LOG_LEVEL, BRABBLE_LOG_FORMAT, BRABBLE_TRANSCRIPTS_ENABLED, BRABBLE_REDACT_PII (1/0).

Notes on VAD options

  • WebRTC VAD ships by default. Silero VAD (onnxruntime) remains an optional future path; onnxruntime is the runtime library for ONNX models and would be pulled in only if we add Silero.

Development / testing

  • Go style: gofmt tabs (default). golangci-lint config lives at .golangci.yml.
  • Tests: go test ./... plus config/env/hook coverage.
  • Build: build whisper.cpp once, then:
    # headers + libs placed in /usr/local/{include,lib}/whisper (see docs/spec.md)
    export CGO_CFLAGS='-I/usr/local/include/whisper'
    export CGO_LDFLAGS='-L/usr/local/lib/whisper'
    go build -o bin/brabble ./cmd/brabble
    install_name_tool -add_rpath /usr/local/lib/whisper bin/brabble
  • Models: defaults to ggml-large-v3-turbo-q8_0.bin; best quality ggml-large-v3-turbo.bin; lighter option ggml-medium-q5_1.bin. Use brabble models download <name> then brabble models set <name>.
  • CI: GitHub Actions (.github/workflows/ci.yml) runs gofmt check, golangci-lint, and go test.

🎙️ Brabble. Make it say. [hooks]

Optional per-wake hooks. First matching entry wins.

[[hooks]]

wake = ["clawd", "claude"]

aliases = ["clawd"]

command = "/path/to/warelay"

args = ["heartbeat", "--message"]

prefix = "Voice brabble from ${hostname}: "

min_chars = 16

cooldown_sec = 1

timeout_sec = 5

queue_size = 16

redact_pii = false

About

"Hey, Computer" from Star Trek. Talk to your agent. Run hooks after trigger comands. Runs locally, cause shit's scary.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Languages