Closes #39. nvim-cmp ships no default keymaps, so the completion menu (including the path source) appeared but nothing could navigate or accept it. Bind the usual set in `plugins.cmp.settings.mapping`: - `<C-n>` / `<C-p>` and `<Tab>` / `<S-Tab>` — select next/previous - `<C-Space>` — open the menu - `<C-e>` — abort - `<CR>` — confirm with `select = false` (bare Enter stays a newline unless an entry is highlighted) Documentation: `KEYBINDINGS.md` gains a completion-menu table under the Neovim section covering these keys, and the Neovim summary is reworded accordingly. Verified by rendering the generated nvim config: the mappings emit as raw Lua (e.g. `["<CR>"] = cmp.mapping.confirm({ select = false })`), not quoted strings. --------- Co-authored-by: Emma Thorpe <emma.thorpe@citrix.com> Reviewed-on: #40
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 (work email, its own ssh config, extra packages,
and the C#/Helm language servers).
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 ../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 (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). Personal email iam@emmathe.dev; the work box overrides email + signing. |
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 |