Class: AgentsControl::ScreenWatcher
- Inherits:
-
Object
- Object
- AgentsControl::ScreenWatcher
- Defined in:
- lib/agents_control/screen_watcher.rb
Overview
Notices Claude Code's local menus on a session's screen.
Dialogs the CLI shows itself in response to a local human command — switching models via /model, folder trust on first launch — never produce a hook event at all. The only way to see this state is to read the screen itself.
Goes through the full Registry — sees bare iTerm2 tabs too, not just tmux panes, same as RateLimitWatcher.
The heuristic is narrow: consecutive lines "❯ 1. …" and " 2. …" — this is how Claude Code draws any choice (folder trust, model switch, a tool permission if it ever made it to the screen), and almost nothing else draws like this.
Constant Summary collapse
- CURSOR_OPTION =
"1." next to the cursor marks the first option — the start of a menu.
/^\s*❯\s*1\.\s+(.+?)\s*$/- OPTION =
Later options come without the cursor, just numbered.
/^\s*(\d+)\.\s+(.+?)\s*$/- LINES =
A 30-line screen comfortably covers any CLI dialog worth catching — Claude Code doesn't draw menus that long.
30
Instance Method Summary collapse
-
#initialize(registry:, config:, store:, api:, logger: nil) ⇒ ScreenWatcher
constructor
A new instance of ScreenWatcher.
- #start ⇒ Object
- #stop ⇒ Object
-
#tick ⇒ Object
One pass over all agent sessions.
Constructor Details
#initialize(registry:, config:, store:, api:, logger: nil) ⇒ ScreenWatcher
Returns a new instance of ScreenWatcher.
31 32 33 34 35 36 37 38 |
# File 'lib/agents_control/screen_watcher.rb', line 31 def initialize(registry:, config:, store:, api:, logger: nil) @registry = registry @config = config @store = store @api = api @logger = logger @running = false end |
Instance Method Details
#start ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/agents_control/screen_watcher.rb', line 40 def start return self unless enabled? @running = true @thread = Thread.new do tick while @running ensure nil end self end |
#stop ⇒ Object
53 54 55 56 |
# File 'lib/agents_control/screen_watcher.rb', line 53 def stop @running = false @thread&.kill end |
#tick ⇒ Object
One pass over all agent sessions. Public method — tests call it directly, without spinning up a real background thread.
60 61 62 63 64 65 66 |
# File 'lib/agents_control/screen_watcher.rb', line 60 def tick candidates.each { |session| check(session) } rescue StandardError => e log("failure: #{e.class}: #{e.}") ensure sleep(interval) if @running end |