Class: Rubino::Tools::ShellEntryAdapter
- Inherits:
-
Object
- Object
- Rubino::Tools::ShellEntryAdapter
- Defined in:
- lib/rubino/tools/shell_entry_adapter.rb
Overview
Read-time adapter that makes a ShellRegistry::Entry look like a BackgroundTasks::Entry to the SHARED UI seams (cards, picker, /stop, attach) WITHOUT copying its state into a second registry. This is the DRY core of "a background shell gets the same dev UX as a subagent": BackgroundTasks#running merges these adapters in, so the cards/picker render them with ZERO branches, and #stop / #feed_input route control to the live ShellRegistry process.
Why an adapter and not a real BackgroundTasks entry: a duplicated entry would need status sync (two sources of truth), would double the completion notice ShellRegistry already pushes, and would consume the subagent concurrency cap
- allocate a dead steer_queue. Reading the live ShellRegistry entry at render time dissolves all three.
The renderers read only plain accessors (id/subagent/status/prompt/...); method_missing returns nil for any field a shell has no analogue for (approval_*, budget_request, runner, steer_queue, …) so a renderer touching one never raises.
Instance Attribute Summary collapse
-
#shell ⇒ Object
readonly
Returns the value of attribute shell.
Instance Method Summary collapse
- #activity_log ⇒ Object
-
#budget_request ⇒ Object
Mirrors BackgroundTasks::Entry#budget_request — a FIELD the renderers read, not a predicate, so it keeps the field name (no
?). -
#depth ⇒ Object
rubocop:disable Naming/PredicateMethod.
-
#feed_input(text, enter: true) ⇒ Object
Attach/focus input: the user's keystrokes/line go straight to the PTY (or pipe) stdin — the shell analogue of steering a subagent.
- #id ⇒ Object
-
#initialize(shell_entry, registry: ShellRegistry.instance) ⇒ ShellEntryAdapter
constructor
A new instance of ShellEntryAdapter.
-
#live? ⇒ Boolean
True while the process is still alive — the single liveness rule every UI surface filters by (mirrors BackgroundTasks.live_status?).
-
#messages ⇒ Object
The attach view replays a subagent's transcript via #messages; a shell has none — it falls through to the live output-tail render instead.
- #method_missing(_name, *_args) ⇒ Object
- #output_all ⇒ Object
-
#output_new ⇒ Object
The live output a shell's attach view tails (no session/transcript).
-
#peek(_question = nil) ⇒ Object
Polymorphic counterpart to a subagent's #peek: a shell has no model context to side-infer over, so a "probe" is an instant snapshot of its recent output — NO LLM round-trip (the question is informational).
-
#peek_hint ⇒ Object
A shell's peek IS its output — no "empty context" caveat applies (the subagent's #peek_hint does).
-
#prompt ⇒ Object
the card's title line.
-
#respond_to_missing?(_name, _include_private = false) ⇒ Boolean
Any field a shell has no analogue for (approval_gate, runner, steer_queue, approval_question, last_activity, finished_at, …) reads as nil so the shared renderers never raise on a shell row.
- #shell? ⇒ Boolean
- #started_at ⇒ Object
-
#status ⇒ Object
:running / :completed / :failed (derived).
-
#steer(text) ⇒ Object
Polymorphic counterpart to a subagent's #steer: a shell has no turn to fold a note into — "steering" it means writing the text to its stdin.
-
#stop ⇒ Object
Stop from the UI (/stop / picker): SIGTERM→grace→SIGKILL the process group, then retire so the captured output stays retrievable.
- #subagent ⇒ Object
-
#tool_count ⇒ Object
a shell runs no tools — nil omits the card's "N tools" segment entirely.
Constructor Details
#initialize(shell_entry, registry: ShellRegistry.instance) ⇒ ShellEntryAdapter
Returns a new instance of ShellEntryAdapter.
23 24 25 26 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 23 def initialize(shell_entry, registry: ShellRegistry.instance) @shell = shell_entry @registry = registry end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(_name, *_args) ⇒ Object
94 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 94 def method_missing(_name, *_args) = nil |
Instance Attribute Details
#shell ⇒ Object (readonly)
Returns the value of attribute shell.
28 29 30 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 28 def shell @shell end |
Instance Method Details
#activity_log ⇒ Object
39 40 41 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 39 def activity_log = [] # Mirrors BackgroundTasks::Entry#budget_request — a FIELD the renderers read, # not a predicate, so it keeps the field name (no `?`). |
#budget_request ⇒ Object
Mirrors BackgroundTasks::Entry#budget_request — a FIELD the renderers read,
not a predicate, so it keeps the field name (no ?).
42 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 42 def budget_request = false # rubocop:disable Naming/PredicateMethod |
#depth ⇒ Object
rubocop:disable Naming/PredicateMethod
43 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 43 def depth = 0 |
#feed_input(text, enter: true) ⇒ Object
Attach/focus input: the user's keystrokes/line go straight to the PTY (or pipe) stdin — the shell analogue of steering a subagent.
63 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 63 def feed_input(text, enter: true) = @registry.write_input(@shell, text, enter: enter) |
#id ⇒ Object
30 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 30 def id = @shell.id |
#live? ⇒ Boolean
True while the process is still alive — the single liveness rule every UI
surface filters by (mirrors BackgroundTasks.live_status?). Routes through
the registry's #running? oracle so a server that backgrounded its leader
(npm run dev &) stays visible instead of vanishing the instant bash exits.
54 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 54 def live? = @registry.running?(@shell) |
#messages ⇒ Object
The attach view replays a subagent's transcript via #messages; a shell has none — it falls through to the live output-tail render instead.
48 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 48 def = [] |
#output_all ⇒ Object
88 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 88 def output_all = @registry.read_all(@shell) |
#output_new ⇒ Object
The live output a shell's attach view tails (no session/transcript).
87 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 87 def output_new = @registry.read_new(@shell) |
#peek(_question = nil) ⇒ Object
Polymorphic counterpart to a subagent's #peek: a shell has no model context to side-infer over, so a "probe" is an instant snapshot of its recent output — NO LLM round-trip (the question is informational).
75 76 77 78 79 80 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 75 def peek(_question = nil) out = output_all.to_s return "(no output captured yet)" if out.strip.empty? out.lines.last(20).join.rstrip end |
#peek_hint ⇒ Object
A shell's peek IS its output — no "empty context" caveat applies (the subagent's #peek_hint does).
84 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 84 def peek_hint = nil |
#prompt ⇒ Object
the card's title line
33 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 33 def prompt = @shell.command |
#respond_to_missing?(_name, _include_private = false) ⇒ Boolean
Any field a shell has no analogue for (approval_gate, runner, steer_queue, approval_question, last_activity, finished_at, …) reads as nil so the shared renderers never raise on a shell row.
93 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 93 def respond_to_missing?(_name, _include_private = false) = true |
#shell? ⇒ Boolean
44 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 44 def shell? = true |
#started_at ⇒ Object
34 35 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 34 def started_at = @shell.started_at # :running / :completed / :failed (derived) |
#status ⇒ Object
:running / :completed / :failed (derived)
36 37 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 36 def status = @registry.status(@shell) # a shell runs no tools — nil omits the card's "N tools" segment entirely |
#steer(text) ⇒ Object
Polymorphic counterpart to a subagent's #steer: a shell has no turn to fold a note into — "steering" it means writing the text to its stdin.
67 68 69 70 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 67 def steer(text) # rubocop:disable Naming/PredicateMethod -- an action mirroring Entry#steer, not a predicate feed_input(text) true end |
#stop ⇒ Object
Stop from the UI (/stop / picker): SIGTERM→grace→SIGKILL the process group, then retire so the captured output stays retrievable. Reuses the one ShellRegistry kill seam (also used by shell_kill).
59 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 59 def stop = @registry.terminate(@shell) |
#subagent ⇒ Object
31 32 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 31 def subagent = "shell" # the card's title line |
#tool_count ⇒ Object
a shell runs no tools — nil omits the card's "N tools" segment entirely
38 |
# File 'lib/rubino/tools/shell_entry_adapter.rb', line 38 def tool_count = nil |