1- .PHONY : cursor codex claude clean-agents help hooks hooks-remove precommit precommit-all
1+ .PHONY : cursor codex claude agent-configs clean-agents help hooks hooks-remove agent-hooks agent- hooks-remove precommit precommit-all
22
33AI_DIR := .agents
44CURSOR_DIR := .cursor
55CLAUDE_DIR := .claude
6+ HOOKS_SRC := .hooks
7+ # Anchor hook paths to this Makefile's directory so `make agent-hooks` resolves
8+ # to this repo regardless of where make was invoked from (subdir, -f from another
9+ # repo, etc.), and works correctly inside a worktree.
10+ MAKEFILE_DIR := $(patsubst % /,% ,$(dir $(realpath $(firstword $(MAKEFILE_LIST ) ) ) ) )
11+ GIT_COMMON_DIR := $(shell cd "$(MAKEFILE_DIR ) " 2>/dev/null && git rev-parse --git-common-dir 2>/dev/null)
12+ HOOKS_DEST := $(if $(GIT_COMMON_DIR ) ,$(GIT_COMMON_DIR ) /hooks)
613
714define link_agent_config
815 @if [ ! -d "$(AI_DIR ) " ]; then \
@@ -39,13 +46,16 @@ help:
3946 @echo " "
4047 @echo " make cursor - Ensure .cursor symlink points to .agents/"
4148 @echo " make codex - Ensure .codex symlink + generate Codex AGENTS.override.md from .agents/rules/*.mdc"
42- @echo " make claude - Sync .agents/ to .claude/ + generate .mcp.json (preserves local files)"
49+ @echo " make claude - Sync .agents/ to .claude/ (generates .mcp.json only on first setup)"
50+ @echo " make agent-configs - Refresh every opted-in surface (Claude/Cursor/Codex); relinks in worktrees"
4351 @echo " make clean-agents - Remove synced files from .claude/ and local Codex artifacts"
4452 @echo " "
4553 @echo " Git Hooks (pre-commit framework — install once per clone)"
4654 @echo " "
4755 @echo " make hooks - Install the pre-commit framework hook"
4856 @echo " make hooks-remove - Uninstall the pre-commit framework hook"
57+ @echo " make agent-hooks - Install local post-merge/post-checkout hooks that keep agent configs in sync"
58+ @echo " make agent-hooks-remove - Remove the local agent-config hooks"
4959 @echo " "
5060 @echo " Lint Checks (root .pre-commit-config.yaml is the single source of truth)"
5161 @echo " make precommit - Run hooks on changed files (vs origin/main)"
@@ -113,6 +123,14 @@ claude:
113123 fi
114124 @echo " Claude ready! Files in $( CLAUDE_DIR) / and .mcp.json"
115125
126+ # Refresh every agent-config surface the user has already opted into (Claude,
127+ # Cursor, Codex) from .agents/. Detects opt-in by folder presence, so it never
128+ # creates a surface the user has not set up. In a worktree it relinks the shared
129+ # config from the main checkout instead of regenerating. This is the target the
130+ # git hooks call.
131+ agent-configs :
132+ @./scripts/sync-agent-configs.sh
133+
116134# Clean generated files (preserves .agents/ and local customizations in .claude/)
117135# Only deletes files that have a corresponding source in .agents/
118136clean-agents :
@@ -128,7 +146,9 @@ clean-agents:
128146 $(call clean_synced_files,agents,-name "* .md",:;,agents)
129147 @# Clean up empty directories
130148 @find $(CLAUDE_DIR ) -type d -empty -delete 2> /dev/null || true
131- @[ -f " .mcp.json" ] && rm -f .mcp.json && echo " Removed .mcp.json" || true
149+ @# .mcp.json is intentionally NOT removed: it may hold personal tokens that
150+ @# exist nowhere else, and clean-agents must never destroy them.
151+ @[ -f " .mcp.json" ] && echo " Left .mcp.json in place (may contain personal tokens)." || true
132152 @echo " Done! Local customizations preserved."
133153
134154# Install the pre-commit framework hook (writes .git/hooks/pre-commit).
@@ -155,6 +175,7 @@ hooks:
155175 @# Clear any legacy hook a prior non-force install left behind.
156176 @rm -f " $$ (git rev-parse --git-path hooks)/pre-commit.legacy"
157177 @echo " pre-commit hook installed."
178+ @echo " Tip: if you use Claude/Cursor/Codex, run 'make agent-hooks' to keep their configs in sync on pull/checkout."
158179
159180# Uninstall the pre-commit framework hook.
160181hooks-remove :
@@ -165,6 +186,50 @@ hooks-remove:
165186 @pre-commit uninstall
166187 @echo " pre-commit hook removed."
167188
189+ # Install the LOCAL agent-config hooks (post-merge, post-checkout). These are
190+ # developer ergonomics only -- they keep .claude/.cursor/.codex in sync with
191+ # .agents/ after pulls and branch switches -- and are unrelated to lint/CI. They
192+ # are deliberately separate from `make hooks` (the pre-commit framework, which
193+ # owns .git/hooks/pre-commit and also runs in CI): agent-config sync must never
194+ # run in CI. Only post-merge/post-checkout are written, so the pre-commit
195+ # framework's hook is never touched.
196+ agent-hooks :
197+ @if [ -z " $( HOOKS_DEST) " ]; then \
198+ echo " Error: $( MAKEFILE_DIR) is not in a git repository." ; \
199+ exit 1; \
200+ fi
201+ @# Git ignores .git/hooks entirely when core.hooksPath is set, so hooks copied
202+ @# there would silently never fire. Detect and explain rather than pretend to
203+ @# install (mirrors the guard in the `hooks` target).
204+ @hp=$$(git config --get core.hooksPath || true ) ; \
205+ if [ -n " $$ hp" ]; then \
206+ echo " Error: core.hooksPath is set to '$$ hp'; hooks in .git/hooks would be ignored." ; \
207+ echo " Clear it, then re-run 'make agent-hooks':" ; \
208+ echo " git config --unset core.hooksPath" ; \
209+ exit 1; \
210+ fi
211+ @cd " $( MAKEFILE_DIR) " && \
212+ if [ ! -d " $( HOOKS_SRC) " ]; then echo " Error: $( MAKEFILE_DIR) /$( HOOKS_SRC) / does not exist." ; exit 1; fi && \
213+ if [ ! -d " $( HOOKS_DEST) " ]; then echo " Error: $( HOOKS_DEST) / does not exist." ; exit 1; fi && \
214+ for h in post-checkout post-merge; do \
215+ cp " $( HOOKS_SRC) /$$ h" " $( HOOKS_DEST) /$$ h" && \
216+ chmod +x " $( HOOKS_DEST) /$$ h" && \
217+ echo " $$ h hook installed (make agent-configs)." ; \
218+ done
219+
220+ # Remove the local agent-config hooks (leaves the pre-commit framework alone).
221+ agent-hooks-remove :
222+ @if [ -z " $( HOOKS_DEST) " ]; then \
223+ echo " Error: $( MAKEFILE_DIR) is not in a git repository." ; \
224+ exit 1; \
225+ fi
226+ @cd " $( MAKEFILE_DIR) " && \
227+ for h in post-checkout post-merge; do \
228+ if [ -f " $( HOOKS_DEST) /$$ h" ]; then \
229+ rm -f " $( HOOKS_DEST) /$$ h" && echo " $$ h hook removed." ; \
230+ fi ; \
231+ done
232+
168233# Run all hooks on files changed vs origin/main (the same hooks a commit runs,
169234# but over the branch diff). Mirrors how CI lints a PR.
170235precommit :
0 commit comments