# Nushell Code Rules and Patterns for AI Agents

## Fundamental Rules for AI-Friendly Nushell Code

### Rule 1: One Command, One Purpose
Every command must do exactly one thing. AI agents struggle with multi-purpose functions.

```nushell
# ✅ GOOD - Single purpose
def extract-errors [log_file: path] -> table {
    open $log_file | lines | parse "{time} [{level}] {msg}" | where level == "ERROR"
}

# ❌ BAD - Multiple purposes
def process-logs [log_file: path, --extract-errors, --count-warnings, --save-output] {
    # Too many responsibilities
}
```

### Rule 2: Explicit Type Signatures Always
AI agents need clear contracts. Never omit types.

```nushell
# ✅ GOOD - Complete type information
def calculate-average [numbers: list<float>] -> float {
    $numbers | math avg
}

# ❌ BAD - Missing type information  
def calculate-average [numbers] {
    $numbers | math avg
}
```

### Rule 3: Return Early, Fail Fast
Check preconditions immediately. Don't nest error handling.

```nushell
# ✅ GOOD - Early returns
def process-file [path: path] -> table {
    if not ($path | path exists) {
        error make {msg: $"File not found: ($path)"}
    }
    
    if (ls $path | get size.0) > 100mb {
        error make {msg: "File too large"}
    }
    
    open $path | process-data
}

# ❌ BAD - Nested conditionals
def process-file [path: path] -> table {
    if ($path | path exists) {
        if (ls $path | get size.0) <= 100mb {
            open $path | process-data
        } else {
            error make {msg: "File too large"}
        }
    } else {
        error make {msg: "File not found"}
    }
}
```

## Essential Patterns for AI Code Generation

### Pattern 1: Command Template Pattern
Use this structure for EVERY command:

```nushell
# [PURPOSE]: Single-line description of what this does
# [INPUT]: Expected input format
# [OUTPUT]: Output format
# [EXAMPLE]: command-name "arg1" --flag value
def command-name [
    required_param: type      # Description of parameter
    optional_param?: type     # Optional parameter
    --flag: type = default    # Flag with default value
] -> return_type {
    # Step 1: Validate inputs
    # validation code here
    
    # Step 2: Process data
    # processing code here
    
    # Step 3: Return result
    # return statement
}
```

### Pattern 2: Pipeline Stage Pattern
Each pipeline stage must be self-contained and testable:

```nushell
# ✅ GOOD - Clear pipeline stages
def analyze-data [input: path] -> table {
    load-data $input
    | validate-schema
    | clean-missing-values
    | calculate-statistics
    | format-output
}

# Each stage is a separate function
def load-data [path: path] -> table { open $path }
def validate-schema [data: table] -> table { $data | where column1 != null }
def clean-missing-values [data: table] -> table { $data | fill-null 0 }
def calculate-statistics [data: table] -> table { $data | group-by category | aggregate }
def format-output [data: table] -> table { $data | select relevant_columns }
```

### Pattern 3: Error Context Pattern
Always provide context for errors that AI can use to fix issues:

in Nushell 0.108, try-catch with error parameter might not be supported when assigning to variables.

```nushell
# ✅ GOOD - Detailed error context
def parse-config [config_path: path] -> record {
    try {
        open $config_path | from json
    } catch {|e|
        error make {
            msg: $"Failed to parse config"
            label: {
                text: $"Invalid JSON at ($config_path)"
                span: (metadata $config_path).span
            }
            help: "Check JSON syntax with: open $config_path | from json"
        }
    }
}
```

### Pattern 4: Data Validation Pattern
Validate data structure at boundaries:

```nushell
# ✅ GOOD - Explicit validation
def process-user-data [data: table] -> table {
    # Define expected schema
    let required_columns = ["id", "name", "email", "age"]
    let actual_columns = ($data | columns)
    
    # Validate columns exist
    for col in $required_columns {
        if $col not-in $actual_columns {
            error make {msg: $"Missing required column: ($col)"}
        }
    }
    
    # Validate data types
    $data | each {|row|
        if ($row.age | describe) != "int" {
            error make {msg: $"Invalid age type for id ($row.id)"}
        }
        $row
    }
}
```

## Critical Rules for AI Tool Integration

### Rule 4: No Side Effects in Functions
Functions must be pure unless explicitly named as mutations:

