This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
# Build and switch to the macOS nix-darwin configuration
nh darwin switch --no-nom
# Build without switching (for testing)
nh darwin build --no-nom
# Update all flake inputs
nix flake update
# Update a specific input
nix flake update nixpkgs-unstableThis is a Nix flake that manages system configuration for macOS (via nix-darwin) and Linux (via home-manager).
flake.nix- Main entry point defining inputs, outputs, overlays, and system configurationsdarwin/- nix-darwin modules for macOS system-level configurationhome/- home-manager modules for user-level configurationmodules/- Reusable modules (both darwin and home-manager)lib/- Helper functions includingmkDarwinSystemconfigs/- Application configs (Claude Code) symlinked for live editingoverlays/- Nixpkgs overlays
darwinConfigurations.MaloBookPro- Primary macOS config, built withlib.mkDarwinSystemdarwinConfigurations.githubCI- CI variant with homebrew disabledhomeConfigurations.malo- Standalone home-manager config for Linux
User info is defined once and referenced throughout:
- Passed to
lib.mkDarwinSystemasusername,fullName,email,nixConfigDirectory - Set on
users.primaryUserin darwin config - Available as
config.home.user-infoin all home-manager modules
Example usage in a home module:
{ config, ... }:
let
inherit (config.home.user-info) email nixConfigDirectory;
in { ... }Files in configs/ are symlinked via mkOutOfStoreSymlink, allowing edits without rebuild:
configs/claude/→~/.claude/(CLAUDE-USER.md → CLAUDE.md, settings.json, commands, plugins, etc.)
Claude Code settings use a hybrid approach:
managed-settings.json(Nix-generated at/Library/Application Support/ClaudeCode/): Has highest precedence. Contains only path-dependent settings needingnixConfigDirectoryinterpolation (additionalDirectories, nix-config permissions,extraKnownMarketplaces). Created bydarwin/claude-managed-settings.nix.settings.json(user-editable symlink fromconfigs/claude/): All other settings using portable~paths. Edit directly without rebuild.
MCP architecture: Uses 1MCP to aggregate all MCP servers. Server definitions are in
configs/claude/1mcp.json (symlinked to ~/.config/1mcp/mcp.json). CLI connects to 1MCP
via claude mcp add; Desktop connects via proxy in its generated config. 1MCP runs as a
LaunchAgent (com.malo.1mcp, defined in home/claude.nix). After editing 1mcp.json,
restart it:
launchctl kickstart -k gui/$(id -u)/com.malo.1mcpAccess packages from different nixpkgs channels via overlays:
pkgs.pkgs-master.some-package # bleeding edge
pkgs.pkgs-unstable.some-package # nixpkgs-unstable
pkgs.pkgs-stable.some-package # stable release
pkgs.pkgs-x86.some-package # x86 version (Apple Silicon only)Add a package: Edit home/packages.nix, add to the appropriate category's inherit block.
Add a new home-manager module:
- Create
home/tool.nixwith the module configuration - Add
malo-tool = import ./home/tool.nix;tohomeManagerModulesinflake.nix - The module is automatically included via
attrValues self.homeManagerModules
Add a darwin module: Same pattern in darwin/ directory and darwinModules in flake.nix.
Add an external Claude Code skill: Add to externalSkills in home/claude.nix, then rebuild.
Custom skills go in configs/claude/skills/<malo-prefix>/ as regular directories (committed to git).
External skills are symlinks managed by the activation script (gitignored).
Iterate on Claude Code plugins: Plugins in configs/claude/plugins/ are symlinked but cached by Claude. After making changes, nuke the cache and restart:
rm -rf ~/.claude/plugins/cache/malos-plugins/<plugin-name>
# Then restart Claude Code- Follow nixpkgs code style
- Line length: 100 columns
- Section banners:
# Section namefollowed by dashes to column 100, with a blank line before and after - Module files named for their purpose (e.g.,
git.nix,fish.nix) - Home-manager modules prefixed with
malo-in flake outputs - Use
inheritfor clarity when pulling from attribute sets - Extract repeated deep paths into
let/inheritat the top of each file. If a dotted path likeconfig.home.homeDirectory,pkgs.stdenv.isDarwin, orlib.mkIfappears two or more times in a file body, pull it into theletblock (e.g.,inherit (config.home) homeDirectory;). Single-use paths are fine inline — the goal is compactness when repetition adds noise
- Home-manager activation PATH: Activation scripts run with a minimal PATH (bash, coreutils,
findutils, etc.) — no
node,git, or user-profile binaries. Use full nix store paths (e.g.${pkgs.nodejs}/bin/npx) or prepend to PATH explicitly. run --silencehides errors: Home-manager'srun --silenceredirects both stdout and stderr to/dev/null. When debugging activation scripts, temporarily remove--silenceor userunwithout flags to see output. The generated script is at~/.local/state/home-manager/gcroots/current-home/activate.