anotifier / guides / windows-wsl-notifications
[ GUIDE · WINDOWS & WSL ]

Claude Code and Codex notifications on Windows and WSL

To get a Claude Code or Codex notification on Windows, run npx anotifier@latest setup in PowerShell. It wires the hooks and installs the BurntToast module, and the toast itself is sent by PowerShell 7 (pwsh). Inside WSL, run the same command inside the distribution: the toast then reaches Windows through powershell.exe interop, with no wsl-notify-send and no BurntToast. This guide covers what each path needs, what you get from it, and the usual reasons a Windows toast does not appear, including Do not disturb and Focus Assist.

Rather skip the manual setup? One command wires every agent you have installed to desktop toasts, phone push, and Slack, Discord or Telegram:

$ npx anotifier@latest setup

Updated 2026-10-16 · also see the full docs

Which road does your toast take?

Where the agent runsWhat sends the toastWhat you getWhat it needs
Windows: PowerShell, CMD, Windows Terminal, the VS Code terminalpwsh running anotifier's toast.ps1 with BurntToastThe agent's icon, a Windows notification sound, a title like my-app · Claude Code, and click-to-focusPowerShell 7 and the BurntToast module; setup installs the module
A WSL distributionpowershell.exe (or pwsh.exe) reached through Windows interop, running toast-wsl.ps1A plain toast with the title and message, shown under Windows PowerShell's name: no agent icon, custom sound or click-to-focusInterop on, and PowerShell reachable through /mnt/c or the PATH

Phone push and webhooks do not depend on any of this, and neither does the Claude Code bell (Claude Code writes it itself), so they still arrive when a toast cannot. The terminal bell for other agents is a separate path: on native Windows it also runs through pwsh.

Native Windows: PowerShell 7 and BurntToast

On Windows every toast is sent by pwsh (PowerShell 7) running anotifier's toast.ps1, which uses the BurntToast module. PowerShell 7 installs side by side with the Windows PowerShell 5.1 that ships with Windows and does not replace it. powershell.exe on its own cannot send these toasts. Install PowerShell 7 with winget, then run setup:

winget install --id Microsoft.PowerShell --source winget
npx anotifier@latest setup
npx anotifier@latest test toast

What setup does for the toast on Windows:

  • Writes the hooks for each agent it finds: Stop and Notification for Claude Code in ~/.claude/settings.json, and Stop, PermissionRequest and SessionStart for Codex in %USERPROFILE%\.codex\hooks.json. Each hook is one node command that runs anotifier's notify.mjs. Claude Code runs a hook command with Git Bash on Windows, or with PowerShell when Git Bash is not installed.
  • Checks for pwsh. If it is there, it installs BurntToast for your user with Install-Module BurntToast -Scope CurrentUser when the module is missing. Nothing is installed while a hook runs.
  • If pwsh is missing, or the BurntToast install fails, it turns the toast channel off and prints the winget command. status then shows toast: off, no backend. Phone push and webhooks stay on. Run setup again once PowerShell 7 is installed and the toast comes back on; a toast you switched off yourself stays off.
  • Each toast carries the agent's own icon and a title like my-app · Claude Code.
  • Sounds are the Windows notification sounds: Default, IM, Mail, Reminder, SMS, Alarm. Finished tasks play IM; approvals and questions play Reminder. Change them per event with events.<event>.toastSound.
  • A toast has 7 seconds to send. If BurntToast is missing or fails, you hear the Windows exclamation sound instead of seeing a toast, and the failure is logged to ~/.anotifier/errors.log as toast:windows with the cause (pwsh not found, exit code 3, or timed out after 7000 ms).

Click-to-focus

Clicking a native toast brings the terminal or editor that ran the agent to the front, so an approval prompt is one click away. When the hook knows the project name or working directory, the toast carries an agentfocus:// link, and on the first toast anotifier registers a handler for it under HKCU\Software\Classes\agentfocus. Clicking the toast runs pwsh to choose a window:

  1. It looks at visible windows whose title contains the project folder name.
  2. Among those it prefers one whose title also names a terminal (Windows Terminal, PowerShell, cmd, Warp, WezTerm and similar), then a Visual Studio Code window, then a Cursor window, then any other match.
  3. If no title matches, it falls back to the window that toast.ps1 found by walking up the process tree from the hook, as long as that window still exists.

So a custom terminal tab title that drops the folder name sends the click to the fallback window. To turn click-to-focus off:

{ "toast": { "clickToFocus": false } }

Inside WSL: how the toast crosses into Windows

