Files
dotfiles/home.nix
T
Lap Tran ab3b67af92 gate: add kunchenguid/no-mistakes for all Nix harnesses
- home.nix: add home.activation.noMistakes (declarative install to
  ~/.no-mistakes/bin + daemon via launchd, global config fallback
  agent: [pi, opencode, grok, claude, codex] covering every harness
  declared in Nix; fix shadowing of jonathanong npm no-mistakes)
- ~/.no-mistakes/config.yaml: switched from auto to ordered fallback,
  pi first (firstmate crewmates), then opencode/grok
- home/.pi/agent/*, home/.grok/config.toml, home/.config/opencode2:
  align to oMLX/qwen3.6-35b local model (was mixed Talos/genesis)
- rebuild.sh: ensure grok-build upgrade and OpenCode 1.x install on
  rebuild (stable CLI for free-tier, opencode2 beta is 426 there)
- dotfiles repo: no-mistakes init (gate at
  ~/.no-mistakes/repos/916cb6ded81b.git, remote no-mistakes)
2026-09-21 17:38:35 -04:00

269 lines
11 KiB
Nix
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{ config, pkgs, lib, ... }:
let
home = config.home.homeDirectory;
dotfiles = "${home}/.dotfiles";
firstmateHome = "${home}/Documents/firstmate";
in
{
home.username = "laptran";
home.homeDirectory = "/Users/laptran";
home.stateVersion = "24.11";
# home-configuration.nix(5) is generated via options.json that embeds a
# nixpkgs store path without string context. Determinate Nix warns on every
# rebuild. Package man pages are unaffected.
manual.manpages.enable = false;
home.packages = with pkgs; [
ripgrep
fd
fzf
jq
lazygit
neovim
nerd-fonts.hack
ngrok # reverse TCP tunnel so the phone can SSH in over cellular (unfree)
beets # music tagger / library organizer + navidrome sync plugin
];
fonts.fontconfig.enable = true;
home.sessionVariables.EDITOR = "nvim";
programs.zsh = {
enable = true;
autosuggestion.enable = true; # ghost text from history
syntaxHighlighting.enable = true; # commands turn green when valid
initContent = ''
bindkey '^f' autosuggest-accept
'';
shellAliases = {
".." = "cd ..";
ll = "ls -plart";
add = "git add .";
push = "git push";
pull = "git pull";
m = "git switch main";
# High-agency agent launchers (same idea across tools)
cc = "claude --dangerously-skip-permissions";
co = "codex --full-auto";
gb = "grok --yolo";
# firstmate primary session via OpenCode 2 (isolated config; crewmates = Pi)
fm = "cd ${firstmateHome} && exec ${home}/.local/bin/oc2";
oc2 = "${home}/.local/bin/oc2";
# Sync ~/Music into the beets library on Unraid (requires NAS mounted).
sync-music = "~/.local/bin/sync-music";
# Start the reverse tunnel so you can SSH into this Mac from your phone.
# Run once when you go remote: `ngrok-tunnel`. Reads authtoken from
# ~/.config/ngrok (set once per machine with `ngrok config add-authtoken <TOK>`)
ngrok-tunnel = "ngrok tcp 22";
};
};
programs.starship = {
enable = true;
settings = {
add_newline = false;
format = "$directory$git_branch$git_status$cmd_duration$line_break$character";
character = {
success_symbol = "[❯](purple)";
error_symbol = "[❯](red)";
};
cmd_duration.format = "[$duration]($style) ";
};
};
# Edit-in-place: real file stays in the repo; live path is an out-of-store symlink.
# force = true: replace a pre-existing regular file once; source of truth is home/.
home.file.".config/wezterm" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.config/wezterm";
force = true;
};
home.file.".config/nvim" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.config/nvim";
force = true;
};
home.file.".config/herdr" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.config/herdr";
force = true;
};
# Beets music library manager - managed by nixpkgs package, config symlinked below.
home.file.".config/beets" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.config/beets";
force = true;
};
# Claude Code settings (also read by Grok for permissions/compat)
home.file.".claude/settings.json" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.claude/settings.json";
force = true;
};
# Shared agent policy - one file, many harnesses
home.file.".claude/CLAUDE.md" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/AGENTS.md";
force = true;
};
home.file.".codex/AGENTS.md" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/AGENTS.md";
force = true;
};
home.file.".config/opencode/AGENTS.md" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/AGENTS.md";
force = true;
};
# OpenCode 2 isolated config - never share ~/.config/opencode with 1.x
home.file.".config/opencode2/opencode.json" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.config/opencode2/opencode.json";
force = true;
};
home.file.".config/opencode2/AGENTS.md" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/AGENTS.md";
force = true;
};
home.file.".local/bin/oc2" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/bin/oc2";
force = true;
};
home.file.".grok/AGENTS.md" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/AGENTS.md";
force = true;
};
# Grok Build native config
home.file.".grok/config.toml" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.grok/config.toml";
force = true;
};
# Pi agent - source of truth under home/.pi (sessions/auth/npm stay live under ~/.pi)
home.file.".pi/agent/settings.json" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.pi/agent/settings.json";
force = true;
};
home.file.".pi/agent/models.json" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.pi/agent/models.json";
force = true;
};
home.file.".pi/agent/themes" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.pi/agent/themes";
force = true;
};
home.file.".pi/agent/extensions/terminal-status-title.js" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.pi/agent/extensions/terminal-status-title.js";
force = true;
};
home.file.".pi/agent/extensions/calm" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.pi/agent/extensions/calm";
force = true;
};
home.file.".pi/agent/AGENTS.md" = {
source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/AGENTS.md";
force = true;
};
# firstmate is a mutable agent distro (self-update, state/, projects/). Clone once;
# never put it in the Nix store. Seed Pi+herdr defaults only when absent.
# Do NOT npm install -g here: activation PATH often resolves Nix's npm, which
# cannot write into the store (EACCES). Use Homebrew's node for globals:
# /opt/homebrew/bin/npm install -g tasks-axi quota-axi no-mistakes gh-axi lavish-axi chrome-devtools-axi
home.activation.firstmate = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
set -euo pipefail
fm="${firstmateHome}"
git="${pkgs.git}/bin/git"
if [ ! -d "$fm/.git" ]; then
mkdir -p "$(dirname "$fm")"
$git clone https://github.com/kunchenguid/firstmate.git "$fm"
fi
mkdir -p "$fm/config" "$fm/data" "$fm/state" "$fm/projects"
# Local gitignored operating choices (do not overwrite captain edits)
[ -f "$fm/config/backend" ] || printf 'herdr\n' > "$fm/config/backend"
[ -f "$fm/config/crew-harness" ] || printf 'pi\n' > "$fm/config/crew-harness"
# Crew dispatch profile - source of truth in dotfiles (reproducible across
# machines). Symlinked into firstmate config so a rebuild restores routing.
# Crewmates run on Pi + opencode-go/mimo-v2.5-pro (see crew-dispatch.json).
ln -sfn "${dotfiles}/home/.config/firstmate/crew-dispatch.json" "$fm/config/crew-dispatch.json"
# Gitea PR helper for local-only mode: after the first mate (opencode)
# reviews a crewmate branch, this pushes it + opens a Gitea PR via tea.
ln -sfn "${dotfiles}/home/bin/fm-gitea-pr.sh" "$HOME/.local/bin/fm-gitea-pr.sh"
'';
# Authorize the SSH key so the phone can log in through the ngrok tunnel.
# (home-manager 26.05 removed programs.ssh.authorizedKeys; manage the file
# here so ~/.ssh is 0700 and authorized_keys is 0600. Public key is not a secret.)
home.activation.authorizeSSHKey = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
set -euo pipefail
mkdir -p "$HOME/.ssh"
chmod 700 "$HOME/.ssh"
printf '%s\n' "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGCZEGVYMDztSryFwoZ6cfpBH3ksP3h0yxZSanlcbrZ0 unraid-omada" > "$HOME/.ssh/authorized_keys"
chmod 600 "$HOME/.ssh/authorized_keys"
'';
# Wire the sync-music script into ~/.local/bin without touching anything else
# that lives there (node, python3.11, hermes, etc.). Source of truth is the
# dotfiles repo so a rebuild restores it if it ever disappears.
home.activation.syncMusic = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
set -euo pipefail
ln -sfn "${dotfiles}/home/bin/sync-music" "$HOME/.local/bin/sync-music"
'';
# OpenCode 2 is not on Homebrew (beta). Install via Homebrew's npm, never
# Nix's npm (EACCES on the store). Skip when already present so a rebuild
# does not pull a new beta under a live TUI.
home.activation.opencode2 = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
set -euo pipefail
npm=/opt/homebrew/bin/npm
bin=/opt/homebrew/bin/opencode2
if [ -x "$bin" ]; then
exit 0
fi
if [ ! -x "$npm" ]; then
echo "opencode2: /opt/homebrew/bin/npm missing (declare node in homebrew.brews)" >&2
exit 1
fi
"$npm" install -g --allow-scripts=@opencode-ai/cli @opencode-ai/cli@beta
'';
# kunchenguid/no-mistakes gate (git push no-mistakes) - declarative install + daemon + global config
# Covers every harness declared in Nix: pi (pi-coding-agent), opencode (oc2/fm), grok (grok-build),
# plus claude/codex aspirational (aliases cc/co). Binary lives in ~/.no-mistakes/bin
# with symlink ~/.local/bin/no-mistakes (installer default). Do NOT npm install -g no-mistakes
# - that npm name is jonathanong's static-analysis tool (shadowing bug fixed 2026-09-21).
home.activation.noMistakes = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
set -euo pipefail
bin="$HOME/.no-mistakes/bin/no-mistakes"
link="$HOME/.local/bin/no-mistakes"
# Install/refresh via upstream installer if missing or not kunchenguid build
if [ ! -x "$bin" ] || ! "$bin" --version 2>/dev/null | grep -q "kunchenguid\|v1\."; then
if [ -x "$bin" ]; then
echo "no-mistakes: replacing unexpected binary at $bin" >&2
fi
curl -fsSL https://raw.githubusercontent.com/kunchenguid/no-mistakes/main/docs/install.sh | sh
fi
# Ensure global config covers all Nix harnesses (idempotent - only writes if missing or still `agent: auto`)
cfg="$HOME/.no-mistakes/config.yaml"
if [ -f "$cfg" ] && grep -qE '^\s*agent:\s*auto\s*$' "$cfg"; then
tmp="$(mktemp)"
awk '
/^\s*agent:\s*auto\s*$/ {
print "# Managed by dotfiles/home.nix home.activation.noMistakes";
print "agent: [pi, opencode, grok, claude, codex]";
next
}
{ print }
' "$cfg" > "$tmp" && mv "$tmp" "$cfg"
fi
if [ ! -f "$cfg" ]; then
mkdir -p "$(dirname "$cfg")"
printf 'agent: [pi, opencode, grok, claude, codex]\n' > "$cfg"
fi
# Ensure daemon running (launchd on macOS)
"$bin" daemon restart >/dev/null 2>&1 || "$bin" daemon start >/dev/null 2>&1 || true
'';
}