The style asked for terseness in tonal terms only, so a dry register wrapped in headers, tables and a full status recap each turn passed its self-check while being exactly the verbose output the style exists to prevent. Add explicit limits: a default length ceiling, headers only for four or more items, report the delta rather than the accumulated state, and state a caveat once. Replace the self-check with one that tests length and form rather than tone.
Interactive shell environment
Everything the shell, terminal multiplexer, git and ssh do beyond their defaults, and where each is defined. All of it is managed declaratively through home-manager — edit the listed file and rebuild, never the generated dotfiles.
Keyboard shortcuts have their own reference: KEYBINDINGS.md.
| Area | Defined in |
|---|---|
| zsh, CLI tools, tmux, ssh, auto-tmux | shell.nix |
| git (+ delta, commitizen) | git.nix |
| Neovim (nixvim) + LSP | editor.nix |
| Claude Code (CLAUDE.md, style, memory) | claude.nix |
| GUI apps, GTK/Firefox theming, cursor | desktop.nix (graphical hosts only) |
Shared by every host via default.nix; the work box also layers
work.nix on top (its own ssh config, extra
packages, and the C#/Helm language servers). The committer identity (name, email,
signing key) comes from the user registry
(../users/registry.nix), not this module.
zsh
| Feature | Notes |
|---|---|
| oh-my-zsh | plugins git, man, sudo (Esc-Esc to prepend sudo), colored-man-pages, extract; theme robbyrussell |
| Autosuggestion | fish-style history suggestions as you type (→ to accept) |
| Syntax highlighting | commands coloured by validity as you type |
| Completion | menu completion; the dump is rebuilt on every activation (see Maintenance) |
| History | 100k in-memory/on-disk, deduped, space-prefixed commands ignored, timestamped, shared live across sessions; file stays at ~/.zsh_history |
| Dotfiles location | dotDir is ~/.config/zsh (XDG) — .zshrc/.zshenv/.zcompdump live there; ~/.zshenv only bootstraps $ZDOTDIR |
| History substring search | type a fragment, then ↑/↓ cycles matching past commands — works in foot, iTerm2 and the Linux TTY (both CSI and SS3 arrow encodings bound) |
| Prompt | hostname is prefixed when over SSH |
Aliases: ls/ll/la/lt → eza (icons + git), cls → clear. git aliases live in git.nix (below).
CLI tools
| Tool | What it gives you |
|---|---|
fzf |
Ctrl-R fuzzy history, Ctrl-T file picker, Alt-C fuzzy cd (Catppuccin-themed) |
zoxide |
z <fragment> jumps to frecent directories |
direnv + nix-direnv |
per-project environments auto-loaded on cd (cached Nix dev shells) |
eza |
modern ls (drives the ls aliases) |
bat |
syntax-highlighting pager (Catppuccin Mocha theme); behaves like cat when piped; also the MANPAGER |
ripgrep / fd |
fast search (rg) and find (fd); also back fzf |
jq |
JSON processor |
gh / tea |
GitHub and Gitea (code.emmathe.dev) CLIs; gh uses SSH |
nix-index |
command-not-found: an unknown command tells you which Nix package provides it (prebuilt DB, no manual indexing) |
comma (,) |
run an uninstalled program once: , cowsay hi |
nh |
nicer nixos-rebuild/home-manager with diffs; $NH_FLAKE set to the repo. No scheduled GC (it could reap paths a running generation still references) — collect garbage manually with nh clean all / nix-collect-garbage -d |
btop |
resource monitor, themed Catppuccin Mocha (vendored theme) |
lazygit |
git TUI for staging/rebasing, themed to match (git.nix) |
hyperfine / sd |
command-line benchmarking; saner find-and-replace than sed |
Theming: fzf, bat, btop, lazygit and git's delta pager are all
Catppuccin Mocha, driven from the shared ../lib/catppuccin-mocha.nix palette / the
catppuccin upstream themes.
Env & defaults: xdg.enable on; PAGER/MANPAGER (bat) set in default.nix
(the editor owns $EDITOR/$VISUAL); xdg.mimeApps maps web→Firefox,
directories→nemo (desktop.nix).
tmux
Auto-start: opening any interactive terminal — foot, iTerm2, the WSL shell, the
Linux console — drops you straight into a tmux session named main (attach if it
exists, else create). Panes run a plain non-login zsh. It deliberately does not
fire for SSH sessions, VS Code's integrated terminal, already-inside-tmux, or
non-interactive shells. Escape hatch: NO_TMUX=1 <terminal> opens a bare shell.
| Setting | Value |
|---|---|
| Mode keys | vi |
| Mouse | on |
| Scrollback | 500000 lines |
escape-time |
10ms (the 500ms default lagged vim's ESC) |
focus-events |
on (vim autoread) |
base-index / pane-base-index |
1 |
| Splits | prefix s vertical, prefix v horizontal (stock %/" unbound) |
| Pane nav | Alt+arrows (no prefix) |
| Terminal | default-terminal tmux-256color; truecolor advertised per outer terminal (foot*, xterm-256color/iTerm2) via terminal-features … RGB |
| Clipboard | set-clipboard on; foot terminal-features advertise truecolor/sync/OSC52/title/cursor |
Plugins: sensible, vim-tmux-navigator (Ctrl-h/j/k/l across vim ↔ tmux),
yank, extrakto (prefix+Tab: fzf-grab paths/URLs/text from the pane into
the prompt), catppuccin (Mocha statusline), resurrect + continuum
(sessions auto-save and restore across reboots). The statusline draws Nerd-Font
glyphs — see Fonts.
Fonts
JetBrainsMono Nerd Font, Noto Sans and Noto Color Emoji are
installed on every host (in common-nixos.nix, because tmux/terminals run
everywhere; the Mac installs the Nerd Font to /Library/Fonts via the Darwin
config). fonts.fontconfig.defaultFonts maps the generic families so anything
asking for monospace gets the Nerd Font (with emoji fallback) — this also
gives the WSL box emoji/sans coverage it otherwise lacked. foot uses the Nerd
Font as its main font automatically. iTerm2's font is a GUI setting — set it to
JetBrainsMono Nerd Font (Settings → Profiles → Text → Font) so the tmux
statusline glyphs render instead of ?.
Editor (Neovim)
nvim — aliased to vi/vim, and set as $EDITOR/$VISUAL — is configured
declaratively with nixvim, so the same plugins and config are baked in on
every host. Migrated from plain vim; the practical gain is a real LSP stack in
place of the old (inert) ALE.
| Feature | Notes |
|---|---|
| Colorscheme | Catppuccin Mocha (matches the terminal and the rest of the desktop) |
| File tree | nvim-tree, toggled with ,, (comma twice; was nerdtree) |
| Fuzzy finder | telescope (+fzf-native): <leader>ff files, <leader>fg grep, <leader>fb buffers |
| Format on save | conform-nvim (nixfmt, stylua, ruff, shfmt, prettier, gofumpt; LSP fallback otherwise) |
| Git | fugitive (:Git …) + gitsigns gutter signs/blame |
| Diagnostics | inline + trouble list (<leader>xx) |
| Completion | nvim-cmp (LSP/buffer/path) with luasnip snippet expansion |
| Indent guides | indent-blankline, on by default (was vim-indent-guides) |
| Statusline | lualine (Catppuccin theme) |
| Editing | which-key hints, comment (gc/gcc), autopairs, treesitter textobjects |
| Pane nav | vim-tmux-navigator — Ctrl+h/j/k/l moves across vim splits and tmux panes |
| Syntax | tree-sitter (nix, lua, bash, markdown, groovy, c#, python, terraform, yaml) |
| LSP | nvim-cmp completion + servers nil_ls (Nix), lua_ls, pyright (Python), terraformls |
| Indentation | 2-wide hard tabs (noexpandtab, tabstop/shiftwidth = 2); line numbers on |
| Filetypes | *Jenkinsfile → groovy |
Leader is Space. LSP keymaps (gd, gr, K, <leader>rn, <leader>ca) and
the file-tree toggle are listed in
KEYBINDINGS.md. Add a universal language server by
enabling it under programs.nixvim.plugins.lsp.servers in editor.nix;
host-specific ones go in that host's module — the work box (work.nix) adds
omnisharp (C#) and helm_ls (Helm), kept off the personal machines.
git
Pager is delta. commitizen is installed on every host; cz defaults to
Conventional Commits. lazygit (themed) is the TUI. The commit-graph is kept
current (gc/fetch.writeCommitGraph) so lg stays fast.
| Aliases | |
|---|---|
st co sw br ci |
status / checkout / switch / branch / commit |
last unstage |
last commit / unstage |
amend fixup undo |
amend-no-edit / commit --fixup / soft-reset HEAD~1 (keep staged) |
lg |
graph log, all branches |
cz cc |
git cz <sub> (e.g. git cz c) and git cc → commitizen prompt |
| Behaviour | |
|---|---|
| Pulls | rebase, with autostash + autosquash |
| Fetch | prune deleted remote branches |
| Conflicts | zdiff3 (shows the common ancestor) |
| Diffs | histogram algorithm, colour-moved |
rerere |
remembers + replays conflict resolutions |
| Commit editor | full diff shown (commit.verbose) |
| Misc | branches sorted by date, column.ui = auto, help.autocorrect = prompt, push.autoSetupRemote |
| Global ignores | result, result-*, .direnv, *.swp, .DS_Store |
| Signing | SSH commit + tag signing (mkDefault, so a host without the key in its agent can disable it). Name, email and signing key all come from the per-user identity (the user registry, ../users/registry.nix). |
ssh
| Feature | Notes |
|---|---|
| ssh-agent | runs on Linux (launchd on macOS); keys added on first use so the passphrase is typed once per login session — this also feeds git commit signing |
| macOS | UseKeychain caches the passphrase in the login keychain (guarded by IgnoreUnknown, so a non-Apple ssh skips it instead of erroring) |
| Gitea remote | code.emmathe.dev → HostName 10.187.1.76 (DNS-override), Port 30009, user git, dedicated key, identitiesOnly |
| Defaults | the module's deprecated default block is opted out; equivalents kept under settings."*" |
The work box keeps its own ~/.ssh/config (home-manager's programs.ssh is
forced off there) but still runs the agent.
Claude Code
Managed declaratively by claude.nix on every host (the CLI is
pkgs.claude-code, tracked to unstable via the flake overlay).
| Managed (static, from Nix) | Left mutable (runtime state) |
|---|---|
~/.claude/CLAUDE.md (persona + memory workflow) |
settings.json (permissions, model, theme, /config) |
~/.claude/output-styles/soviet-engineer.md |
.credentials.json, history, caches |
~/.claude/memory/ (read-only symlink to the repo) |
settings.json is intentionally not managed: Claude rewrites it at runtime
(interactive permission grants, /config), which a read-only store symlink would
break.
Memory is sourced from this repo. The files in
claude/memory/ are the source of truth; they are symlinked
read-only into ~/.claude/memory, so recall works but the runtime "save a
memory" path does not. To add/change/remove a memory, edit claude/memory/
(one file per memory + the MEMORY.md index) and rebuild — CLAUDE.md tells
Claude to route new memories there.
Maintenance behaviours
- zcompdump reset —
~/.config/zsh/.zcompdump*(plus legacy~/.zcompdump*and the cache copy) is removed on every activation, so a stale dump (pointing at/nix/storepaths a rebuild or a manual GC removed) can't break completion with_git: function definition file not found. - GC — no scheduled timer; collect garbage deliberately (
nh clean all/nix-collect-garbage -d) when no important session is running.
Per-host differences
| Personal Linux (sway) | macOS | Work WSL (EDaaS) | |
|---|---|---|---|
| Auto-tmux | yes (foot/TTY) | yes (iTerm2) | yes (WSL shell) |
| git email | iam@emmathe.dev |
iam@emmathe.dev |
…@citrix.com (work) |
| ssh config managed | yes | yes | no (keeps corporate config) |
| ssh-agent | yes | launchd | yes (work module) |
| GUI / theming (desktop.nix) | yes | no | no |