Skip to main content

Command reference

ndo <name> [args...]

The default invocation. Resolves name against the merged central+local recipe set, binds args positionally to the recipe's declared params (checking ndo var lookup tables along the way), and executes the result. If the recipe has depends, those run first, in order, each recipe running at most once even across a shared dependency — see Dependencies for the full rules (cycles, fail-fast, --dry-run/--verbose behavior).

ndo open ./file.txt

Args beyond the declared params are appended raw (quoted) to the end of the command — useful for passthrough:

ndo test -- -run TestFoo

ndo add

ndo add <name> "<command>" [--param <name>]... [--depends <name>]... [--local] [--desc "<text>"]

Adds a new recipe. Fails with an error (not a silent overwrite) if the name already exists in the target file — use ndo edit to change an existing recipe by hand.

ndo add deploy "./scripts/deploy.sh {{env}}" --param env --local --desc "Deploy to the given environment"

# build/lint run first, in order, every time deploy does
ndo add deploy "./scripts/deploy.sh {{env}}" --param env --depends build --depends lint --local
FlagEffect
--param <name>Declares a parameter, in positional order. Repeatable.
--depends <name>Recipe to run first, in order. Repeatable. See Dependencies.
--localWrite to the nearest local .ndo.toml (creating one in the current directory if none is found upward) instead of the central file.
--desc "<text>"Description shown in ndo list.

ndo list

ndo list [--local] [--central]

Prints resolved recipes. With no flags, shows the merged central+local view. --local/--central restrict to just that file's contents, unmerged.

ndo edit

ndo edit [--local] [--central]

Opens the relevant recipe file in $EDITOR (or config.toml's settings.editor, which takes precedence). Defaults to the central file; --local opens the nearest local file instead.

ndo remove

ndo remove <name> [--local]

Deletes a recipe from the target file. Errors if not found in that specific file — it doesn't cascade-search the other one.

ndo init

ndo init

Creates an empty .ndo.toml in the current directory. Errors if a local recipe file already exists anywhere in the tree above the current directory, so it never silently shadows one.

ndo var

Named lookup tables that expand a recipe's positional argument into a longer value, without any new recipe syntax — the lookup is keyed by param name. See Vars: named lookup tables for param shortcuts for the full mechanics and TOML schema.

ndo add o "code {{folder}}" --param folder --local
ndo var add folder work "C:\Users\dev\projects" --local
ndo var add folder ndo "C:\Users\dev\projects\ndo" --local

ndo o work # -> code C:\Users\dev\projects
ndo o D:\other\path # no match in vars.folder -> code D:\other\path (used literally)
SubcommandEffect
ndo var add <group> <key> <value> [--local]Add or overwrite one entry.
ndo var remove <group> <key> [--local]Remove one entry.
ndo var remove <group> [--local]Remove the entire group at once.
ndo var list [group] [--local] [--central]List resolved entries, optionally filtered to one group.

ndo var list output:

folder:
ndo: C:\Users\dev\projects\ndo
work: C:\Users\dev\projects

Central and local vars merge at the key level — unlike recipes, which replace wholesale on a name collision. A local .ndo.toml can add or override individual keys without losing the rest of the central group.

ndo update

ndo update [--check]

Checks the latest GitHub release against the running version. What happens next depends on how ndo was installed, detected from the running binary's path:

Detected asBehavior
Homebrew, Scoop, a .deb/.rpm install, or go installPrints the right command for that channel (brew upgrade ndo, etc.) — never touches the binary, so that package manager's own records stay accurate.
Anything else (raw binary via install.sh or a manual download)Downloads the new release, verifies its checksum against checksums.txt, and atomically replaces the running binary in place.

--check only reports whether an update is available (and the right command to get it) without installing anything, regardless of which channel is detected.

Shell completion

ndo <TAB> completes recipe names; ndo <recipe> <TAB> completes that recipe's next declared param — offering the matching vars group's keys (if any) alongside normal file completion, so you never have to memorize what you named an entry.

The first time you run ndo in a real terminal, it asks once whether to set this up for you — if you say yes, it wires the completion script into your shell's startup file itself, so it just works in every new terminal from then on. It never asks again (the answer is remembered in config.toml), and it's a strict no-op in scripts/CI/pipes. To set it up by hand instead:

# bash (current shell)
source <(ndo completion bash)

# zsh (current shell)
source <(ndo completion zsh)

# fish
ndo completion fish | source
# PowerShell — add to your profile ($PROFILE) to persist across sessions
ndo completion powershell | Out-String | Invoke-Expression

Or skip the one-time prompt and trigger the same auto-detect-and-install logic directly:

ndo completion install # detect your shell, wire it in
ndo completion uninstall # remove what install added

Run ndo completion --help for instructions on installing the script permanently for your shell.

Global flags

FlagEffect
--verbosePrint extra diagnostics, including which file a resolved recipe came from.
--dry-runPrint the resolved, interpolated command without executing it.
--versionPrint the ndo version.
--helpShow help for any command.