1实现原理 · 为什么它能做到
本体不是提示词而是真程序:peon.sh 被安装器注册为宿主的多类生命周期 hook(README 列出 SessionStart / SessionEnd / SubagentStart / Stop / Notification / PermissionRequest / PostToolUseFailure / PreCompact),事件到来时执行播放与通知逻辑(SKILL.md 只是外壳)。
`peon.sh` is a Claude Code hook registered for `SessionStart`, `SessionEnd`, `SubagentStart`, `Stop`, `Notification`, `PermissionRequest`, `PostToolUseFailure`, and `PreCompact` events. On each event:
安装器把同一命令写进用户级 ~/.claude/settings.json 的 hooks 段(--local 则写项目 ./.claude/settings.json),且只清理自己装的 peon.sh / notify.sh 条目。
echo "Updating Claude Code hooks in settings.json..."
多数 skill 是薄提示词:/peon-ping-toggle 让模型去 shell 调安装目录下的 peon.sh toggle,真正逻辑仍在程序里。
bash "${CLAUDE_CONFIG_DIR:-$HOME/.claude}"/hooks/peon-ping/peon.sh toggle
/peon-ping-use 走 hook 拦截路径而非模型路径:UserPromptSubmit hook 读到 prompt 就自己处理并阻断 LLM,零 token 完成配置改写。
The hook scripts (`scripts/hook-handle-use.sh` and `scripts/hook-handle-use.ps1`) do all the work - this SKILL.md file exists solely for discoverability in the `/` command autocomplete menu.
会话级语音包分配=hook 直接改写配置与状态文件:把 pack_rotation_mode 设为 session_override,并在 .state.json 的 session_packs 写 session→pack 映射。
config['pack_rotation_mode'] = 'session_override'
语音包不在仓库里:安装期先读自建 registry 清单,再从 raw.githubusercontent.com 逐文件拉进 <peon_dir>/packs/<pack>/,并记录 sha256 校验和。
PACK_BASE="https://raw.githubusercontent.com/$SOURCE_REPO/$SOURCE_REF/$SOURCE_PATH"
播放走各平台原生程序:macOS 首选 afplay(若装了 peon-play 二进制则改用它以支持 Sound Effects 设备),Linux 依次探测多种播放器。
local player="afplay"
桌面通知分平台实现:Linux 用 notify-send,macOS 用 terminal-notifier(缺则回退 osascript),Windows/WSL 用 PowerShell toast。
nohup notify-send --urgency="$urgency" $expire_time_flag $icon_flag "$title" "$msg" >/dev/null 2>&1 &
同一仓库还提供 MCP server:不依赖 hook,直接 spawn 系统播放器播放指定 pack/sound。
const child = spawn(cmd[0], cmd[1], { stdio: "ignore" });
SSH / devcontainer / Codespaces 场景不本地出声,而是把音频与通知请求发给本机 19998 端口的 relay 服务。
"http://${relay_host}:${relay_port}/notify" >/dev/null 2>&1 &
2核心能力
3外部依赖
| 类型 | 依赖 |
|---|---|
| network | GitHub raw(核心文件与语音包下载,main 分支未 pin) |
| network | 语音包文件下载基址(按 registry 的 source_repo/ref/path 拼接) |
| network | 自建语音包 registry 清单 |
| network | GitHub 仓库 tarball(部分适配器安装包) |
| api | ElevenLabs 声音生成 / TTS(xi-api-key 头鉴权) |
| api | MiniMax 语音合成(T2A v2,两个地区端点) |
| api | ntfy 手机推送(服务器可在配置中覆盖) |
| api | Pushover 推送 |
| api | Telegram Bot 推送 |
| network | 本机音频 / 通知 relay |
| cli | 系统通知 / 播放 / 编译依赖 |
| package | MCP SDK(Node,供 mcp/peon-mcp.js 使用) |
4风险提醒 风险提醒:橙色 · 评估后使用
- 配置驱动的命令执行面(notification_title_script) — peon.sh 会以 `shell=True` 运行 config 里的 notification_title_script 字符串并在每次事件时执行(2 秒超时)。默认空串即关闭,但一旦该键被写入(用户手改、被其它工具写入、或配置模板被替换),就等价于在宿主事件上调任意 shell 命令。
- 安装与自更新走未固定版本的 curl | bash — REPO_BASE / 自更新 URL 都指向 raw.githubusercontent.com 的 main 分支,无 pin、无签名校验,语音包的 sha256 基线同样来自该来源。上游被接管或 main 分支被改写时,用户下次更新即执行新代码——这与同类工具(例如 claude-ads 明言拒绝把远程安装器管进 shell)形成鲜明对比。
- 安装即深度改动用户环境且含自启动 — 写 ~/.claude/settings.json(多类事件 hook(README 列出 8 类)+ 两条 UserPromptSubmit hook)、~/.zshrc / ~/.bashrc / fish config、~/.local/bin 软链;kimi / antigravity 适配器还会注册 macOS LaunchAgent(开机自启)。卸载需依赖随包 uninstall 脚本。
- 凭据面较广,且含一处可疑的文件回退路径 — 除环境变量外,pack-render.py 还会去读 ~/.config/peon-ping/credentials,以及 ~/Documents/github-repos/mypersonalwebsite/.env.local(作者个人路径,普通用户不会命中,但说明凭据查找面向文件系统广泛伸展)。手机推送 token 从 config.json 读出后发往第三方服务。
- 清单可控的外链下载点 — 通知路径会拿语音包清单里的图标 URL 直接 curl(清单字段来自外部 registry);虽只下载不执行、有超时与失败即删,但仍是一个以上游清单为触发源的出网点。
- 宣称与实现的小幅落差 — 仓库有 7 个 SKILL.md 但两个安装器都只装 6 个(rename 不落盘);.env.example 注释指向的 scripts/create-pack.sh 在仓库中不存在。
5第二遍独立确认
- [ok] skill.path 的选取(skills/peon-ping-use) — 重读仓库根目录(无 SKILL.md、无 plugin.json/marketplace.json)、glob **/SKILL.md(恰 7 个,全在 skills/peon-ping-*/)、glob **/plugin.json 与 **/marketplace.json(零命中)、以及 docs/public/llms.txt 与 README 对技能的分组排序。结论:展示名是产品级/仓库级,没有单一插件入口声明,必须主观择一;选 peon-ping-use(frontmatter 带 license: MIT 与 metadata.author: PeonPing,且是会话级语音包切换的主实现)。七个技能的 name/description 原文均已取证,可零成本改选。
- [discrepancy] 仓库 7 个 SKILL.md 与安装器 6 个的矛盾 — 重读 install.sh 的 install_skill 调用段(仅 6 个:toggle/config/use/log/remix/create-pack)与 install.ps1 的技能数组(同样 6 个,无 rename)。结论:peon-ping-rename 的 SKILL.md 不会被装到用户目录,只有它的 hook 脚本被单独下载——宣称 7 个、落地 6 个。
- [ok] external_deps:raw.githubusercontent.com 系列调用点 — 分别重读 install.sh 的 REPO_BASE 定义与下载段、pack-download.sh 的 PACK_BASE 拼接、peon.sh 的自更新与 VERSION 探测、adapters/omp.sh 与 adapters/kilo.sh。全部为明文 HTTPS 拉取,**无 pin、无签名校验**(语音包的 sha256 基线也来自同一来源,故不能防上游作恶)。
- [ok] external_deps:自建 registry 与 peon.sh 内的 urllib 拉取 — 重读 install.sh 的 REGISTRY_URL 常量,并在 peon.sh 检索 urllib.request.urlopen 命中两处同一 URL;另 pack-download.sh 亦有同域请求。属自建静态清单,与包文件所在 GitHub raw 分离。
- [ok] security:hooks 写进了哪里、注册了哪些事件 — 重读 install.sh 的 hook 安装段:HOOK_CMD 指向 $GLOBAL_BASE/hooks/peon-ping/peon.sh,settings.json 两次被以 'w' 模式改写;另确认 UserPromptSubmit 的两个 hook 分别指向 hook-handle-use.sh 与 hook-handle-rename.sh。README 侧 11 类事件清单与之吻合。
- [ok] security:是否提权 / 是否碰系统目录 — 对 install.sh、peon.sh、relay.sh、scripts/、adapters/ 检索 sudo:零命中;重读 install.sh 的 chmod 与软链段确认权限动作只有 chmod +x,写入目标全部在 $HOME 之下(~/.claude、~/.local/bin、~/.zshrc、~/.bashrc、~/.config/fish、~/Library/LaunchAgents)。
- [ok] security:凭据读取清单 — 重读 pack-render.py(env + 两处文件回退 + xi-api-key 头)、tts-minimax.sh/.ps1(MINIMAX_API_KEY / GROUP_ID)、peon.sh 的手机推送凭据读段。结论:本地配置/环境变量 → 明文 HTTPS 出站到用户自选的第三方服务,无第三方中转、不上传至作者域名。
- [ok] security:配置驱动的 shell 执行 — 在 peon.sh 检索 notification_title_script 命中取值与执行两处,重读该段确认 `_sp.run(_script, shell=True, capture_output=True, text=True, timeout=2, env=_env)`;又重读 config.json 确认默认值为空串。结论:默认关闭但机制真实存在。
6结论
6daa61016ffad439…8ef376602f