From 2c692550db3c9be92097da0f69ff4bda16829557 Mon Sep 17 00:00:00 2001 From: Miguel Palhas Date: Tue, 25 Aug 2026 08:27:39 +0100 Subject: [PATCH] chore(claude): stop managing settings.json Claude Code rewrites ~/.claude/settings.json itself, so the nix copy drifted within every session and `nh home switch` aborted on the diff. Drops the mutableFiles entries and the two tracked JSON sources. Co-Authored-By: Claude Opus 5 (1M context) --- home/common/features/mutable-file.nix | 2 +- home/common/programs/claude/default.nix | 4 +- home/common/programs/claude/settings.json | 86 ------- home/yolo/claude-settings.json | 301 ---------------------- home/yolo/default.nix | 23 +- 5 files changed, 7 insertions(+), 409 deletions(-) delete mode 100644 home/common/programs/claude/settings.json delete mode 100644 home/yolo/claude-settings.json diff --git a/home/common/features/mutable-file.nix b/home/common/features/mutable-file.nix index 80697d9..4d99bd1 100644 --- a/home/common/features/mutable-file.nix +++ b/home/common/features/mutable-file.nix @@ -75,7 +75,7 @@ in upstreamPath = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; - example = "home/yolo/claude-settings.json"; + example = "home/yolo/aoe-config.toml"; description = '' Repo-relative file to name in the "bring changes upstream" hint. Required when `source` is generated rather than a file in the diff --git a/home/common/programs/claude/default.nix b/home/common/programs/claude/default.nix index addb4d1..299d673 100644 --- a/home/common/programs/claude/default.nix +++ b/home/common/programs/claude/default.nix @@ -19,6 +19,9 @@ description = "--force-device-scale-factor value for Claude Desktop and T3 Code."; }; + # ~/.claude/settings.json stays unmanaged: Claude Code rewrites it itself + # (model pins, permission grants, plugin state), so any nix copy drifts within + # a session and every `nh home switch` then aborts on the diff. config.home = { packages = with pkgs; [ inputs.claude-code.packages.${pkgs.stdenv.hostPlatform.system}.default @@ -47,6 +50,5 @@ }; }; - mutableFiles.".claude/settings.json".source = ./settings.json; }; } diff --git a/home/common/programs/claude/settings.json b/home/common/programs/claude/settings.json deleted file mode 100644 index 0355fab..0000000 --- a/home/common/programs/claude/settings.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/claude-code-settings.json", - "permissions": { - "allow": [ - "Edit", - "Write", - "Bash(ls:*)", - "Bash(tree:*)", - "Bash(cat:*)", - "Bash(head:*)", - "Bash(tail:*)", - "Bash(find:*)", - "Bash(fd:*)", - "Bash(grep:*)", - "Bash(rg:*)", - "Bash(wc:*)", - "Bash(sort:*)", - "Bash(uniq:*)", - "Bash(diff:*)", - "Bash(pwd:*)", - "Bash(which:*)", - "Bash(jq:*)", - "Bash(git:*)", - "Bash(gh:*)", - "Bash(sed:*)", - "Bash(cp:*)", - "Bash(chmod:*)", - "Bash(mkdir:*)", - "Bash(kitty @ set-tab-title:*)" - ], - "additionalDirectories": [ - "/home/naps62/projects", - "/home/naps62/ethui", - "/home/naps62/labs", - "/home/naps62/subvisual" - ] - }, - "model": "opus[1m]", - "statusLine": { - "type": "command", - "command": "~/.claude/statusline.sh" - }, - "enabledPlugins": { - "typescript-lsp@claude-plugins-official": true - }, - "extraKnownMarketplaces": { - "impeccable": { - "source": { - "source": "github", - "repo": "pbakaus/impeccable" - } - } - }, - "sandbox": { - "enabled": true, - "autoAllowBashIfSandboxed": false, - "allowedNetworkHosts": [ - "github.com", - "api.github.com", - "raw.githubusercontent.com", - "gist.github.com", - "release-assets.githubusercontent.com" - ] - }, - "tui": "fullscreen", - "voiceEnabled": true, - "defaultMode": "acceptEdits", - "feedbackSurveyState": { - "lastShownTime": 1754052643456 - }, - "mcpServers": { - "herd-mcp": { - "type": "http", - "url": "https://mcp.herd.eco/v1" - }, - "linear": { - "type": "http", - "url": "https://mcp.linear.app/mcp" - }, - "home-assistant": { - "type": "http", - "url": "https://ha-mcp.n62.casa/mcp" - } - }, - "agentPushNotifEnabled": true -} diff --git a/home/yolo/claude-settings.json b/home/yolo/claude-settings.json deleted file mode 100644 index 9e644cb..0000000 --- a/home/yolo/claude-settings.json +++ /dev/null @@ -1,301 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/claude-code-settings.json", - "permissions": { - "allow": [ - "Bash", - "Edit", - "Write", - "NotebookEdit", - "WebFetch", - "WebSearch" - ], - "deny": [ - "Read(~/.ssh/**)", - "Read(*.pem)", - "Read(*.key)", - "mcp__dokploy__application-one", - "mcp__dokploy__mounts-one", - "mcp__dokploy__schedule-create", - "mcp__dokploy__schedule-update", - "mcp__dokploy__schedule-runManually" - ], - "defaultMode": "auto", - "additionalDirectories": [ - "/home/naps62/projects", - "/home/naps62/ethui", - "/home/naps62/labs", - "/home/naps62/subvisual" - ] - }, - "model": "sonnet", - "hooks": { - "ElicitationResult": [ - { - "hooks": [ - { - "type": "command", - "command": "sh -c 'unset IFS; set -f; umask 077; [ -n \"$AOE_INSTANCE_ID\" ] || exit 0; case \"$AOE_INSTANCE_ID\" in *[!0-9a-zA-Z_-]*) exit 0 ;; esac; B=/tmp/aoe-hooks-1000; mkdir -p \"$B\" 2>/dev/null || exit 0; LS=$(LC_ALL=C ls -ldn \"$B\" 2>/dev/null) || exit 0; set -- $LS; M=\"$1\"; case \"$M\" in drwx------|drwx------.|drwx------+|drwx------@) ;; *) exit 0 ;; esac; ME=$(id -u 2>/dev/null) || exit 0; [ \"$3\" = \"$ME\" ] || exit 0; D=\"$B/$AOE_INSTANCE_ID\"; mkdir -p \"$D\" 2>/dev/null; LS=$(LC_ALL=C ls -ldn \"$D\" 2>/dev/null) || exit 0; set -- $LS; M=\"$1\"; case \"$M\" in drwx------|drwx------.|drwx------+|drwx------@) ;; *) exit 0 ;; esac; [ \"$3\" = \"$ME\" ] || exit 0; printf running > \"$D/status\" 2>/dev/null; exit 0 # aoe-hooks'" - } - ] - } - ], - "Notification": [ - { - "hooks": [ - { - "type": "command", - "command": "~/.claude/hooks/tmux-attention.sh" - } - ] - }, - { - "matcher": "permission_prompt|elicitation_dialog|agent_needs_input", - "hooks": [ - { - "type": "command", - "command": "sh -c 'unset IFS; set -f; umask 077; [ -n \"$AOE_INSTANCE_ID\" ] || exit 0; case \"$AOE_INSTANCE_ID\" in *[!0-9a-zA-Z_-]*) exit 0 ;; esac; B=/tmp/aoe-hooks-1000; mkdir -p \"$B\" 2>/dev/null || exit 0; LS=$(LC_ALL=C ls -ldn \"$B\" 2>/dev/null) || exit 0; set -- $LS; M=\"$1\"; case \"$M\" in drwx------|drwx------.|drwx------+|drwx------@) ;; *) exit 0 ;; esac; ME=$(id -u 2>/dev/null) || exit 0; [ \"$3\" = \"$ME\" ] || exit 0; D=\"$B/$AOE_INSTANCE_ID\"; mkdir -p \"$D\" 2>/dev/null; LS=$(LC_ALL=C ls -ldn \"$D\" 2>/dev/null) || exit 0; set -- $LS; M=\"$1\"; case \"$M\" in drwx------|drwx------.|drwx------+|drwx------@) ;; *) exit 0 ;; esac; [ \"$3\" = \"$ME\" ] || exit 0; printf waiting > \"$D/status\" 2>/dev/null; exit 0 # aoe-hooks'" - } - ] - }, - { - "matcher": "idle_prompt|agent_completed", - "hooks": [ - { - "type": "command", - "command": "sh -c 'unset IFS; set -f; umask 077; [ -n \"$AOE_INSTANCE_ID\" ] || exit 0; case \"$AOE_INSTANCE_ID\" in *[!0-9a-zA-Z_-]*) exit 0 ;; esac; B=/tmp/aoe-hooks-1000; mkdir -p \"$B\" 2>/dev/null || exit 0; LS=$(LC_ALL=C ls -ldn \"$B\" 2>/dev/null) || exit 0; set -- $LS; M=\"$1\"; case \"$M\" in drwx------|drwx------.|drwx------+|drwx------@) ;; *) exit 0 ;; esac; ME=$(id -u 2>/dev/null) || exit 0; [ \"$3\" = \"$ME\" ] || exit 0; D=\"$B/$AOE_INSTANCE_ID\"; mkdir -p \"$D\" 2>/dev/null; LS=$(LC_ALL=C ls -ldn \"$D\" 2>/dev/null) || exit 0; set -- $LS; M=\"$1\"; case \"$M\" in drwx------|drwx------.|drwx------+|drwx------@) ;; *) exit 0 ;; esac; [ \"$3\" = \"$ME\" ] || exit 0; printf idle > \"$D/status\" 2>/dev/null; exit 0 # aoe-hooks'" - } - ] - } - ], - "PostToolUse": [ - { - "matcher": "Write|Edit|MultiEdit", - "hooks": [ - { - "type": "command", - "command": "~/.claude/hooks/comment-lint.py" - } - ] - }, - { - "matcher": "AskUserQuestion", - "hooks": [ - { - "type": "command", - "command": "sh -c 'unset IFS; set -f; umask 077; [ -n \"$AOE_INSTANCE_ID\" ] || exit 0; case \"$AOE_INSTANCE_ID\" in *[!0-9a-zA-Z_-]*) exit 0 ;; esac; B=/tmp/aoe-hooks-1000; mkdir -p \"$B\" 2>/dev/null || exit 0; LS=$(LC_ALL=C ls -ldn \"$B\" 2>/dev/null) || exit 0; set -- $LS; M=\"$1\"; case \"$M\" in drwx------|drwx------.|drwx------+|drwx------@) ;; *) exit 0 ;; esac; ME=$(id -u 2>/dev/null) || exit 0; [ \"$3\" = \"$ME\" ] || exit 0; D=\"$B/$AOE_INSTANCE_ID\"; mkdir -p \"$D\" 2>/dev/null; LS=$(LC_ALL=C ls -ldn \"$D\" 2>/dev/null) || exit 0; set -- $LS; M=\"$1\"; case \"$M\" in drwx------|drwx------.|drwx------+|drwx------@) ;; *) exit 0 ;; esac; [ \"$3\" = \"$ME\" ] || exit 0; printf running > \"$D/status\" 2>/dev/null; exit 0 # aoe-hooks'" - } - ] - } - ], - "PreToolUse": [ - { - "matcher": "Bash", - "hooks": [ - { - "type": "command", - "command": "~/.claude/hooks/comms-lint.py" - } - ] - }, - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "~/.claude/hooks/tmux-reset.sh" - } - ] - }, - { - "matcher": "Bash", - "hooks": [ - { - "type": "command", - "command": "rtk hook claude" - } - ] - }, - { - "matcher": "Bash|Write|Edit|MultiEdit|NotebookEdit", - "hooks": [ - { - "type": "command", - "command": "sh -c 'for H in ~/.claude/hooks/secret-guard.py /home/naps62/tea/agent-skills/hooks/secret-guard.py /home/naps62/tea/agent-skills/worktrees/week-review-2026-w34/hooks/secret-guard.py; do [ -e \"$H\" ] && exec \"$H\"; done; exit 0'" - } - ] - }, - { - "hooks": [ - { - "type": "command", - "command": "sh -c 'unset IFS; set -f; umask 077; [ -n \"$AOE_INSTANCE_ID\" ] || exit 0; case \"$AOE_INSTANCE_ID\" in *[!0-9a-zA-Z_-]*) exit 0 ;; esac; B=/tmp/aoe-hooks-1000; mkdir -p \"$B\" 2>/dev/null || exit 0; LS=$(LC_ALL=C ls -ldn \"$B\" 2>/dev/null) || exit 0; set -- $LS; M=\"$1\"; case \"$M\" in drwx------|drwx------.|drwx------+|drwx------@) ;; *) exit 0 ;; esac; ME=$(id -u 2>/dev/null) || exit 0; [ \"$3\" = \"$ME\" ] || exit 0; D=\"$B/$AOE_INSTANCE_ID\"; mkdir -p \"$D\" 2>/dev/null; LS=$(LC_ALL=C ls -ldn \"$D\" 2>/dev/null) || exit 0; set -- $LS; M=\"$1\"; case \"$M\" in drwx------|drwx------.|drwx------+|drwx------@) ;; *) exit 0 ;; esac; [ \"$3\" = \"$ME\" ] || exit 0; IN=$(cat 2>/dev/null); S=running; case \"$IN\" in *\\\"tool_name\\\":\\\"AskUserQuestion\\\"*) S=waiting ;; esac; printf %s \"$S\" > \"$D/status\" 2>/dev/null; exit 0 # aoe-hooks'" - } - ] - } - ], - "SessionStart": [ - { - "matcher": "startup|resume|clear", - "hooks": [ - { - "type": "command", - "command": "~/.claude/hooks/git-autoupdate.sh" - } - ] - }, - { - "hooks": [ - { - "type": "command", - "command": "python3 ~/.claude/scripts/aoe-register-remote.py" - } - ] - }, - { - "hooks": [ - { - "type": "command", - "command": "/home/naps62/tea/rev/scripts/rev-hook-session-start.sh" - } - ] - }, - { - "hooks": [ - { - "type": "command", - "command": "sh -c '[ -n \"$AOE_INSTANCE_ID\" ] || exit 0; command -v aoe >/dev/null 2>&1 || exit 0; aoe __extract-session-id 2>/dev/null; exit 0 # aoe-hooks'" - } - ] - } - ], - "Stop": [ - { - "hooks": [ - { - "type": "command", - "command": "/home/naps62/tea/rev/scripts/rev-hook-stop.sh" - } - ] - }, - { - "hooks": [ - { - "type": "command", - "command": "sh -c 'unset IFS; set -f; umask 077; [ -n \"$AOE_INSTANCE_ID\" ] || exit 0; case \"$AOE_INSTANCE_ID\" in *[!0-9a-zA-Z_-]*) exit 0 ;; esac; B=/tmp/aoe-hooks-1000; mkdir -p \"$B\" 2>/dev/null || exit 0; LS=$(LC_ALL=C ls -ldn \"$B\" 2>/dev/null) || exit 0; set -- $LS; M=\"$1\"; case \"$M\" in drwx------|drwx------.|drwx------+|drwx------@) ;; *) exit 0 ;; esac; ME=$(id -u 2>/dev/null) || exit 0; [ \"$3\" = \"$ME\" ] || exit 0; D=\"$B/$AOE_INSTANCE_ID\"; mkdir -p \"$D\" 2>/dev/null; LS=$(LC_ALL=C ls -ldn \"$D\" 2>/dev/null) || exit 0; set -- $LS; M=\"$1\"; case \"$M\" in drwx------|drwx------.|drwx------+|drwx------@) ;; *) exit 0 ;; esac; [ \"$3\" = \"$ME\" ] || exit 0; printf idle > \"$D/status\" 2>/dev/null; exit 0 # aoe-hooks'" - } - ] - } - ], - "StopFailure": [ - { - "hooks": [ - { - "type": "command", - "command": "sh -c 'unset IFS; set -f; umask 077; [ -n \"$AOE_INSTANCE_ID\" ] || exit 0; case \"$AOE_INSTANCE_ID\" in *[!0-9a-zA-Z_-]*) exit 0 ;; esac; B=/tmp/aoe-hooks-1000; mkdir -p \"$B\" 2>/dev/null || exit 0; LS=$(LC_ALL=C ls -ldn \"$B\" 2>/dev/null) || exit 0; set -- $LS; M=\"$1\"; case \"$M\" in drwx------|drwx------.|drwx------+|drwx------@) ;; *) exit 0 ;; esac; ME=$(id -u 2>/dev/null) || exit 0; [ \"$3\" = \"$ME\" ] || exit 0; D=\"$B/$AOE_INSTANCE_ID\"; mkdir -p \"$D\" 2>/dev/null; LS=$(LC_ALL=C ls -ldn \"$D\" 2>/dev/null) || exit 0; set -- $LS; M=\"$1\"; case \"$M\" in drwx------|drwx------.|drwx------+|drwx------@) ;; *) exit 0 ;; esac; [ \"$3\" = \"$ME\" ] || exit 0; printf idle > \"$D/status\" 2>/dev/null; exit 0 # aoe-hooks'" - } - ] - } - ], - "UserPromptSubmit": [ - { - "hooks": [ - { - "type": "command", - "command": "sh -c '[ -n \"$AOE_INSTANCE_ID\" ] || exit 0; command -v aoe >/dev/null 2>&1 || exit 0; aoe __extract-session-id 2>/dev/null; exit 0 # aoe-hooks'" - }, - { - "type": "command", - "command": "sh -c 'unset IFS; set -f; umask 077; [ -n \"$AOE_INSTANCE_ID\" ] || exit 0; case \"$AOE_INSTANCE_ID\" in *[!0-9a-zA-Z_-]*) exit 0 ;; esac; B=/tmp/aoe-hooks-1000; mkdir -p \"$B\" 2>/dev/null || exit 0; LS=$(LC_ALL=C ls -ldn \"$B\" 2>/dev/null) || exit 0; set -- $LS; M=\"$1\"; case \"$M\" in drwx------|drwx------.|drwx------+|drwx------@) ;; *) exit 0 ;; esac; ME=$(id -u 2>/dev/null) || exit 0; [ \"$3\" = \"$ME\" ] || exit 0; D=\"$B/$AOE_INSTANCE_ID\"; mkdir -p \"$D\" 2>/dev/null; LS=$(LC_ALL=C ls -ldn \"$D\" 2>/dev/null) || exit 0; set -- $LS; M=\"$1\"; case \"$M\" in drwx------|drwx------.|drwx------+|drwx------@) ;; *) exit 0 ;; esac; [ \"$3\" = \"$ME\" ] || exit 0; printf running > \"$D/status\" 2>/dev/null; exit 0 # aoe-hooks'" - } - ] - } - ] - }, - "worktree": { - "baseRef": "fresh" - }, - "statusLine": { - "type": "command", - "command": "~/.claude/statusline.sh" - }, - "enabledPlugins": { - "building@subvisual": true, - "caveman@caveman": true, - "rust-analyzer-lsp@claude-plugins-official": false, - "typescript-lsp@claude-plugins-official": true - }, - "extraKnownMarketplaces": { - "caveman": { - "source": { - "source": "github", - "repo": "JuliusBrussee/caveman" - } - }, - "impeccable": { - "source": { - "source": "github", - "repo": "pbakaus/impeccable" - } - }, - "subvisual": { - "source": { - "source": "github", - "repo": "subvisual/harness" - } - }, - "superpowers-marketplace": { - "source": { - "source": "github", - "repo": "obra/superpowers-marketplace" - } - } - }, - "sandbox": { - "enabled": false, - "autoAllowBashIfSandboxed": false, - "allowedNetworkHosts": [ - "github.com", - "api.github.com", - "raw.githubusercontent.com", - "gist.github.com", - "release-assets.githubusercontent.com" - ] - }, - "tui": "fullscreen", - "skipDangerousModePermissionPrompt": true, - "theme": "dark", - "editorMode": "vim", - "verbose": false, - "preferredNotifChannel": "auto", - "autoCompactEnabled": true, - "remoteControlAtStartup": false, - "inputNeededNotifEnabled": true, - "agentPushNotifEnabled": true, - "voiceEnabled": true, - "defaultMode": "acceptEdits", - "feedbackSurveyState": { - "lastShownTime": 1754052643456 - }, - "mcpServers": { - "herd-mcp": { - "type": "http", - "url": "https://mcp.herd.eco/v1" - }, - "home-assistant": { - "type": "http", - "url": "https://ha-mcp.n62.casa/mcp" - }, - "linear": { - "type": "http", - "url": "https://mcp.linear.app/mcp" - }, - "playwright": { - "args": [ - "-y", - "@playwright/mcp@latest" - ], - "command": "npx", - "type": "stdio" - } - } -} diff --git a/home/yolo/default.nix b/home/yolo/default.nix index 715c828..6874ff1 100644 --- a/home/yolo/default.nix +++ b/home/yolo/default.nix @@ -4,19 +4,9 @@ ... }: let - claudeSettings = "home/yolo/claude-settings.json"; - - # ./claude-settings.json holds only what yolo overrides; everything else is - # inherited so common changes reach this host. Attrsets merge key-by-key, - # lists are replaced whole (permissions.allow is yolo's, not a union). - mergedClaudeSettings = (pkgs.formats.json { }).generate "claude-settings.json" ( - lib.recursiveUpdate (lib.importJSON ../common/programs/claude/settings.json) ( - lib.importJSON ./claude-settings.json - ) - ); - - # Same merge, same reason: ./opencode.json holds only yolo's overrides so - # agents, commands and skills keep coming from common. + # ./opencode.json holds only yolo's overrides; everything else is inherited + # so agents, commands and skills keep coming from common. Attrsets merge + # key-by-key, lists are replaced whole. mergedOpencodeConfig = (pkgs.formats.json { }).generate "opencode.json" ( lib.recursiveUpdate (lib.importJSON ../common/programs/opencode/opencode.json) ( lib.importJSON ./opencode.json @@ -68,13 +58,6 @@ in # Host-local, not shared: this sets yolo_mode_default = true, which starts # aoe sessions with permission checks skipped. Only correct on this box. ".config/agent-of-empires/config.toml".source = ./aoe-config.toml; - - # Likewise host-local: carries skipDangerousModePermissionPrompt and the - # rev hook paths, neither of which belong on a workstation. - ".claude/settings.json" = { - source = lib.mkForce mergedClaudeSettings; - upstreamPath = claudeSettings; - }; }; };