Greets nix develop with a MOTD, command picker, docs viewer, and themed prompt. Use it as a flake-parts module or, in any other flake (blueprint, a plain outputs function), through prelude.lib.evalModule.
Prelude keeps docs next to where you run the project. docs in the devshell explains this repo; nix run github:darkmatter/prelude#docs explains whatever checkout you are in, with its own Prelude theme and pages when it has them; nix run github:org/repo#prelude-docs explains a prelude-enabled dependency, and nix run github:org/repo#prelude-menu opens its command menu. The only command to remember is nix develop.
The wizard writes prelude.nix, a sibling title.txt, and a project-root .envrc (use flake):
nix run github:darkmatter/prelude -- wizard
# or:
nix run github:darkmatter/prelude -- wizard -o nix/prelude.nixThe wizard never overwrites an existing flake.nix; wire the sidecar in as Install shows.
The generated file lists every option as a commented default. Put clone-to-running steps on the MOTD; put the rest in the command catalogue (x) and Markdown docs.
x # open the interactive picker
x dev # run a command by catalogue key
x db migrate # …a subcommand ("db migrate" or "db/migrate" in Nix)
x d # …or by its single-key accelerator
x --list # print the command table
Adapt existing packages so the menu does not drift:
prelude.commands.dev = prelude.lib.fromPkg packages.dev {
description = "start the development server";
motd = 1;
};prelude.docs.pages = [
{ text = ./README.md; }
{ text = ./docs/getting-started.md; }
];Each Markdown file is one page. Digits jump, Tab steps, j/k scroll, q quits.
Opt into a managed Bash with a movable docs/menu pane. prelude.workspace.enable defaults to false; enable it in your prelude.nix sidecar with the required surfaces:
{
prelude = {
workspace.enable = true;
menu.enable = true;
motd.enable = true;
docs.pages = [ { text = ./README.md; } ];
};
}Both flakeModules.default and lib.evalModule export packages.prelude-workspace and bundle the active launcher in prelude-shell when enabled. The Menu/MOTD/Docs prerequisites are asserted; prelude.prompt.enable is not required. Keep .envrc as use flake: cd + direnv provides the lightweight environment, MOTD, and theming for an already-initialized Starship prompt. Run interactive nix develop for the full footer and catalogue completion; generated project init enters the enabled workspace in the foreground in Bash with a TTY. No extra Prelude Bash rc hook is required. Automatic Starship follows prelude.prompt.enable; manual launches remain available:
x prelude:workspace # manual launch with the fixed prelude $ prompt
prelude-workspace --starship # manual launch with Starship, even if prelude.prompt.enable = falseBoth use the current checkout's menu, with your own Docs, MOTD, theme, and completion config. The published package honors prelude.root (lib.evalModule users set it to self); PRELUDE_ROOT still overrides it.
Each init is stamped before launch, so exit or failure does not immediately reopen the workspace. Leaving and reentering the devshell, or a changed init, permits automatic entry again. Noninteractive, envrc, lorri, zsh, and non-TTY contexts never auto-launch it; PRELUDE_WORKSPACE_ACTIVE prevents child recursion. Workspace mode skips legacy BLE initialization. The public prelude hook bash remains optional for already-configured interactive handoffs after direnv, not an onboarding requirement.
Disabled consumers avoid workspace native dependencies. This repo dogfoods it enabled, so its default shell includes the launcher and native runtime, but not headers or renderer tooling. See the workspace guide for activation details, published launches, controls, and runtime limitations.
Bun apps get the same picker, MOTD, and docs viewer from TypeScript. Commands are functions declared next to the code they run, with typed arguments:
export const preludeCommand = Command.make({
description: "run the dev server",
args: [{ token: "--port", type: "number", default: 3000 }],
run: (args) => serve(args.port), // args.port: number
});
await Prelude.make({ project: "acme", commands: { dev: preludeCommand } }).main();Install with bun add @drkmttr/prelude. Walkthrough: examples/typescript/. API: ts/.
Add the input, then put prelude-shell in your devshell. It bundles every enabled component and activates through its setup-hook. Direnv's use flake loads the environment, shows the MOTD, and themes an existing Starship prompt; interactive nix develop provides the full devshell. Loader-only MOTD includes “Run nix develop for the full devshell”; that hint is hidden in workspace or quiet mode. Both ways below read the same prelude.nix and build the same packages.
inputs.prelude.url = "github:darkmatter/prelude";With flake-parts, import the module and the sidecar:
outputs = { prelude, flake-parts, ... }@inputs:
flake-parts.lib.mkFlake { inherit inputs; } {
imports = [ prelude.flakeModules.default ./prelude.nix ];
systems = [ "x86_64-linux" "aarch64-darwin" ];
perSystem = { pkgs, config, ... }: {
devShells.default = pkgs.mkShell {
packages = [ config.packages.prelude-shell ];
};
};
};Without flake-parts (blueprint, a plain outputs function), evaluate the sidecar with prelude.lib.evalModule pkgs:
# blueprint: devshell.nix
{ pkgs, inputs, ... }:
let
prelude = inputs.prelude.lib.evalModule pkgs ./prelude.nix;
in
pkgs.mkShell {
packages = [ prelude.packages.prelude-shell ];
}The module receives pkgs, so package-backed commands go straight into prelude.commands; pass anything else it takes (self, inputs) through _module.args. To publish prelude, prelude-menu, or an enabled prelude-workspace bound to your repository, as flakeModules.default does, set prelude.root = self;. A complete plain flake: examples/without-flake-parts/.
A custom shellHook activates with eval "$(prelude-preflight)".
nix run github:darkmatter/prelude#docs shows the docs of the repository you are in. It opens the flake's own prelude-docs package when there is one, so the project's theme, pages and hero apply; any other repository gets Prelude's defaults over its README.md and docs/. The same package serves nix run github:org/repo#prelude-docs.
With flake-parts, packages.prelude-docs exists as soon as prelude.docs.pages lists a page. Without it, expose the packages evalModule returns; legacyPackages keeps them out of nix flake check:
legacyPackages = forAllSystems (pkgs: (prelude.lib.evalModule pkgs ./prelude.nix).packages);In blueprint, a package file does it: packages/prelude-docs.nix, with prelude.nix one directory up.
{ pkgs, inputs, ... }:
(inputs.prelude.lib.evalModule pkgs ../prelude.nix).packages.prelude-docsFull consumer walkthrough: Your own repo. Command keys and grouping: command conventions. Options: reference.
prelude.theme selects a palette: prelude, phosphor, minted, amber, solarized, nord, gruvbox, paper (light), mono, apathy. Override tokens with prelude.palette. Preview every theme with nix run .#example-themes.
Questions and PRs are welcome via GitHub issues.
nix develop
x go:test
x checkUser-visible docs changes: x sync-docs (and x record-docs if media is stale).
Render the README tour with agg docs/recording.cast docs/recording.gif. Preserve the cast's term.theme when replacing the recording so its terminal colors match Prelude instead of the renderer's fallback palette.
MIT © 2026 Darkmatter




