inshellah/doc/nixos.md
2026-03-27 15:02:16 +11:00

6.3 KiB

nixos integration

inshellah provides a nixos module that automatically indexes nushell completions for all installed packages at system build time.

enabling

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

or if importing the module directly:

# configuration.nix
{ pkgs, ... }: {
  imports = [ ./path/to/inshellah/nix/module.nix ];
  programs.inshellah = {
    enable = true;
    package = pkgs.inshellah;  # or your local build
  };
}

what happens at build time

the module hooks into environment.extraSetup, which runs during the system profile build (the buildEnv that creates /run/current-system/sw). at that point, all system packages are merged, so $out/bin contains every executable and $out/share/man contains every manpage.

inshellah runs a single command:

inshellah index "$out" --dir $out/share/inshellah

this executes a three-phase pipeline:

phase 1: native completion detection (parallel)

for each executable, inshellah scans the elf binary for the string completion. if found, it probes common patterns like CMD completions nushell to see if the program can generate its own nushell completions. native output is used verbatim — these are always higher quality than parsed completions.

programs like niri, and any clap/cobra tool with nushell support, are handled this way.

phase 2: manpage parsing (sequential)

for commands not covered by phase 1, inshellah parses manpages from man1 (user commands) and man8 (sysadmin commands). it handles:

  • gnu .TP style (coreutils, help2man)
  • .IP style (curl, hand-written)
  • .PP+.RS/.RE style (git, docbook)
  • nix3 bullet+hyperlink style (nix run, nix build, etc.)
  • mdoc (bsd) format
  • deroff fallback for unusual formats

synopsis sections are parsed to detect subcommands: git-commit.1 generates export extern "git commit", not export extern "git-commit".

phase 3: --help fallback (parallel)

remaining executables without manpages get --help (or -h) called with a 200ms timeout. elf binaries are pre-scanned for the -h string to skip those that don't support help flags. shell scripts are run directly (they're fast). execution is parallelized to available cores.

when --help produces rendered manpage output instead of plain help text (e.g. git stash --help delegates to man), the raw manpage source is located and parsed with the groff parser for richer results.

output

each command gets its own file in /share/inshellah under the system profile. native generators produce .nu files; parsed results produce .json files. the complete command reads both formats.

nushell built-in commands (ls, cd, cp, mv, etc.) are excluded since nushell provides its own completions.

performance

on a typical nixos system (~950 executables, ~1600 manpages):

  • total time: ~4-10 seconds
  • native gzip decompression (camlzip, no process spawning)
  • parallel --help with core-scaled forking
  • elf string scanning to skip ~15% of binaries

module options

programs.inshellah = {
  enable = true;

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

  # where to place indexed completion files under the system profile
  # default: "/share/inshellah"
  completionsPath = "/share/inshellah";

  # additional read-only completion directories to search
  # these are appended to the --dir path alongside the system completions
  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" ];
};

using the completer

the flake module sets a read-only snippet option containing the nushell config needed to wire up the completer. you can access it via config.programs.inshellah.snippet and paste it into your nushell config, or source it from a file generated by your nixos config.

the snippet sets up the external completer. the wrapper installed by the module has the system completion paths hardcoded, so no flags are needed:

let inshellah_complete = {|spans|
    inshellah complete ...$spans | from json
}
$env.config.completions.external = {
    enable: true
    max_results: 100
    completer: $inshellah_complete
}

home manager and other user-level package managers

the nixos module only indexes packages installed at the system level (those that end up in /run/current-system/sw). if you use home-manager, nix-env, or another user-level package manager, those binaries and manpages live elsewhere — typically under /etc/profiles/per-user/<name> or ~/.nix-profile.

to get completions for user-installed packages, 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 the default user cache ($XDG_CACHE_HOME/inshellah), which the completer searches automatically. you can re-run this after installing new packages, or add it to a home-manager activation script.

if you want to automate this in home-manager:

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

the completer will then search both the system index and the user cache, so completions from both sources are available.

troubleshooting

completions not appearing: ensure the completer is configured in your nushell config (see above). check that the system index exists: ls /run/current-system/sw/share/inshellah/.

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

stale completions after update: completions regenerate on every nixos-rebuild. if a command changed its flags, rebuild to pick up the changes.

build-time errors: indexing failures are non-fatal (|| true). check journalctl for the build log if completions are missing.