xbp 10.39.0

XBP is a zero-config build pack that can also interact with proxies, kafka, sockets, synthetic monitors.
use crate::cli::commands;
use crate::cli::error::{CliResult, ErrorFactory};
use crate::commands::{
    is_tray_background_child, run_worktree_watch_start, run_worktree_watch_status,
    run_worktree_watch_stop, run_worktree_watch_sync, run_worktree_watch_tray,
    spawn_detached_worktree_watch_tray, WorktreeWatchStartOptions, WorktreeWatchStatusOptions,
    WorktreeWatchStopOptions, WorktreeWatchSyncOptions, WorktreeWatchTargetOptions,
    WorktreeWatchTrayOptions,
};

fn map_target(target: commands::WorktreeWatchTarget) -> Result<WorktreeWatchTargetOptions, String> {
    crate::commands::normalize_worktree_watch_target(WorktreeWatchTargetOptions {
        repo: target.repo,
        parent: target.parent,
        repos: target.repos,
    })
}

pub async fn handle_worktree_watch(cmd: commands::WorktreeWatchCmd, _debug: bool) -> CliResult<()> {
    let result = match cmd.command {
        commands::WorktreeWatchSubCommand::Start(start_cmd) => match map_target(start_cmd.target)
        {
            Ok(target) => {
                run_worktree_watch_start(WorktreeWatchStartOptions {
                    target,
                    detach: start_cmd.detach,
                    sync_interval_seconds: start_cmd.sync_interval_seconds,
                    once: start_cmd.once,
                })
                .await
            }
            Err(error) => Err(error),
        },
        commands::WorktreeWatchSubCommand::Stop(stop_cmd) => match map_target(stop_cmd.target) {
            Ok(target) => run_worktree_watch_stop(WorktreeWatchStopOptions {
                target,
                force: stop_cmd.force,
            }),
            Err(error) => Err(error),
        },
        commands::WorktreeWatchSubCommand::Sync(sync_cmd) => match map_target(sync_cmd.target) {
            Ok(target) => {
                run_worktree_watch_sync(WorktreeWatchSyncOptions {
                    target,
                    dry_run: sync_cmd.dry_run,
                    resync: sync_cmd.resync,
                })
                .await
            }
            Err(error) => Err(error),
        },
        commands::WorktreeWatchSubCommand::Status(status_cmd) => {
            match map_target(status_cmd.target) {
                Ok(target) => {
                    run_worktree_watch_status(WorktreeWatchStatusOptions {
                        target,
                        json: status_cmd.json,
                        records: status_cmd.records,
                        record_limit: status_cmd.record_limit,
                        stats: status_cmd.stats,
                        repo_activity: status_cmd.repo_activity,
                        stats_gap_minutes: status_cmd.stats_gap_minutes,
                    })
                    .await
                }
                Err(error) => Err(error),
            }
        }
        commands::WorktreeWatchSubCommand::Tray(tray_cmd) => match map_target(tray_cmd.target) {
            Ok(target) => {
                let options = WorktreeWatchTrayOptions {
                    target,
                    sync_interval_seconds: tray_cmd.sync_interval_seconds,
                };

                if !tray_cmd.foreground && !is_tray_background_child() {
                    spawn_detached_worktree_watch_tray(options)
                } else {
                    // Tray owns a blocking native event loop (Windows NotifyIcon / Linux StatusNotifier).
                    tokio::task::spawn_blocking(move || run_worktree_watch_tray(options))
                        .await
                        .map_err(|error| format!("Worktree-watch tray task failed: {error}"))
                        .and_then(|inner| inner)
                }
            }
            Err(error) => Err(error),
        },
    };

    result.map_err(|error| {
        ErrorFactory::operation(
            "worktree-watch",
            "process worktree mutations",
            error,
            Some(
                "Run inside a git repository, pass `--repo <path|name|owner/repo>`, `--repos …`, or `--parent <folder>`. Names resolve via `~/.xbp/config.yaml` system inventory (`xbp diag --refresh-system-inventory`). Use `xbp login` before syncing.",
            ),
        )
    })
}