JianMiauandClaude Fable 5 4e4563e861 完成時氣泡改顯示最後一句回覆,不再延到下一輪才出現
根本原因:
Stop 事件時 main.js 已從 transcript 附上最後一句助理訊息(narration),
但 renderer 的 Stop 分支寫死顯示「完成!」沒使用它,也沒把 narrationId
標記為已顯示;下一輪第一個工具事件呼叫 sayActivity 時,transcript 裡
最新的助理訊息仍是上一輪結尾、id 又沒被消費過,就被當成新話顯示出來。

影響:
對話收尾只看得到「完成!」,看不到 AI 的最後一句回覆;等使用者送出
下一句指令後,寵物反而冒出上一輪的舊訊息,時序錯亂。

修法:
Stop 時若帶有 narration,氣泡直接顯示「 最後一句」並消費 narrationId,
沒有才退回「完成!」;UserPromptSubmit 也先把當下 narrationId 標記為已
顯示,雙重保險。另修 release.js 只挑檔名含當前版本的產物,避免 dist/
殘留的舊版檔案混進新 release。版號 2.0.9。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 16:40:32 +08:00

Claude Pet

Codex Pets 相容的 Claude Code 桌面寵物。 把 Codex /hatch 產出的寵物包原封不動拿來用,讓它站在桌面上,即時反映 Claude Code 正在做什麼:思考、等你授權、失敗、完成……還會盯著你的滑鼠游標看。

狀態總覽

上圖由左至右:SessionStart 揮手、工作中、等待授權、完成時跳躍、檢視成果。氣泡會標出是哪個專案的 session。


目錄


特色

  • 完整實作 Codex V2 pet contract — 8 欄 × 11 列、192×208 cell 的 spritesheet9 個標準動作列 + 16 個看向方向,每一幀的時長都照 Codex 規格,動起來跟在 Codex app 裡一模一樣。
  • 行為邏輯逐項對照 Codex — 狀態優先序、各狀態到期時間、動畫時序、hover 版面、初次問候、拖曳方向判定……都是從 Codex app 的 app.asar 讀出來的數值(見與 Codex 的對照)。
  • 16 方向追視(選配,預設關) — 開啟後、且目前是待機狀態時,游標在哪她就看哪(000 = 正上方、順時針 22.5° 一格),追視時是靜態擺姿;Claude Code 在工作時以狀態動畫優先。Codex 的寵物不看滑鼠——它看的是 quick chat 的文字游標與 computer-use 的虛擬游標,所以預設關閉以求一致;喜歡的話在選單打開。
  • 自動修正圖檔缺陷 — Codex 的 hatch 產生器有時會把整列 look 格子畫得比 idle 小(內附的小念第 10 列就小了 22%,游標一往下移人物就縮水)。以整列中位數偵測、用腳底當支點放大回正確身高;單格的姿勢差異不動。Codex 本身沒有做這件事。
  • 與 Codex 同尺寸 — 100% 就是 Codex 桌面寵物的大小(126 × 137),高度用 Codex 的 ceil(寬 × 208/192) 算,兩邊並排看起來一樣大。
  • 真正的桌面寵物 — 透明、無邊框、永遠置頂;透明區域點穿,只有壓在人物像素上滑鼠才會被吃掉,不擋你操作底下的視窗。
  • 不搶焦點 — 視窗設為 non-focusable,點她不會讓終端機失焦。
  • 拖曳 / 點擊 / 右鍵 — 拖曳時依方向播 running-left / running-right 並記住位置;點一下揮手;右鍵或系統匣開選單。
  • 對話氣泡(兩行) — 第一行是對話名稱/rename 設的)或專案資料夾名,第二行才是訊息。同時開多個 session 時一眼就知道是哪一個在叫你。
  • 工作中即時播報 — 氣泡會顯示正在用的工具與對象(⚙️ 執行 npm run dist📖 讀 renderer.js),以及助理剛說的那句話(💬 …)。注意:這不是思考過程,原因見能顯示什麼、不能顯示什麼
  • 多 session — 同時開好幾個 Claude Code 時,以 waiting > running > review > idle 的優先序顯示最需要你注意的那個。
  • Codex 寵物自動出現 — 預設也掃描 ~/.codex/petsCodex hatch 出來的寵物直接在選單裡切換。
  • Hook 零負擔 — hook 腳本約 100 ms 完成、寵物沒開也靜默 exit 0,不會拖慢或卡住 Claude Code。

