dota2-hero-sounds 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -0
- package/dist/claude-hooks.d.ts +17 -0
- package/dist/claude-hooks.js +106 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +229 -0
- package/dist/codex-hooks.d.ts +23 -0
- package/dist/codex-hooks.js +329 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.js +31 -0
- package/dist/heroes.d.ts +21 -0
- package/dist/heroes.js +50 -0
- package/dist/install-files.d.ts +26 -0
- package/dist/install-files.js +83 -0
- package/dist/opencode-plugin.d.ts +20 -0
- package/dist/opencode-plugin.js +158 -0
- package/dist/pi-extension.d.ts +22 -0
- package/dist/pi-extension.js +61 -0
- package/dist/play-sound.d.ts +29 -0
- package/dist/play-sound.js +106 -0
- package/dist/play.d.ts +6 -0
- package/dist/play.js +8 -0
- package/package.json +54 -0
- package/pi-extension/index.ts +117 -0
- package/pi-extension/package.json +17 -0
- package/sounds/attention/Vo_alchemist_alch_ability_acid_06.mp3 +0 -0
- package/sounds/attention/Vo_alchemist_alch_ability_concoc_03.mp3 +0 -0
- package/sounds/attention/Vo_alchemist_alch_ability_concoc_06.mp3 +0 -0
- package/sounds/attention/Vo_alchemist_alch_attack_05.mp3 +0 -0
- package/sounds/attention/Vo_alchemist_alch_attack_15.mp3 +0 -0
- package/sounds/attention/Vo_alchemist_alch_cast_02.mp3 +0 -0
- package/sounds/attention/Vo_alchemist_alch_move_20.mp3 +0 -0
- package/sounds/attention/Vo_alchemist_alch_move_21.mp3 +0 -0
- package/sounds/attention/Vo_antimage_anti_ability_manavoid_08.mp3 +0 -0
- package/sounds/attention/Vo_antimage_anti_cast_01.mp3 +0 -0
- package/sounds/attention/Vo_antimage_anti_magicuser_01.mp3 +0 -0
- package/sounds/attention/Vo_antimage_anti_magicuser_06.mp3 +0 -0
- package/sounds/attention/Vo_antimage_anti_resist_02.mp3 +0 -0
- package/sounds/attention/Vo_axe_axe_ability_berserk_02.mp3 +0 -0
- package/sounds/attention/Vo_axe_axe_ally_08.mp3 +0 -0
- package/sounds/attention/Vo_axe_axe_blink_02.mp3 +0 -0
- package/sounds/attention/Vo_axe_axe_blinkcull_01.mp3 +0 -0
- package/sounds/attention/Vo_axe_axe_lasthit_06.mp3 +0 -0
- package/sounds/attention/Vo_axe_axe_level_01.mp3 +0 -0
- package/sounds/attention/Vo_axe_axe_level_04.mp3 +0 -0
- package/sounds/attention/Vo_axe_axe_move_02.mp3 +0 -0
- package/sounds/attention/Vo_axe_axe_move_10.mp3 +0 -0
- package/sounds/attention/Vo_axe_axe_move_11.mp3 +0 -0
- package/sounds/attention/Vo_bloodseeker_blod_ability_rupture_04.mp3 +0 -0
- package/sounds/attention/Vo_bloodseeker_blod_attack_04.mp3 +0 -0
- package/sounds/attention/Vo_bloodseeker_blod_level_01.mp3 +0 -0
- package/sounds/attention/Vo_bounty_hunter_bount_ability_shur_02.mp3 +0 -0
- package/sounds/attention/Vo_bounty_hunter_bount_ability_track_02.mp3 +0 -0
- package/sounds/attention/Vo_bounty_hunter_bount_ability_track_04.mp3 +0 -0
- package/sounds/attention/Vo_bounty_hunter_bount_ability_windwalk_04.mp3 +0 -0
- package/sounds/attention/Vo_bounty_hunter_bount_level_01.mp3 +0 -0
- package/sounds/attention/Vo_bounty_hunter_bount_level_02.mp3 +0 -0
- package/sounds/attention/Vo_bounty_hunter_bount_level_09.mp3 +0 -0
- package/sounds/attention/Vo_brewmaster_brew_ability_drunkenhaze_02.mp3 +0 -0
- package/sounds/attention/Vo_brewmaster_brew_ability_drunkenhaze_04.mp3 +0 -0
- package/sounds/attention/Vo_brewmaster_brew_ability_drunkenhaze_06.mp3 +0 -0
- package/sounds/attention/Vo_brewmaster_brew_level_02.mp3 +0 -0
- package/sounds/attention/Vo_brewmaster_brew_move_15.mp3 +0 -0
- package/sounds/attention/Vo_bristleback_bristle_attack_01.mp3 +0 -0
- package/sounds/attention/Vo_bristleback_bristle_attack_11.mp3 +0 -0
- package/sounds/attention/Vo_bristleback_bristle_attack_21.mp3 +0 -0
- package/sounds/attention/Vo_bristleback_bristle_attack_22.mp3 +0 -0
- package/sounds/attention/Vo_bristleback_bristle_levelup_01.mp3 +0 -0
- package/sounds/attention/Vo_bristleback_bristle_nasal_goo_01.mp3 +0 -0
- package/sounds/attention/Vo_bristleback_bristle_nasal_goo_02.mp3 +0 -0
- package/sounds/attention/Vo_bristleback_bristle_nasal_goo_03.mp3 +0 -0
- package/sounds/attention/Vo_bristleback_bristle_purch_05.mp3 +0 -0
- package/sounds/attention/Vo_bristleback_bristle_quill_spray_01.mp3 +0 -0
- package/sounds/attention/Vo_bristleback_bristle_quill_spray_05.mp3 +0 -0
- package/sounds/attention/Vo_crystalmaiden_cm_levelup_02.mp3 +0 -0
- package/sounds/attention/Vo_crystalmaiden_cm_lina_07.mp3 +0 -0
- package/sounds/attention/Vo_crystalmaiden_cm_lina_09.mp3 +0 -0
- package/sounds/attention/Vo_crystalmaiden_cm_lina_11.mp3 +0 -0
- package/sounds/attention/Vo_dark_seer_dkseer_move_10.mp3 +0 -0
- package/sounds/attention/Vo_drowranger_dro_cast_01.mp3 +0 -0
- package/sounds/attention/Vo_drowranger_dro_cast_02.mp3 +0 -0
- package/sounds/attention/Vo_drowranger_dro_lasthit_02.mp3 +0 -0
- package/sounds/attention/Vo_drowranger_drow_ally_01.mp3 +0 -0
- package/sounds/attention/Vo_kunkka_kunk_ability_ghostshp_02.mp3 +0 -0
- package/sounds/attention/Vo_kunkka_kunk_ability_tidebrng_01.mp3 +0 -0
- package/sounds/attention/Vo_kunkka_kunk_ability_tidebrng_02.mp3 +0 -0
- package/sounds/attention/Vo_kunkka_kunk_ability_torrent_03.mp3 +0 -0
- package/sounds/attention/Vo_kunkka_kunk_ability_xmark_01.mp3 +0 -0
- package/sounds/attention/Vo_kunkka_kunk_cast_02-1.mp3 +0 -0
- package/sounds/attention/Vo_kunkka_kunk_cast_02.mp3 +0 -0
- package/sounds/attention/Vo_kunkka_kunk_move_02.mp3 +0 -0
- package/sounds/attention/Vo_kunkka_kunk_move_05.mp3 +0 -0
- package/sounds/attention/Vo_kunkka_kunk_move_11.mp3 +0 -0
- package/sounds/attention/Vo_lina_lina_ally_03.mp3 +0 -0
- package/sounds/attention/Vo_lina_lina_ally_05.mp3 +0 -0
- package/sounds/attention/Vo_lina_lina_cm_02.mp3 +0 -0
- package/sounds/attention/Vo_lina_lina_cm_06.mp3 +0 -0
- package/sounds/attention/Vo_lina_lina_cm_10.mp3 +0 -0
- package/sounds/attention/Vo_lina_lina_level_05.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_ability_multi_01.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_ability_multi_02.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_ability_multi_08.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_ability_multi_09.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_attack_03.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_attack_04.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_attack_05.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_cast_01.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_cast_03.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_lasthit_01.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_lasthit_09.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_laugh_14.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_level_01.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_level_02.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_level_03.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_level_05.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_level_06.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_level_07.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_move_04.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_move_11.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_move_14.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_move_19.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_move_20.mp3 +0 -0
- package/sounds/attention/Vo_ogre_magi_ogmag_move_21.mp3 +0 -0
- package/sounds/attention/Vo_omniknight_omni_ability_degaura_03.mp3 +0 -0
- package/sounds/attention/Vo_omniknight_omni_ability_degaura_06.mp3 +0 -0
- package/sounds/attention/Vo_omniknight_omni_ability_degaura_09.mp3 +0 -0
- package/sounds/attention/Vo_omniknight_omni_ability_guard_06.mp3 +0 -0
- package/sounds/attention/Vo_omniknight_omni_ability_guard_10.mp3 +0 -0
- package/sounds/attention/Vo_omniknight_omni_ability_repel_02.mp3 +0 -0
- package/sounds/attention/Vo_omniknight_omni_attack_09.mp3 +0 -0
- package/sounds/attention/Vo_omniknight_omni_cast_02.mp3 +0 -0
- package/sounds/attention/Vo_omniknight_omni_lasthit_01.mp3 +0 -0
- package/sounds/attention/Vo_omniknight_omni_level_04.mp3 +0 -0
- package/sounds/attention/Vo_omniknight_omni_level_08.mp3 +0 -0
- package/sounds/attention/Vo_omniknight_omni_move_03.mp3 +0 -0
- package/sounds/attention/Vo_omniknight_omni_move_11.mp3 +0 -0
- package/sounds/attention/Vo_pudge_pud_ability_devour_15.mp3 +0 -0
- package/sounds/attention/Vo_pudge_pud_ability_hook_03.mp3 +0 -0
- package/sounds/attention/Vo_pudge_pud_ability_hook_04.mp3 +0 -0
- package/sounds/attention/Vo_pudge_pud_ability_hook_07.mp3 +0 -0
- package/sounds/attention/Vo_pudge_pud_ability_hook_09.mp3 +0 -0
- package/sounds/attention/Vo_pudge_pud_ability_hook_10-1.mp3 +0 -0
- package/sounds/attention/Vo_pudge_pud_ability_hook_10.mp3 +0 -0
- package/sounds/attention/Vo_pudge_pud_ability_hook_miss_06.mp3 +0 -0
- package/sounds/attention/Vo_pudge_pud_ability_rot_07.mp3 +0 -0
- package/sounds/attention/Vo_pudge_pud_ability_rot_12.mp3 +0 -0
- package/sounds/attention/Vo_pudge_pud_acknow_05.mp3 +0 -0
- package/sounds/attention/Vo_pudge_pud_attack_01.mp3 +0 -0
- package/sounds/attention/Vo_pudge_pud_attack_03.mp3 +0 -0
- package/sounds/attention/Vo_pudge_pud_attack_09.mp3 +0 -0
- package/sounds/attention/Vo_pudge_pud_attack_11.mp3 +0 -0
- package/sounds/attention/Vo_pudge_pud_cast_04.mp3 +0 -0
- package/sounds/attention/Vo_pudge_pud_move_01.mp3 +0 -0
- package/sounds/attention/Vo_queenofpain_pain_ability_screamofpain_01.mp3 +0 -0
- package/sounds/attention/Vo_queenofpain_pain_ability_screamofpain_02.mp3 +0 -0
- package/sounds/attention/Vo_queenofpain_pain_ability_screamofpain_04.mp3 +0 -0
- package/sounds/attention/Vo_queenofpain_pain_ability_shadowstrike_02.mp3 +0 -0
- package/sounds/attention/Vo_queenofpain_pain_bottle_02.mp3 +0 -0
- package/sounds/attention/Vo_queenofpain_pain_cast_03.mp3 +0 -0
- package/sounds/attention/Vo_queenofpain_pain_first_02.mp3 +0 -0
- package/sounds/attention/Vo_queenofpain_pain_lasthit_01.mp3 +0 -0
- package/sounds/attention/Vo_queenofpain_pain_lasthit_05.mp3 +0 -0
- package/sounds/attention/Vo_queenofpain_pain_levelup_02.mp3 +0 -0
- package/sounds/attention/Vo_queenofpain_pain_levelup_05.mp3 +0 -0
- package/sounds/attention/Vo_queenofpain_pain_purch_02.mp3 +0 -0
- package/sounds/attention/Vo_queenofpain_pain_taunt_02.mp3 +0 -0
- package/sounds/attention/Vo_rattletrap_ratt_attack_03.mp3 +0 -0
- package/sounds/attention/Vo_rattletrap_ratt_attack_10.mp3 +0 -0
- package/sounds/attention/Vo_rattletrap_ratt_move_24.mp3 +0 -0
- package/sounds/attention/Vo_silencer_silen_ability_curse_05.mp3 +0 -0
- package/sounds/attention/Vo_silencer_silen_ability_intsteal_02.mp3 +0 -0
- package/sounds/attention/Vo_silencer_silen_ability_intsteal_03.mp3 +0 -0
- package/sounds/attention/Vo_silencer_silen_ability_intsteal_04.mp3 +0 -0
- package/sounds/attention/Vo_silencer_silen_ability_intsteal_07.mp3 +0 -0
- package/sounds/attention/Vo_silencer_silen_attack_14.mp3 +0 -0
- package/sounds/attention/Vo_silencer_silen_bottle_02.mp3 +0 -0
- package/sounds/attention/Vo_silencer_silen_bottle_05.mp3 +0 -0
- package/sounds/attention/Vo_silencer_silen_cast_02.mp3 +0 -0
- package/sounds/attention/Vo_silencer_silen_first_01.mp3 +0 -0
- package/sounds/attention/Vo_silencer_silen_lasthit_06.mp3 +0 -0
- package/sounds/attention/Vo_silencer_silen_move_04.mp3 +0 -0
- package/sounds/attention/Vo_stormspirit_ss_ability_lightning_01.mp3 +0 -0
- package/sounds/attention/Vo_stormspirit_ss_ability_lightning_04.mp3 +0 -0
- package/sounds/attention/Vo_stormspirit_ss_ability_static_04.mp3 +0 -0
- package/sounds/attention/Vo_stormspirit_ss_ability_static_06.mp3 +0 -0
- package/sounds/attention/Vo_stormspirit_ss_attack_03.mp3 +0 -0
- package/sounds/attention/Vo_stormspirit_ss_bottle_03.mp3 +0 -0
- package/sounds/attention/Vo_stormspirit_ss_level_02.mp3 +0 -0
- package/sounds/attention/Vo_stormspirit_ss_move_13.mp3 +0 -0
- package/sounds/attention/Vo_stormspirit_stormspirit_ally_01.mp3 +0 -0
- package/sounds/attention/Vo_stormspirit_stormspirit_ally_03.mp3 +0 -0
- package/sounds/attention/Vo_stormspirit_stormspirit_fastres_01.mp3 +0 -0
- package/sounds/attention/Vo_tidehunter_tide_ability_gush_02.mp3 +0 -0
- package/sounds/attention/Vo_tidehunter_tide_arcane_01.mp3 +0 -0
- package/sounds/attention/Vo_tidehunter_tide_attack_01.mp3 +0 -0
- package/sounds/attention/Vo_tidehunter_tide_attack_07.mp3 +0 -0
- package/sounds/attention/Vo_tidehunter_tide_bottle_02.mp3 +0 -0
- package/sounds/attention/Vo_tidehunter_tide_level_03.mp3 +0 -0
- package/sounds/attention/Vo_tidehunter_tide_move_01.mp3 +0 -0
- package/sounds/attention/Vo_tidehunter_tide_move_05.mp3 +0 -0
- package/sounds/attention/Vo_windrunner_wind_ability_focusfire_04.mp3 +0 -0
- package/sounds/attention/Vo_windrunner_wind_attack_04.mp3 +0 -0
- package/sounds/attention/Vo_windrunner_wind_attack_06.mp3 +0 -0
- package/sounds/attention/Vo_zuus_zeus_ally_205.mp3 +0 -0
- package/sounds/attention/Vo_zuus_zeus_mars_ally_07.mp3 +0 -0
- package/sounds/attention/Vo_zuus_zeus_mars_ally_12.mp3 +0 -0
- package/sounds/attention/Vo_zuus_zeus_mars_ally_14.mp3 +0 -0
- package/sounds/attention/Vo_zuus_zuus_ability_thunder_02.mp3 +0 -0
- package/sounds/attention/Vo_zuus_zuus_lasthit_03.mp3 +0 -0
- package/sounds/attention/Vo_zuus_zuus_level_01.mp3 +0 -0
- package/sounds/attention/Vo_zuus_zuus_level_03.mp3 +0 -0
- package/sounds/attention/Vo_zuus_zuus_purch_01.mp3 +0 -0
- package/sounds/error/Vo_antimage_anti_respawn_01.mp3 +0 -0
- package/sounds/error/Vo_antimage_anti_respawn_04.mp3 +0 -0
- package/sounds/error/Vo_axe_axe_respawn_01.mp3 +0 -0
- package/sounds/error/Vo_axe_axe_respawn_04.mp3 +0 -0
- package/sounds/error/Vo_axe_axe_respawn_08.mp3 +0 -0
- package/sounds/error/Vo_bounty_hunter_bount_respawn_02.mp3 +0 -0
- package/sounds/error/Vo_bounty_hunter_bount_respawn_04.mp3 +0 -0
- package/sounds/error/Vo_bounty_hunter_bount_respawn_05.mp3 +0 -0
- package/sounds/error/Vo_lina_lina_respawn_03.mp3 +0 -0
- package/sounds/error/Vo_lina_lina_respawn_08.mp3 +0 -0
- package/sounds/error/Vo_lina_lina_respawn_11.mp3 +0 -0
- package/sounds/error/Vo_ogre_magi_ogmag_respawn_06.mp3 +0 -0
- package/sounds/error/Vo_omniknight_omni_death_03.mp3 +0 -0
- package/sounds/error/Vo_omniknight_omni_death_06.mp3 +0 -0
- package/sounds/error/Vo_omniknight_omni_nomana_01.mp3 +0 -0
- package/sounds/error/Vo_omniknight_omni_respawn_02.mp3 +0 -0
- package/sounds/error/Vo_omniknight_omni_respawn_06.mp3 +0 -0
- package/sounds/error/Vo_omniknight_omni_respawn_08.mp3 +0 -0
- package/sounds/error/Vo_omniknight_omni_respawn_09.mp3 +0 -0
- package/sounds/error/Vo_queenofpain_pain_respawn_01.mp3 +0 -0
- package/sounds/error/Vo_queenofpain_pain_respawn_02.mp3 +0 -0
- package/sounds/error/Vo_queenofpain_pain_respawn_04.mp3 +0 -0
- package/sounds/error/Vo_queenofpain_pain_respawn_09.mp3 +0 -0
- package/sounds/error/Vo_silencer_silen_respawn_01.mp3 +0 -0
- package/sounds/error/Vo_silencer_silen_respawn_04.mp3 +0 -0
- package/sounds/error/Vo_silencer_silen_respawn_10.mp3 +0 -0
- package/sounds/error/Vo_stormspirit_ss_respawn_01.mp3 +0 -0
- package/sounds/error/Vo_stormspirit_ss_respawn_05.mp3 +0 -0
- package/sounds/error/Vo_tidehunter_tide_death_03.mp3 +0 -0
- package/sounds/error/Vo_tidehunter_tide_death_09.mp3 +0 -0
- package/sounds/error/Vo_tidehunter_tide_respawn_03.mp3 +0 -0
- package/sounds/error/Vo_windrunner_wind_respawn_02.mp3 +0 -0
- package/sounds/error/Vo_windrunner_wind_respawn_03.mp3 +0 -0
- package/sounds/error/Vo_windrunner_wind_respawn_05.mp3 +0 -0
- package/sounds/error/Vo_windrunner_wind_respawn_09.mp3 +0 -0
- package/sounds/error/Vo_zuus_zeus_mars_death_04.mp3 +0 -0
- package/sounds/error/Vo_zuus_zeus_mars_death_06_02.mp3 +0 -0
- package/sounds/error/Vo_zuus_zeus_mars_death_07_02.mp3 +0 -0
- package/sounds/error/Vo_zuus_zeus_mars_death_13_05.mp3 +0 -0
- package/sounds/error/Vo_zuus_zuus_death_08.mp3 +0 -0
- package/sounds/error/Vo_zuus_zuus_death_09.mp3 +0 -0
- package/sounds/error/Vo_zuus_zuus_respawn_01.mp3 +0 -0
- package/sounds/error/Vo_zuus_zuus_respawn_03.mp3 +0 -0
- package/sounds/error/Vo_zuus_zuus_respawn_06.mp3 +0 -0
- package/sounds/error/Vo_zuus_zuus_respawn_09.mp3 +0 -0
- package/sounds/error/Vo_zuus_zuus_respawn_10.mp3 +0 -0
- package/sounds/start/Vo_alchemist_alch_spawn_04.mp3 +0 -0
- package/sounds/start/Vo_alchemist_alch_spawn_05.mp3 +0 -0
- package/sounds/start/Vo_alchemist_alch_spawn_06.mp3 +0 -0
- package/sounds/start/Vo_antimage_anti_spawn_04.mp3 +0 -0
- package/sounds/start/Vo_axe_axe_battlebegins_01.mp3 +0 -0
- package/sounds/start/Vo_axe_axe_spawn_02.mp3 +0 -0
- package/sounds/start/Vo_axe_axe_spawn_04.mp3 +0 -0
- package/sounds/start/Vo_axe_axe_spawn_08.mp3 +0 -0
- package/sounds/start/Vo_axe_axe_spawn_10.mp3 +0 -0
- package/sounds/start/Vo_bloodseeker_blod_spawn_03.mp3 +0 -0
- package/sounds/start/Vo_bloodseeker_blod_spawn_05.mp3 +0 -0
- package/sounds/start/Vo_bloodseeker_blod_spawn_08.mp3 +0 -0
- package/sounds/start/Vo_bounty_hunter_bount_battlebegins_01.mp3 +0 -0
- package/sounds/start/Vo_brewmaster_brew_spawn_06.mp3 +0 -0
- package/sounds/start/Vo_bristleback_bristle_begin_01.mp3 +0 -0
- package/sounds/start/Vo_bristleback_bristle_begin_02.mp3 +0 -0
- package/sounds/start/Vo_bristleback_bristle_begin_03.mp3 +0 -0
- package/sounds/start/Vo_bristleback_bristle_spawn_03.mp3 +0 -0
- package/sounds/start/Vo_crystalmaiden_cm_battlebegins_01.mp3 +0 -0
- package/sounds/start/Vo_crystalmaiden_cm_spawn_02.mp3 +0 -0
- package/sounds/start/Vo_dark_seer_dkseer_spawn_03.mp3 +0 -0
- package/sounds/start/Vo_drowranger_dro_spawn_04.mp3 +0 -0
- package/sounds/start/Vo_drowranger_dro_spawn_05.mp3 +0 -0
- package/sounds/start/Vo_drowranger_drow_battlebegins_01.mp3 +0 -0
- package/sounds/start/Vo_kunkka_kunk_battlebegins_01.mp3 +0 -0
- package/sounds/start/Vo_kunkka_kunk_spawn_06.mp3 +0 -0
- package/sounds/start/Vo_kunkka_kunk_spawn_10.mp3 +0 -0
- package/sounds/start/Vo_lina_lina_battlebegins_01.mp3 +0 -0
- package/sounds/start/Vo_lina_lina_spawn_05.mp3 +0 -0
- package/sounds/start/Vo_ogre_magi_ogmag_battlebegins_01.mp3 +0 -0
- package/sounds/start/Vo_ogre_magi_ogmag_spawn_06.mp3 +0 -0
- package/sounds/start/Vo_omniknight_omni_spawn_02.mp3 +0 -0
- package/sounds/start/Vo_omniknight_omni_spawn_04.mp3 +0 -0
- package/sounds/start/Vo_omniknight_omni_spawn_05.mp3 +0 -0
- package/sounds/start/Vo_pudge_pud_spawn_04.mp3 +0 -0
- package/sounds/start/Vo_queenofpain_pain_spawn_02.mp3 +0 -0
- package/sounds/start/Vo_rattletrap_ratt_spawn_03.mp3 +0 -0
- package/sounds/start/Vo_silencer_silen_spawn_02.mp3 +0 -0
- package/sounds/start/Vo_silencer_silen_spawn_03.mp3 +0 -0
- package/sounds/start/Vo_stormspirit_ss_spawn_04.mp3 +0 -0
- package/sounds/start/Vo_stormspirit_ss_spawn_06.mp3 +0 -0
- package/sounds/start/Vo_tidehunter_tide_spawn_04.mp3 +0 -0
- package/sounds/start/Vo_windrunner_wind_spawn_06.mp3 +0 -0
- package/sounds/start/Vo_zuus_zuus_battlebegins_01.mp3 +0 -0
- package/sounds/start/Vo_zuus_zuus_battlebegins_02.mp3 +0 -0
- package/sounds/start/Vo_zuus_zuus_spawn_04.mp3 +0 -0
- package/sounds/start/Vo_zuus_zuus_spawn_05.mp3 +0 -0
- package/sounds/start/Vo_zuus_zuus_spawn_06.mp3 +0 -0
- package/sounds/start/Vo_zuus_zuus_spawn_07.mp3 +0 -0
- package/sounds/start/Vo_zuus_zuus_spawn_08.mp3 +0 -0
- package/sounds/success/Vo_alchemist_alch_kill_15.mp3 +0 -0
- package/sounds/success/Vo_alchemist_alch_rival_20.mp3 +0 -0
- package/sounds/success/Vo_antimage_anti_deny_07.mp3 +0 -0
- package/sounds/success/Vo_antimage_anti_kill_02.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_deny_02.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_deny_08.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_deny_15.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_immort_02.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_inthebag_02.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_kill_01.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_kill_06.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_kill_08.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_kill_12.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_kill_14.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_rival_01.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_rival_02.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_rival_08.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_rival_09.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_rival_10.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_rival_14.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_rival_16.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_rival_17.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_rival_19.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_rival_20.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_rival_21.mp3 +0 -0
- package/sounds/success/Vo_axe_axe_rival_22.mp3 +0 -0
- package/sounds/success/Vo_bloodseeker_blod_inthebag_01.mp3 +0 -0
- package/sounds/success/Vo_bounty_hunter_bount_deny_01.mp3 +0 -0
- package/sounds/success/Vo_bounty_hunter_bount_deny_03.mp3 +0 -0
- package/sounds/success/Vo_bounty_hunter_bount_kill_14.mp3 +0 -0
- package/sounds/success/Vo_brewmaster_brew_kill_02.mp3 +0 -0
- package/sounds/success/Vo_brewmaster_brew_kill_04.mp3 +0 -0
- package/sounds/success/Vo_bristleback_bristle_deny_10.mp3 +0 -0
- package/sounds/success/Vo_bristleback_bristle_deny_11.mp3 +0 -0
- package/sounds/success/Vo_bristleback_bristle_firstblood_02.mp3 +0 -0
- package/sounds/success/Vo_bristleback_bristle_inthebag_01.mp3 +0 -0
- package/sounds/success/Vo_bristleback_bristle_kill_02.mp3 +0 -0
- package/sounds/success/Vo_bristleback_bristle_kill_06.mp3 +0 -0
- package/sounds/success/Vo_bristleback_bristle_kill_11.mp3 +0 -0
- package/sounds/success/Vo_bristleback_bristle_rival_06.mp3 +0 -0
- package/sounds/success/Vo_bristleback_bristle_rival_46.mp3 +0 -0
- package/sounds/success/Vo_crystalmaiden_cm_kill_03.mp3 +0 -0
- package/sounds/success/Vo_crystalmaiden_cm_kill_09.mp3 +0 -0
- package/sounds/success/Vo_crystalmaiden_cm_kill_10.mp3 +0 -0
- package/sounds/success/Vo_crystalmaiden_cm_rival_02.mp3 +0 -0
- package/sounds/success/Vo_crystalmaiden_cm_rival_04.mp3 +0 -0
- package/sounds/success/Vo_crystalmaiden_cm_rival_05.mp3 +0 -0
- package/sounds/success/Vo_crystalmaiden_cm_rival_08.mp3 +0 -0
- package/sounds/success/Vo_crystalmaiden_cm_rival_10.mp3 +0 -0
- package/sounds/success/Vo_crystalmaiden_cm_rival_15.mp3 +0 -0
- package/sounds/success/Vo_drowranger_dro_deny_04.mp3 +0 -0
- package/sounds/success/Vo_drowranger_drow_kill_10.mp3 +0 -0
- package/sounds/success/Vo_kunkka_kunk_inthebag_01.mp3 +0 -0
- package/sounds/success/Vo_kunkka_kunk_kill_10.mp3 +0 -0
- package/sounds/success/Vo_kunkka_kunk_kill_13.mp3 +0 -0
- package/sounds/success/Vo_kunkka_kunk_killspecial_01.mp3 +0 -0
- package/sounds/success/Vo_kunkka_kunk_rival_10.mp3 +0 -0
- package/sounds/success/Vo_kunkka_kunk_rival_21.mp3 +0 -0
- package/sounds/success/Vo_kunkka_kunk_rival_29.mp3 +0 -0
- package/sounds/success/Vo_kunkka_kunk_rival_34.mp3 +0 -0
- package/sounds/success/Vo_lina_lina_deny_12.mp3 +0 -0
- package/sounds/success/Vo_lina_lina_kill_02.mp3 +0 -0
- package/sounds/success/Vo_lina_lina_kill_07.mp3 +0 -0
- package/sounds/success/Vo_lina_lina_kill_08.mp3 +0 -0
- package/sounds/success/Vo_lina_lina_rival_03.mp3 +0 -0
- package/sounds/success/Vo_lina_lina_rival_06.mp3 +0 -0
- package/sounds/success/Vo_lina_lina_rival_10.mp3 +0 -0
- package/sounds/success/Vo_lina_lina_rival_12.mp3 +0 -0
- package/sounds/success/Vo_lina_lina_rival_13.mp3 +0 -0
- package/sounds/success/Vo_lina_lina_rival_14.mp3 +0 -0
- package/sounds/success/Vo_lina_lina_rival_15.mp3 +0 -0
- package/sounds/success/Vo_ogre_magi_ogmag_firstblood_02.mp3 +0 -0
- package/sounds/success/Vo_ogre_magi_ogmag_kill_03.mp3 +0 -0
- package/sounds/success/Vo_ogre_magi_ogmag_kill_06.mp3 +0 -0
- package/sounds/success/Vo_ogre_magi_ogmag_kill_12.mp3 +0 -0
- package/sounds/success/Vo_ogre_magi_ogmag_rival_01.mp3 +0 -0
- package/sounds/success/Vo_ogre_magi_ogmag_rival_02.mp3 +0 -0
- package/sounds/success/Vo_ogre_magi_ogmag_rival_03.mp3 +0 -0
- package/sounds/success/Vo_ogre_magi_ogmag_rival_04.mp3 +0 -0
- package/sounds/success/Vo_ogre_magi_ogmag_rival_05.mp3 +0 -0
- package/sounds/success/Vo_ogre_magi_ogmag_rival_07.mp3 +0 -0
- package/sounds/success/Vo_ogre_magi_ogmag_rival_10.mp3 +0 -0
- package/sounds/success/Vo_ogre_magi_ogmag_rival_20.mp3 +0 -0
- package/sounds/success/Vo_omniknight_omni_deny_07.mp3 +0 -0
- package/sounds/success/Vo_omniknight_omni_immort_02.mp3 +0 -0
- package/sounds/success/Vo_omniknight_omni_kill_02.mp3 +0 -0
- package/sounds/success/Vo_omniknight_omni_kill_03.mp3 +0 -0
- package/sounds/success/Vo_omniknight_omni_kill_08.mp3 +0 -0
- package/sounds/success/Vo_pudge_pud_deny_01.mp3 +0 -0
- package/sounds/success/Vo_pudge_pud_deny_09.mp3 +0 -0
- package/sounds/success/Vo_pudge_pud_immort_02.mp3 +0 -0
- package/sounds/success/Vo_pudge_pud_rival_06.mp3 +0 -0
- package/sounds/success/Vo_queenofpain_pain_deny_01.mp3 +0 -0
- package/sounds/success/Vo_queenofpain_pain_kill_01.mp3 +0 -0
- package/sounds/success/Vo_queenofpain_pain_kill_02.mp3 +0 -0
- package/sounds/success/Vo_queenofpain_pain_kill_04.mp3 +0 -0
- package/sounds/success/Vo_queenofpain_pain_kill_05.mp3 +0 -0
- package/sounds/success/Vo_queenofpain_pain_kill_08.mp3 +0 -0
- package/sounds/success/Vo_queenofpain_pain_kill_11.mp3 +0 -0
- package/sounds/success/Vo_queenofpain_pain_rival_10.mp3 +0 -0
- package/sounds/success/Vo_silencer_silen_kill_10.mp3 +0 -0
- package/sounds/success/Vo_silencer_silen_kill_13.mp3 +0 -0
- package/sounds/success/Vo_silencer_silen_kill_14.mp3 +0 -0
- package/sounds/success/Vo_silencer_silen_rival_01.mp3 +0 -0
- package/sounds/success/Vo_silencer_silen_rival_02.mp3 +0 -0
- package/sounds/success/Vo_silencer_silen_rival_05.mp3 +0 -0
- package/sounds/success/Vo_silencer_silen_rival_11.mp3 +0 -0
- package/sounds/success/Vo_stormspirit_ss_deny_01.mp3 +0 -0
- package/sounds/success/Vo_stormspirit_ss_deny_04.mp3 +0 -0
- package/sounds/success/Vo_stormspirit_ss_kill_03.mp3 +0 -0
- package/sounds/success/Vo_stormspirit_stormspirit_rival_02.mp3 +0 -0
- package/sounds/success/Vo_stormspirit_stormspirit_rival_03.mp3 +0 -0
- package/sounds/success/Vo_stormspirit_stormspirit_rival_18.mp3 +0 -0
- package/sounds/success/Vo_tidehunter_tide_deny_01.mp3 +0 -0
- package/sounds/success/Vo_tidehunter_tide_kill_04.mp3 +0 -0
- package/sounds/success/Vo_tidehunter_tide_rival_04.mp3 +0 -0
- package/sounds/success/Vo_tidehunter_tide_rival_05.mp3 +0 -0
- package/sounds/success/Vo_tidehunter_tide_rival_19.mp3 +0 -0
- package/sounds/success/Vo_windrunner_wind_deny_01.mp3 +0 -0
- package/sounds/success/Vo_windrunner_wind_deny_04.mp3 +0 -0
- package/sounds/success/Vo_windrunner_wind_deny_07.mp3 +0 -0
- package/sounds/success/Vo_windrunner_wind_kill_06.mp3 +0 -0
- package/sounds/success/Vo_windrunner_wind_kill_09.mp3 +0 -0
- package/sounds/success/Vo_windrunner_wind_kill_11.mp3 +0 -0
- package/sounds/success/Vo_windrunner_wind_killspecial_01.mp3 +0 -0
- package/sounds/success/Vo_zuus_zeus_mars_kill_02.mp3 +0 -0
- package/sounds/success/Vo_zuus_zeus_mars_kill_06.mp3 +0 -0
- package/sounds/success/Vo_zuus_zeus_mars_kill_08.mp3 +0 -0
- package/sounds/success/Vo_zuus_zeus_mars_kill_12.mp3 +0 -0
- package/sounds/success/Vo_zuus_zeus_mars_kill_13.mp3 +0 -0
- package/sounds/success/Vo_zuus_zeus_rival_205.mp3 +0 -0
- package/sounds/success/Vo_zuus_zuus_deny_03.mp3 +0 -0
- package/sounds/success/Vo_zuus_zuus_kill_01.mp3 +0 -0
- package/sounds/success/Vo_zuus_zuus_kill_02.mp3 +0 -0
- package/sounds/success/Vo_zuus_zuus_kill_03.mp3 +0 -0
- package/sounds/success/Vo_zuus_zuus_kill_06.mp3 +0 -0
- package/sounds/success/Vo_zuus_zuus_kill_10.mp3 +0 -0
- package/sounds/success/Vo_zuus_zuus_rival_13.mp3 +0 -0
- package/sounds/success/Vo_zuus_zuus_rival_14.mp3 +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# dota2-hero-sounds
|
|
2
|
+
|
|
3
|
+
Dota 2 hero voice line notifications for [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [OpenCode](https://opencode.ai), and [Pi](https://pi.dev). Get notified with voice lines from Axe, Crystal Maiden, Pudge, Zeus, and many more when your AI coding assistant completes tasks, hits errors, or needs your attention.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx dota2-hero-sounds install
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This auto-detects which tools you have installed and configures them. Use flags to target a specific tool:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx dota2-hero-sounds install --claude # Claude Code only
|
|
15
|
+
npx dota2-hero-sounds install --opencode # OpenCode only
|
|
16
|
+
npx dota2-hero-sounds install --pi # Pi only
|
|
17
|
+
npx dota2-hero-sounds install --all # All three
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How It Works
|
|
21
|
+
|
|
22
|
+
When events occur in your coding tool, a random hero speaks:
|
|
23
|
+
|
|
24
|
+
| Event | Category | Example Lines |
|
|
25
|
+
|-------|----------|---------------|
|
|
26
|
+
| Task complete | `success` | "Yes, Axe kills you!", "Burns, doesn't it?", "Told you a storm was coming!" |
|
|
27
|
+
| Error | `error` | "An anchor around my neck!", "Okay, sure, I've made some mistakes.", "That was just a warm-up." |
|
|
28
|
+
| Needs attention | `attention` | "Come to Pudge!", "Prepare thyself.", "Agony awaits." |
|
|
29
|
+
| Session start | `start` | "Let the carnage begin!", "Storm Spirit is alive!", "Your God has arrived." |
|
|
30
|
+
|
|
31
|
+
A random sound is picked from the matching category each time. Sounds have a 3-second cooldown to prevent overlapping voice lines.
|
|
32
|
+
|
|
33
|
+
## Hero Selection
|
|
34
|
+
|
|
35
|
+
By default, sounds are picked randomly from all heroes. You can set preferred heroes:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx dota2-hero-sounds hero list # Show available heroes with sound counts
|
|
39
|
+
npx dota2-hero-sounds hero set axe pudge # Only play Axe and Pudge sounds
|
|
40
|
+
npx dota2-hero-sounds hero show # Show current preference
|
|
41
|
+
npx dota2-hero-sounds hero clear # Reset to all heroes
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Hero preferences are saved to `~/.config/dota2-sounds/config.json` and take effect immediately — no reinstall needed. If a selected hero has no sounds for a category, it falls back to all heroes for that category.
|
|
45
|
+
|
|
46
|
+
## CLI Commands
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
dota2-hero-sounds install # Install hooks/plugins
|
|
50
|
+
dota2-hero-sounds uninstall # Remove everything cleanly
|
|
51
|
+
dota2-hero-sounds test # Play one sound per category
|
|
52
|
+
dota2-hero-sounds list # Show all sounds by category
|
|
53
|
+
dota2-hero-sounds hero list # Show heroes with per-category counts
|
|
54
|
+
dota2-hero-sounds hero set # Set preferred heroes
|
|
55
|
+
dota2-hero-sounds help # Show help
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## What Gets Installed
|
|
59
|
+
|
|
60
|
+
**Claude Code** — Hooks added to `~/.claude/settings.json`:
|
|
61
|
+
- `Stop` → plays success sound
|
|
62
|
+
- `PostToolUseFailure` → plays error sound
|
|
63
|
+
- `Notification` → plays attention sound
|
|
64
|
+
- `SessionStart` → plays start sound
|
|
65
|
+
|
|
66
|
+
**OpenCode** — Plugin written to `~/.config/opencode/plugins/dota2-sounds.js`:
|
|
67
|
+
- `session.status` busy→idle transition → plays success sound
|
|
68
|
+
- `session.error` → plays error sound
|
|
69
|
+
- `permission.asked` → plays attention sound
|
|
70
|
+
- Plugin init → plays start sound (session.created fires too early)
|
|
71
|
+
|
|
72
|
+
**Pi** — Extension installed via `pi install` as a Pi package:
|
|
73
|
+
- `session_start` → plays start sound
|
|
74
|
+
- `agent_end` → plays success sound
|
|
75
|
+
- `tool_execution_end` (when `isError`) → plays error sound
|
|
76
|
+
- `session_shutdown` → plays error sound
|
|
77
|
+
- *Attention sound not available* — Pi has no permission/notification event
|
|
78
|
+
|
|
79
|
+
## Uninstall
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npx dota2-hero-sounds uninstall
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
This cleanly removes only the hooks/plugins added by this package — your other settings are untouched.
|
|
86
|
+
|
|
87
|
+
## Adding Custom Sounds
|
|
88
|
+
|
|
89
|
+
1. Add `.mp3` files to the appropriate `sounds/<category>/` subdirectory
|
|
90
|
+
2. Sound files are discovered at runtime — no code changes needed
|
|
91
|
+
3. Re-run `dota2-hero-sounds install` to update the OpenCode plugin with new sounds
|
|
92
|
+
|
|
93
|
+
Sound files should follow the naming convention `Vo_<hero>_<shortname>_<type>_<num>.mp3` to enable future hero-based filtering.
|
|
94
|
+
|
|
95
|
+
## Finding More Sounds
|
|
96
|
+
|
|
97
|
+
Browse the full collection of Dota 2 hero voice lines at the [Dota 2 Wiki Audio Category](https://dota2.fandom.com/wiki/Category:Audio). Each hero has hundreds of voice lines covering spawns, kills, abilities, and more.
|
|
98
|
+
|
|
99
|
+
## Requirements
|
|
100
|
+
|
|
101
|
+
- **macOS** (uses `afplay` for audio playback)
|
|
102
|
+
- **Node.js** 18+
|
|
103
|
+
- Claude Code, OpenCode, and/or Pi installed
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
MIT
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the path to Claude Code's global settings file.
|
|
3
|
+
*/
|
|
4
|
+
export declare function getClaudeSettingsPath(): string;
|
|
5
|
+
/**
|
|
6
|
+
* Check if Claude Code config directory exists.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isClaudeCodeAvailable(): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Install Claude Code hooks into ~/.claude/settings.json.
|
|
11
|
+
* Merges with existing hooks without overwriting user-defined ones.
|
|
12
|
+
*/
|
|
13
|
+
export declare function installClaudeHooks(playScriptPath: string): void;
|
|
14
|
+
/**
|
|
15
|
+
* Remove all dota2-hero-sounds hooks from ~/.claude/settings.json.
|
|
16
|
+
*/
|
|
17
|
+
export declare function uninstallClaudeHooks(): void;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
/**
|
|
5
|
+
* Marker used to identify hooks added by this package,
|
|
6
|
+
* so we can cleanly uninstall without touching user-defined hooks.
|
|
7
|
+
*/
|
|
8
|
+
const HOOK_MARKER = 'dota2-hero-sounds';
|
|
9
|
+
/**
|
|
10
|
+
* Get the path to Claude Code's global settings file.
|
|
11
|
+
*/
|
|
12
|
+
export function getClaudeSettingsPath() {
|
|
13
|
+
return join(homedir(), '.claude', 'settings.json');
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Check if Claude Code config directory exists.
|
|
17
|
+
*/
|
|
18
|
+
export function isClaudeCodeAvailable() {
|
|
19
|
+
return existsSync(join(homedir(), '.claude'));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Build the hooks configuration for Claude Code.
|
|
23
|
+
* @param playScriptPath - Absolute path to dist/play.js
|
|
24
|
+
*/
|
|
25
|
+
function buildHooks(playScriptPath) {
|
|
26
|
+
const makeHook = (category) => ({
|
|
27
|
+
matcher: '',
|
|
28
|
+
hooks: [
|
|
29
|
+
{
|
|
30
|
+
type: 'command',
|
|
31
|
+
command: `node "${playScriptPath}" ${category} # ${HOOK_MARKER}`,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
Stop: [makeHook('success')],
|
|
37
|
+
PostToolUseFailure: [makeHook('error')],
|
|
38
|
+
Notification: [makeHook('attention')],
|
|
39
|
+
SessionStart: [makeHook('start')],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Install Claude Code hooks into ~/.claude/settings.json.
|
|
44
|
+
* Merges with existing hooks without overwriting user-defined ones.
|
|
45
|
+
*/
|
|
46
|
+
export function installClaudeHooks(playScriptPath) {
|
|
47
|
+
const settingsPath = getClaudeSettingsPath();
|
|
48
|
+
const settingsDir = join(homedir(), '.claude');
|
|
49
|
+
// Ensure directory exists
|
|
50
|
+
if (!existsSync(settingsDir)) {
|
|
51
|
+
mkdirSync(settingsDir, { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
// Read existing settings or start fresh
|
|
54
|
+
let settings = {};
|
|
55
|
+
if (existsSync(settingsPath)) {
|
|
56
|
+
const raw = readFileSync(settingsPath, 'utf-8');
|
|
57
|
+
settings = JSON.parse(raw);
|
|
58
|
+
}
|
|
59
|
+
// Remove any existing dota2-hero-sounds hooks first (clean reinstall)
|
|
60
|
+
if (settings.hooks) {
|
|
61
|
+
for (const event of Object.keys(settings.hooks)) {
|
|
62
|
+
settings.hooks[event] = settings.hooks[event].filter((entry) => !entry.hooks.some((h) => h.command.includes(HOOK_MARKER)));
|
|
63
|
+
// Remove the event key entirely if no hooks remain
|
|
64
|
+
if (settings.hooks[event].length === 0) {
|
|
65
|
+
delete settings.hooks[event];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Merge new hooks
|
|
70
|
+
const newHooks = buildHooks(playScriptPath);
|
|
71
|
+
if (!settings.hooks) {
|
|
72
|
+
settings.hooks = {};
|
|
73
|
+
}
|
|
74
|
+
for (const [event, hookEntries] of Object.entries(newHooks)) {
|
|
75
|
+
if (!settings.hooks[event]) {
|
|
76
|
+
settings.hooks[event] = [];
|
|
77
|
+
}
|
|
78
|
+
settings.hooks[event].push(...hookEntries);
|
|
79
|
+
}
|
|
80
|
+
writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, 'utf-8');
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Remove all dota2-hero-sounds hooks from ~/.claude/settings.json.
|
|
84
|
+
*/
|
|
85
|
+
export function uninstallClaudeHooks() {
|
|
86
|
+
const settingsPath = getClaudeSettingsPath();
|
|
87
|
+
if (!existsSync(settingsPath)) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const raw = readFileSync(settingsPath, 'utf-8');
|
|
91
|
+
const settings = JSON.parse(raw);
|
|
92
|
+
if (!settings.hooks) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
for (const event of Object.keys(settings.hooks)) {
|
|
96
|
+
settings.hooks[event] = settings.hooks[event].filter((entry) => !entry.hooks.some((h) => h.command.includes(HOOK_MARKER)));
|
|
97
|
+
if (settings.hooks[event].length === 0) {
|
|
98
|
+
delete settings.hooks[event];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Remove hooks key entirely if empty
|
|
102
|
+
if (Object.keys(settings.hooks).length === 0) {
|
|
103
|
+
delete settings.hooks;
|
|
104
|
+
}
|
|
105
|
+
writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, 'utf-8');
|
|
106
|
+
}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { getClaudeSettingsPath, installClaudeHooks, isClaudeCodeAvailable, uninstallClaudeHooks, } from './claude-hooks.js';
|
|
4
|
+
import { readConfig, writeConfig } from './config.js';
|
|
5
|
+
import { filterSoundsByHeroes, getAvailableHeroes } from './heroes.js';
|
|
6
|
+
import { copyRuntimeFiles, getInstalledPiExtensionDir, getInstalledPlayScript, getInstalledSoundsDir, removeRuntimeFiles, } from './install-files.js';
|
|
7
|
+
import { getOpenCodePluginPath, installOpenCodePlugin, isOpenCodeAvailable, uninstallOpenCodePlugin, } from './opencode-plugin.js';
|
|
8
|
+
import { getPiExtensionPath, installPiExtension, isPiAvailable, uninstallPiExtension, } from './pi-extension.js';
|
|
9
|
+
import { CATEGORIES, listSounds, playSound } from './play-sound.js';
|
|
10
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
11
|
+
function sleep(ms) {
|
|
12
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
13
|
+
}
|
|
14
|
+
// ── Program ──────────────────────────────────────────────────────────
|
|
15
|
+
const program = new Command();
|
|
16
|
+
program
|
|
17
|
+
.name('dota2-hero-sounds')
|
|
18
|
+
.description('Dota 2 hero voice notifications for Claude Code, OpenCode, and Pi')
|
|
19
|
+
.version('1.0.0');
|
|
20
|
+
// ── install ──────────────────────────────────────────────────────────
|
|
21
|
+
program
|
|
22
|
+
.command('install')
|
|
23
|
+
.description('Install hooks/plugins for detected tools')
|
|
24
|
+
.option('--claude', 'Install Claude Code hooks only')
|
|
25
|
+
.option('--opencode', 'Install OpenCode plugin only')
|
|
26
|
+
.option('--pi', 'Install Pi coding agent extension only')
|
|
27
|
+
.option('--all', 'Install for all supported tools')
|
|
28
|
+
.action(async (opts) => {
|
|
29
|
+
const wantClaude = opts.claude || opts.all;
|
|
30
|
+
const wantOpenCode = opts.opencode || opts.all;
|
|
31
|
+
const wantPi = opts.pi || opts.all;
|
|
32
|
+
const autoDetect = !wantClaude && !wantOpenCode && !wantPi;
|
|
33
|
+
let installedAny = false;
|
|
34
|
+
// Copy runtime files to permanent location first
|
|
35
|
+
copyRuntimeFiles();
|
|
36
|
+
const playScriptPath = getInstalledPlayScript();
|
|
37
|
+
const soundsDir = getInstalledSoundsDir();
|
|
38
|
+
// Claude Code
|
|
39
|
+
const claudeAvailable = isClaudeCodeAvailable();
|
|
40
|
+
const shouldInstallClaude = wantClaude || (autoDetect && claudeAvailable);
|
|
41
|
+
if (shouldInstallClaude) {
|
|
42
|
+
if (!claudeAvailable) {
|
|
43
|
+
console.error('Error: Claude Code config directory (~/.claude/) not found. Is Claude Code installed?');
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
installClaudeHooks(playScriptPath);
|
|
47
|
+
console.log('[+] Claude Code hooks installed');
|
|
48
|
+
console.log(` Config: ${getClaudeSettingsPath()}`);
|
|
49
|
+
installedAny = true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// OpenCode
|
|
53
|
+
const openCodeAvailable = isOpenCodeAvailable();
|
|
54
|
+
const shouldInstallOpenCode = wantOpenCode || (autoDetect && openCodeAvailable);
|
|
55
|
+
if (shouldInstallOpenCode) {
|
|
56
|
+
if (!openCodeAvailable) {
|
|
57
|
+
console.error('Error: OpenCode config directory (~/.config/opencode/) not found. Is OpenCode installed?');
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
installOpenCodePlugin(soundsDir);
|
|
61
|
+
console.log('[+] OpenCode plugin installed');
|
|
62
|
+
console.log(` Plugin: ${getOpenCodePluginPath()}`);
|
|
63
|
+
installedAny = true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Pi
|
|
67
|
+
const piAvailable = isPiAvailable();
|
|
68
|
+
const shouldInstallPi = wantPi || (autoDetect && piAvailable);
|
|
69
|
+
if (shouldInstallPi) {
|
|
70
|
+
if (!piAvailable) {
|
|
71
|
+
console.error('Error: Pi config directory (~/.pi/agent/) not found. Is Pi installed?');
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
installPiExtension(getInstalledPiExtensionDir());
|
|
75
|
+
console.log('[+] Pi extension installed');
|
|
76
|
+
console.log(` Extension: ${getPiExtensionPath()}`);
|
|
77
|
+
installedAny = true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (!installedAny) {
|
|
81
|
+
console.log('No supported tools detected. Use --claude, --opencode, or --pi to force install.');
|
|
82
|
+
console.log('');
|
|
83
|
+
console.log('Supported tools:');
|
|
84
|
+
console.log(' Claude Code (~/.claude/)');
|
|
85
|
+
console.log(' OpenCode (~/.config/opencode/)');
|
|
86
|
+
console.log(' Pi (~/.pi/agent/)');
|
|
87
|
+
process.exitCode = 1;
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
console.log('');
|
|
91
|
+
console.log('Dota 2 Axe is ready. Good luck, have fun!');
|
|
92
|
+
});
|
|
93
|
+
// ── uninstall ────────────────────────────────────────────────────────
|
|
94
|
+
program
|
|
95
|
+
.command('uninstall')
|
|
96
|
+
.description('Remove all hooks and plugins')
|
|
97
|
+
.option('--claude', 'Remove Claude Code hooks only')
|
|
98
|
+
.option('--opencode', 'Remove OpenCode plugin only')
|
|
99
|
+
.option('--pi', 'Remove Pi extension only')
|
|
100
|
+
.action((opts) => {
|
|
101
|
+
const removeAll = !opts.claude && !opts.opencode && !opts.pi;
|
|
102
|
+
if (removeAll || opts.claude) {
|
|
103
|
+
uninstallClaudeHooks();
|
|
104
|
+
console.log('[-] Claude Code hooks removed');
|
|
105
|
+
}
|
|
106
|
+
if (removeAll || opts.opencode) {
|
|
107
|
+
uninstallOpenCodePlugin();
|
|
108
|
+
console.log('[-] OpenCode plugin removed');
|
|
109
|
+
}
|
|
110
|
+
if (removeAll || opts.pi) {
|
|
111
|
+
uninstallPiExtension();
|
|
112
|
+
console.log('[-] Pi extension removed');
|
|
113
|
+
}
|
|
114
|
+
if (removeAll) {
|
|
115
|
+
removeRuntimeFiles();
|
|
116
|
+
console.log('[-] Runtime files removed');
|
|
117
|
+
}
|
|
118
|
+
console.log('');
|
|
119
|
+
console.log('Axe is not Axe! (sounds removed)');
|
|
120
|
+
});
|
|
121
|
+
// ── test ─────────────────────────────────────────────────────────────
|
|
122
|
+
program
|
|
123
|
+
.command('test')
|
|
124
|
+
.description('Play one sound from each category')
|
|
125
|
+
.argument('[category]', 'Play a specific category only')
|
|
126
|
+
.action(async (category) => {
|
|
127
|
+
if (category) {
|
|
128
|
+
if (!listSounds()[category]) {
|
|
129
|
+
console.error(`Unknown category: ${category}`);
|
|
130
|
+
console.error(`Available: ${[...CATEGORIES].join(', ')}`);
|
|
131
|
+
process.exitCode = 1;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
console.log(`Playing: ${category}`);
|
|
135
|
+
playSound(category);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
console.log('Testing all sound categories...\n');
|
|
139
|
+
for (const cat of [...CATEGORIES]) {
|
|
140
|
+
console.log(` Playing: ${cat}`);
|
|
141
|
+
playSound(cat);
|
|
142
|
+
await sleep(2000);
|
|
143
|
+
}
|
|
144
|
+
console.log('\nDone. If you heard Axe, everything is working.');
|
|
145
|
+
});
|
|
146
|
+
// ── list ─────────────────────────────────────────────────────────────
|
|
147
|
+
program
|
|
148
|
+
.command('list')
|
|
149
|
+
.description('Show all available sounds by category')
|
|
150
|
+
.action(() => {
|
|
151
|
+
const config = readConfig();
|
|
152
|
+
const allSounds = listSounds();
|
|
153
|
+
if (config.heroes.length > 0) {
|
|
154
|
+
console.log(`Dota 2 Sound Categories (filtered: ${config.heroes.join(', ')})\n`);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
console.log('Dota 2 Sound Categories\n');
|
|
158
|
+
}
|
|
159
|
+
for (const [cat, sounds] of Object.entries(allSounds)) {
|
|
160
|
+
const filtered = config.heroes.length > 0 ? filterSoundsByHeroes(sounds, config.heroes) : sounds;
|
|
161
|
+
console.log(` ${cat}: (${filtered.length} sounds)`);
|
|
162
|
+
for (const sound of filtered) {
|
|
163
|
+
const name = sound.replace(/^.*\//, '').replace('.mp3', '');
|
|
164
|
+
console.log(` - ${name}`);
|
|
165
|
+
}
|
|
166
|
+
console.log('');
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
// ── hero ─────────────────────────────────────────────────────────────
|
|
170
|
+
const hero = program.command('hero').description('Manage hero preferences');
|
|
171
|
+
hero
|
|
172
|
+
.command('set')
|
|
173
|
+
.description('Set preferred heroes')
|
|
174
|
+
.argument('<heroes...>', 'Hero keys (e.g., axe pudge lina)')
|
|
175
|
+
.action((heroes) => {
|
|
176
|
+
const available = getAvailableHeroes();
|
|
177
|
+
const validKeys = new Set(available.map((h) => h.key));
|
|
178
|
+
const invalid = heroes.filter((h) => !validKeys.has(h));
|
|
179
|
+
if (invalid.length > 0) {
|
|
180
|
+
console.error(`Unknown hero(es): ${invalid.join(', ')}`);
|
|
181
|
+
console.error(`Run 'dota2-hero-sounds hero list' to see available heroes`);
|
|
182
|
+
process.exitCode = 1;
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
writeConfig({ heroes });
|
|
186
|
+
console.log(`Heroes set: ${heroes.join(', ')}`);
|
|
187
|
+
});
|
|
188
|
+
hero
|
|
189
|
+
.command('list')
|
|
190
|
+
.description('Show available heroes with sound counts')
|
|
191
|
+
.action(() => {
|
|
192
|
+
const heroes = getAvailableHeroes();
|
|
193
|
+
const config = readConfig();
|
|
194
|
+
const selected = new Set(config.heroes);
|
|
195
|
+
console.log('Available Heroes\n');
|
|
196
|
+
console.log(` ${'Hero'.padEnd(20)} ${'Success'.padEnd(9)} ${'Error'.padEnd(9)} ${'Attention'.padEnd(11)} ${'Start'.padEnd(9)} Total`);
|
|
197
|
+
console.log(` ${'─'.repeat(20)} ${'─'.repeat(9)} ${'─'.repeat(9)} ${'─'.repeat(11)} ${'─'.repeat(9)} ${'─'.repeat(5)}`);
|
|
198
|
+
for (const h of heroes) {
|
|
199
|
+
const marker = selected.has(h.key) ? ' *' : '';
|
|
200
|
+
console.log(` ${(h.key + marker).padEnd(20)} ${String(h.counts.success).padEnd(9)} ${String(h.counts.error).padEnd(9)} ${String(h.counts.attention).padEnd(11)} ${String(h.counts.start).padEnd(9)} ${h.total}`);
|
|
201
|
+
}
|
|
202
|
+
if (selected.size > 0) {
|
|
203
|
+
console.log(`\n * = selected`);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
hero
|
|
207
|
+
.command('show')
|
|
208
|
+
.description('Show current hero preference')
|
|
209
|
+
.action(() => {
|
|
210
|
+
const config = readConfig();
|
|
211
|
+
if (config.heroes.length === 0) {
|
|
212
|
+
console.log('No hero preference set (using all heroes)');
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
console.log(`Current heroes: ${config.heroes.join(', ')}`);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
hero
|
|
219
|
+
.command('clear')
|
|
220
|
+
.description('Clear hero preference (use all heroes)')
|
|
221
|
+
.action(() => {
|
|
222
|
+
writeConfig({ heroes: [] });
|
|
223
|
+
console.log('Hero preference cleared (using all heroes)');
|
|
224
|
+
});
|
|
225
|
+
// ── parse ────────────────────────────────────────────────────────────
|
|
226
|
+
export { program };
|
|
227
|
+
if (!process.env['VITEST']) {
|
|
228
|
+
program.parse();
|
|
229
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the path to Codex's config file.
|
|
3
|
+
*/
|
|
4
|
+
export declare function getCodexConfigPath(): string;
|
|
5
|
+
/**
|
|
6
|
+
* Get the path to Codex's legacy hooks file.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getCodexHooksPath(): string;
|
|
9
|
+
/**
|
|
10
|
+
* Check if Codex config directory exists.
|
|
11
|
+
*/
|
|
12
|
+
export declare function isCodexAvailable(): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Install Codex notifications into ~/.codex/config.toml.
|
|
15
|
+
* Current Codex exposes only the external notify command and only emits
|
|
16
|
+
* agent-turn-complete, so we install a single notify handler for success sounds.
|
|
17
|
+
*/
|
|
18
|
+
export declare function installCodexNotify(playScriptPath: string): void;
|
|
19
|
+
/**
|
|
20
|
+
* Remove all dota2-code-sounds Codex config from config.toml and clean up
|
|
21
|
+
* legacy hooks.json installs from older versions.
|
|
22
|
+
*/
|
|
23
|
+
export declare function uninstallCodexNotify(): void;
|