Skip to main content

smc/cmd/
tools.rs

1/// smc tools — list tool calls in a session.
2use std::io::Write;
3
4use anyhow::Result;
5use serde::Serialize;
6
7use crate::output::Emitter;
8use crate::util::discover::SessionFile;
9
10// ── Opts ───────────────────────────────────────────────────────────────────
11
12pub struct ToolsOpts {
13    pub session: String,
14    pub max_tokens: usize,
15}
16
17// ── Records ────────────────────────────────────────────────────────────────
18
19#[derive(Serialize, Debug)]
20struct ToolRecord {
21    #[serde(rename = "type")]
22    record_type: &'static str,
23    #[serde(skip_serializing_if = "Option::is_none")]
24    timestamp: Option<String>,
25    role: String,
26    tool_name: String,
27    input_preview: String,
28}
29
30// ── run ────────────────────────────────────────────────────────────────────
31
32pub fn run<W: Write>(_opts: &ToolsOpts, file: &SessionFile, em: &mut Emitter<W>) -> Result<()> {
33    let records = crate::cmd::parse_records(file)?;
34    let start = std::time::Instant::now();
35
36    let mut count = 0usize;
37    'outer: for record in &records {
38        let Some(msg) = record.as_message() else { continue };
39
40        let tools = msg.tool_names();
41        if tools.is_empty() {
42            continue;
43        }
44
45        if let crate::models::MessageContent::Blocks(blocks) = &msg.message.content {
46            for block in blocks {
47                if let crate::models::ContentBlock::ToolUse { name, input, .. } = block {
48                    let preview: String = input.to_string().chars().take(200).collect();
49                    let rec = ToolRecord {
50                        record_type: "tool_call",
51                        timestamp: msg.timestamp.clone(),
52                        role: record.role().to_string(),
53                        tool_name: name.clone(),
54                        input_preview: preview,
55                    };
56                    if !em.emit(&rec)? {
57                        break 'outer;
58                    }
59                    count += 1;
60                }
61            }
62        }
63    }
64
65    let summary = crate::output::SummaryRecord {
66        record_type: "summary",
67        count,
68        files_scanned: None,
69        elapsed_ms: start.elapsed().as_millis(),
70    };
71    em.emit(&summary)?;
72
73    em.flush()?;
74    Ok(())
75}