short confirmation tones on daemon events so the user gets eyes-free "did it hear me?"
feedback without watching the terminal. NOT TTS — short pre-generated .wav beeps.
- audio_out.py — reusable audio-OUT module (the reverse of audio.py's capture, the
less-tested WSLg direction). three-tier player: paplay-first (a SEPARATE process, so
it doesn't contend with the sounddevice mic stream on the duplex-flaky WSLg bridge),
then in-process sounddevice, then powershell.exe SoundPlayer. best-effort per-backend
volume. plays a wav path and knows nothing about events — v0.3 TTS reuses it.
- sound.py — Earcons: the single event->tone map (wake/accept/no_match/submit) gated by
[sound] config (master enabled + per-event flags). daemon._handle wiring: an injected
command plays accept (submit plays submit); no-match / target-missing / unknown-context
plays no_match; pure daemon-control commands (list/version/…) play nothing.
- sounds/ — committed earcon wavs + generate.py (regen-only). committed (not generated
at install) so the package is self-contained and a missing tone can never appear.
packaged via pyproject [tool.setuptools.package-data].
- [sound] config: enabled (master, on), on_wake (OFF by default — bleed/chatty),
on_accept/on_no_match/on_submit (on), volume (0-1 best-effort), [sound.files] overrides.
- claudedo test-tone — plays each tone, the audio-OUT gate (mirrors test-audio).
- install.sh now also checks RDPSink (audio-out) alongside RDPSource.
INVARIANT: earcons are fire-and-forget on a worker thread and NEVER block or break the
inject path. a missing tone file or dead speaker logs once and is swallowed, never
raised — a broken speaker must never stop "claudedo yes" from injecting.
de-risks the WSLg audio-OUT path that v0.3 TTS-readback will reuse.
Signed-off-by: disqualifier <dev@disqualifier.me>
context injection — named reference blurbs from contexts.toml injected ahead of a
dictated instruction, read-before-send (never auto-submits):
- new contexts.py mirrors config.py: [contexts] name = "blurb"; missing file = empty
set; names validated as simple words, looked up on a despaced/lowercased key so
"web hooks"/"web-hooks"/"webhooks" all resolve the same block.
- grammar: context|prepare <name> <instruction> -> Action("context", (name, dictation)).
same-utterance dictation (everything after <name> is literal, incl. "send"); bare
context <name> injects just the blurb. one-shot targeting composes:
[target <name>] [context <ctx>] [filler] <dictation>.
- daemon assembles blurb + (Shift+Enter soft newline | flattened separator) + dictation
via the existing send_literal/type path, tracks the uncommitted-input buffer, and
WAITS. config-gated by behavior.context_multiline / context_separator. unknown context
name announces and injects nothing.
system daemon-control namespace — lands the pass-through vs control split the router was
structured for. reserved leading "system" routes to _do_system (never injects to
claude): system status (mode/target/model/contexts) and system reload [config|contexts].
live reload — voice reload + CLI claudedo reload (SIGHUP) re-read config.toml +
contexts.toml without reinitializing the loaded whisper model. customs now lists loaded
contexts. install.sh installs the contexts.toml template copy-if-absent (else .new).
keys.NEWLINE (S-Enter) added for the soft-newline assembly. wake list unchanged.
Signed-off-by: disqualifier <dev@disqualifier.me>
the daemon's config lookup falls through to ./config.toml only, so without a copy in
the standard dir it was repo-cwd-only. install config.toml to ~/.config/claudedo/ —
copy if absent, else write config.toml.new beside the user's edited copy (never
clobber). also gitignore COMPACT.md (handoff doc kept on disk, untracked).
Signed-off-by: disqualifier <dev@disqualifier.me>
terminal-run is the product, so remove all backgrounding: delete the
claudedo.service unit and autostart.sh, strip the systemd step and the
autostart source-line from install.sh (rc block now sources cc.sh only).
claudedo start now runs a mic check first (warm-up + brief capture, aborts with
guidance if silent; --skip-audio-check to bypass) then drops into a visible
listen loop printing the recognition/action log: a startup banner, then
heard -> matched -> target / injected per utterance, target/mode state changes,
and (listen mode) non-wake speech dropped WITHOUT the transcript per the privacy
invariant.
Signed-off-by: disqualifier <dev@disqualifier.me>
append escape-time 0, large history-limit, allow-passthrough, and extended-keys
to ~/.tmux.conf under an idempotent marker block (no clobber). required for
reliable keystroke injection and for notifications/modified-keys to reach the
claude pane.
Signed-off-by: disqualifier <dev@disqualifier.me>
cc kit as a sourced ~/.config/claudedo/cc.sh (bash+zsh, forced explicit names).
opt-in rc autostart guarded by CLAUDEDO_AUTOSTART + an optional systemd user
unit. install.sh is idempotent: WSL audio deps, ~/.asoundrc pulse shim, audio
verify, model prime, and source-line rc wiring with backups.
Signed-off-by: disqualifier <dev@disqualifier.me>