繁體中文

問題修復

自癒不再把名字像的自製腳本改成官方指令

你自己留了一份備份檔叫 usage-terse-mode-backup.py,usage 的自癒功能卻把它認成自家的掛鉤(hook,特定事件發生時被觸發的小程式),悄悄改寫成官方指令。原因是它只拿 usage-terse-mode 這串字去比對,名字含這幾個字就算數。移除那條路徑先修好,要求這串字前後得是路徑分隔符號、引號、空白或字串邊界,並且結尾是 .py。現在遷移邏輯裡另外四處判斷要不要覆蓋掛鉤指令的地方,也都改用同一套嚴格比對。

問題修復

英文單字不再被當成日文或韓文

語言代碼是拿來標示介面該講哪種語言的短代號,ja 是日文、ko 是韓文。usage 判斷代碼時只看開頭幾個字母就算數,於是 jargon 被讀成日文、koala 被讀成韓文。另外幾種寫法也判錯:zh_TW@variant 因為沒有先去掉 @ 後面那段而掉回英文;zh-HK 不像 zh-TW 那樣接受後面再加細分代碼。再查一輪還發現 zh-Hant 和 zh-Hans 有同樣的開頭比對問題,亂編的 zh-Hantasy 也會被當成繁體中文,而缺了 zh-SG- 這個開頭讓合法的 zh-SG-x-private 掉成英文。這段判斷在四個檔案裡各有一份,現在四份都要求開頭之後有明確邊界。

問題修復

終端機編碼不合就整段訊息不見的問題修好了

工作階段開始與送出訊息時各有一個掛鉤,終端機用 cp950 這類非 UTF-8 編碼時,它們寫出 JSON 沒有轉成安全字元,會拋出 UnicodeEncodeError,你整段訊息就此消失。現在輸出一律轉成 ASCII 安全的 JSON,寫入動作也包在 try/except OSError 裡。另一個問題在版本標記:四個讀取器用 split 拆字串,遇到被截斷的一行或無效位元組就卡住,壞掉的腳本因此永遠不被認定需要重裝;現在改用 partition,也一併接住 UnicodeDecodeError。還有精簡模式的附屬檔內容爛掉時,只要掛鉤版本沒變就不會重建,因為判斷方式只是看檔案在不在;現在改成驗證內容本身。

問題修復

續接提示不再因為一個小錯就整段消失

續接工作階段時 usage 會給你一段提示,講你上次做到哪。以前這段提示很容易整個不見:讀取標準輸入遇到管線斷掉會拋 OSError,但續接不像精簡模式那樣接住它,一個輸入輸出的小狀況就讓掛鉤當掉而不是正常結束。解析工作階段紀錄只接住 OSError,沒接 UnicodeDecodeError,紀錄裡有無效的 UTF-8 位元組時整段提示就消失,不會像遇到空紀錄那樣退回上一份還能用的舊紀錄。現在這兩種狀況都接住了,續接提示照樣出現。

問題修復

全部做完的待辦清單會清掉,只回一個標點不算數

上次的事情都做完了,下次續接時提示還在報那些已完成的工作。續接掛鉤只有在新清單非空時才覆蓋舊的,而一次工作階段最後那筆 TodoWrite,也就是把每項都標成完成、留下空清單的那一筆,就被忽略了。現在全部項目標記完成後,清單會被清掉。同一批還修了三件事。健康檢查提醒存去重狀態時碰上磁碟滿了或權限不足,錯誤會往外冒把續接提示一起吞掉;現在用 contextlib.suppress(OSError) 框住。判斷回覆有沒有實質內容時,只要文字裡有標點就算數,ok. 或 /clear 這種短回覆因此被留下;現在要求標點兩側都得有字母或數字。另外,git status 給未追蹤目錄的路徑帶著結尾斜線,取檔名得到空字串被默默丟掉,剛建好還沒提交的資料夾因此沒出現在未提交清單裡。

新功能

額度回復的通知回來了,一開啟就超標也會提醒