A Linux process in WSL cannot draw a Windows notification, so anotifier asks Windows to do it. It detects WSL from the kernel version string, /proc/version, the WSL_INTEROP and WSL_DISTRO_NAME variables, or other WSL markers (Docker containers are not mistaken for WSL) and uses the Windows path instead of notify-send. Microsoft documents the mechanism it relies on: WSL can run Windows tools from the Linux command line as tool-name.exe. The steps:

  1. wslpath -w converts the path of anotifier's toast-wsl.ps1 into a Windows path such as \\wsl.localhost\Ubuntu\…, so Windows PowerShell can open a file that lives inside the distribution.
  2. It runs a PowerShell with -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File and the title and message as typed parameters, never -Command. It tries these in order until one works: /mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe, then powershell.exe on the PATH, then /mnt/c/Program Files/PowerShell/7/pwsh.exe, then pwsh.exe on the PATH.
  3. toast-wsl.ps1 calls the Windows toast API directly, using PowerShell's own registered app identity. That is why it needs no BurntToast and shows under Windows PowerShell's name.

The result is deliberately simple: the title and the message, with no per-agent icon, custom sound or click-to-focus. Each attempt has 7 seconds. A failure is logged to ~/.anotifier/errors.log as toast:wsl; when none of the four works, the entry lists each one it tried.

  • Run npx anotifier@latest setup inside the distribution. Agents in WSL read their settings from the Linux home directory, so a Windows-side setup does not wire them. Claude Code's own setup page says the same for the agent: install and launch claude inside the WSL terminal, not from PowerShell or CMD.
  • Interop must be on. It is by default: the [interop] section of /etc/wsl.conf has enabled (default true) and appendWindowsPath (default true). With enabled = false there is no route to a toast. With only appendWindowsPath = false, the two absolute /mnt/c paths above do not depend on the PATH, so they are still tried while C: is mounted at /mnt/c.
  • Changes to wsl.conf apply only after WSL has fully stopped. Run wsl --shutdown from PowerShell, then open the distribution again.
  • setup looks for a reachable PowerShell. If it finds none, it turns the toast channel off and tells you to enable interop. Run setup again once it works.

Do you need wsl-notify-send?

No. anotifier never calls wsl-notify-send. It runs PowerShell directly, so there is nothing to download and nothing to put on your PATH. wsl-notify-send is a separate prebuilt Windows executable that accepts a few notify-send options. It is useful when you write your own hook and want a drop-in notify-send inside WSL. The wsl-notify-send guide compares it with calling powershell.exe yourself, shows the toast script, and wires either one to Claude Code and Codex.

Both give the same kind of toast: a title and a message under a generic app name. The native Windows road is the one with the icon, sound and click-to-focus.

WSLg and WSL notifications

WSLg is Microsoft's support for running Linux GUI applications on Windows. Microsoft's documentation lists it for WSL 2 on Windows 10 build 19044 or later and on Windows 11, and describes GUI apps, not desktop notifications. anotifier does not depend on it. The toast is raised by Windows through interop, so you do not need WSLg, a notification daemon or D-Bus inside the distribution for it to work.

Do not disturb and Focus Assist

A common reason for Toast sent with nothing on screen is Windows itself holding the banner back. Windows 11 calls the feature Do not disturb (Focus assist on older builds). While it is on, Windows shows banners only for alarms, reminders and the apps you allow, and sends everything else to the notification center, so a missed toast is usually waiting there. anotifier does not detect it, and test toast reports Toast sent whenever the PowerShell call succeeds.

  • Turn it off from the notification center (the bell icon), or allow the sender under Settings > System > Notifications, in the Do not disturb Apps list (Add apps).
  • It can switch on by itself. Under Turn on do not disturb automatically, Windows lists rules for certain times of day, when duplicating the display, when playing a game, when using an app in full-screen mode, and for the first hour after a Windows feature update.
  • Per-app settings matter too. On Settings > System > Notifications, under Notifications from apps and other senders, open the sender and check that notifications and Show notification banners are on, and Play a sound if you want the ding. For a WSL toast the sender is Windows PowerShell.

Windows toast not showing? Fixes by symptom

Start with anotifier status, which prints the last 8 hook errors from ~/.anotifier/errors.log, then match what you see:

SymptomLikely causeFix
toast:windows, pwsh not foundPowerShell 7 is not installed or not on the PATHwinget install --id Microsoft.PowerShell --source winget, then npx anotifier@latest setup
toast:windows, exit code 3, and a Windows exclamation sound instead of a bannerBurntToast is missing or failed to load or send; the cause is in the log's stderrInstall-Module BurntToast -Scope CurrentUser, or run setup again
toast:windows, timed out after 7000 mspwsh did not finish in the 7 second windowRun anotifier test toast again and check whether it succeeds
doctor fails with an execution policy messageA MachinePolicy or UserPolicy of Restricted or AllSigned is set by Group Policyanotifier passes -ExecutionPolicy Bypass, which covers every other scope. Microsoft documents that Group Policy overrides the policies set in PowerShell, so ask your administrator
status shows toast: off, no backendsetup turned the toast channel off because PowerShell 7, BurntToast, or the WSL route was missing. test toast ignores that switch, so it can pass while real alerts are offInstall the missing piece and run setup again
Toast sent, no bannerDo not disturb, or the sender's notification settingsSee the Do not disturb section above
The test works, the real alert does notThe hook is not wired, the agent was started before setup, or notifications are snoozedRestart the agent, run status, and use the troubleshooting guide
WSL: toast:wsl, no Windows PowerShell reachable through WSL interopInterop is off, or C: is not mounted at /mnt/c and PowerShell is not on the PATHSet [interop] enabled=true in /etc/wsl.conf, run wsl --shutdown from Windows, reopen the distribution
WSL: wslpath could not translate the toast script pathwslpath is missing, so the script path cannot be handed to WindowsRun anotifier inside a WSL distribution, where wslpath ships
Nothing at all from an agent that runs in WSLSetup was run on Windows, not in the distributionRun npx anotifier@latest setup inside WSL, and status on both sides to see which one has the hooks

