一個工具要怎麼知道,你機器上哪個 agent 正在想、哪個正在跑指令、哪個停下來等你點頭?

它沒有裝 hook,沒有要你改任何設定,也不需要 API key。whatsmyagentdoing 的 README 自己這樣寫:Claude Code 與 Codex 本來就會把每則訊息、每次工具呼叫追加到 JSONL 檔裡,它只是在旁邊 tail 那些檔案。裝成 skill 之後輸入 /myoffice,就會開一個畫面,把每個 agent 畫成辦公室裡的人。

我本機的 ~/.claude/skills/myoffice 其實已經躺著這個 skill(clone 下來的 HEAD 是 c9dc82f,package.json 寫 0.3.0,遠端現在已經是 0.3.1)。我跑過一次 node bin/cli.js,只看到它回報 localhost:4545 已經在執行、有 0.3.1 可以更新,沒有拿畫面上的狀態去對照過原始碼。所以這篇不講用起來的感覺,只講我讀 README 和 src/ 底下 watcher.js、parsers.js、server.js 三個檔之後,看懂的那條推理鏈。資料取得日是 2026-10-26。

它讀的東西,是別人家的檔案

三家 CLI 各自把 session 寫在固定位置:

  • Claude Code:~/.claude/projects/<project>/<session>.jsonl,子 agent 在 subagents/
  • Codex:~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl
  • Gemini CLI:~/.gemini/tmp/<project>/chats/session-*.jsonl

換過設定目錄的人可以用 CLAUDE_CONFIG_DIR、CODEX_HOME、GEMINI_CLI_HOME 指給它。README 的說法是 read-only,讀進來、把最新一筆事件轉成狀態、再用 Server-Sent Events 推給網頁。

監看的方式很樸素。watcher.js 開頭的註解說,它用 polling 而不是 fs.watch,因為 polling 在 macOS、Linux、Windows 上行為一致,而且「一秒一次 stat 幾百個檔案」很便宜。程式裡 interval = 1000,就是一秒一輪。檔案很大的時候,常數寫的是只讀頭 128 KB 加尾 768 KB(HEAD_BYTES = 128 * 1024; TAIL_BYTES = 768 * 1024;)。至於超過多大才會切成頭尾,那個判斷式我沒有逐行驗,這裡只確定常數存在。

一筆 log 怎麼變成一個狀態

README 列的狀態有幾種:Thinking(推理中,或在讀工具結果)、Running(有工具呼叫還在跑,會顯示是哪個)、Writing reply(開始回答、這一輪還沒結束)、Waiting for you(問了你問題或要核准)、Done,另外還有 Interrupted、Error、Idle。

parsers.js 裡 Claude Code 的判斷很直接。assistant 的文字區塊如果 stop_reason 是 end_turn 或 stop_sequence,就是 done,不然算 typing。看到 tool_use,就查這個工具名在不在 WAITING_TOOLS 這個 Set 裡。

問題是 log 不會替你把「現在」記下來。README 在限制那段承認:log 是整個區塊寫完才寫,不是一個 token 一個 token 寫,所以 Writing reply 的意思只是「有一個回覆區塊落地了,這一輪還沒完」。那一個區塊寫到一半的時候,磁碟上什麼都沒有。

於是 server.js 第 41 到 44 行出現一組補丁,靠「安靜了多久」往後改狀態:

目前狀態 安靜超過 改判成
typing 45 秒 done
thinking 15 分鐘 idle
tool 45 分鐘 idle
waiting 或 error 2 小時 idle

這張表是我從原始碼逐字抄出來的數字。注意它在 server.js,不在 watcher.js。也就是說,「Writing reply 變成 Done」不是 log 說的,是時鐘說的:45 秒沒新東西,就當它講完了。

最想知道的那一種,Claude Code 偏偏不寫

你開著幾個 session,最想知道的大概是哪一個停在那裡等你按核准。

