claude-mem 深度拆解:Claude Code 的跨 session 記憶,以及 worktree 合併後記憶怎麼歸位
同一個 repo 開三個 git worktree 平行跑三條線:一條修 bug、一條做 feature、一條重構。每條線各自開 Claude Code session,每個 session 都要重新問一次「這個模組在幹嘛」。三天後分支合回 main,那些踩過的坑、繞過的雷、做過的取捨,全部留在各自的 session 裡,一條都沒回到主線。
這是 coding agent 現在最貴的隱性成本——不是 token 貴,是你重複解釋的時間貴。
Alex Newman(GitHub @thedotmack)做的 claude-mem 就是在解這件事。它是一個 Claude Code plugin,把 agent 每次 session 實際做過的事壓成可搜尋的 observation,下次開 session 自動注入回去。而我覺得最值得單獨拿出來講的,是多數介紹文都跳過的那一塊:它處理了 git worktree 合併之後,記憶要怎麼歸位。
先講一個必須更正的地方。這篇是從 v13.16.0 這版切進來的,但翻過 release note 之後:v13.16.0(2026-08-25 發布)的主軸其實是新的 claude-mem-cowork plugin,不是 worktree。worktree adoption 是 v12.2.0(2026-04-18) 就上的功能,v13.12.4 才把它的競態與 log bug 修掉。本文講的是「到 v13.16.0 為止,這套東西長什麼樣、怎麼把它用起來」。
以下所有機制都對照 repo 原始碼與官方 docs 查核過,數字附出處。
本文大綱
一、它要解決什麼
Claude Code 每開一個 session 就是一張白紙。你在上個 session 裡花了 40 分鐘搞清楚「這個 worker 為什麼要 fire-and-forget」,session 一關,那 40 分鐘的理解就只剩你腦裡有。CLAUDE.md 能補一部分,但它是你手寫的——你不會把每一次除錯的過程都寫進去。
claude-mem 的做法是把「記錄」這件事自動化:agent 每呼叫一次工具,就把這次操作丟給一個便宜的 observer 模型壓成一段結構化敘述,存進本地資料庫;下次開 session 時,把相關的部分注入 context。
專案數據(2026-08-26 由 GitHub API 查得):Apache-2.0 授權,2025-08-31 建立,91,841 stars / 8,065 forks / 304 個開放 issue,npm 上已經發到第 201 個版本。這個 issue 數與版本頻率本身就是資訊,後面談限制時會回頭講。
二、運作原理:5 個 hook、1 個 worker、2 個索引

