Optimize CLAUDE.md from 408 to 54 lines
Remove full code style guide with examples, architecture code blocks, game engine patterns, and testing examples. Keep commands, critical rules, and architecture summary. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
69daedfa02
commit
1c30d3fe56
424
CLAUDE.md
424
CLAUDE.md
@ -1,407 +1,53 @@
|
||||
# Mantimon TCG
|
||||
|
||||
Guidelines for agentic coding agents working on this codebase.
|
||||
Pokemon TCG-inspired web app with single-player RPG campaign mode. Home-rule-modified rules.
|
||||
|
||||
## Project-Specific Skills
|
||||
## Project Skills
|
||||
|
||||
Local skills are available in `.claude/skills/`. Load them when the user invokes the corresponding command:
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `/backend-phase` | Manage phased backend development |
|
||||
| `/frontend-phase` | Manage phased frontend development |
|
||||
| `/code-audit` | Audit backend Python code |
|
||||
| `/frontend-code-audit` | Audit frontend Vue/TypeScript/Phaser code |
|
||||
| `/dev-server` | Start/stop/status the dev environment |
|
||||
|
||||
| Command | Skill File | Description |
|
||||
|---------|------------|-------------|
|
||||
| `/backend-phase` | `.claude/skills/backend-phase/SKILL.md` | Manage phased backend development workflow. Track tasks, mark progress, generate phase plans. |
|
||||
| `/frontend-phase` | `.claude/skills/frontend-phase/SKILL.md` | Manage phased frontend development workflow. Track tasks, mark progress, generate phase plans. |
|
||||
| `/code-audit` | `.claude/skills/code-audit/SKILL.md` | Audit backend Python code for errors, security issues, and architecture violations. Uses patterns in `patterns/` subdirectory. |
|
||||
| `/frontend-code-audit` | `.claude/skills/frontend-code-audit/SKILL.md` | Audit frontend Vue/TypeScript/Phaser code for errors, security issues, and architecture violations. |
|
||||
| `/dev-server` | `.claude/skills/dev-server/SKILL.md` | Start/stop/status the complete dev environment (Docker infra + backend + frontend). |
|
||||
Skills in `.claude/skills/` — read the SKILL.md before executing.
|
||||
|
||||
When a user invokes one of these commands, **read the corresponding SKILL.md file first** to understand the full instructions before executing.
|
||||
## Commands
|
||||
|
||||
## Project Overview
|
||||
|
||||
Mantimon TCG is a home-rule-modified Pokemon Trading Card Game web application inspired by the Gameboy Color game *Pokemon TCG*. The core experience is a **single-player RPG campaign**:
|
||||
|
||||
- **Campaign Mode**: Challenge NPCs at themed clubs, defeat Club Leaders to earn medals, collect all medals to face Grand Masters and become Champion
|
||||
- **Collection Building**: Win matches to earn booster packs, build your card collection
|
||||
- **Deck Building**: Construct decks from your collection to take on tougher opponents
|
||||
- **Multiplayer (Optional)**: PvP matches for competitive play
|
||||
|
||||
## Tech Stack
|
||||
|
||||
### Frontend
|
||||
| Technology | Purpose |
|
||||
|------------|---------|
|
||||
| Vue 3 | UI framework (Composition API + `<script setup>`) |
|
||||
| Phaser 3 | Game canvas (matches, pack opening) |
|
||||
| TypeScript | Type safety |
|
||||
| Pinia | State management |
|
||||
| Tailwind CSS | Styling |
|
||||
| Socket.io-client | Real-time communication |
|
||||
| Vite | Build tool |
|
||||
|
||||
### Backend
|
||||
| Technology | Purpose |
|
||||
|------------|---------|
|
||||
| FastAPI | REST API framework |
|
||||
| Python 3.11+ | Backend language |
|
||||
| SQLAlchemy 2.0 | ORM (async) |
|
||||
| PostgreSQL | Database |
|
||||
| Redis | Caching, session storage |
|
||||
| python-socketio | WebSocket server |
|
||||
| Pydantic v2 | Validation |
|
||||
| Alembic | Database migrations |
|
||||
|
||||
---
|
||||
|
||||
## Quick Commands
|
||||
|
||||
### Development Servers
|
||||
```bash
|
||||
cd frontend && npm run dev # Frontend dev server
|
||||
cd backend && uv run uvicorn app.main:app --reload # Backend dev server
|
||||
```
|
||||
|
||||
### Testing
|
||||
```bash
|
||||
# Frontend (Vitest)
|
||||
cd frontend && npm run test # Run all tests
|
||||
cd frontend && npm run test -- path/to/file # Run single test file
|
||||
cd frontend && npm run test -- -t "test name" # Run by test name
|
||||
|
||||
# Backend (pytest with uv)
|
||||
cd backend && uv run pytest # Run all tests
|
||||
cd backend && uv run pytest tests/test_file.py # Single file
|
||||
cd backend && uv run pytest tests/test_file.py::test_fn # Single test
|
||||
cd backend && uv run pytest -k "test_name" # By name pattern
|
||||
cd backend && uv run pytest -x # Stop on first failure
|
||||
cd backend && uv run pytest --cov=app # With coverage
|
||||
```
|
||||
|
||||
### Linting & Formatting
|
||||
```bash
|
||||
# Frontend
|
||||
cd frontend && npm run lint # ESLint
|
||||
cd frontend && npm run typecheck # TypeScript check
|
||||
cd frontend && npm run dev # Dev server
|
||||
cd frontend && npm run test # Vitest
|
||||
cd frontend && npm run lint # ESLint
|
||||
cd frontend && npm run typecheck # TypeScript
|
||||
|
||||
# Backend (uses uv for all commands)
|
||||
cd backend && uv run black app tests # Format with Black
|
||||
cd backend && uv run black --check . # Check formatting (CI)
|
||||
cd backend && uv run ruff check . # Lint with Ruff
|
||||
cd backend && uv run ruff check --fix . # Auto-fix lint issues
|
||||
cd backend && uv run mypy app # Type check
|
||||
# Backend (all via uv)
|
||||
cd backend && uv run uvicorn app.main:app --reload # Dev server
|
||||
cd backend && uv run pytest # Tests
|
||||
cd backend && uv run pytest -x # Stop on first failure
|
||||
cd backend && uv run black app tests && uv run ruff check . # Format + lint
|
||||
cd backend && uv run mypy app # Type check
|
||||
```
|
||||
|
||||
### Dependency Management (Backend)
|
||||
```bash
|
||||
cd backend && uv add <package> # Add runtime dependency
|
||||
cd backend && uv add --dev <package> # Add dev dependency
|
||||
cd backend && uv sync # Install all dependencies
|
||||
cd backend && uv lock # Update lock file
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Code Style
|
||||
|
||||
### General Rules
|
||||
- Line length: 100 characters max
|
||||
- Line length: 100 chars
|
||||
- Indentation: 2 spaces (frontend), 4 spaces (backend)
|
||||
- Trailing commas in multi-line structures
|
||||
- Explicit over implicit
|
||||
- Use `import type` for type-only TS imports, `@/` alias for local imports
|
||||
- Python: type hints required on all function signatures
|
||||
|
||||
### TypeScript/Vue
|
||||
## Architecture
|
||||
|
||||
**Import order** (separated by blank lines):
|
||||
1. Standard library / Vue core
|
||||
2. Third-party packages
|
||||
3. Local imports (use `@/` alias)
|
||||
- **Frontend**: Vue 3 + Phaser 3 (game canvas). Communication via event bridge (`phaserGame.events.emit/on`)
|
||||
- **Backend**: FastAPI + PostgreSQL + Redis + Socket.io. Service layer pattern — never bypass services for DB access
|
||||
- **Game Engine** (`app/core/`): Must stay decoupled from DB/network (forkable for offline mode). No imports from `app.services`, `app.api`, or SQLAlchemy in core.
|
||||
|
||||
```typescript
|
||||
import { ref, computed } from 'vue'
|
||||
## Critical Rules
|
||||
|
||||
import { useGameStore } from '@/stores/game'
|
||||
import type { Card, GameState } from '@/types'
|
||||
|
||||
// Always use `import type` for type-only imports
|
||||
import type { Player } from '@/types/player'
|
||||
|
||||
// Prefer const over let
|
||||
const cards = ref<Card[]>([])
|
||||
```
|
||||
|
||||
**Naming conventions:**
|
||||
| Type | Convention | Example |
|
||||
|------|------------|---------|
|
||||
| Vue components | PascalCase | `CardHand.vue`, `GameBoard.vue` |
|
||||
| Phaser scenes | PascalCase | `MatchScene.ts`, `PackOpeningScene.ts` |
|
||||
| TypeScript files | camelCase | `useWebSocket.ts`, `cardUtils.ts` |
|
||||
| Constants | UPPER_SNAKE_CASE | `MAX_HAND_SIZE`, `PRIZE_COUNT` |
|
||||
|
||||
### Python
|
||||
|
||||
**Import order** (separated by blank lines):
|
||||
1. Standard library
|
||||
2. Third-party packages
|
||||
3. Local imports
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models import Card, Player
|
||||
from app.services.game_service import GameService
|
||||
```
|
||||
|
||||
**Type hints required** for all function signatures:
|
||||
```python
|
||||
async def get_card(card_id: int, db: AsyncSession = Depends(get_db)) -> Card:
|
||||
...
|
||||
```
|
||||
|
||||
**Naming conventions:**
|
||||
| Type | Convention | Example |
|
||||
|------|------------|---------|
|
||||
| Modules | snake_case | `game_engine.py`, `card_service.py` |
|
||||
| Database tables | snake_case | `user_collections`, `match_history` |
|
||||
| Constants | UPPER_SNAKE_CASE | `MAX_HAND_SIZE`, `PRIZE_COUNT` |
|
||||
|
||||
---
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Frontend: Vue + Phaser Integration
|
||||
Phaser mounts as a Vue component. Communication via event bridge:
|
||||
```typescript
|
||||
// Vue -> Phaser
|
||||
phaserGame.value?.events.emit('card:play', { cardId, targetId })
|
||||
|
||||
// Phaser -> Vue
|
||||
phaserGame.value?.events.on('animation:complete', handleAnimationComplete)
|
||||
```
|
||||
|
||||
### Frontend: State Management (Pinia)
|
||||
```typescript
|
||||
export const useGameStore = defineStore('game', () => {
|
||||
const gameState = ref<GameState | null>(null)
|
||||
const myHand = computed(() => gameState.value?.myHand ?? [])
|
||||
return { gameState, myHand }
|
||||
})
|
||||
```
|
||||
|
||||
### Backend: Service Layer
|
||||
**Never bypass services for business logic:**
|
||||
```python
|
||||
# CORRECT
|
||||
card = await card_service.get_card(card_id)
|
||||
result = await game_service.play_card(game_id, player_id, card_id)
|
||||
|
||||
# WRONG - direct DB access in endpoint
|
||||
card = await db.execute(select(Card).where(Card.id == card_id))
|
||||
```
|
||||
|
||||
### Backend: Async by Default
|
||||
All I/O operations use async/await:
|
||||
```python
|
||||
async def resolve_attack(attacker: Card, defender: Card) -> AttackResult:
|
||||
...
|
||||
```
|
||||
|
||||
### Backend: WebSocket Events
|
||||
```python
|
||||
# All game actions go through WebSocket for real-time sync
|
||||
@sio.on('game:action')
|
||||
async def handle_game_action(sid, data):
|
||||
action_type = data['type']
|
||||
|
||||
# Validate action is legal
|
||||
validation = await game_engine.validate_action(game_id, player_id, data)
|
||||
if not validation.valid:
|
||||
await sio.emit('game:error', {'message': validation.reason}, to=sid)
|
||||
return
|
||||
|
||||
# Execute and broadcast
|
||||
new_state = await game_engine.execute_action(game_id, data)
|
||||
await broadcast_game_state(game_id, new_state)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Game Engine Patterns
|
||||
|
||||
### Card Effect System
|
||||
Cards are data-driven. Effects reference handler functions:
|
||||
|
||||
```python
|
||||
# Card definition (JSON/DB)
|
||||
{
|
||||
"id": "pikachu_base_001",
|
||||
"name": "Pikachu",
|
||||
"hp": 60,
|
||||
"type": "lightning",
|
||||
"attacks": [
|
||||
{
|
||||
"name": "Thunder Shock",
|
||||
"cost": ["lightning"],
|
||||
"damage": 20,
|
||||
"effect": "may_paralyze", # References effect handler
|
||||
"effect_params": {"chance": 0.5}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# Effect handler (Python)
|
||||
@effect_handler("may_paralyze")
|
||||
async def handle_may_paralyze(context: EffectContext, params: dict) -> None:
|
||||
if random.random() < params["chance"]:
|
||||
context.defender.add_status("paralyzed")
|
||||
```
|
||||
|
||||
### Turn State Machine
|
||||
```python
|
||||
class TurnPhase(Enum):
|
||||
DRAW = "draw"
|
||||
MAIN = "main"
|
||||
ATTACK = "attack"
|
||||
END = "end"
|
||||
|
||||
# Transitions are explicit
|
||||
VALID_TRANSITIONS = {
|
||||
TurnPhase.DRAW: [TurnPhase.MAIN],
|
||||
TurnPhase.MAIN: [TurnPhase.ATTACK, TurnPhase.END], # Can skip attack
|
||||
TurnPhase.ATTACK: [TurnPhase.END],
|
||||
TurnPhase.END: [TurnPhase.DRAW], # Next player's turn
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Guidelines
|
||||
|
||||
### Test Docstrings Required
|
||||
Every test must include a docstring explaining "what" and "why":
|
||||
```python
|
||||
@pytest.mark.asyncio
|
||||
async def test_draw_card():
|
||||
"""
|
||||
Test that drawing a card moves it from deck to hand.
|
||||
|
||||
Verifies the fundamental draw mechanic works correctly
|
||||
and updates both zones appropriately.
|
||||
"""
|
||||
# test implementation
|
||||
```
|
||||
|
||||
### Frontend Tests (Vitest)
|
||||
```typescript
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
|
||||
describe('CardHand', () => {
|
||||
it('renders cards in hand', () => {
|
||||
const wrapper = mount(CardHand, {
|
||||
props: { cards: [{ id: '1', name: 'Pikachu' }] }
|
||||
})
|
||||
expect(wrapper.text()).toContain('Pikachu')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Critical Security Rules
|
||||
|
||||
### Hidden Information
|
||||
**Never expose to clients:**
|
||||
- Deck order (either player)
|
||||
- Opponent's hand contents
|
||||
- Unrevealed prize cards
|
||||
- RNG seeds or future random results
|
||||
|
||||
```python
|
||||
# Correct: Only send counts for opponent's hidden zones
|
||||
opponent_hand_count=len(opponent.hand) # ONLY count, not contents
|
||||
opponent_deck_count=len(opponent.deck) # ONLY count
|
||||
```
|
||||
|
||||
### Server Authority
|
||||
- All game logic runs server-side
|
||||
- Client sends intentions, server validates and executes
|
||||
- Never trust client-provided game state
|
||||
|
||||
---
|
||||
|
||||
## Critical Rules Summary
|
||||
|
||||
1. **Git**: Never commit directly to `main`. Create feature branches.
|
||||
2. **Commits**: Do not commit without user approval.
|
||||
3. **Hidden Info**: Never send deck contents, opponent hand, or unrevealed prizes to client.
|
||||
4. **Validation**: Always validate actions server-side. Never trust client.
|
||||
5. **Tests**: Include docstrings explaining "what" and "why" for each test.
|
||||
6. **Phaser in Vue**: Keep Phaser scenes focused on rendering. Game logic lives in backend.
|
||||
7. **Services**: Never bypass the service layer for business logic.
|
||||
8. **Core Engine Independence**: Keep `app/core/` decoupled from DB/network (see below).
|
||||
|
||||
---
|
||||
|
||||
## Core Engine Independence (Offline Fork Support)
|
||||
|
||||
> **Long-term goal**: The `backend/app/core/` module should remain forkable as a standalone offline game.
|
||||
|
||||
The game engine must stay **completely decoupled** from network and database concerns to enable a future offline/standalone version of the RPG campaign mode.
|
||||
|
||||
### Rules for `app/core/` Module
|
||||
|
||||
| DO | DON'T |
|
||||
|----|-------|
|
||||
| Accept `CardDefinition` objects as parameters | Import from `app.services` or `app.api` |
|
||||
| Use `RandomProvider` protocol for RNG | Import database session types |
|
||||
| Keep state self-contained in `GameState` | Make network calls or database queries |
|
||||
| Use sync logic (async wrappers at service layer) | Require authentication or user sessions |
|
||||
| Load configuration from `RulesConfig` objects | Hard-code database connection strings |
|
||||
|
||||
### Import Boundaries
|
||||
|
||||
```python
|
||||
# ALLOWED in app/core/
|
||||
from app.core.models import CardDefinition, GameState
|
||||
from app.core.config import RulesConfig
|
||||
from app.core.rng import RandomProvider
|
||||
|
||||
# FORBIDDEN in app/core/
|
||||
from app.services.card_service import CardService # NO - DB dependency
|
||||
from app.api.deps import get_current_user # NO - Auth dependency
|
||||
from sqlalchemy.ext.asyncio import AsyncSession # NO - DB dependency
|
||||
```
|
||||
|
||||
### Why This Matters
|
||||
|
||||
See `docs/ARCHITECTURE.md#offline-standalone-fork` for full details. The core engine should be directly copyable to a standalone Python application with:
|
||||
- Card definitions loaded from JSON files
|
||||
- Save data stored locally
|
||||
- No network or authentication requirements
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
mantimon-tcg/
|
||||
├── frontend/ # Vue 3 + Phaser 3
|
||||
│ ├── src/
|
||||
│ │ ├── components/ # Vue components
|
||||
│ │ ├── pages/ # Route pages
|
||||
│ │ ├── stores/ # Pinia stores
|
||||
│ │ ├── game/ # Phaser scenes and game objects
|
||||
│ │ └── composables/ # Vue composables
|
||||
│ └── package.json
|
||||
├── backend/ # FastAPI + PostgreSQL
|
||||
│ ├── app/
|
||||
│ │ ├── api/ # REST endpoints
|
||||
│ │ ├── core/ # Game engine
|
||||
│ │ ├── models/ # Pydantic + SQLAlchemy models
|
||||
│ │ ├── services/ # Business logic
|
||||
│ │ └── websocket/ # Socket.io handlers
|
||||
│ └── pyproject.toml
|
||||
└── shared/ # Shared types/schemas
|
||||
```
|
||||
1. Never commit without user approval. Never commit directly to `main`.
|
||||
2. **Hidden info**: Never send deck contents, opponent hand, or unrevealed prizes to client
|
||||
3. All game logic server-side. Client sends intentions, server validates.
|
||||
4. Test docstrings required explaining "what" and "why"
|
||||
5. Phaser scenes handle rendering only — game logic lives in backend
|
||||
6. `app/core/` imports only from `app.core.*` — see `docs/ARCHITECTURE.md#offline-standalone-fork`
|
||||
|
||||
Loading…
Reference in New Issue
Block a user