Install
$ agentstack add skill-ifiokjr-monopi-nushell ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Nushell Skill
Use this skill whenever the user's shell is Nushell (nu). All shell commands provided to the user must use Nushell syntax, not Bash/POSIX syntax.
Core Concepts
Nushell treats everything as structured data. Pipelines pass tables, lists, and records — not plain text. This is the single biggest difference from Bash.
Variables
# Immutable (preferred)
let name = "world"
let files = (ls) # Parentheses for subexpressions
let big = (ls | where size > 10kb)
# Mutable
mut count = 0
$count += 1
# Constants (parse-time evaluation)
const config_dir = "~/.config"
# Environment variables — use $env.VAR, NOT $VAR
$env.PATH
$env.HOME
$env.PWD
let path = $env.PATH | append "/usr/local/bin"
# $in — the current pipeline input
[1 2 3] | $in.1 # => 2
ls | $in | where size > 1mb
Commands (not like Bash)
Nushell has structured commands, not text-processing tools:
| Task | Bash | Nushell | | ---------------------- | ---------------------- | -------------------------------------------------------------------------------- | | List files | ls -la | ls (already structured) | | Long listing | ls -la | ls -l or ls --long | | Find files recursively | find . -name '*.rs' | ls **/*.rs | | Filter by condition | grep pattern | where name =~ "pattern" or find "text" | | Sort | sort | sort-by size | | Take first N | head -5 | first 5 | | Take last N | tail -3 | last 3 | | Skip N | tail -n +5 | skip 4 | | Read file | cat file.txt | open file.txt or open --raw file.txt | | Write file | echo "text" > f.txt | "text" \| save f.txt | | Append to file | echo "text" >> f.txt | "text" \| save --append f.txt | | Parse CSV | complex | open data.csv (auto-detects) | | Parse JSON | jq | open data.json (auto-detects) | | Count lines | wc -l | length | | Search in files | grep -r pattern dir/ | rg (via ^rg) or ls **/*.rs \| where (open $in.name \| str contains "TODO") | | Replace in strings | sed 's/a/b/g' | str replace --all 'a' 'b' | | Parse date | date -d "..." | "2024-01-01" \| into datetime | | Get help | man cmd | help cmd or help commands \| find substring |
Pipelines
# Data flows left to right as structured values
ls | where type == dir | sort-by modified | first 5
# Multi-line pipelines in parentheses
let result = (
ls
| where size > 10kb
| sort-by name
| select name size
)
# Chaining: use ; (not &&)
cmd1; cmd2 # Sequential, no output piping between them
cmd1; cmd2 | cmd3 # cmd1 runs, output discarded; cmd2 pipes to cmd3
# Logical chaining uses and/or (not &&/||)
cmd1 and cmd2 # Run cmd2 only if cmd1 succeeds
cmd1 or cmd2 # Run cmd2 only if cmd1 fails
Tables and Data
# Create a table inline
[[name, size]; [file1.txt, 100] [file2.txt, 200]]
# Select columns
ls | select name size modified
# Reject (drop) columns
ls | reject type
# Filter rows
ls | where size > 1mb and type == file
ls | where name =~ "\.rs$" # Regex match
ls | where name starts-with "D"
# Sort
ls | sort-by size --reverse
# Iterate over rows
ls | each { |file| $"($file.name) is ($file.size)" }
ls | each { |it| $it.name } # Shorthand: $it is the current item
# Access nested data with dots
{ a: { b: 3 } } | get a.b # => 3
ls | get name.0 # First filename
# Update/add record fields
{ name: "nu", stars: 5 } | upsert language "Rust"
# Merge records
{ a: 1 } | merge { b: 2 } # => { a: 1, b: 2 }
Strings
# Interpolation — use $"..." with () placeholders
let name = "Alice"
$"Hello, ($name)!" # => Hello, Alice!
# Concatenate
"Hello " + "World"
# Split
"a,b,c" | split row "," # => [a, b, c]
# Join
[a b c] | str join "," # => a,b,c
# Replace
"hello world" | str replace "world" "nushell"
"hello world" | str replace --all 'l' 'L'
# Contains
"hello" | str contains "ell" # => true
# Substring
"Hello World!" | str substring 4..8 # => o Wo
# Parse into columns
"Nushell 0.98" | parse "{shell} {version}"
# Uppercase/lowercase
"hello" | str upcase # => HELLO
Lists
# Create
[1, 2, 3]
1..10 # Range: 1,2,3,...,10
# Access by index
let lst = [a b c]
$lst.0 # => a
$lst.-1 # => c (last element)
# Modify (creates new lists — immutable)
[1, 2, 3] | insert 1 99 # Insert at index
[1, 2, 3] | update 1 99 # Replace at index
[1, 2, 3] | prepend 0 # Add to front
[1, 2, 3] | append 4 # Add to end
[1, 2, 3] | drop 1 # Remove at index
# Iterate
[1, 2, 3] | each { |n| $n * 2 } # => [2, 4, 6]
[1, 2, 3] | each { $in * 2 } # Same, using $in
# Map with index
[a b c] | enumerate | each { |it| $"($it.index): ($it.item)" }
# Reduce
[3, 8, 4] | reduce { |it, acc| $acc + $it } # => 15
[3, 8, 4] | reduce --fold 1 { |it, acc| $acc * $it } # => 96
# Test elements
[1, 2, 3] | any { $in > 2 } # => true
[1, 2, 3] | all { $in > 0 } # => true
# Length
[1, 2, 3] | length # => 3
Custom Commands (Functions)
# Basic command
def greet [name: string] {
$"hello ($name)"
}
# With default value
def greet [name = "nushell"] {
$"hello ($name)"
}
# Typed parameters
def double [x: int]: int {
$x * 2
}
# Flags
def greet [
name: string
--age: int # Optional flag with value
--verbose (-v) # Shorthand flag (-v)
] {
# body
}
greet "Alice" --age 30
greet "Bob" -v
# Rest parameters (variadic)
def multi [...names: string] {
$names | each { $"Hello ($in)" }
}
multi Alice Bob Carol
# Pipeline input in custom commands
def my_filter [] {
let data = $in # Capture pipeline input
$data | where size > 1mb
}
ls | my_filter
Modules
# Define inline
module greetings {
export def hello [] { "hello" }
}
use greetings
greetings hello # Subcommand style
# From file
use path/to/module.nu
External Commands
# Run external commands with ^ prefix
^git status
^cargo build --release
^rg "TODO" src/
# Capture external command output (returns raw string)
let output = (^git branch | lines)
let status = (^git status --porcelain)
# Pipe through external commands
ls | get name | to text | ^grep ".rs"
# Discard output
^noisy-command | ignore
Control Flow
# If/else — must produce same type from both branches
if $condition {
"yes"
} else {
"no"
}
# For loops
for file in (ls) {
print $"Processing ($file.name)"
}
# While
while $count (date now) - 1day
# Convert JSON to CSV
open data.json | to csv | save data.csv
# Parse log files
open access.log | lines | parse "{ip} - - [{date}] \"{method} {path}\" {status} {size}"
Key Gotchas
- No
&&or||— use;,and,or - No
$VARfor env vars — use$env.VAR - No
grep/sed/awk— usewhere,str replace,each lsreturns a table, not text — filter withwhere, notgrepopenis for reading files — useopen file.txtnotcat file.txt- Closures can't capture
mutvariables — use immutableletinside closures - External commands need
^prefix —^gitnot justgit - No
$(cmd)substitution — use(cmd)directly in expressions - No backtick command substitution — use
(cmd) - No
fi/done/esac— just{ }blocks rmis safer — norm -rfneeded, justrm -r$inrefers to pipeline input — very useful for chaining
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: ifiokjr
- Source: ifiokjr/monopi
- License: MIT
- Homepage: https://ifiokjr.github.io/monopi/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.