Расширение Claude Code для VSCode должно проигрывать приятный звонок при уведомлении «Claude is waiting for input». Вместо этого - короткое шипение, похожее на белый шум. Ниже - история расследования на Linux Mint/Cinnamon: три ложных следа, метод поимки реального виновника без root и strace, и неожиданно простая причина.
Окружение: Linux Mint, рабочий стол Cinnamon, VSCode с расширением anthropic.claude-code. При каждом уведомлении о готовности к вводу звучит не звонок, а шипение - как будто играет случайный шум вместо звука.
Первая реакция - искать в настройках темы оформления. Все три гипотезы звучали правдоподобно, и ни одна не подтвердилась:
Тема звуков Cinnamon. У Cinnamon есть свой ключ org.cinnamon.sounds notification-file, который указывает на файл звука уведомлений (по умолчанию message.oga из темы Yaru). Поменял его через gsettings set - звук не изменился. Как выяснилось позже, этот ключ используется для OSD-всплывашек (громкость, батарея), а не для сторонних приложений.
Встроенные аудиосигналы VSCode. У VSCode есть система accessibility.signals.* - в том числе chatUserActionRequired, который логически подходит под «ждёт действия пользователя». У сигнала оказался собственный mp3-файл прямо в установке VSCode (.../accessibilitySignal/browser/media/chatUserActionRequired.mp3). Заменил файл, полностью перезапустил VSCode - шипение осталось. Значит, за это уведомление отвечает не встроенный сигнал VSCode.
Системный X11 bell. Ещё один классический источник «пиканья» в Linux - звонок X-сервера (xset b), полностью независимый от тем оформления и звуковых файлов приложений. Отключил через xset b off - не помогло.
Три гипотезы, три мимо. Пора было переставать гадать по косвенным признакам и ловить реальный процесс, который проигрывает звук.
Первая попытка - подписаться на события аудиосервера (pactl subscribe / pw-mon) и посмотреть, какое приложение создаёт новый аудиопоток в момент уведомления. Способ логичный, но не сработал: pactl не был установлен, а pw-mon (PipeWire) оказался слишком шумным - тысячи строк служебной информации об оборудовании, среди которых сложно найти нужное короткое событие.
Рабочим оказался куда более простой метод - опрос /proc без всякого root или strace:
while true; do
for pid in $(pgrep -x aplay 2>/dev/null); do
echo "cmdline: $(tr '\0' ' ' < /proc/$pid/cmdline)"
ppid=$(awk '/^PPid:/{print $2}' /proc/$pid/status)
echo "parent: $(tr '\0' ' ' < /proc/$ppid/cmdline)"
# и так далее вверх по цепочке PPid
done
sleep 0.02
done
Идея простая: раз есть подозрение, что где-то в системе коротко живёт процесс с определённым именем, достаточно опрашивать pgrep каждые 20 мс и, как только процесс появится, сразу прочитать его /proc/<pid>/cmdline и подняться по цепочке PPid до самого начала. Никакого root, никакого strace -f по всему дереву процессов - просто дешёвый polling ровно на то время, пока копится доказательство.
Через несколько секунд после срабатывания уведомления лог показал точную цепочку:
aplay /usr/share/sounds/freedesktop/stereo/bell.oga
← sh -c 'paplay .../bell.oga 2>/dev/null || aplay .../bell.oga 2>/dev/null || true'
← code --type=utility --utility-sub-type=node.mojom.NodeService ...
← code (сам VSCode)
А непосредственным родителем шелл-команды оказался не абстрактный «VSCode», а сам бинарник Claude Code CLI, встроенный внутрь расширения (resources/native-binary/claude) - то есть источник звука находится не в теме оформления и не в самом VSCode, а в логике уведомлений CLI, когда он работает без настоящего терминала (TTY) внутри расширения.
Команда, которую выполняет CLI:
sh -c 'paplay /usr/share/sounds/freedesktop/stereo/bell.oga 2>/dev/null || aplay /usr/share/sounds/freedesktop/stereo/bell.oga 2>/dev/null || true'
Логика понятная: сначала пробуем paplay (проигрыватель PulseAudio/PipeWire), если не вышло - падаем на aplay (низкоуровневый плеер ALSA). Проблема была в том, что пакет pulseaudio-utils, который предоставляет paplay, не был установлен. Команда всегда проваливалась в aplay.
А у aplay есть неочевидное ограничение: он не умеет декодировать Ogg Vorbis (.oga/.ogg). При запуске aplay file.oga он не распознаёт формат контейнера и по умолчанию трактует файл как сырой PCM-поток:
$ aplay bell.oga
Воспроизведение Сырые данные 'bell.oga': Unsigned 8 bit, Частота 8000 Гц, Моно
То есть aplay буквально проигрывает сжатые байты Ogg-файла как будто это необработанные 8-битные аудиосэмплы. Компрессированные данные, интерпретированные как сырой звук, на слух неотличимы от белого шума - это и было «шипение». Причём это объясняет, почему ни одна из первых трёх гипотез не могла сработать в принципе: какой бы .oga-файл ни лежал по этому пути, результат был бы один и тот же - потому что дело не в содержимом файла, а в том, что программа-плеер вообще не умела его читать.
Всего одна команда:
sudo apt install -y pulseaudio-utils
После установки paplay полностью декодирует Vorbis, срабатывает первым в цепочке ||, и до сломанного aplay-фолбэка дело не доходит вообще. Дополнительно можно проверить финальный результат ровно той же командой, что использует CLI:
sh -c 'paplay /usr/share/sounds/freedesktop/stereo/bell.oga || aplay /usr/share/sounds/freedesktop/stereo/bell.oga || true'
Теперь звук можно свободно менять на любой другой - например, конвертировать произвольный mp3 в Ogg Vorbis и подставить вместо системного (файл системный, root:root, нужен sudo, стоит сохранить бэкап оригинала):
sudo cp /usr/share/sounds/freedesktop/stereo/bell.oga /usr/share/sounds/freedesktop/stereo/bell.oga.bak
ffmpeg -i мой_звук.mp3 -c:a libvorbis -qscale:a 5 /tmp/new_bell.oga
sudo cp /tmp/new_bell.oga /usr/share/sounds/freedesktop/stereo/bell.oga
/proc - недооценённый инструмент. Для короткоживущих процессов не всегда нужен strace, auditd или root: pgrep в цикле с шагом 10–50 мс плюс чтение /proc/<pid>/cmdline и /proc/<pid>/status (поле PPid) достаточно, чтобы поймать процесс и восстановить всю цепочку вызовов.pulseaudio-utils) выглядело куда менее интересной причиной, чем «неправильная тема оформления», поэтому проверялось в последнюю очередь - а стоило бы в первую: команда с ||-фолбэком на менее функциональный инструмент - тревожный звоночек сам по себе.aplay не выдал ошибку и не отказался играть Ogg-файл - он молча проинтерпретировал его неправильно. Отсутствие сообщения об ошибке не гарантирует корректный результат.Комментарии и обсуждение — на интерактивной версии статьи.
Перейти к обсуждению →