GitHub - PeonPing/peon-ping: Warcraft III Peon voice notifications (+ more!) for Claude Code, Codex, IDEs, and any AI agent. Stop babysitting your terminal. Employ a Peon today. · GitHub

Source: original

peon-ping

English | 한국어 | 中文 | 日本語

macOS WSL2 Linux Windows MSYS2 SSH License

Claude Code Amp Gemini CLI GitHub Copilot Codex Cursor OpenCode Kilo CLI Kiro Kimi Code Windsurf Antigravity OpenClaw Rovo Dev CLI DeepAgents oh-my-pi Qwen Code iFlow CLI Trae Kiro IDE ECA

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 , 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 actionpeonping.com

demo-avatar.mp4 * Install * What you'll hear * Quick controls * Configuration * Peon Trainer * MCP server * Multi-IDE support * Remote development * Mobile notifications * Sound packs * Debugging * Uninstall * Requirements * How it works * Links

Install

Option 1: Homebrew (recommended)

brew install PeonPing/tap/peon-ping

Then run peon-ping-setup to register hooks and download sound packs. macOS and Linux.

Option 2: Installer script (macOS, Linux, WSL2)

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:

sudo apt update; sudo apt install -y ffmpeg

You can override the auto-detection with PEON_WSL_AUDIO_BACKEND=auto|mediaplayer|soundplayer:

Option 3: Installer for Windows

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:

-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

Option 4: Clone and inspect first

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

Option 5: Nix (macOS, Linux)

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

Home Manager module (declarative configuration)

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:

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.

What you'll hear

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.

Quick controls

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, core packs, notifications on/off, debug, logs, trainer). More advanced commands like setup, rotation, preview, and mobile are 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 # Search registry packs by name (Windows) peon packs install # Install packs from the registry peon packs install --all # Install all packs from the registry peon packs install-local # Install a pack from a local directory peon packs use # Switch to a specific pack (auto-installs from registry on Windows) peon packs use --install # Switch to pack, installing from registry if needed peon packs next # Cycle to the next pack peon packs remove # Remove specific packs peon packs bind # Bind a pack to the current directory peon packs bind --pattern # 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 # Bind a pack to an IDE id, e.g. codex peon packs ide-unbind # Remove an IDE binding peon packs ide-bindings # List all IDE-based bindings peon packs exclude add # Silence sounds & notifications for a glob or directory peon packs exclude remove # Stop silencing the given path peon packs exclude list # List silenced paths peon sounds list [pack] # List sounds in a pack, marking disabled ones peon sounds disable [--pack=] # Mute a single sound within a pack peon sounds enable [--pack=] # 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 # Play all sounds from a specific category peon preview --list # List all categories in the active pack peon mobile ntfy # 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.

Configuration

Quickstart — peon setup

