inshellah/doc/nixos.md
2026-05-24 18:15:24 +10:00

8.8 KiB

nixos / nix-darwin integration

inshellah provides a module that indexes nushell completions for every installed package at system build time, and a wrapped binary that knows where to find the result. the same module body backs both NixOS and nix-darwin — on Linux it scrapes ELF binaries, on macOS Mach-O ones, selected automatically by the inshellah build's target platform.

enabling (NixOS)

# flake.nix outputs:
{
  nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
    modules = [
      inshellah.nixosModules.default
      { programs.inshellah.enable = true; }
    ];
  };
}

or importing directly:

# configuration.nix
{ pkgs, ... }: {
  imports = [ ./path/to/inshellah-rs/nix/module.nix ];
  programs.inshellah.enable = true;
}

enabling (nix-darwin)

# flake.nix outputs:
{
  darwinConfigurations.mymac = nix-darwin.lib.darwinSystem {
    modules = [
      inshellah.darwinModules.default
      { programs.inshellah.enable = true; }
    ];
  };
}

the options and behaviour are identical to the NixOS module — it reads the same programs.inshellah.* settings and writes the same completion index and nushell shim under the system profile (/run/current-system/sw, which nix-darwin also uses).

on macOS, the tools you reach through /usr/bin (git, clang, …) are xcrun shims whose real binaries and manpages live under the active developer dir, outside the nix system profile — so they aren't indexed by default. rather than probe the host toolchain impurely, list the nix equivalents you want completed in extraScrapePackages; the module rolls their store paths into the build-time scrape (inshellah index … --prefix …). this applies on NixOS too, for any package whose completions you want indexed without putting it on the system path.

after rebuilding, completions are immediately available through the autoloaded nushell shim.

what the module does

  • installs the inshellah binary, wrapped so the system completion path is found automatically.
  • runs inshellah index "$out" during the system profile build, producing one file per command under $out/share/inshellah/.
  • drops the full nushell external-completer shim into /share/nushell/vendor/autoload/, including sudo/doas overrides so elevated commands still complete through inshellah.
  • emits lightweight command-name stubs for dynamic-completion backends that are present in the system profile, so tools like git and jj appear in nushell's command list while inshellah still supplies their argument completions lazily.
  • exposes the same shim as a read-only snippet option for users who want to source or inspect it manually.

module options

programs.inshellah = {
  enable = true;

  # the inshellah package (set automatically by the flake module)
  package = pkgs.inshellah;

  # subdirectory of the system profile holding the index files
  # default: "/share/inshellah"
  completionsPath = "/share/inshellah";

  # additional read-only completion directories to search
  extraDirs = [ "/etc/profiles/per-user/alice/share/inshellah" ];

  # commands to skip entirely during indexing
  ignoreCommands = [ "problematic-tool" ];

  # commands to skip manpage parsing for (uses --help instead)
  helpOnlyCommands = [ "nix" ];

  # extra packages to scrape alongside the system profile; each store path
  # is passed to `inshellah index --prefix`. handy on macOS for the nix
  # equivalents of /usr/bin shim tools (git, clang, …)
  extraScrapePackages = [ pkgs.git pkgs.clang ];

  # per-subprocess timeout in ms during indexing (null = built-in
  # default of 200ms)
  timeoutMs = null;

  # timeout in ms for live dynamic completions at tab-completion time
  # set to 0 to disable the runtime timeout
  dynamicTimeoutMs = 5000;

  # result cap requested from live providers that support native limits
  # set to 0 to omit native result-limit flags
  dynamicLimit = 200;

  # characters that trigger flag completions when a partial token begins
  # with one of them. default "-"; e.g. "-+" also triggers on "+"
  flagTriggers = "-";

  # also surface flags on an empty token (right after a space), mixed in
  # with subcommands. default false
  flagOnEmpty = false;

  # cap on candidates returned and nushell's max_results. 0 = no cap
  # (nushell's built-in default of 200 still applies)
  maxCompletions = 0;

  # per-subprocess timeout (ms) for the completer's on-the-fly --help
  # resolution of uncached commands. null = built-in default of 200ms.
  # distinct from timeoutMs (indexing) and dynamicTimeoutMs (live shim)
  completeTimeoutMs = null;

  # worker-thread count for the parallel scrape
  workers = null;
};

