#![deny(clippy::print_stdout, clippy::print_stderr)]

use std::borrow::Cow;
use std::ffi::OsString;
use std::fmt::Write;
use std::io::stdout;
#[cfg(feature = "self-update")]
use std::ops::Bound;
use std::path::Path;
use std::process::ExitCode;
use std::str::FromStr;
use std::sync::atomic::Ordering;

use anstream::eprintln;
use anyhow::{Result, anyhow, bail};
use clap::error::{ContextKind, ContextValue};
use clap::{CommandFactory, Parser};
use futures::FutureExt;
use owo_colors::OwoColorize;
use settings::PipTreeSettings;
use tokio::task::spawn_blocking;
use tracing::{debug, instrument, trace};

#[cfg(not(feature = "self-update"))]
use crate::install_source::InstallSource;
use uv_cache::{Cache, Refresh};
use uv_cache_info::Timestamp;
#[cfg(feature = "self-update")]
use uv_cli::SelfUpdateArgs;
use uv_cli::{
    AuthCommand, AuthHelperCommand, AuthNamespace, BuildBackendCommand, CacheCommand,
    CacheNamespace, Cli, Commands, PipCommand, PipNamespace, ProjectCommand, PythonCommand,
    PythonNamespace, SelfCommand, SelfNamespace, ToolCommand, ToolNamespace, TopLevelArgs,
    WorkspaceCommand, WorkspaceNamespace, compat::CompatArgs,
};
use uv_client::BaseClientBuilder;
use uv_configuration::min_stack_size;
use uv_flags::EnvironmentFlags;
use uv_fs::{CWD, Simplified, normalize_path};
#[cfg(feature = "self-update")]
use uv_pep440::release_specifiers_to_ranges;
use uv_pep508::VersionOrUrl;
use uv_preview::{Preview, PreviewFeature};
use uv_pypi_types::{ParsedDirectoryUrl, ParsedUrl};
use uv_python::PythonRequest;
use uv_requirements::{GroupsSpecification, RequirementsSource};
use uv_requirements_txt::RequirementsTxtRequirement;
use uv_scripts::{Pep723Error, Pep723Item, Pep723Script};
use uv_settings::{Combine, EnvironmentOptions, FilesystemOptions, Options};
use uv_static::EnvVars;
use uv_warnings::{warn_user, warn_user_once};
use uv_workspace::{DiscoveryOptions, Workspace, WorkspaceCache};

use crate::commands::{ExitStatus, ParsedRunCommand, RunCommand, ScriptPath, ToolRunCommand};
use crate::printer::Printer;
use crate::settings::{
    CacheSettings, GlobalSettings, PipCheckSettings, PipCompileSettings, PipFreezeSettings,
    PipInstallSettings, PipListSettings, PipShowSettings, PipSyncSettings, PipUninstallSettings,
    PublishSettings, resolve_color,
};

pub(crate) mod child;
pub(crate) mod commands;
#[cfg(not(feature = "self-update"))]
mod install_source;
pub(crate) mod logging;
pub(crate) mod printer;
pub(crate) mod settings;

/// uv was installed through an external package manager and cannot update itself.
#[cfg(not(feature = "self-update"))]
#[derive(Debug, thiserror::Error)]
#[error("uv was installed through an external package manager and cannot update itself.")]
struct ExternallyInstalledError {
    install_source: Option<InstallSource>,
}

