Claude Code 桌面版最新一版,輸入框旁邊那個小圓環原本顯示 context window 填了多少,現在變成 5 小時額度的百分比。我第一個問題不是它為什麼改,是 Claude Code 的 mod 能不能在桌面版跑。能跑的話,我自己補一條回來就好。

為什麼改,我叫 Claude 查了,查不到 Anthropic 的說法。changelog 沒寫,找得到的只有使用者在 GitHub 開的 issue。#91075 回報這個圓環換了意思,changelog 沒提;#90708 要求把 context 指示放回來。context 其實還看得到:點模型選單旁邊的圓環,或打 /context,只是不會一直掛在畫面上。CLI 可以用 statusLine 設定自己畫一條,桌面版不吃這個設定。

第一版:一行字,85% 以上變色

mod 是 Claude Code 用 hooks 改自己介面的方式。Claude 讀了 2.1.299 的型別檔:桌面版有自己的元件表,提示框上方那條橫條,型別裡明寫終端機和桌面版都會畫;$.session.usage() 讀的就是狀態列那組 context 數字,文件寫這個呼叫不花成本,不另外打 API。

第一版很短。提示框上方一行 Context 42% · 84k / 200k,85% 以上改成 warning 色加粗,每輪結束更新一次。我問它是不是只有每輪結束才更新,它說長任務跑到一半會停在上一輪的數字,於是又加了「每次工具呼叫結束也重讀一次」。這段有測試:Claude 把那行刷新拿掉跑一次,新加的測試就失敗,畫面停在 40% · 80k。

重開了,看到的還是舊版

我重開過一次 session,橫條出現了,但那是加工具呼叫刷新之前的版本。原因是桌面版的 session 預設不監看 mod 的資料夾,要在 settings.json 的 env 另外設 CLAUDE_CODE_PLUGIN_DIR_WATCH=1,之後存檔才會重載。我還問了壓縮算不算重開:不算,壓縮是在同一個程序裡把對話縮短,程序沒有重啟。

Claude 要把那個變數寫進設定檔,被 auto mode 的分類器擋下來,理由是 Self-Modification。它沒有繞過去,給了我一行指令,我按 Run 才加上。〈Nadella 說控制要放在模型外面〉寫過,放在模型外面的那一層,在我這裡常常是要我親手按的那一下。這次就是那一下。

GitHub 上已經有十幾個在做同一件事

接著我叫 Claude 上 GitHub 找有沒有類似的專案。10/16 查的時候,claude-code-mod 這個主題有 601 個 repo,光是在提示框上方畫 context 用量的就有十幾個。

Claude 的判斷是:只看 context 的話,我這個已經夠用,值得參考的只有兩個。jack21/claude-mod-usage 把 context、5 小時、7 天額度畫成三條綠到紅的漸層條,還有重置倒數。cablate/ctx-handoff-mod 在 context 快滿時自動寫一份交接摘要、/clear,再把摘要送進新對話接著做。

我沒有去裝它們,是把它們搬進我自己的 mod。理由就一個:我的會自動重載。兩個都是 MIT 授權,Claude 先把原始碼讀過,搬過來的部分在 NOTICE.md 寫清楚來源和版權,原本的授權全文放進 licenses/。這跟〈agent 讓重造輪子變便宜,我選擇 fork〉是同一個做法:拿別人寫好的,自己記下改了什麼。

搬了什麼,沒搬什麼

jack21 那邊搬了漸層條、圖示、重置倒數,還有 session.measure 這個事件:context 或額度數字有變,引擎會主動推過來,比我自己每輪去讀準。原本支援 12 國語言,我只留繁體中文。我原本那條「85% 以上變色」也保留下來,畫在 context 那條的百分比標籤上。

ctx-handoff 其實是一整套工具:自動交接、離開時保持快取、把我交代過的事整理成專案筆記(會另外呼叫 Sonnet)、守門規則、管理面板。我只搬自動交接。筆記跟我自己蓋的記憶系統重疊,快取保溫要花錢,這兩塊先不碰。門檻一開始照原版,60 萬 token 或視窗的 80%,取比較小的那個;後來改掉了,原因寫在最後。還有背景工作或子代理在跑,就先延後。交接期間我打的訊息先攔下來,交接完一起帶進新對話。

