Skip to main content

sqry_cli/commands/
watch.rs

1//! Watch mode command for real-time index updates
2
3use crate::args::Cli;
4use crate::commands::index::{create_build_config, create_progress_reporter};
5use crate::plugin_defaults::{self, PluginSelectionMode};
6use anyhow::{Context, Result};
7use sqry_core::graph::unified::persistence::GraphStorage;
8use sqry_core::watch::FileWatcher;
9use std::path::PathBuf;
10use std::time::Duration;
11
12/// Execute the watch command.
13///
14/// # Errors
15/// Returns an error if the index cannot be loaded or watch mode fails.
16pub fn execute(
17    cli: &Cli,
18    path: Option<String>,
19    threads: Option<usize>,
20    debounce: Option<u64>,
21    _show_stats: bool, // Detailed stats not supported for unified graph yet
22    build_if_missing: bool,
23) -> Result<()> {
24    let root_path = resolve_path(path)?;
25    let build_config = create_build_config(cli, &root_path, threads)?;
26
27    // Check if graph exists
28    let storage = GraphStorage::new(&root_path);
29    if !storage.exists() {
30        if build_if_missing {
31            println!("🔨 Building initial graph...");
32            let (_, progress) = create_progress_reporter(cli);
33            let resolved_plugins = plugin_defaults::resolve_plugin_selection(
34                cli,
35                &root_path,
36                PluginSelectionMode::FreshWrite,
37            )?;
38            sqry_core::graph::unified::build::build_and_persist_graph_with_progress(
39                &root_path,
40                &resolved_plugins.plugin_manager,
41                &build_config,
42                "cli:watch",
43                resolved_plugins.persisted_selection.clone(),
44                progress,
45            )?;
46        } else {
47            anyhow::bail!(
48                "No index found at {}. Use --build to create one, or run 'sqry index' first.",
49                root_path.display()
50            );
51        }
52    }
53
54    // Create watcher
55    let watcher = FileWatcher::new(&root_path)?;
56    let debounce_duration = debounce.map_or(Duration::from_millis(500), Duration::from_millis);
57
58    println!("🔍 Watch mode started");
59    println!("📂 Monitoring: {}", root_path.display());
60    println!("⏱️  Debounce: {}ms", debounce_duration.as_millis());
61    println!();
62    println!("Press Ctrl+C to stop...");
63    println!();
64
65    let (_, progress) = create_progress_reporter(cli);
66
67    loop {
68        // Wait for changes
69        let changes = watcher.wait_with_debounce(debounce_duration)?;
70
71        if changes.is_empty() {
72            continue;
73        }
74
75        println!(
76            "📝 Detected {} file changes, updating graph...",
77            changes.len()
78        );
79        let start = std::time::Instant::now();
80
81        // Full rebuild using consolidated pipeline
82        let resolved_plugins = plugin_defaults::resolve_plugin_selection(
83            cli,
84            &root_path,
85            PluginSelectionMode::ExistingWrite,
86        )?;
87        match sqry_core::graph::unified::build::build_and_persist_graph_with_progress(
88            &root_path,
89            &resolved_plugins.plugin_manager,
90            &build_config,
91            "cli:watch",
92            resolved_plugins.persisted_selection.clone(),
93            progress.clone(),
94        ) {
95            Ok((_graph, _build_result)) => {
96                println!("✓ Graph updated in {:.2}s", start.elapsed().as_secs_f64());
97            }
98            Err(e) => {
99                eprintln!("❌ Error updating graph: {e}");
100            }
101        }
102        println!();
103    }
104}
105
106/// Resolve path argument to absolute `PathBuf`
107fn resolve_path(path: Option<String>) -> Result<PathBuf> {
108    let path_str = path.unwrap_or_else(|| ".".to_string());
109    let path = PathBuf::from(path_str);
110
111    if path.exists() {
112        path.canonicalize().context("Failed to resolve path")
113    } else {
114        anyhow::bail!("Path does not exist: {}", path.display());
115    }
116}