pub(crate) use anyhow::Context;
use anyhow::Result;
pub(crate) use chrono::{DateTime, Utc};
use std::collections::HashSet;
pub(crate) use std::fs;
pub(crate) use std::path::{Path, PathBuf};
pub(crate) use crate::sanitize;
pub use crate::timeline::{
CollapseStubKind, ConversationMessage, ExtractionConfig, FrameKind, MessageKind, SourceInfo,
TimelineEntry,
};
pub mod providers;
pub mod shared;
const UNPROTECTED_SOURCE_WARNING: &str = "unprotected source material; run `aicx sources protect --root <path> --backend git-local --apply` to opt in";
pub(crate) use providers::count_codex_sessions;
pub use providers::{
CodescribeTranscript, OperatorMarkdown, discover_codescribe_transcripts,
discover_codescribe_transcripts_at, discover_operator_markdown,
discover_operator_markdown_from, extract_claude, extract_claude_file, extract_claude_history,
extract_codescribe, extract_codescribe_from_home, extract_codex, extract_codex_file,
extract_codex_sessions, extract_gemini, extract_gemini_antigravity_file, extract_gemini_file,
extract_junie, extract_junie_file, extract_operator_markdown,
extract_operator_markdown_from_home, extract_operator_markdown_from_home_and_repo,
parse_codescribe_transcript,
};
pub(crate) use shared::*;
pub use shared::{
ConversationProjection, decode_claude_project_path, detect_project_name,
infer_repo_name_from_current_dir, is_harness_injected_noise, list_available_sources,
repo_labels_from_entries, repo_name_from_cwd, to_conversation, to_conversation_with_stats,
};
pub fn extract_all(config: &ExtractionConfig) -> Result<Vec<TimelineEntry>> {
let mut all: Vec<TimelineEntry> = Vec::new();
match extract_claude(config) {
Ok(entries) => all.extend(entries),
Err(e) => eprintln!("Claude extraction warning: {}", e),
}
match extract_codex(config) {
Ok(entries) => all.extend(entries),
Err(e) => eprintln!("Codex extraction warning: {}", e),
}
match extract_gemini(config) {
Ok(entries) => all.extend(entries),
Err(e) => eprintln!("Gemini extraction warning: {}", e),
}
match extract_junie(config) {
Ok(entries) => all.extend(entries),
Err(e) => eprintln!("Junie extraction warning: {}", e),
}
match extract_codescribe(config) {
Ok(entries) => all.extend(entries),
Err(e) => eprintln!("CodeScribe extraction warning: {}", e),
}
match extract_operator_markdown(config) {
Ok(entries) => all.extend(entries),
Err(e) => eprintln!("Operator markdown extraction warning: {}", e),
}
match extract_claude_history(config) {
Ok(entries) => all.extend(entries),
Err(e) => eprintln!("Claude history extraction warning: {}", e),
}
match extract_codex_sessions(config) {
Ok(entries) => all.extend(entries),
Err(e) => eprintln!("Codex sessions extraction warning: {}", e),
}
all.sort_by_key(|a| a.timestamp);
let mut seen: HashSet<(i64, String)> = HashSet::new();
all.retain(|entry| {
let key_msg: String = format!(
"{}:{}",
entry.frame_kind.map(FrameKind::as_str).unwrap_or("unknown"),
entry.message.chars().take(100).collect::<String>()
);
let key = (entry.timestamp.timestamp(), key_msg);
seen.insert(key)
});
Ok(all)
}
#[cfg(test)]
pub(crate) use providers::claude::parse_claude_jsonl_with_diagnostics;
#[cfg(test)]
pub(crate) use providers::codex::{
CodexSessionDiagnostics, CodexSessionWarning, parse_codex_file_with_diagnostics,
parse_codex_session_file_with_diagnostics,
};
#[cfg(test)]
pub(crate) use providers::gemini::{
GeminiMessage, GeminiSession, parse_gemini_jsonl_session,
parse_gemini_session_with_diagnostics, render_gemini_content_value,
render_gemini_message_content,
};
#[cfg(test)]
pub(crate) use providers::junie::junie_session_id_from_path_with_warning;
#[cfg(test)]
pub(crate) use providers::operator_markdown::resolve_operator_cwd_hint;
#[cfg(test)]
pub(crate) use shared::project::is_windows_absolute_path;
#[cfg(test)]
mod tests;
#[cfg(test)]
mod conversation_tests;