Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 56 additions & 62 deletions src/claude_code.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,11 @@
use crate::config::Config;
use crate::output;
use crate::provider::{AgentSession, BootstrapMessage, OutputChunk, ProviderError, StepMessage};
use serde::Deserialize;
use std::io::{BufRead, BufReader, Write};
use std::path::{Path, PathBuf};
use std::process::{Child, ChildStdin, Command, Stdio};

/// ANSI color codes for verbose output.
mod color {
// Label prefix — dim grey
pub const DIM: &str = "\x1b[38;5;243m";
// Content text — lighter grey
pub const LIGHT: &str = "\x1b[38;5;250m";
// Tool names — soft blue
pub const TOOL: &str = "\x1b[38;5;111m";
// Thinking — soft purple
pub const THINKING: &str = "\x1b[38;5;183m";
// Tool result — soft green
pub const RESULT: &str = "\x1b[38;5;151m";
// Message/prompt — soft yellow
pub const PROMPT: &str = "\x1b[38;5;223m";
// Launch command — soft cyan
pub const CMD: &str = "\x1b[38;5;152m";
// Separator — very dim
pub const SEP: &str = "\x1b[38;5;238m";
// Reset
pub const RESET: &str = "\x1b[0m";
}

/// Claude Code CLI provider adapter.
///
/// Implements the `AgentSession` trait by driving a single long-lived `claude`
Expand Down Expand Up @@ -167,25 +146,26 @@ impl ClaudeCodeAdapter {
.stderr(Stdio::piped());

if self.verbose {
let c = output::stderr_colors();
let args: Vec<_> = cmd
.get_args()
.map(|a| a.to_string_lossy().to_string())
.collect();
eprintln!(
"{}[verbose]{} {}launch:{} {} {}{}",
color::DIM,
color::RESET,
color::DIM,
color::RESET,
color::CMD,
c.dim,
c.reset,
c.dim,
c.reset,
c.cmd,
args.join(" "),
color::RESET
c.reset
);
eprintln!(
"{} binary: {}{}",
color::DIM,
c.dim,
cmd.get_program().to_string_lossy(),
color::RESET
c.reset
);
}

Expand Down Expand Up @@ -226,16 +206,17 @@ impl ClaudeCodeAdapter {
let input_line = format_stream_input(message);

if self.verbose {
let c = output::stderr_colors();
eprintln!(
"{}[verbose]{} {}prompt ({} bytes):{}",
color::DIM,
color::RESET,
color::DIM,
c.dim,
c.reset,
c.dim,
message.len(),
color::RESET
c.reset
);
eprintln!("{}{}{}", color::PROMPT, message, color::RESET);
eprintln!("{}───{}", color::SEP, color::RESET);
eprintln!("{}{}{}", c.prompt, message, c.reset);
eprintln!("{}───{}", c.sep, c.reset);
}

stdin
Expand All @@ -257,6 +238,11 @@ impl ClaudeCodeAdapter {
reader,
done: false,
verbose: self.verbose,
colors: if self.verbose {
Some(output::stderr_colors())
} else {
None
},
}))
}
}
Expand Down Expand Up @@ -350,6 +336,7 @@ struct StreamTurnIterator<'a> {
reader: &'a mut BufReader<std::process::ChildStdout>,
done: bool,
verbose: bool,
colors: Option<&'static output::Colors>,
}

impl<'a> Iterator for StreamTurnIterator<'a> {
Expand All @@ -360,6 +347,8 @@ impl<'a> Iterator for StreamTurnIterator<'a> {
return None;
}

let c = if self.verbose { self.colors } else { None };

loop {
let mut line = String::new();
match self.reader.read_line(&mut line) {
Expand Down Expand Up @@ -392,7 +381,7 @@ impl<'a> Iterator for StreamTurnIterator<'a> {
}
}
"tool_use" => {
if self.verbose {
if let Some(c) = c {
let name =
block.name.as_deref().unwrap_or("unknown");
let input_preview = block
Expand Down Expand Up @@ -427,25 +416,32 @@ impl<'a> Iterator for StreamTurnIterator<'a> {
.chars()
.take(12)
.collect::<String>();
eprintln!("{}[verbose]{} {}tool:{} {}{}{} {}{}{} {}({}){}", color::DIM, color::RESET, color::DIM, color::RESET, color::TOOL, name, color::RESET, color::LIGHT, input_preview, color::RESET, color::DIM, id_short, color::RESET);
eprintln!(
"{}[verbose]{} {}tool:{} {}{}{} {}{}{} {}({}){}",
c.dim,
c.reset,
c.dim,
c.reset,
c.tool,
name,
c.reset,
c.light,
input_preview,
c.reset,
c.dim,
id_short,
c.reset
);
}
}
"thinking" => {
if self.verbose {
if let Some(c) = c {
if let Some(thinking) = &block.thinking {
eprintln!(
"{}[verbose]{} {}thinking:{}",
color::DIM,
color::RESET,
color::DIM,
color::RESET
);
eprintln!(
"{}{}{}",
color::THINKING,
thinking,
color::RESET
c.dim, c.reset, c.dim, c.reset
);
eprintln!("{}{}{}", c.thinking, thinking, c.reset);
}
}
}
Expand All @@ -457,7 +453,7 @@ impl<'a> Iterator for StreamTurnIterator<'a> {
}
"user" => {
// Tool results — log in verbose mode
if self.verbose {
if let Some(c) = c {
if let Some(msg) = &event.message {
for block in &msg.content {
if block.block_type == "tool_result" {
Expand All @@ -479,20 +475,15 @@ impl<'a> Iterator for StreamTurnIterator<'a> {
.collect::<String>();
eprintln!(
"{}[verbose]{} {}result:{} {}({}){}",
color::DIM,
color::RESET,
color::DIM,
color::RESET,
color::DIM,
c.dim,
c.reset,
c.dim,
c.reset,
c.dim,
id_short,
color::RESET
);
eprintln!(
"{}{}{}",
color::RESULT,
result_text,
color::RESET
c.reset
);
eprintln!("{}{}{}", c.result, result_text, c.reset);
}
}
}
Expand Down Expand Up @@ -654,6 +645,7 @@ mod tests {
reader: unsafe { &mut *(&mut reader as *mut BufReader<std::process::ChildStdout>) },
done: false,
verbose: false,
colors: None,
};

let mut collected = Vec::new();
Expand Down Expand Up @@ -686,6 +678,7 @@ mod tests {
reader: unsafe { &mut *(&mut reader as *mut BufReader<std::process::ChildStdout>) },
done: false,
verbose: false,
colors: None,
};

let result = iter.next();
Expand Down Expand Up @@ -715,6 +708,7 @@ mod tests {
reader: unsafe { &mut *(&mut reader as *mut BufReader<std::process::ChildStdout>) },
done: false,
verbose: false,
colors: None,
};

let mut texts = Vec::new();
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod discovery;
pub mod executor;
pub mod exit_code;
pub mod expand;
pub mod output;
pub mod provider;
pub mod report;
pub mod run;
Expand Down
Loading