Skip to content

v0.31.0

Choose a tag to compare

@Kewton Kewton released this 05 Sep 09:07
· 32 commits to main since this release
4971122

[0.31.0] - 2026-09-05

Highlight: Command Code を第一級エージェントとして迎え入れたリリース。起動・送受信・ステータス検出(#2250)から hooks の構造化イベント配線(#2251)、転写リーダー(#2252)、slash カタログと headless 実行(#2253)、Skills discovery の実測(#2302 / #2322)まで、他の 7 ツールと同じ経路に載せた。あわせて v0.30.0 で新設したチャット出力面を「読む面」から「操作できる面」へ進め、TUI のダイアログにチャット面のまま答えられるようにし(#2254)、返答を全幅で描いてツール活動を 1 行チップへ畳んだ(#2284)。三本目の柱は tmux 側からの可観測性(#2317、Phase A〜D)で、commandmate attach の新設・tmux ls -F '#{@cm_status}' での状態確認・capture --pane --follow の追従読み・claude 限定の attach --live(端末サイズへ委譲し detach で 200x1000 へ復帰)を入れた。実測では、attach --live 中に CLI を SIGKILL してもサーバの 1 ポーリングでジオメトリが復帰することを確認している。

Added

  • feat(tmux): tmux から attach・状態確認・追従読みができるようにする(Phase A〜D) (#2317): CommandMate のセッションは 200x1000・window-size manual で作られる(#1163)ため、160x45 の端末で tmux attach すると tmux はカーソル(997 行目)側の下端 44 行しか映さず、alt-screen ツール(claude / opencode / copilot)では空白と入力欄しか見えない。既存の逃げ道(#1623 の prefix+g popup と capture --pane)は動いていたが発見できず・静止画で・attach -r では効かず・状態を出さなかった。Phase A: commandmate attach <worktree-id> [--instance] [--agent] [--read-only] [--live] を新設。サーバの resolve-target でセッションを解決してから argv で tmux attach-session -t '=<name>:' を実行する(シェルを経由しないので = を zsh の equals expansion に食われる実測問題が起きない)。alt-screen ツールでは attach 直前に「なぜ空白に見えるか」と読む 3 経路を stderr に出し、$TMUX があるときは switch-client に落ちる。ls --json に tmuxSession、instances の表と --json に TMUX_SESSION / tmuxSession を追加(CLI 側で resolveSessionName() から導出するので追加リクエストは無い)。README(英日)の 「Session stuck or not responding?」と cli-operations-guide(英日)を書き換えた。Phase B: 状態遷移時にそのセッションの user option(@cm_status / @cm_worktree / @cm_tool / @cm_instance / @cm_updated)と session 限定 status-right を書き、tmux ls -F '#{session_name} #{@cm_status}' で attach せずに一覧できるようにした。遷移時にしか撃たない(GET /api/worktrees は 2 秒ごとに全 worktree の全ツールを probe するので、毎ポーリング 5 回の set-option は数百往復/分になる)。ユーザーが session スコープで status-right を設定済みなら上書きしない。opt-out は CM_TMUX_STATUS=off(起動時 sweep ではなく次のポーリングが 1 セッション 1 回だけ撤去する収束型)。Phase C: capture <id> --pane --follow [--interval <ms>](250〜60000ms、TTY 必須、Ctrl-C 終了)と、CM_READ_MODE_FOLLOW=on で prefix+g を追従 popup に変える経路、CM_READ_MODE_AUTO_POPUP=on の attach 時自動 popup(既定 off)を追加。Phase D: attach --live(claude 限定。他ツールは黙って no-op にせず拒否)が attach 中だけ window-size latest にして端末サイズへ再レイアウトさせ、detach で manual + 200x1000 に戻す。委譲中は @cm_delegated=1 を立て、poller は claude のペイン由来の保存だけを抑止する(転写リーダを先に走らせるので History から本文は欠けず二重保存もしない)。復帰時に last_captured_line をリセットする。決定事項どおり global な tmux 設定には一切触らない — 追加したものは全て session 限定(set-option -t / set-hook -t / user option)で、#1623 の bind-key が唯一の global 介入のまま。Issue 本文の技術検証を実測で 4 点上書きした: (1) session 限定 client-detached フックは tmux 3.5a で発火しない(global なら発火するが #{session_name} が別セッションを指し #{client_control_mode} が空になる)ため、復帰は hook ではなくサーバのステータスポーリング(reconcileDelegatedGeometry)が担う。CLI が kill された場合も端末を閉じた場合も拾える。(2) 追従は 2 本目の bind-key ではなく環境変数にした(global key table を消費しないため)。(3) @cm_status に exited は出ない(ls の STATUS 語彙は 4 語で exited は #2070 以降 REASON 側)。(4) status-right の設置は createSession ではなくポーリングから行う(作成直後も既存も 1 経路で覆える)。実 tmux 検証は私設 socket(tmux -L cm2317-live-test)で行い、global の status-right / status-left / prefix key table が前後で完全一致することを固定している。
  • test(detection): Command Code の検出・転写を 1.49.0 実機で battle-test し、opencode SSE 境界再送の扱いを再検証 (#2304): Command Code の実機フレームを 200x1000 で 7 枚追加(idle / thinking / done / permission / picker の 5 状態を 1.40.1 と 1.49.0 の両方で網羅、/clear 後は起動画面とバイト同一・Esc 中断後は ⠶ 返答行なしという未採取だった 2 つの idle 状態を含む)し、tests/unit/detection/tools/command-code/fixtures.test.ts で行 1000 と列 200 の両方・evidence 込みの全判定・1.47.1 picker の未固定だった判定・busy 行変異を pin。転写側は hook payload 4 件と 1.49.0 転写を fixture 化し、transcript_path 経由の slug 解決が cwd 計算に依存しないことを decoy(claude 規則で計算した同 session id・新しい mtime のディレクトリ)と陽性対照つきで固定。opencode 1.18.27 の実 TUI に自前の SSE リーダーと turn gate を繋いで境界再送の扱い(id 単位初回のみ/再接続で武装のみ破棄)を時刻つき全印字で再確認し、QA ノートに記録(実装は無変更)。
  • feat(onboarding): エージェント CLI の入手導線を第一級ツール並みに揃える (#2301): commandmate init の依存チェックに Command Code CLI(commandcode)/ Antigravity CLI(agy)/ Gemini CLI(gemini)を optional として追加し、CommandMate が動かすエージェント CLI のうち版を確認できるもの全 7 種を init が報告するようにした。あわせて未インストール時の起動拒否メッセージを src/lib/cli-tools/install-hints.ts に一本化し、これまで copilot だけが返していたインストール手順を全ツールへ広げた(例: Command Code CLI (commandcode) is not installed or not in PATH. Install with: npm install -g command-code)。docs/user-guide/cli-setup-guide.md と英語版に opencode / command-code を含む各エージェントの入手 1 行を追記した。
  • docs(onboarding): opencode / Command Code で契約→送信→検証ゲートを 1 周する導線を追加 (#2305): チュートリアルに別紙「opencode / Command Code で同じ 1 周を回す」を新設し、send --contract --instance / wait --verify --instance の実例・capture --json での送り先解決の確認・エージェントによって変わる 3 点(Skill 発見 / worktree-new / 完了検知)を整理した。クイックスタートには「どのエージェントでも回る最小ループ」節を追加し、契約とゲートがエージェント別の分岐を持たないことを明示。英日を同時更新。
  • ci(actions): マージ/close した PR の pull_request run を自動でキャンセルする (#2330): /orchestrate 6-2 は「フル CI の完走を待たずにマージしてよい(develop の push run が安全網)」という設計なので、マージ後も走り続ける PR run は設計上読まれない裁定を作るために self-hosted のジョブを占有していた。2026-09-05 に本ブランチで再実測(直近 200 マージ PR × ci-pr.yml の pull_request run 900 件を head SHA で突合、in-flight = mergedAt より前に作られ 30 秒後もまだ更新されている run)したところ、最終 head の run がマージ時点でまだ走っていた PR は 44 / 200 = 22.0%、マージ後の継続時間は中央値 23.3 分で、裾は 25 分どころではなく #2202〜#2209 は9〜14 時間 queued のまま人手キャンセルされていた(44 件の結末は success 33 / 手動 cancelled 10 / failure 1)。1 run は 12 ジョブ・うち 11 が self-hosted・合計約 33 runner 分(run 33745109236 で実測)。.github/workflows/cancel-pr-runs-on-close.yml を追加し、pull_request: [closed] で head ref の ci-pr.yml の in-flight run(queued / in_progress / waiting / pending / requested の 5 状態を個別に問い合わせて sort -u)を gh run cancel する。--event pull_request で絞るので develop / main の push run には構造的に届かない(ライブ API で確認: --event push --branch develop が返す進行中 run 33892933180 は --event pull_request --branch develop の結果に現れない)=安全網は止まらず、head ref が develop のリリース PR でも同じ。fork PR はトークンが read-only なのでジョブごと skipped、head ref は ${{ }} 展開ではなく env: 経由で渡す(ブランチ名は fork 側が決める文字列で、; やバッククォートを含みうる)。gh run cancel の失敗(list と cancel の間に run が完走した競合)は握って次の id へ進み、gh run list の失敗は set -euo pipefail で赤く落とす。あわせて /orchestrate 6-2 に「マージ後に PR のチェックが cancelled になるのは正常で失敗ではない・手でキャンセルしない」を明記し、6-2 と矛盾していた 6-3 の「pending は待ち」を「6-2 のローカルゲートを通していれば pending は待たなくてよい(最後の 1 本だけ全 pass を待つ)」へ揃えた。tests/unit/guards/cancel-pr-runs-on-close.test.ts が「安全網に届かないこと」(--event pull_request の有無・--branch が $HEAD_REF 以外を名指さないこと・--workflow が ci-pr.yml 限定であること・run: 本文に ${{ }} が無いこと・fork ガード・--status 値が gh の受理集合内かつ非終端であること)を構造で pin し、tests/unit/tasks/orchestrate-merge-gate-consistency.test.ts が 6-2 と 6-3 の pending の扱いが再び食い違わないことを pin する(どちらも変異注入 10 種/編集前ファイルで赤になることを確認済み)
  • feat(ui): 分割表示中に 1 つの split を一時的に最大化する(復元つき、永続化しない) (#2261): split を 2〜3 個並べていると 1 つあたりが狭く、長いダイアログや差分を読むには PaneResizer をドラッグして寄せ、戻すには均等化ボタンか再ドラッグが要った。useTerminalSplits に maximizedIndex: number | null と toggleMaximize(idx) を追加し、最大化中は対象 split を flexGrow: 1、他を display: none(flexGrow: 0 ではない — 幅 0 の flex child は仮想リストの measureElement に 0 の箱を返して測定キャッシュを壊す)にする。他 split は unmount されないので、隠れている間もセッションと useTerminalPanePolling は動き続け、復元するとその間に届いた出力が続きから見える。操作は各 split のタイトルバー右端のトグル(Maximize2 / Minimize2、aria-pressed)、アクションバーの同じトグル(最大化中は他 split のタイトルバーが見えないため復元の入口が要る/未最大化ならフォーカス中の split が対象)、Cmd/Ctrl+Shift+Enter(#2193 の Cmd/Ctrl+Shift+M と同じく data-split-index で所有者を決める。MessageInput は素の Shift+Enter を改行として preventDefault しないため、こちらで確定後に preventDefault して composer に空行が入るのを防ぐ)。maximizedIndex は TerminalSplitConfig の外に置いて localStorage に書かない(「一時的」が要件=リロードで元の分割に戻る。widths は触らないので復元は元の比率そのまま)。split の追加・削除・均等化・worktree 切替、および roster reconcile で対象 index が消えたときに解除される。アクションバーの 2 / 3 splits 表示は最大化中「2 を最大化中」に変わる。履歴列とファイルパネルの表示状態(#2259)は不変で、ファイルパネルは最大化の対象外。
  • feat(ui): ファイルタブを一括で閉じる(すべて/他を/右側を、右クリック・中クリック、未保存タブの確認) (#2260): PC のファイルパネルのタブバー右端に「⋯」ボタンを追加し、「すべて閉じる」「他を閉じる」「右側を閉じる」の 3 コマンドを載せた。同じメニューはタブの右クリックでも開き(アンカーはそのタブ)、タブの中クリック(auxclick の button === 1)でそのタブだけを閉じる。「⋯」は hover-reveal ではなく常時表示なのでタッチでも見え、ボタンのボックスにアンカーするためキーボード起動(Enter / Space、clientX/clientY が 0 になる)でも画面隅に出ない。閉じる対象に isDirty のタブが含まれるときは Modal で**「未保存を残して閉じる」「すべて閉じる」「キャンセル」**の 3 択を挟み、判定は reducer の状態(onDirtyChange の着地先)から読む。useFileTabs に CLOSE_OTHERS / CLOSE_TO_RIGHT と closeOtherTabs() / closeTabsToRight() を追加し、閉じる対象の選択は新しい純関数 selectTabsToClose(tabs, anchorPath, mode) に一本化したので、メニューの活性判定・確認ダイアログ・reducer が別々の集合を見ることはない(CLOSE_TO_RIGHT の境界を 1 つずらす変異注入で 8 テストが赤になることを実測)。ContextMenu は items / ariaLabel を受け取る汎用メニューシェルに拡張し(未指定時は従来のファイルツリー用コマンド)、useContextMenu には要素のボックスにアンカーする openMenuAt() を追加した。なおタブバーから reducer までの prop はスコープ外の WorktreeDetailDesktop.tsx を経由するため、タブバー側は同じ selectTabsToClose の結果を 1 パスずつ CLOSE_TAB として発行しており(右端から順に閉じるので activeIndex は一括版と一致、等価性はテストで固定)、WorktreeDetailDesktop の 3 行を繋げばそのまま一括 dispatch に切り替わる。
  • feat(history): Command Code の転写リーダーを追加し scrape を抑止する (#2252): ~/.commandcode/projects/<slug>/<session_id>.jsonl からターンを読み、エージェント自身の Markdown を chat_messages に書く 5 本目の pull リーダー。ターン境界は Command Code 自身の isFreshUserTurn(role: "user" かつ tool_result ブロックを持たない)で、user 行を書くのは message.meta.source === "user" の積極的証拠があるときだけ。slug は slugify(cwd) で cwd から計算できないため session id でファイルを探す。転写が無い・読めない・ターンが閉じていない場合は false を返して従来どおり scrape が保存される(fail-open)。#2264 と同型に、閉じたターン(散文があり同じ record に tool_use が無い、または後続プロンプトに追い越された)だけを書き、既存行が短ければ長い方向にだけ更新する。
  • feat(agent): Command Code の slash カタログと headless 実行を追加する (#2253): 権威ソース(commandcode.ai の Slash Commands ページ、1.40.1、2026-09-03 読み取り)を attestation に署名し、canonical 56 コマンドを slash-commands-catalog.json へ追加(locale は en/ja 同時、新規フラットキー 19・<name>.command-code leaf 9)。buildCliArgs() に command-code を追加し -p <message> --output-format json + --yolo / --permission-mode <5値> の排他を実装、NDJSON の最終 {"type":"result"} 行から finalText と subtype を取り出して PRINT_EXIT_CODE(0/1/3/4/5/6/7/8/9/10/130)と対にした失敗理由を実行ログへ写す。fixture は 1.40.1 の実出力 4 本。README / architecture / webapp-guide / tutorial / cli-operations-guide の対応ツール一覧も 8 ツールへ更新。
  • feat(hooks): Command Code の hooks を構造化イベントへ配線する (#2251): src/lib/hooks/sources/command-code/ を追加し registry に 7 つ目のソースとして登録。SessionStart / PreToolUse / PostToolUse / Stop の 4 イベント(このツールが持つ全部。他 3 語はロード時に拒否される)を worktree 直下の .commandcode/settings.local.json へ merge 方式で登録し、相関キーは CM_HOOK_URL として env に載せる(#1846)。matcher は空文字必須 — "*" は警告なくロードされたうえで SessionStart と Stop を発火させなくする(実測)。PreToolUse は承認ダイアログの後に発火するため裁定には使わず観測として記録し、Auto-Yes は TUI 番号応答のまま。CommandCodeTool.startSession に世代フェンス beginAgentSession を追加。
  • feat(agent): Command Code を対話エージェントとして起動・送受信・ステータス検出できるようにする (#2250): CLI_TOOL_IDS に command-code を追加(8 ツール)し、CommandCodeTool(起動コマンド commandcode --trust --skip-onboarding --no-auto-update、/exit での graceful exit、送信時の capture キャッシュ無効化)と専用検出モジュール detection/tools/command-code/detect.ts を新設。claude 系定数を流用せず COMMAND_CODE_* パターン一式(プロンプト・区切り・thinking・応答マーカー ⠶・バナー・hooks 通知・モード表示)と構造的な composer 境界 findCommandCodeChromeStart() を実測 200x1000 フレームから起こし、起動画面をユーザエコー不在で除外することで #2247 型の「バージョン文字列を含む短い返答が保存されない」退行を持ち込まずに完了判定を実装。権限モードは実バイナリの .choices() から default / standard / plan / auto-accept / dont-ask の 5 種を採用した。

Changed

  • docs(public): チャット出力面を README(英・日)とランディングページに反映する (#2298): Epic #2192 で出荷したチャット出力面が公開面に 1 行も出ていなかったのを解消した。README 英の ## Key Features と 日の ## 主な機能 に「Conversation view / 会話ビュー」の行を 1 本ずつ追加し(見出しは増やさないので ja-en-heading-parity は不変)、LP のギャラリー(On desktop and on mobile)に新規撮影した screenshot-worktree-desktop-chat.webp(2560x1600 / 131KB)と screenshot-worktree-mobile-chat.webp(1170x2532 / 98KB)を追加した。撮影は隔離環境(demo-video skill の env-up.sh + cassette 再生のスタブエージェント・ポート 3421・専用 DB・使い捨て seed リポジトリ)で行っており本番のデータは写っていない。文言は監視・操作の面として書き、証跡(evidence)とは呼ばない — LP の「With / Without」表は "A chat transcript" を Without 側(置き換えるべき弱いもの)に置いており(website/index.html:507)、チャット面を証跡として紹介すると LP の主張が自己矛盾するため。ギャラリー直下に 「実行を見て答えるための面であり、判定に使うのは commit・ゲートログ・verify history である」旨の注記を置き、同じ理由を HTML コメントとして図の直前にも残した。"A chat transcript" 行と docs/concept.md は無変更(develop と行単位で一致することを確認済み)。
  • docs(user-guide): command-code の Auto-Yes が画面ベース経路のみであることを webapp-guide(日英)と cli-operations-guide(日英)に明記 (#2303): 「Auto-Yes 対応」と一括で謳っていた 4 ガイドに、承認の経路が hooks 承認(claude / codex / copilot / antigravity、opencode は SSE で同等)と画面ベース=TUI 番号応答(command-code / gemini / vibe-local)の 2 系統に分かれ、どちらになるかはエージェントで決まることを表で書き、command-code が構造上 hooks 承認を選べない理由(PreToolUse が権限ダイアログの承認後に発火するため hook の応答でダイアログを消せず、parsePermissionRequest が () => null。当該 PreToolUse は /api/hooks/permission-request ではなく観測用の /api/hooks/agent-event に登録されている)と、そこから来る 3 つの帰結(ダイアログが一度描かれる/ターミナルを読めないフレームは無応答で残る/auto-yes --enable の保留承認 再裁定行は resync を持つ opencode だけ)を記載した。数字は隔離ハーネス(CM_PORT=3011・専用 CM_DB_PATH・使い捨て repo、本番 3000 と本番 DB は不使用)での Command Code v1.49.0 実機確認による: Create File と Execute Shell Command の両ダイアログが Auto-Yes ON で answeredBy: "auto"・answer: "1" として自動応答され(rm -f probe.txt は実際に実行された)、OFF の対照実験では同じダイアログが 45 秒放置しても残ったので、自動応答が Command Code 自身ではなく CommandMate の legacy 経路によるものだと確定している。ダイアログの滞在は 1 秒刻みのペインキャプチャで 3〜4 秒、検出→番号送信は 0.11 / 0.52 秒。pre_tool_use の到達は番号送信の 201 ms 後(23:02:19.919Z 送信 → 23:02:20.120Z 到達)で、JSDoc が v1.40.1 で測った順序が v1.49.0 でも成り立つことを再確認した(実装は 1 行も変更していない)。あわせて英語版 webapp-guide の Available Agents 表が claude / codex / gemini / vibe-local の 4 行で止まっており日本語版の 8 行と食い違っていたため、OpenCode / Copilot / Antigravity / Command Code の 4 行を補って parity を回復した。## 見出し数は日英とも webapp-guide 19 / cli-operations-guide 23 のままで、tests/unit/docs/ja-en-heading-parity.test.ts の等価性は維持している(追加はすべて ###)。モデル選択(#2297)は 2026-09-04 に PR #2308(merge commit 4583929c)で着地済みで本ブランチにも入っているため、本 Issue では重複実装せず状態記録のみ行った。
  • docs(skills): Command Code と Copilot の Skill 互換行を実測で埋める (#2302): 隔離環境(HOME 差し替え・skills 未導入の新規 git repo・root ごとに固有 token の probe Skill)で計測し、compatibility-matrix.ts と docs/reference/skill-agent-compatibility.md の該当行を未計測から実測へ更新。Command Code 1.49.0 は .agents/skills のみを読み(.claude/skills は読まない)発見・呼出とも機械的に確認、Copilot 1.0.83 は 2 つの install root を両方読み自分の palette にも出す。Gemini 0.58.0 も同じ probe で発見軸だけ測ったが呼出軸が sign-in 不能で測れず、片側だけの行は起こさず doc §9.1 に証跡として残した。判別子は skill_loaded イベントで finalText の token は陰性対照でも返ること、Gemini は folder を信頼しないと project Skill が丸ごと落ちることを罠として記録。手順は dev-reports/qa/issue-2302-*-skill-probe.sh、生ログは同 doc §8 / §9
  • docs(public): opencode と command-code を公開面で第一級エージェントに引き上げる (#2300): #2253 で「README / architecture / webapp-guide / tutorial / cli-operations-guide の対応ツール一覧も 8 ツールへ更新」したときに README 日本語版と LP が漏れていたため、docs/ja/README.md の主な機能表(マルチエージェント対応)と With / Without 表(使えるエージェント)、および website/index.html の With / Without 表に Command Code を追加し、英版と語彙・並び順を揃えた(8 ツール = claude / codex / gemini / copilot / opencode / antigravity / command-code / local が README 英・README 日・LP の 3 面すべてに揃ったことを grep で確認)。あわせて LP のツール数カウント文言 3 箇所を seven → eight に更新した(:103 のループ図 aria-label、:120 の図中ノート any of seven CLIs、:246 の「Any agent, in parallel」カード。起票時に指摘されていたのは :246 だけだが、同じ数を述べる箇所が図の内部にあと 2 つあり、片方だけ直すと図と本文が食い違う)。数え方の権威は docs/design/public-messaging.md §11 の根拠表で、これは CLI_TOOL_IDS に pin されており vibe-local を含む id 数を数える規約(#1808 着地時は id 7 件で「7 種のエージェント CLI とローカルモデル」、command-code 追加後の現在は「8 種」に更新済み)— したがって「7 CLI + ローカルモデル」と読んで seven を据え置く選択は規約違反になる。さらに README.md と docs/ja/README.md の主な機能表の直後に ### Supported agents / ### 対応エージェント 節を新設し、opencode と command-code を「同じ扱いを受ける第一級」として 1 行ずつ紹介した(第一級であることの根拠は、両者とも src/lib/hooks/sources/ 配下に専用の hook ソースを、src/lib/detection/tools/ 配下に専用の検出モジュールを持ち、claude / codex / gemini / copilot / antigravity と同じ構成であること)。見出しは ### を選んだので ## の数は英日とも 10 のままで、tests/unit/docs/public-messaging.test.ts が README 英日に課している「## セクション数一致」の pin を壊さない。LP は python3 -m http.server で配信して Playwright で実測し、1200px / 375px のいずれでもドキュメントの横スクロールは発生せず(scrollWidth === clientWidth)、変更した SVG テキスト any of eight CLIs は bbox 272.95〜361.05 で外枠 rect(x=242 w=150)に収まり、Which agent セルとカードも overflow なし、With / Without の <tr> は 7 行のまま、console エラー 0 件だった(375px で table.compare が viewport を超えるのは親 div.table-scroll の overflow-x: auto による既存の設計どおりの挙動で、本変更とは無関係)。なお docs/design/public-messaging.md は scope 外のため未修正だが、同ファイル §3(:67)の LP カード原文と §5(:133)のデモテロップが seven / 7 種 のまま残っており、権威ソース側が §11 と自己矛盾している(LP 本体は §11 に合わせて eight にしたので、次に同ファイルを触る Issue で §3 / §5 を 8 種へ揃える必要がある)
  • ci(workflow): self-hosted runner では npm キャッシュ復元をやめ、PR 単位の concurrency で古い run を止める (#2329): .github/workflows/ci-pr.yml の Setup Node.js 7 箇所の cache を ${{ runner.environment == 'github-hosted' && 'npm' || '' }} に変えた。self-hosted ARM64 では 287 MB のキャッシュ復元が約 8 MB/s(36 秒)で、それが短縮するはずの cold npm ci --no-audit(runner image 内で 27 秒実測)より遅く、Setup Node.js が 0.08 分から 0.77 分に伸びていた(GitHub-hosted は 130〜180 MB/s なので fork PR は従来どおり復元する)。あわせて workflow 全体に concurrency を追加し、同じ PR への連続 push で古い run を cancel する(W35〜36 の PR run 285 件中 32 件が superseded なまま runner を占有していた)。develop / main への push は github.run_id を group に使うため互いに衝突せず cancel-in-progress も false で、統合の検知点である post-merge run は決して cancel されない。式の意味は tests/unit/guards/ci-runner-cache-and-concurrency.test.ts が Actions 式を評価して pin する。
  • feat(ui): ターミナル分割まわりの操作アイコンにホバー tooltip を追加する (#2307): Split の増減・幅を揃える・行/split の最大化・ターミナル⇄チャット切替・セッション終了(×)・履歴/開いているファイルの切替・検索の 11 個のアイコンボタンが、ネイティブ title(表示まで約 1〜1.5 秒・キーボードフォーカス不可・OS 標準の見た目)しか持たず「クリックしないと意味が分からない」状態だったのを、ActivityBar(#730)と同じ common/Tooltip(TOOLTIP_DELAY_MS=100、ビューポート端で自動クランプ)に統一した。TerminalSplitContainer.tsx / TerminalSplitPane.tsx / TerminalSplitPaneContent.tsx の対象ボタンから title= を外して <Tooltip content={...}> で包み、aria-label と data-testid は変更前のまま維持。検索ボタンだけ title すら無く英語ハードコードの aria-label だったため、新規 i18n キー terminal.searchOutput(locales/{en,ja}/worktree.json へ英日同時追加)で aria-label と Tooltip 本文を共用させた。横並びツールバー内で title を持っていたボタンの flex-shrink-0 / ml-auto はラッパ側で失われるため、common/Tooltip に任意の className prop を追加してツールバーのレイアウトを崩さないようにした。
  • feat(ui): チャット面の返答を全幅にし、ツール活動を 1 行のチップへ畳む (#2284): Assistant の応答から吹き出し(枠・背景・角丸・max-w-[92%])を外して行の全幅で描くようにし、user の発話は右寄せの吹き出しのまま残した。あわせて転写リーダーが本文末尾に書く > **Tool calls (N)** 節を splitToolLog(src/lib/chat/chat-tool-log.ts)で本文から切り離し、Tool calls · N の 1 行チップに畳んだ。Tool calls・Thinking(#2272)・承認ダイアログ(#2245)の 3 つは同じ見た目のチップになり、トランスクリプト右上の 1 つのトグル(commandmate:chatShowToolActivity に永続化)で一括開閉できる。旧形式(本文先頭の - `Bash` — … 連続行)も同じ経路で畳まれ、生成中・確定待ちのバブルも同じ扱いになる。検索が畳まれたツールログにヒットした行は自動で開く。
  • feat(ui): チャット面のまま yes/no・選択リスト・pager に答えられるようにした(Epic #2192 決定 5 の撤回) (#2254): チャット出力面で選択リスト/pager/未分類フレーム/読み取れない待ちのいずれかが起きたとき、これまでは「ターミナルで確認」バナーを出して terminal 面へ送るだけだった(Epic #2192 決定 5)。この決定を撤回し、バナーの下にターミナルペインの末尾をそのまま描いた ChatDialogCard(PC 16 行 / スマホ 12 行、ANSI 保持の常時ダーク島)と、状態別の操作(選択リストと pager は NavigationButtons、未分類は TerminalEscapeHatch + 新設 PromptAnswerKeys、読み取れない待ちは PromptAnswerKeys)を出すようにした。あわせて NAVIGATION_KEY_VALUES に 1〜9 / y / n(ANSWER_KEY_VALUES)を追加し、/special-keys から文字で答えられるようにした(自由文字列は通さず、/send の prompt_waiting ガードは不変)。切り出し(lib/chat/dialog-frame.ts)は空行圧縮 → 末尾 N 行の順で、逆順にすると codex のように内容がペイン上端に来るツールでカードが空になる(隔離 tmux 200x1000 実測 2026-09-03: claude 2.1.259 /model は 1000 行中の 986〜1000 行目に、codex 0.151.0 /model は 1〜32 行目に描かれ、後者は素の slice(-16) が全て空行。frame に realtimeSnippet ではなく output を渡すのも同じ理由)。二重化を避けるため、チャット面表示中は PC footer の NavigationButtons / TerminalEscapeHatch とスマホ docked の NavigationButtons を出さない(PromptPanel / MobilePromptSheet は答えられる待ち専用なので対象外)。
  • feat(ui): 履歴列とファイルパネルの開閉をヘッダのトグル 1 つに寄せ、非表示時の縦帯を撤去 (#2259): 同じ 1 つの状態を動かすボタンが 3 か所(ヘッダ/列ヘッダの >/非表示時の縦帯)にあった状態を、アクションバーのトグルだけに一本化。TerminalSplitPaneContent の split-history-expand-bar-*(w-9=36px/3 split で 108px)、FilePanelSplit の file-panel-expand-bar(FILE_PANEL_BAR_WIDTH_PX=36px)と panel 左端の 20px 折りたたみ列、TerminalContainer の同型 expand bar(#744 以降 production から history prop が渡らず未使用だったもの)を撤去し、隠したぶんの幅はターミナルが width:100% で使い切る。ヘッダの [履歴] は全 split が chat 面のとき disabled+aria-disabled(chat 面は #2232 の判断どおり History 列を持たないため、押しても無効果だった/chatSurfaceSlot が履歴列を含まないのが根拠)、[開いているファイル] はタブ 0 件かつ差分なしのとき disabled にし、タブ数をバッジ表示。語も分け、ActivityBar の activityBar.files を「ファイルツリー」/"File Tree"、ヘッダの terminal.filesLabel を「開いているファイル」/"Open Files" へ(activity id files は不変)。列ヘッダの折りたたみアイコンは畳む向き(ChevronRight→ChevronLeft)に直し、両トグルへ aria-expanded と aria-controls(履歴は split ごとの split-history-slot-N を空白区切りで全列挙、ファイルは新設の FILE_PANEL_PANE_ID)を付与。split の表示モードはペインが持つため、useSplitSurfaceModes(localStorage 読み+commandmate:surfaceModeChange CustomEvent)を新設してアクションバーへ伝え、タブ数は FilePanelSplit が OpenFilesContext で配下のアクションバーへ公開する(useFileTabs / FilePanelTabs には触れていない=#2260 と非干渉)。useHistoryPaneState / useFilePanelState の CustomEvent 同期経路はそのまま維持。

Fixed

  • fix(slash-commands): Command Code セッションの palette に install 済み Skill が 1 件も出ない問題を修正 (#2322): loadCommandCodeSkills() を追加し、Command Code 1.47.0 で実測した discovery root 2 つ(.commandcode/skills → .agents/skills、COMMAND_CODE_SKILL_SUBDIRS)を worktree と $HOME の両方でスキャンして source:'skill' / cliTools:['command-code'] を付ける。これまで .agents/skills の行は loadAgentsSkills() が cliTools:['codex','antigravity'] で作っていたため filterCommandsByCliTool が command-code セッションで全件落としており、Skill が 1 件も出ていなかった。codex-skill に相乗りさせないのは getSlashCommandTrigger がそれを $name と綴るからで、1.47.0 で skill_loaded イベントを出した呼出経路は /name の方(cmd -p "/probe-agents-root" --output-format json が {"type":"event","event":{"type":"skill_loaded","name":"probe-agents-root"}} を出して PROBE_OK_probe-agents-root で終了)。keyOf は name::command-code になるので codex / antigravity / claude / opencode の行は不変。.claude/skills は意図的に走査しない — 陰性対照として .claude/skills にだけ植えた /probe-claude-root は I don't see a skill named probe-claude-root available と答え skill_loaded は 0 件だったので、出すと「palette に在るのに Command Code が知らない」実行不能な行になる(CommandMate の installer は #1460 で .agents/skills にも同一 payload を置くため追加配置は不要)。route 側は opencode 分岐(#2037)と同じ置き方で cliTool === 'command-code' のときだけ loadCommandCodeSkills(worktree.path) と loadCommandCodeSkills(os.homedir()) を追加ロードし、global → worktree の順で merge するので同名なら worktree の Skill が勝つ。getSlashCommandGroups()(tool 非依存の共有 worktree 層)からは呼ばない。opencode と共通化した private foldSkillRoots() が両者の root fold を担う。
  • docs(user-guide): チャット出力面の記述を #2232 以降の実装に合わせる (#2299): webapp-guide(日英)が「チャット=あなたの発言とエージェントの返答をペアで表示」のままだったのを、#2232 が撤回した実装(ConversationPairCard ではなくフラットな吹き出し列=メッセージ 1 件が 1 バブル、ユーザーは右の吹き出し、Assistant の返答は折りたたみ無しの全文)に合わせて書き直した。#2232 でチャット面から外れた「アーカイブも表示」トグルの記述を削除し、表示件数セレクト・User only と併せて History 列側の制御であることを注記(webapp-guide.md:251 付近の History 節にある同名トグルとは別物)。「チャット面に出るもの」に、Tool calls・思考・承認ダイアログが既定で畳まれた 1 行チップになること(#2245 / #2272 / #2284)と、生成中の本文がその場で確定行に置き換わり位置も文字組も変わらないこと(#2233 / #2248)、末尾/先頭ジャンプ FAB(#2283)を追記した。あわせて docs/implementation-history.md に Epic #2192 (#2193 / #2201 / #2232 / #2233 / #2245 / #2248 / #2254 / #2272 / #2283 / #2284)のまとめ行を追加した。docs/architecture.md は #2241 で追随済みのため無変更。
  • fix(ui): チャット面のダイアログカードで Command Code のピッカーだけを描く (#2326): extractDialogFrameTail の selectionList 経路をツール別の矩形切り出しレジストリ(SELECTION_LIST_FRAME_CROPPERS)にし、opencode の overlay 矩形に加えて Command Code のダイアログを extractCommandCodeSelectionListFrame(フッタ直上の全幅 ─ 区切り行の次の行〜フッタ行)で切り出すようにした。Command Code はインライン描画(alternate_on=0)なので /model を開いてもペインの会話が残り、実測(v1.47.1 / 200x1000 / 5 ターン)では 333 行中 256 行が会話でカードは picker を折り返しの下に押し出していた。フッタが無いフレーム(ピッカーを閉じた直後など)と切り出し結果が空になるフレームは従来どおり全行にフォールバックしてカードが空にならない。あわせてカード高さ上限の引き上げ条件を「選択リストかどうか」から「切り出し後の行数が 24 行を超えるかどうか」に変え、超える場合は PC max-h-[28rem]・スマホ max-h-32 に代えて両面 max-h-[35vh] を使う(picker を 21 行見せたうえでチャット本文に 8 行以上を残す。2 択の folder-trust のような短いダイアログは #2106 の縦予算のまま)。
  • fix(codex): hooks レビュー画面での起動固着を解消 (#2315): $CODEX_HOME/hooks.json の hook command がサーバの checkout 絶対パス(process.cwd()/scripts/hooks/…)を埋め込んでいたため、同一マシンで複数の worktree サーバが 1 本の共有ファイルを互いに書き換え合い、そのたびに codex の trusted_hash が無効化されて Modified since last trusted のレビューが再発していた。リレースクリプトを $CODEX_HOME/commandmate/ に配置して checkout 非依存にし、生成内容を $CODEX_HOME だけの関数にした。あわせて起動時のレビュー画面処理を作り直し、画面1〜3 を位置ベース(getCodexLifecycleDialog)で判定して画面ごとに予算を持たせ(進捗でリセット)、既定で 2(Trust all and continue)を送って信頼を確立するようにした(自前の .codex/hooks.json を持つ worktree と CM_CODEX_HOOK_TRUST=never は従来どおり 3+esc)。起動後に再表示された場合も sendMessage 側で回収する。
  • fix(detection): codex のダイアログをフッタ文言ではなく構造で判定し、入力待ちを ready と誤報しないようにする (#2310): codex の検出器は「ダイアログかどうか」を CODEX_SELECTION_LIST_PATTERN(press enter to confirm / press enter to select)というフッタ文言のホワイトリストだけで決めていたため、それ以外の文言で終わるダイアログは素通りして ^› を idle composer と読む 2 分岐(afterThinking の B と共有チェーンの step 3)に落ち、キー入力待ちのまま ready(緑)になっていた。結果として Auto-Yes は答えるべきものが無いと判断し、サイドバーは緑になり、commandmate wait は basis=scraper_ready で作業途中に「完了」を返しうる。隔離 tmux(200x1000・codex-cli 0.153.2)での実測で、/experimental(フッタ Press space to select or enter to save for next conversation)と /keymap(フッタ left/right group · enter edit shortcut · … · esc close)の 2 画面が実際に ready を publish していた(フレームは tests/fixtures/codex-live-2310/)。この 2 つはどちらも番号なしのリストで、Issue 本文が主軸に想定していた findNumberedOptionBlock だけでは救えないため、主判定は SGR 属性の方に置いた: 新設 src/lib/detection/tools/codex/cli-patterns.ts の readCodexGlyphRowKind() が生フレームの › 行を composer / 転写エコー / ダイアログ選択肢の 3 用途に判別し(composer=太字グリフ+非太字ラベル、転写エコー=dim グリフ、選択肢=太字ラベル または 着色グリフ。0.151 以降の trust ダイアログは太字でなく色だけなので「composer でない」ではなく選択肢を積極的に認識する)、readCodexDialogFrame() が「最下段の › 行が選択肢である」か「› 選択つき番号ブロックが内容領域の末尾にある」のいずれかでダイアログと判定する。番号ブロック側は属性が composer だと言えば取り下げるので、手打ちの 1. buy milk を抱えた composer(composer-residual-leading-number.txt)は従来どおり ready のまま。判定は分岐 0.85 として 0.8(フッタ白リスト)の直後・detectPrompt より前に置き、detectPrompt が読めるフレームは従来どおり prompt_detected + hasActivePrompt(trust ダイアログや文言が変わった承認が wait --on-prompt agent の exit 10 を失わない)、読めないものは codex_selection_list(=waiting・menu カード・NavigationButtons)にするので、既に解決していたフレームの結論は 1 つも動かない。#1160 の stale ガードは 0.8 と同じく適用され、答え済みブロックの下で codex が動いているフレームは最下段の › が composer なので属性側も独立に同じ答えを返す。分岐 B と D の CODEX_PROMPT_PATTERN 一致にも同じ属性読みを拒否権として足した(生フレームが積極的にダイアログ行だと示したときだけ ready を止めるので、ANSI 除去済みのキャプチャは従来どおり idle と読む)。フッタ語彙は判定には使わず CODEX_DIALOG_FOOTER_PATTERN(実測 7 文言を網羅)で副次シグナルとして保持し、構造でダイアログと認めたのに語彙が一致しなければ logger.warn('codexDialogFooterUnrecognised') をプロセス内 1 回だけ出す(codex の文言変更が静かに取りこぼされないための tripwire。CODEX_DIALOG_RULES_VERIFIED_AGAINST = 0.153.2 / 2026-09-04 / 200x1000 で規則の実測版を刻印。detector 全体のスタンプ CODEX_VERIFIED_AGAINST は 0.148.0 のままで、tools/verified-against.ts が本 Issue のスコープ外のため追随していない)。既知の限界として、番号なしダイアログの ANSI 除去済みキャプチャは属性も番号も残らないため依然 ready と読む(Auto-Yes の captureAndCleanOutput 経路。セッションステータスは publish しない)ことをテストで明示的に pin した。
  • fix(ui): チャット面のダイアログカードで背景色ハイライトのピッカーを追従させる (#2323): Command Code の /model のように選択行をキャレットではなく背景色で示す TUI で、矢印キーでハイライトを動かしてもカードの scrollTop が一度も動かなかった問題を修正。findHighlightLineIndex() がキャレット(❯ / › / ●)しか見ておらず、検索ボックス行「› Type to search models...」(矢印では動かない行)に誤ヒットして固定されていた(#2318 の算出式は正しく、入力の行番号が常に誤っていた)。SGR 背景色の走査を src/lib/detection/opencode-modal-overlay.ts から src/lib/detection/sgr-background.ts へ切り出して共有し、検出を「キャレット行と隣接行が使っていない背景色で塗られた行のうち、画面上で最後にある方」に変更。あわせて走査の前景色バグ(38;2;r;g;b の各チャンネルを 16 色背景コードとして読んでいた)を修正した(実測: Command Code /model の 1001 行中、背景色を持つ行が 70 → 8)。キャレットを持つ claude / codex / copilot / gemini の index は不変(リポジトリの ANSI 実キャプチャ 103 件で、背景色がキャレットに優先するのは本 Issue のフレーム 1 件のみ)。opencode の 200 桁ペインはテーマがペイン全体を塗るため引き続き追従対象外(#2255 の構造化ピッカーで解消予定)。
  • fix(ui): PC の Agent アクティビティパネルを縦スクロール可能にする (#2316): AgentInstancesPane に className prop を追加し、PC マウント(WorktreeDetailDesktop)から他のアクティビティパネルと同じ className="h-full" を渡すことで、パネル自身が overflow-y-auto のスクロール担当になるようにした。これまでは「Use this layout as the default」「Agent CLI versions」を展開すると、overflow-hidden のアクティビティ列にクリップされて下端が確認できなかった。モバイル(MobileAgentInstancesPane, #874)は同じパネルを自前のスクロールタブ内にコンテンツ高で埋め込むため className を渡さず従来どおり(二重スクロール・高さ潰れを避ける)。
  • fix(a11y): common/Tooltip をキーボードフォーカスでも表示する (#2319): wrapper <span> が onMouseEnter/onMouseLeave しか購読しておらず、Tab でアイコンボタンにフォーカスしても tooltip が出なかった(#730 の実装当初からの制限で、#2307 がネイティブ title から移行した動機である「キーボードフォーカスでも出る」という利点が得られていなかった)。onFocus/onBlur(React が bubbling する focusin/focusout として委譲するため、子ボタンのフォーカスが wrapper に届く)を追加し、ホバーとフォーカスを独立した 2 チャネル(hoverVisible / focusVisible)の OR として扱うことで、クリック直後のようにホバーとフォーカスが同時に立つ状態で片方が解除されても、もう片方が保持している bubble を消さないようにした。フォーカス経路は遅延なしで即時表示(ホバーの 100ms はポインタの通過による誤爆を防ぐためのもので、明示操作であるフォーカスには不要)。あわせて WAI-ARIA tooltip パターンに従い Escape で dismiss できるようにした(visible の間だけ window に keydown を張るのでホバー表示中も効き、preventDefault/stopPropagation はしないので利用側の Escape 処理は従来どおり動く)。wrapper の tabIndex={-1} と aria-describedby を配線しない #730 の設計は変更なし。消費側 7 ファイル・17 箇所(ActivityBar / Sidebar / TerminalSplitContainer / TerminalSplitPane / TerminalSplitPaneContent / WorktreeDetailSubComponents / SortSelectorBase 経由)は無変更で恩恵を受ける。
  • fix(ui): チャット面のダイアログカードでハイライト行がカードの可視範囲に入らない (#2318): #2309 の「ハイライト追従」が、行 i の位置を スクロール可能距離(scrollHeight - clientHeight)に対する比率として計算していたため、実際の行位置(scrollHeight に対する比率)より scrollable / scrollHeight の分だけ圧縮され、リストの下に行くほど誤差が拡大していた。UAT 実測(claude の /model、PC 1440x900、79 行 / scrollHeight 1211 / clientHeight 446 / scrollable 765 = 圧縮率 0.63)では ▼ を 4 回押しても全サンプルでハイライトが可視範囲外に留まり、行 71 の実位置 1074px に対して可視範囲は 473..919 と 155px 手前で止まっていた(手動で scrollTop を最大の 765 にすると可視になり、そこで ▼ を押すと 473 へスナップバックする、という決定的な対照つき)。分母を totalLines - 1 から totalLines に(行 i は i 行ぶん下、行は全部で totalLines 本ある)、被乗数を scrollable から 行が実際に占める高さ に直した。行は 1 行 1 要素ではなく 1 つの HTML blob なので scrollIntoView は使えず、行高からの算出であること自体は #2309 のままで、#2255 の構造化ピッカーが着地すれば計算そのものが不要になる繋ぎの修正。あわせて scrollHeight がスクローラのパディングボックス(p-2)を含む点を扱い、パディングは 8 と直書きせず getComputedStyle から読む(後日 p-3 に変えたときに無言でハイライトが可視外へ戻らないようにするため。パディングを報告しない箱=jsdom は 0 にフォールバックする)。中央寄せも行の上端ではなく行そのものを中央に置く(clientHeight - lineHeight の半分)ので、クランプが効く最下行でも行全体が可視範囲に収まる。テストは行数・scrollHeight・clientHeight を固定した擬似フレームで、ハイライト行が scrollTop .. scrollTop + clientHeight に入ることを 上端・中央・下端の 3 位置と ▼ 4 連打の系列で確認する(誤差が index に比例するため上端は旧実装でも緑になる=上端だけを見るテストは空虚。scrollHeight を scrollable に戻す変異注入で中央・下端の 5 テストが赤くなることを実測)。
  • fix(ui): チャット面のダイアログカードで command-code / opencode の長い /model ピッカーを見えるようにする (#2309): 選択リストでは末尾 16 行への切り詰めをやめてカード内をスクロール可能にし、opencode の中段オーバーレイはオーバーレイ矩形の行範囲だけを切り出して表示、矢印移動で動くハイライト行をカードのスクロール内に追従させるようにしました。
  • fix(ci): npm ci と npm audit を npm レジストリの audit エンドポイント障害から切り離す (#2313): 2026-09-04、レジストリの audit エンドポイント(/-/npm/v1/security/advisories/bulk と audits/quick)が POST に応答しなくなった(TCP 接続は受け付け、リクエスト送信後に 0 バイト無応答か {"error":"Service Unavailable"}。同時刻に status.npmjs.org は全コンポーネント operational と表示)。npm ci は既定で最後に audit を POST し npm 自身の fetch-timeout(300000ms = ちょうど 5 分)まで待つため、step 上限 5 分と同じ瞬間に打ち切られ、1 日で 15 件の CI ジョブが常に 312 秒で落ちた。いずれも本来の検査(npm run lint / next build / playwright)を一度も実行しないまま skipped になっており、PR 画面には「Lint failed」とコードの問題であるかのように表示されていた(同じコードはローカルで全ゲート exit 0)。対応は 2 段: (1) 7 箇所の npm ci に --no-audit(install に audit は不要。同じ障害中に devDeps 込み 1,079 パッケージで 16 秒・exit 0)、(2) Security Audit ジョブは npm audit --json --fetch-timeout=60000 --fetch-retries=1 の出力を scripts/check-npm-audit.mjs で読み、auditReportVersion を持つ本物のレポートでゲート以上の件数があれば exit 1、npm のエラー封筒(レポート無し)なら「この run では監査していない」旨の ::warning で exit 0、どちらでもない出力は推測せず exit 1。ジョブ全体を continue-on-error にすると本物の critical も素通りするため採らなかった。step / job の timeout-minutes は #1830 の値のまま(当初「ランナー競合」と誤診して延長したが、真因は npm 側だったため取り消した)。tests/unit/guards/verify-build-integration-gates.test.ts の install 除外を npm ci の完全一致から /^npm ci(\s|$)/ に広げた。
  • fix(ui): チャット面の /model をツールごとの確定キーで撃てるようにする (#2297): 選択リストのダイアログカードが矢印+Enter 固定だったのを、フレームの実測(readSelectionListShape())に応じて claude の「このセッションのみ(s)/既定に設定(Enter)」の 2 ボタン・候補数ぶんの番号キー・opencode のモデルチョード(C-t / C-x m / C-p)に切り替える。s を claude / Command Code の navigationKeys() と tmux 送信許可リストに追加し、Command Code の /model ピッカー(検索ボックス付き・番号なし)を選択リストとして検出するようにして、未分類扱いで番号キーが検索欄に入力される経路を塞ぐ。キー送出後はキャプチャキャッシュを再破棄+カード側で再取得し、1 秒以内にハイライト移動が見えるようにする。
  • fix(ui): PC でサイドバー表示中にファイルを拡大するとサイドバーの下に潜る (#2294): 拡大オーバーレイ(MaximizableWrapper)・MarkdownEditor の CSS フォールバック全画面・ContextMenu を createPortal で document.body 直下に描くようにした。main[role="main"] の view-transition-name: cm-content(#1122)がスタッキングコンテキストを作るため、main 内にインラインで描く fixed 要素は Z_INDEX.MAXIMIZED_EDITOR (55) を宣言してもサイドバー (30) の下に回り込み、左端 224px ぶんの表示と操作が奪われていた
  • fix(detection): agy のスタンプが 0.4.x のままで antigravity が恒久的に STALE 報告されていた (#2292): ANTIGRAVITY_VERIFIED_AGAINST を #2270 が実際にルールを読み直した実測値(1.1.25 / 2026-09-04 / 200x1000)へ更新した。#2270 の契約 scope.allow に tools/verified-against.ts が入っておらず、実測は 1.1.25 の実ペイン(200x1000、2026-09-04 採取)で行われたのにスタンプだけが 0.4 系(0.4.x / 2026-07-30 / inline)に取り残されていたため、getDetectorFreshness() が 1.1.25 を 0.4.0 と比較し、ルールが最新の環境でも commandmate status が毎回「antigravity: installed 1.1.25, rules read off 0.4.x」を出していた。ワイルドカード形式のスタンプは 0.4.x が唯一の利用者だったため parseVerifiedAgainstVersion() の該当分岐は利用者ゼロになるが、形式自体は手書きスタンプで今後も書けてしまい、分岐を削ると将来の 2.0.x が null として unmeasured 扱い(=無警告で恒久的に非 stale)になるため残し、判断理由を同関数の docstring に記録した。
  • fix(detection): antigravity の番号つき権限ダイアログを選択リストではなくプロンプトとして検出 (#2270): Do you want to proceed? と N. 行を持つフレームでは beforePrompt の選択リスト判定をスキップして priority 1 の prompt 検出に渡すようにし、hasActivePrompt: true / promptData(multiple_choice 4択)/ isSelectionListActive: false を publish するようにした。チャット面に PromptPanel が出て 2〜4 を番号で選べるようになり、poller が保存する prompt 行・push 通知の kind: 'prompt' と状態検出の答えが一致する。Switch Model ピッカー(#995)は従来どおり antigravity_selection_list
  • fix(ui): チャット面を開いた直後に最新のやり取りが見えるようにし、先頭/末尾へのジャンプを 1 つの FAB に集約 (#2283): ターミナル→チャット切替時・再読み込み時・ローディング解除時のいずれでも ChatTranscript が仮想リストの最終行へアンカーし、行の実測で総高が伸びても末尾に留まるようにした(scrollToIndex(last, 'end') を総高が動く間だけ再照準。最大 12 フレーム / 600ms)。ChatSurface のマウント時 scrollTop = scrollHeight を撤去。右下に円形 FAB を追加し、末尾では先頭へ(↑)、それ以外では末尾へ(↓、生成中はスピナー+chatSurface.jumpToLatestGenerating)ジャンプする。ジャンプは両方向とも virtualizer 経由で scrollTop 直接代入を使わない(実測で 7,770px 手前に止まっていた)。スクロール領域を tabIndex={0} にして Home/End を効かせ、先頭へ飛んだあとは新着で末尾へ引き戻さない。ChatTranscript が scroll controls を publish している間は ChatSurface の「新着へ」チップを引っ込め、ジャンプ手段が同時に 2 つ出ないようにした。
  • fix(history): opencode の返答から reasoning を本文の外へ出し、チャット面では折りたたみチップにする (#2272): separateTurnBody に reasoning ブロック種別を追加し、opencode の reasoning part を末尾の > **Thinking (N)** セクション 1 つへ畳んだ(本文 → reasoning → ツールログの順)。チャット面の ChatMarkdownBody は新形式と旧形式(先頭の > **Thinking** 引用)の両方を splitChatThinking で本文から外し、#2245 のツール承認チップと同じ部品で既定は閉じた状態で描く(開くと全文が読める。コピーは全文のまま)。History 列(ConversationPairCard)と claude / codex / antigravity / command-code の aside は無変更。
  • fix(ui): 版ズレバナーと SW 更新トーストが消えない/footer を塞ぐ問題を解消 (#2271): 版ズレ判定の比較対象を実行時 package.json から .next に焼かれた bundle の版(resolveBundleDrift() / getServedBundleVersion())へ変え、bump だけでは発火せず再ビルドで bundle が入れ替わったときだけ発火するようにした。あわせて Service Worker の更新トーストをバナー表示中は抑止し、コンテナを pointer-events-none(カードのみ pointer-events-auto)にして右下へ寄せ、composer 下の Auto Yes スイッチを塞がないようにした
  • fix(history): 返答行をターン終了時刻で記録し、チャット面で承認チップを返答の前に描く (#2273): 転写リーダー 5 種(claude / codex / antigravity / command-code / opencode)が assistant 行をターン開始ではなくそのターンの最後の記録の時刻で書くようにし、buildChatTranscriptRows に「同じターンの prompt 行を返答より前へ寄せる」規則を足した。ツール承認のチップが返答の後ろに落ちて「質問→返答→Tool approvals」と並んでいたのが「質問→承認→返答」になる。user 行より前に出ないこと(#2196)と、次ターンの queued な /send 行を追い越さないことは上下限で保証する
  • fix(ui): チャット面のファイルパス検出に左境界を入れ、クリック時に存在確認する (#2274): FILE_PATH_REGEX が語境界なしに / から一致していたため commandmate-skills/docs/uat/x.md の後半 /docs/uat/x.md だけがリンク化され、1 つのパスが 2 色に割れて存在しないファイルを開こうとしていた。行頭・空白・バッククォート・引用符・開き括弧の直後だけを一致開始位置とし(相対パスと URL はリンク化しない)、ConversationPairCard の正規表現コピーを廃して splitFilePathParts を共有、クリック時は HEAD /api/worktrees/:id/files/:path(新設・本文なしの存在プローブ)で確かめ、404/400/403 のときはファイルパネルを開かずトーストを出す
  • fix(detection): copilot 1.0.82 の返答が ▀ の壁とフッタごと保存されるのを直す (#2269): copilot 1.0.82 は composer の枠を全幅 ─ 罫線 2 本から半角ブロックの枠(╻▄… / ┃ / ╹▀…)へ描き替えたため、1.0.80 の罫線しか知らない COPILOT_RULE_ROW に一致せず findCopilotChromeStart が全フレームで -1 を返し、contentEnd がペイン全体に落ちて cwd 行・枠・composer・← open sidebar · / commands · ? help · tab next tab フッタごと返答として保存されていた(1 語の返答でも同じ)。加えて 1.0.82 はプロンプトのエコーを ▄ と ▀ の全幅区切りで囲むようになり、COPILOT_SKIP_PATTERNS の /[█▘▝▖▗▔▄▌▐]/ は ▄ を落とすが ▀(U+2580)を含んでいなかったため、抽出がエコーの 1 行下から始まる以上「199 個の ▀」が保存された返答の先頭行になっていた。COPILOT_RULE_ROW に 1.0.82 の枠を(角グリフ ╻/╹ 必須 — 転写側の区切りは角を持たないので、これを緩めると境界が約 985 行上にずれて返答ごと消える)、COPILOT_COMPOSER_GLYPH に枠の左端 ┃ を追加し、新設の COPILOT_TRANSCRIPT_DIVIDER_PATTERN([╻╹]?[▀▄]{10,} の単独行のみ。1 個だけの半角ブロックを含む返答は残る)と COPILOT_TOOL_ROW_PATTERN(1.0.82 はツール行の動詞の前にファイル種別バッジ / {} MD TS PY を置くようになった。バッジは 2〜4 文字必須 — 1 文字だと英文の "I Read …" に当たる)を COPILOT_SKIP_PATTERNS へ追加した。ツール行の動詞は COPILOT_TOOL_VERBS に一本化して response-cleaner.ts の ● 版と共有し、copilot が返答できないプロンプトに対して呼ぶ ask-user ツールの行(● Asked user Hi — what would you like to work on?)を落とすため Asked user / Asking user を追加した(Issue が「起動画面が返答として保存される」と報告した現象の実体はこの行で、hasUserEcho はアンカーのエコー自体が抽出範囲の外にあるため常に false であり、エコーが 1 文字かどうかは判定に効いていなかった)。さらに cleanCopilotResponse は skip パターンを正規化の後に当てるためグリフ由来の規則が全て死んでおり、1.0.82 の起動画面の 2 行(╰─╯╰─╯ Copilot v1.0.82 uses AI. と └ Enable all permissions …)が素通りしていたので、extractCopilotContentLines と同じく COPILOT_BOX_ROW_PATTERN を正規化前に当てるようにし、同パターンとタブバー行・● Tip: /… 行の規則を COPILOT_SKIP_PATTERNS に足して起動画面が空にクリーニングされて保存され得ない状態にした。実測は 1.0.82 を 200x1000 の私設 tmux ソケットで駆動した生ペイン 8 枚(tests/unit/lib/detection/fixtures/copilot-live-2269/)で、保存経路(accumulator → cleanCopilotResponse)を通した結果が uat-run1 / 13 / hello-2269 / 2269-ok / done と起動画面・ask-user ターンの空文字であることを固定した。COPILOT_VERIFIED_AGAINST は 1.0.80 → 1.0.82(2026-09-04)へ更新。完了判定(readCopilotStatusBar)は 1.0.82 でも壊れておらず無変更。