2025-10-19 00:05:16 +01:00
# 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!
# Summary
fix: help system integration, build process optimization, and plugin rebuild efficiency
## Detailed Description
This commit addresses critical issues in the help system discoverability, build process robustness, and plugin rebuild efficiency.
### 1. Help System Integration (New Feature)
**Issue**: Version-update module recipes were not discoverable
- Not shown in `just help modules`
- Not referenced in `just help`
- Not included in help navigation system
- Users had to manually run `just --list` to find update commands
**Solution**:
- Added version-update module to all help outputs
- Updated `justfiles/help.just` to document all 30+ version-update recipes
- Created new `just commands` recipe as discoverable alias for `just --list`
- Integrated version-update into help-all workflow
**Impact**:
- Version-update commands now fully discoverable via help system
- Users can find update commands with: `just help modules`, `just help`, `just commands`
- Improved overall help system navigation
**Files Modified**:
- `justfiles/help.just` (+23 lines)
- Added version-update module to help sections
- Added to modules list
- Added to help-all workflow
- New `commands` recipe showing all recipes by group
### 2. Build Process Fixes (Phase 3: Bin Archives)
#### 2a. Plugin Archive Collection Bug
**Issue**: "No plugins found to package" warning in Phase 3
- Collected 26 plugin binaries but reported 0
- Archive creation skipped because count was wrong
**Root Cause**: `each` command returns null, so `| length` returned 0
```nushell
# ❌ OLD - each returns null
let plugin_count = (ls nu_plugin_*/target/release/nu_plugin_* | each {|p|
cp $p.name $"($temp_dir)/"
} | length) # Returns 0!
```
**Solution**: Separated counting from copying with proper filtering
```nushell
# ✅ NEW - count before operations
let plugins_to_copy = (ls nu_plugin_*/target/release/nu_plugin_* | where type == "file")
let plugin_count = ($plugins_to_copy | length)
```
**Impact**:
- Now correctly collects and reports 26 plugins
- Filters out .d dependency files automatically
- Warning eliminated
#### 2b. Tar Archive Path Handling
**Issue**: Tar command failing silently with relative paths in subshell
- `cd $temp_dir` changes context unpredictably
- Relative path `../$archive_name` fails in subshell
- Archive file not created despite exit code 0
**Root Cause**: Shell context and relative path issues in Nushell `do` block
**Solution**: Used `tar -C` with absolute paths instead of `cd`
```nushell
# ❌ OLD - unreliable context switching
do {
cd $temp_dir
tar -czf ../$archive_name .
}
# ✅ NEW - absolute paths, no context switching
tar -C $temp_dir -czf $archive_path .
```
**Additional Improvements**:
- Absolute path construction using `pwd | path join`
- Better error diagnostics with exit code and stderr output
- File verification after creation
**Impact**:
- Tar archives now created successfully
- Robust path handling across platforms
- Clear error messages for debugging
#### 2c. File Size Calculation Type Error
**Issue**: Runtime error when calculating archive size
```
Error: The '/' operator does not work on values of type 'list<filesize>'
```
**Root Cause**: `ls` returns list of records, so `.size` was a list
```nushell
# ❌ OLD - returns list<filesize>
(ls $archive_path).size / 1024 / 1024
# ✅ NEW - returns filesize
(ls $archive_path | get 0.size) / 1024 / 1024
```
**Impact**:
- Proper file size calculation in MB
- No more type errors
**Files Modified**:
- `scripts/create_full_distribution.nu` (+58 lines, refactored plugin collection)
- Fixed plugin counting logic
- Improved path handling with absolute paths
- Enhanced error diagnostics
### 3. Plugin Rebuild Optimization
**Issue**: All plugins marked for rebuild even when dependencies unchanged
- Step 4 (`update_all_plugins.nu`) touched all Cargo.toml files at 01:00:32
- Step 5 saw all files as "newer" than binaries
- Marked ALL plugins for rebuild, though cargo only rebuilt changed ones
**Root Cause**: Script always saved files, even when no changes made
```nushell
# ❌ OLD - always saves, touching file timestamp
$updated_content | to toml | save -f $cargo_toml
```
**Solution**: Only save if content actually changed
```nushell
# ✅ NEW - compare before writing
let original_toml = $content | to toml
let new_toml = $updated_content | to toml
if $original_toml != $new_toml {
$updated_content | to toml | save -f $cargo_toml
}
```
**Impact**:
- Unchanged files preserve original timestamps
- Only plugins with actual dependency changes are rebuilt
- Efficient rebuild process with accurate file modification detection
**Files Modified**:
- `scripts/update_all_plugins.nu` (+12 lines, added content comparison)
- Only touches files with real changes
- Preserves timestamps for efficiency
- Clearer logic and comments
### 4. Documentation
**Files Modified**:
- `CHANGELOG.md` (+56 lines)
- Added comprehensive 2025-10-19 entry
- Documented all fixes with root causes
- Listed files modified and impact summary
## Technical Details
### Nushell Patterns Used
1. **Proper List Handling**:
- `ls` returns list of records, access with `| get 0.size`
- Filter with `where type == "file"` to exclude metadata
2. **Absolute Path Construction**:
- `pwd | append "path" | path join` for cross-platform paths
- Safer than string concatenation with `/`
3. **Content Comparison**:
- Compare TOML string representation before saving
- Preserves file timestamps for efficiency
4. **Error Diagnostics**:
- Capture `stderr` from commands
- Report exit codes and error messages separately
## Testing
- [x] Help system shows version-update module
- [x] `just commands` displays all recipes by group
- [x] Phase 3 bin archive creation works
- [x] Plugin collection reports correct count (26)
- [x] Tar archives created successfully
- [x] File size calculated correctly
- [x] Plugin rebuild only touches changed files
- [x] CHANGELOG updated with all changes
## Files Changed
```
38 files changed, 2721 insertions(+), 2548 deletions(-)
Core Changes:
- justfiles/help.just (+23) Help system integration
- scripts/create_full_distribution.nu (+58) Build process fixes
- scripts/update_all_plugins.nu (+12) Rebuild optimization
- CHANGELOG.md (+56) Documentation
Dependency Updates:
- All plugin Cargo.toml and Cargo.lock files (version consistency)
```
## Breaking Changes
None. These are bug fixes and optimizations that maintain backward compatibility.
## Migration Notes
No migration needed. Improvements are transparent to users.
## Related Issues
- Help system discoverability
- Build process Phase 3 failures
- Unnecessary plugin rebuilds
- Build process reliability
## Checklist
- [x] Changes follow Rust/Nushell idioms
- [x] Code is well-commented
- [x] Error handling is comprehensive
- [x] Documentation is updated
- [x] All changes tested
- [x] No breaking changes introduced
2025-10-19 01:17:13 +01:00
### 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"
```
---
2025-10-19 00:05:16 +01:00
## 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
}