[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-74862":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":18,"stars30d":19,"stars90d":16,"forks30d":16,"starsTrendScore":20,"compositeScore":21,"rankGlobal":10,"rankLanguage":10,"license":22,"archived":23,"fork":23,"defaultBranch":24,"hasWiki":25,"hasPages":23,"topics":26,"createdAt":10,"pushedAt":10,"updatedAt":35,"readmeContent":36,"aiSummary":37,"trendingCount":16,"starSnapshotCount":16,"syncStatus":38,"lastSyncTime":39,"discoverSource":40},74862,"peon-ping","PeonPing\u002Fpeon-ping","PeonPing","Warcraft III Peon voice notifications (+ more!) for Claude Code, Codex, IDEs, and any AI agent. Stop babysitting your terminal. Employ a Peon today.","https:\u002F\u002Fwww.peonping.com",null,"Shell",4816,349,6,19,0,13,35,117,39,29.63,"MIT License",false,"main",true,[27,28,29,30,31,32,33,34],"ai","ai-engineering","antigravity","claude-code","codex","cursor","opencode","terminal","2026-06-12 02:03:29","# peon-ping\n\u003Cdiv align=\"center\">\n\n**English** | [한국어](README_ko.md) | [中文](README_zh.md) | [日本語](README_ja.md)\n\n![macOS](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FmacOS-blue) ![WSL2](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FWSL2-blue) ![Linux](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLinux-blue) ![Windows](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FWindows-blue) ![MSYS2](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FMSYS2-blue) ![SSH](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FSSH-blue)\n![License](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-MIT-green)\n\n![Claude Code](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FClaude_Code-hook-ffab01) ![Amp](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FAmp-adapter-ffab01) ![Gemini CLI](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FGemini_CLI-adapter-ffab01) ![GitHub Copilot](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FGitHub_Copilot-adapter-ffab01) ![Codex](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FCodex-adapter-ffab01) ![Cursor](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FCursor-adapter-ffab01) ![OpenCode](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FOpenCode-adapter-ffab01) ![Kilo CLI](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FKilo_CLI-adapter-ffab01) ![Kiro](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FKiro-adapter-ffab01) ![Kimi Code](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FKimi_Code-adapter-ffab01) ![Windsurf](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FWindsurf-adapter-ffab01) ![Antigravity](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FAntigravity-adapter-ffab01) ![OpenClaw](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FOpenClaw-adapter-ffab01) ![Rovo Dev CLI](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FRovo_Dev_CLI-adapter-ffab01) ![DeepAgents](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FDeepAgents-adapter-ffab01) ![oh-my-pi](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Foh--my--pi-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**, **Kimi Code**, **Windsurf**, **Google Antigravity**, **Rovo Dev CLI**, **DeepAgents**, and any MCP client.\n\n**See it in action** &rarr; [peonping.com](https:\u002F\u002Fpeonping.com\u002F)\n\n\u003Cvideo src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002F149b6d15-65c2-41f2-9b56-13575ff8364b\" autoplay loop muted playsinline width=\"400\">\u003C\u002Fvideo>\n\n\u003C\u002Fdiv>\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- [Debugging](#debugging)\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\u002Ftap\u002Fpeon-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:\u002F\u002Fraw.githubusercontent.com\u002FPeonPing\u002Fpeon-ping\u002Fmain\u002Finstall.sh | bash\n```\n\n⚠️ **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:\n\n- On **Windows 10 \u002F Windows 11 pre-24H2**, WPF MediaPlayer is used directly — native MP3 + WAV, no extra dependencies.\n- On **Windows 11 24H2+** (build 26100+), Microsoft removed legacy Windows Media Player from the OS and WPF MediaPlayer fails (`MILAVERR_INVALIDWMPVERSION`). peon-ping falls back to `System.Media.SoundPlayer`, which uses the Win32 `PlaySound` API and works everywhere — but it's WAV-only, so MP3 packs require **ffmpeg** to transcode on the fly:\n\n  ```sh\n  sudo apt update; sudo apt install -y ffmpeg\n  ```\n\nYou can override the auto-detection with `PEON_WSL_AUDIO_BACKEND=auto|mediaplayer|soundplayer`:\n\n- `auto` (default) — probe + cache as described above\n- `mediaplayer` — force WPF MediaPlayer over the WSL UNC path (fails silently on 24H2+)\n- `soundplayer` — force tmpfile copy + `SoundPlayer` (universal, requires ffmpeg for non-WAV files)\n\n### Option 3: Installer for Windows\n\n```powershell\nInvoke-WebRequest -Uri \"https:\u002F\u002Fraw.githubusercontent.com\u002FPeonPing\u002Fpeon-ping\u002Fmain\u002Finstall.ps1\" -OutFile \".\\install.ps1\" -UseBasicParsing\npowershell -ExecutionPolicy Bypass -File .\\install.ps1\n```\n\nInstalls a curated starter set of packs by default. Re-run to update while preserving config\u002Fstate. Or **[pick your packs interactively at peonping.com](https:\u002F\u002Fpeonping.com\u002F#picker)** and get a custom install command.\n\nWindows installer parameters:\n\n- `-All` — install all available packs\n- `-Packs peon,sc_kerrigan,...` — install specific packs only\n- `-Lang en,fr,...` — install only packs matching language(s)\n- `-Local` — install packs, config, hooks, and skills into `.\u002F.claude\u002F` for the current project\n- `-Global` — explicit global install (same as default)\n- `-InitLocalConfig` — create `.\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fconfig.json` only\n\n`-Local` does not install the global `peon` CLI shim or modify your user `PATH`. Hooks are registered in the project-level `.\u002F.claude\u002Fsettings.json` with absolute paths so they work from any working directory within the project.\n\nWindows examples:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File .\\install.ps1 -All\npowershell -ExecutionPolicy Bypass -File .\\install.ps1 -Packs peon,sc_kerrigan\npowershell -ExecutionPolicy Bypass -File .\\install.ps1 -Local\npowershell -ExecutionPolicy Bypass -File .\\install.ps1 -InitLocalConfig\n```\n\nIf the initial download fails with a TLS error on older Windows PowerShell, run this once in the same session and retry:\n\n```powershell\n[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12\n```\n\n### Option 4: Clone and inspect first\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002FPeonPing\u002Fpeon-ping.git\ncd peon-ping\n.\u002Finstall.sh\n```\n\nOn Windows PowerShell:\n\n```powershell\ngit clone https:\u002F\u002Fgithub.com\u002FPeonPing\u002Fpeon-ping.git\nSet-Location peon-ping\n.\\install.ps1\n```\n\n### Option 5: Nix (macOS, Linux)\n\nRun directly from source without installing:\n\n```bash\nnix run github:PeonPing\u002Fpeon-ping -- status\nnix run github:PeonPing\u002Fpeon-ping -- packs install peon\n```\n\nOr install to your profile:\n\n```bash\nnix profile install github:PeonPing\u002Fpeon-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\nlet\n  peonCursorAdapterPath = \"${inputs.peon-ping.packages.${pkgs.system}.default}\u002Fshare\u002Fpeon-ping\u002Fadapters\u002Fcursor.sh\";\nin {\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    claudeCodeIntegration = true;\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    # Install packs from og-packs (simple string notation)\n    # and custom sources (attrset with name + src)\n    installPacks = [\n      \"peon\"\n      \"glados\"\n      \"sc_kerrigan\"\n      # Custom pack from GitHub (openpeon.com registry)\n      {\n        name = \"mr_meeseeks\";\n        src = pkgs.fetchFromGitHub {\n          owner = \"kasperhendriks\";\n          repo = \"openpeon-mrmeeseeks\";\n          rev = \"main\";  # or use a commit hash for reproducibility\n          sha256 = \"sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\";\n        };\n      }\n    ];\n    enableZshIntegration = true;\n  };\n\n  # Optional extra IDE hooks, like Cursor\n  home.file.\".cursor\u002Fhooks.json\".text = builtins.toJSON {\n    version = 1;\n    hooks = {\n      afterAgentResponse = [{ command = \"bash ${peonCursorAdapterPath} afterAgentResponse\"; }];\n      stop               = [{ command = \"bash ${peonCursorAdapterPath} stop\"; }];\n    };\n  };\n}\n```\n\n**Sound pack installation**: The `installPacks` option supports two formats:\n- **Simple strings** (e.g., `\"peon\"`, `\"glados\"`) — fetched from the [og-packs](https:\u002F\u002Fgithub.com\u002FPeonPing\u002Fog-packs) repository\n- **Custom sources** — attrset with `name` and `src` fields, where `src` can be any Nix fetcher result (e.g., `pkgs.fetchFromGitHub`)\n\nFor packs listed on [openpeon.com](https:\u002F\u002Fopenpeon.com\u002F), find the GitHub repository link and use `pkgs.fetchFromGitHub`:\n```nix\n{\n  name = \"pack_name\";\n  src = pkgs.fetchFromGitHub {\n    owner = \"github-owner\";\n    repo = \"repo-name\";\n    rev = \"main\";  # or a commit hash\u002Ftag\n    sha256 = \"\";   # Leave empty first, Nix will tell you the correct hash\n  };\n}\n```\n\n**Claude Code hooks**: set `programs.peon-ping.claudeCodeIntegration = true;` to install the Claude Code hook scripts under `~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002F` and merge the standard peon-ping hook entries into `~\u002F.claude\u002Fsettings.json`.\n\n**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\u002F`](https:\u002F\u002Fgithub.com\u002FPeonPing\u002Fpeon-ping\u002Ftree\u002Fmain\u002Fadapters), and you can wire them like this:\n  ```sh\n  ${inputs.peon-ping.packages.${pkgs.system}.default}\u002Fshare\u002Fpeon-ping\u002Fadapters\u002F$YOUR_IDE.sh EVENT_NAME\n  ```\n  See the Cursor example above.\n\n## What you'll hear\n\n| Event | CESP Category | Examples |\n|---|---|---|\n| Session starts | `session.start` | *\"Ready to work!\"*, *\"Something need doing?\"* |\n| Task finishes | `task.complete` | *\"Work complete.\"*, *\"Work, work.\"* |\n| Agent acknowledged task | `task.acknowledge` | *\"I can do that.\"*, *\"Be happy to.\"*, *\"Okie dokie.\"* *(disabled by default)* |\n| Permission needed | `input.required` | *\"Hmm?\"*, *\"What you want?\"*, *\"Yes?\"* |\n| Tool or command error | `task.error` | *\"Me not that kind of orc!\"*, *\"Ugh.\"* |\n| Rate or token limit hit | `resource.limit` | *\"Why not?\"* |\n| Rapid prompts (3+ in 10s) | `user.spam` | *\"Whaaat?\"*, *\"Me busy, leave me alone!\"*, *\"No time for play.\"* |\n\nPlus **large overlay banners** on every screen (macOS\u002FWSL\u002FMSYS2) 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:\u002F\u002Fgithub.com\u002FPeonPing\u002Fopenpeon) — 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** | `\u002Fpeon-ping-toggle` | While working in Claude Code |\n| **CLI** | `peon toggle` | From any terminal tab |\n\nOther CLI commands:\n\n> Windows note: Windows currently supports the day-one controls (`status`, `toggle`, `volume`, core `packs`, `notifications on\u002Foff`, `debug`, `logs`, `trainer`). More advanced commands like `setup`, `rotation`, `preview`, and `mobile` are tracked as follow-up Windows parity work.\n\n```bash\npeon setup                # Interactive setup wizard (volume, categories, notifications)\npeon pause                # Mute sounds\npeon resume               # Unmute sounds\npeon mute                 # Alias for 'pause'\npeon unmute               # Alias for 'resume'\npeon status               # Check if paused or active (concise)\npeon status --verbose     # Show full details (notifications, headphones, IDEs, etc.)\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 community      # List all registry packs grouped by trust tier (Windows)\npeon packs search \u003Cquery> # Search registry packs by name (Windows)\npeon packs install \u003Cp1,p2> # Install packs from the registry\npeon packs install --all  # Install all packs from the registry\npeon packs install-local \u003Cpath> # Install a pack from a local directory\npeon packs use \u003Cname>     # Switch to a specific pack (auto-installs from registry on Windows)\npeon packs use --install \u003Cname>  # Switch to pack, installing from registry if needed\npeon packs next           # Cycle to the next pack\npeon packs remove \u003Cp1,p2> # Remove specific packs\npeon packs bind \u003Cname>    # Bind a pack to the current directory\npeon packs bind --pattern \u003Cpath> # Bind a pack to a directory pattern, e.g. \"*\u002Fservices\"\npeon packs unbind         # Remove the current directory\npeon packs bindings       # List all assigned bindings\npeon packs ide-bind \u003Cide> \u003Cname> # Bind a pack to an IDE id, e.g. codex\npeon packs ide-unbind \u003Cide> # Remove an IDE binding\npeon packs ide-bindings   # List all IDE-based bindings\npeon packs exclude add \u003Cpath> # Silence sounds & notifications for a glob or directory\npeon packs exclude remove \u003Cpath> # Stop silencing the given path\npeon packs exclude list   # List silenced paths\npeon sounds list [pack]   # List sounds in a pack, marking disabled ones\npeon sounds disable \u003Ccategory> \u003Cfile> [--pack=\u003Cname>]  # Mute a single sound within a pack\npeon sounds enable \u003Ccategory> \u003Cfile> [--pack=\u003Cname>]   # Re-enable a previously disabled sound\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 notifications position [pos]    # Get\u002Fset notification position (top-left, top-center, top-right, bottom-left, bottom-center, bottom-right)\npeon notifications dismiss [N]       # Get\u002Fset auto-dismiss time in seconds (0 = persistent)\npeon notifications label [text|reset] # Get\u002Fset project label override for notifications\npeon notifications template [key] [fmt]  # Get\u002Fset\u002Freset message templates (keys: stop, permission, error, idle, question)\npeon preview              # Play all sounds from session.start\npeon preview \u003Ccategory>   # Play all sounds from a specific category\npeon preview --list       # List all categories in the active pack\npeon mobile ntfy \u003Ctopic>  # Set up phone notifications (free)\npeon mobile off           # Disable phone notifications\npeon mobile test          # Send a test notification\npeon debug on             # Enable debug logging\npeon debug off            # Disable debug logging\npeon debug status         # Show debug state, log directory, file count, total size\npeon logs                 # Show last 50 lines of today's log\npeon logs --last N        # Show last N lines across all log files\npeon logs --session ID    # Filter today's log by session ID\npeon logs --session ID --all  # Search all log files for session ID\npeon logs --clear         # Delete all log files (with confirmation)\npeon relay --daemon       # Start audio relay (for SSH\u002Fdevcontainer)\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>` 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\n### Quickstart — `peon setup`\n\nThe fastest way to configure peon-ping is the interactive wizard:\n\n```bash\npeon setup\n```\n\nIt walks you through every common setting in one go — press **Enter** at any prompt to keep the current value:\n\n```\n  ╔══════════════════════════════════════╗\n  ║       peon-ping  setup wizard        ║\n  ╚══════════════════════════════════════╝\n\n  ── Volume ──\n  > Volume (0.0 - 1.0) (0.5):\n\n  ── Sound categories ──\n  >   Session start [on\u002Foff] (on):\n  >   Task acknowledge [on\u002Foff] (off):\n  >   Task complete [on\u002Foff] (on):\n  >   Task error [on\u002Foff] (on):\n  >   Input required (permissions, questions) [on\u002Foff] (on):\n  >   Resource limit (context compaction) [on\u002Foff] (on):\n  >   User spam (rapid prompts) [on\u002Foff] (on):\n\n  ── Notifications ──\n  > Desktop notifications [on\u002Foff] (on):\n\n  Overlay theme:\n    1) Neon (cyberpunk)\n    2) Glass (translucent)\n    3) Sakura (cherry blossom)\n    4) Jarvis (iron man)\n  > Theme [neon]:\n\n  Notification position:\n    1) Top center\n    2) Top right\n    ...\n  > Position [top-center]:\n\n  Auto-dismiss:\n    1) Persistent (click to dismiss)\n    2) 3 seconds\n    3) 4 seconds\n    ...\n  > Dismiss time [4]:\n\n  ✓ Configuration saved!\n```\n\n**What the wizard covers:**\n- **Volume** — playback volume (0.0 – 1.0)\n- **Sound categories** — enable\u002Fdisable each CESP category individually (session start, task complete, permission prompts, errors, etc.)\n- **Desktop notifications** — master switch for overlay banners\n- **Overlay theme** — choose the visual style (neon, glass, sakura, jarvis)\n- **Position** — where notifications appear (top-center, top-right, etc.)\n- **Auto-dismiss** — how long notifications stay visible (`0` = persistent, click to dismiss)\n\nWhen you're done, the wizard prints a summary and saves everything to `~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fconfig.json`. You can rerun `peon setup` anytime to tweak settings — it always shows your current values as defaults.\n\n> **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](#quick-controls) section.\n\n### Slash commands and manual config\n\npeon-ping also installs slash commands in Claude Code:\n\n- `\u002Fpeon-ping-toggle` — mute\u002Funmute sounds\n- `\u002Fpeon-ping-config` — change any setting (volume, packs, categories, etc.)\n- `\u002Fpeon-ping-rename \u003Cname>` — give this session a custom name shown in notification titles and the terminal tab title (zero tokens, hook-intercepted); no argument resets to auto-detect\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\u002Fhooks\u002Fpeon-ping\u002Fconfig.json` (default `~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fconfig.json`)\n- Local install: `.\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fconfig.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### Independent Controls\n\npeon-ping has three independent controls that can be mixed and matched:\n\n| Config Key | Controls | Affects Sounds | Affects Desktop Popups | Affects Mobile Push |\n|------------|----------|----------------|------------------------|---------------------|\n| `enabled` | Master audio switch | ✅ Yes | ❌ No | ❌ No |\n| `desktop_notifications` | Desktop popup banners | ❌ No | ✅ Yes | ❌ No |\n| `mobile_notify.enabled` | Phone push notifications | ❌ No | ❌ No | ✅ Yes |\n\nThis means you can:\n- Keep sounds but disable desktop popups: `peon notifications off`\n- Keep desktop popups but disable sounds: `peon pause`\n- Enable mobile push without desktop popups: set `desktop_notifications: false` and `mobile_notify.enabled: true`\n\n- **volume**: 0.0–1.0 (quiet enough for the office)\n- **desktop_notifications**: `true`\u002F`false` — toggle desktop notification popups independently from sounds (default: `true`). When disabled, sounds continue playing but visual popups are suppressed. Mobile notifications are unaffected.\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\u002FMSYS2. Clicking the overlay focuses your terminal (supports Ghostty, Warp, iTerm2, Zed, Terminal.app). On iTerm2, clicking focuses the correct tab\u002Fpane\u002Fwindow — not just the app.\n  - **standard**: system notifications — [`terminal-notifier`](https:\u002F\u002Fgithub.com\u002FjulienXX\u002Fterminal-notifier) \u002F `osascript` on macOS, Windows toast on WSL\u002FMSYS2. When `terminal-notifier` is installed (`brew install terminal-notifier`), clicking a standard notification focuses your terminal automatically (supports Ghostty, Warp, iTerm2, Zed, Terminal.app). On native Windows, clicking a toast notification focuses the IDE or terminal window (supports VS Code, Cursor, Windsurf, Windows Terminal, PowerShell). With multiple windows open, the notification targets the exact window that originated the event via PID-based process tree matching.\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\u002Foff (e.g. `\"session.start\": false` to disable greeting sounds)\n- **annoyed_threshold \u002F 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- **session_start_cooldown_seconds** (number, default: `30`): Deduplicates greeting sounds when multiple workspaces start at the same time (e.g. opening OpenCode or Cursor with many folders). Only the first session start plays the greeting; subsequent ones within this window stay silent. Set to `0` to disable deduplication and always play a greeting.\n- **suppress_idle_prompt_repeats** (boolean, default: `true`): Claude Code re-fires its `idle_prompt` notification every ~60s while the terminal is unfocused. peon-ping routes `idle_prompt` to `task.complete` so you still get a sound when input is needed — but without dedupe the same sound replays on every poke. When `true`, an `idle_prompt` is suppressed if a `task.complete` for the same session already fired inside `idle_prompt_suppress_window_seconds`. Set to `false` to restore the periodic nudge.\n- **idle_prompt_suppress_window_seconds** (number, default: `3600`): Window used by `suppress_idle_prompt_repeats`. After a `task.complete` fires for a session, subsequent `idle_prompt` notifications for that session stay silent for this many seconds. Set to `0` to disable the window (effectively the same as `suppress_idle_prompt_repeats: false`).\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\": \"*\u002Fwork\u002Fclient-a\u002F*\", \"pack\": \"glados\" },\n    { \"pattern\": \"*\u002Fpersonal\u002F*\",      \"pack\": \"peon\" }\n  ]\n  ```\n- **exclude_dirs**: Array of glob or directory patterns. If the current working directory matches one of these entries, **all sounds and notifications are silenced** for that invocation (the hook logs `suppressed=True reason=excluded_dir pattern=\u003Cmatch>`). Bare directory paths also match descendants, so `\"~\u002Fconductor\u002Fworkspaces\"` silences everything under that tree. Use this for noisy background agents (e.g. `CodexBar\u002FClaudeProbe`), throwaway scratch dirs, or sensitive workspaces where audio alerts are unwanted.\n  ```json\n  \"exclude_dirs\": [\n    \"~\u002Fconductor\u002Fworkspaces\",\n    \"~\u002FLibrary\u002FApplication Support\u002FCodexBar*\"\n  ]\n  ```\n- **ide_rules**: Array of `{ \"ide\": \"...\", \"pack\": \"...\" }` objects. Assigns a pack by IDE\u002Fsource after `path_rules` and before rotation\u002Fdefault fallback. First matching rule wins. Common ids: `claude`, `codex`, `cursor`, `opencode`, `kilo`, `kiro`, `gemini`, `copilot`, `windsurf`, `kimi`, `antigravity`, `amp`, `deepagents`, `openclaw`, `rovodev`.\n  ```json\n  \"ide_rules\": [\n    { \"ide\": \"codex\",  \"pack\": \"glados\" },\n    { \"ide\": \"claude\", \"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` \u002F `ide_rules`) only.\n- **pack_rotation_mode**: `\"random\"` (default), `\"round-robin\"`, or `\"session_override\"`. With `random`\u002F`round-robin`, each session picks one pack from `pack_rotation`. With `session_override`, the `\u002Fpeon-ping-use \u003Cpack>` 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- **terminal_tab_title** (boolean, default: `true`): Update the terminal tab title with the current session status (for example `● project: done`). Set to `false` if you already manage tab titles with your own shell prompt or terminal automation and only want peon-ping's sounds\u002Fnotifications.\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\u002Ffocused 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- **meeting_detect** Detects if the microphone is currently being used and temporarily suppresses the audio only until the microphone is no longer in use. Notification still appears.\n- **notification_position** (string, default: `\"top-center\"`): Where overlay notifications appear on screen. Options: `\"top-left\"`, `\"top-center\"`, `\"top-right\"`, `\"bottom-left\"`, `\"bottom-center\"`, `\"bottom-right\"`.\n- **notification_dismiss_seconds** (number, default: `4`): Auto-dismiss overlay notifications after N seconds. Set to `0` for persistent notifications that require a click to dismiss.\n- **notification_all_screens** (boolean, default: `true`): Show overlay notifications on all screens (`true`) or only the main screen (`false`). Themed overlays (`glass`, `jarvis`, `sakura`) previously only showed on one screen — existing configs with those themes are migrated to `false` automatically. macOS only.\n- **`CLAUDE_SESSION_NAME` env var**: Set before launching `claude` to give a session a custom name. Shows in both desktop notification titles and terminal tab titles. Priority over all config-based naming. Example: `CLAUDE_SESSION_NAME=\"Auth Refactor\" claude` or `export CLAUDE_SESSION_NAME=\"Feature: Auth\"` then `claude`. Each terminal gets its own title automatically since peon-ping runs as a child of that Claude instance.\n- **notification_title_override** (string, default: `\"\"`): Override the project name shown in notification titles. When empty, the project name is auto-detected from `\u002Fpeon-ping-rename` > `CLAUDE_SESSION_NAME` > `.peon-label` > `notification_title_script` > `project_name_map` > git repo name > folder name.\n- **notification_title_marker** (string, default: `\"●\"`): Character(s) shown before the project name in notification titles and terminal tab titles. Desktop notification titles use `Project` by default; terminal tab titles keep `Project: status`. Set to `\"\"` to disable. Example: `\"🔔\"`.\n- **notification_title_ide** (boolean, default: `false`): Include the normalized IDE label in desktop notification titles as `Project - IDE`. When disabled, the title stays `Project` and the message\u002Fbody carries the status\u002Fdetails.\n- **notification_title_script** (string, default: `\"\"`): Shell command run at event time to compute the project name dynamically. Receives env vars: `PEON_SESSION_ID`, `PEON_CWD`, `PEON_HOOK_EVENT`, `PEON_IDE`, `PEON_SESSION_NAME`. Use stdout (trimmed, max 50 chars); non-zero exit falls through to the next tier. `PEON_IDE` is the normalized IDE\u002Fsource id such as `codex` or `claude`. Example: `\"basename $PEON_CWD\"`.\n- **project_name_map** (object, default: `{}`): Map directory paths to custom project labels for notifications. Keys are path patterns, values are display names. Example: `{ \"\u002Fhome\u002Fuser\u002Fwork\u002Fclient-a\": \"Client A\" }`.\n- **notification_templates** (object, default: `{}`): Custom message\u002Fbody format strings for notification events. Keys are event types (`stop`, `permission`, `error`, `idle`, `question`), values are template strings with variable substitution. Available variables: `{project}`, `{ide}`, `{ide_id}`, `{summary}`, `{tool_name}`, `{status}`, `{event}`. Example: `{ \"stop\": \"{status}: {summary}\", \"permission\": \"{status}: {tool_name}\" }`.\n\n### Pack Selection Hierarchy\n\npeon-ping resolves which sound pack to use through a 6-layer hierarchy. The first layer that produces a valid, installed pack wins:\n\n| Priority | Layer | Source | How to set |\n|----------|-------|--------|------------|\n| 1 (highest) | **session_override** | Per-session assignment | `\u002Fpeon-ping-use \u003Cpack>` skill or MCP |\n| 2 | **path_rules** | Glob match on working directory | `peon packs bind` or `path_rules` in config |\n| 3 | **ide_rules** | IDE\u002Fsource match | `peon packs ide-bind` or `ide_rules` in config |\n| 4 | **pack_rotation** | Random or round-robin from a list | `pack_rotation` array + `pack_rotation_mode` in config |\n| 5 | **default_pack** | Static fallback | `peon packs use \u003Cname>` or `default_pack` in config |\n| 6 (lowest) | **hardcoded** | Built-in default | `\"peon\"` |\n\nIf a layer references a pack that is not installed, it falls through to the next layer.\nIf `exclude_dirs` matches the current working directory, the entire invocation is silenced — no sound, no notification.\n\n### Per-Project Pack Assignment (path_rules)\n\nAssign different sound packs to different projects based on directory path. Use the CLI or edit `config.json` directly.\n\n**CLI (recommended):**\n\n```bash\npeon packs bind glados                     # Bind glados to the current directory\npeon packs bind sc_kerrigan --pattern \"*\u002Fservices\u002F*\"  # Bind to a glob pattern\npeon packs bind duke_nukem --install       # Bind and install from registry if needed\npeon packs unbind                          # Remove binding for the current directory\npeon packs unbind --pattern \"*\u002Fservices\u002F*\" # Remove a specific pattern binding\npeon packs bindings                        # List all bindings\n```\n\n**Manual config:**\n\n```json\n\"path_rules\": [\n  { \"pattern\": \"*\u002Fwork\u002Fclient-a\u002F*\", \"pack\": \"glados\" },\n  { \"pattern\": \"*\u002Fpersonal\u002F*\",      \"pack\": \"peon\" },\n  { \"pattern\": \"*\u002Fservices\u002F*\",      \"pack\": \"sc_kerrigan\" }\n]\n```\n\nRules use glob matching (`*`, `?`). First matching rule wins. Path rules override `pack_rotation` and `default_pack` but are overridden by `session_override` assignments.\n\n### Per-IDE Pack Assignment (ide_rules)\n\nUse this layer when a path is noisy or shared across tools and you want a pack to follow the IDE instead.\n\n**CLI (recommended):**\n\n```bash\npeon packs ide-bind codex glados        # Use glados for Codex sessions\npeon packs ide-bind claude peon         # Use peon for Claude Code\npeon packs ide-unbind codex             # Remove one IDE rule\npeon packs ide-bindings                 # List IDE rules and recent detections\n```\n\n**Manual config:**\n\n```json\n\"ide_rules\": [\n  { \"ide\": \"codex\",  \"pack\": \"glados\" },\n  { \"ide\": \"claude\", \"pack\": \"peon\" }\n]\n```\n\n`ide_rules` run after `path_rules`.\n\n## Common Use Cases\n\n### Sounds without popups\n\nWant voice feedback but no visual distractions?\n\n```bash\npeon notifications off\n```\n\nThis keeps all sound categories playing while suppressing desktop notification banners. Mobile notifications (if configured) continue working.\n\nYou can also use the alias:\n\n```bash\npeon popups off\n```\n\n### Silent mode with notifications only\n\nWant visual alerts but no audio?\n\n```bash\npeon pause  # or set \"enabled\": false in config\n```\n\nWith `desktop_notifications: true`, you'll get popups but no sounds.\n\n### Complete silence\n\nDisable everything:\n\n```bash\npeon pause\npeon notifications off\npeon mobile off\n```\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\u002F300)\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> \u003Cexercise>` | Log reps (e.g. `log 25 pushups`) |\n| `peon trainer goal \u003Cn>` | Set uniform daily goal for all exercises |\n| `peon trainer goal \u003Cexercise> \u003Cn>` | Set uniform daily goal for one exercise |\n| `peon trainer goal \u003Cexercise> \u003Cday> \u003Cn>` | Set goal for specific day (mon, tue, etc.) |\n| `peon trainer goal \u003Cday> \u003Cn>` | Set all exercises for a specific day |\n\n### Schedule vs uniform goals\n\nExercises can have either a **uniform daily goal** (same every day) or a **per-day schedule** (different goals on different days). These are mutually exclusive:\n\n- Setting a uniform goal removes any schedule for that exercise\n- Setting a day-specific goal removes any uniform goal for that exercise\n\nDays use short names: `mon`, `tue`, `wed`, `thu`, `fri`, `sat`, `sun`\n\n```bash\npeon trainer goal pushups 300         # 300 pushups every day (uniform)\npeon trainer goal pushups mon 400     # Override: 400 on Monday (creates schedule)\npeon trainer goal squats sun 0        # Rest day for squats on Sunday\npeon trainer goal fri 150             # Light day for all exercises on Friday\n```\n\nOn rest days (goal=0), reminders are skipped and status shows `[REST DAY]`. You can still log reps on rest days if you want.\n\n### Claude Code skill\n\nIn Claude Code, you can log reps without leaving your conversation:\n\n```\n\u002Fpeon-ping-log 25 pushups\n\u002Fpeon-ping-log 30 squats\n```\n\n### Custom voice lines\n\nDrop your own audio files into `~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Ftrainer\u002Fsounds\u002F`:\n\n```\ntrainer\u002Fsounds\u002Fsession_start\u002F  # session greeting (\"Pushups first, code second! Zug zug!\")\ntrainer\u002Fsounds\u002Fremind\u002F         # reminder lines (\"Something need doing? YES. PUSHUPS.\")\ntrainer\u002Fsounds\u002Flog\u002F            # acknowledgment (\"Work work! Muscles getting bigger maybe!\")\ntrainer\u002Fsounds\u002Fcomplete\u002F       # celebration (\"Zug zug! Human finish all reps!\")\ntrainer\u002Fsounds\u002Fslacking\u002F       # disappointment (\"Peon very disappointed.\")\n```\n\nUpdate `trainer\u002Fmanifest.json` to register your sound files.\n\n## MCP server\n\npeon-ping includes an [MCP (Model Context Protocol)](https:\u002F\u002Fmodelcontextprotocol.io\u002F) 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\u002FSonOfABitch` when a build fails, `sc_kerrigan\u002FIReadYou` 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\": [\"\u002Fpath\u002Fto\u002Fpeon-ping\u002Fmcp\u002Fpeon-mcp.js\"]\n    }\n  }\n}\n```\n\nIf installed via Homebrew: `$(brew --prefix peon-ping)\u002Flibexec\u002Fmcp\u002Fpeon-mcp.js`. See [`mcp\u002FREADME.md`](mcp\u002FREADME.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\u002FSonOfABitch`, `peon\u002FPeonReady1`) |\n| **`peon-ping:\u002F\u002Fcatalog`** | Full pack catalog as an MCP Resource — client prefetches once, no repeated tool calls |\n| **`peon-ping:\u002F\u002Fpack\u002F{name}`** | Individual pack details and available sound keys |\n\nRequires Node.js 18+. Contributed by [@tag-assistant](https:\u002F\u002Fgithub.com\u002Ftag-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:\u002F\u002Fgithub.com\u002FPeonPing\u002Fopenpeon).\n\n| IDE | Status | Setup |\n|---|---|---|\n| **Claude Code** | Built-in | `curl \\| bash` install handles everything |\n| **Amp** | Adapter | `bash adapters\u002Famp.sh` \u002F `powershell adapters\u002Famp.ps1` ([setup](#amp-setup)) |\n| **Gemini CLI** | Adapter | Add hooks pointing to `adapters\u002Fgemini.sh` (or `.ps1` on Windows) ([setup](#gemini-cli-setup)) |\n| **GitHub Copilot** | Adapter | Add hooks to `.github\u002Fhooks\u002Fhooks.json` pointing to `adapters\u002Fcopilot.sh` (or `.ps1`) ([setup](#github-copilot-setup)) |\n| **OpenAI Codex** | Adapter | Install the peon-ping runtime first, then add `notify` in `~\u002F.codex\u002Fconfig.toml` pointing to `adapters\u002Fcodex.sh` (or `.ps1`) ([setup](#openai-codex-setup)) |\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 `~\u002F.claude\u002Fsettings.json` for SessionStart\u002FStop sounds. |\n| **OpenCode** | Adapter | `bash adapters\u002Fopencode.sh` \u002F `powershell adapters\u002Fopencode.ps1` ([setup](#opencode-setup)) |\n| **Kilo CLI** | Adapter | `bash adapters\u002Fkilo.sh` \u002F `powershell adapters\u002Fkilo.ps1` ([setup](#kilo-cli-setup)) |\n| **Kiro** | Adapter | Add hook entries pointing to `adapters\u002Fkiro.sh` (or `.ps1`) ([setup](#kiro-setup)) |\n| **Windsurf** | Adapter | Add hook entries pointing to `adapters\u002Fwindsurf.sh` (or `.ps1`) ([setup](#windsurf-setup)) |\n| **Google Antigravity** | Adapter | `bash adapters\u002Fantigravity.sh` \u002F `powershell adapters\u002Fantigravity.ps1`. For headless \u002F macOS LaunchAgent use, also see `bash adapters\u002Fantigravity-py.sh --install` (Python `watchdog` watcher with 25s idle threshold; requires `pip3 install watchdog`). |\n| **Kimi Code** | Adapter | `bash adapters\u002Fkimi.sh --install` \u002F `powershell adapters\u002Fkimi.ps1 -Install` ([setup](#kimi-code-setup)) |\n| **OpenClaw** | Adapter | Call `adapters\u002Fopenclaw.sh \u003Cevent>` (or `openclaw.ps1`) from your OpenClaw skill |\n| **Rovo Dev CLI** | Adapter | Auto-registered by `install.sh` if `~\u002F.rovodev` exists, or add hooks to `~\u002F.rovodev\u002Fconfig.yml` manually ([setup](#rovo-dev-cli-setup)) |\n| **DeepAgents** | Adapter | `bash adapters\u002Fdeepagents.sh` \u002F `powershell adapters\u002Fdeepagents.ps1` ([setup](#deepagents-setup)) |\n| **oh-my-pi (omp)** | Adapter | `bash adapters\u002Fomp.sh` ([setup](#oh-my-pi-omp-setup)) |\n\n> **Windows:** All adapters have native PowerShell (`.ps1`) versions. The Windows installer (`install.ps1`) copies them to `~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002F`. Filesystem watchers (Amp, Antigravity, Kimi) use .NET `FileSystemWatcher` instead of fswatch\u002Finotifywait — no extra dependencies needed.\n\n### OpenAI Codex setup\n\nCodex support uses an adapter and is not auto-registered by `peon-ping-setup`.\n\nThe Codex adapter expects the peon-ping runtime to exist at `~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002F`, even if you only use Codex and do not use Claude Code.\n\n**Setup:**\n\n1. Install the peon-ping runtime first:\n\n   ```bash\n   bash \"$(brew --prefix peon-ping)\"\u002Flibexec\u002Finstall.sh --no-rc\n   ```\n\n   Or with the standard installer:\n\n   ```bash\n   curl -fsSL https:\u002F\u002Fraw.githubusercontent.com\u002FPeonPing\u002Fpeon-ping\u002Fmain\u002Finstall.sh | bash -s -- --no-rc\n   ```\n\n2. Add this to `~\u002F.codex\u002Fconfig.toml`:\n\n   ```toml\n   notify = [\"bash\", \"~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fcodex.sh\"]\n   ```\n\n3. Restart Codex.\n\nIf you installed with Homebrew, the runtime files are managed under `~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002F`, and the Codex adapter forwards Codex notify events into that shared runtime.\n\n### Amp setup\n\nA filesystem watcher adapter for [Amp](https:\u002F\u002Fampcode.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:\u002F\u002Fpeonping.com\u002Finstall | 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 ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Famp.sh        # foreground\n   bash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Famp.sh &       # 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 `~\u002F.local\u002Fshare\u002Famp\u002Fthreads\u002F` 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` | `~\u002F.local\u002Fshare\u002Famp` | Amp data directory |\n| `AMP_THREADS_DIR` | `$AMP_DATA_DIR\u002Fthreads` | 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:\u002F\u002Fgithub.com\u002Ffeatures\u002Fcopilot) with full [CESP v1.0](https:\u002F\u002Fgithub.com\u002FPeonPing\u002Fopenpeon) conformance.\n\n**Setup:**\n\n1. Ensure peon-ping is installed (`curl -fsSL https:\u002F\u002Fpeonping.com\u002Finstall | bash`)\n\n2. Create `.github\u002Fhooks\u002Fhooks.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 ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fcopilot.sh sessionStart\"\n         }\n       ],\n       \"userPromptSubmitted\": [\n         {\n           \"type\": \"command\",\n           \"bash\": \"bash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fcopilot.sh userPromptSubmitted\"\n         }\n       ],\n       \"postToolUse\": [\n         {\n           \"type\": \"command\",\n           \"bash\": \"bash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fcopilot.sh postToolUse\"\n         }\n       ],\n       \"errorOccurred\": [\n         {\n           \"type\": \"command\",\n           \"bash\": \"bash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fcopilot.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`\u002F`paplay`\u002F`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:\u002F\u002Fopencode.ai\u002F) with full [CESP v1.0](https:\u002F\u002Fgithub.com\u002FPeonPing\u002Fopenpeon) conformance.\n\n**Quick install:**\n\n```bash\ncurl -fsSL https:\u002F\u002Fraw.githubusercontent.com\u002FPeonPing\u002Fpeon-ping\u002Fmain\u002Fadapters\u002Fopencode.sh | bash\n```\n\nThe installer copies `peon-ping.ts` to `~\u002F.config\u002Fopencode\u002Fplugins\u002F` and creates a config at `~\u002F.config\u002Fopencode\u002Fpeon-ping\u002Fconfig.json`. Packs are stored at the shared CESP path (`~\u002F.openpeon\u002Fpacks\u002F`).\n\n**Features:**\n\n- **Sound playback** via `afplay` (macOS), `pw-play`\u002F`paplay`\u002F`ffplay` (Linux) — same priority chain as the shell hook\n- **CESP event mapping** — `session.created` \u002F `session.idle` \u002F `session.error` \u002F `permission.asked` \u002F 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:\u002F\u002Fgithub.com\u002FjulienXX\u002Fterminal-notifier) \u002F `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...` \u002F `✓ project: done` \u002F `✗ 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>\n\u003Csummary>🖼️ Screenshot: desktop notifications with custom peon icon\u003C\u002Fsummary>\n\n![peon-ping OpenCode notifications](https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002Fe433f9d1-2782-44af-a176-71875f3f532c)\n\n\u003C\u002Fdetails>\n\n> **Tip:** Install `terminal-notifier` (`brew install terminal-notifier`) for richer notifications with subtitle and grouping support.\n\n\u003Cdetails>\n\u003Csummary>🎨 Optional: custom peon icon for notifications\u003C\u002Fsummary>\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:\u002F\u002Fraw.githubusercontent.com\u002FPeonPing\u002Fpeon-ping\u002Fmain\u002Fadapters\u002Fopencode\u002Fsetup-icon.sh)\n```\n\nOr if installed locally (Homebrew \u002F git clone):\n\n```bash\nbash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fopencode\u002Fsetup-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> **Future:** When [jamf\u002FNotifier](https:\u002F\u002Fgithub.com\u002Fjamf\u002FNotifier) ships to Homebrew ([#32](https:\u002F\u002Fgithub.com\u002Fjamf\u002FNotifier\u002Fissues\u002F32)), the plugin will migrate to it — Notifier has built-in `--rebrand` support, no icon hacks needed.\n\n\u003C\u002Fdetails>\n\n### Kilo CLI setup\n\nA native TypeScript plugin for [Kilo CLI](https:\u002F\u002Fgithub.com\u002Fkilocode\u002Fcli) with full [CESP v1.0](https:\u002F\u002Fgithub.com\u002FPeonPing\u002Fopenpeon) 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:\u002F\u002Fraw.githubusercontent.com\u002FPeonPing\u002Fpeon-ping\u002Fmain\u002Fadapters\u002Fkilo.sh | bash\n```\n\nThe installer copies `peon-ping.ts` to `~\u002F.config\u002Fkilo\u002Fplugins\u002F` and creates a config at `~\u002F.config\u002Fkilo\u002Fpeon-ping\u002Fconfig.json`. Packs are stored at the shared CESP path (`~\u002F.openpeon\u002Fpacks\u002F`).\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:\u002F\u002Fgithub.com\u002FPeonPing\u002Fopenpeon) conformance.\n\n**Setup:**\n\n1. Ensure peon-ping is installed (`curl -fsSL https:\u002F\u002Fpeonping.com\u002Finstall | bash`)\n\n2. Add the following hooks to your `~\u002F.gemini\u002Fsettings.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 ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fgemini.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 ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fgemini.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 ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fgemini.sh AfterTool\"\n              }\n            ]\n          }\n        ],\n        \"Notification\": [\n          {\n            \"matcher\": \"*\",\n            \"hooks\": [\n              {\n                \"name\": \"peon-notification\",\n                \"type\": \"command\",\n                \"command\": \"bash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fgemini.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 `~\u002F.codeium\u002Fwindsurf\u002Fhooks.json` (user-level) or `.windsurf\u002Fhooks.json` (workspace-level):\n\n```json\n{\n  \"hooks\": {\n    \"post_cascade_response\": [\n      { \"command\": \"bash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fwindsurf.sh post_cascade_response\", \"show_output\": false }\n    ],\n    \"pre_user_prompt\": [\n      { \"command\": \"bash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fwindsurf.sh pre_user_prompt\", \"show_output\": false }\n    ],\n    \"post_write_code\": [\n      { \"command\": \"bash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fwindsurf.sh post_write_code\", \"show_output\": false }\n    ],\n    \"post_run_command\": [\n      { \"command\": \"bash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fwindsurf.sh post_run_command\", \"show_output\": false }\n    ]\n  }\n}\n```\n\n### Kiro setup\n\nCreate `~\u002F.kiro\u002Fagents\u002Fpeon-ping.json`:\n\n```json\n{\n  \"name\": \"peon-ping\",\n  \"hooks\": {\n    \"agentSpawn\": [\n      { \"command\": \"bash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fkiro.sh\" }\n    ],\n    \"userPromptSubmit\": [\n      { \"command\": \"bash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fkiro.sh\" }\n    ],\n    \"stop\": [\n      { \"command\": \"bash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fkiro.sh\" }\n    ]\n  }\n}\n```\n\n`preToolUse`\u002F`postToolUse` are intentionally excluded — they fire on every tool call and would be extremely noisy.\n\n### Rovo Dev CLI setup\n\nA shell adapter for [Rovo Dev CLI](https:\u002F\u002Fdeveloper.atlassian.com\u002Fcloud\u002Frovo\u002F) (Atlassian) with full [CESP v1.0](https:\u002F\u002Fgithub.com\u002FPeonPing\u002Fopenpeon) conformance.\n\n**Auto-setup:**\n\nIf `~\u002F.rovodev\u002Fconfig.yml` exists when you run `install.sh` or `peon-ping-setup`, event hooks are registered automatically.\n\n**Manual setup:**\n\n1. Ensure peon-ping is installed (`curl -fsSL https:\u002F\u002Fpeonping.com\u002Finstall | bash`)\n\n2. Add to `~\u002F.rovodev\u002Fconfig.yml`:\n\n   ```yaml\n   eventHooks:\n     events:\n       - name: on_complete\n         commands:\n           - command: bash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Frovodev.sh on_complete\n       - name: on_error\n         commands:\n           - command: bash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Frovodev.sh on_error\n       - name: on_tool_permission\n         commands:\n           - command: bash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Frovodev.sh on_tool_permission\n   ```\n\n3. Restart Rovo Dev CLI for the hooks to take effect.\n\n**Event mapping:**\n\n- `on_complete` → Completion sound (*\"Work, work.\"*, *\"Job's done!\"*)\n- `on_error` → Error sound (*\"I can't do that.\"*, *\"Son of a bitch!\"*)\n- `on_tool_permission` → Permission prompt sound (*\"Something need doing?\"*, *\"Hmm?\"*)\n\n**Features:**\n\n- **Sound playback** via `afplay` (macOS), `pw-play`\u002F`paplay`\u002F`ffplay` (Linux) — same priority chain as the shell hook\n- **CESP event mapping** — Rovo Dev events map to standard CESP categories (`task.complete`, `task.error`, `input.required`)\n- **Desktop notifications** — large overlay banners by default, or standard notifications\n- **Debounce** — suppresses duplicate sounds from rapid completions\n\n### Kimi Code setup\n\nA filesystem watcher adapter for [Kimi Code CLI](https:\u002F\u002Fgithub.com\u002FMoonshotAI\u002Fkimi-cli) (MoonshotAI). Kimi Code writes Wire Mode events to `~\u002F.kimi\u002Fsessions\u002F` — this adapter watches those files as a background daemon and translates events to CESP format.\n\n```bash\n# Install (starts background daemon)\nbash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fkimi.sh --install\n\n# Check status \u002F stop\nbash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fkimi.sh --status\nbash ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fadapters\u002Fkimi.sh --uninstall\n```\n\nRequires `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.\n\n**On macOS, `--install` registers a LaunchAgent** at `~\u002FLibrary\u002FLaunchAgents\u002Fcom.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.\n\n**Kimi-only install (no Claude required):**\n\nIf you don't have Claude Code and just want peon-ping for Kimi, install with `--kimi`:\n\n```bash\ncurl -fsSL peonping.com\u002Finstall | bash -s -- --kimi\n```\n\nFiles land in `~\u002F.kimi\u002Fhooks\u002Fpeon-ping\u002F` instead of `~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002F`, and no `~\u002F.claude\u002F` directory is created. The installer also auto-detects this layout: running it with no flags on a machine that has `~\u002F.kimi\u002F` but no `~\u002F.claude\u002F` selects `--kimi` mode automatically. The watcher daemon starts during install and re-starts on every login via the LaunchAgent.\n\n**Sharing voice packs with a Claude install:**\n\nIf `~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Fpacks\u002F` already exists with packs, a `--kimi` install symlinks `~\u002F.kimi\u002Fhooks\u002Fpeon-ping\u002Fpacks` at it instead of re-downloading. One pack download serves both IDEs, and `peon packs install \u003Cname>` from either side updates the shared set. State, config, and mute toggles stay isolated per install. Pass `--no-shared-packs` (or `--packs=` \u002F `--all`) to download a separate copy.\n\n**Event mapping:**\n\n- New session → Greeting sound (*\"Ready to work?\"*, *\"Yes?\"*)\n- Agent finishes turn → Completion sound (*\"Work, work.\"*, *\"Job's done!\"*)\n- Context compaction → Token limit sound\n- Sub-agent spawned → Sub-agent tracking\n\n### oh-my-pi (omp) setup\n\nA native TypeScript extension for [oh-my-pi](https:\u002F\u002Fgithub.com\u002Fcan1357\u002Foh-my-pi) (`omp`) with full [CESP v1.0](https:\u002F\u002Fgithub.com\u002FPeonPing\u002Fopenpeon) 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\u002Fdevcontainer relay, and tab-title updates.\n\n**Quick install:**\n\n```bash\ncurl -fsSL https:\u002F\u002Fraw.githubusercontent.com\u002FPeonPing\u002Fpeon-ping\u002Fmain\u002Fadapters\u002Fomp.sh | bash\n```\n\nThe installer copies `peon-ping.ts` and `package.json` to `~\u002F.omp\u002Fagent\u002Fextensions\u002Fpeon-ping\u002F`. Restart omp afterward.\n\n**Event mapping:**\n\n| omp event                              | peon-ping event       |\n|----------------------------------------|-----------------------|\n| `session_start`                        | `SessionStart`        |\n| `turn_start`                           | `UserPromptSubmit`    |\n| `turn_end`                             | `Stop`                |\n| `tool_result` with `isError: true`     | `PostToolUseFailure`  |\n| `auto_compaction_start`                | `PreCompact`          |\n| `session_shutdown`                     | `SessionEnd`          |\n\n**Uninstall:**\n\n```bash\ncurl -fsSL https:\u002F\u002Fraw.githubusercontent.com\u002FPeonPing\u002Fpeon-ping\u002Fmain\u002Fadapters\u002Fomp.sh | bash -s -- --uninstall\n```\n\n## Remote development (SSH \u002F Devcontainers \u002F 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\nOptional SSH routing modes:\n\n```bash\npeon ssh-audio relay   # default, always use relay\npeon ssh-audio auto    # try relay, fall back to local playback on SSH host\npeon ssh-audio local   # always play on SSH host\n```\n\n### Devcontainers \u002F 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 \u002Fhealth` | Health check (returns \"OK\") |\n| `GET \u002Fplay?file=\u003Cpath>` | Play a specific sound file (legacy) |\n| `GET \u002Fplay?category=\u003Ccat>` | Play random sound from category (recommended) |\n| `POST \u002Fnotify` | Send desktop notification |\n\n**Example remote hook (`scripts\u002Fremote-hook.sh`):**\n\n```bash\n#!\u002Fbin\u002Fbash\nRELAY_URL=\"${PEON_RELAY_URL:-http:\u002F\u002F127.0.0.1:19998}\"\nEVENT=$(cat | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('hook_event_name',''))\" 2>\u002Fdev\u002Fnull)\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}\u002Fplay?category=${CATEGORY}\" >\u002Fdev\u002Fnull 2>&1 &\n```\n\nCopy this to your remote machine and register it in `~\u002F.claude\u002Fsettings.json`:\n\n```json\n{\n  \"hooks\": {\n    \"SessionStart\": [{\"command\": \"bash \u002Fpath\u002Fto\u002Fremote-hook.sh\"}],\n    \"Stop\": [{\"command\": \"bash \u002Fpath\u002Fto\u002Fremote-hook.sh\"}],\n    \"PermissionRequest\": [{\"command\": \"bash \u002Fpath\u002Fto\u002Fremote-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:\u002F\u002Fntfy.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:\u002F\u002Fpushover.net) and [Telegram](https:\u002F\u002Fcore.telegram.org\u002Fbots):\n\n```bash\npeon mobile pushover \u003Cuser_key> \u003Capp_token>\npeon mobile telegram \u003Cbot_token> \u003Cchat_id>\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\n165 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:\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 &rarr; openpeon.com\u002Fpacks](https:\u002F\u002Fopenpeon.com\u002Fpacks)**\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\u002Fcreate](https:\u002F\u002Fopenpeon.com\u002Fcreate) or [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## Debugging\n\nWhen sounds aren't playing or notifications aren't appearing, structured debug logging helps you trace exactly what happened during a hook invocation.\n\n### Enabling debug logs\n\n```bash\npeon debug on             # Enable — logs written to ~\u002F.claude\u002Fhooks\u002Fpeon-ping\u002Flogs\u002F\npeon debug off            # Disable\npeon debug status         # Show state, log directory, file count, total size\n```\n\nYou can also enable debug logging per-invocation without changing config by setting the environment variable `PEON_DEBUG=1`.\n\n### Reading logs\n\n```bash\npeon logs                 # Last 50 lines of today's log\npeon logs --last 100      # Last 100 lines across all log files\npeon logs --session \u003CID>  # Filter today's log by session ID\npeon logs --session \u003CID> --all  # Search all log files for session ID\npeon logs --clear         # Delete all log files (with confirmation)\n```\n\n### Log format\n\nEach log line is a structured key=value record:\n\n```\n2026-03-26T14:32:01.042 [config] inv=a3f1 loaded=\u002Fpath\u002Fto\u002Fconfig.json volume=0.5 pack=peon enabled=True\n2026-03-26T14:32:01.045 [event] inv=a3f1 hook_event=Stop cesp=task.complete session=abc123\n2026-03-26T14:32:01.048 [sound] inv=a3f1 file=work-work.wav label=\"Work, work.\" category=task.complete\n2026-03-26T14:32:01.120 [play] inv=a3f1 player=afplay file=work-work.wav\n2026-03-26T14:32:01.125 [notify] inv=a3f1 title=\"peon: done\" body=\"Work, work.\"\n```\n\n- **inv** -- unique 4-character invocation ID linking all phases of a single hook call\n- **Phases**: `[config","peon-ping 是一个为 Claude Code、Codex、IDEs 以及任何 AI 代理提供魔兽争霸 III 农民语音通知的工具。它通过游戏人物的语音提示和醒目的屏幕横幅提醒开发者注意 AI 编码代理的状态变化，如任务完成或需要授权等。支持包括 Claude Code、GitHub Copilot、Codex 等在内的多种 AI 开发助手，并且可以跨平台运行在 macOS、Linux、Windows 上。该项目非常适合那些希望提高编码效率、减少因频繁切换上下文而造成的时间浪费的开发者使用。",2,"2026-06-11 03:51:10","high_star"]