Skip to content

Commit a95bfbb

Browse files
jeremyederclaude
andauthored
feat: add AGENTS.md symlink support with comprehensive testing (#429)
## Summary Add support for Cursor and other AI tools that use `AGENTS.md` by creating a symlink to `CLAUDE.md`. This provides zero-maintenance dual-tool support with comprehensive validation. ## Changes - ✅ Create `AGENTS.md` → `CLAUDE.md` symlink for Cursor compatibility - ✅ Add minimal inline symlink validation to e2e workflow (fails fast) - ✅ Add comprehensive standalone test script (10 checks) - ✅ Add dedicated GitHub Actions workflow for symlink validation - ✅ Add `tests/README.md` documenting the testing strategy - ✅ Update `.gitignore` to exclude `.cursor/` and `.tessl/` directories ## Testing Strategy Three-tier validation approach: 1. **E2E inline check** - Fast validation on every e2e run (~1 second) - Runs after checkout, before builds - Fails fast to save CI time - 4 essential checks (exists, is symlink, target, content match) 2. **Dedicated workflow** - Runs when `AGENTS.md` or `CLAUDE.md` change - Comprehensive validation including git tracking - Platform compatibility checks 3. **Local test script** - `tests/test_agents_md_symlink.sh` - 10 detailed checks with informative error messages - Validates project context and key documentation sections ## Why a Symlink? **Problem**: Claude Code uses `CLAUDE.md`, Cursor uses `AGENTS.md` **Solution**: Symlink eliminates duplication and maintenance overhead **Benefits**: - Zero maintenance (single source of truth) - No sync issues between files - Git tracks symlinks correctly across platforms - Works on macOS, Linux, WSL, and Windows (Git for Windows) ## Test Results ``` ✅ All tests passed! AGENTS.md symlink is working correctly. - Symlink: AGENTS.md -> CLAUDE.md - Content size: 36394 bytes - Cursor and other AI tools can use AGENTS.md ``` ## Test Plan - [x] Local test script passes all 10 checks - [ ] E2E workflow runs successfully with new inline validation - [ ] Dedicated symlink workflow validates correctly - [ ] Cursor can read `AGENTS.md` successfully (manual verification) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
1 parent e589f3c commit a95bfbb

File tree

6 files changed

+266
-1
lines changed

6 files changed

+266
-1
lines changed

.github/workflows/e2e.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,16 @@ jobs:
4242
steps:
4343
- name: Checkout code
4444
uses: actions/checkout@v5
45-
45+
46+
- name: Validate AGENTS.md symlink
47+
run: |
48+
echo "Validating AGENTS.md → CLAUDE.md symlink..."
49+
[ -L AGENTS.md ] || (echo "❌ AGENTS.md is not a symlink" && exit 1)
50+
[ "$(readlink AGENTS.md)" = "CLAUDE.md" ] || (echo "❌ AGENTS.md points to wrong target" && exit 1)
51+
[ -f CLAUDE.md ] || (echo "❌ CLAUDE.md does not exist" && exit 1)
52+
diff -q AGENTS.md CLAUDE.md > /dev/null || (echo "❌ AGENTS.md content differs from CLAUDE.md" && exit 1)
53+
echo "✅ AGENTS.md symlink is valid"
54+
4655
- name: Set up Node.js
4756
uses: actions/setup-node@v6
4857
with:
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: Test AGENTS.md Symlink
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- 'AGENTS.md'
7+
- 'CLAUDE.md'
8+
- 'tests/test_agents_md_symlink.sh'
9+
- '.github/workflows/test-agents-md-symlink.yml'
10+
push:
11+
branches:
12+
- main
13+
paths:
14+
- 'AGENTS.md'
15+
- 'CLAUDE.md'
16+
- 'tests/test_agents_md_symlink.sh'
17+
18+
jobs:
19+
test-symlink:
20+
runs-on: ubuntu-latest
21+
timeout-minutes: 5
22+
23+
steps:
24+
- name: Checkout code
25+
uses: actions/checkout@v5
26+
27+
- name: Validate AGENTS.md symlink
28+
run: |
29+
chmod +x tests/test_agents_md_symlink.sh
30+
./tests/test_agents_md_symlink.sh
31+
32+
- name: Test cross-platform compatibility
33+
run: |
34+
echo "Testing symlink on Linux..."
35+
36+
# Verify symlink properties
37+
ls -la AGENTS.md
38+
39+
# Verify git stores it correctly
40+
git ls-files -s AGENTS.md
41+
42+
# Verify content is accessible
43+
head -n 5 AGENTS.md
44+
45+
echo "✅ Symlink works correctly on Linux"
46+
47+
- name: Validate git tracking
48+
run: |
49+
# Check that AGENTS.md is in git index
50+
if git ls-files --error-unmatch AGENTS.md > /dev/null 2>&1; then
51+
echo "✅ AGENTS.md is tracked by git"
52+
else
53+
echo "❌ ERROR: AGENTS.md is not tracked by git"
54+
exit 1
55+
fi
56+
57+
# Check that it's stored as a symlink in git
58+
MODE=$(git ls-files -s AGENTS.md | awk '{print $1}')
59+
if [ "$MODE" = "120000" ]; then
60+
echo "✅ AGENTS.md is stored as symlink (mode 120000)"
61+
else
62+
echo "❌ ERROR: AGENTS.md is not stored as symlink (mode: $MODE)"
63+
exit 1
64+
fi

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,7 @@ e2e/cypress/videos/
128128
# Langfuse secrets and deployment credentials
129129
e2e/.env.langfuse
130130
e2e/langfuse/.env.langfuse-keys
131+
132+
# AI assistant configuration
133+
.cursor/
134+
.tessl/

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CLAUDE.md

tests/README.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Tests
2+
3+
This directory contains project-level tests for the Ambient Code Platform.
4+
5+
## Test Structure
6+
7+
- `integration/` - Integration tests requiring real Kubernetes clusters
8+
- `test_agents_md_symlink.sh` - Validates AGENTS.md symlink for Cursor compatibility
9+
10+
## AGENTS.md Symlink Test
11+
12+
The `test_agents_md_symlink.sh` script validates that the `AGENTS.md``CLAUDE.md` symlink works correctly for Cursor and other AI coding tools.
13+
14+
### What It Tests
15+
16+
1. ✅ AGENTS.md exists and is a valid symlink
17+
2. ✅ Symlink points to CLAUDE.md
18+
3. ✅ Content is readable through symlink
19+
4. ✅ Content matches CLAUDE.md exactly
20+
5. ✅ File is tracked by git correctly (mode 120000)
21+
6. ✅ Contains expected project context
22+
7. ✅ All required documentation sections exist
23+
8. ✅ File size is reasonable
24+
25+
### Running Locally
26+
27+
```bash
28+
./tests/test_agents_md_symlink.sh
29+
```
30+
31+
### CI Integration
32+
33+
The test runs automatically in GitHub Actions on PRs that modify:
34+
- `AGENTS.md`
35+
- `CLAUDE.md`
36+
- `tests/test_agents_md_symlink.sh`
37+
- `.github/workflows/test-agents-md-symlink.yml`
38+
39+
See `.github/workflows/test-agents-md-symlink.yml` for the CI workflow.
40+
41+
### Why a Symlink?
42+
43+
**Problem**: Claude Code uses `CLAUDE.md`, Cursor uses `AGENTS.md`
44+
45+
**Solution**: Symlink eliminates duplication and maintenance overhead
46+
47+
**Benefits**:
48+
- Zero maintenance (single source of truth)
49+
- No sync issues between files
50+
- Git tracks symlinks correctly across platforms
51+
- Works on macOS, Linux, WSL, and Windows (with symlink support)
52+
53+
### Cross-Platform Notes
54+
55+
- **macOS/Linux/WSL**: Native symlink support ✅
56+
- **Windows**: Git for Windows handles symlinks correctly when cloned ✅
57+
- **Git behavior**: Stores symlinks as special objects (mode 120000), content is the link target
58+
59+
## Component-Specific Tests
60+
61+
See component README files for testing details:
62+
63+
- Backend: `components/backend/tests/`
64+
- Frontend: `components/frontend/` (Cypress e2e tests)
65+
- Operator: `components/operator/` (controller tests)
66+
- Claude Runner: `components/runners/claude-code-runner/tests/`

tests/test_agents_md_symlink.sh

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#!/bin/bash
2+
3+
# Test that AGENTS.md symlink works for Cursor and other AI tools
4+
# This validates that:
5+
# 1. AGENTS.md exists and is a valid symlink
6+
# 2. It points to CLAUDE.md
7+
# 3. Content is readable and identical to CLAUDE.md
8+
# 4. File is tracked by git correctly
9+
10+
set -e
11+
12+
echo "Testing AGENTS.md symlink..."
13+
14+
# Test 1: Check that AGENTS.md exists
15+
echo -n "✓ Checking AGENTS.md exists... "
16+
if [ ! -e AGENTS.md ]; then
17+
echo "FAILED"
18+
echo "Error: AGENTS.md does not exist"
19+
exit 1
20+
fi
21+
echo "OK"
22+
23+
# Test 2: Check that AGENTS.md is a symlink
24+
echo -n "✓ Checking AGENTS.md is a symlink... "
25+
if [ ! -L AGENTS.md ]; then
26+
echo "FAILED"
27+
echo "Error: AGENTS.md is not a symlink"
28+
exit 1
29+
fi
30+
echo "OK"
31+
32+
# Test 3: Check that symlink points to CLAUDE.md
33+
echo -n "✓ Checking symlink target is CLAUDE.md... "
34+
TARGET=$(readlink AGENTS.md)
35+
if [ "$TARGET" != "CLAUDE.md" ]; then
36+
echo "FAILED"
37+
echo "Error: AGENTS.md points to '$TARGET', expected 'CLAUDE.md'"
38+
exit 1
39+
fi
40+
echo "OK"
41+
42+
# Test 4: Check that CLAUDE.md exists (symlink target)
43+
echo -n "✓ Checking CLAUDE.md exists... "
44+
if [ ! -f CLAUDE.md ]; then
45+
echo "FAILED"
46+
echo "Error: CLAUDE.md (symlink target) does not exist"
47+
exit 1
48+
fi
49+
echo "OK"
50+
51+
# Test 5: Check that content is readable through symlink
52+
echo -n "✓ Checking AGENTS.md content is readable... "
53+
if ! cat AGENTS.md > /dev/null 2>&1; then
54+
echo "FAILED"
55+
echo "Error: Cannot read content through AGENTS.md symlink"
56+
exit 1
57+
fi
58+
echo "OK"
59+
60+
# Test 6: Check that content is identical to CLAUDE.md
61+
echo -n "✓ Checking AGENTS.md content matches CLAUDE.md... "
62+
if ! diff -q AGENTS.md CLAUDE.md > /dev/null 2>&1; then
63+
echo "FAILED"
64+
echo "Error: AGENTS.md content does not match CLAUDE.md"
65+
exit 1
66+
fi
67+
echo "OK"
68+
69+
# Test 7: Check that file is tracked by git
70+
echo -n "✓ Checking AGENTS.md is tracked by git... "
71+
if ! git ls-files --error-unmatch AGENTS.md > /dev/null 2>&1; then
72+
echo "WARNING"
73+
echo "Warning: AGENTS.md is not tracked by git (will be added in commit)"
74+
else
75+
echo "OK"
76+
fi
77+
78+
# Test 8: Validate that symlink contains expected project context
79+
echo -n "✓ Checking AGENTS.md contains project context... "
80+
if ! grep -q "Ambient Code Platform" AGENTS.md; then
81+
echo "FAILED"
82+
echo "Error: AGENTS.md does not contain expected project context"
83+
exit 1
84+
fi
85+
echo "OK"
86+
87+
# Test 9: Validate key sections exist
88+
echo -n "✓ Checking key sections exist... "
89+
REQUIRED_SECTIONS=(
90+
"Project Overview"
91+
"Development Commands"
92+
"Key Architecture Patterns"
93+
"Backend and Operator Development Standards"
94+
"Frontend Development Standards"
95+
)
96+
97+
for section in "${REQUIRED_SECTIONS[@]}"; do
98+
if ! grep -q "$section" AGENTS.md; then
99+
echo "FAILED"
100+
echo "Error: Section '$section' not found in AGENTS.md"
101+
exit 1
102+
fi
103+
done
104+
echo "OK"
105+
106+
# Test 10: Check file size is reasonable (should match CLAUDE.md)
107+
echo -n "✓ Checking file size is reasonable... "
108+
SIZE=$(wc -c < AGENTS.md)
109+
if [ "$SIZE" -lt 1000 ]; then
110+
echo "FAILED"
111+
echo "Error: AGENTS.md content is too small ($SIZE bytes), symlink may be broken"
112+
exit 1
113+
fi
114+
echo "OK (${SIZE} bytes)"
115+
116+
echo ""
117+
echo "✅ All tests passed! AGENTS.md symlink is working correctly."
118+
echo " - Symlink: AGENTS.md -> CLAUDE.md"
119+
echo " - Content size: ${SIZE} bytes"
120+
echo " - Cursor and other AI tools can use AGENTS.md"
121+
echo ""

0 commit comments

Comments
 (0)