A Windows CLI tool that shows toast notifications when AI coding agents (Claude Code, Gemini CLI, GitHub Copilot) finish tasks. Key feature: click the toast to bring your terminal back to foreground.
When using AI CLI agents, you alt-tab away while waiting. The agent finishes but you don't notice. Toasty solves this with:
- Native Windows toast notification when agent stops
- Click-to-focus: clicking the toast brings your terminal window back
main.cpp- Single-file C++ using WinRT for toast notifications- Embedded PNG icons for each AI agent (Claude, Gemini, Copilot, Codex, Cursor)
toasty://focusprotocol handler for click-to-focus
- When showing a toast, we walk the process tree to find the parent terminal window (Windows Terminal, VS Code, etc.)
- Save that HWND to registry (
HKCU\Software\Toasty\LastConsoleWindow) - Toast XML includes
activationType="protocol" launch="toasty://focus" - When user clicks toast, Windows launches
toasty.exe --focus --focusreads HWND from registry and brings window to foreground
Protocol handlers run in a restricted context - Windows blocks random apps from stealing focus. We use multiple techniques:
AttachThreadInput()to borrow focus permissionSendInput()with empty mouse event to simulate user activitySwitchToThisWindow()works better thanSetForegroundWindow()for protocol handlerskeybd_event()with ALT key to help with focus
{
"hooks": {
"Stop": [{
"hooks": [{
"type": "command",
"command": "D:\\path\\to\\toasty.exe \"Task complete\" -t \"Claude Code\"",
"timeout": 5000
}]
}]
}
}Note: Nested hooks array is required!
{
"hooks": {
"AfterAgent": [{
"hooks": [{
"type": "command",
"command": "D:\\path\\to\\toasty.exe \"Gemini finished\" -t \"Gemini\"",
"timeout": 5000
}]
}]
}
}Note: Same nested structure as Claude. Uses AfterAgent not Stop.
{
"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
}]
}
}Toasty walks the process tree looking for known parent processes:
- If launched by
claude.exe-> uses Claude icon/title - If launched by node with
@google/geminiin cmdline -> uses Gemini icon/title - etc.
This means you can just run toasty "Done" and it picks the right branding.
Problem: Changed hook settings but nothing happened.
Solution: Must restart Claude Code to reload settings.json. Settings are only read at startup.
Problem: Gemini CLI wasn't triggering toasty.
Solution: Gemini needs the nested hooks array structure (same as Claude), not flat format.
Problem: Clicking toast worked when testing manually, but not when triggered from Claude Code hook. Cause: We thought it was Windows security restrictions on protocol handlers, but it was actually just that hook settings weren't reloaded. Solution: Restart Claude Code. The aggressive focus techniques we added (SendInput, SwitchToThisWindow) may help in edge cases anyway.
Problem: GetConsoleWindow() returns NULL for GUI subsystem apps, and process tree walking sometimes fails.
Solution: Multiple fallbacks:
- Walk process tree with
CreateToolhelp32Snapshotto find parent with visible window - If that fails, enumerate all windows looking for
CASCADIA_HOSTING_WINDOW_CLASS(Windows Terminal) orConsoleWindowClass
- v0.1 - Basic toast notifications
- v0.2 - Auto-detection of parent AI agent, embedded icons
- v0.3 - Click-to-focus feature (protocol handler)
- v0.4 - Code cleanup (HandleGuard, to_lower), fixed Gemini hook format
- v0.5 - Improved click-to-focus reliability, removed debug logging
cmake -B build -G "Visual Studio 17 2022"
cmake --build build --config Release
# Output: build/Release/toasty.exetoasty "message" # Show toast (auto-detects agent)
toasty "message" -t "Title" # Custom title
toasty "message" --app claude # Force specific agent branding
toasty --install # Install hooks for detected agents
toasty --install claude # Install hook for specific agent
toasty --uninstall # Remove all hooks
toasty --status # Show detection/installation status
toasty --register # Re-register app (troubleshooting)
toasty --focus # Internal: called by protocol handlermain.cpp- All the coderesource.h/resources.rc- Icon resourcesicons/*.png- Source icons (embedded at compile time)CMakeLists.txt- Build config
- After changing hook settings, restart Claude Code
- Test toasty directly first:
.\build\Release\toasty.exe "Test" -t "Test" - Click the toast to verify focus works
- Then test via hook by completing a task
Scott Hanselman (@shanselman)