AgentSkillsCN

Nushell

Nushell

SKILL.md

Nushell Coding Skill

A practical guide for writing effective nushell code.

Looking Up Information

Built-in Help System

  • help <command> - Get detailed help for any command
  • help commands - List all available commands
  • help --find <term> - Search help for a specific term
  • <command> --help - Quick help flag for most commands

Type Information

  • <value> | describe - Get the type of any value
  • <value> | describe --detailed - Get detailed type information including structure

Documentation Resources

Invoking Nushell Commands

From Other Shells (bash/zsh)

bash
# Execute a single nushell command
nu -c "use module.nu; module command"

# Run a nushell script
nu script.nu

# Start an interactive nushell session
nu

Module System

nushell
# Import and use a module
use module.nu
module command-name

# Import specific exports
use module.nu [command1, command2]

# Import with alias
use module.nu *

Pipeline Execution

Nushell is built around pipelines where data flows from left to right:

nushell
# Data flows through pipeline
$data | filter {|item| $item.value > 10} | each {|item| $item.name}

# Commands can be chained
ls | where size > 1mb | sort-by modified | reverse

Functional Coding Style

Immutability

Prefer immutable variables over mutable ones:

nushell
# Preferred: immutable
let result = ($data | where {|item| $item.active == true})

# Avoid: mutable unless necessary
mut result = []
for item in $data {
    if $item.active {
        $result = ($result | append $item)
    }
}

# Exception: mutable variables are appropriate when state must persist across loop iterations
mut current_items = $initial_items
loop {
    # Rebuild choices based on current state each iteration
    let choices = ($current_items | each {|item| $item.description})
    let selection = ($choices | input list "Select: ")

    if $selection == "Add" {
        $current_items = (add_item $current_items)  # Updates persist to next iteration
    }
}

Pure Functions

Write functions that return data rather than mutating state:

nushell
# Good: returns new data
def add_entry [entries, new_entry] {
    $entries | append $new_entry
}

# Avoid: mutates global state
def add_entry [new_entry] {
    $env.entries = ($env.entries | append $new_entry)
}

Function Composition

Break down complex operations into smaller, composable functions:

nushell
# Good: small, focused functions
def filter_active [items] {
    $items | where active == true
}

def extract_names [items] {
    $items | each {|item| $item.name}
}

def get_active_names [items] {
    $items | filter_active | extract_names
}

# Avoid: large functions that do multiple things
def get_active_names [items] {
    $items | where active == true | each {|item| $item.name} | sort | uniq | # ... more logic
}

Prefer Pipelines Over Intermediate Variables

nushell
# Good: direct pipeline
let result = ($data
    | where status == "active"
    | sort-by priority
    | first 10)

# Less ideal: unnecessary intermediate variables
let filtered = ($data | where status == "active")
let sorted = ($filtered | sort-by priority)
let result = ($sorted | first 10)

Common Pitfalls

Constants as Default Parameters

Problem: Using a constant name as a default parameter value converts it to a string, not the constant's value.

nushell
const DEFAULT_CONFIG = {timeout: 30, retries: 3}

# Wrong: $config will be the string "DEFAULT_CONFIG"
def process [config = DEFAULT_CONFIG] {
    # $config is a string, not a record!
}

# Correct: use optional parameter with explicit check
def process [config?: record] {
    let config = if ($config | is-empty) { $DEFAULT_CONFIG } else { $config }
    # Now $config is properly a record
}

Pipeline vs Variable Context

Problem: Some commands expect pipeline input and don't work with variable arguments.

nushell
let items = [{name: "foo"}, {name: "bar"}]

# This may fail depending on nushell version
let result = ($items | where name == "foo")

# More reliable: use closures with where
let result = ($items | where {|e| $e.name == "foo"})

# Or use each with filtering
let result = ($items | each {|e| if $e.name == "foo" { $e } else { null }} | compact)

Type Expectations in Pipelines

Problem: Commands in pipelines have specific input type requirements.

nushell
# Check what type a value is
$value | describe

# Ensure correct type is being passed
# Example: some commands expect tables, others expect lists or strings
let data = (open file.json)  # Returns structured data
$data | where field == value  # Expects table/list input

Variable Shadowing in Loops

Problem: Using let with the same name as an outer variable creates a new local variable that shadows the outer one. Changes don't persist to the next iteration.

nushell
def display_menu [menu_entries?: list] {
    let menu_entries = if ($menu_entries | is-empty) { $default } else { $menu_entries }

    loop {
        let selection = ($menu_entries | input list)

        # Wrong: this creates a NEW local variable, doesn't update the outer one
        let menu_entries = (add_entry $menu_entries)
        # After this block, the outer $menu_entries is unchanged in next iteration!
    }
}

# Correct: use mut for variables that need to persist across iterations
def display_menu [menu_entries?: list] {
    mut current_entries = if ($menu_entries | is-empty) { $default } else { $menu_entries }

    loop {
        let selection = ($current_entries | input list)

        # This properly updates the mutable variable for the next iteration
        $current_entries = (add_entry $current_entries)
    }
}

Empty Checks

Problem: Different types have different empty states.

nushell
# For lists/tables
if ($list | is-empty) { }

# For strings
if ($str | str length) == 0 { }

# For null/nothing
if ($value == null) { }

# For optional parameters
if ($param | is-empty) { }

Best Practices Summary

  1. Look up types - Use describe liberally when debugging
  2. Read the help - help <command> is comprehensive and accurate
  3. Test incrementally - Build pipelines step by step, checking output at each stage
  4. Prefer immutability - Use let over mut whenever possible
  5. Return data - Functions should return values, not mutate globals
  6. Compose functions - Break complex logic into small, reusable functions
  7. Use closures - Modern nushell prefers closures with where, each, etc.
  8. Check types - When errors occur, verify input/output types match expectations