照搬會出事的地方在「哪些訊息算我本人打的」。原版只認終端機和 Remote Control 兩種來源。桌面版的 session 是 SDK 在驅動,我在桌面版打的字,來源標的是 sdk。照原版搬過來,交接中途我打的訊息不會被攔下,會直接送進一段馬上要被清掉的對話。Claude 是讀型別檔裡來源種類的清單才發現的;mod 自己送出的訊息標的是 plugin,所以把 sdk 加進來,也不會連交接摘要都自己攔下。

驗證器也擋了兩次。一個事件在同一個 mod 裡只能註冊一次,所以交接那邊的 session.start 要併進主程式;state 的值要在用到它的那個檔案裡用字面值宣告,驗證器才掃得到。最後 11 個測試全過(後面又補了一個),包括桌面版三條進度條的數字、84% 和 85% 的分界、交接中打的訊息有沒有出現在新對話的摘要後面。

拿真的對話來試

自動交接預設是關的。測試裡的 fork、/clear、送出訊息都是替身,證明不了桌面版真的跑得動,所以要手動打 /handoff now 試一次。原本打算開一個用不到的 session,最後我直接拿「遊戲系統功能清單」來試,那段對話有 858 則訊息。/clear 不會刪掉舊對話的紀錄檔,真的出事還找得回來。

第一次失敗了,橫條上的紅字是「交接失敗:這段對話還沒有可以接手的內容」。那個 session 是我重新打開的舊對話,重開之後還沒跟 Claude 來回過。fork 接的是主對話上一次送出的請求,這時候還沒有那一次,就沒東西可以接。自動交接不會遇到這個問題,它是在 Claude 剛回完、停下來的時候才判斷;會撞到的只有手動的 /handoff now。

麻煩的是事後查不到原因。我跟 Claude 說「交接失敗」,它去翻那個 session 的紀錄檔,只看到「開始交接」那一行,後面什麼都沒有。橫條的狀態只在記憶體裡,mod 的 log 在桌面版也只進 debug log。最後是我把紅字貼給它,它才確定。所以補了兩件事:失敗原因存進 store,打 /handoff 就看得到;遇到這種情況,提示直接寫「先隨便問一句,等它回完再打 /handoff now」。照這樣做,第二次就成功了。

不過那一句問完要留著。我試過先問一句、等它回完,再把那一輪倒回(rewind)掉,接著打 /handoff now,又是同一個錯誤。看起來倒回也會把 fork 要接的那次請求一起拿掉;原因我沒有再往下查,只把它加進提示裡:那一來一回要留著,倒回去會再失敗。

門檻改成 85%

成功之後,我問 Claude 交接跟 Claude Code 自己的壓縮差在哪。它講了一串,其中一句說原版把門檻設在 60 萬,是為了不要等到 context 太長、品質變差才處理。

可是這件事我測過。9/27 在 Workbench 一個 session,從 39 萬一路做到 87.4 萬都沒壓縮,看不出品質減損,82.4 萬時做的回憶測試 5 題全對,之後我就不再手動壓縮,交給自動壓縮。Claude 回頭查才承認,ctx-handoff 的 README 根本沒提品質,是它把另一個 repo 的說法混進來了。

照原版的門檻,1M 視窗在 60 萬就交接,等於把我測過沒問題的那一段直接切掉。交接只要搶在自動壓縮前面就好,所以改成視窗的 85%。1M 視窗是 85 萬,Opus 5.5 的自動壓縮大約在 96.7 萬,延後的上限是 90 萬。剛好也跟橫條變色的 85% 對上,顏色一變就代表快要交接了。

還沒確認的有兩件。96.7 萬是我 9/27 記下的數字,這次沒有重查;200k 視窗的模型在哪裡自動壓縮,我不知道,85% 是 17 萬,有可能比它晚。交接寫出來的摘要有沒有比自動壓縮好,也還沒量過。