Nushell

Bash gets pipelines; Nushell gets pipelines and structure. A generated module wraps every operation so tensors come from and return to native Nushell values — no JSON wrangling.

Setup

After installing with Homebrew, tell Nushell where to find the installed module. This setup also works in ahsh; edit the configuration file that the shell loads. Find its location by running:

$nu.config-path

Open that config.nu in your editor. Add '/opt/homebrew/share/nutorch' to its existing $env.NU_LIB_DIRS list. Preserve your other entries; this is a full example, not an instruction to replace your entire configuration:

$env.NU_LIB_DIRS = [
  ($nu.default-config-dir | path join 'scripts')
  ($nu.data-dir | path join 'completions')
  '/opt/homebrew/share/nutorch'
]

/opt/homebrew is the standard Apple-silicon Homebrew prefix. For a custom installation, run brew --prefix and use that path followed by /share/nutorch. Make sure a later NU_LIB_DIRS assignment does not overwrite your addition. Keeping env.nu empty is fine for this interactive configuration.

This adds a module search directory, not an automatic import. Reopen your shell, then explicitly load the module and try a matrix multiplication:

use nutorch.nu *
nutorch --version
let t = ([[1 2] [3 4]] | torch tensor)
$t | torch mm $t | torch value

The result is a native nested list with rows [7 10] and [15 22]. The version command reports your installed package version.

Standalone scripts do not generally load config.nu. For those, explicitly configure their module search path or import the installed module by full path; do not assume this interactive setup is loaded in every script process.

A current copy of nutorch.nu also ships in the public source repo root; regenerate it any time with:

torch nu-module | save -f nutorch.nu

Structured data in and out

let t = ([[1 2] [3 4]] | torch tensor)
$t | torch mm $t | torch value            # a native table
torch tensors | where bytes > 1_000_000 | get handle | each {|h| torch free $h }

Wrappers honor the dual input pattern — pipe the leftmost tensor in as $in, or pass handles as arguments and the CLI’s grammar fills the missing slots — and every one of the 185 ops is wrapped, plus the registry and daemon verbs. Non-finite values cross the boundary as REAL Nushell NaN/infinity floats; the JSON dialect ("NaN", "Infinity", "-Infinity") is handled for you in both directions.

Use ^torch when you explicitly want the external CLI’s raw text or JSON output instead of the structured Nushell wrapper result. The old nutorch <op> namespace remains as a compatibility alias for existing scripts.

Training, natively

The regression from neural networks, as Nushell:

use nutorch.nu *

torch manual_seed 42 | ignore
let x = ([[0.0] [1.0] [2.0] [3.0]] | torch tensor)
let y = ([[1.0] [3.0] [5.0] [7.0]] | torch tensor)
let model = (torch nn linear 1 1)
let opt = (torch nn sgd $model --lr 0.05)

for i in 1..200 {
  let loss = ($x | torch forward $model | torch mse_loss $y)
  $loss | torch backward
  $opt | torch step
  torch nn zero_grad $opt | ignore
}

The full script (with assertions) is scripts/train-regression.nu in the repo — it converges to the same losses as its zsh twin.

Plain JSON everywhere else

The structured verbs also serve any other tool via --json:

torch tensors --json
torch ops --json
torch daemon status --json
torch nn info $m --json