Class: Rubino::Commands::Executor
- Inherits:
-
Object
- Object
- Rubino::Commands::Executor
- Defined in:
- lib/rubino/commands/executor.rb
Overview
Executes a slash command, rendering its template and feeding it to the agent.
‘runner:` (optional) is the live Agent::Runner for the interactive REPL. When present, `/status` and `/sessions` can read the current session / model straight off it. It is nil for non-interactive callers (and unit tests that don’t exercise those commands), in which case those commands degrade gracefully instead of raising.
Constant Summary collapse
- MODEL_LIST_LIMIT =
How many model ids the bare ‘/model` listing renders before deferring the rest to the completion dropdown.
12- IMMEDIATE_WHILE_BUSY =
Local meta-commands SAFE to run IMMEDIATELY while a turn is active (the type-ahead QUEUE-by-default is overridden for these). Read-only/control only: they INSPECT or SIGNAL the running tree without mutating session / conversation / config / turn state, so they run on the composer’s reader thread concurrently with the turn thread without a race (output routes through the SAME render-mutex-serialized UI). /stop reuses the cancel machinery Esc / ‘–stop` use (already concurrent-safe). /reply is kept BLOCKED: its interactive form `/reply <id>` (-> @ui.ask) can’t be told apart by NAME from the safe inline form, and would steal the reader’s stdin (default-to-blocked on a concurrency hazard). Single source of truth for the busy-time classification — #busy_disposition reads it.
%w[agents tasks stop status jobs help commands dirs].freeze
Class Method Summary collapse
-
.welcome(runner: nil, ui: nil) ⇒ Object
Renders the welcome variant on first interactive boot.
Instance Method Summary collapse
-
#busy_disposition(input) ⇒ Object
Classifies an input line for the BUSY (turn-active) input gate: :immediate - a registered local meta-command in IMMEDIATE_WHILE_BUSY; the composer dispatches it NOW (does not queue) via #try_execute.
-
#initialize(loader: nil, ui: nil, runner: nil) ⇒ Executor
constructor
A new instance of Executor.
-
#try_execute(input) ⇒ Object
Attempts to execute input as a slash command.
Constructor Details
Class Method Details
.welcome(runner: nil, ui: nil) ⇒ Object
Renders the welcome variant on first interactive boot. Best-effort: a welcome banner must never block the REPL from starting, so any assembler hiccup degrades to no banner rather than a crash. The boot header (workspace/branch/model) is printed by the chat command; this adds only the orientation, with no duplicate identity/session-id renderings.
99 100 101 102 103 |
# File 'lib/rubino/commands/executor.rb', line 99 def self.welcome(runner: nil, ui: nil) new(ui: ui, runner: runner).send(:show_welcome) rescue StandardError nil end |
Instance Method Details
#busy_disposition(input) ⇒ Object
Classifies an input line for the BUSY (turn-active) input gate:
:immediate - a registered local meta-command in IMMEDIATE_WHILE_BUSY;
the composer dispatches it NOW (does not queue) via
#try_execute.
:blocked - a registered local built-in NOT in the immediate set
(state-mutating / turn-affecting); the composer neither
queues nor runs it, and shows the not-available notice.
:pass - not a recognized local built-in (free text, a `?` probe,
a `!` shell escape, an @file line, an agent name, a custom
.md command, an unknown slash): fall through to the normal
QUEUE-by-default behavior, handled by the post-turn path
exactly as today.
42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/rubino/commands/executor.rb', line 42 def busy_disposition(input) return :pass unless @loader.slash_command?(input) name, = @loader.parse(input) return :pass unless name return :immediate if IMMEDIATE_WHILE_BUSY.include?(name) # Only a KNOWN local built-in is blocked; anything else passes through to # queue so the existing post-turn dispatch handles it unchanged. return :blocked if BuiltIns::NAMES.include?("/#{name}") :pass end |
#try_execute(input) ⇒ Object
Attempts to execute input as a slash command. Returns the rendered prompt if it’s a command, nil otherwise.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/rubino/commands/executor.rb', line 64 def try_execute(input) return nil unless @loader.slash_command?(input) name, arguments = @loader.parse(input) return nil unless name # Check built-in commands first built_in_result = handle_built_in(name, arguments) return built_in_result if built_in_result # Agent switching (#320): a bare `/<primary>` pins it, a `/<agent> # <message>` routes one turn to it. Resolved against the live registry so # built-in (build/plan/explore/general) AND user-registered agents are # reachable — checked BEFORE custom .md commands so an agent name wins. agent_result = agent_switch_handler.handle_command(name, arguments) return agent_result if agent_result # Look up custom command command = @loader.find(name) unless command @ui.error("unknown command: /#{name}") # "Did you mean /X?" on the closest known command before the full # roster (FRICTION-4) — the affordance Thor/git/bundler ship for typos. # Returns :handled so try_execute reports it handled (even if failed). return help_handler.suggest_and_list(name) end run_custom_command(command, name, arguments) end |