Class: Clacky::JsonUIController

Inherits:
Object
  • Object
show all
Includes:
UIInterface
Defined in:
lib/clacky/json_ui_controller.rb

Overview

JsonUIController implements UIInterface for JSON (NDJSON) output mode. All output is written as one-JSON-per-line to stdout. Confirmation requests read responses from stdin.

Instance Method Summary collapse

Methods included from UIInterface

#show_tool_stdout

Constructor Details

#initialize(output: $stdout, input: $stdin) ⇒ JsonUIController

Returns a new instance of JsonUIController.



14
15
16
17
18
# File 'lib/clacky/json_ui_controller.rb', line 14

def initialize(output: $stdout, input: $stdin)
  @output = output
  @input = input
  @mutex = Mutex.new
end

Instance Method Details

#append_output(content) ⇒ Object

append_output is a no-op in JSON mode (content is already emitted via semantic methods)



90
91
92
# File 'lib/clacky/json_ui_controller.rb', line 90

def append_output(content)
  # no-op
end

#clear_inputObject

Input control (no-ops in JSON mode) ===



183
184
185
# File 'lib/clacky/json_ui_controller.rb', line 183

def clear_input
  # no-op
end

#clear_progressObject



127
128
129
130
131
# File 'lib/clacky/json_ui_controller.rb', line 127

def clear_progress
  elapsed = @progress_start_time ? (Time.now - @progress_start_time).round(1) : 0
  @progress_start_time = nil
  emit("progress", status: "stop", elapsed: elapsed)
end

#emit(type, **data) ⇒ Object

Emit a raw NDJSON event



21
22
23
24
25
26
27
# File 'lib/clacky/json_ui_controller.rb', line 21

def emit(type, **data)
  event = { type: type }.merge(data)
  @mutex.synchronize do
    @output.puts(JSON.generate(event))
    @output.flush
  end
end

#log(message, level: :info) ⇒ Object



116
117
118
# File 'lib/clacky/json_ui_controller.rb', line 116

def log(message, level: :info)
  emit("log", level: level.to_s, message: message)
end

#request_confirmation(message, default: true) ⇒ Object

Blocking interaction ===



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/clacky/json_ui_controller.rb', line 157

def request_confirmation(message, default: true)
  conf_id = "conf_#{SecureRandom.hex(4)}"
  emit("request_confirmation", id: conf_id, message: message, default: default)

  # Read response from stdin (blocking)
  line = @input.gets
  return default if line.nil?

  begin
    response = JSON.parse(line.strip)
    result = response["result"] || response[:result]

    case result.to_s.downcase
    when "yes", "y" then true
    when "no", "n" then false
    else
      # Return as feedback text
      result.to_s
    end
  rescue JSON::ParserError
    default
  end
end

#set_idle_statusObject



151
152
153
# File 'lib/clacky/json_ui_controller.rb', line 151

def set_idle_status
  emit("session_update", status: "idle")
end

#set_input_tips(message, type: :info) ⇒ Object



187
188
189
# File 'lib/clacky/json_ui_controller.rb', line 187

def set_input_tips(message, type: :info)
  # no-op
end

#set_working_statusObject



147
148
149
# File 'lib/clacky/json_ui_controller.rb', line 147

def set_working_status
  emit("session_update", status: "working")
end

#show_assistant_message(content, files:) ⇒ Object

Output display ===



31
32
33
34
35
36
37
# File 'lib/clacky/json_ui_controller.rb', line 31

def show_assistant_message(content, files:)
  return if (content.nil? || content.strip.empty?) && files.empty?

  data = { content: content.to_s }
  data[:files] = files if files.any?
  emit("assistant_message", **data)
end

#show_complete(iterations:, cost:, duration: nil, cache_stats: nil, awaiting_user_feedback: false) ⇒ Object



81
82
83
84
85
86
87
# File 'lib/clacky/json_ui_controller.rb', line 81

def show_complete(iterations:, cost:, duration: nil, cache_stats: nil, awaiting_user_feedback: false)
  data = { iterations: iterations, cost: cost }
  data[:duration] = duration if duration
  data[:cache_stats] = cache_stats if cache_stats
  data[:awaiting_user_feedback] = awaiting_user_feedback if awaiting_user_feedback
  emit("complete", **data)
end

#show_diff(old_content, new_content, max_lines: 50) ⇒ Object



73
74
75
# File 'lib/clacky/json_ui_controller.rb', line 73

