This commit is contained in:
atagen 2026-05-19 23:32:51 +10:00
parent da4bc139eb
commit 9745ef9c56
49 changed files with 9039 additions and 5483 deletions

View file

@ -1,105 +1,47 @@
# nixos integration
inshellah provides a nixos module that automatically indexes nushell
completions for all installed packages at system build time.
inshellah provides a nixos module that indexes nushell completions for
every installed package at system build time, and a wrapped binary
that knows where to find the result.
## enabling
```nix
# in your flake.nix outputs:
# flake.nix outputs:
{
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [
inshellah.nixosModules.default
{
programs.inshellah.enable = true;
}
{ programs.inshellah.enable = true; }
];
};
}
```
or if importing the module directly:
or importing directly:
```nix
# configuration.nix
{ pkgs, ... }: {
imports = [ ./path/to/inshellah/nix/module.nix ];
programs.inshellah = {
enable = true;
package = pkgs.inshellah; # or your local build
};
imports = [ ./path/to/inshellah-rs/nix/module.nix ];
programs.inshellah.enable = true;
}
```
## what happens at build time
after rebuilding, completions are immediately available through the
autoloaded nushell shim.
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.
## what the module does
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
- 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.
- exposes the same shim as a read-only `snippet` option for users who
want to source or inspect it manually.
## module options
@ -110,12 +52,11 @@ programs.inshellah = {
# the inshellah package (set automatically by the flake module)
package = pkgs.inshellah;
# where to place indexed completion files under the system profile
# subdirectory of the system profile holding the index files
# 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
@ -123,41 +64,68 @@ programs.inshellah = {
# commands to skip manpage parsing for (uses --help instead)
helpOnlyCommands = [ "nix" ];
# per-subprocess timeout in ms during indexing (null = built-in
# default of 200ms)
timeoutMs = null;
# worker-thread count for the parallel scrape
workers = null;
};
```
## 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 module installs the completer under nushell's vendor autoload path,
so no hand-written nushell config is needed for the normal NixOS case.
the snippet sets up the external completer. the wrapper installed by
the module has the system completion paths hardcoded, so no flags are
needed:
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:
```nu
let inshellah_complete = {|spans|
inshellah complete ...$spans | from json
}
$env.config.completions.external = {
enable: true
max_results: 100
completer: $inshellah_complete
}
```nix
# generate a config file from the snippet
environment.etc."nushell/inshellah.nu".text = config.programs.inshellah.snippet;
```
## home manager and other user-level package managers
then source that file from your nushell config:
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`.
```nu
source /etc/nushell/inshellah.nu
```
to get completions for user-installed packages, run `inshellah index`
against those prefixes separately:
or copy the snippet directly into `~/.config/nushell/config.nu`:
```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:
| 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 |
| `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:
```sh
# home-manager / per-user profile
@ -167,35 +135,28 @@ inshellah index /etc/profiles/per-user/$USER
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:
this indexes into `$XDG_CACHE_HOME/inshellah`, which the completer
searches automatically. to automate via home-manager:
```nix
# 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/`.
**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 because nushell serves its own completions for them.
built-in (`help commands | where name == "thecommand"`) — built-ins
are excluded.
**stale completions after update**: completions regenerate on every
`nixos-rebuild`. if a command changed its flags, rebuild to pick up
the changes.
**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 (`|| true`).
check `journalctl` for the build log if completions are missing.
**build-time errors**: indexing failures are non-fatal. check
`journalctl` for the build log if completions are missing for a
specific command.