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) <noreply@anthropic.com>
This commit is contained in:
Miguel Palhas
2026-08-25 08:27:39 +01:00
parent a23488dc52
commit 2c692550db
5 changed files with 7 additions and 409 deletions
+1 -1
View File
@@ -75,7 +75,7 @@ in
upstreamPath = lib.mkOption { upstreamPath = lib.mkOption {
type = lib.types.nullOr lib.types.str; type = lib.types.nullOr lib.types.str;
default = null; default = null;
example = "home/yolo/claude-settings.json"; example = "home/yolo/aoe-config.toml";
description = '' description = ''
Repo-relative file to name in the "bring changes upstream" hint. Repo-relative file to name in the "bring changes upstream" hint.
Required when `source` is generated rather than a file in the Required when `source` is generated rather than a file in the
+3 -1
View File
@@ -19,6 +19,9 @@
description = "--force-device-scale-factor value for Claude Desktop and T3 Code."; 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 = { config.home = {
packages = with pkgs; [ packages = with pkgs; [
inputs.claude-code.packages.${pkgs.stdenv.hostPlatform.system}.default inputs.claude-code.packages.${pkgs.stdenv.hostPlatform.system}.default
@@ -47,6 +50,5 @@
}; };
}; };
mutableFiles.".claude/settings.json".source = ./settings.json;
}; };
} }
-86
View File
@@ -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
}
-301
View File
@@ -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"
}
}
}
+3 -20
View File
@@ -4,19 +4,9 @@
... ...
}: }:
let let
claudeSettings = "home/yolo/claude-settings.json"; # ./opencode.json holds only yolo's overrides; everything else is inherited
# so agents, commands and skills keep coming from common. Attrsets merge
# ./claude-settings.json holds only what yolo overrides; everything else is # key-by-key, lists are replaced whole.
# 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.
mergedOpencodeConfig = (pkgs.formats.json { }).generate "opencode.json" ( mergedOpencodeConfig = (pkgs.formats.json { }).generate "opencode.json" (
lib.recursiveUpdate (lib.importJSON ../common/programs/opencode/opencode.json) ( lib.recursiveUpdate (lib.importJSON ../common/programs/opencode/opencode.json) (
lib.importJSON ./opencode.json lib.importJSON ./opencode.json
@@ -68,13 +58,6 @@ in
# Host-local, not shared: this sets yolo_mode_default = true, which starts # Host-local, not shared: this sets yolo_mode_default = true, which starts
# aoe sessions with permission checks skipped. Only correct on this box. # aoe sessions with permission checks skipped. Only correct on this box.
".config/agent-of-empires/config.toml".source = ./aoe-config.toml; ".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;
};
}; };
}; };