/buddy 敲下去,跳出一隻 common 的 snail。你盯著那隻蝸牛三秒鐘,心想:我想要的是 legendary shiny dragon,不是這個。

問題是,官方沒有給重新 Roll 的入口。寵物的外觀、物種、稀有度全部由 hash(userID + SALT) 確定性生成——同一個 userID 永遠會得到同一隻寵物。想換?得從底層動手。

這篇把整個寵物系統的原理拆給你看,然後教你怎麼用腳本暴力搜尋到你想要的組合,最後寫進設定檔完成替換。手動派和懶人派的方法都有。

寵物系統長什麼樣

兩層架構:骨架跟靈魂

寵物分成兩層,儲存方式完全不同:

Bones(骨架)——外觀、物種、稀有度、屬性值。不存在任何檔案裡,每次都是由 hash(userID + SALT) 即時計算出來的。換句話說,只要 userID 一樣,骨架就永遠一樣。

Soul(靈魂)——名字和性格。存在 ~/.claude.jsoncompanion 欄位裡。這部分可以隨便改。

1
2
3
4
5
6
7
// src/buddy/companion.ts
const SALT = 'friend-2026-401'

export function companionUserId(): string {
const config = getGlobalConfig()
return config.oauthAccount?.accountUuid ?? config.userID ?? 'anon'
}

注意優先順序:accountUuid 優先於 userID。如果你是 OAuth 登入的使用者,buddy 預設會拿 accountUuid 當種子,不是 userID。這個細節後面會很重要。

userID 到底是啥

首次啟動 Claude Code 時隨機生成的 32 位元組 hex 字串。用途只有三個:遙測分析、A/B 分桶、buddy 種子。跟你的對話歷史、API key、本地設定完全無關。換掉它,什麼功能都不會受影響。

18 種物種 x 5 稀有度

物種清單

duck、goose、blob、cat、dragon、octopus、owl、penguin、turtle、snail、ghost、axolotl、capybara、cactus、robot、rabbit、mushroom、chonk——總共 18 種。

稀有度與機率

稀有度 權重 機率
common 60 60%
uncommon 25 25%
rare 10 10%
epic 4 4%
legendary 1 1%

其他可變屬性

眼睛有 6 種:· × @ °

帽子有 8 種:none、crown、tophat、propeller、halo、wizard、beanie、tinyduck

屬性值有 5 項:DEBUGGING / PATIENCE / CHAOS / WISDOM / SNARK,各自 0-100 隨機。

Shiny 閃光版:1% 機率。疊上 legendary 的 1%,想拿到一隻 legendary shiny 特定物種的機率大概是十萬分之五——不靠腳本基本上沒戲。

Hash 演算法的坑:必須用 Bun

Claude Code 的二進位檔是 Bun 打包的,hash 用的是 Bun.hash()。你可能想用 Node.js 跑搜尋腳本省事,但 Node.js 的 FNV-1a fallback 跟 Bun.hash() 的結果完全不同

1
2
3
4
5
6
7
8
9
10
11
12
function hashString(s) {
if (typeof Bun !== 'undefined') {
return Number(BigInt(Bun.hash(s)) & 0xffffffffn) // Bun(正確)
}
// FNV-1a fallback(Node.js,結果不對)
let h = 2166136261
for (let i = 0; i < s.length; i++) {
h ^= s.charCodeAt(i)
h = Math.imul(h, 16777619)
}
return h >>> 0
}

用 Node.js 跑出來的 userID 寫進設定,領養到的寵物會跟你預期的完全不同。踩這個坑會浪費你一整個下午,別問我怎麼知道的。

結論:搜尋腳本必須用 bun 執行。

手動方法:完整操作流程

適合想完全理解原理、不依賴第三方工具的人。

Step 1:建立搜尋腳本

建立 ~/buddy-reroll.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#!/usr/bin/env bun
const crypto = require('crypto')

const SALT = 'friend-2026-401'
const SPECIES = ['duck','goose','blob','cat','dragon','octopus','owl',
'penguin','turtle','snail','ghost','axolotl','capybara','cactus',
'robot','rabbit','mushroom','chonk']
const RARITIES = ['common','uncommon','rare','epic','legendary']
const RARITY_WEIGHTS = { common:60, uncommon:25, rare:10, epic:4, legendary:1 }
const EYES = ['·', '✦', '×', '◉', '@', '°']
const HATS = ['none','crown','tophat','propeller','halo','wizard','beanie','tinyduck']
const STAT_NAMES = ['DEBUGGING','PATIENCE','CHAOS','WISDOM','SNARK']

function mulberry32(seed) {
let a = seed >>> 0
return function() {
a |= 0; a = (a + 0x6d2b79f5) | 0
let t = Math.imul(a ^ (a >>> 15), 1 | a)
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t
return ((t ^ (t >>> 14)) >>> 0) / 4294967296
}
}