額度用完後回復時,usage 應該通知你,實際上這則通知一直沒出現。原因是任何低於 100% 的讀數都會把「曾經用完」這個記號清掉,不管跌幅多小;等到真正的重置被認出來,記號早就被先前的小波動清掉了。那個重置指的是一次夠大的單步下降,用來跟雜訊區分開。現在只有那個重置判斷本身會清掉它。另外,警告門檻以前只能靠跟前一筆讀數比較才跨得過去,所以第一次觀測就已經超過門檻的情況完全不會警告,例如程式在你的工作階段進行到 95% 時才啟動。這不像用完的通知,那個第一次讀到 100% 就一定會發。

問題修復

系統匣選單不再被一個讀取失敗整個拖垮

Windows 上點開選單列的圖示,整個系統匣選單可能整個壞掉打不開。壞的其實只有一列:畫「開機時啟動」那個勾選框時,程式要去讀登錄檔(registry,Windows 存放系統與程式設定的資料庫)確認狀態,而它只接住找不到檔案這一種錯誤。權限不足時錯誤就往外冒,整份系統匣選單跟著垮。現在它接住所有 OSError,那一列直接顯示為未啟用。同一批也清掉一個沒有作用的設定鍵 StartDelay。launchd.plist(5) 根本不認得它,從加進去那天起就沒起過任何作用。

新功能

精簡模式的白話規則也管到子代理貼過來的字

精簡模式(terse mode)要求把話講白,但這條規則以前只管 Claude 自己寫的句子。子代理的回報、工具的原始輸出被直接引用時,模式的提示裡沒有任何指示,裡面的術語就這樣原封不動貼出來,沒人解釋。現在在既有的「術語第一次出現時要解釋」那條規則後面加了一行,五種語言都加了。

English

BUGFIX

Self-heal stops taking over your own script that just looks like a hook

You keep your own backup named usage-terse-mode-backup.py, and usage's self-heal treated it as one of its own hooks and silently rewrote it to the official command. The cause was a plain substring check against a marker like usage-terse-mode: any filename containing those characters counted. The removal path was fixed first, requiring a path separator, quote, whitespace, or string boundary around the marker and a trailing .py. The same precise check is now used at the four remaining call sites in the migration logic that decide whether to overwrite a hook's command.

BUGFIX

Plain English words are no longer read as Japanese or Korean

A language code is the short tag that says which language the interface should speak: ja for Japanese, ko for Korean. usage matched those with an unbounded prefix test, so jargon was read as Japanese and koala as Korean. Other forms were misread too: zh_TW@variant fell through to English because the part after @ was not stripped, and zh-HK did not accept further subtags the way zh-TW and zh-Hant did. A second pass found the same unbounded-prefix bug in zh-Hant and zh-Hans, letting a nonsense zh-Hantasy match as Traditional Chinese, and a missing zh-SG- prefix that dropped a legitimate zh-SG-x-private to English. This logic was duplicated across four files; all four now require a boundary after every prefix.

BUGFIX

A non-UTF-8 terminal no longer swallows the whole message

When a terminal uses a non-UTF-8 encoding such as cp950, usage's hooks wrote JSON without escaping, raised UnicodeEncodeError, and lost your entire message. Output is now forced to ASCII-safe JSON, with the write wrapped in try/except OSError. A second problem sat in the version marker: four readers used split and choked on a truncated line or invalid bytes, so a broken installed script was never recognized as needing reinstalling; they now use partition and also catch UnicodeDecodeError. And when a terse-mode sidecar's content had rotted, it was never rebuilt as long as the hook version was unchanged, because staleness was checked with a plain exists(); it is now checked by validating the sidecar's content.

BUGFIX

The resume prompt no longer vanishes over one small failure

When you resume a session, usage gives you a prompt saying where you left off, and that prompt used to disappear whole. _read_stdin_utf8() could raise OSError on a broken pipe and, unlike terse mode and the terse reminder, session resume did not catch it, so an I/O hiccup crashed the hook instead of exiting 0. _parse_session() caught OSError but not UnicodeDecodeError, so a session log with invalid UTF-8 bytes made the whole resume prompt vanish instead of falling through to the next older, valid log the way an empty log already did. Both are now caught, and the resume prompt still appears.

BUGFIX

