2026年9月13日
AI Coding AgentsDeveloper ToolsCLI ToolsClaude CodeOpen Source

turnbell: AIエージェントのターミナルを見張るのをやめる

turnbell は AI コーディングエージェントの終了シグナルを音声・通知・スマホアラートに変える CLI。デーモンも SDK も不要。

1 分で読めます

長時間のエージェント実行を始めては、まるで借金取りのようにターミナルに張り付いていた。無駄な待ち時間が積み重なっていく。だから、自分の代わりにターミナルを見張ってくれる小さなツールを作った。

ターミナルで完了したエージェントの実行、音の輪が広がってスマホの通知「turnbell: claude-code finished」に届く様子

恥ずかしい話がある。AIエージェントに本物のタスク(認証モジュールのリファクタリング、ルートの移行、テストスイート全体の実行)を与えて、そして、ただ…見ているだけだった。6分間のプログレススピナー、そして自分はそれを、目を離した隙に消えてしまうかのようにじっと見つめていた。

代替案はもっと悪かった。Slack に切り替えて何かに引き込まれ、エージェントがとっくに終わって冷え切ってから10分後にふらっと戻ってくる。その頃には、自分が何を頼んだのかさえ見失っていた。

つまり実行自体は6分でも、自分には15分かかっていた。毎回だ。開始と完了の間にあるその隙間が、静かに自分の一日を食いつぶしていた。

🔔 シグナルはすでにそこにあった

厄介なのは、自分が知りたかった瞬間はすでに存在しているということだ。どのエージェントも、ターンを終えた瞬間や何かを尋ねるために止まった瞬間を正確に把握している。Claude Code は Stop フックを発火させる。Codex には notify コマンドがある。Gemini には AfterAgent イベントがある。Copilot には agentStop がある。

そのイベントは各ツールの中にすでに存在していた。誰も、それを単純な*「おい、戻ってこい」*に変換していなかっただけだ。

その捉え方こそが、不平を言うのをやめて作り始めるきっかけになった。何かをポーリングする必要も、ログをスクレイピングする必要もなかった。エージェントがすでに発信しているシグナルを捕まえて、音声、通知、サウンドに変えるだけでよかった。自分がどこに迷い出ていても届く、ちょっとした合図に。それをやってくれるのが turnbell だ。

仕組み: エージェント自身のフックが turnbell emit を呼び出し、それがローカルで音声・通知・サウンドに分岐する。ネットワークを経由するのはスマホへの webhook だけ

🧱 最初の決断は機能ではなく制約だった

1行も書く前に、このツールに許さないことを決めた。

プロセスリストの中でうなり続けるバックグラウンドデーモンはなし。すべてのプロジェクトにインポートするSDKもなし。何に触れているのか不安になるようなものもなし。各エージェント自身のフック設定を配線して1つのコマンドを呼ばせる、それだけが全フットプリントだ。

そこから2つのルールが生まれた。

  • すべての変更はバイト単位で可逆だ。 turnbell(npm にもある)は設定編集をアトミックに適用し、たった今書き込んだものの正確な差分を見せて、その場で維持するか元に戻すかを選ばせる。触れていないファイルは元とまったく同じバイト列に復元される。ブロックを注入しただけのファイル(エージェントの指示ファイルなど)は、そのブロックだけがきれいに取り除かれるので、その後周りに加えた編集はそのまま残る。
  • アンインストールは正直でなければならない。 追加したものだけを正確に元に戻し、それ以外は何もしない。上の2つのルールと同じ原則を使っているので、「可逆」だと謳っておきながらこっそりそうではない、ということが起きない。

実際のエンジニアリングの大半はここに注がれた。このツールが見つけたときとまったく同じ状態にマシンを残す、という地味な約束に。グローバル設定を編集するツールは、その権限に見合うだけの信頼を勝ち取らなければならない。

🔒 マシンから出ていく、たった一つのもの

スマホアラートが欲しかった、つまり何かがネットワークを経由する必要があった。それによって、自分は油断するどころかより慎重になった。

音声、デスクトップ通知、サウンドはマシンの外に出ることはない。定義上ローカルだ。唯一の送信経路はオプションの webhook で、それにはちょうど6つのフィールドだけを出力する単一のシリアライザがある。バージョン、タイムスタンプ、エージェント名、イベント、プロジェクトのフォルダ名、そして短いステータス行だ。

コードなし。作業ディレクトリなし。エージェントの応答なし。しかも、信頼する前に正確なバイト列を印字して確認できる。1つのフラグでペイロードを表示するだけで、何も送信しない。

プライバシーは、最後に取ってつけたマーケティング文句ではない。それは構造的な選択だった。ネットワークにはドアをちょうど1つだけ与え、そのドアを小さくする。

🧩 8つのエージェント、1つの契約

そして厄介な中間部がやってきた。たいてい、面白い仕事はそこに隠れている。

どのエージェントも異なる権限を持つ異なるフック群を公開している。Claude Code は完了、ブロック、読み上げ、スラッシュコマンドまで、すべてをこなせる。Cursor は完了したことしか知らせられない。opencode はトランスクリプトを公開できないので、返信の読み上げはそもそも選択肢にない。

それを取り繕って「均一にサポートしている」と謳いたくなる誘惑がある。自分はその逆をした。単一の wire/unwire 契約の裏に、エージェントごとに1つのアダプタを作った。そして機能一覧表は、それぞれが実際に何を提供できるのかという、ありのままの真実を伝えている。

なぜなら、誰かの信頼を最速で失う方法は、その人の環境では実現できない機能を約束することだからだ。正直な限界は、嘘をつく美しい表よりも優れている。

🎯 本当に学んだこと

楽しかったのは、しゃべるターミナルそのものではなかった。その周りにあるすべてだった。

本当の課題は、グローバル設定に、エージェントに、マシンに手を伸ばしながら、そこに居る資格に値するものを作ることだった。アトミックに適用する。可逆にする。ネットワークには小さなドアを1つだけ与える。できないことは声に出して言う。

自分は一つのテストに何度も立ち返った。アンインストールする人に見られているつもりで作れ。 そのたった一つの視点が、どんな機能要望よりも多くの設計判断を決めた。

今では長時間の実行を始めて、実際にその場を離れる。数分後、ノートPCが返信を読み上げてくれるか、エージェントが行き詰まって判断が必要になったときにスマホが震える。無駄な時間はなくなった。重要な瞬間にちょうど呼び戻されるだけだ。1分早くも、10分遅くもなく。

可逆な設定編集と、ネットワークにドアを1つだけ与えるという方針に共感するなら: turnbell はオープンソースだ: GitHub · npm。

可逆でないと分かった瞬間に、即座に信頼をやめる設定編集ツールは何ですか?

それがすでに自分に起きたことがあるのか、dotfile を書き換えて後戻りする方法を残さなかった何らかの CLI があったのか、そしてそれ以来、インストーラーの約束をどれだけ注意深く読むようになったのか、気になっている。