```nushell
# ✅ GOOD - Pure function
def calculate-tax [amount: float, rate: float] -> float {
    $amount * $rate
}

# ✅ GOOD - Mutation clearly indicated
def write-to-file! [data: any, path: path] -> nothing {
    $data | save $path
    null
}

# ❌ BAD - Hidden side effect
def calculate-tax [amount: float, rate: float] -> float {
    let result = $amount * $rate
    $result | save "tax_log.txt" --append  # Hidden side effect!
    $result
}
```

### Rule 5: Atomic Operations
Every operation must be atomic - it either completely succeeds or completely fails:

```nushell
# ✅ GOOD - Atomic operation
def update-json-file [path: path, updates: record] -> nothing {
    # Read, modify, write as single operation
    let original = try { open $path } catch { {} }
    let updated = ($original | merge $updates)
    
    try {
        $updated | save $path
    } catch {|e|
        error make {
            msg: $"Failed to update ($path)"
            help: "Ensure file is writable and valid JSON"
        }
    }
    null
}
```

### Rule 6: Explicit Dependencies
Never rely on global state or environment without declaring it:

```nushell
# ✅ GOOD - Explicit dependencies
def api-request [
    endpoint: string
    --api-key: string = ""  # Will use env if not provided
    --base-url: string = ""
] -> any {
    let key = if $api_key == "" { $env.API_KEY } else { $api_key }
    let url = if $base_url == "" { $env.BASE_URL } else { $base_url }
    
    http get $"($url)/($endpoint)" --headers {Authorization: $"Bearer ($key)"}
}

# ❌ BAD - Hidden dependencies
def api-request [endpoint: string] -> any {
    http get $"($env.BASE_URL)/($endpoint)" --headers {Authorization: $"Bearer ($env.API_KEY)"}
}
```

## Structured Data Patterns

### Pattern 5: Table Transformation Pattern
Always use Nushell's table operations instead of loops:

```nushell
# ✅ GOOD - Table operations
def transform-sales [sales: table] -> table {
    $sales
    | insert quarter {|row| ($row.date | date quarter)}
    | insert profit {|row| $row.revenue - $row.cost}
    | group-by quarter
    | transpose quarter data  
    | insert total {|row| $row.data | get profit | math sum}
}

# ❌ BAD - Manual iteration
def transform-sales [sales: table] -> table {
    let result = []
    for row in $sales {
        let quarter = ($row.date | date quarter)
        let profit = $row.revenue - $row.cost
        # Manual accumulation
    }
    $result
}
```

### Pattern 6: Schema Definition Pattern
Define data schemas explicitly for AI understanding:

```nushell
# Schema definitions at module level
const USER_SCHEMA = {
    id: "int"
    name: "string"  
    email: "string"
    created_at: "datetime"
    active: "bool"
}

const API_RESPONSE_SCHEMA = {
    status: "int"
    data: "table"
    error: "string?"  # ? indicates optional
}

# Use schemas in functions
def validate-user [user: record] -> bool {
    # Check against schema
    for key in ($USER_SCHEMA | columns) {
        if $key not-in ($user | columns) {
            return false
        }
    }
    true
}
```

## AI-Specific Patterns

### Pattern 7: Self-Documenting Code Pattern
Include inline documentation that AI can parse:

```nushell
def complex-calculation [
    data: table  # @format: [{x: float, y: float, weight: float}]
] -> record {  # @returns: {mean: float, std: float, correlation: float}
    # @algorithm: Weighted Pearson correlation
    # @performance: O(n) time, O(1) space
    
    let weighted_mean_x = (
        $data 
        | reduce -f 0 {|row acc| $acc + ($row.x * $row.weight)}
        | $in / ($data | get weight | math sum)
    )
    
    # ... rest of calculation
    
    # @output: Statistical measures
    {
        mean: $weighted_mean_x
        std: $std_dev
        correlation: $correlation
    }
}
```

### Pattern 8: Testable Unit Pattern
Every function must include test examples:

