Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"voice": {
"type": "string",
"title": "Voice output",
"description": "Enable voice output (on / off / true / false, default: on)",
"description": "Enable voice output (on / off / true / false, default: off)",
"sensitive": false
},
"style": {
Expand Down
6 changes: 4 additions & 2 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
![Shell](https://img.shields.io/badge/Shell-Bash-89e051?logo=gnu-bash&logoColor=white)
![GitHub Repo stars](https://img.shields.io/github/stars/chinadbo/cheerer?style=social)

Whenever Claude Code finishes a task, cheerer plays a danmaku (bullet-screen) floating-subtitle animation and a multilingual voice encouragement to make coding more fun.
Whenever Claude Code finishes a task, cheerer plays a danmaku (bullet-screen) floating-subtitle animation and a contextual encouragement message to make coding more fun. Voice output is available and can be enabled when you want spoken encouragement.

## ✨ Features

Expand Down Expand Up @@ -160,7 +160,7 @@ bash scripts/voices/cheer_ja.sh
| `CHEERER_ENABLED` | Master switch | `true` / `false` | `true` |
| `CHEERER_LANG` | Voice language | `zh` / `en` / `ja` / `ko` / `es` | `zh` |
| `CHEERER_ANIM` | Animation style | `basketball` / `dance` / `fireworks` / `rocket` / `trophy` / `wave` / `epic` / `random` | `random` |
| `CHEERER_VOICE` | Enable or disable voice | `on` / `off` / `true` / `false` | `on` |
| `CHEERER_VOICE` | Enable or disable voice | `on` / `off` / `true` / `false` | `off` |
| `CHEERER_DUMB` | Force text-only fallback or keep auto-detect | `auto` / `true` / `false` | `auto` |
| `CHEERER_MODE` | Output mode | `auto` / `full` / `text` | `auto` |
| `CHEERER_COOLDOWN` | Cooldown seconds between triggers | positive integer | `3` |
Expand All @@ -172,6 +172,8 @@ bash scripts/voices/cheer_ja.sh

`CHEERER_*` env vars override plugin settings.

Voice is opt-in on fresh installs. Leave CHEERER_VOICE unset (or set it to off) to keep cheerer text-first, and set it to on when you want spoken encouragement.

### Runtime behavior

- `CHEERER_MODE=auto` animates `TaskCompleted` hooks and keeps `Stop` hooks text-only unless `CHEERER_INTENSITY=high`.
Expand Down
8 changes: 5 additions & 3 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

**言語:** [English](README.md) | [中文](README.zh.md) | 日本語

Claude Code がタスクを完了すると、cheerer はターミナルで弾幕アニメーションと多言語の音声応援を再生し、コーディングをもっと楽しくします。
Claude Code がタスクを完了すると、cheerer はターミナルで弾幕アニメーションと応援メッセージを表示し、コーディングをもっと楽しくします。音声応援が必要なときは音声出力を有効にできます

## ✨ 主な機能

Expand Down Expand Up @@ -122,13 +122,15 @@ Claude Code が `/plugin enable cheerer` 中に設定入力を表示した場合
/plugin enable cheerer
> 音声言語(zh / en / ja / ko / es): ja
> アニメーション(random / basketball / dance / fireworks / rocket / trophy / wave / epic): random
> 音声出力(on / off): on
> 音声出力(on / off): off
> 応援スタイル(adaptive / balanced / hype / cozy): adaptive
> 応援の強さ(soft / normal / high): normal
```

入力プロンプトが表示されない場合は、同じ設定を環境変数で指定してください。

新規インストールでは音声はオプトインです。CHEERER_VOICE を未設定のままにするか off に設定するとテキスト優先のままになり、音声応援が必要な場合だけ on にしてください。

### 方法2:環境変数

`~/.bashrc` / `~/.zshrc` または `.claude/settings.json` に設定:
Expand All @@ -138,7 +140,7 @@ Claude Code が `/plugin enable cheerer` 中に設定入力を表示した場合
| `CHEERER_ENABLED` | マスタースイッチ | `true` / `false` | `true` |
| `CHEERER_LANG` | 音声言語 | `zh` / `en` / `ja` / `ko` / `es` | `zh` |
| `CHEERER_ANIM` | アニメーション | `basketball` / `dance` / `fireworks` / `rocket` / `trophy` / `wave` / `epic` / `random` | `random` |
| `CHEERER_VOICE` | 音声出力 | `on` / `off` / `true` / `false` | `on` |
| `CHEERER_VOICE` | 音声出力 | `on` / `off` / `true` / `false` | `off` |
| `CHEERER_DUMB` | テキストのみを強制するか自動判定を使う | `auto` / `true` / `false` | `auto` |
| `CHEERER_MODE` | 出力モード | `auto` / `full` / `text` | `auto` |
| `CHEERER_COOLDOWN` | トリガー間クールダウン(秒) | 正の整数 | `3` |
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

**Language:** English | [中文](README.zh.md) | [日本語](README.ja.md)

Whenever Claude Code finishes a task, cheerer plays a danmaku (bullet-screen) floating-subtitle animation and a multilingual voice encouragement to make coding more fun.
Whenever Claude Code finishes a task, cheerer plays a danmaku (bullet-screen) floating-subtitle animation and a contextual encouragement message to make coding more fun. Voice output is available and can be enabled when you want spoken encouragement.

## ✨ Features

Expand Down Expand Up @@ -122,13 +122,15 @@ If Claude Code prompts for plugin settings during `/plugin enable cheerer`, you
/plugin enable cheerer
> Voice language (zh / en / ja): zh
> Animation style (random / basketball / dance / fireworks / epic): random
> Enable voice output (on / off): on
> Enable voice output (on / off): off
> Celebration style (adaptive / balanced / hype / cozy): adaptive
> Celebration intensity (soft / normal / high): normal
```

If no prompt appears, set the same values with environment variables instead.

Voice is opt-in on fresh installs. Leave CHEERER_VOICE unset (or set it to off) to keep cheerer text-first, and set it to on when you want spoken encouragement.

### Option 2: Environment variables

Set in your shell profile (`~/.bashrc`, `~/.zshrc`) or `.claude/settings.json`:
Expand All @@ -138,7 +140,7 @@ Set in your shell profile (`~/.bashrc`, `~/.zshrc`) or `.claude/settings.json`:
| `CHEERER_ENABLED` | Master switch | `true` / `false` | `true` |
| `CHEERER_LANG` | Voice language | `zh` / `en` / `ja` / `ko` / `es` | `zh` |
| `CHEERER_ANIM` | Animation style | `basketball` / `dance` / `fireworks` / `rocket` / `trophy` / `wave` / `epic` / `random` | `random` |
| `CHEERER_VOICE` | Voice output | `on` / `off` / `true` / `false` | `on` |
| `CHEERER_VOICE` | Voice output | `on` / `off` / `true` / `false` | `off` |
| `CHEERER_DUMB` | Force text-only fallback or keep auto-detect | `auto` / `true` / `false` | `auto` |
| `CHEERER_MODE` | Output mode | `auto` / `full` / `text` | `auto` |
| `CHEERER_COOLDOWN` | Cooldown between triggers (seconds) | positive integer | `3` |
Expand Down
8 changes: 5 additions & 3 deletions README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

**语言:** [English](README.md) | 中文 | [日本語](README.ja.md)

每当 Claude Code 完成任务时,在终端播放弹幕动画 + 多语言语音鼓励,让编码更快乐!
每当 Claude Code 完成任务时,在终端播放弹幕动画与鼓励文字,让编码更快乐!开启语音后还可享受多语言语音鼓励。

## ✨ 功能

Expand Down Expand Up @@ -122,13 +122,15 @@ chmod +x ~/.cheerer/bin/cheer
/plugin enable cheerer
> 语音语言(zh / en / ja / ko / es):zh
> 动画类型(random / basketball / dance / fireworks / rocket / trophy / wave / epic):random
> 启用语音(on / off):on
> 启用语音(on / off):off
> 鼓励风格(adaptive / balanced / hype / cozy):adaptive
> 鼓励强度(soft / normal / high):normal
```

如果没有出现交互提示,也可以直接通过环境变量完成同样的配置。

语音在全新安装时默认为可选关闭状态。保持 CHEERER_VOICE 未设置(或显式设为 off)即可维持纯文本优先,需要语音鼓励时再设为 on。

### 方式二:环境变量

在 `~/.bashrc` / `~/.zshrc` 或 `.claude/settings.json` 中设置:
Expand All @@ -138,7 +140,7 @@ chmod +x ~/.cheerer/bin/cheer
| `CHEERER_ENABLED` | 主开关 | `true` / `false` | `true` |
| `CHEERER_LANG` | 语音语言 | `zh` / `en` / `ja` / `ko` / `es` | `zh` |
| `CHEERER_ANIM` | 动画类型 | `basketball` / `dance` / `fireworks` / `rocket` / `trophy` / `wave` / `epic` / `random` | `random` |
| `CHEERER_VOICE` | 语音开关 | `on` / `off` / `true` / `false` | `on` |
| `CHEERER_VOICE` | 语音开关 | `on` / `off` / `true` / `false` | `off` |
| `CHEERER_DUMB` | 强制纯文本降级或保持自动检测 | `auto` / `true` / `false` | `auto` |
| `CHEERER_MODE` | 输出模式 | `auto` / `full` / `text` | `auto` |
| `CHEERER_COOLDOWN` | 两次触发的冷却时间(秒) | 正整数 | `3` |
Expand Down
2 changes: 1 addition & 1 deletion bin/cheer
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Environment variables:
CHEERER_ENABLED Master switch (true/false, default: true)
CHEERER_LANG Voice language (zh/en/ja/ko/es, default: zh)
CHEERER_ANIM Animation style (random/[name]/epic, default: random)
CHEERER_VOICE Voice output (on/off, default: on)
CHEERER_VOICE Voice output (on/off, default: off)
CHEERER_STYLE Celebration style (adaptive/balanced/hype/cozy)
CHEERER_INTENSITY Intensity (soft/normal/high)
CHEERER_MODE Output mode (auto/full/text)
Expand Down
2 changes: 1 addition & 1 deletion scripts/lib/config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ config_apply_defaults() {
CHEERER_ENABLED="${CHEERER_ENABLED:-true}"
CHEERER_LANG="${CHEERER_LANG:-${CLAUDE_PLUGIN_OPTION_LANG:-zh}}"
CHEERER_ANIM="${CHEERER_ANIM:-${CLAUDE_PLUGIN_OPTION_ANIM:-random}}"
CHEERER_VOICE="${CHEERER_VOICE:-${CLAUDE_PLUGIN_OPTION_VOICE:-on}}"
CHEERER_VOICE="${CHEERER_VOICE:-${CLAUDE_PLUGIN_OPTION_VOICE:-off}}"
CHEERER_STYLE="${CHEERER_STYLE:-${CLAUDE_PLUGIN_OPTION_STYLE:-adaptive}}"
CHEERER_INTENSITY="${CHEERER_INTENSITY:-${CLAUDE_PLUGIN_OPTION_INTENSITY:-normal}}"
CHEERER_DUMB="${CHEERER_DUMB:-auto}"
Expand Down
2 changes: 1 addition & 1 deletion scripts/lib/render.sh
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ render_emit() {
fi

export CHEERER_DUMB="${CHEERER_DUMB:-false}"
export CHEERER_VOICE="${CHEERER_VOICE:-on}"
export CHEERER_VOICE="${CHEERER_VOICE:-off}"

if [[ -f "$voice_script" ]]; then
bash "$voice_script"
Expand Down
2 changes: 1 addition & 1 deletion scripts/voices/cheer.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ else
printf '\033[1;32m🎉 %s\033[0m\n' "$_msg"
fi

CHEERER_VOICE="${CHEERER_VOICE:-on}"
CHEERER_VOICE="${CHEERER_VOICE:-off}"
if [[ "$CHEERER_VOICE" == "off" ]] || [[ "$CHEERER_VOICE" == "false" ]]; then
exit 0
fi
Expand Down
15 changes: 15 additions & 0 deletions tests/config_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
. "$ROOT_DIR/tests/test_lib.sh"
. "$ROOT_DIR/scripts/lib/config.sh"

# ---------------------------------------------------------------------------
# test_config_apply_defaults_uses_voice_off_when_unset
# ---------------------------------------------------------------------------
test_config_apply_defaults_uses_voice_off_when_unset() {
unset CHEERER_LANG CHEERER_ANIM CHEERER_VOICE CHEERER_STYLE CHEERER_INTENSITY \
CHEERER_DUMB CHEERER_MODE CHEERER_COOLDOWN CHEERER_ANIM_DURATION \
CHEERER_EPIC CHEERER_EPIC_THRESHOLD CHEERER_ENABLED 2>/dev/null || true
unset CLAUDE_PLUGIN_OPTION_VOICE 2>/dev/null || true

config_apply_defaults

assert_eq "off" "$CHEERER_VOICE" || return 1
}

# ---------------------------------------------------------------------------
# test_config_apply_defaults_uses_plugin_options
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -187,6 +201,7 @@ test_config_print_current_lists_effective_values() {
# ---------------------------------------------------------------------------
# Run
# ---------------------------------------------------------------------------
run_test "config_apply_defaults_uses_voice_off_when_unset" test_config_apply_defaults_uses_voice_off_when_unset
run_test "config_apply_defaults_uses_plugin_options" test_config_apply_defaults_uses_plugin_options
run_test "config_apply_defaults_normalizes_invalid_values" test_config_apply_defaults_normalizes_invalid_values
run_test "config_load_file_rejects_non_cheerer_lines" test_config_load_file_rejects_non_cheerer_lines
Expand Down
47 changes: 47 additions & 0 deletions tests/integration_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ test_help_flag_shows_usage() {
assert_contains "$output" "CHEERER_LANG"
assert_contains "$output" "CHEERER_COOLDOWN"
assert_contains "$output" "CHEERER_ANIM_DURATION"
assert_contains "$output" "CHEERER_VOICE Voice output (on/off, default: off)"
}

test_config_flag_shows_values() {
Expand All @@ -545,6 +546,7 @@ test_config_flag_shows_defaults() {
local output
output="$(bash bin/cheer --config 2>&1)"
assert_contains "$output" "CHEERER_LANG=zh"
assert_contains "$output" "CHEERER_VOICE=off"
assert_contains "$output" "CHEERER_COOLDOWN=3"
assert_contains "$output" "CHEERER_ANIM_DURATION=30"
}
Expand Down Expand Up @@ -730,4 +732,49 @@ run_test "doctor_cli_reports_missing_optional_file_as_warn" test_doctor_cli_repo
run_test "doctor_cli_exits_non_zero_for_invalid_animation" test_doctor_cli_exits_non_zero_for_invalid_animation
run_test "why_cli_explains_short_task_fixture" test_why_cli_explains_short_task_fixture
run_test "why_cli_uses_installed_plugin_root_resolution" test_why_cli_uses_installed_plugin_root_resolution

test_shared_voice_script_defaults_off_when_unset() {
local tmp_dir bin_dir output
tmp_dir="$(make_tmp_dir)"
bin_dir="$tmp_dir/bin"
mkdir -p "$bin_dir"

printf '#!/bin/bash\nprintf "say-called\\n" >> "%s"\n' "$tmp_dir/voice.log" > "$bin_dir/say"
chmod +x "$bin_dir/say"

output="$(PATH="$bin_dir:$PATH" CHEERER_LANG="en" CHEERER_DUMB="true" bash scripts/voices/cheer.sh)"

assert_contains "$output" "🎉 Great work. Task complete."
[[ ! -f "$tmp_dir/voice.log" ]] || return 1
}

test_shared_voice_script_respects_explicit_on() {
local tmp_dir bin_dir i
tmp_dir="$(make_tmp_dir)"
bin_dir="$tmp_dir/bin"
mkdir -p "$bin_dir"

printf '#!/bin/bash\nsleep 0.6\nprintf "say-called\\n" > "%s"\n' "$tmp_dir/voice.log" > "$bin_dir/say"
chmod +x "$bin_dir/say"

PATH="$bin_dir:$PATH" CHEERER_LANG="en" CHEERER_DUMB="true" CHEERER_VOICE="on" bash scripts/voices/cheer.sh >/dev/null

for i in 1 2 3 4 5 6 7 8 9 10; do
[[ -f "$tmp_dir/voice.log" ]] && break
sleep 0.2
done

[[ -f "$tmp_dir/voice.log" ]] || return 1
}

run_test "shared_voice_script_defaults_off_when_unset" test_shared_voice_script_defaults_off_when_unset
run_test "shared_voice_script_respects_explicit_on" test_shared_voice_script_respects_explicit_on

test_plugin_manifest_reports_voice_default_off() {
local manifest
manifest="$(< .claude-plugin/plugin.json)"
assert_contains "$manifest" "Enable voice output (on / off / true / false, default: off)"
}

run_test "plugin_manifest_reports_voice_default_off" test_plugin_manifest_reports_voice_default_off
finish_tests
28 changes: 28 additions & 0 deletions tests/render_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -330,4 +330,32 @@ test_render_custom_message_skips_tab_only_lines() {
}

run_test "render_custom_message_skips_tab_only_lines" test_render_custom_message_skips_tab_only_lines

test_render_emit_exports_voice_off_by_default() {
local tmp_dir result
tmp_dir="$(make_tmp_dir)"
mkdir -p "$tmp_dir/voices" "$tmp_dir/animations"

cat > "$tmp_dir/voices/cheer_en.sh" <<'VOICE_EOF'
#!/bin/bash
printf 'voice=%s\n' "${CHEERER_VOICE:-unset}"
VOICE_EOF
chmod +x "$tmp_dir/voices/cheer_en.sh"

VOICE_DIR="$tmp_dir/voices"
ANIM_DIR="$tmp_dir/animations"
CHEERER_LANG="en"
unset CHEERER_VOICE 2>/dev/null || true
CHEERER_DUMB="true"
RENDER_ANIMATE="false"
IN_COOLDOWN="false"
RENDER_MESSAGE_TEXT="Text path still runs"
RENDER_MESSAGE_ID="voice_default_off"

result="$(render_emit)"

assert_contains "$result" "voice=off"
}

run_test "render_emit_exports_voice_off_by_default" test_render_emit_exports_voice_off_by_default
finish_tests
Loading