The 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:

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 peon subcommands (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.

Slash commands and manual config

peon-ping also installs slash commands in Claude Code:

You can also just ask Claude to change settings for you — e.g. "enable round-robin pack rotation", "set volume to 0.3", or "add glados to my pack rotation". No need to edit config files manually.

Config location depends on install mode:

{ "volume": 0.5, "categories": { "session.start": true, "task.acknowledge": true, "task.complete": true, "task.error": true, "input.required": true, "resource.limit": true, "user.spam": true } }

Independent Controls

peon-ping has three independent controls that can be mixed and matched:

Config Key | Controls | Affects Sounds | Affects Desktop Popups | Affects Mobile Push

enabled | Master audio switch | ✅ Yes | ❌ No | ❌ No desktop_notifications | Desktop popup banners | ❌ No | ✅ Yes | ❌ No mobile_notify.enabled | Phone push notifications | ❌ No | ❌ No | ✅ Yes

This means you can:

"path_rules": [ { "pattern": "/work/client-a/", "pack": "glados" }, { "pattern": "/personal/", "pack": "peon" } ]

"exclude_dirs": [ "~/conductor/workspaces", "~/Library/Application Support/CodexBar*" ]

"ide_rules": [ { "ide": "codex", "pack": "glados" }, { "ide": "claude", "pack": "peon" } ]

Pack Selection Hierarchy

peon-ping resolves which sound pack to use through a 6-layer hierarchy. The first layer that produces a valid, installed pack wins:

Priority | Layer | Source | How to set

1 (highest) | session_override | Per-session assignment | /peon-ping-use <pack> skill or MCP 2 | path_rules | Glob match on working directory | peon packs bind or path_rules in config 3 | ide_rules | IDE/source match | peon packs ide-bind or ide_rules in config 4 | pack_rotation | Random or round-robin from a list | pack_rotation array + pack_rotation_mode in config 5 | default_pack | Static fallback | peon packs use <name> or default_pack in config 6 (lowest) | hardcoded | Built-in default | "peon"

If a layer references a pack that is not installed, it falls through to the next layer. If exclude_dirs matches the current working directory, the entire invocation is silenced — no sound, no notification.

Per-Project Pack Assignment (path_rules)

Assign different sound packs to different projects based on directory path. Use the CLI or edit config.json directly.

CLI (recommended):

peon packs bind glados # Bind glados to the current directory peon packs bind sc_kerrigan --pattern "/services/" # Bind to a glob pattern peon packs bind duke_nukem --install # Bind and install from registry if needed peon packs unbind # Remove binding for the current directory peon packs unbind --pattern "/services/" # Remove a specific pattern binding peon packs bindings # List all bindings

Manual config:

"path_rules": [ { "pattern": "/work/client-a/", "pack": "glados" }, { "pattern": "/personal/", "pack": "peon" }, { "pattern": "/services/", "pack": "sc_kerrigan" } ]

Rules use glob matching (*, ?). First matching rule wins. Path rules override pack_rotation and default_pack but are overridden by session_override assignments.

Per-IDE Pack Assignment (ide_rules)

Use this layer when a path is noisy or shared across tools and you want a pack to follow the IDE instead.

CLI (recommended):

peon packs ide-bind codex glados # Use glados for Codex sessions peon packs ide-bind claude peon # Use peon for Claude Code peon packs ide-unbind codex # Remove one IDE rule peon packs ide-bindings # List IDE rules and recent detections

Manual config:

"ide_rules": [ { "ide": "codex", "pack": "glados" }, { "ide": "claude", "pack": "peon" } ]

ide_rules run after path_rules.

Common Use Cases

Sounds without popups

Want voice feedback but no visual distractions?

peon notifications off

This keeps all sound categories playing while suppressing desktop notification banners. Mobile notifications (if configured) continue working.

You can also use the alias:

peon popups off

Silent mode with notifications only

Want visual alerts but no audio?

peon pause # or set "enabled": false in config

With desktop_notifications: true, you'll get popups but no sounds.

Complete silence

Disable everything:

peon pause peon notifications off peon mobile off

Peon Trainer

Your peon is also your personal trainer. Built-in Pavel-style daily exercise mode — the same orc who tells you "work work" now tells you to drop and give him twenty.

Quick start

peon trainer on # enable trainer peon trainer goal 200 # set daily goal (default: 300/300)

... code for a while, peon nags you every ~20 min ...

peon trainer log 25 pushups # log what you did peon trainer log 30 squats peon trainer status # check progress

How it works

Trainer reminders piggyback on your coding session. When you start a new session, the peon immediately encourages you to start strong with pushups before you write any code. Then every ~20 minutes of active coding, you'll hear the peon yelling at you to do more reps. No background daemon needed. Log your reps with peon trainer log, and progress resets automatically at midnight.

Commands

Command | Description

peon trainer on | Enable trainer mode peon trainer off | Disable trainer mode peon trainer status | Show today's progress peon trainer log <n> <exercise> | Log reps (e.g. log 25 pushups) peon trainer goal <n> | Set uniform daily goal for all exercises peon trainer goal <exercise> <n> | Set uniform daily goal for one exercise peon trainer goal <exercise> <day> <n> | Set goal for specific day (mon, tue, etc.) peon trainer goal <day> <n> | Set all exercises for a specific day

Schedule vs uniform goals

Exercises can have either a uniform daily goal (same every day) or a per-day schedule (different goals on different days). These are mutually exclusive:

Days use short names: mon, tue, wed, thu, fri, sat, sun

peon trainer goal pushups 300 # 300 pushups every day (uniform) peon trainer goal pushups mon 400 # Override: 400 on Monday (creates schedule) peon trainer goal squats sun 0 # Rest day for squats on Sunday peon trainer goal fri 150 # Light day for all exercises on Friday

On rest days (goal=0), reminders are skipped and status shows [REST DAY]. You can still log reps on rest days if you want.

Claude Code skill

In Claude Code, you can log reps without leaving your conversation:

/peon-ping-log 25 pushups /peon-ping-log 30 squats

Custom voice lines

Drop your own audio files into ~/.claude/hooks/peon-ping/trainer/sounds/:

trainer/sounds/session_start/ # session greeting ("Pushups first, code second! Zug zug!") trainer/sounds/remind/ # reminder lines ("Something need doing? YES. PUSHUPS.") trainer/sounds/log/ # acknowledgment ("Work work! Muscles getting bigger maybe!") trainer/sounds/complete/ # celebration ("Zug zug! Human finish all reps!") trainer/sounds/slacking/ # disappointment ("Peon very disappointed.")

Update trainer/manifest.json to register your sound files.

MCP server

peon-ping includes an MCP (Model Context Protocol) server so any MCP-compatible AI agent can play sounds directly via tool calls — no hooks required.

The key difference: the agent chooses the sound. Instead of automatically playing a fixed sound on every event, the agent calls play_sound with exactly what it wants — duke_nukem/SonOfABitch when a build fails, sc_kerrigan/IReadYou when reading files.

Setup

Add to your MCP client config (Claude Desktop, Cursor, etc.):

{ "mcpServers": { "peon-ping": { "command": "node", "args": ["/path/to/peon-ping/mcp/peon-mcp.js"] } } }

If installed via Homebrew: $(brew --prefix peon-ping)/libexec/mcp/peon-mcp.js. See mcp/README.md for full setup instructions.

What the agent can do

Feature | Description

play_sound | Play one or more sounds by key (e.g. duke_nukem/SonOfABitch, peon/PeonReady1) peon-ping://catalog | Full pack catalog as an MCP Resource — client prefetches once, no repeated tool calls peon-ping://pack/{name} | Individual pack details and available sound keys

Requires Node.js 18+. Contributed by @tag-assistant.

Multi-IDE Support

peon-ping works with any agentic IDE that supports hooks. Adapters translate IDE-specific events to the CESP standard.

IDE | Status | Setup

Claude Code | Built-in | curl | bash install handles everything Amp | Adapter | bash adapters/amp.sh / powershell adapters/amp.ps1 (setup) Gemini CLI | Adapter | Add hooks pointing to adapters/gemini.sh (or .ps1 on Windows) (setup) GitHub Copilot CLI | Built-in (auto-detect) | install.sh / install.ps1 auto-registers hooks at ~/.copilot/hooks/peon-ping.json if ~/.copilot exists. Per-repo manual wiring also available via adapters/copilot.sh / .ps1 (setup) OpenAI Codex | Adapter | Install the peon-ping runtime first, then add notify in ~/.codex/config.toml pointing to adapters/codex.sh (or .ps1) (setup) Cursor | Built-in | curl | bash, peon-ping-setup, or Windows install.ps1 auto-detect and register hooks. On Windows, enable Settings → Features → Third-party skills so Cursor loads ~/.claude/settings.json for SessionStart/Stop sounds. OpenCode | Adapter | bash adapters/opencode.sh / powershell adapters/opencode.ps1 (setup) Kilo CLI | Adapter | bash adapters/kilo.sh / powershell adapters/kilo.ps1 (setup) Kiro | Adapter | Add hook entries pointing to adapters/kiro.sh (or .ps1) (setup) Windsurf | Adapter | Add hook entries pointing to adapters/windsurf.sh (or .ps1) (setup) Google Antigravity | Adapter | bash adapters/antigravity.sh / powershell adapters/antigravity.ps1. For headless / macOS LaunchAgent use, also see bash adapters/antigravity-py.sh --install (Python watchdog watcher with 25s idle threshold; requires pip3 install watchdog). Kimi Code | Adapter | bash adapters/kimi.sh --install / powershell adapters/kimi.ps1 -Install (setup) OpenClaw | Adapter | Call adapters/openclaw.sh <event> (or openclaw.ps1) from your OpenClaw skill Rovo Dev CLI | Adapter | Auto-registered by install.sh if ~/.rovodev exists, or add hooks to ~/.rovodev/config.yml manually (setup) DeepAgents | Adapter | bash adapters/deepagents.sh / powershell adapters/deepagents.ps1 (setup) oh-my-pi (omp) | Adapter | bash adapters/omp.sh (setup) Qwen Code | Adapter | Add hooks pointing to adapters/qwen.sh (or .ps1 on Windows) (setup) iFlow CLI | Adapter | Add hooks pointing to adapters/iflow.sh (or .ps1) (setup) Trae | Adapter | Filesystem watcher: bash adapters/trae.sh & / powershell adapters/trae.ps1 -Install (setup) Kiro IDE | Adapter | Agent hooks in .kiro/hooks/*.kiro.hook calling adapters/kiro-ide.sh (or .ps1) (setup) ECA | Adapter | Add a shell hook pointing to adapters/eca.sh (or .ps1) (setup)

Windows: All adapters have native PowerShell (.ps1) versions. The Windows installer (install.ps1) copies them to ~/.claude/hooks/peon-ping/adapters/. Filesystem watchers (Amp, Antigravity, Kimi, Trae) use .NET FileSystemWatcher instead of fswatch/inotifywait — no extra dependencies needed.

OpenAI Codex setup

Codex support uses an adapter and is not auto-registered by peon-ping-setup.

The Codex adapter expects the peon-ping runtime to exist at ~/.claude/hooks/peon-ping/, even if you only use Codex and do not use Claude Code.

Setup:

  1. Install the peon-ping runtime first:

bash "$(brew --prefix peon-ping)"/libexec/install.sh --no-rc

Or with the standard installer:

curl -fsSL https://raw.githubusercontent.com/PeonPing/peon-ping/main/install.sh | bash -s -- --no-rc

  1. Add this to ~/.codex/config.toml:

notify = ["bash", "~/.claude/hooks/peon-ping/adapters/codex.sh"]

  1. Restart Codex.

If you installed with Homebrew, the runtime files are managed under ~/.claude/hooks/peon-ping/, and the Codex adapter forwards Codex notify events into that shared runtime.

Amp setup

A filesystem watcher adapter for Amp (by Sourcegraph). Amp doesn't expose event hooks like Claude Code, so this adapter watches Amp's thread files on disk and detects when the agent finishes a turn.

Setup:

  1. Ensure peon-ping is installed (curl -fsSL https://peonping.com/install | bash)

  2. Install fswatch (macOS) or inotify-tools (Linux):

brew install fswatch # macOS sudo apt install inotify-tools # Linux

  1. Start the watcher:

bash ~/.claude/hooks/peon-ping/adapters/amp.sh # foreground bash ~/.claude/hooks/peon-ping/adapters/amp.sh & # background

Event mapping:

How it works:

The adapter watches ~/.local/share/amp/threads/ for JSON file changes. When a thread file stops updating (1s idle timeout) and the last message is from the assistant with text content (not a pending tool call), it emits a Stop event — meaning the agent is done and waiting for your input.

Environment variables:

Variable | Default | Description

AMP_DATA_DIR | ~/.local/share/amp | Amp data directory AMP_THREADS_DIR | $AMP_DATA_DIR/threads | Threads directory to watch AMP_IDLE_SECONDS | 1 | Seconds of no changes before emitting Stop AMP_STOP_COOLDOWN | 10 | Minimum seconds between Stop events per thread

GitHub Copilot CLI setup

Native GitHub Copilot CLI integration with full CESP v1.0 conformance.

Recommended: user-level (global) wiring — no per-repo setup.

install.sh and install.ps1 automatically register Copilot CLI hooks at ~/.copilot/hooks/peon-ping.json whenever the ~/.copilot/ directory exists. Re-run the installer if you installed Copilot CLI after peon-ping. The wiring uses PascalCase event names , which tells Copilot CLI to deliver the VS Code-compatible (snake_case) payload that peon.sh / peon.ps1 reads natively — no per-repo adapter required.

Hooks registered globally:

Event | Category | Triggered by

SessionStart | session.start | Launching copilot (greeting) SessionEnd | (silent today) | Quitting the CLI UserPromptSubmit | user.spam (after 3+ rapid prompts) | Each prompt you submit Stop (= agentStop) | task.complete | Agent finishes a turn (debounced 5s) Notification | input.required (elicitation) | Idle, elicitation dialogs, permission popups PermissionRequest | input.required | Tool permission asks PreToolUse | input.required (only on dangerous-pattern match) | Before each tool call PostToolUseFailure | task.error | Tool failure PreCompact | resource.limit | Context compaction starting

postToolUse is intentionally not wired: peon has no PostToolUse handler and routing it through Stop floods the debounce window, swallowing real Stop events.

Prerequisite (Windows): Copilot CLI hooks require PowerShell 7+ (pwsh on PATH) and a permissive execution policy:

winget install Microsoft.PowerShell powershell -NoProfile -Command "Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned -Force"

Alternative: per-repository wiring with the adapter.

If you want Copilot CLI hooks committed to a specific repository (e.g. for a team workflow), use .github/hooks/hooks.json with the adapters/copilot.sh (or .ps1 on Windows) translator:

{ "version": 1, "hooks": { "sessionStart": [ { "type": "command", "bash": "bash ~/.claude/hooks/peon-ping/adapters/copilot.sh sessionStart", "powershell": "powershell -NoProfile -File %USERPROFILE%\.claude\hooks\peon-ping\adapters\copilot.ps1 sessionStart" } ], "agentStop": [ { "type": "command", "bash": "bash ~/.claude/hooks/peon-ping/adapters/copilot.sh agentStop", "powershell": "powershell -NoProfile -File %USERPROFILE%\.claude\hooks\peon-ping\adapters\copilot.ps1 agentStop" } ], "postToolUseFailure": [ { "type": "command", "bash": "bash ~/.claude/hooks/peon-ping/adapters/copilot.sh postToolUseFailure", "powershell": "powershell -NoProfile -File %USERPROFILE%\.claude\hooks\peon-ping\adapters\copilot.ps1 postToolUseFailure" } ], "notification": [ { "type": "command", "bash": "bash ~/.claude/hooks/peon-ping/adapters/copilot.sh notification", "powershell": "powershell -NoProfile -File %USERPROFILE%\.claude\hooks\peon-ping\adapters\copilot.ps1 notification" } ] } }

Add additional events from the table above as desired. The adapter translates Copilot CLI's camelCase payload (sessionId, toolName, stopReason, etc.) to the snake_case shape (session_id, tool_name, stop_reason) that peon.sh / peon.ps1 reads.

Features:

OpenCode setup

A native TypeScript plugin for OpenCode with full CESP v1.0 conformance.

Quick install:

curl -fsSL https://raw.githubusercontent.com/PeonPing/peon-ping/main/adapters/opencode.sh | bash

The installer copies peon-ping.ts to ~/.config/opencode/plugins/ and creates a config at ~/.config/opencode/peon-ping/config.json. Packs are stored at the shared CESP path (~/.openpeon/packs/).

Features:

🖼️ Screenshot: desktop notifications with custom peon icon

peon-ping OpenCode notifications

Tip: Install terminal-notifier (brew install terminal-notifier) for richer notifications with subtitle and grouping support.

🎨 Optional: custom peon icon for notifications

By default, terminal-notifier shows a generic Terminal icon. The included script replaces it with the peon icon using built-in macOS tools (sips + iconutil) — no extra dependencies.

bash <(curl -fsSL https://raw.githubusercontent.com/PeonPing/peon-ping/main/adapters/opencode/setup-icon.sh)

Or if installed locally (Homebrew / git clone):

bash ~/.claude/hooks/peon-ping/adapters/opencode/setup-icon.sh

The script auto-finds the peon icon (Homebrew libexec, OpenCode config, or Claude hooks dir), generates a proper .icns, backs up the original Terminal.icns, and replaces it. Re-run after brew upgrade terminal-notifier.

Future: When jamf/Notifier ships to Homebrew (#32), the plugin will migrate to it — Notifier has built-in --rebrand support, no icon hacks needed.

Kilo CLI setup

A native TypeScript plugin for Kilo CLI with full CESP v1.0 conformance. Kilo CLI is a fork of OpenCode and uses the same plugin system — this installer downloads the OpenCode plugin and patches it for Kilo.

Quick install:

curl -fsSL https://raw.githubusercontent.com/PeonPing/peon-ping/main/adapters/kilo.sh | bash

The installer copies peon-ping.ts to ~/.config/kilo/plugins/ and creates a config at ~/.config/kilo/peon-ping/config.json. Packs are stored at the shared CESP path (~/.openpeon/packs/).

Features: Same as the OpenCode adapter — sound playback, CESP event mapping, desktop notifications, terminal focus detection, tab titles, pack switching, no-repeat logic, and spam detection.

Gemini CLI setup

A shell adapter for Gemini CLI with full CESP v1.0 conformance.

Setup:

  1. Ensure peon-ping is installed (curl -fsSL https://peonping.com/install | bash)

  2. Add the following hooks to your ~/.gemini/settings.json:

{ "hooks": { "SessionStart": [ { "matcher": "startup", "hooks": [ { "name": "peon-start", "type": "command", "command": "bash ~/.claude/hooks/peon-ping/adapters/gemini.sh SessionStart" } ] } ], "AfterAgent": [ { "matcher": "", "hooks": [ { "name": "peon-after-agent", "type": "command", "command": "bash ~/.claude/hooks/peon-ping/adapters/gemini.sh AfterAgent" } ] } ], "AfterTool": [ { "matcher": "", "hooks": [ { "name": "peon-after-tool", "type": "command", "command": "bash ~/.claude/hooks/peon-ping/adapters/gemini.sh AfterTool" } ] } ], "Notification": [ { "matcher": "*", "hooks": [ { "name": "peon-notification", "type": "command", "command": "bash ~/.claude/hooks/peon-ping/adapters/gemini.sh Notification" } ] } ] } }

Event mapping:

Windsurf setup

Add to ~/.codeium/windsurf/hooks.json (user-level) or .windsurf/hooks.json (workspace-level):

{ "hooks": { "post_cascade_response": [ { "command": "bash ~/.claude/hooks/peon-ping/adapters/windsurf.sh post_cascade_response", "show_output": false } ], "pre_user_prompt": [ { "command": "bash ~/.claude/hooks/peon-ping/adapters/windsurf.sh pre_user_prompt", "show_output": false } ], "post_write_code": [ { "command": "bash ~/.claude/hooks/peon-ping/adapters/windsurf.sh post_write_code", "show_output": false } ], "post_run_command": [ { "command": "bash ~/.claude/hooks/peon-ping/adapters/windsurf.sh post_run_command", "show_output": false } ] } }

Kiro setup

Create ~/.kiro/agents/peon-ping.json:

{ "name": "peon-ping", "hooks": { "agentSpawn": [ { "command": "bash ~/.claude/hooks/peon-ping/adapters/kiro.sh" } ], "userPromptSubmit": [ { "command": "bash ~/.claude/hooks/peon-ping/adapters/kiro.sh" } ], "stop": [ { "command": "bash ~/.claude/hooks/peon-ping/adapters/kiro.sh" } ] } }

preToolUse/postToolUse are intentionally excluded — they fire on every tool call and would be extremely noisy.

Rovo Dev CLI setup

A shell adapter for Rovo Dev CLI (Atlassian) with full CESP v1.0 conformance.

Auto-setup:

If ~/.rovodev/config.yml exists when you run install.sh or peon-ping-setup, event hooks are registered automatically.

Manual setup:

  1. Ensure peon-ping is installed (curl -fsSL https://peonping.com/install | bash)

  2. Add to ~/.rovodev/config.yml:

eventHooks: events: - name: on_complete commands: - command: bash ~/.claude/hooks/peon-ping/adapters/rovodev.sh on_complete - name: on_error commands: - command: bash ~/.claude/hooks/peon-ping/adapters/rovodev.sh on_error - name: on_tool_permission commands: - command: bash ~/.claude/hooks/peon-ping/adapters/rovodev.sh on_tool_permission

  1. Restart Rovo Dev CLI for the hooks to take effect.

Event mapping:

Features:

Kimi Code setup

A filesystem watcher adapter for Kimi Code CLI (MoonshotAI). Kimi Code writes Wire Mode events to ~/.kimi/sessions/ — this adapter watches those files as a background daemon and translates events to CESP format.

Install (starts background daemon)

bash ~/.claude/hooks/peon-ping/adapters/kimi.sh --install

Check status / stop

bash ~/.claude/hooks/peon-ping/adapters/kimi.sh --status bash ~/.claude/hooks/peon-ping/adapters/kimi.sh --uninstall

Requires fswatch (brew install fswatch) on macOS or inotifywait (apt install inotify-tools) on Linux. The curl | bash installer auto-detects Kimi Code and starts the daemon.

On macOS,--install registers a LaunchAgent at ~/Library/LaunchAgents/com.peonping.kimi-adapter.plist so the watcher auto-starts on login and auto-restarts on crash — survives reboots without re-running --install. Set KIMI_NO_LAUNCHD=1 to fall back to nohup+pidfile (e.g. for tests). Linux always uses nohup+pidfile.

Kimi-only install (no Claude required):

If you don't have Claude Code and just want peon-ping for Kimi, install with --kimi:

curl -fsSL peonping.com/install | bash -s -- --kimi

Files land in ~/.kimi/hooks/peon-ping/ instead of ~/.claude/hooks/peon-ping/, and no ~/.claude/ directory is created. The installer also auto-detects this layout: running it with no flags on a machine that has ~/.kimi/ but no ~/.claude/ selects --kimi mode automatically. The watcher daemon starts during install and re-starts on every login via the LaunchAgent.

Sharing voice packs with a Claude install:

If ~/.claude/hooks/peon-ping/packs/ already exists with packs, a --kimi install symlinks ~/.kimi/hooks/peon-ping/packs at it instead of re-downloading. One pack download serves both IDEs, and peon packs install <name> from either side updates the shared set. State, config, and mute toggles stay isolated per install. Pass --no-shared-packs (or --packs= / --all) to download a separate copy.

Event mapping:

oh-my-pi (omp) setup

A native TypeScript extension for oh-my-pi (omp) with full CESP v1.0 conformance. Subscribes to omp's ExtensionAPI lifecycle events and routes them through peon.sh so omp users get every peon-ping feature: sound packs, desktop notifications, trainer reminders, mobile push, SSH/devcontainer relay, and tab-title updates.

Quick install:

curl -fsSL https://raw.githubusercontent.com/PeonPing/peon-ping/main/adapters/omp.sh | bash

The installer copies peon-ping.ts and package.json to ~/.omp/agent/extensions/peon-ping/. Restart omp afterward.

Event mapping:

omp event | peon-ping event

session_start | SessionStart turn_start | UserPromptSubmit turn_end | Stop tool_result with isError: true | PostToolUseFailure auto_compaction_start | PreCompact session_shutdown | SessionEnd

Uninstall:

curl -fsSL https://raw.githubusercontent.com/PeonPing/peon-ping/main/adapters/omp.sh | bash -s -- --uninstall

Qwen Code setup

A thin passthrough adapter for Qwen Code (Alibaba). Qwen Code ships a Claude-Code-style hook system — events are piped as JSON on stdin using the same PascalCase CESP names peon-ping expects — so this adapter simply re-tags the session id with a qwen- prefix and drops the noisy per-tool-call events.

Add to ~/.qwen/settings.json:

{ "hooks": { "SessionStart": [{ "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/peon-ping/adapters/qwen.sh" }] }], "UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/peon-ping/adapters/qwen.sh" }] }], "Stop": [{ "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/peon-ping/adapters/qwen.sh" }] }], "Notification": [{ "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/peon-ping/adapters/qwen.sh" }] }], "PostToolUseFailure": [{ "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/peon-ping/adapters/qwen.sh" }] }], "SessionEnd": [{ "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/peon-ping/adapters/qwen.sh" }] }] } }

On Windows, point the command at qwen.ps1 via powershell -NoProfile -File %USERPROFILE%\.claude\hooks\peon-ping\adapters\qwen.ps1.

iFlow CLI setup

A passthrough adapter for iFlow CLI (iflow-ai). iFlow ships a Claude-Code-style hook system (PascalCase events on stdin); this adapter forwards the meaningful lifecycle events with an iflow- session prefix and maps a failed PostToolUse to PostToolUseFailure.

Add to ~/.iflow/settings.json (or per-project ./.iflow/settings.json):

{ "hooks": { "SessionStart": [{ "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/peon-ping/adapters/iflow.sh" }] }], "UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/peon-ping/adapters/iflow.sh" }] }], "Stop": [{ "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/peon-ping/adapters/iflow.sh" }] }], "Notification": [{ "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/peon-ping/adapters/iflow.sh" }] }], "PostToolUse": [{ "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/peon-ping/adapters/iflow.sh" }] }], "SessionEnd": [{ "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/peon-ping/adapters/iflow.sh" }] }] } }

Trae setup

A filesystem-watcher adapter for Trae (ByteDance). Trae is a VS Code-derived AI IDE with no synchronous shell-hook API, so peon-ping uses the same watcher approach as Amp and Antigravity: a new session file means SessionStart, and an idle timer (the session file stops updating) means Stop.

Foreground

bash ~/.claude/hooks/peon-ping/adapters/trae.sh

Background

bash ~/.claude/hooks/peon-ping/adapters/trae.sh &

On Windows: powershell -File adapters\trae.ps1 -Install registers a background watcher (-Status / -Uninstall to manage it).

Trae's on-disk session layout varies by platform/version and isn't publicly documented, so the watched paths are environment-overridable:

Variable | Default

TRAE_DATA_DIR | ~/.trae TRAE_SESSIONS_DIR | $TRAE_DATA_DIR/sessions TRAE_SESSION_GLOB | *.json

Requires fswatch (macOS: brew install fswatch) or inotifywait (Linux: apt install inotify-tools). Windows uses .NET FileSystemWatcher — no extra dependency.

Kiro IDE setup

A hook adapter for Kiro IDE (Amazon) — distinct from the Kiro CLI (adapters/kiro.sh). The IDE's Agent Hooks are .kiro/hooks/*.kiro.hook JSON files; their then.type: runCommand action runs a shell command with no stdin JSON , passing the triggering event name to the adapter as an argv argument.

Create one hook file per event, e.g. .kiro/hooks/peon-ping-stop.kiro.hook:

{ "version": "1.0.0", "enabled": true, "name": "peon-ping-stop", "when": { "type": "agentStop" }, "then": { "type": "runCommand", "command": "bash ~/.claude/hooks/peon-ping/adapters/kiro-ide.sh agentStop" } }

Repeat with when.type = promptSubmit (→ UserPromptSubmit), preToolUse (→ permission prompt), or sessionStart (→ SessionStart), each passing the matching event name as the command argument. postToolUse and file/user-triggered hooks carry no peon-relevant signal and are ignored. On Windows, point the command at kiro-ide.ps1 via powershell -NoProfile -File.

ECA setup

A shell-hook adapter for ECA (Editor Code Assistant), an editor-agnostic LLM-agent integration. ECA pipes JSON on stdin (snake_case top-level keys) and also passes the hook type as an argv argument; this adapter maps ECA's events to CESP with a stable eca- session prefix derived from the ECA db_cache_path. Originally contributed in #261, vendored first-party here.

Add a shell hook to your ECA config pointing at the adapter, one per event:

{ "hooks": { "sessionStart": [{ "actions": [{ "type": "shell", "command": "bash ~/.claude/hooks/peon-ping/adapters/eca.sh sessionStart" }] }], "preRequest": [{ "actions": [{ "type": "shell", "command": "bash ~/.claude/hooks/peon-ping/adapters/eca.sh preRequest" }] }], "postRequest": [{ "actions": [{ "type": "shell", "command": "bash ~/.claude/hooks/peon-ping/adapters/eca.sh postRequest" }] }], "preToolCall": [{ "actions": [{ "type": "shell", "command": "bash ~/.claude/hooks/peon-ping/adapters/eca.sh preToolCall" }] }], "sessionEnd": [{ "actions": [{ "type": "shell", "command": "bash ~/.claude/hooks/peon-ping/adapters/eca.sh sessionEnd" }] }] } }

Event mapping: sessionStart/chatStartSessionStart, preRequestUserPromptSubmit, postRequest/subagentPostRequest/postToolCallStop, preToolCall → permission prompt, sessionEndSessionEnd.

Remote development (SSH / Devcontainers / Codespaces)

Coding on a remote server or inside a container? peon-ping auto-detects SSH sessions, devcontainers, and Codespaces, then routes audio and notifications through a lightweight relay running on your local machine.

SSH setup

  1. On your local machine , start the relay:

peon relay --daemon

  1. SSH with port forwarding :

ssh -R 19998:localhost:19998 your-server

  1. Install peon-ping on the remote — it auto-detects the SSH session and sends audio requests back through the forwarded port to your local relay.

That's it. Sounds play on your laptop, not the remote server.

Optional SSH routing modes:

peon ssh-audio relay # default, always use relay peon ssh-audio auto # try relay, fall back to local playback on SSH host peon ssh-audio local # always play on SSH host

Devcontainers / Codespaces

No port forwarding needed — peon-ping auto-detects REMOTE_CONTAINERS and CODESPACES environment variables and routes audio to host.docker.internal:19998. Just run peon relay --daemon on your host machine.

Relay commands

peon relay # Start relay in foreground peon relay --daemon # Start in background peon relay --stop # Stop background relay peon relay --status # Check if relay is running peon relay --port=12345 # Custom port (default: 19998) peon relay --bind=0.0.0.0 # Listen on all interfaces (less secure)

Environment variables: PEON_RELAY_PORT, PEON_RELAY_HOST, PEON_RELAY_BIND.

If peon-ping detects an SSH or container session but can't reach the relay, it prints setup instructions on SessionStart.

Category-based API (for lightweight remote hooks)

The relay supports a category-based endpoint that handles sound selection server-side. This is useful for remote machines where peon-ping isn't installed — the remote hook only needs to send a category name, and the relay picks a random sound from the active pack.

Endpoints:

Endpoint | Description

GET /health | Health check (returns "OK") GET /play?file=<path> | Play a specific sound file (legacy) GET /play?category=<cat> | Play random sound from category (recommended) POST /notify | Send desktop notification

Example remote hook (scripts/remote-hook.sh):

!/bin/bash

RELAY_URL="${PEON_RELAY_URL:-http://127.0.0.1:19998}" EVENT=$(cat | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('hook_event_name',''))" 2>/dev/null) case "$EVENT" in SessionStart) CATEGORY="session.start" ;; Stop) CATEGORY="task.complete" ;; PermissionRequest) CATEGORY="input.required" ;; *) exit 0 ;; esac curl -sf "${RELAY_URL}/play?category=${CATEGORY}" >/dev/null 2>&1 &

Copy this to your remote machine and register it in ~/.claude/settings.json:

{ "hooks": { "SessionStart": [{"command": "bash /path/to/remote-hook.sh"}], "Stop": [{"command": "bash /path/to/remote-hook.sh"}], "PermissionRequest": [{"command": "bash /path/to/remote-hook.sh"}] } }

The relay reads config.json on your local machine to get the active pack and volume, loads the pack's manifest, and picks a random sound while avoiding repeats.

Mobile notifications

Get push notifications on your phone when tasks finish or need attention — useful when you're away from your desk.

Quick start (ntfy.sh — free, no account needed)

  1. Install the ntfy app on your phone
  2. Subscribe to a unique topic in the app (e.g. my-peon-notifications)
  3. Run:

peon mobile ntfy my-peon-notifications

Also supports Pushover and Telegram:

peon mobile pushover peon mobile telegram

Mobile commands

peon mobile on # Enable mobile notifications peon mobile off # Disable mobile notifications peon mobile status # Show current config peon mobile test # Send a test notification

Mobile notifications fire on every event regardless of window focus — they're independent from desktop notifications and sounds.

Sound packs

165 packs across Warcraft, StarCraft, Red Alert, Portal, Zelda, Dota 2, Helldivers 2, Elder Scrolls, and more. The default install includes a curated starter set; commonly used packs include:

Pack | Character | Sounds

peon (default) | Orc Peon (Warcraft III) | "Ready to work?", "Work, work.", "Okie dokie." peasant | Human Peasant (Warcraft III) | "Yes, milord?", "Job's done!", "Ready, sir." sc_kerrigan | Sarah Kerrigan (StarCraft) | "I gotcha", "What now?", "Easily amused, huh?" sc_battlecruiser | Battlecruiser (StarCraft) | "Battlecruiser operational", "Make it happen", "Engage" glados | GLaDOS (Portal) | "Oh, it's you.", "You monster.", "Your entire team is dead."

Browse all packs with audio previews → openpeon.com/packs

Install all with --all, or switch packs anytime:

peon packs use glados # switch to a specific pack peon packs use --install glados # install (or update) and switch in one step peon packs next # cycle to the next pack peon packs list # list all installed packs peon packs list --registry # browse all available packs peon packs install glados,murloc # install specific packs peon packs install --all # install every pack in the registry

Want to add your own pack? See the full guide at openpeon.com/create or CONTRIBUTING.md.

Debugging

When sounds aren't playing or notifications aren't appearing, structured debug logging helps you trace exactly what happened during a hook invocation.

Enabling debug logs

peon debug on # Enable — logs written to ~/.claude/hooks/peon-ping/logs/ peon debug off # Disable peon debug status # Show state, log directory, file count, total size

You can also enable debug logging per-invocation without changing config by setting the environment variable PEON_DEBUG=1.

Reading logs

peon logs # Last 50 lines of today's log peon logs --last 100 # Last 100 lines across all log files peon logs --session # Filter today's log by session ID peon logs --session --all # Search all log files for session ID peon logs --clear # Delete all log files (with confirmation)

Log format

Each log line is a structured key=value record:

2026-03-26T14:32:01.042 [config] inv=a3f1 loaded=/path/to/config.json volume=0.5 pack=peon enabled=True 2026-03-26T14:32:01.045 [event] inv=a3f1 hook_event=Stop cesp=task.complete session=abc123 2026-03-26T14:32:01.048 [sound] inv=a3f1 file=work-work.wav label="Work, work." category=task.complete 2026-03-26T14:32:01.120 [play] inv=a3f1 player=afplay file=work-work.wav 2026-03-26T14:32:01.125 [notify] inv=a3f1 title="peon: done" body="Work, work."

Common failure examples

Symptom | What to look for in logs

No sound plays | [event] line shows exit=early (category disabled, paused, or debounced) Wrong pack | [config] line shows unexpected pack= value -- check path_rules or rotation Missing sound file | [sound] line shows error= with file path Notification missing | [notify] line absent -- check desktop_notifications in config

Config keys

Key | Default | Description

debug | false | Enable structured debug logging debug_retention_days | 7 | Auto-prune logs older than N days

Logs are stored at ~/.claude/hooks/peon-ping/logs/peon-ping-YYYY-MM-DD.log (one file per day). Old logs are automatically pruned based on debug_retention_days when a new day's log is created.

Uninstall

macOS/Linux:

bash "${CLAUDE_CONFIG_DIR:-$HOME/.claude}"/hooks/peon-ping/uninstall.sh # global bash .claude/hooks/peon-ping/uninstall.sh # project-local

Windows (PowerShell):

Standard uninstall (prompts before deleting sounds)

powershell -File "$env:USERPROFILE.claude\hooks\peon-ping\uninstall.ps1"

Keep sound packs (removes everything else)

powershell -File "$env:USERPROFILE.claude\hooks\peon-ping\uninstall.ps1" -KeepSounds

Requirements

How it works

peon.sh is a Claude Code hook registered for SessionStart, SessionEnd, SubagentStart, Stop, Notification, PermissionRequest, PostToolUseFailure, and PreCompact events. On each event:

  1. Event mapping — an embedded Python block maps the hook event to a CESP sound category (session.start, task.complete, input.required, etc.)
  2. Sound selection — picks a random voice line from the active pack's manifest, avoiding repeats
  3. Audio playback — plays the sound asynchronously via afplay (macOS), PowerShell MediaPlayer (WSL2/MSYS2 fallback), or pw-play/paplay/ffplay/mpv/aplay (Linux/MSYS2)
  4. Notifications — updates the Terminal tab title and sends a desktop notification if the terminal isn't focused
  5. Remote routing — in SSH sessions, devcontainers, and Codespaces, audio and notification requests are forwarded over HTTP to a relay server on your local machine

Sound packs are downloaded from the OpenPeon registry at install time. The official packs are hosted in PeonPing/og-packs. Sound files are property of their respective publishers (Blizzard, Valve, EA, etc.) and are distributed under fair use for personal notification purposes.

Links

Support the project