refactor(templates): drop README from cookiecutter templates (#272)
* refactor(templates): remove README.md from cookiecutter templates * test(templates): drop README expectations, expect WARN instead * docs(skills): note README is generated post-init via repo-readme * docs(skills): repo-readme creates README from scratch post-init * fix(templates): drop readme field from pyproject after README removal --------- Co-authored-by: opencode-agent <agent@opencode.local>
This commit is contained in:
parent
70b8203704
commit
ca2a04ce47
9 changed files with 55 additions and 260 deletions
|
|
@ -112,6 +112,11 @@ Tool вернёт отчёт: `Project: <type>`, 7 групп `[OK]/[WARN]/[FAIL
|
|||
`Рекомендации:`. Покажи отчёт юзеру. Если tool вернул `⚠️ ...failed` → WARN,
|
||||
продолжай без отчёта.
|
||||
|
||||
> README у свежего проекта отсутствует (issue #269) — README генерируется
|
||||
> позже через repo-readme skill по ручному вызову. Поэтому группа README
|
||||
> даст WARN «README.md отсутствует» (не FAIL, exit code 0 в non-blocking
|
||||
> режиме). Это ожидаемое поведение, не ошибка — упомяни в отчёте.
|
||||
|
||||
### Шаг 6: Финальный репорт
|
||||
|
||||
```
|
||||
|
|
@ -119,6 +124,7 @@ Project <project_name> created at ./<project_name>/.
|
|||
GitHub: https://github.com/<owner>/<project_name>
|
||||
Branch protection: main (PR + required_status_checks + linear history)
|
||||
Project-status: <summary из шага 5>
|
||||
README: отсутствует (WARN у project-status) — появится позже через repo-readme
|
||||
Дальше: /run-pipeline для реализации фич, или /spec для генерации spec.
|
||||
```
|
||||
|
||||
|
|
@ -206,6 +212,11 @@ Template FIX (ниже), передав путь и описание пробл
|
|||
заблокирован deny).
|
||||
7. Верни: "done: project created at ./<project_name>/, git init + initial commit".
|
||||
|
||||
README.md в рендеренном проекте НЕТ (issue #269) — шаблоны его больше не
|
||||
содержат. README появится позже через repo-readme skill по ручному вызову
|
||||
(сначала юзер смотрит контент и cover.png из draw-image). project-status на
|
||||
свежем проекте даст WARN по отсутствующему README — это ожидаемо, не чинить.
|
||||
|
||||
Если найдёшь баг вне scope — загрузи skill `bug-discovery` через
|
||||
`skill("bug-discovery")` и следуй протоколу. НЕ чини баг сам.
|
||||
```
|
||||
|
|
@ -331,7 +342,8 @@ Cookiecutter templates доступны для: `backend`, `fullstack`, `cli`
|
|||
- `project-status` tool — read-only oracle, ALLOWED для оркестратора (как
|
||||
`pipeline-status` / `spec-status`).
|
||||
- init flow порядок: cookiecutter → git init/commit → gh repo create → branch
|
||||
protection → project-status. Не меняй порядок.
|
||||
protection → project-status. Не меняй порядок. README в init flow НЕ
|
||||
генерируется — только по ручному вызову repo-readme (issue #269).
|
||||
- check flow: project-status → отчёт → рекомендации → subagents (последовательно).
|
||||
- Subagent error → 1 retry, потом STOP + report.
|
||||
- Совместим с spec-pipeline: Phase 8 EXECUTE (spec/SKILL.md Template I) может
|
||||
|
|
|
|||
|
|
@ -201,9 +201,10 @@ switcher `[English]` / `[Русский]`, заголовок `## 🇷🇺 Ру
|
|||
## 6. Независимость от project-template
|
||||
|
||||
- Скилл `project-template` (init flow) создаёт проект через cookiecutter —
|
||||
шаблон уже включает `README.md` с базовой структурой.
|
||||
- `repo-readme` (через тулзу `create-readme`) **наполняет** его
|
||||
стандартизированным контентом (delimiter tags, bilingual, cover).
|
||||
шаблоны README.md НЕ содержат (issue #269): свежий проект рождается без
|
||||
README.
|
||||
- `repo-readme` (через тулзу `create-readme`) **создаёт** полный README с нуля
|
||||
(delimiter tags, bilingual, cover) по ручному вызову после init.
|
||||
- Может применяться к существующим репо без `project-template` — тулза
|
||||
перезапишет `README.md` (локально) или обновит через GitHub API (с SHA).
|
||||
|
||||
|
|
|
|||
|
|
@ -1,67 +0,0 @@
|
|||
# 🚀 {{ cookiecutter.project_name }}
|
||||
|
||||
> Language switcher: **[English](#-english)** | **[Русский](#-русский)**
|
||||
|
||||

|
||||
|
||||
<!-- tagline-en:start -->
|
||||
{{ cookiecutter.description }}
|
||||
<!-- tagline-en:end -->
|
||||
|
||||
<!-- summary-en:start -->
|
||||
{{ cookiecutter.description }}
|
||||
<!-- summary-en:end -->
|
||||
|
||||
## 🇺🇸 English
|
||||
|
||||
<!-- features-en:start -->
|
||||
- FastAPI backend with Tortoise ORM
|
||||
- Pydantic-settings configuration
|
||||
- Ruff + mypy strict + pytest
|
||||
- Pre-commit hooks
|
||||
<!-- features-en:end -->
|
||||
|
||||
### ⚡ Quick Start
|
||||
|
||||
```bash
|
||||
uv sync --extra dev
|
||||
uv run uvicorn main:app --reload
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💬 Support and contacts / Поддержка и контакты
|
||||
|
||||
👉 **[slaid098.dev/contacts](https://slaid098.dev/contacts)**
|
||||
|
||||
---
|
||||
|
||||
## 🇷🇺 Русский
|
||||
|
||||
<!-- tagline-ru:start -->
|
||||
{{ cookiecutter.description }}
|
||||
<!-- tagline-ru:end -->
|
||||
|
||||
<!-- summary-ru:start -->
|
||||
{{ cookiecutter.description }}
|
||||
<!-- summary-ru:end -->
|
||||
|
||||
<!-- features-ru:start -->
|
||||
- Бэкенд на FastAPI с Tortoise ORM
|
||||
- Конфигурация через pydantic-settings
|
||||
- Ruff + mypy strict + pytest
|
||||
- Pre-commit хуки
|
||||
<!-- features-ru:end -->
|
||||
|
||||
### ⚡ Быстрый старт
|
||||
|
||||
```bash
|
||||
uv sync --extra dev
|
||||
uv run uvicorn main:app --reload
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💬 Support and contacts / Поддержка и контакты
|
||||
|
||||
👉 **[slaid098.dev/contacts](https://slaid098.dev/contacts)**
|
||||
|
|
@ -6,7 +6,6 @@ build-backend = "hatchling.build"
|
|||
name = "{{ cookiecutter.project_name }}"
|
||||
version = "0.1.0"
|
||||
description = "{{ cookiecutter.description }}"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
requires-python = ">={{ cookiecutter.python_version }}"
|
||||
authors = [{ name = "slaid098" }]
|
||||
|
|
|
|||
|
|
@ -1,65 +0,0 @@
|
|||
# 🚀 {{ cookiecutter.project_name }}
|
||||
|
||||
> Language switcher: **[English](#-english)** | **[Русский](#-русский)**
|
||||
|
||||

|
||||
|
||||
<!-- tagline-en:start -->
|
||||
{{ cookiecutter.description }}
|
||||
<!-- tagline-en:end -->
|
||||
|
||||
<!-- summary-en:start -->
|
||||
{{ cookiecutter.description }}
|
||||
<!-- summary-en:end -->
|
||||
|
||||
## 🇺🇸 English
|
||||
|
||||
<!-- features-en:start -->
|
||||
- Typer-based CLI with rich output
|
||||
- Ruff + mypy strict + pytest
|
||||
- Pre-commit hooks
|
||||
<!-- features-en:end -->
|
||||
|
||||
### ⚡ Quick Start
|
||||
|
||||
```bash
|
||||
uv sync --extra dev
|
||||
{{ cookiecutter.project_name }} --help
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💬 Support and contacts / Поддержка и контакты
|
||||
|
||||
👉 **[slaid098.dev/contacts](https://slaid098.dev/contacts)**
|
||||
|
||||
---
|
||||
|
||||
## 🇷🇺 Русский
|
||||
|
||||
<!-- tagline-ru:start -->
|
||||
{{ cookiecutter.description }}
|
||||
<!-- tagline-ru:end -->
|
||||
|
||||
<!-- summary-ru:start -->
|
||||
{{ cookiecutter.description }}
|
||||
<!-- summary-ru:end -->
|
||||
|
||||
<!-- features-ru:start -->
|
||||
- CLI на Typer с rich-выводом
|
||||
- Ruff + mypy strict + pytest
|
||||
- Pre-commit хуки
|
||||
<!-- features-ru:end -->
|
||||
|
||||
### ⚡ Быстрый старт
|
||||
|
||||
```bash
|
||||
uv sync --extra dev
|
||||
{{ cookiecutter.project_name }} --help
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💬 Support and contacts / Поддержка и контакты
|
||||
|
||||
👉 **[slaid098.dev/contacts](https://slaid098.dev/contacts)**
|
||||
|
|
@ -6,7 +6,6 @@ build-backend = "hatchling.build"
|
|||
name = "{{ cookiecutter.project_name }}"
|
||||
version = "0.1.0"
|
||||
description = "{{ cookiecutter.description }}"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
requires-python = ">={{ cookiecutter.python_version }}"
|
||||
authors = [{ name = "slaid098" }]
|
||||
|
|
|
|||
|
|
@ -1,67 +0,0 @@
|
|||
# 🚀 {{ cookiecutter.project_name }}
|
||||
|
||||
> Language switcher: **[English](#-english)** | **[Русский](#-русский)**
|
||||
|
||||

|
||||
|
||||
<!-- tagline-en:start -->
|
||||
{{ cookiecutter.description }}
|
||||
<!-- tagline-en:end -->
|
||||
|
||||
<!-- summary-en:start -->
|
||||
{{ cookiecutter.description }}
|
||||
<!-- summary-en:end -->
|
||||
|
||||
## 🇺🇸 English
|
||||
|
||||
<!-- features-en:start -->
|
||||
- FastAPI backend (Tortoise ORM) + SvelteKit frontend (Svelte 5 runes)
|
||||
- pydantic-settings configuration; JS frontend with Biome + Knip + Vitest + Playwright e2e
|
||||
- Ruff + mypy strict + pytest (backend); SvelteKit + Vitest (frontend)
|
||||
- Pre-commit hooks (backend); Biome + Knip (frontend)
|
||||
<!-- features-en:end -->
|
||||
|
||||
### ⚡ Quick Start
|
||||
|
||||
```bash
|
||||
cd backend && uv sync --extra dev && uv run uvicorn main:app --reload
|
||||
cd frontend && npm install && npm run dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💬 Support and contacts / Поддержка и контакты
|
||||
|
||||
👉 **[slaid098.dev/contacts](https://slaid098.dev/contacts)**
|
||||
|
||||
---
|
||||
|
||||
## 🇷🇺 Русский
|
||||
|
||||
<!-- tagline-ru:start -->
|
||||
{{ cookiecutter.description }}
|
||||
<!-- tagline-ru:end -->
|
||||
|
||||
<!-- summary-ru:start -->
|
||||
{{ cookiecutter.description }}
|
||||
<!-- summary-ru:end -->
|
||||
|
||||
<!-- features-ru:start -->
|
||||
- Бэкенд FastAPI (Tortoise ORM) + фронтенд SvelteKit (Svelte 5 runes)
|
||||
- Конфигурация через pydantic-settings; JS-фронтенд с Biome + Knip + Vitest + Playwright e2e
|
||||
- Ruff + mypy strict + pytest (бэкенд); SvelteKit + Vitest (фронтенд)
|
||||
- Pre-commit хуки (бэкенд); Biome + Knip (фронтенд)
|
||||
<!-- features-ru:end -->
|
||||
|
||||
### ⚡ Быстрый старт
|
||||
|
||||
```bash
|
||||
cd backend && uv sync --extra dev && uv run uvicorn main:app --reload
|
||||
cd frontend && npm install && npm run dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💬 Support and contacts / Поддержка и контакты
|
||||
|
||||
👉 **[slaid098.dev/contacts](https://slaid098.dev/contacts)**
|
||||
|
|
@ -6,7 +6,6 @@ build-backend = "hatchling.build"
|
|||
name = "{{ cookiecutter.project_name }}"
|
||||
version = "0.1.0"
|
||||
description = "{{ cookiecutter.description }}"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
requires-python = ">={{ cookiecutter.python_version }}"
|
||||
authors = [{ name = "slaid098" }]
|
||||
|
|
|
|||
|
|
@ -104,7 +104,6 @@ def test_backend_structure_full(render):
|
|||
".python-version",
|
||||
"env.example",
|
||||
".gitignore",
|
||||
"README.md",
|
||||
"LICENSE",
|
||||
".pre-commit-config.yaml",
|
||||
".github/workflows/ci.yml",
|
||||
|
|
@ -201,7 +200,6 @@ def test_cli_structure(render):
|
|||
expected = [
|
||||
"pyproject.toml",
|
||||
".python-version",
|
||||
"README.md",
|
||||
"LICENSE",
|
||||
".github/workflows/ci.yml",
|
||||
"src/cl/__init__.py",
|
||||
|
|
@ -263,7 +261,6 @@ def test_fullstack_structure(render):
|
|||
# removed: counter.svelte.js, Header.svelte, jsconfig.json, +page.js
|
||||
# root CI runs both
|
||||
assert (render / ".github/workflows/ci.yml").exists()
|
||||
assert (render / "README.md").exists()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
@ -400,46 +397,6 @@ def test_fullstack_uses_typescript(render):
|
|||
assert "tw-animate" not in deps or "tw-animate-css" in deps
|
||||
|
||||
|
||||
# ── README delimiter tags (create-readme standard) ──────────────────────────
|
||||
|
||||
|
||||
README_REQUIRED_TAGS = [
|
||||
"<!-- tagline-en:start -->",
|
||||
"<!-- tagline-en:end -->",
|
||||
"<!-- tagline-ru:start -->",
|
||||
"<!-- tagline-ru:end -->",
|
||||
"<!-- summary-en:start -->",
|
||||
"<!-- summary-en:end -->",
|
||||
"<!-- features-en:start -->",
|
||||
"<!-- features-en:end -->",
|
||||
"<!-- summary-ru:start -->",
|
||||
"<!-- summary-ru:end -->",
|
||||
"<!-- features-ru:start -->",
|
||||
"<!-- features-ru:end -->",
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_name, extra_context",
|
||||
[
|
||||
("backend", {"project_name": "be"}),
|
||||
("cli", {"project_name": "cl"}),
|
||||
("fullstack", {"project_name": "fs"}),
|
||||
],
|
||||
)
|
||||
def test_readme_has_delimiter_tags(render):
|
||||
"""README must contain all 12 delimiter tags + standard headers."""
|
||||
content = (render / "README.md").read_text()
|
||||
for tag in README_REQUIRED_TAGS:
|
||||
assert tag in content, f"missing tag: {tag}"
|
||||
assert "# 🚀 " in content
|
||||
assert "## 🇺🇸 English" in content
|
||||
assert "## 🇷🇺 Русский" in content
|
||||
assert "[English](#-english)" in content
|
||||
assert "[Русский](#-русский)" in content
|
||||
assert "assets/cover.png" in content
|
||||
|
||||
|
||||
# ── pyproject.toml completeness ──────────────────────────────────────────────
|
||||
|
||||
|
||||
|
|
@ -462,6 +419,9 @@ def test_pyproject_has_required_sections(render):
|
|||
]
|
||||
for section in required:
|
||||
assert section in content, f"missing section: {section}"
|
||||
assert 'readme = "README.md"' not in content, (
|
||||
"README.md is absent (issue #269) — readme field must not reference it"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("template_name, extra_context", [("backend", {"project_name": "be"})])
|
||||
|
|
@ -471,6 +431,29 @@ def test_backend_pytest_asyncio_auto(render):
|
|||
assert 'asyncio_mode = "auto"' in pyproject
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_name, extra_context",
|
||||
[
|
||||
("backend", {"project_name": "be"}),
|
||||
("cli", {"project_name": "cl"}),
|
||||
("fullstack", {"project_name": "fs", "use_db": "yes"}),
|
||||
],
|
||||
)
|
||||
def test_pyproject_no_readme_field(render, template_name):
|
||||
"""pyproject must not reference README.md (issue #269 — README is absent).
|
||||
|
||||
hatchling fails the build when ``readme = "README.md"`` points to a
|
||||
missing file, so the field must be dropped entirely.
|
||||
"""
|
||||
if template_name == "fullstack":
|
||||
content = (render / "backend" / "pyproject.toml").read_text()
|
||||
else:
|
||||
content = (render / "pyproject.toml").read_text()
|
||||
assert 'readme = "README.md"' not in content, (
|
||||
"README.md is absent (issue #269) — readme field must not reference it"
|
||||
)
|
||||
|
||||
|
||||
# ── nested src/<package>/ layout (issue #241) ──────────────────────────────────
|
||||
|
||||
|
||||
|
|
@ -559,8 +542,13 @@ def _run_status_checks(repo_root: Path, fast: bool = True) -> tuple[str, list[ps
|
|||
"template_name, extra_context",
|
||||
[("backend", {"project_name": "be", "use_db": "yes", "use_auth": "yes"})],
|
||||
)
|
||||
def test_backend_project_status_quality_and_readme_pass(render):
|
||||
"""Backend template passes the quality + README + infra checks.
|
||||
def test_backend_project_status_quality_and_infra_pass(render):
|
||||
"""Backend template passes the quality + infra checks.
|
||||
|
||||
README is intentionally absent (issue #269 — generated later via
|
||||
repo-readme/create-readme on manual call), so the README group yields
|
||||
WARN, not FAIL (non-blocking). Coverage of the WARN behavior lives in
|
||||
``test_readme_absent_is_warn``.
|
||||
|
||||
Note: the structure check (``src/api/v1`` flat layout) expects the layout
|
||||
from issue #2 — out of scope for this PR. The nested ``src/<pkg>/api/v1``
|
||||
|
|
@ -573,12 +561,6 @@ def test_backend_project_status_quality_and_readme_pass(render):
|
|||
assert by_name["ruff"].status == ps.CheckStatus.OK
|
||||
assert by_name["mypy"].status == ps.CheckStatus.OK
|
||||
assert by_name["pytest"].status == ps.CheckStatus.OK
|
||||
# README delimiter tags + standard headers must pass
|
||||
assert by_name["12 delimiter tags"].status == ps.CheckStatus.OK
|
||||
assert by_name["# 🚀 "].status == ps.CheckStatus.OK
|
||||
assert by_name["## 🇺🇸 English"].status == ps.CheckStatus.OK
|
||||
assert by_name["## 🇷🇺 Русский"].status == ps.CheckStatus.OK
|
||||
assert by_name["[English](#-english)"].status == ps.CheckStatus.OK
|
||||
# infra: ci.yml + LICENSE + pre-commit
|
||||
assert by_name[".github/workflows/ci.yml"].status == ps.CheckStatus.OK
|
||||
assert by_name["LICENSE"].status == ps.CheckStatus.OK
|
||||
|
|
@ -605,11 +587,13 @@ def test_pyproject_quality_checks_pass(render):
|
|||
"template_name, extra_context",
|
||||
[("backend", {"project_name": "be"}), ("cli", {"project_name": "cl"})],
|
||||
)
|
||||
def test_readme_check_passes(render):
|
||||
"""README delimiter-tag check passes in the rendered project."""
|
||||
def test_readme_absent_is_warn(render):
|
||||
"""README.md is intentionally absent (issue #269) — project-status yields
|
||||
WARN, not FAIL (non-blocking, exit 0)."""
|
||||
_, checks = _run_status_checks(render)
|
||||
by_name = {c.name: c for c in checks}
|
||||
assert by_name["12 delimiter tags"].status == ps.CheckStatus.OK
|
||||
assert by_name["README.md"].status == ps.CheckStatus.WARN
|
||||
assert "12 delimiter tags" not in by_name
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue