use anyhow::Result;
use claco::{claude_home, SessionEntry};
use regex::Regex;
use std::fs;
use std::io::{BufRead, BufReader};
use super::format_timestamp_local;
pub fn handle_history(session_id: Option<String>) -> Result<()> {
let cwd = std::env::current_dir()?;
let cwd_str = cwd.to_string_lossy();
let projects_dir = claude_home()?.join("projects");
if !projects_dir.exists() {
println!("No Claude projects directory found");
return Ok(());
}
let mut matched_project_path = None;
'outer: for entry in fs::read_dir(&projects_dir)? {
let entry = entry?;
let path = entry.path();
if !path.is_dir() {
continue;
}
for session_entry in fs::read_dir(&path)? {
let session_entry = session_entry?;
let session_path = session_entry.path();
if session_path.extension().and_then(|s| s.to_str()) == Some("jsonl") {
if let Ok(file) = fs::File::open(&session_path) {
let reader = BufReader::new(file);
if let Some(Ok(first_line)) = reader.lines().next() {
if let Ok(entry) = serde_json::from_str::<SessionEntry>(&first_line) {
if let Some(ref entry_cwd) = entry.cwd {
if entry_cwd == &cwd_str {
matched_project_path = Some(path);
break 'outer;
}
}
}
}
}
}
}
if matched_project_path.is_some() {
break;
}
}
let project_path = match matched_project_path {
Some(path) => path,
None => {
println!("No Claude project found for current directory: {cwd_str}");
return Ok(());
}
};
let entries = fs::read_dir(&project_path)?;
let command_regex = Regex::new(r"<command-name>(/[^<]+)</command-name>").unwrap();
for entry in entries {
let entry = entry?;
let path = entry.path();
if path.extension().and_then(|s| s.to_str()) == Some("jsonl") {
let file_name = match path.file_stem() {
Some(stem) => stem.to_string_lossy(),
None => {
eprintln!("warning: could not get file stem from: {}", path.display());
continue;
}
};
if let Some(ref sid) = session_id {
if file_name != *sid {
continue;
}
}
let file = fs::File::open(&path)?;
let reader = BufReader::new(file);
let mut skip_next = false;
for line in reader.lines() {
let line = line?;
if line.trim().is_empty() {
continue;
}
if skip_next {
skip_next = false;
continue;
}
if !(line.contains(r#""type":"user""#)
&& line.contains(r#""role":"user""#)
&& line.contains(r#""isSidechain":false"#))
{
continue;
}
if line.contains(r#"Caveat: The messages below were generated by the user while running local commands."#) {
continue;
}
if let Ok(entry) = serde_json::from_str::<SessionEntry>(&line) {
if let (Some(ref message), Some(ref timestamp)) =
(&entry.message, &entry.timestamp)
{
if let Some(captures) = command_regex.captures(&message.content) {
if let Some(command) = captures.get(1) {
println!(
"{}: {}",
format_timestamp_local(timestamp),
command.as_str()
);
skip_next = true;
}
} else {
let content = message.content.trim();
if !content.is_empty() {
println!(
"{}: {}",
format_timestamp_local(timestamp),
message.content
);
}
}
}
}
}
}
}
Ok(())
}