33 lines
1.6 KiB
Bash
Executable File
33 lines
1.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# SessionStart hook: refuse to let a severed dotfiles link go unnoticed.
|
|
#
|
|
# Claude Code writes settings with an atomic temp+rename. Through the stow layer that can
|
|
# replace ~/.config/claude/<file> (a symlink into the repo) with a plain FILE holding whatever
|
|
# snapshot the writing process had in memory -- silently freezing every later repo edit out
|
|
# of the live config. It happened on 2026-09-15 23:31: a stale copy dropped four hook events
|
|
# (git-guard among them) and nobody noticed for an hour. link.sh heals the identical case but
|
|
# only when run; this makes every session start say so instead.
|
|
#
|
|
# Read-only: it never relinks. Exit 2 on SessionStart shows stderr to the user and the session
|
|
# continues. Exit 0 when everything resolves into the repo.
|
|
set -u
|
|
src="$HOME/.config/claude"
|
|
bad=""
|
|
for f in settings.json statusline.py keybindings.json CLAUDE.md link.sh; do
|
|
p="$src/$f"
|
|
[ -e "$p" ] || continue
|
|
if [ ! -L "$p" ]; then bad="$bad $p is a plain file (should be a symlink into the dots repo)"$'\n'; fi
|
|
done
|
|
# the ~/.claude side too: link.sh's own guard covers it, but only when link.sh runs
|
|
for f in settings.json keybindings.json CLAUDE.md statusline.py; do
|
|
p="$HOME/.claude/$f"
|
|
[ -e "$p" ] || continue
|
|
case "$(readlink -f "$p")" in
|
|
*"/.dots/"*) ;;
|
|
*) bad="$bad $p resolves to $(readlink -f "$p"), not into the dots repo"$'\n' ;;
|
|
esac
|
|
done
|
|
[ -z "$bad" ] && exit 0
|
|
printf 'DRIFT: Claude config has broken out of the dots repo; the live config is a stale copy.\n%s fix: review with `diff`, then `bash ~/.config/claude/link.sh` (heals identical copies, warns otherwise)\n' "$bad" >&2
|
|
exit 2
|