需求

項目 說明
作業系統 Windows 10 / 11(透明視窗、點穿、系統匣、開機啟動皆以 Windows 實作)
Node.js 18 以上(開發時為 24.19
Claude Code 2.1 以上(使用 SessionStartPermissionRequestPostToolUseFailureStopFailurePreCompact 等事件)
寵物包 一個 Codex V2 寵物包(pet.json + spritesheet.webp)。本專案內附 pets/xiao-nian(小念)

快速開始

直接用打包好的(一般使用)

三種都不需要系統管理員權限,選一種即可(見打包成 exe):

方式 檔案 適合
免安裝資料夾(推薦) Claude Pet-<版本>-win.zip 解壓到任何地方,點兩下裡面的 Claude Pet.exe 就跑。啟動快、寵物放旁邊的 pets\、適合開機自動啟動
安裝檔 Claude Pet-<版本>-setup.exe 裝到 %LOCALAPPDATA%\Programs\Claude Pet,有開始選單與桌面捷徑
免安裝單檔 Claude Pet-<版本>-portable.exe 只想帶一個檔案走。代價是每次啟動都要重新解壓約 200 MB,開得比較慢

開起來之後用系統匣選單的 Claude Code hooks → 安裝 接上 Claude Code,然後重開一個新的 Claude Code session

從原始碼跑(開發)

cd E:\Project\Test\AI\Claude\claude-pet
npm install
npm run hooks:install        # 把 13 個 hook 合併進 ~/.claude/settings.json(會先備份到 ~/.claude/backups
npm start                    # 啟動寵物
npm run autostart:install    # (可選)開機自動啟動

注意

  1. 已開啟的 Claude Code session 不會重新讀 hooks重開一個新 session 才會看到她反應。
  2. npm installnode_modules\electron\dist\ 是空的,請看 疑難排解

日常使用

操作 效果
左鍵點一下 揮手(若有非固定的氣泡,先關閉氣泡)
左鍵拖曳 搬家;往左播 running-left、往右播 running-right;放開後位置寫入設定檔
右鍵 / 系統匣左鍵 開啟選單
滑鼠移到她身上 跳躍最多 3 次;游標一離開就立刻停止、回到原本狀態(Codex 的 hover 行為)
滑鼠靠近 盯著游標看(選單開啟「跟著游標看」後才有)

選單功能

  • 切換寵物 — 列出所有找到的寵物;v1(9 列)寵物會標示「無追視」
  • 大小 — 50% / 75% / 100% / 125% / 150%,以腳底為基準縮放(100% = Codex 桌面寵物的大小
  • 跟著游標看永遠置頂開機自動啟動 — 勾選即生效
  • 動作測試 — 不用真的跑 Claude Code 也能看每個動畫(idle / running / waiting / review / failed / jumping / waving
  • Claude Code hooks — 安裝 / 移除 hooks
  • 回到預設位置 — 跑到螢幕外面時用
  • 開啟設定檔 / 開啟事件紀錄
  • 結束

行為對照表

Claude Code hook 條件 動作 氣泡
(程式啟動) 該寵物第一次出現 waving × 3 → 慢速 idle 👋 嗨,我是〈寵物名〉(8 秒)
SessionStart source = startup idle 👋 嗨!(專案)
UserPromptSubmit running (關閉舊氣泡)
PreToolUse 一般工具 running 有新的助理說明就顯示 💬 …,否則顯示 ⚙️ 執行 … 之類的工具動作
PreToolUse AskUserQuestion waiting 有問題想問你
PreToolUse ExitPlanMode waiting 📋 計畫等你確認
PostToolUse running
PostToolUseFailure running(不變) 😵 〈工具〉失敗了
PermissionRequest waiting 🔐 需要你授權:〈工具〉
Notification permission_prompt waiting 🔐 需要你授權
Notification idle_prompt (不變) 💤 在等你回覆
Notification elicitation_dialog waiting 有問題想問你
Notification 其他 (不變) 💬 訊息內容
Stop stop_hook_active = false review,直到該 session 下一個動作/結束/7 天 完成!(專案)
StopFailure failed,直到該 session 下一個動作/結束/1 小時 錯誤訊息
SubagentStart / SubagentStop running
PreCompact running 🗜️ 整理記憶中…
SessionEnd 移除該 session 最後一個結束時 👋 掰掰
(無事件) 游標移動(追視開啟時、且目前為 idle) 16 方向 look
(無事件) 拖曳 單次位移 ≥ 4 px 才決定 running-left / running-right
(無事件) 游標進入人物方框 jumping 最多 × 3;離開即中斷

規則:

  • 待機時幾乎不動 — idle 每幀時長是合約值的 6 倍(一輪 6.6 秒,不是 1.1 秒),與 Codex 的 codex-pet-assets 一致。
  • 非 idle 狀態播 3 次就沉澱 成慢速 idle。動畫只在狀態改變時重新開始(Codex 的 effect 相依只有 state),工作中連續的工具呼叫不會讓她一直動。
  • 一次性動作(點擊揮手、初次問候)播完自動回到基礎狀態。
  • 基礎狀態依 session 彙整:waiting(4) > failed(3) > review(2) > running(1) > idle(0),取最高者顯示——與 Codex 通知排序 Ai() 相同,注意 review 高於 running
  • 到期與 Codex 的 Ei() 相同:failed 1 小時、waiting 24 小時、review 7 天後退回 idlerunningidle 不到期。另有一個 Codex 沒有的安全網:running 30 分鐘沒任何事件視為終端機已被關掉。
  • 系統「減少動態效果」開啟時只顯示每個狀態的第一格,與 Codex 相同。
  • waiting 的氣泡是固定的,狀態離開 waiting 時自動消失。

寵物包格式(Codex V2

與 Codex 完全相同,可直接互通。一個寵物是一個資料夾:

<pet-id>/
├── pet.json
└── spritesheet.webp     (或 .png

pet.json

{
  "id": "xiao-nian",
  "displayName": "小念",
  "description": "一句話描述",
  "spriteVersionNumber": 2,
  "spritesheetPath": "spritesheet.webp"
}

Spritesheet1536 × 22888 欄 × 11 列,每格 192 × 208,透明背景。

狀態 使用欄 每幀時長(ms
0 idle 05 280, 110, 110, 140, 140, 320
1 running-right 07 120 ×7, 220
2 running-left 07 120 ×7, 220
3 waving 03 140 ×3, 280
4 jumping 04 140 ×4, 280
5 failed 07 140 ×7, 240
6 waiting 05 150 ×5, 260
7 running(工作中,不是跑步) 05 120 ×5, 220
8 review 05 150 ×5, 280
9 look A 07 000, 022.5, 045, 067.5, 090, 112.5, 135, 157.5 度
10 look B 07 180, 202.5, 225, 247.5, 270, 292.5, 315, 337.5 度
  • look 列的 000 是正上方(12 點鐘),順時針。正面/中立不在 16 格裡,那是游標死區,退回 idle。
  • 列 0 第 6 欄若有圖會被當作「中立正面」,拿來做系統匣圖示。
  • 沒有 spriteVersionNumber: 2 的舊版(8×9、1536×1872)寵物也能用,只是沒有追視。

規格來源:Codex 的 hatch-pet skill~/.codex/skills/hatch-pet/references/codex-pet-contract.mdanimation-rows.md)。

新增 / 切換寵物

掃描順序:執行目錄的 pets\portable 版是那顆 exe 旁邊的,其次才是內附的)→ ~/.codex/pets → 設定檔 petSources 裡的額外路徑。同一個 id 以先掃到的為準。

  1. 手動放入(最直接)— 把寵物資料夾丟到 exe 旁邊的 pets\<id>\,選單「切換寵物 → 重新掃描」就會出現。選單的「開啟寵物資料夾」會直接開這一個。
  2. 用 Codex hatch — 在 Codex 裡 /hatch,完成後寵物在 %USERPROFILE%\.codex\pets\<id>\,本程式一律會掃這個資料夾。
  3. 其他位置 — 在設定檔 petSources 加路徑。

切換後立即生效,不用重啟。

設定檔

位置:%USERPROFILE%\.claude-pet\config.json(第一次啟動後自動產生,選單「開啟設定檔」可直接開)。

欄位 預設 說明
activePetId "xiao-nian" 目前使用的寵物 id
petSources [] 額外的寵物資料夾。兩個隱含來源永遠會被掃到且不寫進這裡:執行目錄的 pets/(exe 旁邊,開發時是專案資料夾,排最前面)與 ~/.codex/pets。不存在的路徑會在載入時自動剔除
scale 1 縮放:0.5 / 0.75 / 1 / 1.25 / 1.5。1 表示 126 × 137,與 Codex 桌面寵物同大小
position null 視窗左上角座標;null 為主螢幕右下角。若座標落在所有螢幕之外會自動拉回
alwaysOnTop true 永遠置頂
followCursor false 追視游標(Codex 的寵物不看滑鼠,預設關)
greetedPetIds [] 已打過招呼的寵物 id;刪掉可讓該寵物下次啟動再揮手一次
port 17333 接收 hook 事件的本機埠(改了之後 hook 端要設環境變數 CLAUDE_PET_PORT

事件紀錄:%USERPROFILE%\.claude-pet\events.log(超過 2 MB 自動清空)。

氣泡的第一行是對話名稱或專案名,第二行是訊息;標題為空時整行會收起來,不會留空白。

架構與檔案

Claude Code ──hookstdin JSON)──▶ hook/claude-pet-hook.js ──POST /event──▶ 127.0.0.1:17333
                                                                                   │  Electron mainmain.js
                                                                                   │  視窗 / 系統匣 / 游標輪詢 / 拖曳 / 設定
                                                                                   ▼  IPC
                                                                          renderer/renderer.js
                                                                          狀態機 + 幀動畫 + 追視 + 氣泡 + alpha 點穿判定
檔案 職責
main.js Electron 主程序:透明置頂視窗、系統匣選單、HTTP 伺服器、游標輪詢(50 ms)、拖曳(60 fps)、設定讀寫、寵物掃描
preload.js 以 contextBridge 暴露白名單 IPC 頻道給 renderersandbox 模式)
renderer/renderer.js Codex V2 幀時序播放器、session 狀態彙整、一次性動作、看向方向計算(含遲滯防抖)、氣泡、滑鼠 alpha 判定
renderer/index.htmlstyle.css 版面與氣泡樣式(CSP 僅允許 self / data:
hook/claude-pet-hook.js Claude Code hook 端:讀 stdin → 擷取欄位 → POST800 ms 逾時、永遠 exit 0
lib/hooks-installer.js 安全地合併 / 移除 ~/.claude/settings.json 的 hooks(保留其他設定與其他 hooks,先備份)
lib/autostart.js HKCU\Software\Microsoft\Windows\CurrentVersion\Run\ClaudePet 登錄值;開發時指向 electron.exe + 專案路徑,打包後直接指向那顆 exe
lib/paths.js 開發/安裝版/portable 三種情境的路徑解析(app.asarapp.asar.unpacked、是否打包、portable 的真實 exe 位置)
scripts/*.js 上述兩者的 CLI 包裝,以及 release.js(發佈到 Gitea Releases
pets/ 內附寵物
docs/ README 用圖
build/icon.ico exe 與安裝檔的圖示,取自小念 spritesheet 第 0 列第 6 欄(中立正面)的頭肩方形裁切

設計重點:

  • 點穿:視窗預設 setIgnoreMouseEvents(true, { forward: true })renderer 讀 canvas 該點的 alpha(或是否壓在氣泡上)來即時切換,所以透明處永遠點得到底下的視窗。判定的座標有兩個來源:renderer 自己的 mousemove,以及主程序每 50 ms 的游標輪詢。後者是必要的——視窗處於點穿狀態時 Electron 的 forward 不保證會把 mousemove 轉發進來(游標直接跳到寵物上常常收不到),只靠 mousemove 會在「游標還停在寵物身上時進入點穿」之後再也收不到事件,變成永遠卡在點穿、按不下去也拖不動。另外每一格畫完也會用最後已知座標重驗一次,因為換格會改變游標底下的像素。
  • 拖曳:由主程序用 screen.getCursorScreenPoint() 每 16 ms 更新視窗位置,游標離開視窗也不會掉;方向由水平位移決定。
  • 置頂要定期重新宣告:Windows 有時候會把視窗踢出 topmost band,卻留著 WS_EX_TOPMOST 樣式位元——從外部檢查看到 TOPMOST=true,但 z-order 排在一般視窗(例如 VS Code)之下,寵物就被蓋住,而 Electron 這邊仍以為自己是置頂的,不會自己修好。所以每 2 秒重新宣告一次,並在螢幕配置變動時立刻補一次。setAlwaysOnTop 在狀態沒變時可能不會真的呼叫 SetWindowPos,因此再補一個 moveTop() 強制插回 topmost band 最上面;視窗是 WS_EX_NOACTIVATE + focusable: false,不會搶焦點。
  • 行為邏輯以 Codex 為準,細節見與 Codex 的對照
  • 顯示尺寸與 sprite 來源尺寸分開CELL_W/CELL_H192 × 208)只用來從 spritesheet 取格子,畫到畫面上的是 PET_W/PET_H126 × 137Codex 的 mascot 尺寸)。視窗寬度另有 MIN_WIN_W = 240 的下限,免得寵物變小後氣泡被截掉。
  • Spritesheet 以 data URL 傳給 renderer,避免 file:// 跨來源污染 canvas 導致 getImageData 失敗。
  • hook 合併規則:以指令中是否含 claude-pet-hook 辨識自己的項目,重裝只替換自己的、不動別人的。

能顯示什麼、不能顯示什麼

氣泡顯示不了 Claude 的思考過程。Claude Code 從 2.1.238 起就不再把 thinking 的文字寫進 transcript,只留加密簽章:

{ "type": "thinking", "thinking": "", "signature": "CAQSwgQKEAgRGAI4AUII…" }

(2.1.237 以前有文字。這是 Claude Code 版本的差異,跟模型無關——同樣是 claude-opus-5 2.1.237 的 session 存得到、2.1.241 的存不到。)Claude Code 也沒有對外的事件串流, 所以 hook 拿不到思考內容。這點跟 Codex 不同:Codex CLI 的 --json 會直接送出 reasoning 事件。

拿得到的是這些,氣泡就顯示這些:

來源 內容 從哪來
工具動作 ⚙️ 執行 npm run dist📖 讀 renderer.js hook 的 tool_nametool_input
助理說明 助理剛講那段話的第一句 transcript_path 讀 transcript 尾端的 text 區塊
氣泡標題 對話名稱,沒設就用專案資料夾名 transcript 的 custom-title/rename 寫進去的),退回 cwd 的最後一段

氣泡長這樣:

claude-pet
💬 兩行氣泡正確運作,這是真實事件。

同一段說明只播報一次(以 transcript 的 uuid 判斷),markdown 標記會先清掉, 等待授權時不播報以免蓋掉固定氣泡。讀 transcript 只讀最後 192 KB 並依檔案大小快取, 不會因為 transcript 長到幾 MB 就變慢。

與 Codex 的對照

所有數值都是從 Codex appWindowsApps\OpenAI.Codex_*\app\resources\app.asar)的 codex-pet-assetsavatar-overlay-native-frame 與主程序讀出來的。

項目 Codex 本程式
idle 速度 合約幀長 × 6_ = 6 相同
非 idle 狀態 播 3 次 → 慢速 idle[...n,...n,...n] 相同
動畫何時重播 只在狀態改變時(effect 相依 [state, lookFrame, reducedMotion] 相同
跨 thread 優先序 waiting 0 > failed 1 > review 2 > running 3 > idle 4Ai() 相同
狀態到期 failed 1 h、waiting 24 h、review 7 d、running/idle 不到期(Ei() 相同,另加 running 30 分鐘無事件的安全網
failed 持續狀態(turn 失敗/取消) 相同(StopFailure);工具失敗不算
waiting 授權、requestUserInput、計畫待確認 相同;idle_prompt 不算
review turn 完成且未讀,直到使用者回到該 thread 直到該 session 下一個動作
waving 只在 first-awake(每隻寵物一次,8 秒) 相同(記在設定檔 greetedPetIds
jumping 定義在 respondToHover ? "jumping" : state,但整個 app 沒人開啟那個 prop 選擇打開:游標進入人物方框就播,最多 3 次
hover 離開 顯示狀態立刻翻回 state,動畫重跑,不會把跳躍播完 相同(游標離開就中斷跳躍)
hover 版面 40 px 內進入、56 px 外退出;人物縮 0.9、下移 14 px、上方 24 px 控制鈕、200 ms 未採用——控制鈕的功能就是選單,右鍵已經有了
hover 判定 用矩形(pet-area-hover-changed 由 rect 算) 相同(逐像素會因每格輪廓不同而抖動)
追視來源 quick chat 的文字游標/computer-use 游標 沒有對應來源;改為選配看滑鼠,預設關
追視優先序 lookFrame 覆蓋所有狀態 只在 idle 時追視。Codex 的文字游標只有你在打字時才存在,滑鼠卻是一直都在,照搬會讓工作中的動畫永遠被靜態擺姿蓋掉
拖曳方向 單次取樣水平位移 ≥ 4 px 相同
拖曳放開 彈簧回彈(spring 220 / damping 26 無(直接停在放開處)
點擊 開 quick chat 揮手(沒有對應功能)
reduced motion 只顯示第一格 相同
預設位置 工作區右下各留 24 px 相同
尺寸 寬 80224 px 可調 65%175%82221 px

刻意不同的地方都是因為 Claude Code 沒有對應的功能(quick chat、computer-use 游標)或對應的訊號(thread 是否已讀)。

HTTP API

只監聽 127.0.0.1

方法 路徑 說明
POST /event 接收事件(JSON,≤ 64 KB),回 204
GET /state 目前狀態:{"anim","base","sessions","pet","scale"}
GET /health ok

POST /event 的 payload(hook 腳本送出的格式):

{
  "event": "Stop",
  "sessionId": "uuid",
  "cwd": "E:\\Project\\foo",
  "toolName": null,
  "notificationType": null,
  "message": null,
  "error": null,
  "source": null,
  "stopHookActive": false,
  "ts": 1787278568523
}

手動觸發範例:

# 模擬需要授權
curl -X POST http://127.0.0.1:17333/event -H "content-type: application/json" `
  -d '{"event":"Notification","sessionId":"demo","cwd":"D:/demo","notificationType":"permission_prompt"}'

# 模擬完成
curl -X POST http://127.0.0.1:17333/event -H "content-type: application/json" `
  -d '{"event":"Stop","sessionId":"demo","cwd":"D:/demo"}'

# 查狀態
curl http://127.0.0.1:17333/state

也可以送 {"event":"__test","anim":"waiting"}idle|running|waiting|review|failed|jumping|waving|clear)直接指定動畫,與選單「動作測試」相同。

打包成 exe

npm run dist            # 三種都做
npm run dist:portable   # 只做免安裝單檔
npm run pack            # 只產 win-unpacked 資料夾,最快,改程式時驗證用

版號會自動遞增 distdist:portable 都掛了 pre 腳本執行 npm version patch --no-git-tag-version, 每次打包 patch 版號自動 +1。產物檔名帶版號,同版號重包會讓人分不清手上是哪一份, 所以不要繞過這個機制。要改 minor / major 就先手動 npm version minor --no-git-tag-versionpack 不遞增(它只產 win-unpacked 供開發驗證,不是散布用的檔案)。

產物在 dist\

檔案 大小 說明
Claude Pet-<版本>-win.zip ~140 MB 免安裝資料夾版。解壓後點兩下 Claude Pet.exe。啟動最快,寵物就放旁邊的 pets\
Claude Pet-<版本>-setup.exe ~100 MB NSIS 安裝檔。per-user、可改安裝路徑、建立桌面與開始選單捷徑
Claude Pet-<版本>-portable.exe ~100 MB 免安裝單檔,點兩下直接跑
win-unpacked\ ~200 MB 上面 zip 的未壓縮版,整個資料夾複製走也能用

第一次建置時 electron-builder 會從 GitHub 抓 NSIS 與相關工具到 %LOCALAPPDATA%\electron-builder\Cache,需要網路,之後走快取。

portable 單檔版的代價

NSIS 的 portable 外殼每次啟動都會把整包解壓到 %TEMP%\ClaudePet程式結束後又整個刪掉(見 app-builder-lib/templates/nsis/portable.nsiRMDir /r $INSTDIR)。所以:

  • 每次開都要重新解壓約 200 MB,比資料夾版慢好幾秒。要開機自動啟動的話建議用資料夾版
  • 程式本體的位置是暫時的,因此 hook 腳本會另外複製一份到 %USERPROFILE%\.claude-pet\hook\,用那個穩定路徑去註冊。否則寵物沒開的時候 Claude Code 每次觸發 hook 都會找不到檔案而報錯。
  • 使用者自己的寵物要放在那顆 exe 旁邊pets\(程式第一次跑會自己建),放進解壓目錄的會跟著被刪掉。同 id 時以 exe 旁邊那份為準。
  • 程式被強制結束(工作管理員)時外殼來不及清理,%TEMP%\ClaudePet 會留下約 300 MB,可以手動刪。

打包後和原始碼跑不一樣的地方

都已經處理好,改程式時要留意:

項目 開發 安裝/資料夾版 portable 單檔
內附寵物 <專案>\pets <exe 旁>\pets %TEMP%\ClaudePet\pets
使用者寵物 同上 同上 <exe 旁>\pets
hook 腳本 <專案>\hook\ resources\app.asar.unpacked\hook\ %USERPROFILE%\.claude-pet\hook\
開機自動啟動 electron.exe + 專案路徑 那顆 exe PORTABLE_EXECUTABLE_FILE

判斷邏輯都在 lib/paths.jsisPackaged()process.defaultAppisPortable()PORTABLE_EXECUTABLE_FILEunpacked()app.asar 換成 app.asar.unpacked

  • pets/extraFiles 放到 exe 旁邊(不是 extraResources,那會進 resources\),使用者直接把寵物資料夾丟進去就會出現,不用碰 asar。
  • hook/asarUnpack,因為 Claude Code 是用 node <路徑> 執行它,而 node 讀不到 asar 裡的檔案。
  • hook 指令仍然需要 node 在 PATH 裡。路徑含空白時會自動加引號。

換圖示就換掉 build/icon.ico(要含 256×256)。目前這顆是從 pets/xiao-nian/spritesheet.webp 第 0 列第 6 欄(中立正面)取頭肩方形裁切產生的。

發佈到 Gitea Releases

$env:GITEA_TOKEN = "<token>"
npm run release -- --dry-run   # 只檢查:產物、附件限制、release 是否已存在,不做任何寫入
npm run release                # 實際發佈

會建立 tag v<版本> 的 release(說明自動取自上一個 tag 之後的 commit 標題), 並上傳 dist\ 裡的 .exe.zip。同名附件會先刪再傳,所以可以重跑。 .blockmap 不上傳——那是 electron-updater 的差分更新才需要,本專案沒有用到。

前置一:伺服器設定。 Gitea 預設的附件上限是 100 MB,而且 ALLOWED_TYPES 白名單沒有 .exe 三個產物全部會被擋下。在 app.ini 調整後重啟 Gitea

[attachment]
MAX_SIZE = 300
ALLOWED_TYPES = */*

腳本會先讀 /api/v1/settings/attachment 做預檢,設定不足時會直接列出哪個檔案卡在哪一條、 不會傳到一半才失敗。

前置二:token。 到 Gitea 的「設定 → 應用程式 → 產生新的權杖」, 勾選 write:repository(只有套件庫權限的 token 不行,repo API 會回 403)。 放環境變數就好,不要寫進檔案。

其他環境變數

變數 用途
GITEA_TOKEN 必填,需 write:repository
GITEA_URL 網頁網址與 remote 主機不同時覆寫(預設 https://<remote 主機>)。SSH 走 8022 埠不影響,腳本只取主機名

疑難排解

Electron 裝完沒有 electron.exe

npm install 成功但 node_modules\electron\dist\ 是空的 → 你的 ~/.npmrc 可能有 allow-scripts=...(npm 11 的供應鏈防護,只放行清單上套件的 postinstall)。兩種解法:

node node_modules\electron\install.js                                # 手動跑一次下載
# 或把 electron 加進白名單
npm config set allow-scripts=@anthropic-ai/claude-code,electron --location=user

寵物對 Claude Code 沒反應

  1. 確認 hooks 已安裝:npm run hooks:install,並重開 Claude Code session
  2. 確認寵物在跑:curl http://127.0.0.1:17333/health 應回 ok
  3. %USERPROFILE%\.claude-pet\events.log 有沒有事件進來。有事件但沒動 → renderer 問題;沒事件 → hook 沒被呼叫。
  4. hook 指令是 node E:/.../claude-pet-hook.jsClaude Code 執行 hook 的環境要找得到 nodePATH)。

啟動時跳出「無法監聽 127.0.0.1:17333」

已經有一隻在跑(本程式有單一實例鎖,通常不會發生),或埠被別的程式佔用。改設定檔 port,並在 Claude Code 的環境設 CLAUDE_PET_PORT 為同一個值。

她跑到螢幕外 / 換了螢幕配置看不到

選單「回到預設位置」,或刪掉設定檔的 position。啟動時若座標不在任何螢幕的工作區內會自動拉回。

專案搬家了

重新執行 npm run hooks:installnpm run autostart:install(兩者都以目前路徑重寫),寵物資料夾會自動跟著程式走。

移除

先在系統匣選單關掉 開機自動啟動、並用 Claude Code hooks → 移除 拆掉 hooks,然後結束程式。從原始碼跑的話也可以下指令:

npm run hooks:uninstall        # 從 ~/.claude/settings.json 移除所有 Claude Pet hook(先備份)
npm run autostart:uninstall    # 移除開機啟動登錄值

裝過安裝檔的話,到「設定 → 應用程式 → 已安裝的應用程式」移除 Claude Pet(解除安裝不會動到 %USERPROFILE%\.claude-pet\)。最後刪掉專案資料夾與 %USERPROFILE%\.claude-pet\ 即可。


致謝

  • 寵物規格與動畫時序來自 OpenAI Codex 的 hatch-pet skillCodex V2 pet contract)。
  • 內附寵物「小念」由 Codex /hatch 產生。
S
Description
No description provided
Readme
4.3 MiB
2026-08-25 07:21:39 +00:00
Languages
JavaScript 97.6%
CSS 1.8%
HTML 0.6%