llmgrep 3.11.2

Smart grep over Magellan code maps with schema-aligned JSON output
Documentation
//! Opt-in telemetry for llmgrep usage tracking.
//!
//! Records command invocations to a local SQLite database at
//! `~/.magellan/llmgrep-telemetry.db`. Only writes when explicitly
//! enabled via `LLMGREP_TELEMETRY=1` environment variable or `--record`
//! CLI flag. No data is sent anywhere — this is purely local.

use rusqlite::Connection;
use std::path::PathBuf;
use std::time::Instant;

#[derive(Debug)]
pub struct TelemetryGuard {
    db_path: PathBuf,
    command: String,
    start: Instant,
    enabled: bool,
}

impl TelemetryGuard {
    pub fn new(command: &str) -> Self {
        let enabled = std::env::var("LLMGREP_TELEMETRY")
            .map(|v| v == "1")
            .unwrap_or(false);

        let db_path = dirs_magellan().join("llmgrep-telemetry.db");

        Self {
            db_path,
            command: command.to_string(),
            start: Instant::now(),
            enabled,
        }
    }

    pub fn with_record(mut self) -> Self {
        self.enabled = true;
        self
    }

    pub fn record(&self, status: &str, result_count: usize) {
        if !self.enabled {
            return;
        }

        if let Err(e) = self.record_inner(status, result_count) {
            eprintln!("Note: telemetry write failed: {}", e);
        }
    }

    fn record_inner(
        &self,
        status: &str,
        result_count: usize,
    ) -> Result<(), Box<dyn std::error::Error>> {
        if let Some(parent) = self.db_path.parent() {
            std::fs::create_dir_all(parent)?;
        }

        let conn = Connection::open(&self.db_path)?;

        conn.execute_batch(
            "CREATE TABLE IF NOT EXISTS telemetry (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp INTEGER NOT NULL,
                command TEXT NOT NULL,
                status TEXT NOT NULL,
                duration_ms INTEGER NOT NULL,
                result_count INTEGER NOT NULL
            );
            CREATE INDEX IF NOT EXISTS idx_telemetry_command ON telemetry(command);
            CREATE INDEX IF NOT EXISTS idx_telemetry_timestamp ON telemetry(timestamp);",
        )?;

        let duration_ms = self.start.elapsed().as_millis() as i64;
        let timestamp = chrono::Utc::now().timestamp();

        conn.execute(
            "INSERT INTO telemetry (timestamp, command, status, duration_ms, result_count) \
             VALUES (?1, ?2, ?3, ?4, ?5)",
            rusqlite::params![
                timestamp,
                self.command,
                status,
                duration_ms,
                result_count as i64
            ],
        )?;

        Ok(())
    }
}

fn dirs_magellan() -> PathBuf {
    if let Ok(home) = std::env::var("HOME") {
        PathBuf::from(home).join(".magellan")
    } else {
        PathBuf::from("/tmp/.magellan")
    }
}