公開日
Windows のコンソールを外から操作する
Slack からターミナルの画面を読み、選択肢を選び、/exit を打ち込むために PowerShell スクリプトを書いた。別のコンソールで画面が読めない、複数キーが届かない、文字化けで文言が一致しないなど、泥臭い問題を順に解いた記録。
はじめに
claude-slack-channel には、Slack から手元のターミナルの画面を読み、選択肢を選び、/exit や /compact を打ち込む機能がある。Claude Code の channel には、セッションにコマンドを送る手段が無い。ターミナル側の選択画面で止まると、Slack からは何もできなかったんだよな。
そこで、Windows のコンソールを外から操作する PowerShell スクリプト console.ps1 を書いた。やることは 3 つ。画面の文字を読む、決まったキーを送る、決まったコマンドを打つ。自由な文字入力は、わざと作っていない。
この記事は、このスクリプトで踏んだ問題を順に書く。AI と壁打ちしながら原因を絞ったが、どれもやっぱ実機で確かめないと分からなかった。
読めるのは、決まったキーだけ
画面の読み取りは Win32 API の ReadConsoleOutputCharacter を使う。送れるキーは上下と Enter、それに警告ダイアログに答えるための 1 だけ。コマンドも /exit /compact /clear の 3 つに固定した。
Slack のアカウントを乗っ取られたときに、ターミナルへ任意の文字を打てる経路は流石に作りたくなかった。選択肢を選べれば、止まった画面は解除できる。
複数のキーが届かない(#5)
!screen の選択肢ボタンは、カーソルを動かしてから Enter を送る。ところが、実機ではキーが 1 つも届かなかった。
原因は PowerShell の引数の扱い。powershell.exe -File で -Keys Down,Enter を渡すと配列にならず、"Down,Enter" という 1 つの文字列として ValidateSet に弾かれていた。1 キーなら通るので、テストでは気付かなかった。
-Keys は文字列で受け、カンマで分けて 1 つずつ許可一覧と照合する形にした。1 つでも違えば何も送らない。実機では、別のコンソールを開いてそこで受け取ったキーを記録し、修正前は 0 件、修正後は 4 つのキーが順に届くことを確かめた。
別のコンソールで画面が読めない(#39)
!screen が常に「(空)」を返し、!restart は常に「入力待ちでない」と断っていた。
Claude Code は MCP サーバーを別のコンソールで起動する。ブリッジの子として動く console.ps1 は、ブリッジのコンソールを読んでいたが、そこには何も表示されていないんだよな。Claude Code の画面は、親である claude.exe のコンソールにある。
-TargetPid を足し、FreeConsole で自分のコンソールを離してから AttachConsole で対象のコンソールに付け直すようにした。ブリッジは親プロセスの pid を渡す。付け直せなければ元のコンソールに戻し、stderr に知らせて続ける。テストは、別のコンソールを持つプロセスを起動して、その画面を読めることで確かめた。
この修正の実機確認で、もう 2 つ見つかった。起動直後の入力欄には薄く入力例が出ていて、打ちかけの文字と区別できず「入力待ちでない」になっていた。入力例だけの行は空とみなすようにした。もう 1 つは、再起動後に警告ダイアログで止まる件で、起動スクリプトが空白を含む引数を引用符なしで渡していた。
警告ダイアログに自動で答える
experimental の channels 機能を使うと、起動時に警告ダイアログが出る。!restart で起動し直すたびに手元で答えるのでは意味が無さすぎるので、dialog-answer.ps1 が画面を見張って 1 と Enter を送る。
最初はタイトルの文言だけで判定していた。これだと、前のセッションの発言に同じ文字列があると誤爆する。選択肢の文言も画面にあるときだけ答え、起動し直す前に画面を消すようにした(#28)。
文言は Claude Code の版で変わりうる。変わったら起動スクリプトの正規表現を直す前提で、どの版の文言で確かめたかをドキュメントに残している。初回の起動でも同じように答えるようにしたのは、今日の変更。
UTF-8(#24)
ダイアログの判定が日本語の文言で一致しないことがあった。console.ps1 は画面を UTF-8 で書き出すが、それを読む側が既定の cp932 で読んでいた。読む側の OutputEncoding を UTF-8 に合わせて直った。
PowerShell 5.1 は BOM なしの UTF-8 を ANSI として読むので、スクリプト自体も BOM 付きで保存している。
Claude Code の更新でチャンネルが無効になった
今日、Claude Code 2.1.289 でメッセージがセッションに届かなくなった。ターミナルには「no MCP server configured with that name」と出ていた。
この版から、--dangerously-load-development-channels server:<名前> は、設定ファイルに同じ名前の MCP サーバーが登録されていないと受け付けない。--mcp-config で渡したサーバーは数に入らない。
対処として、起動スクリプトが起動前に、作業フォルダーの local スコープへ同じ名前のサーバーを登録するようにした。この登録には目印の環境変数を付け、普段のセッションで読み込まれたときはツールを持たずに待つだけのサーバーとして動く。Slack のセッションは --strict-mcp-config なので、動くのは今までどおり --mcp-config のサーバー。
experimental の機能に乗っている以上、こうした変更はまた来る。起動スクリプトの判定と文言を 1 か所に寄せて、直す範囲を小さくしている。
次回は、形だけのテストを信頼できるテストに変えていった話。