agpm_cli/cli/
mod.rs

1//! Command-line interface for AGPM (Claude Code Package Manager).
2//!
3//! This module contains all CLI command implementations for the Claude Code Package Manager.
4//! The CLI provides a comprehensive set of commands for managing Claude Code resources,
5//! from project initialization to dependency management and global configuration.
6//!
7//! # Command Architecture
8//!
9//! Each command is implemented as a separate module with its own argument structures
10//! and execution logic. This modular design allows for:
11//! - Clear separation of concerns
12//! - Independent testing of each command
13//! - Easy addition of new commands
14//! - Consistent documentation and error handling
15//!
16//! # Available Commands
17//!
18//! ## Project Management
19//! - `init` - Initialize a new AGPM project with a manifest file
20//! - `add` - Add sources and dependencies to the project manifest
21//! - `remove` - Remove sources and dependencies from the project manifest  
22//! - `install` - Install dependencies from the manifest
23//! - `update` - Update dependencies within version constraints
24//!
25//! ## Information and Inspection
26//! - `list` - List installed resources from the lockfile
27//! - `tree` - Display dependency tree for installed resources
28//! - `outdated` - Check for available updates to dependencies
29//! - `validate` - Validate project configuration and dependencies
30//!
31//! ## System Management
32//! - `cache` - Manage the global Git repository cache
33//! - `config` - Manage global configuration settings
34//! - `upgrade` - Upgrade AGPM to newer versions with backup support
35//!
36//! # Command Usage Patterns
37//!
38//! ## Basic Workflow
39//! ```bash
40//! # 1. Initialize a new project
41//! agpm init
42//!
43//! # 2. Add sources and dependencies
44//! agpm add source official https://github.com/org/agpm-resources.git
45//! agpm add dep official:agents/code-reviewer.md@v1.0.0 --agent
46//!
47//! # 3. Install dependencies
48//! agpm install
49//!
50//! # 4. List what's installed
51//! agpm list
52//! ```
53//!
54//! ## Maintenance Operations
55//! ```bash
56//! # Check for available updates
57//! agpm outdated
58//!
59//! # Validate project configuration
60//! agpm validate --resolve --sources
61//!
62//! # Update dependencies
63//! agpm update
64//!
65//! # Manage cache
66//! agpm cache clean
67//!
68//! # Configure global settings
69//! agpm config add-source private https://oauth2:TOKEN@github.com/org/private.git
70//!
71//! # Check for and install AGPM updates
72//! agpm upgrade --check     # Check for updates
73//! agpm upgrade             # Upgrade to latest
74//! agpm upgrade --rollback  # Restore previous version
75//! ```
76//!
77//! # Global vs Project Configuration
78//!
79//! AGPM uses two types of configuration:
80//!
81//! | Type | File | Purpose | Version Control |
82//! |------|------|---------|----------------|
83//! | Project | `agpm.toml` | Define dependencies | ✅ Commit |
84//! | Global | `~/.agpm/config.toml` | Authentication tokens | ❌ Never commit |
85//!
86//! # Cross-Platform Support
87//!
88//! The CLI is designed to work consistently across:
89//! - Windows (Command Prompt, `PowerShell`)
90//! - macOS (Terminal, various shells)
91//! - Linux (bash, zsh, fish, etc.)
92//!
93//! # Command Modules
94//!
95//! Each command is implemented in its own module:
96//!
97//! # Global Options
98//!
99//! All commands support these global options:
100//! - `--verbose` - Enable debug output
101//! - `--quiet` - Suppress all output except errors
102//! - `--no-progress` - Disable progress bars and spinners
103//! - `--config` - Path to custom config file
104//!
105//! # Example
106//!
107//! ```bash
108//! # Initialize a new project
109//! agpm init --with-examples
110//!
111//! # Install dependencies
112//! agpm install --verbose
113//!
114//! # Update dependencies
115//! agpm update --no-progress
116//! ```
117
118mod add;
119mod cache;
120pub mod common;
121mod config;
122mod init;
123mod install;
124mod list;
125mod migrate;
126mod outdated;
127mod remove;
128mod tree;
129mod update;
130/// Self-update functionality for upgrading AGPM to newer versions.
131///
132/// This module provides the `upgrade` command which enables AGPM to update itself
133/// by downloading and installing new versions from GitHub releases. It includes
134/// safety features like automatic backups and rollback capabilities.
135///
136/// The upgrade functionality is implemented as a separate public module to allow
137/// both CLI usage and programmatic access to the self-update features.
138pub mod upgrade;
139pub mod validate;
140
141#[cfg(test)]
142mod tests;
143
144use anyhow::Result;
145use clap::{Parser, Subcommand};
146use std::path::PathBuf;
147
148/// Runtime configuration for CLI execution.
149///
150/// This struct holds configuration that would otherwise be set as environment variables,
151/// enabling dependency injection and better testability. It allows tests and programmatic
152/// usage to control CLI behavior without modifying global environment state.
153///
154/// # Design Rationale
155///
156/// Rather than directly setting environment variables in CLI parsing, this struct:
157/// - Enables clean testing without global state pollution
158/// - Allows configuration composition and validation
159/// - Provides a single point for environment variable management
160/// - Supports configuration serialization if needed
161///
162/// # Usage Pattern
163///
164/// ```rust,ignore
165/// let config = CliConfig::new()
166///     .with_log_level("debug")
167///     .with_no_progress(true);
168/// config.apply_to_env();
169/// ```
170#[derive(Debug, Clone, Default)]
171pub struct CliConfig {
172    /// Log level for the `RUST_LOG` environment variable.
173    ///
174    /// Controls the verbosity of logging output throughout AGPM. Common values:
175    /// - `"error"`: Only errors are logged
176    /// - `"warn"`: Errors and warnings
177    /// - `"info"`: Errors, warnings, and informational messages
178    /// - `"debug"`: All messages including debug information
179    /// - `"trace"`: Maximum verbosity for troubleshooting
180    ///
181    /// When `None`, the existing `RUST_LOG` value is preserved.
182    pub log_level: Option<String>,
183
184    /// Whether to disable progress indicators and animated output.
185    ///
186    /// When `true`, sets the `AGPM_NO_PROGRESS` environment variable to disable:
187    /// - Progress bars during long operations
188    /// - Spinner animations
189    /// - Real-time status updates
190    ///
191    /// This is useful for:
192    /// - Automated scripts and CI/CD pipelines
193    /// - Terminal environments that don't support ANSI codes
194    /// - Debugging where animated output interferes with logs
195    pub no_progress: bool,
196
197    /// Custom path to the global configuration file.
198    ///
199    /// When specified, sets the `AGPM_CONFIG` environment variable to override
200    /// the default configuration file location (`~/.agpm/config.toml`).
201    ///
202    /// This enables:
203    /// - Testing with isolated configuration files
204    /// - Alternative configuration layouts
205    /// - Shared configuration in team environments
206    pub config_path: Option<String>,
207}
208
209impl CliConfig {
210    /// Create a new CLI configuration with default values.
211    ///
212    /// Returns a configuration with:
213    /// - No log level override (`log_level: None`)
214    /// - Progress indicators enabled (`no_progress: false`)
215    /// - Default config file location (`config_path: None`)
216    ///
217    /// # Examples
218    ///
219    /// ```rust,ignore
220    /// use agpm_cli::cli::CliConfig;
221    ///
222    /// let config = CliConfig::new();
223    /// assert_eq!(config.log_level, None);
224    /// assert_eq!(config.no_progress, false);
225    /// assert_eq!(config.config_path, None);
226    /// ```
227    #[must_use]
228    pub fn new() -> Self {
229        Self::default()
230    }
231}
232
233/// Main CLI structure for AGPM (Claude Code Package Manager).
234///
235/// This struct represents the root command and all its global options. It uses the
236/// `clap` derive API to automatically generate command-line parsing, help text, and
237/// validation. All options marked as `global = true` are available to all subcommands.
238///
239/// # Design Philosophy
240///
241/// The CLI follows standard Unix conventions:
242/// - Short options use single dashes (`-v`)
243/// - Long options use double dashes (`--verbose`)
244/// - Global options work with all subcommands
245/// - Mutually exclusive options are validated automatically
246///
247/// # Global Options
248///
249/// All subcommands inherit these global options:
250/// - **Verbosity control**: `--verbose` and `--quiet` for output level
251/// - **Configuration**: `--config` for custom config file paths
252/// - **UI control**: `--no-progress` for automation-friendly output
253///
254/// # Examples
255///
256/// ```bash
257/// # Basic command with global options
258/// agpm --verbose install
259/// agpm --quiet --no-progress list
260/// agpm --config ./custom.toml validate
261///
262/// # Global options work with any subcommand
263/// agpm --verbose install
264/// agpm --quiet cache clean
265/// ```
266///
267/// # Subcommand Structure
268///
269/// Commands are organized by functionality:
270/// - **Project management**: `install`, `update`, `add`, `remove`
271/// - **Information**: `list`, `validate`
272/// - **System**: `cache`, `config`, `upgrade`
273///
274/// # Integration Points
275///
276/// This CLI integrates with:
277/// - [`CliConfig`] for dependency injection and testing
278/// - Environment variables for runtime configuration
279/// - Global and project-specific configuration files
280/// - Cross-platform file system operations
281///
282/// # Main CLI application structure for AGPM
283///
284/// This struct represents the top-level command-line interface for the Claude Code
285/// Package Manager. It handles global flags and delegates to subcommands for
286/// specific operations.
287#[derive(Parser)]
288#[command(
289    name = "agpm",
290    about = "Claude Code Package Manager - Manage Claude Code resources",
291    version,
292    author,
293    long_about = "AGPM is a Git-based package manager for Claude Code resources including agents, snippets, and more."
294)]
295pub struct Cli {
296    /// The subcommand to execute.
297    ///
298    /// Each subcommand provides a specific functionality area within AGPM.
299    /// The available commands are defined in the [`Commands`] enum.
300    #[command(subcommand)]
301    command: Commands,
302
303    /// Enable verbose output for debugging and detailed information.
304    ///
305    /// When enabled, shows:
306    ///   - Detailed progress information
307    ///   - Debug messages and internal state
308    ///   - Expanded error context and suggestions
309    ///   - Git operation details and network calls
310    ///
311    /// This is equivalent to setting `RUST_LOG=debug`. Mutually exclusive
312    /// with `--quiet`.
313    ///
314    /// # Examples
315    ///
316    /// ```bash
317    /// agpm --verbose install     # Verbose installation
318    /// agpm -v update             # Short form
319    /// ```
320    #[arg(short, long, global = true)]
321    verbose: bool,
322
323    /// Suppress all output except errors for automation.
324    ///
325    /// When enabled:
326    ///   - Suppresses informational messages and progress indicators
327    ///   - Only outputs errors and warnings
328    ///   - Ideal for scripts and CI/CD pipelines
329    ///   - JSON output (where supported) remains unchanged
330    ///
331    /// Mutually exclusive with `--verbose`.
332    ///
333    /// # Examples
334    ///
335    /// ```bash
336    /// agpm --quiet install       # Silent installation
337    /// agpm -q list               # Short form
338    /// agpm --quiet cache clean   # Automated cache cleanup
339    /// ```
340    #[arg(short, long, global = true)]
341    quiet: bool,
342
343    /// Path to custom global configuration file.
344    ///
345    /// Overrides the default configuration file location (`~/.agpm/config.toml`)
346    /// with a custom path. This is useful for:
347    ///
348    /// - **Testing**: Using isolated configuration files
349    /// - **Deployment**: Shared configuration in team environments
350    /// - **Development**: Different configurations per project
351    ///
352    /// The configuration file contains:
353    /// - Global source repository definitions with authentication
354    /// - Default settings for cache and network operations
355    /// - User preferences and customizations
356    ///
357    /// # Examples
358    ///
359    /// ```bash
360    /// agpm --config ./dev-config.toml install    # Custom config
361    /// agpm -c ~/.agpm/team-config.toml list      # Team config
362    /// agpm --config /etc/agpm/global.toml update # System config
363    /// ```
364    #[arg(short, long, global = true)]
365    config: Option<String>,
366
367    /// Path to the manifest file (agpm.toml).
368    ///
369    /// By default, AGPM searches for agpm.toml in the current directory
370    /// and parent directories. This option allows you to specify an exact
371    /// path to the manifest file, which is useful for:
372    ///
373    /// - Running commands from outside the project directory
374    /// - CI/CD pipelines with non-standard layouts
375    /// - Testing with temporary manifests
376    ///
377    /// # Examples
378    ///
379    /// ```bash
380    /// agpm --manifest-path /path/to/agpm.toml install
381    /// agpm --manifest-path ../other-project/agpm.toml list
382    /// ```
383    #[arg(long, global = true)]
384    manifest_path: Option<PathBuf>,
385
386    /// Disable progress bars and spinners for automation.
387    ///
388    /// When enabled:
389    /// - Disables animated progress indicators
390    /// - Uses plain text status messages instead
391    /// - Prevents cursor manipulation and ANSI escape codes
392    /// - Ideal for terminals without ANSI support
393    ///
394    /// This option is automatically enabled in non-TTY environments
395    /// (pipes, redirects, CI systems) but can be explicitly controlled.
396    ///
397    /// # Use Cases
398    ///
399    /// - **CI/CD pipelines**: Clean log output for build systems
400    /// - **Scripts**: Avoid interference with text processing
401    /// - **Legacy terminals**: Compatibility with older terminal emulators
402    /// - **Debugging**: Easier to follow operation sequences
403    ///
404    /// # Examples
405    ///
406    /// ```bash
407    /// agpm --no-progress install         # No animations
408    /// agpm install 2>&1 | tee log.txt   # Auto-detected non-TTY
409    /// CI=true agpm install               # CI environment
410    /// ```
411    #[arg(long, global = true)]
412    no_progress: bool,
413}
414
415/// Available subcommands for the AGPM CLI.
416///
417/// This enum defines all the subcommands available in AGPM, organized by
418/// functional categories. Each variant contains the specific command structure
419/// with its own arguments and options.
420///
421/// # Command Categories
422///
423/// ## Project Management
424/// - [`Init`](Commands::Init): Initialize new AGPM projects
425/// - [`Add`](Commands::Add): Add sources and dependencies
426/// - [`Remove`](Commands::Remove): Remove sources and dependencies
427/// - [`Install`](Commands::Install): Install dependencies from manifest
428/// - [`Update`](Commands::Update): Update dependencies within constraints
429///
430/// ## Information & Validation
431/// - [`List`](Commands::List): Display installed resources
432/// - [`Tree`](Commands::Tree): Display dependency tree
433/// - [`Validate`](Commands::Validate): Verify project configuration
434///
435/// ## System Management
436/// - [`Cache`](Commands::Cache): Manage Git repository cache
437/// - [`Config`](Commands::Config): Manage global configuration
438/// - [`Upgrade`](Commands::Upgrade): Self-update AGPM to newer versions
439/// - [`Migrate`](Commands::Migrate): Migrate from legacy CCPM naming to AGPM
440///
441/// # Command Execution
442///
443/// Each command is executed through its respective `execute()` method,
444/// which handles:
445/// - Argument validation and parsing
446/// - Async operation coordination
447/// - Error handling and user feedback
448/// - Cross-platform compatibility
449///
450/// # Examples
451///
452/// ```bash
453/// # Project setup and management
454/// agpm init                    # Initialize new project
455/// agpm add source official ... # Add a source repository
456/// agpm install                 # Install all dependencies
457/// agpm update                  # Update to latest versions
458///
459/// # Information and validation
460/// agpm list                    # Show installed resources
461/// agpm outdated                # Check for available updates
462/// agpm validate --resolve      # Comprehensive validation
463///
464/// # System management
465/// agpm cache info              # Show cache information
466/// agpm config show             # Show configuration
467/// agpm migrate                 # Migrate from CCPM to AGPM
468/// ```
469#[derive(Subcommand)]
470enum Commands {
471    /// Initialize a new AGPM project with a manifest file.
472    ///
473    /// Creates a new `agpm.toml` manifest file in the current directory with
474    /// basic project structure and example configurations. This is the first
475    /// step in setting up a new AGPM project.
476    ///
477    /// See [`init::InitCommand`] for detailed options and behavior.
478    Init(init::InitCommand),
479
480    /// Add sources and dependencies to the project manifest.
481    ///
482    /// Provides subcommands to add Git repository sources and resource
483    /// dependencies (agents, snippets, commands, MCP servers) to the
484    /// `agpm.toml` manifest file.
485    ///
486    /// See [`add::AddCommand`] for detailed options and behavior.
487    Add(add::AddCommand),
488
489    /// Remove sources and dependencies from the project manifest.
490    ///
491    /// Provides subcommands to remove Git repository sources and resource
492    /// dependencies (agents, snippets, commands, MCP servers) from the
493    /// `agpm.toml` manifest file.
494    ///
495    /// See [`remove::RemoveCommand`] for detailed options and behavior.
496    Remove(remove::RemoveCommand),
497
498    /// Install Claude Code resources from manifest dependencies.
499    ///
500    /// Reads the `agpm.toml` manifest, resolves all dependencies, downloads
501    /// resources from Git repositories, and installs them to the project
502    /// directory. Creates or updates the `agpm.lock` lockfile.
503    ///
504    /// See [`install::InstallCommand`] for detailed options and behavior.
505    Install(install::InstallCommand),
506
507    /// Update installed resources within version constraints.
508    ///
509    /// Updates existing dependencies to newer versions while respecting
510    /// version constraints defined in the manifest. Updates the lockfile
511    /// with resolved versions.
512    ///
513    /// See [`update::UpdateCommand`] for detailed options and behavior.
514    Update(update::UpdateCommand),
515
516    /// Check for available updates to installed dependencies.
517    ///
518    /// Compares installed versions from the lockfile against available versions
519    /// in source repositories. Reports both compatible updates (within version
520    /// constraints) and major updates that exceed current constraints.
521    ///
522    /// See [`outdated::OutdatedCommand`] for detailed options and behavior.
523    Outdated(outdated::OutdatedCommand),
524
525    /// Upgrade AGPM to the latest version.
526    ///
527    /// Downloads and installs the latest version of AGPM from GitHub releases.
528    /// Supports checking for updates, upgrading to specific versions, and
529    /// rolling back to previous versions if needed.
530    ///
531    /// See [`upgrade::UpgradeArgs`] for detailed options and behavior.
532    Upgrade(upgrade::UpgradeArgs),
533
534    /// List installed Claude Code resources.
535    ///
536    /// Displays information about currently installed dependencies based on
537    /// the lockfile. Supports various output formats and filtering options
538    /// for different use cases.
539    ///
540    /// See [`list::ListCommand`] for detailed options and behavior.
541    List(list::ListCommand),
542
543    /// Display dependency tree for installed resources.
544    ///
545    /// Shows dependencies and their transitive dependencies in a hierarchical
546    /// tree format, similar to `cargo tree`. Helps visualize the dependency
547    /// graph and identify duplicate or redundant dependencies.
548    ///
549    /// See [`tree::TreeCommand`] for detailed options and behavior.
550    Tree(tree::TreeCommand),
551
552    /// Validate AGPM project configuration and dependencies.
553    ///
554    /// Performs comprehensive validation of the project manifest, dependencies,
555    /// source accessibility, and configuration consistency. Supports multiple
556    /// validation levels and output formats.
557    ///
558    /// See [`validate::ValidateCommand`] for detailed options and behavior.
559    Validate(validate::ValidateCommand),
560
561    /// Manage the global Git repository cache.
562    ///
563    /// Provides operations for managing the global cache directory where
564    /// AGPM stores cloned Git repositories. Includes cache information,
565    /// cleanup, and size management.
566    ///
567    /// See [`cache::CacheCommand`] for detailed options and behavior.
568    Cache(cache::CacheCommand),
569
570    /// Manage global AGPM configuration.
571    ///
572    /// Provides operations for managing the global configuration file
573    /// (`~/.agpm/config.toml`) which contains authentication tokens,
574    /// default sources, and user preferences.
575    ///
576    /// See [`config::ConfigCommand`] for detailed options and behavior.
577    Config(config::ConfigCommand),
578
579    /// Migrate from legacy CCPM naming to AGPM.
580    ///
581    /// Detects and renames ccpm.toml and ccpm.lock files to agpm.toml
582    /// and agpm.lock respectively. This is a one-time migration command
583    /// for projects upgrading from the legacy CCPM naming.
584    Migrate(migrate::MigrateCommand),
585}
586
587impl Cli {
588    /// Execute the CLI with default configuration.
589    ///
590    /// This is the main entry point for CLI execution. It builds a configuration
591    /// from the parsed command-line arguments and delegates to [`execute_with_config`](Self::execute_with_config).
592    ///
593    /// # Process Flow
594    ///
595    /// 1. **Configuration Building**: Converts CLI arguments to [`CliConfig`]
596    /// 2. **Environment Setup**: Applies configuration to process environment
597    /// 3. **Command Dispatch**: Routes to the appropriate subcommand handler
598    /// 4. **Error Handling**: Provides user-friendly error messages
599    ///
600    /// # Returns
601    ///
602    /// - `Ok(())` if the command executed successfully
603    /// - `Err(anyhow::Error)` if the command failed with details for user feedback
604    ///
605    /// # Examples
606    ///
607    /// ```rust,ignore
608    /// use agpm_cli::cli::Cli;
609    /// use clap::Parser;
610    ///
611    /// # tokio_test::block_on(async {
612    /// let cli = Cli::parse(); // From command-line arguments
613    /// cli.execute().await?;
614    /// # Ok::<(), anyhow::Error>(())
615    /// # });
616    /// ```
617    pub async fn execute(self) -> Result<()> {
618        let config = self.build_config();
619        self.execute_with_config(config).await
620    }
621
622    /// Build a [`CliConfig`] from the parsed CLI arguments.
623    ///
624    /// This method translates command-line flags into a structured configuration
625    /// that can be applied to the environment or injected into tests.
626    ///
627    /// # Configuration Logic
628    ///
629    /// - **Verbose mode**: Sets log level to "debug" for detailed output
630    /// - **Quiet mode**: Disables logging for automation-friendly output
631    /// - **Default mode**: Uses "info" level for normal operation
632    /// - **Progress control**: Honors `--no-progress` flag for animations
633    /// - **Config path**: Uses custom config file if specified
634    ///
635    /// # Validation
636    ///
637    /// The CLI parser already handles mutual exclusion between `--verbose` and
638    /// `--quiet`, so this method doesn't need additional validation.
639    ///
640    /// # Returns
641    ///
642    /// A [`CliConfig`] instance ready for environment application or testing.
643    ///
644    /// # Examples
645    ///
646    /// ```rust,ignore
647    /// use agpm_cli::cli::Cli;
648    /// use clap::Parser;
649    ///
650    /// let cli = Cli::parse_from(&["agpm", "--verbose", "install"]);
651    /// let config = cli.build_config();
652    /// assert_eq!(config.log_level, Some("debug".to_string()));
653    /// ```
654    #[must_use]
655    pub fn build_config(&self) -> CliConfig {
656        let log_level = if self.verbose {
657            Some("debug".to_string())
658        } else if self.quiet {
659            None // No logging when quiet
660        } else {
661            Some("info".to_string())
662        };
663
664        CliConfig {
665            log_level,
666            no_progress: self.no_progress,
667            config_path: self.config.clone(),
668        }
669    }
670
671    /// Execute the CLI with a specific configuration for dependency injection.
672    ///
673    /// This method enables testing and programmatic usage by accepting an
674    /// external configuration instead of building one from CLI arguments.
675    /// It's the core execution method that all entry points eventually call.
676    ///
677    /// # Design Benefits
678    ///
679    /// - **Testability**: Tests can inject custom configurations
680    /// - **Flexibility**: Programmatic usage without CLI parsing
681    /// - **No Global State**: Configuration passed explicitly, no environment variables
682    /// - **Consistency**: Single execution path for all scenarios
683    ///
684    /// # Arguments
685    ///
686    /// * `config` - The configuration to pass to command execution
687    ///
688    /// # Execution Flow
689    ///
690    /// 1. **Command Matching**: Dispatches to the appropriate subcommand
691    /// 2. **Config Passing**: Passes configuration to commands that need it
692    /// 3. **Async Execution**: Awaits the async command execution
693    /// 4. **Error Propagation**: Returns any errors for higher-level handling
694    ///
695    /// # Returns
696    ///
697    /// - `Ok(())` if the command completed successfully
698    /// - `Err(anyhow::Error)` if the command failed with context for debugging
699    ///
700    /// # Examples
701    ///
702    /// ```rust,ignore
703    /// use agpm_cli::cli::{Cli, CliConfig};
704    /// use clap::Parser;
705    ///
706    /// # tokio_test::block_on(async {
707    /// let cli = Cli::parse_from(&["agpm", "install"]);
708    /// let mut config = CliConfig::new();
709    /// config.log_level = Some("trace".to_string());
710    /// config.no_progress = true;
711    ///
712    /// cli.execute_with_config(config).await?;
713    /// # Ok::<(), anyhow::Error>(())
714    /// # });
715    /// ```
716    pub async fn execute_with_config(self, config: CliConfig) -> Result<()> {
717        // Check for updates automatically (non-blocking, best-effort)
718        // Skip for the upgrade command itself to avoid recursion
719        if !matches!(self.command, Commands::Upgrade(_)) {
720            Self::check_for_updates_if_needed().await;
721        }
722
723        // Pass configuration directly to commands that need it
724        match self.command {
725            Commands::Init(cmd) => cmd.execute_with_manifest_path(self.manifest_path).await,
726            Commands::Add(cmd) => cmd.execute_with_manifest_path(self.manifest_path).await,
727            Commands::Remove(cmd) => cmd.execute_with_manifest_path(self.manifest_path).await,
728            Commands::Install(mut cmd) => {
729                // Pass no_progress and verbose flags to install command
730                cmd.no_progress = cmd.no_progress || config.no_progress;
731                cmd.verbose = config.log_level == Some("debug".to_string());
732                cmd.execute_with_manifest_path(self.manifest_path).await
733            }
734            Commands::Update(mut cmd) => {
735                // Pass no_progress and verbose flags to update command
736                cmd.no_progress = cmd.no_progress || config.no_progress;
737                cmd.verbose = config.log_level == Some("debug".to_string());
738                cmd.execute_with_manifest_path(self.manifest_path).await
739            }
740            Commands::Outdated(mut cmd) => {
741                // Pass no_progress flag to outdated command
742                cmd.no_progress = cmd.no_progress || config.no_progress;
743                cmd.execute_with_manifest_path(self.manifest_path).await
744            }
745            Commands::Upgrade(cmd) => upgrade::execute(cmd).await,
746            Commands::List(cmd) => cmd.execute_with_manifest_path(self.manifest_path).await,
747            Commands::Tree(cmd) => cmd.execute_with_manifest_path(self.manifest_path).await,
748            Commands::Validate(cmd) => cmd.execute_with_manifest_path(self.manifest_path).await,
749            Commands::Cache(cmd) => cmd.execute_with_manifest_path(self.manifest_path).await,
750            Commands::Config(cmd) => {
751                // Pass config_path to config command if provided
752                let config_path = config.config_path.as_ref().map(PathBuf::from);
753                cmd.execute(config_path).await
754            }
755            Commands::Migrate(cmd) => cmd.execute().await,
756        }
757    }
758
759    /// Check for AGPM updates automatically based on configuration.
760    ///
761    /// This method performs a non-blocking, best-effort check for updates.
762    /// It respects the user's configuration for automatic update checking
763    /// and uses intelligent caching to avoid excessive GitHub API calls.
764    ///
765    /// The check is designed to be unobtrusive:
766    /// - Runs asynchronously without blocking the main command
767    /// - Fails silently if there are network issues
768    /// - Respects user preferences for update intervals
769    /// - Shows notification only when appropriate
770    async fn check_for_updates_if_needed() {
771        use crate::upgrade::VersionChecker;
772
773        // Spawn the check in a background task so it doesn't block
774        tokio::spawn(async {
775            // Create version checker
776            let checker = match VersionChecker::new().await {
777                Ok(c) => c,
778                Err(_) => return, // Silently fail if can't create checker
779            };
780
781            // Check for updates based on configuration
782            if let Ok(Some(latest_version)) = checker.check_for_updates_if_needed().await {
783                // Display notification to user
784                VersionChecker::display_update_notification(&latest_version);
785            }
786        });
787
788        // Give the check a small amount of time to complete
789        // This prevents the main command from exiting before the check can display
790        tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
791    }
792}