关于 nushell-plugin-builder
Nushell Plugin Builder 是面向 Rust 开发者的技能,用于创建扩展 Nushell 命令和数据处理能力的插件。它涵盖命令定义、流式处理、序列化、测试及安装流程。
主要功能
- 01脚手架生成插件项目
- 02定义命令签名与参数
- 03支持值处理和流式处理
- 04选择 MsgPack 或 JSON 序列化
- 05提供测试与调试指导
适用场景
- 01创建 Nushell 自定义命令
- 02构建处理大型数据集的流式插件
- 03开发访问 HTTP API 或文件系统的插件
- 04测试并注册 Rust 插件
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ ypares/ agent-skills --skill nushell-plugin-builder安装后在 Claude Code 中输入/nushell-plugin-builder确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/plugin-template/.gitignoreCargo.tomlREADME.mdassets/plugin-template/src/main.rsreferences/advanced-features.mdexamples.mdplugin-protocol.mdtesting-debugging.mdscripts/init_plugin.py详细介绍
Nushell 插件构建器
概述
此技能可帮助你使用 Rust 创建 Nushell 插件。插件是独立的可执行文件,可通过自定义命令、数据转换和集成扩展 Nushell。
快速入门
1. 创建插件项目
cargo new nu_plugin_<name>
cd nu_plugin_<name>
cargo add nu-plugin nu-protocol
2. 插件基本结构
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
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
命令类型
SimplePluginCommand
用于处理单个值的命令:
- 输入:
&Value - 输出:
Result<Value, LabeledError> - 适用于:转换、简单筛选、单值操作
PluginCommand
用于处理流的命令:
- 输入:
PipelineData - 输出:
Result<PipelineData, LabeledError> - 适用于:流式转换、惰性处理、大型数据集
有关流式处理示例,请参阅 references/advanced-features.md。
定义命令签名
输入与输出类型
use nu_protocol::{Signature, Type};
Signature::build("my-command")
.input_output_type(Type::String, Type::Int)
常见类型:String、Int、Float、Bool、List(Box<Type>)、Record(...)、Any
参数
Signature::build("my-command")
// Named flags
.named("output", SyntaxShape::Filepath, "output file", Some('o'))
.switch("verbose", "enable verbose output", Some('v'))
// Positional arguments
.required("input", SyntaxShape::String, "input value")
.optional("count", SyntaxShape::Int, "repeat count")
.rest("files", SyntaxShape::Filepath, "files to process")
访问参数
fn run(&self, call: &EvaluatedCall, ...) -> Result<Value, LabeledError> {
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
}
错误处理
始终返回包含跨度信息的 LabeledError:
Err(LabeledError::new("Error message")
.with_label("specific issue", call.head))
这样用户就能准确了解命令中的错误发生位置。
序列化
MsgPackSerializer(推荐)
- 二进制格式,速度快得多
- 用于生产环境中的插件
JsonSerializer
- 基于文本,易于阅读
- 适用于调试
在 main() 中选择:
serve_plugin(&MyPlugin, MsgPackSerializer) // Production
// serve_plugin(&MyPlugin, JsonSerializer) // Debug
常见模式
字符串转换
Value::String { val, .. } => {
Ok(Value::string(val.to_uppercase(), call.head))
}
列表生成
let items = vec![
Value::string("a", call.head),
Value::string("b", call.head),
];
Ok(Value::list(items, call.head))
记录(表格行)
use nu_protocol::record;
Ok(Value::record(
record! {
"name" => Value::string("example", call.head),
"size" => Value::int(42, call.head),
},
call.head,
))
表格(记录列表)
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))
完整的可运行示例(包括以下内容),请参阅 references/examples.md:
- 筛选流
- HTTP API 调用
- 文件系统操作
- 多命令插件
开发工作流
迭代开发
# 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>
自动化测试
[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)
}
}
有关调试技巧和故障排除,请参阅 references/testing-debugging.md。
高级功能
流式数据
若要惰性处理大型数据集,请使用 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))
}
}
引擎交互
// 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.<name>
let config = engine.get_plugin_config()?;
// Get current directory for path resolution
let cwd = engine.get_current_dir()?;
自定义值
定义可扩展 Nushell 内置类型的自定义数据类型。完整指南请参阅 references/advanced-features.md。
重要限制
Stdio 限制
- 插件不能使用 stdin/stdout(它们保留给协议使用)
- 尝试访问 stdio 前,请检查
engine.is_using_stdio()
路径处理
- 始终使用相对于
engine.get_current_dir()的路径 - 切勿假定当前工作目录
版本兼容性
nu-plugin和nu-protocol的版本应匹配- 两者都应与目标 Nushell 版本匹配
参考文档
references/plugin-protocol.md- 协议详情、序列化、生命周期references/advanced-features.md- 流式处理、EngineInterface、自定义值references/examples.md- 完整的可运行示例和模式references/testing-debugging.md- 开发工作流、调试、故障排除
外部资源
- 官方插件指南
- nu-plugin API 文档
- 插件示例仓库
- Awesome Nu - 社区插件
- nushellWith - 用于构建可复现 Nushell 环境的 Nix flake,支持插件。包含 100 多个来自 crates.io 的预打包插件,并通过 Garnix 提供二进制缓存。适用于在隔离环境中测试插件或构建独立的 Nu 脚本。
模板脚本
使用 scripts/init_plugin.py 按照合适的结构搭建新插件:
python3 scripts/init_plugin.py <plugin-name> [--output-dir <path>]
此脚本会创建一个完整的可运行插件模板,可供你自定义。
评论
支持 Markdown