Skip to main content

claude_code_cli_acp/
print_mode.rs

1use std::{path::PathBuf, str::FromStr, time::Duration};
2
3use clap::ValueEnum;
4use serde::Serialize;
5use uuid::Uuid;
6
7use crate::session::manager::{ManagedSession, SessionManager, TurnOptions};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
10pub enum OutputFormat {
11    Text,
12    Json,
13    StreamJson,
14}
15
16impl FromStr for OutputFormat {
17    type Err = String;
18
19    fn from_str(value: &str) -> Result<Self, Self::Err> {
20        match value {
21            "text" => Ok(Self::Text),
22            "json" => Ok(Self::Json),
23            "stream-json" => Ok(Self::StreamJson),
24            other => Err(format!("unsupported output format: {other}")),
25        }
26    }
27}
28
29#[derive(Debug, Clone)]
30pub struct PrintRequest {
31    pub prompt: String,
32    pub output_format: OutputFormat,
33    pub resume: Option<String>,
34    pub continue_last: bool,
35    pub session_id: Option<String>,
36    pub cwd: Option<PathBuf>,
37    pub model: Option<String>,
38    pub permission_mode: Option<String>,
39    pub timeout: Duration,
40    pub attach_on_timeout: bool,
41    pub attach_on_permission: bool,
42}
43
44#[derive(Debug, Serialize)]
45struct JsonOutput {
46    session_id: String,
47    text: String,
48    model: Option<String>,
49}
50
51pub async fn run(request: PrintRequest) -> anyhow::Result<()> {
52    let cwd = request.cwd.clone().unwrap_or(std::env::current_dir()?);
53    let session_id = request
54        .session_id
55        .clone()
56        .unwrap_or_else(|| Uuid::new_v4().to_string());
57    let manager = SessionManager::new();
58    let session = manager.create_print_session(session_id.clone(), cwd, request.model.clone())?;
59    let turn = session
60        .prompt(
61            request.prompt,
62            TurnOptions {
63                timeout: request.timeout,
64                model: request.model,
65                permission_mode: request.permission_mode,
66                resume: request.resume,
67                continue_last: request.continue_last,
68                initial_prompt_argument: false,
69                attach_on_timeout: request.attach_on_timeout,
70                attach_on_permission: request.attach_on_permission,
71            },
72        )
73        .await?;
74    let emit_result = emit_output(
75        request.output_format,
76        &session,
77        &turn.final_text(),
78        turn.model(),
79    )
80    .await;
81    let shutdown_result = session.shutdown().await;
82    emit_result?;
83    shutdown_result
84}
85
86async fn emit_output(
87    format: OutputFormat,
88    session: &ManagedSession,
89    text: &str,
90    model: Option<String>,
91) -> anyhow::Result<()> {
92    match format {
93        OutputFormat::Text => {
94            write_stdout(format!("{text}\n"))?;
95        }
96        OutputFormat::Json => {
97            write_stdout(format!(
98                "{}\n",
99                serde_json::to_string(&JsonOutput {
100                    session_id: session.session_id().0.to_string(),
101                    text: text.to_string(),
102                    model,
103                })?
104            ))?;
105        }
106        OutputFormat::StreamJson => {
107            write_stdout(format!(
108                "{}\n",
109                serde_json::to_string(&serde_json::json!({
110                    "type": "message",
111                    "session_id": session.session_id().0.to_string(),
112                    "text": text,
113                    "model": model,
114                }))?
115            ))?;
116        }
117    }
118    Ok(())
119}
120
121fn write_stdout(output: String) -> anyhow::Result<()> {
122    use std::io::Write;
123    std::io::stdout().write_all(output.as_bytes())?;
124    Ok(())
125}