Skip to content

Commit dacbbaf

Browse files
committed
fix(trainer): honor pause/mute for trainer reminder sound (#528)
The trainer reminder was gated only on trainer.enabled, so it kept playing while pack sounds correctly stayed silent under peon mute / peon pause. The trainer sound now also checks PAUSED, matching the adjacent notification guards. Adds a BATS regression test. Also enriches the v2.30.0 CHANGELOG with the focus_detect (#531) and tab-color TTY fallback (#516, #527) entries that landed for this release.
1 parent ae1058f commit dacbbaf

3 files changed

Lines changed: 28 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
## v2.30.0 (2026-06-07)
1+
## v2.30.0 (2026-06-08)
22

33
### Added
4-
- **Five new native IDE adapters: Qwen Code, iFlow CLI, Trae, Kiro IDE, and ECA.** Extends "whatever AI coding agent you run, the Peon shows up natively" to five more agentic IDEs. Each ships both `.sh` and `.ps1` versions with BATS coverage, and is wired into the session-id routing so sounds attribute to the right IDE.
4+
- **Five new native IDE adapters: Qwen Code, iFlow CLI, Trae, Kiro IDE, and ECA.** Extends "whatever AI coding agent you run, the Peon shows up, natively" to five more agentic IDEs. Each ships both `.sh` and `.ps1` versions with BATS coverage, and is wired into the session-id routing so sounds attribute to the right IDE.
55
- **Qwen Code** (Alibaba) and **iFlow CLI** (iflow-ai) expose Claude-Code-style stdin-JSON hook systems with PascalCase CESP event names. The adapters are thin passthroughs that re-tag the session id (`qwen-` / `iflow-` prefix) and drop noisy per-tool-call events; iFlow additionally maps a failed `PostToolUse` to `PostToolUseFailure`. Configure in `~/.qwen/settings.json` / `~/.iflow/settings.json`.
6-
- **Trae** (ByteDance) is a VS Code-derived AI IDE with no synchronous shell-hook API, so the adapter follows the Amp/Antigravity filesystem-watcher pattern: a new session file `SessionStart`, an idle timer `Stop`. Watched paths are environment-overridable (`TRAE_DATA_DIR`, `TRAE_SESSIONS_DIR`, `TRAE_SESSION_GLOB`); Windows uses .NET `FileSystemWatcher`.
7-
- **Kiro IDE** (Amazon)distinct from the existing **Kiro CLI** adapter uses `.kiro/hooks/*.kiro.hook` Agent Hooks whose `runCommand` action passes the event name on argv with no stdin JSON. `agentStop` / `promptSubmit` / `preToolUse` / `sessionStart` map to CESP with a `kiro-ide-` session prefix.
6+
- **Trae** (ByteDance) is a VS Code-derived AI IDE with no synchronous shell-hook API, so the adapter follows the Amp/Antigravity filesystem-watcher pattern: a new session file starts `SessionStart`, an idle timer fires `Stop`. Watched paths are environment-overridable (`TRAE_DATA_DIR`, `TRAE_SESSIONS_DIR`, `TRAE_SESSION_GLOB`); Windows uses .NET `FileSystemWatcher`.
7+
- **Kiro IDE** (Amazon), distinct from the existing **Kiro CLI** adapter, uses `.kiro/hooks/*.kiro.hook` Agent Hooks whose `runCommand` action passes the event name on argv with no stdin JSON. `agentStop` / `promptSubmit` / `preToolUse` / `sessionStart` map to CESP with a `kiro-ide-` session prefix.
88
- **ECA** (Editor Code Assistant) pipes stdin JSON with the hook type also on argv; events map to CESP with a stable `eca-` session prefix derived from `db_cache_path`. Vendored first-party from the community contribution in #261.
99
- `peon.sh` and the embedded Windows runtime (generated by `install.ps1`) recognize all five via `IDE_ALIASES`, the session-id `prefix_map` / `Detect-SessionIde`, and `IDE_DISPLAY_NAMES` (`kiro-ide-` ordered before `kiro-` so the longer prefix wins). `install.sh` and `install.ps1` ship the new adapters; `README.md`, `README_zh.md`, and `docs/public/llms.txt` document setup for each.
10+
- **macOS Focus / Do Not Disturb awareness (`focus_detect`).** New opt-in config key (boolean, default `false`, macOS only). When enabled, peon reads the Focus state from the Do Not Disturb daemon's assertion store (`~/Library/DoNotDisturb/DB/Assertions.json`) and suppresses output whenever any Focus (Do Not Disturb, Work, Sleep, and so on) is active, resuming automatically when Focus ends. `focus_detect_mode` (`all` / `sound` / `notifications`) scopes what gets suppressed. Fails open so sounds keep working if the store format changes (#531, thanks @Reebz).
11+
12+
### Fixed
13+
- **Tab title and iTerm2 tab colors no longer silently drop under Claude Code.** When `peon.sh` runs as a Claude Code hook there is no controlling terminal, so writing the OSC title/color escapes to `/dev/tty` failed silently while sounds and notifications kept working. `peon.sh` now falls back to the ancestor-walked session TTY (`PEON_ENV_HOOK_TTY`) when `/dev/tty` is not writable (#516, fixed by #527, thanks @juliends).
14+
- **Peon Trainer no longer talks over `peon mute` / `peon pause`.** The trainer reminder sound was gated only on `trainer.enabled`, not the paused flag, so it kept playing while pack sounds correctly stayed silent. The trainer sound now also honors `PAUSED`, matching the adjacent notification guards (#528, thanks @incik).
1015

1116
## v2.29.0 (2026-05-29)
1217

peon.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6512,7 +6512,9 @@ else
65126512
fi
65136513

65146514
# --- Trainer reminder sound (after main sound finishes) ---
6515-
if [ -n "${TRAINER_SOUND:-}" ] && [ -f "$TRAINER_SOUND" ]; then
6515+
# Honor `peon pause` / mute: the trainer is a sound like any other, so it must
6516+
# stay silent when PAUSED is set (mirrors the notification guards below). See #528.
6517+
if [ -n "${TRAINER_SOUND:-}" ] && [ -f "$TRAINER_SOUND" ] && [ "$PAUSED" != "true" ]; then
65166518
if [ "$_PEON_SYNC" = "true" ]; then
65176519
play_sound "$TRAINER_SOUND" "$VOLUME"
65186520
# Speak trainer TTS text after trainer sound when TTS enabled

tests/trainer.bats

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,22 @@ json.dump(s, open('$TEST_DIR/.state.json', 'w'))
232232
[ "$count" = "2" ]
233233
}
234234

235+
@test "hook event skips trainer reminder when paused (#528)" {
236+
bash "$PEON_SH" trainer on
237+
touch "$TEST_DIR/.paused"
238+
python3 -c "
239+
import json, time
240+
s = json.load(open('$TEST_DIR/.state.json'))
241+
s['trainer'] = {'date': '$(date +%Y-%m-%d)', 'reps': {'pushups': 0, 'squats': 0}, 'last_reminder_ts': int(time.time()) - 3600}
242+
json.dump(s, open('$TEST_DIR/.state.json', 'w'))
243+
"
244+
run_peon '{"hook_event_name":"Stop","cwd":"/tmp/myproject","session_id":"s1","permission_mode":"default"}'
245+
[ "$PEON_EXIT" -eq 0 ]
246+
count=$(afplay_call_count)
247+
# Pause must silence everything: neither the main sound nor the trainer plays.
248+
[ "$count" = "0" ]
249+
}
250+
235251
@test "hook event skips trainer reminder when interval not elapsed" {
236252
bash "$PEON_SH" trainer on
237253
python3 -c "

0 commit comments

Comments
 (0)