#[cfg(not(feature = "self-update"))]
impl uv_errors::Hint for ExternallyInstalledError {
    fn hints(&self) -> uv_errors::Hints<'_> {
        if let Some(source) = &self.install_source {
            uv_errors::Hints::from(format!(
                "You installed uv using {}. To update uv, run `{}`",
                source.description(),
                source.update_instructions(),
            ))
        } else {
            uv_errors::Hints::from("Please use your package manager to update uv")
        }
    }
}
#[instrument(skip_all)]
async fn run(cli: Cli) -> Result<ExitStatus> {
    // Enable flag to pick up warnings generated by workspace loading.
    if cli.top_level.global_args.quiet == 0 {
        uv_warnings::enable();
    }

    // Respect `UV_WORKING_DIRECTORY` for backwards compatibility.
    let directory =
        cli.top_level.global_args.directory.clone().or_else(|| {
            std::env::var_os(EnvVars::UV_WORKING_DIRECTORY).map(std::path::PathBuf::from)
        });

    // Switch directories as early as possible.
    if let Some(directory) = directory.as_ref() {
        std::env::set_current_dir(directory)?;
    }

    // Parse the external command, if necessary.
    let parsed_run_command = if let Commands::Project(command) = &*cli.command
        && let ProjectCommand::Run(uv_cli::RunArgs {
            command: Some(ref command),
            module,
            script,
            gui_script,
            ..
        }) = **command
    {
        Some(ParsedRunCommand::from_args(
            command, module, script, gui_script,
        )?)
    } else {
        None
    };

    // Load environment variables not handled by Clap.
    let environment = EnvironmentOptions::new()?;

    // Resolve preview flags before config discovery for decisions that affect the discovery root.
    let early_preview = Preview::from_args(
        settings::resolve_preview(&cli.top_level.global_args, None, &environment),
        cli.top_level.global_args.no_preview,
        &cli.top_level.global_args.preview_features,
    );

    // Make the early preview flags globally available.
    uv_preview::set(early_preview)?;

    // Configure the `tracing` crate, which controls internal logging.
    #[cfg(feature = "tracing-durations-export")]
    let (durations_layer, _duration_guard) =
        logging::setup_durations(environment.tracing_durations_file.as_ref())?;
    #[cfg(not(feature = "tracing-durations-export"))]
    let durations_layer = None::<tracing_subscriber::layer::Identity>;
    logging::setup_logging(
        match cli.top_level.global_args.verbose {
            0 => logging::Level::Off,
            1 => logging::Level::DebugUv,
            2 => logging::Level::TraceUv,
            3.. => logging::Level::TraceAll,
        },
        durations_layer,
        resolve_color(&cli.top_level.global_args),
        environment.log_context.unwrap_or_default(),
    )?;

    // Determine the project directory.
    //
    // If `--project` points to a `pyproject.toml` file, resolve to its parent directory,
    // since downstream code (e.g., `FilesystemOptions::find`) expects a directory.
    let project_dir: Cow<'_, Path> = if let Some(project) = &cli.top_level.global_args.project {
        let path = normalize_path(std::path::absolute(project)?);
        if let Some(name) = path.file_name()
            && name == "pyproject.toml"
            && path.is_file()
            && let Some(parent) = path.parent()
        {
            Cow::Owned(parent.to_path_buf())
        } else {
            path
        }
    } else if let Some(run_command) = &parsed_run_command
        && early_preview.is_enabled(PreviewFeature::TargetWorkspaceDiscovery)
        && let Some(dir) = run_command.script_dir()
    {
        // When running a target with the preview flag enabled, discover the workspace starting
        // from the target's directory rather than the current working directory.
        Cow::Owned(std::path::absolute(dir)?)
    } else {
        Cow::Borrowed(&*CWD)
    };

    // Validate that the project directory exists if explicitly provided via --project, except for
    // `uv init`, which creates the project directory (separate deprecation).
    let skip_project_validation = matches!(
        &*cli.command,
        Commands::Project(command) if matches!(**command, ProjectCommand::Init(_))
    );

    if !skip_project_validation {
        if let Some(project_path) = cli.top_level.global_args.project.as_ref() {
            if !project_dir.exists() {
                if early_preview.is_enabled(PreviewFeature::ProjectDirectoryMustExist) {
                    bail!(
                        "Project directory `{}` does not exist",
                        project_path.user_display()
                    );
                }
                warn_user_once!(
                    "Project directory `{}` does not exist. \
                    This will become an error in a future release. \
                    Use `--preview-features project-directory-must-exist` to error on this now.",
                    project_path.user_display()
                );
            } else if !project_dir.is_dir() {
                // `--project path/to/pyproject.toml` is resolved to its parent above,
                // so this only triggers for other file types (see #18508).
                if early_preview.is_enabled(PreviewFeature::ProjectDirectoryMustExist) {
                    bail!(
                        "Project path `{}` is not a directory",
                        project_path.user_display()
                    );
                }
                warn_user_once!(
                    "Project path `{}` is not a directory. \
                    This will become an error in a future release. \
                    Use `--preview-features project-directory-must-exist` to error on this now.",
                    project_path.user_display()
                );
            }
        }
    }

    // The `--isolated` argument is deprecated on preview APIs, and warns on non-preview APIs.
    let deprecated_isolated = if cli.top_level.global_args.isolated {
        match &*cli.command {
            // Supports `--isolated` as its own argument, so we can't warn either way.
            Commands::Tool(ToolNamespace {
                command: ToolCommand::Uvx(_) | ToolCommand::Run(_),
            }) => false,

            // Supports `--isolated` as its own argument, so we can't warn either way.
            Commands::Project(command) if matches!(**command, ProjectCommand::Run(_)) => false,

            // `--isolated` moved to `--no-workspace`.
            Commands::Project(command) if matches!(**command, ProjectCommand::Init(_)) => {
                warn_user!(
                    "The `--isolated` flag is deprecated and has no effect. Instead, use `--no-config` to prevent uv from discovering configuration files or `--no-workspace` to prevent uv from adding the initialized project to the containing workspace."
                );
                false
            }

            // Preview APIs. Ignore `--isolated` and warn.
            Commands::Project(_) | Commands::Tool(_) | Commands::Python(_) => {
                warn_user!(
                    "The `--isolated` flag is deprecated and has no effect. Instead, use `--no-config` to prevent uv from discovering configuration files."
                );
                false
            }

            // Non-preview APIs. Continue to support `--isolated`, but warn.
            _ => {
                warn_user!(
                    "The `--isolated` flag is deprecated. Instead, use `--no-config` to prevent uv from discovering configuration files."
                );
                true
            }
        }
    } else {
        false
    };

    // Load configuration from the filesystem, prioritizing (in order):
    // 1. The configuration file specified on the command-line.
    // 2. The nearest configuration file (`uv.toml` or `pyproject.toml`) above the workspace root.
    //    If found, this file is combined with the user configuration file.
    // 3. The nearest configuration file (`uv.toml` or `pyproject.toml`) in the directory tree,
    //    starting from the current directory.
    let workspace_cache = WorkspaceCache::default();
    let filesystem = if let Some(config_file) = cli.top_level.config_file.as_ref() {
        if config_file
            .file_name()
            .is_some_and(|file_name| file_name == "pyproject.toml")
        {
            warn_user!(
                "The `--config-file` argument expects to receive a `uv.toml` file, not a `pyproject.toml`. If you're trying to run a command from another project, use the `--project` argument instead."
            );
        }
        Some(FilesystemOptions::from_file(config_file).map_err(map_settings_error)?)
    } else if deprecated_isolated || cli.top_level.no_config {
        None
    } else if matches!(&*cli.command, Commands::Tool(_) | Commands::Self_(_)) {
        // For commands that operate at the user-level, ignore local configuration.
        FilesystemOptions::user()
            .map_err(map_settings_error)?
            .combine(FilesystemOptions::system().map_err(map_settings_error)?)
    } else if let Ok(workspace) =
        Workspace::discover(&project_dir, &DiscoveryOptions::default(), &workspace_cache).await
    {
        let project =
            FilesystemOptions::find(workspace.install_path()).map_err(map_settings_error)?;
        let system = FilesystemOptions::system().map_err(map_settings_error)?;
        let user = FilesystemOptions::user().map_err(map_settings_error)?;
        project.combine(user).combine(system)
    } else {
        let project = FilesystemOptions::find(&project_dir).map_err(map_settings_error)?;
        let system = FilesystemOptions::system().map_err(map_settings_error)?;
        let user = FilesystemOptions::user().map_err(map_settings_error)?;
        project.combine(user).combine(system)
    };

    // If the target is a remote script, download it.
    // If the target is a PEP 723 script, parse it.
    let (run_script, run_command) = if let Some(parsed_run_command) = parsed_run_command {
        let (script, run_command) = parsed_run_command
            .resolve(
                &cli.top_level.global_args,
                filesystem.as_ref(),
                &environment,
            )
            .await?;
        (script, Some(run_command))
    } else {
        (None, None)
    };
    let script = if let Some(run_script) = run_script {
        Some(run_script)
    } else if let Commands::Project(command) = &*cli.command {
        match &**command {
            // For `uv add --script` and `uv lock --script`, we'll create a PEP 723 tag if it
            // doesn't already exist.
            ProjectCommand::Add(uv_cli::AddArgs {
                script: Some(script),
                ..
            })
            | ProjectCommand::Lock(uv_cli::LockArgs {
                script: Some(script),
                ..
            }) => match Pep723Script::read(script).await {
                Ok(Some(script)) => Some(Pep723Item::Script(script)),
                Ok(None) => None,
                Err(err) => return Err(err.into()),
            },
            // For the remaining commands, the PEP 723 tag must exist already.
            ProjectCommand::Remove(uv_cli::RemoveArgs {
                script: Some(script),
                ..
            })
            | ProjectCommand::Sync(uv_cli::SyncArgs {
                script: Some(script),
                ..
            })
            | ProjectCommand::Tree(uv_cli::TreeArgs {
                script: Some(script),
                ..
            })
            | ProjectCommand::Export(uv_cli::ExportArgs {
                script: Some(script),
                ..
            })
            | ProjectCommand::Audit(uv_cli::AuditArgs {
                script: Some(script),
                ..
            }) => match Pep723Script::read(script).await {
                Ok(Some(script)) => Some(Pep723Item::Script(script)),
                Ok(None) => {
                    bail!(
                        "`{}` does not contain a PEP 723 metadata tag; run `{}` to initialize the script",
                        script.user_display().cyan(),
                        format!("uv init --script {}", script.user_display()).green()
                    )
                }
                Err(Pep723Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => {
                    bail!(
                        "Failed to read `{}` (not found); run `{}` to create a PEP 723 script",
                        script.user_display().cyan(),
                        format!("uv init --script {}", script.user_display()).green()
                    )
                }
                Err(err) => return Err(err.into()),
            },
            _ => None,
        }
    } else if let Commands::Python(uv_cli::PythonNamespace {
        command:
            PythonCommand::Find(uv_cli::PythonFindArgs {
                script: Some(script),
                ..
            }),
    }) = &*cli.command
    {
        match Pep723Script::read(&script).await {
            Ok(Some(script)) => Some(Pep723Item::Script(script)),
            Ok(None) => {
                bail!(
                    "`{}` does not contain a PEP 723 metadata tag; run `{}` to initialize the script",
                    script.user_display().cyan(),
                    format!("uv init --script {}", script.user_display()).green()
                )
            }
            Err(Pep723Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => {
                bail!(
                    "Failed to read `{}` (not found); run `{}` to create a PEP 723 script",
                    script.user_display().cyan(),
                    format!("uv init --script {}", script.user_display()).green()
                )
            }
            Err(err) => return Err(err.into()),
        }
    } else {
        None
    };

    // If the target is a PEP 723 script, merge the metadata into the filesystem metadata.
    let filesystem = script
        .as_ref()
        .map(Pep723Item::metadata)
        .and_then(|metadata| metadata.tool.as_ref())
        .and_then(|tool| tool.uv.as_ref())
        .map(|uv| Options::simple(uv.globals.clone(), uv.top_level.clone()))
        .map(FilesystemOptions::from)
        .combine(filesystem);

    // Resolve the global settings.
    let globals = GlobalSettings::resolve(
        &cli.top_level.global_args,
        filesystem.as_ref(),
        &environment,
    );

    // Set the global flags.
    uv_flags::init(EnvironmentFlags::from(&environment))
        .map_err(|()| anyhow::anyhow!("Flags are already initialized"))?;

    debug!("uv {}", uv_cli::version::uv_self_version());
    if let Some(config_file) = cli.top_level.config_file.as_ref() {
        debug!("Using configuration file: {}", config_file.user_display());
    }
    if globals.preview.all_enabled() {
        debug!("All preview features are enabled");
    } else if globals.preview.any_enabled() {
        debug!(
            "The following preview features are enabled: {}",
            globals.preview
        );
    }

    // Adjust open file limits on Unix if the preview feature is enabled.
    #[cfg(unix)]
    if globals.preview.is_enabled(PreviewFeature::AdjustUlimit) {
        match uv_unix::adjust_open_file_limit() {
            Ok(_) | Err(uv_unix::OpenFileLimitError::AlreadySufficient { .. }) => {}
            // TODO(zanieb): When moving out of preview, consider changing this to a log instead of
            // a warning because it's okay if we fail here.
            Err(err) => warn_user!("{err}"),
        }
    }

    // Resolve the cache settings.
    let cache_settings = CacheSettings::resolve(*cli.top_level.cache_args, filesystem.as_ref());

    // Set and finalize the global preview configuration.
    uv_preview::set(globals.preview)?;
    uv_preview::finalize()?;

    // Enforce the required version.
    if let Some(required_version) = globals.required_version.as_ref() {
        let package_version = uv_pep440::Version::from_str(uv_version::version())?;
        if !required_version.contains(&package_version) {
            return Err(required_version_error(required_version, &package_version));
        }
    }

    // Configure the `Printer`, which controls user-facing output in the CLI.
    let printer = if globals.quiet == 1 {
        Printer::Quiet
    } else if globals.quiet > 1 {
        Printer::Silent
    } else if globals.verbose > 0 {
        Printer::Verbose
    } else if globals.no_progress {
        Printer::NoProgress
    } else {
        Printer::Default
    };

    // Configure the `warn!` macros, which control user-facing warnings in the CLI.
    if globals.quiet > 0 {
        uv_warnings::disable();
    } else {
        uv_warnings::enable();
    }

    anstream::ColorChoice::write_global(globals.color.into());

    miette::set_hook(Box::new(|_| {
        Box::new(
            miette::MietteHandlerOpts::new()
                .break_words(false)
                .word_separator(textwrap::WordSeparator::AsciiSpace)
                .word_splitter(textwrap::WordSplitter::NoHyphenation)
                .wrap_lines(
                    std::env::var(EnvVars::UV_NO_WRAP)
                        .map(|_| false)
                        .unwrap_or(true),
                )
                .build(),
        )
    }))?;

    // Don't initialize the rayon threadpool yet, this is too costly when we're doing a noop sync.
    uv_configuration::RAYON_PARALLELISM.store(globals.concurrency.installs, Ordering::Relaxed);

    // Write out any resolved settings.
    macro_rules! show_settings {
        ($arg:expr) => {
            if globals.show_settings {
                writeln!(printer.stdout(), "{:#?}", $arg)?;
                return Ok(ExitStatus::Success);
            }
        };
        ($arg:expr, false) => {
            if globals.show_settings {
                writeln!(printer.stdout(), "{:#?}", $arg)?;
            }
        };
    }
    show_settings!(globals, false);
    show_settings!(cache_settings, false);

    // Configure the cache.
    if cache_settings.no_cache {
        debug!("Disabling the uv cache due to `--no-cache`");
    }
    let cache = Cache::from_settings(cache_settings.no_cache, cache_settings.cache_dir)?;

    // Configure the global network settings.
    let client_builder = BaseClientBuilder::new(
        globals.network_settings.connectivity,
        globals.network_settings.system_certs,
        globals.network_settings.allow_insecure_host.clone(),
        globals.preview,
        globals.network_settings.read_timeout,
        globals.network_settings.connect_timeout,
        globals.network_settings.retries,
    )
    .http_proxy(globals.network_settings.http_proxy.clone())
    .https_proxy(globals.network_settings.https_proxy.clone())
    .no_proxy(globals.network_settings.no_proxy.clone());

    match *cli.command {
        Commands::Auth(AuthNamespace {
            command: AuthCommand::Login(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::AuthLoginSettings::resolve(args);
            show_settings!(args);

            commands::auth_login(
                args.service,
                args.username,
                args.password,
                args.token,
                client_builder,
                printer,
                globals.preview,
            )
            .await
        }
        Commands::Auth(AuthNamespace {
            command: AuthCommand::Logout(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::AuthLogoutSettings::resolve(args);
            show_settings!(args);

            commands::auth_logout(
                args.service,
                args.username,
                client_builder,
                printer,
                globals.preview,
            )
            .await
        }
        Commands::Auth(AuthNamespace {
            command: AuthCommand::Token(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::AuthTokenSettings::resolve(args);
            show_settings!(args);

            commands::auth_token(
                args.service,
                args.username,
                client_builder,
                printer,
                globals.preview,
            )
            .await
        }
        Commands::Auth(AuthNamespace {
            command: AuthCommand::Dir(args),
        }) => {
            commands::auth_dir(args.service.as_ref(), printer)?;
            Ok(ExitStatus::Success)
        }
        Commands::Auth(AuthNamespace {
            command: AuthCommand::Helper(args),
        }) => {
            use uv_cli::AuthHelperProtocol;

            // Validate protocol (currently only Bazel is supported)
            match args.protocol {
                AuthHelperProtocol::Bazel => {}
            }

            match args.command {
                AuthHelperCommand::Get => {
                    commands::auth_helper(client_builder, globals.preview, printer).await
                }
            }
        }
        Commands::Help(args) => commands::help(
            args.command.unwrap_or_default().as_slice(),
            printer,
            args.no_pager,
        ),
        Commands::Pip(PipNamespace {
            command: PipCommand::Compile(args),
        }) => {
            args.compat_args.validate()?;

            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = PipCompileSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Check for conflicts between offline and refresh.
            globals
                .network_settings
                .check_refresh_conflict(&args.refresh);

            // Initialize the cache.
            let cache = cache.init().await?.with_refresh(
                args.refresh
                    .combine(Refresh::from(args.settings.reinstall.clone()))
                    .combine(Refresh::from(args.settings.upgrade.clone())),
            );

            let requirements = args
                .src_file
                .into_iter()
                .map(RequirementsSource::from_requirements_file)
                .collect::<Result<Vec<_>, _>>()?;
            let constraints = args
                .constraints
                .into_iter()
                .map(RequirementsSource::from_constraints_txt)
                .collect::<Result<Vec<_>, _>>()?;
            let overrides = args
                .overrides
                .into_iter()
                .map(RequirementsSource::from_overrides_txt)
                .collect::<Result<Vec<_>, _>>()?;
            let excludes = args
                .excludes
                .into_iter()
                .map(RequirementsSource::from_requirements_txt)
                .collect::<Result<Vec<_>, _>>()?;
            let build_constraints = args
                .build_constraints
                .into_iter()
                .map(RequirementsSource::from_constraints_txt)
                .collect::<Result<Vec<_>, _>>()?;
            let groups = GroupsSpecification {
                root: project_dir.to_path_buf(),
                groups: args.settings.groups,
            };

            Box::pin(commands::pip_compile(
                &requirements,
                &constraints,
                &overrides,
                &excludes,
                &build_constraints,
                args.constraints_from_workspace,
                args.overrides_from_workspace,
                args.excludes_from_workspace,
                args.build_constraints_from_workspace,
                args.environments,
                args.required_environments,
                args.settings.extras,
                groups,
                args.settings.output_file.as_deref(),
                args.format,
                args.settings.resolution,
                args.settings.prerelease,
                args.settings.fork_strategy,
                args.settings.dependency_mode,
                args.settings.upgrade,
                args.settings.generate_hashes,
                args.settings.no_emit_package,
                args.settings.no_strip_extras,
                args.settings.no_strip_markers,
                !args.settings.no_annotate,
                !args.settings.no_header,
                args.settings.custom_compile_command,
                args.settings.emit_index_url,
                args.settings.emit_find_links,
                args.settings.emit_build_options,
                args.settings.emit_marker_expression,
                args.settings.emit_index_annotation,
                args.settings.index_locations,
                args.settings.index_strategy,
                args.settings.torch_backend,
                args.settings.dependency_metadata,
                args.settings.keyring_provider,
                &client_builder.subcommand(vec!["pip".to_owned(), "compile".to_owned()]),
                args.settings.config_setting,
                args.settings.config_settings_package,
                args.settings.build_isolation.clone(),
                &args.settings.extra_build_dependencies,
                &args.settings.extra_build_variables,
                args.settings.build_options,
                args.settings.install_mirrors,
                args.settings.python_version,
                args.settings.python_platform,
                globals.python_downloads,
                args.settings.universal,
                args.settings.exclude_newer,
                args.settings.sources,
                args.settings.annotation_style,
                args.settings.link_mode,
                args.settings.python,
                args.settings.system,
                globals.python_preference,
                globals.concurrency,
                globals.quiet > 0,
                cache,
                workspace_cache,
                printer,
                globals.preview,
            ))
            .await
        }
        Commands::Pip(PipNamespace {
            command: PipCommand::Sync(args),
        }) => {
            args.compat_args.validate()?;

            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = PipSyncSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Check for conflicts between offline and refresh.
            globals
                .network_settings
                .check_refresh_conflict(&args.refresh);

            // Initialize the cache.
            let cache = cache.init().await?.with_refresh(
                args.refresh
                    .combine(Refresh::from(args.settings.reinstall.clone()))
                    .combine(Refresh::from(args.settings.upgrade.clone())),
            );

            let requirements = args
                .src_file
                .into_iter()
                .map(RequirementsSource::from_requirements_file)
                .collect::<Result<Vec<_>, _>>()?;
            let constraints = args
                .constraints
                .into_iter()
                .map(RequirementsSource::from_constraints_txt)
                .collect::<Result<Vec<_>, _>>()?;
            let build_constraints = args
                .build_constraints
                .into_iter()
                .map(RequirementsSource::from_constraints_txt)
                .collect::<Result<Vec<_>, _>>()?;
            let groups = GroupsSpecification {
                root: project_dir.to_path_buf(),
                groups: args.settings.groups,
            };

            Box::pin(commands::pip_sync(
                &requirements,
                &constraints,
                &build_constraints,
                &args.settings.extras,
                &groups,
                args.settings.reinstall,
                args.settings.link_mode,
                args.settings.compile_bytecode,
                args.settings.hash_checking,
                args.settings.index_locations,
                args.settings.index_strategy,
                args.settings.torch_backend,
                args.settings.dependency_metadata,
                args.settings.keyring_provider,
                &client_builder.subcommand(vec!["pip".to_owned(), "sync".to_owned()]),
                args.settings.allow_empty_requirements,
                globals.installer_metadata,
                &args.settings.config_setting,
                &args.settings.config_settings_package,
                args.settings.build_isolation.clone(),
                &args.settings.extra_build_dependencies,
                &args.settings.extra_build_variables,
                args.settings.build_options,
                args.settings.python_version,
                args.settings.python_platform,
                globals.python_downloads,
                args.settings.install_mirrors,
                args.settings.strict,
                args.settings.exclude_newer,
                args.settings.python,
                args.settings.system,
                args.settings.break_system_packages,
                args.settings.target,
                args.settings.prefix,
                args.settings.sources,
                globals.python_preference,
                globals.concurrency,
                cache,
                workspace_cache,
                args.dry_run,
                printer,
                globals.preview,
            ))
            .await
        }
        Commands::Pip(PipNamespace {
            command: PipCommand::Install(args),
        }) => {
            args.compat_args.validate()?;

            // Resolve the settings from the command-line arguments and workspace configuration.
            let mut args = PipInstallSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            let mut requirements = Vec::with_capacity(
                args.package.len() + args.editables.len() + args.requirements.len(),
            );
            for package in args.package {
                requirements.push(RequirementsSource::from_package_argument(&package)?);
            }
            for package in args.editables {
                requirements.push(RequirementsSource::from_editable(&package)?);
            }
            requirements.extend(
                args.requirements
                    .into_iter()
                    .map(RequirementsSource::from_requirements_file)
                    .collect::<Result<Vec<_>, _>>()?,
            );
            let constraints = args
                .constraints
                .into_iter()
                .map(RequirementsSource::from_constraints_txt)
                .collect::<Result<Vec<_>, _>>()?;
            let overrides = args
                .overrides
                .into_iter()
                .map(RequirementsSource::from_overrides_txt)
                .collect::<Result<Vec<_>, _>>()?;
            let excludes = args
                .excludes
                .into_iter()
                .map(RequirementsSource::from_requirements_txt)
                .collect::<Result<Vec<_>, _>>()?;
            let build_constraints = args
                .build_constraints
                .into_iter()
                .map(RequirementsSource::from_overrides_txt)
                .collect::<Result<Vec<_>, _>>()?;
            let groups = GroupsSpecification {
                root: project_dir.to_path_buf(),
                groups: args.settings.groups,
            };

            // Special-case: any source trees specified on the command-line are automatically
            // reinstalled. This matches user expectations: `uv pip install .` should always
            // re-build and re-install the package in the current working directory.
            for requirement in &requirements {
                let requirement = match requirement {
                    RequirementsSource::Package(requirement) => requirement,
                    RequirementsSource::Editable(requirement) => requirement,
                    _ => continue,
                };
                match requirement {
                    RequirementsTxtRequirement::Named(requirement) => {
                        if let Some(VersionOrUrl::Url(url)) = requirement.version_or_url.as_ref() {
                            if let ParsedUrl::Directory(ParsedDirectoryUrl {
                                install_path, ..
                            }) = &url.parsed_url
                            {
                                debug!(
                                    "Marking explicit source tree for reinstall: `{}`",
                                    install_path.display()
                                );
                                args.settings.reinstall = args
                                    .settings
                                    .reinstall
                                    .with_package(requirement.name.clone());
                            }
                        }
                    }
                    RequirementsTxtRequirement::Unnamed(requirement) => {
                        if let ParsedUrl::Directory(ParsedDirectoryUrl { install_path, .. }) =
                            &requirement.url.parsed_url
                        {
                            debug!(
                                "Marking explicit source tree for reinstall: `{}`",
                                install_path.display()
                            );
                            args.settings.reinstall =
                                args.settings.reinstall.with_path(install_path.clone());
                        }
                    }
                }
            }

            // Check for conflicts between offline and refresh.
            globals
                .network_settings
                .check_refresh_conflict(&args.refresh);

            // Initialize the cache.
            let cache = cache.init().await?.with_refresh(
                args.refresh
                    .combine(Refresh::from(args.settings.reinstall.clone()))
                    .combine(Refresh::from(args.settings.upgrade.clone())),
            );

            Box::pin(commands::pip_install(
                &requirements,
                &constraints,
                &overrides,
                &excludes,
                &build_constraints,
                args.constraints_from_workspace,
                args.overrides_from_workspace,
                args.excludes_from_workspace,
                args.build_constraints_from_workspace,
                args.editable,
                &args.settings.extras,
                &groups,
                args.settings.resolution,
                args.settings.prerelease,
                args.settings.dependency_mode,
                args.settings.upgrade,
                args.settings.index_locations,
                args.settings.index_strategy,
                args.settings.torch_backend,
                args.settings.dependency_metadata,
                args.settings.keyring_provider,
                &client_builder.subcommand(vec!["pip".to_owned(), "install".to_owned()]),
                args.settings.reinstall,
                args.settings.link_mode,
                args.settings.compile_bytecode,
                args.settings.hash_checking,
                globals.installer_metadata,
                &args.settings.config_setting,
                &args.settings.config_settings_package,
                args.settings.build_isolation.clone(),
                &args.settings.extra_build_dependencies,
                &args.settings.extra_build_variables,
                args.settings.build_options,
                args.modifications,
                args.settings.python_version,
                args.settings.python_platform,
                globals.python_downloads,
                args.settings.install_mirrors,
                args.settings.strict,
                args.settings.exclude_newer,
                args.settings.sources,
                args.settings.python,
                args.settings.system,
                args.settings.break_system_packages,
                args.settings.target,
                args.settings.prefix,
                globals.python_preference,
                globals.concurrency,
                cache,
                workspace_cache,
                args.dry_run,
                printer,
                globals.preview,
            ))
            .await
        }
        Commands::Pip(PipNamespace {
            command: PipCommand::Uninstall(args),
        }) => {
            args.compat_args.validate()?;

            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = PipUninstallSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Initialize the cache.
            let cache = cache.init().await?;

            let mut sources = Vec::with_capacity(args.package.len() + args.requirements.len());
            for package in args.package {
                sources.push(RequirementsSource::from_package_argument(&package)?);
            }
            sources.extend(
                args.requirements
                    .into_iter()
                    .map(RequirementsSource::from_requirements_file)
                    .collect::<Result<Vec<_>, _>>()?,
            );
            commands::pip_uninstall(
                &sources,
                args.settings.python,
                args.settings.system,
                args.settings.break_system_packages,
                args.settings.target,
                args.settings.prefix,
                cache,
                args.settings.keyring_provider,
                &client_builder.subcommand(vec!["pip".to_owned(), "uninstall".to_owned()]),
                args.dry_run,
                printer,
                globals.preview,
            )
            .await
        }
        Commands::Pip(PipNamespace {
            command: PipCommand::Freeze(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = PipFreezeSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Initialize the cache.
            let cache = cache.init().await?;

            commands::pip_freeze(
                args.exclude_editable,
                &args.exclude,
                args.settings.strict,
                &args.settings.dependency_metadata,
                args.settings.python.as_deref(),
                args.settings.system,
                args.settings.target,
                args.settings.prefix,
                args.paths,
                &cache,
                printer,
                globals.preview,
            )
        }
        Commands::Pip(PipNamespace {
            command: PipCommand::List(args),
        }) => {
            args.compat_args.validate()?;

            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = PipListSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Initialize the cache.
            let cache = cache.init().await?;

            commands::pip_list(
                args.editable,
                &args.exclude,
                &args.format,
                args.outdated,
                args.settings.prerelease,
                args.settings.index_locations,
                args.settings.index_strategy,
                args.settings.keyring_provider,
                &client_builder.subcommand(vec!["pip".to_owned(), "list".to_owned()]),
                globals.concurrency,
                args.settings.strict,
                args.settings.exclude_newer,
                &args.settings.dependency_metadata,
                args.settings.python.as_deref(),
                args.settings.system,
                args.settings.target,
                args.settings.prefix,
                &cache,
                printer,
                globals.preview,
            )
            .await
        }
        Commands::Pip(PipNamespace {
            command: PipCommand::Show(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = PipShowSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Initialize the cache.
            let cache = cache.init().await?;

            commands::pip_show(
                args.package,
                args.settings.strict,
                &args.settings.dependency_metadata,
                args.settings.python.as_deref(),
                args.settings.system,
                args.settings.target,
                args.settings.prefix,
                args.files,
                &cache,
                printer,
                globals.preview,
            )
        }
        Commands::Pip(PipNamespace {
            command: PipCommand::Tree(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = PipTreeSettings::resolve(args, filesystem, environment);

            // Initialize the cache.
            let cache = cache.init().await?;

            commands::pip_tree(
                args.show_version_specifiers,
                args.depth,
                &args.prune,
                &args.package,
                args.no_dedupe,
                args.invert,
                args.outdated,
                args.settings.prerelease,
                args.settings.index_locations,
                args.settings.index_strategy,
                args.settings.keyring_provider,
                client_builder.subcommand(vec!["pip".to_owned(), "tree".to_owned()]),
                globals.concurrency,
                args.settings.strict,
                args.settings.exclude_newer,
                &args.settings.dependency_metadata,
                args.settings.python.as_deref(),
                args.settings.system,
                &cache,
                printer,
                globals.preview,
            )
            .await
        }
        Commands::Pip(PipNamespace {
            command: PipCommand::Check(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = PipCheckSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Initialize the cache.
            let cache = cache.init().await?;

            commands::pip_check(
                args.settings.python.as_deref(),
                args.settings.system,
                args.settings.python_version.as_ref(),
                args.settings.python_platform.as_ref(),
                &args.settings.dependency_metadata,
                &cache,
                printer,
                globals.preview,
            )
        }
        Commands::Pip(PipNamespace {
            command: PipCommand::Debug(_),
        }) => Err(anyhow!(
            "pip's `debug` is unsupported (consider using `uvx pip debug` instead)"
        )),
        Commands::Cache(CacheNamespace {
            command: CacheCommand::Clean(args),
        })
        | Commands::Clean(args) => {
            show_settings!(args);
            commands::cache_clean(&args.package, args.force, cache, printer).await
        }
        Commands::Cache(CacheNamespace {
            command: CacheCommand::Prune(args),
        }) => {
            show_settings!(args);
            commands::cache_prune(args.ci, args.force, cache, printer).await
        }
        Commands::Cache(CacheNamespace {
            command: CacheCommand::Dir,
        }) => commands::cache_dir(&cache, printer),
        Commands::Cache(CacheNamespace {
            command: CacheCommand::Size(args),
        }) => commands::cache_size(&cache, args.human, printer, globals.preview),
        Commands::Build(args) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::BuildSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Check for conflicts between offline and refresh.
            globals
                .network_settings
                .check_refresh_conflict(&args.refresh);

            // Initialize the cache.
            let cache = cache.init().await?.with_refresh(
                args.refresh
                    .combine(Refresh::from(args.settings.upgrade.clone())),
            );

            // Resolve the build constraints.
            let build_constraints = args
                .build_constraints
                .into_iter()
                .map(RequirementsSource::from_constraints_txt)
                .collect::<Result<Vec<_>, _>>()?;

            commands::build_frontend(
                &project_dir,
                args.src,
                args.package,
                args.all_packages,
                args.out_dir,
                args.sdist,
                args.wheel,
                args.list,
                args.build_logs,
                args.gitignore,
                args.force_pep517,
                args.clear,
                build_constraints,
                args.build_constraints_from_workspace,
                args.hash_checking,
                args.python,
                args.install_mirrors,
                &args.settings,
                &client_builder.subcommand(vec!["build".to_owned()]),
                cli.top_level.no_config,
                globals.python_preference,
                globals.python_downloads,
                globals.concurrency,
                &cache,
                &workspace_cache,
                printer,
                globals.preview,
            )
            .await
        }
        Commands::Venv(args) => {
            args.compat_args.validate()?;

            if args.no_system {
                warn_user_once!(
                    "The `--no-system` flag has no effect, `uv venv` always ignores virtual environments when finding a Python interpreter; did you mean `--managed-python`?"
                );
            }

            if args.system {
                warn_user_once!(
                    "The `--system` flag has no effect, `uv venv` always ignores virtual environments when finding a Python interpreter; did you mean `--no-managed-python`?"
                );
            }

            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::VenvSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Check for conflicts between offline and refresh.
            globals
                .network_settings
                .check_refresh_conflict(&args.refresh);

            // Initialize the cache.
            let cache = cache.init().await?.with_refresh(
                args.refresh
                    .combine(Refresh::from(args.settings.reinstall.clone()))
                    .combine(Refresh::from(args.settings.upgrade.clone())),
            );

            // Since we use ".venv" as the default name, we use "." as the default prompt.
            let prompt = args.prompt.or_else(|| {
                if args.path.is_none() {
                    Some(".".to_string())
                } else {
                    None
                }
            });

            let python_request: Option<PythonRequest> =
                args.settings.python.as_deref().map(PythonRequest::parse);

            let on_existing = uv_virtualenv::OnExisting::from_args(
                args.allow_existing,
                args.clear,
                args.no_clear,
            );

            Box::pin(commands::venv(
                &project_dir,
                args.path,
                python_request,
                args.settings.install_mirrors,
                globals.python_preference,
                globals.python_downloads,
                args.settings.link_mode,
                &args.settings.index_locations,
                args.settings.index_strategy,
                args.settings.dependency_metadata,
                args.settings.keyring_provider,
                &client_builder.subcommand(vec!["venv".to_owned()]),
                uv_virtualenv::Prompt::from_args(prompt),
                args.system_site_packages,
                args.seed,
                on_existing,
                args.settings.exclude_newer,
                globals.concurrency,
                cli.top_level.no_config,
                args.no_project,
                &cache,
                &workspace_cache,
                printer,
                args.relocatable
                    || (globals
                        .preview
                        .is_enabled(PreviewFeature::RelocatableEnvsDefault)
                        && !args.no_relocatable),
                globals.preview,
            ))
            .await
        }
        Commands::Project(project) => {
            Box::pin(run_project(
                project,
                &project_dir,
                run_command,
                script,
                globals,
                cli.top_level.no_config,
                cli.top_level.global_args.project.is_some(),
                client_builder,
                filesystem,
                cache,
                &workspace_cache,
                printer,
            ))
            .await
        }
        #[cfg(feature = "self-update")]
        Commands::Self_(SelfNamespace {
            command:
                SelfCommand::Update(SelfUpdateArgs {
                    target_version,
                    token,
                    dry_run,
                }),
        }) => {
            commands::self_update(
                target_version,
                token,
                dry_run,
                printer,
                client_builder.subcommand(vec!["self".to_owned(), "update".to_owned()]),
            )
            .await
        }
        Commands::Self_(SelfNamespace {
            command:
                SelfCommand::Version {
                    short,
                    output_format,
                },
        }) => {
            commands::self_version(short, output_format, printer)?;
            Ok(ExitStatus::Success)
        }
        #[cfg(not(feature = "self-update"))]
        Commands::Self_(_) => {
            return Err(ExternallyInstalledError {
                install_source: InstallSource::detect(),
            }
            .into());
        }
        Commands::GenerateShellCompletion(args) => {
            args.shell.generate(&mut Cli::command(), &mut stdout());
            Ok(ExitStatus::Success)
        }
        Commands::Tool(ToolNamespace {
            command: run_variant @ (ToolCommand::Uvx(_) | ToolCommand::Run(_)),
        }) => {
            let (args, invocation_source) = match run_variant {
                ToolCommand::Uvx(args) => (args.tool_run, ToolRunCommand::Uvx),
                ToolCommand::Run(args) => (args, ToolRunCommand::ToolRun),
                // OK guarded by the outer match statement
                _ => unreachable!(),
            };

            if let Some(shell) = args.generate_shell_completion {
                // uvx: combine `uv tool uvx` with the top-level arguments
                let mut uvx = Cli::command()
                    .find_subcommand("tool")
                    .unwrap()
                    .find_subcommand("uvx")
                    .unwrap()
                    .clone()
                    // Avoid duplicating the `--help` and `--version` flags from the top-level
                    // arguments.
                    .disable_help_flag(true)
                    .disable_version_flag(true);

                // Copy the top-level arguments into the `uvx` command, as in `Args::augment_args`,
                // but expanded to skip collisions.
                for arg in TopLevelArgs::command().get_arguments() {
                    if arg.get_id() != "isolated" && arg.get_id() != "version" {
                        uvx = uvx.arg(arg);
                    }
                }
                shell.generate(&mut uvx, &mut stdout());
                return Ok(ExitStatus::Success);
            }

            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::ToolRunSettings::resolve(
                args,
                filesystem,
                invocation_source,
                environment,
            );
            show_settings!(args);

            // Check for conflicts between offline and refresh.
            globals
                .network_settings
                .check_refresh_conflict(&args.refresh);

            // Initialize the cache.
            let cache = cache.init().await?.with_refresh(
                args.refresh
                    .combine(Refresh::from(args.settings.reinstall.clone()))
                    .combine(Refresh::from(args.settings.resolver.upgrade.clone())),
            );

            let requirements = {
                let mut requirements = Vec::with_capacity(
                    args.with.len() + args.with_editable.len() + args.with_requirements.len(),
                );
                for package in args.with {
                    requirements.push(RequirementsSource::from_with_package_argument(&package)?);
                }
                for package in args.with_editable {
                    requirements.push(RequirementsSource::from_editable(&package)?);
                }
                requirements.extend(
                    args.with_requirements
                        .into_iter()
                        .map(RequirementsSource::from_requirements_file)
                        .collect::<Result<Vec<_>, _>>()?,
                );
                requirements
            };
            let constraints = args
                .constraints
                .into_iter()
                .map(RequirementsSource::from_constraints_txt)
                .collect::<Result<Vec<_>, _>>()?;
            let overrides = args
                .overrides
                .into_iter()
                .map(RequirementsSource::from_overrides_txt)
                .collect::<Result<Vec<_>, _>>()?;

            let build_constraints = args
                .build_constraints
                .into_iter()
                .map(RequirementsSource::from_constraints_txt)
                .collect::<Result<Vec<_>, _>>()?;

            let client_builder = match invocation_source {
                ToolRunCommand::Uvx => client_builder.subcommand(vec!["uvx".to_owned()]),
                ToolRunCommand::ToolRun => {
                    client_builder.subcommand(vec!["tool".to_owned(), "run".to_owned()])
                }
            };

            Box::pin(commands::tool_run(
                args.command,
                args.from,
                &requirements,
                &constraints,
                &overrides,
                &build_constraints,
                args.show_resolution || globals.verbose > 0,
                args.lfs,
                args.python,
                args.python_platform,
                args.install_mirrors,
                args.options,
                args.settings,
                client_builder,
                invocation_source,
                args.isolated,
                globals.python_preference,
                globals.python_downloads,
                globals.installer_metadata,
                globals.concurrency,
                cache,
                workspace_cache,
                printer,
                args.env_file,
                args.no_env_file,
                globals.preview,
            ))
            .await
        }
        Commands::Tool(ToolNamespace {
            command: ToolCommand::Install(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::ToolInstallSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Check for conflicts between offline and refresh.
            globals
                .network_settings
                .check_refresh_conflict(&args.refresh);

            // Initialize the cache.
            let cache = cache.init().await?.with_refresh(
                args.refresh
                    .combine(Refresh::from(args.settings.reinstall.clone()))
                    .combine(Refresh::from(args.settings.resolver.upgrade.clone())),
            );

            let mut entrypoints = Vec::with_capacity(args.with_executables_from.len());
            let mut requirements = Vec::with_capacity(
                args.with.len()
                    + args.with_editable.len()
                    + args.with_requirements.len()
                    + args.with_executables_from.len(),
            );
            for pkg in args.with {
                requirements.push(RequirementsSource::from_with_package_argument(&pkg)?);
            }
            for pkg in args.with_editable {
                requirements.push(RequirementsSource::from_editable(&pkg)?);
            }
            for path in args.with_requirements {
                requirements.push(RequirementsSource::from_requirements_file(path)?);
            }
            for pkg in &args.with_executables_from {
                let source = RequirementsSource::from_with_package_argument(pkg)?;
                let RequirementsSource::Package(RequirementsTxtRequirement::Named(requirement)) =
                    &source
                else {
                    bail!(
                        "Expected a named package for `--with-executables-from`, but got: {}",
                        source.to_string().cyan()
                    )
                };
                entrypoints.push(requirement.name.clone());
                requirements.push(source);
            }

            let constraints = args
                .constraints
                .into_iter()
                .map(RequirementsSource::from_constraints_txt)
                .collect::<Result<Vec<_>, _>>()?;
            let overrides = args
                .overrides
                .into_iter()
                .map(RequirementsSource::from_overrides_txt)
                .collect::<Result<Vec<_>, _>>()?;
            let excludes = args
                .excludes
                .into_iter()
                .map(RequirementsSource::from_requirements_txt)
                .collect::<Result<Vec<_>, _>>()?;
            let build_constraints = args
                .build_constraints
                .into_iter()
                .map(RequirementsSource::from_constraints_txt)
                .collect::<Result<Vec<_>, _>>()?;

            Box::pin(commands::tool_install(
                args.package,
                args.editable,
                args.from,
                &requirements,
                &constraints,
                &overrides,
                &excludes,
                &build_constraints,
                &entrypoints,
                args.lfs,
                args.python,
                args.python_platform,
                args.install_mirrors,
                args.force,
                args.options,
                args.settings,
                client_builder.subcommand(vec!["tool".to_owned(), "install".to_owned()]),
                globals.python_preference,
                globals.python_downloads,
                globals.installer_metadata,
                globals.concurrency,
                cli.top_level.no_config,
                cache,
                &workspace_cache,
                printer,
                globals.preview,
            ))
            .await
        }
        Commands::Tool(ToolNamespace {
            command: ToolCommand::List(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::ToolListSettings::resolve(args, filesystem);
            show_settings!(args);

            // Initialize the cache.
            let cache = cache.init().await?;

            commands::tool_list(
                args.show_paths,
                args.show_version_specifiers,
                args.show_with,
                args.show_extras,
                args.show_python,
                args.outdated,
                args.args,
                args.filesystem,
                client_builder.subcommand(vec!["tool".to_owned(), "list".to_owned()]),
                globals.concurrency,
                &cache,
                printer,
            )
            .await
        }
        Commands::Tool(ToolNamespace {
            command: ToolCommand::Upgrade(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::ToolUpgradeSettings::resolve(args, filesystem, &environment);
            show_settings!(args);

            // Initialize the cache.
            let cache = cache
                .init()
                .await?
                .with_refresh(Refresh::All(Timestamp::now()));

            Box::pin(commands::tool_upgrade(
                args.names,
                args.python,
                args.python_platform,
                args.install_mirrors,
                args.args,
                args.filesystem,
                client_builder.subcommand(vec!["tool".to_owned(), "upgrade".to_owned()]),
                globals.python_preference,
                globals.python_downloads,
                globals.installer_metadata,
                globals.concurrency,
                &cache,
                &workspace_cache,
                printer,
                globals.preview,
            ))
            .await
        }
        Commands::Tool(ToolNamespace {
            command: ToolCommand::Uninstall(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::ToolUninstallSettings::resolve(args, filesystem);
            show_settings!(args);

            commands::tool_uninstall(args.name, printer).await
        }
        Commands::Tool(ToolNamespace {
            command: ToolCommand::UpdateShell,
        }) => {
            commands::tool_update_shell(printer).await?;
            Ok(ExitStatus::Success)
        }
        Commands::Tool(ToolNamespace {
            command: ToolCommand::Dir(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::ToolDirSettings::resolve(args, filesystem);
            show_settings!(args);

            commands::tool_dir(args.bin, globals.preview, printer)?;
            Ok(ExitStatus::Success)
        }
        Commands::Python(PythonNamespace {
            command: PythonCommand::List(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::PythonListSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Initialize the cache.
            let cache = cache.init().await?;

            commands::python_list(
                args.request,
                args.kinds,
                args.all_versions,
                args.all_platforms,
                args.all_arches,
                args.show_urls,
                args.output_format,
                args.python_downloads_json_url,
                args.python_install_mirror,
                args.pypy_install_mirror,
                globals.python_preference,
                globals.python_downloads,
                &client_builder.subcommand(vec!["python".to_owned(), "list".to_owned()]),
                &cache,
                printer,
                globals.preview,
            )
            .await
        }
        Commands::Python(PythonNamespace {
            command: PythonCommand::Install(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::PythonInstallSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Initialize the cache.
            let cache = cache.init().await?;

            commands::python_install(
                &project_dir,
                args.install_dir,
                args.targets,
                args.reinstall,
                args.upgrade,
                args.bin,
                args.registry,
                args.force,
                args.python_install_mirror,
                args.pypy_install_mirror,
                args.python_downloads_json_url,
                client_builder.subcommand(vec!["python".to_owned(), "install".to_owned()]),
                args.default,
                globals.python_downloads,
                cli.top_level.no_config,
                args.compile_bytecode,
                &globals.concurrency,
                &cache,
                globals.preview,
                printer,
            )
            .await
        }
        Commands::Python(PythonNamespace {
            command: PythonCommand::Upgrade(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::PythonUpgradeSettings::resolve(args, filesystem, environment);
            show_settings!(args);
            let upgrade = commands::PythonUpgrade::Enabled(commands::PythonUpgradeSource::Upgrade);

            // Initialize the cache.
            let cache = cache.init().await?;

            commands::python_install(
                &project_dir,
                args.install_dir,
                args.targets,
                args.reinstall,
                upgrade,
                args.bin,
                args.registry,
                args.force,
                args.python_install_mirror,
                args.pypy_install_mirror,
                args.python_downloads_json_url,
                client_builder.subcommand(vec!["python".to_owned(), "upgrade".to_owned()]),
                args.default,
                globals.python_downloads,
                cli.top_level.no_config,
                args.compile_bytecode,
                &globals.concurrency,
                &cache,
                globals.preview,
                printer,
            )
            .await
        }
        Commands::Python(PythonNamespace {
            command: PythonCommand::Uninstall(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::PythonUninstallSettings::resolve(args, filesystem);
            show_settings!(args);

            commands::python_uninstall(args.install_dir, args.targets, args.all, printer).await
        }
        Commands::Python(PythonNamespace {
            command: PythonCommand::Find(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::PythonFindSettings::resolve(args, filesystem, environment);

            // Initialize the cache.
            let cache = cache.init().await?;

            if let Some(Pep723Item::Script(script)) = script {
                commands::python_find_script(
                    (&script).into(),
                    args.show_version,
                    args.resolve_links,
                    // TODO(zsol): is this the right thing to do here?
                    &client_builder.subcommand(vec!["python".to_owned(), "find".to_owned()]),
                    globals.python_preference,
                    globals.python_downloads,
                    cli.top_level.no_config,
                    &cache,
                    printer,
                    globals.preview,
                )
                .await
            } else {
                commands::python_find(
                    &project_dir,
                    args.request,
                    args.show_version,
                    args.resolve_links,
                    args.no_project,
                    cli.top_level.no_config,
                    args.system,
                    globals.python_preference,
                    args.python_downloads_json_url.as_deref(),
                    &client_builder.subcommand(vec!["python".to_owned(), "find".to_owned()]),
                    &cache,
                    &workspace_cache,
                    printer,
                    globals.preview,
                )
                .await
            }
        }
        Commands::Python(PythonNamespace {
            command: PythonCommand::Pin(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::PythonPinSettings::resolve(args, filesystem, environment);

            // Initialize the cache.
            let cache = cache.init().await?;

            Box::pin(commands::python_pin(
                &project_dir,
                args.request,
                args.resolved,
                globals.python_preference,
                globals.python_downloads,
                args.no_project,
                args.global,
                args.rm,
                args.install_mirrors,
                client_builder.subcommand(vec!["python".to_owned(), "pin".to_owned()]),
                &cache,
                &workspace_cache,
                printer,
                globals.preview,
            ))
            .await
        }
        Commands::Python(PythonNamespace {
            command: PythonCommand::Dir(args),
        }) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::PythonDirSettings::resolve(args, filesystem);
            show_settings!(args);

            commands::python_dir(args.bin, printer)?;
            Ok(ExitStatus::Success)
        }
        Commands::Python(PythonNamespace {
            command: PythonCommand::UpdateShell,
        }) => {
            commands::python_update_shell(printer).await?;
            Ok(ExitStatus::Success)
        }
        Commands::Publish(args) => {
            show_settings!(args);

            if args.skip_existing {
                bail!(
                    "`uv publish` does not support `--skip-existing` because there is not a \
                    reliable way to identify when an upload fails due to an existing \
                    distribution. Instead, use `--check-url` to provide the URL to the simple \
                    API for your index. uv will check the index for existing distributions before \
                    attempting uploads."
                );
            }

            // Resolve the settings from the command-line arguments and workspace configuration.
            let PublishSettings {
                files,
                username,
                password,
                dry_run,
                no_attestations,
                direct,
                publish_url,
                trusted_publishing,
                keyring_provider,
                check_url,
                index,
                index_locations,
            } = PublishSettings::resolve(args, filesystem);

            commands::publish(
                files,
                publish_url,
                trusted_publishing,
                keyring_provider,
                &environment,
                &client_builder.subcommand(vec!["publish".to_owned()]),
                username,
                password,
                check_url,
                index,
                index_locations,
                dry_run,
                no_attestations,
                direct,
                globals.preview,
                &cache,
                printer,
            )
            .await
        }
        Commands::Workspace(WorkspaceNamespace { command }) => match command {
            WorkspaceCommand::Metadata(args) => {
                // Resolve the settings from the command-line arguments and workspace configuration.
                let args = settings::MetadataSettings::resolve(args, filesystem, environment);
                show_settings!(args);

                // Check for conflicts between offline and refresh.
                globals
                    .network_settings
                    .check_refresh_conflict(&args.refresh);

                // Initialize the cache.
                let cache = cache.init().await?.with_refresh(
                    args.refresh
                        .clone()
                        .combine(Refresh::from(args.settings.upgrade.clone())),
                );

                Box::pin(commands::metadata(
                    &project_dir,
                    args.lock_check,
                    args.frozen,
                    args.dry_run,
                    args.refresh,
                    args.python,
                    args.install_mirrors,
                    args.settings,
                    client_builder.subcommand(vec!["workspace".to_owned(), "metadata".to_owned()]),
                    globals.python_preference,
                    globals.python_downloads,
                    globals.concurrency,
                    cli.top_level.no_config,
                    &cache,
                    &workspace_cache,
                    printer,
                    globals.preview,
                ))
                .await
            }
            WorkspaceCommand::Dir(args) => {
                commands::dir(args.package, &project_dir, &workspace_cache, printer).await
            }
            WorkspaceCommand::List(args) => {
                commands::list(&project_dir, args.paths, &workspace_cache, printer).await
            }
        },
        Commands::BuildBackend { command } => spawn_blocking(move || match command {
            BuildBackendCommand::BuildSdist { sdist_directory } => {
                commands::build_backend::build_sdist(&sdist_directory)
            }
            BuildBackendCommand::BuildWheel {
                wheel_directory,
                metadata_directory,
            } => commands::build_backend::build_wheel(
                &wheel_directory,
                metadata_directory.as_deref(),
            ),
            BuildBackendCommand::BuildEditable {
                wheel_directory,
                metadata_directory,
            } => commands::build_backend::build_editable(
                &wheel_directory,
                metadata_directory.as_deref(),
            ),
            BuildBackendCommand::GetRequiresForBuildSdist => {
                commands::build_backend::get_requires_for_build_sdist()
            }
            BuildBackendCommand::GetRequiresForBuildWheel => {
                commands::build_backend::get_requires_for_build_wheel()
            }
            BuildBackendCommand::PrepareMetadataForBuildWheel { wheel_directory } => {
                commands::build_backend::prepare_metadata_for_build_wheel(&wheel_directory)
            }
            BuildBackendCommand::GetRequiresForBuildEditable => {
                commands::build_backend::get_requires_for_build_editable()
            }
            BuildBackendCommand::PrepareMetadataForBuildEditable { wheel_directory } => {
                commands::build_backend::prepare_metadata_for_build_editable(&wheel_directory)
            }
        })
        .await
        .expect("tokio threadpool exited unexpectedly"),
    }
}

fn map_settings_error(err: uv_settings::Error) -> anyhow::Error {
    match err {
        uv_settings::Error::RequiredVersion {
            required_version,
            package_version,
        } => required_version_error(&required_version, &package_version),
        err => err.into(),
    }
}

fn required_version_error(
    required_version: &uv_configuration::RequiredVersion,
    package_version: &uv_pep440::Version,
) -> anyhow::Error {
    #[cfg(feature = "self-update")]
    let hint = {
        // If the required version range includes a lower bound that's higher than the current
        // version, suggest `uv self update`.
        let ranges = release_specifiers_to_ranges(required_version.specifiers().clone());

        if let Some(singleton) = ranges.as_singleton() {
            format!(
                ". Update `uv` by running `{}`.",
                format!("uv self update {singleton}").green()
            )
        } else if ranges
            .bounding_range()
            .iter()
            .any(|(lowest, _highest)| match lowest {
                Bound::Included(version) => **version > *package_version,
                Bound::Excluded(version) => **version > *package_version,
                Bound::Unbounded => false,
            })
        {
            format!(". Update `uv` by running `{}`.", "uv self update".cyan())
        } else {
            String::new()
        }
    };
    #[cfg(not(feature = "self-update"))]
    let hint = "";

    anyhow!(
        "Required uv version `{required_version}` does not match the running version `{package_version}`{hint}",
    )
}

/// Run a [`ProjectCommand`].
async fn run_project(
    project_command: Box<ProjectCommand>,
    project_dir: &Path,
    command: Option<RunCommand>,
    script: Option<Pep723Item>,
    globals: GlobalSettings,
    // TODO(zanieb): Determine a better story for passing `no_config` in here
    no_config: bool,
    explicit_project: bool,
    client_builder: BaseClientBuilder<'_>,
    filesystem: Option<FilesystemOptions>,
    cache: Cache,
    workspace_cache: &WorkspaceCache,
    printer: Printer,
) -> Result<ExitStatus> {
    // Write out any resolved settings.
    macro_rules! show_settings {
        ($arg:expr) => {
            if globals.show_settings {
                writeln!(printer.stdout(), "{:#?}", $arg)?;
                return Ok(ExitStatus::Success);
            }
        };
    }

    // Load environment variables not handled by Clap
    let environment = EnvironmentOptions::new()?;

    match *project_command {
        ProjectCommand::Init(args) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::InitSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // The `--project` arg is being deprecated for `init` with a warning now and an error in preview.
            if explicit_project {
                if globals.preview.is_enabled(PreviewFeature::InitProjectFlag) {
                    bail!(
                        "The `--project` option cannot be used in `uv init`. {}",
                        if args.path.is_some() {
                            "Use `--directory` instead."
                        } else {
                            "Use `--directory` or a positional path instead."
                        }
                    )
                }

                warn_user!(
                    "Use of the `--project` option in `uv init` is deprecated and will be removed in a future release. {}",
                    if args.path.is_some() {
                        "Since a positional path was provided, the `--project` option has no effect. Consider using `--directory` instead."
                    } else {
                        "Consider using `uv init <PATH>` instead."
                    }
                );
            }

            // Initialize the cache.
            let cache = cache.init().await?;

            Box::pin(commands::init(
                project_dir,
                args.path,
                args.name,
                args.package,
                args.kind,
                args.bare,
                args.description,
                args.no_description,
                args.vcs,
                args.build_backend,
                args.no_readme,
                args.author_from,
                args.pin_python,
                args.python,
                args.install_mirrors,
                args.no_workspace,
                &client_builder.subcommand(vec!["init".to_owned()]),
                globals.python_preference,
                globals.python_downloads,
                no_config,
                &cache,
                printer,
                globals.preview,
            ))
            .await
        }
        ProjectCommand::Run(args) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::RunSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Check for conflicts between offline and refresh.
            globals
                .network_settings
                .check_refresh_conflict(&args.refresh);

            // Initialize the cache.
            let cache = cache.init().await?.with_refresh(
                args.refresh
                    .combine(Refresh::from(args.settings.reinstall.clone()))
                    .combine(Refresh::from(args.settings.resolver.upgrade.clone())),
            );

            let mut requirements = Vec::with_capacity(
                args.with.len() + args.with_editable.len() + args.with_requirements.len(),
            );
            for package in args.with {
                requirements.push(RequirementsSource::from_with_package_argument(&package)?);
            }
            for package in args.with_editable {
                requirements.push(RequirementsSource::from_editable(&package)?);
            }
            requirements.extend(
                args.with_requirements
                    .into_iter()
                    .map(RequirementsSource::from_requirements_file)
                    .collect::<Result<Vec<_>, _>>()?,
            );

            Box::pin(commands::run(
                project_dir,
                script,
                command,
                requirements,
                args.show_resolution || globals.verbose > 0,
                args.lock_check,
                args.frozen,
                args.active,
                args.no_sync,
                args.isolated,
                args.all_packages,
                args.package,
                args.no_project,
                no_config,
                args.extras,
                args.groups,
                args.editable,
                args.modifications,
                args.python,
                args.python_platform,
                args.install_mirrors,
                args.settings,
                client_builder.subcommand(vec!["run".to_owned()]),
                globals.python_preference,
                globals.python_downloads,
                globals.installer_metadata,
                globals.concurrency,
                cache,
                workspace_cache,
                printer,
                args.env_file,
                globals.preview,
                args.max_recursion_depth,
                args.malware_settings,
            ))
            .await
        }
        ProjectCommand::Sync(args) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::SyncSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Check for conflicts between offline and refresh.
            globals
                .network_settings
                .check_refresh_conflict(&args.refresh);

            // Initialize the cache.
            let cache = cache.init().await?.with_refresh(
                args.refresh
                    .combine(Refresh::from(args.settings.reinstall.clone()))
                    .combine(Refresh::from(args.settings.resolver.upgrade.clone())),
            );

            // Unwrap the script.
            let script = script.map(|script| match script {
                Pep723Item::Script(script) => script,
                Pep723Item::Stdin(..) => unreachable!("`uv lock` does not support stdin"),
                Pep723Item::Remote(..) => unreachable!("`uv lock` does not support remote files"),
            });

            Box::pin(commands::sync(
                project_dir,
                args.lock_check,
                args.frozen,
                args.dry_run,
                args.active,
                args.all_packages,
                args.package,
                args.extras,
                args.groups,
                args.editable,
                args.install_options,
                args.modifications,
                args.python,
                args.python_platform,
                args.install_mirrors,
                globals.python_preference,
                globals.python_downloads,
                args.settings,
                client_builder.subcommand(vec!["sync".to_owned()]),
                script,
                globals.installer_metadata,
                globals.concurrency,
                no_config,
                &cache,
                workspace_cache,
                printer,
                globals.preview,
                args.output_format,
                args.malware_settings,
            ))
            .await
        }
        ProjectCommand::Lock(args) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::LockSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Check for conflicts between offline and refresh.
            globals
                .network_settings
                .check_refresh_conflict(&args.refresh);

            // Initialize the cache.
            let cache = cache.init().await?.with_refresh(
                args.refresh
                    .clone()
                    .combine(Refresh::from(args.settings.upgrade.clone())),
            );

            // If the script already exists, use it; otherwise, propagate the file path and we'll
            // initialize it later on.
            let script = script
                .map(|script| match script {
                    Pep723Item::Script(script) => script,
                    Pep723Item::Stdin(..) => unreachable!("`uv add` does not support stdin"),
                    Pep723Item::Remote(..) => {
                        unreachable!("`uv add` does not support remote files")
                    }
                })
                .map(ScriptPath::Script)
                .or(args.script.map(ScriptPath::Path));

            Box::pin(commands::lock(
                project_dir,
                args.lock_check,
                args.frozen,
                args.dry_run,
                args.refresh,
                args.python,
                args.install_mirrors,
                args.settings,
                client_builder.subcommand(vec!["lock".to_owned()]),
                script,
                globals.python_preference,
                globals.python_downloads,
                globals.concurrency,
                no_config,
                &cache,
                workspace_cache,
                printer,
                globals.preview,
            ))
            .await
        }
        ProjectCommand::Add(args) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let mut args = settings::AddSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // If the script already exists, use it; otherwise, propagate the file path and we'll
            // initialize it later on.
            let script = script
                .map(|script| match script {
                    Pep723Item::Script(script) => script,
                    Pep723Item::Stdin(..) => unreachable!("`uv add` does not support stdin"),
                    Pep723Item::Remote(..) => {
                        unreachable!("`uv add` does not support remote files")
                    }
                })
                .map(ScriptPath::Script)
                .or(args.script.map(ScriptPath::Path));

            let requirements = args
                .packages
                .iter()
                .map(String::as_str)
                .map(RequirementsSource::from_package_argument)
                .chain(
                    args.requirements
                        .into_iter()
                        .map(RequirementsSource::from_requirements_file),
                )
                .collect::<Result<Vec<_>>>()?;

            // Special-case: any local source trees specified on the command-line are automatically
            // reinstalled.
            for requirement in &requirements {
                let requirement = match requirement {
                    RequirementsSource::Package(requirement) => requirement,
                    RequirementsSource::Editable(requirement) => requirement,
                    _ => continue,
                };
                match requirement {
                    RequirementsTxtRequirement::Named(requirement) => {
                        if let Some(VersionOrUrl::Url(url)) = requirement.version_or_url.as_ref() {
                            if let ParsedUrl::Directory(ParsedDirectoryUrl {
                                install_path, ..
                            }) = &url.parsed_url
                            {
                                debug!(
                                    "Marking explicit source tree for reinstall: `{}`",
                                    install_path.display()
                                );
                                args.settings.reinstall = args
                                    .settings
                                    .reinstall
                                    .with_package(requirement.name.clone());
                            }
                        }
                    }
                    RequirementsTxtRequirement::Unnamed(requirement) => {
                        if let ParsedUrl::Directory(ParsedDirectoryUrl { install_path, .. }) =
                            &requirement.url.parsed_url
                        {
                            debug!(
                                "Marking explicit source tree for reinstall: `{}`",
                                install_path.display()
                            );
                            args.settings.reinstall =
                                args.settings.reinstall.with_path(install_path.clone());
                        }
                    }
                }
            }

            // Check for conflicts between offline and refresh.
            globals
                .network_settings
                .check_refresh_conflict(&args.refresh);

            // Initialize the cache.
            let cache = cache.init().await?.with_refresh(
                args.refresh
                    .combine(Refresh::from(args.settings.reinstall.clone()))
                    .combine(Refresh::from(args.settings.resolver.upgrade.clone())),
            );

            let constraints = args
                .constraints
                .into_iter()
                .map(RequirementsSource::from_constraints_txt)
                .collect::<Result<Vec<_>, _>>()?;

            Box::pin(commands::add(
                project_dir,
                args.lock_check,
                args.frozen,
                args.active,
                args.no_sync,
                args.no_install_project,
                args.only_install_project,
                args.no_install_workspace,
                args.only_install_workspace,
                args.no_install_local,
                args.only_install_local,
                args.no_install_package,
                args.only_install_package,
                requirements,
                constraints,
                args.marker,
                args.editable,
                args.dependency_type,
                args.raw,
                args.bounds,
                args.indexes,
                args.rev,
                args.tag,
                args.branch,
                args.lfs,
                args.extras,
                args.package,
                args.python,
                args.workspace,
                args.install_mirrors,
                args.settings,
                client_builder.subcommand(vec!["add".to_owned()]),
                script,
                globals.python_preference,
                globals.python_downloads,
                globals.installer_metadata,
                globals.concurrency,
                no_config,
                &cache,
                printer,
                globals.preview,
                &args.malware_settings,
            ))
            .await
        }
        ProjectCommand::Remove(args) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::RemoveSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Check for conflicts between offline and refresh.
            globals
                .network_settings
                .check_refresh_conflict(&args.refresh);

            // Initialize the cache.
            let cache = cache.init().await?.with_refresh(
                args.refresh
                    .combine(Refresh::from(args.settings.reinstall.clone()))
                    .combine(Refresh::from(args.settings.resolver.upgrade.clone())),
            );

            // Unwrap the script.
            let script = script.map(|script| match script {
                Pep723Item::Script(script) => script,
                Pep723Item::Stdin(..) => unreachable!("`uv remove` does not support stdin"),
                Pep723Item::Remote(..) => unreachable!("`uv remove` does not support remote files"),
            });

            Box::pin(commands::remove(
                project_dir,
                args.lock_check,
                args.frozen,
                args.active,
                args.no_sync,
                args.packages,
                args.dependency_type,
                args.package,
                args.python,
                args.install_mirrors,
                args.settings,
                client_builder.subcommand(vec!["remove".to_owned()]),
                script,
                globals.python_preference,
                globals.python_downloads,
                globals.installer_metadata,
                globals.concurrency,
                no_config,
                &cache,
                printer,
                globals.preview,
                args.malware_settings,
            ))
            .await
        }
        ProjectCommand::Version(args) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::VersionSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Check for conflicts between offline and refresh.
            globals
                .network_settings
                .check_refresh_conflict(&args.refresh);

            // Initialize the cache.
            let cache = cache.init().await?.with_refresh(
                args.refresh
                    .combine(Refresh::from(args.settings.reinstall.clone()))
                    .combine(Refresh::from(args.settings.resolver.upgrade.clone())),
            );

            Box::pin(commands::project_version(
                args.value,
                args.bump,
                args.short,
                args.output_format,
                project_dir,
                args.package,
                explicit_project,
                args.dry_run,
                args.lock_check,
                args.frozen,
                args.active,
                args.no_sync,
                args.python,
                args.install_mirrors,
                args.settings,
                client_builder.subcommand(vec!["version".to_owned()]),
                globals.python_preference,
                globals.python_downloads,
                globals.installer_metadata,
                globals.concurrency,
                no_config,
                &cache,
                workspace_cache,
                printer,
                globals.preview,
                args.malware_settings,
            ))
            .await
        }
        ProjectCommand::Tree(args) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::TreeSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Initialize the cache.
            let cache = cache.init().await?;

            // Unwrap the script.
            let script = script.map(|script| match script {
                Pep723Item::Script(script) => script,
                Pep723Item::Stdin(..) => unreachable!("`uv tree` does not support stdin"),
                Pep723Item::Remote(..) => unreachable!("`uv tree` does not support remote files"),
            });

            Box::pin(commands::tree(
                project_dir,
                args.groups,
                args.lock_check,
                args.frozen,
                args.universal,
                args.depth,
                args.prune,
                args.package,
                args.no_dedupe,
                args.invert,
                args.outdated,
                args.show_sizes,
                args.python_version,
                args.python_platform,
                args.python,
                args.install_mirrors,
                args.resolver,
                &client_builder.subcommand(vec!["tree".to_owned()]),
                script,
                globals.python_preference,
                globals.python_downloads,
                globals.concurrency,
                no_config,
                &cache,
                printer,
                globals.preview,
            ))
            .await
        }
        ProjectCommand::Export(args) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::ExportSettings::resolve(args, filesystem, environment);
            show_settings!(args);

            // Initialize the cache.
            let cache = cache.init().await?;

            // Unwrap the script.
            let script = script.map(|script| match script {
                Pep723Item::Script(script) => script,
                Pep723Item::Stdin(..) => unreachable!("`uv export` does not support stdin"),
                Pep723Item::Remote(..) => unreachable!("`uv export` does not support remote files"),
            });

            commands::export(
                project_dir,
                args.format,
                args.all_packages,
                args.package,
                args.prune,
                args.hashes,
                args.install_options,
                args.output_file,
                args.extras,
                args.groups,
                args.editable,
                args.lock_check,
                args.frozen,
                args.include_annotations,
                args.include_header,
                script,
                args.python,
                args.install_mirrors,
                args.settings,
                client_builder.subcommand(vec!["export".to_owned()]),
                globals.python_preference,
                globals.python_downloads,
                globals.concurrency,
                no_config,
                globals.quiet > 0,
                &cache,
                printer,
                globals.preview,
            )
            .boxed_local()
            .await
        }
        ProjectCommand::Format(args) => {
            // Resolve the settings from the command-line arguments and workspace configuration.
            let args = settings::FormatSettings::resolve(args, filesystem);
            show_settings!(args);

            // Initialize the cache.
            let cache = cache.init().await?;

            Box::pin(commands::format(
                project_dir,
                args.check,
                args.diff,
                args.extra_args,
                args.version,
                args.exclude_newer,
                args.show_version,
                client_builder.subcommand(vec!["format".to_owned()]),
                cache,
                printer,
                globals.preview,
                args.no_project,
            ))
            .await
        }
        ProjectCommand::Audit(audit_args) => {
            let args = settings::AuditSettings::resolve(audit_args, filesystem, environment);
            show_settings!(args);

            // Initialize the cache.
            let cache = cache.init().await?;

            // Unwrap the script.
            let script = script.map(|script| match script {
                Pep723Item::Script(script) => script,
                Pep723Item::Stdin(..) => unreachable!("`uv audit` does not support stdin"),
                Pep723Item::Remote(..) => unreachable!("`uv audit` does not support remote files"),
            });

            Box::pin(commands::audit(
                project_dir,
                args.extras,
                args.groups,
                args.lock_check,
                args.frozen,
                script,
                args.python_version,
                args.python_platform,
                args.install_mirrors,
                args.settings,
                client_builder.subcommand(vec!["audit".to_owned()]),
                globals.python_preference,
                globals.python_downloads,
                globals.concurrency,
                no_config,
                cache,
                printer,
                globals.preview,
                args.output_format,
                args.service_format,
                args.service_url,
                args.ignore,
                args.ignore_until_fixed,
            ))
            .await
        }
    }
}

/// The main entry point for a uv invocation.
///
/// # Usage
///
/// This entry point is not recommended for external consumption, the uv binary interface is the
/// official public API.
///
/// When using this entry point, uv assumes it is running in a process it controls and that the
/// entire process lifetime is managed by uv. Unexpected behavior may be encountered if this entry
/// point is called multiple times in a single process.
///
/// # Safety
///
/// It is only safe to call this routine when it is known that multiple threads are not running.
#[allow(unsafe_code)]
pub unsafe fn main<I, T>(args: I) -> ExitCode
where
    I: IntoIterator<Item = T>,
    T: Into<OsString> + Clone,
{
    #[cfg(windows)]
    uv_windows::install_unhandled_exception_handler();

    // Set the `UV` variable to the current executable so it is implicitly propagated to all child
    // processes, e.g., in `uv run`.
    if let Ok(current_exe) = std::env::current_exe() {
        // SAFETY: The proof obligation must be satisfied by the caller.
        unsafe {
            // This will become unsafe in Rust 2024
            // See https://doc.rust-lang.org/std/env/fn.set_var.html#safety
            std::env::set_var(EnvVars::UV, current_exe);
        }
    }

    // `std::env::args` is not `Send` so we parse before passing to our runtime
    // https://github.com/rust-lang/rust/pull/48005
    let cli = match Cli::try_parse_from(args) {
        Ok(cli) => cli,
        Err(mut err) => {
            if let Some(ContextValue::String(subcommand)) = err.get(ContextKind::InvalidSubcommand)
            {
                match subcommand.as_str() {
                    "compile" => {
                        err.insert(
                            ContextKind::SuggestedSubcommand,
                            ContextValue::String("uv pip compile".to_string()),
                        );
                    }
                    "install" => {
                        err.insert(
                            ContextKind::SuggestedSubcommand,
                            ContextValue::String("uv pip install".to_string()),
                        );
                    }
                    "uninstall" => {
                        err.insert(
                            ContextKind::SuggestedSubcommand,
                            ContextValue::String("uv pip uninstall".to_string()),
                        );
                    }
                    "freeze" => {
                        err.insert(
                            ContextKind::SuggestedSubcommand,
                            ContextValue::String("uv pip freeze".to_string()),
                        );
                    }
                    "list" => {
                        err.insert(
                            ContextKind::SuggestedSubcommand,
                            ContextValue::String("uv pip list".to_string()),
                        );
                    }
                    "show" => {
                        err.insert(
                            ContextKind::SuggestedSubcommand,
                            ContextValue::String("uv pip show".to_string()),
                        );
                    }
                    _ => {}
                }
            }
            err.exit()
        }
    };

    // See `min_stack_size` doc comment about `main2`
    let min_stack_size = min_stack_size();
    let main2 = move || {
        let runtime = tokio::runtime::Builder::new_current_thread()
            .enable_all()
            .thread_stack_size(min_stack_size)
            .build()
            .expect("Failed building the Runtime");
        // Box the large main future to avoid stack overflows.
        let result = runtime.block_on(Box::pin(run(cli)));
        // Avoid waiting for pending tasks to complete.
        //
        // The resolver may have kicked off HTTP requests during resolution that
        // turned out to be unnecessary. Waiting for those to complete can cause
        // the CLI to hang before exiting.
        runtime.shutdown_background();
        result
    };
    let result = std::thread::Builder::new()
        .name("main2".to_owned())
        .stack_size(min_stack_size)
        .spawn(main2)
        .expect("Tokio executor failed, was there a panic?")
        .join()
        .expect("Tokio executor failed, was there a panic?");

    match result {
        Ok(code) => code.into(),
        Err(err) => {
            trace!("Error trace: {err:?}");

            // Collect hints before rendering the error chain.
            let hints = commands::diagnostics::hints_for_error(&err);

            let mut causes = err.chain();
            eprintln!(
                "{}: {}",
                "error".red().bold(),
                causes.next().unwrap().to_string().trim()
            );
            for err in causes {
                eprintln!("  {}: {}", "Caused by".red().bold(), err.to_string().trim());
            }

            // Render hints after the error chain.
            anstream::eprint!("{hints}");

            ExitStatus::Error.into()
        }
    }
}