def show_diff(old_content, new_content, max_lines: 50)
  emit("diff", old_size: old_content.bytesize, new_size: new_content.bytesize)
end

#show_error(message) ⇒ Object



108
109
110
# File 'lib/clacky/json_ui_controller.rb', line 108

def show_error(message)
  emit("error", message: message)
end

#show_file_edit_preview(path) ⇒ Object



61
62
63
# File 'lib/clacky/json_ui_controller.rb', line 61

def show_file_edit_preview(path)
  emit("file_preview", path: path, operation: "edit")
end

#show_file_error(error_message) ⇒ Object



65
66
67
# File 'lib/clacky/json_ui_controller.rb', line 65

def show_file_error(error_message)
  emit("file_error", error: error_message)
end

#show_file_write_preview(path, is_new_file:) ⇒ Object



57
58
59
# File 'lib/clacky/json_ui_controller.rb', line 57

def show_file_write_preview(path, is_new_file:)
  emit("file_preview", path: path, operation: "write", is_new_file: is_new_file)
end

#show_idle_status(phase:, message:) ⇒ Object



100
101
102
# File 'lib/clacky/json_ui_controller.rb', line 100

def show_idle_status(phase:, message:)
  emit("idle_status", phase: phase.to_s, message: message)
end

#show_info(message, prefix_newline: true) ⇒ Object

Status messages ===



96
97
98
# File 'lib/clacky/json_ui_controller.rb', line 96

def show_info(message, prefix_newline: true)
  emit("info", message: message)
end

#show_progress(message = nil, prefix_newline: true, output_buffer: nil) ⇒ Object

Progress ===



122
123
124
125
# File 'lib/clacky/json_ui_controller.rb', line 122

def show_progress(message = nil, prefix_newline: true, output_buffer: nil)
  @progress_start_time = Time.now
  emit("progress", message: message, status: "start")
end

#show_shell_preview(command) ⇒ Object



69
70
71
# File 'lib/clacky/json_ui_controller.rb', line 69

def show_shell_preview(command)
  emit("shell_preview", command: command)
end

#show_success(message) ⇒ Object



112
113
114
# File 'lib/clacky/json_ui_controller.rb', line 112

def show_success(message)
  emit("success", message: message)
end

#show_token_usage(token_data) ⇒ Object



77
78
79
# File 'lib/clacky/json_ui_controller.rb', line 77

def show_token_usage(token_data)
  emit("token_usage", **token_data)
end

#show_tool_args(formatted_args) ⇒ Object



53
54
55
# File 'lib/clacky/json_ui_controller.rb', line 53

def show_tool_args(formatted_args)
  emit("tool_args", args: formatted_args)
end

#show_tool_call(name, args) ⇒ Object



39
40
41
42
# File 'lib/clacky/json_ui_controller.rb', line 39

def show_tool_call(name, args)
  args_data = args.is_a?(String) ? (JSON.parse(args) rescue args) : args
  emit("tool_call", name: name, args: args_data)
end

#show_tool_error(error) ⇒ Object



48
49
50
51
# File 'lib/clacky/json_ui_controller.rb', line 48

def show_tool_error(error)
  error_msg = error.is_a?(Exception) ? error.message : error.to_s
  emit("tool_error", error: error_msg)
end

#show_tool_result(result) ⇒ Object



44
45
46
# File 'lib/clacky/json_ui_controller.rb', line 44

def show_tool_result(result)
  emit("tool_result", result: result)
end

#show_warning(message) ⇒ Object



104
105
106
# File 'lib/clacky/json_ui_controller.rb', line 104

def show_warning(message)
  emit("warning", message: message)
end

#stopObject

Lifecycle ===



193
194
195
# File 'lib/clacky/json_ui_controller.rb', line 193

def stop
  # no-op
end

#update_sessionbar(tasks: nil, cost: nil, status: nil) ⇒ Object

State updates ===



135
136
137
138
139
140
141
# File 'lib/clacky/json_ui_controller.rb', line 135

def update_sessionbar(tasks: nil, cost: nil, status: nil)
  data = {}
  data[:tasks] = tasks if tasks
  data[:cost] = cost if cost
  data[:status] = status if status
  emit("session_update", **data) unless data.empty?
end

#update_todos(todos) ⇒ Object



143
144
145
# File 'lib/clacky/json_ui_controller.rb', line 143

def update_todos(todos)
  emit("todo_update", todos: todos)
end