tracevault_cli/hooks/
claude_code.rs1use std::path::Path;
2use tracevault_core::streaming::StreamEventRequest;
3
4use super::HookAdapter;
5
6pub struct ClaudeCodeAdapter;
7
8impl HookAdapter for ClaudeCodeAdapter {
9 fn tool_name(&self) -> &str {
10 "claude-code"
11 }
12
13 fn parse_event(&self, raw: &str) -> Result<StreamEventRequest, String> {
14 let mut req: StreamEventRequest =
15 serde_json::from_str(raw).map_err(|e| format!("Failed to parse event JSON: {e}"))?;
16 req.tool = Some("claude-code".to_string());
17 if req.protocol_version == 0 {
18 req.protocol_version = 2;
19 }
20 Ok(req)
21 }
22
23 fn parse_transcript(&self, path: &Path) -> Result<Vec<serde_json::Value>, String> {
24 let content =
25 std::fs::read_to_string(path).map_err(|e| format!("Failed to read transcript: {e}"))?;
26 let mut lines = Vec::new();
27 for line in content.lines() {
28 if line.trim().is_empty() {
29 continue;
30 }
31 match serde_json::from_str(line) {
32 Ok(v) => lines.push(v),
33 Err(_) => continue,
34 }
35 }
36 Ok(lines)
37 }
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43
44 #[test]
45 fn parse_event_sets_tool_name() {
46 let adapter = ClaudeCodeAdapter;
47 let json = serde_json::json!({
48 "protocol_version": 2,
49 "event_type": "tool_use",
50 "session_id": "sess-1",
51 "timestamp": "2026-01-01T00:00:00Z"
52 });
53 let result = adapter
54 .parse_event(&serde_json::to_string(&json).unwrap())
55 .unwrap();
56 assert_eq!(result.tool.as_deref(), Some("claude-code"));
57 }
58
59 #[test]
60 fn parse_event_upgrades_version_0() {
61 let adapter = ClaudeCodeAdapter;
62 let json = serde_json::json!({
63 "protocol_version": 0,
64 "event_type": "tool_use",
65 "session_id": "sess-1",
66 "timestamp": "2026-01-01T00:00:00Z"
67 });
68 let result = adapter
69 .parse_event(&serde_json::to_string(&json).unwrap())
70 .unwrap();
71 assert_eq!(result.protocol_version, 2);
72 }
73}