```nushell
# Function with embedded test cases
def parse-version [version: string] -> record {
    # @test: "1.2.3" -> {major: 1, minor: 2, patch: 3}
    # @test: "2.0.0-beta" -> {major: 2, minor: 0, patch: 0}
    
    $version
    | parse "{major}.{minor}.{patch}"
    | get 0
    | update major {|x| $x.major | into int}
    | update minor {|x| $x.minor | into int}
    | update patch {|x| $x.patch | into int}
}

# Separate test function
def test-parse-version [] {
    assert ((parse-version "1.2.3") == {major: 1, minor: 2, patch: 3})
    assert ((parse-version "2.0.0") == {major: 2, minor: 0, patch: 0})
}
```

### Pattern 9: Incremental Computation Pattern
Break complex computations into verifiable steps:

```nushell
# ✅ GOOD - Each step is verifiable
def analyze-dataset [data: path] -> record {
    # Load and get shape
    let dataset = (open $data)
    let shape = {rows: ($dataset | length), cols: ($dataset | columns | length)}
    print $"Loaded: ($shape.rows) rows, ($shape.cols) columns"
    
    # Check for missing values
    let missing = (check-missing $dataset)
    print $"Missing values: ($missing)"
    
    # Calculate statistics
    let stats = (calculate-stats $dataset)
    print $"Statistics calculated"
    
    # Generate report
    {
        shape: $shape
        missing: $missing  
        statistics: $stats
        generated_at: (date now)
    }
}
```

## Module Organization Rules

### Rule 7: Single Responsibility Modules
Each module handles one domain:

```nushell
# file: data_validation.nu
module data_validation {
    export def validate-email [email: string] -> bool { }
    export def validate-phone [phone: string] -> bool { }
    export def validate-date [date: string] -> bool { }
}

# file: data_transformation.nu  
module data_transformation {
    export def normalize-text [text: string] -> string { }
    export def parse-csv [path: path] -> table { }
    export def to-json [data: any] -> string { }
}
```

### Rule 8: Explicit Exports
Only export what's needed:

```nushell
module api_client {
    # Public API
    export def get [endpoint: string] -> any {
        make-request "GET" $endpoint
    }
    
    export def post [endpoint: string, data: any] -> any {
        make-request "POST" $endpoint $data
    }
    
    # Private helper - not exported
    def make-request [method: string, endpoint: string, data?: any] -> any {
        # Implementation
    }
}
```

## Performance Rules for AI Tools

### Rule 9: Lazy Evaluation
Don't compute until necessary:

```nushell
# ✅ GOOD - Lazy evaluation
def process-conditionally [
    data: table
    --expensive-analysis: bool = false
] -> any {
    # Quick return for common case
    if not $expensive_analysis {
        return ($data | first 10)
    }
    
    # Expensive computation only when needed
    $data 
    | complex-analysis
    | generate-report
}
```

### Rule 10: Stream Large Data
Never load entire large files into memory:

```nushell
# ✅ GOOD - Streaming
def process-large-file [path: path] -> nothing {
    open --raw $path
    | lines
    | each {|line|
        $line | parse-and-process
    }
    | save output.jsonl
    null
}

# ❌ BAD - Loading everything
def process-large-file [path: path] -> table {
    let all_data = (open $path)  # Loads entire file
    $all_data | process-all
}
```

## Error Handling Rules

### Rule 11: Never Swallow Errors
Always propagate or handle errors explicitly:

```nushell
# ✅ GOOD - Explicit error handling
def safe-divide [a: float, b: float] -> float {
    if $b == 0 {
        error make {msg: "Division by zero"}
    }
    $a / $b
}

# ❌ BAD - Silent failure
def safe-divide [a: float, b: float] -> float {
    if $b == 0 { 0 } else { $a / $b }  # Hiding error!
}
```

### Rule 12: Structured Error Returns
Use consistent error structures:

```nushell
# Define error type
const ERROR_SCHEMA = {
    success: "bool"
    error: "string?"
    data: "any?"
    timestamp: "datetime"
}

def api-call [url: string] -> record {
    try {
        let response = (http get $url)
        {
            success: true
            error: null
            data: $response
            timestamp: (date now)
        }
    } catch {|e|
        {
            success: false
            error: $e.msg
            data: null
            timestamp: (date now)
        }
    }
}
```

## Code Generation Rules for AI

### Rule 13: Predictable Naming
Use consistent, predictable names:

