AC Bridge

Claude Code notifications on Windows with PowerShell

Last updated · Claude Code docs read the same day

The short answer Save a short PowerShell script as notify.ps1, then point a Claude Code Stop hook and a Notification hook at it through powershell.exe. It shows a Windows toast with the toast API built into Windows, so there is no module to install. We ran the script and read its toast back on one PC. We have not yet run it through a real Claude Code hook, and the page says which steps that covers.

Or let a light show it

A toast needs you looking at the screen. A light works from across the room, and adds no extra ping.

What it does

AC Bridge turns the smart lights and plugs you already own (Hue, LIFX, Govee, WiZ, Home Assistant and more) into a status light for Claude Code and the Codex CLI. It runs on Windows, macOS and Linux. Its Claude Code hooks come with its plugin, so there is nothing to paste into your settings files. In the example colours from our homepage, the lamp flashes red when the agent needs permission and turns green when it has finished; you pick the colours.

What it follows

On Claude Code, AC Bridge takes needs permission from the Notification hook's permission_prompt matcher; finished follows Stop. See the hook events AC Bridge uses for the full list.

The Claude desktop app has no plugins in its sessions inside the Windows Subsystem for Linux, so those sessions drive no light, and neither do cloud sessions.

What it costs

One plan: a 7-day free trial with no card needed, then £2.99 a month if you add a card to keep going; without one the trial just ends and nothing is charged. Cancel any time from your account. There is no API key to buy.

What it collects

LAN devices are driven straight from your machine and their keys never leave it; your account, settings, device list, usage counts and session states sync through our servers. Your prompts, code and session transcripts are never collected. Privacy policy.

Try it on your own lights Start the 7-day free trial, or read the setup docs and the status light guide first.

What ran on Windows 11, and what has not

The first card is what we saw work. The second is what we did not run, so treat those steps as written from the docs until you have tried them yourself.

Ran on 31 October 2026

  • Windows 11 Home, build 10.0.26300, with Windows PowerShell 5.1 and no extra module.
  • The script, with JSON piped in from a bash shell: exit 0, and the toast read back from the Windows notification history.
  • The check script, and the listing of app IDs.
  • A message holding odd characters, a message with no text, and empty input.
  • The execution policy error, and the switch that clears it.

Not run yet

  • A real Claude Code hook starting the script, on Windows or in a Linux distribution. The hook entries here come from the hooks reference.
  • Git Bash, a PowerShell prompt, and "shell": "powershell" on a hook.
  • PowerShell 7, which that PC does not have.
  • The script saved under .claude\hooks. It ran from a temporary folder.
  • The terminal bell in Windows Terminal.
  • Do Not Disturb, and a full-screen app.

Claude Code toast notification on Windows with PowerShell

Claude Code's own hooks guide uses a message box on Windows, and warns that it can open behind your terminal. A toast appears in the corner of the screen instead.

Save it as notify.ps1

notify.ps1

# Show a Windows toast for a Claude Code hook. Needs no module.
[Console]::InputEncoding = [Text.Encoding]::UTF8
$hook = [Console]::In.ReadToEnd() | ConvertFrom-Json
$body = if ($hook.message) { $hook.message } else { 'Finished' }

$manager = [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime]
$xml = $manager::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastText02)
$lines = $xml.GetElementsByTagName('text')
[void]$lines.Item(0).AppendChild($xml.CreateTextNode('Claude Code'))
[void]$lines.Item(1).AppendChild($xml.CreateTextNode($body))

$appId = '{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe'
$manager::CreateToastNotifier($appId).Show([Windows.UI.Notifications.ToastNotification]::new($xml))

What each part does

  • The input. Claude Code passes a hook its event as JSON on standard input. The script reads that as UTF-8, so accented letters survive.
  • The text. A Notification event carries a message field. A Stop event does not, so the toast says Finished.
  • The XML. CreateTextNode handles the escaping, so a message holding & or < cannot break the toast.
  • The app ID. A toast needs an app to belong to. This one borrows Windows PowerShell's Start menu entry, so nothing has to be registered.