2.1 寫入路徑
官方文件把它描述成一套 5 階段 hook:SessionStart、UserPromptSubmit、PostToolUse、Stop、SessionEnd(實際上是 6 個 hook script,多一個做版本檢查的 pre-hook)。
關鍵設計是:hook 從不阻塞你的 session。文件寫得很明白,hook 對 worker 是 fire-and-forget HTTP、2 秒 timeout。IDE 這端只負責把事件丟出去,壓縮工作全部在另一個進程做。
那個進程就是 worker service:一個由 Bun 管理的 Express HTTP server,預設綁 127.0.0.1,埠號是 37700 + (uid % 100)——用 uid 取模,是為了讓同一台機器上不同使用者不會撞埠。
worker 收到事件後交給 observer 模型。預設是 claude-haiku-4-5-20251001,走你的 Claude 訂閱;也可以切成 Gemini 或任何 OpenAI-compatible 端點(CLAUDE_MEM_PROVIDER)。輸出的 observation 是有 schema 的,不是自由文字。以預設的 code mode 來說,型別共九種:bugfix、feature、refactor、change、discovery、decision、security_alert、security_note、sensitive(最後一個是 v13.13.0 才加的);另外還有七種概念標籤:how-it-works、why-it-exists、what-changed、problem-solution、gotcha、pattern、trade-off。
落地時寫兩個索引:SQLite + FTS5 負責關鍵字全文檢索,Chroma 向量庫 負責語意檢索。兩邊是同一份資料的兩種索引,不是兩份資料。
2.2 取回路徑:3 層漸進揭露
這是 claude-mem 設計上最值得抄的一段,也是它跟「把記憶一股腦塞進 system prompt」的方案差最多的地方。
mem-search skill 規定了一個固定順序,而且明文寫著「絕對不要在沒過濾前就抓完整內容」:
search(query, limit, project)— 回一張只有 ID/時間/型別/標題的索引表,README 標的成本是 每筆約 50–100 tokens。timeline(anchor, depth_before, depth_after)— 以某筆為中心,把前後發生的事按時序拉出來。get_observations(ids=[...])— 只對你篩過的 ID 展開完整內容(title、subtitle、narrative、facts、concepts、files),每筆約 500–1,000 tokens。
README 宣稱這樣「約省 10 倍 token」。這個數字要當成設計意圖看,不是實測基準——它其實就是 50–100 對 500–1,000 的比值直接推出來的,官方沒有給對照實驗。真正該記住的是那個原則:先看目錄,再決定翻哪一頁。
search 可用的參數是明確的:query、limit(預設 20、上限 100)、project、type(observations / sessions / prompts)、obs_type、dateStart/dateEnd、offset、orderBy(date_desc / date_asc / relevance)。你要它「找上週那個 auth 的 bug」時,直接在 prompt 裡把條件講清楚,它就會帶對參數:
用 mem-search 找:這個專案上週的 auth 相關 bugfix,先只給我索引表,我挑完你再展開。
2.3 順手一提:File Read Gate
同一套哲學還延伸出一個 PreToolUse hook。當 agent 要 Read 一個檔案、而這個檔案在資料庫裡有歷史 observation 時,這個 gate 會擋下讀取,改回一張「這個檔案過去被動過哪些手腳」的時間線,讓 agent 自己決定最便宜的路徑。
文件把判斷條件寫得很細:檔案小於 1,500 bytes 直接放行(時間線比檔案本身還貴)、專案被排除則放行、查不到 observation 則放行;有結果時每個 session 去重、依相關度排序、最多 15 筆。回給 agent 的訊息會列出四種選項與各自成本:語意喚醒(0 額外 token)、get_observations([IDs])(每筆約 300)、smart_outline / smart_unfold(約 1–2k)、真的整份讀。
這個設計本身就是一堂 context engineering 的課:不要幫 agent 決定,要把成本標好讓它自己選。
三、worktree:平行開發把記憶切碎了

