Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` interpolates but does not process escape sequences.\n- **A literal `(` directly inside interpolation requires `$\"...\"`.** Since `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` does no escape processing,\n every `(` inside it opens an interpolation expression and `\\(` cannot prevent\n that. Write `$\"\\(abc)($var)\"`, never `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

\\(abc)($var)'`. The failure is often\n silent rather than an error: `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

(1 + 1) items'` evaluates to `2 items`. See\n [String Formats](references/string-formats.md). For generated code with many\n delimiters, join plain literal fragments and serialized data instead.\n- Keep code and data separate across interpreters: pass argv/stdin/JSON to a\n fixed program. If an API only accepts source, use a fixed, trusted template\n with target-language serialization; a single argv string is still code when\n the receiving tool evaluates it. Do not concatenate raw values into source.\n- Use kebab-case for commands/flags, snake_case for variables/parameters, and\n SCREAMING_SNAKE_CASE for environment variables.\n\n### Data formats and grouping\n\n- `from xml` loses XML namespace identity (reproduced on Nu 0.115.1): it\n stores only local names for elements and attributes, with no namespace URI\n or declaration in the result. For example,\n `\u003cx xmlns:r=\"urn:r\" id=\"plain\" r:id=\"rel\"/>` becomes an `attributes` record\n containing only `id: rel`; reversing the two attributes leaves `id: plain`.\n Later attributes overwrite earlier ones when their local names collide.\n Do not use this record for namespace-sensitive XML lookups, edits, or\n round-trips. Use a namespace-aware XML parser or, when the `query xml` plugin\n is available, XPath with `--namespaces` (and `--output-names` when names\n matter). Match namespace URI plus local name, not prefix spelling.\n- Nu 0.115 makes `from yaml` default to YAML 1.2 with strict non-string keys\n and tags. Pin `--spec`, `--multiple`, tag handling, and key resolution when\n the input contract is controlled by another system instead of inheriting\n defaults accidentally.\n- Let `to yaml` reject non-round-trippable values by default. Opt out only when\n that data loss is part of the documented contract, and quote the value:\n `--non-roundtrip 'null'` works across 0.115/0.116; bare `null` works from\n 0.116. `--non-roundtrip 'lossy'` alone is still rejected on 0.116.0, so use\n `--serialize` when a lossy encoding is genuinely wanted. On 0.116 replace\n `--compact-list-indent` with `--list-indent compact|indented`.\n- `group-by` record output cannot represent a null key and omits that group in\n Nu 0.115. Use `group-by --to-table` when null groups must be retained or\n distinguished from empty strings.\n\n### Data flow and performance\n\n- Prefer pipelines and immutable `let` bindings.\n- Use `where`, `select`, `update`, `insert`, `items`, `transpose`, `reduce`, and\n `enumerate` instead of manual parsing and mutable accumulation.\n- Do not capture `mut` variables in closures.\n- `for` is appropriate for sequential side effects but is not a transforming\n expression; use `each` when a list result is required.\n- Use `par-each` only when concurrency is safe and beneficial; preserve `each`\n when order or sequential side effects matter.\n- Add `lines` before `parse` when line-by-line stream parsing is intended.\n- On Nu 0.116, streamed errors reach `length`, `columns`, `is-empty`, and\n `each while`; consume streams in tests and fix upstream failures. Put flags\n before predicates in row-condition commands such as `take until`.\n- Prefer direct row conditions for simple `any`/`all` predicates on Nu 0.115;\n retain closures when the predicate needs setup, destructuring, or reuse.\n- Use native tables for small interactive data and Polars for large columnar\n group-by/join/aggregation workloads.\n\n### Modules and scripts\n\n- Export only the intended API; keep helpers private.\n- Use `export def main` when the command should match the module name.\n- Use `def --env`/`export def --env` for caller-visible environment changes.\n- Do not name commands, aliases, modules, or exports after parser keywords.\n Also treat `$ans` as reserved in Nu 0.115; rename older `ans` bindings.\n- `source`, `use`, and `run` targets must be trusted and available at parse time.\n- Test at the correct seam: direct functions for stable structured errors, CLI\n subprocesses for argument parsing/process boundaries, and both when needed.\n- For Nu 0.116 completers, use named `token`/`place`/`buffer` inputs and\n `place.command` for shell words. Test via `commandline complete`; load the\n 0.116 migration guide for output/fallback rules and `@interactive` ownership.\n\n## Review Order\n\nWhen reviewing code, report findings in this order:\n\n1. Security: injection, traversal, credentials, destructive operations, temp\n files, environment poisoning.\n2. Correctness: types, null handling, parse-time constraints, exit codes,\n cleanup/rollback, data-format contracts, platform/version behavior, stable\n tests.\n3. Maintainability: naming, module boundaries, duplication, documentation.\n4. Performance: streaming, unnecessary collection, safe parallelism, Polars.\n\nSkip issues already enforced by the project's formatter/linter unless the tool\noutput shows they are currently failing.\n\n## Validation\n\nFor ordinary script/module validation, prefer `nu-check --debug`. It parses\nwithout running the target's top-level commands and reports parse-time type\nerrors as well as syntax errors. Use the matching parse mode:\n\n```bash\nnu --no-config-file -c 'nu-check --debug path/to/script.nu'\nnu --no-config-file -c 'nu-check --debug --as-module path/to/module.nu'\nnu --no-config-file path/to/test-script.nu\n```\n\n- Without `--debug`, invalid content returns `false` and the surrounding `nu`\n process can still exit `0`; consume the boolean explicitly. With `--debug`,\n a parse error throws, so an uncaught failure exits nonzero. Missing files are\n errors. Neither mode proves runtime correctness or provides a sandbox.\n- Pass the file path directly when checking a saved script with relative\n imports. Piping its text to `nu-check` checks an anonymous source; the optional\n path does not restore the file's import context when pipeline input is present.\n- For editor/CI integrations needing spans and diagnostic records, use\n `nu --no-config-file --ide-check 100 path/to/script.nu`.\n- `--ide-check` emits JSON Lines on stdout and may still exit with code `0`\n when a record has `type: \"diagnostic\"` and `severity: \"Error\"`. It also\n exits `0` with empty output for missing files on older versions; 0.116 fixes\n that case with a nonzero exit. Verify the path for cross-version checks. Parse every\n non-empty line with `from json`; do not use the process exit code alone.\n Treat `severity: \"Error\"` diagnostics as blockers, surface other severities\n such as `Warning` without blocking, and ignore `type: \"hint\"` records.\n Handle a non-zero exit code or stderr separately as a CLI startup or I/O\n failure.\n- Prefer `--no-config-file` for reproducible standalone checks. Omit it when\n the script intentionally depends on commands or environment from user\n configuration, and document that dependency.\n- `source` executes top-level code; it is not a syntax check. Use it only when\n intentionally loading audited definitions/setup. Validate side-effecting\n scripts with `nu-check`/`--ide-check`, then execute in an owned temp fixture.\n When saving structured values, use a recognized data extension or\n serialize explicitly (`to json`, `to yaml`, `to nuon`, and so on) before\n `save`.\n- Do not use `nu --testbin` in Nu 0.115+. Recreate the required behavior with\n Nushell itself or a purpose-built test fixture.\n- Reproduce terminal-sensitive tests under a narrow PTY when diagnostics or\n tables are involved, for example `stty cols 24 && nu tests/example.nu`.\n- Check diffs for debug markers and accidental changes before finishing.\n- If validation fails, fix the smallest reproducible issue and rerun the exact\n failing command before broadening the test suite.","attachment_filenames":["docs/nushell-0.114-fact-check-independent.md","docs/scripting-validation-evidence.md","references/advanced-patterns.md","references/anti-patterns.md","references/archive/README.md","references/bash-to-nushell.md","references/daemon-and-e2e-smoke-tests.md","references/data-and-types.md","references/dataframes.md","references/modules-and-scripts.md","references/nu-0.114-migration.md","references/nu-0.115-migration.md","references/nu-0.116-migration.md","references/script-review.md","references/security.md","references/string-formats.md"],"attachments":[{"filename":"docs/nushell-0.114-fact-check-independent.md","content":"# Nushell 0.114+ 独立事实核查(主源优先)\n\n核查日期:2026-07-10\n核查对象:`SKILL.md` 与 `references/*.md` 中面向 Nushell 0.114+ 的可验证技术陈述。被核查文件只用于提取 claims,不作为证据。\n实测环境:Nushell MCP `0.114.0`,commit `57a036c4b011c027609b90afefb52f5930d84496`,默认实验选项包含 `enforce-runtime-annotations=true`。\n\n## 结论摘要\n\n- 0.114 的核心迁移说明整体准确:类型收紧、可选参数的 `nothing` 联合、显式子模块导出、`run`、SemVer、错误 `details`、表格导入 flags、大小写命令弃用等均有官方发布说明或源码支持。\n- 安全章节存在三项需要立即修订的问题:`mktemp` 后直接 `save` 的示例不能工作;`safe-glob` 可被绝对路径绕过;不存在建议中的 `--no-expand` flag。\n- 若干陈述方向正确但写得过度绝对:环境变量作用域、类型系统的安全收益、`do -c` 的“错误作为值”、`run --full-reparse` 的使用场景、外部命令 glob 展开均应补充边界。\n\n## 官方证据基线\n\n- [Nushell 0.114.0 官方发布说明](https://www.nushell.sh/blog/2026-07-04-nushell_v0_114_0.html)\n- [0.114.0 GitHub Release](https://github.com/nushell/nushell/releases/tag/0.114.0)\n- [官方源码 tag 0.114.0](https://github.com/nushell/nushell/tree/0.114.0)\n- MCP 实测均使用与该 tag 相同的 commit;涉及命令签名时,同时以 `help`/`command_help` 与源码实现交叉确认。\n\n## 逐条核查\n\n| 状态 | Claim 位置 | 核查结论与官方证据 | 修订建议 |\n| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| true | `SKILL.md:47-48`;`data-and-types.md:64-75`;`anti-patterns.md:518-520` | 0.114 发布说明明确:无默认值的可选 positional 和 typed flag 的解析期类型为 `oneof\u003cT, nothing>`;有默认值则为 `T`。布尔 switch 仍为 `bool`。见发布说明 “Optional parameters' type…”;源码签名/类型测试见 [`tests/repl/test_signatures.rs`](https://github.com/nushell/nushell/blob/0.114.0/tests/repl/test_signatures.rs)。 | 保留。可将 “named options” 统一写成官方常用语 “typed named flags”。 |\n| true | `SKILL.md:49-50`;`data-and-types.md:239-250` | MCP 实测 `if false { 42 } | describe`与未匹配、无`_`的`match`均得到`nothing`;发布说明的类型系统章节也列出无 fallback 时联合 `nothing`。 | 保留;最好明确这是“可能的输出类型/未命中运行结果”,不是要求 `match` 必须穷尽。 |\n| true | `SKILL.md:51`;`data-and-types.md:253-258` | 官方发布说明明确 `enforce-runtime-annotations` 在 0.114 变为 opt-out;MCP `version` 显示默认 `enforce-runtime-annotations=true`。 | 保留,并注明用户仍可通过实验选项关闭,所以是“官方默认”,不是不可配置语义。 |\n| true | `SKILL.md:52-54`;`modules-and-scripts.md:77-102`;`anti-patterns.md:495-514` | 官方 breaking change “Submodules are no longer implicitly imported” 与示例完全支持;`export use sub` 保留命名空间,`export use sub *` 扁平导出。 | 保留。 |\n| needs nuance | `SKILL.md:55-56`;`modules-and-scripts.md:151-183` | `run` 是 0.114 新增的 parser keyword,接收 pipeline input、隔离脚本定义,并有 `--full-reparse/-f`。源码见 [`crates/nu-command/src/misc/run.rs`](https://github.com/nushell/nushell/blob/0.114.0/crates/nu-command/src/misc/run.rs)。官方说明把 `--full-reparse` 定位为绕过 parser-time script cache,特别用于文件变化后的 watch 流程;“repeated-run workflows”过宽,因为普通重复运行的缓存 bug 本身已修复。 | 改为:“脚本内容可能在调用间变化时(如 watch/test regeneration)使用 `run --full-reparse`;普通重复执行通常不需要。” |\n| true | `SKILL.md:57-58`;`anti-patterns.md:541-555` | 0.114 新增 `semver` custom value、`into semver`、`into semver-range`、`semver bump`,官方发布说明包含排序、bump、range membership 示例;源码见 [`crates/nu-command/src/conversions/into/semver.rs`](https://github.com/nushell/nushell/blob/0.114.0/crates/nu-command/src/conversions/into/semver.rs)。 | 保留。 |\n| true | `SKILL.md:59-60`;`anti-patterns.md:481-493` | 官方 0.114 说明:新增 `str uppercase`/`str lowercase`,弃用 `str upcase`/`str downcase`,旧命令仍可用但会警告。 | 保留;不要写成“旧命令已移除”。 |\n| true | `SKILL.md:61-63`;`data-and-types.md:340-356`;`anti-patterns.md:569-585` | 官方说明确认 `from xlsx`/`from ods` 实际返回“record containing one table per sheet”,移除 `--header-row`,新增 `--noheaders`、`--first-row`;另一 0.114 PR 新增 `--prefer-integers`。 | 保留。`--first-row 0` 的语义应写为“保留 leading empty rows/从指定行开始”,当前详细 reference 已基本正确。 |\n| true | `SKILL.md:64-65`;`anti-patterns.md:557-567` | 官方说明明确 catch error record 删除 `json` 字段,由已结构化的 `details` 取代;MCP 实测 error record 列为 `msg debug raw rendered details`,`details` 含 `msg labels code url help inner`。 | 保留。建议说“`details` 取代 `json`”,不说所有错误信息“只存在”于 details,因为顶层仍有 `msg/raw/rendered` 等字段。 |\n| true | `advanced-patterns.md:125-136`;`anti-patterns.md:409-423` | MCP 实测多行文件直接 `parse '{x} {y}'` 只产生一条、第二字段吞入后续行;插入 `lines` 后逐行产生两条。与 0.113 后行为相符。 | 保留。 |\n| true | `advanced-patterns.md:163-165` | 官方发布说明确认 builtins、custom commands 支持 POSIX `--`;known externs 原样收到 `--`。官方示例还显示 `def --wrapped` 的 `--` 会转发而非消费。 | 将原句拆清楚:普通 builtin/custom 消费 `--`;`def --wrapped` 与 known extern 的转发行为需按官方示例描述,避免读成三者完全一致。 |\n| true | `advanced-patterns.md:170-187` 的 `split --right`、`ignore` flags、KDL v2、`to nuon --pretty`、`lines --strict`、URL binary、fractional range | 均在 0.114 官方发布说明中有对应条目;命令实现分别位于 `crates/nu-command` 下。MCP `help ignore` 确认 `--stderr/-e`、`--stdout/-o`、`--show-errors/-x`。 | 可保留。`to nuon --pretty` 建议写“启用官方 pretty formatting(当前为两空格并对齐表列)”,避免把实现细节承诺为永久格式。 |\n| needs nuance | `advanced-patterns.md:312-330`:`do -c` “catches errors and returns them as values” | 官方 `help do` 确实如此描述,但 MCP 实测 `do -c { error make {msg: 'x'} } | describe`仍作为 pipeline error 传播,而不是可任意`get`/`describe`的普通 record。它主要让 closure 的错误作为 error value 返回并终止下游,不等同于`try/catch` 恢复。 | 改为:“`do -c` 捕获并返回 error value,但该值仍按错误传播;要检查/恢复请用 `try/catch`。” |\n| true | `advanced-patterns.md:157-159`:`finally` 返回值不覆盖 try/catch | MCP 实测 `try {42} finally {99}` 返回 `42`,catch 分支同理;符合当前实现。 | 保留。 |\n| false | `security.md:204-213`:`mktemp` 后 `'data' | save $tmp` 是可工作的安全示例 | `mktemp` 会创建文件;MCP 0.114 实测随后普通 `save $tmp` 报 `Destination file already exists`。官方 `save` 默认拒绝覆盖。 | 必须改为 `'data' | save --force $tmp`,或让 `mktemp`只生成目录再在其中选择新叶名。清理最好用`finally`,并避免清理失败掩盖原始错误。 |\n| false | `security.md:51`:外部 glob 可“use --no-expand” | 0.114 的 `run-external` 只有 `--help`,不存在 `--no-expand`;官方源码 [`crates/nu-command/src/system/run_external.rs`](https://github.com/nushell/nushell/blob/0.114.0/crates/nu-command/src/system/run_external.rs) 也无此 flag。 | 删除 `--no-expand` 建议。应区分 `string` 与 `glob` typed value:MCP 实测 string `'*'` 作为外部参数保持字面量,而 `'*' | into glob` 会展开。安全规则应检查/禁止 glob typed input,或显式转回 string。 |\n| false | `security.md:245-259`:仅拒绝 `..`、然后 `cd $base_dir; glob $pattern` 可限制在 base 内 | MCP 实测在 `/tmp` 下 `glob '/etc/passwd' --depth 3` 直接返回 `/etc/passwd`;绝对 pattern 无需 `..` 即可逃逸。`--depth` 只限制递归深度,不提供 sandbox/containment。 | 不要把该函数称为 safe。拒绝绝对路径、home expansion、父级组件还不够稳;更可靠的是对每个结果 `path expand --strict` 后用 `path relative-to $base` 验证,且明确 TOCTOU 风险。 |\n| needs nuance | `security.md:14-18`:无 eval、所有代码执行前完全解析、类型系统防注入、环境变化默认 block-local | “没有 `eval` builtin”方向正确,但同文也承认 `nu -c`/`run` 可解析并执行新代码;“所有代码”过度绝对。类型注解主要是正确性边界,不应宣称能普遍阻止 injection。环境作用域依语法而异:MCP 实测 `do { $env.X='x' }` 和普通 `def` 默认不传播,但 `if true { $env.X='x' }` 的修改在当前 stack 可见;`with-env` 才是明确的临时作用域。 | 改成逐项精确定义,不要用一个“blocks by default”总括。安全收益应归因于“不经 shell 的 argv 传递”和避免字符串再解释,类型系统只作辅助验证。 |\n| needs nuance | `security.md:222-239`:`^rm -rf $user_provided_path` 被标成“glob from user input” | 若变量是普通 string,MCP 实测外部参数 `'*'` 不展开;若值是 `glob` 类型则会展开。因此把所有变量都描述为 glob injection 不准确,但 destructive arbitrary-path 风险仍真实存在。 | 标成“untrusted path / typed-glob expansion risk”,并分别验证值类型、绝对路径、根/home、symlink/canonical containment。 |\n| needs nuance | `security.md:287-302`:`safe-exec` 清理 env 后执行 untrusted command | 清理 loader env 和固定 PATH 能降低 hijacking,但 `run-external $cmd` 仍允许调用者选择 PATH 内任意程序;这不是“安全执行不可信命令”的充分边界。 | 增加 command allowlist 或要求绝对、预配置 executable;函数名避免暗示已建立完整 sandbox。 |\n| true | `security.md:311-320`:Windows CMD internal args 禁止换行和 `%` | 官方源码 [`run_external.rs`](https://github.com/nushell/nushell/blob/0.114.0/crates/nu-command/src/system/run_external.rs#L690-L710) 明确拒绝 CMD internal command 参数中的 newline 与 percent sign,并返回对应错误。 | 保留;注明仅适用于 Nushell 自动转交给 `cmd.exe` 的 CMD internal commands,不代表 `& | \u003c>^` 已全部安全化。 |\n| needs nuance | `SKILL.md:154-156`;`security.md:121-153`:`path relative-to` 可证明 existing path containment | MCP 实测外部路径相对基目录会报 “prefix not found”,基本方法成立;但必须先 canonical/strict expand 以解析 symlink,并只适用于已存在路径。当前 reference 版本先检查存在再 strict expand,比 SKILL 的简版错误体验更好。 | 保留原则,统一采用 reference 的存在性检查和 span label;明确结果验证与实际 `open` 之间仍有 TOCTOU。 |\n| needs nuance | `SKILL.md:120-143`;`anti-patterns.md:587-615`:nested Nu diagnostics 必然含 ANSI/gutters,并按 caller PTY width hard-wrap | 这是 presentation-layer 风险的合理建议,但 `complete` 是否出现 ANSI/PTY wrapping 取决于颜色配置、是否分配 PTY、调用环境和错误 renderer,不是每次都必然发生。 | 把 “contain/include” 改为 “may contain”;保留“不把 rendered diagnostics 当稳定协议”的结论。 |\n| needs nuance | `advanced-patterns.md:141-156` 中 0.113/0.114 命令变更 | structured verbose file-op output、grid 不再接受 single record、metadata `--path-columns`、kill signal syntax 均有官方支持;但 `grid` 的组合陈述有一半错误:0.114 `help grid` 仍明确“默认寻找 `name` column”,表输入省略 column 时仍会渲染 `name`。 | 将 `grid` 条目拆开:“single record 支持已移除”为 true;“不再依赖 implicit name column”为 false。另给 partial-success 行为附官方测试路径。 |\n| false | `advanced-patterns.md:278-281`;`anti-patterns.md:370`;`security.md:269`:`o+e> | complete`后读取`$result.stderr` | MCP 实测合流重定向后 `complete` 只有 `[stdout exit_code]`;直接 ` | complete`才有`[stdout stderr exit_code]`。合流后的 stderr 已进入 `stdout`,示例读取 `stderr`会报列不存在。官方`complete`/redirection 行为与此一致。 | 要分别检查输出时改成 `^cmd | complete`;只有确实需要保持 stdout/stderr 交错顺序时才用 `o+e> | complete`,并读取 `stdout`。 |\n| false | `advanced-patterns.md:106-113`:`open | lines | where`“loads entire file”,而`each --flatten` 才 streaming | `where` 可消费并输出 stream;该管线并不因 `where` 自动全量 materialize。`each --flatten` 可改变嵌套/逐项流的扁平化方式,但不是让简单过滤首次获得 streaming 的必要条件。 | 删除“Bad—loads entire file”标签;若要讨论 streaming,改用确实会 collect/materialize 的命令作为反例。 |\n| false | `bash-to-nushell.md:44`:`${var:0:5}` 等价于 `str substring 0..5` | Nushell range `0..5` 包含右端点;MCP 实测对 `abcdef` 返回 6 个字符。Bash `${var:0:5}` 返回 5 个字符。 | 改为 `str substring 0..\u003c5`(或适合语境的 inclusive `0..4`)。 |\n| false | `bash-to-nushell.md:154`:`rm -rf path` 等价于 `rm -r path` | `-f/--force` 的“不因不存在等情况失败/提示”语义被丢失;这不是等价翻译。0.114 `rm` 有独立 `--force` flag。 | 改为 `rm -rf path` 对应 `rm -r -f path`(并提醒 Nushell destructive 操作仍需 guard)。 |\n| false | `bash-to-nushell.md:158`:`test -f path` 等价于 `path exists` | `path exists` 对目录同样返回 true;MCP 对 `/tmp` 实测为 true,而 POSIX `test -f /tmp` 为 false。 | 改为先检查存在并确认类型,例如 `($path | path type) == 'file'`;按 symlink 需求补充 `path type` 的跟随语义。 |\n| needs nuance | `security.md:319-327`:CMD internal commands 的 `& | \u003c>^` “仍可能危险” | 0.114 官方 `run_external.rs` 不仅拒绝 newline/`%`,还在自动 CMD-internal dispatch 路径对参数整体加引号,并处理相关 CMD metacharacters。把自动 dispatch 一概描述成仍可注入会高估风险;但显式 `cmd.exe /C` 重新解释字符串仍是独立高风险边界。 | 区分两条路径:Nushell 自动调用 CMD internal command 的官方转义保护;用户显式 `cmd.exe /C \u003cuntrusted string>` 的注入风险。不要混写。 |\n| needs nuance | `bash-to-nushell.md` 转换表中的 `cat` 与 `find` 映射 | `cat file` 最接近 `open --raw file`;普通 `open file` 会按扩展名/内容调用格式解析器,可能返回结构化值。`find . -name '*.rs'` 与 `glob **/*.rs` 在隐藏项、symlink、匹配和遍历语义上也不是无条件等价。 | 转换表标注“常见意图映射,不保证 POSIX 逐项等价”;`cat` 默认给 `open --raw`,`find` 条目列出 hidden/symlink/depth 差异。 |\n| needs nuance | `anti-patterns.md`:`http get` 直接返回 structured data | Nushell 会依据服务端 `Content-Type` 和可识别格式解析响应;若 header 缺失/错误或 body 格式不受支持,结果可能仍是 string/binary。官方 `http get --raw` 可明确要求原始 body。 | 改为“当响应 `Content-Type` 指示受支持的结构化格式时会自动解析”;不要无条件禁止 `from json`,应先确认实际返回类型或使用 `--raw` 明确控制。 |\n| false | `SKILL.md:247-253`:用 `nu -c 'source path/to/module.nu'` 对有副作用脚本做 “source/parse-check” | `source` 的官方定义是“Runs a script file in the current context”,不是只解析。MCP 实测被 source 文件的顶层 `save` 确实创建了 marker 文件,因此该建议会执行顶层副作用。0.114 CLI 提供 `nu --ide-check \u003cmax-errors> file.nu` 进行诊断检查。 | 将 parse-only 检查改为 `nu --ide-check 100 file.nu`(按需配 `--no-config-file`);仅在已确认文件顶层无副作用时才用 `source` 作为加载/执行验证。 |\n| false | `modules-and-scripts.md:249-256`:`@search-terms ['http' 'web' 'api']` 可添加三个 search terms | MCP 实测该写法在 `scope commands` 中得到单个字符串 `\"['http' 'web' 'api']\"`;官方 `help attr search-terms` 的签名是 rest string:`@search-terms ...(terms)`。`@search-terms http web api` 才得到 `http, web, api`。 | 示例改为 `@search-terms http web api`。其他列出的 `@example`、`@deprecated`、`@category` 形状在 0.114 `attr` help 中存在。 |\n| needs nuance | `SKILL.md:214-215`;`anti-patterns.md:189-199`;`script-review.md:157-164`:只要输出顺序重要就必须保留 `each` | 0.114 `par-each` 默认不保证输出顺序,但官方有 `--keep-order/-k`。顺序要求本身不必然排除并行;真正要求串行的是有顺序依赖的副作用或任务间依赖。 | 改为:“需要保持结果顺序时可用 `par-each --keep-order`;需要严格串行副作用/依赖时用 `each`。”仍应说明小任务可能不值得并行开销。 |\n| needs nuance | `dataframes.md` 的规模阈值与性能结论;其余 0.114/Polars 命令清单 | 官方 0.114 源码锁定 Polars `0.54.4`,`polars open` lazy-by-default、`collect`、object store、selectors、append/concat、join、pivot、save 等命令/主要 flags 可在 [`crates/nu_plugin_polars`](https://github.com/nushell/nushell/tree/0.114.0/crates/nu_plugin_polars) 中对应;未发现清单级硬错误。但“100k 行分界”“billions”“dramatically faster”“often faster than pandas”是工作负载相关经验值,不是 API 保证或通用事实。 | 保留命令参考,并把固定规模阈值改成 profiling-based guidance;性能比较注明取决于数据类型、操作、内存、线程和是否保持 lazy。`~150` 子命令数量也应视为版本快照而非稳定承诺。 |\n| true | `modules-and-scripts.md:280-293`:Nupm 执行 tests module 中导出的命令 | Nushell 官方组织下的 [Nupm README](https://github.com/nushell/nupm#test_tube-running-a-test-suite-toc) 明确说明:`tests/` 是常规目录模块,tests module 中定义并 `export` 的所有命令会作为测试运行。 | 保留;这是 Nupm 行为,不是 Nushell core 语言保证,建议把证据链接放在该段。 |\n\n## 建议的修订优先级\n\n1. **P0:修复安全示例**:`mktemp` + `save --force`;重写 `safe-glob`;删除不存在的 `--no-expand`。\n2. **P0:修复不可运行/不等价示例**:`o+e>| complete` 后不得读取 `stderr`;substring 使用半开区间;`rm -rf` 与 `test -f` 映射保持原语义;不要把 `source` 当成 parse-only 检查。\n3. **P1:收紧安全模型措辞**:环境作用域、类型系统防注入、动态解析、typed glob 与 string 的差别,以及自动 CMD dispatch 与显式 `/C` 的边界。\n4. **P1:澄清错误处理**:`do -c` 返回的是仍会传播的 error value;`$err.details` 是新增结构化字段但不是 error record 唯一字段。\n5. **P2:降低绝对措辞**:diagnostic presentation、`run --full-reparse`、`to nuon --pretty`、streaming、`grid` 默认列、`par-each` 顺序和 Polars 性能阈值的当前实现细节。\n\n## 可复现实测摘要(Nushell MCP 0.114.0)\n\n```nu\n# mktemp 创建了现有文件,普通 save 会失败\nlet tmp = (mktemp --suffix .tmp)\n'data' | save $tmp\n# => Destination file already exists\n\n# base 目录并不限制绝对 glob\ncd /tmp\nglob '/etc/passwd' --depth 3\n# => /etc/passwd\n\n# string 外部参数不展开,glob typed value 会展开\nlet p = '*'; ^printf '\u003c%s>\\n' $p\nlet p = ('*' | into glob); ^printf '\u003c%s>\\n' $p\n\n# 合流后 complete 没有 stderr 列\nlet merged = (^sh -c 'printf out; printf err >&2; exit 3' o+e>| complete)\n$merged | columns\n# => [stdout exit_code]\n\nlet separate = (^sh -c 'printf out; printf err >&2; exit 3' | complete)\n$separate | columns\n# => [stdout stderr exit_code]\n\n# catch 的 0.114 error record\ntry { error make {msg: 'x'} } catch {|e|\n {top: ($e | columns), details: ($e.details | columns), json: $e.json?}\n}\n# top: [msg debug raw rendered details]\n# details: [msg labels code url help inner]\n# json: null\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":39869,"content_sha256":"1e966b983263139a173e50e1f2ac9d66d3a50ea8421ee131d8c20cd15a700ad8"},{"filename":"docs/scripting-validation-evidence.md","content":"# Scripting validation and interpreter-boundary evidence\n\nVerified on 2026-09-14 with Nu **0.115.1**. The pre-change skill baseline was\n`759a7d2`. Existing validation/daemon and 0.115 smoke suites passed before edits.\nThis is runtime/example validation, not an agent-performance A/B benchmark.\nThe new suite has not been rerun on Nu 0.114.\n\n## Authoritative inputs\n\n- English docs checkout: `nu-docs` at `5ae31c05`, including\n `commands/docs/nu-check.md`, `commands/docs/source.md`, and\n `book/working_with_strings.md`. Both command pages declare version 0.115.1.\n- Nushell source checkout: `b6e6562a9`, specifically\n `crates/nu-command/src/system/nu_check.rs`,\n `crates/nu-command/tests/commands/nu_check.rs`, and\n `crates/nu-command/src/misc/source.rs`.\n- Public English references:\n [nu-check](https://www.nushell.sh/commands/docs/nu-check.html),\n [source](https://www.nushell.sh/commands/docs/source.html), and\n [strings](https://www.nushell.sh/book/working_with_strings.html).\n\nThe local source implementation checks newly added `parse_errors` and returns\n`false`, or prints a diagnostic and throws with `--debug`. It creates a fresh\nworking set without merging it or evaluating the parsed script body. File\ninput sets a file stack; piped text is parsed without a filename, even if a path\nargument is supplied. `source` explicitly evaluates its parsed block.\n\n## Reproductions and corrections\n\n| Input or operation | Observed result | Guidance |\n| ------------------------------------------------ | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |\n| Unclosed parenthesis / `let value: int = \"text\"` | Plain `nu-check`: false with CLI exit 0; `--debug`: nonzero uncaught error | Prefer `--debug` for a CLI gate, consume plain booleans explicitly |\n| Same invalid scripts with `--ide-check` | Exit 0 with Error diagnostic JSONL | Retain record-based checks for structured diagnostics |\n| Missing input | `nu-check` throws; IDE check's missing-file trap remains covered by the existing suite | Do not infer file existence from IDE success |\n| Top-level write to an owned sentinel | Neither checker writes it; `source` writes it | Remove source-as-validation advice |\n| `error make` at runtime | Parse check passes, execution fails | Parsing does not prove behavior |\n| `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

(1 + 1) items'` | Parse check passes, execution produces `2 items` | Assert intended string output |\n| Saved script with sibling import | Direct file check passes; anonymous piped text with the same path fails in the controlled fixture | Pass saved file paths directly |\n| Original basic test-runner example | Parser rejects the escaped single quote in its interpolated print string | Replace generated command source with explicit test closures |\n\n## Executable coverage\n\nRun `nu --no-config-file tests/strings-and-validation-smoke.nu` (Node required,\nno npm packages). Its six groups cover the parse-check contracts, top-level\nside effects versus intentional sourcing, relative imports, string meaning,\nreal JavaScript argument round-trip, and the actual Markdown test-runner example.\nJavaScript values include quotes, parentheses, backslashes, a newline, Unicode,\nan empty string, a leading-zero string, an integer, a boolean and a harmless\ninjection-looking string. The fixed evaluator records that the string did not\nchange its sentinel. The documented runner must pass two tests, reject a forced\nassertion failure and reject zero selected tests without printing success.\n\nOnly the suite's own temporary fixture is removed, in `finally`, and cleanup\nis asserted. The suite captures an explicit pass/failure outcome before cleanup\nand rethrows failure afterward; a negative run against the old documentation\nexits nonzero and does not print its success message. Ordinary Nu parse checks\nare not a sandbox for untrusted source.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4710,"content_sha256":"03aa691dde5e7ad1b9a26da9138fee7e63f5cefa8f4ec9d4afe363b945dd2108"},{"filename":"references/advanced-patterns.md","content":"# Advanced Nushell Patterns Reference\n\n## Contents\n\n- Performance and memory-efficient patterns\n- Nu 0.113/0.114 command behavior\n- Closures, streams, and iteration pitfalls\n- Error handling and stable diagnostics\n- Globs, custom data, row conditions, and debugging\n\n## Performance Optimization\n\n### Lazy vs eager evaluation\n\n```nu\n# Lazy (streaming) — memory efficient for large data, single-pass\nopen large.csv | where status == 'active' | first 10\n\n# Eager (load all) — faster for small data with multiple operations\nlet data = (open large.csv | where status == 'active')\n$data | first 10\n$data | last 10\n$data | length\n```\n\n**Use lazy when:** Large files/streams, single-pass operations, memory constrained\n**Use eager when:** Small datasets (\u003c10k rows), multiple operations on same data, random access needed\n\n> The lazy/eager split above is about **native** row-oriented streaming. For\n> **columnar** large-data work (heavy group-by/join/aggregation over big\n> CSV/Parquet), use the Polars plugin's lazy dataframes instead — see\n> [Dataframes](dataframes.md).\n\n### Inspect streams without collecting: `peek`\n\nUse `peek` when a command needs to inspect whether input is a stream, or sample\nthe first few values, without forcing the whole stream into memory. It exposes\nthe information through pipeline metadata, which you read with `metadata access`.\n\n```nu\n.. | peek 1 | metadata access {|md|\n if $md.peek.stream {\n # Keep the stream lazy; do not call `collect` just to inspect it.\n $in\n } else if ($md.peek.value?.0? != null) {\n $in\n }\n}\n```\n\nThis is useful for commands that want different behavior for lists vs streams,\nor that need to decide based on a small sample while preserving streaming.\n\n### Avoid repeated computation\n\n```nu\n# Bad — computes expensive-func 3 times\nif (expensive-func) > 10 {\n print (expensive-func)\n save-to-file (expensive-func)\n}\n\n# Good — compute once\nlet result = expensive-func\nif $result > 10 {\n print $result\n save-to-file $result\n}\n```\n\n### Parallel processing\n\n```nu\n# Sequential\n$urls | each {|url| http get $url }\n\n# Parallel — faster for I/O operations\n$urls | par-each {|url| http get $url }\n\n# With thread pool size\n$urls | par-each --threads 4 {|url| http get $url }\n```\n\n**Best for:** I/O operations, CPU-intensive transforms, independent operations\n**Avoid for:** Small lists (overhead > benefit), side effects, order-dependent processing\n\n### Stream flattening with each --flatten\n\n```nu\n# Without --flatten: waits for each stream to complete, returns list\u003clist\u003cstring>>\nls *.txt | each {|f| open $f.name | lines }\n\n# With --flatten: streams items as they arrive, returns list\u003cstring>\nls *.txt | each --flatten {|f| open $f.name | lines }\n\n# Practical: search across files without waiting for all to load\nls **/*.nu | each --flatten {|f|\n open $f.name | lines | find 'export def'\n} | str join (char nl)\n```\n\n## Memory-Efficient Patterns\n\n### Processing large files\n\n```nu\n# Bad — `collect` materializes the whole stream before filtering\nopen large.log | lines | collect | where {$in =~ 'ERROR'}\n\n# Good — `where` preserves streaming input\nopen large.log | lines | where {$in =~ 'ERROR'}\n```\n\n### Batched processing\n\n```nu\n# Process in chunks of 1000\nopen large.csv | chunks 1000 | each {|batch|\n $batch | process-batch\n} | flatten\n```\n\n### `parse` stream behavior in 0.113+\n\n`parse` no longer implicitly splits byte/string streams into lines. It collects\nthe stream and parses it as one input value. If you intend line-by-line parsing,\ninsert `lines` explicitly:\n\n```nu\n# Whole-input parse: useful for multiline regexes\nopen file.txt | parse -r r#'(?ms)^(?\u003cid>\\d+): (?\u003cbody>.*)

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

#\n\n# Line-by-line parse\nopen file.txt | lines | parse -r '^(?\u003clevel>\\w+) (?\u003cmessage>.*)

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

\n```\n\n## Command Behavior Notes (0.113 / 0.114+)\n\n- `mkdir -v`, `mv -v`, and `rm -v` return structured tables. Script against\n columns such as `path`, `created`, `deleted`, `error`, and `message` instead\n of parsing human text.\n- `rm` and `mkdir` try the remaining path arguments even when one path errors.\n Review partial-success behavior for destructive or setup scripts.\n- `from md` now defaults to concise output. Use `from md --verbose` when code\n needs the full AST-style detail.\n- `watch`'s optional closure argument is deprecated. Pipe events into `each` or\n iterate with `for event in (watch ...)`.\n- `grid` no longer accepts a single record as input. For table/list input it\n still uses a `name` column by default when present; pass a column explicitly\n when another field is intended, for example `ls | grid name`.\n- `metadata set --datasource-ls` was removed. Use\n `metadata set --path-columns [name]` for path metadata on table columns.\n- On Unix-like systems, `kill -9 pid` shorthand is no longer accepted. Use\n `kill -s 9 \u003cpid>` (or `kill -s 0 \u003cpid>` for signal 0 checks).\n- `finally` runs for cleanup but its return value does not override `try` or\n `catch`. Use the `try`/`catch` result for values and keep `finally` for side\n effects such as cleanup.\n- In 0.113.1, `to yaml` emits more idiomatic plain scalars where safe and uses\n block style for multiline strings. Do not assert exact quotes around every\n string in YAML golden tests.\n- Nu 0.114 supports POSIX-style `--` for builtins and ordinary custom commands,\n which consume it before dash-prefixed positional values. `def --wrapped`\n commands and known externs preserve/forward `--` to their rest arguments.\n- Use `run script.nu` for reusable pipeline-stage scripts. It is a parser\n keyword, so the script must exist when the calling block is parsed. It accepts\n pipeline input, runs isolated from the caller, and supports `--full-reparse`\n for watch or repeated execution loops.\n- `try/catch` error records now expose structured diagnostics as `$err.details`;\n `$err.json` was removed.\n- `str upcase` / `str downcase` are deprecated; use `str uppercase` /\n `str lowercase`.\n- `split row` and `split column` support `--right` to count skipped splits from\n the right side, useful for package/version strings.\n- `ignore` supports `--stderr`, `--stdout`, and `--show-errors` for precise\n stream handling.\n- `append` accepts multiple values; `union`, `intersect`, `difference`,\n `combinations`, and `permutations` cover common immutable list/set work.\n- `from kdl` / `to kdl` support KDL v2 data. `to nuon --pretty` enables the\n current readable pretty formatting (two-space indentation and aligned table\n columns in 0.114).\n- `lines` replaces invalid UTF-8 by default; use `lines --strict` when invalid\n bytes should fail the pipeline.\n- `url encode` accepts binary input and `url decode --binary` can return binary\n for non-UTF-8 data.\n- `0.1..0.3` now uses natural fractional steps (`0.1, 0.2, 0.3`) and rounds\n serialized values to the step precision.\n\n## Advanced Closure Patterns\n\n### Closure composition\n\n```nu\nlet double = {|x| $x * 2 }\nlet add_ten = {|x| $x + 10 }\n\n# Compose manually\n[1 2 3] | each {|x| do $add_ten (do $double $x) }\n# Result: [12, 14, 16]\n\n# Or build a composed closure\nlet transform = {|x| do $double $x | do $add_ten $in }\n[1 2 3] | each $transform\n```\n\n### Closure currying pattern\n\n```nu\ndef make-multiplier [factor: int] {\n {|x| $x * $factor }\n}\n\nlet triple = (make-multiplier 3)\nlet quadruple = (make-multiplier 4)\n\n[1 2 3] | each $triple # [3, 6, 9]\n[1 2 3] | each $quadruple # [4, 8, 12]\n```\n\n### Closures capture environment (immutable only)\n\n```nu\nlet multiplier = 10\nlet compute = {|x| ($x * 2) + $multiplier }\ndo $compute 5 # 20\n\n# Mutable variables CANNOT be captured in closures\nmut sum = 0\n[1 2 3] | each {|x| $sum += $x } # Error!\n\n# Use reduce instead\nlet sum = [1 2 3] | reduce {|x, acc| $acc + $x }\n```\n\n## Stream Patterns\n\n### Generate infinite sequences\n\n```nu\n# Fibonacci using generate\ngenerate {|state|\n let a = $state.0\n let b = $state.1\n {out: $a, next: [$b, ($a + $b)]}\n} [0, 1] | first 10\n```\n\n### Stream control\n\n```nu\n$stream | skip while {|x| $x \u003c 100 } # Skip until condition false\n$stream | take while {|x| $x \u003c 1000 } # Take until condition false\n[1 2 3 4 5] | window 3 # Sliding window: [[1,2,3], [2,3,4], [3,4,5]]\n$data | chunks 100 # Fixed-size batches\n```\n\n## Advanced Error Handling\n\n### Graceful degradation\n\n```nu\ndef robust-fetch [url: string] {\n try {\n http get $url\n } catch {\n try {\n ^curl -s $url | from json\n } catch {\n {error: 'All fetch methods failed'}\n }\n }\n}\n```\n\n### External command error handling with complete\n\n```nu\nlet result = (^cargo build | complete)\nif $result.exit_code != 0 {\n print -e

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Build failed:\\n($result.stderr)'\n} else {\n print 'Build succeeded'\n}\n```\n\n### Terminal-width-independent diagnostic assertions\n\nNested Nushell failures captured through `complete` are formatted for humans.\nANSI styling, diagnostic `|` gutters, and PTY-width hard wrapping make raw\n`stderr` unsuitable for exact or contiguous substring assertions. Prefer\nstructured `$err.details` for in-process tests. For a CLI boundary, normalize\nboth the captured diagnostic and expected phrase before comparing:\n\n```nu\ndef compact-diagnostic [value: any]: nothing -> string {\n $value\n | into string\n | ansi strip\n | str replace --all --regex r#'[\\s|]+'# ''\n}\n\n(\n assert str contains\n (compact-diagnostic $result.stderr)\n (compact-diagnostic 'License JSON exceeds 65536 UTF-8 bytes')\n)\n```\n\nUse a long, domain-specific expected phrase. Verify terminal-sensitive\nregressions with a narrow PTY as well as the normal test command.\n\n### Suppress errors with `do -i` / `do -c`\n\n`do -i` (ignore errors) runs a closure and suppresses errors, returning null on failure.\n`do -c` (capture errors) turns failures inside the closure, including non-zero\nexternal exits, into Nushell pipeline errors. Those errors stop downstream\ncommands unless an enclosing `try/catch` handles them; they are not ordinary\nrecord values.\n\n```nu\n# Fire-and-forget — silently ignore failure\ndo -i { rm $old_file }\n\n# Concise default value pattern\nlet config = (do -i { open settings.toml } | default {})\n\n# Convert an external non-zero exit into a catchable Nushell error\ntry {\n do -c { ^failing-cmd }\n} catch {|err|\n $err.details\n}\n\n# Compare error handling approaches:\n# do -i — suppress error, return null (simplest)\n# do -c — convert closure/external failures into pipeline errors\n# try/catch — inspect/log/recover from errors\n# complete — full exit_code + stdout + stderr for externals\n```\n\n## Advanced Glob Patterns\n\n```nu\n# Multiple extensions\nglob **/*.{rs,toml,md}\n\n# Exclusions\nglob **/*.rs --exclude [**/target/** **/tests/**]\nglob **/tsconfig.json --exclude [**/node_modules/**]\n\n# Character classes\nglob '[Cc]*' # Files starting with C or c\nglob '[!0-9]*' # Files NOT starting with digit\nglob 'src/[a-m]*.rs' # Files starting with a-m\n\n# Depth limit\nglob **/*.rs --depth 2 # Max 2 directories deep\n\n# Directory only\nglob '[A-Z]*' --no-file --no-symlink # Only directories starting with uppercase\n\n# Follow symlinks\nglob '**/*.txt' --follow-symlinks\n\n# Case-insensitive (wax syntax)\nglob '(?i)readme*'\n```\n\n## Custom Data Types with Structured Output\n\n```nu\ndef make-report [title: string, data: table]: nothing -> record {\n {\n title: $title\n generated: (date now)\n row_count: ($data | length)\n columns: ($data | columns)\n data: $data\n }\n}\n```\n\n## Row Conditions vs Closures (Deep Dive)\n\n### Row conditions — short-hand syntax\n\n```nu\n# Left side auto-expands to $it.field\n$table | where size > 100 # $it.size > 100\n$table | where name =~ 'test' # $it.name =~ 'test'\nls | where type == file # Simple and readable\n\n# Limitation: subexpressions need explicit $it\nls | where ($it.name | str lowercase) =~ readme\n```\n\n### Closures — full flexibility\n\n```nu\n$table | where {|row| $row.size > 100 }\n$table | where {$in.size > 100 }\n\n# Can be stored and reused\nlet big_files = {|row| $row.size > 1mb }\nls | where $big_files\n\n# Works anywhere\n$list | each {|x| $x * 2 }\n```\n\n**Row conditions:** Simple field comparisons (cleaner syntax), cannot be stored in variables\n**Closures:** Complex logic, reusable conditions, nested operations\n\n## Iteration Pitfalls\n\n### each on single records\n\n```nu\n# Bad — runs only once, not iterating fields!\nlet rec = {a: 1, b: 2}\n$rec | each {|field| print $field } # Only runs once\n\n# Good — use items, values, or transpose\n$rec | items {|key, val| print

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

($key): ($val)' }\n$rec | transpose key val | each {|row| ... }\n```\n\n### Pipe vs call ambiguity\n\n```nu\n# These are different!\n$list | my-func arg1 arg2 # $list piped as input, arg1 & arg2 as params\nmy-func $list arg1 arg2 # All three as positional params (if signature allows)\n```\n\n## Debugging Techniques\n\n```nu\n# Inspect type\n$value | describe\n\n# Print intermediate values without breaking pipeline\n$data | each {|x| print $x; $x }\n\n# Measure execution time\ntimeit { expensive-command }\n\n# Inspect metadata (span info for error reporting)\nmetadata $value\n\n# View full command signature\nhelp my-command\nscope commands | where name == 'my-command'\n```\n\n## Nu 0.116 diagnostics and cleanup\n\nNested `finally` now unwinds before an outer `catch`; the old 0.115.0 boundary\nworkaround is unnecessary when the minimum supported version is 0.116. Errors\ninside cleanup still replace the original error, so test failure paths too.\nStream consumers (`length`, `columns`, `is-empty`) and `each while` now surface\nupstream errors. Same-size chained `par-each` pools no longer deadlock.\n\nFor interactive tools use `tui debug` for headless key replay; `nu --dap` is\nthe editor debugging interface. `std/log` emitters accept `--context` fields;\ndo not include secrets. See [Nu 0.116 Migration](nu-0.116-migration.md) for\ntested examples and the limits of noninteractive verification.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":14038,"content_sha256":"9f84e3656f1dac7fdec0e3b3f3db2c7267ed7caa55733e1b26028a40d38f93f7"},{"filename":"references/anti-patterns.md","content":"# Nushell Anti-Patterns Reference\n\nCommon mistakes and their idiomatic fixes when writing Nushell scripts.\n\n## Contents\n\n- 1–10: Return values, loops, mutation, imports, redirection, types, closures, environment, and strings\n- 11–20: Parallelism, documentation, defaults, structured data, branching, modules, pipeline input, records, and null safety\n- 21–27: External commands, errors, collections, large data, parsing, multiline flags, and format strings\n- 28–34: Nu 0.114 migrations, SemVer, error records, spreadsheets, and stable diagnostic assertions\n- 35: Literal parentheses inside string interpolation\n\n## 1. Using `echo` Instead of Implicit Return\n\nNushell implicitly returns the last expression's value. `echo` is almost never needed.\n\n```nu\n# Bad\ndef greet [name: string]: nothing -> string {\n echo

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Hello, ($name)!'\n}\n\n# Good\ndef greet [name: string]: nothing -> string {\n

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Hello, ($name)!'\n}\n```\n\nUse `print` when you want to display a message as a side effect (not as return value):\n\n```nu\ndef process [] {\n print 'Processing...' # Side effect: displayed to user\n do-work # Return value: result of do-work\n}\n```\n\n**Bash-migration trap — `echo` is not `print`.** Unlike Bash's `echo`,\nNushell's `echo` only _produces a pipeline value_; it does not print as a side\neffect. That value is rendered only when it becomes final output. Used as a\nnon-final statement, the value is silently discarded — so `echo 'msg'` meant as a\n\"print this\" statement displays **nothing**:\n\n```nu\n# Bad — nothing is printed; the echo value is dropped before exit\nif (has-ref $version) {\n echo

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Version ($version) already exists' # silently discarded!\n exit 1\n}\n\n# Good — print writes to stdout as a side effect\nif (has-ref $version) {\n print

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Version ($version) already exists'\n exit 1\n}\n```\n\n## 2. Using `for` as Final Expression\n\n`for` is a statement that returns `null`. Use `each` for transformations.\n\n```nu\n# Bad — returns nothing\ndef squares []: nothing -> list\u003cint> {\n for x in [1 2 3 4] { $x ** 2 }\n}\n\n# Good — returns the list\ndef squares []: nothing -> list\u003cint> {\n [1 2 3 4] | each {|x| $x ** 2 }\n}\n```\n\n## 3. Mutable Variables for Accumulation\n\nWhen mutation is genuinely needed, declare it with `mut`; `let mut` is invalid\nNushell syntax. Type annotations follow the binding name.\n\n```nu\n# Bad — Rust-style declaration does not parse\nlet mut errors: list\u003cstring> = []\n\n# Correct declaration syntax\nmut errors: list\u003cstring> = []\n```\n\n```nu\n# Bad — imperative accumulation\nmut total = 0\nfor item in $items { $total += $item.price }\n\n# Good — math sum\n$items | get price | math sum\n\n# Bad — building a list with mutation\nmut result = []\nfor f in (ls) {\n if ($f.size > 1mb) { $result = ($result | append $f.name) }\n}\n\n# Good — filter pipeline\nls | where size > 1mb | get name\n```\n\n## 4. Dynamic `source`/`use` Paths\n\nNushell parses all code before evaluation. `source`/`use` require parse-time constant paths.\n\n```nu\n# Bad — let is evaluated at runtime\nlet my_path = '~/scripts'\nsource

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

($my_path)/utils.nu' # Error!\n\n# Good — use const for parse-time resolution\nconst my_path = '~/scripts'\nsource

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

($my_path)/utils.nu'\n```\n\n## 5. Bash-Style Redirection\n\n```nu\n# Bad — > is the comparison operator in Nushell\n'hello' > file.txt # This is a boolean comparison, not redirection!\n\n# Good — use save command\n'hello' | save file.txt\n'hello' | save --append file.txt\n```\n\n## 6. String Parsing External Commands\n\n```nu\n# Bad — parsing ls output as strings\n^ls -la | lines | each {|l| $l | split column ' ' }\n\n# Good — use Nushell's structured ls\nls -la\n\n# Bad — parsing JSON from curl\n^curl -s https://api.example.com | from json\n\n# Good — supported structured responses are parsed according to Content-Type\nhttp get https://api.example.com\n```\n\n## 7. Ignoring Type Annotations\n\n```nu\n# Bad — untyped, hard to catch errors\ndef process [data] { $data | get name }\n\n# Good — typed, catches misuse at parse time\ndef process [data: record\u003cname: string, age: int>]: nothing -> string {\n $data.name\n}\n\n# Good — I/O signature\ndef double []: int -> int { $in * 2 }\n```\n\n## 8. Space Before Closure Parameters\n\n```nu\n# Bad — space before |params|\nls | each { |f| $f.name }\n\n# Good — no space before |params|\nls | each {|f| $f.name }\n```\n\n## 9. Environment Changes in Regular `def`\n\n```nu\n# Bad — cd change is lost after command returns\ndef go-project [] { cd ~/projects/my-app }\ngo-project\npwd # Still in original directory!\n\n# Good — use def --env to propagate environment changes\ndef --env go-project [] { cd ~/projects/my-app }\ngo-project\npwd # Now in ~/projects/my-app\n```\n\n## 10. Unnecessary String Interpolation\n\n```nu\n# Bad — interpolation with no variables\nlet msg = $\"hello world\"\n\n# Good — simple string\nlet msg = 'hello world'\n\n# Bad — double-quoted interpolation without escapes\nlet greeting = $\"Hello, ($name)!\"\n\n# Good — single-quoted interpolation (no escapes needed)\nlet greeting =

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Hello, ($name)!'\n```\n\nThis preference has one hard exception: a string containing a **literal `(`**\nmust use `$\"...\"`. See [#35](#35-using-single-quoted-interpolation-for-strings-with-literal-parentheses).\n\n## 11. Using `each` When `par-each` Works\n\n```nu\n# Suboptimal — sequential file processing\nls **/*.json | each {|f| open $f.name | get version }\n\n# Better — parallel processing for I/O bound work\nls **/*.json | par-each {|f| open $f.name | get version }\n```\n\nUse `each` only when: order must be preserved, side effects must be sequential, or list is very small.\n\n## 12. Missing Command Documentation\n\n```nu\n# Bad — no documentation\ndef deploy [env, --force] { ... }\n\n# Good — documented command\n# Deploy the application to the specified environment\n#\n# Handles building, testing, and deployment in one step.\n@example 'Deploy to staging' { deploy staging }\ndef deploy [\n env: string # Target environment (staging, production)\n --force (-f) # Skip confirmation prompts\n] { ... }\n```\n\n## 13. Not Using `default` for Optional Values\n\n```nu\n# Bad — verbose null check\nlet name = if $input == null { 'anonymous' } else { $input }\n\n# Good — use default command\nlet name = $input | default 'anonymous'\n```\n\nA negative literal in command-argument position can be parsed as an unknown\nflag. Parenthesize it as an expression or terminate flag parsing explicitly:\n\n```nu\n# Bad — may be parsed as flag `-1`\n$input | default -1\n\n# Good\n$input | default (-1)\n$input | default -- -1\n```\n\n**Gotcha — the default _argument_ is evaluated eagerly**, even when the input is\nnon-null, because it is an ordinary argument (not a closure). A \"fallback\" that\ncan itself error or is expensive runs regardless:\n\n```nu\n# Bad — $rec.maybe_missing is evaluated even when $primary is non-null,\n# and throws if that column is absent\n$primary | default $rec.maybe_missing\n\n# Bad — coalesce-style fallback still throws when the key is missing,\n# because default's argument is evaluated before default runs\n$delta.reasoning? | default $delta.content # errors if there is no `content` key\n\n# Good — make the fallback itself null-safe\n$delta.reasoning? | default ($delta.content? | default '')\n\n# Good — branch explicitly when the fallback is expensive\nif ($primary | is-not-empty) { $primary } else { compute-fallback }\n```\n\n## 14. Manual JSON/YAML/TOML Parsing\n\n```nu\n# Bad — manual string manipulation\nlet version = (open Cargo.toml | lines | where $it =~ '^version' | first | split column '=' | get column2.0 | str trim)\n\n# Good — native structured data support\nlet version = (open Cargo.toml | get package.version)\n```\n\nDo not assume a decoded JSON array of objects will describe as `list\u003crecord>`.\nHomogeneous rows are commonly inferred as `table\u003c...>`, which is still\nlist-like:\n\n```nu\nlet rows = ('[{\"id\":1},{\"id\":2}]' | from json)\n$rows | describe # table\u003cid: int>\n$rows | describe --detailed | get type # list\n```\n\nFor a list-or-table boundary, prefer a typed `list` parameter or the structured\ntop-level type from `describe --detailed`; a string-prefix test for only `list`\nrejects valid tables.\n\n## 15. Not Using `match` for Multi-Branch Logic\n\nPrefer `match` whenever several branches dispatch on the same value. It keeps\nthe compared value in one place, makes the fallback explicit with `_`, and\navoids drifting conditions in long `if`/`else if` ladders. Keep `if` for one-off\nboolean predicates or branches that genuinely compare different expressions.\n\n```nu\n# Less clear — chain of if/else\nif $status == 'ok' { handle-ok }\nelse if $status == 'error' { handle-error }\nelse if $status == 'pending' { handle-pending }\nelse { handle-unknown }\n\n# Preferred — pattern matching\nmatch $status {\n ok => { handle-ok }\n error => { handle-error }\n pending => { handle-pending }\n _ => { handle-unknown }\n}\n```\n\n## 16. Incorrect Shebang for stdin Scripts\n\n```nu\n# Bad — script won't receive stdin\n#!/usr/bin/env nu\n\n# Good — add --stdin flag\n#!/usr/bin/env -S nu --stdin\ndef main [] { $in | process }\n```\n\n## 17. Forgetting `export` in Modules\n\n```nu\n# Bad — command is private, can't be imported\n# my-module.nu\ndef helper [] { 'hello' }\n\n# Good — export makes it public\nexport def helper [] { 'hello' }\n\n# Also good — keep internal helpers private intentionally\ndef internal-helper [] { 'private' }\nexport def public-cmd [] { internal-helper }\n```\n\n## 18. Confusing Pipeline Input with Parameters\n\n```nu\n# Bad — treats pipeline data as positional parameter\ndef my-func [items: list, value: any] {\n $items | append $value\n}\n\n# Good — declares pipeline input signature\ndef my-func [value: any]: list -> list {\n $in | append $value\n}\n\n# Usage: [1 2 3] | my-func 4\n```\n\n**Why:** Pipeline input is lazily evaluated (streaming); parameters are eagerly loaded. Different calling conventions entirely.\n\n## 19. Using `each` on Single Records\n\n```nu\n# Bad — runs only once, not iterating fields!\nlet rec = {a: 1, b: 2}\n$rec | each {|field| print $field }\n\n# Good — iterate key-value pairs\n$rec | items {|key, val| print

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

($key): ($val)' }\n$rec | transpose key val | each {|row| ... }\n```\n\n## 20. Accessing Missing Fields Without `?`\n\n```nu\n# Bad — error if field doesn't exist\n$record.missing_field # Error!\n\n# Good — use ? for optional access\n$record.missing_field? # Returns null\n$record.missing_field? | default 0 # Provide fallback\n```\n\n## 21. Not Prefixing External Commands with `^`\n\n```nu\n# Ambiguous — could be Nushell builtin or external\nfind pattern # This is Nushell's find, NOT Unix find!\nsort # This is Nushell's sort, NOT Unix sort!\n\n# Clear — explicitly calls external\n^find . -name '*.rs' # Unix find\n^sort file.txt # Unix sort\n^grep pattern file # External grep\n```\n\n**Rule:** Nushell builtins always take precedence. Use `^` to unambiguously call external commands.\n\n## 22. Ignoring `complete` for External Command Errors\n\n```nu\n# Bad — no error handling for external commands\nlet output = (^cargo build)\n\n# Good — use complete for full error info\nlet result = (^cargo build | complete)\nif $result.exit_code != 0 {\n print -e

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Build failed:\\n($result.stderr)'\n}\n```\n\n## 23. Empty Collection Checks\n\n```nu\n# Bad — comparing length\nif ($list | length) == 0 { ... }\n\n# Good — use is-empty / is-not-empty\nif ($list | is-empty) { ... }\nif ($list | is-not-empty) { ... }\n```\n\n## 24. Native Loops or `group-by` on Huge Datasets\n\nNative `each`/`reduce`/`group-by` process data row by row. For very large\ndatasets this is slow and memory-hungry. Push the work into the `polars`\nplugin's columnar dataframes instead.\n\n```nu\n# Bad — native group-by + manual aggregation over millions of rows\nopen huge.csv\n| group-by category --to-table\n| update items {|g| $g.items.amount | math sum }\n\n# Good — Polars: lazy by default, optimized columnar aggregation\npolars open huge.csv\n| polars group-by category\n| polars agg (polars col amount | polars sum | polars as total)\n| polars collect\n```\n\nAlso remember to **`collect`** a lazy frame — without it you hold a plan, not\nresults. See [Dataframes](dataframes.md).\n\n## 25. Assuming `parse` Splits Streams into Lines\n\nSince Nushell 0.113, `parse` no longer implicitly splits byte/string streams\nfrom files or external commands into lines. It collects the stream and parses it\nas one input value. Add `lines` when you want the old line-by-line behavior.\n\n```nu\n# Bad — expects one match per line, but parses the stream as one input value\nopen app.log | parse -r '^(?\u003clevel>\\w+) (?\u003cmessage>.*)

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

\n\n# Good — explicit line-by-line parsing\nopen app.log | lines | parse -r '^(?\u003clevel>\\w+) (?\u003cmessage>.*)

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

\n\n# Also good — intentionally parse the whole file with a multiline regex\nopen changelog.txt | parse -r r#'(?ms)^## (?\u003cversion>.*?)\\n(?\u003cbody>.*)'#\n```\n\n## 26. Splitting Custom Command Flags Across Lines Without Grouping\n\nA newline can terminate a command invocation. If the next line starts with a\nnamed flag, Nushell may parse it as a separate statement that emits a plain\nstring or produces a parse error instead of passing the flag to the command.\n\n```nu\n# Bad — flags start new statements\nbuild-report $target\n--format json\n--strict\n\n# Good — short calls stay on one line\nbuild-report $target --format json --strict\n\n# Good — explicit multiline invocation\nlet report = (\n build-report $target\n --format json\n --strict\n)\n```\n\nUse the same pattern when reviewing scripts generated from Bash-style wrapped\ncommands: either keep named flags on the command line or group the whole call.\n\n## 27. Expecting Single-Quoted External Format Strings to Interpret Backslash Escapes\n\nSingle-quoted Nushell strings do not process backslash escapes. Double-quoted\nstrings do, but `\\\\t` means \"literal backslash followed by t\". Prefer one\nbackslash in double quotes for simple escapes so Nushell passes a real tab or\nnewline. Use `char tab` / `char nl` when the string also needs Nushell\ninterpolation.\n\n```nu\n# Bad — single quotes pass literal \"\\t\"\n^git log --format='%H\\t%an'\n\n# Bad — double backslash also passes literal \"\\t\"\n^git log --format=\"%H\\\\t%an\"\n\n# Preferred — simple and Nushell passes an actual tab\n^git log --format=\"%H\\t%an\"\n\n# Good — explicit separator, useful with interpolation\nlet tab = (char tab)\n^git log --format=

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

%H($tab)%an'\n\n# Good — use real line delimiters when that makes parsing simpler\nlet nl = (char nl)\n^some-tool --format=

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

name=%n($nl)email=%e'\n| lines\n| parse '{key}={value}'\n```\n\n## 28. Using Deprecated Case Conversion Commands\n\nNu 0.114 deprecates `str upcase` and `str downcase`.\n\n```nu\n# Bad — deprecated\n$name | str upcase\n$name | str downcase\n\n# Good\n$name | str uppercase\n$name | str lowercase\n```\n\n## 29. Depending on Implicit Submodule Imports\n\nNu 0.114 no longer imports exported submodules implicitly when the parent module\nis imported.\n\n```nu\n# Bad — callers expect `use foo` to expose `foo sub cmd` implicitly\nexport module sub {\n export def cmd [] { 'ok' }\n}\n\n# Good — re-export the intended submodule surface\nexport module sub {\n export def cmd [] { 'ok' }\n}\nexport use sub\n```\n\nUse `export use sub *` only when flattening submodule commands into the parent\nnamespace is the public API you want.\n\n## 30. Treating Optional Parameters as Non-Null\n\nOptional positional parameters and typed named options without defaults are\n`oneof\u003cT, nothing>` in Nu 0.114+. Boolean switch flags such as `--flag` remain\n`bool` and default to `false`. Handle `null` before arithmetic, string\noperations, or assignment to a non-null annotated variable.\n\n```nu\n# Bad — $count can be null\ndef repeat [count?: int] {\n 0..\u003c$count\n}\n\n# Good — provide a default\ndef repeat [count?: int = 0] {\n 0..\u003c$count\n}\n\n# Also good — branch explicitly\ndef repeat [count?: int] {\n let count = ($count | default 0)\n 0..\u003c$count\n}\n```\n\n## 31. Hand-Rolling Semantic Version Logic\n\nLexical string sort and manual splitting are wrong for SemVer (`1.10.0` sorts\nbefore `1.2.0` as a string).\n\n```nu\n# Bad\n['1.10.0' '1.2.0'] | sort\n$version | split row '.' | update 1 {|n| ($n | into int) + 1 }\n\n# Good\n['1.10.0' '1.2.0'] | each { into semver } | sort\n'1.2.3' | into semver | semver bump minor\n'1.2.3' | into semver | $in in ('>=1.0.0' | into semver-range)\n```\n\n## 32. Reading Removed Error JSON\n\nNu 0.114 removed `$err.json` from `catch` records. Use `$err.details` directly.\n\n```nu\n# Bad\ntry { risky-command } catch {|err| $err.json | from json }\n\n# Good\ntry { risky-command } catch {|err| $err.details }\n```\n\n## 33. Using Removed Spreadsheet Header Flags\n\n`from xlsx --header-row` was removed. `from xlsx` and `from ods` return a record\nof sheet tables and use explicit header/start controls.\n\n```nu\n# Bad\nopen report.xlsx --raw | from xlsx --header-row 2\n\n# Good — one sheet table from the workbook record\nopen report.xlsx | get Sheet1\n\n# Good — control headers and starting rows\nopen report.xlsx --raw | from xlsx --noheaders\nopen report.xlsx --raw | from xlsx --first-row 0\nopen report.xlsx --raw | from xlsx --prefer-integers\n```\n\n## 34. Asserting Raw Rendered Nushell Diagnostics\n\nDiagnostics captured from a nested `nu` process are formatted according to the\ncaller's terminal width. Nu may insert `|` gutters and line breaks inside a\nmessage, so a direct `assert str contains $result.stderr 'expected message'`\ncan fail only in narrower terminals.\n\n```nu\n# Bad — depends on PTY width and ANSI rendering\nassert str contains $result.stderr 'OpenSSL key generation failed'\n\n# Good — normalize actual and expected presentation text\ndef diagnostic-text [value: any]: nothing -> string {\n $value\n | into string\n | ansi strip\n | str replace --all --regex r#'[\\s|]+'# ''\n}\n\n(\n assert str contains\n (diagnostic-text $result.stderr)\n (diagnostic-text 'OpenSSL key generation failed')\n)\n```\n\nPrefer direct `try/catch` and `$err.details` when the test does not need a real\nsubprocess boundary. When it does, use a long, specific phrase and verify once\nunder a narrow PTY.\n\n## 35. Using Single-Quoted Interpolation for Strings With Literal Parentheses\n\n`

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` does no escape processing, so every `(` inside it opens an\ninterpolation expression — and `\\(` cannot escape it. A literal paren is only\nexpressible in `$\"...\"`, written `\\(`. This is the most frequently repeated\nNushell string mistake, and two of its three failure modes are silent.\n\n```nu\nlet count = 3\n\n# Bad — silently evaluated, no error: yields \"2 items\"\n

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

(1 + 1) items'\n\n# Bad — `env` is a real command, so its whole output (secrets included)\n# is spliced into the message\n

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Usage: deploy (env) --force'\n\n# Bad — runtime error: `s` is neither a built-in nor an external command\n

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Done ($count) file(s)'\n\n# Good — `\\(` is a literal paren and `($count)` still interpolates\n$\"Done ($count) file\\(s)\" # => \"Done 3 file(s)\"\n$\"Usage: deploy \\(env) --force\" # => \"Usage: deploy (env) --force\"\n$\"\\(1 + 1) items\" # => \"(1 + 1) items\"\n$\"[docs]\\(https://example.com)\" # => \"[docs](https://example.com)\"\n```\n\nOnly `(` needs escaping; a `)` outside an expression is already literal. Keep\n`

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` for the common case with no literal parens — especially when the string\ncontains literal backslashes, which `$\"...\"` would force you to double.\n\nSee [String Formats](string-formats.md) for the parser rule and the full\ndecision table.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":19465,"content_sha256":"7d72d6b3babd6a550f1b63abd57d870abdb30a3a06440a3130dc3ecded192f1f"},{"filename":"references/archive/README.md","content":"# Migration archive\n\nThe main skill links only the three newest minor-version migration guides.\nOlder guides are moved here without deleting their contents. Update incoming\nlinks and each moved guide's relative links when archiving.\n\nNo guides are archived yet: 0.114, 0.115, and 0.116 are the three current guides.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":316,"content_sha256":"1be8fc8ff5f7dadf5510a9c130050bed880084f9cdcd745d1eedcfd73383c6fa"},{"filename":"references/bash-to-nushell.md","content":"# Bash to Nushell Conversion Reference\n\nQuick reference for converting common Bash patterns to idiomatic Nushell.\n\n## Contents\n\n- Redirections, variables, strings, and conditionals\n- Loops, files, command substitution, and functions\n- Lists, records, processes, and JSON\n- Error handling and common migration patterns\n\n## Redirections & Pipes\n\n| Bash | Nushell | Notes |\n| ----------------------------- | -------------------------------------- | ------------------------ |\n| `echo \"text\" > file` | `'text' \\| save file` | |\n| `echo \"text\" >> file` | `'text' \\| save --append file` | |\n| `cmd 2>/dev/null` | `cmd e>\\| ignore` | Discard stderr |\n| `cmd > /dev/null 2>&1` | `cmd o+e>\\| ignore` | Discard all output |\n| `cmd 2>&1` | `cmd o+e>\\| ...` | Merge stderr into stdout |\n| `cmd1 \\| tee log.txt \\| cmd2` | `cmd1 \\| tee { save log.txt } \\| cmd2` | |\n| `cmd \\| head -5` | `cmd \\| first 5` | |\n| `cmd \\| tail -3` | `cmd \\| last 3` | |\n\n## Variables\n\n| Bash | Nushell | Notes |\n| ---------------------- | -------------------------------- | -------------------- |\n| `FOO=\"bar\"` | `let foo = 'bar'` | Immutable by default |\n| `FOO=\"bar\"` | `mut foo = 'bar'` | When mutation needed |\n| `readonly FOO=\"bar\"` | `const foo = 'bar'` | Parse-time constant |\n| `export FOO=\"bar\"` | `$env.FOO = 'bar'` | Environment variable |\n| `echo $FOO` | `$env.FOO` | Access env var |\n| `echo ${FOO:-default}` | `$env.FOO? \\| default 'default'` | With fallback |\n| `echo $?` | `$env.LAST_EXIT_CODE` | Last exit code |\n| `echo $RANDOM` | `random int` | Random number |\n\n## String Operations\n\n| Bash | Nushell |\n| ----------------- | ----------------------------------- |\n| `${var^^}` | `$var \\| str uppercase` |\n| `${var,,}` | `$var \\| str lowercase` |\n| `${var:0:5}` | `$var \\| str substring 0..\u003c5` |\n| `${#var}` | `$var \\| str length` |\n| `${var/old/new}` | `$var \\| str replace old new` |\n| `${var//old/new}` | `$var \\| str replace --all old new` |\n| `${var%.ext}` | `$var \\| path parse \\| get stem` |\n\nNu 0.114 also adds stream-specific `ignore` flags. Use them when they make a\nBash redirection clearer: `ignore --stderr` consumes stderr, `ignore --stdout`\nconsumes stdout, and `ignore --show-errors` keeps errors visible while updating\n`$env.LAST_EXIT_CODE`.\n\n## Conditionals\n\nUse `if` for single boolean predicates and short threshold checks. When several\nbranches dispatch on the same value, prefer `match` over an `if`/`else if`\nchain.\n\n```bash\n# Bash\nif [ \"$x\" -gt 10 ]; then\n echo \"big\"\nelif [ \"$x\" -gt 5 ]; then\n echo \"medium\"\nelse\n echo \"small\"\nfi\n```\n\n```nu\n# Nushell\nif $x > 10 {\n 'big'\n} else if $x > 5 {\n 'medium'\n} else {\n 'small'\n}\n```\n\n```bash\n# Bash case dispatch\ncase \"$status\" in\n ok) handle_ok ;;\n error) handle_error ;;\n pending) handle_pending ;;\n *) handle_unknown ;;\nesac\n```\n\n```nu\n# Nushell — prefer match for enum-like or multi-branch dispatch\nmatch $status {\n ok => { handle-ok }\n error => { handle-error }\n pending => { handle-pending }\n _ => { handle-unknown }\n}\n```\n\n## Loops\n\n```bash\n# Bash — iterate files\nfor f in *.txt; do\n echo \"$f\"\ndone\n```\n\n```nu\n# Nushell — functional\nls *.txt | get name | each {|f| print $f }\n\n# Or simply\nls *.txt | get name\n```\n\n```bash\n# Bash — C-style loop\nfor ((i=0; i\u003c10; i++)); do\n echo $i\ndone\n```\n\n```nu\n# Nushell\n0..9 | each {|i| print $i }\n# Or\nfor i in 0..9 { print $i }\n```\n\n```bash\n# Bash — while loop\nwhile read -r line; do\n echo \"$line\"\ndone \u003c file.txt\n```\n\n```nu\n# Nushell\nopen file.txt | lines | each {|line| $line }\n```\n\n## File Operations\n\n| Bash | Nushell |\n| --------------------- | ------------------------------------------------------------------------------- |\n| `cat file` | `open --raw file` |\n| `wc -l file` | `open --raw file \\| lines \\| length` |\n| `touch file` | `touch file` |\n| `mkdir -p dir` | `mkdir dir` |\n| `rm -rf dir` | `rm -r -f dir` |\n| `cp src dst` | `cp src dst` |\n| `mv src dst` | `mv src dst` |\n| `find . -name \"*.rs\"` | `glob **/*.rs` |\n| `test -f file` | `try { ('file' \\| path expand --strict \\| path type) == file } catch { false }` |\n| `test -d dir` | `try { ('dir' \\| path expand --strict \\| path type) == dir } catch { false }` |\n| `basename path` | `'path' \\| path basename` |\n| `dirname path` | `'path' \\| path dirname` |\n\nThese are common-intent mappings, not byte-for-byte POSIX equivalences. In\nparticular, `glob` differs from `find` in hidden-file, symlink, depth, and\nmatching behavior; set its flags explicitly when those details matter.\n\n## Command Substitution\n\n```bash\n# Bash\nFILES=$(ls *.txt)\nCOUNT=$(wc -l \u003c file.txt)\n```\n\n```nu\n# Nushell — no special syntax needed\nlet files = (ls *.txt)\nlet count = (open file.txt | lines | length)\n```\n\n## Functions → Custom Commands\n\n```bash\n# Bash\ngreet() {\n local name=\"$1\"\n local greeting=\"${2:-Hello}\"\n echo \"${greeting}, ${name}!\"\n}\n```\n\n```nu\n# Nushell\ndef greet [\n name: string\n --greeting (-g): string = 'Hello'\n]: nothing -> string {\n

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

($greeting), ($name)!'\n}\n```\n\n## Arrays → Lists\n\n```bash\n# Bash\narr=(one two three)\necho ${arr[0]}\necho ${#arr[@]}\narr+=(\"four\")\n```\n\n```nu\n# Nushell\nlet arr = [one two three]\n$arr | get 0 # or $arr.0\n$arr | length\n$arr | append four # Returns new list (immutable)\n```\n\n## Associative Arrays → Records\n\n```bash\n# Bash\ndeclare -A config\nconfig[host]=\"localhost\"\nconfig[port]=\"8080\"\necho \"${config[host]}\"\n```\n\n```nu\n# Nushell\nlet config = {host: localhost, port: 8080}\n$config.host\n```\n\n## Process Management\n\n| Bash | Nushell |\n| ------------------------ | ------------------------------------- |\n| `command &` | `job spawn { command }` |\n| `jobs` | `job list` |\n| `kill $PID` | `kill $pid` or `job kill $id` |\n| `ps aux` | `ps` |\n| `command1 && command2` | `command1; command2` |\n| `command1 \\|\\| command2` | `try { command1 } catch { command2 }` |\n\n## JSON Processing (jq → Nushell)\n\n```bash\n# Bash + jq\ncurl -s api | jq '.users[] | {name, email}'\ncurl -s api | jq '.items | length'\ncurl -s api | jq '.data | sort_by(.date)'\n```\n\n```nu\n# Nushell — automatically parsed when Content-Type names a supported format\nhttp get api | get users | select name email\nhttp get api | get items | length\nhttp get api | get data | sort-by date\n```\n\n## Error Handling\n\n```bash\n# Bash\nset -e # Exit on error\ntrap cleanup EXIT\n\nif ! command; then\n echo \"Failed\" >&2\n exit 1\nfi\n```\n\n```nu\n# Nushell\ntry {\n some-command\n} catch {|err|\n print -e

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Failed: ($err.msg)'\n exit 1\n}\n```\n\n## Common Patterns\n\n### Check if command exists\n\n```bash\n# Bash\nif command -v git &> /dev/null; then echo \"found\"; fi\n```\n\n```nu\n# Nushell\nif (which git | is-not-empty) { print 'found' }\n```\n\n### Read environment with default\n\n```bash\n# Bash\nPORT=\"${PORT:-8080}\"\n```\n\n```nu\n# Nushell\nlet port = ($env.PORT? | default 8080)\n```\n\n### Multiline strings\n\n```bash\n# Bash heredoc\ncat \u003c\u003c 'EOF'\nline 1\nline 2\nEOF\n```\n\n```nu\n# Nushell raw string\nr#'line 1\nline 2'#\n```\n\n### Semantic versions\n\n```bash\n# Bash — fragile lexical/manual version handling\nprintf '%s\\n' \"$@\" | sort -V\nIFS=. read -r major minor patch \u003c\u003c\u003c \"$version\"\n```\n\n```nu\n# Nushell 0.114+ — SemVer values sort and bump correctly\n$versions | each { into semver } | sort\n$version | into semver | semver bump patch\n$version | into semver | $in in ('>=1.0.0' | into semver-range)\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":9132,"content_sha256":"5fb72d34d32108442be7eb876a76e88f568e53952fcb9038103190117c676125"},{"filename":"references/daemon-and-e2e-smoke-tests.md","content":"# Daemon and E2E Smoke Tests\n\nUse this reference for tests that start a server, watcher, worker, database,\nlanguage service, or any other long-running process. A useful smoke test proves\nboth the happy path and the lifecycle contract: isolated startup, bounded\nreadiness, a real probe, and complete cleanup.\n\n## Lifecycle checklist\n\n1. Create a unique root with `mktemp --directory`.\n2. Put state, logs, sockets, caches, and temporary files under that root.\n3. Scope environment changes with `with-env`; do not alter the developer's\n normal config or state directories.\n4. Start the daemon and immediately retain its Nushell job ID. Observe child\n PIDs from `job list` when the job launches external processes.\n5. Poll an explicit readiness condition until a deadline.\n6. Run the smallest real client request and inspect its structured result.\n7. Stop the job/process in `finally`, even when startup or assertions fail.\n8. Verify that the job, child processes, and owned state are gone.\n\nThe job ID returned by `job spawn` is a Nushell job-table ID, not an operating\nsystem PID. `job list` currently exposes `id`, `type`, `pids`, and\n`description`; `pids` can be empty briefly before an external child starts.\nTrack the job ID first, then retain any observed PIDs needed for post-cleanup\nchecks.\n\nCurrent Nushell provides `job spawn`, `job list`, `job kill`, `job describe`,\n`job id`, `job send`, `job recv`, `job flush`, and `job unfreeze`. There is no\n`job wait` command; do not invent or depend on one. Use readiness/deadline\npolling and explicit cleanup instead. When the spawned fixture is itself\nNushell code, its mailbox is a marker-free readiness channel: the job runs\n`'ready' | job send 0` (the main thread is job `0`) and the test blocks on\n`job recv --timeout 5sec`.\n\n## Deadline-based readiness\n\nA fixed `sleep 2sec` is both slow on fast machines and flaky on slow ones. Poll\nthe condition that makes the daemon usable: a successful health request, an\nopen socket, a ready marker, or a parseable status record.\n\n```nu\ndef wait-until [\n predicate: closure\n --timeout: duration = 5sec\n --interval: duration = 50ms\n] {\n let deadline = ((date now) + $timeout)\n\n loop {\n if (do $predicate) {\n return\n }\n\n if (date now) >= $deadline {\n error make {msg:

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Readiness deadline exceeded after ($timeout)'}\n }\n\n sleep $interval\n }\n}\n```\n\nKeep the interval short but non-zero. The timeout is the maximum wait, not an\nexpected startup duration. If the tracked job disappears before readiness,\nfail immediately with its logs rather than waiting until the deadline.\n\n## Isolated background fixture\n\nThis small fixture uses a marker for readiness and a nested Nushell process as\nthe controlled long-running child. A real test should replace the marker-only\nprobe with the product's health command or client request.\n\n```nu\nuse std/assert\n\nlet fixture_root = (mktemp --directory)\nlet state_dir = ($fixture_root | path join 'state')\nlet temp_dir = ($fixture_root | path join 'tmp')\nlet ready_file = ($state_dir | path join 'ready.json')\nmkdir $temp_dir\n\ntry {\n let job_id = (with-env {\n APP_STATE_DIR: $state_dir\n TMPDIR: $temp_dir\n } {\n job spawn --description 'daemon smoke fixture' {\n mkdir $env.APP_STATE_DIR\n {status: 'ready', state_dir: $env.APP_STATE_DIR}\n | to json\n | save --force ($env.APP_STATE_DIR | path join 'ready.json')\n\n ^nu --no-config-file -c 'loop { sleep 1sec }'\n }\n })\n\n try {\n wait-until {\n if (job list | where id == $job_id | is-empty) {\n error make {msg: 'Background job stopped before readiness'}\n }\n\n $ready_file | path exists\n } --timeout 3sec\n\n let ready = (open $ready_file)\n assert equal $ready.status 'ready'\n\n # Replace this marker assertion with one real client/health probe.\n assert equal $ready.state_dir $state_dir\n } finally {\n try { job kill $job_id }\n }\n\n wait-until {\n $job_id not-in (job list | get id)\n } --timeout 2sec\n} finally {\n rm -r -f $fixture_root\n}\n```\n\nThe nested `try/finally` matters. The inner block owns the background job; the\nouter block owns the temporary directory. This preserves cleanup if a probe or\nassertion raises an error. `job kill` errors when the job has already exited,\nso the `try` wrapper keeps cleanup idempotent without a check-then-kill race\nthat could mask the original failure; the later poll on `job list` still\nproves the job terminated.\n\nA runnable variant of this fixture is maintained at\n`tests/validation-and-daemon-smoke.nu` in this skill's repository, with\nrepo-specific environment names. When editing either copy, mirror structural\nchanges in the other.\n\n## External probes and expected failures\n\nEvery status-sensitive external invocation should use `complete` and inspect\n`exit_code`, `stdout`, and `stderr` separately:\n\n```nu\nlet probe = (^curl --silent --show-error --fail $health_url | complete)\nif $probe.exit_code != 0 {\n error make {msg:

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Health probe failed: ($probe.stderr | str trim)'}\n}\n\nlet status = ($probe.stdout | from json)\n```\n\nDuring readiness polling, connection-refused stderr is often expected. Use the\nexit code to return `false` and retry; retain only the last probe or a bounded\nlog excerpt for the timeout error. After readiness, the same non-zero result is\na real smoke-test failure. Do not merge stdout and stderr before `complete` if\nthe test needs to distinguish them.\n\nPass every external argument as a separate value. Never interpolate a daemon\nor probe command into a string for `nu -c`, `source`, `run`, or a shell.\n\n## Portability and cleanup evidence\n\n- Discover optional clients and OS tools with `which` before using them. Skip\n with an explicit reason or select a supported alternative; do not silently\n weaken the probe. `which name` returns only the highest-precedence match, so\n a Nushell built-in shadows an external tool of the same name — `which ps`\n reports the built-in, and a `which ps | where type == 'external'` guard is\n always empty. Use `which --all name | where type == 'external'` when only\n the external tool will do.\n- Prefer a product-native status command over `ps`, port inspection, or log\n text. OS-level checks are useful additional cleanup evidence, not the primary\n readiness protocol.\n- After `job kill`, poll until the job ID disappears. Verify observed child\n PIDs are gone with the built-in `ps` — `ps | where pid == $pid | is-empty` —\n which works on every platform Nushell supports, with no external `ps` probe\n or `which` gate.\n- Nu 0.116's `job kill` uses external `kill` on Unix or `taskkill` on Windows\n (`nu-system/src/util.rs`). If a test strips PATH, retain that executable or\n reject the fixture before spawning. An empty PATH can make cancellation fail\n while the job entry is removed; polling only the job table will miss the\n surviving child. This does not add an external `ps` dependency.\n- Assert that sockets, PID files, or state files owned by the fixture are gone\n before removing the fixture root. Remove only the directory returned by this\n test's own `mktemp --directory` call.\n- Keep logs inside the fixture while the test runs. On failure, print a bounded\n diagnostic excerpt before cleanup or copy it to an explicitly requested\n artifact directory.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7448,"content_sha256":"42c74b7aa1111e1e9453e555d1edce65200bf7feabaf60da099e10af27ec2469"},{"filename":"references/data-and-types.md","content":"# Nushell Data & Type System Reference\n\n## Contents\n\n- Type hierarchy, annotations, scalars, and collections\n- Conversions and Nu 0.114 type checking\n- Guards, coercion, records, tables, and lists\n- Null safety, discriminated unions, and Polars boundaries\n\n## Type Hierarchy\n\n```\nany\n├── nothing (null/void)\n├── bool\n├── int\n├── float\n├── number (int | float)\n├── string\n├── datetime\n├── duration\n├── filesize\n├── binary\n├── range\n├── glob\n├── list\u003cT>\n├── record\u003cK: V, ...>\n├── table\u003cT> (list\u003crecord\u003cT>>)\n├── closure\n├── cell-path\n├── custom (semver, semver-range, plugin values)\n└── error\n```\n\n## Type Annotations\n\n### Function signatures\n\n```nu\n# No types (accepts any)\ndef func [x] { ... }\n\n# Typed parameters\ndef func [x: int, y: string] { ... }\n\n# Pipeline types\ndef func []: string -> int { ... }\n\n# Both pipeline and parameters\ndef func [multiplier: int]: list\u003cint> -> list\u003cint> {\n $in | each {|x| $x * $multiplier }\n}\n\n# Optional parameters with defaults\ndef func [\n x: int\n y: int = 10 # Default value\n --flag # Named flag (boolean switch)\n --option: string # Named param (oneof\u003cstring, nothing> if not passed)\n --foo: string = 'bar' # Named param with default value\n] { ... }\n```\n\nNu 0.114+ models optional parameters and named parameters without defaults as\nunions with `nothing`:\n\n```nu\ndef maybe-add [x?: int] {\n # $x is oneof\u003cint, nothing>\n ($x | default 0) + 1\n}\n\ndef with-default [x?: int = 0] {\n # $x is int\n $x + 1\n}\n```\n\n### Complex types\n\n```nu\ndef func [items: list\u003cstring>] { ... }\ndef func [matrix: list\u003clist\u003cint>>] { ... }\ndef func [config: record\u003chost: string, port: int>] { ... }\ndef func [data: table] { ... }\ndef func [transform: closure] { ... }\ndef func [value: oneof\u003cint, string, nothing>] { ... }\n```\n\n### Type annotations for custom commands\n\n```nu\n# Shapes valid for parameter annotations\nany, binary, bool, cell-path, closure, datetime, duration, filesize,\nfloat, glob, int, list, nothing, number, range, record, string, table\n\n# Special shapes\npath # String with ~ and . expansion\ndirectory # Subset of path, only directories for tab-completion\n```\n\n## Built-in Scalar Types\n\n### Numbers\n\n```nu\n42 # int\n0xFF # hexadecimal\n0o77 # octal\n0b1010 # binary\n10_000 # underscore separator\n\n3.14 # float\n1.5e-3 # scientific notation\ninf # infinity\n-inf # negative infinity\nNaN # not a number\n```\n\n### Dates and durations\n\n```nu\ndate now # Current datetime\n'2024-01-15' | into datetime\n'2024-01-15T10:30:00Z' | into datetime\n\n1sec, 5min, 2hr, 3day, 1wk # Duration literals\n500ms + 2sec # 2sec 500ms\n(date now) + 5day # 5 days from now\n(date now) - 1wk # 1 week ago\n```\n\n### Filesizes\n\n```nu\n1kb, 500mb, 2gb, 1tb # Filesize literals\n1024 | into filesize # 1.0 KiB\n100mb + 50mb # 150mb\n1gb / 4 # 250mb\n```\n\n### Binary data\n\n```nu\n0x[01 FF 3A 00] # Binary literal\n'hello' | into binary # String to binary\n0x[68656c6c6f] | decode utf-8 # Binary to string\nopen --raw file.bin # Read raw binary\n```\n\n### Glob patterns\n\n```nu\nglob *.rs # Current dir\nglob **/*.rs # Recursive\nglob **/*.{rs,toml} # Multiple extensions\nglob **/*.rs --exclude [**/target/**] # With exclusions\nglob **/*.rs --depth 2 # Max depth\nglob '[A-Z]*' --no-file --no-symlink # Only directories\nglob '(?i)readme*' # Case-insensitive (wax syntax)\n```\n\n## Collection Types\n\n### Lists\n\n```nu\n[1 2 3] # list\u003cint>\n['Alice' 'Bob'] # list\u003cstring>\n[[1 2] [3 4]] # list\u003clist\u003cint>>\n[] # Empty list\n```\n\n### Records\n\n```nu\n{name: 'Alice', age: 30} # Create\n{} # Empty record\n{user: {name: 'Alice', contact: {email: '[email protected]'}}} # Nested\n```\n\n### Tables (list of records)\n\n```nu\nlet users = [\n {name: 'Alice', age: 30}\n {name: 'Bob', age: 25}\n]\n```\n\nStructured decoders can infer a homogeneous array of records as a table:\n\n```nu\n'[{\"name\":\"Alice\"},{\"name\":\"Bob\"}]' | from json | describe\n# => table\u003cname: string>\n\n'[{\"name\":\"Alice\"},{\"name\":\"Bob\"}]'\n| from json\n| describe --detailed\n| get type\n# => list\n```\n\nA table remains list-like and satisfies `list` command parameters. Avoid a\nguard that only checks whether plain `describe` starts with `list`; it will\nreject valid tables. Use a typed `list` boundary when the command owns the\nerror, or inspect the structured top-level type with `describe --detailed`.\n\n### Ranges\n\n```nu\n1..5 # Inclusive: [1, 2, 3, 4, 5]\n1..\u003c5 # Exclusive end: [1, 2, 3, 4]\n1..2..10 # With step: [1, 3, 5, 7, 9]\n5..1 # Reverse: [5, 4, 3, 2, 1]\nseq char a e # Character sequence: [a, b, c, d, e]\n```\n\n## Type Conversions\n\n```nu\n42 | into string # '42'\n'42' | into int # 42\n3.7 | into int # 3 (truncates)\n'3.14' | into float # 3.14\n42 | into float # 42.0\n'true' | into bool # true\n1 | into bool # true\n0 | into bool # false\n'hello' | into binary # binary representation\n'2024-01-15' | into datetime # datetime\n1024 | into filesize # 1.0 KiB\n60 | into duration --unit sec # 1min\n'*.rs' | into glob # glob type\n'1.2.3-alpha.1' | into semver # semver custom value\n'>=1.0.0' | into semver-range # semver range custom value\n```\n\n## Nu 0.114 Type Checking\n\nNu 0.114 preserves more type information at parse time. This is useful because\nbad scripts fail before doing work, but old annotations may need cleanup.\n\n```nu\n# $in is typed from the surrounding pipeline, so this is a parse-time mismatch\n2 | $in + 'foo'\n\n# Pipeline let bindings preserve input type\n42 | let x\nscope variables | where name == '$x' | get type.0 # int\n\n# def bodies are checked against declared pipeline signatures\ndef accepts-int []: int -> int { $in }\ndef bad []: string -> int { accepts-int } # string input rejected\n```\n\nConditionals now produce union output types. Without an `else` or `_` fallback,\nthe output includes `nothing`:\n\n```nu\nif $ok { 42 } # oneof\u003cint, nothing>\nif $ok { 42 } else { 0 } # int\n\nmatch $state {\n ready => 1\n failed => 'error'\n _ => 0\n} # oneof\u003cint, string>\n```\n\n`enforce-runtime-annotations` is enabled by default in Nu 0.114, so runtime\nassignment annotations are checked too:\n\n```nu\nlet value: int = (if $ok { 42 } else { null }) # errors when branch returns null\n\n# Mutable declarations use `mut`, not Rust-style `let mut`.\nmut errors: list\u003cstring> = []\n$errors = ($errors | append 'invalid input')\n```\n\n## Type Checking and Guards\n\n```nu\n42 | describe # 'int'\n[1 2 3] | describe # 'list\u003cint>'\n{a: 1} | describe # 'record\u003ca: int>'\n\n# Type guard pattern\ndef safe-process [value: any] {\n match ($value | describe) {\n 'int' => ($value * 2)\n 'string' => ($value | str uppercase)\n _ => null\n }\n}\n\n# Type predicates. `describe --detailed` reports both native lists and tables\n# with the top-level type `list`; plain `describe` renders tables as `table\u003c...>`.\ndef is-list-like []: any -> bool {\n ($in | describe --detailed | get type) == 'list'\n}\n```\n\n## Type Coercion Rules\n\nNushell does **NOT** auto-coerce types except in string interpolation:\n\n```nu\n'5' + 3 # TYPE ERROR\n

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Value: (42)' # 'Value: 42' (interpolation auto-converts)\n42 == '42' # false (different types, no coercion)\n42 == ('42' | into int) # true (explicit conversion)\n```\n\n## Record Operations\n\n```nu\n$rec1 | merge $rec2 # Merge (right-biased)\n[$r1 $r2 $r3] | into record # Merge many records\n$rec | update name {|r|

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Dr. ($r.name)' } # Update field\n$rec | insert active true # Insert new field\n$rec | insert z {|r| $r.x + $r.y } # Computed field\n$rec | upsert count {|r| ($r.count? | default 0) + 1 } # Update or insert\n$rec | reject password secret_key # Remove fields\n$rec | select name age email # Keep only these fields\n$rec | items {|k, v| ... } # Iterate key-value pairs\n$rec | transpose key val # Convert to table\n```\n\n### Dynamic field access\n\n```nu\nlet field_name = 'age'\n$record | get $field_name # Access by variable\n$record | update $field_name 42 # Update by variable\n\n# Dynamic field selection\nlet fields = if $detailed { [id name email] } else { [id name] }\n$table | select ...$fields\n```\n\n## Table Operations\n\n```nu\n$table | where age > 25 # Filter rows\n$table | insert retired {|row| $row.age > 65 } # Add column\n$table | rename -c {age: years} # Rename column\n$table | group-by status --to-table # Group by\n$table | transpose name data # Transpose\n\n# Joins\n$users | join $orders user_id # Inner join\n$users | join --left $orders user_id # Left join\n$users | join --outer $orders user_id # Outer join\n\n# Group-by aggregations\n$sales | group-by category --to-table | insert stats {|g| {\n count: ($g.items | length)\n total: ($g.items | get price | math sum)\n avg: ($g.items | get price | math avg)\n}}\n```\n\n### Spreadsheet imports\n\n`from xlsx` and `from ods` return a **record of sheet-name -> table**, not a\nsingle table. In Nu 0.114, use the new header controls:\n\n```nu\nopen workbook.xlsx # record of sheet tables\nopen workbook.xlsx | get Sheet1 # one sheet table\n\nopen workbook.xlsx --raw | from xlsx --noheaders\nopen workbook.xlsx --raw | from xlsx --first-row 0\nopen workbook.xlsx --raw | from xlsx --prefer-integers\n```\n\nUse `--noheaders` when the first non-empty row is data, `--first-row 0` when\nleading empty rows must be preserved or a specific row should start import, and\n`--prefer-integers` when whole-number floats should become `int`.\n\n## List Operations\n\n```nu\n$list | enumerate | where {|e| $e.index > 5 } # Filter with index\n$list | reduce --fold 0 {|it, acc| $acc + $it } # Accumulate with initial\n[1 2 3 4] | reduce {|it, acc| $acc - $it } # Without fold: ((1-2)-3)-4\n$list | window 3 # Sliding window\n$list | chunks 100 # Batched processing\n$list | flatten # Flatten nested lists\n$list | skip while {|x| $x \u003c 100 } # Skip while condition true\n$list | take while {|x| $x \u003c 1000 } # Take while condition true\n$list | uniq # Remove duplicates\n$list | union $other # Deduplicated union\n$list | intersect $other # Deduplicated intersection\n$list | difference $other # Values only in left list\n$list | combinations 2 # Lazy k-combinations\n$list | permutations # Lazy permutations\n$list | sort-by field # Sort\n$list | reverse # Reverse order\n```\n\nFor SemVer lists, convert before sorting or comparing:\n\n```nu\n['1.10.0' '1.2.0'] | each { into semver } | sort\n'1.2.3' | into semver | semver bump minor # 1.3.0\n'1.2.3' | into semver | $in in ('>=1.0.0' | into semver-range)\n```\n\n## Null Safety Patterns\n\n```nu\n$record.field? # null if field missing (no error)\n$record.field? | default 'N/A' # Provide fallback\nif ($record.field? != null) { ... } # Check existence\n$list | default -e $fallback # Default for empty collections\n$input | default 'anonymous' # Default for null values\n$input | default (-1) # Parenthesize a negative positional value\n$input | default -- -1 # Or terminate flag parsing explicitly\n```\n\n> **`default`'s argument is eager.** `$x | default \u003cexpr>` evaluates `\u003cexpr>`\n> whether or not `$x` is null — `default` is a normal command, not lazy. So\n> `$x | default $rec.maybe_missing` throws when that column is absent, even when\n> `$x` is non-null. Make the fallback null-safe (`... | default ($rec.maybe? | default 0)`)\n> or branch with `if` when it is expensive.\n\n## Discriminated Unions Pattern\n\n```nu\nlet result = {type: 'success', value: 42}\nlet error = {type: 'error', message: 'Failed'}\n\nmatch $result.type {\n 'success' => $result.value\n 'error' => { print -e $result.message; null }\n}\n```\n\n## Dataframes (Polars)\n\nEverything above describes Nushell's **native**, row-oriented `record` / `table` /\n`list`. For large datasets and heavy columnar analytics (multi-million row\ngroup-by, joins, aggregations, big CSV/Parquet), Nushell also offers **Polars\ndataframes** through the `polars` plugin — a separate, column-oriented data\nstructure (`polars_dataframe` / `polars_lazyframe`), not a native `table`.\n\nRule of thumb: native `table` for small/interactive data and tight integration\nwith the Nushell command set; Polars dataframes for large-scale or columnar work.\nConvert with `polars into-df` / `polars into-lazy` (in) and `polars into-nu`\n(back out). See [Dataframes](dataframes.md) for the full guide.\n\n## Nu 0.116 data-boundary updates\n\nFor 0.116 targets, `default 5 a.b` fills a nested field while `default 5 'a.b'`\ntargets a literal dotted key. `flatten` renames nested fields that collide with\ntop-level columns to `\u003cparent>_\u003cfield>` in either field order. This is not a\ngeneral lossless guarantee: on 0.116.0, an existing column with that generated\nname can still collide, silently overwriting a value depending on field order.\nCheck generated names or explicitly rename conflicting fields before flattening\nwhen all values must be preserved.\n`update cells --recursive` transforms leaf values in nested lists/records, so\nuse type guards for heterogeneous leaves. Record spreads into internal/custom\ncommands forward named flags: null omits a flag unless its type accepts\n`nothing`. See [Nu 0.116 Migration](nu-0.116-migration.md) for examples and tests.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":14627,"content_sha256":"4783cda585f19ae64c7a6ea7152e9b819da92ceb24813f20b19ad53140e7f7ae"},{"filename":"references/dataframes.md","content":"# Nushell Dataframes (Polars) Reference\n\nNushell's native `list`/`record`/`table` are row-oriented and great for small,\ninteractive data. For **large datasets** and **heavy analytics** (multi-million\nrow group-by, joins, aggregations, columnar math, big CSV/Parquet files), use\nthe **Polars dataframes** provided by the `polars` plugin. Dataframes store data\ncolumn-wise (Apache Arrow) and run on the Polars engine, which is dramatically\nfaster and more memory-efficient than native loops for this class of work.\n\n> Command shapes below are maintained for Nu 0.114 / Polars 0.54 behavior.\n> Polars evolves quickly; when in doubt, confirm a command's signature with\n> `scope commands | where name == 'polars \u003ccmd>'` or\n> `help polars \u003ccmd>` rather than trusting older docs. The 0.114 plugin ships\n> more than 180 `polars *` subcommands — `help polars` lists them all.\n\n## Contents\n\n- Choosing Polars, setup, lazy/eager execution, and object storage\n- Loading, inspecting, expressions, selectors, and column operations\n- Aggregation, windows, joins, concatenation, and reshaping\n- Nested data, binning, math, strings, dates, and null handling\n- Sampling, replacement, custom batches, SQL, casting, and saving\n- Migration and performance notes\n\n## When to Use Dataframes\n\n| Use native `table`/`list` when… | Use Polars dataframes when… |\n| ------------------------------------------------ | ------------------------------------------------------ |\n| Data is small (≲ 100k rows) | Data is large (100k–billions of rows) |\n| You need random access or row-by-row logic | You do columnar ops: group-by, join, aggregate, window |\n| You compose with the broader Nushell command set | You read/write big CSV/Parquet/Arrow/JSON files |\n| You stream line-by-line (logs) | You want a query optimizer to plan the whole pipeline |\n\n**Eager vs lazy:**\n\n- **Lazy** (`polars into-lazy`, `polars open`) builds a logical plan that is only\n executed on `polars collect`. The optimizer can prune columns, push down\n filters, and fuse steps. Prefer lazy for large files and multi-step pipelines.\n- **Eager** (`polars into-df`, `polars open --eager`) materializes immediately.\n Prefer eager for small data you inspect or reuse many times.\n\nSome commands only work on one shape (e.g. `polars unique --subset`,\n`polars shift --fill` are documented as **lazy-only**; bare reductions like\n`polars sum` work eager). When a command errors with a shape complaint, switch\nthe frame with `polars into-lazy` / `polars collect`.\n\n## Plugin Setup\n\nThe `polars` plugin ships as `nu_plugin_polars` (install via `cargo install\nnu_plugin_polars` or your package manager, matching your Nushell version).\n\n```nu\nplugin add ~/.cargo/bin/nu_plugin_polars # Register once (path to the binary)\nplugin use polars # Load into the current scope/config\nhelp polars # Verify: lists all polars subcommands\nplugin stop polars # Reset the plugin + clear its object store\n```\n\n`plugin use polars` must run at parse time (top of a script or in your config),\njust like `use` — it cannot be created from a runtime `let`.\n\n## Eager, Lazy, `collect`, and `cache`\n\n```nu\n[[a b]; [1 2] [3 4]] | polars into-df | describe # => polars_dataframe (eager)\n[[a b]; [1 2] [3 4]] | polars into-lazy | describe # => polars_lazyframe\npolars open data.csv | describe # => polars_lazyframe (LAZY by default!)\npolars open --eager data.csv | describe # => polars_dataframe\n\n# A lazy frame is just a plan until you collect it\npolars open data.csv\n| polars filter ((polars col status) == 'active')\n| polars select name status\n| polars collect # executes the optimized plan\n\n# polars cache adds a cache node to a new lazy frame. The plan is still lazy;\n# when executed, a shared sub-plan can be computed once instead of per branch.\npolars open data.csv | polars filter ((polars col ok) == true) | polars cache\n```\n\n> **Gotcha:** `polars open` is **lazy by default** (since 0.97). Pass `--eager`\n> only when you truly need an in-memory dataframe up front. Note also that\n> `describe` returns `polars_dataframe` / `polars_lazyframe` — not the bare\n> `DataFrame` string used in older docs.\n\n## The Object Store\n\nDataframes live in the plugin's object store, not as plain Nushell values. A\nNushell variable holds a _reference_; the store **persists across commands** for\nthe plugin's lifetime.\n\n```nu\npolars store-ls | select key type columns rows estimated_size # List stored objects\npolars store-ls | get key | first | polars store-get $in # Re-fetch an object by key\npolars store-ls | get key | first | polars store-rm $in # Drop one object by key\nplugin stop polars # Drop everything (full reset)\n```\n\n`polars store-get \u003ckey>` returns any stored object (dataframe, lazyframe,\nexpression, group-by, schema, selector…) — useful for reconnecting to a frame\nby key, e.g. across `do`/closure boundaries.\n\n> **Gotcha:** A lazy frame from `polars open` references the _source file_. If\n> you delete or move that file before `collect` (or before `polars store-ls`,\n> which inspects every stored frame), execution fails with\n> `Error collecting lazy frame: No such file or directory`. Collect to an eager\n> frame first if the source may disappear.\n\n## Loading, Creating, and Bridging Back\n\n```nu\n# Create from native Nushell data\n[[a b]; [1 2] [3 4] [5 6]] | polars into-df # table -> eager dataframe\n[1 2 3 4] | polars into-df # list -> single-column dataframe\n[[a b]; [1 a] [2 b]] | polars into-lazy # table -> lazy frame\n\n# Pin a schema explicitly with -s (dtypes, including nested struct/list types)\n[[id person]; [1 {name: Bob, age: 36}]]\n| polars into-df -s {id: i64, person: {name: str, age: u8}}\n\n# Open files: csv, tsv, parquet, json, jsonl/ndjson, arrow, avro\npolars open sales.parquet # lazy\npolars open --eager sales.csv\npolars open data.csv --infer-schema 1000 --skip-rows 2 --delimiter ';' --no-header\npolars open data.csv --schema {a: i64, b: str} # force column dtypes\n\n# Bridge back to native Nushell for further piping or display\n$df | polars into-nu # dataframe/expression -> native value\n```\n\n`polars open` flags include `--eager`, `--type`, `--delimiter`, `--no-header`,\n`--infer-schema \u003cn>`, `--skip-rows \u003cn>`, `--columns`, `--schema`,\n`--truncate-ragged-lines`, plus Hive-partition flags (`--hive-enabled`,\n`--hive-schema`, …). Use `polars into-nu` whenever you want to hand results back\nto ordinary Nushell commands (`to json`, `save`, `table`, etc.).\n\n## Inspecting\n\n```nu\n$df | polars schema # column -> dtype map, e.g. {a: i64, b: str}\n$df | polars shape # => {rows: 3, columns: 2}\n$df | polars columns # list of column names\n$df | polars first 5 # first n rows (also: polars last)\n$df | polars summary # descriptive stats (count/mean/std/min/quantiles/max) for numeric cols\n$df | polars into-repr # the native Polars text table (with shape + dtypes), as a string\n```\n\n`polars into-repr` is handy in messages/logs — it renders the familiar Polars\nbox table (including the `shape: (rows, cols)` header and per-column dtypes).\n\n## Expressions\n\nExpressions describe column operations. Build them with `polars col` and combine\nwith arithmetic, comparisons, and `polars as` (alias). They are consumed by\n`select`, `with-column`, `filter`, `agg`, etc.\n\n```nu\npolars col price # reference a column\npolars col '*' # all columns\n(polars col price) * 1.1 # arithmetic on a column\npolars lit 100 # a literal value\n(polars col qty | polars sum | polars as total) # aggregate + alias\n\n# Conditional (if/else) expression: when ... otherwise ...\n$df | polars with-column (\n polars when ((polars col score) >= 60) 'pass'\n | polars otherwise 'fail'\n | polars as grade\n)\n```\n\n`polars when` can be chained for multi-branch logic\n(`when A a | when B b | otherwise c`). Negate a boolean expression with\n`polars expr-not`; invert a boolean mask/series with `polars not`.\n\n## Column Selectors\n\nSelectors pick columns by property instead of by exact name — handy for wide\nframes. They produce a `polars_selector` usable anywhere a column set is\nexpected (`select`, `with-column`, `is-in`, etc.). In Nu 0.114+, commands that\naccept Polars expressions can accept selectors as input too.\n\n```nu\n$df | polars select (polars selector numeric) # all int/float columns\n$df | polars select (polars selector float) # all float columns\n$df | polars select (polars selector by-name a c) # explicit names\n$df | polars select (polars selector starts-with user) # user_id, user_name, ...\n$df | polars select (polars selector ends-with _id) # account_id, user_id, ...\n$df | polars select (polars selector contains name) # columns containing substring\n$df | polars select (polars selector matches '_name

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

) # regex on column names\n$df | polars select (polars selector by-index 0 -1) # first and last columns\n$df | polars select (polars selector alpha) # names made of letters\n$df | polars select (polars selector alphanumeric) # names made of letters/digits\n$df | polars select (polars selector by-dtype str) # by dtype\n$df | polars select (polars selector numeric | polars selector exclude id) # compose/exclude\n```\n\nThe full selector family: `all`, `alpha`, `alphanumeric`, `array`, `binary`,\n`boolean`, `by-dtype`, `by-index`, `by-name`, `categorical`, `contains`, `date`,\n`datetime`, `decimal`, `digit`, `duration`, `empty`, `ends-with`, `enum`,\n`exclude`, `first`, `float`, `integer`, `last`, `list`, `matches`, `nested`,\n`not`, `numeric`, `object`, `signed-integer`, `starts-with`, `string`, `struct`,\n`temporal`, `unsigned-integer`.\n\n## Select, Filter, With-Column, Rename, Drop\n\n```nu\n# select: keep/compute columns (string names or expressions)\n$df | polars select a b\n$df | polars select (polars col a) ((polars col b) * 2 | polars as b2)\n$df | polars get a b # like select but returns those columns directly\n\n# filter: keep rows matching an expression\n$df | polars filter ((polars col age) > 25)\n$df | polars filter ((polars col status) == 'active')\n$df | polars filter-with (polars col flag) # filter by a boolean mask/expression\n\n# with-column: add/replace a derived column\n$df | polars with-column ((polars col a) + (polars col b) | polars as ab)\n$df | polars with-column {ab: ((polars col a) + (polars col b))} # record form: name -> expr\n\n# rename / drop columns\n$df | polars rename a alpha # rename one column (also takes lists)\n$df | polars drop b c # drop columns by name\n```\n\n> Columns are immutable and shared between frames for efficiency — you cannot\n> mutate a value in place. Derive a **new** column with `with-column` instead.\n\n## Group-by and Aggregation\n\nThe core analytics pattern: `group-by` → `agg [...]` → `collect`. Use a lazy\nframe so the optimizer only touches the columns you aggregate.\n\n```nu\n[[name value]; [one 1] [two 2] [one 1] [two 3]]\n| polars into-lazy\n| polars group-by name\n| polars agg [\n (polars col value | polars sum | polars as total)\n (polars col value | polars mean | polars as mean)\n (polars col value | polars len | polars as n)\n (polars col value | polars quantile 0.5 | polars as median)\n]\n| polars sort-by name\n| polars collect\n# => name=one total=2 mean=1.0 n=2 median=1.0 ; name=two total=5 mean=2.5 n=2 median=2.5\n```\n\nAggregation expressions usable inside `agg` (and as standalone reductions):\n`sum`, `mean`, `min`, `max`, `median`, `quantile`, `std`, `var`, `len` (row\ncount), `count` (non-null count), `n-unique`, `implode` (collect into a list),\n`first`, `last`, `entropy`. On an **eager** frame, a bare `polars sum` (and\nfriends `mean`/`min`/`max`/`median`/`std`/`var`/`quantile`) reduces every numeric\ncolumn at once:\n\n```nu\n$df | polars sum # one-row frame: sum of each numeric column (text -> null)\n$col | polars value-counts # frequency table: distinct values + count\n```\n\n`polars value-counts` also accepts `--sort` (order by count) and `--normalize`\n(return proportions instead of raw counts).\n\n## Window and Sequence Functions\n\n`polars over` computes an aggregation **per partition** and broadcasts the result\nback to every row — no collapsing, unlike group-by.\n\n```nu\n[[g v]; [a 1] [a 2] [b 3]]\n| polars into-df\n| polars with-column ((polars col v | polars sum | polars over g) | polars as g_sum)\n# => g=a v=1 g_sum=3 ; g=a v=2 g_sum=3 ; g=b v=3 g_sum=3\n```\n\n`polars shift` lags/leads a column by a period; `polars cumulative` runs a\nrunning min/max/sum; `polars rolling` runs a fixed-window reduction.\n\n```nu\n# shift (lag by 2; --fill replaces the leading nulls, lazy frame)\n[[a]; [1] [2] [2] [3] [3]]\n| polars into-lazy\n| polars with-column {b: (polars col a | polars shift 2 --fill 0)}\n| polars collect\n# => b column = 0 0 1 2 2\n\n# cumulative sum (also: min / max ; --reverse to run end-to-start)\n[[a]; [1] [2] [3]]\n| polars into-df\n| polars select (polars col a | polars cumulative sum | polars as cum)\n| polars collect\n# => cum = 1 3 6\n\n# rolling window over a series (type = sum / min / max / mean)\n[1 2 3 4 5] | polars into-df | polars rolling sum 2 | polars drop-nulls\n# => 0_rolling_sum = 3 5 7 9\n```\n\n## Joins\n\n```nu\n$left | polars join $right id id # inner join (default), key on both sides\n$left | polars join --left $right id id # left join (unmatched right -> null)\n$left | polars join --full $right id id # full outer join\n$left | polars join --cross $right # cross join\n$left | polars join $right [id region] [id region] # multi-column key\n\n# Useful flags\n--coalesce-columns # merge key columns (esp. with --full)\n--suffix '_r' # rename overlapping non-key columns from the right\n--nulls-equal # treat nulls as matching\n\n# Non-equi / conditional join: match on arbitrary predicates, not just equality\n$left | polars join-where $right ((polars col amount) > (polars col threshold))\n```\n\n> Join types are `--inner` (default) / `--left` / `--full` / `--cross`. The old\n> `--outer` flag was renamed to `--full`. `polars join` operates on lazy frames.\n\n## Combining Frames: Concat and Append\n\n```nu\n# concat: stack two or more frames (vertically by default). Clear and explicit —\n# prefer this for \"union all\" style row stacking.\n[[a b]; [1 2]] | polars into-df\n| polars concat ([[a b]; [3 4]] | polars into-df) ([[a b]; [5 6]] | polars into-df)\n| polars collect\n# => 3 rows. Flags: --diagonal (union mismatched columns), --to-supertypes\n# (reconcile dtypes), --rechunk, --no-maintain-order.\n\n# append: note the counter-intuitive flag semantics (verified on 0.114) —\nlet a = ([[a b]; [1 2] [3 4]] | polars into-df)\n$a | polars append $a # DEFAULT: adds the other frame as NEW COLUMNS -> a b a_x b_x\n$a | polars append $a --col # --col: stacks ROWS -> 4 rows of (a b)\n```\n\n> **Gotcha:** `polars append`'s default appends _columns_ (with `_x` suffixes on\n> name clashes); `--col` appends _rows_. This is the opposite of what the flag\n> name suggests. For row stacking, **prefer `polars concat`** — it reads clearly.\n\n## Reshaping: Unpivot and Pivot\n\n```nu\n# unpivot: wide -> long (formerly \"melt\")\n[[id m1 m2]; [a 1 2] [b 3 4]]\n| polars into-df\n| polars unpivot --index [id] --on [m1 m2] --variable-name metric --value-name val\n# => id=a metric=m1 val=1 ; id=a metric=m2 val=2 ; id=b ...\n\n# pivot: long -> wide\n[[g k v]; [a x 1] [a y 2] [b x 3] [b y 4]]\n| polars into-df\n| polars pivot --on [k] --index [g] --values [v]\n# => g=a x=1 y=2 ; g=b x=3 y=4\n```\n\n`polars pivot` also takes `--aggregate (-a)` (first/sum/min/max/mean/median/count/\nlast or a custom expression) when multiple rows collapse into one cell, plus\n`--separator`, `--maintain-order`, `--always-combine-names`, `--stable`, and\n`--streamable`.\n\n## Nested Data: Lists and Structs\n\nPolars columns can hold `list\u003c…>` and `struct\u003c…>` values. These commands move\nbetween nested and flat layouts.\n\n```nu\n# explode / flatten: one list element per row (flatten is an alias for explode)\n[[id hobbies]; [1 [Cycling Knitting]] [2 [Skiing]]]\n| polars into-df\n| polars explode hobbies\n| polars collect\n# => (1, Cycling) (1, Knitting) (2, Skiing)\n\n# implode: the inverse — aggregate a column's values into a single list (in agg/select)\n$df | polars select (polars col v | polars implode)\n# Nu 0.114+: `polars implode` supports --maintain-order\n\n# unnest: split a struct column into one column per field (inserted in place)\n[[id person]; [1 {name: Bob, age: 36}] [2 {name: Betty, age: 63}]]\n| polars into-df -s {id: i64, person: {name: str, age: u8}}\n| polars unnest person # -> columns id, name, age (-s '_' to prefix names)\n\n# struct-json-encode: serialize a struct column to a JSON string column\n$df | polars select id (polars col person | polars struct-json-encode | polars as json)\n\n# membership tests\n$df | polars with-column (polars col a | polars is-in [one two] | polars as a_in) # scalar in set\n# Nu 0.114+: `polars is-in` supports --maintain-order\n$df | polars with-column (polars col tags | polars list-contains (polars lit urgent) | polars as has) # element in list column\n```\n\n## Binning and Encoding\n\n```nu\n# cut: bin a numeric series by explicit break points (n breaks -> n+1 categories)\n[-2 -1 0 1 2] | polars into-df | polars cut [-1 1] --labels [low mid high]\n# => category column: low low mid high high (--left_closed, --include_breaks)\n\n# qcut: bin by quantile probabilities instead of fixed breaks\n[-2 -1 0 1 2] | polars into-df | polars qcut [0.25 0.75] --labels [a b c] --allow_duplicates\n\n# dummies: one-hot encode (--drop-first to avoid the dummy-variable trap)\n[[a b]; [1 2] [3 4]] | polars into-df | polars dummies\n# => columns a_1 a_3 b_2 b_4 (0/1)\n```\n\n## Math Functions\n\n```nu\n# polars math: scalar math over column expressions\n# abs, sign, sqrt, exp, log \u003cbase; default e>, log1p, sin, cos, dot \u003cexpr>\n# bitwise-and, bitwise-or, bitwise-xor, bitwise-count-ones, bitwise-count-zeros,\n# bitwise-leading-ones, bitwise-leading-zeros, bitwise-trailing-ones,\n# bitwise-trailing-zeros\n[[a]; [-1] [4]]\n| polars into-df\n| polars select (polars col a | polars math abs | polars as a_abs)\n| polars collect\n\n# polars horizontal: reduce ACROSS columns per row (all/any/min/max/sum/mean).\n# Nulls are skipped by default; pass --nulls to make any null produce null.\n[[a b]; [1 2] [3 4]]\n| polars into-df\n| polars select (polars horizontal sum a b | polars as row_sum)\n| polars collect\n# => row_sum = 3 7\n```\n\n## String Columns\n\nString ops live under `polars str-*` (regrouped since older versions; old\nsubstring replacement examples that used generic names should now use the\nstring-specific commands):\n\n```nu\n$df | polars select (polars col w | polars str-replace -p '[0-9]+' -r 'N') # regex replace (leftmost)\n$df | polars select (polars col w | polars str-replace-all -p '[0-9]+' -r 'N') # regex replace all\n$df | polars with-column (polars concat-str '-' [(polars col a) (polars col b)] | polars as ab)\n$df | polars select (polars col s | polars str-split ',') # -> list\u003cstr> column\n$df | polars select (polars col s | polars str-strip-chars 'x') # trim chars from both ends\n# also: str-slice, str-lengths, str-join, lowercase, uppercase, contains\n```\n\n## Date, Time, and Time Zones\n\n```nu\n$df\n| polars with-column (polars col d | polars as-datetime '%Y-%m-%d' --naive | polars as ts)\n| polars with-column (polars col ts | polars get-year | polars as year)\n| polars with-column (polars col ts | polars datepart month | polars as month)\n| polars with-column (polars col ts | polars strftime '%B' | polars as month_name)\n```\n\n- **Parsing:** `polars as-date` / `polars as-datetime '\u003cfmt>'`. Pass `--naive`\n for timezone-naive timestamps; `--time-zone`, `--time-unit`, `--ambiguous`\n refine the parse.\n- **Extracting parts:** the `polars get-*` family (`get-year`, `get-month`,\n `get-day`, `get-hour`, `get-minute`, `get-second`, `get-nanosecond`,\n `get-week`, `get-weekday`, `get-ordinal`) — or the unified\n `polars datepart \u003cpart>` (`year`/`quarter`/`month`/`week`/`weekday`/`day`/\n `hour`/`minute`/`second`/`millisecond`/`microsecond`/`nanosecond`).\n- **Formatting:** `polars strftime '\u003cfmt>'`.\n- **Time zones:** `polars convert-time-zone 'America/New_York'` shifts the\n instant to another zone; `polars replace-time-zone 'America/New_York'` relabels\n the wall-clock time without shifting it (`--ambiguous`/`--nonexistent` handle\n DST edge cases; pass `null` to unset the zone).\n- **Bucketing:** `polars truncate 1h` (or `5d`, `1mo`, `1wk`…) floors each\n timestamp to the start of its bucket — the basis for time-series grouping.\n\n## Null Handling\n\n```nu\n$df | polars count-null # per-column null counts\n$df | polars fill-null 0 # replace nulls with a value/expression\n$df | polars fill-nan 0 # replace float NaN (distinct from null)\n$df | polars drop-nulls # drop rows with any null\n$df | polars filter (polars col x | polars is-null) # keep only rows where x is null\n# also: is-not-null\n```\n\n## Sampling, Slicing, and Dedup\n\n```nu\n$df | polars sample --n-rows 100 # random subset (--fraction, --replace, --shuffle, --seed)\n$df | polars slice 10 20 # 20 rows starting at offset 10\n$df | polars take [0 2 4] # rows at the given indices\n$df | polars first 5 # head (also: polars last 5)\n$df | polars reverse # reverse row order\n$df | polars into-lazy | polars unique --subset [a] | polars collect # dedup by column subset (lazy)\n$df | polars drop-duplicates # drop fully-duplicate rows\n```\n\n## Replace and Conditional Values\n\n```nu\n# polars replace: value mapping (old -> new) via two positional args.\n# `old` is a list/record of values to match; `new` is the list of replacements.\n$df | polars with-column (polars col grade | polars replace [A B] [4 3] | polars as gpa)\n# Flags: --strict (every value must match), --default \u003cexpr> (value for unmatched).\n# Nu 0.114 / Polars 0.54: with `--strict` and `--return-dtype`, pass\n# `--default \u003cvalue>` when any input values may be unmapped.\n\n# polars set / set-with-idx / filter-with: mask- or index-based value assignment (eager series)\n```\n\n`polars replace` does **value mapping** (different from `polars str-replace`,\nwhich does regex substring replacement on string columns).\n\n## Custom Logic Escape Hatch: `map-batches`\n\nWhen an operation has no native Polars expression, `polars map-batches` runs a\n**Nushell closure** over one or more columns. The closure receives a list of\nsingle-column dataframes and returns a series-like value (list, scalar, or\nsingle-column dataframe).\n\n```nu\n[[a b]; [1 4] [2 5] [3 6]]\n| polars into-df\n| polars map-batches --name out { |cols| $cols | first | polars get a | each { |v| $v * 2 } } a\n# => out = 2 4 6\n```\n\n> Use sparingly: dropping into a Nushell closure forfeits Polars' vectorized\n> speed. Reach for it only when no native expression (`math`, `str-*`,\n> `when/otherwise`, arithmetic) can express the transform.\n\n## SQL Queries\n\n```nu\n$df | polars query 'select category, sum(amount) as total from df group by category order by total desc'\n```\n\nThe source frame is always referenced as `df` in the `from` clause.\n\n## Type Casting\n\nUse Polars dtype names (`i64`, `f64`, `str`, `bool`, `date`, `datetime`, …), not\nNushell type names:\n\n```nu\n$df | polars cast f64 price # cast one column (column arg required on a dataframe)\n$df | polars cast i64 a | polars cast str b\n$df | polars schema # => {a: i64, b: str}\n\n# Build dtype / schema objects when a command needs them\n'i64' | polars into-dtype # dtype-name string -> dtype object\n{a: i64, b: str} | polars into-schema # record -> schema object\n```\n\nDedicated string→number converters also exist: `polars integer` and\n`polars decimal` parse a string column into integer/decimal columns.\n\n## Saving\n\n`polars save` writes a dataframe to disk by extension. For a **lazy** frame it\nperforms a streaming **sink** when the format supports it (parquet, ipc/arrow,\ncsv, ndjson) — efficient for results too large to fit in memory.\n\n```nu\n$df | polars save out.parquet\npolars open big.csv | polars filter ((polars col ok) == true) | polars save filtered.parquet\n$df | polars save out.csv --csv-delimiter ';' --csv-no-header\n```\n\nFlags include `--type` (force format), `--csv-delimiter`, `--csv-no-header`, and\n`--avro-compression`.\n\n## Migration Notes (older docs → 0.113/0.114)\n\n| Old | Now |\n| ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |\n| `polars melt` | `polars unpivot` (and `polars pivot` for the reverse) |\n| `polars join --outer` | `polars join --full` |\n| old substring replacement examples using `replace` / `replace-all` | `polars str-replace` / `polars str-replace-all` (`polars replace` is value mapping) |\n| `polars concatenate` | `polars str-join` / `polars concat-str` (string concat), or `polars concat` (stack frames) |\n| `polars fetch` | removed (use `polars collect` / `polars first`) |\n| `describe` → `DataFrame` | `describe` → `polars_dataframe` / `polars_lazyframe` |\n| `polars open` (eager) | `polars open` is **lazy**; use `--eager` for eager |\n| `polars count` for row count | `polars len` (`polars count` = non-null count, SQL `COUNT(col)`) |\n\nNew since older docs: `polars over` (window), `polars shift`/`cumulative`/\n`rolling` (sequence ops), `polars selector *` (column-select DSL),\n`polars when`/`otherwise`, `polars join-where` (non-equi join), `polars concat`,\n`polars cut`/`qcut`/`dummies` (binning + one-hot), `polars math`/`horizontal`\n(numeric and bitwise ops), `polars explode`/`implode`/`unnest`/`struct-json-encode` (nested\ndata), `polars convert-time-zone`/`replace-time-zone`/`truncate`/`datepart`\n(time zones + buckets), `polars map-batches` (Nushell-closure escape hatch),\n`polars profile`, `polars cache`, `polars store-get`, `polars into-repr`,\n`polars query` (SQL).\n\n## Performance Notes\n\n- Polars uses a **columnar layout (Apache Arrow)** plus a **query optimizer**, so\n heavy `group-by`/`join`/aggregation on large data can run substantially\n faster than native Nushell pipelines, and often faster than pandas as well.\n (See the official\n [Dataframes chapter](https://www.nushell.sh/book/dataframes.html) for\n benchmarks.)\n- Stay **lazy** end-to-end and `collect` once at the end — that lets the\n optimizer prune unused columns and push filters down to the file reader.\n- Reuse a stored frame for multiple aggregations instead of re-reading the file;\n use `polars cache` to add a cache node when several executed branches share\n the same sub-plan.\n- Prefer native Polars expressions over `polars map-batches`; the closure path\n gives up vectorization.\n- For results that don't fit in memory, `polars save` from a lazy frame to\n Parquet/Arrow streams to disk.\n- Bridge to native (`polars into-nu`) only at the boundaries; keep the heavy work\n inside Polars.\n\n## See Also\n\n- [Data & Type System](data-and-types.md) — native `record`/`table`/`list` ops\n- [Advanced Patterns](advanced-patterns.md) — native lazy/eager streaming, `par-each`\n- [Nushell Dataframes book chapter](https://www.nushell.sh/book/dataframes.html)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":28676,"content_sha256":"1fe4225dc167f86903e1b186ef24190af2ca5967a9fe6b9fbac54f6a8972ffa7"},{"filename":"references/modules-and-scripts.md","content":"# Nushell Modules & Scripts Reference\n\n## Contents\n\n- Module organization, exports, `main`, and submodules\n- Environment setup, inline modules, and imports\n- Scripts, `run`, named flags, shebangs, and attributes\n- Parse-time/runtime behavior and testing\n\n## Module Organization\n\n### File-form (simple modules)\n\n```\nmy-command.nu # Single-file module, module name = filename\n```\n\n### Directory-form (larger modules)\n\n```\nmy-module/\n├── mod.nu # Module entry point (required)\n├── utils.nu # Submodule\n├── config.nu # Submodule\n└── tests/\n ├── mod.nu # Test module entry point\n └── utils_test.nu # Test file\n```\n\nBoth forms behave identically once imported; only the path changes.\n\n## Export Types\n\n| Export | Keyword | Example |\n| ------------ | ------------------ | ----------------------------------- |\n| Commands | `export def` | `export def my-cmd [] { ... }` |\n| Env commands | `export def --env` | `export def --env setup [] { ... }` |\n| Aliases | `export alias` | `export alias ll = ls -l` |\n| Constants | `export const` | `export const version = '1.0.0'` |\n| Externals | `export extern` | `export extern \"git push\" [...]` |\n| Submodules | `export module` | `export module utils.nu` |\n| Re-exports | `export use` | `export use utils.nu *` |\n| Env setup | `export-env` | `export-env { $env.FOO = 'bar' }` |\n\nOnly `export`-ed definitions are public. Non-exported definitions are private (local to the module).\n\n## The `main` Convention\n\nWhen a command name matches the module name, use `export def main`:\n\n```nu\n# increment.nu\nexport def main []: int -> int {\n $in + 1\n}\n\nexport def by [amount: int]: int -> int {\n $in + $amount\n}\n```\n\n```nu\nuse increment\n5 | increment # => 6\n5 | increment by 3 # => 8\n```\n\n## Submodule Patterns\n\n### `export module` — Preserves submodule namespace\n\n```nu\n# mod.nu\nexport module utils.nu # Commands accessed as: my-module utils \u003ccmd>\n```\n\nNu 0.114+ no longer imports exported submodules implicitly when the parent\nmodule is imported. If callers should reach a submodule through the parent\nnamespace, re-export it explicitly from the parent:\n\n```nu\nexport module utils {\n export def clean [] { 'clean' }\n}\n\nexport use utils # Enables: my-module utils clean\n```\n\nWithout the explicit `export use utils`, `use my-module` imports the parent\nexports but not `my-module utils clean`.\n\n### `export use` — Flattens into parent namespace\n\n```nu\n# mod.nu\nexport use utils.nu * # Commands accessed as: my-module \u003ccmd>\n```\n\nUse `export use utils.nu *` only when flattening the commands into the parent is\nthe intended public API. Use `export use utils.nu` when preserving the submodule\nnamespace is clearer. A `use` inside another module does not run the imported\nmodule's `export-env` block, matching the previous implicit behavior.\n\n## Environment Setup\n\n```nu\n# mod.nu\nexport-env {\n $env.MY_MODULE_PATH = ($env.CURRENT_FILE | path dirname)\n $env.MY_MODULE_VERSION = '2.0.0'\n}\n```\n\n## Inline Module Definition\n\n```nu\nmodule my_module {\n export def public-func [] { 'hello' }\n def private-func [] { 'private' }\n export const MY_CONST = 42\n}\n\nuse my_module *\nuse my_module [public-func MY_CONST]\n```\n\n## Import Patterns\n\n```nu\nuse my-module # Import module namespace\nuse my-module * # Import all exports into current scope\nuse my-module [func-a func-b] # Import specific exports\nuse lib/helpers.nu * # Import from file path\n```\n\n## Scripts\n\n### Basic script\n\n```nu\n# myscript.nu\ndef greet [name] {\n

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Hello, ($name)!'\n}\n\ngreet 'World'\n```\n\nDefinitions run first (regardless of position in file), then the script body runs top-to-bottom.\n\n### Pipeline scripts with `run` (Nu 0.114+)\n\n`run` lets a `.nu` script act as a pipeline stage. Prefer it when the script\nshould transform pipeline input and keep its definitions isolated from the\ncaller.\n\n```nu\n# shout.nu\nstr uppercase\n```\n\n```nu\n'Hello Nushell!' | run shout.nu | str camel-case\n```\n\nIf a top-level `def main` exists, `run` invokes it and passes pipeline input\nthrough the declared signature:\n\n```nu\n# length-plus.nu\ndef main []: string -> int {\n str length\n}\n\n'Hello' | run length-plus.nu\n```\n\n`run` is a parser keyword, so the target script file must already exist when the\ncalling block is parsed; do not create the script earlier in the same parsed\nblock and expect `run` to see it. Resolution uses the current directory,\n`NU_LIB_DIRS`, or explicit paths; it does not search `PATH`. Use\n`run --full-reparse script.nu` when the script file can change between\ninvocations, such as file-watch loops or tests that regenerate the script.\n\n### Parameterized scripts with main\n\n```nu\n#!/usr/bin/env nu\n\n# Build the project\ndef \"main build\" [\n --release (-r) # Build in release mode\n] {\n print 'Building...'\n}\n\n# Run tests\ndef \"main test\" [\n --verbose (-v) # Show test details\n] {\n print 'Testing...'\n}\n\ndef main [] {\n print 'Usage: script.nu \u003cbuild|test>'\n}\n```\n\n```nu\nnu myscript.nu # => Usage: script.nu \u003cbuild|test>\nnu myscript.nu build # => Building...\nnu myscript.nu test # => Testing...\n```\n\n**Important:** You must define a `main` command for subcommands to be accessible. An empty `def main [] {}` suffices.\n\n### Multiline calls with named flags\n\nWhen calling custom commands with named flags, keep short invocations on one\nline. If a call must span lines, wrap the whole invocation in parentheses. A\nbare newline can end the command before the flags are parsed.\n\n```nu\n# Good\ndeploy staging --target api --dry-run\n\n# Good\nlet plan = (\n deploy staging\n --target api\n --dry-run\n)\n\n# Bad\ndeploy staging\n--target api\n--dry-run\n```\n\nA multiline closure argument is the exception: braces keep the parser inside\nthe call, so flags on the closing-brace line — for example\n`wait-until { ... } --timeout 3sec` — parse as part of the same invocation\nwithout extra parentheses.\n\n### Shebang\n\n```nu\n#!/usr/bin/env nu\n'Hello World!'\n```\n\nFor stdin access: `#!/usr/bin/env -S nu --stdin`\n\n## Attribute System (v0.103+)\n\n```nu\n@example 'Greet a user' { greet 'Alice' } --result 'Hello, Alice!'\n@deprecated 'Use new-command instead.'\n@category 'network'\n@search-terms ['http' 'web' 'api']\n```\n\n## Parse-Time vs Runtime\n\n| Feature | Parse-time | Runtime |\n| ------------------ | --------------- | ------------------------------------ |\n| `const` values | Yes | No (already resolved) |\n| `let` values | No | Yes |\n| `source` path | Must be known | Executes script body |\n| `use` path | Must be known | Imports module; may run `export-env` |\n| `run` script paths | File must exist | Runs isolated |\n| Type checking | Yes | Some |\n| `def` names | Must be literal | N/A |\n| Syntax errors | Caught here | N/A |\n\n```nu\n# Works — const is resolved at parse time\nconst path = 'scripts/utils.nu'\nsource $path\n\n# Error — let is runtime only\nlet path = 'scripts/utils.nu'\nsource $path # Error: not a parse-time constant\n```\n\n## Testing\n\n### Parse Checks Before Execution\n\nUse `nu-check --debug script.nu` for ordinary script checks, and add\n`--as-module` only for module content. From a host shell, run a fixed command\nwith `nu --no-config-file -c 'nu-check --debug script.nu'`. Within Nu, pass a\nruntime path directly as `nu-check --debug $script`; never interpolate an\nuntrusted path into `nu -c` source.\n\n| Check | Failure contract | Intended use |\n| ---------------------------- | -------------------------------------------- | ------------------------------------------------ |\n| `nu-check file.nu` | `false` for parse errors; I/O failures throw | In-process boolean checks |\n| `nu-check --debug file.nu` | Parse errors throw and show a diagnostic | Default script validation |\n| `nu --ide-check 100 file.nu` | JSONL errors can accompany exit `0` | Structured spans/diagnostics; parse every record |\n| `source file.nu` | Executes the top-level code | Intentional loading, not validation |\n\nPrefer file input when relative imports matter. `nu-check` with piped content\nparses anonymous input, even if a path argument is supplied; it does not use\nthat path to recover the source file's import directory. Both parse checkers\ncan catch static type errors but do not run the target's ordinary top-level\ncommands, prove expected output, or isolate untrusted code in a sandbox.\nRun behavioral tests separately against owned fixtures.\n\nFor `--ide-check`, verify the target exists, decode JSON Lines, fail on\n`type: diagnostic` / `severity: Error`, and surface other diagnostic severities.\nHints are not failures. Check CLI exit/stderr and malformed JSONL separately;\nsee the main Skill's validation rules and `tests/validation-and-daemon-smoke.nu`.\nNu 0.116 fixes the old missing-file success case: unreadable targets now exit\nnonzero. Keep the path precheck for cross-version runners, and keep inspecting\nJSONL for ordinary diagnostics. See [Nu 0.116 Migration](nu-0.116-migration.md).\n\nBehavior verified with Nu 0.115.1 and the English\n[nu-check](https://www.nushell.sh/commands/docs/nu-check.html) /\n[source](https://www.nushell.sh/commands/docs/source.html) documentation.\n\n### Nupm package tests\n\n```\nmy-package/\n├── nupm.nuon\n├── mod.nu\n└── tests/\n ├── mod.nu # Test entry point\n └── utils_test.nu # Test file\n```\n\nOnly fully exported commands from `tests` module are run by `nupm test`.\n\n### Standalone tests with std assert\n\n```nu\nuse std/assert\n\nfor t in [\n [input expected];\n [0 0]\n [1 1]\n [2 1]\n [3 2]\n] {\n assert equal (fib $t.input) $t.expected\n}\n```\n\n### Available assert commands\n\n```nu\nuse std/assert\n\nassert (condition) # Basic assertion\nassert equal $actual $expected # Equality check\nassert not equal $a $b # Inequality check\nassert str contains $haystack $needle # String containment\nassert length $list $expected_len # List length\nassert error { failing-command } # Expect an error\n```\n\n### Custom assertions\n\n```nu\ndef \"assert positive\" [n: int] {\n assert ($n > 0) --error-label {\n text:

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Expected positive number, got ($n)'\n span: (metadata $n).span\n }\n}\n```\n\n### Stable assertions for nested Nushell diagnostics\n\nRendered diagnostics from `nu script.nu | complete` are presentation text, not\na stable protocol. They may include ANSI sequences and `|` gutters, and can\nhard-wrap according to PTY width, sometimes inside words. Direct substring checks can pass\nin CI but fail in a narrower interactive terminal.\n\nPrefer direct `try/catch` and `$err.details` for in-process tests. When a CLI\nintegration test must inspect rendered `stderr`, normalize both values:\n\n```nu\ndef diagnostic-text [value: any]: nothing -> string {\n $value\n | into string\n | ansi strip\n | str replace --all --regex r#'[\\s|]+'# ''\n}\n\ndef \"assert diagnostic contains\" [value: any, expected: string] {\n assert str contains (diagnostic-text $value) (diagnostic-text $expected)\n}\n\nlet result = (^nu failing-script.nu | complete)\nassert not equal $result.exit_code 0\nassert diagnostic contains $result.stderr 'OpenSSL key generation failed'\n```\n\nUse a long, domain-specific phrase to keep the normalized assertion selective.\nFor a regression involving diagnostics or tables, also run the test under a\nnarrow PTY such as `stty cols 24 && nu tests/example.nu`.\n\n### Basic test framework (without Nupm)\n\nKeep test registration explicit and execute closures directly. Building Nu\nsource from discovered command names adds quoting, path and parser-state\nproblems to the test runner itself. Fail on an empty selection and let failed\nassertions stop the run before printing a success count.\n\n```nu\nuse std/assert\n\ndef increment [n: int]: nothing -> int {\n $n + 1\n}\n\ndef run-tests [cases: table\u003cname: string, run: closure>]: nothing -> nothing {\n if ($cases | is-empty) {\n error make {msg: 'No tests selected'}\n }\n for case in $cases {\n print

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Running test: ($case.name)'\n do $case.run\n }\n print

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Tests passed: ($cases | length)'\n}\n\ndef main [] {\n let cases = [\n {name: positive, run: {|| assert equal (increment 1) 2 }}\n {name: negative, run: {|| assert equal (increment (-1)) 0 }}\n ]\n run-tests $cases\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":12995,"content_sha256":"ccfc3fc4bf94085b7e65899b0e2ea65770d0244373bcbdacfda7cf267caf686f"},{"filename":"references/nu-0.114-migration.md","content":"# Nu 0.114 Migration Reference\n\nUse this reference when upgrading scripts to Nu 0.114+, reviewing code written\nfor an older release, or diagnosing behavior that may depend on the active Nu\nversion.\n\n## Contents\n\n- Establish the active version\n- Type and control-flow changes\n- Scripts, modules, and pipeline execution\n- Commands, values, and data formats\n- Errors, diagnostics, and validation\n\n## Establish the Active Version\n\nFrom any shell, prefer the direct CLI check:\n\n```console\nnu --version\n```\n\nWhen already inside Nushell or when structured version data is needed, use:\n\n```nu\nversion | get version\n```\n\nConfirm the project-supported version before rewriting working compatibility\ncode. For broader command-level changes, also read the command behavior notes\nin [Advanced Patterns](advanced-patterns.md).\n\n## Type and Control-Flow Changes\n\n- Optional positional parameters and typed named options without defaults are\n `oneof\u003cT, nothing>`. Boolean switch flags remain `bool`.\n- `if` without `else` and `match` without `_` may return `nothing`; provide a\n fallback when the surrounding signature requires a non-null value.\n- Runtime assignment annotations are enforced by default.\n- Declare mutable bindings with `mut name[: type] = value`; `let mut` is not\n valid Nushell syntax.\n- A bare negative number in command-argument position can be parsed as a flag.\n Wrap it as an expression (`default (-1)`) or use `default -- -1` when the\n command supports the POSIX-style separator.\n- Homogeneous arrays of records loaded from JSON commonly describe as\n `table\u003c...>`, even though tables remain list-like. Use a typed `list`\n parameter when both lists and tables should be accepted, or inspect the\n structured result of `describe --detailed`.\n\n```nu\ndef maybe-add [value?: int]: nothing -> int {\n ($value | default 0) + 1\n}\n\nmut errors: list\u003cstring> = []\n```\n\nSee [Data & Type System](data-and-types.md) for unions, collection types,\nruntime annotations, and null-safe `default` patterns.\n\n## Scripts, Modules, and Pipeline Execution\n\n- Exported submodules are not imported implicitly. Re-export the intended\n namespace with `export use sub`, or flatten it deliberately with\n `export use sub *`.\n- Use `run` when a script should act as an isolated pipeline stage. The target\n file must exist when the calling block is parsed.\n- Use `run --full-reparse script.nu` when the script can change between\n invocations, such as watch loops or tests that regenerate the file.\n- Keep pipeline input in the command's input/output signature. It is not a\n substitute for a positional parameter, and streams may be single-pass.\n\nSee [Modules & Scripts](modules-and-scripts.md) for module exports, `main`,\nparse-time resolution, pipeline scripts, and test seams.\n\n## Commands, Values, and Data Formats\n\n- Use `into semver`, `into semver-range`, and `semver bump` instead of lexical\n sorting or manual version splitting.\n- Use `str uppercase` and `str lowercase`; `str upcase` and `str downcase` are\n deprecated.\n- `from xlsx` and `from ods` return records of sheet tables. Use\n `--noheaders`, `--first-row`, and `--prefer-integers` instead of removed\n header flags.\n- Nu 0.114 supports POSIX-style `--` for builtins and ordinary custom commands.\n Wrapped commands and known externs preserve or forward it to rest arguments.\n\n```nu\n['1.10.0' '1.2.0'] | each { into semver } | sort\n'1.2.3' | into semver | semver bump minor\nopen workbook.xlsx | get Sheet1\n```\n\n## Errors, Diagnostics, and Validation\n\n- In `catch`, structured diagnostics live at `$err.details`; `$err.json` was\n removed.\n- Prefer structured `try/catch` assertions for in-process tests.\n- Rendered errors from nested `nu ... | complete` calls are presentation text.\n ANSI styling, diagnostic gutters, and PTY wrapping make raw or contiguous\n substring assertions unstable. Normalize both values and match a long,\n domain-specific phrase when testing a CLI boundary.\n\nValidate migrations narrowly before running the full suite:\n\n```console\nnu -c 'source path/to/module.nu'\nnu path/to/test-script.nu\n```\n\nWhen diagnostics or tables are involved, repeat the focused regression under a\nnarrow PTY, for example `stty cols 24 && nu tests/example.nu`.\n\nFor implementation patterns, continue with [Advanced Patterns](advanced-patterns.md)\nand [Modules & Scripts](modules-and-scripts.md). For a migration-focused review,\nuse [Anti-Patterns](anti-patterns.md) and [Script Review](script-review.md).\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4464,"content_sha256":"7021e54ecd30d63546750954b7f014dba057aa9efd8eefd7d348c996bb4e4eda"},{"filename":"references/nu-0.115-migration.md","content":"# Nu 0.115 Migration and Review Reference\n\nUse this reference when upgrading to Nu 0.115, reviewing code whose behavior\nchanged in 0.115, or deciding whether a new 0.115 idiom is compatible with a\nproject's supported Nushell range.\n\nFor newer targets see [Nu 0.116](nu-0.116-migration.md): nested `finally`,\nSemVer comparison inference and missing-file IDE checks are fixed; raw YAML\n`null` is accepted and list indentation uses a different flag. Historical\n0.115.0 reproductions below remain relevant to that version. The separate\nYAML `--non-roundtrip 'lossy'` rejection still reproduces on 0.116.0.\n\n## Contents\n\n- [Establish the compatibility target](#establish-the-compatibility-target)\n- [Breaking changes and migration blockers](#breaking-changes-and-migration-blockers)\n- [High-frequency command improvements](#high-frequency-command-improvements)\n- [Correctness fixes that affect reviews](#correctness-fixes-that-affect-reviews)\n - [Nested `finally` cleanup is still unreliable](#nested-finally-cleanup-is-still-unreliable)\n- [Secondary additions and performance](#secondary-additions-and-performance)\n- [Migration review checklist](#migration-review-checklist)\n- [Focused validation](#focused-validation)\n- [Related references](#related-references)\n\n## Establish the Compatibility Target\n\nCheck the project's documented minimum/maximum version and CI matrix before\nusing the local binary as evidence:\n\n```console\nnu --version\n```\n\nFrom inside Nushell:\n\n```nu\nversion | get version\n```\n\nDirect comparison operators on `semver` values were added in 0.115. If a\nscript must still run on 0.114, use a `semver-range` membership test rather than\nputting a 0.115-only comparison in the compatibility gate.\n\n## Breaking Changes and Migration Blockers\n\n### YAML now defaults to 1.2 and enforces explicit contracts\n\n`from yaml` now defaults to YAML 1.2. Values such as `yes` and `off` remain\nstrings, leading-zero decimal values are decimal, octal uses the `0o` prefix,\nand YAML 1.1 sexagesimal (base-60) scalars stay strings. Use `--spec 1.1` only\nfor an input contract that intentionally relies on YAML 1.1 coercion:\n\n```nu\n'yes' | from yaml # string\n'yes' | from yaml --spec 1.1 # bool\n\n'0247' | from yaml # decimal 247\n'0247' | from yaml --spec 1.1 # octal 0o247\n\n'190:20:30' | from yaml # string '190:20:30'\n'190:20:30' | from yaml --spec 1.1 # 685230 (base-60)\n```\n\nThe sexagesimal case is the easiest one to miss during review: colon-bearing\nscalars such as timestamps, ID fields, and `HH:MM:SS` durations changed type in\nboth directions with no error on either side.\n\nThe parser is also stricter at the boundaries where YAML does not map cleanly\nto Nushell values:\n\n- Plain mapping keys that resolve to bool, number, or null error because Nu\n record keys are strings; this is `--key-resolution strict`, the default. Use\n `--key-resolution verbatim` only when retaining the original key text is the\n intended contract.\n- Unknown tags error. Use `--ignore-tags` only when discarding tag semantics is\n safe; it is not a validation mechanism.\n- `--multiple auto` returns one document directly but a list for multiple\n documents. Use `--multiple list` for a stable list-shaped API or\n `--multiple single` to reject streams.\n- Anchors, aliases, and merge keys now resolve correctly. Re-test code that\n previously worked around missing merge behavior.\n\n`to yaml` now errors on values such as closures that cannot round-trip. Keep\nthat safe default and opt out only when the caller explicitly accepts data loss\nor Nushell-specific tags. Generated quoting, indentation, and tags also changed,\nso semantic round-trip assertions are more stable than byte-for-byte golden\nfiles.\n\nThe opt-out value must be quoted. A bare `null` parses as the `nothing`\nliteral, not as the string the flag expects:\n\n```nu\n{a: {|| $in}} | to yaml --non-roundtrip null # parse error: expected string\n{a: {|| $in}} | to yaml --non-roundtrip 'null' # a: null\n{a: {|| $in}} | to yaml --serialize # a: !closure \"{|| $in}\"\n```\n\nVerified by reproduction on 0.115.0: `to yaml --non-roundtrip 'lossy'` is\nrejected with an \"expected `error`, `null` or `lossy`\" incompatible-parameters\nerror even though `lossy` is exactly what was passed. Only `'error'` (the\ndefault) and `'null'` work on `to yaml`; use `--serialize` where a lossy\nencoding is genuinely wanted. `to kdl --non-roundtrip 'lossy'` is unaffected.\nDo not build a contract on `--serialize` either — the release notes call it\n\"still available but will probably be deprecated in the future\". The durable\nfix is to keep non-round-trippable values out of the writer.\n\nUseful explicit writer options include `--spec`, `--add-directives`,\n`--multiple`, `--indent`, `--compact-list-indent`, `--quote`,\n`--non-roundtrip`, and `--serialize`. On the writing side `to yaml --multiple`\nemits each item of a list as its own YAML document, mirroring\n`from yaml --multiple` on the reading side.\n\n### Parser keywords and `$ans` are reserved\n\nCommands, aliases, module names, exports, and wildcard imports must not shadow\nparser keywords. `export main` also fails when the _module_ is named to shadow\na keyword — the conflict is on the module name, not on `main` itself. Check the\nfinal imported namespace, not just the local `def` declarations.\n\n`$ans` is reserved for the last successful REPL result. Rename older bindings\nsuch as `let ans = ...`; do not use `$ans.last` as script state. At the REPL,\nthe record exposes `last`, `exit_code`, `duration`, and `command`.\n`$ans.last` storage is opt-in through `$env.config.max_last_result_size` and\ndefaults to `0b`, so code must not assume the previous output is retained.\n\n### Test and index commands removed\n\n- `nu --testbin` no longer exists. Replace it with a small Nushell fixture or a\n purpose-built test executable.\n- `idx import` and `idx export` were removed. Build the in-memory index with\n `idx init`; use `--no-watch` only when live watching is not wanted.\n\n## High-Frequency Command Improvements\n\n### Preserve raw script CLI tokens with `external_arg`\n\nUse `external_arg` on `main` parameters when a script intentionally needs the\ncaller's token spelling rather than Nushell literal coercion. For example,\nunquoted `0001` and `true` stay token-like `glob` values instead of becoming an\ninteger and a boolean:\n\n```nu\ndef main [\n revision: external_arg\n --define (-D): external_arg\n ...rest: external_arg\n] {\n let revision_text = ($revision | into string)\n let rest_text = ($rest | each { into string })\n {revision: $revision_text, define: $define, rest: $rest_text}\n}\n```\n\nPrefer concrete types such as `string`, `int`, or `path` when coercion and type\nvalidation are part of the API. `external_arg` preserves a token; it does not\nsanitize it. Validate it before using it as a path or pattern, and pass it as a\nseparate argv value rather than interpolating it into `nu -c` or a shell\ncommand string. See [Security](security.md) for the containment rules that\napply to any externally supplied path or pattern.\n\nNu 0.115 also accepts arguments after `--` for `nu -c` / `nu --commands`.\nWhen a nested Nu process is genuinely required, keep the command text constant,\ndefine a typed `main`, and pass data separately:\n\n```console\nnu -c 'def main [value: external_arg] { $value | into string }' -- 0001\n```\n\n### Use row conditions with `any` and `all`\n\nSimple predicates can now use the same row-condition syntax as `where`:\n\n```nu\n[9 8 7 6] | enumerate | any item == index * 2\n[1sec 1min 1hr] | all ($it | describe) == duration\n```\n\nKeep a closure when the predicate needs local setup, destructuring, or reuse.\nDuring review, verify that a direct column name is evaluated against a table\nrow and is not mistaken for an outer variable.\n\n### Slice binary data with filesize counts\n\n`chunks`, `first`, `last`, `take`, `skip`, and `drop` accept `filesize` counts;\n`drop` now also accepts binary input. This makes byte intent visible:\n\n```nu\nopen --raw archive.7z | chunks 10MiB\nopen --raw packet.bin | first 16b\nopen --raw packet.bin | skip 8b | take 4b\n```\n\nUse an integer for lists/tables unless a byte-sized count is genuinely clearer.\nDo not assume a binary chunk is text; decode only after validating its encoding\nor at a known character boundary.\n\n### Compare and convert SemVer values directly\n\nNu 0.115 supports `==`, `!=`, `\u003c`, `\u003c=`, `>`, and `>=` on `semver` values. A\nvalid version string is accepted on the right-hand side:\n\n```nu\nlet actual = ('2.0.1' | into semver)\n$actual >= '2.0.0'\n('1.0.0-alpha' | into semver) \u003c '1.0.0'\n```\n\n`into semver` also accepts lists and cell paths. Use `--loose` only for the\ndocumented `v`-style prefixes (`v1.2.3`, `v.1.2.3`, `v:1.2.3`, `v-1.2.3`, or\n`v_1.2.3`); it should not replace validation of arbitrary version text. A\nloose value converts successfully to the `semver` custom type but retains its\noriginal prefix when converted back to a string; do not assume normalization.\nThe release notes do not state this — it is verified by reproduction on 0.115.0\nand locked by the repository smoke test.\n\nNu 0.115.0 has a parser inference edge case that the release notes do not\nmention; it is verified by reproduction. When a comparison is passed to a\nparameter declared `bool` (for example `assert (\u003ccomparison>)`), the parser\nreports `expected bool, found semver`. Binding it with `let` fails the other\nway: the parser infers `semver`, then the runtime reports\n`expected semver, got bool`. Bare expressions and `if (\u003ccomparison>)` are\nunaffected, so this is narrower than \"any boolean context\".\nUntil the supported Nu patch release fixes this, evaluate the comparison in an\nunconstrained one-item list and extract it at the use site, for example\n`assert equal ([($actual >= '2.0.0')] | first) true`. Keep this workaround\nversion-scoped; do not replace semantic version comparison with lexical string\ncomparison.\n\n### Include the stopping boundary in stream prefixes\n\n`take while` and `take until` accept `--include (-i) N`. `--include 1` commonly\nincludes the first item that would otherwise stop the command:\n\n```nu\n[1 2 3 4] | take until --include 1 {|n| $n == 3 } # [1 2 3]\n[1 2 3 4] | take while --include 1 {|n| $n \u003c 3 } # [1 2 3]\n```\n\nReview off-by-one behavior explicitly. Values greater than one include more\nitems after the original stopping point and may trigger additional upstream\nside effects on a lazy stream.\n\nKeep flags before the predicate for compatibility with 0.116's row-condition\nparser; the old trailing-flag form fails to parse there.\n\n### Preserve null groups deliberately\n\n`group-by` now treats null consistently across direct values, cell paths, and\nclosures. Record output omits the null group because record keys must be\nstrings; null no longer collapses into the empty-string key. Use table output\nwhen null is data:\n\n```nu\n[a '' null] | group-by --to-table\n[{x: a} {x: null}] | group-by x --to-table\n```\n\nOptional cell paths such as `group-by x?` still skip missing and null values.\nThis is different from a required path with `--to-table`, which retains the\nnull group.\n\n### Apply math commands to selected record columns\n\nReducing commands such as `math avg`, `math sum`, `math max`, and `math\nvariance`, plus element-wise commands such as `math abs`, `math floor`, and\n`math sqrt`, now accept optional cell paths for records containing list-valued\ncolumns:\n\n```nu\n{alice: [1 2 3], bob: [4 5 6]} | math avg alice\n{alice: [-1 -2], bob: [-3 -4]} | math abs alice\n```\n\nCheck whether unselected columns intentionally remain unchanged; selecting one\ncolumn is not a whole-record reduction.\n\n## Correctness Fixes That Affect Reviews\n\n- Nested `try/finally` no longer consumes an outer error handler: the error\n now reaches the outer `catch`. The inner `finally` still does not run in that\n shape — see [Nested `finally` cleanup is still\n unreliable](#nested-finally-cleanup-is-still-unreliable) before removing a\n pre-0.115 workaround.\n- `error make` rejects malformed labels. A spanned label is\n `{text: ..., span: {start: ..., end: ...}}`, not the old flat\n `{text: ..., start: ..., end: ...}` record.\n- `scope variables`, `scope commands`, `scope aliases`, `scope modules`, and\n `scope externs` include active local scopes. `scope commands` also exposes\n `deprecation_info` as a table, so read a single command's entry with\n `deprecation_info.0`. Review tooling can use it instead of maintaining a\n hard-coded deprecated-command list.\n- `source` can see current outer variables after a rebinding. Reassess\n pre-0.115 workarounds, but keep source targets trusted and parse-time known.\n- `path type` on an empty string returns `null`, not `dir`. An empty or missing\n type must fail validation rather than being treated as a directory.\n- Saving structured data to an unknown/no-extension file still requires an\n explicit serializer. 0.115 only made the failure message clearer; it did not\n add new enforcement. Use `to json | save ...`, another `to ...` converter, or\n render a table intentionally.\n- `seq` now errors on a zero increment and terminates safely at integer bounds.\n Validate a user-supplied step before calling it to provide domain-specific\n feedback.\n- Quoted `#` values now pass correctly as script parameters. Remove shell-style\n escaping workarounds that alter values such as hex colors.\n- Assigning `$env.config.keybindings = []` or `.menus = []` merges with defaults\n and no longer clears them. Use a matching binding with `event: null` to\n unbind a key. Setting only part of `$env.config.color_config` now keeps the\n defaults for the parts that were not set; unlike keybindings, the stored\n record still shows only the keys you assigned, so assert on rendered colors\n rather than on `$env.config.color_config | columns`.\n- Incompatible right-hand-side uses of `$in` in typed math are now reported at\n parse time, matching the existing left-hand-side behavior. For example\n `def f []: string -> int { 2 + $in }` no longer parses. Code that relied on\n the mismatch surfacing at runtime now fails earlier and more visibly.\n- Quotes inside `(...)` subexpressions of interpolated strings now lex\n correctly, so `$\"('\" \"')\"` prints `\" \"`. This does not relax the separate\n rule that a _literal_ parenthesis still requires `$\"...\"` with `\\(`; see\n [Anti-Patterns](anti-patterns.md).\n- `stor import --file-name` with a missing path now fails instead of creating\n an empty file and silently discarding the in-memory database.\n- `generate` accepts `null` pipeline input the same way it accepts empty input.\n- `match` arms accept constant expressions, and `oneof` parameters accept\n negative numeric arguments. Both remove workarounds rather than break code.\n\n### Nested `finally` cleanup is still unreliable\n\nVerified by reproduction on 0.115.0. When a `try { ... } finally { ... }` is\nlexically nested inside an outer `try` whose handler is `catch` — or inside a\nbare `try` with no handler — the inner `finally` is silently skipped:\n\n```nu\ntry {\n try { error make {msg: 'inner'} } finally { print 'INNER-CLEANUP' }\n} catch {|e| print

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

outer caught: ($e.msg)' }\n# outer caught: inner \u003c- INNER-CLEANUP never printed\n```\n\nA bare outer `try` is worse: the error is swallowed, the exit code stays `0`,\nand the cleanup is still skipped. A plain runtime error such as\n`[1 2 3] | get 99` behaves identically, so this is not specific to\n`error make`.\n\nBoth of these shapes do run the inner cleanup on 0.115.0:\n\n```nu\n# The outer handler is `finally` rather than `catch`\ntry {\n try { error make {msg: 'inner'} } finally { print 'INNER-CLEANUP' }\n} finally { print 'OUTER-CLEANUP' }\n\n# The inner `try/finally` sits behind a `do` or a custom-command boundary\ntry {\n do { try { error make {msg: 'inner'} } finally { print 'INNER-CLEANUP' } }\n} catch {|e| print

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

outer caught: ($e.msg)' }\n```\n\nDo not delete a pre-0.115 cleanup workaround on the strength of the release\nnote alone. Where cleanup must happen, prefer the outer-`finally` shape and\nassert afterwards that the owned state is actually gone. See\n[Daemon & E2E Smoke Tests](daemon-and-e2e-smoke-tests.md) for the job/fixture\nownership pattern that already follows this rule.\n\n## Secondary Additions and Performance\n\n- `from kdl` and `to kdl` support `--spec 1|2` and default to KDL 2. Their\n `--format` defaults differ: `from kdl` uses `nodes` (KDL AST rows), while\n `to kdl` uses JSON-in-KDL (`jik`). Pin both `--spec` and `--format` at\n file/API boundaries.\n- `lines` now produces a list stream lazily even for an in-memory string value.\n Avoid collecting it unless later code needs random access or reuse.\n- Large binary slicing and repeated access to large lists/tables are much\n cheaper. Keep streaming for bounded memory; do not add manual caching solely\n to work around the old copy cost without re-measuring.\n- `str replace --regex` and `--multiline` are substantially faster on nested\n string data, and built-in regex parameters use a cache. Prefer the built-in\n structured transform before introducing an external text-processing step.\n- `date floor` and `date ceil` are available from `std-rfc/date` for duration\n boundary rounding.\n- `idx watch` streams indexed filesystem changes and supports filtering,\n timeout, and maximum-event bounds.\n- The new `matrix` custom value provides dedicated arithmetic, linear algebra,\n mapping, and reduction commands. Build one with `into matrix` from a table or\n a `list\u003clist\u003cnumber>>`; bare `matrix` is only the subcommand namespace and\n accepts no pipeline input. Standard `each`, `par-each`, and `reduce`\n intentionally reject matrices, so use `matrix map`, `matrix reduce --fold`,\n and `matrix into-nu` at ordinary Nu-data boundaries.\n\n## Migration Review Checklist\n\n1. Record the project's supported Nu range and reproduce on the lowest relevant\n version before recommending 0.115-only syntax.\n2. Search YAML call sites and make spec, key, tag, multiple-document, and\n round-trip expectations explicit at external boundaries.\n3. Search definitions/imports for parser-keyword names and bindings named\n `ans`.\n4. Replace `nu --testbin`, `idx import`, and `idx export` dependencies.\n5. Decide whether each script argument wants Nushell typing or raw-token\n preservation; do not apply `external_arg` mechanically.\n6. Check `group-by` consumers for null-key loss and empty-string conflation.\n7. Check `error make` labels and tests that compare rendered diagnostics.\n8. Check `path type`, unknown-extension `save`, and keybinding-reset code for\n assumptions changed in 0.115.\n9. Use `scope commands` deprecation metadata to identify active deprecated\n commands, including commands imported into local scopes.\n10. Add focused tests for boundary values: YAML 1.1 scalars, multi-document\n inputs, null groups, SemVer prereleases, binary chunk sizes, and include\n counts of zero/one/many.\n\n## Focused Validation\n\nRun the repository smoke test when Nu 0.115 is the active target:\n\n```console\nnu --no-config-file tests/nu-0.115-smoke.nu\n```\n\nFor a migrated script, start with parsing and a no-config execution seam:\n\n```console\nnu --no-config-file --ide-check 100 path/to/script.nu\nnu --no-config-file path/to/test-script.nu\n```\n\nRemember that `--ide-check` requires parsing its JSON Lines output; exit status\nalone is not proof that the script has no error diagnostics.\n\n## Related References\n\n- [Nu 0.114 Migration](nu-0.114-migration.md) for the previous release's\n stricter typing, explicit submodule imports, and `run`.\n- [Security](security.md) for `external_arg`, path containment, and the rules\n that govern any nested `nu -c` invocation.\n- [String Formats](string-formats.md) for quoting rules, including the\n interpolation and literal-parenthesis constraints referenced above.\n- [Data & Type System](data-and-types.md) for `oneof\u003cT, nothing>`, null\n handling, and conversion semantics behind the YAML and `group-by` changes.\n- [Daemon & E2E Smoke Tests](daemon-and-e2e-smoke-tests.md) for the cleanup\n ownership pattern that survives the nested-`finally` defect.\n- [Script Review](script-review.md) and [Anti-Patterns](anti-patterns.md) for\n the checklist items these changes add.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":20216,"content_sha256":"6075dcaddb663eabefd05a888c8d6599c880bfade360d0c99aefb051095d828f"},{"filename":"references/nu-0.116-migration.md","content":"# Nu 0.116 Migration and Review Reference\n\nUse for 0.116 upgrades, custom completion/config maintenance, and reviews of\nwrappers, data pipelines, or cleanup code. Keep the project's minimum Nu version\nseparate from the local binary; the syntax below targets 0.116 unless stated.\n\n## Evidence and scope\n\nVerified on macOS arm64 with Nu **0.116.0**, binary commit\n`2459fdd134ea4fdbae42efd6924e2b41201cf363` (also the local source HEAD).\nRelease notes: `nu-docs` commit `4c2f11558a9fb15f592f417ba8c6ce8922ca7e82`,\n[`blog/2026-09-26-nushell_v0_116_0.md`](https://github.com/nushell/nushell.github.io/blob/4c2f11558a9fb15f592f417ba8c6ce8922ca7e82/blog/2026-09-26-nushell_v0_116_0.md).\nThe originally supplied October 23 path was renamed in commit `dc92785b`.\nBoth checkouts matched fetched `origin/main`; no pull was needed.\n\nRun [the smoke suite](../tests/nu-0.116-smoke.nu):\n\n```console\nnu --no-config-file -c 'nu-check --debug tests/nu-0.116-smoke.nu'\nnu --no-config-file tests/nu-0.116-smoke.nu\n```\n\nThe 0.115/0.116 suites launch nested Nu through `$nu.current-exe`, so an\nexplicit binary selection is preserved even when PATH points elsewhere. The\nchained `par-each` regression runs in an isolated process with a deadline;\nthe suite also exercises forced timeout and verifies job/process cleanup.\nThese harness checks were rerun on 0.116.0; no older binary was revalidated\nin this follow-up review.\n\nCore examples and regressions below are CLI-tested. Interactive terminal\nownership, editor DAP sessions, plugins, Windows fixes, and performance claims\nare release-note guidance, not measured results on those environments.\n\n## Breaking changes and migration blockers\n\n### Custom completion inputs and outputs\n\nParameter, command-wide, external and menu completers now share named inputs.\nDeclare positional parameters named `token`, `place`, or `buffer` in any order:\n\n```nu\ndef complete-branch [token: record] {\n ^git branch --format '%(refname:short)' | lines\n | where {|branch| $branch | str starts-with $token.text }\n}\n```\n\n- `token.text` is the current token; `token.span` is its source range.\n- `place.command` is the command's shell-word list, with aliases expanded.\n Use it instead of splitting `buffer` on spaces: quoting, pipelines, closures,\n separators, and incomplete arguments make that split unreliable.\n- `place` describes the cursor/site; `flag`, `index`, and `shape` are conditional.\n Use optional access when the site can vary.\n- `buffer` contains the line through the cursor. Prefer it to calling\n `commandline` inside a completer, which can be empty in some editor states.\n- Migrate old `[context offset]`, `[input pos]`, menu `[buffer position]` and\n external `spans` interfaces to these named inputs. The notes mention legacy\n `spans` compatibility; new code should use `place.command`. Arbitrary positional\n names are not aliases for `token`. Contrary to the notes' blanket claim that\n unknown names receive `nothing`, this build bridges the first two unrecognized\n positions to legacy inputs, with deprecation warnings. A parameter completer\n `[anything]` still receives a context string (CLI-tested). Do not depend on\n that bridge: later unrecognized positions are not equivalent to named inputs.\n\n```nu\n$env.config.completions.external.completer = {|place|\n ^carapace $place.command.0 nushell ...$place.command | from json\n}\n```\n\nReturn `null` to decline; strings, lists and suggestion records are accepted.\nAn envelope `{completions: [...], options: {...}, fallback: true}` keeps the\nsuggestions and continues to other sources. Parameter completers filter by\ndefault, while command-wide/external completers normally filter themselves.\nSet `options.filter` explicitly when composing sources. A failing parameter\ncompleter now returns no suggestions rather than falling back to the directory.\n\nThe global `background-completions` option is removed. Mark terminal-owning\ncommands (fzf, `input list`) with `@interactive`; external completion closures\ncan call an annotated command. Do not test this merely by direct invocation:\nthe completion engine supplies inputs and decides terminal ownership.\n\n```nu\n'git checkout mai' | commandline complete --input\n'choose al' | commandline complete --detailed\n# Built-in source composition:\n'./' | commandline complete --type directory\n```\n\n`--input` cannot combine with `--detailed` or `--type`. The release-note sample\nshows `place.kind: positional` and `shape: external-argument` for `git`; the\ntested binary instead returns `kind: external-arg` with no `shape` for this\nundeclared external command. Do not hard-code the sample's whole record.\nSource corroboration: `crates/nu-cli/src/completions/completer.rs`,\n`CompletionSiteKind` display mapping at the recorded revision.\nLegacy bridge source: `crates/nu-cli/src/completions/custom_completions/mod.rs`,\n`LegacyInputs::when_needed` and `LegacyInputKind`.\n\n### YAML writer options\n\nReplace `--compact-list-indent` with `--list-indent compact`; choose\n`--list-indent indented` when that layout is required. The old flag fails to\nparse. Round-trip structured data rather than asserting formatting unless the\nformat itself is the contract.\n\n`to yaml --non-roundtrip null` now accepts the bare literal. Quoted `'null'`\nstill works and remains portable to 0.115. **`--non-roundtrip 'lossy'` alone is\nstill rejected on 0.116.0**; `--serialize` remains available for deliberate lossy\nserialization. Prefer removing unsupported values to silently dropping them.\nConfirmed in `crates/nu-command/src/formats/to/yaml.rs`: lossy handling requires\nthe serialize flag. The raw-null fix did not fix the separate lossy behavior.\n\n### Additional parser compatibility changes\n\n- Quote external literal arguments beginning with `{`; they now parse as\n records/closures. Keep argv data separate from command source.\n- `[1; 2]`, list patterns using semicolon separators, and header-only\n `[[a b];]` now fail instead of silently losing content. Use ordinary list\n separators or complete table rows.\n- `take until`, `take while`, `skip until`, `skip while`, and `chunk-by` accept\n row conditions. Put flags **before** the predicate; this also works on 0.115:\n\n ```nu\n [1 2 3 4] | take until --include 1 {|n| $n == 3 } # [1 2 3]\n [1 2 3 4] | take until $it == 3 # [1 2]\n ```\n\n CLI reproduction found that the former `take until {|n| ... } --include 1`\n form fails to parse on 0.116. The release notes do not call out this migration.\n\n## Named flags and wrapper commands\n\nInternal/custom commands accept records spread into named flags:\n\n```nu\ndef options [--count: int = 5, --nullable: oneof\u003cint, nothing> = 7, --verbose] {\n {count: $count, nullable: $nullable, verbose: $verbose}\n}\noptions ...{count: null, nullable: null, verbose: false}\n# {count: 5, nullable: null, verbose: false}\n```\n\nA null value omits a flag whose type excludes `nothing`, preserving defaults.\nTypes accepting `nothing` (`any`, `oneof\u003cint, nothing>`) receive explicit null.\nFor switches, true enables; false/null omits. Unknown names and incompatible\nfield values error at runtime. Validate/filter untrusted records before forwarding\nflags, rather than granting arbitrary options to a wrapped command.\n\nFor explicit typed flags, `--count=(null)` works; bare `--count=null` can fail\nparse-time checks. List spreads still mean positional/rest arguments, not flags.\n\n## Correctness fixes and data transformations\n\n- Nested `finally` now runs before an outer `catch`, innermost first. `return`,\n `break` and `continue` unwind cleanup, too. An error in `finally` replaces\n the original error. Keep cleanup ownership tests, but do not impose the\n 0.115.0 `do`-boundary workaround on projects requiring 0.116.\n- `$in` and the optional `finally` parameter receive the successful value,\n the error record on failure, or `nothing` on control-flow exit (release-note\n contract). Cleanup remains for side effects, not replacing the return value.\n- Both `semver | into string` inference and the old comparison-to-bool inference\n workaround were retested successfully. Direct `let newer = ($v > '1.0.0')`\n and `assert ($v > '1.0.0')` work on 0.116.\n- `--ide-check` now reports unreadable/missing files with a nonzero exit.\n Parse JSONL diagnostics for existing files as before; semantic errors can\n still accompany exit zero. Keep explicit path checks for older Nu versions.\n- `length`, `columns`, `is-empty`/`is-not-empty`, and `each while` surface stream\n errors instead of silently returning misleading values. Tests must consume\n the stream. Fix invalid upstream predicates rather than ignoring new errors.\n- `default 5 a.b` fills a nested field (creating parents); `default 5 'a.b'`\n targets a literal dotted key. This changes existing unquoted dotted arguments.\n- `flatten` now renames nested fields colliding with later top-level names to\n `\u003cparent>_\u003cfield>`, matching the opposite field order. This does not guarantee\n lossless flattening: on 0.116.0, if the generated name already exists, a value\n is still silently overwritten depending on field order. For example,\n `{nested: {x: 1}, nested_x: 3, x: 2} | flatten` yields\n `[{nested_x: 3, x: 2}]`; putting `nested` last instead leaves `nested_x: 1`.\n Check generated names or explicitly rename conflicts before flattening when\n all values must survive; also review downstream expected column names.\n- `update cells --recursive` visits nested leaf values. Default behavior remains\n nonrecursive; heterogeneous leaves require a type-aware closure.\n- `lines --skip-empty` now drops empty lines for string/byte-stream inputs.\n YAML text such as `a.infra`/`a.nanotube` stays a string rather than a float.\n- `save --force` creates missing parent directories as well as permitting\n overwrites. Validate the destination before calling it; a missing parent is\n no longer a guard against an unintended write.\n- `mkdir --fail-if-exists` opts into rejecting existing paths. Ordinary `mkdir`\n is still idempotent; verbose rows report `created: false` for existing dirs.\n- Template-based `mktemp --directory` now uses the working directory. Prefer\n no-template `mktemp --directory` for the usual temporary root.\n- Chained equal-size `par-each` pools no longer deadlock. Keep bounded tests\n and choose concurrency for independent work; this does not promise output order.\n\n## Optional tools and configuration\n\nUse `tui debug` to test widget layouts and key sequences without a terminal:\n\n```nu\n[{name: a} {name: b}] | tui table --id items\n| tui debug --size [40 10] --keys [down enter] | get selected.name # b\n```\n\nUse stable explicit widget IDs when connecting widgets. `tui run` returns\n`{action, focused, selected, page, values, rows, live}`; debug adds layout/screen\nfields. Hooks returning `{action: submit, selected: ...}` or `{action: quit}`\nclose the UI; other non-null output replaces shared data. Unbounded producers\nbelong in the outer pipeline, not collected child lists. Actual terminal\nrestoration and interactive pickers still need a PTY/manual check.\n\nOther release-note items worth routing on demand:\n\n| Area | Guidance |\n| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Debugging | `nu --dap` serves DAP over stdio; validate with a DAP client, not as a syntax checker. |\n| Source inspection | `view source --dependencies` includes transitive private helpers and captured constants; inspect output before sharing it. |\n| Logging | `std/log` emitters accept `--context` records; keep credentials out of context. |\n| Conversion/hash | `into float` accepts decimal commas; `hash sha512` is available. Do not confuse decimal commas with thousands grouping. |\n| Completion menus | `completions.persistent_menus` defaults false; true keeps menus open while editing. |\n| Keybindings | `SwitchMode`, `vi_visual`, and its cursor shape are new; `vi_normal` bindings no longer apply in visual mode. |\n| Polars | New `date-range`, `date-ranges`, `datetime-range`, `datetime-ranges`; requires the plugin, absent in this validation environment. |\n| MCP | HTTP bind default is now loopback `127.0.0.1`; remote clients need explicit configuration, not a broad-bind workaround by default. |\n| Startup/performance | `$nu.startup-time` measures through first prompt readiness; hooks see provisional timing. Do not compare it directly with older measurements. Source caching/parser/plugin startup improved; benchmark the actual workload. |\n| Windows | Dot-glob deletion skips `.`/`..`; interactive rm prompt handling fixed. Retain deletion/path guards. |\n| Display/tools | Locale date formatting, narrow tables, redirected help, `idx search` limits and `query web` headerless tables have fixes; retest affected integrations. |\n\n## Review checklist\n\n1. Migrate all completion entry points, menu sources, and background settings.\n2. Search YAML writer flags, unquoted dotted `default` keys, brace-leading\n external literals, list semicolons, and flags following row predicates.\n3. Check null/default intent and allowed keys in record-spread wrappers.\n4. Replace only the reproduced obsolete workarounds; preserve old-version support.\n5. Recheck stream failure handling, cleanup ordering, save destinations and\n expected flattened columns; run existing tests as well as new release tests.\n\nSee [0.115 history](nu-0.115-migration.md), [Script Review](script-review.md),\n[Data & Types](data-and-types.md), and [Modules & Scripts](modules-and-scripts.md).\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":15020,"content_sha256":"092a5491b4913dad7091db5b6651e8828ae8b037ffb8b4c6e0219a990750d669"},{"filename":"references/script-review.md","content":"# Nushell Script Review Checklist\n\nComprehensive checklist for reviewing Nushell scripts. Check items in order of priority.\n\n## Contents\n\n- [Review method](#review-method)\n- [Security](#1-security-critical)\n- [Correctness](#2-correctness)\n- [Style and idiom](#3-style--idiom)\n- [Performance](#4-performance)\n- [Robustness](#5-robustness)\n- [Review workflow](#review-workflow)\n\n---\n\n## Review Method\n\nBefore listing findings:\n\n1. Read the repository instructions, supported Nu versions, changed files, and\n relevant call sites/tests. A local `nu --version` is evidence about the test\n environment, not necessarily the project's compatibility target.\n2. Summarize the script's entry points, pipeline contracts, external inputs,\n side effects, and trust boundaries. Follow changed helpers into their callers\n when the bug depends on a wider data flow.\n3. Run the narrowest safe check that can falsify a suspected issue. Prefer a\n minimal reproduction on the relevant Nu version for version-sensitive semantics, then run the\n project's focused test.\n4. Separate blocking correctness/security defects from migration notes,\n maintainability suggestions, and measured performance opportunities.\n5. If no actionable defect remains, say so and identify any validation gap or\n compatibility version that was not available.\n\n### Finding quality\n\nEach finding should contain:\n\n- A severity (`P0` critical, `P1` high, `P2` medium, or `P3` low) based on\n impact and likelihood, not how easy the fix is.\n- A concise title and the smallest useful `file:line` location.\n- The concrete input/state that triggers it, the observable impact, and why the\n current tests do not prevent it.\n- Evidence from the language contract, command signature, minimal reproduction,\n or focused test. Mark version-dependent conclusions explicitly.\n- A focused repair direction; do not require unrelated refactoring.\n\nDo not report speculative failures that cannot be connected to a reachable\ninput, stylistic preferences already enforced by project tooling, or behavior\nthat changed in the supported Nu version without first reproducing it.\n\n---\n\n## 1. Security (Critical)\n\n### Code injection\n\n- [ ] No `nu -c $variable` with untrusted input\n- [ ] No `source $variable` with runtime paths (must be `const`)\n- [ ] No `run` of untrusted `.nu` script paths; `run` targets must be parse-time constants\n- [ ] No `^sh -c`, `^bash -c`, or `^cmd.exe /C` with interpolated user input\n- [ ] No `run-external` with user-controlled command names\n- [ ] `external_arg` values are treated as untrusted raw tokens, not as\n sanitized strings or safe paths/patterns\n\n### Path safety\n\n- [ ] Existing user-provided paths validated with strict expansion + `path relative-to`; no string-prefix containment checks\n- [ ] No raw `open $user_input` without path traversal guard\n- [ ] `..` sequences in user paths detected and rejected\n- [ ] Base directory enforcement for file operations\n\n### Credential handling\n\n- [ ] No hardcoded secrets in source code\n- [ ] Credentials scoped with `with-env`, not set on `$env` directly\n- [ ] Secrets read from files/stdin, not passed as command-line arguments\n- [ ] No credentials logged via `print` or written to non-secure files\n\n### Destructive operations\n\n- [ ] `rm` operations validate target path (not `/`, not `$nu.home-dir`)\n- [ ] Glob patterns from user input are validated (no unintended expansion)\n- [ ] `--depth` limits on `glob` to prevent DoS on large trees\n\n### Temp files\n\n- [ ] Temp files created with built-in `mktemp`, not predictable paths\n- [ ] Temp files cleaned up in `try/catch` or equivalent\n- [ ] Temp directories use `mktemp --directory`\n\n### Environment safety\n\n- [ ] `$env.PATH` not modifiable by untrusted input\n- [ ] Dangerous env vars (`LD_PRELOAD`, `DYLD_INSERT_LIBRARIES`) cleared before running untrusted commands\n- [ ] `with-env` used for scoped environment changes in security-sensitive contexts\n\n---\n\n## 2. Correctness\n\n### Type safety\n\n- [ ] All exported commands have type annotations on parameters\n- [ ] I/O pipeline signatures (`]: type -> type {`) match actual behavior\n- [ ] Complex types use proper syntax: `record\u003cname: string>`, `list\u003cint>`, `table\u003ccol: type>`\n- [ ] Optional parameters use `?` suffix: `name?: string`\n- [ ] Optional params and typed named options without defaults are handled as `oneof\u003cT, nothing>`; boolean switch flags remain `bool`\n- [ ] Rest parameters typed: `...args: string`\n- [ ] Runtime assignment annotations are valid under Nu 0.114's default `enforce-runtime-annotations`\n- [ ] `external_arg` is used only where raw CLI token spelling matters; values\n are converted/validated before structured use\n- [ ] No command/module/export shadows a parser keyword, and no binding uses\n reserved `ans` on Nu 0.115+\n\n### Error handling\n\n- [ ] Fallible operations wrapped in `try/catch`\n- [ ] External commands checked with `complete` when exit code matters\n- [ ] Tests do not assert raw nested-Nu diagnostics without normalizing ANSI, PTY wrapping, and `|` gutters\n- [ ] `catch` blocks include meaningful error context (not empty)\n- [ ] `finally` used for cleanup side effects, not as the returned value\n- [ ] `catch` blocks read `$err.details` for structured diagnostics, not removed `$err.json`\n- [ ] Custom errors include `label` with `span` for good error messages\n- [ ] No bare `error make {msg: '...'}` without span when metadata is available\n- [ ] Nu 0.115 labels use `{text: ..., span: {start: ..., end: ...}}`, not flat\n `{text, start, end}` records\n- [ ] When supporting Nu 0.115.0, avoid nesting `try/finally` directly in an\n outer `try/catch`: use outer `finally` or a `do`/command boundary.\n Nu 0.116 fixes the skipped cleanup; test ownership without requiring the workaround\n\n### Null safety\n\n- [ ] Optional record fields accessed with `?`: `$rec.field?`\n- [ ] `default` used for fallback values: `$val | default 'N/A'`\n- [ ] No bare field access on records from external/untrusted sources\n- [ ] `$in` captured early with `let` when used multiple times\n- [ ] `group-by` uses `--to-table` when null groups must survive or remain\n distinct from empty strings\n\n### Logic correctness\n\n- [ ] `for` not used as final expression (returns null, use `each`)\n- [ ] `mut` variables not captured in closures (will error)\n- [ ] `source`/`use` paths are `const`, not `let`\n- [ ] Long `if`/`else if` chains on one value prefer `match` unless `if` is clearer\n- [ ] `if`/`match` expressions have `else`/`_` fallbacks when a non-null output is required\n- [ ] `each` not used on single records (use `items` or `transpose`)\n- [ ] `parse` gets `lines` first when line-by-line parsing of stream input is intended\n- [ ] Correct operator: `>` in non-pipeline context is comparison, not redirect\n- [ ] Multiline custom command calls with named flags are one-line or wrapped in parentheses\n- [ ] SemVer logic uses `into semver`, direct comparison on Nu 0.115 (including\n the 0.115.0 bool-context inference workaround when required; fixed in 0.116),\n `into semver-range`, or `semver bump`, not string surgery\n- [ ] `take while/until --include` has boundary tests for include counts zero,\n one, and greater than one when off-by-one behavior matters\n\n### Data-format contracts\n\n- [ ] Namespace-sensitive XML does not rely on `from xml` records: element and\n attribute prefixes/URIs are dropped, and attributes such as `id` and\n `r:id` collide with the later value winning (verified on Nu 0.115.1)\n- [ ] YAML call sites pin `--spec` when upstream YAML 1.1/1.2 semantics matter\n- [ ] `from yaml --multiple` produces a stable expected shape (`list` or\n `single`) at API boundaries instead of relying unintentionally on `auto`\n- [ ] Non-string YAML keys and unknown tags are rejected or explicitly handled;\n `--key-resolution verbatim` / `--ignore-tags` are not used as validation\n- [ ] `to yaml` non-round-trip handling is deliberate, and golden tests compare\n semantics unless exact formatting is the contract\n- [ ] Quote `--non-roundtrip 'null'` for 0.115 compatibility; raw null works\n on 0.116, but `'lossy'` alone still fails on 0.116.0\n- [ ] Nu 0.116 YAML writers use `--list-indent compact|indented`, not the removed flag\n- [ ] Nu 0.116 completers use named inputs and `place.command`, with engine-driven tests\n- [ ] Record-spread flags have validated keys and intentional null/default semantics\n- [ ] Dotted `default` arguments distinguish nested paths from quoted literal keys\n- [ ] `save --force` destinations are validated even when parents do not yet exist (0.116 creates them)\n- [ ] YAML call sites that read colon-bearing scalars (`HH:MM:SS`, IDs) account\n for the 1.1 sexagesimal / 1.2 string split\n- [ ] KDL spec and `nodes`/`jik` format are pinned when files cross a system\n boundary\n\n### External commands\n\n- [ ] External commands prefixed with `^` when name conflicts with builtins\n- [ ] `find` (Nushell builtin) vs `^find` (Unix) distinction maintained\n- [ ] `sort` (Nushell builtin) vs `^sort` (Unix) distinction maintained\n- [ ] Arguments to external commands separated (not concatenated strings)\n- [ ] Format strings passed to external commands use double quotes for simple escapes, or `char tab` / `char nl` when interpolation is needed\n- [ ] CLI tests use real fixtures rather than removed `nu --testbin` on Nu\n 0.115+\n\n---\n\n## 3. Style & Idiom\n\n### Naming\n\n- [ ] Commands: `kebab-case` (`fetch-user`, not `fetchUser` or `fetch_user`)\n- [ ] Variables/params: `snake_case` (`$user_id`, not `$userId`)\n- [ ] Env vars: `SCREAMING_SNAKE_CASE` (`$env.APP_VERSION`)\n- [ ] Flags: `kebab-case` (`--output-dir`, not `--output_dir`)\n- [ ] Full words preferred (`$user_name`, not `$usr_nm`)\n\n### String format priority\n\n- [ ] Bare words in arrays: `[foo bar]` not `[\"foo\" \"bar\"]`\n- [ ] Single quotes for simple strings: `'hello'` not `\"hello\"`\n- [ ] Single-quoted interpolation preferred: `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

val: ($x)'` not `$\"val: ($x)\"`\n- [ ] Strings needing a **literal** `(` use `$\"...\\(...\"`, never `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` — in\n `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` the paren always opens an expression and `\\(` does not escape it\n- [ ] Double quotes only when escape sequences needed: `\"\\n\"`, `\"\\t\"`\n- [ ] Raw strings for regex: `r#'pattern'#`\n- [ ] `str uppercase` / `str lowercase` used instead of deprecated `str upcase` / `str downcase`\n\n### Pipeline & functional style\n\n- [ ] Pipelines preferred over imperative loops\n- [ ] `$items | get price | math sum` instead of `mut total; for ...`\n- [ ] `ls | where size > 1mb` instead of manual filtering\n- [ ] `enumerate` instead of manual index counters\n- [ ] `reduce` instead of `mut` accumulator + `for`\n- [ ] `match` preferred for multi-branch dispatch on a single value\n- [ ] Simple `any`/`all` predicates may use Nu 0.115 row conditions; closures\n remain when they improve scope clarity or support older Nu versions\n\n### Formatting\n\n- [ ] No space before `|params|` in closures: `{|x| ...}` not `{ |x| ...}`\n- [ ] Spaces around pipe: `cmd | cmd` not `cmd|cmd`\n- [ ] Commas omitted in lists: `[1 2 3]` not `[1, 2, 3]`\n- [ ] One space after `:` in records: `{x: 1}` not `{x:1}`\n- [ ] Multi-line format for expressions >80 chars\n\n### Documentation\n\n- [ ] Exported commands have `#` comment above `def`\n- [ ] Parameter descriptions as inline `#` comments\n- [ ] `@example` attributes for non-trivial commands\n- [ ] `@category` for organization when applicable\n\n### Modules\n\n- [ ] Only necessary definitions are `export`-ed\n- [ ] `export def main` used when command matches module name\n- [ ] Submodules are re-exported explicitly when callers need the submodule namespace in Nu 0.114+\n- [ ] Private helpers are not exported\n- [ ] `export-env` for environment setup blocks\n\n---\n\n## 4. Performance\n\n### Parallelism\n\n- [ ] `par-each` is suggested only for independent work with safe concurrency;\n ordering, rate limits, shared state, and side effects are accounted for\n- [ ] `--threads` is bounded when resource usage or service limits matter\n- [ ] Performance findings include a scale argument or measurement; do not flag\n every sequential `each` mechanically\n\n### Streaming & memory\n\n- [ ] `each --flatten` for streaming nested results\n- [ ] Large files not loaded entirely when streaming suffices\n- [ ] `lines` + pipeline for line-by-line processing of large files\n- [ ] Nu 0.115 lazy `lines` output is not collected unless random access or\n repeated traversal requires materialization\n- [ ] `peek` used for stream metadata/sample inspection instead of collecting\n- [ ] `first N` / `take while` to limit processing early\n- [ ] Binary byte windows use `filesize` counts (`first 16b`, `chunks 10MiB`)\n where this makes units and boundaries clearer\n\n### Caching & computation\n\n- [ ] Expensive results cached in `let` bindings, not recomputed\n- [ ] `glob` with `--depth` to avoid scanning huge trees\n- [ ] Built-in commands preferred over external for small data\n- [ ] External tools (`^rg`, `^jq`) used for large-scale operations\n\n---\n\n## 5. Robustness\n\n### Input validation\n\n- [ ] Parameter types annotated (catches misuse at parse time)\n- [ ] Range/value checks at function entry for critical params\n- [ ] User-facing commands validate inputs before processing\n- [ ] Consistent return types (don't mix null and value unexpectedly)\n\n### File operations\n\n- [ ] `path exists` checked before `open` when file may not exist\n- [ ] `save --force` used intentionally (overwrites without warning)\n- [ ] File encoding handled appropriately (`open --raw` for binary)\n- [ ] Structured output uses a recognized extension or an explicit `to ...`\n serializer before `save`\n- [ ] Empty input is rejected before `path type`; Nu 0.115 returns `null` for\n `'' | path type`\n- [ ] `from xlsx` / `from ods` output handled as a record of sheet tables\n- [ ] Spreadsheet imports use `--noheaders`, `--first-row`, or `--prefer-integers` instead of removed `--header-row`\n- [ ] `mkdir -v` / `mv -v` / `rm -v` outputs treated as tables, not parsed text\n- [ ] `rm` / `mkdir` partial-success behavior considered when multiple paths are passed\n\n### Process management\n\n- [ ] Long-running external processes have timeouts or cancellation\n- [ ] Background jobs (`job spawn`) tracked and cleaned up\n- [ ] Exit codes checked for critical external commands\n\n### Tests and compatibility\n\n- [ ] Generated scripts are checked before their first real side effects;\n `nu-check --debug` is the default, with `--as-module` only for modules\n- [ ] A plain `nu-check` boolean is consumed; exit `0` alone cannot prove it passed\n- [ ] `source` is used for intentional execution/loading, never as a parse-only check\n- [ ] Generated messages/payloads have output or consumer round-trip assertions;\n valid but unintended interpolation can pass every parser check\n- [ ] Tests cover success, invalid input, boundary values, and external-command\n failure for changed behavior\n- [ ] Side-effecting tests use isolated temp fixtures and assert after the\n `try`/`finally` that the owned state is gone, or record partial-success\n state explicitly\n- [ ] Version-sensitive tests run on the project's lowest supported Nu version\n as well as the current target when compatibility is claimed\n- [ ] `--ide-check` JSON Lines are parsed for error diagnostics; exit code `0`\n and empty output are not accepted blindly\n- [ ] On Nu 0.115+, `scope commands` `deprecation_info` is used to confirm\n deprecations instead of relying only on memory or rendered help text; it\n is a table, so read a single command's entry as `deprecation_info.0`\n\n---\n\n## Review Workflow\n\n1. **Establish scope and versions** — Read instructions, diff, callers, tests,\n and supported Nu range.\n2. **Model data and trust flow** — Trace entry points, types, paths, external\n processes, serialization, and side effects.\n3. **Security pass** — Check Section 1 systematically.\n4. **Correctness and migration pass** — Verify types, null handling, errors,\n data-format contracts, process status, and behavior on the supported Nu versions.\n5. **Robustness and tests pass** — Exercise boundary/failure paths and cleanup.\n6. **Maintainability/performance pass** — Report only actionable, non-tooling\n issues and performance claims with a scale argument or measurement.\n7. **Falsify and rank findings** — Reproduce version-sensitive claims, remove\n false positives, then report by severity with precise locations.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":16464,"content_sha256":"3ee86f8946dca1edbe5919e34e0c26b83fc4355f07c7235c3741eda2745789d7"},{"filename":"references/security.md","content":"# Nushell Security Reference\n\n## Contents\n\n- Nushell's security model and threat model\n- Safe commands, paths, credentials, temp files, removal, globs, and environments\n- Windows-specific risks\n- Security review checklist\n\n## Nushell's Security Model\n\n### Built-in safety advantages over Bash\n\n- **No in-process `eval` builtin** — Ordinary values are not reinterpreted as\n Nushell source; explicit boundaries such as `nu -c` and `run` can still parse\n and execute additional trusted code\n- **Parse-before-run blocks** — A parsed block is checked before that block is\n evaluated; nested `nu -c` processes and `run --full-reparse` parse later code\n- **Arguments passed as arrays** — External command arguments go through `std::process::Command`, not through shell interpretation\n- **Type system** — Parameter and pipeline types catch many correctness errors;\n injection resistance still depends on avoiding string re-interpretation and\n validating command/path boundaries\n- **Explicit environment scopes** — Ordinary `def`/`do` calls do not leak\n environment changes to callers, while same-stack control flow such as `if`\n can; use `with-env` when temporary scope is security-sensitive\n\n### Remaining attack surfaces\n\nDespite these advantages, Nushell scripts can still be vulnerable to:\n\n- Command injection via `^sh -c`, `^bash -c`, or `nu -c` with untrusted input\n- Accidental command execution and data disclosure via unintended interpolation\n — `(...)` inside `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'`/`$\"...\"` is evaluated, so an unescaped literal paren\n in a message can run a command and splice its output into the string\n- Path traversal via unvalidated user-provided paths\n- Credential leaking through environment variables\n- Glob injection from user-controlled patterns\n- TOCTOU (Time-of-Check-Time-of-Use) race conditions\n- Unsafe temp file creation\n- Unhandled external command failures\n\n---\n\n## Threat Model\n\n### Critical risk\n\n| Threat | Vector | Mitigation |\n| ---------------- | ---------------------------- | ----------------------------------------------------- |\n| Code injection | `nu -c $user_input` | Never pass untrusted input to `nu -c` or `source` |\n| Shell injection | `^sh -c $untrusted` | Never use `sh -c`/`bash -c` with interpolated strings |\n| Plugin injection | `plugin add $untrusted_path` | Only install plugins from trusted sources |\n\n### High risk\n\n| Threat | Vector | Mitigation |\n| ------------------------ | ---------------------------------------- | ------------------------------------------------------------------------ |\n| Path traversal | `open $user_path` | Validate and canonicalize paths, check against base directory |\n| Credential leak | `$env.API_KEY = 'secret'` | Use `with-env` for scoped credentials |\n| PATH hijacking | `$env.PATH` poisoning | Use absolute paths for critical commands |\n| Glob injection | typed `glob` reaches an external command | Validate value types; keep literal external arguments as `string` values |\n| Env var injection | `$env.LD_PRELOAD` | Clear dangerous env vars before running untrusted commands |\n| Unintended interpolation | literal `(` in `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` | Escape it as `\\(` in `$\"...\"`; `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` cannot escape parens |\n\n### Medium risk\n\n| Threat | Vector | Mitigation |\n| ---------------- | ------------------------------- | ------------------------------------------- |\n| TOCTOU | Check then use file | Use atomic operations where possible |\n| Temp file race | Predictable `/tmp/myfile` | Use built-in `mktemp` for unique temp files |\n| Unhandled errors | External command silently fails | Use `complete` and check `exit_code` |\n| Glob DoS | `glob **/*` on huge trees | Use `--depth` limits |\n| Config tampering | Modified `config.nu` | Protect config file permissions |\n\n---\n\n## Safe Patterns\n\n### 1. External command execution\n\n```nu\n# DANGEROUS — shell interprets the entire string\n^bash -c

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

echo ($user_input)'\n^sh -c $user_input\n\n# SAFE — arguments passed directly, no shell interpretation\n^echo $user_input\nrun-external 'ls' '-la' $user_dir\n\n# SAFE — separated command and arguments\nlet args = [$user_file '--format' 'json']\n^cat ...$args\n```\n\n**Why Nushell is safer:** When you run `^cmd $arg`, Nushell passes `$arg` as a single argument to the OS process API. It does NOT go through a shell, so `; rm -rf /` in `$arg` is treated as literal text, not a command separator.\n\n**The exception:** `^sh -c`, `^bash -c`, `^cmd.exe /C`, and `nu -c` explicitly invoke a shell interpreter, which WILL interpret the string. Never use these with untrusted input.\n\n**Allowlisting a command _string_ before `nu -c` is fragile — pass argv instead.**\nValidating with a regex that \"only allows `git show`/`git diff`\" still feeds a\nsingle string to an interpreter, so any gap in the pattern becomes command\ninjection. A common, real mistake: using `\\s` as an argument separator —\nbecause `\\s` matches **newlines** (a literal space does not), a payload like\n`\"git diff abc\\nrm -rf /\"` slips through an allowlist such as\n`^git (diff|show)\\s.*\\s.* nushell-pro — Skillopedia (the second `\\s` swallows the injected newline), and\n`nu -c` then runs the second line as its own command. Validate structured argv/list data\n(or a deliberately simple token grammar), then run the external with\n**separated arguments** instead of re-interpreting the string:\n\n```nu\n# Fragile — the validated string still goes through an interpreter\nif (is-allowed $cmd) { ^nu -c $cmd } # a newline in $cmd starts a second command\n\n# Robust — validate argv data and exec the binary directly\nlet argv = $request.argv # e.g. [git diff abc], not \"git diff abc\\n...\"\nif (is-allowed-argv $argv) {\n ^($argv | first) ...($argv | skip 1) # no shell, no re-parsing of the string\n}\n```\n\nIf a string allowlist is unavoidable, also reject control characters explicitly —\nanchored regexes (`^... nushell-pro — Skillopedia ) do not guarantee a single line, `\\s` is broader than\na literal space, and word-boundary/word-class shortcuts like `\\b`/`\\w` are not\ncommand-token rules. Do not use space-splitting as a shell parser; only split\nstrings whose grammar has already rejected quotes, metacharacters, control\ncharacters, and arguments containing spaces:\n\n```nu\nif ($cmd =~ r#'[\\r\\n]'#) { return false } # inside a validator: no embedded newlines\n```\n\n### 2. Path validation\n\n```nu\n# Validate user paths against a base directory\ndef safe-open [name: string, --base-dir: path = '.'] {\n let base = ($base_dir | path expand --strict)\n let candidate = ($base | path join $name)\n\n # Check before strict expansion so this branch can provide a custom span.\n if not ($candidate | path exists) {\n error make {\n msg:

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

File not found: ($candidate)'\n label: {text: 'this file', span: (metadata $name).span}\n }\n }\n\n let full = ($candidate | path expand --strict)\n\n # Prove containment by path components, not a string prefix.\n try {\n $full | path relative-to $base | ignore\n } catch {\n error make {\n msg: 'Path traversal detected'\n label: {\n text:

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Path ($name) escapes base directory ($base)'\n span: (metadata $name).span\n }\n }\n }\n\n open $full\n}\n```\n\n### 3. Home-relative paths\n\nFor paths that intentionally live under the user's home directory, compute the\nprefix from `$nu.home-dir` instead of hardcoding a machine-specific absolute\npath. This is especially important for constants used by `source`/`use`, where\n`const` is required for parse-time resolution.\n\n```nu\n# Good — portable across users and machines\nconst work_dir =

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

($nu.home-dir)/work/dir'\n\n# Bad — tied to one local account\nconst work_dir = '/user/name/work/dir'\n```\n\n### 4. Credential handling\n\n```nu\n# Bad — credential persists in environment, visible to all child processes\n$env.DB_PASSWORD = 'hunter2'\n^psql -U admin $db_name\n# Password is now in env of psql AND any commands after\n\n# Good — scoped credential, only visible within the block\nwith-env {PGPASSWORD: (open ~/.secrets/db_pass | str trim)} {\n ^psql -U admin $db_name\n}\n# PGPASSWORD no longer exists here\n\n# Good — read from file, use directly\nlet token = (open ~/.config/api-token | str trim)\nhttp get $url -H {Authorization:

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Bearer ($token)'}\n\n# Bad — credential in command line (visible in process listing)\n^curl -u

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

admin:($password)' $url\n\n# Better — use stdin or config file for credentials\n$password | ^tool --password-stdin\n```\n\n### 5. Safe file operations\n\n```nu\n# Bad — predictable temp file path (race condition)\nlet tmp = '/tmp/my-script-output'\n'data' | save $tmp\n# Another process could create/symlink this path first!\n\n# Good — Nu 0.114 built-in, portable, and already returns a path string\nlet tmp = mktemp --suffix .tmp\ntry {\n # mktemp already created the file, so overwriting must be explicit.\n 'data' | save --force $tmp\n # ... process the file ...\n} finally {\n rm -f $tmp\n}\n\n# Good — unique temp directory\nlet tmpdir = mktemp --directory\n```\n\n### 6. Safe rm and destructive operations\n\n```nu\n# Bad — an untrusted path or typed glob can target unintended files\n^rm -rf $user_provided_path\n\n# Good — validate first\ndef safe-remove [target: path] {\n let resolved = ($target | path expand)\n\n # Never allow removing root or home\n if $resolved == '/' or $resolved == $nu.home-dir {\n error make {msg:

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Refusing to remove ($resolved)'}\n }\n\n # Verify it exists and is expected type\n if not ($resolved | path exists) {\n error make {msg:

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Path does not exist: ($resolved)'}\n }\n\n rm -r $resolved\n}\n```\n\n### 7. Glob safety\n\n```nu\n# Bad — user input could contain glob characters\nlet pattern = $user_input\nglob $pattern # Could expand to unintended files\n\n# Good — escape or validate\ndef safe-glob [pattern: string, --base-dir: path = '.'] {\n let base = ($base_dir | path expand --strict)\n let full_pattern = ($base | path join $pattern | path expand)\n\n # Reject an absolute or parent-traversing pattern before glob walks it.\n try {\n $full_pattern | path relative-to $base | ignore\n } catch {\n error make {msg:

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Glob pattern escapes base directory: ($pattern)'}\n }\n\n # Also validate every resolved match so symlink targets cannot leave base.\n glob $full_pattern --depth 3\n | each {|matched|\n let full = ($matched | path expand --strict)\n try {\n $full | path relative-to $base | ignore\n } catch {\n error make {msg:

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Glob result escapes base directory: ($matched)'}\n }\n $full\n }\n}\n```\n\n### 8. External command error handling\n\n```nu\n# Bad — silently ignores failures\n^git push origin main\n\n# Good — check exit code\nlet result = (^git push origin main | complete)\nif $result.exit_code != 0 {\n error make {msg:

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

git push failed: ($result.stderr)'}\n}\n\n# Good — try/catch for simple cases\ntry {\n ^cargo test\n} catch {\n print -e 'Tests failed'\n exit 1\n}\n```\n\n### 9. Environment variable safety\n\n```nu\n# Sanitize PATH to prevent command hijacking\ndef with-safe-path [block: closure] {\n with-env {PATH: [/usr/local/bin /usr/bin /bin]} {\n do $block\n }\n}\n\n# Clear dangerous env vars and allow only pre-approved absolute executables.\n# This reduces environment/PATH hijacking; it is not a process sandbox.\ndef restricted-exec [cmd: path, allowed: list\u003cpath>, ...args: string] {\n let executable = ($cmd | path expand --strict)\n let allowed = ($allowed | each { path expand --strict })\n if $executable not-in $allowed {\n error make {msg:

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Executable is not allowed: ($executable)'}\n }\n\n with-env {\n LD_PRELOAD: null\n LD_LIBRARY_PATH: null\n DYLD_INSERT_LIBRARIES: null\n } {\n run-external $executable ...$args\n }\n}\n```\n\n---\n\n## Windows-Specific Risks\n\n### CMD.EXE argument injection\n\nWhen Nushell calls CMD internal commands on Windows, arguments pass through `cmd.exe /D /C`:\n\n```nu\n# These characters need CMD-aware quoting:\n# & | \u003c > ^ %\n# % expands environment variables: %USERNAME%\n# & chains commands: echo hello & whoami\n\n# For Nushell's automatic CMD-internal dispatch, Nu rejects \\r, \\n, %, and\n# embedded unsafe quotes, and quotes arguments containing & | \u003c > ^.\n```\n\n### Mitigation\n\n- Prefer Nushell native commands or direct executables when possible.\n- Rely on Nushell's automatic CMD-internal argument escaping rather than\n constructing a `cmd.exe /C` string yourself.\n- Treat explicit `^cmd.exe /C $untrusted_string` as a separate injection risk;\n it reintroduces command-string interpretation.\n\n---\n\n## Security Review Checklist\n\nWhen auditing a Nushell script for security:\n\n1. **Code injection** — Search for `nu -c`, `source`, `^sh`, `^bash`, `^cmd.exe`, `run-external` with user-controlled arguments; a regex-validated command string re-fed to `nu -c` is still vulnerable (pass validated argv/list data as separated args instead)\n2. **Path traversal** — Search for `open`, `save`, `rm`, `cp`, `mv`, `glob` with user-provided paths; check for `..` validation\n3. **Home paths** — Search for hardcoded home-directory prefixes like `/Users/name`, `/home/name`, or `C:\\Users\\name`; compute them from `$nu.home-dir` instead\n4. **Credentials** — Search for `$env.*KEY`, `$env.*SECRET`, `$env.*PASSWORD`, `$env.*TOKEN`; check if scoped with `with-env`\n5. **External commands** — Verify `complete` or `try/catch` is used for error handling; check for `^` prefix\n6. **File operations** — Check temp file creation uses `mktemp`; verify `rm` operations are guarded\n7. **Glob patterns** — Check if user input flows into `glob` or `ls` patterns; verify `--depth` limits\n8. **Environment** — Check if `$env.PATH` or `$env.LD_PRELOAD` could be poisoned\n9. **Error masking** — Verify errors are not silently swallowed; check `try` blocks have meaningful `catch`\n10. **Unintended interpolation** — Search interpolated strings for parenthesized text that was meant to be literal (`(env)`, `(s)`, `(y/n)`, markdown links); in `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` it is executed, so escape it as `\\(` in `$\"...\"`\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":14685,"content_sha256":"4ebc3671c092054f015803807a879277c7842ecc3b8b1f0eb9463a9e20f61eb1"},{"filename":"references/string-formats.md","content":"# Nushell String Formats Reference\n\n## String Format Priority (High to Low)\n\n1. **Bare word** — Simple word-character-only strings in data contexts\n2. **Raw string** `r#'...'#` — Regex patterns, paths with quotes, multi-line content\n3. **Single-quoted** `'...'` — Simple strings without embedded single quotes\n4. **Single-quoted interpolation** `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` — Interpolation without escape\n sequences **and without literal parentheses**\n5. **Backtick** `` `...` `` — Paths/globs with spaces\n6. **Double-quoted** `\"...\"` — Only when escape sequences are needed (`\\n`, `\\t`, `\\\"`, etc.)\n7. **Double-quoted interpolation** `$\"...\"` — When interpolation is combined with\n escape sequences **or with a literal `(`** (written `\\(`)\n\n## Conversion Rules\n\n### Use bare words when:\n\n- Inside arrays: `[foo bar baz]` not `[\"foo\" \"bar\" \"baz\"]`\n- Path join arrays: `[$dir patches]` not `[$dir \"patches\"]`\n- Match patterns: `match $x { absolute => ... }` not `match $x { \"absolute\" => ... }`\n\n### Use raw strings when:\n\n- Regex patterns with special chars: `r#'(?:a/|b/)?'#` not `\"(?:a/|b/)?\"`\n- Strings containing both single and double quotes\n- Multi-line content without interpolation\n\n### Use single quotes when:\n\n- Simple strings: `'hello world'` not `\"hello world\"`\n- No escape sequences or interpolation needed\n\n### Use single-quoted interpolation when:\n\n- Variables/expressions present but NO escape sequences:\n - `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Package: ($pkg.name)'` not `$\"Package: ($pkg.name)\"`\n - `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Error: ($msg)'` not `$\"Error: ($msg)\"`\n- …and the string contains **no literal `(`**. If it does, `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` cannot\n express it at all — see\n [Literal parentheses](#literal-parentheses-need-double-quoted-interpolation)\n below.\n\n### Keep double quotes ONLY when:\n\n- Escape sequences present: `\"\\n\"`, `\"\\t\"`, `\"\\r\"`, `\"\\\"\"`\n- Need both interpolation and escapes: `$\"Line: ($n)\\n\"`\n- A **literal parenthesis** is needed alongside interpolation: `$\"\\(abc)($var)\"`\n\n### YAML output in 0.113.1+\n\n`to yaml` no longer quotes every string. It emits plain scalars when they are\nsafe to round-trip as strings, keeps quotes for values that YAML could\nreinterpret (for example `'off'`), and emits multiline strings as block scalars.\n\n```nu\n{value: 'off', path: '/dev/stdout', name: 'kong'} | to yaml\n# value: 'off'\n# path: /dev/stdout\n# name: kong\n```\n\nWhen testing YAML output, assert parsed structure where possible instead of exact\nquote style.\n\n### Important: Single quotes don't escape\n\nIn Nushell, `\\'` inside `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` is NOT an escape — it's a literal backslash + quote.\n\n```nu\n# Correct — use double quotes when literal single quotes needed\nlet marker = $\"'($pkg)@($ver)':\"\n\n# Wrong — backslash doesn't escape in single quotes\nlet marker =

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

\\'($pkg)@($ver)\\':' # Can terminate the string and fail to parse.\n```\n\n### Literal Parentheses Need Double-Quoted Interpolation\n\n**Rule: a literal `(` directly in an interpolated string needs `$\"...\\(...\"`.\n`

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` cannot escape a literal paren.** Plain strings and values inserted from\nvariables can contain parentheses; they are not recursively interpreted.\n\nThis is the single most common interpolation mistake. Because `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` does no\nescape processing, `\\` is always a literal backslash and `(` _always_ opens an\ninterpolation expression — `\\(` does not stop it. Only `$\"...\"` treats `\\(` as\nan escaped, literal paren. From `crates/nu-parser/src/parse_literals.rs`, a `(`\nopens an expression when:\n\n```rust\ncurrent_byte == b'(' && (!double_quote || preceding_consecutive_backslashes.is_multiple_of(2))\n```\n\nFor `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` the first disjunct is unconditionally true, so no amount of\nbackslashes changes the outcome.\n\nBoth quote styles interpolate `($var)` identically. The only difference that\nmatters here is that `$\"...\"` can escape and `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` cannot.\n\n#### Three failure modes, two of them silent\n\n```nu\nlet count = 3\n\n# 1. Silent corruption — no error, just a wrong string\n

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

(1 + 1) items' # => \"2 items\"\n\n# 2. Silent information disclosure — `env` really exists, so it runs\n

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Usage: deploy (env) --force' # => \"Usage: deploy HOME=/Users/me\n # API_TOKEN=sk-... --force\"\n\n# 3. Runtime error — the paren content is run as an external command\n

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Done ($count) file(s)' # => Error: `s` is neither a Nushell built-in\n # nor a known external command\n

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

[link](https://example.com)' # => Error: external command failed\n

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

expected list(int)' # => Error: external command failed\n```\n\nFailure mode 2 is the reason to treat this as a security item, not just a\ncorrectness one: any parenthesized text that happens to name a real command is\nexecuted and its output is spliced into the string. A message intended for a log\ncan end up carrying command output or credentials.\n\n#### The fix\n\n```nu\nlet var = 'X'\nlet n = 5\nlet count = 3\n\n$\"\\(abc)($var)\" # => \"(abc)X\"\n$\"\\(1 + 1) items\" # => \"(1 + 1) items\"\n$\"[link]\\(https://example.com) ($n)\" # => \"[link](https://example.com) 5\"\n$\"Done ($count) file\\(s)\" # => \"Done 3 file(s)\"\n$\"Usage: deploy \\(env) --force\" # => \"Usage: deploy (env) --force\"\n$\"expected list\\(int)\" # => \"expected list(int)\"\n```\n\nOnly `(` needs escaping. A `)` outside an expression is already literal, so\n`$\"\\(abc) ok\"` and `$\"\\(abc\\) ok\"` both produce `(abc) ok`.\n\n#### Frequent real-world triggers\n\nMarkdown links, usage/help text, type names in error messages, `file(s)`-style\npluralization, `(y/n)` prompts, and any regex or code snippet embedded in a\nmessage.\n\n#### When `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` is still the better choice\n\nWhen there are no literal parens but there _are_ literal backslashes — `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'`\navoids doubling every one:\n\n```nu\n

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

C:\\Users\\Data ($var)' # => C:\\Users\\Data X\n$\"C:\\\\Users\\\\Data ($var)\" # same result, noisier\n```\n\nIf an interpolated literal needs both `(` and backslashes, escape both in\n`$\"...\"` (`\\(` and `\\\\`). For code or other delimiter-heavy content, plain\nfragments joined with serialized values are often clearer; see below.\n\n#### Decision table\n\n| String contains | Use |\n| ------------------------------------ | --------------------- |\n| No interpolation | `'...'` or `r#'...'#` |\n| Interpolation only | `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` |\n| Interpolation + literal `\\` | `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` |\n| Interpolation + literal `(` | `$\"...\\(...\"` |\n| Interpolation + escapes (`\\n`, `\\t`) | `$\"...\"` |\n| Interpolation + literal `(` and `\\` | `$\"...\\(...\\\\...\"` |\n\n### Important: Command expressions require ` nushell-pro — Skillopedia prefix\n\nStrings containing Nushell command expressions wrapped in `()` MUST keep the ` nushell-pro — Skillopedia prefix:\n\n```nu\n# Correct — $ prefix required for command expressions\nprint

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

(char nl)Done:'\nprint

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

(ansi g)Success!(ansi rst)'\n\n# Wrong — without $ these are literal text\nprint '(char nl)Done:' # Prints literal \"(char nl)\"\nprint '(ansi g)Success!' # Prints literal \"(ansi g)\"\n```\n\n**Rule**: If a string contains `(...)` that should be evaluated as a command, always use `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` or `$\"...\"`.\n\nThe converse also holds: if the `(...)` should **not** be evaluated, `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` is\nnot an option — escape it as `\\(` inside `$\"...\"`. See\n[Literal parentheses](#literal-parentheses-need-double-quoted-interpolation).\n\n### External command format strings\n\nFor external command format strings, prefer double quotes with a single escape\nbackslash for simple escape sequences so Nushell materializes the separator\nbefore calling the external tool. `\"%H\\t%an\"` passes an actual tab; `\"%H\\\\t%an\"`\npasses a literal `\\t`. Use `char tab` / `char nl` when the format string also\nneeds Nushell interpolation or when explicit separators make parsing clearer.\n\n```nu\n# Wrong — single quotes pass literal \"\\t\" to git\n^git log --format='%H\\t%an'\n\n# Wrong — double backslash also passes literal \"\\t\"\n^git log --format=\"%H\\\\t%an\"\n\n# Preferred — simple and Nushell passes an actual tab\n^git log --format=\"%H\\t%an\"\n\n# Also good — explicit separator, useful with interpolation\nlet tab = (char tab)\n^git log --format=

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

%H($tab)%an'\n\n# Also good — use a real newline delimiter, then parse with lines/parse\nlet nl = (char nl)\n^some-tool --format=

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

field1=%a($nl)field2=%b'\n| lines\n| parse '{key}={value}'\n```\n\n## Code and Data Across Interpreter Boundaries\n\nWhen Nu invokes Node, a browser evaluator, or another shell, identify which\nparts are program text and which are data. Separate argv prevents the host\nshell from interpreting a value, but an `eval`/`-c` recipient still interprets\nits code argument. JSON serialization is not shell escaping.\n\nPrefer a fixed script with data passed as argv, stdin, or a file. Keep values\nstructured until that boundary; do not manually escape quotes or backslashes.\nFor example, an existing Node helper can read `JSON.parse(process.argv[2])`:\n\n```nu\nlet values = ['deck (draft).pptx' 'wasm' 8]\nlet payload = ($values | to json --raw)\n^node probe.mjs $payload\n```\n\nWhen an API only accepts JavaScript source, keep the function/template fixed\nand serialize the supported JSON values. Plain literal fragments make the\nparentheses unambiguous without nested Nu escape rules:\n\n```nu\nlet values = ['deck (draft).pptx' 'wasm' 8]\nlet payload = ($values | to json --raw)\nlet expression = (['window.renderProbe(...' $payload ')'] | str join)\n# window.renderProbe(...[\"deck (draft).pptx\",\"wasm\",8])\n```\n\nThe template is trusted; strings, numbers and booleans enter only through the\nJSON array. Do not insert raw data or a user-chosen function name into source.\nThis recipe is for direct JavaScript evaluation, not HTML, shell, SQL, or\narbitrary Nu values such as closures. Use the receiving language's data API\nwhen its serialization or embedding rules differ.\n\nFor larger JS programs, keep the program in an `.mjs` file and use the tool's\nfile/stdin mechanism; pass dynamic data separately where the API allows it.\nChanging Nu's quote style does not make raw JavaScript interpolation safe.\n\n### Verify Meaning as Well as Syntax\n\n`nu-check` accepts `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

(1 + 1) items'`, because evaluating `1 + 1` is valid Nu.\nAn exact-output assertion catches the unintended result. Test values with\nquotes, parentheses, backslashes, newlines, Unicode, empty strings and leading\nzeros; for generated JavaScript, run a small controlled consumer and compare\nthe decoded values, rather than only checking that Nu parsed the outer script.\n\n```nu\nuse std/assert\nlet count = 3\nlet label = ([\"Done \" ($count | into string) ' file(s)'] | str join)\nassert equal $label 'Done 3 file(s)'\nassert equal $\"Done ($count) file\\(s)\" $label\n```\n\nExecutable coverage is in `tests/strings-and-validation-smoke.nu`. Use English\nofficial documentation and the target Nu runtime to resolve syntax questions:\n[Working with Strings](https://www.nushell.sh/book/working_with_strings.html).\n\n## String Type Overview\n\n| Format | Syntax | Escapes | Interpolation | Literal `(` | Use case |\n| ------------------- | ----------- | ------------------ | ------------- | ----------- | ------------------------------------------- |\n| Single-quoted | `'...'` | None | No | Yes | Simple strings, Windows paths |\n| Double-quoted | `\"...\"` | `\\n \\t \\\" \\\\` etc. | No | Yes | Strings needing escape sequences |\n| Raw string | `r#'...'#` | None | No | Yes | Regex, strings with quotes, multi-line |\n| Bare word | `hello` | None | No | — | Command arguments, list items |\n| Backtick | `` `...` `` | None | No | Yes | Paths/args with spaces, globs |\n| Single-interpolated | `

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'` | None | Yes | **No** | Embedding variables (preferred default) |\n| Double-interpolated | `$\"...\"` | Yes | Yes | Yes (`\\(`) | Variables plus escapes **or** a literal `(` |\n\n## Examples\n\n### Array optimization\n\n```nu\n# Before\nlet dirs = [$root, \"node_modules\", \".pnpm\"]\nlet tools = [\"git\", \"patch\"]\n\n# After\nlet dirs = [$root node_modules .pnpm]\nlet tools = [git patch]\n```\n\n### Interpolation optimization\n\n```nu\n# Before\nprint $\"Package: ($pkg.name)\"\nprint $\"Error: ($tool) not found\"\n\n# After\nprint

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Package: ($pkg.name)'\nprint

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

Error: ($tool) not found'\n```\n\nDo **not** apply this rewrite when the string contains a literal `(` — the\ndouble-quoted form is required there and converting it breaks the string:\n\n```nu\n# Keep as-is — `\\(` has no equivalent in

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'\nprint $\"Removed ($n) file\\(s)\"\nprint $\"See [docs]\\(https://example.com)\"\n```\n\n### Keep double quotes for escapes or literal parens\n\n```nu\n# Keep — has \\n escape\nprint $\"\\nNext steps:\"\nlet content = \"line1\\nline2\"\n\n# Keep — contains literal single quote and interpolation\nlet marker = $\"'($name)@($ver)':\"\n\n# Keep — literal parentheses alongside interpolation;

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...' cannot do this\nlet usage = $\"deploy \\(env) [--force] — ($count) target\\(s)\"\n```\n\n### Regex with raw strings\n\n```nu\n# Before\nlet pattern = \"(?:a/|b/)?\"\n\n# After\nlet pattern = r#'(?:a/|b/)?'#\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":13365,"content_sha256":"60cc11457066717c5e2ea0c2bebd62917d49d61adfddcf73dc7cb8d34816f24b"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Nushell Pro","type":"text"}]},{"type":"paragraph","content":[{"text":"Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Operating Workflow","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read the target ","type":"text"},{"text":".nu","type":"text","marks":[{"type":"code_inline"}]},{"text":" files, nearby tests, ","type":"text"},{"text":"AGENTS.md","type":"text","marks":[{"type":"code_inline"}]},{"text":", and existing project conventions before changing code.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the compatibility target. Prefer the official English docs; translations can lag. When local docs/source checkouts are available, inspect them and record their revision separately from the installed Nu version. From any shell, prefer:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"console"},"content":[{"text":"nu --version","type":"text"}]},{"type":"paragraph","content":[{"text":"When already inside Nushell or when structured version data is needed, use:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"nu"},"content":[{"text":"version | get version","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Load the smallest relevant reference set:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Task","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reference","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Nu 0.116 migration, completions, flag spreading, cleanup","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Nu 0.116 Migration","type":"text","marks":[{"type":"link","attrs":{"href":"references/nu-0.116-migration.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Nu 0.115 migration, YAML, CLI args, command changes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Nu 0.115 Migration","type":"text","marks":[{"type":"link","attrs":{"href":"references/nu-0.115-migration.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Nu 0.114 migration and version compatibility","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Nu 0.114 Migration","type":"text","marks":[{"type":"link","attrs":{"href":"references/nu-0.114-migration.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Strings, regex/globs, generated JS/JSON","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"String Formats","type":"text","marks":[{"type":"link","attrs":{"href":"references/string-formats.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Security, paths, credentials, destructive operations","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Security","type":"text","marks":[{"type":"link","attrs":{"href":"references/security.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Script/code review","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Script Review","type":"text","marks":[{"type":"link","attrs":{"href":"references/script-review.md","title":null}}]},{"text":" and ","type":"text"},{"text":"Anti-Patterns","type":"text","marks":[{"type":"link","attrs":{"href":"references/anti-patterns.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Bash/POSIX conversion","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Bash to Nushell","type":"text","marks":[{"type":"link","attrs":{"href":"references/bash-to-nushell.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Modules, exports, scripts, tests","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Modules & Scripts","type":"text","marks":[{"type":"link","attrs":{"href":"references/modules-and-scripts.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Daemons, background jobs, E2E smoke tests","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Daemon & E2E Smoke Tests","type":"text","marks":[{"type":"link","attrs":{"href":"references/daemon-and-e2e-smoke-tests.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Types, records, lists, conversions","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Data & Type System","type":"text","marks":[{"type":"link","attrs":{"href":"references/data-and-types.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Streaming, closures, performance, diagnostics","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Advanced Patterns","type":"text","marks":[{"type":"link","attrs":{"href":"references/advanced-patterns.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Large columnar data","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Dataframes","type":"text","marks":[{"type":"link","attrs":{"href":"references/dataframes.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Common mistakes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Anti-Patterns","type":"text","marks":[{"type":"link","attrs":{"href":"references/anti-patterns.md","title":null}}]}]}]}]}]},{"type":"paragraph","content":[{"text":"Only the three newest migration guides are listed here. For earlier versions, consult the ","type":"text"},{"text":"migration archive","type":"text","marks":[{"type":"link","attrs":{"href":"references/archive/README.md","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Apply the cross-cutting guardrails below before style or performance cleanup.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Before a generated script's first real run, use ","type":"text"},{"text":"nu-check --debug","type":"text","marks":[{"type":"code_inline"}]},{"text":" in the intended scope. Assert exact output for tricky strings/serialized payloads, then run the relevant tests; parsing alone cannot detect unintended but valid interpolation. Use ","type":"text"},{"text":"--ide-check","type":"text","marks":[{"type":"code_inline"}]},{"text":" when structured diagnostics are needed.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Report security/correctness findings before style and performance notes.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"If a referenced file is unavailable, say so and continue with this file rather than inventing its contents.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Cross-Cutting Guardrails","type":"text"}]},{"type":"paragraph","content":[{"text":"These rules apply across task types. Load the routed reference before relying on syntax or behavior that changed between Nushell releases.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Types and pipeline contracts","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Declare pipeline input in the I/O signature rather than as a positional parameter. Capture ","type":"text"},{"text":"$in","type":"text","marks":[{"type":"code_inline"}]},{"text":" once when it must be reused because streams can be single-pass.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Type exported command parameters and input/output signatures.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Treat external/config records as untrusted; use optional access such as ","type":"text"},{"text":"$record.field?","type":"text","marks":[{"type":"code_inline"}]},{"text":" and validate the resulting type/value.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Remember that ","type":"text"},{"text":"default","type":"text","marks":[{"type":"code_inline"}]},{"text":" evaluates its fallback argument eagerly. Make the fallback null-safe or branch explicitly when it can fail or is expensive.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Keep return types consistent. Avoid ","type":"text"},{"text":"any","type":"text","marks":[{"type":"code_inline"}]},{"text":" unless the function is genuinely polymorphic.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"external_arg","type":"text","marks":[{"type":"code_inline"}]},{"text":" on ","type":"text"},{"text":"main","type":"text","marks":[{"type":"code_inline"}]},{"text":" parameters only when the CLI must preserve the caller's token spelling instead of applying Nushell literal coercion. It produces ","type":"text"},{"text":"glob","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"list\u003cglob>","type":"text","marks":[{"type":"code_inline"}]},{"text":" values in Nu 0.115, so validate or convert them before treating them as structured application data.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer ","type":"text"},{"text":"match","type":"text","marks":[{"type":"code_inline"}]},{"text":" for several branches on one value; use ","type":"text"},{"text":"if","type":"text","marks":[{"type":"code_inline"}]},{"text":" for one-off boolean predicates.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"On Nu 0.116, record spreads pass named flags to internal/custom commands. Null omits flags unless their type accepts ","type":"text"},{"text":"nothing","type":"text","marks":[{"type":"code_inline"}]},{"text":"; switch false/null also omits the flag. Validate forwarded keys and preserve default/null intent.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"External commands and errors","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pass external arguments as separate values, never as an interpolated command string. Use ","type":"text"},{"text":"complete","type":"text","marks":[{"type":"code_inline"}]},{"text":" and check ","type":"text"},{"text":"exit_code","type":"text","marks":[{"type":"code_inline"}]},{"text":" when status matters.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer ","type":"text"},{"text":"try/catch","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"$err.details","type":"text","marks":[{"type":"code_inline"}]},{"text":" for structured in-process errors.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"On Nu 0.115, a spanned ","type":"text"},{"text":"error make","type":"text","marks":[{"type":"code_inline"}]},{"text":" label has the shape ","type":"text"},{"text":"{text: ..., span: {start: ..., end: ...}}","type":"text","marks":[{"type":"code_inline"}]},{"text":". Do not use the obsolete flat ","type":"text"},{"text":"{text, start, end}","type":"text","marks":[{"type":"code_inline"}]},{"text":" form.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"On Nu 0.115.0, a ","type":"text"},{"text":"try/finally","type":"text","marks":[{"type":"code_inline"}]},{"text":" nested directly inside an outer ","type":"text"},{"text":"try","type":"text","marks":[{"type":"code_inline"}]},{"text":" whose handler is ","type":"text"},{"text":"catch","type":"text","marks":[{"type":"code_inline"}]},{"text":" silently skips the inner ","type":"text"},{"text":"finally","type":"text","marks":[{"type":"code_inline"}]},{"text":". Give the outer block a ","type":"text"},{"text":"finally","type":"text","marks":[{"type":"code_inline"}]},{"text":", or put the inner one behind a ","type":"text"},{"text":"do","type":"text","marks":[{"type":"code_inline"}]},{"text":"/command boundary, and assert that the owned state is gone. This defect is fixed in 0.116; keep the workaround only when the supported versions need it.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Treat rendered nested-Nu diagnostics as presentation text, not a stable protocol. For CLI tests, normalize ANSI styling, gutters, and PTY wrapping before matching a long, domain-specific phrase.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Security stop checkpoint","type":"text"}]},{"type":"paragraph","content":[{"text":"Before approving code that executes commands, deletes files, reads credentials, or accepts paths/patterns, confirm these boundaries:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never pass untrusted strings to ","type":"text"},{"text":"nu -c","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"source","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"run","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"^sh -c","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"^bash -c","type":"text","marks":[{"type":"code_inline"}]},{"text":", or ","type":"text"},{"text":"^cmd.exe /C","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pass external command arguments as separate values, not an interpolated shell command string.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For existing paths restricted to a base directory, expand both paths and use ","type":"text"},{"text":"path relative-to","type":"text","marks":[{"type":"code_inline"}]},{"text":" to prove containment. A string ","type":"text"},{"text":"starts-with","type":"text","marks":[{"type":"code_inline"}]},{"text":" check is unsafe because ","type":"text"},{"text":"/safe/base2","type":"text","marks":[{"type":"code_inline"}]},{"text":" starts with ","type":"text"},{"text":"/safe/base","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer Nu's built-in ","type":"text"},{"text":"mktemp","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"mktemp --directory","type":"text","marks":[{"type":"code_inline"}]},{"text":"; it is portable and returns a path directly. Do not use predictable temp names.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Scope secrets with ","type":"text"},{"text":"with-env","type":"text","marks":[{"type":"code_inline"}]},{"text":"; do not log them or pass them in argv when a stdin/config-file mechanism exists.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Guard destructive paths against root, ","type":"text"},{"text":"$nu.home-dir","type":"text","marks":[{"type":"code_inline"}]},{"text":", unexpected types, and untrusted globs. Consider TOCTOU and partial-success behavior.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"For output paths that do not exist yet, validate the existing parent directory with the same containment rule, then join only a validated leaf name.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Strings and formatting","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer simple literals and raw regex strings; use double quotes only when actual escapes are required.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Remember that ","type":"text"},{"text":"

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'","type":"text","marks":[{"type":"code_inline"}]},{"text":" interpolates but does not process escape sequences.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"A literal ","type":"text","marks":[{"type":"strong"}]},{"text":"(","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" directly inside interpolation requires ","type":"text","marks":[{"type":"strong"}]},{"text":"$\"...\"","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":".","type":"text","marks":[{"type":"strong"}]},{"text":" Since ","type":"text"},{"text":"

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

...'","type":"text","marks":[{"type":"code_inline"}]},{"text":" does no escape processing, every ","type":"text"},{"text":"(","type":"text","marks":[{"type":"code_inline"}]},{"text":" inside it opens an interpolation expression and ","type":"text"},{"text":"\\(","type":"text","marks":[{"type":"code_inline"}]},{"text":" cannot prevent that. Write ","type":"text"},{"text":"$\"\\(abc)($var)\"","type":"text","marks":[{"type":"code_inline"}]},{"text":", never ","type":"text"},{"text":"

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

\\(abc)($var)'","type":"text","marks":[{"type":"code_inline"}]},{"text":". The failure is often silent rather than an error: ","type":"text"},{"text":"

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…

(1 + 1) items'","type":"text","marks":[{"type":"code_inline"}]},{"text":" evaluates to ","type":"text"},{"text":"2 items","type":"text","marks":[{"type":"code_inline"}]},{"text":". See ","type":"text"},{"text":"String Formats","type":"text","marks":[{"type":"link","attrs":{"href":"references/string-formats.md","title":null}}]},{"text":". For generated code with many delimiters, join plain literal fragments and serialized data instead.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Keep code and data separate across interpreters: pass argv/stdin/JSON to a fixed program. If an API only accepts source, use a fixed, trusted template with target-language serialization; a single argv string is still code when the receiving tool evaluates it. Do not concatenate raw values into source.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use kebab-case for commands/flags, snake_case for variables/parameters, and SCREAMING_SNAKE_CASE for environment variables.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Data formats and grouping","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"from xml","type":"text","marks":[{"type":"code_inline"}]},{"text":" loses XML namespace identity (reproduced on Nu 0.115.1): it stores only local names for elements and attributes, with no namespace URI or declaration in the result. For example, ","type":"text"},{"text":"\u003cx xmlns:r=\"urn:r\" id=\"plain\" r:id=\"rel\"/>","type":"text","marks":[{"type":"code_inline"}]},{"text":" becomes an ","type":"text"},{"text":"attributes","type":"text","marks":[{"type":"code_inline"}]},{"text":" record containing only ","type":"text"},{"text":"id: rel","type":"text","marks":[{"type":"code_inline"}]},{"text":"; reversing the two attributes leaves ","type":"text"},{"text":"id: plain","type":"text","marks":[{"type":"code_inline"}]},{"text":". Later attributes overwrite earlier ones when their local names collide. Do not use this record for namespace-sensitive XML lookups, edits, or round-trips. Use a namespace-aware XML parser or, when the ","type":"text"},{"text":"query xml","type":"text","marks":[{"type":"code_inline"}]},{"text":" plugin is available, XPath with ","type":"text"},{"text":"--namespaces","type":"text","marks":[{"type":"code_inline"}]},{"text":" (and ","type":"text"},{"text":"--output-names","type":"text","marks":[{"type":"code_inline"}]},{"text":" when names matter). Match namespace URI plus local name, not prefix spelling.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Nu 0.115 makes ","type":"text"},{"text":"from yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":" default to YAML 1.2 with strict non-string keys and tags. Pin ","type":"text"},{"text":"--spec","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"--multiple","type":"text","marks":[{"type":"code_inline"}]},{"text":", tag handling, and key resolution when the input contract is controlled by another system instead of inheriting defaults accidentally.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Let ","type":"text"},{"text":"to yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":" reject non-round-trippable values by default. Opt out only when that data loss is part of the documented contract, and quote the value: ","type":"text"},{"text":"--non-roundtrip 'null'","type":"text","marks":[{"type":"code_inline"}]},{"text":" works across 0.115/0.116; bare ","type":"text"},{"text":"null","type":"text","marks":[{"type":"code_inline"}]},{"text":" works from 0.116. ","type":"text"},{"text":"--non-roundtrip 'lossy'","type":"text","marks":[{"type":"code_inline"}]},{"text":" alone is still rejected on 0.116.0, so use ","type":"text"},{"text":"--serialize","type":"text","marks":[{"type":"code_inline"}]},{"text":" when a lossy encoding is genuinely wanted. On 0.116 replace ","type":"text"},{"text":"--compact-list-indent","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"--list-indent compact|indented","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"group-by","type":"text","marks":[{"type":"code_inline"}]},{"text":" record output cannot represent a null key and omits that group in Nu 0.115. Use ","type":"text"},{"text":"group-by --to-table","type":"text","marks":[{"type":"code_inline"}]},{"text":" when null groups must be retained or distinguished from empty strings.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Data flow and performance","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer pipelines and immutable ","type":"text"},{"text":"let","type":"text","marks":[{"type":"code_inline"}]},{"text":" bindings.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"where","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"select","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"update","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"insert","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"items","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"transpose","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"reduce","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"enumerate","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead of manual parsing and mutable accumulation.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not capture ","type":"text"},{"text":"mut","type":"text","marks":[{"type":"code_inline"}]},{"text":" variables in closures.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"for","type":"text","marks":[{"type":"code_inline"}]},{"text":" is appropriate for sequential side effects but is not a transforming expression; use ","type":"text"},{"text":"each","type":"text","marks":[{"type":"code_inline"}]},{"text":" when a list result is required.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"par-each","type":"text","marks":[{"type":"code_inline"}]},{"text":" only when concurrency is safe and beneficial; preserve ","type":"text"},{"text":"each","type":"text","marks":[{"type":"code_inline"}]},{"text":" when order or sequential side effects matter.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Add ","type":"text"},{"text":"lines","type":"text","marks":[{"type":"code_inline"}]},{"text":" before ","type":"text"},{"text":"parse","type":"text","marks":[{"type":"code_inline"}]},{"text":" when line-by-line stream parsing is intended.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"On Nu 0.116, streamed errors reach ","type":"text"},{"text":"length","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"columns","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"is-empty","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"each while","type":"text","marks":[{"type":"code_inline"}]},{"text":"; consume streams in tests and fix upstream failures. Put flags before predicates in row-condition commands such as ","type":"text"},{"text":"take until","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer direct row conditions for simple ","type":"text"},{"text":"any","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"all","type":"text","marks":[{"type":"code_inline"}]},{"text":" predicates on Nu 0.115; retain closures when the predicate needs setup, destructuring, or reuse.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use native tables for small interactive data and Polars for large columnar group-by/join/aggregation workloads.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Modules and scripts","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Export only the intended API; keep helpers private.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"export def main","type":"text","marks":[{"type":"code_inline"}]},{"text":" when the command should match the module name.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"def --env","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"export def --env","type":"text","marks":[{"type":"code_inline"}]},{"text":" for caller-visible environment changes.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not name commands, aliases, modules, or exports after parser keywords. Also treat ","type":"text"},{"text":"$ans","type":"text","marks":[{"type":"code_inline"}]},{"text":" as reserved in Nu 0.115; rename older ","type":"text"},{"text":"ans","type":"text","marks":[{"type":"code_inline"}]},{"text":" bindings.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"source","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"use","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"run","type":"text","marks":[{"type":"code_inline"}]},{"text":" targets must be trusted and available at parse time.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Test at the correct seam: direct functions for stable structured errors, CLI subprocesses for argument parsing/process boundaries, and both when needed.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For Nu 0.116 completers, use named ","type":"text"},{"text":"token","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"place","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"buffer","type":"text","marks":[{"type":"code_inline"}]},{"text":" inputs and ","type":"text"},{"text":"place.command","type":"text","marks":[{"type":"code_inline"}]},{"text":" for shell words. Test via ","type":"text"},{"text":"commandline complete","type":"text","marks":[{"type":"code_inline"}]},{"text":"; load the 0.116 migration guide for output/fallback rules and ","type":"text"},{"text":"@interactive","type":"text","marks":[{"type":"code_inline"}]},{"text":" ownership.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Review Order","type":"text"}]},{"type":"paragraph","content":[{"text":"When reviewing code, report findings in this order:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Security: injection, traversal, credentials, destructive operations, temp files, environment poisoning.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Correctness: types, null handling, parse-time constraints, exit codes, cleanup/rollback, data-format contracts, platform/version behavior, stable tests.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Maintainability: naming, module boundaries, duplication, documentation.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Performance: streaming, unnecessary collection, safe parallelism, Polars.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Skip issues already enforced by the project's formatter/linter unless the tool output shows they are currently failing.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Validation","type":"text"}]},{"type":"paragraph","content":[{"text":"For ordinary script/module validation, prefer ","type":"text"},{"text":"nu-check --debug","type":"text","marks":[{"type":"code_inline"}]},{"text":". It parses without running the target's top-level commands and reports parse-time type errors as well as syntax errors. Use the matching parse mode:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"nu --no-config-file -c 'nu-check --debug path/to/script.nu'\nnu --no-config-file -c 'nu-check --debug --as-module path/to/module.nu'\nnu --no-config-file path/to/test-script.nu","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Without ","type":"text"},{"text":"--debug","type":"text","marks":[{"type":"code_inline"}]},{"text":", invalid content returns ","type":"text"},{"text":"false","type":"text","marks":[{"type":"code_inline"}]},{"text":" and the surrounding ","type":"text"},{"text":"nu","type":"text","marks":[{"type":"code_inline"}]},{"text":" process can still exit ","type":"text"},{"text":"0","type":"text","marks":[{"type":"code_inline"}]},{"text":"; consume the boolean explicitly. With ","type":"text"},{"text":"--debug","type":"text","marks":[{"type":"code_inline"}]},{"text":", a parse error throws, so an uncaught failure exits nonzero. Missing files are errors. Neither mode proves runtime correctness or provides a sandbox.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pass the file path directly when checking a saved script with relative imports. Piping its text to ","type":"text"},{"text":"nu-check","type":"text","marks":[{"type":"code_inline"}]},{"text":" checks an anonymous source; the optional path does not restore the file's import context when pipeline input is present.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For editor/CI integrations needing spans and diagnostic records, use ","type":"text"},{"text":"nu --no-config-file --ide-check 100 path/to/script.nu","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--ide-check","type":"text","marks":[{"type":"code_inline"}]},{"text":" emits JSON Lines on stdout and may still exit with code ","type":"text"},{"text":"0","type":"text","marks":[{"type":"code_inline"}]},{"text":" when a record has ","type":"text"},{"text":"type: \"diagnostic\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"severity: \"Error\"","type":"text","marks":[{"type":"code_inline"}]},{"text":". It also exits ","type":"text"},{"text":"0","type":"text","marks":[{"type":"code_inline"}]},{"text":" with empty output for missing files on older versions; 0.116 fixes that case with a nonzero exit. Verify the path for cross-version checks. Parse every non-empty line with ","type":"text"},{"text":"from json","type":"text","marks":[{"type":"code_inline"}]},{"text":"; do not use the process exit code alone. Treat ","type":"text"},{"text":"severity: \"Error\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" diagnostics as blockers, surface other severities such as ","type":"text"},{"text":"Warning","type":"text","marks":[{"type":"code_inline"}]},{"text":" without blocking, and ignore ","type":"text"},{"text":"type: \"hint\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" records. Handle a non-zero exit code or stderr separately as a CLI startup or I/O failure.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer ","type":"text"},{"text":"--no-config-file","type":"text","marks":[{"type":"code_inline"}]},{"text":" for reproducible standalone checks. Omit it when the script intentionally depends on commands or environment from user configuration, and document that dependency.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"source","type":"text","marks":[{"type":"code_inline"}]},{"text":" executes top-level code; it is not a syntax check. Use it only when intentionally loading audited definitions/setup. Validate side-effecting scripts with ","type":"text"},{"text":"nu-check","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"--ide-check","type":"text","marks":[{"type":"code_inline"}]},{"text":", then execute in an owned temp fixture. When saving structured values, use a recognized data extension or serialize explicitly (","type":"text"},{"text":"to json","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"to yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"to nuon","type":"text","marks":[{"type":"code_inline"}]},{"text":", and so on) before ","type":"text"},{"text":"save","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not use ","type":"text"},{"text":"nu --testbin","type":"text","marks":[{"type":"code_inline"}]},{"text":" in Nu 0.115+. Recreate the required behavior with Nushell itself or a purpose-built test fixture.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reproduce terminal-sensitive tests under a narrow PTY when diagnostics or tables are involved, for example ","type":"text"},{"text":"stty cols 24 && nu tests/example.nu","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check diffs for debug markers and accidental changes before finishing.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If validation fails, fix the smallest reproducible issue and rerun the exact failing command before broadening the test suite.","type":"text"}]}]}]}]},"metadata":{"date":"2026-11-02","name":"nushell-pro","author":"@skillopedia","source":{"stars":25,"repo_name":"nushell-pro","origin_url":"https://github.com/hustcer/nushell-pro/blob/HEAD/SKILL.md","repo_owner":"hustcer","body_sha256":"c1b6c4dc6e474bf4ebe1a9beb77631a27b7eeba0c37ce39ee68d3419ced95feb","cluster_key":"130bdf723e566db71f0a033a1ede9bd9328356b5c26472eeeff616555103ac80","clean_bundle":{"format":"clean-skill-bundle-v1","source":"hustcer/nushell-pro/SKILL.md","text_bytes":235490,"source_commit":"9873936534b579be43886787689c75f078e24249","skills_sh_rank":7336,"attachment_count":16,"text_attachments":16,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"software-engineering","category_label":"Engineering"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"software-engineering","import_tag":"skills-sh-top20k-2026-11","description":"Comprehensive Nushell scripting best practices, idioms, security, and evidence-driven code review. Use when writing, reviewing, auditing, debugging, or refactoring Nushell (.nu) scripts, modules, custom commands, pipelines, config, and tests. Also use for Bash/POSIX-to-Nushell conversion and Nu migration issues including custom completions, record-spread flags, YAML, SemVer, cleanup, and subprocess diagnostics.\n"}},"renderedAt":1793665984512}

Nushell Pro Write secure, idiomatic, portable, and testable Nushell. Keep the main workflow in this file; load detailed references only when the task needs them. Operating Workflow 1. Identify the task: new script, debugging, review, refactor, module design, migration, performance work, security audit, or Bash conversion. 2. Read the target files, nearby tests, , and existing project conventions before changing code. 3. Establish both the project's supported Nu range (from CI, documentation, or project configuration) and the active local version. Do not assume the local binary defines the com…