602 lines
17 KiB
Markdown
602 lines
17 KiB
Markdown
|
|
# 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
|
||
|
|
|
||
|
|
```
|
||
|
|
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
|
||
|
|
```
|
||
|
|
|
||
|
|
## 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
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**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
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**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
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**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
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 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
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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)..."
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 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
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**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
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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
|
||
|
|
```
|
||
|
|
|
||
|
|
## 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
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 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"
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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 *
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Issue: "Command not routing correctly"
|
||
|
|
|
||
|
|
**Cause**: Shortcut not in command registry
|
||
|
|
|
||
|
|
**Fix**: Add to `dispatcher.nu:get_command_registry`:
|
||
|
|
```nushell
|
||
|
|
"myshortcut" => "domain command"
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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
|
||
|
|
```
|
||
|
|
|
||
|
|
## Quick Reference
|
||
|
|
|
||
|
|
### File Locations
|
||
|
|
|
||
|
|
```
|
||
|
|
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
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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
|
||
|
|
```
|
||
|
|
|
||
|
|
## 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*
|