flag-triggering behaviour

flagTriggers and flagOnEmpty control when option/flag completions are offered. By default flags appear only after a leading -. Add characters to flagTriggers (e.g. "-+") to trigger on them as well — for a non-dash trigger the text after it is matched against the bare flag name, so +ver completes to --verbose. Set flagOnEmpty = true to list flags immediately after a space, alongside subcommands. These map to the INSHELLAH_FLAG_TRIGGERS / INSHELLAH_FLAG_ON_EMPTY environment variables (see runtime-completions.md).

using the completer

the module installs the completer under nushell's vendor autoload path, so no hand-written nushell config is needed for the normal NixOS case.

the read-only snippet option still holds the complete external-completer config. to manage sourcing yourself instead of using autoload, write it to a file:

# generate a config file from the snippet
environment.etc."nushell/inshellah.nu".text = config.programs.inshellah.snippet;

then source that file from your nushell config:

source /etc/nushell/inshellah.nu

or copy the snippet directly into ~/.config/nushell/config.nu:

# (the snippet is many lines — copy it from `nix eval` of the option,
# or use the environment.etc approach above)
$env.config.completions.external = { ... }

the snippet provides both static lookups against the system index and runtime fallbacks for cases the static index can't cover:

runtime fallbacks have a default 5s timeout, controlled by programs.inshellah.dynamicTimeoutMs or INSHELLAH_DYNAMIC_TIMEOUT_MS when sourcing the snippet manually. providers with native result caps use programs.inshellah.dynamicLimit or INSHELLAH_DYNAMIC_LIMIT, defaulting to 200. set either value to 0 to disable that guard. on timeout the completer returns null so nushell can fall back to its normal completion behavior.

command dynamic source
nix flake refs via NIX_GET_COMPLETIONS, with optional meta.description
systemctl / journalctl unit names from list-units
coredumpctl units + pids
loginctl users / sessions
machinectl / networkctl machines / links
ssh / scp / sftp hostnames from ssh config + known_hosts
docker / podman containers + image refs by subcommand
kubectl resource names from the live cluster
git refs + worktree paths
jj revisions, operations, bookmarks, remotes, files, and workspaces
npm / pnpm / yarn scripts from package.json
make / just targets / recipes
cargo workspace targets behind --bin / --example / etc.
kill / pkill pid+comm pairs

home manager and user-level package managers

the system module only indexes packages installed system-wide. for home-manager or per-user nix profiles, run inshellah index against those prefixes separately:

# home-manager / per-user profile
inshellah index /etc/profiles/per-user/$USER

# classic nix-env profile
inshellah index ~/.nix-profile

this indexes into $XDG_CACHE_HOME/inshellah, which the completer searches automatically. to automate via home-manager:

home.activation.inshellah-index = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
  ${pkgs.inshellah}/bin/inshellah index /etc/profiles/per-user/$USER 2>/dev/null || true
'';

troubleshooting

completions not appearing: check that the system index exists (ls /run/current-system/sw/share/inshellah/) and that the completer is configured.

missing completions for a specific command: check if it's a nushell built-in (help commands | where name == "thecommand") — built-ins are excluded.

command name missing but arguments complete after typing it: the command may be installed only in a user profile. the system module can only generate command-name stubs for binaries linked into the system profile, though the external completer can still complete arguments once the command word has been typed.

stale completions after update: the index regenerates on every nixos-rebuild. if a command changed its flags, rebuild.

build-time errors: indexing failures are non-fatal. check journalctl for the build log if completions are missing for a specific command.