Make Claude Code config reproducible across hosts + cut permission prompts#61
Merged
Merged
Conversation
Extend claude.nix (and the non-nix setup-claude recipe) to symlink five ~/.claude targets from the repo instead of just commands/skills. Adds a link_repo guard that refreshes existing symlinks, creates missing ones, and refuses to clobber a pre-existing real file (warns and skips so it can be migrated first). Symlinks point into the repo (writable) so Claude's runtime writes to settings.json keep working.
…allowlist Bring the previously-untracked ~/.claude/settings.json and hooks/ under repo management (force-added past the global .claude/ gitignore). Add 15 data-driven safe/read-only allowlist entries mined from transcripts (nix build/eval/fmt, docker compose logs/ps/config, npm/pnpm typecheck & test, npm view/root) to cut permission prompts across hosts.
…init pattern Global CLAUDE.md loads in every repo, so drop the verbose Toolbox enumeration (covered by toolbox/CLAUDE.md) down to a one-line pointer, and add a File Inspection nudge to prefer Read/Grep/Glob over cat/sed for file reads. Update toolbox/CLAUDE.md 'How they reach each host' for the five managed targets + clobber guard, and add a per-repo /init pattern note.
Native exporters scraped by the home-lab Prometheus over host.docker.internal (a containerised exporter only sees the OrbStack Linux VM): - node_exporter - host CPU/disk/net/load/filesystem - macmon - Apple-Silicon temp/power/GPU/RAM - glances - native system-monitor web UI, replacing the container
… dungeon Runs the exporters as launchd user agents on dungeon (bind 0.0.0.0 so the OrbStack VM can scrape them via host.docker.internal): - node-exporter :9100 (+ textfile collector dir for battery) - macmon serve :9101 (default 9090 collides with Prometheus) - glances -w :61208 (same port the removed container used) - mac-battery-textfile.sh: every 60s writes node_battery_percent / node_power_source via pmset Pairs with home-lab PR #29 (the Prometheus/Grafana containers).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Make Claude Code config reproducible across hosts + cut permission prompts
Why
A review of the Claude Code Insights report (1,528 messages / 134 sessions over 36
days) flagged three friction areas: permission prompts (123 rejections + 430 failed
commands), repeated context re-discovery across many repos, and edit churn.
Digging into how
~/.claudeis actually wired surfaced the real root cause: the filesthat would carry any of those fixes to my other hosts weren't under nix at all.
~/.claude/commands,skills~/.claude/CLAUDE.md~/.claude/settings.json~/.claude/hooks/What changed
Reproducibility plumbing
claude.nixnow symlinks five~/.claudetargets from the repo via alink_repohelper. It refreshes existing symlinks, creates missing ones, and refuses to
clobber a pre-existing real file — it warns loudly and skips so the file can be
migrated first (non-fatal, so deploys never break).
/nix/store, so Claude's ownruntime writes to
settings.jsonkeep working — they just show up as git diffs.just setup-claudebrought to parity for non-nix hosts (same five targets + guard).settings.json+hooks/intodot/claude/.claude/(force-added past the global
.claude/gitignore). No secrets in either file.Fewer permission prompts (friction #1)
transcripts — only safe/read-only commands that aren't already auto-allowed:
nix build/eval/fmt,docker compose logs/ps/config,npm/pnpm typecheck & test,npm view/root. Mutating/arbitrary-exec commands (git push, ssh, installs,docker compose exec, interpreters) were deliberately excluded.Leaner context (friction #2 & #3)
CLAUDE.md(it loads in every repo): dropped the verboseToolbox enumeration down to a one-line pointer, and added a File Inspection nudge to
prefer
Read/Grep/Globovercat/sedfor file reads.toolbox/CLAUDE.md, and addeda per-repo
/initpattern so other repos (ccs,home-lab, …) get their own tightproject map instead of being re-derived each session.
Verification
nix fmt .— clean.nix build .#darwinConfigurations.moria.system --dry-run— evaluates, 5 derivations.link_repounit-tested for all three cases (symlink → refresh, missing → create,real file → WARN + preserve).
~/.claude/{settings.json,hooks,CLAUDE.md}confirmed as repo symlinks; settingsparses as valid JSON with the new entries; RTK hook still resolves and is executable.
Notes
isengarddoesn't importprograms/tui, so it won't receive this (unchanged, out of scope).untouched and are not part of this PR.