A finished todo list gets cleared, and a lone punctuation reply does not count

Everything from last time was finished, yet the next resume prompt kept reporting that work. The resume hook only overwrote its stored todo list when the new one was non-empty, so the last TodoWrite call of a session — the one that marks everything complete, leaving an empty list — was ignored. A todo list is now cleared once every item on it is marked done. Three more fixes landed alongside it. Saving the health-check reminder's dedup state could raise OSError on a full disk or a permissions error, and that exception propagated far enough to swallow the main resume prompt; it is now scoped with contextlib.suppress(OSError). _has_structural_signal treated any punctuation anywhere in the text as a sign of real content, so short replies like ok. or /clear were kept as if they were substantive; it now requires alphanumeric characters on both sides of the mark. And git status's trailing slash on an untracked directory path made basename return an empty string, which was silently dropped, so a brand-new uncommitted folder never appeared in the uncommitted-work summary.

FEATURE

The quota-restored notification is back, and a first reading past the threshold warns

usage is supposed to tell you when depleted quota comes back, and that notification never fired. Any reading below 100% cleared the "was depleted" flag, regardless of how small the drop was, so by the time a genuine reset was detected — a single-step drop large enough to distinguish a real period rollover from noise — the flag had already been cleared by an earlier, tiny fluctuation. It is now cleared only by that same reset detection. Separately, a warning threshold could only be crossed relative to a previous reading, so quota that was already above the threshold on the very first observation — the app launching mid-session at 95%, for instance — produced no warning at all, unlike the depleted notification, which always fires on a first reading of 100%.

BUGFIX

One registry read failure no longer takes down the tray menu

On Windows, clicking the menu bar icon could leave the whole tray menu broken. Only one row was actually at fault: to draw the "launch at login" checkbox, the app reads the registry, the database where Windows keeps system and program settings, and its is_enabled() caught only FileNotFoundError. A registry permission error therefore propagated out and could break the whole tray menu instead of just that one row; it now catches any OSError and reports "not enabled." The same batch removed a dead settings key, StartDelay: launchd.plist(5) does not recognize it, so it had no effect and was doing nothing since it was added.

FEATURE

Terse mode's plain-language rule now covers text pasted from a subagent

Terse mode asks for plain language, but the rule used to govern only sentences Claude wrote itself. When a subagent's report or a tool's raw output was quoted directly, the mode's own prompt gave no instruction for it, so jargon inside went unexplained. A line was added after the existing "explain a term on first use" rule, in all five languages.

原始 CHANGELOG

