Claude Code 在 Windows 上的 ChatGPT 風格桌面卡片——✅ 任務完成 / ❓ 需要使用者提供相關資訊 / ⏳ 正在等你回答或核准。等待類彈窗會持續顯示、回答或核准後自動關閉。純系統 PowerShell + WPF,零第三方相依。
📄 此檔案為精簡翻譯版;完整內容以 English README 為準。
- 強制生效——由 Claude Code 的 hook 觸發(harness 層執行,設定於
~/.claude/settings.json),每次 Claude 停止回應必然彈窗,不依賴模型自覺。 - 智慧判斷——回覆以問號結尾或含請求詞(「請提供…」)→ ❓ 需要使用者提供相關資訊;否則 → ✅ 任務完成(8 秒自動消失)。
- 回覆摘要——卡片內文顯示本次回覆首段(截斷 180 字,自動清理程式碼區塊/HTML/實體/URL),不切回終端機也知道 Claude 說了什麼。
- 專案名稱——標題旁顯示目前專案目錄名稱,多專案同時執行一眼區分。
- 等待提醒——Claude 提問(AskUserQuestion)或權限確認時彈出「❓ 正在等你回答」「⏳ 正在等你核准操作」卡片(附問題/命令摘要)——你在別的視窗寫程式也不會錯過等待。
- 持久彈窗,回答即關——等待類卡片持續顯示,直到:你回答/核准(自動關閉)、手動點 ✕、或 30 分鐘安全閥到期。
/notify_AskUserQuestion_persistence false可切回 8 秒自動消失。 - 自動關閉訊號通道——監聽所有工具的 PostToolUse / PostToolUseFailure / PermissionDenied(涵蓋 WebFetch、WebSearch、Skill、MCP 工具等一切可能觸發權限的工具),經輕量前置過濾器
notify-close-check.cmd:無等待彈窗時約 +30ms 秒退(不啟動 PowerShell),有彈窗等待時才走完整鏈路。雙通道比對(工具呼叫 ID + 工具參數內容指紋——權限請求事件官方設計不帶 ID);彈窗存續期間以waiting-*握手檔案登記,無等待者不產生任何訊號檔案。 - 去重與過濾——同一工作階段 2 秒內重複事件只彈一次;AskUserQuestion 自身的權限雜訊自動略過。
- 全域生效——設定於使用者層級
~/.claude/settings.json,對所有專案生效。 - 零第三方相依——僅用系統內建的 PowerShell 5.1 + WPF(DirectWrite 渲染,文字與瀏覽器同源清晰)。
- 不搶焦點——
ShowActivated=false+WS_EX_NOACTIVATE,絕不打断你正在進行的輸入。 - 非阻塞——hook 入口 500ms 內返回,彈窗 UI 由獨立處理程序管理。
- 去識別化日誌——
%TEMP%\claude-code-notify\notify.log(200KB 滾動),只記時間/工作階段前 8 碼/結果,不落全文。
┌────────────────────────────────┐
│ ✅ 任務完成 │
│ 已完成所有修改,共改動 3 個檔 │
│ 案,測試全部通過。 │
└────────────────────────────────┘
↑ 右下角 · 白底圓角 · 淡入淡出
8 秒自動消失 · 點擊/✕ 立即關閉
Claude Code 事件(~/.claude/settings.json 全域設定,timeout 5s)
│ stdin JSON
├─ Stop(主回覆結束)──────────────────┐
├─ PreToolUse(AskUserQuestion)(提問,等你回答)─┤
├─ PermissionRequest(權限確認,等你核准)───────┤
└─ PostToolUse / PostToolUseFailure / PermissionDenied(全部工具)
→ notify-close-check.cmd 前置過濾(無 waiting 秒退)
→ 有 waiting 才啟動 PS 寫關閉旗標 → exit ─────┘
▼
notify-complete.ps1(入口,500ms 內返回)
│ ① 過濾事件類型(略過 AskUserQuestion 自身的權限雜訊)
│ ② 本機確定性摘要(程式碼區塊/HTML/實體/URL → 首段 180 字)
│ ③ 去重:SHA-256(session+內容),2 秒視窗
│ ④ 寫暫存 payload(persist/toolUseId/contentKey)→ 派生獨立 UI 處理程序
▼
show-popup.ps1(獨立處理程序,-STA)
│ 讀取 payload 後刪除 → 寫 waiting 握手 → WPF(DirectWrite) 渲染
▼
右下角非模態卡片:不搶焦點 · 圓角+陰影 · 點擊/✕ 關閉
· Stop:8 秒自動淡出
· 等待類(提問/權限確認):回答/核准後自動關閉(雙通道訊號)· 手動 ✕ · 30 分鐘安全閥
環境需求:Windows 10/11 · Claude Code v2.x · PowerShell 5.1(系統內建)
把下面這段話連同本倉庫連結一起發給你的 Claude Code,它會自動完成全部安裝並自我測試:
請把倉庫
https://github.com/yyyyolo7a79-sketch/claude-task-notify安裝到我的全域設定(Windows):
- 將
scripts\下的notify-complete.ps1、show-popup.ps1、notify-close-check.cmd複製到~\.claude\scripts\- 在
~\.claude\settings.json頂層加入 hook(已有其他條目保留追加、不要覆蓋):Stop(matcher 空)·PreToolUse(matcherAskUserQuestion)·PermissionRequest(matcher 空)·PostToolUse/PostToolUseFailure/PermissionDenied(matcher*全工具);彈窗類 command 用絕對解譯器路徑 +-WindowStyle Hidden("\"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe\" -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File \"C:\Users\<你的使用者名稱>\.claude\scripts\notify-complete.ps1\""),關閉訊號類 command 指向前置過濾器("\"C:\Users\<你的使用者名稱>\.claude\scripts\notify-close-check.cmd\"");"timeout": 5- 將
skills\claude-task-notify\複製到~\.claude\skills\;將commands\notify_AskUserQuestion_persistence.md複製到~\.claude\commands\- 在
~\.claude\CLAUDE.md末尾追加「任務完成彈窗」小節- 自我測試(先設
$OutputEncoding = [Text.Encoding]::UTF8):$OutputEncoding = [Text.Encoding]::UTF8; $evt = @{session_id="test"; cwd=(Get-Location).Path; hook_event_name="Stop"; stop_hook_active=$false; last_assistant_message="彈窗運作正常"} | ConvertTo-Json; $evt | & "$env:WINDIR\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File "$HOME\.claude\scripts\notify-complete.ps1"
第 1 步——複製腳本:
# 三個檔案複製到使用者層級腳本目錄
Copy-Item scripts\notify-complete.ps1 "$HOME\.claude\scripts\"
Copy-Item scripts\show-popup.ps1 "$HOME\.claude\scripts\"
Copy-Item scripts\notify-close-check.cmd "$HOME\.claude\scripts\"第 2 步——設定全域 hook。編輯 ~/.claude/settings.json,在頂層新增 hooks 鍵(保留原有設定):
{
"hooks": {
"Stop": [{ "matcher": "", "hooks": [{ "type": "command", "command": "<CMD>", "timeout": 5 }] }],
"PreToolUse": [{ "matcher": "AskUserQuestion", "hooks": [{ "type": "command", "command": "<CMD>", "timeout": 5 }] }],
"PermissionRequest": [{ "matcher": "", "hooks": [{ "type": "command", "command": "<CMD>", "timeout": 5 }] }],
"PostToolUse": [
{ "matcher": "*", "hooks": [{ "type": "command", "command": "<CMD2>", "timeout": 5 }] }
],
"PostToolUseFailure": [
{ "matcher": "*", "hooks": [{ "type": "command", "command": "<CMD2>", "timeout": 5 }] }
],
"PermissionDenied": [
{ "matcher": "*", "hooks": [{ "type": "command", "command": "<CMD2>", "timeout": 5 }] }
]
}
}
<CMD>即:"\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File \"C:\\Users\\<你的使用者名稱>\\.claude\\scripts\\notify-complete.ps1\""。若PostToolUse等鍵下已有其他條目,追加而非覆蓋。💡
<CMD2>="\"C:\\Users\\<你的使用者名稱>\\.claude\\scripts\\notify-close-check.cmd\""是「等待彈窗自動關閉」的訊號通道(matcher*):無等待彈窗時每次工具完成約 +30ms(前置過濾器秒退、不啟動 PowerShell),等待中約 +0.2 秒。刪除這三個條目則等待彈窗退化為手動關閉 / 30 分鐘安全閥。
⚠️ 把<你的使用者名稱>替換為實際路徑;設定後無需重啟,下一次任務即生效。💡 解譯器必須用絕對路徑 +
-WindowStyle Hidden:PATH 裡的powershell可被劫持,且不加Hidden每次 Stop 會閃現主控台視窗。
第 3 步(選用)——行為規範 skill:
Copy-Item skills\claude-task-notify "$HOME\.claude\skills\" -Recurse第 4 步(選用)——~/.claude/CLAUDE.md 末尾追加宣告。
# 直接測入口(stdin 餵 Stop 事件 JSON,應快速返回並彈出卡片)
# ⚠️ PS 5.1 的 $OutputEncoding 預設 ASCII——不設 UTF-8 時管道中文會降級為 "?"
$OutputEncoding = [Text.Encoding]::UTF8
$evt = @{ session_id="test"; cwd=(Get-Location).Path; hook_event_name="Stop";
stop_hook_active=$false; last_assistant_message="彈窗運作正常" } | ConvertTo-Json
$evt | & "$env:WINDIR\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File "$HOME\.claude\scripts\notify-complete.ps1"測試結果見日誌 %TEMP%\claude-code-notify\notify.log(OK = 已通知,DEDUPE = 去重,SKIP = 被過濾)。
用瀏覽器開啟 弹窗参数调整器.html,拖動滑桿即時預覽卡片效果,調好後把頁面生成的參數代碼發回,替換 show-popup.ps1 頂部常數區即可。
| 參數 | 預設值 | 說明 |
|---|---|---|
$W / $H |
400 / 200 | 卡片寬度 / 高度(px,100% DPI 基準,自動依系統縮放) |
$PAD_X / $PAD_TOP |
20 / 15 | 標題左邊距 / 上邊距 |
$BODY_TOP / $BODY_H |
52 / 80 | 內文上邊距 / 內文區域高度 |
$F_TITLE / $F_BODY |
12 / 10(腳本內 * 1.3333) |
標題 / 內文字級(pt;WPF 單位是 DIP px,pt→px ×4/3) |
$MAX_CHARS |
180 | 摘要截斷長度——在 notify-complete.ps1 頂部 |
$MARGIN |
20 | 彈窗距螢幕右下角邊距 |
$PERSIST_MAX_MS |
30 分鐘 | 持久彈窗安全閥——在 show-popup.ps1 頂部 |
持久化開關:/notify_AskUserQuestion_persistence true|false(預設 true = 等待類彈窗持久;false = 8 秒消失)。
| 現象 | 處理 |
|---|---|
| 彈窗不出現 | 看日誌 %TEMP%\claude-code-notify\notify.log:SKIP = 被過濾、DEDUPE = 2 秒內重複、ERR = 輸入或派生失敗 |
| 中文亂碼 | 兩個 .ps1 必須是 UTF-8 with BOM(PS 5.1 對無 BOM 的 UTF-8 按 ANSI 解讀)。倉庫檔案已帶 BOM,編輯過請重新儲存為 UTF-8 with BOM |
| 文字模糊 | 已用 WPF(DirectWrite)渲染,與瀏覽器同源清晰;如仍模糊請檢查顯示器縮放設定 |
| 與系統通知雙彈 | /config 中關閉 Claude Code 內建通知 |
| 彈窗內容被截斷 | 用調整器調大 $BODY_H / $H 或減小字級 |
| 等待彈窗一直不消失 | 持久模式的設計——點 ✕ 關閉,或回答後自動關(安全閥 30 分鐘);/notify_AskUserQuestion_persistence false 可切回 8 秒 |
| 權限彈窗核准後沒自動消失 | 已修復(兩代問題:① 權限請求事件無 ID → 內容指紋通道;② 固定 matcher 清單漏 WebFetch/Skill 等工具 → 全工具掛載 + cmd 前置過濾);僅在極罕見情境(工具被拒後無後續事件)退化為手動 ✕ / 30 分鐘安全閥 |
測試命令偶發 ERR invalid-json |
PS 5.1 管道傳輸的間歇問題(僅測試鏈路;真實 hook 由 Node 寫 stdin 不受影響)——重跑一次即可 |
| 提問時彈出兩張卡片 | 舊版已知問題(AskUserQuestion 權限雜訊)——更新 notify-complete.ps1 到最新版即可 |
- 根因:關閉訊號 hook 用了固定 matcher 清單(
Bash|Edit|Write|MultiEdit|NotebookEdit)——清單外工具(WebFetch、Skill、MCP 等)的權限彈窗永遠收不到「工具完成」訊號,持續滯留。 - 修復:三個關閉訊號事件 matcher 改
*全工具 + 新增notify-close-check.cmd前置過濾器(無等待時約 30ms 秒退,不啟動 PowerShell)。
- 根因:權限請求事件(PermissionRequest)官方設計沒有
tool_use_id——權限彈窗無法比對關閉訊號。 - 修復:雙通道關閉訊號(呼叫 ID + 內容指紋
SHA256(tool_name+tool_input)[:16])、waiting-*握手檔案(無等待者零檔案)、補PermissionDenied事件。
- 4 個 bug 修復(去重時區、定位偏移 30 DIP、提問雙彈窗、檔案/管道)。
- 新增:提問/權限確認時的 ❓/⏳ 等待提醒,預設持久 + 回答自動關,
/notify_AskUserQuestion_persistence命令。
📖 完整歷史(含更早的初始開發階段)見 English README。
MIT——見 LICENSE。
互動靈感來自 ChatGPT 桌面端與 Codex 的任務完成彈窗:非模態、不搶焦點、右下角、自動消失。