Class: Clacky::PlainUIController
- Inherits:
-
Object
- Object
- Clacky::PlainUIController
show all
- Includes:
- UIInterface
- Defined in:
- lib/clacky/plain_ui_controller.rb
Overview
PlainUIController implements UIInterface for non-interactive (--message) mode.
Writes human-readable plain text directly to stdout so the caller can capture
or pipe the output. No spinners, no TUI — just clean lines.
Instance Method Summary
collapse
-
#append_output(content) ⇒ Object
-
#clear_input ⇒ Object
-
#emit(type, **data) ⇒ Object
Surface extension events the plain CLI cares about (e.g. advisor recommendations).
-
#initialize(output: $stdout) ⇒ PlainUIController
constructor
A new instance of PlainUIController.
-
#log(message, level: :info) ⇒ Object
-
#puts_line(text) ⇒ Object
-
#request_confirmation(message, default: true) ⇒ Object
Blocking interaction (auto-approve in non-interactive mode) ===.
-
#set_idle_status ⇒ Object
-
#set_input_tips(message, type: :info) ⇒ Object
-
#set_working_status ⇒ Object
-
#show_assistant_message(content, files:, interim: false, created_at: nil) ⇒ Object
Output display ===.
-
#show_complete(iterations:, cost:, duration: nil, cache_stats: nil, awaiting_user_feedback: false, cost_source: nil, task_id: nil) ⇒ Object
-
#show_error(message, code: nil, top_up_url: nil, raw_message: nil) ⇒ Object
-
#show_file_edit_preview(path) ⇒ Object
-
#show_file_error(error_message) ⇒ Object
-
#show_file_write_preview(path, is_new_file:) ⇒ Object
-
#show_info(message, prefix_newline: true) ⇒ Object
Status messages ===.
-
#show_progress(message = nil, prefix_newline: true, progress_type: "thinking", phase: "active", metadata: {}) ⇒ Object
Progress (no-ops — no spinner in plain mode) ===.
-
#show_shell_preview(command) ⇒ Object
-
#show_success(message) ⇒ Object
-
#show_tool_call(name, args) ⇒ Object
-
#show_tool_error(error) ⇒ Object
-
#show_tool_result(result) ⇒ Object
-
#show_warning(message) ⇒ Object
-
#stop ⇒ Object
-
#update_sessionbar(tasks: nil, cost: nil, cost_source: nil, status: nil, latency: nil) ⇒ Object
State updates (no-ops) ===.
-
#update_todos(todos) ⇒ Object
#phase_end, #phase_start, #request_feedback_with_countdown, #show_diff, #show_feedback_request, #show_goal_status, #show_subagent_end, #show_subagent_start, #show_token_usage, #show_tool_args, #show_tool_stdout, #start_progress, #stream_thinking_progress, #update_permission_mode, #with_phase, #with_progress
Constructor Details
Returns a new instance of PlainUIController.
12
13
14
15
|
# File 'lib/clacky/plain_ui_controller.rb', line 12
def initialize(output: $stdout)
@output = output
@mutex = Mutex.new
end
|
Instance Method Details
#append_output(content) ⇒ Object
96
97
98
|
# File 'lib/clacky/plain_ui_controller.rb', line 96
def append_output(content)
puts_line(content)
end
|
167
|
# File 'lib/clacky/plain_ui_controller.rb', line 167
def clear_input; end
|
#emit(type, **data) ⇒ Object
Surface extension events the plain CLI cares about (e.g. advisor
recommendations). Everything else is dropped, matching UIInterface's
no-op default.
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
# File 'lib/clacky/plain_ui_controller.rb', line 121
def emit(type, **data)
return unless type == "ext.advisor.recommendations"
options = data[:options] || data["options"]
return unless options.is_a?(Array)
lines = options.map do |opt|
action = (opt[:action] || opt["action"]).to_s.strip
next if action.empty?
reason = (opt[:reason] || opt["reason"]).to_s.strip
reason.empty? ? "- #{action}" : "- #{action} (#{reason})"
end.compact
return if lines.empty?
puts_line("")
puts_line("💡")
lines.each { |line| puts_line(line) }
end
|
#log(message, level: :info) ⇒ Object
141
142
143
144
|
# File 'lib/clacky/plain_ui_controller.rb', line 141
def log(message, level: :info)
puts_line("[#{level}] #{message}") if %i[error warn].include?(level.to_sym)
end
|
#puts_line(text) ⇒ Object
172
173
174
175
176
177
|
# File 'lib/clacky/plain_ui_controller.rb', line 172
def puts_line(text)
@mutex.synchronize do
@output.puts(text)
@output.flush
end
end
|
#request_confirmation(message, default: true) ⇒ Object
Blocking interaction (auto-approve in non-interactive mode) ===
159
160
161
162
163
|
# File 'lib/clacky/plain_ui_controller.rb', line 159
def request_confirmation(message, default: true)
true
end
|
#set_idle_status ⇒ Object
155
|
# File 'lib/clacky/plain_ui_controller.rb', line 155
def set_idle_status; end
|
168
|
# File 'lib/clacky/plain_ui_controller.rb', line 168
def set_input_tips(message, type: :info); end
|
#set_working_status ⇒ Object
154
|
# File 'lib/clacky/plain_ui_controller.rb', line 154
def set_working_status; end
|
#show_assistant_message(content, files:, interim: false, created_at: nil) ⇒ Object
19
20
21
22
|
# File 'lib/clacky/plain_ui_controller.rb', line 19
def show_assistant_message(content, files:, interim: false, created_at: nil)
puts_line(content) unless content.nil? || content.strip.empty?
files.each { |f| puts_line("📄 File: #{f[:path]}") }
end
|
#show_complete(iterations:, cost:, duration: nil, cache_stats: nil, awaiting_user_feedback: false, cost_source: nil, task_id: nil) ⇒ Object
90
91
92
93
94
|
# File 'lib/clacky/plain_ui_controller.rb', line 90
def show_complete(iterations:, cost:, duration: nil, cache_stats: nil, awaiting_user_feedback: false, cost_source: nil, task_id: nil)
parts = ["[done] iterations=#{iterations}", "cost=$#{cost.round(4)}"]
parts << "duration=#{duration.round(1)}s" if duration
puts_line(parts.join(" "))
end
|
#show_error(message, code: nil, top_up_url: nil, raw_message: nil) ⇒ Object
110
111
112
|
# File 'lib/clacky/plain_ui_controller.rb', line 110
def show_error(message, code: nil, top_up_url: nil, raw_message: nil)
puts_line("[error] #{message}")
end
|
#show_file_edit_preview(path) ⇒ Object
78
79
80
|
# File 'lib/clacky/plain_ui_controller.rb', line 78
def show_file_edit_preview(path)
puts_line("[file] edit: #{path}")
end
|
#show_file_error(error_message) ⇒ Object
82
83
84
|
# File 'lib/clacky/plain_ui_controller.rb', line 82
def show_file_error(error_message)
puts_line("[file error] #{error_message}")
end
|
#show_file_write_preview(path, is_new_file:) ⇒ Object
73
74
75
76
|
# File 'lib/clacky/plain_ui_controller.rb', line 73
def show_file_write_preview(path, is_new_file:)
action = is_new_file ? "create" : "overwrite"
puts_line("[file] #{action}: #{path}")
end
|
#show_info(message, prefix_newline: true) ⇒ Object
102
103
104
|
# File 'lib/clacky/plain_ui_controller.rb', line 102
def show_info(message, prefix_newline: true)
puts_line("[info] #{message}")
end
|
#show_progress(message = nil, prefix_newline: true, progress_type: "thinking", phase: "active", metadata: {}) ⇒ Object
Progress (no-ops — no spinner in plain mode) ===
148
|
# File 'lib/clacky/plain_ui_controller.rb', line 148
def show_progress(message = nil, prefix_newline: true, progress_type: "thinking", phase: "active", metadata: {}); end
|
#show_shell_preview(command) ⇒ Object
86
87
88
|
# File 'lib/clacky/plain_ui_controller.rb', line 86
def show_shell_preview(command)
puts_line("[shell] #{command}")
end
|
#show_success(message) ⇒ Object
114
115
116
|
# File 'lib/clacky/plain_ui_controller.rb', line 114
def show_success(message)
puts_line("[ok] #{message}")
end
|
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
# File 'lib/clacky/plain_ui_controller.rb', line 24
def show_tool_call(name, args)
args_data = args.is_a?(String) ? (JSON.parse(args) rescue args) : args
if Clacky::Tools::AskUser.feedback_tool?(name)
questions = Clacky::Tools::AskUser.normalize_questions(args_data)
context = args_data.is_a?(Hash) ? (args_data[:context] || args_data["context"]).to_s : ""
puts_line(Clacky::Tools::AskUser.render_text(questions, context)) unless questions.empty?
return
end
display = case name
when "terminal"
cmd = args_data.is_a?(Hash) ? (args_data[:command] || args_data["command"]) : args_data
sid = args_data.is_a?(Hash) ? (args_data[:session_id] || args_data["session_id"]) : nil
if cmd
"$ #{cmd}"
elsif sid
"$ (session ##{sid})"
else
"$ terminal"
end
when "write"
path = args_data.is_a?(Hash) ? (args_data[:path] || args_data["path"]) : args_data
"Write → #{path}"
when "edit"
path = args_data.is_a?(Hash) ? (args_data[:path] || args_data["path"]) : args_data
"Edit → #{path}"
else
label = args_data.is_a?(Hash) ? args_data.map { |k, v| "#{k}=#{v.to_s[0..40]}" }.join(", ") : args_data.to_s[0..80]
"#{name}(#{label})"
end
puts_line("[tool] #{display}")
end
|
68
69
70
71
|
# File 'lib/clacky/plain_ui_controller.rb', line 68
def show_tool_error(error)
msg = error.is_a?(Exception) ? error.message : error.to_s
puts_line("[error] #{msg}")
end
|
59
60
61
62
63
64
65
66
|
# File 'lib/clacky/plain_ui_controller.rb', line 59
def show_tool_result(result)
text = result.to_s.strip
return if text.empty?
indented = text.lines.map { |l| " #{l}" }.join
puts_line(indented)
end
|
#show_warning(message) ⇒ Object
106
107
108
|
# File 'lib/clacky/plain_ui_controller.rb', line 106
def show_warning(message)
puts_line("[warn] #{message}")
end
|
#stop ⇒ Object
169
|
# File 'lib/clacky/plain_ui_controller.rb', line 169
def stop; end
|
#update_sessionbar(tasks: nil, cost: nil, cost_source: nil, status: nil, latency: nil) ⇒ Object
State updates (no-ops) ===
152
|
# File 'lib/clacky/plain_ui_controller.rb', line 152
def update_sessionbar(tasks: nil, cost: nil, cost_source: nil, status: nil, latency: nil); end
|
#update_todos(todos) ⇒ Object
153
|
# File 'lib/clacky/plain_ui_controller.rb', line 153
def update_todos(todos); end
|