{"id":45519005,"url":"https://github.com/PeonPing/peon-ping","last_synced_at":"2026-02-24T12:00:40.113Z","repository":{"id":337522407,"uuid":"1154009261","full_name":"PeonPing/peon-ping","owner":"PeonPing","description":"Warcraft III Peon voice notifications (+ more!) for Claude Code, Codex, IDEs, and any AI agent. Stop babysitting your terminal. Employ a Peon today.","archived":false,"fork":false,"pushed_at":"2026-02-18T16:27:31.000Z","size":94058,"stargazers_count":2423,"open_issues_count":11,"forks_count":166,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-02-18T16:33:50.813Z","etag":null,"topics":["ai","ai-engineering","antigravity","claude-code","codex","cursor","opencode","terminal"],"latest_commit_sha":null,"homepage":"https://www.peonping.com","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PeonPing.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-09T23:12:48.000Z","updated_at":"2026-02-18T16:33:44.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/PeonPing/peon-ping","commit_stats":null,"previous_names":["tonyyont/peon-ping","peonping/peon-ping"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/PeonPing/peon-ping","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PeonPing%2Fpeon-ping","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PeonPing%2Fpeon-ping/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PeonPing%2Fpeon-ping/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PeonPing%2Fpeon-ping/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PeonPing","download_url":"https://codeload.github.com/PeonPing/peon-ping/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PeonPing%2Fpeon-ping/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29781196,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-24T10:45:18.109Z","status":"ssl_error","status_checked_at":"2026-02-24T10:45:09.911Z","response_time":75,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","ai-engineering","antigravity","claude-code","codex","cursor","opencode","terminal"],"created_at":"2026-02-22T22:00:19.899Z","updated_at":"2026-02-24T12:00:40.106Z","avatar_url":"https://github.com/PeonPing.png","language":"Shell","readme":"# peon-ping\n\u003cdiv align=\"center\"\u003e\n\n**English** | [한국어](README_ko.md) | [中文](README_zh.md)\n\n![macOS](https://img.shields.io/badge/macOS-blue) ![WSL2](https://img.shields.io/badge/WSL2-blue) ![Linux](https://img.shields.io/badge/Linux-blue) ![Windows](https://img.shields.io/badge/Windows-blue) ![MSYS2](https://img.shields.io/badge/MSYS2-blue) ![SSH](https://img.shields.io/badge/SSH-blue)\n![License](https://img.shields.io/badge/license-MIT-green)\n\n![Claude Code](https://img.shields.io/badge/Claude_Code-hook-ffab01) ![Amp](https://img.shields.io/badge/Amp-adapter-ffab01) ![Gemini CLI](https://img.shields.io/badge/Gemini_CLI-adapter-ffab01) ![GitHub Copilot](https://img.shields.io/badge/GitHub_Copilot-adapter-ffab01) ![Codex](https://img.shields.io/badge/Codex-adapter-ffab01) ![Cursor](https://img.shields.io/badge/Cursor-adapter-ffab01) ![OpenCode](https://img.shields.io/badge/OpenCode-adapter-ffab01) ![Kilo CLI](https://img.shields.io/badge/Kilo_CLI-adapter-ffab01) ![Kiro](https://img.shields.io/badge/Kiro-adapter-ffab01) ![Windsurf](https://img.shields.io/badge/Windsurf-adapter-ffab01) ![Antigravity](https://img.shields.io/badge/Antigravity-adapter-ffab01) ![OpenClaw](https://img.shields.io/badge/OpenClaw-adapter-ffab01)\n\n**Game character voice lines + visual overlay notifications when your AI coding agent needs attention — or let the agent pick its own sound via MCP.**\n\nAI 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**, **Windsurf**, **Google Antigravity**, and any MCP client.\n\n**See it in action** \u0026rarr; [peonping.com](https://peonping.com/)\n\n\u003cvideo src=\"docs/public/demo-avatar.mp4\" autoplay loop muted playsinline width=\"400\"\u003e\u003c/video\u003e\n\n\u003c/div\u003e\n\n---\n\n- [Install](#install)\n- [What you'll hear](#what-youll-hear)\n- [Quick controls](#quick-controls)\n- [Configuration](#configuration)\n- [Peon Trainer](#peon-trainer)\n- [MCP server](#mcp-server)\n- [Multi-IDE support](#multi-ide-support)\n- [Remote development](#remote-development-ssh--devcontainers--codespaces)\n- [Mobile notifications](#mobile-notifications)\n- [Sound packs](#sound-packs)\n- [Uninstall](#uninstall)\n- [Requirements](#requirements)\n- [How it works](#how-it-works)\n- [Links](#links)\n\n---\n\n## Install\n\n### Option 1: Homebrew (recommended)\n\n```bash\nbrew install PeonPing/tap/peon-ping\n```\n\nThen run `peon-ping-setup` to register hooks and download sound packs. macOS and Linux.\n\n### Option 2: Installer script (macOS, Linux, WSL2)\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/PeonPing/peon-ping/main/install.sh | bash\n```\n\n### Option 3: Installer for Windows\n\n```powershell\nInvoke-WebRequest -Uri \"https://raw.githubusercontent.com/PeonPing/peon-ping/main/install.ps1\" -UseBasicParsing | Invoke-Expression\n```\n\nInstalls 5 curated packs by default (Warcraft, StarCraft, Portal). Re-run to update while preserving config/state. Or **[pick your packs interactively at peonping.com](https://peonping.com/#picker)** and get a custom install command.\n\nUseful installer flags:\n\n- `--all` — install all available packs\n- `--packs=peon,sc_kerrigan,...` — install specific packs only\n- `--local` — install packs and config into `./.claude/` for the current project (hooks are always registered globally in `~/.claude/settings.json`)\n- `--global` — explicit global install (same as default)\n- `--init-local-config` — create `./.claude/hooks/peon-ping/config.json` only\n\n`--local` does not modify your shell rc files (no global `peon` alias/completion injection). Hooks are always written to the global `~/.claude/settings.json` with absolute paths so they work from any project directory.\n\nExamples:\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/PeonPing/peon-ping/main/install.sh | bash -s -- --all\ncurl -fsSL https://raw.githubusercontent.com/PeonPing/peon-ping/main/install.sh | bash -s -- --packs=peon,sc_kerrigan\ncurl -fsSL https://raw.githubusercontent.com/PeonPing/peon-ping/main/install.sh | bash -s -- --local\n```\n\nIf a global install exists and you install local (or vice versa), the installer prompts you to remove the existing one to avoid conflicts.\n\n### Option 4: Clone and inspect first\n\n```bash\ngit clone https://github.com/PeonPing/peon-ping.git\ncd peon-ping\n./install.sh\n```\n\n### Option 5: Nix (macOS, Linux)\n\nRun directly from source without installing:\n\n```bash\nnix run github:PeonPing/peon-ping -- status\nnix run github:PeonPing/peon-ping -- packs install peon\n```\n\nOr install to your profile:\n\n```bash\nnix profile install github:PeonPing/peon-ping\n```\n\nDevelopment shell (bats, shellcheck, nodejs):\n\n```bash\nnix develop  # or use direnv\n```\n\n#### Home Manager module (declarative configuration)\n\nFor reproducible setups, use the Home Manager module:\n\n```nix\n# In your home.nix or flake.nix\n{ inputs, pkgs, ... }: {\n  imports = [ inputs.peon-ping.homeManagerModules.default ];\n\n  programs.peon-ping = {\n    enable = true;\n    package = inputs.peon-ping.packages.${pkgs.system}.default;\n    \n    settings = {\n      default_pack = \"glados\";\n      volume = 0.7;\n      enabled = true;\n      desktop_notifications = true;\n      categories = {\n        \"session.start\" = true;\n        \"task.complete\" = true;\n        \"task.error\" = true;\n        \"input.required\" = true;\n        \"resource.limit\" = true;\n        \"user.spam\" = true;\n      };\n    };\n    \n    installPacks = [ \"peon\" \"glados\" \"sc_kerrigan\" ];\n    enableZshIntegration = true;\n  };\n}\n```\n\nThis creates `~/.openpeon/config.json` and installs specified packs automatically.\n\n## What you'll hear\n\n| Event | CESP Category | Examples |\n|---|---|---|\n| Session starts | `session.start` | *\"Ready to work?\"*, *\"Yes?\"*, *\"What you want?\"* |\n| Task finishes | `task.complete` | *\"Work, work.\"*, *\"I can do that.\"*, *\"Okie dokie.\"* |\n| Permission needed | `input.required` | *\"Something need doing?\"*, *\"Hmm?\"*, *\"What you want?\"* |\n| Tool or command error | `task.error` | *\"I can't do that.\"*, *\"Son of a bitch!\"* |\n| Agent acknowledged task | `task.acknowledge` | *\"I read you.\"*, *\"On it.\"* *(disabled by default)* |\n| Rate or token limit hit | `resource.limit` | *\"Zug zug.\"* *(pack dependent)* |\n| Rapid prompts (3+ in 10s) | `user.spam` | *\"Me busy, leave me alone!\"* |\n\nPlus **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.\n\npeon-ping implements the [Coding Event Sound Pack Specification (CESP)](https://github.com/PeonPing/openpeon) — an open standard for coding event sounds that any agentic IDE can adopt.\n\n## Quick controls\n\nNeed to mute sounds and notifications during a meeting or pairing session? Two options:\n\n| Method | Command | When |\n|---|---|---|\n| **Slash command** | `/peon-ping-toggle` | While working in Claude Code |\n| **CLI** | `peon toggle` | From any terminal tab |\n\nOther CLI commands:\n\n```bash\npeon pause                # Mute sounds\npeon resume               # Unmute sounds\npeon status               # Check if paused or active\npeon volume               # Show current volume\npeon volume 0.7           # Set volume (0.0–1.0)\npeon rotation             # Show current rotation mode\npeon rotation random      # Set rotation mode (random|round-robin|session_override)\npeon packs list           # List installed sound packs\npeon packs list --registry # Browse all available packs in the registry\npeon packs install \u003cp1,p2\u003e # Install packs from the registry\npeon packs install --all  # Install all packs from the registry\npeon packs install-local \u003cpath\u003e # Install a pack from a local directory\npeon packs use \u003cname\u003e     # Switch to a specific pack\npeon packs use --install \u003cname\u003e  # Switch to pack, installing from registry if needed\npeon packs next           # Cycle to the next pack\npeon packs remove \u003cp1,p2\u003e # Remove specific packs\npeon notifications on     # Enable desktop notifications\npeon notifications off    # Disable desktop notifications\npeon notifications overlay   # Use large overlay banners (default)\npeon notifications standard  # Use standard system notifications\npeon notifications test      # Send a test notification\npeon preview              # Play all sounds from session.start\npeon preview \u003ccategory\u003e   # Play all sounds from a specific category\npeon preview --list       # List all categories in the active pack\npeon mobile ntfy \u003ctopic\u003e  # Set up phone notifications (free)\npeon mobile off           # Disable phone notifications\npeon mobile test          # Send a test notification\npeon relay --daemon       # Start audio relay (for SSH/devcontainer)\npeon relay --stop         # Stop background relay\n```\n\nAvailable 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.)\n\nTab completion is supported — type `peon packs use \u003cTAB\u003e` to see available pack names.\n\nPausing mutes sounds and desktop notifications instantly. Persists across sessions until you resume. Tab titles remain active when paused.\n\n## Configuration\n\npeon-ping installs two slash commands in Claude Code:\n\n- `/peon-ping-toggle` — mute/unmute sounds\n- `/peon-ping-config` — change any setting (volume, packs, categories, etc.)\n\nYou 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.\n\nConfig location depends on install mode:\n\n- Global install: `$CLAUDE_CONFIG_DIR/hooks/peon-ping/config.json` (default `~/.claude/hooks/peon-ping/config.json`)\n- Local install: `./.claude/hooks/peon-ping/config.json`\n\n```json\n{\n  \"volume\": 0.5,\n  \"categories\": {\n    \"session.start\": true,\n    \"task.acknowledge\": true,\n    \"task.complete\": true,\n    \"task.error\": true,\n    \"input.required\": true,\n    \"resource.limit\": true,\n    \"user.spam\": true\n  }\n}\n```\n\n- **volume**: 0.0–1.0 (quiet enough for the office)\n- **desktop_notifications**: `true`/`false` — toggle desktop notification popups independently from sounds (default: `true`)\n- **notification_style**: `\"overlay\"` or `\"standard\"` — controls how desktop notifications appear (default: `\"overlay\"`)\n  - **overlay**: large, visible banners — JXA Cocoa overlay on macOS, Windows Forms popup on WSL/MSYS2. Clicking the overlay focuses your terminal (supports Ghostty, Warp, iTerm2, Zed, Terminal.app). On iTerm2, clicking focuses the correct tab/pane/window — not just the app.\n  - **standard**: system notifications — [`terminal-notifier`](https://github.com/julienXX/terminal-notifier) / `osascript` on macOS, Windows toast on WSL/MSYS2. When `terminal-notifier` is installed (`brew install terminal-notifier`), clicking a standard notification focuses your terminal automatically (supports Ghostty, Warp, iTerm2, Zed, Terminal.app)\n- **overlay_theme**: `\"jarvis\"`, `\"glass\"`, `\"sakura\"`, or omit for the default overlay — macOS only (default: none)\n  - **jarvis**: circular HUD with rotating arcs, graduation ticks, and progress ring\n  - **glass**: glassmorphism panel with accent color bar, progress line, and timestamp\n  - **sakura**: zen garden with bonsai tree and animated cherry blossom petals\n- **categories**: Toggle individual CESP sound categories on/off (e.g. `\"session.start\": false` to disable greeting sounds)\n- **annoyed_threshold / annoyed_window_seconds**: How many prompts in N seconds triggers the `user.spam` easter egg\n- **silent_window_seconds**: Suppress `task.complete` sounds and notifications for tasks shorter than N seconds. (e.g. `10` to only hear sounds for tasks that take longer than 10 seconds)\n- **suppress_subagent_complete** (boolean, default: `false`): Suppress `task.complete` sounds and notifications when a sub-agent session finishes. When Claude Code's Task tool dispatches parallel sub-agents, each one fires a completion sound — set this to `true` to hear only the parent session's completion sound.\n- **default_pack**: The fallback pack used when no more specific rule applies (default: `\"peon\"`). Replaces the old `active_pack` key — existing configs are migrated automatically on `peon update`.\n- **path_rules**: Array of `{ \"pattern\": \"...\", \"pack\": \"...\" }` objects. Assigns a pack to sessions based on the working directory using glob matching (`*`, `?`). First matching rule wins. Beats `pack_rotation` and `default_pack`; overridden by `session_override` assignments.\n  ```json\n  \"path_rules\": [\n    { \"pattern\": \"*/work/client-a/*\", \"pack\": \"glados\" },\n    { \"pattern\": \"*/personal/*\",      \"pack\": \"peon\" }\n  ]\n  ```\n- **pack_rotation**: Array of pack names (e.g. `[\"peon\", \"sc_kerrigan\", \"peasant\"]`). Used when `pack_rotation_mode` is `random` or `round-robin`. Leave empty `[]` to use `default_pack` (or `path_rules`) only.\n- **pack_rotation_mode**: `\"random\"` (default), `\"round-robin\"`, or `\"session_override\"`. With `random`/`round-robin`, each session picks one pack from `pack_rotation`. With `session_override`, the `/peon-ping-use \u003cpack\u003e` command assigns a pack per session. Invalid or missing packs fall back through the hierarchy. (`\"agentskill\"` is accepted as a legacy alias for `\"session_override\"`.)\n- **session_ttl_days** (number, default: 7): Expire stale per-session pack assignments older than N days. Keeps `.state.json` from growing unbounded when using `session_override` mode.\n- **headphones_only** (boolean, default: `false`): Only play sounds when headphones or external audio devices are detected. When enabled, sounds are suppressed if built-in speakers are the active output — useful for open offices. Check status with `peon status`. Supported on macOS (via `system_profiler`) and Linux (via PipeWire `wpctl` or PulseAudio `pactl`).\n- **suppress_sound_when_tab_focused** (boolean, default: `false`): Skip sound playback when the terminal tab that generated the hook event is the currently active/focused tab. Sounds still play for background tabs as an alert that something happened elsewhere. Desktop and mobile notifications are unaffected. Useful when you only want audio cues from tabs you're not watching. macOS only (uses `osascript` to check frontmost app and iTerm2 tab focus).\n\n## Peon Trainer\n\nYour 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.\n\n### Quick start\n\n```bash\npeon trainer on              # enable trainer\npeon trainer goal 200        # set daily goal (default: 300/300)\n# ... code for a while, peon nags you every ~20 min ...\npeon trainer log 25 pushups  # log what you did\npeon trainer log 30 squats\npeon trainer status          # check progress\n```\n\n### How it works\n\nTrainer 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.\n\n### Commands\n\n| Command | Description |\n|---------|-------------|\n| `peon trainer on` | Enable trainer mode |\n| `peon trainer off` | Disable trainer mode |\n| `peon trainer status` | Show today's progress |\n| `peon trainer log \u003cn\u003e \u003cexercise\u003e` | Log reps (e.g. `log 25 pushups`) |\n| `peon trainer goal \u003cn\u003e` | Set goal for all exercises |\n| `peon trainer goal \u003cexercise\u003e \u003cn\u003e` | Set goal for one exercise |\n\n### Claude Code skill\n\nIn Claude Code, you can log reps without leaving your conversation:\n\n```\n/peon-ping-log 25 pushups\n/peon-ping-log 30 squats\n```\n\n### Custom voice lines\n\nDrop your own audio files into `~/.claude/hooks/peon-ping/trainer/sounds/`:\n\n```\ntrainer/sounds/session_start/  # session greeting (\"Pushups first, code second! Zug zug!\")\ntrainer/sounds/remind/         # reminder lines (\"Something need doing? YES. PUSHUPS.\")\ntrainer/sounds/log/            # acknowledgment (\"Work work! Muscles getting bigger maybe!\")\ntrainer/sounds/complete/       # celebration (\"Zug zug! Human finish all reps!\")\ntrainer/sounds/slacking/       # disappointment (\"Peon very disappointed.\")\n```\n\nUpdate `trainer/manifest.json` to register your sound files.\n\n## MCP server\n\npeon-ping includes an [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server so any MCP-compatible AI agent can play sounds directly via tool calls — no hooks required.\n\nThe 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.\n\n### Setup\n\nAdd to your MCP client config (Claude Desktop, Cursor, etc.):\n\n```json\n{\n  \"mcpServers\": {\n    \"peon-ping\": {\n      \"command\": \"node\",\n      \"args\": [\"/path/to/peon-ping/mcp/peon-mcp.js\"]\n    }\n  }\n}\n```\n\nIf installed via Homebrew: `$(brew --prefix peon-ping)/libexec/mcp/peon-mcp.js`. See [`mcp/README.md`](mcp/README.md) for full setup instructions.\n\n### What the agent can do\n\n| Feature | Description |\n|---|---|\n| **`play_sound`** | Play one or more sounds by key (e.g. `duke_nukem/SonOfABitch`, `peon/PeonReady1`) |\n| **`peon-ping://catalog`** | Full pack catalog as an MCP Resource — client prefetches once, no repeated tool calls |\n| **`peon-ping://pack/{name}`** | Individual pack details and available sound keys |\n\nRequires Node.js 18+. Contributed by [@tag-assistant](https://github.com/tag-assistant).\n\n## Multi-IDE Support\n\npeon-ping works with any agentic IDE that supports hooks. Adapters translate IDE-specific events to the [CESP standard](https://github.com/PeonPing/openpeon).\n\n| IDE | Status | Setup |\n|---|---|---|\n| **Claude Code** | Built-in | `curl \\| bash` install handles everything |\n| **Amp** | Adapter | `bash ~/.claude/hooks/peon-ping/adapters/amp.sh` (requires `fswatch`: `brew install fswatch`) ([setup](#amp-setup)) |\n| **Gemini CLI** | Adapter | Add hooks to `~/.gemini/settings.json` pointing to `adapters/gemini.sh` ([setup](#gemini-cli-setup)) |\n| **GitHub Copilot** | Adapter | Add hooks to `.github/hooks/hooks.json` pointing to `adapters/copilot.sh` ([setup](#github-copilot-setup)) |\n| **OpenAI Codex** | Adapter | Add `notify = [\"bash\", \"/absolute/path/to/.claude/hooks/peon-ping/adapters/codex.sh\"]` to `~/.codex/config.toml` |\n| **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. |\n| **OpenCode** | Adapter | `curl -fsSL https://raw.githubusercontent.com/PeonPing/peon-ping/main/adapters/opencode.sh \\| bash` ([setup](#opencode-setup)) |\n| **Kilo CLI** | Adapter | `curl -fsSL https://raw.githubusercontent.com/PeonPing/peon-ping/main/adapters/kilo.sh \\| bash` ([setup](#kilo-cli-setup)) |\n| **Kiro** | Adapter | Add hook entries to `~/.kiro/agents/peon-ping.json` pointing to `adapters/kiro.sh` ([setup](#kiro-setup)) |\n| **Windsurf** | Adapter | Add hook entries to `~/.codeium/windsurf/hooks.json` pointing to `adapters/windsurf.sh` ([setup](#windsurf-setup)) |\n| **Google Antigravity** | Adapter | `bash ~/.claude/hooks/peon-ping/adapters/antigravity.sh` (requires `fswatch`: `brew install fswatch`) |\n| **OpenClaw** | Adapter | Call `adapters/openclaw.sh \u003cevent\u003e` from your OpenClaw skill. Supports all CESP categories and raw Claude Code event names. |\n\n### Amp setup\n\nA filesystem watcher adapter for [Amp](https://ampcode.com) (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.\n\n**Setup:**\n\n1. Ensure peon-ping is installed (`curl -fsSL https://peonping.com/install | bash`)\n\n2. Install `fswatch` (macOS) or `inotify-tools` (Linux):\n\n   ```bash\n   brew install fswatch        # macOS\n   sudo apt install inotify-tools  # Linux\n   ```\n\n3. Start the watcher:\n\n   ```bash\n   bash ~/.claude/hooks/peon-ping/adapters/amp.sh        # foreground\n   bash ~/.claude/hooks/peon-ping/adapters/amp.sh \u0026       # background\n   ```\n\n**Event mapping:**\n\n- New thread file created → Greeting sound (*\"Ready to work?\"*, *\"Yes?\"*)\n- Thread file stops updating + agent finished turn → Completion sound (*\"Work, work.\"*, *\"Job's done!\"*)\n\n**How it works:**\n\nThe 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.\n\n**Environment variables:**\n\n| Variable | Default | Description |\n|---|---|---|\n| `AMP_DATA_DIR` | `~/.local/share/amp` | Amp data directory |\n| `AMP_THREADS_DIR` | `$AMP_DATA_DIR/threads` | Threads directory to watch |\n| `AMP_IDLE_SECONDS` | `1` | Seconds of no changes before emitting Stop |\n| `AMP_STOP_COOLDOWN` | `10` | Minimum seconds between Stop events per thread |\n\n### GitHub Copilot setup\n\nA shell adapter for [GitHub Copilot](https://github.com/features/copilot) with full [CESP v1.0](https://github.com/PeonPing/openpeon) conformance.\n\n**Setup:**\n\n1. Ensure peon-ping is installed (`curl -fsSL https://peonping.com/install | bash`)\n\n2. Create `.github/hooks/hooks.json` in your repository (on the default branch):\n\n   ```json\n   {\n     \"version\": 1,\n     \"hooks\": {\n       \"sessionStart\": [\n         {\n           \"type\": \"command\",\n           \"bash\": \"bash ~/.claude/hooks/peon-ping/adapters/copilot.sh sessionStart\"\n         }\n       ],\n       \"userPromptSubmitted\": [\n         {\n           \"type\": \"command\",\n           \"bash\": \"bash ~/.claude/hooks/peon-ping/adapters/copilot.sh userPromptSubmitted\"\n         }\n       ],\n       \"postToolUse\": [\n         {\n           \"type\": \"command\",\n           \"bash\": \"bash ~/.claude/hooks/peon-ping/adapters/copilot.sh postToolUse\"\n         }\n       ],\n       \"errorOccurred\": [\n         {\n           \"type\": \"command\",\n           \"bash\": \"bash ~/.claude/hooks/peon-ping/adapters/copilot.sh errorOccurred\"\n         }\n       ]\n     }\n   }\n   ```\n\n3. Commit and merge to your default branch. Hooks will activate on your next Copilot agent session.\n\n**Event mapping:**\n\n- `sessionStart` → Greeting sound (*\"Ready to work?\"*, *\"Yes?\"*)\n- `userPromptSubmitted` → First prompt = greeting, subsequent = spam detection\n- `postToolUse` → Completion sound (*\"Work, work.\"*, *\"Job's done!\"*)\n- `errorOccurred` → Error sound (*\"I can't do that.\"*)\n- `preToolUse` → Skipped (too noisy)\n- `sessionEnd` → No sound (session.end not yet implemented)\n\n**Features:**\n\n- **Sound playback** via `afplay` (macOS), `pw-play`/`paplay`/`ffplay` (Linux) — same priority chain as the shell hook\n- **CESP event mapping** — GitHub Copilot hooks map to standard CESP categories (`session.start`, `task.complete`, `task.error`, `user.spam`)\n- **Desktop notifications** — large overlay banners by default, or standard notifications\n- **Spam detection** — detects 3+ rapid prompts within 10 seconds, triggers `user.spam` voice lines\n- **Session tracking** — separate session markers per Copilot sessionId\n\n### OpenCode setup\n\nA native TypeScript plugin for [OpenCode](https://opencode.ai/) with full [CESP v1.0](https://github.com/PeonPing/openpeon) conformance.\n\n**Quick install:**\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/PeonPing/peon-ping/main/adapters/opencode.sh | bash\n```\n\nThe 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/`).\n\n**Features:**\n\n- **Sound playback** via `afplay` (macOS), `pw-play`/`paplay`/`ffplay` (Linux) — same priority chain as the shell hook\n- **CESP event mapping** — `session.created` / `session.idle` / `session.error` / `permission.asked` / rapid prompt detection all map to standard CESP categories\n- **Desktop notifications** — large overlay banners by default (JXA Cocoa, visible on all screens), or standard notifications via [`terminal-notifier`](https://github.com/julienXX/terminal-notifier) / `osascript`. Fires only when the terminal is not focused.\n- **Terminal focus detection** — checks if your terminal app (Terminal, iTerm2, Warp, Alacritty, kitty, WezTerm, ghostty, Hyper) is frontmost via AppleScript before sending notifications\n- **Tab titles** — updates the terminal tab to show task status (`● project: working...` / `✓ project: done` / `✗ project: error`)\n- **Pack switching** — reads `default_pack` from config (with `active_pack` fallback for legacy configs), loads the pack's `openpeon.json` manifest at runtime. `path_rules` can override the pack per working directory.\n- **No-repeat logic** — avoids playing the same sound twice in a row per category\n- **Spam detection** — detects 3+ rapid prompts within 10 seconds, triggers `user.spam` voice lines\n\n\u003cdetails\u003e\n\u003csummary\u003e🖼️ Screenshot: desktop notifications with custom peon icon\u003c/summary\u003e\n\n![peon-ping OpenCode notifications](https://github.com/user-attachments/assets/e433f9d1-2782-44af-a176-71875f3f532c)\n\n\u003c/details\u003e\n\n\u003e **Tip:** Install `terminal-notifier` (`brew install terminal-notifier`) for richer notifications with subtitle and grouping support.\n\n\u003cdetails\u003e\n\u003csummary\u003e🎨 Optional: custom peon icon for notifications\u003c/summary\u003e\n\nBy 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.\n\n```bash\nbash \u003c(curl -fsSL https://raw.githubusercontent.com/PeonPing/peon-ping/main/adapters/opencode/setup-icon.sh)\n```\n\nOr if installed locally (Homebrew / git clone):\n\n```bash\nbash ~/.claude/hooks/peon-ping/adapters/opencode/setup-icon.sh\n```\n\nThe 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`.\n\n\u003e **Future:** When [jamf/Notifier](https://github.com/jamf/Notifier) ships to Homebrew ([#32](https://github.com/jamf/Notifier/issues/32)), the plugin will migrate to it — Notifier has built-in `--rebrand` support, no icon hacks needed.\n\n\u003c/details\u003e\n\n### Kilo CLI setup\n\nA native TypeScript plugin for [Kilo CLI](https://github.com/kilocode/cli) with full [CESP v1.0](https://github.com/PeonPing/openpeon) 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.\n\n**Quick install:**\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/PeonPing/peon-ping/main/adapters/kilo.sh | bash\n```\n\nThe 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/`).\n\n**Features:** Same as the [OpenCode adapter](#opencode-setup) — sound playback, CESP event mapping, desktop notifications, terminal focus detection, tab titles, pack switching, no-repeat logic, and spam detection.\n\n### Gemini CLI setup\n\nA shell adapter for **Gemini CLI** with full [CESP v1.0](https://github.com/PeonPing/openpeon) conformance.\n\n**Setup:**\n\n1. Ensure peon-ping is installed (`curl -fsSL https://peonping.com/install | bash`)\n\n2. Add the following hooks to your `~/.gemini/settings.json`:\n\n   ```json\n    {\n      \"hooks\": {\n        \"SessionStart\": [\n          {\n            \"matcher\": \"startup\",\n            \"hooks\": [\n              {\n                \"name\": \"peon-start\",\n                \"type\": \"command\",\n                \"command\": \"bash ~/.claude/hooks/peon-ping/adapters/gemini.sh SessionStart\"\n              }\n            ]\n          }\n        ],\n        \"AfterAgent\": [\n          {\n            \"matcher\": \"*\",\n            \"hooks\": [\n              {\n                \"name\": \"peon-after-agent\",\n                \"type\": \"command\",\n                \"command\": \"bash ~/.claude/hooks/peon-ping/adapters/gemini.sh AfterAgent\"\n              }\n            ]\n          }\n        ],\n        \"AfterTool\": [\n          {\n            \"matcher\": \"*\",\n            \"hooks\": [\n              {\n                \"name\": \"peon-after-tool\",\n                \"type\": \"command\",\n                \"command\": \"bash ~/.claude/hooks/peon-ping/adapters/gemini.sh AfterTool\"\n              }\n            ]\n          }\n        ],\n        \"Notification\": [\n          {\n            \"matcher\": \"*\",\n            \"hooks\": [\n              {\n                \"name\": \"peon-notification\",\n                \"type\": \"command\",\n                \"command\": \"bash ~/.claude/hooks/peon-ping/adapters/gemini.sh Notification\"\n              }\n            ]\n          }\n        ]\n      }\n    }\n   ```\n\n**Event mapping:**\n\n- `SessionStart` (startup) → Greeting sound (*\"Ready to work?\"*, *\"Yes?\"*)\n- `AfterAgent` → Task completion sound (*\"Work, work.\"*, *\"Job's done!\"*)\n- `AfterTool` → Success = Task completion sound, Failure = Error sound (*\"I can't do that.\"*)\n- `Notification` → System notification\n\n### Windsurf setup\n\nAdd to `~/.codeium/windsurf/hooks.json` (user-level) or `.windsurf/hooks.json` (workspace-level):\n\n```json\n{\n  \"hooks\": {\n    \"post_cascade_response\": [\n      { \"command\": \"bash ~/.claude/hooks/peon-ping/adapters/windsurf.sh post_cascade_response\", \"show_output\": false }\n    ],\n    \"pre_user_prompt\": [\n      { \"command\": \"bash ~/.claude/hooks/peon-ping/adapters/windsurf.sh pre_user_prompt\", \"show_output\": false }\n    ],\n    \"post_write_code\": [\n      { \"command\": \"bash ~/.claude/hooks/peon-ping/adapters/windsurf.sh post_write_code\", \"show_output\": false }\n    ],\n    \"post_run_command\": [\n      { \"command\": \"bash ~/.claude/hooks/peon-ping/adapters/windsurf.sh post_run_command\", \"show_output\": false }\n    ]\n  }\n}\n```\n\n### Kiro setup\n\nCreate `~/.kiro/agents/peon-ping.json`:\n\n```json\n{\n  \"hooks\": {\n    \"agentSpawn\": [\n      { \"command\": \"bash ~/.claude/hooks/peon-ping/adapters/kiro.sh\" }\n    ],\n    \"userPromptSubmit\": [\n      { \"command\": \"bash ~/.claude/hooks/peon-ping/adapters/kiro.sh\" }\n    ],\n    \"stop\": [\n      { \"command\": \"bash ~/.claude/hooks/peon-ping/adapters/kiro.sh\" }\n    ]\n  }\n}\n```\n\n`preToolUse`/`postToolUse` are intentionally excluded — they fire on every tool call and would be extremely noisy.\n\n## Remote development (SSH / Devcontainers / Codespaces)\n\nCoding 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.\n\n### SSH setup\n\n1. **On your local machine**, start the relay:\n   ```bash\n   peon relay --daemon\n   ```\n\n2. **SSH with port forwarding**:\n   ```bash\n   ssh -R 19998:localhost:19998 your-server\n   ```\n\n3. **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.\n\nThat's it. Sounds play on your laptop, not the remote server.\n\n### Devcontainers / Codespaces\n\nNo 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.\n\n### Relay commands\n\n```bash\npeon relay                # Start relay in foreground\npeon relay --daemon       # Start in background\npeon relay --stop         # Stop background relay\npeon relay --status       # Check if relay is running\npeon relay --port=12345   # Custom port (default: 19998)\npeon relay --bind=0.0.0.0 # Listen on all interfaces (less secure)\n```\n\nEnvironment variables: `PEON_RELAY_PORT`, `PEON_RELAY_HOST`, `PEON_RELAY_BIND`.\n\nIf peon-ping detects an SSH or container session but can't reach the relay, it prints setup instructions on `SessionStart`.\n\n### Category-based API (for lightweight remote hooks)\n\nThe 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.\n\n**Endpoints:**\n\n| Endpoint | Description |\n|---|---|\n| `GET /health` | Health check (returns \"OK\") |\n| `GET /play?file=\u003cpath\u003e` | Play a specific sound file (legacy) |\n| `GET /play?category=\u003ccat\u003e` | Play random sound from category (recommended) |\n| `POST /notify` | Send desktop notification |\n\n**Example remote hook (`scripts/remote-hook.sh`):**\n\n```bash\n#!/bin/bash\nRELAY_URL=\"${PEON_RELAY_URL:-http://127.0.0.1:19998}\"\nEVENT=$(cat | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('hook_event_name',''))\" 2\u003e/dev/null)\ncase \"$EVENT\" in\n  SessionStart)      CATEGORY=\"session.start\" ;;\n  Stop)              CATEGORY=\"task.complete\" ;;\n  PermissionRequest) CATEGORY=\"input.required\" ;;\n  *)                 exit 0 ;;\nesac\ncurl -sf \"${RELAY_URL}/play?category=${CATEGORY}\" \u003e/dev/null 2\u003e\u00261 \u0026\n```\n\nCopy this to your remote machine and register it in `~/.claude/settings.json`:\n\n```json\n{\n  \"hooks\": {\n    \"SessionStart\": [{\"command\": \"bash /path/to/remote-hook.sh\"}],\n    \"Stop\": [{\"command\": \"bash /path/to/remote-hook.sh\"}],\n    \"PermissionRequest\": [{\"command\": \"bash /path/to/remote-hook.sh\"}]\n  }\n}\n```\n\nThe 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.\n\n## Mobile notifications\n\nGet push notifications on your phone when tasks finish or need attention — useful when you're away from your desk.\n\n### Quick start (ntfy.sh — free, no account needed)\n\n1. Install the [ntfy app](https://ntfy.sh) on your phone\n2. Subscribe to a unique topic in the app (e.g. `my-peon-notifications`)\n3. Run:\n   ```bash\n   peon mobile ntfy my-peon-notifications\n   ```\n\nAlso supports [Pushover](https://pushover.net) and [Telegram](https://core.telegram.org/bots):\n\n```bash\npeon mobile pushover \u003cuser_key\u003e \u003capp_token\u003e\npeon mobile telegram \u003cbot_token\u003e \u003cchat_id\u003e\n```\n\n### Mobile commands\n\n```bash\npeon mobile on            # Enable mobile notifications\npeon mobile off           # Disable mobile notifications\npeon mobile status        # Show current config\npeon mobile test          # Send a test notification\n```\n\nMobile notifications fire on every event regardless of window focus — they're independent from desktop notifications and sounds.\n\n## Sound packs\n\n99 packs across Warcraft, StarCraft, Red Alert, Portal, Zelda, Dota 2, Helldivers 2, Elder Scrolls, and more. The default install includes 5 curated packs:\n\n| Pack | Character | Sounds |\n|---|---|---|\n| `peon` (default) | Orc Peon (Warcraft III) | \"Ready to work?\", \"Work, work.\", \"Okie dokie.\" |\n| `peasant` | Human Peasant (Warcraft III) | \"Yes, milord?\", \"Job's done!\", \"Ready, sir.\" |\n| `sc_kerrigan` | Sarah Kerrigan (StarCraft) | \"I gotcha\", \"What now?\", \"Easily amused, huh?\" |\n| `sc_battlecruiser` | Battlecruiser (StarCraft) | \"Battlecruiser operational\", \"Make it happen\", \"Engage\" |\n| `glados` | GLaDOS (Portal) | \"Oh, it's you.\", \"You monster.\", \"Your entire team is dead.\" |\n\n**[Browse all packs with audio previews \u0026rarr; openpeon.com/packs](https://openpeon.com/packs)**\n\nInstall all with `--all`, or switch packs anytime:\n\n```bash\npeon packs use glados             # switch to a specific pack\npeon packs use --install glados   # install (or update) and switch in one step\npeon packs next                   # cycle to the next pack\npeon packs list                   # list all installed packs\npeon packs list --registry        # browse all available packs\npeon packs install glados,murloc  # install specific packs\npeon packs install --all          # install every pack in the registry\n```\n\nWant to add your own pack? See the [full guide at openpeon.com/create](https://openpeon.com/create) or [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## Uninstall\n\n**macOS/Linux:**\n\n```bash\nbash \"${CLAUDE_CONFIG_DIR:-$HOME/.claude}\"/hooks/peon-ping/uninstall.sh        # global\nbash .claude/hooks/peon-ping/uninstall.sh           # project-local\n```\n\n**Windows (PowerShell):**\n\n```powershell\n# Standard uninstall (prompts before deleting sounds)\npowershell -ExecutionPolicy Bypass -File \"$env:USERPROFILE\\.claude\\hooks\\peon-ping\\uninstall.ps1\"\n\n# Keep sound packs (removes everything else)\npowershell -ExecutionPolicy Bypass -File \"$env:USERPROFILE\\.claude\\hooks\\peon-ping\\uninstall.ps1\" -KeepSounds\n```\n\n## Requirements\n\n- **macOS** — `afplay` (built-in), JXA Cocoa overlay or AppleScript for notifications\n- **Linux** — one of: `pw-play`, `paplay`, `ffplay`, `mpv`, `play` (SoX), or `aplay`; `notify-send` for notifications\n- **Windows** — native PowerShell with `MediaPlayer` and WinForms (no WSL required), or WSL2\n- **MSYS2 / Git Bash** — `python3`, `cygpath` (built-in); audio via `ffplay`/`mpv`/`play` or PowerShell fallback\n- **All platforms** — `python3` (not required for native Windows)\n- **SSH/remote** — `curl` on the remote host\n- **IDE** — Claude Code with hooks support, Amp, or any supported IDE via [adapters](#multi-ide-support)\n\n## How it works\n\n`peon.sh` is a Claude Code hook registered for `SessionStart`, `SessionEnd`, `SubagentStart`, `Stop`, `Notification`, `PermissionRequest`, `PostToolUseFailure`, and `PreCompact` events. On each event:\n\n1. **Event mapping** — an embedded Python block maps the hook event to a [CESP](https://github.com/PeonPing/openpeon) sound category (`session.start`, `task.complete`, `input.required`, etc.)\n2. **Sound selection** — picks a random voice line from the active pack's manifest, avoiding repeats\n3. **Audio playback** — plays the sound asynchronously via `afplay` (macOS), PowerShell `MediaPlayer` (WSL2/MSYS2 fallback), or `pw-play`/`paplay`/`ffplay`/`mpv`/`aplay` (Linux/MSYS2)\n4. **Notifications** — updates the Terminal tab title and sends a desktop notification if the terminal isn't focused\n5. **Remote routing** — in SSH sessions, devcontainers, and Codespaces, audio and notification requests are forwarded over HTTP to a [relay server](#remote-development-ssh--devcontainers--codespaces) on your local machine\n\nSound packs are downloaded from the [OpenPeon registry](https://github.com/PeonPing/registry) at install time. The official packs are hosted in [PeonPing/og-packs](https://github.com/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.\n\n## Links\n\n- [@peonping on X](https://x.com/peonping) — updates and announcements\n- [peonping.com](https://peonping.com/) — landing page\n- [openpeon.com](https://openpeon.com/) — CESP spec, pack browser, [integration guide](https://openpeon.com/integrate), creation guide\n- [OpenPeon registry](https://github.com/PeonPing/registry) — pack registry (GitHub Pages)\n- [og-packs](https://github.com/PeonPing/og-packs) — official sound packs\n- [peon-pet](https://github.com/PeonPing/peon-pet) — macOS desktop pet (orc sprite, reacts to hook events)\n- [License (MIT)](LICENSE)\n\n## Support the project\n\n- Venmo: [@garysheng](https://venmo.com/garysheng)\n- Community Token (DYOR / have fun): Someone created a $PEON token on Base — we receive TX fees which help fund development. [`0xf4ba744229afb64e2571eef89aacec2f524e8ba3`](https://dexscreener.com/base/0xf4bA744229aFB64E2571eef89AaceC2F524e8bA3)\n\n","funding_links":[],"categories":["Skills \u0026 Plugins","🛠️ Tools \u0026 Utilities","Plugins","🚀 AI Tools for Vim, Neovim, and Terminal"],"sub_categories":["All Plugins"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FPeonPing%2Fpeon-ping","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FPeonPing%2Fpeon-ping","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FPeonPing%2Fpeon-ping/lists"}