Skip to content

Latest commit

 

History

History
73 lines (57 loc) · 7.52 KB

File metadata and controls

73 lines (57 loc) · 7.52 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Repository Overview

Cross-platform Nix configuration for macOS (nix-darwin) and NixOS. Manages system and user configurations declaratively using Nix.

Hosts (config attr = hostname for every host):

  • burnedapple — personal Mac (aarch64-darwin), darwinConfigurations, nix-darwin + Home Manager
  • personal-nixos — NixOS shell-only VM in UTM on the personal Mac (aarch64-linux), nixosConfigurations
  • work-nixos — NixOS shell-only VM in VMware Fusion on the work Mac (aarch64-linux), nixosConfigurations

Both NixOS hosts share system/nixos + hosts/common.nix; each pulls its own machine-specific system/nixos/hardware/<host>.nix (generated by nixos-generate-config inside that VM). just switch picks the NixOS config by hostname; pass one explicitly to bootstrap (just switch personal-nixos).

Essential Commands

just switch                   # Apply configuration changes
just check                    # Run all linting checks
just fmt                      # Format all files
nix flake update              # Update all flake inputs
sops secrets/secrets.yaml     # Edit encrypted secrets
git meta <init|link|status>    # Sync shared .meta/ config into worktrees (symlink .envrc/external; copy docs/specs+plans up)

Tools: selene, stylua, alejandra, statix, deadnix, typos, taplo, shellcheck, actionlint, jaq, gitleaks

Architecture

flake.nix                    # Entry point (inputs + module composition)
├── hosts/                   # Host configs: common.nix, macbook.nix, personal-nixos.nix, work-nixos.nix
├── modules/                 # Shared: options.nix, nix.nix, theme.nix
├── system/{darwin,nixos}/   # Platform system configs
├── home/{common,darwin,linux}/ # Home Manager configs
├── claude/                  # Everything Claude: home/, security/, overrides/, package.nix, update.sh, statusline.sh, direnv-rules.sh, direnv-agents.sh
├── config/                  # Dotfiles: nvim/, wezterm/, yazi/, karabiner/
├── llm/                     # Shared assistant content (rules/skills/agents/commands consumed by claude + codex)
├── scripts/                 # Helper scripts (setup, git-meta, git-bare-clone, keymaps, update-codex)
├── tests/                   # Integration tests (claude-security.nix, claude-settings.nix, codex.nix)
├── overlays/                # Custom package overlays
├── patches/                 # Custom patches for packages
└── pkgs/                    # Custom package definitions

Path Templating (configPath)

  • @configPath@ placeholder (wezterm, yazi): Substituted via builtins.replaceStrings in home/common/xdg.nix at build time.
  • nix-info module (nvim): nix-wrapper-modules injects config_directory. init.lua reads _G.nixInfo.settings.config_directory.

Key Patterns

  • config.custom.*: Typed NixOS options in modules/options.nix. All modules consume these instead of extraSpecialArgs. Options defined in hosts/common.nix, overridden per-host.
  • lze plugin loading: Uses on_require, dep_of, on_plugin. Does NOT have a dep field. Library deps in config/nvim/lua/plugins/deps.lua.
  • allowUnfreePredicate: Shared unfree allowlist in flake.nix, applied to both platforms.
  • Claude consolidation: All Claude code lives under claude/ — claude/home.nix is the HM entry, claude/security/ is the security module generating settingsFragment (hooks + permissions), claude/home/settings.nix merges it into ~/.claude/settings.json, claude/package.nix is the vendored binary, claude/overrides/ holds CLAUDE.md and agent overrides. Codex follows the conventional split (pkgs/codex.nix + home/common/codex/ + scripts/update-codex.sh) since it lacks the custom surface area that justifies a top-level dir.
  • Git worktree tools: git bclone (bare clone → .bare/ + sibling worktrees) and git meta (syncs shared .meta/ config into each worktree: .envrc/external are symlinked, docs/specs+docs/plans are real per-worktree dirs copied up into .meta with no overwrite) installed via home/packages.nix.
  • Sandbox wrappers on PATH: config.custom.sandboxedPackages.{claude,codex} (set per platform in home/{darwin,linux}/sandboxed.nix) install binaries named claude-sandboxed / codex-sandboxed via home/common/packages.nix. The a/ar shell functions and the o/or/ox aliases invoke those names, never lib.getExe — a store path baked into zshrc makes a long-lived shell keep launching the version that was current when it started. Plain claude/codex on PATH remain the unsandboxed CLIs.
  • Claude rules (direnv): Language rules stored in ~/.config/claude-rules/ (nix-managed). Use use claude_rules in .envrc to symlink relevant rules into project-local .claude/rules/. Auto-detects languages when called without args. Explicit: use claude_rules go nix. See claude/direnv-rules.sh.
  • Claude agents (direnv): Agents from llm/shared/agents/ are stored in ~/.config/claude-agents/, deliberately not ~/.claude/agents/ — a globally installed agent spends prompt tokens in every session, including projects it can never apply to. programs.direnv.stdlib calls use_claude_agents from ~/.config/direnv/direnvrc, so every direnv project links its relevant agents into project-local .claude/agents/ with no .envrc line. Detection is by project shape (Cargo.toml → rust-engineer, a bevy dep → bevy-engineer, Anchor.toml/anchor-lang → solana-developer, Unity layout or any .asmdef → unity-csharp-engineer); probes are depth-bounded because this runs on entry to every project. A project that matches nothing links nothing and leaves no directory behind. Override explicitly with use claude_agents rust-engineer in .envrc. See claude/direnv-agents.sh. Codex is unaffected; it still installs all agents to ~/.codex/agents/.
  • MCP tool-surface budget: Every MCP tool name is spent in each session's prompt, so servers are narrowed at the wrapper. github runs with --toolsets/--exclude-tools (44 default tools → 31, see home/common/mcp.nix); gitlab is gated behind custom.gitlab.enable (work host only) and runs read-only.
  • MR review (<leader>cv): config/nvim/lua/plugins/review.lua holds both halves. The local ones (diff vs the base branch, commit-by-commit walk, difftastic, glab MR picker) work in any repo on any forge and always diff base...HEAD, since base..HEAD would also show whatever landed on the base branch after the fork. The gitlab.nvim ones (discussion tree, line comments, approvals) only register when custom.gitlab.enable is set, which also gates the Go server build (gitlab-nvim-server in home/common/neovim/module.nix, pinned with the gitlab-nvim flake input — bumping the tag needs a new vendorHash). The prebuilt binary is what makes the plugin skip its own go build at runtime. diffview carries on_require because gitlab.nvim pulls in diffview.async before any Diffview* command runs.
  • glab aliases: written by a home.activation copy at 0600, not xdg.configFile — glab refuses to read a config file that is not mode 600 and then fails every command, and a store symlink is 444.

Secrets Management

SOPS with age encryption. Key: ~/.config/sops/age/key.txt

sops secrets/secrets.yaml              # Edit secrets
# Define in nix: sops.secrets.my-secret = { };
# Access at runtime: /run/secrets/<secret-name>