diff --git a/plugins/README.md b/plugins/README.md
index cf4a21ecc5..975bcb07cc 100644
--- a/plugins/README.md
+++ b/plugins/README.md
@@ -21,6 +21,7 @@ Learn more in the [official plugins documentation](https://docs.claude.com/en/do
| [frontend-design](./frontend-design/) | Create distinctive, production-grade frontend interfaces that avoid generic AI aesthetics | **Skill:** `frontend-design` - Auto-invoked for frontend work, providing guidance on bold design choices, typography, animations, and visual details |
| [hookify](./hookify/) | Easily create custom hooks to prevent unwanted behaviors by analyzing conversation patterns or explicit instructions | **Commands:** `/hookify`, `/hookify:list`, `/hookify:configure`, `/hookify:help`
**Agent:** `conversation-analyzer` - Analyzes conversations for problematic behaviors
**Skill:** `writing-rules` - Guidance on hookify rule syntax |
| [learning-output-style](./learning-output-style/) | Interactive learning mode that requests meaningful code contributions at decision points (mimics the unshipped Learning output style) | **Hook:** SessionStart - Encourages users to write meaningful code (5-10 lines) at decision points while receiving educational insights |
+| [notifier](./notifier/) | Cross-platform notifications when Claude Code completes tasks, requests permissions, or waits for input (macOS, Linux, Windows Native/WSL2) | **Hooks:** UserPromptSubmit, Stop, Notification, SessionEnd - Smart notifications for long-running tasks
**Command:** `/notifier` - Configure language, duration threshold, and preview length |
| [plugin-dev](./plugin-dev/) | Comprehensive toolkit for developing Claude Code plugins with 7 expert skills and AI-assisted creation | **Command:** `/plugin-dev:create-plugin` - 8-phase guided workflow for building plugins
**Agents:** `agent-creator`, `plugin-validator`, `skill-reviewer`
**Skills:** Hook development, MCP integration, plugin structure, settings, commands, agents, and skill development |
| [pr-review-toolkit](./pr-review-toolkit/) | Comprehensive PR review agents specializing in comments, tests, error handling, type design, code quality, and code simplification | **Command:** `/pr-review-toolkit:review-pr` - Run with optional review aspects (comments, tests, errors, types, code, simplify, all)
**Agents:** `comment-analyzer`, `pr-test-analyzer`, `silent-failure-hunter`, `type-design-analyzer`, `code-reviewer`, `code-simplifier` |
| [ralph-wiggum](./ralph-wiggum/) | Interactive self-referential AI loops for iterative development. Claude works on the same task repeatedly until completion | **Commands:** `/ralph-loop`, `/cancel-ralph` - Start/stop autonomous iteration loops
**Hook:** Stop - Intercepts exit attempts to continue iteration |
diff --git a/plugins/notifier/.claude-plugin/plugin.json b/plugins/notifier/.claude-plugin/plugin.json
new file mode 100644
index 0000000000..a17fc9b960
--- /dev/null
+++ b/plugins/notifier/.claude-plugin/plugin.json
@@ -0,0 +1,11 @@
+{
+ "name": "notifier",
+ "description": "Get notified when Claude Code completes tasks, requests permissions, or waits for input. Supports macOS, Linux, and Windows (Native/WSL2).",
+ "version": "2.0.0",
+ "author": {
+ "name": "Joo-Seung Koo",
+ "email": "dev.jooseung@gmail.com"
+ },
+ "repository": "https://github.com/js-koo/claude-code-notifier",
+ "keywords": ["notifications", "productivity", "cross-platform"]
+}
diff --git a/plugins/notifier/README.md b/plugins/notifier/README.md
new file mode 100644
index 0000000000..3fec0b0c25
--- /dev/null
+++ b/plugins/notifier/README.md
@@ -0,0 +1,154 @@
+# claude-code-notifier
+
+Get notified when Claude Code completes your tasks (Windows/macOS/Linux)
+
+## Features
+
+- **Cross-platform**: Windows (Native/WSL2), macOS, and Linux support
+- **Smart notifications**: Only notifies for tasks taking 20+ seconds
+- **Prompt preview**: Shows the first few characters of your prompt
+- **Session-aware**: Multiple Claude Code sessions work independently
+- **Zero config**: Works out of the box with sensible defaults
+- **Slash command**: Easy configuration with `/notifier` command
+
+## Tested Environments
+
+| Platform | Status |
+|----------|--------|
+| macOS | ✅ Tested |
+| Linux | ✅ Tested (Docker) |
+| Windows (Native) | ✅ Tested |
+| Windows (WSL2) | ✅ Tested |
+
+> Found an issue? Please [open an issue](https://github.com/js-koo/claude-code-notifier/issues)!
+
+## Requirements
+
+- [Claude Code CLI](https://claude.ai/code)
+- `jq` (JSON processor)
+- **Linux only**: `libnotify-bin` (for `notify-send`)
+
+## Installation
+
+This plugin is included in the official Claude Code plugins directory.
+
+For standalone installation, see [claude-code-notifier](https://github.com/js-koo/claude-code-notifier).
+
+## Configuration
+
+### Using Slash Command (Recommended)
+
+Use the `/notifier` command in Claude Code:
+
+| Command | Description |
+|---------|-------------|
+| `/notifier help` | Show available commands |
+| `/notifier status` | Show current configuration |
+| `/notifier lang ` | Set language (en: English, ko: 한국어) |
+| `/notifier duration ` | Set minimum task duration (default: 20) |
+| `/notifier preview ` | Set prompt preview length (default: 45) |
+| `/notifier test` | Send a test notification |
+| `/notifier uninstall` | Uninstall claude-code-notifier |
+
+### Manual Configuration
+
+Edit `config.sh` in the plugin's `hooks-handlers/` directory:
+
+```bash
+# Language setting: "en" (English) or "ko" (한국어)
+NOTIFIER_LANG="en"
+
+# Minimum task duration (seconds) to trigger notification
+MIN_DURATION_SECONDS=20
+
+# Number of characters to preview from the prompt
+PROMPT_PREVIEW_LENGTH=45
+```
+
+### Configuration Options Explained
+
+| Option | Default | Description |
+|--------|---------|-------------|
+| `NOTIFIER_LANG` | `en` | UI language. `en` for English, `ko` for Korean. Affects notification messages and slash command responses. |
+| `MIN_DURATION_SECONDS` | `20` | Minimum task duration to trigger notification. Tasks completing faster than this won't show notifications. Set to `0` to notify on every task. |
+| `PROMPT_PREVIEW_LENGTH` | `45` | Number of characters to show from your original prompt in the notification. |
+
+### Notification Messages
+
+Messages are automatically set based on `NOTIFIER_LANG`:
+
+| Event | English | 한국어 |
+|-------|---------|--------|
+| Task completed | Task completed! | 작업 완료! |
+| Permission required | Permission required! | 권한 필요! |
+| Waiting for input | Waiting for input... | 입력 대기 중... |
+
+### Examples
+
+**Quick tasks without notifications:**
+```bash
+# Only notify for tasks taking 60+ seconds
+/notifier duration 60
+```
+
+**Always notify:**
+```bash
+# Notify on every task completion
+/notifier duration 0
+```
+
+**Longer prompt preview:**
+```bash
+# Show more of the original prompt
+/notifier preview 100
+```
+
+**Switch to Korean:**
+```bash
+/notifier lang ko
+```
+
+## How It Works
+
+This tool uses Claude Code's [hooks system](https://docs.anthropic.com/en/docs/claude-code/hooks) to:
+
+1. **UserPromptSubmit**: Save the prompt and start time when you submit a task
+2. **Stop**: Show a notification when Claude Code finishes (if duration > threshold)
+3. **Notification**: Alert when permission is required or Claude is waiting for input
+4. **SessionEnd**: Clean up temporary files when the session ends
+
+Session data is stored in `~/.claude-code-notifier/data/`.
+
+## Troubleshooting
+
+### Notifications not appearing
+
+**Windows (WSL)**:
+- Ensure Windows notifications are enabled in Settings > System > Notifications
+- Check that Focus Assist is not blocking notifications
+
+**macOS**:
+- Allow notifications from "Script Editor" in System Preferences > Notifications
+
+**Linux**:
+- Install `libnotify-bin`: `sudo apt install libnotify-bin`
+- Check if `notify-send` works: `notify-send "Test" "Hello"`
+
+### jq not found
+
+Install jq using your package manager (see Installation section).
+
+### WSL path issues
+
+If you're using a non-default WSL distribution, the path conversion should still work automatically. If issues persist, check that `wslpath` is available.
+
+## License
+
+MIT License - see the [original repository](https://github.com/js-koo/claude-code-notifier) for details.
+
+## Contributing
+
+Contributions are welcome!
+
+- **Bug reports / Feature requests**: [Open an Issue](https://github.com/js-koo/claude-code-notifier/issues)
+- **Code contributions**: Submit a Pull Request
diff --git a/plugins/notifier/commands/notifier.md b/plugins/notifier/commands/notifier.md
new file mode 100644
index 0000000000..444149e0ad
--- /dev/null
+++ b/plugins/notifier/commands/notifier.md
@@ -0,0 +1,120 @@
+---
+allowed-tools: Bash(sed:*), Bash(cat:*), Bash(grep:*), Bash(mkdir:*), Bash(echo:*), Bash(~/.claude-code-notifier/uninstall.sh)
+argument-hint: [value]
+description: Configure claude-code-notifier settings
+---
+
+# Claude Code Notifier Configuration
+
+## Available Commands
+
+| Command | Description |
+|---------|-------------|
+| `help` | Show this help message |
+| `status` | Show current configuration |
+| `lang ` | Set language (en: English, ko: 한국어) |
+| `duration ` | Set minimum task duration (default: 20) |
+| `preview ` | Set prompt preview length (default: 45) |
+| `test` | Send a test notification |
+| `uninstall` | Uninstall claude-code-notifier |
+
+## Language Detection
+
+First, read NOTIFIER_LANG from ~/.claude-code-notifier/hooks-handlers/config.sh to determine response language.
+
+## Task
+
+User command: `$ARGUMENTS`
+
+Perform the appropriate action based on the detected language:
+
+### 1. `status`
+Display current settings:
+
+**English (en):**
+```
+📊 Current Configuration
+━━━━━━━━━━━━━━━━━━━━━━━━
+🌐 Language: {en|ko}
+⏱️ Min Duration: {value} seconds
+📝 Preview Length: {value} characters
+━━━━━━━━━━━━━━━━━━━━━━━━
+```
+
+**Korean (ko):**
+```
+📊 현재 설정
+━━━━━━━━━━━━━━━━━━━━━━━━
+🌐 언어: {en|ko}
+⏱️ 최소 시간: {value}초
+📝 미리보기 길이: {value}자
+━━━━━━━━━━━━━━━━━━━━━━━━
+```
+
+### 2. `lang `
+- Validate that the value is "en" or "ko"
+- Update NOTIFIER_LANG in config.sh using sed
+- **en:** "✅ Language updated to {value}"
+- **ko:** "✅ 언어가 {value}(으)로 변경되었습니다"
+
+### 3. `duration `
+- Validate that the value is a positive number
+- Update MIN_DURATION_SECONDS in config.sh using sed
+- **en:** "✅ Duration updated to {value} seconds"
+- **ko:** "✅ 최소 시간이 {value}초로 변경되었습니다"
+
+### 4. `preview `
+- Validate that the value is a positive number
+- Update PROMPT_PREVIEW_LENGTH in config.sh using sed
+- **en:** "✅ Preview length updated to {value} characters"
+- **ko:** "✅ 미리보기 길이가 {value}자로 변경되었습니다"
+
+### 5. `test`
+Run these commands:
+```bash
+mkdir -p ~/.claude-code-notifier/data
+echo "Test notification from /notifier" > ~/.claude-code-notifier/data/prompt-test.txt
+echo $(date +%s) > ~/.claude-code-notifier/data/timestamp-test.txt
+echo '{"session_id": "test"}' | ~/.claude-code-notifier/hooks-handlers/notify.sh
+```
+- **en:** "✅ Test notification sent!"
+- **ko:** "✅ 테스트 알림을 전송했습니다!"
+
+### 6. `uninstall`
+Run the uninstall script:
+```bash
+~/.claude-code-notifier/uninstall.sh
+```
+
+**English (en):**
+```
+✅ claude-code-notifier has been uninstalled.
+
+⚠️ Please restart Claude Code to complete the uninstallation.
+ (Type /exit or press Ctrl+C)
+```
+
+**Korean (ko):**
+```
+✅ claude-code-notifier가 삭제되었습니다.
+
+⚠️ 삭제를 완료하려면 Claude Code를 재시작하세요.
+ (/exit 입력 또는 Ctrl+C)
+```
+
+### 7. `help` or empty/invalid command
+Show the available commands table above.
+
+### Error Handling
+
+**English (en):**
+- If config.sh not found: "❌ claude-code-notifier is not installed. Run install.sh first."
+- If invalid number provided: "❌ Please provide a valid positive number."
+- If invalid language provided: "❌ Please provide a valid language (en or ko)."
+- If unknown command: "❌ Unknown command. Use `/notifier help` to see available commands."
+
+**Korean (ko):**
+- If config.sh not found: "❌ claude-code-notifier가 설치되지 않았습니다. install.sh를 먼저 실행하세요."
+- If invalid number provided: "❌ 유효한 양수를 입력하세요."
+- If invalid language provided: "❌ 유효한 언어를 입력하세요 (en 또는 ko)."
+- If unknown command: "❌ 알 수 없는 명령어입니다. `/notifier help`로 사용 가능한 명령어를 확인하세요."
diff --git a/plugins/notifier/hooks-handlers/cleanup-session.sh b/plugins/notifier/hooks-handlers/cleanup-session.sh
new file mode 100755
index 0000000000..f9397863fb
--- /dev/null
+++ b/plugins/notifier/hooks-handlers/cleanup-session.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+# cleanup-session.sh - Cleans up session files when Claude Code session ends
+# Hook: SessionEnd
+
+set -e
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+source "$SCRIPT_DIR/common.sh"
+
+# Read stdin (JSON from Claude Code)
+INPUT=$(cat)
+
+SESSION_ID=$(get_session_id "$INPUT")
+
+# Clean up session files
+rm -f "$DATA_DIR/prompt-${SESSION_ID}.txt"
+rm -f "$DATA_DIR/timestamp-${SESSION_ID}.txt"
+
+exit 0
diff --git a/plugins/notifier/hooks-handlers/common.sh b/plugins/notifier/hooks-handlers/common.sh
new file mode 100755
index 0000000000..ce783d9ee0
--- /dev/null
+++ b/plugins/notifier/hooks-handlers/common.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+# common.sh - Shared utilities for claude-code-notifier
+# Sourced by notifier scripts
+
+DATA_DIR="$HOME/.claude-code-notifier/data"
+
+# Extract value from JSON using jq or fallback to grep/sed
+# Usage: json_get "$JSON_STRING" "field_name"
+json_get() {
+ local json="$1"
+ local field="$2"
+
+ if command -v jq &> /dev/null; then
+ echo "$json" | jq -r ".$field // empty" 2>/dev/null
+ else
+ echo "$json" | grep -oE "\"$field\":[[:space:]]*\"[^\"]*\"" | sed "s/\"$field\":[[:space:]]*\"\(.*\)\"/\1/" | head -1
+ fi
+}
+
+# Get session ID from JSON, defaulting to "default"
+# Usage: get_session_id "$JSON_STRING"
+get_session_id() {
+ local json="$1"
+ local session_id
+
+ session_id=$(json_get "$json" "session_id")
+ echo "${session_id:-default}"
+}
+
+# Check if notification should be shown based on duration
+# Returns 0 if should notify, 1 if should skip
+# Usage: should_notify "$SESSION_ID" "$MIN_DURATION"
+should_notify() {
+ local session_id="$1"
+ local min_duration="$2"
+ local timestamp_file="$DATA_DIR/timestamp-${session_id}.txt"
+
+ if [ ! -f "$timestamp_file" ]; then
+ return 1
+ fi
+
+ local start_time
+ start_time=$(cat "$timestamp_file" 2>/dev/null)
+ local current_time
+ current_time=$(date +%s)
+ local elapsed=$((current_time - start_time))
+
+ if [ "$elapsed" -lt "$min_duration" ]; then
+ return 1
+ fi
+
+ return 0
+}
+
+# Get saved prompt text for session
+# Usage: get_prompt_text "$SESSION_ID"
+get_prompt_text() {
+ local session_id="$1"
+ local prompt_file="$DATA_DIR/prompt-${session_id}.txt"
+
+ if [ -f "$prompt_file" ]; then
+ cat "$prompt_file" 2>/dev/null
+ fi
+}
diff --git a/plugins/notifier/hooks-handlers/config.sh b/plugins/notifier/hooks-handlers/config.sh
new file mode 100755
index 0000000000..c2c8954817
--- /dev/null
+++ b/plugins/notifier/hooks-handlers/config.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# claude-code-notifier configuration
+# Edit these values to customize notification behavior
+# shellcheck disable=SC2034 # Variables are used by sourcing scripts
+
+# Language setting: "en" (English) or "ko" (한국어)
+NOTIFIER_LANG="en"
+
+# Minimum task duration (seconds) to trigger notification
+# Tasks shorter than this will not show notifications
+MIN_DURATION_SECONDS=20
+
+# Number of characters to show from the original prompt
+PROMPT_PREVIEW_LENGTH=45
+
+# =============================================================================
+# Messages (auto-set based on NOTIFIER_LANG, or customize below)
+# =============================================================================
+if [ "$NOTIFIER_LANG" = "ko" ]; then
+ MSG_COMPLETED="작업 완료!"
+ MSG_PERMISSION="권한 필요!"
+ MSG_IDLE="입력 대기 중..."
+else
+ MSG_COMPLETED="Task completed!"
+ MSG_PERMISSION="Permission required!"
+ MSG_IDLE="Waiting for input..."
+fi
diff --git a/plugins/notifier/hooks-handlers/notifiers/linux.sh b/plugins/notifier/hooks-handlers/notifiers/linux.sh
new file mode 100755
index 0000000000..be801ddc7d
--- /dev/null
+++ b/plugins/notifier/hooks-handlers/notifiers/linux.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+# linux.sh - Linux notification using notify-send
+# Called from notify.sh
+
+set -e
+
+# Check if notify-send is available
+if ! command -v notify-send &> /dev/null; then
+ exit 0
+fi
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+source "$SCRIPT_DIR/../common.sh"
+
+# Read config from environment or use defaults
+MIN_DURATION=${MIN_DURATION_SECONDS:-20}
+NOTIFY_MSG=${NOTIFY_MESSAGE:-"Task completed!"}
+NOTIF_TYPE=${NOTIFICATION_TYPE:-""}
+
+# Read stdin (JSON from Claude Code)
+INPUT=$(cat)
+
+SESSION_ID=$(get_session_id "$INPUT")
+
+# For Notification hooks (permission_prompt, idle_prompt), show immediately
+if [ -n "$NOTIF_TYPE" ] && [ "$NOTIF_TYPE" != "null" ]; then
+ notify-send "Claude Code" "$NOTIFY_MSG"
+ exit 0
+fi
+
+# Stop hook: check duration threshold
+if ! should_notify "$SESSION_ID" "$MIN_DURATION"; then
+ exit 0
+fi
+
+# Get prompt preview and show notification
+PROMPT_TEXT=$(get_prompt_text "$SESSION_ID")
+
+if [ -n "$PROMPT_TEXT" ]; then
+ notify-send "Claude Code" "$NOTIFY_MSG\n$PROMPT_TEXT"
+else
+ notify-send "Claude Code" "$NOTIFY_MSG"
+fi
+
+exit 0
diff --git a/plugins/notifier/hooks-handlers/notifiers/macos.sh b/plugins/notifier/hooks-handlers/notifiers/macos.sh
new file mode 100755
index 0000000000..3493c4e39c
--- /dev/null
+++ b/plugins/notifier/hooks-handlers/notifiers/macos.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+# macos.sh - macOS notification using AppleScript
+# Called from notify.sh
+
+set -e
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+source "$SCRIPT_DIR/../common.sh"
+
+# Read config from environment or use defaults
+MIN_DURATION=${MIN_DURATION_SECONDS:-20}
+NOTIFY_MSG=${NOTIFY_MESSAGE:-"Task completed!"}
+NOTIF_TYPE=${NOTIFICATION_TYPE:-""}
+
+# Read stdin (JSON from Claude Code)
+INPUT=$(cat)
+
+SESSION_ID=$(get_session_id "$INPUT")
+
+# For Notification hooks (permission_prompt, idle_prompt), show immediately
+if [ -n "$NOTIF_TYPE" ] && [ "$NOTIF_TYPE" != "null" ]; then
+ osascript -e "display notification \"$NOTIFY_MSG\" with title \"Claude Code\""
+ exit 0
+fi
+
+# Stop hook: check duration threshold
+if ! should_notify "$SESSION_ID" "$MIN_DURATION"; then
+ exit 0
+fi
+
+# Get prompt preview and show notification
+PROMPT_TEXT=$(get_prompt_text "$SESSION_ID")
+
+if [ -n "$PROMPT_TEXT" ]; then
+ osascript -e "display notification \"$PROMPT_TEXT\" with title \"Claude Code\" subtitle \"$NOTIFY_MSG\""
+else
+ osascript -e "display notification \"$NOTIFY_MSG\" with title \"Claude Code\""
+fi
+
+exit 0
diff --git a/plugins/notifier/hooks-handlers/notifiers/windows.ps1 b/plugins/notifier/hooks-handlers/notifiers/windows.ps1
new file mode 100644
index 0000000000..119183a407
--- /dev/null
+++ b/plugins/notifier/hooks-handlers/notifiers/windows.ps1
@@ -0,0 +1,115 @@
+# windows.ps1 - Windows Toast Notification for Claude Code
+# Called from notify.sh via Windows Native (Git Bash/MSYS2) or WSL
+
+# Read configuration from environment variables
+$MIN_DURATION = [int]$env:MIN_DURATION_SECONDS
+if ($MIN_DURATION -eq 0) { $MIN_DURATION = 20 }
+
+$NOTIFY_MSG = $env:NOTIFY_MESSAGE
+if (-not $NOTIFY_MSG) { $NOTIFY_MSG = "Task completed!" }
+
+$NOTIF_TYPE = $env:NOTIFICATION_TYPE
+
+$DATA_DIR = $env:NOTIFIER_DATA_DIR_WIN
+if (-not $DATA_DIR) {
+ Write-Host "Error: NOTIFIER_DATA_DIR_WIN not set"
+ exit 0
+}
+
+# UTF-8 encoding setup
+$OutputEncoding = [System.Text.Encoding]::UTF8
+[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
+
+# Load Toast Notification assemblies
+[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
+[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null
+
+# Read session_id from stdin (piped JSON)
+$sessionId = "default"
+try {
+ $inputData = [Console]::In.ReadToEnd()
+ if ($inputData) {
+ $jsonData = $inputData | ConvertFrom-Json -ErrorAction SilentlyContinue
+ if ($jsonData.session_id) {
+ $sessionId = $jsonData.session_id
+ }
+ }
+} catch {
+ # Use default session ID on error
+}
+
+$escapedPrompt = ""
+
+# For Notification hooks (permission_prompt, idle_prompt), skip duration check
+if (-not $NOTIF_TYPE -or $NOTIF_TYPE -eq "null") {
+ # Stop hook: check elapsed time
+ $timestampFile = Join-Path $DATA_DIR "timestamp-$sessionId.txt"
+ if (-not (Test-Path $timestampFile)) {
+ exit 0
+ }
+
+ try {
+ $startTime = [int](Get-Content $timestampFile -Raw -ErrorAction SilentlyContinue)
+ $currentTime = [int](Get-Date -UFormat %s)
+ $elapsed = $currentTime - $startTime
+
+ # Skip notification if task was too short
+ if ($elapsed -lt $MIN_DURATION) {
+ exit 0
+ }
+ } catch {
+ # Continue with notification on error
+ }
+
+ # Read prompt preview
+ $promptFile = Join-Path $DATA_DIR "prompt-$sessionId.txt"
+
+ if (Test-Path $promptFile) {
+ try {
+ $savedPrompt = Get-Content $promptFile -Raw -Encoding UTF8 -ErrorAction SilentlyContinue
+ if ($savedPrompt -and $savedPrompt.Trim()) {
+ # Escape XML special characters
+ $escapedPrompt = $savedPrompt.Trim()
+ $escapedPrompt = $escapedPrompt -replace '&', '&'
+ $escapedPrompt = $escapedPrompt -replace '<', '<'
+ $escapedPrompt = $escapedPrompt -replace '>', '>'
+ $escapedPrompt = $escapedPrompt -replace '"', '"'
+ $escapedPrompt = $escapedPrompt -replace "'", '''
+ }
+ } catch {
+ # Empty prompt on error
+ }
+ }
+}
+
+# Build Toast XML
+$xml = [Windows.Data.Xml.Dom.XmlDocument]::new()
+
+if ($escapedPrompt) {
+ $toastXml = @"
+
+
+
+ $NOTIFY_MSG
+ $escapedPrompt
+
+
+
+"@
+} else {
+ $toastXml = @"
+
+
+
+ $NOTIFY_MSG
+
+
+
+"@
+}
+
+$xml.LoadXml($toastXml)
+$toast = [Windows.UI.Notifications.ToastNotification]::new($xml)
+[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('Claude Code').Show($toast)
+
+exit 0
diff --git a/plugins/notifier/hooks-handlers/notify.sh b/plugins/notifier/hooks-handlers/notify.sh
new file mode 100755
index 0000000000..962ffa4410
--- /dev/null
+++ b/plugins/notifier/hooks-handlers/notify.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+# notify.sh - OS Router for Claude Code notifications
+# Hooks: Stop, Notification
+# Detects OS and routes to appropriate notifier
+
+set -e
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+source "$SCRIPT_DIR/common.sh"
+source "$SCRIPT_DIR/config.sh"
+
+# Read stdin into variable (for passing to child scripts)
+INPUT=$(cat)
+
+# Detect notification type from JSON
+NOTIFICATION_TYPE=$(json_get "$INPUT" "notification_type")
+
+# Determine message based on notification type
+case "$NOTIFICATION_TYPE" in
+ "permission_prompt")
+ NOTIFY_MESSAGE="$MSG_PERMISSION"
+ ;;
+ "idle_prompt")
+ NOTIFY_MESSAGE="$MSG_IDLE"
+ ;;
+ *)
+ NOTIFY_MESSAGE="$MSG_COMPLETED"
+ ;;
+esac
+
+# Export config as environment variables for child scripts
+export MIN_DURATION_SECONDS
+export NOTIFY_MESSAGE
+export NOTIFICATION_TYPE
+
+# Detect OS and route to appropriate notifier
+if [ "$OS" = "Windows_NT" ]; then
+ # Windows Native (Git Bash, MSYS2)
+ # Convert Unix path to Windows path
+ NOTIFIER_DATA_DIR_WIN=$(cygpath -w "$DATA_DIR" 2>/dev/null || echo "$DATA_DIR")
+ export NOTIFIER_DATA_DIR_WIN
+ PS1_PATH_WIN=$(cygpath -w "$SCRIPT_DIR/notifiers/windows.ps1" 2>/dev/null || echo "$SCRIPT_DIR/notifiers/windows.ps1")
+ echo "$INPUT" | powershell.exe -ExecutionPolicy Bypass -File "$PS1_PATH_WIN"
+elif grep -qi microsoft /proc/version 2>/dev/null; then
+ # WSL (Windows Subsystem for Linux)
+ NOTIFIER_DATA_DIR_WIN=$(wslpath -w "$DATA_DIR")
+ export NOTIFIER_DATA_DIR_WIN
+ # WSLENV is required to pass environment variables from WSL to Windows processes
+ export WSLENV="MIN_DURATION_SECONDS:NOTIFY_MESSAGE:NOTIFICATION_TYPE:NOTIFIER_DATA_DIR_WIN"
+ PS1_PATH_WIN=$(wslpath -w "$SCRIPT_DIR/notifiers/windows.ps1")
+ echo "$INPUT" | powershell.exe -ExecutionPolicy Bypass -File "$PS1_PATH_WIN"
+elif [ "$(uname)" = "Darwin" ]; then
+ # macOS
+ echo "$INPUT" | "$SCRIPT_DIR/notifiers/macos.sh"
+else
+ # Linux (native)
+ echo "$INPUT" | "$SCRIPT_DIR/notifiers/linux.sh"
+fi
+
+exit 0
diff --git a/plugins/notifier/hooks-handlers/save-prompt.sh b/plugins/notifier/hooks-handlers/save-prompt.sh
new file mode 100755
index 0000000000..061720f904
--- /dev/null
+++ b/plugins/notifier/hooks-handlers/save-prompt.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+# save-prompt.sh - Saves user prompt and timestamp for notifications
+# Hook: UserPromptSubmit
+
+set -e
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+source "$SCRIPT_DIR/common.sh"
+source "$SCRIPT_DIR/config.sh"
+
+# Ensure data directory exists
+mkdir -p "$DATA_DIR"
+
+# Read stdin (JSON from Claude Code)
+INPUT=$(cat)
+
+SESSION_ID=$(get_session_id "$INPUT")
+PROMPT=$(json_get "$INPUT" "prompt")
+
+# Exit silently if no prompt
+if [ -z "$PROMPT" ]; then
+ exit 0
+fi
+
+# Format prompt preview
+LENGTH=${#PROMPT}
+if [ "$LENGTH" -le "$PROMPT_PREVIEW_LENGTH" ]; then
+ FORMATTED_PROMPT="$PROMPT"
+else
+ FORMATTED_PROMPT="${PROMPT:0:$PROMPT_PREVIEW_LENGTH}..."
+fi
+
+# Save prompt and timestamp
+echo "$FORMATTED_PROMPT" > "$DATA_DIR/prompt-${SESSION_ID}.txt"
+date +%s > "$DATA_DIR/timestamp-${SESSION_ID}.txt"
+
+exit 0
diff --git a/plugins/notifier/hooks/hooks.json b/plugins/notifier/hooks/hooks.json
new file mode 100644
index 0000000000..097966c01b
--- /dev/null
+++ b/plugins/notifier/hooks/hooks.json
@@ -0,0 +1,55 @@
+{
+ "description": "Notification hooks for task completion, permission requests, and input waiting",
+ "hooks": {
+ "UserPromptSubmit": [
+ {
+ "hooks": [
+ {
+ "type": "command",
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks-handlers/save-prompt.sh"
+ }
+ ]
+ }
+ ],
+ "Stop": [
+ {
+ "hooks": [
+ {
+ "type": "command",
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks-handlers/notify.sh"
+ }
+ ]
+ }
+ ],
+ "SessionEnd": [
+ {
+ "hooks": [
+ {
+ "type": "command",
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks-handlers/cleanup-session.sh"
+ }
+ ]
+ }
+ ],
+ "Notification": [
+ {
+ "matcher": "permission_prompt",
+ "hooks": [
+ {
+ "type": "command",
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks-handlers/notify.sh"
+ }
+ ]
+ },
+ {
+ "matcher": "idle_prompt",
+ "hooks": [
+ {
+ "type": "command",
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks-handlers/notify.sh"
+ }
+ ]
+ }
+ ]
+ }
+}