Original changelog自癒不再把名字像的自製腳本改成官方指令
**Self-heal no longer takes over a user's own script that merely shares part of a hook's filename.** Both the hook-removal path and the migration path that keeps an installed hook's command current used a plain substring check against markers like `usage-terse-mode`, so a user's own script — say, a backup named `usage-terse-mode-backup.py` — matched and was silently rewritten to the official command. The removal path was fixed first, requiring a path separator, quote, whitespace, or string boundary around the marker and a trailing `.py`; the same precise check is now used at the four remaining call sites in the migration logic that decide whether to overwrite a hook's command.
Original changelog英文單字不再被當成日文或韓文
**Language detection no longer misreads plain English words as Japanese or Korean, or a made-up Chinese-script tag as a real one.** The `_normalize_lang` logic — duplicated across four files — matched `ja`/`ko` with an unbounded `startswith`, so `jargon` and `koala` were read as Japanese and Korean; a POSIX `@modifier` suffix wasn't stripped, so `zh_TW@variant` fell through to English; and `zh-HK` didn't accept further BCP 47 subtags the way `zh-TW`/`zh-Hant` did. A second pass found the same unbounded-prefix bug in `zh-Hant`/`zh-Hans` (letting a nonsense `zh-Hantasy` match as Traditional Chinese) and a missing `zh-SG-` prefix (dropping a legitimate `zh-SG-x-private` tag to English). All four duplicated copies now require a boundary after every prefix.
Original changelog終端機編碼不合就整段訊息不見的問題修好了
**A non-UTF-8 terminal no longer crashes terse mode or session resume, and a truncated or corrupted version marker no longer wedges self-heal.** The SessionStart/UserPromptSubmit hooks wrote JSON without `ensure_ascii`, so a terminal using an encoding like cp950 raised `UnicodeEncodeError` and lost the entire message; output is now forced to ASCII-safe JSON with the write wrapped in `try`/`except OSError`. The four version-marker readers used `split` and choked on a truncated line or invalid bytes, which meant a broken installed script was never recognized as needing reinstalling; they now use `partition` and also catch `UnicodeDecodeError`. Separately, a stale terse-mode sidecar whose content had rotted was never rebuilt as long as the hook version hadn't changed, because staleness was checked with a plain `exists()`; it's now checked by validating the sidecar's content.
Original changelog續接提示不再因為一個小錯就整段消失
**Session resume's stdin and stdout failures are handled the same way its sibling hooks already were, and a corrupted newest session log falls back to an older one instead of going silent.** `_read_stdin_utf8()` could raise `OSError` on a broken pipe, and unlike terse mode and the terse reminder, session resume didn't catch it — an I/O hiccup crashed the hook instead of exiting 0. Separately, `_parse_session()` caught `OSError` but not `UnicodeDecodeError`, so a session log with invalid UTF-8 bytes made the whole resume prompt disappear instead of falling through to the next older, valid log the way an empty log already did.
Original changelog全部做完的待辦清單會清掉,只回一個標點不算數
**A todo list is now cleared once every item on it is marked done.** The resume hook only overwrote its stored todo list when the new one was non-empty, so the last `TodoWrite` call of a session — the one that marks everything complete, leaving an empty list — was ignored, and the next session's resume prompt kept reporting work that was already finished.
**The health-check reminder's own state write no longer takes the whole resume prompt down with it, a bare punctuation reply is no longer read as a real request, and a newly created but uncommitted directory now shows up in the "not saved yet" list.** Saving the health-check reminder's dedup state could raise `OSError` (a full disk, a permissions error) and that exception propagated out far enough to swallow the main resume prompt along with it; it's now scoped with `contextlib.suppress(OSError)`. Separately, `_has_structural_signal` treated any punctuation anywhere in the text as a sign of real content, so short replies like `ok.` or `/clear` were kept as if they were substantive; it now requires alphanumeric characters on both sides of the mark. And `git status`'s trailing slash on an untracked directory path made `basename` return an empty string, which was silently dropped, so a brand-new uncommitted folder never appeared in the uncommitted-work summary.
Original changelog額度回復的通知回來了,一開啟就超標也會提醒
**The quota-restored notification fires again, and a warning now fires on the very first reading if quota is already past the threshold.** Any reading below 100% cleared the "was depleted" flag, regardless of how small the drop was, so by the time a genuine reset was detected — a single-step drop large enough to distinguish a real period rollover from noise — the flag had already been cleared by an earlier, tiny fluctuation, and the restored notification never fired. It's now cleared only by that same reset detection. Separately, a warning threshold could only be crossed relative to a previous reading, so quota that was already above the threshold on the very first observation — for instance, the app launching mid-session at 95% — produced no warning at all, unlike the depleted notification, which always fires on a first reading of 100%.
Original changelog系統匣選單不再被一個讀取失敗整個拖垮
**A dead plist key was removed, and a Windows registry read failure can no longer take down the whole tray menu.** `StartDelay` isn't a key `launchd.plist(5)` recognizes, so it had no effect and was doing nothing since it was added; it's gone. Separately, the Windows login-item's `is_enabled()` caught only `FileNotFoundError`, but it's called once per menu render to draw the "launch at login" checkbox — a registry permission error there propagated out and could break the whole tray menu instead of just that one row; it now catches any `OSError` and reports "not enabled."
Original changelog精簡模式的白話規則也管到子代理貼過來的字
**Terse mode's plain-language rule now also covers text pasted verbatim from a subagent.** The mode's own prompt only governed sentences Claude wrote itself, leaving no instruction for handling a subagent's report or a tool's raw output when quoted directly — jargon inside it went unexplained. A line was added after the existing "explain a term on first use" rule, in all five languages.