Nushell Plugin Builder Overview This skill helps create Nushell plugins in Rust. Plugins are standalone executables that extend Nushell with custom commands, data transformations, and integrations. Quick Start 1. Create Plugin Project cargo new nu_plugin_ < name
cd nu_plugin_ < name
cargo add nu-plugin nu-protocol 2. Basic Plugin Structure use nu_plugin :: { EvaluatedCall , MsgPackSerializer , serve_plugin } ; use nu_plugin :: { EngineInterface , Plugin , PluginCommand , SimplePluginCommand } ; use nu_protocol :: { LabeledError , Signature , Type , Value } ; struct MyPlugin ; impl Plugin for MyPlugin { fn version ( & self ) -> String { env! ( "CARGO_PKG_VERSION" ) . into ( ) } fn commands ( & self ) -> Vec < Box < dyn PluginCommand < Plugin = Self
{ vec! [ Box :: new ( MyCommand ) ] } } struct MyCommand ; impl SimplePluginCommand for MyCommand { type Plugin = MyPlugin ; fn name ( & self ) -> & str { "my-command" } fn signature ( & self ) -> Signature { Signature :: build ( "my-command" ) . input_output_type ( Type :: String , Type :: Int ) } fn run ( & self , _plugin : & MyPlugin , _engine : & EngineInterface , call : & EvaluatedCall , input : & Value , ) -> Result < Value , LabeledError
{ match input { Value :: String { val , .. } => { Ok ( Value :: int ( val . len ( ) as i64 , call . head ) ) } _ => Err ( LabeledError :: new ( "Expected string input" ) . with_label ( "requires string" , call . head ) ) } } } fn main ( ) { serve_plugin ( & MyPlugin , MsgPackSerializer ) } 3. Build and Install
Build
cargo build --release
Install to cargo bin
cargo install --path . --locked
Register with nushell
plugin add ~/.cargo/bin/nu_plugin_ < name
Add .exe on Windows
plugin use < name
Test
"hello"
|
my-command
Command Types
SimplePluginCommand
For commands that operate on single values:
Input:
&Value
Output:
Result
{ let output : Option < String
= call . get_flag ( "output" ) ? ; let verbose : bool = call . has_flag ( "verbose" ) ? ; let input : String = call . req ( 0 ) ? ; // First positional let count : Option < i64
= call . opt ( 1 ) ? ; // Second positional let files : Vec < String
= call . rest ( 2 ) ? ; // Remaining args } Error Handling Always return LabeledError with span information: Err ( LabeledError :: new ( "Error message" ) . with_label ( "specific issue" , call . head ) ) This shows users exactly where the error occurred in their command. Serialization MsgPackSerializer (Recommended) Binary format, much faster Use for production plugins JsonSerializer Text-based, human-readable Useful for debugging Choose in main() : serve_plugin ( & MyPlugin , MsgPackSerializer ) // Production // serve_plugin(&MyPlugin, JsonSerializer) // Debug Common Patterns String Transformation Value :: String { val , .. } => { Ok ( Value :: string ( val . to_uppercase ( ) , call . head ) ) } List Generation let items = vec! [ Value :: string ( "a" , call . head ) , Value :: string ( "b" , call . head ) , ] ; Ok ( Value :: list ( items , call . head ) ) Record (Table Row) use nu_protocol :: record ; Ok ( Value :: record ( record! { "name" => Value :: string ( "example" , call . head ) , "size" => Value :: int ( 42 , call . head ) , } , call . head , ) ) Table (List of Records) let records = vec! [ Value :: record ( record! { "name" => Value :: string ( "a" , span ) } , span ) , Value :: record ( record! { "name" => Value :: string ( "b" , span ) } , span ) , ] ; Ok ( Value :: list ( records , call . head ) ) See references/examples.md for complete working examples including: Filtering streams HTTP API calls File system operations Multi-command plugins Development Workflow Iterative Development
Build
cargo build
Test (debug build)
plugin add target/debug/nu_plugin_ < name
plugin use < name
"test" | my-command
After changes, reload
plugin rm < name
plugin add target/debug/nu_plugin_ < name
plugin use < name
Automated Testing [ dev-dependencies ] nu-plugin-test-support = "0.109.1"
[cfg(test)]
mod tests { use nu_plugin_test_support :: PluginTest ;
[test]
fn test_command ( ) -> Result < ( ) , nu_protocol :: ShellError
{ PluginTest :: new ( "myplugin" , MyPlugin . into ( ) ) ? . test_examples ( & MyCommand ) } } See references/testing-debugging.md for debugging techniques and troubleshooting. Advanced Features Streaming Data For lazy processing of large datasets, use PipelineData : impl PluginCommand for MyCommand { fn run ( & self , input : PipelineData , ... ) -> Result < PipelineData , LabeledError
{ let filtered = input . into_iter ( ) . filter ( | v | / condition / ) ; Ok ( PipelineData :: ListStream ( ListStream :: new ( filtered , span , None ) , None ) ) } } Engine Interaction // Get environment variables let home = engine . get_env_var ( "HOME" ) ? ; // Set environment variables (before response) engine . add_env_var ( "MY_VAR" , Value :: string ( "value" , span ) ) ? ; // Get plugin config from $env.config.plugins.
let config = engine . get_plugin_config ( ) ? ; // Get current directory for path resolution let cwd = engine . get_current_dir ( ) ? ; Custom Values Define custom data types that extend beyond Nushell's built-in types. See references/advanced-features.md for complete guide. Important Constraints Stdio Restrictions Plugins cannot use stdin/stdout (reserved for protocol) Check engine.is_using_stdio() before attempting stdio access Path Handling Always use paths relative to engine.get_current_dir() Never assume current working directory Version Compatibility Match nu-plugin and nu-protocol versions Both should match target Nushell version Reference Documentation references/plugin-protocol.md - Protocol details, serialization, lifecycle references/advanced-features.md - Streaming, EngineInterface, custom values references/examples.md - Complete working examples and patterns references/testing-debugging.md - Development workflow, debugging, troubleshooting External Resources Official Plugin Guide nu-plugin API Docs Plugin Examples Repository Awesome Nu - Community plugins nushellWith - Nix flake for building reproducible Nushell environments with plugins. Includes 100+ pre-packaged plugins from crates.io with binary caching via Garnix. Useful for testing plugins in isolated environments or building standalone Nu scripts. Template Script Use scripts/init_plugin.py to scaffold a new plugin with proper structure: python3 scripts/init_plugin.py < plugin-name [ --output-dir < path
] This creates a complete working plugin template ready to customize.