Skip to main content

talon_cli/output/
mod.rs

1//! Stdout emission for CLI responses.
2
3mod ask;
4mod human;
5pub(crate) mod json;
6mod obsidian;
7mod recall;
8mod search;
9mod style;
10
11use crate::exit_codes;
12use eyre::Result;
13use std::io::{self, Write};
14use talon_core::TalonEnvelope;
15
16pub use ask::format_ask_human;
17pub use human::{format_inspect_human, format_status_human, format_sync_human};
18pub use recall::{format_recall_human, format_recall_prompt_xml};
19pub use search::format_search_human;
20
21/// CLI output mode.
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum OutputMode {
24    /// Human-readable formatted output (colored headings, result cards).
25    Human,
26    /// Full pretty JSON for debugging.
27    JsonPretty,
28    /// Compact token-efficient JSON for agents.
29    Agent,
30}
31
32/// Options controlling human-readable rendering.
33#[derive(Debug, Clone, Copy)]
34pub struct RenderOptions {
35    /// Terminal column width used for wrapping.
36    pub width: u16,
37    /// Whether ANSI color codes should be emitted.
38    pub colors: bool,
39    /// Show compact one-liner cards (title + path + score only, no snippet).
40    pub compact: bool,
41}
42
43impl RenderOptions {
44    /// Detects the current terminal width and color support.
45    #[must_use]
46    pub fn for_terminal() -> Self {
47        use terminal_size::{Width, terminal_size};
48        let width = terminal_size().map_or(80, |(Width(w), _)| w);
49        Self {
50            width,
51            colors: crate::platform::stdout_is_tty() && crate::platform::user_accepts_ansi_color(),
52            compact: false,
53        }
54    }
55}
56
57/// Writes bytes to stdout.
58#[must_use]
59pub fn write_stdout_bytes(bytes: &[u8]) -> u8 {
60    match io::stdout().lock().write_all(bytes) {
61        Ok(()) => exit_codes::SUCCESS,
62        Err(error) => {
63            eprintln!("Error: {error}");
64            exit_codes::GENERIC_ERROR
65        }
66    }
67}
68
69/// Emits a Talon envelope.
70///
71/// # Errors
72///
73/// Returns an error if serialization or stdout writes fail.
74pub fn emit_response(envelope: &TalonEnvelope, mode: OutputMode) -> Result<()> {
75    match mode {
76        OutputMode::Human => human::emit(envelope),
77        OutputMode::JsonPretty => json::emit_pretty(envelope),
78        OutputMode::Agent => json::emit_agent(envelope),
79    }
80}