hx-plugins 0.7.16

Plugin system for hx using Steel Scheme
Documentation
# Custom Commands

Plugins can register custom commands that appear as `hx <command>`.

## Defining Commands

### In Rust

Use the `CustomCommand` struct:

```rust
use hx_plugins::{CustomCommand, CommandBuilder};

// Simple construction
let cmd = CustomCommand::new("deploy")
    .with_description("Deploy the application")
    .with_usage("hx deploy [--env <environment>]")
    .with_source("deploy-plugin");

// Using the builder
let cmd = CommandBuilder::new("deploy")
    .description("Deploy the application")
    .usage("hx deploy [--env <environment>]")
    .source("deploy-plugin")
    .build();
```

### In Steel

Commands are registered using the prelude API:

```scheme
;; plugins/deploy.scm

(hx/register-command
  "deploy"
  "Deploy the application to production"
  (lambda (args)
    (hx/status "Deploy" "Starting deployment...")

    ;; Parse arguments
    (define env (if (member "--prod" args) "production" "staging"))

    ;; Run deployment
    (let ((result (hx/run "deploy.sh" (list env))))
      (if (= 0 (cadr (assoc 'exit-code result)))
          (begin
            (hx/info "Deployment successful")
            0)
          (begin
            (hx/error "Deployment failed")
            1)))))
```

## CustomCommand Structure

```rust
pub struct CustomCommand {
    /// Command name (what users type after `hx`)
    pub name: String,

    /// Description shown in help
    pub description: String,

    /// Usage string for help
    pub usage: Option<String>,

    /// Source plugin that registered this command
    pub source_plugin: Option<String>,
}
```

## Running Custom Commands

### Via Plugin Manager

```rust
use hx_plugins::{PluginManager, PluginConfig};

let mut manager = PluginManager::new(config)?;
manager.initialize()?;
manager.load_all(&project_root)?;

// Check if command exists
if manager.has_command("deploy") {
    // Run the command
    let exit_code = manager.run_command("deploy", &["--prod".to_string()])?;
    std::process::exit(exit_code);
}
```

### Via CLI

```bash
# Run custom command
hx deploy --prod

# Get help
hx deploy --help
```

## Command Discovery

List all available custom commands:

```rust
let commands = manager.commands();
for (name, cmd) in commands {
    println!("  {} - {}", name, cmd.description);
}
```

## Help Text Generation

Commands automatically generate help text:

```rust
let help = command.help_text();
// Output:
// Deploy the application
//
// Usage: hx deploy [--env <environment>]
//
// (Defined by: deploy-plugin)
```

## Best Practices

### 1. Clear Naming

Choose command names that are:
- Short but descriptive
- Don't conflict with built-in commands
- Use lowercase with hyphens

```scheme
;; Good
"run-tests"
"deploy"
"check-deps"

;; Bad
"rt"           ; Too cryptic
"Build"        ; Conflicts with built-in
"runAllTests"  ; Use hyphens
```

### 2. Argument Handling

Parse arguments consistently:

```scheme
(define (parse-args args)
  (define env "staging")
  (define verbose #f)

  (for-each
    (lambda (arg)
      (cond
        ((string=? arg "--prod") (set! env "production"))
        ((string=? arg "--staging") (set! env "staging"))
        ((string=? arg "-v") (set! verbose #t))))
    args)

  (list (cons 'env env) (cons 'verbose verbose)))
```

### 3. Exit Codes

Return meaningful exit codes:

```scheme
(define (my-command args)
  (cond
    ((check-success?) 0)    ; Success
    ((check-warning?) 0)    ; Warning but OK
    ((check-error?) 1)      ; General error
    (else 1)))              ; Unknown error
```

### 4. User Feedback

Provide clear status updates:

```scheme
(define (deploy args)
  (hx/status "Deploy" "Validating configuration...")

  (if (not (valid-config?))
      (begin
        (hx/error "Invalid configuration")
        (hx/info "Run 'hx deploy --check' to validate")
        1)
      (begin
        (hx/status "Deploy" "Building artifacts...")
        ;; ... continue
        )))
```

### 5. Documentation

Include usage information:

```rust
CustomCommand::new("deploy")
    .with_description("Deploy the application to a target environment")
    .with_usage("hx deploy [OPTIONS]\n\n\
                 Options:\n\
                   --prod       Deploy to production\n\
                   --staging    Deploy to staging (default)\n\
                   --dry-run    Show what would be deployed\n\
                   -v           Verbose output")
```

## Example: Full Custom Command

```scheme
;; plugins/release.scm
;; Custom command for creating releases

(define (on-init)
  (hx/register-command
    "release"
    "Create a new release"
    release-command))

(define (release-command args)
  (define version (if (null? args) #f (car args)))

  (if (not version)
      (begin
        (hx/error "Usage: hx release <version>")
        (hx/info "Example: hx release 1.2.0")
        2)  ; Usage error
      (create-release version)))

(define (create-release version)
  (hx/status "Release" (string-append "Creating release v" version))

  ;; 1. Run tests
  (hx/info "Running tests...")
  (let ((test-result (hx/run-silent "hx" '("test"))))
    (if (not (= 0 test-result))
        (begin
          (hx/error "Tests failed - aborting release")
          1)
        (continue-release version))))

(define (continue-release version)
  ;; 2. Update version in .cabal
  (hx/status "Release" "Updating version...")
  (let ((cabal (hx/cabal-file)))
    (if cabal
        (update-cabal-version cabal version)
        (hx/warn "No .cabal file found")))

  ;; 3. Create git tag
  (hx/status "Release" "Creating git tag...")
  (let ((tag-result (hx/run "git" (list "tag" "-a"
                                         (string-append "v" version)
                                         "-m" (string-append "Release " version)))))
    (if (= 0 (cadr (assoc 'exit-code tag-result)))
        (begin
          (hx/info (string-append "Created tag v" version))
          (hx/info "Run 'git push --tags' to publish")
          0)
        (begin
          (hx/error "Failed to create tag")
          1))))

(define (update-cabal-version cabal-file version)
  (let ((content (hx/read-file cabal-file)))
    ;; Simple version replacement
    ;; In real code, use proper parsing
    (hx/debug (string-append "Would update " cabal-file " to " version))))
```

## Integration with CLI

Custom commands integrate with the hx CLI:

```bash
# List commands (including custom)
hx --help

# Run custom command
hx release 1.0.0

# Custom command with flags
hx deploy --prod --verbose
```

The CLI automatically routes unknown commands to the plugin system.