Class: OllamaAgent::TUI

Inherits:
Object
  • Object
show all
Defined in:
lib/ollama_agent/tui.rb

Overview

Linear scrolling TUI for interactive agent sessions (TTY toolkit). Avoids fixed multi-pane layouts; pairs with CLI::TuiRepl. rubocop:disable Metrics/ClassLength – façade over multiple tty-* components

Constant Summary collapse

HISTORY_FILE =
File.join(Dir.home, ".config", "ollama_agent", "repl_history")
MAX_HISTORY =
500

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(stdout: $stdout, stderr: $stderr, logger: nil, god_mode: false) ⇒ TUI

Returns a new instance of TUI.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/ollama_agent/tui.rb', line 25

def initialize(stdout: $stdout, stderr: $stderr, logger: nil, god_mode: false)
  @stdout = stdout
  @stderr = stderr
  @god_mode = god_mode
  @prompt = TTY::Prompt.new(output: stdout, input: $stdin)
  @pastel = Pastel.new
  @logger = logger || TTY::Logger.new(output: stdout)
  @slash_reader = TuiSlashReader.new(
    completion_candidates: [],
    input: $stdin,
    output: @stdout,
    interrupt: :error
  )
  load_history
end

Instance Attribute Details

#promptObject (readonly)

Returns the value of attribute prompt.



23
24
25
# File 'lib/ollama_agent/tui.rb', line 23

def prompt
  @prompt
end

Instance Method Details

#ask_interactive(question, options, god_mode: nil) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/ollama_agent/tui.rb', line 75

def ask_interactive(question, options, god_mode: nil)
  return auto_pick_first(options) if god_mode.nil? ? @god_mode : god_mode

  @stdout.puts ""
  @prompt.select(
    @pastel.yellow.bold("Action required: ") + question.to_s,
    options,
    cycle: true,
    filter: true,
    per_page: 10
  )
end

#ask_user_inputObject



88
89
90
91
92
# File 'lib/ollama_agent/tui.rb', line 88

def ask_user_input
  @prompt.ask(@pastel.green.bold("")) { |q| q.required true }
rescue TTY::Reader::InputInterrupt
  nil
end

#ask_user_line(completion_candidates: [], command_palette: nil, prompt_prefix: nil) ⇒ String?

Line editor with Tab completion for lines starting with / (uses OllamaAgent::TuiSlashReader).

Parameters:

  • completion_candidates (Array<String>) (defaults to: [])

    e.g. /help, /model; empty falls back to TTY::Prompt#ask.

Returns:

  • (String, nil)


98
99
100
101
102
103
104
105
106
107
108
# File 'lib/ollama_agent/tui.rb', line 98

def ask_user_line(completion_candidates: [], command_palette: nil, prompt_prefix: nil)
  prefix = prompt_prefix.to_s
  prompt = "#{prefix}#{@pastel.green.bold("")}"
  @slash_reader.completion_candidates = Array(completion_candidates).uniq.sort
  @slash_reader.command_palette = command_palette
  line = @slash_reader.read_line(prompt).to_s
  save_history
  line
rescue TTY::Reader::InputInterrupt
  nil
end

#goodbyeObject



124
125
126
# File 'lib/ollama_agent/tui.rb', line 124

def goodbye
  @stdout.puts "\n#{@pastel.dim("Goodbye.")}"
end

#log(level, message) ⇒ Object



110
111
112
113
114
115
116
117
118
# File 'lib/ollama_agent/tui.rb', line 110

def log(level, message)
  case level
  when :info  then @logger.info(message)
  when :warn  then @logger.warn(message)
  when :error then @logger.error(message)
  else
    @logger.debug(message)
  end
end


120
121
122
# File 'lib/ollama_agent/tui.rb', line 120

def print_error(message)
  @stderr.puts @pastel.red(message.to_s)
end

#render_assistant_message(message) ⇒ Object



67
68
69
70
71
72
73
# File 'lib/ollama_agent/tui.rb', line 67

def render_assistant_message(message)
  thinking = message.respond_to?(:thinking) ? message.thinking : nil
  content  = message.respond_to?(:content) ? message.content : message.to_s
  print_thinking_block(thinking) if thinking_present?(thinking)
  print_content_block(content) if content_present?(content)
  @stdout.puts @pastel.dim("-" * [TTY::Screen.width, 40].min)
end

#render_dashboard(**options) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/ollama_agent/tui.rb', line 41

def render_dashboard(**options)
  config = options.fetch(:config)
  skills = options.fetch(:skills)
  scripts = options.fetch(:scripts) { [] }
  status = options.fetch(:status, "IDLE")
  budget = options[:budget]
  memory_line = options[:memory_line]
  table = build_context_table(config, skills, scripts, status, budget, memory_line)
  rendered_table = table.render(:unicode, padding: [0, 1])
  frame = box_frame(" Ollama Agent ", rendered_table)
  @stdout.puts "\n#{frame}\n"
end

#render_providers_dashboard(pool_status:, routing_decisions: [], aggregate_usage: nil) ⇒ Object

Render the three-panel providers dashboard: status, usage, routing decisions.

Parameters:

  • pool_status (Array<Hash>)

    from CredentialRouter#pool_status

  • routing_decisions (Array<String>) (defaults to: [])

    from CredentialRouter#routing_decisions

  • aggregate_usage (Hash, nil) (defaults to: nil)

    from CredentialRouter#aggregate_usage



59
60
61
62
63
64
65
# File 'lib/ollama_agent/tui.rb', line 59

def render_providers_dashboard(pool_status:, routing_decisions: [], aggregate_usage: nil)
  @stdout.puts "\n"
  @stdout.puts render_credential_status_box(pool_status)
  @stdout.puts render_usage_box(aggregate_usage || {})          if aggregate_usage
  @stdout.puts render_routing_decisions_box(routing_decisions)  unless routing_decisions.empty?
  @stdout.puts ""
end