Initial commit
This commit is contained in:
327
skills/project-structure/SKILL.md
Normal file
327
skills/project-structure/SKILL.md
Normal file
@@ -0,0 +1,327 @@
|
||||
---
|
||||
name: grey-haven-project-structure
|
||||
description: Organize Grey Haven projects following standard structures for TanStack Start (frontend) and FastAPI (backend). Use when creating new projects, organizing files, or refactoring project layout.
|
||||
---
|
||||
|
||||
# Grey Haven Project Structure
|
||||
|
||||
Follow Grey Haven Studio's standardized project structures for **TypeScript/React** (TanStack Start) and **Python/FastAPI** projects.
|
||||
|
||||
## Frontend Structure (TanStack Start + React 19)
|
||||
|
||||
Based on `cvi-template` - TanStack Start, React 19, Drizzle, Better-auth.
|
||||
|
||||
### Directory Layout
|
||||
|
||||
```
|
||||
project-root/
|
||||
├── .claude/ # Claude Code configuration
|
||||
├── .github/workflows/ # GitHub Actions (CI/CD)
|
||||
├── public/ # Static assets
|
||||
├── src/
|
||||
│ ├── routes/ # TanStack Router file-based routes
|
||||
│ │ ├── __root.tsx # Root layout
|
||||
│ │ ├── index.tsx # Homepage
|
||||
│ │ ├── _authenticated/ # Protected routes group
|
||||
│ │ │ ├── _layout.tsx # Auth layout wrapper
|
||||
│ │ │ ├── dashboard.tsx # /dashboard
|
||||
│ │ │ └── profile.tsx # /profile
|
||||
│ │ └── auth/ # Auth routes
|
||||
│ │ ├── login.tsx # /auth/login
|
||||
│ │ └── signup.tsx # /auth/signup
|
||||
│ ├── lib/
|
||||
│ │ ├── components/ # React components
|
||||
│ │ │ ├── ui/ # Shadcn UI (PascalCase)
|
||||
│ │ │ ├── auth/ # Auth components
|
||||
│ │ │ ├── layout/ # Layout components
|
||||
│ │ │ └── shared/ # Shared components
|
||||
│ │ ├── server/ # Server-side code
|
||||
│ │ │ ├── schema/ # Drizzle schema (snake_case)
|
||||
│ │ │ ├── functions/ # Server functions
|
||||
│ │ │ ├── auth.ts # Better-auth config
|
||||
│ │ │ └── db.ts # Database connections
|
||||
│ │ ├── config/ # Configuration
|
||||
│ │ │ ├── env.ts # Environment validation
|
||||
│ │ │ └── auth.ts # Auth configuration
|
||||
│ │ ├── middleware/ # Route middleware
|
||||
│ │ ├── hooks/ # Custom React hooks (use-*)
|
||||
│ │ ├── utils/ # Utility functions
|
||||
│ │ └── types/ # TypeScript types
|
||||
│ └── tests/
|
||||
│ ├── unit/ # Vitest unit tests
|
||||
│ ├── integration/ # Vitest integration tests
|
||||
│ └── e2e/ # Playwright E2E tests
|
||||
├── migrations/ # Drizzle migrations
|
||||
├── .env.example # Example environment variables
|
||||
├── .prettierrc # Prettier (90 chars, double quotes)
|
||||
├── .eslintrc # ESLint (any allowed, strict off)
|
||||
├── tsconfig.json # TypeScript (~/* path alias)
|
||||
├── commitlint.config.cjs # Commitlint (100 char header)
|
||||
├── drizzle.config.ts # Drizzle ORM config
|
||||
├── vite.config.ts # Vite configuration
|
||||
├── vitest.config.ts # Vitest test configuration
|
||||
├── package.json # Dependencies (use bun!)
|
||||
└── README.md # Project documentation
|
||||
```
|
||||
|
||||
### Key Frontend Patterns
|
||||
|
||||
#### Path Imports - Always Use ~/* Alias
|
||||
|
||||
```typescript
|
||||
// ✅ Correct - Use ~/* path alias
|
||||
import { Button } from "~/lib/components/ui/button";
|
||||
import { getUserById } from "~/lib/server/functions/users";
|
||||
import { env } from "~/lib/config/env";
|
||||
import { useAuth } from "~/lib/hooks/use-auth";
|
||||
|
||||
// ❌ Wrong - Relative paths
|
||||
import { Button } from "../../lib/components/ui/button";
|
||||
```
|
||||
|
||||
#### File Naming Conventions
|
||||
|
||||
- **Components**: PascalCase (`UserProfile.tsx`, `LoginForm.tsx`)
|
||||
- **Routes**: lowercase with hyphens (`user-profile.tsx`, `auth/login.tsx`)
|
||||
- **Utilities**: camelCase or kebab-case (`formatDate.ts`, `use-auth.ts`)
|
||||
- **Server functions**: camelCase (`auth.ts`, `users.ts`)
|
||||
- **Schema files**: plural lowercase (`users.ts`, `organizations.ts`)
|
||||
|
||||
#### Component Structure Order
|
||||
|
||||
```typescript
|
||||
import { useState } from "react"; // 1. External imports
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Button } from "~/lib/components/ui/button"; // 2. Internal imports
|
||||
import { getUserById } from "~/lib/server/functions/users";
|
||||
|
||||
interface UserProfileProps { // 3. Types/Interfaces
|
||||
userId: string;
|
||||
}
|
||||
|
||||
export default function UserProfile({ userId }: UserProfileProps) { // 4. Component
|
||||
const [editing, setEditing] = useState(false); // 5. State hooks
|
||||
|
||||
const { data: user } = useQuery({ // 6. Queries/Mutations
|
||||
queryKey: ["user", userId],
|
||||
queryFn: () => getUserById(userId),
|
||||
staleTime: 60000,
|
||||
});
|
||||
|
||||
const handleSave = () => { // 7. Event handlers
|
||||
// ...
|
||||
};
|
||||
|
||||
return ( // 8. JSX
|
||||
<div>
|
||||
<h1>{user?.full_name}</h1>
|
||||
<Button onClick={handleSave}>Save</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Backend Structure (FastAPI + Python)
|
||||
|
||||
Based on `cvi-backend-template` - FastAPI, SQLModel, Alembic.
|
||||
|
||||
### Directory Layout
|
||||
|
||||
```
|
||||
project-root/
|
||||
├── .github/workflows/ # GitHub Actions
|
||||
├── app/
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py # FastAPI app entry point
|
||||
│ ├── routers/ # API routers (endpoints)
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── auth.py # /auth routes
|
||||
│ │ ├── users.py # /users routes
|
||||
│ │ └── health.py # /health routes
|
||||
│ ├── services/ # Business logic layer
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── user_service.py
|
||||
│ │ ├── auth_service.py
|
||||
│ │ └── billing_service.py
|
||||
│ ├── db/ # Database layer
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── models/ # SQLModel models (snake_case)
|
||||
│ │ │ ├── __init__.py
|
||||
│ │ │ ├── user.py
|
||||
│ │ │ ├── tenant.py
|
||||
│ │ │ └── organization.py
|
||||
│ │ ├── repositories/ # Data access layer
|
||||
│ │ │ ├── __init__.py
|
||||
│ │ │ ├── base.py # Base repository
|
||||
│ │ │ └── user_repository.py
|
||||
│ │ └── session.py # Database sessions
|
||||
│ ├── schemas/ # Pydantic schemas
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── user.py # UserCreate, UserResponse
|
||||
│ │ ├── auth.py # AuthRequest, AuthResponse
|
||||
│ │ └── common.py # Shared schemas
|
||||
│ ├── middleware/ # FastAPI middleware
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── auth.py # JWT verification
|
||||
│ │ └── tenant.py # Tenant context
|
||||
│ ├── core/ # Core configuration
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── config.py # Settings (Doppler)
|
||||
│ │ ├── security.py # Password hashing, JWT
|
||||
│ │ └── deps.py # FastAPI dependencies
|
||||
│ └── utils/ # Utility functions
|
||||
│ ├── __init__.py
|
||||
│ └── format.py
|
||||
├── tests/ # Pytest tests
|
||||
│ ├── __init__.py
|
||||
│ ├── unit/ # @pytest.mark.unit
|
||||
│ ├── integration/ # @pytest.mark.integration
|
||||
│ └── e2e/ # @pytest.mark.e2e
|
||||
├── alembic/ # Alembic migrations
|
||||
│ ├── versions/
|
||||
│ └── env.py
|
||||
├── .env.example # Example environment variables
|
||||
├── .env # Local environment (gitignored)
|
||||
├── pyproject.toml # Ruff, mypy, pytest config
|
||||
├── alembic.ini # Alembic configuration
|
||||
├── Taskfile.yml # Task runner commands
|
||||
├── requirements.txt # Production dependencies
|
||||
├── requirements-dev.txt # Development dependencies
|
||||
└── README.md # Project documentation
|
||||
```
|
||||
|
||||
### Key Backend Patterns
|
||||
|
||||
#### Import Organization (Automatic with Ruff)
|
||||
|
||||
```python
|
||||
"""Module docstring describing purpose."""
|
||||
|
||||
# 1. Standard library imports
|
||||
import os
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from uuid import UUID
|
||||
|
||||
# 2. Third-party imports
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlmodel import select
|
||||
|
||||
# 3. Local imports
|
||||
from app.db.models.user import User
|
||||
from app.db.repositories.user_repository import UserRepository
|
||||
from app.schemas.user import UserCreate, UserResponse
|
||||
```
|
||||
|
||||
#### File Naming Conventions
|
||||
|
||||
- **Modules**: snake_case (`user_service.py`, `auth_middleware.py`)
|
||||
- **Models**: PascalCase class, snake_case file (`User` in `user.py`)
|
||||
- **Tests**: `test_` prefix (`test_user_service.py`)
|
||||
|
||||
#### Repository Pattern (with Tenant Isolation)
|
||||
|
||||
```python
|
||||
# app/db/repositories/base.py
|
||||
from typing import Generic, TypeVar, Optional
|
||||
from uuid import UUID
|
||||
from sqlmodel import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
class BaseRepository(Generic[T]):
|
||||
"""Base repository with CRUD and tenant isolation."""
|
||||
|
||||
def __init__(self, session: AsyncSession, model: type[T]):
|
||||
self.session = session
|
||||
self.model = model
|
||||
|
||||
async def get_by_id(self, id: UUID, tenant_id: UUID) -> Optional[T]:
|
||||
"""Get by ID with automatic tenant filtering."""
|
||||
result = await self.session.execute(
|
||||
select(self.model)
|
||||
.where(self.model.id == id)
|
||||
.where(self.model.tenant_id == tenant_id)
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
```
|
||||
|
||||
#### Service Layer Pattern
|
||||
|
||||
```python
|
||||
# app/services/user_service.py
|
||||
from uuid import UUID
|
||||
from app.db.repositories.user_repository import UserRepository
|
||||
from app.schemas.user import UserCreate, UserResponse
|
||||
|
||||
class UserService:
|
||||
"""User business logic."""
|
||||
|
||||
def __init__(self, user_repo: UserRepository):
|
||||
self.user_repo = user_repo
|
||||
|
||||
async def create_user(self, data: UserCreate, tenant_id: UUID) -> UserResponse:
|
||||
"""Create new user with validation."""
|
||||
existing = await self.user_repo.get_by_email(data.email_address, tenant_id)
|
||||
if existing:
|
||||
raise ValueError("Email already registered")
|
||||
|
||||
user = await self.user_repo.create(data, tenant_id)
|
||||
return UserResponse.model_validate(user)
|
||||
```
|
||||
|
||||
## Supporting Documentation
|
||||
|
||||
All supporting files are under 500 lines per Anthropic best practices:
|
||||
|
||||
- **[examples/](examples/)** - Complete project examples
|
||||
- [frontend-directory-structure.md](examples/frontend-directory-structure.md) - Full TanStack Start structure
|
||||
- [backend-directory-structure.md](examples/backend-directory-structure.md) - Full FastAPI structure
|
||||
- [component-structure.md](examples/component-structure.md) - React component patterns
|
||||
- [repository-pattern.md](examples/repository-pattern.md) - Repository with tenant isolation
|
||||
- [service-pattern.md](examples/service-pattern.md) - Service layer examples
|
||||
- [INDEX.md](examples/INDEX.md) - Examples navigation
|
||||
|
||||
- **[reference/](reference/)** - Structure references
|
||||
- [file-naming.md](reference/file-naming.md) - Naming conventions
|
||||
- [import-organization.md](reference/import-organization.md) - Import ordering rules
|
||||
- [path-aliases.md](reference/path-aliases.md) - Path alias configuration
|
||||
- [INDEX.md](reference/INDEX.md) - Reference navigation
|
||||
|
||||
- **[templates/](templates/)** - Copy-paste ready templates
|
||||
- [tanstack-start-project/](templates/tanstack-start-project/) - Frontend scaffold
|
||||
- [fastapi-project/](templates/fastapi-project/) - Backend scaffold
|
||||
|
||||
- **[checklists/](checklists/)** - Structure checklists
|
||||
- [project-setup-checklist.md](checklists/project-setup-checklist.md) - New project setup
|
||||
- [refactoring-checklist.md](checklists/refactoring-checklist.md) - Structure refactoring
|
||||
|
||||
## When to Apply This Skill
|
||||
|
||||
Use this skill when:
|
||||
- Creating new Grey Haven projects
|
||||
- Organizing files in existing projects
|
||||
- Refactoring project layout
|
||||
- Setting up directory structure
|
||||
- Deciding where to place new files
|
||||
- Onboarding new team members to project structure
|
||||
|
||||
## Template Reference
|
||||
|
||||
These patterns are from Grey Haven's production templates:
|
||||
- **Frontend**: cvi-template (TanStack Start + React 19)
|
||||
- **Backend**: cvi-backend-template (FastAPI + Python)
|
||||
|
||||
## Critical Reminders
|
||||
|
||||
1. **Path aliases**: Always use ~/* for TypeScript imports
|
||||
2. **File naming**: Components PascalCase, modules snake_case
|
||||
3. **Import order**: External → Internal → Local
|
||||
4. **Component structure**: Imports → Types → Component → Hooks → Handlers → JSX
|
||||
5. **Tenant isolation**: Repository pattern includes tenant_id filtering
|
||||
6. **Database fields**: snake_case in schema files (NOT camelCase)
|
||||
7. **Test organization**: unit/, integration/, e2e/ directories
|
||||
8. **Configuration**: Doppler for all environment variables
|
||||
9. **Routes**: TanStack file-based routing in src/routes/
|
||||
10. **Backend layers**: Routers → Services → Repositories → Models
|
||||
Reference in New Issue
Block a user