Claude codeが暇になってたら通知してくれたら便利じゃyね!?天才すぎる!!
とおもってつくってたんだけど、Hookができて投げ捨てたやつ。
供養カレンダー
> このプロジェクトが使っているClaudeCodeの暇状態を検出する方法は?色々あると思うので全部洗い出して。
Claude Code暇状態検出方法の全リスト
- 通常の入力プロンプト検出 (claude_monitor.py:119, claude_monitor_multi.py:186)
tmux出力から以下のパターンを検索:
- │ > - 通常のスペースでの組み合わせ
- │\xa0> - ノンブレーキングスペース(\xa0)での組み合わせ
-
\xa0 - プロンプトとノンブレーキングスペースの組み合わせ
- UI フレーム検出 (claude_monitor.py:121-122, claude_monitor_multi.py:188-189)
Claude CodeのTUIフレームを検出:
- ╰─ - 下部フレーム
- ╭─ - 上部フレーム
- Yes/No 確認プロンプト検出 (claude_monitor.py:126-139, claude_monitor_multi.py:193-206)
12種類のパターンで確認ダイアログを検出(大文字小文字区別なし):
- (y/n) - 汎用yes/noプロンプト
- [Y/n] - デフォルトYes
- [y/N] - デフォルトNo
- (yes/no) - 汎用yes/noプロンプト
- Continue? - 継続確認
- Proceed? - 実行確認
- confirm - 確認キーワード
- Are you sure - 確認フレーズ
- Do you want to - 意思確認フレーズ
- ❯ 1. Yes - Claude Code固有の選択肢UI
-
- Yes, and don't ask again - Claude Code固有の選択肢
-
- No, and tell Claude - Claude Code固有の選択肢
判定ロジック (claude_monitor.py:148, claude_monitor_multi.py:214)
return (has_prompt and has_frame) or has_yesno
待機状態と判定される条件:
- (方法A) プロンプト検出 AND フレーム検出
- (方法B) Yes/Noパターンのいずれかを検出
どちらか一方でも満たせば「暇状態」として通知が送られます。
> 画面の表示をテキストでとる方法は?
⏺ このプロジェクトでは tmux capture-pane コマンド を使って画面の表示をテキストで取得しています。
取得方法
- 基本コマンド (test_local.py:22)
tmux capture-pane -t : -p
- -t : ターゲット(セッション:ウィンドウ番号)
- -p : 標準出力に出力(print to stdout)
- SSH経由での取得 (claude_monitor.py:67-72)
ssh -p user@host "tmux capture-pane -t : -p | tail -10"
リモートサーバー上のtmuxセッションから取得する場合の方法です。
- 実装での具体例
単一サーバー版 (claude_monitor.py:67-72)
cmd = [
"ssh",
f"-p{self.config['ssh_port']}",
f"{self.config['ssh_user']}@{self.config['ssh_host']}",
f"tmux capture-pane -t {self.config['tmux_session']}:{self.config['tmux_window']} -p | tail -10"
]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
output = result.stdout # ここにテキストが入る
複数サーバー版 (claude_monitor_multi.py:120-127)
cmd = [
"ssh",
f"-p{config['ssh_port']}",
f"{config['ssh_user']}@{config['ssh_host']}",
f"tmux capture-pane -t {config['tmux_session']}:{config['tmux_window']} -p | tail -20"
]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
output = result.stdout # 最後の20行のテキスト
ポイント
- 取得範囲: tail -10 や tail -20 で最後のN行だけを取得(効率化)
- エンコーディング: text=True でUTF-8テキストとして取得
- タイムアウト: timeout=10 で10秒でタイムアウト
- 出力形式: tmuxのpane内容がそのままテキストとして返される(Box Drawing文字や絵文字も含む)
この方法で、Claude CodeのTUI(Terminal User Interface)の表示内容を文字列として取得し、パターンマッチングで待機状態を検出しています。
tmuxでClaudeCodeをたくさん動かすのが流行っていたときだ!
> なんか、pythonのライブラリでwrapする方法もためしてたような?
⏺ はい!pyteというPythonライブラリを使った方法も試していますね。
Pythonライブラリで画面表示をテキストで取る方法
pyte (Pure Python Terminal Emulator) を使った実装
- 基本的な仕組み (claude-wrapper-pyte.py:36-39)
import pyte
仮想ターミナル画面を作成
self.screen = pyte.Screen(80, 24) # 80列 x 24行
self.stream = pyte.Stream(self.screen)
- ターミナル出力を処理 (claude-wrapper-pyte.py:168-171)
Claude Codeの出力を受け取る
data = os.read(master_fd, 4096)
pyteで解析(エスケープシーケンスを解釈)
decoded = data.decode('utf-8', errors='replace')
self.stream.feed(decoded)
- 画面内容を取得 (claude-wrapper-pyte.py:78-87)
def get_screen_content(self, lines=None):
"""tmux capture-paneの代替"""
if lines is None:
# 全画面
return '\n'.join(self.screen.display)
else:
# 最後のN行
display = self.screen.display
return '\n'.join(display[-lines:])
- カーソル位置も取得可能 (claude-wrapper-pyte.py:89-100)
def get_visible_content_with_cursor(self):
"""カーソル位置も含めた画面情報"""
cursor_y, cursor_x = self.screen.cursor.y, self.screen.cursor.x
# カーソル位置をマーク
# ...
2つの方法の比較
| 方法 | tmux capture-pane | pyte ライブラリ |
|---|---|---|
| 対象 | 既存のtmuxセッション | Claude Codeをラップ |
| セットアップ | tmux必須、SSH可 | ラッパー経由で起動 |
| 精度 | tmuxの表示そのまま | エスケープシーケンスを完全解析 |
| カーソル位置 | ❌ 取得不可 | ✅ 取得可能 |
| 画面サイズ変更 | tmuxが処理 | ✅ pyteが処理 (claude-wrapper-pyte.py:60-76) |
| オーバーヘッド | 低い | やや高い(2-10倍) |
| 独立性 | 別プロセスから監視 | 同一プロセス内 |
パフォーマンス (benchmark_pyte.py より)
実使用では、pyteは1秒あたり1-5ms程度のオーバーヘッド。リアルタイム処理には十分高速です (benchmark_pyte.py:203-207)。
どちらを使うべきか?
- 既存のtmuxセッションを監視: tmux capture-pane (現在のメイン実装)
- tmuxなしで直接監視: pyte ラッパー (claude-wrapper-pyte.py)
pyteの利点は、tmux不要で、カーソル位置や正確な画面状態が取得できる点ですね。
> ほかにも面白いライブラリ使ってたり、面白いポイントがないか徹底的に探して
⏺ わお!すごい探索結果になりました!まとめます!
🎯 Claude Code暇状態検出プロジェクトの全貌
📚 使用ライブラリ一覧
- pyte - Pure Python Terminal Emulator ⭐
- VT102互換のターミナルエミュレータ
- エスケープシーケンスを完全に解析
- 画面バッファ、カーソル位置を追跡
- claude-wrapper-pyte.py, benchmark_pyte.py で使用
- rumps - macOSメニューバーアプリ 🍎
- macOSのメニューバーアプリを簡単に作成
- 通知センター連携
- 全バージョンで使用(claude_monitor*.py)
- asciinema形式 - ターミナル録画 📹
- JSONベースの軽量録画形式
- analyze_asciinema_approach.py, terminal_recording_tools.py で分析
- ハイブリッド版で録画機能として実装可能
- AppleScript / Accessibility API 🖥️
- test_accessibility.py で実験
- ウィンドウタイトル、UI要素の取得
- 権限が必要で実用性は限定的
- PTY (Pseudo-Terminal) 🔌
- explain_tty_pty.py で詳細解説
- 全ラッパー実装の基盤技術
- マスター/スレーブ構造で透過的な仲介
🔍 暇状態検出方法の全パターン
方法1: tmux capture-pane (メイン実装)
- ファイル: claude_monitor.py, claude_monitor_multi.py
- 仕組み: SSH経由でtmuxから画面をテキスト取得
- 検出パターン:
- │ > (プロンプト) + ╰─ (フレーム)
- │\xa0> (ノンブレーキングスペース対応!)
- yes/no: Do you want to, ❯ 1. Yes など12種類
方法2: pyteエミュレーション (ラッパー方式)
- ファイル: claude-wrapper-pyte.py
- 仕組み: Claude Codeをラップし、出力をpyteで解析
- 利点:
- カーソル位置取得可能 (claude-wrapper-pyte.py:89-100)
- 画面サイズ変更対応 (SIGWINCH)
- tmux不要
方法3: スクロールバック管理 (履歴ベース)
- ファイル: claude-wrapper-scrollback.py
- 仕組み: 最後10,000行をdequeで保持
- 特徴:
- tmux capture-paneの代替 (scrollback.py:78-81)
- エスケープシーケンス除去版と生データの両方保存
方法4: CPU使用率推測 (補助的)
- ファイル: test_cpu_monitor.py
- 仕組み: プロセスのCPU使用率を監視
- 判定: CPU < 5%で安定 → 待機中の可能性
- 精度: 限定的(test_cpu_monitor.py:195-199)
方法5: 画面ハッシュ変更検出 ⚡
- ファイル: screen_idle_monitor.py
- 仕組み: MD5ハッシュで画面変更を検出
- 特徴:
- 段階的トリガー: "30,60,60" → 30秒後、90秒後、150秒後...
- 無限繰り返し: "60,300*∞" (screen_idle_monitor.py:32-112)
- アクション実行: 変数展開で通知コマンド
方法6: 定期画面キャプチャ 📸
- ファイル: periodically_pyte_capture_demo.py
- 仕組み: N秒ごとに画面をファイル保存(変更時のみ)
- 用途: デバッグ、履歴記録
- 実例: test_captures/ に100個以上のキャプチャ保存済み!
方法7: 統合監視 🎛️
- ファイル: claude_monitor_unified.py
- 仕組み: tmux + wrapper + プロセスを同時監視
- 機能:
- 3ソース並行監視 (unified.py:56-64)
- リマインダー: 2分、5分、10分、15分、30分 (unified.py:27)
- 自動クリーンアップ
🎨 実装バリエーション
A. ハイブリッド版 ⚡💾
ファイル: claude-wrapper-hybrid.py
- リアルタイム監視(pyte)+ オプション録画(asciinema)
- メモリ内イベントバッファ(1000件)
- 非ブロッキング録画(hybrid.py:213-217)
B. アイドル監視版 ⏱️
ファイル: screen_idle_monitor.py
- 画面変化がN秒ないときアクション実行
- スケジュール機能が超高機能!
- コマンド変数: {idle_time}, {screen_content}, {trigger_count}
C. 複数サーバー版 🌐
ファイル: claude_monitor_multi.py
- 複数サーバーの同時監視
- サーバーごとの有効/無効切り替え
- 通知履歴記録(~/.claude_monitor_history.txt)
🧪 テスト・デモファイルの宝庫
- test_local.py - ローカルtmux検出テスト
- test_yesno.py - yes/no待ち検出テスト
- test_cpu_monitor.py - CPU使用率監視テスト
- test_accessibility.py - macOS Accessibility API実験
- test_non_tmux.py - tmux外での検出実験
- benchmark_pyte.py - pyteのパフォーマンス計測
- demo_tui_resize.py - TUIリサイズ動作デモ
- demo_safe_redraw.py - 安全な再描画デモ
- demo_resize_detection.py - リサイズ検出デモ
- explain_tty_pty.py - TTY/PTYの詳細解説
💎 超面白い技術ポイント
- ノンブレーキングスペース対応 🎯
Claude Codeは通常のスペースではなく \xa0 を使用!
if "│ >" in line or "│\xa0>" in line or ">\xa0" in line: # 3パターン対応
- SIGWINCH処理 📐
ウィンドウサイズ変更をリアルタイムで追跡:
- ターミナルサイズ取得
- pyteスクリーンをリサイズ
- PTYに新サイズを伝達
(claude-wrapper-pyte.py:60-76)
- dequeによる効率的スクロールバック 🔄
self.event_buffer = deque(maxlen=1000) # 自動で古いデータを削除
- 段階的リマインダー ⏰
self.reminder_intervals = [2, 5, 10, 15, 30] # 分
2分後、5分後、10分後...と段階的に通知
- PTYの透過性 🪟
1. 即座に転送(最優先)
sys.stdout.buffer.write(data)
sys.stdout.flush()
2. 同時に解析
self.stream.feed(decoded)
- 画面状態のJSONエクスポート 📤
{
"state": "waiting",
"screen_content": "最後の20行",
"cursor_position": {"y": 10, "x": 5},
"event_count": 523
}
- asciinema形式の再利用 🎬
[elapsed_time, "o", data] # タイムスタンプ + 出力データ
- 実際のキャプチャデータ保存 📂
test_captures/aaa/ に実際のClaude Code画面が100個以上保存されている!
- 176x55の画面サイズ
- プロンプト、ウェルカムメッセージ含む
- タイムスタンプ付き(20250717_111619.txt など)
📖 ドキュメント構造
- investigation.md - 技術調査結果(待機パターン分析)
- REQ.md - 要件定義
- todos.md - タスク管理(ほぼ完了!)
- README.md - 使用方法
- CLAUDE.md - プロジェクトガイド
- simulate_yesno.md - yes/no待ちシミュレーション
🏗️ アーキテクチャの進化
基本版 (tmux)
↓
複数サーバー版
↓
ラッパー版 (pyte)
↓
スクロールバック版
↓
ハイブリッド版 (監視+録画)
↓
統合版 (全ソース統合)
🎪 最も面白い発見
- 段階的アイドル検出 - screen_idle_monitor.py のスケジュール機能が異常に柔軟!
30秒後、その後300秒ごとに10回通知
--idle "30,300*10"
2. 3ソース統合監視 - tmux、wrapper、生プロセスを同時監視
3. 100個以上の実キャプチャ - test_captures/ ディレクトリに実データ
4. パフォーマンス検証済み - pyteは2-10倍遅いが、実用上は1-5ms/秒で問題なし
5. 完全な録画機能 - asciinema形式で入出力を完全記録可能
6. TTY/PTYの詳細解説 - explain_tty_pty.py が教科書レベル
すごいプロジェクトです!多様なアプローチを試行錯誤し、それぞれの長所短所を検証した痕跡が見られます。特に「段階的リマインダー」「統合監視」「ハイブリッド実装」
のアイデアが秀逸ですね!
供養...