# Command Handler Developer Guide

**Target Audience**: Developers working on the provisioning CLI
**Last Updated**: 2025-09-30
**Related**: [ADR-006 CLI Refactoring](../architecture/adr/ADR-006-provisioning-cli-refactoring.md)

## Overview

The provisioning CLI uses a **modular, domain-driven architecture** that separates concerns into focused command handlers. This guide shows you how to work with this architecture.

### Key Architecture Principles

1. **Separation of Concerns**: Routing, flag parsing, and business logic are separated
2. **Domain-Driven Design**: Commands organized by domain (infrastructure, orchestration, etc.)
3. **DRY (Don't Repeat Yourself)**: Centralized flag handling eliminates code duplication
4. **Single Responsibility**: Each module has one clear purpose
5. **Open/Closed Principle**: Easy to extend, no need to modify core routing

### Architecture Components

```plaintext
provisioning/core/nulib/
├── provisioning (211 lines) - Main entry point
├── main_provisioning/
│   ├── flags.nu (139 lines) - Centralized flag handling
│   ├── dispatcher.nu (264 lines) - Command routing
│   ├── help_system.nu - Categorized help system
│   └── commands/ - Domain-focused handlers
│       ├── infrastructure.nu (117 lines) - Server, taskserv, cluster, infra
│       ├── orchestration.nu (64 lines) - Workflow, batch, orchestrator
│       ├── development.nu (72 lines) - Module, layer, version, pack
│       ├── workspace.nu (56 lines) - Workspace, template
│       ├── generation.nu (78 lines) - Generate commands
│       ├── utilities.nu (157 lines) - SSH, SOPS, cache, providers
│       └── configuration.nu (316 lines) - Env, show, init, validate
```plaintext

## Adding New Commands

### Step 1: Choose the Right Domain Handler

Commands are organized by domain. Choose the appropriate handler:

| Domain | Handler | Responsibility |
|--------|---------|----------------|
| `infrastructure.nu` | Server/taskserv/cluster/infra lifecycle |
| `orchestration.nu` | Workflow/batch operations, orchestrator control |
| `development.nu` | Module discovery, layers, versions, packaging |
| `workspace.nu` | Workspace and template management |
| `configuration.nu` | Environment, settings, initialization |
| `utilities.nu` | SSH, SOPS, cache, providers, utilities |
| `generation.nu` | Generate commands (server, taskserv, etc.) |

### Step 2: Add Command to Handler

**Example: Adding a new server command `server status`**

Edit `provisioning/core/nulib/main_provisioning/commands/infrastructure.nu`:

```nushell
# Add to the handle_infrastructure_command match statement
export def handle_infrastructure_command [
  command: string
  ops: string
  flags: record
] {
  set_debug_env $flags

  match $command {
    "server" => { handle_server $ops $flags }
    "taskserv" | "task" => { handle_taskserv $ops $flags }
    "cluster" => { handle_cluster $ops $flags }
    "infra" | "infras" => { handle_infra $ops $flags }
    _ => {
      print $"❌ Unknown infrastructure command: ($command)"
      print ""
      print "Available infrastructure commands:"
      print "  server      - Server operations (create, delete, list, ssh, status)"  # Updated
      print "  taskserv    - Task service management"
      print "  cluster     - Cluster operations"
      print "  infra       - Infrastructure management"
      print ""
      print "Use 'provisioning help infrastructure' for more details"
      exit 1
    }
  }
}

# Add the new command handler
def handle_server [ops: string, flags: record] {
  let args = build_module_args $flags $ops
  run_module $args "server" --exec
}
```plaintext

**That's it!** The command is now available as `provisioning server status`.

### Step 3: Add Shortcuts (Optional)

If you want shortcuts like `provisioning s status`:

Edit `provisioning/core/nulib/main_provisioning/dispatcher.nu`:

```nushell
export def get_command_registry []: nothing -> record {
  {
    # Infrastructure commands
    "s" => "infrastructure server"           # Already exists
    "server" => "infrastructure server"      # Already exists

    # Your new shortcut (if needed)
    # Example: "srv-status" => "infrastructure server status"

    # ... rest of registry
  }
}
```plaintext

**Note**: Most shortcuts are already configured. You only need to add new shortcuts if you're creating completely new command categories.

## Modifying Existing Handlers

### Example: Enhancing the `taskserv` Command

Let's say you want to add better error handling to the taskserv command:

**Before:**

```nushell
def handle_taskserv [ops: string, flags: record] {
  let args = build_module_args $flags $ops
  run_module $args "taskserv" --exec
}
```plaintext

**After:**

```nushell
def handle_taskserv [ops: string, flags: record] {
  # Validate taskserv name if provided
  let first_arg = ($ops | split row " " | get -o 0)
  if ($first_arg | is-not-empty) and $first_arg not-in ["create", "delete", "list", "generate", "check-updates", "help"] {
    # Check if taskserv exists
    let available_taskservs = (^$env.PROVISIONING_NAME module discover taskservs | from json)
    if $first_arg not-in $available_taskservs {
      print $"❌ Unknown taskserv: ($first_arg)"
      print ""
      print "Available taskservs:"
      $available_taskservs | each { |ts| print $"  • ($ts)" }
      exit 1
    }
  }

  let args = build_module_args $flags $ops
  run_module $args "taskserv" --exec
}
```plaintext

## Working with Flags

### Using Centralized Flag Handling

The `flags.nu` module provides centralized flag handling:

```nushell
# Parse all flags into normalized record
let parsed_flags = (parse_common_flags {
  version: $version, v: $v, info: $info,
  debug: $debug, check: $check, yes: $yes,
  wait: $wait, infra: $infra, # ... etc
})

# Build argument string for module execution
let args = build_module_args $parsed_flags $ops

# Set environment variables based on flags
set_debug_env $parsed_flags
```plaintext

### Available Flag Parsing

The `parse_common_flags` function normalizes these flags:

| Flag Record Field | Description |
|-------------------|-------------|
| `show_version` | Version display (`--version`, `-v`) |
| `show_info` | Info display (`--info`, `-i`) |
| `show_about` | About display (`--about`, `-a`) |
| `debug_mode` | Debug mode (`--debug`, `-x`) |
| `check_mode` | Check mode (`--check`, `-c`) |
| `auto_confirm` | Auto-confirm (`--yes`, `-y`) |
| `wait` | Wait for completion (`--wait`, `-w`) |
| `keep_storage` | Keep storage (`--keepstorage`) |
| `infra` | Infrastructure name (`--infra`) |
| `outfile` | Output file (`--outfile`) |
| `output_format` | Output format (`--out`) |
| `template` | Template name (`--template`) |
| `select` | Selection (`--select`) |
| `settings` | Settings file (`--settings`) |
| `new_infra` | New infra name (`--new`) |

### Adding New Flags

If you need to add a new flag:

1. **Update main `provisioning` file** to accept the flag
2. **Update `flags.nu:parse_common_flags`** to normalize it
3. **Update `flags.nu:build_module_args`** to pass it to modules

**Example: Adding `--timeout` flag**

```nushell
# 1. In provisioning main file (parameter list)
def main [
  # ... existing parameters
  --timeout: int = 300        # Timeout in seconds
  # ... rest of parameters
] {
  # ... existing code
  let parsed_flags = (parse_common_flags {
    # ... existing flags
    timeout: $timeout
  })
}

# 2. In flags.nu:parse_common_flags
export def parse_common_flags [flags: record]: nothing -> record {
  {
    # ... existing normalizations
    timeout: ($flags.timeout? | default 300)
  }
}

# 3. In flags.nu:build_module_args
export def build_module_args [flags: record, extra: string = ""]: nothing -> string {
  # ... existing code
  let str_timeout = if ($flags.timeout != 300) { $"--timeout ($flags.timeout) " } else { "" }
  # ... rest of function
  $"($extra) ($use_check)($use_yes)($use_wait)($str_timeout)..."
}
```plaintext

## Adding New Shortcuts

### Shortcut Naming Conventions

- **1-2 letters**: Ultra-short for common commands (`s` for server, `ws` for workspace)
- **3-4 letters**: Abbreviations (`orch` for orchestrator, `tmpl` for template)
- **Aliases**: Alternative names (`task` for taskserv, `flow` for workflow)

### Example: Adding a New Shortcut

Edit `provisioning/core/nulib/main_provisioning/dispatcher.nu`:

```nushell
export def get_command_registry []: nothing -> record {
  {
    # ... existing shortcuts

    # Add your new shortcut
    "db" => "infrastructure database"          # New: db command
    "database" => "infrastructure database"    # Full name

    # ... rest of registry
  }
}
```plaintext

**Important**: After adding a shortcut, update the help system in `help_system.nu` to document it.

## Testing Your Changes

### Running the Test Suite

```bash
# Run comprehensive test suite
nu tests/test_provisioning_refactor.nu
```plaintext

### Test Coverage

The test suite validates:

- ✅ Main help display
- ✅ Category help (infrastructure, orchestration, development, workspace)
- ✅ Bi-directional help routing
- ✅ All command shortcuts
- ✅ Category shortcut help
- ✅ Command routing to correct handlers

### Adding Tests for Your Changes

Edit `tests/test_provisioning_refactor.nu`:

```nushell
# Add your test function
export def test_my_new_feature [] {
  print "\n🧪 Testing my new feature..."

  let output = (run_provisioning "my-command" "test")
  assert_contains $output "Expected Output" "My command works"
}

# Add to main test runner
export def main [] {
  # ... existing tests

  let results = [
    # ... existing test calls
    (try { test_my_new_feature; "passed" } catch { "failed" })
  ]

  # ... rest of main
}
```plaintext

### Manual Testing

```bash
# Test command execution
provisioning/core/cli/provisioning my-command test --check

# Test with debug mode
provisioning/core/cli/provisioning --debug my-command test

# Test help
provisioning/core/cli/provisioning my-command help
provisioning/core/cli/provisioning help my-command  # Bi-directional
```plaintext

## Common Patterns

### Pattern 1: Simple Command Handler

**Use Case**: Command just needs to execute a module with standard flags

```nushell
def handle_simple_command [ops: string, flags: record] {
  let args = build_module_args $flags $ops
  run_module $args "module_name" --exec
}
```plaintext

### Pattern 2: Command with Validation

**Use Case**: Need to validate input before execution

```nushell
def handle_validated_command [ops: string, flags: record] {
  # Validate
  let first_arg = ($ops | split row " " | get -o 0)
  if ($first_arg | is-empty) {
    print "❌ Missing required argument"
    print "Usage: provisioning command <arg>"
    exit 1
  }

  # Execute
  let args = build_module_args $flags $ops
  run_module $args "module_name" --exec
}
```plaintext

### Pattern 3: Command with Subcommands

**Use Case**: Command has multiple subcommands (like `server create`, `server delete`)

```nushell
def handle_complex_command [ops: string, flags: record] {
  let subcommand = ($ops | split row " " | get -o 0)
  let rest_ops = ($ops | split row " " | skip 1 | str join " ")

  match $subcommand {
    "create" => { handle_create $rest_ops $flags }
    "delete" => { handle_delete $rest_ops $flags }
    "list" => { handle_list $rest_ops $flags }
    _ => {
      print "❌ Unknown subcommand: $subcommand"
      print "Available: create, delete, list"
      exit 1
    }
  }
}
```plaintext

### Pattern 4: Command with Flag-Based Routing

**Use Case**: Command behavior changes based on flags

```nushell
def handle_flag_routed_command [ops: string, flags: record] {
  if $flags.check_mode {
    # Dry-run mode
    print "🔍 Check mode: simulating command..."
    let args = build_module_args $flags $ops
    run_module $args "module_name" # No --exec, returns output
  } else {
    # Normal execution
    let args = build_module_args $flags $ops
    run_module $args "module_name" --exec
  }
}
```plaintext

## Best Practices

### 1. Keep Handlers Focused

Each handler should do **one thing well**:

- ✅ Good: `handle_server` manages all server operations
- ❌ Bad: `handle_server` also manages clusters and taskservs

### 2. Use Descriptive Error Messages

```nushell
# ❌ Bad
print "Error"

# ✅ Good
print "❌ Unknown taskserv: kubernetes-invalid"
print ""
print "Available taskservs:"
print "  • kubernetes"
print "  • containerd"
print "  • cilium"
print ""
print "Use 'provisioning taskserv list' to see all available taskservs"
```plaintext

### 3. Leverage Centralized Functions

Don't repeat code - use centralized functions:

```nushell
# ❌ Bad: Repeating flag handling
def handle_bad [ops: string, flags: record] {
  let use_check = if $flags.check_mode { "--check " } else { "" }
  let use_yes = if $flags.auto_confirm { "--yes " } else { "" }
  let str_infra = if ($flags.infra | is-not-empty) { $"--infra ($flags.infra) " } else { "" }
  # ... 10 more lines of flag handling
  run_module $"($ops) ($use_check)($use_yes)($str_infra)..." "module" --exec
}

# ✅ Good: Using centralized function
def handle_good [ops: string, flags: record] {
  let args = build_module_args $flags $ops
  run_module $args "module" --exec
}
```plaintext

### 4. Document Your Changes

Update relevant documentation:

- **ADR-006**: If architectural changes
- **CLAUDE.md**: If new commands or shortcuts
- **help_system.nu**: If new categories or commands
- **This guide**: If new patterns or conventions

### 5. Test Thoroughly

Before committing:

- [ ] Run test suite: `nu tests/test_provisioning_refactor.nu`
- [ ] Test manual execution
- [ ] Test with `--check` flag
- [ ] Test with `--debug` flag
- [ ] Test help: both `provisioning cmd help` and `provisioning help cmd`
- [ ] Test shortcuts

## Troubleshooting

### Issue: "Module not found"

**Cause**: Incorrect import path in handler

**Fix**: Use relative imports with `.nu` extension:

```nushell
# ✅ Correct
use ../flags.nu *
use ../../lib_provisioning *

# ❌ Wrong
use ../main_provisioning/flags *
use lib_provisioning *
```plaintext

### Issue: "Parse mismatch: expected colon"

**Cause**: Missing type signature format

**Fix**: Use proper Nushell 0.107 type signature:

```nushell
# ✅ Correct
export def my_function [param: string]: nothing -> string {
  "result"
}

# ❌ Wrong
export def my_function [param: string] -> string {
  "result"
}
```plaintext

### Issue: "Command not routing correctly"

**Cause**: Shortcut not in command registry

**Fix**: Add to `dispatcher.nu:get_command_registry`:

```nushell
"myshortcut" => "domain command"
```plaintext

### Issue: "Flags not being passed"

**Cause**: Not using `build_module_args`

**Fix**: Use centralized flag builder:

```nushell
let args = build_module_args $flags $ops
run_module $args "module" --exec
```plaintext

## Quick Reference

### File Locations

```plaintext
provisioning/core/nulib/
├── provisioning - Main entry, flag definitions
├── main_provisioning/
│   ├── flags.nu - Flag parsing (parse_common_flags, build_module_args)
│   ├── dispatcher.nu - Routing (get_command_registry, dispatch_command)
│   ├── help_system.nu - Help (provisioning-help, help-*)
│   └── commands/ - Domain handlers (handle_*_command)
tests/
└── test_provisioning_refactor.nu - Test suite
docs/
├── architecture/
│   └── ADR-006-provisioning-cli-refactoring.md - Architecture docs
└── development/
    └── COMMAND_HANDLER_GUIDE.md - This guide
```plaintext

### Key Functions

```nushell
# In flags.nu
parse_common_flags [flags: record]: nothing -> record
build_module_args [flags: record, extra: string = ""]: nothing -> string
set_debug_env [flags: record]
get_debug_flag [flags: record]: nothing -> string

# In dispatcher.nu
get_command_registry []: nothing -> record
dispatch_command [args: list, flags: record]

# In help_system.nu
provisioning-help [category?: string]: nothing -> string
help-infrastructure []: nothing -> string
help-orchestration []: nothing -> string
# ... (one for each category)

# In commands/*.nu
handle_*_command [command: string, ops: string, flags: record]
# Example: handle_infrastructure_command, handle_workspace_command
```plaintext

### Testing Commands

```bash
# Run full test suite
nu tests/test_provisioning_refactor.nu

# Test specific command
provisioning/core/cli/provisioning my-command test --check

# Test with debug
provisioning/core/cli/provisioning --debug my-command test

# Test help
provisioning/core/cli/provisioning help my-command
provisioning/core/cli/provisioning my-command help  # Bi-directional
```plaintext

## Further Reading

- **[ADR-006: CLI Refactoring](../architecture/adr/ADR-006-provisioning-cli-refactoring.md)** - Complete architectural decision record
- **[Project Structure](project-structure.md)** - Overall project organization
- **[Workflow Development](workflow.md)** - Workflow system architecture
- **[Development Integration](integration.md)** - Integration patterns

## Contributing

When contributing command handler changes:

1. **Follow existing patterns** - Use the patterns in this guide
2. **Update documentation** - Keep docs in sync with code
3. **Add tests** - Cover your new functionality
4. **Run test suite** - Ensure nothing breaks
5. **Update CLAUDE.md** - Document new commands/shortcuts

For questions or issues, refer to ADR-006 or ask the team.

---

*This guide is part of the provisioning project documentation. Last updated: 2025-09-30*
