Awesome-claude init-repo
install
source · Clone the upstream repo
git clone https://github.com/Hedgehogues/awesome-claude
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Hedgehogues/awesome-claude "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/init-repo" ~/.claude/skills/hedgehogues-awesome-claude-init-repo && rm -rf "$T"
manifest:
skills/init-repo/SKILL.mdsource content
Задача
Принципы архитектуры: DESIGN.md
Инициализируй DDD-монорепу. $ARGUMENTS
— имя проекта (kebab-case). Если не задано — спроси.$0
— имя первого bounded context (snake_case). Если не задано — используй$1
.core
Контекст (предвычислено)
!
pwd
!ls -la 2>/dev/null
Подстановки
| Placeholder | Источник | Пример |
|---|---|---|
| kebab-case | |
| snake_case | |
| PascalCase | |
| или | |
| singular | |
| PascalCase singular | |
| plural | |
1. Root
Makefile — delegation-only
Targets:
check, lint, test, test-all, up, down, migrate, revision.
Pattern: $(MAKE) -C packages/<pkg> <target>.
docker-compose.yml
— PostgreSQL 16, volumepostgres
, port 5432pgdata
— buildback
, port 8000, depends_on postgrespackages/back
— buildfront
, port 3000packages/front
.env.example
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/<project_name> OPENAI_API_KEY=test-key ENVIRONMENT=local CORS_ORIGINS=["http://localhost:3000"]
CLAUDE.md — описание проекта, tech stack, команды. Адаптируй под имя.
2. Backend (packages/back/
)
packages/back/Структура
packages/back/ ├── Makefile ├── pyproject.toml ├── alembic.ini ├── migrations/env.py + versions/ ├── src/ │ ├── __init__.py │ ├── main.py # FastAPI app + lifespan + structlog + CORS │ ├── config.py # Settings(BaseSettings) + engine + async_session │ ├── dependencies.py # Composition root — единственный файл с infra-импортами │ ├── models.py # Model registry: import Base + models → combined_metadata │ ├── router.py # APIRouter(prefix="/api"), include_router из каждого BC │ └── <bc_name>/ │ ├── __init__.py │ ├── domain/ │ │ ├── __init__.py │ │ ├── <aggregate>/ │ │ │ ├── __init__.py │ │ │ ├── entity.py │ │ │ └── repository.py │ │ └── exceptions.py │ ├── application/ │ │ ├── __init__.py │ │ ├── create_<aggregate>.py │ │ ├── get_<aggregate>.py │ │ └── list_<aggregates>.py │ ├── infrastructure/ │ │ ├── __init__.py │ │ └── persistence/ │ │ ├── __init__.py │ │ ├── base.py │ │ ├── models.py │ │ └── <aggregate>_repo.py │ └── presentation/ │ ├── __init__.py │ └── api/ │ ├── __init__.py │ ├── routes.py │ └── schemas.py └── tests/ ├── conftest.py # .env.test load + shared fixtures + mock repos ├── .env.test ├── unit/<bc_name>/ │ ├── __init__.py │ ├── test_entity.py │ └── test_use_cases.py └── architecture/ ├── __init__.py ├── conftest.py └── test_layer_boundaries.py
pyproject.toml
[project] name = "<project-name>-back" version = "0.1.0" requires-python = ">=3.12" dependencies = [ "fastapi>=0.115.0", "uvicorn[standard]>=0.34.0", "sqlalchemy[asyncio]>=2.0.36", "asyncpg>=0.30.0", "alembic>=1.14.0", "httpx>=0.28.0", "pydantic-settings>=2.7.0", "structlog>=24.4.0", ] [dependency-groups] dev = [ "pytest>=8.3.0", "pytest-asyncio>=0.24.0", "pytest-cov>=6.0.0", "ruff>=0.8.0", "mypy>=1.13.0", "black>=24.0.0", ] [tool.ruff] target-version = "py312" line-length = 120 [tool.ruff.lint] select = ["E", "F", "I", "N", "W", "UP", "B", "A", "SIM"] [tool.ruff.lint.isort] known-first-party = ["src"] [tool.mypy] python_version = "3.12" strict = true plugins = ["pydantic.mypy"] [[tool.mypy.overrides]] module = "tests.*" disallow_untyped_defs = false [tool.black] line-length = 120 target-version = ["py312"] [tool.pytest.ini_options] testpaths = ["tests"] asyncio_mode = "auto" markers = [ "unit: Unit tests", "architecture: DDD architecture validation tests", "state: State matrix tests", "security: Security tests", "cases: Business scenario tests", "integration: Integration tests", ] [tool.coverage.run] source = ["src"] omit = ["src/main.py"] [tool.coverage.report] show_missing = true fail_under = 0
Makefile (back)
.PHONY: lint test test-unit architecture-check test-cov check migrate revision format RUNNER ?= uv run lint: $(RUNNER) ruff check --fix . $(RUNNER) black --check . $(RUNNER) mypy src/ tests/ format: $(RUNNER) ruff check --fix . $(RUNNER) black . test-unit: $(RUNNER) pytest tests/unit -v architecture-check: $(RUNNER) pytest tests/architecture -v test-cov: $(RUNNER) pytest tests/unit tests/architecture -v --cov --cov-report=term-missing test: test-unit architecture-check check: lint test test-cov @echo "All checks passed!" migrate: $(RUNNER) alembic upgrade head revision: $(RUNNER) alembic revision --autogenerate -m "$(msg)"
Код: DDD-слои
entity.py
@dataclass class <Aggregate>: <aggregate>_id: uuid.UUID name: str version: int created_at: datetime updated_at: datetime @classmethod def create(cls, *, name: str) -> <Aggregate>: now = datetime.now(UTC) return cls(<aggregate>_id=uuid.uuid4(), name=name, version=1, created_at=now, updated_at=now)
repository.py
class <Aggregate>Repository(ABC): @abstractmethod async def save(self, entity: <Aggregate>) -> None: ... @abstractmethod async def update(self, entity: <Aggregate>) -> None: ... @abstractmethod async def get_by_id(self, <aggregate>_id: uuid.UUID) -> <Aggregate> | None: ... @abstractmethod async def get_all(self) -> list[<Aggregate>]: ...
create_<aggregate>.py
class Create<Aggregate>UseCase: def __init__(self, <aggregate>_repo: <Aggregate>Repository) -> None: self._<aggregate>_repo = <aggregate>_repo async def execute(self, *, name: str) -> <Aggregate>: entity = <Aggregate>.create(name=name) await self._<aggregate>_repo.save(entity) return entity
persistence/models.py
class <Aggregate>Model(Base): __tablename__ = "<aggregates>" <aggregate>_id: Mapped[uuid.UUID] = mapped_column(primary_key=True) name: Mapped[str] = mapped_column(nullable=False) version: Mapped[int] = mapped_column(default=1) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True)) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
<aggregate>_repo.py
class SqlAlchemy<Aggregate>Repository(<Aggregate>Repository): def __init__(self, session: AsyncSession) -> None: self._session = session async def save(self, entity: <Aggregate>) -> None: model = <Aggregate>Model( <aggregate>_id=entity.<aggregate>_id, name=entity.name, version=entity.version, created_at=entity.created_at, updated_at=entity.updated_at, ) self._session.add(model) await self._session.flush() async def get_by_id(self, <aggregate>_id: uuid.UUID) -> <Aggregate> | None: result = await self._session.execute( select(<Aggregate>Model).where(<Aggregate>Model.<aggregate>_id == <aggregate>_id) ) row = result.scalar_one_or_none() if row is None: return None return <Aggregate>( <aggregate>_id=row.<aggregate>_id, name=row.name, version=row.version, created_at=row.created_at, updated_at=row.updated_at, )
routes.py
router = APIRouter(prefix="/<aggregates>", tags=["<Aggregates>"]) @router.post("") async def create_<aggregate>(request: Create<Aggregate>Request) -> <Aggregate>Response: async with async_session() as db, db.begin(): repo = SqlAlchemy<Aggregate>Repository(db) uc = Create<Aggregate>UseCase(<aggregate>_repo=repo) entity = await uc.execute(name=request.name) return _to_response(entity)
Тесты (back)
tests/architecture/ — DDD structural validation
conftest.py: AST-helpers — parse_imports(), parse_classes(), discover_aggregates() (glob */entity.py), discover_domain_files(), discover_layer_files(). Dataclasses: ImportInfo, AggregateInfo.
test_layer_boundaries.py — parametrize по файлам каждого слоя:
- Domain NOT imports application, infrastructure, presentation
- Application NOT imports presentation
- Presentation NOT imports infrastructure (use
)src.dependencies - Domain/Application NOT imports sqlalchemy, fastapi, httpx, openai
tests/unit/<bc>/test_entity.py
pytestmark = pytest.mark.unit def test_create_<aggregate>(): """<Aggregate>.create produces entity with correct initial state.""" entity = <Aggregate>.create(name="test") assert isinstance(entity.<aggregate>_id, uuid.UUID) assert entity.name == "test" assert entity.version == 1
tests/unit/<bc>/test_use_cases.py
pytestmark = [pytest.mark.unit, pytest.mark.asyncio] async def test_create_<aggregate>_flow(): """Create<Aggregate> persists new entity via repository.""" repo = AsyncMock() repo.save.return_value = None uc = Create<Aggregate>UseCase(<aggregate>_repo=repo) result = await uc.execute(name="test") assert result.name == "test" repo.save.assert_awaited_once()
3. Frontend (packages/front/
)
packages/front/Структура
packages/front/ ├── Makefile # install, lint (eslint + tsc), test, build, check, dev ├── package.json # React 19, Router v7, Vite, Vitest, Testing Library, TS strict ├── vite.config.ts # react plugin + vitest (jsdom, globals, setup) ├── tsconfig.json # references → app + node ├── tsconfig.app.json # ES2022, strict, react-jsx, vitest/globals ├── tsconfig.node.json ├── eslint.config.js # flat config: js + tseslint + react-hooks + react-refresh ├── index.html # <div id="root">, Google Fonts Inter ├── Dockerfile # multi-stage: npm build → nginx ├── nginx.conf # SPA fallback, /api proxy └── src/ ├── main.tsx # StrictMode + createRoot ├── App.tsx # BrowserRouter + Routes + AppLayout ├── index.css ├── api/client.ts # BASE_URL from VITE_API_URL, ApiError, fetchJson/postJson/patchJson/putJson/deleteJson ├── types/api.ts # API response types ├── components/ │ ├── ui/ │ │ ├── Button/Button.tsx + Button.module.css + Button.test.tsx │ │ └── index.ts # barrel export │ ├── domain/ # empty │ └── layout/AppLayout.tsx + AppLayout.test.tsx ├── pages/HomePage.tsx + HomePage.test.tsx ├── hooks/ # empty ├── styles/base.css + layout.css └── test/ ├── setup.ts # import '@testing-library/jest-dom/vitest' ├── renderWithRouter.tsx └── fixtures.ts
vite.config.ts
/// <reference types="vitest/config" /> import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], test: { globals: true, environment: 'jsdom', setupFiles: './src/test/setup.ts', css: true, exclude: ['node_modules', 'e2e'], }, })
Makefile (front)
.PHONY: install lint test build check dev install: npm install lint: npm run lint npx tsc -b --noEmit test: npm run test build: npm run build check: lint test build @echo "All checks passed!" dev: npm run dev
4. После генерации
Создай root → back (config → src по слоям → tests) → front (config → src → tests). Выведи:
Монорепа <project-name> создана. cd <project-name> git init && git add -A && git commit -m "init: scaffold <project-name> monorepo" cd packages/back && uv sync && make check cd packages/front && npm install && make check make up
Правила
- Все файлы рабочие —
проходит с первого разаmake check - Только каркас (CRUD entity, empty pages), без бизнес-логики
- Только базовый стек: FastAPI + SQLAlchemy + React. Без OpenAI, Celery, Redis
- Подставляй имена из аргументов во все файлы (imports, classes, tables)
в каждой Python-директории__init__.py- Architecture tests, unit tests, frontend tests — все проходят