Repository navigation
v0.31.0
[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+gpopup と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_requestrun を自動でキャンセルする (#2330):/orchestrate6-2 は「フル CI の完走を待たずにマージしてよい(develop の push run が安全網)」という設計なので、マージ後も走り続ける PR run は設計上読まれない裁定を作るために self-hosted のジョブを占有していた。2026-09-05 に本ブランチで再実測(直近 200 マージ PR ×ci-pr.ymlのpull_requestrun 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 のpushrun には構造的に届かない(ライブ 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で赤く落とす。あわせて/orchestrate6-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-codeleaf 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-videoskill の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 commit4583929c)で着地済みで本ブランチにも入っているため、本 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.js7 箇所のcacheを${{ runner.environment == 'github-hosted' && 'npm' || '' }}に変えた。self-hosted ARM64 では 287 MB のキャッシュ復元が約 8 MB/s(36 秒)で、それが短縮するはずの coldnpm 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に任意のclassNameprop を追加してツールバーのレイアウトを崩さないようにした。 - 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 からhistoryprop が渡らず未使用だったもの)を撤去し、隠したぶんの幅はターミナルがwidth:100%で使い切る。ヘッダの [履歴] は全 split が chat 面のときdisabled+aria-disabled(chat 面は #2232 の判断どおり History 列を持たないため、押しても無効果だった/chatSurfaceSlotが履歴列を含まないのが根拠)、[開いているファイル] はタブ 0 件かつ差分なしのときdisabledにし、タブ数をバッジ表示。語も分け、ActivityBar のactivityBar.filesを「ファイルツリー」/"File Tree"、ヘッダのterminal.filesLabelを「開いているファイル」/"Open Files" へ(activity idfilesは不変)。列ヘッダの折りたたみアイコンは畳む向き(ChevronRight→ChevronLeft)に直し、両トグルへaria-expandedとaria-controls(履歴は split ごとのsplit-history-slot-Nを空白区切りで全列挙、ファイルは新設のFILE_PANEL_PANE_ID)を付与。split の表示モードはペインが持つため、useSplitSurfaceModes(localStorage 読み+commandmate:surfaceModeChangeCustomEvent)を新設してアクションバーへ伝え、タブ数は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 と共通化した privatefoldSkillRoots()が両者の 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 行を超えるかどうか」に変え、超える場合は PCmax-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にclassNameprop を追加し、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 行 /scrollHeight1211 /clientHeight446 /scrollable765 = 圧縮率 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_choice4択)/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 のreasoningpart を末尾の> **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 はツール行の動詞の前にファイル種別バッジ/{}MDTSPYを置くようになった。バッジは 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 でも壊れておらず無変更。