Skip to content

Commit 3f75c09

Browse files
committed
Merge remote-tracking branch 'upstream/main'
2 parents 1cb2d47 + 1735591 commit 3f75c09

18 files changed

Lines changed: 1145 additions & 52 deletions

File tree

.agents/.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Copyright Contributors to the OpenImageIO project.
2+
# SPDX-License-Identifier: Apache-2.0
3+
# https://github.com/AcademySoftwareFoundation/OpenImageIO
4+
5+
# Ignore skills that might be installed locally by a developer, to prevent
6+
# cluttering the output of `git status` and prevent people from accidentally
7+
# checking in locally installed skills.
8+
skills/*
9+
10+
# But don't ignore the ones that belong to this project and are checked in. As
11+
# we add skills that we intend to commit and make available to all project
12+
# developers, they must be individually added here if they don't start with
13+
# "oiio-".
14+
!skills/oiio-*/
15+
!skills/prepare-patch-release/
16+
!skills/release-notes-update/

.agents/setup-agent

Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
#!/usr/bin/env bash
2+
# .agents/setup-agent — Set up AI coding tool integration for this repository.
3+
#
4+
# Creates the tool-specific symlinks and config files needed to use the shared
5+
# resources in AGENTS.md and .agents/ with your preferred AI coding assistant.
6+
# Run "setup-agent <tool>" once after cloning; re-running is always safe.
7+
#
8+
# The canonical project instructions live in AGENTS.md (root) and skills live
9+
# in .agents/skills/<name>/SKILL.md. Everything tool-specific is either a
10+
# symlink into those locations, or a thin wrapper that references them.
11+
# Tool-specific directories (.claude/, .cursor/, etc.) are git-ignored so that
12+
# each developer runs this script for the tool they actually use.
13+
#
14+
# ─────────────────────────────────────────────────────────────────────────────
15+
# TOOL STRATEGIES (update this section when tool conventions change)
16+
# ─────────────────────────────────────────────────────────────────────────────
17+
#
18+
# CLAUDE CODE
19+
# Reads: .claude/CLAUDE.md (project) or CLAUDE.md (root) — NOT AGENTS.md natively.
20+
# If Anthropic eventually adds native AGENTS.md support, the
21+
# .claude/CLAUDE.md wrapper can be retired.
22+
# Skills: .claude/skills/ (each subdirectory is a skill)
23+
# Setup: Write .claude/CLAUDE.md containing "@AGENTS.md" (Claude's include
24+
# syntax), symlink .claude/skills -> ../.agents/skills.
25+
# Docs: https://docs.anthropic.com/en/docs/claude-code/memory
26+
# https://docs.anthropic.com/en/docs/claude-code/skills
27+
#
28+
# OPENAI CODEX
29+
# Reads: AGENTS.md natively — no wrapper needed.
30+
# Skills: .agents/skills/ natively, and also .codex/skills/ as a fallback.
31+
# Setup: Symlink .codex/skills -> ../.agents/skills so either path works.
32+
# Docs: https://developers.openai.com/codex/guides/agents-md
33+
# https://developers.openai.com/codex/skills
34+
#
35+
# CURSOR
36+
# Reads: .cursor/rules/*.mdc files — does NOT read AGENTS.md natively.
37+
# If Cursor adds native AGENTS.md support, the rules symlink can be
38+
# retired.
39+
# Skills: Cursor uses a different slash-command mechanism; .agents/skills/
40+
# are not directly usable today, but the convention may change.
41+
# Setup: Symlink .cursor/rules/project.mdc -> ../../AGENTS.md so Cursor
42+
# picks up our instructions as a project rule.
43+
# Docs: https://docs.cursor.com/context/rules-for-ai
44+
# https://docs.cursor.com/cmdk/overview
45+
#
46+
# OPENCODE
47+
# Reads: AGENTS.md natively — no wrapper needed.
48+
# Skills: Documented to read .agents/skills/ natively, but in practice also
49+
# requires .opencode/commands/<name>.md symlinks (as of 2025-05).
50+
# Remove the per-command symlinks once opencode reliably finds
51+
# .agents/skills/ on its own.
52+
# Setup: Symlink .opencode/skills -> ../.agents/skills (future-proof),
53+
# plus individual .opencode/commands/<name>.md -> skill SKILL.md
54+
# files (current workaround).
55+
# Docs: https://opencode.ai/docs/configuration
56+
# https://opencode.ai/docs/skills
57+
#
58+
# GITHUB COPILOT
59+
# Reads: AGENTS.md natively (VS Code Copilot ≥ 1.99 / GitHub Copilot Chat).
60+
# Also reads .github/copilot-instructions.md as a fallback for older
61+
# clients or non-VS Code surfaces.
62+
# Skills: .agents/skills/ natively supported.
63+
# Setup: Symlink .github/copilot-instructions.md -> ../AGENTS.md so older
64+
# clients also receive our instructions. Remove once all Copilot
65+
# surfaces reliably pick up AGENTS.md directly.
66+
# Docs: https://code.visualstudio.com/docs/copilot/customization/custom-instructions
67+
# https://docs.github.com/en/copilot/concepts/about-prompting-copilot
68+
# https://docs.github.com/en/copilot/concepts/agents/about-agent-skills
69+
# ─────────────────────────────────────────────────────────────────────────────
70+
71+
set -euo pipefail
72+
73+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
74+
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
75+
SKILLS_DIR=".agents/skills"
76+
SUPPORTED_TOOLS="claude codex cursor opencode copilot"
77+
78+
usage() {
79+
cat <<EOF
80+
Usage: .agents/setup-agent <tool|all|clear|list> [tool...]
81+
82+
Commands:
83+
<tool> [...] Set up one or more tools
84+
all Set up all supported tools
85+
clear Remove all tool-specific setup created by this script
86+
clear <tool> Remove setup for one specific tool
87+
list List supported tools
88+
89+
Supported tools: $SUPPORTED_TOOLS
90+
91+
Examples:
92+
.agents/setup-agent claude
93+
.agents/setup-agent claude cursor
94+
.agents/setup-agent all
95+
.agents/setup-agent clear
96+
.agents/setup-agent clear opencode
97+
EOF
98+
}
99+
100+
rel() { echo "${1#"$REPO_ROOT"/}"; }
101+
102+
# Create a symlink only if the path doesn't already exist.
103+
# Silent if the link already points to the correct target.
104+
safe_symlink() {
105+
local link="$1" target="$2"
106+
if [ -L "$link" ] && [ "$(readlink "$link")" = "$target" ]; then
107+
return 0
108+
elif [ -e "$link" ] || [ -L "$link" ]; then
109+
echo " SKIP (already exists): $(rel "$link")"
110+
return 0
111+
fi
112+
mkdir -p "$(dirname "$link")"
113+
ln -s "$target" "$link"
114+
echo " LINK: $(rel "$link") -> $target"
115+
}
116+
117+
# Write a file only if the path doesn't already exist.
118+
safe_write() {
119+
local file="$1"
120+
shift
121+
local content="$*"
122+
if [ -e "$file" ]; then
123+
echo " SKIP (already exists): $(rel "$file")"
124+
return 0
125+
fi
126+
mkdir -p "$(dirname "$file")"
127+
printf '%s\n' "$content" > "$file"
128+
echo " FILE: $(rel "$file")"
129+
}
130+
131+
# Remove a symlink only if it still points to the expected target.
132+
safe_remove_symlink() {
133+
local link="$1" expected_target="$2"
134+
[ ! -L "$link" ] && return 0
135+
if [ "$(readlink "$link")" = "$expected_target" ]; then
136+
rm "$link"
137+
echo " REMOVED: $(rel "$link")"
138+
else
139+
echo " SKIP (modified, not removing): $(rel "$link")"
140+
fi
141+
}
142+
143+
# Remove a file only if its content still matches what we wrote.
144+
safe_remove_file() {
145+
local file="$1"
146+
shift
147+
local expected="$*"
148+
[ ! -f "$file" ] || [ -L "$file" ] && return 0
149+
if [ "$(cat "$file")" = "$expected" ]; then
150+
rm "$file"
151+
echo " REMOVED: $(rel "$file")"
152+
else
153+
echo " SKIP (modified, not removing): $(rel "$file")"
154+
fi
155+
}
156+
157+
try_rmdir() { rmdir "$1" 2>/dev/null && echo " RMDIR: $(rel "$1")" || true; }
158+
159+
#------------------------------------------------------------------------------
160+
# Claude Code (.claude/)
161+
#------------------------------------------------------------------------------
162+
setup_claude() {
163+
echo "Setting up Claude Code..."
164+
safe_write "$REPO_ROOT/.claude/CLAUDE.md" "@AGENTS.md"
165+
safe_symlink "$REPO_ROOT/.claude/skills" "../.agents/skills"
166+
safe_write "$REPO_ROOT/.claude/.gitignore" "settings.local.json"
167+
}
168+
clear_claude() {
169+
echo "Removing Claude Code setup..."
170+
safe_remove_file "$REPO_ROOT/.claude/CLAUDE.md" "@AGENTS.md"
171+
safe_remove_symlink "$REPO_ROOT/.claude/skills" "../.agents/skills"
172+
safe_remove_file "$REPO_ROOT/.claude/.gitignore" "settings.local.json"
173+
try_rmdir "$REPO_ROOT/.claude"
174+
}
175+
176+
#------------------------------------------------------------------------------
177+
# OpenAI Codex (.codex/)
178+
#------------------------------------------------------------------------------
179+
setup_codex() {
180+
echo "Setting up OpenAI Codex..."
181+
safe_symlink "$REPO_ROOT/.codex/skills" "../.agents/skills"
182+
safe_write "$REPO_ROOT/.codex/.gitignore" "*.log"
183+
}
184+
clear_codex() {
185+
echo "Removing OpenAI Codex setup..."
186+
safe_remove_symlink "$REPO_ROOT/.codex/skills" "../.agents/skills"
187+
safe_remove_file "$REPO_ROOT/.codex/.gitignore" "*.log"
188+
try_rmdir "$REPO_ROOT/.codex"
189+
}
190+
191+
#------------------------------------------------------------------------------
192+
# Cursor (.cursor/)
193+
#------------------------------------------------------------------------------
194+
setup_cursor() {
195+
echo "Setting up Cursor..."
196+
safe_symlink "$REPO_ROOT/.cursor/rules/project.mdc" "../../AGENTS.md"
197+
safe_write "$REPO_ROOT/.cursor/.gitignore" "$(printf 'composer/\nchat/')"
198+
}
199+
clear_cursor() {
200+
echo "Removing Cursor setup..."
201+
safe_remove_symlink "$REPO_ROOT/.cursor/rules/project.mdc" "../../AGENTS.md"
202+
safe_remove_file "$REPO_ROOT/.cursor/.gitignore" "$(printf 'composer/\nchat/')"
203+
try_rmdir "$REPO_ROOT/.cursor/rules"
204+
try_rmdir "$REPO_ROOT/.cursor"
205+
}
206+
207+
#------------------------------------------------------------------------------
208+
# Opencode (.opencode/)
209+
#------------------------------------------------------------------------------
210+
OPENCODE_GITIGNORE="$(printf 'node_modules\npackage.json\npackage-lock.json\nbun.lock')"
211+
212+
setup_opencode() {
213+
echo "Setting up Opencode..."
214+
safe_symlink "$REPO_ROOT/.opencode/skills" "../.agents/skills"
215+
safe_write "$REPO_ROOT/.opencode/.gitignore" "$OPENCODE_GITIGNORE"
216+
# Individual command links (needed in practice despite docs implying otherwise)
217+
for skill_dir in "$REPO_ROOT/$SKILLS_DIR"/*/; do
218+
[ -d "$skill_dir" ] || continue
219+
local skill_name
220+
skill_name="$(basename "$skill_dir")"
221+
safe_symlink "$REPO_ROOT/.opencode/commands/${skill_name}.md" \
222+
"../../$SKILLS_DIR/${skill_name}/SKILL.md"
223+
done
224+
}
225+
clear_opencode() {
226+
echo "Removing Opencode setup..."
227+
safe_remove_symlink "$REPO_ROOT/.opencode/skills" "../.agents/skills"
228+
safe_remove_file "$REPO_ROOT/.opencode/.gitignore" "$OPENCODE_GITIGNORE"
229+
for skill_dir in "$REPO_ROOT/$SKILLS_DIR"/*/; do
230+
[ -d "$skill_dir" ] || continue
231+
local skill_name
232+
skill_name="$(basename "$skill_dir")"
233+
safe_remove_symlink "$REPO_ROOT/.opencode/commands/${skill_name}.md" \
234+
"../../$SKILLS_DIR/${skill_name}/SKILL.md"
235+
done
236+
try_rmdir "$REPO_ROOT/.opencode/commands"
237+
try_rmdir "$REPO_ROOT/.opencode"
238+
}
239+
240+
#------------------------------------------------------------------------------
241+
# GitHub Copilot (.github/copilot-instructions.md)
242+
# Copilot natively reads AGENTS.md, but also checks copilot-instructions.md.
243+
#------------------------------------------------------------------------------
244+
setup_copilot() {
245+
echo "Setting up GitHub Copilot..."
246+
safe_symlink "$REPO_ROOT/.github/copilot-instructions.md" "../AGENTS.md"
247+
}
248+
clear_copilot() {
249+
echo "Removing GitHub Copilot setup..."
250+
safe_remove_symlink "$REPO_ROOT/.github/copilot-instructions.md" "../AGENTS.md"
251+
}
252+
253+
#------------------------------------------------------------------------------
254+
# Dispatch
255+
#------------------------------------------------------------------------------
256+
run_tool() {
257+
local action="$1" tool="$2"
258+
case "$tool" in
259+
claude) "${action}_claude" ;;
260+
codex) "${action}_codex" ;;
261+
cursor) "${action}_cursor" ;;
262+
opencode) "${action}_opencode" ;;
263+
copilot) "${action}_copilot" ;;
264+
*)
265+
echo "Unknown tool: $tool (supported: $SUPPORTED_TOOLS)" >&2
266+
exit 1
267+
;;
268+
esac
269+
}
270+
271+
[ $# -eq 0 ] && { usage; exit 0; }
272+
273+
case "$1" in
274+
all)
275+
for t in $SUPPORTED_TOOLS; do run_tool setup "$t"; done
276+
;;
277+
clear)
278+
shift
279+
if [ $# -eq 0 ]; then
280+
for t in $SUPPORTED_TOOLS; do run_tool clear "$t"; done
281+
else
282+
for t in "$@"; do run_tool clear "$t"; done
283+
fi
284+
;;
285+
list)
286+
echo "Supported tools: $SUPPORTED_TOOLS"
287+
;;
288+
-h|--help|help)
289+
usage
290+
;;
291+
*)
292+
for t in "$@"; do run_tool setup "$t"; done
293+
;;
294+
esac

0 commit comments

Comments
 (0)