Try it before you add a hook

Pipe a sample event into the script. This is the line we ran, from a bash shell, with the folder changed to yours. You should get a toast titled Claude Code with that message under it.

bash

printf '{"message":"Claude needs your permission to use Bash"}' | powershell.exe -NoProfile -ExecutionPolicy Bypass -File 'C:\Users\you\.claude\hooks\notify.ps1'

Set up a Claude Code notification hook on Windows

Not run yet: this entry follows the example in the hooks reference. We have not watched it fire.

~/.claude/settings.json

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe",
            "args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "C:\\Users\\you\\.claude\\hooks\\notify.ps1"],
            "async": true
          }
        ]
      }
    ],
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe",
            "args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "C:\\Users\\you\\.claude\\hooks\\notify.ps1"],
            "async": true
          }
        ]
      }
    ]
  }
}

Why it is written this way

  • With args set, the docs say Claude Code starts the program directly, with no shell. That avoids any question of whether Git Bash or PowerShell reads the line, and on Windows command must then be a real program.
  • In JSON every backslash in the path is doubled.
  • "async": true runs the hook in the background, so the toast does not hold Claude Code up.
  • The script prints nothing, because a hook's output can change what Claude Code does.

Which events to pick, and how they behave, is in the Claude Code notifications guide.

Claude Code PowerShell script blocked: running scripts is disabled

The error

PowerShell error

File C:\Users\you\.claude\hooks\notify.ps1 cannot be loaded because running scripts is disabled on this system.

Restricted is the default on Windows client PCs. Our PC was set to RemoteSigned, so we brought the error up by passing -ExecutionPolicy Restricted. It exited with code 1 and no toast.

The fix

Pass -ExecutionPolicy Bypass, as the test line and the hook entries do. It lasts for that one PowerShell session, so your machine's policy does not change. Microsoft calls the execution policy defence in depth rather than a security boundary, and the Claude Code hooks reference uses the same switch.

-NoProfile sits beside it for a different reason: it skips your PowerShell profile so the hook starts fast.

Windows toast not showing from Claude Code

This script asks Windows whether the app may show toasts, then prints the toasts it holds for that app.

Save it as check.ps1

check.ps1

$manager = [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime]
$appId = '{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe'
"setting=" + $manager::CreateToastNotifier($appId).Setting
$h = $manager::History.GetHistory($appId)
"history=" + $h.Count
foreach ($t in $h) { ($t.Content.GetElementsByTagName('text') | ForEach-Object { $_.InnerText }) -join ' | ' }

What it printed

setting=Enabled
history=1
Claude Code | Claude needs your permission to use Bash

Read the setting

We only saw Enabled. The others are from Microsoft's list:

  • DisabledForApplication: you turned toasts off for this app.
  • DisabledForUser: all notifications are off for your account on this PC.
  • DisabledByGroupPolicy: an administrator turned them off for the PC.
  • DisabledByManifest: the app has not declared that it can show toasts.

Wrong app ID?

If the app ID itself is wrong on your PC, this lists the ones Windows knows:

PowerShell

Get-StartApps | Where-Object { $_.Name -match "PowerShell|Terminal" }

Claude Code notifications in the Windows Subsystem for Linux

Every command above ran from a bash shell in a Linux distribution, so the piped test holds here too. The hook entry is the part that differs, and it is not run yet.

What changes

Windows programs reach the Linux side through interop, which is how powershell.exe runs at all, and the Claude Code docs ask for it on your PATH. The script stays on the Windows side and you give its Windows path, because that is what we ran.

~/.claude/settings.json in Linux

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File 'C:\\Users\\you\\.claude\\hooks\\notify.ps1'",
            "async": true
          }
        ]
      }
    ]
  }
}

Claude Code notification sound in Windows Terminal

Not run yet. The terminal bell setting is the one-line option, and what you then get is up to your terminal. Microsoft's docs give Windows Terminal a bellStyle profile setting, audible by default, and all adds a taskbar flash.