feat(bin): add sfx to render notification sounds offline from TOML recipes

sfx is a single-file uv script that synthesises sounds from sine partials and seeded noise, so its output contains no samples, SoundFonts or model output, renders without a network once uv has installed numpy and mido, and comes out byte-identical on every run. Each sound in a recipe is normalised to a target loudness (ITU-R BS.1770, default -26 LUFS, the level of the macOS Glass alert) and written as WAV, with optional MP3 through ffmpeg and a MIDI file of the same notes. Unknown keys and bad note names stop the render with an error naming the table.

The new sound-design skill documents the recipe format and how to design notification sounds; link-ai-configs.sh links it for omo and Claude Code.
This commit is contained in:
David Ibia
2026-10-18 12:55:57 +01:00
parent 161d205dfd
commit 5805f45358
5 changed files with 396 additions and 4 deletions
+3 -2
View File
@@ -103,7 +103,7 @@ The script creates individual relative symlinks, skips existing correct links, a
- `.config/omo/omo.jsonc` supplies `~/.omo/omo.jsonc` with OmO's model routing, including the `design-critique-a` and `design-critique-b` categories that the Impeccable critique delegates its two assessments to.
- `.config/ai/AGENTS.md` holds shared personal preferences. OmO reads it through `~/.config/opencode/AGENTS.md`, which is hard-wired in OmO's rules engine ahead of `~/.claude/CLAUDE.md`; the engine stops at the first file it finds, so this link both supplies these preferences and keeps Claude's OMC orchestration instructions out of OmO. Keep it even though OpenCode is retired. Claude imports the same file from its own `CLAUDE.md`.
- `.config/claude/` supplies Claude's global instructions, settings, and OMC preferences. The machine-specific OMC `nodeBinary` cache is intentionally omitted; install Node.js and run OMC setup on each new machine to configure its runtime and HUD.
- `.config/omo/skills/` holds the vendored Impeccable, Vercel `web-design-guidelines`, `frontend-workflow` and marketing skills. The script links every one into `~/.omo/agent/skills/`, and all but `frontend-workflow` into `~/.claude/skills/`; see [the skills README](.config/omo/skills/README.md) for provenance and upgrades. Portable Claude plugins and unrelated user skills remain enabled.
- `.config/omo/skills/` holds the vendored Impeccable, Vercel `web-design-guidelines`, `frontend-workflow`, `sound-design` and marketing skills. The script links every one into `~/.omo/agent/skills/`, and all but `frontend-workflow` into `~/.claude/skills/`; see [the skills README](.config/omo/skills/README.md) for provenance and upgrades. Portable Claude plugins and unrelated user skills remain enabled.
Credentials, `settings.local.json`, plugin installations, unrelated user skills, histories, caches, and runtime state are not copied into this repository or managed by this script. Install other desired plugins and portable skills separately. OmO's MCP servers live in `~/.omo/agent/mcp.json`, which this repository does not manage; the local ones (headroom, agcanvas, uvx, ast-grep-mcp, portfolio-mcp) must be installed on a new machine before they resolve.
@@ -180,7 +180,7 @@ OpenCode is retired. Its agents, commands and plugins are left in `.config/openc
- `/owt-done [notes]` finishes a workspace after its PR merges: it comments on each issue and moves it to Done, then runs `owt done --detach`. It asks before discarding uncommitted or unmerged work.
- `/impeccable [command] [target]` runs the Impeccable design skill.
- `/setup-notes`, `/sync-notes`, `/ingest-notes`, `/teleport-notes`, `/analyze-vault`, `/reorganize-notes`, `/preview-notes-delegation` and `/validate-notes-config` manage with-context documentation delegation. They came over from OpenCode and need the with-context MCP server, which is not in `~/.omo/agent/mcp.json`.
- `skills/` (linked into `~/.omo/agent/skills/`, and into `~/.claude/skills/` for the shared ones): the vendored Impeccable, Vercel `web-design-guidelines`, `frontend-workflow` and marketing skills. See [the skills README](.config/omo/skills/README.md) for provenance, local modifications and upgrades.
- `skills/` (linked into `~/.omo/agent/skills/`, and into `~/.claude/skills/` for the shared ones): the vendored Impeccable, Vercel `web-design-guidelines`, `frontend-workflow`, `sound-design` and marketing skills. See [the skills README](.config/omo/skills/README.md) for provenance, local modifications and upgrades.
- `archive/`: the OpenCode-era `DEVELOPMENT-WORKFLOW.md` and `UI-WORKFLOW.md`, kept for reference and loaded by nothing.
MCP servers (in `~/.omo/agent/mcp.json`, which this repository does not manage): Playwright, Chrome DevTools, Linear, reoclo, time, ast-grep, headroom, agcanvas and portfolio.
@@ -232,6 +232,7 @@ Custom scripts and binaries are stored in `.local/bin/`:
- `omo-notify`: Sound, tab marker and notification for OmO's hooks. Ghostty posts the notification, so clicking it opens the session's tab and pane
- `omo-status`: Coloured marker in front of the Ghostty tab title for each tmux session running omo: 🔴 stopped with an error, 🟡 waiting for your answer, 🟢 finished, 🔵 working. A tab with several agents shows the most urgent one; focusing the tab clears red, amber and green, and undoes any title you gave the tab by hand so it always shows its session name
- `omo-remote`: The same sound, tab marker and notification for omo sessions on a remote server (flowstate-remote). The server's `omo-notify` (kept on the server in `~/.local/bin`, not in this repository) writes the session's state into its terminal title, marker first (`🟢 studio · omo:done:...`). mosh drops notifications but carries titles, and so does ssh, so tmux's `pane-title-changed` hook passes the event to `omo-notify` for the pane running mosh or ssh. Everything else reaches you through ntfy: `omo-remote install` starts a launchd agent that subscribes to the topic and plays the sound and posts the notification for omo pushes, unless a tmux pane here already handled that event through its title, so nothing arrives twice. That covers sessions no terminal is showing and `fs` in a plain Ghostty tab, whose title shows the marker and which posts the notification itself (the coloured tab marker needs tmux). The agent reads `NTFY_URL`, `NTFY_TOPIC` and `NTFY_TOKEN` from `~/.config/ntfy/omo-remote.env` (chmod 600, not in this repository) and needs `brew "ntfy"`
- `sfx`: Renders notification sounds and short effects from a TOML recipe, offline and from synthesis alone, normalised to one loudness, as WAV plus optional MP3 and MIDI. A uv script; `sfx --help` lists the options and the `sound-design` skill documents the recipe format
- `docker-nuke`: Force-restart a wedged Docker Desktop, capturing diagnostics to `~/.local/state/docker-nuke/` first (`--dry-run` to preview, `--no-restart` to leave it down, `--deep` for root helpers)
- `impeccable-browser`: The browser for Impeccable's URL scans (`impeccable detect <url>`) on flowstate-remote, set as `IMPECCABLE_BROWSER` in the server's `~/.zshenv`. Public URLs render on Browserless. Its key, `BROWSERLESS_API_KEY` from the Reoclo project `flowstate-tools`, is cached for 12 hours in `~/.impeccable/browserless.token`. Localhost and private addresses reach Browserless through a cloudflared quick tunnel that lasts only for the scan, and reports still name the local URL. Other launches, and any scan whose tunnel or Browserless connection fails, run on the headless Chrome in the Puppeteer cache with `--no-sandbox`, because the server's AppArmor blocks Chrome's sandbox. `IMPECCABLE_TUNNEL=0` keeps private addresses on local Chrome, which pages that call a second local port (a separate API, say) need. Every decision is logged to `~/.impeccable/browser.log`