```nushell
# Naming conventions AI can predict:
# - get-*     : Returns data without modification
# - set-*     : Updates data
# - is-*      : Returns boolean
# - has-*     : Checks existence
# - find-*    : Searches for items
# - create-*  : Creates new items
# - delete-*  : Removes items
# - update-*  : Modifies existing items
# - validate-*: Checks validity
# - parse-*   : Converts formats
# - format-*  : Outputs in specific format

def get-user [id: int] -> record { }
def is-valid-email [email: string] -> bool { }
def find-duplicates [list: list] -> list { }
def create-backup [path: path] -> path { }
def parse-json [text: string] -> any { }
```

### Rule 14: Consistent Parameter Order
Always use this parameter order:

```nushell
# Order: required positional, optional positional, flags
def command [
    required1: type        # Required parameters first
    required2: type
    optional?: type        # Optional parameters second
    --flag1: type          # Boolean flags
    --flag2: type = value  # Flags with defaults
] -> return_type { }
```

### Rule 15: Return Type Consistency
Use consistent return types for similar operations:

```nushell
# All getters return record or null
def get-config [] -> record? {
    try { open config.json } catch { null }
}

# All validators return bool
def validate-data [data: any] -> bool {
    # validation logic
    true  # or false
}

# All transformers return same type as input
def transform-table [input: table] -> table {
    $input | modifications
}
```

### Rule 16: Function Signature Syntax with Pipeline Types (Nushell 0.107.1+)
**CRITICAL**: When using return type annotations, use the pipeline signature: `[params]: input_type -> return_type`

```nushell
# ✅ GOOD - Complete pipeline signature
def process-data [input: string]: nothing -> table {
    $input | from json
}

def calculate-sum [numbers: list<int>]: nothing -> int {
    $numbers | math sum
}

def check-status []: nothing -> bool {
    true
}

# ❌ BAD - Missing arrow (syntax error - will not parse!)
def process-data [input: string]: table {
    $input | from json
}

# ⚠️ ALTERNATIVE - Omit return type entirely if unsure
def process-data [input: string] {
    $input | from json
}
```

**Note**: The syntax is `[parameters]: input_type -> return_type`. Both the colon AND arrow are required when specifying return types. This has been the syntax since Nushell 0.107.1.

### Rule 17: String Interpolation - Always Use Parentheses
**CRITICAL**: In string interpolations, ALWAYS use parentheses `($var)` or `($expr)` for ALL interpolations.

```nushell
# ✅ GOOD - Parentheses for variables
print $"Processing file ($filename) at ($timestamp)"
print $"Server ($hostname) running on port ($port)"
print $"User ($username) has ($count) items"

# ✅ GOOD - Parentheses for expressions and function calls
print $"Total: (1 + 2 + 3)"
print $"Date: (date now | format date '%Y-%m-%d')"
print $"Size: ($data | length) items"

# ❌ BAD - Square brackets DO NOT interpolate!
print $"Processing file [$filename] at [$timestamp]"
# Output: "Processing file [$filename] at [$timestamp]" (LITERAL!)

# ❌ BAD - Without parentheses
print $"Server $hostname on port $port"
# This will cause a parse error
```

**Why**:
- Parentheses `($var)` or `($expr)` are the ONLY way to interpolate in Nushell strings
- Square brackets `[...]` are treated as literal characters (no interpolation)
- Both variables and expressions use the same syntax: `($something)`
- Consistent syntax reduces errors and improves maintainability

**Rule of thumb**:
- Variable? Use `($var)`
- Expression? Use `($expr)`
- Function call? Use `($fn arg)`
- **NEVER** use square brackets for interpolation!

### Rule 18: Escape Literal Parentheses in Interpolated Strings ⚠️

**CRITICAL**: In `$"..."` strings, parentheses that are NOT variable interpolation MUST be escaped.

❌ **WRONG**:
```nushell
let msg = $"Update? (yes/no): "  # ERROR: (yes/no) treated as command
let text = $"Use format (JSON/YAML)"  # ERROR: tries to execute command
```

✅ **CORRECT**:
```nushell
let msg = $"Update? \(yes/no\): "      # Escaped literal parentheses
let text = $"Use format \(JSON/YAML\)" # Escaped literal parentheses
let msg = $"Value: ($var)"             # Unescaped for variable interpolation
```

**Simple rule**:
- If `()` contains a variable name → keep as is: `($variable)`
- If `()` is literal text → escape with backslash: `\(text\)`

