436 lines
11 KiB
Markdown
436 lines
11 KiB
Markdown
# Auto-updating Documentation Implementation Plan
|
|
|
|
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
|
|
**Goal:** Create README.md and AGENTS.md that auto-update via git pre-commit hook when files change.
|
|
|
|
**Architecture:** Template files with placeholders + Python generator script that reads deck/collection data + pre-commit hook that runs on every commit.
|
|
|
|
**Tech Stack:** Python 3 stdlib (json, pathlib, argparse)
|
|
|
|
---
|
|
|
|
### Task 1: Create Template Files
|
|
|
|
**Files:**
|
|
- Create: `docs/templates/README.template.md`
|
|
- Create: `docs/templates/AGENTS.template.md`
|
|
|
|
**Step 1: Create docs/templates directory**
|
|
|
|
```bash
|
|
mkdir -p docs/templates
|
|
```
|
|
|
|
**Step 2: Create README template**
|
|
|
|
Create `docs/templates/README.template.md`:
|
|
|
|
```markdown
|
|
# MTG EDH Deck Manager
|
|
|
|
Command-line toolkit for managing Magic: The Gathering Commander (EDH) decks using the Scryfall API.
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
git clone <repo-url>
|
|
cd Decks
|
|
```
|
|
|
|
No dependencies required - pure Python 3 standard library.
|
|
|
|
## Usage
|
|
|
|
### Hydrate a Collection
|
|
|
|
Fetch card data from Scryfall for a decklist:
|
|
|
|
```bash
|
|
python hydrate.py hydrate collection/decklist.txt -o collection_hydrated/ -c card_cache.json
|
|
```
|
|
|
|
### Create a New Deck
|
|
|
|
```bash
|
|
python hydrate.py new my_deck
|
|
```
|
|
|
|
### Analyze Decks
|
|
|
|
Find upgrade options from your collection:
|
|
|
|
```bash
|
|
python scripts/analyze_decks.py --collection collection_hydrated/deck.json --deck-dir decks/
|
|
```
|
|
|
|
### Find Synergies
|
|
|
|
Search for cards by keywords, colors, and type:
|
|
|
|
```bash
|
|
# Find landfall cards in Simic colors
|
|
python scripts/find_synergies.py --collection collection_hydrated/deck.json --colors U G --keywords landfall
|
|
|
|
# Find Bird creatures in Bant colors
|
|
python scripts/find_synergies.py --collection collection_hydrated/deck.json --colors U G W --creature-type Bird
|
|
|
|
# Find instants with CMC 2 or less
|
|
python scripts/find_synergies.py --collection collection_hydrated/deck.json --type instant --cmc-max 2
|
|
```
|
|
|
|
### Generate Reports
|
|
|
|
```bash
|
|
python scripts/deck_report.py --collection collection_hydrated/deck.json --decks-dir decks/ --output report.md
|
|
```
|
|
|
|
## Decks
|
|
|
|
{{DECK_TABLE}}
|
|
|
|
## Workflow
|
|
|
|
1. **Import** - Place text decklists in `collection/`
|
|
2. **Hydrate** - Run `hydrate.py` to fetch Scryfall data → `collection_hydrated/`
|
|
3. **Define** - Create deck JSON files in `decks/`
|
|
4. **Analyze** - Run `analyze_decks.py` to find upgrade options
|
|
5. **Report** - Use `deck_report.py` for markdown summaries
|
|
|
|
## Collection Stats
|
|
|
|
{{COLLECTION_STATS}}
|
|
|
|
---
|
|
|
|
*This file is auto-generated. Edit `docs/templates/README.template.md` instead.*
|
|
```
|
|
|
|
**Step 3: Create AGENTS template**
|
|
|
|
Create `docs/templates/AGENTS.template.md`:
|
|
|
|
```markdown
|
|
# AGENTS.md
|
|
|
|
Guide for AI assistants working on this codebase.
|
|
|
|
## Build/Verify Commands
|
|
|
|
This project has no external dependencies or test framework.
|
|
|
|
```bash
|
|
# No lint/typecheck commands currently
|
|
# Verify scripts work by running them:
|
|
python hydrate.py --help
|
|
python scripts/analyze_decks.py --help
|
|
```
|
|
|
|
## Code Style
|
|
|
|
- **Language:** Python 3 (stdlib only, no external packages)
|
|
- **Formatting:** 4-space indent, max line length ~100
|
|
- **Imports:** Standard library only (argparse, json, pathlib, urllib, re, sys, os, time)
|
|
- **CLI:** Use `argparse` with subparsers for multi-command tools
|
|
- **Error handling:** Print to stderr, use sys.exit(1) for failures
|
|
- **Docstrings:** Triple-quoted at module and function level with usage examples
|
|
- **Type hints:** Optional but encouraged for complex functions
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
├── hydrate.py # Main CLI - fetches card data from Scryfall
|
|
├── card_cache.json # Local cache of Scryfall card data
|
|
├── deck_analysis.json # Analysis output
|
|
├── collection/ # Raw decklist text files
|
|
├── collection_hydrated/ # Enriched card data by type
|
|
│ ├── deck.json # All cards combined
|
|
│ ├── commander.json
|
|
│ ├── creatures.json
|
|
│ ├── instants.json
|
|
│ ├── sorceries.json
|
|
│ ├── artifacts.json
|
|
│ ├── enchantments.json
|
|
│ ├── lands.json
|
|
│ └── planeswalkers.json
|
|
├── decks/ # Deck definitions (JSON)
|
|
│ └── <deck_name>.json # name, commander, colors, archetype, cards
|
|
├── scripts/
|
|
│ ├── analyze_decks.py # Find upgrade options for decks
|
|
│ ├── find_synergies.py # Search for synergistic cards
|
|
│ ├── parse_deck.py # Parse text decklists to JSON
|
|
│ └── deck_report.py # Generate markdown reports
|
|
└── docs/
|
|
└── templates/ # Documentation templates
|
|
```
|
|
|
|
## Data Formats
|
|
|
|
### Deck JSON (`decks/*.json`)
|
|
|
|
```json
|
|
{
|
|
"name": "Deck Name",
|
|
"commander": "Commander Name",
|
|
"colors": ["U", "G", "W"],
|
|
"archetype": "Theme Description",
|
|
"cards": {
|
|
"Card Name": count,
|
|
...
|
|
}
|
|
}
|
|
```
|
|
|
|
### Card Data (`collection_hydrated/*.json`)
|
|
|
|
Array of card objects with Scryfall fields:
|
|
- `name`, `mana_cost`, `cmc`, `colors`, `color_identity`
|
|
- `type_line`, `oracle_text`, `power`, `toughness`, `loyalty`
|
|
- `scryfall_uri`, `count`
|
|
|
|
## Extension Guide
|
|
|
|
### Adding a New Deck
|
|
|
|
1. Create `decks/<name>.json` with required fields
|
|
2. Run `python scripts/update_docs.py` to regenerate documentation
|
|
|
|
### Adding a New Script
|
|
|
|
1. Create `scripts/<name>.py` with argparse CLI
|
|
2. Follow existing patterns: `load_collection()`, argparse with --help
|
|
3. Update README template with usage example
|
|
4. Run `python scripts/update_docs.py`
|
|
|
|
### Modifying Deck Analysis
|
|
|
|
- `scripts/analyze_decks.py`: Core logic for finding upgrades
|
|
- Color identity matching uses `color_identity` field from Scryfall
|
|
- Cards are categorized by `type_line`
|
|
|
|
## Current Decks
|
|
|
|
{{DECK_TABLE}}
|
|
```
|
|
|
|
**Step 4: Commit templates**
|
|
|
|
```bash
|
|
git add docs/templates/
|
|
git commit -m "Add documentation templates"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 2: Create update_docs.py Script
|
|
|
|
**Files:**
|
|
- Create: `scripts/update_docs.py`
|
|
|
|
**Step 1: Create the generator script**
|
|
|
|
Create `scripts/update_docs.py`:
|
|
|
|
```python
|
|
#!/usr/bin/env python3
|
|
"""
|
|
Generate README.md and AGENTS.md from templates.
|
|
Usage:
|
|
python scripts/update_docs.py
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
|
|
|
|
SCRIPT_DIR = Path(__file__).parent
|
|
PROJECT_ROOT = SCRIPT_DIR.parent
|
|
TEMPLATES_DIR = PROJECT_ROOT / "docs" / "templates"
|
|
DECKS_DIR = PROJECT_ROOT / "decks"
|
|
COLLECTION_DIR = PROJECT_ROOT / "collection_hydrated"
|
|
|
|
|
|
def load_decks() -> list[dict]:
|
|
"""Load all deck JSON files."""
|
|
decks = []
|
|
if DECKS_DIR.exists():
|
|
for path in sorted(DECKS_DIR.glob("*.json")):
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
data["filename"] = path.stem
|
|
decks.append(data)
|
|
return decks
|
|
|
|
|
|
def load_collection_stats() -> dict:
|
|
"""Get collection statistics."""
|
|
stats = {
|
|
"total_cards": 0,
|
|
"unique_cards": 0,
|
|
"by_type": {}
|
|
}
|
|
deck_file = COLLECTION_DIR / "deck.json"
|
|
if deck_file.exists():
|
|
with open(deck_file, "r", encoding="utf-8") as f:
|
|
cards = json.load(f)
|
|
stats["unique_cards"] = len(cards)
|
|
stats["total_cards"] = sum(c.get("count", 1) for c in cards)
|
|
for category in ["creatures", "instants", "sorceries", "artifacts", "enchantments", "lands", "planeswalkers"]:
|
|
cat_file = COLLECTION_DIR / f"{category}.json"
|
|
if cat_file.exists():
|
|
with open(cat_file, "r", encoding="utf-8") as f:
|
|
cards = json.load(f)
|
|
stats["by_type"][category] = len(cards)
|
|
return stats
|
|
|
|
|
|
def generate_deck_table(decks: list[dict]) -> str:
|
|
"""Generate markdown table of decks."""
|
|
if not decks:
|
|
return "*No decks defined yet.*"
|
|
lines = ["| Deck | Colors | Commander | Archetype |", "|------|--------|-----------|-----------|"]
|
|
color_map = {"W": "White", "U": "Blue", "B": "Black", "R": "Red", "G": "Green"}
|
|
for deck in decks:
|
|
name = deck.get("name", deck.get("filename", "Unknown"))
|
|
colors = deck.get("colors", [])
|
|
color_str = "".join(colors) if colors else "Colorless"
|
|
commander = deck.get("commander", "Unknown")
|
|
archetype = deck.get("archetype", "-")
|
|
lines.append(f"| {name} | {color_str} | {commander} | {archetype} |")
|
|
return "\n".join(lines)
|
|
|
|
|
|
def generate_collection_stats(stats: dict) -> str:
|
|
"""Generate collection statistics section."""
|
|
lines = [f"- **Total cards:** {stats['total_cards']}", f"- **Unique cards:** {stats['unique_cards']}"]
|
|
if stats["by_type"]:
|
|
lines.append("- **By type:**")
|
|
for category, count in sorted(stats["by_type"].items()):
|
|
lines.append(f" - {category.title()}: {count}")
|
|
return "\n".join(lines)
|
|
|
|
|
|
def generate_docs() -> None:
|
|
"""Generate README.md and AGENTS.md from templates."""
|
|
decks = load_decks()
|
|
stats = load_collection_stats()
|
|
deck_table = generate_deck_table(decks)
|
|
collection_stats = generate_collection_stats(stats)
|
|
|
|
for template_name in ["README.template.md", "AGENTS.template.md"]:
|
|
template_path = TEMPLATES_DIR / template_name
|
|
if not template_path.exists():
|
|
print(f"Template not found: {template_path}")
|
|
continue
|
|
with open(template_path, "r", encoding="utf-8") as f:
|
|
content = f.read()
|
|
content = content.replace("{{DECK_TABLE}}", deck_table)
|
|
content = content.replace("{{COLLECTION_STATS}}", collection_stats)
|
|
output_name = template_name.replace(".template", "")
|
|
output_path = PROJECT_ROOT / output_name
|
|
with open(output_path, "w", encoding="utf-8") as f:
|
|
f.write(content)
|
|
print(f"Generated: {output_path}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
generate_docs()
|
|
```
|
|
|
|
**Step 2: Test the script**
|
|
|
|
```bash
|
|
python scripts/update_docs.py
|
|
```
|
|
|
|
Expected: Creates README.md and AGENTS.md in project root
|
|
|
|
**Step 3: Verify generated files**
|
|
|
|
```bash
|
|
ls -la README.md AGENTS.md
|
|
head -20 README.md
|
|
```
|
|
|
|
**Step 4: Commit the script**
|
|
|
|
```bash
|
|
git add scripts/update_docs.py README.md AGENTS.md
|
|
git commit -m "Add update_docs.py and generate initial documentation"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 3: Create Pre-commit Hook
|
|
|
|
**Files:**
|
|
- Create: `.git/hooks/pre-commit`
|
|
|
|
**Step 1: Create the hook**
|
|
|
|
```bash
|
|
cat > .git/hooks/pre-commit << 'EOF'
|
|
#!/bin/bash
|
|
# Auto-update documentation on commit
|
|
|
|
python scripts/update_docs.py
|
|
|
|
if [ -f README.md ]; then
|
|
git add README.md
|
|
fi
|
|
|
|
if [ -f AGENTS.md ]; then
|
|
git add AGENTS.md
|
|
fi
|
|
EOF
|
|
chmod +x .git/hooks/pre-commit
|
|
```
|
|
|
|
**Step 2: Test the hook**
|
|
|
|
```bash
|
|
# Make a small change to trigger the hook
|
|
echo "# test" >> hydrate.py
|
|
git add hydrate.py
|
|
git commit -m "Test pre-commit hook"
|
|
```
|
|
|
|
Expected: Hook runs, updates docs if needed, includes them in commit
|
|
|
|
**Step 3: Verify hook worked**
|
|
|
|
```bash
|
|
git log -1 --stat
|
|
```
|
|
|
|
Expected: README.md and AGENTS.md may appear in commit if changed
|
|
|
|
---
|
|
|
|
### Task 4: Cleanup and Final Commit
|
|
|
|
**Step 1: Remove test change if added**
|
|
|
|
```bash
|
|
# If you added the test line, revert it:
|
|
git checkout hydrate.py
|
|
```
|
|
|
|
**Step 2: Make a clean commit documenting the hook**
|
|
|
|
```bash
|
|
git add .git/hooks/pre-commit
|
|
git commit -m "Add pre-commit hook for auto-updating documentation"
|
|
```
|
|
|
|
---
|
|
|
|
## Success Criteria
|
|
|
|
- [ ] `README.md` exists with deck table and usage docs
|
|
- [ ] `AGENTS.md` exists with build commands and project structure
|
|
- [ ] `scripts/update_docs.py` generates both files from templates
|
|
- [ ] Pre-commit hook runs and stages updated docs
|
|
- [ ] Templates are editable without touching generated files
|