# AGENTS.md - Mantimon TCG Guidelines for agentic coding agents working on this codebase. ## 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 # 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 ``` ### Dependency Management (Backend) ```bash cd backend && uv add # Add runtime dependency cd backend && uv add --dev # 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 - Indentation: 2 spaces (frontend), 4 spaces (backend) - Trailing commas in multi-line structures - Explicit over implicit ### TypeScript/Vue **Import order** (separated by blank lines): 1. Standard library / Vue core 2. Third-party packages 3. Local imports (use `@/` alias) ```typescript import { ref, computed } from 'vue' 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([]) ``` **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(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: ... ``` --- ## 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 ```