**Common mistake patterns:**
```nushell
# BAD - will try to execute 'yes/no' command
input $"Continue? (yes/no): "

# GOOD - escaped literal parentheses
input $"Continue? \(yes/no\): "

# BAD - will try to execute 'example' command
log_info $"Format (example): data"

# GOOD - escaped
log_info $"Format \(example\): data"
```

---

## Quick Reference Card for AI Agents

```nushell
# TEMPLATE: Copy-paste this for new commands (Nushell 0.107.1+)
# [PURPOSE]:
# [INPUT]:
# [OUTPUT]:
# [EXAMPLE]:
def command-name [
    param: type  # Description
]: nothing -> return_type {
    # NOTE: Use `: nothing -> return_type` for pipeline signature

    # Validate
    if VALIDATION_FAILS {
        error make {msg: $"Clear error message for ($param)"}
        # NOTE: ALWAYS use ($var) for string interpolation
    }

    # Process
    let result = $param | pipeline

    # Return
    $result
}

# COMMON PATTERNS
# Load file: open $path
# Save file: $data | save $path
# Parse JSON: open $path | from json
# Parse CSV: open $path | from csv
# Filter table: $table | where column == value
# Transform: $table | update column {|row| expression}
# Group: $table | group-by column
# Sort: $table | sort-by column
# Select columns: $table | select col1 col2
# Check existence: $path | path exists
# Get env: $env.VAR_NAME? | default "value"
```

## Summary Checklist for AI-Compatible Nushell

✅ **Every function has:**
- Explicit type signatures with pipeline syntax `[param: type]: input_type -> return_type {`
- Single responsibility
- Early validation
- Clear error messages with `($var)` for ALL string interpolations
- Test examples

✅ **Never use:**
- Global state without declaration
- Hidden side effects
- Nested conditionals (prefer early returns)
- Manual loops for table operations
- Generic error messages
- Try-catch with error parameter in variable assignments (use `do { } | complete`)
- Square brackets `[$var]` for string interpolation (they don't work!)
- Function signatures without both colon AND arrow when specifying return types

✅ **Always prefer:**
- Pipeline operations over loops
- Pure functions over stateful
- Explicit over implicit
- Composition over complexity
- Streaming over loading
- Parentheses `($var)` for ALL string interpolations (variables and expressions)

✅ **For AI tools specifically:**
- Use predictable naming patterns
- Include operation markers (! for mutations)
- Document schemas inline
- Provide test cases
- Return consistent types
- Follow Nushell 0.107.1+ syntax requirements (colon + arrow for return types)

Following these rules and patterns ensures that AI agents like Claude Code can effectively read, understand, generate, and modify your Nushell code with high accuracy and reliability.

1. Try-Catch Block Pattern (10 files)

  - Issue: Nushell 0.108 has stricter parsing for try-catch blocks
  - Solution: Replace try {...} catch {...} with complete-based error handling:
  let result = (do { ... } | complete)
  if $result.exit_code == 0 { $result.stdout } else { error_value }
  - Files fixed:
    - workspace/version.nu
    - workspace/migration.nu (4 blocks)
    - user/config.nu
    - config/loader.nu
    - oci/client.nu (8 blocks - OCI currently disabled)

2. Function Signature Syntax (2 instances)

  - Issue: Missing input type in signatures
  - Old: def foo [x: string] -> bool
  - New: def foo [x: string]: nothing -> bool
  - Files fixed: workspace/helpers.nu

3. Boolean Flag Syntax (1 instance)

  - Issue: Type annotations not allowed on boolean flags
  - Old: --flag: bool = true
  - New: --flag = true
  - Files fixed: main_provisioning/contexts.nu

  4. Variable Type Initialization (1 instance)

  - Issue: Can't assign record to null variable in 0.108
  - Old: mut var = null; $var = {record}
  - New: mut var = {success: false}; $var = {record}


  #" Before (fails in Nushell 0.107.1)
  try {
      # operations
      result
  } catch { |err|
      log-error $"Failed: ($err.msg)"
      default_value
  }

  # After (works in Nushell 0.107.1)
  let result = (do {
      # operations
      result
  } | complete)

  if $result.exit_code == 0 {
      $result.stdout
  } else {
      log-error $"Failed: ($result.stderr)"
      default_value
  }
