From 04cf954d2dc998a620836fabea7ff400a816ebfd Mon Sep 17 00:00:00 2001 From: Tim 'Piepmatz' Hesse Date: Sat, 4 Jul 2026 22:19:56 +0200 Subject: [PATCH 1/9] Release notes for `v0.114.0` Please add your new features and breaking changes to the release notes by opening PRs against the `release-notes-v0.114.0` branch. ## TODO - [ ] PRs that need to land before the release, e.g. [deprecations] or [removals] - [ ] add the full changelog - [ ] categorize each PR - [ ] write all the sections and complete all the `TODO`s [deprecations]: https://github.com/nushell/nushell/labels/deprecation [removals]: https://github.com/nushell/nushell/pulls?q=is%3Apr+is%3Aopen+label%3Aremoval-after-deprecation --- blog/2026-07-04-nushell_v0_114_0.md | 2071 +++++++++++++++++++++++++++ 1 file changed, 2071 insertions(+) create mode 100644 blog/2026-07-04-nushell_v0_114_0.md diff --git a/blog/2026-07-04-nushell_v0_114_0.md b/blog/2026-07-04-nushell_v0_114_0.md new file mode 100644 index 00000000000..d8fe08fc0e8 --- /dev/null +++ b/blog/2026-07-04-nushell_v0_114_0.md @@ -0,0 +1,2071 @@ +--- +title: Nushell 0.114.0 +author: The Nu Authors +author_site: https://www.nushell.sh/blog +author_image: https://www.nushell.sh/blog/images/nu_logo.png +excerpt: Today, we're releasing version 0.114.0 of Nu. This release adds... +--- + + + + + +# Nushell 0.114.0 + + + +Today, we're releasing version 0.114.0 of Nu. This release adds... + +# Where to get it + +Nu 0.114.0 is available as [pre-built binaries](https://github.com/nushell/nushell/releases/tag/0.114.0) or from [crates.io](https://crates.io/crates/nu). If you have Rust installed you can install it using `cargo install nu`. + +As part of this release, we also publish a set of optional [plugins](https://www.nushell.sh/book/plugins.html) you can install and use with Nushell. + +# Table of contents + + + +# Highlights and themes of this release + + + + +# Changes + +## Breaking changes + +### Submodules are no longer implicitly imported + +Importing a module no longer implicitly imports its exported sub-modules. + +```nushell +export module foo { + export def bar [] {} + + export module sub { + export def baz [] {} + } +} + +use foo +foo bar # valid +foo sub baz # invalid, `sub` is not implicitly imported +``` + +To have your modules keep the old behavior, you will need to export the modules contents explicitly: +```nushell +export module foo { + export def bar [] {} + + export module sub { + export def baz [] {} + } + export use sub +} + +use foo +foo bar # valid +foo sub baz # valid +``` +> [!NOTE] +> `use`ing a module inside another module does *not* run the imported module's `export-env` block. +> (See [`export-env` runs only when the use call is evaluated](https://www.nushell.sh/book/modules/creating_modules.html#export-env-runs-only-when-the-use-call-is-evaluated)) +> Thus, explicit `use`s as recommended above will not run `export-env` blocks, which is inline with how the previous implicit imports worked. + +### Type System Improvements + +This release comes with a handful of improvements to the type system, specifically the parse time type checking. But beware! Type annotations that previously seemed fine might raise errors, as they might have been passing only because typing information was insufficient to check it at parse time. + +#### Commands' output types are now inferred based on pipeline input type. + +```nushell +let str = "foo" | str uppercase +let str_list = ["foo", "bar" ] | str uppercase + +scope variables +| where name starts-with '$str' +| select name type +``` + +
+ +```ansi:no-line-numbers title="Before" +^[[39m╭───┬───────────┬──────╮^[[0m +^[[39m│^[[0m ^[[1;32m#^[[0m ^[[39m│^[[0m ^[[1;32mname^[[0m ^[[39m│^[[0m ^[[1;32mtype^[[0m ^[[39m│^[[0m +^[[39m├───┼───────────┼──────┤^[[0m +^[[39m│^[[0m ^[[1;32m0^[[0m ^[[39m│^[[0m ^[[39m$str^[[0m ^[[39m│^[[0m ^[[39many^[[0m ^[[39m│^[[0m +^[[39m│^[[0m ^[[1;32m1^[[0m ^[[39m│^[[0m ^[[39m$str_list^[[0m ^[[39m│^[[0m ^[[39many^[[0m ^[[39m│^[[0m +^[[39m╰───┴───────────┴──────╯^[[0m +``` + + + +```ansi:no-line-numbers title="After" +^[[39m╭───┬───────────┬──────────────╮^[[0m +^[[39m│^[[0m ^[[1;32m#^[[0m ^[[39m│^[[0m ^[[1;32mname^[[0m ^[[39m│^[[0m ^[[1;32mtype^[[0m ^[[39m│^[[0m +^[[39m├───┼───────────┼──────────────┤^[[0m +^[[39m│^[[0m ^[[1;32m0^[[0m ^[[39m│^[[0m ^[[39m$str^[[0m ^[[39m│^[[0m ^[[39mstring^[[0m ^[[39m│^[[0m +^[[39m│^[[0m ^[[1;32m1^[[0m ^[[39m│^[[0m ^[[39m$str_list^[[0m ^[[39m│^[[0m ^[[39mlist^[[0m ^[[39m│^[[0m +^[[39m╰───┴───────────┴──────────────╯^[[0m +``` + +
+ +### Optional parameters' type now reflects the fact it might be `null` + +The (parse time) types of optional parameters and flags *without a default value* are now a union of their type `T` and `nothing` +- with no default value the type is `oneof` +- with a default value type is `T` + +Demonstrating with LSP inlay hints: +Before: + image +After: + image + image + +### More precise type widening + +More type information is preserved. e.g.: +```nushell +let list = [["a" "b" "c"] [1 2 3]] +let elem = $list.0 +``` +Previously `$elem`'s type would be `list>`, now `$elem`'s type is `oneof, list>`. + +### Operator type checking now accounts of `oneof` types + +nushell as language tries to strike a careful balance between correctness and convenience. +One should be able to trust nu's type system will help them write correct and robust scripts, but nu shouldn't shouldn't get in the way of a nice REPL experience. + +Because of that, some operations that might be invalid but aren't definitely so do not raise parse errors and such checks are delegated to runtime. + +For example `int`'s can only be added with `int`'s and `float`'s, yet the following code does not raise parse time error. + +```nushell +def foo [anything: any] { + 10 + $anything +} +``` + +But previously, despite being `oneof` being specific than `any`, would be rejected by the type checker for addition to `int`s. + +```nushell +def foo [maybe_int: oneof] { + 10 + $maybe_int +} +``` +```ansi:no-line-numbers +Error: ^[[31mnu::parser::operator_unsupported_type + +^[[39m ^[[31m×^[[39m The '+' operator does not work on values of type 'oneof'. + ╭─[^[[22;1;36;4mrepl_entry #2:2:8^[[22;39;24m] + ^[[22;2m1^[[22m │ def foo [maybe_int: oneof] { + ^[[22;2m2^[[22m │ 10 + $maybe_int + · ^[[22;1;35m ┬^[[33m ─────┬──── +^[[22;39m · ^[[22;1;35m│^[[22;39m ^[[22;1;33m╰── oneof +^[[22;39m · ^[[22;1;35m╰── does not support 'oneof' +^[[22;39m ^[[22;2m3^[[22m │ } + ╰────^[[m +``` + +Now, as long as the two operands have a possibility of being valid for an operation the parse time type checker will accept the expression, leaving errors to runtime type checking. + +### Promoted `enforce-runtime-annotations` to opt-out + +In Nushell 0.108.0, [`enforce-runtime-annotations`](https://www.nushell.sh/blog/2025-10-15-nushell_v0_108_0.html#enforce-assignment-type-annotations-at-runtime-16079-toc) experimental option was added. With this release `enforce-runtime-annotations` is promoted to being opt-out, meaning that by default this experimental option is enabled now. + +This option enforces type checking of let assignments at runtime, catching type errors that might have been missed in parse time type checking due to insufficient type information. + +If you experience any trouble with this, you can disable it via the command-line argument: +```nushell +nu --experimental-options '[enforce-runtime-annotations=false]' +``` +or via the environment variable: +```nushell +NU_EXPERIMENTAL_OPTIONS="enforce-runtime-annotations=false" nu +``` + +### Bump reedline to 4a5ebce + +- Vi mode gains a real **visual mode**: press `v` to start a selection, then use motions and `d`/`c`/`y`/`r` (including across multiple lines). +- In vi normal mode, the caret now rests *on* the last grapheme, and `h`/`l` move across line boundaries. +- Visual mode uses its own prompt indicator, defaulting to the vi-normal indicator. + +### Changes to spreadsheet commands: `from xlsx` & `from ods` + +This release comes with various improvements to `from xlsx` and `from ods` commands, and makes them behave as similarly as the file formats themselves allow. + +#### More accurate command signatures + +These commands' signatures previously incorrectly indicated that they return tables. +They return *records* that contain *tables*, with a table for each sheet in the file. + +Also, signature of the `from ods` command previously had `string` as its input type rather than `binary`. This has been fixed. + +#### Support for more data types + +Previously importing `datetime` values were supported only for `xlsx` files. + +This release not only adds that for `ods` files as well, but also broadens the `datetime` formats that can be imported. + +Aside from that, values that couldn't be properly coerced to nushell types were simply imported as `null`. Now those values are imported as `string` or `float` depending on the source data. + +#### Finer control over spreadsheet headers + +Last release changed `from xlsx` to use the first row of a sheet as the imported table's headers, and added a new flag, `--header-row`, to control which row should be used as the header or if any row should become the headers at all. + +This release removes it. + +Don't worry! Instead of the `--header-row` flag, `from xlsx` *and* `from ods` get two new flags that will provide finer control over how spreadsheets are imported: + +- `--noheaders`: What it says on the tin, identical to same flag on `from csv`, `from tsv` and `from ssv` commands. +- `--first-row`: This is where it gets a little interesting. `from xlsx` and `from ods` skip reading all leading empty rows. This is how it has worked for a long time. + `--first-row=0` allows you to turn that off and keep those empty lines. It can also be used to start reading from an arbitrary row: `--first-row $n` + +Let's see how this works with an example: +- The contents of the file, with all the conveniences like skipping empty rows or auto headers: + ```nushell + open example.xlsx --raw + | from xlsx --noheaders --first-row 0 + ``` + ```ansi + ╭────────┬───────────────────────────────────────────────╮ + │ │ ╭───┬─────────┬─────────┬─────────┬─────────╮ │ + │ ^[[22;1;32mSheet1^[[22;39m │ │ ^[[22;1;32m#^[[22;39m │ ^[[22;1;32mcolumn0^[[22;39m │ ^[[22;1;32mcolumn1^[[22;39m │ ^[[22;1;32mcolumn2^[[22;39m │ ^[[22;1;32mcolumn3^[[22;39m │ │ + │ │ ├───┼─────────┼─────────┼─────────┼─────────┤ │ + │ │ │ ^[[22;1;32m0^[[22;39m │ │ │ │ │ │ + │ │ │ ^[[22;1;32m1^[[22;39m │ │ │ │ │ │ + │ │ │ ^[[22;1;32m2^[[22;39m │ │ │ │ │ │ + │ │ │ ^[[22;1;32m3^[[22;39m │ a │ b │ c │ d │ │ + │ │ │ ^[[22;1;32m4^[[22;39m │ 0.00 │ 1.00 │ 2.00 │ 3.00 │ │ + │ │ │ ^[[22;1;32m5^[[22;39m │ 10.00 │ 11.00 │ 12.00 │ 13.00 │ │ + │ │ │ ^[[22;1;32m6^[[22;39m │ 20.00 │ 21.00 │ 22.00 │ 23.00 │ │ + │ │ │ ^[[22;1;32m7^[[22;39m │ 30.00 │ 31.00 │ 32.00 │ 33.00 │ │ + │ │ ╰───┴─────────┴─────────┴─────────┴─────────╯ │ + ╰────────┴───────────────────────────────────────────────╯ + ``` +- How it is imported by default: + ```nushell + open example.xlsx + # or `open example.xlsx --raw | from xlsx` + ``` + ```ansi + ╭────────┬───────────────────────────────────────╮ + │ │ ╭───┬───────┬───────┬───────┬───────╮ │ + │ ^[[22;1;32mSheet1^[[22;39m │ │ ^[[22;1;32m#^[[22;39m │ ^[[22;1;32ma^[[22;39m │ ^[[22;1;32mb^[[22;39m │ ^[[22;1;32mc^[[22;39m │ ^[[22;1;32md^[[22;39m │ │ + │ │ ├───┼───────┼───────┼───────┼───────┤ │ + │ │ │ ^[[22;1;32m0^[[22;39m │ 0.00 │ 1.00 │ 2.00 │ 3.00 │ │ + │ │ │ ^[[22;1;32m1^[[22;39m │ 10.00 │ 11.00 │ 12.00 │ 13.00 │ │ + │ │ │ ^[[22;1;32m2^[[22;39m │ 20.00 │ 21.00 │ 22.00 │ 23.00 │ │ + │ │ │ ^[[22;1;32m3^[[22;39m │ 30.00 │ 31.00 │ 32.00 │ 33.00 │ │ + │ │ ╰───┴───────┴───────┴───────┴───────╯ │ + ╰────────┴───────────────────────────────────────╯ + ``` + Most of the time, this is what you want. +- Or if your file doesn't have any headers and all the rows are data: + ```nushell + open example.xlsx --raw | from xlsx --noheaders + ``` + ```ansi + ╭────────┬───────────────────────────────────────────────╮ + │ │ ╭───┬─────────┬─────────┬─────────┬─────────╮ │ + │ ^[[22;1;32mSheet1^[[22;39m │ │ ^[[22;1;32m#^[[22;39m │ ^[[22;1;32mcolumn0^[[22;39m │ ^[[22;1;32mcolumn1^[[22;39m │ ^[[22;1;32mcolumn2^[[22;39m │ ^[[22;1;32mcolumn3^[[22;39m │ │ + │ │ ├───┼─────────┼─────────┼─────────┼─────────┤ │ + │ │ │ ^[[22;1;32m0^[[22;39m │ a │ b │ c │ d │ │ + │ │ │ ^[[22;1;32m1^[[22;39m │ 0.00 │ 1.00 │ 2.00 │ 3.00 │ │ + │ │ │ ^[[22;1;32m2^[[22;39m │ 10.00 │ 11.00 │ 12.00 │ 13.00 │ │ + │ │ │ ^[[22;1;32m3^[[22;39m │ 20.00 │ 21.00 │ 22.00 │ 23.00 │ │ + │ │ │ ^[[22;1;32m4^[[22;39m │ 30.00 │ 31.00 │ 32.00 │ 33.00 │ │ + │ │ ╰───┴─────────┴─────────┴─────────┴─────────╯ │ + ╰────────┴───────────────────────────────────────────────╯ + ``` + +### Type System Improvements + +#### Typed `$in` + +`$in` variable is now typed based on the pipeline input type of the surrounding expression. + + + + + + +
+ +```ansi:no-line-numbers title="Before" +Error: ^[[31mnu::shell::operator_incompatible_types^[[0m + + ^[[31m×^[[0m Types 'int' and 'string' are not compatible for the '+' operator. + ╭─[^[[36;1;4msource:1:5^[[0m] + ^[[2m1^[[0m │ 2 | $in + "foo" + · ^[[35;1m ─┬─^[[0m^[[33;1m ┬^[[0m^[[32;1m ──┬──^[[0m + · ^[[35;1m│^[[0m ^[[33;1m│^[[0m ^[[32;1m╰── ^[[32;1mstring^[[0m^[[0m + · ^[[35;1m│^[[0m ^[[33;1m╰── ^[[33;1mdoes not operate between 'int' and 'string'^[[0m^[[0m + · ^[[35;1m╰── ^[[35;1mint^[[0m^[[0m + ╰──── +``` + + + +```ansi:no-line-numbers title="After" +Error: ^[[31mnu::parser::operator_incompatible_types^[[0m + + ^[[31m×^[[0m Types 'int' and 'string' are not compatible for the '+' operator. + ╭─[^[[36;1;4msource:1:5^[[0m] + ^[[2m1^[[0m │ 2 | $in + "foo" + · ^[[35;1m ─┬─^[[0m^[[33;1m ┬^[[0m^[[32;1m ──┬──^[[0m + · ^[[35;1m│^[[0m ^[[33;1m│^[[0m ^[[32;1m╰── ^[[32;1mstring^[[0m^[[0m + · ^[[35;1m│^[[0m ^[[33;1m╰── ^[[33;1mdoes not operate between 'int' and 'string'^[[0m^[[0m + · ^[[35;1m╰── ^[[35;1mint^[[0m^[[0m + ╰──── +``` + +
+ + +#### Better type inference of `let` bindings + +`let` bindings in the middle of a pipeline, or at the end of it, are now properly typed based ont the pipeline input type. + +You can observe this using `scope variables`: +```nushell +42 | let x + +scope variables +| where name == '$x' +| select name type +``` +```ansi:no-line-numbers +^[[39m╭───┬──────┬──────╮^[[0m +^[[39m│^[[0m ^[[1;32m#^[[0m ^[[39m│^[[0m ^[[1;32mname^[[0m ^[[39m│^[[0m ^[[1;32mtype^[[0m ^[[39m│^[[0m +^[[39m├───┼──────┼──────┤^[[0m +^[[39m│^[[0m ^[[1;32m0^[[0m ^[[39m│^[[0m ^[[39m$x^[[0m ^[[39m│^[[0m ^[[39mint^[[0m ^[[39m│^[[0m +^[[39m╰───┴──────┴──────╯^[[0m +``` + +or with LSP type hints: +```ansi:no-line-numbers +42^[[3;90m ^[[23;39m|^[[3;90m ^[[23;35mlet^[[3;90m ^[[23;31mx^[[3;90m: int +``` + +This type information is of course passed through to the rest of the pipeline: +```nushell +def accepts-int []: int -> int {} + +"foo" | let x | accepts-int +``` +```ansi:no-line-numbers +Error: ^[[31mnu::shell::only_supports_this_input_type^[[0m + + ^[[31m×^[[0m Input type not supported. + ╭─[^[[36;1;4msource:3:9^[[0m] + ^[[2m2^[[0m │ + ^[[2m3^[[0m │ "foo" | let x | accepts-int + · ^[[35;1m ─┬─^[[0m^[[33;1m ─────┬─────^[[0m + · ^[[35;1m│^[[0m ^[[33;1m╰── ^[[33;1monly int input data is supported^[[0m^[[0m + · ^[[35;1m╰── ^[[35;1minput type: string^[[0m^[[0m + ╰──── +``` + + +#### Command `def`initions + +Type checking and inference inside `def` blocks are now affected by the commands input/output signatures. + +
+ +```nushell +def accepts-int []: int -> int {} + +def foo []: string -> any { + accepts-int +} +``` +```ansi:no-line-numbers +Error: ^[[31mnu::parser::input_type_mismatch^[[0m + + ^[[31m×^[[0m Command does not support string input. + ╭─[^[[36;1;4msource:4:2^[[0m] + ^[[2m3^[[0m │ def foo []: string -> any { + ^[[2m4^[[0m │ accepts-int + · ^[[35;1m ─────┬─────^[[0m + · ^[[35;1m╰── ^[[35;1mcommand doesn't support string input^[[0m^[[0m + ^[[2m5^[[0m │ } + ╰──── +``` + +
+ + +#### `if-else`, `match` + +Conditionals (`if`, `match`) are now properly typed: + + - output of the expression is a union of all possible output types (based on the branches) + ```nushell + if $x == $y { + 42 + } else { + "foo" + } + | let out # oneof + + match $x { + 1 => 1, + 2 => "foo", + _ => { {a: 1} } + } + | let out # oneof> + ``` + + - without a fallback (a final `else` branch for `if` chains, a wildcard pattern for `match` expressions) the output types of conditional expressions include `nothing` + ```nushell + if $x == $y { + 42 + } + | let out # oneof + + match $x { + 1 => 1, + 2 => "foo", + } + | let out # oneof + ``` + + - branch blocks get pipeline input type information + ```nushell + [1, 2, 3] + | if true { + let input # list + $input | into string + } else { + # pass through + } + | let out # oneof, list> + ``` + +### Errors with more `details` and file relative spans + +The error record you receive in `try {..} catch {..}` blocks no longer has a `json` field. A new field named `details` takes its place instead. It holds the same information as the `json` field but requires no deserialization stop like `from json`. + +There is one small but significant difference: the error labels now have additional field `location` in addition to `span`. +`location` contains the name of the file the label points to, with `start` and `end` offsets that are relative to the file itself. + + + + + + + + + + + + +
+ +`/tmp/example.nu` + +rendered
+ +```nushell +use std/assert + +export def wrong [] { + assert equal (2 + 2) 5 +} +``` + + + +```nushell +use /tmp/example.nu *; wrong +``` +![](https://gist.githubusercontent.com/Bahex/c56672ea1ca0791039495fa1d07df2b4/raw/2de7941390150eb75383a04a309699813c792a81/rendered-error.svg) + +
+ +```nushell +use /tmp/example.nu * +try { wrong } catch { get details } +``` +``` +╭────────┬─────────────────────────────────────────────────────────────────────╮ +│ msg │ These are not equal. │ +│ │ ╭───┬──────────┬────────────────────┬─────────────────────────────╮ │ +│ labels │ │ # │ text │ span │ location │ │ +│ │ ├───┼──────────┼────────────────────┼─────────────────────────────┤ │ +│ │ │ 0 │ left: 4 │ ╭───────┬────────╮ │ ╭───────┬─────────────────╮ │ │ +│ │ │ │ │ │ start │ 239936 │ │ │ file │ /tmp/example.nu │ │ │ +│ │ │ │ │ │ end │ 239943 │ │ │ start │ 52 │ │ │ +│ │ │ │ │ ╰───────┴────────╯ │ │ end │ 59 │ │ │ +│ │ │ │ │ │ ╰───────┴─────────────────╯ │ │ +│ │ │ 1 │ right: 5 │ ╭───────┬────────╮ │ ╭───────┬─────────────────╮ │ │ +│ │ │ │ │ │ start │ 239944 │ │ │ file │ /tmp/example.nu │ │ │ +│ │ │ │ │ │ end │ 239945 │ │ │ start │ 60 │ │ │ +│ │ │ │ │ ╰───────┴────────╯ │ │ end │ 61 │ │ │ +│ │ │ │ │ │ ╰───────┴─────────────────╯ │ │ +│ │ ╰───┴──────────┴────────────────────┴─────────────────────────────╯ │ +│ code │ │ +│ url │ │ +│ help │ │ +│ inner │ [list 0 items] │ +╰────────┴─────────────────────────────────────────────────────────────────────╯ +``` + +
+ +With these file relative spans, making external tools for reporting nushell errors should be much easier. + +### Removed deprecated `grid` record input support +Removed deprecated features of `grid` command. +```nushell +# before +{ name: test } | grid + +# after +[{ name: test }] | grid name +``` + +### Standard library improvements + +- Following general type system improvements, standard library commands now also have more accurate type annotations. + +- `std/iter find-index` returns `null` instead of `-1` when failing to find a suitable element + +#### `std/iter scan` now has the same signature as `reduce` + +> [!WARNING] +> This is a breaking change. + +Previously `iter scan` always needed an initial value to start off of (equivalent to `reduce`'s `--fold`), and to compensate for it a `--noinit` flag to remove that initial value from the output. + +Now it's signature is identical to `reduce`: + +```nushell +# Old +[1 2 3] | iter scan 0 {|x, y| $x + $y} +# New +[1 2 3] | iter scan --fold 0 {|x, y| $x + $y} +# => [0, 1, 3, 6] + +# Old +[1 2 3] | iter scan 0 --noinit {|x, y| $x + $y} +# New +[1 2 3] | iter scan {|x, y| $x + $y} +# => [1, 3, 6] +``` + +#### More streaming + +The following commands now stream: +- `std/iter`: + - `intersperse` + - `flat-map` +- `std-rfc/conversion` + - `into list` +- `std-rfc/tables` + - `select column-slices` + - `reject column-slices` + +These commands don't really "stream", in that they don't return a stream. Instead they now avoid eagerly collecting input streams: +- `std-rfc/conversions` + - `name-values`: strictly an internal change with possible performance benefits +- `std-rfc/iter` + - `only`: will at most consume 2 items from the input + +### Other breaking changes + +* We changed how "hits" are displayed by making them relative to the cwd in the hopes that it makes them more usable. It works this way for `idx find` and `idx search`. ([#18477](https://github.com/nushell/nushell/pull/18477)) + +## Additions + +### Introducing `polars map-batches` + +Provides a new polars command `polars map-batches` that provides the ability to map a custom Nushell closure over one or more dataframe columns. + +```nu + Return a constant series from a closure + > [[a b]; [1 4] [2 5] [3 6]] + | polars into-df + | polars map-batches --name out { |_cols| [10 20 30] } a + ╭───┬─────╮ + │ # │ out │ + ├───┼─────┤ + │ 0 │ 10 │ + │ 1 │ 20 │ + │ 2 │ 30 │ + ╰───┴─────╯ + + Double the values of column a via a Nushell closure + > [[a b]; [1 4] [2 5] [3 6]] + | polars into-df + | polars map-batches { |cols| $cols | first | polars get a | each { |v| $v * 2 } } a + ╭───┬───╮ + │ # │ a │ + ├───┼───┤ + │ 0 │ 2 │ + │ 1 │ 4 │ + │ 2 │ 6 │ + ╰───┴───╯ + + Sum two columns element-wise and rename the result + > [[a b]; [1 4] [2 5] [3 6]] + | polars into-df + | polars map-batches --name a_plus_b { |cols| + let a = $cols | get 0 | polars get a + let b = $cols | get 1 | polars get b + $a | zip $b | each { |pair| $pair.0 + $pair.1 } + } a b + ╭───┬──────────╮ + │ # │ a_plus_b │ + ├───┼──────────┤ + │ 0 │ 5 │ + │ 1 │ 7 │ + │ 2 │ 9 │ + ╰───┴──────────╯ +``` + +### Add `commandline complete` to invoke nushell completions + +Add `commandline complete` command. This can be used to obtain the suggestions that nushell itself would normally suggest. Some examples: +- `commandline complete` — return completions based on the current commandline contents (i.e. what would be suggested when pressing `Tab`) +- `'./a' | commandline complete --type directory` — return directory paths relative to the current directory that start with `a` +- `'%ls -' | commandline complete --detailed` — return all flags the builtin `ls` command accepts, including their descriptions. + +These completions can be used in custom command completions, for example to wrap a builtin command with additional options, or to obtain a list of suggested paths for further filtering or processing. + +### Add `--pretty` flag and align table columns in `to nuon` + +`to nuon` now aligns table columns when using `--indent`, `--tabs`, or the new `--pretty` (`-p`) flag, making output easier to read. `--pretty` is shorthand for `--indent 2`. + +```nushell +> [[name, age]; [Alice, 30], [Bob, 25]] | to nuon --pretty +[ + [name, age]; + [Alice, 30], + [Bob, 25] +] +``` + +### Added `run` command for using scripts in pipelines + +There's a new command named `run` that allows scripts to participate in a nushell pipeline with input and output. + +It keeps execution isolated so script artifacts do not leak into the parent scope. Script resolution remains aligned with existing behavior (cwd / NU_LIB_DIRS / explicit paths); PATH-based lookup is not included at this time. + +#### Script Forms Supported +##### Bare scripts (scripts without def main) +The script body is evaluated directly as a pipeline transform (using implicit or explicit `$in` as usual). +```nushell +❯ open run-script1.nu +str upcase +❯ "Hello Nushell!" | run run-script1.nu +HELLO NUSHELL! +``` + +##### Scripts with `def main` entry point +If a top-level `def main` is defined, run invokes that entrypoint. +```nushell +❯ open run-script2.nu +def main [] { + str length +} +❯ "Hello Nushell!" | run run-script2.nu +14 +``` +##### Scripts with full pipelines +```nushell +❯ "Hello Nushell!" | run run-script1.nu | str camel-case +helloNushell +❯ "Hello Nushell!" | run run-script2.nu | $in + 5 +19 +``` + +### Added POSIX-style `--` option parsing for Nushell commands + +Nushell now supports the POSIX `--` end-of-options delimiter for built-in commands, custom commands, and `def --wrapped` commands. + +When `--` appears in a command invocation, it stops all flag parsing. Any arguments after `--` are treated as positional operands, even if they start with `-` or `--`. The `--` token itself is consumed and does not appear in the command's arguments. + +```nushell +# Pass dash-prefixed values as positional args, not flags +def greet [--upper, name] { + if $upper { $name | str upcase } else { $name } +} +greet -- -Alice # -Alice is now a positional, not an unknown flag +# returns -Alice + +# Useful for passing flag-like values to rest parameters +def process [...args] { $args } +process -- --verbose -x foo # -- consumed; args = ["--verbose", "-x", "foo"] +# returns +# ╭───┬───────────╮ +# │ 0 │ --verbose │ +# │ 1 │ -x │ +# │ 2 │ foo │ +# ╰───┴───────────╯ + +# Works with def --wrapped too +def --wrapped my-git [...args] { print ...$args } +my-git commit -- -m "my message" # -- NOT consumed; -m passed as operand +# returns +# commit +# -- +# -m +# my message +``` + +For `extern`-declared known external commands, `--` continues to be passed through to the external binary unchanged (as those programs manage their own argument parsing). + +### Added stream and error controls to `ignore` +Update the `ignore` command with flags to control whether stderr, stdout, or both get consumed and if errors are show which update `$env.LAST_EXIT_CODE`. This should work on internal and external commands. + +Existing behavior is the default when no new flags are provided to avoid breaking changes. + +- Added `--stderr` (`-e`) to consume stderr while allowing stdout to pass through. +- Added `--stdout` (`-o`) to consume stdout while allowing stderr output through. +- Added `--show-errors` (`-x`) to show external/internal errors and set `$env.LAST_EXIT_CODE` (external exit code for external failures, `1` for internal failures). + +### Added `--context` support to `idx search` +Add context to your `idx search` queries by using `--context/-c` and a nushell range. The range has to be in the form of negative_number..positive_number where negative_number is the before lines to show and the positive_number is the after lines to show in the `with_context` record key. + +Screenshot 2026-06-08 at 10-15-31 + +### Allowed `idx search --context` to accept counts or ranges + +This change allows `idx search --context` to be supplied as either an `int` or as a `range`. +Screenshot 2026-06-08 at 13-38-25 + +### Introducing Polars bitwise commands + +Introducing polars bitwise commands: +- `polars math bitwise-and` - Perform an aggregation of bitwise ANDs over a column expression. +- `polars math bitwise-count-ones` - Compute the number of set bits for each element in an integer column expression. +- `polars math bitwise-count-zeros` - Compute the number of unset bits for each element in an integer column expression. +- `polars math bitwise-leading-ones` - Compute the number of leading set bits for each element in an integer column expression. +- `polars math bitwise-leading-zeros` - Compute the number of leading unset bits for each element in an integer column expression. +- `polars math bitwise-or` - Perform an aggregation of bitwise ORs over a column expression. +- `polars math bitwise-trailing-ones` - Compute the number of trailing set bits for each element in an integer column expression. +- `polars math bitwise-trailing-zeros` - Compute the number of trailing unset bits for each element in an integer column expression. +- `polars math bitwise-xor` - Perform an aggregation of bitwise XORs over a column expression. + +### Add non UTF-8 text support for `url encode`/`url decode` + +- `url encode` can now encode binary input too, adding support for encoding non UTF-8 texts + ```nushell + '£ rates' | encode iso-8859-1 | url encode + # => %A3%20rates + ``` +- `url decode` can now decode inputs that do not evaluate to UTF-8 texts by returning a binary value (only with the `--binary` switch) + ```nushell + '%A3%20rates' | url decode --binary | decode iso-8859-1 + # => £ rates + ``` + +### Support reedline menu input/output modes and list description position + +Reedline menus can now be configured with input_mode (diff, cursor_prefix, full_buffer) and output_mode (suggested_span, full_buffer, extend_to_end) to control what text the menu reads from the buffer and how an accepted suggestion is written back. +List menus additionally accept description_position (before / after). The existing only_buffer_difference flag still works and is treated as a shorthand (true = diff, false = cursor_prefix). + +### Added `random pass` for generating passwords + +New command `random pass` generates random passwords. +```nushell +❯ 0..10 | each {random pass} +╭────┬──────────────╮ +│ 0 │ o(3&BU2EkWv] │ +│ 1 │ 3~5y9PpXKH-| │ +│ 2 │ 7Wa6]`q-TR?- │ +│ 3 │ r|F=5d:.-BZp │ +│ 4 │ 7ZjM$xg?h^o3 │ +│ 5 │ CMg.4-NKxy^e │ +│ 6 │ b8&zy)ZhN}MV │ +│ 7 │ }Hc#>GpyK%@6 │ +│ 8 │ KvJOL4VoTgN] │ +│ 9 │ 7HN!%1A0Vm-X │ +│ 10 │ 4|`M&8x%wX@S │ +╰────┴──────────────╯ +``` +`random pass` supports these flags. +```nushell + -c, --chars : Length of the generated password (default 12) + -u, --no-uppercase: Exclude uppercase letters A-Z + -l, --no-lowercase: Exclude lowercase letters a-z + -n, --no-numbers: Exclude numbers 0-9 + -s, --no-symbols: Exclude symbols like !@#$% + --include-ambiguous: Include ambiguous characters O, 0, l, 1 + --include-similar: Include similar characters i, l, 1 + --require-each-type: Guarantee at least one char from each enabled character type +``` + +### Allow `is-terminal` to detect redirection + +The `is-terminal` command can detect redirection now. It now defaults to `--stdout`. + +Scenario | Current +-- | -- +is-terminal (in terminal)| true +is-terminal \| cmd | false  +is-terminal o> file | false  +let x = (is-terminal) | false  + +### Added `--right` to `split row` and `split column` +Both `split row` and `split column` now support `--right`, which modifies where splits are skipped, e.g.: + +```nushell +> 'some-package-1.0' | split row --number 2 +['some' 'package-1.0'] +> 'some-package-1.0' | split row --number 2 --right +['some-package' '1.0'] +``` + +### Added some commands for set operations and combinations +Added `union`, `intersect`, `difference`, `combinations`, and `permutations` as built-in filter commands. These provide immutable, functional list operations. + +#### Commands + +##### `union` + +Returns a deduplicated list of unique elements present in either the input list or the given list. + +```nushell +[1 2 3 4] | union [3 4 5 6] +# ╭─┬───╮ +# │0│ 1 │ +# │1│ 2 │ +# │2│ 3 │ +# │3│ 4 │ +# │4│ 5 │ +# │5│ 6 │ +# ╰─┴───╯ + +[{a:1} {a:2}] | union [{a:2} {a:3}] +# ╭─┬───────╮ +# │0│ a: 1 │ +# │1│ a: 2 │ +# │2│ a: 3 │ +# ╰─┴───────╯ +``` + +##### `intersect` + +Returns a deduplicated list of unique elements present in both the input list and the given list. + +```nushell +[1 2 3 4] | intersect [3 4 5 6] +# ╭─┬───╮ +# │0│ 3 │ +# │1│ 4 │ +# ╰─┴───╯ + +[1 2 3] | intersect [4 5 6] +# ╭──────────╮ +# │ empty │ +# ╰──────────╯ +``` + +##### `difference` + +Returns a deduplicated list of unique elements present in the input list but not in the given list. + +```nushell +[1 2 3 4] | difference [3 4 5 6] +# ╭─┬───╮ +# │0│ 1 │ +# │1│ 2 │ +# ╰─┴───╯ + +[{a:1} {a:2} {a:3}] | difference [{a:2} {a:4}] +# ╭─┬───────╮ +# │0│ a: 1 │ +# │1│ a: 3 │ +# ╰─┴───────╯ +``` + +##### `combinations` + +Generates all combinations of a given size k from the input list, streamed lazily via `ListStream`. If k > n, returns an empty list. + +```nushell +[1 2 3] | combinations 2 +# ╭─┬──────────╮ +# │0│ ╭─┬───╮ │ +# │ │ │0│ 1 │ │ +# │ │ │1│ 2 │ │ +# │ │ ╰─┴───╯ │ +# │1│ ╭─┬───╮ │ +# │ │ │0│ 1 │ │ +# │ │ │1│ 3 │ │ +# │ │ ╰─┴───╯ │ +# │2│ ╭─┬───╮ │ +# │ │ │0│ 2 │ │ +# │ │ │1│ 3 │ │ +# │ │ ╰─┴───╯ │ +# ╰─┴──────────╯ + +[1 2] | combinations 3 +# ╭──────────╮ +# │ empty │ +# ╰──────────╯ +``` + +##### `permutations` + +Generates all permutations of the input list using Heap's algorithm, streamed lazily via `ListStream`. + +```nushell +[1 2 3] | permutations +# ╭─┬──────────╮ +# │0│ ╭─┬───╮ │ +# │ │ │0│ 1 │ │ +# │ │ │1│ 2 │ │ +# │ │ │2│ 3 │ │ +# │ │ ╰─┴───╯ │ +# │1│ ╭─┬───╮ │ +# │ │ │0│ 2 │ │ +# │ │ │1│ 1 │ │ +# │ │ │2│ 3 │ │ +# │ │ ╰─┴───╯ │ +# │2│ ╭─┬───╮ │ +# │ │ │0│ 3 │ │ +# │ │ │1│ 1 │ │ +# │ │ │2│ 2 │ │ +# │ │ ╰─┴───╯ │ +# │3│ ╭─┬───╮ │ +# │ │ │0│ 1 │ │ +# │ │ │1│ 3 │ │ +# │ │ │2│ 2 │ │ +# │ │ ╰─┴───╯ │ +# │4│ ╭─┬───╮ │ +# │ │ │0│ 2 │ │ +# │ │ │1│ 3 │ │ +# │ │ │2│ 1 │ │ +# │ │ ╰─┴───╯ │ +# │5│ ╭─┬───╮ │ +# │ │ │0│ 3 │ │ +# │ │ │1│ 2 │ │ +# │ │ │2│ 1 │ │ +# │ │ ╰─┴───╯ │ +# ╰─┴──────────╯ + +[1 2] | permutations +# ╭─┬──────────╮ +# │0│ ╭─┬───╮ │ +# │ │ │0│ 1 │ │ +# │ │ │1│ 2 │ │ +# │ │ ╰─┴───╯ │ +# │1│ ╭─┬───╮ │ +# │ │ │0│ 2 │ │ +# │ │ │1│ 1 │ │ +# │ │ ╰─┴───╯ │ +# ╰─┴──────────╯ +``` + +### Add math cbrt builtin + +Added `math cbrt` (cube root) function. + + - - - # Nushell 0.114.0 - - -Today, we're releasing version 0.114.0 of Nu. This release adds... +Today, we're releasing version 0.114.0 of Nu. This release brings sharper type checking, POSIX-style `--` option parsing, the new `run` command for pipeline-friendly scripts, and nice built-in support for working with Semantic Versioning. # Where to get it @@ -28,41 +22,52 @@ As part of this release, we also publish a set of optional [plugins](https://www # Highlights and themes of this release -- type system improvements +## The type checker got new glasses + +[@Bahex](https://github.com/Bahex) continued tightening up Nushell's type system this release. Commands can now infer output types from pipeline input more precisely, `$in` gets typed from the surrounding pipeline, optional values carry their `nothing`-ness more honestly, and `let` bindings in pipelines no longer have to shrug and call everything `any`. + +This means some mistakes move from "surprise, runtime error" to "hey, maybe fix that before running it". Very rude of the parser, but also very helpful. + +There is also one important default change: `enforce-runtime-annotations` is now opt-out, so runtime assignment annotations get checked by default. + +Read the details in the first [type system entry](#type-system-improvements-1), the second [type system entry](#type-system-improvements), and the note about [`enforce-runtime-annotations`](#promoted-enforce-runtime-annotations-to-opt-out). + +## `--` means "no really, stop parsing flags" + +Thanks to [@fdncred](https://github.com/fdncred), Nushell commands now understand the classic POSIX `--` end-of-options delimiter. If you need to pass `-weird`, `--looks-like-a-flag`, or similar gremlins as plain positional values, you can now do that without trying to outsmart the parser. + +```nushell +def greet [--upper, name] { if $upper { $name | str uppercase } else { $name } } +greet -- -Alice +``` + +That `-Alice` is now just Alice having a dash day, not an accidental flag. + +Take a look at the main [`--` option parsing entry](#added-posix-style-option-parsing-for-nushell-commands) and the follow-up [`--wrapped` fix](#fixed-a-bug-in-option-parsing). + +## Scripts can join the pipeline with `run` - - by Bahex - - #type-system-improvements-1 - - #type-system-improvements - - #promoted-enforce-runtime-annotations-to-opt-out +The new `run` command from [@fdncred](https://github.com/fdncred) lets a Nushell script behave like a pipeline stage. Pipe data in, let the script transform it, and pipe the result onward. Tiny reusable pipeline workers, basically. -- `--` support +```nushell +"Hello Nushell!" | run shout.nu | str camel-case +``` + +Scripts can be simple bare pipeline transforms or define a `main` entry point, and they run in isolation so their local bits do not leak back into your session. - - by fdncred - - #added-posix-style-option-parsing-for-nushell-commands - - #fixed-a-bug-in-option-parsing +See more examples [here](#added-run-command-for-using-scripts-in-pipelines). -- `run` command +## SemVer is a real value now - - by fdncred - - #added-run-command-for-using-scripts-in-pipelines +Nushell now has semantic version support, also from [@fdncred](https://github.com/fdncred). You can parse SemVer strings, turn them into records, build them back up, sort them correctly, and bump versions without doing string surgery with safety scissors. -- semver commands - - by fdncred - - #added-semantic-version-parsing-and-commands +```nushell +'1.2.3-alpha.1' | into semver | semver bump release +``` - - +Read the full SemVer tour [here](#added-semantic-version-parsing-and-commands). # Changes