README 的限制清單把這件事寫得很白:Claude Code 不記權限提示,所以等你核准的工具在你回答之前會顯示成 Running。Codex 和 Gemini CLI 有記,所以那兩家會顯示 Waiting for you。我讀碼的時候確認了 Gemini 那邊是靠工具呼叫的 status 判斷,awaiting_approval 對應 waiting;Codex 那邊 256 行有 set(s, 'waiting', 'Needs your approval')。

這裡有一個 README 沒寫、我讀碼才看到的例外。parsers.js 第 17 行:

1
const WAITING_TOOLS = new Set(['AskUserQuestion', 'ExitPlanMode']);

Claude Code 這兩個工具被呼叫的時候,會直接被標成 waiting。所以 agent 用 AskUserQuestion 問你問題,或者交出計畫等你同意,畫面上看得到。只有「一般工具等你核准」那一種,會藏在 Running 後面。

所以 Claude Code 這一側,Waiting for you 有兩個來源可以被看見,一個看不見。看不見的那個,偏偏是你最想被提醒的場景。

它自己踩到的一個洞

專案 2026-10-23 建立,10-25 就發了 v0.3.0 和 v0.3.1。v0.3.1 的 commit 訊息寫的修正是:掃描 session 日期資料夾時不再管資料夾多舊,改用 log 的修改時間挑候選,並且加了六個隔離檔案系統的回歸測試。

我從這句訊息推測(commit 沒有明說),舊版是被日期資料夾的新舊擋掉了,所以很早開始、一直續聊的 Codex 長對話會被漏掉。README 現在也改成這樣寫:日期資料夾記的是對話開始的時間,所以要檢查所有日期,用修改時間找最近活躍的,連幾個月前開始的對話也算。

這個 bug 本身不稀奇。有意思的是它點出旁觀式的先天處境:你讀的目錄結構是別人的實作細節。README 也直說了,log 格式不是公開 API,更新之後狀態可能會變差。Google Antigravity 的對話存在沒有文件的二進位格式裡,目前乾脆不支援。

我的立場

我會把這類工具放進「值得裝」,前提是你清楚它的天花板在哪。

不碰設定、不裝 hook,它就不會動到你的 Claude Code 設定,也沒有 hook 要你維護。代價是它的所有判斷都建立在別人願意寫進 log 的東西上,而且只在區塊完成時才寫。所以我的用法會是:看到 Writing reply 大致信它,看到 Done 要記得其中一部分是 45 秒沒聲音判出來的;看到 Running 很久,不要當成沒事,那可能是在等你。

會讓我改變想法的條件有兩個。一是 Claude Code 之後把權限提示寫進 log,那個盲區就消失了,Running 才能放心當成真的在跑。二是我自己實際開起來,發現 README 與原始碼對不上,比如 Waiting for you 在我的環境根本不亮。這兩件我都還沒驗證。

至於 Claude Code 官方提供的 hooks 或 status line 能不能補上這個盲區,我沒有查,這篇不下結論。

讀完還沒回答的事

誠實說一下邊界:我讀了 README 和 src/ 三個檔(watcher.js 299 行、parsers.js 422 行、server.js 200 行),沒有拿 /myoffice 的畫面與狀態去對照原始碼,沒有看過 2D 或 3D 畫面,也沒有跑 tests/watcher.cjs 與 tests/ui.cjs。畫面長怎樣,我只能轉述 README:Medium 是手繪的 2D canvas,High 用 three.js 畫 3D,List 把 agent 分成「需要你、工作中、已完成」三組。Windows 與 Linux 上的實際表現,我也沒查證。

另外兩件事:GitHub 在 2026-10-26 顯示 247 星、17 forks、1 位貢獻者、MIT 授權;本機隱私這邊,README 說 server 只綁 127.0.0.1,並拒絕不是送往 localhost 的請求,我在 server.js 看到的是 Host 檢查不符就回 403。至於這個檢查的動機,程式碼本身沒寫,我不替作者下判斷。

Writing reply 轉成 Done 的那一刻,依原始碼的寫法,靠的是 45 秒沒聲音。