This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
# Install dependencies (uses uv package manager)
uv sync
# Install with dev dependencies
uv sync --extra dev
# Run the CLI
uv run gh-space-shooter <username>
# Run tests
uv run pytest tests/ -v
# Run a single test file
uv run pytest tests/test_strategies.py -v
# Run a specific test
uv run pytest tests/test_strategies.py::test_column_strategy -v
# Run the web app (from app/ directory)
uv run --project gh-space-shooter-app uvicorn main:appRequires a GitHub Personal Access Token with read:user scope:
export GH_TOKEN=your_token_here
# Or create a .env file with GH_TOKEN=your_tokenCurrent main usage: GitHub Action that automatically updates a game GIF in user repositories daily (see .github/workflows/ for the action definition).
Web App: A FastAPI-based web application is available in the app/ directory for on-demand GIF generation. See app/README.md for details.
This is a CLI tool that transforms GitHub contribution graphs into animated space shooter GIFs using Pillow.
- CLI (
cli.py) - Typer-based entry point that orchestrates the pipeline- Options:
--write-dataurl-to/-wdt- Generate WebP as data URL in HTML<img>tag and write to text file--output/-o- Generate animated visualization (GIF or WebP)--write-dataurl-toand--outputare mutually exclusive
- Options:
- GitHubClient (
github_client.py) - Fetches contribution data via GitHub GraphQL API, returns typedContributionDatadict - Animator (
game/animator.py) - Main game loop that coordinates strategy execution and frame generation - GameState (
game/game_state.py) - Central state container holding ship, enemies, bullets, explosions - Renderer (
game/renderer.py) - Converts GameState to PIL Images each frame
Strategies (game/strategies/) define how the ship clears enemies:
BaseStrategy- Abstract base defininggenerate_actions(game_state) -> Iterator[Action]ColumnStrategy- Clears enemies column by column (left to right)RowStrategy- Clears enemies row by row (top to bottom)RandomStrategy- Targets enemies in random order
Strategies yield Action(x, shoot) objects. The Animator processes these: moving the ship to position x, waiting for movement/cooldown to complete, then shooting if shoot=True.
All game objects inherit from Drawable (game/drawables/drawable.py):
animate(delta_time)- Update state (position, cooldowns, particles)draw(draw, context)- Render to PIL ImageDraw
Drawables: Ship, Enemy, Bullet, Explosion, Starfield
The RenderContext (game/render_context.py) holds theming (colors, cell sizes, padding) and coordinate conversion helpers.
Output providers encode frames to different formats for writing:
GifOutputProvider- Animated GIF formatWebPOutputProvider- Animated WebP formatWebpDataUrlOutputProvider- HTML<img>tag with WebP data URL for direct embedding
Each provider implements:
encode(frames, frame_duration) -> bytes- Encode frames to output formatwrite(path, data) -> None- Write encoded data to file (providers store path from constructor)
In Animator._generate_frames():
- Strategy yields next action
- Ship moves to target x position (animate frames until arrived)
- Ship shoots if action.shoot (animate frames for bullet travel + explosions)
- Repeat until all enemies destroyed
Frame rate is configurable (default 40 FPS). All speeds use delta_time for frame-rate independence.
NUM_WEEKS = 52- Contribution graph widthNUM_DAYS = 7- Contribution graph height- Speeds are in cells/second, durations in seconds