-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Expand file tree
/
Copy pathMakefile
More file actions
185 lines (173 loc) · 7.7 KB
/
Copy pathMakefile
File metadata and controls
185 lines (173 loc) · 7.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
.PHONY: cursor codex claude clean-agents help hooks hooks-remove precommit precommit-all
AI_DIR := .agents
CURSOR_DIR := .cursor
CLAUDE_DIR := .claude
define link_agent_config
@if [ ! -d "$(AI_DIR)" ]; then \
echo "Error: $(AI_DIR)/ does not exist. Run the migration first."; \
exit 1; \
fi
@if [ -e "$(1)" ] && [ ! -L "$(1)" ]; then \
echo "Error: $(1) exists and is not a symlink."; \
echo "Remove it manually if you want to use .agents/ as source."; \
exit 1; \
fi
@if [ -L "$(1)" ]; then \
echo "$(1) symlink already exists"; \
else \
echo "Creating symlink $(1) -> $(AI_DIR)..."; \
ln -s "$(AI_DIR)" "$(1)"; \
fi
endef
define clean_synced_files
@if [ -d "$(AI_DIR)/$(1)" ]; then \
find "$(AI_DIR)/$(1)" $(2) 2>/dev/null | while read src; do \
rel=$${src#$(AI_DIR)/$(1)/}; \
dest_rel=$$rel; \
$(3) \
dest="$(CLAUDE_DIR)/$(4)/$$dest_rel"; \
[ -f "$$dest" ] && rm -f "$$dest" && echo " Removed $$dest" || true; \
done; \
fi
endef
help:
@echo "AI Editor Configuration Sync"
@echo ""
@echo " make cursor - Ensure .cursor symlink points to .agents/"
@echo " make codex - Ensure .codex symlink + generate Codex AGENTS.override.md from .agents/rules/*.mdc"
@echo " make claude - Sync .agents/ to .claude/ + generate .mcp.json (preserves local files)"
@echo " make clean-agents - Remove synced files from .claude/ and local Codex artifacts"
@echo ""
@echo "Git Hooks (pre-commit framework — install once per clone)"
@echo ""
@echo " make hooks - Install the pre-commit framework hook"
@echo " make hooks-remove - Uninstall the pre-commit framework hook"
@echo ""
@echo "Lint Checks (root .pre-commit-config.yaml is the single source of truth)"
@echo " make precommit - Run hooks on changed files (vs origin/main)"
@echo " make precommit-all - Run all hooks on the whole repo (full audit)"
@echo ""
# Sync to Cursor (symlink .cursor -> .agents)
cursor:
$(call link_agent_config,$(CURSOR_DIR))
@echo "Cursor ready!"
codex:
$(call link_agent_config,.codex)
@./scripts/sync-codex.sh "$(AI_DIR)" "AGENTS.md" "AGENTS.override.md"
@echo "Codex ready! Generated AGENTS.override.md and .agents/generated/codex/rules/*.md"
# Sync to Claude (preserve nested structure, convert frontmatter)
# Converts: .mdc -> .md, Cursor frontmatter -> Claude frontmatter
# Cursor: globs/alwaysApply -> Claude: paths
# Preserves local customizations (files in .claude/ not in .agents/)
claude:
@if [ ! -d "$(AI_DIR)/rules" ]; then \
echo "Error: $(AI_DIR)/rules/ does not exist."; \
exit 1; \
fi
@echo "Syncing $(AI_DIR)/rules/ to $(CLAUDE_DIR)/rules/ (preserving local files)..."
@mkdir -p $(CLAUDE_DIR)/rules
@find $(AI_DIR)/rules -name "*.mdc" | while read src; do \
rel=$${src#$(AI_DIR)/rules/}; \
dest="$(CLAUDE_DIR)/rules/$${rel%.mdc}.md"; \
mkdir -p "$$(dirname "$$dest")"; \
./scripts/convert-frontmatter.sh cursor-to-claude "$$src" "$$dest"; \
echo " $$rel -> $${rel%.mdc}.md"; \
done
@# Sync commands (preserve local files)
@if [ -d "$(AI_DIR)/commands" ]; then \
echo "Syncing $(AI_DIR)/commands/ to $(CLAUDE_DIR)/commands/ (preserving local files)..."; \
mkdir -p $(CLAUDE_DIR)/commands; \
find $(AI_DIR)/commands -name "*.md" | while read src; do \
rel=$${src#$(AI_DIR)/commands/}; \
dest="$(CLAUDE_DIR)/commands/$$rel"; \
mkdir -p "$$(dirname "$$dest")"; \
cp "$$src" "$$dest"; \
echo " $$rel"; \
done; \
fi
@# Sync skills (preserve local files)
@if [ -d "$(AI_DIR)/skills" ]; then \
echo "Syncing $(AI_DIR)/skills/ to $(CLAUDE_DIR)/skills/ (preserving local files)..."; \
mkdir -p $(CLAUDE_DIR)/skills; \
cp -r $(AI_DIR)/skills/* $(CLAUDE_DIR)/skills/; \
echo " skills synced"; \
fi
@# Sync agents (preserve local files)
@if [ -d "$(AI_DIR)/agents" ]; then \
echo "Syncing $(AI_DIR)/agents/ to $(CLAUDE_DIR)/agents/ (preserving local files)..."; \
mkdir -p $(CLAUDE_DIR)/agents; \
cp -r $(AI_DIR)/agents/* $(CLAUDE_DIR)/agents/; \
echo " agents synced"; \
fi
@# Convert MCP config to Claude CLI format (.mcp.json at repo root)
@if [ -f "$(AI_DIR)/mcp.json" ]; then \
echo "Converting MCP config to Claude CLI format..."; \
./scripts/convert-mcp.sh "$(AI_DIR)/mcp.json" ".mcp.json"; \
fi
@echo "Claude ready! Files in $(CLAUDE_DIR)/ and .mcp.json"
# Clean generated files (preserves .agents/ and local customizations in .claude/)
# Only deletes files that have a corresponding source in .agents/
clean-agents:
@echo "Removing generated files (preserving local customizations)..."
@[ -L "$(CURSOR_DIR)" ] && rm -f $(CURSOR_DIR) && echo "Removed $(CURSOR_DIR) symlink" || true
@[ -L ".codex" ] && rm -f .codex && echo "Removed .codex symlink" || true
@[ -f "AGENTS.override.md" ] && rm -f AGENTS.override.md && echo "Removed AGENTS.override.md" || true
@[ -d "$(AI_DIR)/generated/codex" ] && rm -rf "$(AI_DIR)/generated/codex" && echo "Removed $(AI_DIR)/generated/codex" || true
@# Clean synced files from Claude surfaces using shared deletion helper.
$(call clean_synced_files,rules,-name "*.mdc",dest_rel=$${dest_rel%.mdc}.md;,rules)
$(call clean_synced_files,commands,-name "*.md",:;,commands)
$(call clean_synced_files,skills,-type f,:;,skills)
$(call clean_synced_files,agents,-name "*.md",:;,agents)
@# Clean up empty directories
@find $(CLAUDE_DIR) -type d -empty -delete 2>/dev/null || true
@[ -f ".mcp.json" ] && rm -f .mcp.json && echo "Removed .mcp.json" || true
@echo "Done! Local customizations preserved."
# Install the pre-commit framework hook (writes .git/hooks/pre-commit).
hooks:
@command -v pre-commit >/dev/null 2>&1 || { \
echo "Error: pre-commit not found. Install it: pip install pre-commit (or brew install pre-commit)."; \
exit 1; \
}
@# pre-commit refuses to install while core.hooksPath is set (it would write a
@# hook git then ignores). Detect it and tell the user exactly how to clear it,
@# rather than silently mutating their git config.
@hp=$$(git config --get core.hooksPath || true); \
if [ -n "$$hp" ]; then \
echo "Error: core.hooksPath is set to '$$hp', which makes pre-commit refuse to install"; \
echo " (a hook written to .git/hooks would be ignored by git)."; \
echo " Clear it, then re-run 'make hooks':"; \
echo " git config --unset core.hooksPath # local (this repo)"; \
echo " git config --global --unset core.hooksPath # if it was set globally"; \
exit 1; \
fi
@# -f: install only pre-commit's hook, never chain a pre-existing one in
@# "migration mode" (a stale chained hook breaks commits). See OPIK-7235.
@pre-commit install -f
@# Clear any legacy hook a prior non-force install left behind.
@rm -f "$$(git rev-parse --git-path hooks)/pre-commit.legacy"
@echo "pre-commit hook installed."
# Uninstall the pre-commit framework hook.
hooks-remove:
@command -v pre-commit >/dev/null 2>&1 || { \
echo "pre-commit not found; nothing to uninstall."; \
exit 0; \
}
@pre-commit uninstall
@echo "pre-commit hook removed."
# Run all hooks on files changed vs origin/main (the same hooks a commit runs,
# but over the branch diff). Mirrors how CI lints a PR.
precommit:
@command -v pre-commit >/dev/null 2>&1 || { \
echo "Error: pre-commit not found. Install it: pip install pre-commit (or brew install pre-commit)."; \
exit 1; \
}
@git fetch -q origin main 2>/dev/null || true
@pre-commit run --from-ref origin/main --to-ref HEAD --show-diff-on-failure --verbose
# Full-repo audit: run every hook against every file. Slow; surfaces pre-existing
# debt. Not a routine gate (commits and CI are changed-files-only).
precommit-all:
@command -v pre-commit >/dev/null 2>&1 || { \
echo "Error: pre-commit not found. Install it: pip install pre-commit (or brew install pre-commit)."; \
exit 1; \
}
@pre-commit run --all-files --show-diff-on-failure --verbose