From 56773b83c2a3a1d79b44a98efb82c13a7107d69d Mon Sep 17 00:00:00 2001 From: Tim 'Piepmatz' Hesse Date: Sat, 15 Aug 2026 17:52:19 +0200 Subject: [PATCH 1/5] Release notes for `v0.115.0` Please add your new features and breaking changes to the release notes by opening PRs against the `release-notes-v0.115.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-08-15-nushell_v0_115_0.md | 1598 +++++++++++++++++++++++++++ 1 file changed, 1598 insertions(+) create mode 100644 blog/2026-08-15-nushell_v0_115_0.md diff --git a/blog/2026-08-15-nushell_v0_115_0.md b/blog/2026-08-15-nushell_v0_115_0.md new file mode 100644 index 00000000000..03a7f8f6696 --- /dev/null +++ b/blog/2026-08-15-nushell_v0_115_0.md @@ -0,0 +1,1598 @@ +--- +title: Nushell 0.115.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.115.0 of Nu. This release adds... +--- + + + + + +# Nushell 0.115.0 + + + +Today, we're releasing version 0.115.0 of Nu. This release adds... + +# Where to get it + +Nu 0.115.0 is available as [pre-built binaries](https://github.com/nushell/nushell/releases/tag/0.115.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 + +### YAML got a proper rework + +YAML has been a problem child for quite some time now. This release replaces the old `serde_yaml`-based implementation with a new one built on `serde-saphyr` and `granit-parser`. With that we now get clearer YAML behavior, better round-tripping, proper tag support, multi-document streams, anchors, aliases, merge keys, and a few new options for configuring how the output should look like. + +#### Breaking changes + +The big breaking change is that Nu no longer tries to parse YAML as an awkward mix of YAML 1.1 and YAML 1.2. We now default to YAML 1.2, which is usually the the less crazy choice. If your files or scripts relied on old YAML 1.1 scalar magic, pass `--spec 1.1`. + +```nushell +use std/assert + +# Default YAML 1.2 behavior: these stay strings. +assert equal ("yes" | from yaml) "yes" +assert equal ("off" | from yaml --spec 1.2) "off" + +# YAML 1.1 keeps the classic YAML oddities. +assert equal ("yes" | from yaml --spec 1.1) true +assert equal ("off" | from yaml --spec 1.1) false +``` + +This specifically breaks octal numbers. In the 1.1 spec, a value is interpreted as an octal number if it starts with the `0` digit. It is then parsed as an octal value. If the value is not valid octal, it is instead returned as a string. + +The 1.2 spec made this much more sensible by allowing leading zeros on regular numbers and requiring octal numbers to use the `0o` prefix. + +```nushell +use std/assert + +# YAML 1.1 uses a leading zero as an octal indicator. +assert equal ("0247" | from yaml --spec 1.1) 0o247 +assert equal ("0o247" | from yaml --spec 1.1) "0o247" + +# YAML 1.2 replaced that with the `0o` prefix. +assert equal ("0247" | from yaml --spec 1.2) 0247 +assert equal ("0o247" | from yaml --spec 1.2) 0o247 +``` + +Did you know that YAML 1.1 supports sexagesimal values like `190:20:30`? No, well in the 1.1 spec these as base-60 numbers that can be used to represent time but noone knows that, so 1.2 got rid of them. And as Nushell now defaults to 1.2 we interpret these as strings. + +```nushell +use std/assert + +assert equal ("190:20:30" | from yaml) "190:20:30" +assert equal ("190:20:30" | from yaml --spec 1.1) 685230 + +assert equal ("02472256" | from yaml) 2472256 +assert equal ("02472256" | from yaml --spec 1.1) 685230 +``` + +`from yaml` is stricter about mapping keys now, too. Nushell record keys are strings, so plain YAML keys that resolve to booleans, numbers, or null are rejected by default. If you want the old loose behavior, use `--key-resolution verbatim` and Nushell will keep the original key text. + +```nushell +'true: enabled' | from yaml +# Error: YAML key resolves to a boolean, but Nushell record keys are strings + +'true: enabled' | from yaml --key-resolution verbatim +# => {true: enabled} +``` + +Tags also mean tags now. Previously, tagged scalars weren't really handled properly, often they were just ignored and handled as plain strings but in YAML they describe the data, usually as some types. Now unknown tags error by default but you can use `--ignore-tags` to just ignore the tags and deal with them yourself. + +```nushell +'Key: !Sub ${AWS::StackName}' | from yaml +# Error: unknown YAML tag + +'Key: !Sub ${AWS::StackName}' | from yaml --ignore-tags +# => {Key: "${AWS::StackName}"} +``` + +`to yaml` is also more specific about values that cannot round-trip. By default it errors instead of quietly turning them into something else. If that is unwatned, choose the behavior explicitly with `--non-roundtrip null` or `--non-roundtrip lossy`. The `--serialize` flag is still available but will probably be deprecated in the future. + +```nushell +{|| $in } | to yaml +# Error: closures are not round-trippable through YAML + +{|| $in } | to yaml --non-roundtrip null +# => null + +{|| $in } | to yaml --serialize +# => !closure "{|| $in }" +``` + +One more relevant breaking change: generated YAML may not look exactly like it did before. The new serializer quotes strings more carefully, writes tags for Nushell-specific values, and lets you configure indentation. If you depended on a specific YAML output, you might need to check that. + +#### Tags and round-tripping + +Nushell now understands standard YAML tags like [`!!timestamp`](https://yaml.org/type/timestamp.html),[`!!binary`](https://yaml.org/type/binary.html), [`!!omap`](https://yaml.org/type/omap.html), [`!!pairs`](https://yaml.org/type/pairs.html), and [`!!set`](https://yaml.org/type/set.html). It also writes Nushell-specific local tags for values that YAML did not define globally, such as filesizes, durations, ranges, globs, and cell paths. + +```yaml +name: Cargo.toml +size: !filesize 17515 +modified: !!timestamp 2026-08-14T23:30:19.144306+02:00 +path: !cell-path $.items.0.name +``` + +Here, `!filesize` and `!cell-path` are Nushell tags, while `!!timestamp` is a standard YAML tag. When you read this back with `from yaml`, those fields come back as proper typed values instead of plain strings or integers. + +If you want the YAML version and Nushell tag prefix written out explicitly, use `to yaml --add-directives` or `to yaml -d`. + +```nushell +ls | where name == Cargo.toml | first | to yaml --add-directives +``` + +```yaml +%YAML 1.2 +%TAG ! tag:nushell.sh,2026: +--- +name: Cargo.toml +type: file +size: !filesize 17515 +modified: !!timestamp 2026-08-14T23:30:19.144306+02:00 +``` + +#### Multiple documents + +YAML streams with more than one document work properly now. By default, `from yaml` returns a single document directly, but returns a list if the stream has multiple documents. If you want to be explicit, use `--multiple list` to always get a list or `--multiple single` to reject multi-document input. + +```yaml +name: dev +--- +name: prod +``` + +```nushell +$yaml | from yaml +# => [{name: dev}, {name: prod}] + +'name: dev' | from yaml --multiple list +# => [{name: dev}] + +$yaml | from yaml --multiple single +# Error: expected a single YAML document +``` + +This also works the other way round. `to yaml --multiple` writes each item in a list as its own YAML document. + +```nushell +[{name: dev}, {name: prod}] | to yaml --multiple +``` + +```yaml +name: dev +--- +name: prod +``` + +#### Anchors, aliases, and merge keys + +Anchors and aliases are handled more completely now, including merge keys. Previously this just did not work at all. + +```yaml +defaults: &defaults + timeout: 30 + retries: 3 + +production: + <<: *defaults + timeout: 60 +``` + +```nushell +$yaml | from yaml | get production +# => {timeout: 60, retries: 3} +``` + +#### Useful flags + +On the parsing side, the main new flags are `from yaml --spec 1.1|1.2`, `--multiple auto|list|single`, `--ignore-tags`, and `--key-resolution strict|verbatim`. + +On the writing side, the handy flags are `--spec`, `--add-directives`/`-d`, `--multiple`/`-m`, `--indent`/`-i`, `--compact-list-indent`, `--quote`/`-q`, `--non-roundtrip`, and `--serialize`/`-s`. + +For example, `--quote` and `--indent` can help when another tool expects a particular style. + +```nushell +{outer: {inner: value}} | to yaml --indent 4 --quote double +``` + +```yaml +outer: + inner: "value" +``` + +### Disallowed shadowing parser keywords + +Trying to shadow a keyword will produce an error now. +```nushell +❯ def def [] {} +Error: nu::parser::name_is_keyword + + × Can't use parser keyword `def` as command name. + ╭─[repl_entry #1:1:5] + 1 │ def def [] {} + · ─┬─ + · ╰── 'def' is a parser keyword + ╰──── + help: Parser keywords cannot be shadowed (including via module exports and `use *`). Choose a different command name so language + constructs keep working. +``` + +### The `nu` binary no longer ships with `--testbin` + +The `nu` binary used to include a handful of test binaries for our integration tests. These were available through `nu --testbin`. To reduce the size and complexity of the binary, this option is no longer available. Everything the test binaries could do can also be done with Nushell itself. + +If your scripts depended on these test binaries, use an equivalent Nushell command or call `nu -n -c "some commands"` with the same behavior. + +### Other breaking changes + +* Remove the `idx import` and `idx export` commands, which had very little use case, as the search backend (FFF) is not designed for persistent disk caching. `idx init` is now the one way to build the in-memory index; watching remains enabled by default and can be disabled with `--no-watch`. ([#18679](https://github.com/nushell/nushell/pull/18679)) + +## Additions + +### `drop` now supports `binary` inputs as well + +### `chunks`, `first`, `last`, `take`, `skip` and `drop` can now be used with a `filesize` arguments + +These commands can work on `binary` data, it only makes sense for them to work with not just `int` but `filesize` arguments as well: +```nushell +# split compressed file into 10MiB chunks +open --raw file.7z + | chunks 10MiB + | enumerate + | each {|chunk| + let suffix = $chunk.index + 1 | fill -a right -c '0' -w 3 + $chunk.item + | save $"file.7z.($suffix)" + } +``` + +### Added constant expressions in `match` arms BAD_ENCODING + +The `match` command can now evaluate constants in match arms like this: +```nushell +❯ match "test" { + ('t' + 'es' + 't') => { print 'OK' } +} +OK +``` +or a more real-world case: +```nushell +❯ const MY_DIR_CONSTANT = "/Users/fdncred" +❯ let path = pwd +❯ $path +/Users/fdncred/src/nushell +❯ match $path { + ($MY_DIR_CONSTANT + '/sub-dir1') => { print "sub-dir" } + ($MY_DIR_CONSTANT + '/src/nushell') => { print "nu" } +} +nu +``` + +### Added `external_arg` annotations for script parameters + +You can now do the following: +script.nu +```nushell +def main [ + a: external_arg + b: external_arg + -c: external_arg + ...rest: external_arg +] { + [ + [label value type]; + [a $a ($a | describe)] + [b $b ($b | describe)] + [c $c ($c | describe)] + [rest $rest ($rest | describe)] + ] +} +``` +Then: +``` +./script2.nu 0001 true -c true -- 001 true +┌───┬───────┬──────────────┬────────────┐ +│ # │ label │ value │ type │ +├───┼───────┼──────────────┼────────────┤ +│ 0 │ a │ 0001 │ glob │ +├───┼───────┼──────────────┼────────────┤ +│ 1 │ b │ true │ glob │ +├───┼───────┼──────────────┼────────────┤ +│ 2 │ c │ true │ glob │ +├───┼───────┼──────────────┼────────────┤ +│ 3 │ rest │ ┌───┬──────┐ │ list │ +│ │ │ │ 0 │ 001 │ │ │ +│ │ │ ├───┼──────┤ │ │ +│ │ │ │ 1 │ true │ │ │ +│ │ │ └───┴──────┘ │ │ +└───┴───────┴──────────────┴────────────┘ +``` + +### `any` and `all` now accept row conditions just like `where` +In addition to the current closure syntax, `any` and `all` can now be given row conditions to make writing filters more streamlined. + +```nushell +[9 8 7 6] | enumerate | any item == index * 2 +``` + +Just like with `where`, you can reference column names directly, and use the `$it` variable to construct predicates given to both commands, or continue to use closures for more extensive constructions. + +```nushell +[1sec 1min 1hr] | all ($it | describe) == 'duration' +``` + +### Added `matrix` custom value and 21 subcommands + +A new `matrix` custom value type enables high-performance matrix math in Nushell, backed by the ndarray library for Rust. + +**Constructors:** +- `matrix zeros ` — create a matrix filled with zeros +- `matrix identity ` — create an n×n identity matrix +- `into matrix` — convert a table/list-of-lists/list-of-records into a matrix + +**Access:** +- `matrix get-row ` — extract a row as a list +- `matrix get-col ` — extract a column as a list (2D only) +- `matrix set-row ` — replace a row +- `matrix set-col ` — replace a column (2D only) + +**Arithmetic:** +- `matrix add ` — element-wise addition with optional `--broadcast` +- `matrix subtract ` — element-wise subtraction with optional `--broadcast` +- `matrix scale ` — multiply all elements by a scalar +- Matrix values also support `` expressions: `$m + $n`, `$m * 2.0`, `$m == $n` + +**Linear algebra:** +- `matrix multiply ` — dot product (supports 1D×1D, 2D×1D, 1D×2D, 2D×2D) +- `matrix transpose` — swap rows and columns (nD reverses all axes) + +**Transforms:** +- `matrix reshape ` — change dimensions +- `matrix reshape --flatten` — flatten to 1D + +**Element-wise:** (could rename this to matrix each if map is confusing) +- `matrix map { |e| ... }` — apply a closure to each element, returning a new matrix + +**Reductions:** +- `matrix sum` / `matrix sum --axis ` — sum all or along an axis +- `matrix mean` — arithmetic mean of all elements +- `matrix max` / `matrix max --axis ` — maximum all or along an axis +- `matrix reduce --fold { |acc e| ... }` — fold all elements to a single value + +**Output:** +- `matrix into-nu` — convert to table (list of lists) +- `matrix into-nu --as-records` — convert to table of records with auto-generated column names + +### Examples + +```nushell +# Create and manipulate matrices +[[1 2 3] [4 5 6]] | into matrix | matrix transpose | matrix into-nu | to nuon +# [[1.0, 4.0], [2.0, 5.0], [3.0, 6.0]] + +matrix identity 3 | matrix scale 5 | matrix into-nu | to nuon +# [[5.0, 0.0, 0.0], [0.0, 5.0, 0.0], [0.0, 0.0, 5.0]] + +# Matrix multiplication +[[1 2] [3 4]] | into matrix | matrix multiply ([[1 0] [0 1]] | into matrix) | matrix into-nu | to nuon +# [[1.0, 2.0], [3.0, 4.0]] + +# Element-wise operations +[[1 2] [3 4]] | into matrix | matrix map { |e| $e * 2 } | matrix sum +# 20.0 + +# Broadcasting +matrix zeros 2 3 | matrix add --broadcast ([[1.0 2.0 3.0]] | into matrix) | matrix into-nu | to nuon +# [[1.0, 2.0, 3.0], [1.0, 2.0, 3.0]] + +# Row/column access +matrix identity 2 | matrix get-row 1 | to nuon +# [0.0, 1.0] + +# Shape metadata via cell path +matrix identity 2 | $in.shape +# ╭───┬───╮ +# │ 0 │ 2 │ +# │ 1 │ 2 │ +# ╰───┴───╯ +matrix identity 2 | $in.ndim # 2 +matrix identity 2 | $in.size # 4 +``` + +### Standard iterator commands redirect to matrix-specific ones + +- `each` on a matrix → errors: "Use `matrix map` for element-wise operations" +- `par-each` on a matrix → errors: "Use `matrix map` for element-wise operations" +- `reduce` on a matrix → errors: "Use `matrix reduce --fold { ... }`" + +### Added `date floor` and `date ceil` commands + +Users will have access to two new commands `date floor` and `date ceil` that rounds a date value down and up, respectively, to a specified duration boundary. +```nushell +# Round down to the nearest hour + > 2026-07-15T12:11:10-04:00 | date floor 1hr + Wed, 15 Jul 2026 12:00:00 -0400 + +# Round list of dates down to the nearest 2day boundary + > [2026-07-10T00:00:00-04:00 2026-07-15T00:00:00-04:00] | date floor 2day + ╭───┬───────────────────────╮ + │ 0 │ 07/10/2026 12:00:00AM │ + │ 1 │ 07/14/2026 12:00:00AM │ + ╰───┴───────────────────────╯ + +# Round date up to nearest hour + > 2026-07-15T12:11:10-04:00 | date ceil 1hr + Wed, 15 Jul 2026 12:59:59 -0400 + +# Round list of dates up to nearest 2day boundary + > [2026-07-10T00:00:00-04:00 2026-07-15T00:00:00-04:00] | date ceil 2day + ╭───┬───────────────────────╮ + │ 0 │ 07/11/2026 11:59:59PM │ + │ 1 │ 07/15/2026 11:59:59PM │ + ╰───┴───────────────────────╯ +``` + +### Added `std-rfc` `date floor` and `date ceil` commands + +Added two new `std-rfc` commands, `date floor` and `date ceil` for rounding datetime values down and up, respectively, to specified duration boundaries. +```nushell +use std-rfc/date * + +# Round down to the nearest hour +2026-07-15T12:11:10-04:00 | date floor 1hr +# => Wed, 15 Jul 2026 12:00:00 -0400 + +1969-12-31T23:30:00+00:00 | date floor 1hr +# => Wed, 31 Dec 1969 23:00:00 +0000 + + +# Round date up to nearest hour +2026-07-15T12:11:10-04:00 | date ceil 1hr +# => Wed, 15 Jul 2026 13:00:00 -0400 + +1969-12-31T23:30:00+00:00 | date ceil 1hr +# => Thu, 1 Jan 1970 00:00:00 +0000 +``` + +### Added `idx watch` for streaming indexed filesystem changes + +Added `idx watch` to stream filesystem change events from a live `idx` index as tabular records (`kind`, `path`). Requires `idx init` with watching enabled. Optional pattern, `--ignore`, `--timeout`, and `--max-events` are supported for filtering and clean stream termination. Events respect gitignore/index ignores and can be piped into normal Nushell pipelines. + +Also updated the `fff-search` dependency to 0.10.0 (enables the watch subscription API). + +Examples: + +```nushell +idx init . --wait +idx watch +idx watch "**/*.rs" --ignore [target] +idx watch | where kind == "modified" | each {|e| print $"changed: ($e.path)"} +idx watch --max-events 1 --timeout 5sec +``` + +### Added comparison operators for `semver` values + +`semver` values can now be compared with `==`, `!=`, `<`, `<=`, `>`, and `>=`, using normal semantic version ordering: + +```nushell +(2.0.1 | into semver) > (1.9.9 | into semver) +# true + +(2.0.1 | into semver) < (1.9.9 | into semver) +# false + +(1.2.3 | into semver) == (1.2.3 | into semver) +# true + +(1.0.0-alpha | into semver) < (1.0.0 | into semver) +# true +``` + +A version string on the right-hand side is also accepted when it is a valid semver: + +```nushell +(2.0.1 | into semver) > 1.9.9 +# true +``` + +### Added `--include` to `take while` and `take until` + +`take until` and `take while` commands get a new `--include (-i)` flag, which allows you to take extra items after the stream would have otherwise stopped: + +```nushell +date now +| into record +| transpose key val +| take until { $in.key == day } --include 1 +``` +``` +╭───┬───────┬──────╮ +│ # │ key │ val │ +├───┼───────┼──────┤ +│ 0 │ year │ 2026 │ +│ 1 │ month │ 7 │ +│ 2 │ day │ 16 │ +╰───┴───────┴──────╯ +``` + +### Improved completion caching, dispatch, and reliability + +- Added `$env.config.completions.cache_size` (default: `100`) to control that cap. +- Completion results now persist across prompts instead of being discarded on every new prompt,. +- Fixed a potential panic when narrowing file/directory completions on a path containing multi-byte (non-ASCII) characters. +- `commandline complete --type` now validates its `--type` argument and no longer panics on an out-of-range cursor. +- `use`, `overlay use`, `export use`, `source-env`, `hide-env`, `attr complete`, and `which` now go through the same completion dispatch as other builtins, fixing inconsistent/missing completions in a few of them + +### Added `commandline set-prompt` for async prompt updates + +- Added the `commandline set-prompt` command for ad-hoc updates to a rendered prompt. + + +### Examples: + +Stand-in values, with background job spawn: + +```nushell +$env.PROMPT_COMMAND = { $"(ansi green)~(ansi reset)> " } + +$env.PROMPT_COMMAND_RIGHT = { + job spawn { + let branch = (git branch --show-current | complete | get stdout | str trim) + commandline set-prompt --right $"(ansi yellow)($branch)(ansi reset)" + } + + "" # show nothing on the right until the background job fills it in +} +``` +```nushell + Replace the left prompt with a freshly rendered string. + > job spawn { sleep 1sec; commandline set-prompt $"(ansi green)me> (ansi reset)" } + + Replace the right prompt. + > job spawn { sleep 1sec; commandline set-prompt --right $"right (date now | format date '%H:%M:%S')" } + + Replace the default/emacs indicator. + > job spawn { sleep 1sec; commandline set-prompt --indicator $" (char prompt)" } + + Replace the vi insert and normal mode indicators independently. + > job spawn { sleep 1sec; commandline set-prompt --vi-insert ": " --vi-normal "n " } + + Replace the multiline continuation indicator. + > job spawn { sleep 1sec; commandline set-prompt --multiline "... " } + + Replace multiple prompt segments in one call. + > job spawn { sleep 1sec; commandline set-prompt --right "67" --indicator "69" } + + Stream a slow prompt segment in from a background job. + > job spawn { sleep 1sec; commandline set-prompt $"(git branch --show-current) > " } +``` + +### Added `$ans` for accessing the last REPL result +- `$ans` now stores the last successful nushell output, duration, and exit code as a nushell record. `$ans` is a reserved variable name now like `$in`, `$nu`, `$env` so if there are scripts that do `let ans = ...` this will be a breaking change. +- `$env.config.last_result_size` takes a filesize to configure the maximum amount of memory this functionality should take for the `.last` element. We default to `0b` which turns the feature off and makes the functionality opt-in. +- When max size is reached the data in `$ans` is truncated and a warning is shown when you access it. +- You can turn this functionality on by setting the `$env.config.last_result_size` to `1Mb`, or any reasonable filesize. When disabled with `0b`, `$ans` is `nothing`. + + +```nushell +> ls +# normal output +> $ans +# ╭───────────┬────────────────╮ +# │ last │ [table N rows] │ +# │ exit_code │ 0 │ +# │ duration │ 12ms │ +# ╰───────────┴────────────────╯ + +$ans.last # previous pipeline value +$ans.exit_code # int (same idea as $env.LAST_EXIT_CODE) +$ans.duration # duration value (from the same timing as $env.CMD_DURATION_MS) +``` + +### Added the last command line to `$ans` + +Add the `cli` key to the `$ans` record so people can see the last command executed. +Rename the config point `last_result_size` to `max_last_result_size` to be more descriptive. + +### Added Helix edit mode + +Use `$env.config.edit_mode = helix` to enable a selection-first, Helix/Kakoune-style edit mode (normal, select, insert): motions carry the selection, verbs act on it. +Menu keybindings (Tab, Ctrl-r, F1) work as in the other modes, keybindings can target `helix_normal` / `helix_insert` / `helix_select` and cursor shapes can be customized via `cursor_shape.helix_*`. +On by default, opt-out via `--no-default-features`. + +### Added configurable visual selection styling + +Added `color_config.selection` and `color_config.selection_cursor` to style the line editor's visual selection and the cursor cell inside it: + +```nushell +$env.config.color_config.selection = { attr: r } # default +$env.config.color_config.selection_cursor = { attr: n } # default +``` + +With the defaults a block cursor looks as before; `underscore` and `line` cursor shapes are now visible inside selections. + +### Other additions + +* Added support for passing lists into `into semver`, and for providing cell paths to do things like `$nu.os-info | into semver kernel_version` or `["1.2.0", "0.3.12"] | into semver`. ([#18567](https://github.com/nushell/nushell/pull/18567)) +* Might change behavior of existing functionality for people relying on their system `ln` instead of the coreutils `ln`. ([#18571](https://github.com/nushell/nushell/pull/18571)) +* - Drastically improves completion UX, and eliminates any resentment towards completions as I had begun to develop, finding it better to just memorize input to avoid a long terminal freeze. ([#18334](https://github.com/nushell/nushell/pull/18334)) +* - `polars rolling` can now be used natively with lazy frames and be used in expressions. ([#18730](https://github.com/nushell/nushell/pull/18730)) +* Add support for additional arguments to `nu -c`/`nu --commands` with `--` ([#18576](https://github.com/nushell/nushell/pull/18576)) +* Added completions to `format duration`'s and `format filesize`'s unit argument. ([#18783](https://github.com/nushell/nushell/pull/18783)) +* In helix edit mode, keybindings with `mode: helix_select` now target select mode's own keybinding table instead of being shared with `helix_normal`. ([#18833](https://github.com/nushell/nushell/pull/18833)) + +## Performance + +### Faster large binary value processing +Large binary values are now substantially cheaper to clone, stream, slice, and convert. + +Commands that repeatedly process large binary values now avoid copying the entire value at each step, with repeated slicing and integer conversion roughly 3.4x faster. + +### Faster large list and table access + +Large lists and tables are now much faster to read from, load from variables, and capture in closures. + +Accessing a small part of a large list, such as `$list.0`, no longer copies the entire list each time the variable is loaded. In a 100,000-element test, 200 repeated reads improved from roughly 131 ms to 138 µs (~950x faster). + +### Other performance improvements + +* Improved performance of `str replace --regex` and `str replace --multiline` when working with lists, tables, and records with many string values, roughly a 10x speed boost. ([#18508](https://github.com/nushell/nushell/pull/18508)) +* `lines` command with string *value* (non-stream) input no longer eagerly creates a list. Instead it returns a list stream, producing items lazily just like it does with text/byte stream inputs. ([#18753](https://github.com/nushell/nushell/pull/18753)) +* Using built-in commands with `--regex` parameters should be faster in tight loops now because they are able to use the LRU cache for regex. ([#18797](https://github.com/nushell/nushell/pull/18797)) + +## Other changes + +### Added `--config-home` for alternate configuration directories BAD_ENCODING + +- Added `--config-home` to point Nushell at an alternate config directory for a session (default `config.nu` / `env.nu` / history location / `$nu.default-config-dir`, and related defaults). +- Config path resolution is centralized; `$nu` paths and history defaults stay consistent with CLI overrides for the session. + +Internal restructuring (crate layout, path resolution) has no intentional change to normal default-path behavior on a typical install. + +### Additional changes + +* Renamed `$ans.cli` to `$ans.command`. ([#18836](https://github.com/nushell/nushell/pull/18836)) + +## Bug fixes + +### Fixed nested `try/finally` blocks interfering with outer error handling + +When nesting a `try/finally` block within a `try` or `try/catch` block, it no longer prevents the outer block from catching errors. + +This is showcased by the following code: + +```nushell +# /path/to/file.nu +try { + try { print "inner" } finally { print "finally" } + error make { msg: "error" } +} +print "outer" +``` + +Output before this change: + +```nushell +> nu /path/to/file.nu +inner +finally +Error: nu::shell::error + + × error + ╭─[/path/to/file.nu:3:16] + 2 │ try { print "inner" } finally { print "finally" } + 3 │ error make { msg: "error" } + · ──────────────── + 4 │ } + ╰──── +``` + +Note that the error was surfaced despite being thrown inside a `try` block! + +Output after this change: + +```nushell +> nu /path/to/file.nu +inner +finally +outer +``` + +### Fixed negative arguments for `oneof` parameters + +Parameters with `int`, `float` and `number` types can be supplied negative arguments: +```nushell +def foo [p: int] { + $p +} + +foo -2 +# => -2 +``` + +However this didn't work with parameters with types like `oneof`: +```nushell +def foo [p: ] { + $p +} + +foo -2 +# => Error: nu::parser::unknown_flag +# => +# => × The `foo` command doesn't have flag `-2`. +# => ╭─[repl_entry #1:3:6] +# => 2 │ +# => 3 │ foo -2 +# => · ┬ +# => · ╰── unknown flag +# => ╰──── +# => help: Use `--help` to see available flags +``` + +This is now fixed: +```nushell +def foo [p: ] { + $p +} + +foo -2 +# => -2 +``` + +### Fixed `ps -l` failing when processes exit during collection + +Fixed a Linux race condition where `ps -l` could fail if a process exited +while process information was being collected. + +The exited process is now skipped instead of causing the command to fail. + +### Example + +Before: + +```text +Error getting process stat +File not found: /proc//stat +```` + +After: + +`ps -l` continues running and omits the exited process. + +### Fix panic and infinite loop in `seq` on overflow and zero increment + +- `seq 0 ` now errors with `increment cannot be 0` instead of looping forever or returning nothing. +- `seq` sequences that reach the `i64` boundary now terminate cleanly instead of panicking or wrapping. + +### Fixed recursive glob behavior in the experimental `dc-glob` backend + +With `--experimental-options=[dc-glob]` (or the equivalent config): + +- Bare `**` now expands to directories at any depth (including the current directory), not an empty list. It does **not** list regular files; use `**/*` for files and directories under the start path. +- Prefixed trailing patterns like `foo/**` include the prefix directory itself and nested directories only (not files). +- Patterns like `**/*/*` and `**/*/*/*` again respect minimum depth (extra `/*` segments are no longer ignored). +- `**/*` continues to list contents **under** the start directory and does **not** include the start directory itself (differs from legacy `nu-glob`, matches common `glob` crate behavior). + + +### Examples + +```nushell +# enable dc-glob for the session +$env.NU_EXPERIMENTAL_OPTIONS = [dc-glob] +# or: nu --experimental-options=[dc-glob] + +mkdir 0/1/2/3 +touch 0/1/2/3/file.txt +cd 0 + +glob '**' +# start dir + nested directories only (no file.txt) + +glob '**/*' +# everything under start ÔÇö not the start dir itself + +glob '**/*/*' +# paths at least 2 components deep + +glob '**/*/*/*' +# paths at least 3 components deep (e.g. 1/2/3 and 1/2/3/file.txt) + +mkdir foo/bar +touch foo/sibling.txt +glob 'foo/**' +# foo and foo/bar only (not sibling.txt) +``` + +Matcher-level checks (debug flags require dc-glob): + +```nushell +glob --dbg-matches '**/*/*' '1' # false +glob --dbg-matches '**/*/*' '1/2' # true +glob --dbg-matches '**/*/*/*' '1/2' # false +glob --dbg-matches '**/*/*/*' '1/2/3' # true +glob --dbg-matches '**/foo' 'foo' # true +glob --dbg-matches 'foo/**' 'foo' # true +glob --dbg-matches 'foo/**' 'foo/bar' # true +glob --dbg-matches 'foo/**' 'foobar' # false +glob --dbg-matches '*/*' '1' # false +``` + +### Fixed inconsistent `group-by` handling of null keys + +`group-by` no longer maps `null` to the empty string, and treats null the same for list values, cell paths, and closures. + +#### Before (inconsistent): + +```nushell +# list: null became "" → 2 groups +[ a null ] | group-by | values | length +# => 2 + +# cell path: null dropped → 1 group +[ { x: a } { x: null } ] | group-by x | values | length +# => 1 + +# closure: null became "" → 2 groups +[ { x: a } { x: null } ] | group-by { get x } | values | length +# => 2 + +# null and "" collapsed into one group +[ a "" null ] | group-by | to nuon --raw +# => {a: [a], "": ["", null]} +``` + +#### After (consistent): + +```nushell +# All three return 1 (null omitted from record output) +[ a null ] | group-by | values | length +# => 1 + +[ { x: a } { x: null } ] | group-by x | values | length +# => 1 + +[ { x: a } { x: null } ] | group-by { get x } | values | length +# => 1 + +# Use --to-table to keep null groups +[ a null ] | group-by --to-table +# ╭───┬───────┬───────╮ +# │ # │ group │ items │ +# ├───┼───────┼───────┤ +# │ 0 │ a │ [a] │ +# │ 1 │ │ [null]│ # group is null +# ╰───┴───────┴───────╯ + +[ { x: a } { x: null } ] | group-by x --to-table +# ╭───┬────┬─────────────────╮ +# │ # │ x │ items │ +# ├───┼────┼─────────────────┤ +# │ 0 │ a │ [[x]; [a]] │ +# │ 1 │ │ [[x]; [null]] │ # x is null +# ╰───┴────┴─────────────────╯ + +# null and empty string stay separate +[ "" null ] | group-by --to-table | to nuon --raw +# => [[group, items]; ["", [""]], [null, [null]]] + +# record output: only "" remains; null is omitted +[ a "" null ] | group-by | to nuon --raw +# => {a: [a], "": [""]} +``` + +#### Optional cell path (unchanged): + +```nushell +# Missing optional column still skipped +[{foo: 123}, {foo: 234}, {bar: 345}] | group-by foo? +# only groups "123" and "234" + +# Optional path with explicit null is also skipped +[{x: a}, {x: null}] | group-by x? +# only group "a" +``` + +### Fixed `scope` commands to include local scopes + +Fixed `scope variables`, `scope commands`, `scope aliases`, `scope modules`, and `scope externs` so they report **both the current local scope and the global/permanent scope**. Nested definitions inside `do`, `if`/`for` bodies, and custom commands now appear while that scope is active (and disappear afterward). Outer variables remain visible inside closures (for example `let a = 1; do { let b = 2; scope variables }` lists both `$a` and `$b`). + +### After this change + +**Outer + local variables (#14071):** + +```nushell +let a = 1 +do { + let b = 2 + scope variables | where name in ["$a", "$b"] | sort-by name | select name value +} +# name value +# $a 1 +# $b 2 +``` + +**Local commands / aliases / modules:** + +```nushell +do { + def local-cmd [] { "hi" } + alias la = ls + use spam.nu # module file in the cwd + scope commands | where name == "local-cmd" | length # 1 + scope aliases | where name == "la" | length # 1 + scope modules | where name == "spam" | length # 1 +} +# after the block ends, those local names are gone again +``` + +**Keyword blocks (IR-inlined):** + +```nushell +if true { + def local-cmd [] { "hi" } + scope commands | where name == "local-cmd" | length # 1 +} +# after if: +scope commands | where name == "local-cmd" | length # 0 +``` + +**`for` loop variable + locals:** + +```nushell +for i in 1..1 { + let d = 4 + # scope variables includes $i and $d (and outer globals) +} +``` + +**Shadowed `let` (related #17414):** + +```nushell +let x = "first" +# scope variables shows $x with value "first" after the first let +let x = "second" +# then shows the live second binding +``` + +### Fixed `source` losing visibility of outer variables + +Fixes the bugs found when a script or REPL input used `source` to run a `.nu` file, variables defined before the `source` call could become invisible inside the sourced file, producing a "variable not found" error. + +**Example reproducing the bug** — script file `lll.nu`: + +```nushell +let xxx = 'let in script' +source sss.nu +``` + +Where `sss.nu` contains: + +```nushell +print $xxx +``` + +Running `nu lll.nu` would fail with: + +```nushell +Error: nu::shell::variable_not_found + + × Variable not found + ╭─[sss.nu:1:7] + 1 │ print $xxx + · ──┬─ + · ╰── variable not found + ╰──── +``` + +The same error could also appear in the REPL after re-declaring a variable: + +```nushell +❯ let xxx = 'value 1' +❯ source sss.nu # OK: prints "value 1" +❯ let xxx = 'value 2' +❯ source sss.nu # Error: variable not found +``` +After this change the last statement above would print `value 2`. See the tests and the issue to see more variations. + +### Errors for... `error make`? + +Not specifying the span for error labels (or not doing so correctly) confusingly fell back to using the record's own span instead: + + + + + + + + +
+ +```nushell +let var = 2 +let span = (metadata $var).span + +error make { + msg: "my error" + label: { text: "two" } +} +``` + +
+ +``` +Error: nu::shell::error + + × my error + ╭─[repl_entry #1:6:9] + 5 │ msg: "my error" + 6 │ label: {text: "two"} + · ──────┬────── + · ╰── two + 7 │ } + ╰──── +``` + +
+ + + + + + + +
+ +```nushell +let var = 2 +let span = (metadata $var).span + +error make { + msg: "my error" + label: { text: "two", start: $span.start, end: $span.end } +} +``` + +
+ +``` +Error: nu::shell::error + + × my error + ╭─[repl_entry #2:6:9] + 5 │ msg: "my error" + 6 │ label: { text: "two", start: $span.start, end: $span.end } + · ─────────────────────────┬───────────────────────── + · ╰── two + 7 │ } + ╰──── +``` + +
+ +So `error make` succeeded instead of throwing an error. Well, it *did* throw an error, the one user was trying to, not the one it should due to `error make` receiving an invalid argument. + +From now on `error make` will raise an error of its own when receiving an invalid argument. + + + + + + + + +
+ +```nushell +let var = 2 +let span = (metadata $var).span + +error make { + msg: "my error" + label: { text: "two" } +} +``` + +
+ +``` +Error: nu::shell::missing_required_columns + + × Value is missing required columns. + ╭─[repl_entry #1:6:9] + 4 │ error make { + 5 │ msg: "my error" + 6 │ label: { text: "two" } + · ───────┬─────── + · ╰── missing `span: record` column + 7 │ } + ╰──── +``` + +
+ + + + + + + +
+ +```nushell +let var = 2 +let span = (metadata $var).span + +error make { + msg: "my error" + label: { text: "two", start: $span.start, end: $span.end } +} +``` + +
+ +``` +Error: nu::shell::missing_required_columns + + × Value is missing required columns. + ╭─[repl_entry #2:6:9] + 4 │ error make { + 5 │ msg: "my error" + 6 │ label: { text: "two", start: $span.start, end: $span.end } + · ─────────────────────────┬───────────────────────── + · ╰── missing `span: record` column + 7 │ } + ╰──── +``` + +
+ +### Math commands: records with list columns and optional cell paths + +Fixed reducing math commands on records whose columns are lists (including uneven lengths). For example: + +```nushell +{ alice: [0.1 0.6 0.2], bob: [0.8 0.3 0.2 0.9] } | math avg +# ╭───────┬──────╮ +# │ alice │ 0.30 │ +# │ bob │ 0.55 │ +# ╰───────┴──────╯ +``` + +All of the following math commands now accept optional cell paths / columns to operate on only those fields: + +**Reducing** (list cells become a scalar): +`math avg`, `math sum`, `math product`, `math max`, `math min`, `math median`, `math mode`, `math stddev`, `math variance` + +**Element-wise** (list cells stay lists): +`math abs`, `math cbrt`, `math ceil`, `math floor`, `math sqrt`, `math round`, `math log` + +```nushell +# Reduce only one column +{ alice: [1 2 3], bob: [4 5 6] } | math avg alice +# {alice: 2.00, bob: [list 3 items]} + +# Element-wise on one column +{ alice: [-1 -2 -3], bob: [-4 -5] } | math abs alice +# {alice: [1, 2, 3], bob: [-4, -5]} + +# Works in const context too +const data = {alice: [1 2 3], bob: [4 5 6]} +$data | math sum alice +``` + +Examples for each command: + +```nushell +let rec = { alice: [1 2 3], bob: [4 5 6] } + +$rec | math avg alice # alice: 2 +$rec | math sum alice # alice: 6 +$rec | math product alice # alice: 24 +$rec | math max bob # bob: 6 +$rec | math min bob # bob: 4 +$rec | math median alice # alice: 2 +$rec | math mode alice # alice: [1, 2, 3] modes as list +$rec | math stddev alice # alice: ~0.82 +$rec | math variance alice # alice: ~0.67 + +{ alice: [-1 -2], bob: [-3 -4] } | math abs alice | to nuon +# {alice: [1, 2], bob: [-3, -4]} +{ alice: [8 27], bob: [64] } | math cbrt alice | to nuon +# {alice: [2.0, 3.0], bob: [64]} +{ alice: [1.2 2.3], bob: [3.4] } | math ceil alice | to nuon +# {alice: [2, 3], bob: [3.4]} +{ alice: [1.2 2.3], bob: [3.4] } | math floor alice | to nuon +# {alice: [1, 2], bob: [3.4]} +{ alice: [4 9], bob: [16] } | math sqrt alice | to nuon +# {alice: [2.0, 3.0], bob: [16]} +{ alice: [1.2 2.7], bob: [3.1] } | math round alice | to nuon +# {alice: [1, 3], bob: [3.1]} +{ alice: [1 10 100], bob: [1000] } | math log 10 alice | to nuon +# {alice: [0.0, 1.0, 2.0], bob: [1000]} +``` + +Existing list, table, number, duration, and range inputs keep their previous behavior when no cell paths are given. + +Made error messages more consistent. +On the commands that make sense, I added int, float, duration, filesize. On others, product, sqrt, cbrt, and log, only work with ints and floats. + +### Fixed `math max` on empty streams + +Previously the empty list (top) would display and error but the empty stream (bottom) would not. This should now be fixed. +```nushell +[] | math max +['x'] | each { try { into int } } | math max +``` + +### Clearer delimiter errors + +Nushell now reports **unclosed** and **unbalanced** delimiters with: + +- The **kind** of delimiter involved (`{`, `}`, `[`, `]`, `(`, `)`, `"`, `'`, `>`, `|`, …) +- **Where it opened** and **where the closer was expected** (when known) +- Help text that suggests a fix, sometimes with a structure hint (e.g. which `def` or record field is open) + +Common mistakes such as forgetting `[` before list elements, forgetting `(` before a grouped expression, forgetting `{` after `if`/`while`/`for`/`try`/`match`, or writing bare record fields without `{` point **near the mistake** instead of only at a distant `}` or EOF. + +### Fixed `path type` treating empty strings as directories + +`"" | path type` now returns `null` instead of `"dir"` + + - +## YAML's Always More Labor + +We reworked our YAML implementation, thanks to [@cptpiepmatz](https://github.com/cptpiepmatz) we now have a stable, modern and maintained implementation that works and no longer tries to make YAML 1.1, the scary one, and YAML 1.2 work at the same time. These two are now strictly separated. Take a look at all the examples [here](#yaml-got-a-proper-rework). + +## Ah, the `$ans.last` pipeline took so long to run + +Ever had the issue that your last pipeline took forever to run and you forgot to assign it to a variable. Worry no longer! By setting `$env.config.max_last_result_size` to some filesize of your choosing, you can capture every last result of a pipeline. Thanks to [@fdncred](https://github.com/fdncred), we have now the nushell specific variable `$ans` (answer, heavily inspired by calculators) that keeps track of the last pipeline with a duration, exit code and what you typed in. With that you can finally breath when your pipeline took 15 minutes and you don't want to rerun it again, just do a quick `let took_too_long = $ans.last` and you're golden. Take a close look [here](#added-ans-for-accessing-the-last-repl-result). + +## Helix mode! + +So, we have next to the vim mode the `helix`. Set it by `$env.config.edit_mode = helix`, that's it. Some more on this [here](#added-helix-edit-mode). Thanks [@kronberger-droid](https://github.com/kronberger-droid). + +## Lots of internal wrangling + +This release did not add very much public user facing additions but we did a lot of things inside Nushell. Be in awe about the long list of things happening in the ✨ [Hall of Fame](#hall-of-fame) ✨. As always we have cool internal changes but this release we truly have a lot in there. Also check out the [long list of fixes](#bug-fixes). A big shoutout to everyone contributing and making Nushell better. # Changes From 99cf31cc4b243bec3bc3603c66dfc4c6c7490b1e Mon Sep 17 00:00:00 2001 From: Tim 'Piepmatz' Hesse Date: Sat, 15 Aug 2026 22:37:33 +0200 Subject: [PATCH 5/5] update highlights section Signed-off-by: Tim 'Piepmatz' Hesse --- blog/2026-08-15-nushell_v0_115_0.md | 38 +++++++++++++++++------------ 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/blog/2026-08-15-nushell_v0_115_0.md b/blog/2026-08-15-nushell_v0_115_0.md index 5cb382d2da2..3ca522c61a8 100644 --- a/blog/2026-08-15-nushell_v0_115_0.md +++ b/blog/2026-08-15-nushell_v0_115_0.md @@ -3,18 +3,12 @@ title: Nushell 0.115.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.115.0 of Nu. This release adds... +excerpt: Today, we're releasing version 0.115.0 of Nu. This release brings a major YAML rework, the new `$ans` REPL variable for inspecting your last result, Helix-style editing, and a lot of internal cleanup and fixes. --- - - - - # Nushell 0.115.0 - - -Today, we're releasing version 0.115.0 of Nu. This release adds... +Today, we're releasing version 0.115.0 of Nu. This release brings a major YAML rework, the new `$ans` REPL variable for inspecting your last result, Helix-style editing, and a lot of internal cleanup and fixes. # Where to get it @@ -28,21 +22,33 @@ As part of this release, we also publish a set of optional [plugins](https://www # Highlights and themes of this release -## YAML's Always More Labor +## YAML's Always More Labor + +Thanks to [@cptpiepmatz](https://github.com/cptpiepmatz), Nushell's YAML support got a proper rebuild. We now use a stable, modern, maintained implementation that no longer tries to make YAML 1.1, the scary one, and YAML 1.2 behave like one big YAML-shaped compromise. + +The two specs are now handled separately, `from yaml` defaults to YAML 1.2, tags work more deliberately, multi-document streams are supported, anchors and merge keys behave properly, and `to yaml` is clearer about values that cannot round-trip. + +Take a look at all the examples [here](#yaml-got-a-proper-rework). + +## Ah, the `$ans.last` pipeline took so long to run + +Ever had your last pipeline take forever, only to realize you forgot to assign the result to a variable? Worry no longer. Thanks to [@fdncred](https://github.com/fdncred), Nushell now has `$ans`, a Nushell-specific answer variable heavily inspired by calculators. + +`$ans` keeps track of the last REPL result, including the output, duration, exit code, and the command you typed. Storing the output is opt-in: set `$env.config.max_last_result_size` to a filesize of your choosing, and `$ans.last` will keep up to that much of the previous pipeline result. -We reworked our YAML implementation, thanks to [@cptpiepmatz](https://github.com/cptpiepmatz) we now have a stable, modern and maintained implementation that works and no longer tries to make YAML 1.1, the scary one, and YAML 1.2 work at the same time. These two are now strictly separated. Take a look at all the examples [here](#yaml-got-a-proper-rework). +That means if your pipeline took 15 minutes and you do not want to run it again, you can finally breathe, do a quick `let took_too_long = $ans.last`, and you're golden. Take a closer look [here](#added-ans-for-accessing-the-last-repl-result). -## Ah, the `$ans.last` pipeline took so long to run +## Helix mode! -Ever had the issue that your last pipeline took forever to run and you forgot to assign it to a variable. Worry no longer! By setting `$env.config.max_last_result_size` to some filesize of your choosing, you can capture every last result of a pipeline. Thanks to [@fdncred](https://github.com/fdncred), we have now the nushell specific variable `$ans` (answer, heavily inspired by calculators) that keeps track of the last pipeline with a duration, exit code and what you typed in. With that you can finally breath when your pipeline took 15 minutes and you don't want to rerun it again, just do a quick `let took_too_long = $ans.last` and you're golden. Take a close look [here](#added-ans-for-accessing-the-last-repl-result). +Alongside Vim mode, Nushell now has `helix` edit mode too. Set it with `$env.config.edit_mode = helix`, and that's it. You get a selection-first, Helix/Kakoune-style editing experience with normal, select, and insert modes. -## Helix mode! +Thanks to [@kronberger-droid](https://github.com/kronberger-droid) for the work here. More details are [here](#added-helix-edit-mode). -So, we have next to the vim mode the `helix`. Set it by `$env.config.edit_mode = helix`, that's it. Some more on this [here](#added-helix-edit-mode). Thanks [@kronberger-droid](https://github.com/kronberger-droid). +## Lots of internal wrangling -## Lots of internal wrangling +This release does not have a huge pile of public user-facing additions, but there was a lot of work inside Nushell. Be in awe of the long list of things happening in the ✨ [Hall of Fame](#hall-of-fame) ✨, and also check out the [long list of fixes](#bug-fixes). -This release did not add very much public user facing additions but we did a lot of things inside Nushell. Be in awe about the long list of things happening in the ✨ [Hall of Fame](#hall-of-fame) ✨. As always we have cool internal changes but this release we truly have a lot in there. Also check out the [long list of fixes](#bug-fixes). A big shoutout to everyone contributing and making Nushell better. +As always, a big shoutout to everyone contributing and making Nushell better. The internals may not always get the flashiest highlight section, but they are doing a lot of heavy lifting this time. # Changes