1use crate::console::is_stdout_terminal;
2use crate::exceptions::AicoError;
3use crate::models::{DisplayItem, MessagePairJson, MessageWithId};
4use crate::session::Session;
5use std::io::Write;
6
7pub fn run(
8 index_str: String,
9 prompt: bool,
10 verbatim: bool,
11 recompute: bool,
12 json: bool,
13) -> Result<(), AicoError> {
14 let session = Session::load_active()?;
15 let resolved_idx = session.resolve_pair_index(&index_str)?;
16
17 let (user_rec, asst_rec, user_id, asst_id) = session.fetch_pair(resolved_idx)?;
18
19 if json {
20 let output = MessagePairJson {
21 pair_index: resolved_idx,
22 user: MessageWithId {
23 record: user_rec,
24 id: user_id,
25 },
26 assistant: MessageWithId {
27 record: asst_rec,
28 id: asst_id,
29 },
30 };
31
32 let mut stdout = std::io::stdout();
33 let res = if crate::console::is_stdout_terminal() {
34 serde_json::to_writer_pretty(&mut stdout, &output)
35 } else {
36 serde_json::to_writer(&mut stdout, &output)
37 };
38
39 if let Err(e) = res
40 && !e.is_io()
41 {
42 return Err(AicoError::Serialization(e));
43 }
44 let _ = writeln!(stdout);
45 return Ok(());
46 }
47
48 if prompt {
49 if recompute {
50 return Err(AicoError::InvalidInput(
51 "--recompute cannot be used with --prompt.".into(),
52 ));
53 }
54 print!("{}", user_rec.content);
55 return Ok(());
56 }
57
58 if verbatim {
59 print!("{}", asst_rec.content);
60 return Ok(());
61 }
62
63 let is_tty = is_stdout_terminal();
64
65 let (unified_diff, display_items, warnings) = match (&asst_rec.derived, recompute) {
68 (Some(derived), false) => (
69 derived.unified_diff.clone(),
70 derived
71 .display_content
72 .clone()
73 .unwrap_or_else(|| vec![DisplayItem::Markdown(asst_rec.content.clone())]),
74 vec![],
75 ),
76 _ => {
77 use crate::diffing::parser::StreamParser;
78
79 let mut parser = StreamParser::new(&session.context_content);
80 let gated_content = if asst_rec.content.ends_with('\n') {
81 asst_rec.content.clone()
82 } else {
83 format!("{}\n", asst_rec.content)
84 };
85 parser.feed(&gated_content);
86
87 let (diff, items, warnings) = parser.final_resolve(&session.root);
88
89 (Some(diff), items, warnings)
90 }
91 };
92
93 if is_tty {
95 let width = crate::console::get_terminal_width();
96 let mut engine = crate::ui::markdown_streamer::MarkdownStreamer::new();
97 engine.set_width(width);
98 engine.set_margin(0);
99
100 let mut stdout = std::io::stdout();
101 for item in &display_items {
102 match item {
103 DisplayItem::Markdown(m) => {
104 let _ = engine.print_chunk(&mut stdout, m);
105 }
106 DisplayItem::Diff(d) => {
107 let _ = engine.print_chunk(&mut stdout, "\n~~~~~diff\n");
108 let _ = engine.print_chunk(&mut stdout, d);
109 let _ = engine.print_chunk(&mut stdout, "\n~~~~~\n");
110 }
111 }
112 }
113 let _ = engine.flush(&mut stdout);
114 } else {
115 if matches!(asst_rec.mode, crate::models::Mode::Diff) {
118 if let Some(diff) = unified_diff {
119 print!("{}", diff);
120 }
121 } else {
122 if let Some(diff) = unified_diff {
124 if !diff.is_empty() {
125 print!("{}", diff);
126 } else {
127 print!("{}", asst_rec.content);
128 }
129 } else {
130 print!("{}", asst_rec.content);
131 }
132 }
133 }
134
135 if !warnings.is_empty() {
136 eprintln!("\nWarnings:");
137 for w in warnings {
138 eprintln!("{}", w);
139 }
140 }
141
142 Ok(())
143}