Toasty uses C++/WinRT to call the Windows.UI.Notifications API directly. No Windows App SDK runtime required.
- C++/WinRT: Modern C++ projection for Windows Runtime APIs
- AUMID Registration: Creates a Start Menu shortcut with
System.AppUserModel.IDproperty - ToastNotificationManager: Windows built-in toast notification system
- Protocol Handler:
toasty://focusURL scheme for click-to-focus functionality - Embedded Icons: PNG icons for AI agents compiled into the executable
Windows App SDK requires the WindowsAppRuntime to be installed on the user's machine. For a simple CLI tool, this is unnecessary overhead. The raw Windows.UI.Notifications API works on any Windows 10/11 system.
The csharp branch has a .NET 10 Native AOT version that works, but produces a 3.4 MB binary. The C++ version is ~250 KB - 14x smaller.
- Visual Studio 2022 with "Desktop development with C++" workload
- CMake 3.20+
- Windows 10 SDK
# Configure (from Developer Command Prompt)
cmake -S . -B build -G "Visual Studio 17 2022" -A x64
# Build Release
cmake --build build --config Release
# Build Debug
cmake --build build --config Debugcmake -S . -B build -G "Visual Studio 17 2022" -A ARM64
cmake --build build --config ReleaseWindows toast notifications require an AppUserModelId (AUMID) to identify the sending application. For unpackaged desktop apps, this requires:
- A Start Menu shortcut (.lnk file)
- The shortcut must have the
System.AppUserModel.IDproperty set - The AUMID used in code must match the shortcut's property
Toasty automatically creates this shortcut on first use at:
%APPDATA%\Microsoft\Windows\Start Menu\Programs\Toasty.lnk
When you click a toast notification, toasty brings your terminal window to the foreground.
- On toast display: Walk the process tree to find the parent terminal window (Windows Terminal, VS Code, etc.)
- Save HWND: Store the window handle in registry (
HKCU\Software\Toasty\LastConsoleWindow) - Toast activation: Toast XML includes
activationType="protocol" launch="toasty://focus" - On click: Windows launches
toasty.exe --focusvia protocol handler - Focus window: Read HWND from registry and bring window to foreground
Protocol handlers run in a restricted context - Windows prevents arbitrary apps from stealing focus. Toasty uses multiple techniques:
AttachThreadInput()to borrow focus permission from foreground threadSendInput()with empty mouse event to simulate user activitySwitchToThisWindow()works better thanSetForegroundWindow()for protocol handlerskeybd_event()with ALT key press/release to help with focus
The toasty:// protocol is registered in HKCU\Software\Classes\toasty pointing to:
"C:\path\to\toasty.exe" --focus "%1"
Toasty walks the process tree looking for known parent processes:
| Process | Detection Method |
|---|---|
| Claude Code | claude.exe or @anthropic in command line |
| Gemini CLI | gemini-cli, @google/gemini in command line |
| Cursor | cursor in process name |
| Copilot/Codex | Process name match |
When detected, toasty automatically uses the appropriate icon and title.
main.cpp
├── Utilities
│ ├── HandleGuard - RAII wrapper for Windows handles
│ ├── to_lower() - Case-insensitive string helper
│ ├── escape_xml() - XML entity escaping
│ └── escape_json_string() - JSON string escaping
│
├── Icon Extraction
│ └── extract_icon_to_temp() - Extract embedded PNG to temp file
│
├── Process Tree Walking
│ ├── get_process_command_line() - Read process command line via NtQueryInformationProcess
│ ├── detect_preset_from_ancestors() - Walk tree to find AI agent parent
│ └── find_ancestor_window() - Walk tree to find terminal window
│
├── Focus Management
│ ├── save_console_window_handle() - Save HWND to registry
│ ├── get_saved_console_window_handle() - Read HWND from registry
│ ├── force_foreground_window() - Aggressive focus with thread attachment
│ └── focus_console_window() - Main focus logic with fallbacks
│
├── Hook Installation
│ ├── install_claude() / uninstall_claude()
│ ├── install_gemini() / uninstall_gemini()
│ ├── install_copilot() / uninstall_copilot()
│ ├── is_*_installed() - Check functions
│ └── has_toasty_hook() - JSON array scanner
│
├── Registration
│ ├── create_shortcut() - AUMID registration via Start Menu shortcut
│ ├── register_protocol() - Register toasty:// URL handler
│ └── ensure_registered() - Auto-register on first use
│
└── wmain() - Entry point, argument parsing, toast display
Basic toast:
<toast>
<visual>
<binding template="ToastGeneric">
<text>Title</text>
<text>Message</text>
</binding>
</visual>
</toast>With click-to-focus and icon:
<toast activationType="protocol" launch="toasty://focus">
<visual>
<binding template="ToastGeneric">
<image placement="appLogoOverride" src="C:\path\to\icon.png"/>
<text>Title</text>
<text>Message</text>
</binding>
</visual>
</toast>{
"hooks": {
"Stop": [{
"hooks": [{
"type": "command",
"command": "D:\\path\\to\\toasty.exe \"Task complete\" -t \"Claude Code\"",
"timeout": 5000
}]
}]
}
}{
"hooks": {
"AfterAgent": [{
"hooks": [{
"type": "command",
"command": "D:\\path\\to\\toasty.exe \"Gemini finished\" -t \"Gemini\"",
"timeout": 5000
}]
}]
}
}{
"version": 1,
"hooks": {
"sessionEnd": [{
"type": "command",
"bash": "toasty 'Copilot finished' -t 'GitHub Copilot'",
"powershell": "D:\\path\\to\\toasty.exe 'Copilot finished' -t 'GitHub Copilot'",
"timeoutSec": 5
}]
}
}Important: Claude and Gemini require the nested hooks array structure!
Icons are embedded as PNG resources in the executable:
| Resource ID | Icon |
|---|---|
| IDI_CLAUDE | Claude Code |
| IDI_COPILOT | GitHub Copilot |
| IDI_GEMINI | Gemini |
| IDI_CODEX | OpenAI Codex |
| IDI_CURSOR | Cursor |
| IDI_TOASTY | Default mascot |
Defined in resource.h, linked via resources.rc.
- Toasty auto-registers on first use
- Check Windows Settings > System > Notifications > Toasty is enabled
- Check Focus Assist / Do Not Disturb is off
- Run
toasty --registerto re-register
- Ensure protocol handler is registered: check
HKCU\Software\Classes\toasty - Try
toasty --registerto re-register protocol - Make sure you're clicking the toast body, not dismissing it
- Restart the AI agent after changing settings (settings only load at startup)
- Check the hook JSON format - nested
hooksarray is required for Claude/Gemini - Test toasty directly first:
toasty.exe "Test" -t "Test"
Ensure Windows 10 SDK is installed via Visual Studio Installer.