function hashString(s) {
return Number(BigInt(Bun.hash(s)) & 0xffffffffn)
}

function pick(rng, arr) { return arr[Math.floor(rng() * arr.length)] }

function rollRarity(rng) {
let roll = rng() * 100
for (const r of RARITIES) { roll -= RARITY_WEIGHTS[r]; if (roll < 0) return r }
return 'common'
}

function generateBuddy(uid) {
const rng = mulberry32(hashString(uid + SALT))
const rarity = rollRarity(rng)
const species = pick(rng, SPECIES)
const eye = pick(rng, EYES)
const hat = pick(rng, HATS)
const shiny = rng() < 0.01
const stats = {}
for (const s of STAT_NAMES) stats[s] = Math.floor(rng() * 101)
return { rarity, species, eye, hat, shiny, stats }
}

// === 搜尋條件(改成你要的)===
const TARGET_SPECIES = 'capybara'
const TARGET_RARITY = 'legendary'
const REQUIRE_SHINY = true
const MAX = 80_000_000

for (let i = 0; i < MAX; i++) {
const uid = crypto.randomBytes(32).toString('hex')
const b = generateBuddy(uid)
if (b.species === TARGET_SPECIES && b.rarity === TARGET_RARITY && b.shiny) {
console.log(`${b.rarity} shiny ${b.species} | Eye: ${b.eye} | Hat: ${b.hat}`)
console.log(`Stats: ${JSON.stringify(b.stats)}`)
console.log(`UID: ${uid}`)
}
}

TARGET_SPECIESTARGET_RARITYREQUIRE_SHINY 改成你要的組合。不要求 shiny 的話把條件拿掉,速度會快很多。

Step 2:用 Bun 跑搜尋

1
bun ~/buddy-reroll.js

Legendary 通常 50 萬次迭代內就能找到,幾秒鐘的事。Legendary + Shiny 則需要幾千萬次,跑個幾分鐘。找到後記下 UID。

Step 3-9:寫入設定檔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Step 3:備份
cp ~/.claude.json ~/.claude.json.backup

# Step 4:取得 OAuth Token
claude setup-token
# 複製輸出的 token

# Step 5:重設設定檔
echo '{"hasCompletedOnboarding":true,"theme":"dark"}' > ~/.claude.json

# Step 6:用 Token 初始化(不寫入 accountUuid)
CLAUDE_CODE_OAUTH_TOKEN="<你的Token>" claude -p "exit"

# Step 7:寫入目標 userID
bun -e "
const fs = require('fs');
const config = JSON.parse(fs.readFileSync(process.env.HOME + '/.claude.json', 'utf8'));
config.userID = '<你搜尋到的UID>';
delete config.companion;
if (config.oauthAccount) delete config.oauthAccount.accountUuid;
fs.writeFileSync(process.env.HOME + '/.claude.json', JSON.stringify(config, null, 2));
"

# Step 8:領養!
CLAUDE_CODE_OAUTH_TOKEN="<你的Token>" claude
# 輸入 /buddy

# Step 9:永久設定
echo 'export CLAUDE_CODE_OAUTH_TOKEN="<你的Token>"' >> ~/.zshrc

為什麼 Step 6 要用環境變數而不是正常登入?因為正常 OAuth 登入會把 accountUuid 寫進設定檔,而 companionUserId() 會優先用 accountUuid,你辛苦搜來的 userID 就被忽略了。用環境變數登入不會寫 accountUuid/buddy 才會改用 userID 當種子。

番外:社群 CLI 工具

不想自己寫腳本?社群已經有幾款開源工具把整個流程包好了。這是給一般使用者最簡單的路線——一行指令就能換到你想要的寵物,連 OAuth Token 都不用碰。

為什麼會有現成工具?

手動方法的核心是「換 userID 種子」,但需要繞開 accountUuid 優先權,所以要操作環境變數和重設設定檔。社群工具走的是另一條路:不換 userID,改去暴力搜尋一個替代的 salt 字串,讓 hash(原 accountUuid + 新 salt) 符合你的目標,然後直接修補 Claude Code 的 binary。使用者不用碰 ~/.claude.json,也不用設環境變數。

三大工具一覽

工具 風格 特色
any-buddy 互動式 TUI 23 個預設組合、即時 ASCII 預覽、支援多寵物切換
buddy-reroll CLI 為主 輕量直接、Bun 加速、--doctor 系統檢查
cc-buddy 雙模式 EN/中文 UI、物種圖鑑、自我測試

方法 A:any-buddy(推薦初心者)

互動式 TUI,會引導你一步步完成所有步驟,每步都有即時 ASCII 預覽,適合喜歡視覺化操作的人。

安裝與啟動

1
npx any-buddy@latest

第一次執行會自動下載,無需手動安裝。也可以全域安裝:

1
npm install -g any-buddy

互動模式

直接執行 any-buddy 進入 TUI,會讓你選擇:

  1. Build — 自己挑物種、稀有度、眼睛、帽子
  2. Browse presets — 從 23 個預設組合挑選(如 “Arcane Dragon”)
  3. Switch buddies — 切換已儲存的寵物

選完後工具會自動執行 salt 搜尋、修補 binary、設定 hook,全程在 TUI 內完成。

一行指令模式

1
2
3
4
5
6
7
8
# 傳說級閃光龍,戴巫師帽,自訂名字 Draco
any-buddy -s dragon -r legendary -e '✦' -t wizard --shiny --name Draco -y

# 史詩級貓
any-buddy -s cat -r epic -y

# 用預設組合
any-buddy --preset "Arcane Dragon" -y

-y 旗標跳過所有確認,適合寫進 script。

常用指令速查

動作 指令
看目前寵物 any-buddy current
預覽不套用 any-buddy preview
還原原始寵物 any-buddy restore
更新後重新套用 any-buddy apply
切換已存寵物 any-buddy buddies
重新孵化 any-buddy rehatch

CLI 旗標完整列表

1
2
3
4
5
6
7
8
9
10
11
12
13
-s, --species <name>   物種(duck, cat, dragon, ...,共 18 種)
-r, --rarity <level> 稀有度(common ~ legendary)
-e, --eye <char> 眼睛(· ✦ × ◉ @ °)
-t, --hat <name> 帽子(crown, tophat, propeller, halo, wizard, beanie, tinyduck)
-n, --name <name> 自訂寵物名字
-p, --personality 自訂性格(影響對話氣泡)
--preset <name> 使用預設組合
--shiny 強制要求 shiny 變體(搜尋時間 ~100x)
--peak <stat> 指定最高數值
--dump <stat> 指定最低數值
-y, --yes 跳過所有確認
--no-hook 不安裝自動 patch hook
--silent 靜默模式(適合 hook)

方法 B:buddy-reroll(輕量派)

沒有 TUI,純 CLI,適合喜歡一行指令搞定、寫進 dotfile 的人。底層使用 wyhash,跟 /buddy 的 hash 演算法完全一致。

安裝與啟動

1
2
3
4
5
# Bun(推薦,原生 hash 較快)
bunx buddy-reroll

# 或使用 Node.js(>= 20)
npx buddy-reroll

直接指定屬性

1
2
3
4
5
6
7
8
# 全部指定
buddy-reroll --species dragon --rarity legendary --eye ✦ --hat propeller --shiny

# 部分指定,其餘隨機
buddy-reroll --species cat --rarity epic

# 優化數值:最高 WISDOM、最低 CHAOS
buddy-reroll --peak WISDOM --dump CHAOS

Hook 持久化(重要)

Claude Code 自動更新會重設寵物,啟用 hook 自動恢復:

1
2
buddy-reroll --hook       # 啟用持久化(執行一次即可)
buddy-reroll --unhook # 停用

啟用後,每次 Claude 啟動都會自動套用你選的寵物。

系統檢查與還原

1
2
3
4
buddy-reroll --doctor     # 檢查安裝路徑、寫入權限、設定狀態
buddy-reroll --current # 顯示目前的寵物設定
buddy-reroll --list # 列出所有可用選項
buddy-reroll --restore # 還原成原始寵物

方法選擇建議

你的情況 推薦方法
只想快速換一隻寵物,喜歡視覺操作 any-buddy(互動式預覽最直觀)
喜歡 CLI、想寫進 dotfile buddy-reroll(一行搞定)
想完全掌握原理、不依賴第三方 手動腳本(見上方「完整操作流程」)
Team / Pro 帳號 any-buddy 或 buddy-reroll(已內建 accountUuid trap fix)

注意事項

這篇基於 Claude Code 2.1.89+ 版本分析。如果後續版本更改 SALT 值或 hash 演算法,腳本需要對應調整。

Claude Code 自動更新會覆蓋 binary,社群工具改的東西就沒了。務必啟用 --hook 持久化。手動腳本的使用者因為是改 userID 而不是改 binary,不受自動更新影響。

社群工具都是 MIT 授權、公開原始碼,但會修改 Claude Code 的 binary。介意的人請用手動腳本方法,只改設定檔不碰 binary。

macOS 上修改 binary 後需要 re-sign,社群工具都會自動處理。環境需求:Node.js >= 20,或安裝 Bun runtime。

Shiny 搜尋很慢——1% 機率疊上 legendary 的 1% 再疊上特定物種的 1/18,期望搜尋次數大約是 18 萬次。耐心等幾分鐘就好,Bun 的速度夠快。


一隻 legendary shiny capybara 戴著巫師帽,WISDOM 95 分。值得花五分鐘搞定嗎?值得。