The project's README from github.com/PeonPing/peon-ping. As one markdown file: README.md.
Game character voice lines + visual overlay notifications when your AI coding agent needs attention — or let the agent pick its own sound via MCP.
AI coding agents don't notify you when they finish or need permission. You tab away, lose focus, and waste 15 minutes getting back into flow. peon-ping fixes this with voice lines and bold on-screen banners from Warcraft, StarCraft, Portal, Zelda, and more — works with Claude Code, Amp, GitHub Copilot, Codex, Grok Build, Cursor, OpenCode, Kilo CLI, Kiro, Kimi Code, Windsurf, Google Antigravity, Rovo Dev CLI, DeepAgents, Qwen Code, iFlow CLI, Trae, Kiro IDE, ECA, and any MCP client.
See it in action → peonping.com
brew install PeonPing/tap/peon-ping
Then run peon-ping-setup to register hooks and download sound packs. macOS and Linux.
curl -fsSL https://raw.githubusercontent.com/PeonPing/peon-ping/main/install.sh | bash
⚠️ WSL2 audio notes. peon-ping plays audio on the Windows side. On first run it probes your Windows host once (cached per Windows build) to pick the best playback path:
On Windows 10 / Windows 11 pre-24H2, WPF MediaPlayer is used directly — native MP3 + WAV, no extra dependencies.
On Windows 11 24H2+ (build 26100+), Microsoft removed legacy Windows Media Player from the OS and WPF MediaPlayer fails (MILAVERR_INVALIDWMPVERSION). peon-ping falls back to System.Media.SoundPlayer, which uses the Win32 PlaySound API and works everywhere — but it's WAV-only, so MP3 packs require ffmpeg to transcode on the fly:
sudo apt update; sudo apt install -y ffmpeg
You can override the auto-detection with PEON_WSL_AUDIO_BACKEND=auto|mediaplayer|soundplayer:
auto (default) — probe + cache as described abovemediaplayer — force WPF MediaPlayer over the WSL UNC path (fails silently on 24H2+)soundplayer — force tmpfile copy + SoundPlayer (universal, requires ffmpeg for non-WAV files)Invoke-WebRequest -Uri "https://raw.githubusercontent.com/PeonPing/peon-ping/main/install.ps1" -OutFile ".\install.ps1" -UseBasicParsing
powershell -ExecutionPolicy Bypass -File .\install.ps1
Installs a curated starter set of packs by default. Re-run to update while preserving config/state. Or pick your packs interactively at peonping.com and get a custom install command.
Windows installer parameters:
-All — install all available packs-Packs peon,sc_kerrigan,... — install specific packs only-Lang en,fr,... — install only packs matching language(s)-Local — install packs, config, hooks, and skills into ./.claude/ for the current project-Global — explicit global install (same as default)-InitLocalConfig — create ./.claude/hooks/peon-ping/config.json only-Local does not install the global peon CLI shim or modify your user PATH. Hooks are registered in the project-level ./.claude/settings.json with absolute paths so they work from any working directory within the project.
Windows examples:
powershell -ExecutionPolicy Bypass -File .\install.ps1 -All
powershell -ExecutionPolicy Bypass -File .\install.ps1 -Packs peon,sc_kerrigan
powershell -ExecutionPolicy Bypass -File .\install.ps1 -Local
powershell -ExecutionPolicy Bypass -File .\install.ps1 -InitLocalConfig
If the initial download fails with a TLS error on older Windows PowerShell, run this once in the same session and retry:
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
git clone https://github.com/PeonPing/peon-ping.git
cd peon-ping
./install.sh
On Windows PowerShell:
git clone https://github.com/PeonPing/peon-ping.git
Set-Location peon-ping
.\install.ps1
Run directly from source without installing:
nix run github:PeonPing/peon-ping -- status
nix run github:PeonPing/peon-ping -- packs install peon
Or install to your profile:
nix profile install github:PeonPing/peon-ping
Development shell (bats, shellcheck, nodejs):
nix develop # or use direnv
For reproducible setups, use the Home Manager module:
# In your home.nix or flake.nix
{ inputs, pkgs, ... }:
let
peonCursorAdapterPath = "${inputs.peon-ping.packages.${pkgs.system}.default}/share/peon-ping/adapters/cursor.sh";
in {
imports = [ inputs.peon-ping.homeManagerModules.default ];
programs.peon-ping = {
enable = true;
package = inputs.peon-ping.packages.${pkgs.system}.default;
claudeCodeIntegration = true;
settings = {
default_pack = "glados";
volume = 0.7;
enabled = true;
desktop_notifications = true;
categories = {
"session.start" = true;
"task.complete" = true;
"task.error" = true;
"input.required" = true;
"resource.limit" = true;
"user.spam" = true;
};
};
# Install packs from og-packs (simple string notation)
# and custom sources (attrset with name + src)
installPacks = [
"peon"
"glados"
"sc_kerrigan"
# Custom pack from GitHub (openpeon.com registry)
{
name = "mr_meeseeks";
src = pkgs.fetchFromGitHub {
owner = "kasperhendriks";
repo = "openpeon-mrmeeseeks";
rev = "main"; # or use a commit hash for reproducibility
sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
}
];
enableZshIntegration = true;
};
# Optional extra IDE hooks, like Cursor
home.file.".cursor/hooks.json".text = builtins.toJSON {
version = 1;
hooks = {
afterAgentResponse = [{ command = "bash ${peonCursorAdapterPath} afterAgentResponse"; }];
stop = [{ command = "bash ${peonCursorAdapterPath} stop"; }];
};
};
}
Sound pack installation: The installPacks option supports two formats:
"peon", "glados") — fetched from the og-packs repositoryname and src fields, where src can be any Nix fetcher result (e.g., pkgs.fetchFromGitHub)For packs listed on openpeon.com, find the GitHub repository link and use pkgs.fetchFromGitHub:
{
name = "pack_name";
src = pkgs.fetchFromGitHub {
owner = "github-owner";
repo = "repo-name";
rev = "main"; # or a commit hash/tag
sha256 = ""; # Leave empty first, Nix will tell you the correct hash
};
}
Claude Code hooks: set programs.peon-ping.claudeCodeIntegration = true; to install the Claude Code hook scripts under ~/.claude/hooks/peon-ping/ and merge the standard peon-ping hook entries into ~/.claude/settings.json.
Other IDE hooks: adapters for other IDEs are still opt-in so the module does not overwrite unrelated IDE settings. peon-ping provides adapter scripts such as cursor.sh in adapters/, and you can wire them like this:
${inputs.peon-ping.packages.${pkgs.system}.default}/share/peon-ping/adapters/$YOUR_IDE.sh EVENT_NAME
See the Cursor example above.
| Event | CESP Category | Examples |
|---|---|---|
| Session starts | session.start |
"Ready to work!", "Something need doing?" |
| Task finishes | task.complete |
"Work complete.", "Work, work." |
| Agent acknowledged task | task.acknowledge |
"I can do that.", "Be happy to.", "Okie dokie." (disabled by default) |
| Permission needed | input.required |
"Hmm?", "What you want?", "Yes?" |
| Tool or command error | task.error |
"Me not that kind of orc!", "Ugh." |
| Rate or token limit hit | resource.limit |
"Why not?" |
| Rapid prompts (3+ in 10s) | user.spam |
"Whaaat?", "Me busy, leave me alone!", "No time for play." |
Plus large overlay banners on every screen (macOS/WSL/MSYS2) and terminal tab titles (● project: done) — you'll know something happened even if you're in another app.
peon-ping implements the Coding Event Sound Pack Specification (CESP) — an open standard for coding event sounds that any agentic IDE can adopt.
Need to mute sounds and notifications during a meeting or pairing session? Two options:
| Method | Command | When |
|---|---|---|
| Slash command | /peon-ping-toggle |
While working in Claude Code |
| CLI | peon toggle |
From any terminal tab |
Prefer it to happen automatically? Set focus_detect to have peon-ping honor macOS Focus / Do Not Disturb. Sounds and notifications go quiet whenever a Focus is on and resume when you turn it off. See also headphones_only and meeting_detect.
Other CLI commands:
Windows note: Windows currently supports the day-one controls (
status,toggle,volume, corepacks,notifications on/off,debug,logs,trainer). More advanced commands likesetup,rotation,preview, andmobileare tracked as follow-up Windows parity work.
peon setup # Interactive setup wizard (volume, categories, notifications)
peon pause # Mute sounds
peon resume # Unmute sounds
peon mute # Alias for 'pause'
peon unmute # Alias for 'resume'
peon status # Check if paused or active (concise)
peon status --verbose # Show full details (notifications, headphones, IDEs, etc.)
peon volume # Show current volume
peon volume 0.7 # Set volume (0.0–1.0)
peon rotation # Show current rotation mode
peon rotation random # Set rotation mode (random|round-robin|session_override)
peon packs list # List installed sound packs
peon packs list --registry # Browse all available packs in the registry
peon packs community # List all registry packs grouped by trust tier (Windows)
peon packs search <query> # Search registry packs by name (Windows)
peon packs install <p1,p2> # Install packs from the registry
peon packs install --all # Install all packs from the registry
peon packs install-local <path> # Install a pack from a local directory
peon packs use <name> # Switch to a specific pack (auto-installs from registry on Windows)
peon packs use --install <name> # Switch to pack, installing from registry if needed
peon packs next # Cycle to the next pack
peon packs remove <p1,p2> # Remove specific packs
peon packs bind <name> # Bind a pack to the current directory
peon packs bind --pattern <path> # Bind a pack to a directory pattern, e.g. "*/services"
peon packs unbind # Remove the current directory
peon packs bindings # List all assigned bindings
peon packs ide-bind <ide> <name> # Bind a pack to an IDE id, e.g. codex
peon packs ide-unbind <ide> # Remove an IDE binding
peon packs ide-bindings # List all IDE-based bindings
peon packs exclude add <path> # Silence sounds & notifications for a glob or directory
peon packs exclude remove <path> # Stop silencing the given path
peon packs exclude list # List silenced paths
peon create # Draft a new sound pack via your own Claude Code (interactive, or --name/--flavor/--vibe)
peon eval <name-or-path> # Open the eval gate to audition/reroll/approve a draft (or re-draft an installed pack)
peon sounds list [pack] # List sounds in a pack, marking disabled ones
peon sounds disable <category> <file> [--pack=<name>] # Mute a single sound within a pack
peon sounds enable <category> <file> [--pack=<name>] # Re-enable a previously disabled sound
peon notifications on # Enable desktop notifications
peon notifications off # Disable desktop notifications
peon notifications overlay # Use large overlay banners (default)
peon notifications standard # Use standard system notifications
peon notifications test # Send a test notification
peon notifications position [pos] # Get/set notification position (top-left, top-center, top-right, bottom-left, bottom-center, bottom-right)
peon notifications dismiss [N] # Get/set auto-dismiss time in seconds (0 = persistent)
peon notifications label [text|reset] # Get/set project label override for notifications
peon notifications template [key] [fmt] # Get/set/reset message templates (keys: stop, permission, error, idle, question)
peon preview # Play all sounds from session.start
peon preview <category> # Play all sounds from a specific category
peon preview --list # List all categories in the active pack
peon mobile ntfy <topic> # Set up phone notifications (free)
peon mobile off # Disable phone notifications
peon mobile test # Send a test notification
peon debug on # Enable debug logging
peon debug off # Disable debug logging
peon debug status # Show debug state, log directory, file count, total size
peon logs # Show last 50 lines of today's log
peon logs --last N # Show last N lines across all log files
peon logs --session ID # Filter today's log by session ID
peon logs --session ID --all # Search all log files for session ID
peon logs --clear # Delete all log files (with confirmation)
peon relay --daemon # Start audio relay (for SSH/devcontainer)
peon relay --stop # Stop background relay
Available CESP categories for peon preview: session.start, task.acknowledge, task.complete, task.error, input.required, resource.limit, user.spam. (Extended categories session.end and task.progress are defined in the CESP spec and supported by pack manifests, but not currently triggered by built-in hook events.)
Tab completion is supported — type peon packs use <TAB> to see available pack names.
Pausing mutes sounds and desktop notifications instantly. Persists across sessions until you resume. Tab titles remain active when paused.
peon setupThe fastest way to configure peon-ping is the interactive wizard:
peon setup
It walks you through every common setting in one go — press Enter at any prompt to keep the current value:
╔══════════════════════════════════════╗
║ peon-ping setup wizard ║
╚══════════════════════════════════════╝
── Volume ──
> Volume (0.0 - 1.0) (0.5):
── Sound categories ──
> Session start [on/off] (on):
> Task acknowledge [on/off] (off):
> Task complete [on/off] (on):
> Task error [on/off] (on):
> Input required (permissions, questions) [on/off] (on):
> Resource limit (context compaction) [on/off] (on):
> User spam (rapid prompts) [on/off] (on):
── Notifications ──
> Desktop notifications [on/off] (on):
Overlay theme:
1) Neon (cyberpunk)
2) Glass (translucent)
3) Sakura (cherry blossom)
4) Jarvis (iron man)
> Theme [neon]:
Notification position:
1) Top center
2) Top right
...
> Position [top-center]:
Auto-dismiss:
1) Persistent (click to dismiss)
2) 3 seconds
3) 4 seconds
...
> Dismiss time [4]:
✓ Configuration saved!
What the wizard covers:
0 = persistent, click to dismiss)When you're done, the wizard prints a summary and saves everything to ~/.claude/hooks/peon-ping/config.json. You can rerun peon setup anytime to tweak settings — it always shows your current values as defaults.
Tip: All individual
peonsubcommands (peon volume,peon notifications position top-right, etc.) still work if you prefer scripting or tweaking one setting at a time — see the Quick controls section.
peon-ping also installs slash commands in Claude Code:
/peon-ping-toggle — mute/unmute sounds/peon-ping-config — change any setting (volume, packs, categories, etc.)/peon-ping-rename <name> — give this session a custom name shown in notification titles and the terminal tab title (zero tokens, hook-intercepted); no argument resets to auto-detectYou can also just ask Claude to change settings for you — e.g. "ena