nix: add a flake with a package profile
Assisted-by: Claude Opus 5.5 via Claude Code
diff --git a/docs/nix-migration.md b/docs/nix-migration.md
index 878f479d..404d7718 100644
--- a/docs/nix-migration.md
+++ b/docs/nix-migration.md
@@ -1,6 +1,8 @@
# Moving the dotfiles to nix
-Status: exploring, nothing built yet (2026-11-03).
+Status: `flake.nix` builds a package profile, and imperishable-night
+installs it for `alpha`. The Mac still uses its hand-installed
+`nix profile` entries (2026-11-05).
This note records a plan to install packages with nix, so the dotfiles
work on Linux hosts as well as the Mac. frork keeps creating the
@@ -231,10 +233,9 @@ but `.hammerspoon/init.lua` would then need a nix store path on
## Risks
Nix flakes in a git repo only see files that are in the git index. In a
-colocated jj repo, new files in `@` may be missing from the index, which
-would cause "path is not tracked by Git" errors right after adding a
-file to the flake. This is unverified with jj 0.46; test it before
-settling the layout. The `path:` flake URL skips the git check, but it
+colocated jj repo, new files in `@` are missing from the index, so
+evaluating fails with "path is not tracked by Git" until the file is
+committed. The `path:` flake URL skips the git check, but it
copies the whole directory into the store, including untracked and
ignored paths like `ai/claude/projects` and `.direnv`.
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 00000000..032638e8
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,101 @@
+{
+ "nodes": {
+ "flake-parts": {
+ "inputs": {
+ "nixpkgs-lib": [
+ "neovim-nightly-overlay",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1793576659,
+ "narHash": "sha256-V1h85lUaJBQoHFrB2vpXZOE0Ju1MYSnIpsehoVZYlDo=",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "rev": "ea2630b7470978622c7689a1ff75d2b389175399",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "type": "github"
+ }
+ },
+ "neovim-nightly-overlay": {
+ "inputs": {
+ "flake-parts": "flake-parts",
+ "neovim-src": "neovim-src",
+ "nixpkgs": "nixpkgs"
+ },
+ "locked": {
+ "lastModified": 1793837004,
+ "narHash": "sha256-ZDOEpcyM7iJUqAFPjZiGeQQw5d4nUk0ND/v9pbVpgfc=",
+ "owner": "nix-community",
+ "repo": "neovim-nightly-overlay",
+ "rev": "79bc7f43b343fadd4f6f42a4e7f3c3942ae1378b",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "repo": "neovim-nightly-overlay",
+ "type": "github"
+ }
+ },
+ "neovim-src": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1793835735,
+ "narHash": "sha256-vEoHG2Mz0Myh7CwcL6eEUFSG+wNsi+AI4AxgvSjzIqo=",
+ "owner": "neovim",
+ "repo": "neovim",
+ "rev": "ab7d2d42a47bf167807d7b0303aaa52405b59579",
+ "type": "github"
+ },
+ "original": {
+ "owner": "neovim",
+ "repo": "neovim",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1793735720,
+ "narHash": "sha256-smoxPKrzAhdGuFd2LLnG4m8P6IrZuW95DVyzjnlab7w=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "9d3b8db48266c17be0b5f9c6360c3ecfbac3d1f5",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixpkgs-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs_2": {
+ "locked": {
+ "lastModified": 1793795574,
+ "narHash": "sha256-pgG/CO+txdf2sA2z6DsM+2G3F9ajfIp/3BMC4wfKMDY=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "13e53762c5afaf384d4614e94527a9ccfafe41d0",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixpkgs-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "neovim-nightly-overlay": "neovim-nightly-overlay",
+ "nixpkgs": "nixpkgs_2"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 00000000..e70128ce
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,42 @@
+{
+ inputs = {
+ nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
+ # Its own nixpkgs, since a different tree-sitter breaks its vendored-deps hash.
+ neovim-nightly-overlay.url = "github:nix-community/neovim-nightly-overlay";
+ };
+
+ outputs =
+ { nixpkgs, neovim-nightly-overlay, ... }:
+ let
+ forAllSystems = nixpkgs.lib.genAttrs [
+ "aarch64-darwin"
+ "x86_64-linux"
+ ];
+ in
+ {
+ # Tools with a DotSlash file in bin/ stay there.
+ packages = forAllSystems (
+ system:
+ let
+ pkgs = nixpkgs.legacyPackages.${system};
+ in
+ {
+ profile = pkgs.buildEnv {
+ name = "profile";
+ paths = with pkgs; [
+ # nix-direnv needs bash 4 or later.
+ bash
+ dotslash
+ entr
+ eza
+ git
+ neovim-nightly-overlay.packages.${system}.default
+ nix-direnv
+ tmux
+ zsh
+ ];
+ };
+ }
+ );
+ };
+}