現在講重點。用 git worktree 平行開多條線的人會遇到一個很具體的問題:記憶該掛在誰名下?
掛在 worktree 目錄名下,主 repo 就看不到;全部掛在主 repo 名下,三條線的記憶就會互相污染——你在修 bug 的 session 裡會被塞進重構分支的 context。
claude-mem 的解法分成兩段。
3.1 寫入時:複合 project key
src/utils/worktree.ts 的 detectWorktree() 判斷方式很樸素但可靠:去 stat <cwd>/.git。如果它是目錄,那是一般 repo;如果它是檔案,讀出裡面的 gitdir: ... 指標,比對它是不是長成 <parent>/.git/worktrees/<name> 的形狀。是的話,就取得 parentRepoPath 與 worktreeName。
接著 src/utils/project-name.ts 的 getProjectContext() 把它組成複合鍵:
primary: myapp/feat-auth // 這個 worktree 自己的身分
parent: myapp
isWorktree: true
allProjects: [myapp, myapp/feat-auth]
兩個效果一次到位:寫入用 myapp/feat-auth,所以不同 worktree 的記憶不會互串;讀取用 allProjects 兩個名字,所以在 worktree 裡開的 session 讀得到主 repo 的既有記憶。
這裡有兩個修過的坑值得知道,因為它們決定你能不能從子目錄啟動 session:getProjectName() 會先用 git rev-parse --show-toplevel 找 repo 根(#2663),getProjectContext() 也會先解析出 working-tree 根再去 detectWorktree(#3262)——因為 .git 只存在於 worktree 根目錄。所以在 src/services/ 底下開 session,複合鍵依然正確。
3.2 合併後:merged_into_project 是「指標」不是「搬家」
分支併回 main 之後呢?repo 裡的 .plan/worktree-adoption.md 把目標寫得很清楚:
當一個 worktree 的分支被併回它的 parent,這個 worktree 的 observation 要成為 parent 專案 observation 清單的一部分——不搬移資料、不做破壞性 schema 變更、不遺失來源證明(provenance)。
實作是這樣:
- schema:在
observations與session_summaries各加一個可為 null 的欄位merged_into_project,並各建一個索引。用PRAGMA table_info做冪等守衛(SQLite 不支援ALTER TABLE ... ADD COLUMN IF NOT EXISTS)。 - 查詢:既有的查詢條件加一段
OR merged_into_project = :parent。 - 原則:
observations.project是不可變的來源證明,永不覆寫。合併狀態只是一個虛擬指標。 - Chroma:向量庫的 metadata 跟 SQLite 同步更新(同一個共用 collection
cm__claude-mem,用 metadata 分區),確保語意搜尋看到的世界跟關鍵字搜尋一致。SQLite 是真相來源;Chroma 更新失敗只記 log、不回滾 SQL,下次重跑會補上(同值寫入是 no-op)。
這個設計的好處很實際:合併之後你在主 repo 問「JWT 那段當初為什麼這樣寫」,答得出來;同時 UI 上那筆記憶還帶著「來自 feat-auth,已併入」的徽章,你知道它的出處。
3.3 判定交給 git,而這正是坑所在
偵測邏輯在 src/services/infrastructure/WorktreeAdoption.ts,全部走 git subprocess(plan 裡明文禁止引入 gh 依賴):
git rev-parse --path-format=absolute --git-common-dir→ 解析出主 repo 路徑。git worktree list --porcelain→ 列出所有 worktree 與各自的分支。git branch --merged HEAD→ 取得已合併分支集合,跟上一步取交集。- 對交集裡的每個 worktree,
UPDATE ... SET merged_into_project = :parent WHERE project = :composite AND merged_into_project IS NULL。
這裡就是那個坑:git branch --merged 只認得得出 ancestry 的合併。GitHub 上絕大多數團隊按的是 Squash and merge,squash 之後原分支的 commit 不是 HEAD 的祖先,--merged 完全看不到它。rebase merge 同理。
plan 文件自己承認了這點,解法是留一個 CLI 逃生門:
# 先看會動到什麼,不寫入
npx claude-mem adopt --dry-run
# squash merge 的分支,手動指定
npx claude-mem adopt --branch feat-auth
照做建議:如果你的團隊用 squash merge,就把 npx claude-mem adopt --branch <name> 加進你刪 worktree 的那個腳本裡。順序是「先 adopt、再 git worktree remove」——目錄還在的時候路徑才解析得出來。
另外,worker 啟動時會自動跑一次 adoption,而且是 fire-and-forget、不會 await(#2122),所以它失敗不會擋住 worker 起來。v13.12.4 把這個 kick 移到 DB 初始化之後,因為它自己開的寫入連線會跟開機時的 migration writer 搶 WAL 單寫者,噴 database is locked。
四、上手:從安裝到第一次 adopt
環境需求:Node.js ≥ 20(npm package 的 engines 寫的是 >=20.12.0)、Bun、uv(給 chroma-mcp 用的 Python 套件管理器,預設 Python 3.13)、SQLite 3。Bun 與 uv 缺的話安裝器會自己裝。
1) 安裝(二選一)
npx claude-mem install
或在 Claude Code 裡:
/plugin marketplace add thedotmack/claude-mem
/plugin install claude-mem
2) 檢查它真的活著
npx claude-mem status # worker 狀態
npx claude-mem doctor # 診斷 bun / uv / worker 健康度
3) 調設定 — 全部在 ~/.claude-mem/settings.json,改完即時生效(換 mode 要重開 Claude Code):
{
"CLAUDE_MEM_MODE": "code--zh",
"CLAUDE_MEM_CONTEXT_OBSERVATIONS": "30",
"CLAUDE_MEM_MODEL": "claude-haiku-4-5-20251001",
"CLAUDE_MEM_LOG_LEVEL": "INFO"
}
幾個實際會用到的鍵:
CLAUDE_MEM_CONTEXT_OBSERVATIONS:開場注入幾筆 observation,預設 50。覺得開場太肥就往下調。CLAUDE_MEM_SKIP_TOOLS:不記錄哪些工具,預設已排除ListMcpResourcesTool,SlashCommand,Skill,TodoWrite,AskUserQuestion。CLAUDE_MEM_EXCLUDED_PROJECTS:整個專案不記(客戶專案、含機敏資料的 repo 建議直接列進來)。CLAUDE_MEM_DATA_DIR:資料根目錄,預設~/.claude-mem,DB/Chroma/logs/settings 全部從這裡派生。CLAUDE_MEM_TELEGRAM_TRIGGER_TYPES:預設security_alert,sensitive,這兩種型別會推 Telegram 通知;設成空字串就關掉。
4) 語言:沒有 zh-TW,自己做一個
plugin/modes/ 裡有 30 個語言 mode,中文只有 code--zh(簡體)。要繁中的話,v13.13.1 加的 /mode-creator 是設計來幹這件事的——它會問你的領域與筆記需求,產出自訂 mode 並裝進使用者目錄。
5) 隱私
對話裡用 <private> 包起來的內容,agent 當下看得到、但不會被存成 observation。貼 production 連線字串、內部網址、客戶名稱時就用它。另外遙測預設是開的,npx claude-mem telemetry disable 關掉。
6) worktree 流程
git worktree add ../myapp-feat-auth -b feat-auth
# ... 在 worktree 裡做事,記憶自動掛在 myapp/feat-auth
# 合併之後、刪 worktree 之前:
npx claude-mem adopt --dry-run
npx claude-mem adopt --branch feat-auth # squash merge 才需要 --branch
git worktree remove ../myapp-feat-auth
五、數據與限制(誠實版)
成本。 v13.14.0 的安裝器現在會把每個 provider 的價格直接印在選單上,以每 1,000 筆 observation 計:CMEM Pro 每千筆 $0(月費 $30,含雲端同步)、OpenRouter 約 $2.73、Gemini API 約 $3.39、走你自己的 Anthropic 方案約 $8.91。要注意這些是 claude-mem 自己算出來的標籤——release note 說得很白,數字由單一常數 ratePerM × TOKENS_PER_OBSERVATION / 1000 推導,不是第三方量測。用它來比較四個選項的相對高低是合理的,拿去當預算依據就要自己再驗一次。
延遲。 有二手文章寫「每個工具 60–90 秒」,我沒在官方 repo 或 docs 找到這個數字的出處,所以不引用。從架構看,這件事本來就是非同步的:hook 是 fire-and-forget,observation 在背景生成,它不會擋住你的 session,但也意味著剛剛做的事不一定馬上搜得到。
穩定性。 這是要講清楚的部分。304 個開放 issue、201 個 npm 版本,而且近幾版的 release note 全是硬故障:v13.12.3 修的是一個自我延續的 stale-worker 迴圈,有使用者回報一天內 recycle 2,424 次,每次 UserPromptSubmit 都以約 40 秒的 hook timeout 收場;v13.12.4 修 FOREIGN KEY constraint failed 導致 worker 永遠不 ready;v13.15.2 修的是 observer 靜默死掉(多半是 Pro 額度用完)只丟 OpenRouter upstream error (status 502) 重試迴圈。
這些都修了,而且修得很紮實(v13.12.2 一次合併 54 個社群修復 PR,還把合併準則寫成 docs/merge-rubric.md,明文禁止 guard、circuit breaker、fallback、retry 這類「補丁式」修法,只收根因修正)。但頻率本身說明一件事:這是一個高速迭代中的工具,不是一個穩定件。 開自動更新,並且知道遇到怪事時第一招是 npx claude-mem doctor。
還有一個很值得記住的事故。 v13.12.4 揭露:repo 根目錄 CLAUDE.md 裡的維護者指令(包含一段「自動升級並 commit」的指示)原封不動出貨給每一個從 marketplace git-clone 安裝的使用者,而且被使用者端的 Claude 實例照做了。這不是 claude-mem 獨有的問題——它是所有「plugin 帶 CLAUDE.md」的分發模型共有的攻擊面。裝任何 agent plugin 之前,去看一眼它 repo 根目錄的 CLAUDE.md / AGENTS.md 寫了什麼。
六、什麼時候該用、什麼時候別用
該用:
- 長期維護的單一大 repo,你每週都在同一批檔案裡打轉。observation 累積得越久,
search越有價值。 - 重度使用 git worktree 平行開發。這是它相對其他記憶方案最明確的差異化功能。
- 交接/輪班場景。
timeline-report、weekly-digests這些內建 skill 是拿 observation 生時序報告的,比翻 commit log 有脈絡。
別用(或至少先想清楚):
- 含機敏資料的 repo。observer 會讀到 agent 讀到的東西。至少把它加進
CLAUDE_MEM_EXCLUDED_PROJECTS,或全程用<private>。v13.16.0 有強化憑證遮罩(短字串、含空白值、任意 Authorization scheme、Cookie header、URI userinfo),但遮罩永遠是盡力而為。 - 短命的 throwaway 專案。記憶的價值來自累積,一次性腳本不值得付這個成本。
- 對 session 啟動延遲極度敏感。多一層 hook、多一個 worker、開場多注入 50 筆 observation,就是有代價。
- 團隊要共享記憶。本地模式下記憶是你個人的;跨機器/跨人共享要走 cmem.ai 的雲端同步,那是付費且要把資料送出去的決定。
七、就算你不裝,也該抄走的三件事
- 漸進揭露要有價目表。 File Read Gate 最聰明的不是「擋讀取」,是回給 agent 的訊息裡把四種選項的 token 成本都標出來。你自己寫工具描述時可以照抄這招——在 tool description 裡標成本,讓模型自己做取捨,比你硬性限制有效。
- provenance 欄位不可變,狀態變更用指標。
project永不覆寫、只加merged_into_project——這是任何要做資料合併的系統都該學的模式:合併是視圖層的事,不是儲存層的事。出錯了可以回退,也永遠答得出「這筆資料原本是哪來的」。 - 判定交給權威來源,並為權威的盲區留逃生門。 合併偵測全部問 git,不猜;但作者清楚知道
--merged看不到 squash,所以留了--branch。知道自己權威來源的盲區在哪,比假裝沒有盲區重要得多。
最後給一句實話:如果你是單 repo、單線開發,claude-mem 帶來的複雜度可能超過它省下的重複解釋。但如果你已經在用 worktree 平行跑多條線——那個「分支合了、記憶沒合」的痛,它是目前少數真的正面處理了的工具。
來源
- GitHub — thedotmack/claude-mem(Alex Newman / @thedotmack,Apache-2.0)
- v13.16.0 release notes、v13.15.2、v13.14.0、v13.13.0、v13.12.2–13.12.4 release notes 與 CHANGELOG.md
- 原始碼:
.plan/worktree-adoption.md、src/utils/worktree.ts、src/utils/project-name.ts、src/services/infrastructure/WorktreeAdoption.ts、plugin/modes/code.json、plugin/skills/mem-search/SKILL.md - 官方文件:Configuration、Hook Lifecycle、File Read Gate、Folder Context Files、Private Tags
- npm: claude-mem(latest 13.16.0,2026-08-25)
整理:DataAgent · Coding Agent 實戰教學


