Add AGENTS.md with coding guidelines for AI agents

Comprehensive guide covering:
- Build/lint/test commands including single test execution
- Code style: imports, formatting, types, naming, error handling
- Discord patterns: @logged_command, autocomplete, embed emojis
- Service layer abstraction rules
- Model patterns (from_api_data, required IDs)
- Testing with aioresponses and complete model data
- Critical rules for git, services, and commits

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2026-01-23 15:13:25 -06:00
parent 2881207ca4
commit d6ec2a11ec

190
AGENTS.md Normal file
View File

@ -0,0 +1,190 @@
# AGENTS.md - Discord Bot v2.0
Guidelines for AI coding agents working in this repository.
## Quick Reference
**Start bot**: `python bot.py`
**Run all tests**: `python -m pytest --tb=short -q`
**Run single test file**: `python -m pytest tests/test_models.py -v`
**Run single test**: `python -m pytest tests/test_models.py::TestTeamModel::test_team_creation_minimal -v`
**Run tests matching pattern**: `python -m pytest -k "test_player" -v`
## Project Structure
- `bot.py` - Main entry point
- `commands/` - Discord slash commands (package-based)
- `services/` - API service layer (BaseService pattern)
- `models/` - Pydantic data models
- `views/` - Discord UI components (embeds, modals)
- `utils/` - Logging, decorators, caching
- `tests/` - pytest test suite
## Code Style
### Imports
Order: stdlib, third-party, local. Separate groups with blank lines.
```python
import asyncio
from typing import Optional, List
import discord
from discord.ext import commands
from services.player_service import player_service
from utils.decorators import logged_command
```
### Formatting
- Line length: 100 characters max
- Docstrings: Google style with triple quotes
- Indentation: 4 spaces
- Trailing commas in multi-line structures
### Type Hints
Always use type hints for function signatures:
```python
async def get_player(self, player_id: int) -> Optional[Player]:
async def search_players(self, query: str, limit: int = 10) -> List[Player]:
```
### Naming Conventions
- Classes: `PascalCase` (PlayerService, TeamInfoCommands)
- Functions/methods: `snake_case` (get_player, search_players)
- Constants: `UPPER_SNAKE_CASE` (SBA_CURRENT_SEASON)
- Private: prefix with `_` (_client, _team_service)
### Error Handling
Use custom exceptions from `exceptions.py`. Prefer "raise or return" over Optional:
```python
from exceptions import APIException, PlayerNotFoundError
async def get_player(self, player_id: int) -> Player:
result = await self.get_by_id(player_id)
if result is None:
raise PlayerNotFoundError(f"Player {player_id} not found")
return result
```
## Discord Command Patterns
### Always use @logged_command decorator
Eliminates boilerplate logging. Class must have `self.logger` attribute:
```python
class PlayerInfoCommands(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.logger = get_contextual_logger(f'{__name__}.PlayerInfoCommands')
@discord.app_commands.command(name="player")
@logged_command("/player")
async def player_command(self, interaction, name: str):
# Business logic only - no try/catch boilerplate needed
player = await player_service.get_player_by_name(name)
await interaction.followup.send(embed=create_embed(player))
```
### Autocomplete: Use standalone functions (not methods)
```python
async def player_name_autocomplete(
interaction: discord.Interaction,
current: str,
) -> List[discord.app_commands.Choice[str]]:
if len(current) < 2:
return []
try:
players = await player_service.search_players(current, limit=25)
return [discord.app_commands.Choice(name=p.name, value=p.name) for p in players]
except Exception:
return [] # Never break autocomplete
class MyCommands(commands.Cog):
@discord.app_commands.command()
@discord.app_commands.autocomplete(name=player_name_autocomplete)
async def my_command(self, interaction, name: str): ...
```
### Embed emoji rules
Template methods auto-add emojis. Never double up:
```python
# CORRECT - template adds emoji
embed = EmbedTemplate.success(title="Operation Completed") # Results in: "Operation Completed"
# WRONG - double emoji
embed = EmbedTemplate.success(title="Operation Completed") # Results in: " Operation Completed"
# For custom emoji, use create_base_embed
embed = EmbedTemplate.create_base_embed(title="Custom Title", color=EmbedColors.SUCCESS)
```
## Service Layer
### Never bypass services for API calls
```python
# CORRECT
player = await player_service.get_player(player_id)
# WRONG - never do this
client = await player_service.get_client()
await client.get(f'players/{player_id}')
```
### Key service methods
- `TeamService.get_team(team_id)` - not `get_team_by_id()`
- `PlayerService.search_players(query, limit, all_seasons=True)` - cross-season search
## Models
### Use from_api_data() classmethod
```python
player = Player.from_api_data(api_response)
```
### Database entities require id field
```python
class Player(SBABaseModel):
id: int = Field(..., description="Player ID from database") # Required, not Optional
```
## Testing
### Use aioresponses for HTTP mocking
```python
from aioresponses import aioresponses
@pytest.mark.asyncio
async def test_get_player():
with aioresponses() as m:
m.get("https://api.example.com/v3/players/1", payload={"id": 1, "name": "Test"})
result = await api_client.get("players", object_id=1)
assert result["name"] == "Test"
```
### Provide complete model data
Pydantic validates all fields. Use helper functions for test data:
```python
def create_player_data(player_id: int, name: str, **kwargs):
return {"id": player_id, "name": name, "wara": 2.5, "season": 13, "pos_1": "CF", **kwargs}
```
## Critical Rules
1. **Git**: Never commit directly to `main`. Create feature branches.
2. **Services**: Always use service layer methods, never direct API client access.
3. **Embeds**: Don't add emojis to titles when using template methods (success/error/warning/info).
4. **Tests**: Include docstrings explaining "what" and "why" for each test.
5. **Commits**: Do not commit without user approval.
## Documentation
Check `CLAUDE.md` files in directories for detailed patterns:
- `commands/CLAUDE.md` - Command architecture
- `services/CLAUDE.md` - Service patterns
- `models/CLAUDE.md` - Model validation
- `tests/CLAUDE.md` - Testing strategies