Skip to main content

eval_magic/adapters/claude_code/
mod.rs

1//! Claude Code harness support — the default harness.
2//!
3//! The declarative half of this harness lives in `harnesses/claude-code.toml`
4//! (including the write-guard data rendered by the generic engine in
5//! [`crate::adapters::guard`]); this module tree keeps only the code-backed
6//! capabilities the descriptor references: `claude -p` stream-json transcript
7//! parsing ([`stream_json`] + [`transcript`]) and plugin-shadow detection
8//! ([`plugin_shadow`]).
9
10pub mod plugin_shadow;
11pub mod stream_json;
12pub mod transcript;
13
14#[cfg(test)]
15mod tests {
16    use crate::adapters::adapter_for;
17    use crate::core::Harness;
18
19    #[test]
20    fn claude_adapter_advertises_cli_events_file_and_model_flag() {
21        let a = adapter_for(Harness::resolve("claude-code").unwrap());
22        assert_eq!(
23            a.cli_events_filename().as_deref(),
24            Some("claude-events.jsonl")
25        );
26        assert_eq!(a.cli_model_flag().as_deref(), Some("--model"));
27    }
28
29    #[test]
30    fn claude_parse_cli_events_full_reads_stream_json_result_event() {
31        use serde_json::json;
32        let dir = tempfile::TempDir::new().unwrap();
33        let path = dir.path().join("claude-events.jsonl");
34        // No per-line timestamps; the result event is the only source of duration.
35        let lines = [
36            json!({"type": "assistant", "message": {"id": "msg_1", "role": "assistant", "content": [
37                {"type": "tool_use", "id": "toolu_1", "name": "Bash", "input": {"command": "ls"}}
38            ]}}),
39            json!({"type": "result", "subtype": "success", "is_error": false, "result": "Done", "duration_ms": 5637, "usage": {"input_tokens": 1, "output_tokens": 2, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0}}),
40        ];
41        let body = lines
42            .iter()
43            .map(|l| l.to_string())
44            .collect::<Vec<_>>()
45            .join("\n");
46        std::fs::write(&path, format!("{body}\n")).unwrap();
47
48        let a = adapter_for(Harness::resolve("claude-code").unwrap());
49        let summary = a.parse_cli_events_full(&path).unwrap();
50        assert_eq!(summary.final_text, Some("Done".into()));
51        assert_eq!(summary.duration_ms, Some(5637));
52        assert_eq!(summary.tool_invocations.len(), 1);
53        assert_eq!(summary.tool_invocations[0].name, "Bash");
54    }
55}