"Terminal process wsl.exe terminated with exit code: -1"

This message is not an anotifier error. VS Code prints "The terminal process … terminated with exit code" when the shell it launched exits, and here the shell is wsl.exe. anotifier never starts wsl.exe: on Windows it runs pwsh, and from inside WSL it runs powershell.exe, and its own failures go to errors.log as toast:windows or toast:wsl.

VS Code's terminal troubleshooting page covers the WSL case. Run wsl --status and wsl --list --verbose in PowerShell outside the editor to see whether WSL starts at all. Use a real Linux distribution rather than a Docker-managed one such as docker-desktop-data, set the default with wsl --set-default "<distribution-name>", and update a terminal profile that names a distribution with -d. That page documents exit code 1 for WSL and does not explain -1, so this guide cannot tell you what -1 means. If WSL itself starts but the toast does not appear, the table above applies.

Send a test toast

npx anotifier@latest test toast   # real toast: prints Toast sent or Toast failed
npx anotifier@latest doctor       # PowerShell 7, BurntToast, execution policy (WSL: wslpath, PowerShell)
npx anotifier@latest status       # wired agents, the toast backend and the last 8 errors
  • test toast uses the same backend as the hooks and exits with code 1 when it fails. It skips the agent's hook, snooze and quiet hours, and it ignores toast.enabled, so it can pass while real alerts are off. status shows whether the toast channel is on.
  • On Windows, doctor fails when PowerShell 7 is missing, warns when BurntToast is not installed, and fails on a blocking Group Policy execution policy. In WSL it checks for wslpath and a reachable Windows PowerShell. doctor --deep has no read-back on Windows or WSL, so test toast is the real delivery check.
  • To test BurntToast without anotifier, run pwsh -NoProfile -Command "New-BurntToastNotification -Text 'Test','Hello'". To test the WSL road by hand, use the script in the wsl-notify-send guide.
  • If the test works and real alerts do not, the problem is between the agent and anotifier. Restart the agent so it reloads its hooks, and see the troubleshooting guide.

Related Windows guides: Claude Code notification sounds on Windows and WSL, Codex CLI notifications on Windows, and Cursor notifications on Windows and WSL.

Windows & WSL: common questions

Why don't I get a Claude Code notification on Windows?

Claude Code runs hooks on its Stop and Notification events, and something has to turn them into a toast. Run npx anotifier@latest setup in PowerShell: it wires the hooks and installs BurntToast, and the toast is sent by PowerShell 7. If nothing appears, run anotifier test toast, then check anotifier status for a toast:windows error, and check that Do not disturb is off.

Does it work in WSL2?

Yes. Run npx anotifier@latest setup inside the WSL distribution where the agent runs. anotifier detects WSL and sends the toast through powershell.exe and Windows interop, which is on by default. The toast is plain: a title and a message under Windows PowerShell's name, with no agent icon, custom sound or click-to-focus.

Do I need wsl-notify-send or BurntToast inside WSL?

No. anotifier does not use wsl-notify-send, and the WSL toast calls the Windows toast API directly through Windows PowerShell, so it needs no BurntToast either. BurntToast is only used for native Windows toasts, and setup installs it there. wsl-notify-send is an option if you write your own hooks.

Why is a Windows toast silent or missing even though the test says Toast sent?

Usually Do not disturb, called Focus Assist on older Windows. While it is on, Windows shows banners only for alarms, reminders and the apps you allow, and sends the rest to the notification center. It can also switch on by itself, for example when an app is full screen. anotifier does not detect it. Turn it off, or allow the sender in Settings > System > Notifications.

What does "terminal process wsl.exe terminated with exit code -1" mean?

It is a VS Code terminal message, not an anotifier error: the wsl.exe shell VS Code launched exited. anotifier never starts wsl.exe. VS Code's terminal troubleshooting page says to check wsl --status and wsl --list --verbose outside the editor, use a real Linux distribution, and fix the default distribution or the profile's -d setting. It documents exit code 1 for WSL and does not explain -1.

Are Windows notifications tested?

Native Windows toasts are verified in CI on a real Windows runner by reading them back out of Windows' own notification database. The WSL path has unit tests but no end-to-end CI run, because hosted runners cannot start WSL.

Try it

One command wires every agent you have installed: Claude Code, Codex CLI, Cursor, and Gemini CLI.

npx anotifier@latest setup

Other guides