//! Claude Code JSON protocol types for stdin communication. //! //! When using `--input-format=stream-json`, Claude Code expects //! newline-delimited JSON messages on stdin. use serde::Serialize; /// Message sent to Claude Code via stdin. /// /// Format based on Claude Code's stream-json input protocol. #[derive(Debug, Clone, Serialize)] #[serde(tag = "type", rename_all = "snake_case")] pub enum ClaudeInputMessage { /// A user message to send to Claude. User { message: UserMessage }, } /// The inner user message structure. #[derive(Debug, Clone, Serialize)] pub struct UserMessage { /// Always "user" for user messages. pub role: String, /// The message content. pub content: String, } impl ClaudeInputMessage { /// Create a new user message. pub fn user(content: impl Into) -> Self { Self::User { message: UserMessage { role: "user".to_string(), content: content.into(), }, } } /// Serialize to a JSON string with trailing newline (NDJSON format). pub fn to_json_line(&self) -> Result { let mut json = serde_json::to_string(self)?; json.push('\n'); Ok(json) } } #[cfg(test)] mod tests { use crate::daemon::*; #[test] fn test_user_message_serialization() { let msg = ClaudeInputMessage::user("Hello, Claude!"); let json = msg.to_json_line().unwrap(); // Should produce: {"type":"user","message":{"role":"user","content":"Hello, Claude!"}}\n assert!(json.starts_with(r#"{"type":"user","message":{"role":"user","content":"Hello, Claude!"}}"#)); assert!(json.ends_with('\n')); } }