Parse a Claude Code JSONL transcript into condensed entries. Reads each line as JSON, extracts user prompts, assistant text responses, and tool calls. Filters out: - Skill content injections (verbose skill instructions in user messages) - Full file contents from Read tool responses - Verbose tool outputs # Arguments `raw` — Raw JSONL bytes from the Claude Code transcript file # Returns A vect
(raw: &[u8])
| 30 | /// A vector of condensed entries suitable for display and summarization. |
| 31 | /// Returns an empty vector if the transcript is empty or unparseable. |
| 32 | pub fn condense_claude_transcript(raw: &[u8]) -> Vec<CondensedEntry> { |
| 33 | let mut entries = Vec::new(); |
| 34 | |
| 35 | for line in raw.split(|&b| b == b'\n') { |
| 36 | if line.is_empty() { |
| 37 | continue; |
| 38 | } |
| 39 | |
| 40 | let Ok(parsed) = serde_json::from_slice::<TranscriptLine>(line) else { |
| 41 | continue; |
| 42 | }; |
| 43 | |
| 44 | match parsed.r#type.as_str() { |
| 45 | "user" => { |
| 46 | if let Some(content) = extract_user_content(&parsed.message) { |
| 47 | // Skip skill content injections |
| 48 | if !content.starts_with(SKILL_CONTENT_PREFIX) { |
| 49 | entries.push(CondensedEntry::user(content)); |
| 50 | } |
| 51 | } |
| 52 | } |
| 53 | "assistant" => { |
| 54 | if let Ok(msg) = serde_json::from_value::<AssistantMessage>(parsed.message) { |
| 55 | for block in &msg.content { |
| 56 | match block.r#type.as_str() { |
| 57 | "text" if !block.text.is_empty() => { |
| 58 | entries.push(CondensedEntry::assistant(&block.text)); |
| 59 | } |
| 60 | "tool_use" => { |
| 61 | let detail = extract_tool_detail(&block.name, &block.input); |
| 62 | entries.push(CondensedEntry::tool(&block.name, detail)); |
| 63 | } |
| 64 | _ => {} |
| 65 | } |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | _ => {} |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | entries |
| 74 | } |
| 75 | |
| 76 | /// Condense a transcript from raw bytes, auto-detecting format. |
| 77 | /// |