Class: BruteCLI::Execution

Inherits:
Object
  • Object
show all
Defined in:
lib/brute_cli/execution.rb

Overview

Execution encapsulates running a single prompt (or repeated prompts) against an agent, streaming output to the terminal. It owns the agent lifecycle and stats rendering; streaming/rendering is handled by CLIEventHandler.

Use directly for non-interactive (pipe / single-prompt) mode:

BruteCLI::Execution.new(options).run(prompt)

Or let BruteCLI::REPL wrap it for interactive use.

Defined Under Namespace

Classes: ShellCallTerminal

Constant Summary collapse

AGENTS =
%w[build plan bash ruby python nix].freeze
SHELL_AGENTS =

Shell-mode agents: agent name -> shell interpreter (model name). These agents use the Shell provider instead of the current LLM provider.

{
  "bash"   => "bash",
  "ruby"   => "ruby",
  "python" => "python",
  "nix"    => "nix",
}.freeze
SESSIONS_DIR =

Default sessions directory: ~/.brute/sessions/

File.join(Dir.home, ".brute", "sessions")

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Execution

Returns a new instance of Execution.



45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/brute_cli/execution.rb', line 45

def initialize(options = {})
  @options = options
  @terminal = options[:terminal] || Terminal.new
  @current_agent = AGENTS.first
  @agent = nil
  @session = nil
  @selected_model = nil    # user-chosen model override (nil = provider default)
  @streamer = StreamFormatter.new(output: @terminal.buffer.io, width: @terminal.width)
  spinner_class = options[:spinner] || BruteCLI.config.spinner
  @spinner = spinner_class.new(output: @terminal.buffer.io)
  @event_handler = nil
end

Instance Attribute Details

#agentObject

Returns the value of attribute agent.



37
38
39
# File 'lib/brute_cli/execution.rb', line 37

def agent
  @agent
end

#current_agentObject

Returns the value of attribute current_agent.



36
37
38
# File 'lib/brute_cli/execution.rb', line 36

def current_agent
  @current_agent
end

#model_nameObject (readonly)

Returns the value of attribute model_name.



36
37
38
# File 'lib/brute_cli/execution.rb', line 36

def model_name
  @model_name
end

#provider_nameObject (readonly)

Returns the value of attribute provider_name.



36
37
38
# File 'lib/brute_cli/execution.rb', line 36

def provider_name
  @provider_name
end

#selected_model=(value) ⇒ Object (writeonly)

Sets the attribute selected_model

Parameters:

  • value

    the value to set the attribute selected_model to.



38
39
40
# File 'lib/brute_cli/execution.rb', line 38

def selected_model=(value)
  @selected_model = value
end

#terminalObject (readonly)

Returns the value of attribute terminal.



36
37
38
# File 'lib/brute_cli/execution.rb', line 36

def terminal
  @terminal
end

Class Method Details

.list_sessionsObject

List saved sessions by scanning the sessions directory.



142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/brute_cli/execution.rb', line 142

def self.list_sessions
  dir = SESSIONS_DIR
  return [] unless Dir.exist?(dir)

  Dir.glob(File.join(dir, "*", "session.jsonl")).map do |path|
    {
      id: File.basename(File.dirname(path)),
      path: path,
      mtime: File.mtime(path),
    }
  end.sort_by { |s| s[:mtime] }.reverse
end

Instance Method Details

#ensure_agent!Object



126
127
128
129
130
131
132
# File 'lib/brute_cli/execution.rb', line 126

def ensure_agent!
  return if @agent

  ensure_session!
  resolve_provider_info
  @agent = build_agent
end

#ensure_session!Object

── Agent ──



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/brute_cli/execution.rb', line 110

def ensure_session!
  return if @session

  if @options[:session_id]
    path = build_session_path(@options[:session_id])
    if File.exist?(path)
      @session = Brute::Session.from_jsonl(path)
    else
      @session = Brute::Session.new(path: path)
    end
  else
    id = SecureRandom.hex(8)
    @session = Brute::Session.new(path: build_session_path(id))
  end
end

#execute(prompt) ⇒ Object



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
# File 'lib/brute_cli/execution.rb', line 64

def execute(prompt)
  @streamer.reset
  @event_handler = build_event_handler
  @event_handler.start_spinner

  begin
    @session.user(prompt)
    env = @agent.call(@session, events: @event_handler)
  rescue Interrupt
    @event_handler.stop_spinner
    @event_handler.flush_content
    print_stats_bar
    return
  rescue => e
    @event_handler.stop_spinner
    @event_handler.flush_content
    print_error(e)
    print_stats_bar
    return
  end

  @event_handler.stop_spinner
  @event_handler.flush_content
  print_stats_bar
end

#model_shortObject



104
105
106
# File 'lib/brute_cli/execution.rb', line 104

def model_short
  @model_name&.sub(/^claude-/, "")&.sub(/-\d{8}$/, "") || @model_name
end

#resolve_provider_infoObject

── Provider ──



93
94
95
96
97
98
99
100
101
102
# File 'lib/brute_cli/execution.rb', line 93

def resolve_provider_info
  if (shell_model = SHELL_AGENTS[@current_agent])
    @provider_name = "shell"
    @model_name = shell_model
  else
    provider = Brute.provider rescue nil
    @provider_name = provider.to_s
    @model_name = @selected_model || default_model_for(provider)
  end
end

#run(prompt) ⇒ Object

Run a single prompt against the agent. This is the primary public API.



59
60
61
62
# File 'lib/brute_cli/execution.rb', line 59

def run(prompt)
  ensure_agent!
  execute(prompt)
end

#session_idObject

Session ID derived from the session path.



135
136
137
138
139
# File 'lib/brute_cli/execution.rb', line 135

def session_id
  return @options[:session_id] if @options[:session_id]
  return nil unless @session&.path
  File.basename(File.dirname(@session.path))
end

#session_pathObject

Expose session path for Banner display (read-only).



41
42
43
# File 'lib/brute_cli/execution.rb', line 41

def session_path
  @session&.path
end