Skip to main content

lean_ctx/core/import/
claude_code.rs

1//! Claude Code transcript import.
2
3use std::path::{Path, PathBuf};
4
5use super::{ImportResult, ImportSource, home_dir, push_error, read_jsonl_files};
6
7/// Imports Claude Code project histories from `~/.claude/projects`.
8#[must_use]
9pub fn import() -> ImportResult {
10    let Some(home) = home_dir() else {
11        return ImportResult::default();
12    };
13    import_from_root(&home.join(".claude/projects"))
14}
15
16/// Imports JSONL histories below a Claude Code projects directory.
17///
18/// This is public to keep filesystem discovery separate from transcript parsing.
19#[must_use]
20pub fn import_from_root(root: &Path) -> ImportResult {
21    let projects = match std::fs::read_dir(root) {
22        Ok(projects) => projects,
23        Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
24            return ImportResult::default();
25        }
26        Err(error) => {
27            let mut result = ImportResult::default();
28            push_error(&mut result, format!("{}: {error}", root.display()));
29            return result;
30        }
31    };
32
33    let mut files = Vec::new();
34    for project in projects {
35        let project = match project {
36            Ok(project) => project,
37            Err(error) => {
38                let mut result = ImportResult::default();
39                push_error(&mut result, format!("{}: {error}", root.display()));
40                return result;
41            }
42        };
43        let path = project.path();
44        if !path.is_dir() {
45            continue;
46        }
47        match std::fs::read_dir(&path) {
48            Ok(entries) => collect_jsonl_files(entries, &mut files),
49            Err(error) => {
50                let mut result = read_jsonl_files(ImportSource::ClaudeCode, root, files);
51                push_error(&mut result, format!("{}: {error}", path.display()));
52                return result;
53            }
54        }
55    }
56    files.sort();
57    read_jsonl_files(ImportSource::ClaudeCode, root, files)
58}
59
60fn collect_jsonl_files(entries: std::fs::ReadDir, files: &mut Vec<PathBuf>) {
61    for entry in entries.flatten() {
62        let path = entry.path();
63        if path.is_file()
64            && path
65                .extension()
66                .is_some_and(|extension| extension == "jsonl")
67        {
68            files.push(path);
69        }
70    }
71}