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}