ai-skills-api/examples/seed-data.py
Lukas Parsons 7f7699ff94 Initial commit: Skills API with MCP servers
- FastAPI backend with SQLite (ai.db)
- Tables: skills, snippets, conventions, cache, memory
- MCP servers: homelab, gameservers, skills
- Docker Compose setup
- Seed data with 8 skills, 2 conventions, 2 snippets
- Token savings patterns via context bundles and caching
2026-03-22 21:18:23 -04:00

221 lines
7.5 KiB
Python

#!/usr/bin/env python3
"""Seed the skills database with useful defaults"""
import httpx
BASE_URL = "http://localhost:8080"
SKILLS = [
{
"id": "homelab-docker-compose",
"name": "Docker Compose Standard",
"category": "homelab",
"description": "Standard Docker Compose configuration patterns",
"content": """Always use docker-compose v3.8+. Include:
- health checks for all services
- restart: unless-stopped policy
- resource limits (memory, CPU)
- named volumes for persistent data
- .env file for secrets (never hardcode)
- explicit network definitions
- logging driver with size limits""",
"tags": ["docker", "compose", "infrastructure"]
},
{
"id": "homelab-traefik",
"name": "Traefik Reverse Proxy",
"category": "homelab",
"description": "Traefik configuration standards",
"content": """For Traefik reverse proxy setups:
- Use Docker provider with watched containers
- Enable ACME/Let's Encrypt for HTTPS
- Store certs in persistent volume
- Use middlewares for auth, rate limiting, redirects
- Label-based routing on containers
- Dashboard protected by auth middleware""",
"tags": ["traefik", "proxy", "https", "infrastructure"]
},
{
"id": "typescript-react",
"name": "React TypeScript Component",
"category": "coding",
"description": "Standard React component patterns with TypeScript",
"content": """React component standards:
- Use functional components with TypeScript interfaces
- Props defined as interface, not type alias
- Use React.FC only when children needed
- Prefer composition over inheritance
- Custom hooks for reusable logic
- Strict null checks enabled
- Avoid any, use unknown if needed""",
"tags": ["react", "typescript", "frontend"]
},
{
"id": "python-async",
"name": "Python Async Patterns",
"category": "coding",
"description": "Async/await best practices in Python",
"content": """Python async standards:
- Use async/await consistently, don't mix sync/async
- Use asyncio.gather() for concurrent operations
- Proper exception handling in async contexts
- Use async context managers (async with)
- Avoid blocking calls in async functions
- Use httpx over requests for async HTTP
- Timeout all async operations""",
"tags": ["python", "async", "asyncio"]
},
{
"id": "api-design",
"name": "REST API Design",
"category": "coding",
"description": "RESTful API design patterns",
"content": """API design standards:
- Use nouns for resources, not verbs
- Proper HTTP methods (GET/POST/PUT/DELETE)
- Return appropriate status codes
- Version APIs (/api/v1/)
- Use query params for filtering, sorting
- Pagination with limit/offset or cursor
- Consistent error response format
- Rate limiting headers""",
"tags": ["api", "rest", "backend"]
},
{
"id": "valheim-server",
"name": "Valheim Server Setup",
"category": "gameserver",
"description": "Valheim dedicated server configuration",
"content": """Valheim server standards:
- Run in Docker with persistent volumes
- Backup world files regularly
- Set -public 0 for private servers
- Configure admin list properly
- Monitor RAM usage (2-4GB typical)
- Use server sync for crossplay
- Restart nightly for memory leaks""",
"tags": ["valheim", "gaming", "docker"]
},
{
"id": "minecraft-server",
"name": "Minecraft Server Setup",
"category": "gameserver",
"description": "Minecraft server configuration patterns",
"content": """Minecraft server standards:
- Use PaperMC for performance
- Pre-generate world chunks
- Configure view-distance appropriately (6-10)
- Use Aikar's flags for JVM optimization
- Regular backups with rotation
- Whitelist for private servers
- Monitor TPS and chunk loading""",
"tags": ["minecraft", "gaming", "java"]
},
{
"id": "git-commits",
"name": "Git Commit Standards",
"category": "coding",
"description": "Commit message conventions",
"content": """Commit message format:
- Conventional Commits (feat:, fix:, chore:, etc.)
- Imperative mood ("add feature" not "added feature")
- First line max 50 chars
- Blank line before body
- Body wraps at 72 chars
- Reference issues/PRs when applicable""",
"tags": ["git", "workflow", "documentation"]
}
]
CONVENTIONS = [
{
"id": "home-server-conventions",
"project_path": "/opt/home-server",
"name": "Home Server Standards",
"content": """All home server deployments:
- Docker Compose for all services
- Traefik for reverse proxy
- Health checks on all containers
- Centralized logging
- Automated backups
- Resource limits defined""",
"auto_inject": True
}
]
SNIPPETS = [
{
"id": "docker-healthcheck",
"name": "Docker Health Check Template",
"language": "yaml",
"category": "docker",
"content": """healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s""",
"tags": ["docker", "health", "template"]
},
{
"id": "traefik-labels",
"name": "Traefik Docker Labels",
"language": "yaml",
"category": "docker",
"content": """labels:
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=Host(`myapp.example.com`)"
- "traefik.http.routers.myapp.entrypoints=websecure"
- "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
- "traefik.http.services.myapp.loadbalancer.server.port=8080""",
"tags": ["traefik", "labels", "template"]
}
]
def seed():
with httpx.Client(timeout=30.0) as client:
print("Seeding skills...")
for skill in SKILLS:
try:
response = client.post(f"{BASE_URL}/skills", json=skill)
if response.status_code == 200:
print(f"{skill['id']}")
elif response.status_code == 400:
print(f" ~ {skill['id']} (already exists)")
else:
print(f"{skill['id']}: {response.text}")
except Exception as e:
print(f"{skill['id']}: {e}")
print("\nSeeding conventions...")
for convention in CONVENTIONS:
try:
response = client.post(f"{BASE_URL}/conventions", json=convention)
if response.status_code == 200:
print(f"{convention['id']}")
elif response.status_code == 400:
print(f" ~ {convention['id']} (already exists)")
else:
print(f"{convention['id']}: {response.text}")
except Exception as e:
print(f"{convention['id']}: {e}")
print("\nSeeding snippets...")
for snippet in SNIPPETS:
try:
response = client.post(f"{BASE_URL}/snippets", json=snippet)
if response.status_code == 200:
print(f"{snippet['id']}")
elif response.status_code == 400:
print(f" ~ {snippet['id']} (already exists)")
else:
print(f"{snippet['id']}: {response.text}")
except Exception as e:
print(f"{snippet['id']}: {e}")
print("\nDone! Check http://localhost:8080/docs")
if __name__ == "__main__":
seed()