Files
gh-vanman2024-cli-builder-p…/skills/fire-patterns/examples/nested-commands.md
2025-11-30 09:04:14 +08:00

6.1 KiB

Nested Commands Example

This example demonstrates using nested classes to organize related commands into groups.

Generate Nested CLI

./scripts/generate-fire-cli.sh \
  --name "DeployTool" \
  --description "Deployment management tool" \
  --template nested \
  --output deploy_tool.py

Command Structure

deploy_tool.py
├── config               # Configuration group
│   ├── get             # Get config value
│   ├── set             # Set config value
│   ├── list            # List all config
│   └── reset           # Reset config
├── resources            # Resources group
│   ├── create          # Create resource
│   ├── delete          # Delete resource
│   └── list            # List resources
└── info                 # Display info

Usage Examples

Configuration Management

# Set configuration value
python deploy_tool.py config set api_key abc123

# Get configuration value
python deploy_tool.py config get api_key
# Output: api_key: abc123

# List all configuration
python deploy_tool.py config list
# Output:
# Configuration:
#   api_key: abc123
#   endpoint: https://api.example.com

# Reset configuration
python deploy_tool.py config reset --confirm

Resource Management

# Create resource with default template
python deploy_tool.py resources create my-resource

# Create resource with custom template
python deploy_tool.py resources create my-resource --template=advanced

# List all resources
python deploy_tool.py resources list
# Output:
# Resources:
#   • item1
#   • item2
#   • item3

# Delete resource (requires confirmation)
python deploy_tool.py resources delete my-resource
# Output: ⚠ Use --confirm to delete resource

python deploy_tool.py resources delete my-resource --confirm
# Output: ✓ resource deleted

Display Information

python deploy_tool.py info
# Output:
# DeployTool v1.0.0
# Config file: /home/user/.deploytool/config.json

Implementation Pattern

Main CLI Class

class DeployTool:
    """Main CLI application"""

    def __init__(self):
        self.version = "1.0.0"
        self.config_file = Path.home() / ".deploytool" / "config.json"
        # Initialize nested command groups
        self.config = self.Config(self)
        self.resources = self.Resources()

    def info(self):
        """Display CLI information"""
        console.print(f"[bold]DeployTool[/bold] v{self.version}")
        return {"version": self.version}

Nested Command Group

class Config:
    """Configuration management commands"""

    def __init__(self, parent):
        self.parent = parent  # Access to main CLI instance

    def get(self, key):
        """Get configuration value

        Args:
            key: Configuration key to retrieve
        """
        config = self._load_config()
        value = config.get(key)
        console.print(f"[blue]{key}[/blue]: {value}")
        return value

    def set(self, key, value):
        """Set configuration value

        Args:
            key: Configuration key to set
            value: Configuration value
        """
        config = self._load_config()
        config[key] = value
        self._save_config(config)
        console.print(f"[green]✓[/green] Set {key} = {value}")

    def _load_config(self):
        """Private helper method (not exposed as command)"""
        if not self.parent.config_file.exists():
            return {}
        return json.loads(self.parent.config_file.read_text())

    def _save_config(self, config):
        """Private helper method (not exposed as command)"""
        self.parent.config_file.parent.mkdir(parents=True, exist_ok=True)
        self.parent.config_file.write_text(json.dumps(config, indent=2))

Key Concepts

Parent Access

Nested classes can access the parent CLI instance:

class Config:
    def __init__(self, parent):
        self.parent = parent  # Store parent reference

    def some_command(self):
        # Access parent properties
        version = self.parent.version
        config_file = self.parent.config_file

Private Methods

Methods starting with _ are not exposed as CLI commands:

def list(self):
    """Public command - accessible via CLI"""
    pass

def _load_config(self):
    """Private helper - not accessible via CLI"""
    pass

Multiple Nesting Levels

You can nest command groups multiple levels deep:

class CLI:
    class Database:
        """Database commands"""

        class Migration:
            """Migration subcommands"""

            def up(self):
                """Run migrations up"""
                pass

            def down(self):
                """Run migrations down"""
                pass

# Usage:
# python cli.py database migration up
# python cli.py database migration down

Help Navigation

Top-Level Help

python deploy_tool.py --help

Shows all command groups and top-level commands.

Group-Level Help

python deploy_tool.py config --help

Shows all commands in the config group.

Command-Level Help

python deploy_tool.py config set --help

Shows help for specific command including arguments.

Best Practices

  1. Logical Grouping: Group related commands together
  2. Clear Names: Use descriptive names for groups and commands
  3. Parent Access: Use parent reference to share state
  4. Private Helpers: Use _ prefix for helper methods
  5. Comprehensive Docs: Document each command group and command
  6. Shallow Nesting: Keep nesting to 2-3 levels maximum

Advanced Pattern: Shared Context

class CLI:
    def __init__(self):
        self.context = {"verbose": False, "config": {}}

    class Commands:
        def __init__(self, parent):
            self.parent = parent

        def run(self, verbose=False):
            """Run command"""
            self.parent.context["verbose"] = verbose
            if verbose:
                console.print("[dim]Verbose mode enabled[/dim]")

This allows sharing state across command groups through the parent context.