# Command Handler Developer Guide\n\n**Target Audience**: Developers working on the provisioning CLI\n**Last Updated**: 2025-09-30\n**Related**: [ADR-006 CLI Refactoring](../architecture/adr/adr-006-provisioning-cli-refactoring.md)\n\n## Overview\n\nThe provisioning CLI uses a **modular, domain-driven architecture** that separates concerns into focused command handlers. This guide shows you how to\nwork with this architecture.\n\n### Key Architecture Principles\n\n1. **Separation of Concerns**: Routing, flag parsing, and business logic are separated\n2. **Domain-Driven Design**: Commands organized by domain (infrastructure, orchestration, etc.)\n3. **DRY (Don't Repeat Yourself)**: Centralized flag handling eliminates code duplication\n4. **Single Responsibility**: Each module has one clear purpose\n5. **Open/Closed Principle**: Easy to extend, no need to modify core routing\n\n### Architecture Components\n\n```\nprovisioning/core/nulib/\n├── provisioning (211 lines) - Main entry point\n├── main_provisioning/\n│   ├── flags.nu (139 lines) - Centralized flag handling\n│   ├── dispatcher.nu (264 lines) - Command routing\n│   ├── help_system.nu - Categorized help system\n│   └── commands/ - Domain-focused handlers\n│       ├── infrastructure.nu (117 lines) - Server, taskserv, cluster, infra\n│       ├── orchestration.nu (64 lines) - Workflow, batch, orchestrator\n│       ├── development.nu (72 lines) - Module, layer, version, pack\n│       ├── workspace.nu (56 lines) - Workspace, template\n│       ├── generation.nu (78 lines) - Generate commands\n│       ├── utilities.nu (157 lines) - SSH, SOPS, cache, providers\n│       └── configuration.nu (316 lines) - Env, show, init, validate\n```\n\n## Adding New Commands\n\n### Step 1: Choose the Right Domain Handler\n\nCommands are organized by domain. Choose the appropriate handler:\n\n| Domain | Handler | Responsibility |\n| -------- | --------- | ---------------- |\n| infrastructure | `infrastructure.nu` | Server/taskserv/cluster/infra lifecycle |\n| orchestration | `orchestration.nu` | Workflow/batch operations, orchestrator control |\n| development | `development.nu` | Module discovery, layers, versions, packaging |\n| workspace | `workspace.nu` | Workspace and template management |\n| configuration | `configuration.nu` | Environment, settings, initialization |\n| utilities | `utilities.nu` | SSH, SOPS, cache, providers, utilities |\n| generation | `generation.nu` | Generate commands (server, taskserv, etc.) |\n\n### Step 2: Add Command to Handler\n\n**Example: Adding a new server command `server status`**\n\nEdit `provisioning/core/nulib/main_provisioning/commands/infrastructure.nu`:\n\n```\n# Add to the handle_infrastructure_command match statement\nexport def handle_infrastructure_command [\n  command: string\n  ops: string\n  flags: record\n] {\n  set_debug_env $flags\n\n  match $command {\n    "server" => { handle_server $ops $flags }\n    "taskserv" | "task" => { handle_taskserv $ops $flags }\n    "cluster" => { handle_cluster $ops $flags }\n    "infra" | "infras" => { handle_infra $ops $flags }\n    _ => {\n      print $"❌ Unknown infrastructure command: ($command)"\n      print ""\n      print "Available infrastructure commands:"\n      print "  server      - Server operations (create, delete, list, ssh, status)"  # Updated\n      print "  taskserv    - Task service management"\n      print "  cluster     - Cluster operations"\n      print "  infra       - Infrastructure management"\n      print ""\n      print "Use 'provisioning help infrastructure' for more details"\n      exit 1\n    }\n  }\n}\n\n# Add the new command handler\ndef handle_server [ops: string, flags: record] {\n  let args = build_module_args $flags $ops\n  run_module $args "server" --exec\n}\n```\n\n**That's it!** The command is now available as `provisioning server status`.\n\n### Step 3: Add Shortcuts (Optional)\n\nIf you want shortcuts like `provisioning s status`:\n\nEdit `provisioning/core/nulib/main_provisioning/dispatcher.nu`:\n\n```\nexport def get_command_registry []: nothing -> record {\n  {\n    # Infrastructure commands\n    "s" => "infrastructure server"           # Already exists\n    "server" => "infrastructure server"      # Already exists\n\n    # Your new shortcut (if needed)\n    # Example: "srv-status" => "infrastructure server status"\n\n    # ... rest of registry\n  }\n}\n```\n\n**Note**: Most shortcuts are already configured. You only need to add new shortcuts if you're creating completely new command categories.\n\n## Modifying Existing Handlers\n\n### Example: Enhancing the `taskserv` Command\n\nLet's say you want to add better error handling to the taskserv command:\n\n**Before:**\n\n```\ndef handle_taskserv [ops: string, flags: record] {\n  let args = build_module_args $flags $ops\n  run_module $args "taskserv" --exec\n}\n```\n\n**After:**\n\n```\ndef handle_taskserv [ops: string, flags: record] {\n  # Validate taskserv name if provided\n  let first_arg = ($ops | split row " " | get -o 0)\n  if ($first_arg | is-not-empty) and $first_arg not-in ["create", "delete", "list", "generate", "check-updates", "help"] {\n    # Check if taskserv exists\n    let available_taskservs = (^$env.PROVISIONING_NAME module discover taskservs | from json)\n    if $first_arg not-in $available_taskservs {\n      print $"❌ Unknown taskserv: ($first_arg)"\n      print ""\n      print "Available taskservs:"\n      $available_taskservs | each { |ts| print $"  • ($ts)" }\n      exit 1\n    }\n  }\n\n  let args = build_module_args $flags $ops\n  run_module $args "taskserv" --exec\n}\n```\n\n## Working with Flags\n\n### Using Centralized Flag Handling\n\nThe `flags.nu` module provides centralized flag handling:\n\n```\n# Parse all flags into normalized record\nlet parsed_flags = (parse_common_flags {\n  version: $version, v: $v, info: $info,\n  debug: $debug, check: $check, yes: $yes,\n  wait: $wait, infra: $infra, # ... etc\n})\n\n# Build argument string for module execution\nlet args = build_module_args $parsed_flags $ops\n\n# Set environment variables based on flags\nset_debug_env $parsed_flags\n```\n\n### Available Flag Parsing\n\nThe `parse_common_flags` function normalizes these flags:\n\n| Flag Record Field | Description |\n| ------------------- | ------------- |\n| `show_version` | Version display (`--version`, `-v`) |\n| `show_info` | Info display (`--info`, `-i`) |\n| `show_about` | About display (`--about`, `-a`) |\n| `debug_mode` | Debug mode (`--debug`, `-x`) |\n| `check_mode` | Check mode (`--check`, `-c`) |\n| `auto_confirm` | Auto-confirm (`--yes`, `-y`) |\n| `wait` | Wait for completion (`--wait`, `-w`) |\n| `keep_storage` | Keep storage (`--keepstorage`) |\n| `infra` | Infrastructure name (`--infra`) |\n| `outfile` | Output file (`--outfile`) |\n| `output_format` | Output format (`--out`) |\n| `template` | Template name (`--template`) |\n| `select` | Selection (`--select`) |\n| `settings` | Settings file (`--settings`) |\n| `new_infra` | New infra name (`--new`) |\n\n### Adding New Flags\n\nIf you need to add a new flag:\n\n1. **Update main `provisioning` file** to accept the flag\n2. **Update `flags.nu:parse_common_flags`** to normalize it\n3. **Update `flags.nu:build_module_args`** to pass it to modules\n\n**Example: Adding `--timeout` flag**\n\n```\n# 1. In provisioning main file (parameter list)\ndef main [\n  # ... existing parameters\n  --timeout: int = 300        # Timeout in seconds\n  # ... rest of parameters\n] {\n  # ... existing code\n  let parsed_flags = (parse_common_flags {\n    # ... existing flags\n    timeout: $timeout\n  })\n}\n\n# 2. In flags.nu:parse_common_flags\nexport def parse_common_flags [flags: record]: nothing -> record {\n  {\n    # ... existing normalizations\n    timeout: ($flags.timeout? | default 300)\n  }\n}\n\n# 3. In flags.nu:build_module_args\nexport def build_module_args [flags: record, extra: string = ""]: nothing -> string {\n  # ... existing code\n  let str_timeout = if ($flags.timeout != 300) { $"--timeout ($flags.timeout) " } else { "" }\n  # ... rest of function\n  $"($extra) ($use_check)($use_yes)($use_wait)($str_timeout)..."\n}\n```\n\n## Adding New Shortcuts\n\n### Shortcut Naming Conventions\n\n- **1-2 letters**: Ultra-short for common commands (`s` for server, `ws` for workspace)\n- **3-4 letters**: Abbreviations (`orch` for orchestrator, `tmpl` for template)\n- **Aliases**: Alternative names (`task` for taskserv, `flow` for workflow)\n\n### Example: Adding a New Shortcut\n\nEdit `provisioning/core/nulib/main_provisioning/dispatcher.nu`:\n\n```\nexport def get_command_registry []: nothing -> record {\n  {\n    # ... existing shortcuts\n\n    # Add your new shortcut\n    "db" => "infrastructure database"          # New: db command\n    "database" => "infrastructure database"    # Full name\n\n    # ... rest of registry\n  }\n}\n```\n\n**Important**: After adding a shortcut, update the help system in `help_system.nu` to document it.\n\n## Testing Your Changes\n\n### Running the Test Suite\n\n```\n# Run comprehensive test suite\nnu tests/test_provisioning_refactor.nu\n```\n\n### Test Coverage\n\nThe test suite validates:\n\n- ✅ Main help display\n- ✅ Category help (infrastructure, orchestration, development, workspace)\n- ✅ Bi-directional help routing\n- ✅ All command shortcuts\n- ✅ Category shortcut help\n- ✅ Command routing to correct handlers\n\n### Adding Tests for Your Changes\n\nEdit `tests/test_provisioning_refactor.nu`:\n\n```\n# Add your test function\nexport def test_my_new_feature [] {\n  print "\n🧪 Testing my new feature..."\n\n  let output = (run_provisioning "my-command" "test")\n  assert_contains $output "Expected Output" "My command works"\n}\n\n# Add to main test runner\nexport def main [] {\n  # ... existing tests\n\n  let results = [\n    # ... existing test calls\n    (try { test_my_new_feature; "passed" } catch { "failed" })\n  ]\n\n  # ... rest of main\n}\n```\n\n### Manual Testing\n\n```\n# Test command execution\nprovisioning/core/cli/provisioning my-command test --check\n\n# Test with debug mode\nprovisioning/core/cli/provisioning --debug my-command test\n\n# Test help\nprovisioning/core/cli/provisioning my-command help\nprovisioning/core/cli/provisioning help my-command  # Bi-directional\n```\n\n## Common Patterns\n\n### Pattern 1: Simple Command Handler\n\n**Use Case**: Command just needs to execute a module with standard flags\n\n```\ndef handle_simple_command [ops: string, flags: record] {\n  let args = build_module_args $flags $ops\n  run_module $args "module_name" --exec\n}\n```\n\n### Pattern 2: Command with Validation\n\n**Use Case**: Need to validate input before execution\n\n```\ndef handle_validated_command [ops: string, flags: record] {\n  # Validate\n  let first_arg = ($ops | split row " " | get -o 0)\n  if ($first_arg | is-empty) {\n    print "❌ Missing required argument"\n    print "Usage: provisioning command <arg>"\n    exit 1\n  }\n\n  # Execute\n  let args = build_module_args $flags $ops\n  run_module $args "module_name" --exec\n}\n```\n\n### Pattern 3: Command with Subcommands\n\n**Use Case**: Command has multiple subcommands (like `server create`, `server delete`)\n\n```\ndef handle_complex_command [ops: string, flags: record] {\n  let subcommand = ($ops | split row " " | get -o 0)\n  let rest_ops = ($ops | split row " " | skip 1 | str join " ")\n\n  match $subcommand {\n    "create" => { handle_create $rest_ops $flags }\n    "delete" => { handle_delete $rest_ops $flags }\n    "list" => { handle_list $rest_ops $flags }\n    _ => {\n      print "❌ Unknown subcommand: $subcommand"\n      print "Available: create, delete, list"\n      exit 1\n    }\n  }\n}\n```\n\n### Pattern 4: Command with Flag-Based Routing\n\n**Use Case**: Command behavior changes based on flags\n\n```\ndef handle_flag_routed_command [ops: string, flags: record] {\n  if $flags.check_mode {\n    # Dry-run mode\n    print "🔍 Check mode: simulating command..."\n    let args = build_module_args $flags $ops\n    run_module $args "module_name" # No --exec, returns output\n  } else {\n    # Normal execution\n    let args = build_module_args $flags $ops\n    run_module $args "module_name" --exec\n  }\n}\n```\n\n## Best Practices\n\n### 1. Keep Handlers Focused\n\nEach handler should do **one thing well**:\n\n- ✅ Good: `handle_server` manages all server operations\n- ❌ Bad: `handle_server` also manages clusters and taskservs\n\n### 2. Use Descriptive Error Messages\n\n```\n# ❌ Bad\nprint "Error"\n\n# ✅ Good\nprint "❌ Unknown taskserv: kubernetes-invalid"\nprint ""\nprint "Available taskservs:"\nprint "  • kubernetes"\nprint "  • containerd"\nprint "  • cilium"\nprint ""\nprint "Use 'provisioning taskserv list' to see all available taskservs"\n```\n\n### 3. Leverage Centralized Functions\n\nDon't repeat code - use centralized functions:\n\n```\n# ❌ Bad: Repeating flag handling\ndef handle_bad [ops: string, flags: record] {\n  let use_check = if $flags.check_mode { "--check " } else { "" }\n  let use_yes = if $flags.auto_confirm { "--yes " } else { "" }\n  let str_infra = if ($flags.infra | is-not-empty) { $"--infra ($flags.infra) " } else { "" }\n  # ... 10 more lines of flag handling\n  run_module $"($ops) ($use_check)($use_yes)($str_infra)..." "module" --exec\n}\n\n# ✅ Good: Using centralized function\ndef handle_good [ops: string, flags: record] {\n  let args = build_module_args $flags $ops\n  run_module $args "module" --exec\n}\n```\n\n### 4. Document Your Changes\n\nUpdate relevant documentation:\n\n- **ADR-006**: If architectural changes\n- **CLAUDE.md**: If new commands or shortcuts\n- **help_system.nu**: If new categories or commands\n- **This guide**: If new patterns or conventions\n\n### 5. Test Thoroughly\n\nBefore committing:\n\n- [ ] Run test suite: `nu tests/test_provisioning_refactor.nu`\n- [ ] Test manual execution\n- [ ] Test with `--check` flag\n- [ ] Test with `--debug` flag\n- [ ] Test help: both `provisioning cmd help` and `provisioning help cmd`\n- [ ] Test shortcuts\n\n## Troubleshooting\n\n### Issue: "Module not found"\n\n**Cause**: Incorrect import path in handler\n\n**Fix**: Use relative imports with `.nu` extension:\n\n```\n# ✅ Correct\nuse ../flags.nu *\nuse ../../lib_provisioning *\n\n# ❌ Wrong\nuse ../main_provisioning/flags *\nuse lib_provisioning *\n```\n\n### Issue: "Parse mismatch: expected colon"\n\n**Cause**: Missing type signature format\n\n**Fix**: Use proper Nushell 0.107 type signature:\n\n```\n# ✅ Correct\nexport def my_function [param: string]: nothing -> string {\n  "result"\n}\n\n# ❌ Wrong\nexport def my_function [param: string] -> string {\n  "result"\n}\n```\n\n### Issue: "Command not routing correctly"\n\n**Cause**: Shortcut not in command registry\n\n**Fix**: Add to `dispatcher.nu:get_command_registry`:\n\n```\n"myshortcut" => "domain command"\n```\n\n### Issue: "Flags not being passed"\n\n**Cause**: Not using `build_module_args`\n\n**Fix**: Use centralized flag builder:\n\n```\nlet args = build_module_args $flags $ops\nrun_module $args "module" --exec\n```\n\n## Quick Reference\n\n### File Locations\n\n```\nprovisioning/core/nulib/\n├── provisioning - Main entry, flag definitions\n├── main_provisioning/\n│   ├── flags.nu - Flag parsing (parse_common_flags, build_module_args)\n│   ├── dispatcher.nu - Routing (get_command_registry, dispatch_command)\n│   ├── help_system.nu - Help (provisioning-help, help-*)\n│   └── commands/ - Domain handlers (handle_*_command)\ntests/\n└── test_provisioning_refactor.nu - Test suite\ndocs/\n├── architecture/\n│   └── adr-006-provisioning-cli-refactoring.md - Architecture docs\n└── development/\n    └── COMMAND_HANDLER_GUIDE.md - This guide\n```\n\n### Key Functions\n\n```\n# In flags.nu\nparse_common_flags [flags: record]: nothing -> record\nbuild_module_args [flags: record, extra: string = ""]: nothing -> string\nset_debug_env [flags: record]\nget_debug_flag [flags: record]: nothing -> string\n\n# In dispatcher.nu\nget_command_registry []: nothing -> record\ndispatch_command [args: list, flags: record]\n\n# In help_system.nu\nprovisioning-help [category?: string]: nothing -> string\nhelp-infrastructure []: nothing -> string\nhelp-orchestration []: nothing -> string\n# ... (one for each category)\n\n# In commands/*.nu\nhandle_*_command [command: string, ops: string, flags: record]\n# Example: handle_infrastructure_command, handle_workspace_command\n```\n\n### Testing Commands\n\n```\n# Run full test suite\nnu tests/test_provisioning_refactor.nu\n\n# Test specific command\nprovisioning/core/cli/provisioning my-command test --check\n\n# Test with debug\nprovisioning/core/cli/provisioning --debug my-command test\n\n# Test help\nprovisioning/core/cli/provisioning help my-command\nprovisioning/core/cli/provisioning my-command help  # Bi-directional\n```\n\n## Further Reading\n\n- **[ADR-006: CLI Refactoring](../architecture/adr/adr-006-provisioning-cli-refactoring.md)** - Complete architectural decision record\n- **[Project Structure](project-structure.md)** - Overall project organization\n- **[Workflow Development](workflow.md)** - Workflow system architecture\n- **[Development Integration](integration.md)** - Integration patterns\n\n## Contributing\n\nWhen contributing command handler changes:\n\n1. **Follow existing patterns** - Use the patterns in this guide\n2. **Update documentation** - Keep docs in sync with code\n3. **Add tests** - Cover your new functionality\n4. **Run test suite** - Ensure nothing breaks\n5. **Update CLAUDE.md** - Document new commands/shortcuts\n\nFor questions or issues, refer to ADR-006 or ask the team.\n\n---\n\n*This guide is part of the provisioning project documentation. Last updated: 2025-09-30*
