agtrace_runtime/client/
insights.rs

1use crate::ops::{
2    CorpusStats, IndexProgress, IndexService, StatsResult, collect_tool_stats, get_corpus_overview,
3};
4use agtrace_index::Database;
5use agtrace_providers::{ProviderAdapter, ScanContext};
6use agtrace_types::{discover_project_root, project_hash_from_root};
7use anyhow::Result;
8use std::path::PathBuf;
9use std::sync::{Arc, Mutex};
10
11pub struct InsightOps {
12    db: Arc<Mutex<Database>>,
13    provider_configs: Arc<Vec<(String, PathBuf)>>,
14}
15
16impl InsightOps {
17    pub fn new(db: Arc<Mutex<Database>>, provider_configs: Arc<Vec<(String, PathBuf)>>) -> Self {
18        Self {
19            db,
20            provider_configs,
21        }
22    }
23
24    pub fn corpus_stats(&self, project_hash: Option<&str>, limit: usize) -> Result<CorpusStats> {
25        self.ensure_index_is_fresh()?;
26
27        let db = self.db.lock().unwrap();
28        get_corpus_overview(&db, project_hash, limit)
29    }
30
31    pub fn tool_usage(
32        &self,
33        limit: Option<usize>,
34        provider: Option<String>,
35    ) -> Result<StatsResult> {
36        self.ensure_index_is_fresh()?;
37
38        let db = self.db.lock().unwrap();
39        collect_tool_stats(&db, limit, provider)
40    }
41
42    fn ensure_index_is_fresh(&self) -> Result<()> {
43        let db = self.db.lock().unwrap();
44
45        let providers: Vec<(ProviderAdapter, PathBuf)> = self
46            .provider_configs
47            .iter()
48            .filter_map(|(name, path)| {
49                agtrace_providers::create_adapter(name)
50                    .ok()
51                    .map(|p| (p, path.clone()))
52            })
53            .collect();
54
55        let service = IndexService::new(&db, providers);
56
57        // Scan all projects: project_root=None means no filtering
58        // project_hash is only used for reporting and as fallback
59        let project_hash = discover_project_root(None)
60            .ok()
61            .map(|root| project_hash_from_root(&root.display().to_string()))
62            .unwrap_or_else(|| "unknown".to_string());
63
64        let scan_context = ScanContext {
65            project_hash,
66            project_root: None,
67            provider_filter: None,
68        };
69
70        service.run(&scan_context, false, |_progress: IndexProgress| {})?;
71
72        Ok(())
73    }
74}