module CursorAgentOutput
Constants
- MARKER_LINE
- StreamResult
Public Instance Methods
Source
# File lib/cursor_agent_output.rb, line 129 def build_args(prompt:, model:, workspace:, trust:, force:, partial_output:) args = [ "cursor-agent", "--model", model, "--print", "--output-format", "stream-json" ] args << "--stream-partial-output" if partial_output args << "--trust" if trust args << "--force" if force args << "--workspace" << workspace args << prompt.to_s args end
Source
# File lib/cursor_agent_output.rb, line 144 def execute_stream(args, model:, on_assistant_text:, partial_output:) parser = StreamParser.new( model: model, on_assistant_text: on_assistant_text, partial_output: partial_output ) stderr_text = +"" CursorAgentRunner.popen3(*args, lane: :build) do |stdin, stdout, stderr, wait_thr| stdin.close stdout.each_line { |line| parser.ingest_line(line) } stderr_text = stderr.read status = wait_thr.value result = parser.to_result(status: status) result ||= parse_single_json("", stderr_text, status, model: model) [result, stderr_text] end end
Source
# File lib/cursor_agent_output.rb, line 206 def extract_todos(event) update = event.dig("tool_call", "updateTodosToolCall") return nil unless update raw_todos = update.dig("result", "success", "todos") || update.dig("args", "todos") return nil unless raw_todos.is_a?(Array) && raw_todos.any? raw_todos.map do |todo| { content: todo["content"].to_s.strip, status: normalize_todo_status(todo["status"]) } end.reject { |todo| todo[:content].empty? } end
Source
# File lib/cursor_agent_output.rb, line 239 def format_irc_report(result, include_summary: true) parts = [] if include_summary summary = format_summary_text(result.text) parts << "Summary:" << summary if summary end todos = result.todos if todos.is_a?(Array) && todos.any? completed = todos.count { |todo| todo[:status] == :completed } parts << "Tasks (#{completed} completed):" todos.each do |todo| mark = case todo[:status] when :completed then "[x]" when :in_progress then "[~]" else "[ ]" end parts << " #{mark} #{todo[:content]}" end end report = parts.join("\n").strip report.empty? ? nil : report end
Source
# File lib/cursor_agent_output.rb, line 232 def format_summary_text(text) lines = text.to_s.lines.map(&:strip).reject(&:empty?) lines.reject! { |line| line.match?(MARKER_LINE) } summary = lines.join("\n").strip summary.empty? ? nil : summary end
Source
# File lib/cursor_agent_output.rb, line 221 def normalize_todo_status(status) case status.to_s when "TODO_STATUS_COMPLETED" :completed when "TODO_STATUS_IN_PROGRESS" :in_progress else :pending end end
Source
# File lib/cursor_agent_output.rb, line 163 def parse(stdout, stderr, status, model: nil) stream = parse_stream(stdout, model: model, status: status) return stream if stream parse_single_json(stdout, stderr, status, model: model) end
Source
# File lib/cursor_agent_output.rb, line 176 def parse_single_json(stdout, stderr, status, model: nil) json_line = stdout.to_s.lines.map(&:strip).reject(&:empty?).last json = JSON.parse(json_line) usage = json["usage"] || {} StreamResult.new( success: status.success? && json["is_error"] != true, text: json["result"].to_s, duration_ms: json["duration_ms"].to_i, model: model, input_tokens: usage["inputTokens"].to_i, output_tokens: usage["outputTokens"].to_i, error: nil, todos: [] ) rescue JSON::ParserError StreamResult.new( success: false, text: stdout.to_s, duration_ms: 0, model: model, input_tokens: 0, output_tokens: 0, error: CursorAgentRunner.friendly_error( stderr.to_s.strip.empty? ? stdout.to_s.strip : stderr.to_s.strip ), todos: [] ) end
Source
# File lib/cursor_agent_output.rb, line 170 def parse_stream(stdout, model: nil, status: nil, on_assistant_text: nil, partial_output: true) parser = StreamParser.new(model: model, on_assistant_text: on_assistant_text, partial_output: partial_output) stdout.to_s.each_line { |line| parser.ingest_line(line) } parser.to_result(status: status) end
Source
# File lib/cursor_agent_output.rb, line 99 def run(prompt, model:, workspace:, trust: true, force: true, partial_output: true, on_assistant_text: nil, runner: nil) args = build_args( prompt: prompt, model: model, workspace: workspace, trust: trust, force: force, partial_output: partial_output ) result, stderr = if runner stdout, err, status = runner.call(args) [parse(stdout, err, status, model: model), err] else execute_stream(args, model: model, on_assistant_text: on_assistant_text, partial_output: partial_output) end unless result.success? message = CursorAgentRunner.failure_message( stdout: result.text, stderr: stderr, fallback: "AI request failed" ) result.error = message unless message.empty? end result end