This commit is contained in:
atagen 2026-05-19 23:32:51 +10:00
parent da4bc139eb
commit 66cbf003f8
44 changed files with 7166 additions and 5426 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 — no extra
nushell config needed if you paste in the snippet (see below).
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 a nushell autoload shim into `/share/nushell/vendor/autoload/`
that overrides nushell's hard-coded sudo/doas bypass so completions
fire through inshellah even for elevated commands.
- exposes a `snippet` option carrying the full external-completer
config — see "using the completer" below.
## 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,65 @@ 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's `snippet` option holds a complete external-completer
config. drop it into nushell:
the snippet sets up the external completer. the wrapper installed by
the module has the system completion paths hardcoded, so no flags are
needed:
```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 in 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` if you
prefer to manage it by hand:
```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 +132,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.