feat(readme): new bilingual standard v2 with features table (#116)

* feat(readme): new bilingual standard v2 with features table

* docs(handoff): rename handoff and ADR to PR number convention

* docs(review): fix ADR-051 section name and add PR#116 refs

---------

Co-authored-by: opencode-agent <agent@opencode.local>
This commit is contained in:
Sergey 2026-07-29 03:22:28 +03:00 committed by GitHub
parent fc03889c6f
commit 464fa6ec83
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 340 additions and 175 deletions

View file

@ -6,14 +6,14 @@ description: Use when creating or updating README.md for any slaid098 repository
# Repo README
Стандартизация `README.md` во всех репозиториях slaid098 через тулзу
`create-readme`. Тулза гарантирует структуру (разделители, Support block,
Quick Start, language switcher). Скилл даёт контекст: когда вызывать тулзу и
какие параметры передавать.
`create-readme`. Тулза гарантирует структуру (разделители, Features table,
Support block, Quick Start, language switcher). Скилл даёт контекст: когда
вызывать тулзу и какие параметры передавать.
## 1. Когда использовать тулзу
- **Новый репо**`create-readme` (mode: `create`) — генерирует
стандартизированный README с нуля.
стандартизированный двуязычный README с нуля.
- **Проверка существующего README**`create-readme` (mode: `validate`) —
проверяет, что структура соответствует стандарту витрины.
- **После ручных правок README** → всегда `validate`. Любая правка руками
@ -23,20 +23,7 @@ Quick Start, language switcher). Скилл даёт контекст: когд
Не генерируй README вручную через Write — структура критична для парсинга
витриной. Только через тулзу `create-readme`.
## 2. Tier system
Тулза принимает параметр `tier`:
- **`flagship`** — флагманские проекты (Anti-Detect MCP, Mobile Whisper и
т.п.). Полный набор параметров: `demo_gif` (демо-гифка), `custom_sections`,
`telegram`. Используется для заметных проектов на витрине.
- **`utility`** (default) — бытовые утилиты. Минимальный набор: `problem` /
`solution` (EN+RU), `quick_start`. `demo_gif` игнорируется.
Выбор tier влияет только на блок `demo_gif` после EN-разделителей. Остальная
структура одинакова для обоих tier'ов.
## 3. GitHub metadata (отдельный шаг, НЕ в тулзе)
## 2. GitHub metadata (отдельный шаг, НЕ в тулзе)
`create-readme` отвечает только за файл `README.md`. Метаданные репозитория
настраиваются отдельно через `gh repo edit` (это bash, не тулза):
@ -47,7 +34,7 @@ Quick Start, language switcher). Скилл даёт контекст: когд
Метаданные не дублируют README — они для карточки репо на GitHub и поиска.
## 4. Workflow
## 3. Workflow
1. `create-readme` (mode: `create`) → генерирует README с гарантированной
структурой.
@ -62,20 +49,25 @@ Quick Start, language switcher). Скилл даёт контекст: когд
(`owner/name`) — тулза сделает PUT через `gh api
repos/{owner}/{repo}/contents/README.md` с base64-контентом и SHA.
## 5. Зачем разделители (контекст для агента)
## 4. Зачем разделители (контекст для агента)
Витрина **slaid098.dev** скачивает raw `README.md` из каждого репо и
извлекает фрагмент между разделителями:
извлекает фрагменты между разделителями:
- `<!-- summary-en:start -->` ... `<!-- summary-en:end -->` — английский
блок для карточки.
- `<!-- summary-ru:start -->` ... `<!-- summary-ru:end -->` — русский блок.
блок Why/What для карточки.
- `<!-- features-en:start -->` ... `<!-- features-en:end -->` — английская
таблица фич.
- `<!-- summary-ru:start -->` ... `<!-- summary-ru:end -->` — русский блок
Зачем/Что.
- `<!-- features-ru:start -->` ... `<!-- features-ru:end -->` — русская
таблица фич.
Поэтому структура README **должна быть гарантирована тулзой**, а не агентом.
Агент может менять текст *внутри* разделителей, но не должен удалять/двигать
сами разделители. `validate` ловит такие нарушения.
## 6. Шаблон README (reference)
## 5. Шаблон README (reference)
Тулза `create-readme` генерирует ровно эту структуру (параметры подставляются):
@ -90,14 +82,22 @@ repos/{owner}/{repo}/contents/README.md` с base64-контентом и SHA.
## 🇬🇧 English
<!-- summary-en:start -->
### 🔴 Problem
{problem_en}
### ❓ Why
{why_en}
### 🟢 Solution
{solution_en}
{custom_sections_en — доп. секции, если переданы}
### ✅ What
{what_en}
<!-- summary-en:end -->
{demo_gif строка, только если tier=flagship}
<!-- features-en:start -->
### Features
| Feature | Description |
|---------|-------------|
| {emoji} {name} | {description} |
...
<!-- features-en:end -->
{custom_sections_en — доп. секции, если переданы}
### ⚡ Quick Start
\`\`\`bash
@ -110,14 +110,23 @@ git clone https://github.com/slaid098/{repo_name}.git
## 🇷🇺 Русская версия
<!-- summary-ru:start -->
### 🔴 Проблема
{problem_ru}
### ❓ Зачем
{why_ru}
### 🟢 Решение
{solution_ru}
{custom_sections_ru — доп. секции, если переданы}
### ✅ Что
{what_ru}
<!-- summary-ru:end -->
<!-- features-ru:start -->
### Фичи
| Фича | Описание |
|------|----------|
| {emoji} {name} | {description} |
...
<!-- features-ru:end -->
{custom_sections_ru — доп. секции, если переданы}
### ⚡ Быстрый старт
\`\`\`bash
git clone https://github.com/slaid098/{repo_name}.git
@ -126,18 +135,19 @@ git clone https://github.com/slaid098/{repo_name}.git
---
## 💬 Support & Contact / Поддержка и связь
## 💬 Support and contacts / Поддержка и контакты
Have questions, need custom features, or want to support this project?
👉 **[Visit Support & Contact Page](https://slaid098.dev/support)**
Have questions or want to support?
👉 **[slaid098.dev/support](https://slaid098.dev/support)**
{telegram строка, если передан}
```
`validate` проверяет: наличие обоих EN/RU разделителей, непустой контент
между ними, ссылку `slaid098.dev/support`, секции Quick Start (EN) и Быстрый
старт (RU), language switcher `[English]` / `[Русский]`.
`validate` проверяет: наличие всех 4 пар EN/RU разделителей (summary +
features), непустой контент между ними, ссылку `slaid098.dev/support`, секции
Quick Start (EN) и Быстрый старт (RU), language switcher `[English]` /
`[Русский]`.
## 7. Независимость от repo-init
## 6. Независимость от repo-init
- Скилл `repo-init` создаёт **пустой** `README.md` как часть инициализации
репо.
@ -146,17 +156,16 @@ Have questions, need custom features, or want to support this project?
- Может применяться к существующим репо без `repo-init` — тулза перезапишет
`README.md` (локально) или обновит через GitHub API (с SHA).
## 8. Параметры тулзы (кратко)
## 7. Параметры тулзы (кратко)
`create-readme`:
- `mode``"create"` | `"validate"` (обязательный).
- `repo_name`, `tagline`, `problem_en`, `solution_en`, `problem_ru`,
`solution_ru`, `quick_start` — обязательны для `create`.
- `tier``"flagship"` | `"utility"` (default `utility`).
- `telegram` — username без `@` (optional).
- `demo_gif` — путь/URL (только для `flagship`).
- `repo_name`, `tagline`, `why_en`, `what_en`, `why_ru`, `what_ru`,
`quick_start`, `features_en`, `features_ru` — обязательны для `create`.
- `features_en` / `features_ru` — массив `{ emoji, name, description }[]`.
- `custom_sections_en` / `custom_sections_ru` — массивы
`{ title, content }` (optional).
- `telegram` — username без `@` (optional).
- `repo``owner/name` для удалённой операции (optional).
- `file_path` — локальный путь (default `README.md`).

View file

@ -2,19 +2,21 @@ import { spawnSync } from "child_process"
import { readFileSync, writeFileSync } from "fs"
import { tool } from "@opencode-ai/plugin"
type Feature = { emoji: string; name: string; description: string }
type CustomSection = { title: string; content: string }
type CreateArgs = {
repo_name: string
tagline: string
problem_en: string
solution_en: string
problem_ru: string
solution_ru: string
why_en: string
what_en: string
why_ru: string
what_ru: string
quick_start: string
tier?: "flagship" | "utility"
features_en: Feature[]
features_ru: Feature[]
telegram?: string
demo_gif?: string
custom_sections_en?: CustomSection[]
custom_sections_ru?: CustomSection[]
}
@ -26,6 +28,16 @@ function extractBetween(text: string, start: string, end: string): string | null
return text.substring(s + start.length, e)
}
function renderFeaturesTable(features: Feature[], isRu: boolean): string {
const header = isRu
? "### Фичи\n\n| Фича | Описание |\n|------|----------|\n"
: "### Features\n\n| Feature | Description |\n|---------|-------------|\n"
const rows = features
.map((f) => `| ${f.emoji} ${f.name} | ${f.description} |`)
.join("\n")
return header + rows
}
function generateReadme(args: CreateArgs): string {
const customEn = (args.custom_sections_en || [])
.map((s) => `\n\n### ${s.title}\n${s.content}`)
@ -33,10 +45,8 @@ function generateReadme(args: CreateArgs): string {
const customRu = (args.custom_sections_ru || [])
.map((s) => `\n\n### ${s.title}\n${s.content}`)
.join("")
const demoLine =
args.tier === "flagship" && args.demo_gif ? `\n![Demo](${args.demo_gif})\n` : ""
const telegramLine = args.telegram
? `💬 **Direct Telegram:** [@${args.telegram}](https://t.me/${args.telegram})`
? `\n💬 **Direct Telegram:** [@${args.telegram}](https://t.me/${args.telegram})`
: ""
return `# 🚀 ${args.repo_name}
@ -49,13 +59,17 @@ function generateReadme(args: CreateArgs): string {
## 🇬🇧 English
<!-- summary-en:start -->
### 🔴 Problem
${args.problem_en}
### Why
${args.why_en}
### 🟢 Solution
${args.solution_en}${customEn}
### What
${args.what_en}
<!-- summary-en:end -->
${demoLine}
<!-- features-en:start -->
${renderFeaturesTable(args.features_en, false)}
<!-- features-en:end -->${customEn}
### Quick Start
\`\`\`bash
git clone https://github.com/slaid098/${args.repo_name}.git
@ -67,13 +81,17 @@ ${args.quick_start}
## 🇷🇺 Русская версия
<!-- summary-ru:start -->
### 🔴 Проблема
${args.problem_ru}
### Зачем
${args.why_ru}
### 🟢 Решение
${args.solution_ru}${customRu}
### Что
${args.what_ru}
<!-- summary-ru:end -->
<!-- features-ru:start -->
${renderFeaturesTable(args.features_ru, true)}
<!-- features-ru:end -->${customRu}
### Быстрый старт
\`\`\`bash
git clone https://github.com/slaid098/${args.repo_name}.git
@ -82,43 +100,45 @@ ${args.quick_start}
---
## 💬 Support & Contact / Поддержка и связь
## 💬 Support and contacts / Поддержка и контакты
Have questions, need custom features, or want to support this project?
👉 **[Visit Support & Contact Page](https://slaid098.dev/support)**
${telegramLine}
Have questions or want to support?
👉 **[slaid098.dev/support](https://slaid098.dev/support)**${telegramLine}
`
}
function validateReadme(content: string): { ok: boolean; issues: string[] } {
const issues: string[] = []
if (!content.includes("<!-- summary-en:start -->"))
issues.push("Missing <!-- summary-en:start --> delimiter")
if (!content.includes("<!-- summary-en:end -->"))
issues.push("Missing <!-- summary-en:end --> delimiter")
if (!content.includes("<!-- summary-ru:start -->"))
issues.push("Missing <!-- summary-ru:start --> delimiter")
if (!content.includes("<!-- summary-ru:end -->"))
issues.push("Missing <!-- summary-ru:end --> delimiter")
const delimiters = [
"summary-en:start",
"summary-en:end",
"features-en:start",
"features-en:end",
"summary-ru:start",
"summary-ru:end",
"features-ru:start",
"features-ru:end",
]
for (const d of delimiters) {
if (!content.includes(`<!-- ${d} -->`))
issues.push(`Missing <!-- ${d} --> delimiter`)
}
const enContent = extractBetween(
content,
"<!-- summary-en:start -->",
"<!-- summary-en:end -->",
)
if (enContent !== null && !enContent.trim())
issues.push("EN summary content between delimiters is empty")
const ruContent = extractBetween(
content,
"<!-- summary-ru:start -->",
"<!-- summary-ru:end -->",
)
if (ruContent !== null && !ruContent.trim())
issues.push("RU summary content between delimiters is empty")
const pairs = [
{ start: "<!-- summary-en:start -->", end: "<!-- summary-en:end -->", label: "EN summary" },
{ start: "<!-- features-en:start -->", end: "<!-- features-en:end -->", label: "EN features" },
{ start: "<!-- summary-ru:start -->", end: "<!-- summary-ru:end -->", label: "RU summary" },
{ start: "<!-- features-ru:start -->", end: "<!-- features-ru:end -->", label: "RU features" },
]
for (const p of pairs) {
const between = extractBetween(content, p.start, p.end)
if (between !== null && !between.trim())
issues.push(`${p.label} content between delimiters is empty`)
}
if (!content.includes("slaid098.dev/support"))
issues.push("Missing Support & Contact link (slaid098.dev/support)")
issues.push("Missing Support link (slaid098.dev/support)")
if (!content.includes("Quick Start"))
issues.push("Missing 'Quick Start' section (English)")
if (!content.includes("Быстрый старт"))
@ -133,7 +153,7 @@ function validateReadme(content: string): { ok: boolean; issues: string[] } {
export default tool({
description:
"Create or validate README.md for slaid098 repositories. 'create' mode generates a standardized bilingual README with delimiter tags (<!-- summary-en:start/end -->, <!-- summary-ru:start/end -->) parsed by the slaid098.dev showcase. 'validate' mode checks an existing README against the standard. Supports local file (fs) and remote (gh api repos/{owner}/{repo}/contents/README.md) operation.",
"Create or validate README.md for slaid098 repositories. 'create' mode generates a standardized bilingual README with delimiter tags (<!-- summary-en:start/end -->, <!-- features-en:start/end -->, <!-- summary-ru:start/end -->, <!-- features-ru:start/end -->) parsed by the slaid098.dev showcase. 'validate' mode checks an existing README against the standard. Supports local file (fs) and remote (gh api repos/{owner}/{repo}/contents/README.md) operation.",
args: {
mode: tool.schema
.enum(["create", "validate"])
@ -146,38 +166,50 @@ export default tool({
.string()
.optional()
.describe("Short English tagline (1 sentence). Required for create mode."),
problem_en: tool.schema
why_en: tool.schema
.string()
.optional()
.describe("Problem statement in English (1-2 sentences). Required for create mode."),
solution_en: tool.schema
.describe("Why this exists — in English (1-2 sentences). Required for create mode."),
what_en: tool.schema
.string()
.optional()
.describe("Solution in English (1-2 sentences). Required for create mode."),
problem_ru: tool.schema
.describe("What it does — in English (1-2 sentences). Required for create mode."),
why_ru: tool.schema
.string()
.optional()
.describe("Problem statement in Russian (1-2 sentences). Required for create mode."),
solution_ru: tool.schema
.describe("Зачем этот проект — на русском (1-2 предложения). Required for create mode."),
what_ru: tool.schema
.string()
.optional()
.describe("Solution in Russian (1-2 sentences). Required for create mode."),
.describe("Что делает — на русском (1-2 предложения). Required for create mode."),
quick_start: tool.schema
.string()
.optional()
.describe("Install/setup command (e.g. 'pip install -r requirements.txt'). Required for create mode."),
tier: tool.schema
.enum(["flagship", "utility"])
features_en: tool.schema
.array(
tool.schema.object({
emoji: tool.schema.string(),
name: tool.schema.string(),
description: tool.schema.string(),
}),
)
.optional()
.describe("Repository tier: 'flagship' (enables demo_gif) or 'utility' (minimal). Default: 'utility'."),
.describe("Array of features for EN table. Each: { emoji, name, description }. Required for create mode."),
features_ru: tool.schema
.array(
tool.schema.object({
emoji: tool.schema.string(),
name: tool.schema.string(),
description: tool.schema.string(),
}),
)
.optional()
.describe("Array of features for RU table. Each: { emoji, name, description }. Required for create mode."),
telegram: tool.schema
.string()
.optional()
.describe("Telegram username without @. Optional; adds Direct Telegram line to Support section."),
demo_gif: tool.schema
.string()
.optional()
.describe("Path or URL to demo GIF. Only emitted when tier='flagship'."),
custom_sections_en: tool.schema
.array(
tool.schema.object({
@ -186,7 +218,7 @@ export default tool({
}),
)
.optional()
.describe("Additional sections rendered inside EN delimiters (after Solution)."),
.describe("Additional sections rendered after EN features block (outside delimiters)."),
custom_sections_ru: tool.schema
.array(
tool.schema.object({
@ -195,7 +227,7 @@ export default tool({
}),
)
.optional()
.describe("Additional sections rendered inside RU delimiters (after Решение)."),
.describe("Additional sections rendered after RU features block (outside delimiters)."),
repo: tool.schema
.string()
.optional()
@ -208,33 +240,36 @@ export default tool({
async execute(args, context) {
try {
const file_path = args.file_path ?? "README.md"
const tier = args.tier ?? "utility"
if (args.mode === "create") {
const required: Record<string, string | undefined> = {
repo_name: args.repo_name,
tagline: args.tagline,
problem_en: args.problem_en,
solution_en: args.solution_en,
problem_ru: args.problem_ru,
solution_ru: args.solution_ru,
why_en: args.why_en,
what_en: args.what_en,
why_ru: args.why_ru,
what_ru: args.what_ru,
quick_start: args.quick_start,
}
for (const [k, v] of Object.entries(required)) {
if (!v) return `${k} is required for create mode`
}
if (!args.features_en || args.features_en.length === 0)
return `❌ features_en is required for create mode`
if (!args.features_ru || args.features_ru.length === 0)
return `❌ features_ru is required for create mode`
const content = generateReadme({
repo_name: args.repo_name!,
tagline: args.tagline!,
problem_en: args.problem_en!,
solution_en: args.solution_en!,
problem_ru: args.problem_ru!,
solution_ru: args.solution_ru!,
why_en: args.why_en!,
what_en: args.what_en!,
why_ru: args.why_ru!,
what_ru: args.what_ru!,
quick_start: args.quick_start!,
tier,
features_en: args.features_en!,
features_ru: args.features_ru!,
telegram: args.telegram,
demo_gif: args.demo_gif,
custom_sections_en: args.custom_sections_en,
custom_sections_ru: args.custom_sections_ru,
})

114
README.md
View file

@ -1,11 +1,39 @@
# opencode-config
# 🚀 opencode-config
> Portable AI coding assistant config with memory & subagent pipeline
Personal opencode setup — Docker-based AI coding assistant with pipeline automation.
[English](#-english) | [Русский](#-русская-версия)
## Русский
---
### Быстрый старт
## 🇬🇧 English
<!-- summary-en:start -->
### ❓ Why
Moving your AI coding assistant config to a new server meant setting everything up from scratch every time. Plus no versioning — break a skill and you can't rollback to what worked.
### ✅ What
A ready-to-run Docker config. Clone, fill `.env`, start — it works. Versioning via Git, memory syncs through a private repository.
<!-- summary-en:end -->
<!-- features-en:start -->
### Features
| Feature | Description |
|---------|-------------|
| 🐳 Portable | Docker config — runs on any server with one command |
| 📦 Versioning | Git under the hood — rollback to any config version |
| 🌐 Global skills | All skills mounted via volume — work across all projects |
| 🧠 Portable memory | Markdown + git, syncs through private repo — assistant remembers everything |
| 🔍 Dual search | Keyword (ripgrep) always works, semantic — optional via OpenAI API |
| 🔧 Pipeline | ISSUE → IMPLEMENT → DOCS → CI → REVIEW → MERGE → MEMORY |
| 🤖 Subagent orchestration | Orchestrator only plans, subagents execute |
| 📋 Linear execution | One pipeline at a time — no branch conflicts |
| 🐛 Auto-issue | Bug found mid-task — agent creates GitHub issue and continues |
| 🛠️ Skills & commands | Built-in skills and slash commands, main one — `/run-pipeline` |
| 👥 Subagents | Built-in role-based agents — review, docs, memory sync |
<!-- features-en:end -->
### ⚡ Quick Start
```bash
git clone https://github.com/slaid098/opencode-config.git
cd opencode-config
@ -13,33 +41,37 @@ cp .env.example .env
docker compose up -d
```
Доступ: http://localhost:4096
---
### Структура
## 🇷🇺 Русская версия
| Путь | Описание |
<!-- summary-ru:start -->
### ❓ Зачем
Перенос конфига на новый сервер требовал настройки с нуля каждый раз. Плюс отсутствие версионирования — накосячил со скиллом и не можешь посмотреть, как было раньше.
### ✅ Что
Готовый конфиг в Docker. Клонировал, заполнил `.env`, запустил — работает. Версионирование через Git, память синкается через приватный репозиторий.
<!-- summary-ru:end -->
<!-- features-ru:start -->
### Фичи
| Фича | Описание |
|------|----------|
| `AGENTS.md` | Глобальные правила оркестратора |
| `.opencode/` | Конфигурация проекта |
| `.opencode/agents/` | Определения subagent'ов |
| `.opencode/skills/` | Определения skills (14 skills) |
| `.opencode/scripts/` | Python скрипты (pipeline-status, spec-status) |
| `src/` | Python RAG CLI |
| `docs/` | Handoffs, ADRs, project map |
| `docker-compose.yml` | Docker compose конфиг |
### Конфигурация
Скопируйте `.env.example``.env`, заполните ключи.
### Память
Файловая память (markdown + git) с keyword и semantic поиском. 5 tools: `memory-save`, `memory-search`, `memory-list`, `memory-access`, `memory-doctor`. Zero-config — первый `memory-save` создаёт репозиторий и индекс автоматически. Semantic поиск опционален: работает если задан `OPENAI_BASE_URL`, иначе keyword (ripgrep) всегда работает как fallback.
## English
### Quick start
| 🐳 Переносимость | Docker-конфиг — поднимается на любом сервере одной командой |
| 📦 Версионирование | Git под капотом — откат к любой версии конфига |
| 🌐 Глобальные скиллы | Все скиллы прокидываются через volume — работают во всех проектах |
| 🧠 Переносимая память | Markdown + git, синк через приватный репозиторий — ассистент помнит всё |
| 🔍 Двойной поиск | Keyword (ripgrep) всегда работает, semantic — опционально через OpenAI API |
| 🔧 Пайплайн | ISSUE → IMPLEMENT → DOCS → CI → REVIEW → MERGE → MEMORY |
| 🤖 Subagent-оркестрация | Оркестратор только планирует, исполняют subagent'ы |
| 📋 Линейное выполнение | Один пайплайн за раз — без конфликтов веток |
| 🐛 Auto-issue | Баг найден в процессе — агент сам создаёт GitHub issue и продолжает работу |
| 🛠️ Скиллы и команды | Встроенные скиллы и slash-команды, основной — `/run-pipeline` |
| 👥 Subagent'ы | Встроенные агенты по ролям — review, docs, memory sync |
<!-- features-ru:end -->
### ⚡ Быстрый старт
```bash
git clone https://github.com/slaid098/opencode-config.git
cd opencode-config
@ -47,29 +79,9 @@ cp .env.example .env
docker compose up -d
```
Access at http://localhost:4096
---
### Structure
## 💬 Support and contacts / Поддержка и контакты
| Path | Description |
|------|-------------|
| `AGENTS.md` | Global orchestrator rules |
| `.opencode/` | Project configuration |
| `.opencode/agents/` | Subagent definitions |
| `.opencode/skills/` | Skill definitions (14 skills) |
| `.opencode/scripts/` | Python scripts (pipeline-status, spec-status) |
| `src/` | Python RAG CLI |
| `docs/` | Handoffs, ADRs, project map |
| `docker-compose.yml` | Docker compose config |
### Configuration
Copy `.env.example``.env`, fill in keys.
### Memory
File-based memory (markdown + git) with keyword and semantic search. 5 tools: `memory-save`, `memory-search`, `memory-list`, `memory-access`, `memory-doctor`. Zero-config — first `memory-save` creates the repo and index automatically. Semantic search is optional: works if `OPENAI_BASE_URL` is set, otherwise keyword (ripgrep) always works as fallback.
## License
MIT — see [LICENSE](LICENSE).
Have questions or want to support?
👉 **[slaid098.dev/support](https://slaid098.dev/support)**

View file

@ -0,0 +1,47 @@
# ADR-051: README standard v2 — Why/What + Features table
## Статус
Accepted (2026-07-29)
## Контекст
Витрина slaid098.dev парсит `README.md` из каждого репозитория: извлекает
фрагменты между разделителями. В стандарте v1 (ADR-049, PR#112) были 2 пары
разделителей (`summary-en/ru`) с Problem/Solution текстом и опциональный
`tier` (flagship/utility), влияющий только на блок `demo_gif`. Problem/Solution
оказался не универсален — не каждый проект укладывается в "проблема → решение"
(например, конфиг-репозитории, утилиты). Tier system усложнял поддержку: два
пути генерации, флаг demo_gif, разные описания параметров. Витрине нужна
таблица фич для каждой карточки — в v1 её не было.
## Решение
Перейти на стандарт v2:
- **Why/What** вместо Problem/Solution — универсальная структура: зачем
существует проект и что делает. Применима к любому репо.
- **Features table** — обязательная часть README. Каждая фича: `{ emoji, name,
description }`. Рендерится в markdown-таблицу между новыми разделителями
`<!-- features-en:start/end -->` и `<!-- features-ru:start/end -->`.
- **4 пары разделителей** (summary-en, features-en, summary-ru, features-ru) —
витрина парсит и summary, и features независимо.
- **Tier system убран** — один формат для всех репо, без условной логики.
- **`custom_sections_en/ru` вне delimiter tags** — после `features-en:end`,
до Quick Start. Раньше были внутри summary delimiter.
- **`demo_gif` убран** — не использовался витриной.
- `validate` проверяет наличие и непустоту всех 4 пар разделителей.
## Альтернативы
- **Сохранить Problem/Solution** — отвергнуто: не универсально. Не каждый
проект укладывается в "проблема → решение" (конфиг-репозитории, утилиты).
Why/What применимо к любому репо.
- **Сохранить tier system (flagship/utility)** — отвергнуто: усложнял поддержку
(два пути генерации, флаг demo_gif, разные описания параметров). Один формат
для всех репо проще в поддержке.
- **Оставить 2 пары разделителей (summary только)** — отвергнуто: витрина не
может парсить features независимо от summary. 4 пары позволяют извлекать и
summary, и таблицу фич как независимые блоки per language.
- **Держать custom_sections внутри summary delimiter** — отвергнуто: перенос
вне delimiter tags (после features-en:end, до Quick Start) разделяет
пользовательский контент и парсимый витриной.

View file

@ -0,0 +1,62 @@
---
pr: 116
title: feat(readme): new bilingual standard v2 with features table
---
## Что сделано
Обновлён `create-readme` tool (`.opencode/tools/create-readme.ts`):
- Убраны параметры `tier` (flagship/utility), `demo_gif`, `problem_en/ru`,
`solution_en/ru` — из типа `CreateArgs`, schema, `execute()`.
- Добавлены параметры `why_en`, `what_en`, `why_ru`, `what_ru`,
`features_en`, `features_ru` (массив `{ emoji, name, description }[]`).
- Добавлена функция `renderFeaturesTable()` для рендеринга markdown-таблицы
фич.
- `generateReadme()` переписан под новый шаблон: Why/What вместо
Problem/Solution, Features table между `<!-- features-en/ru:start/end -->`,
`custom_sections` теперь вне delimiter tags (после features-en:end, до
Quick Start).
- `validateReadme()` обновлён: проверяет 4 пары разделителей (summary-en/ru +
features-en/ru), непустой контент в каждой паре.
- `description` тулзы обновлён (упоминание 4 пар разделителей).
- `.describe()` для новых параметров; убраны для tier/demo_gif/problem/solution.
Обновлён скилл `repo-readme` (`.opencode/skills/repo-readme/SKILL.md`):
- §2 (Tier system) убран полностью, секции перенумерованы (3→2, 4→3, …, 8→7).
- §1 убрано упоминание tier.
- §4 (Зачем разделители) — обновлён список: 4 пары (summary + features, EN +
RU).
- §5 (Шаблон README) — новый шаблон с Why/What + Features table.
- §7 (Параметры тулзы) — обновлён список: убраны tier/demo_gif/problem/solution,
добавлены why/what/features_en/features_ru.
`README.md` перегенерирован по новому стандарту (11 фич в Features table, EN +
RU).
`docs/project-map/README.md` — описания `repo-readme/SKILL.md` и
`create-readme.ts` обновлены (убрано "tier system", добавлено "features table,
4 delimiter pairs").
Проверки: `tsc --noEmit` (с `--types node`) чисто; `pytest
tests/test_permissions.py` OK; `check-permissions.py` OK.
## Почему
Problem/Solution не универсален для всех репо — не каждый проект укладывается
в "проблема → решение". Tier system усложнял поддержку (два пути генерации).
Features table — обязательная часть для витрины slaid098.dev: каждая карточка
должна показывать фичи. 4 пары разделителей (summary + features, EN + RU)
позволяют витрине парсить и summary, и таблицу фич независимо.
## Pending
## Watch out
`custom_sections_en/ru` теперь рендерятся ВНЕ delimiter tags — после
`<!-- features-en:end -->` и до `### ⚡ Quick Start`. В старом формате они
были внутри `<!-- summary-en:end -->`. `validate` проверяет 4 пары
разделителей вместо 2 — все существующие README, сгенерированные старой
версией тулзы, не пройдут валидацию (нет features-en/ru delimiter pairs).
Требуется перегенерация через `create-readme` (mode: create).

View file

@ -35,7 +35,7 @@ opencode-config/
│ │ ├── python-development/SKILL.md # Python dev patterns
│ │ ├── release/SKILL.md # Tag + GitHub Release
│ │ ├── repo-init/SKILL.md # New repository bootstrap
│ │ ├── repo-readme/SKILL.md # Standardized README generation (create-readme tool: create vs validate, tier system, GitHub metadata) — PR#112
│ │ ├── repo-readme/SKILL.md # Standardized README generation (create-readme tool: create vs validate, bilingual Why/What + Features table, GitHub metadata) — PR#112, PR#116
│ │ ├── tunnel/SKILL.md # Cloudflare tunnel toggle (tool `tunnel()`: 1-й вызов start, 2-й stop) — PR#63 (восстановлен, удалён в PR#42)
│ │ ├── run-tests/SKILL.md # Test runner guide
│ │ └── spec/SKILL.md # 9-phase spec generation
@ -44,7 +44,7 @@ opencode-config/
│ │ ├── commit.ts # commit tool wrapper (1 arg message, validates format+staged) — PR#38
│ │ ├── create-issue.ts # create-issue tool wrapper (3 args, validates format+labels; optional repo?: string) — PR#38, PR#65
│ │ ├── create-pr.ts # create-pr tool wrapper (3 args, validates format+Closes #N; optional repo?: string) — PR#38, PR#65
│ │ ├── create-readme.ts # create-readme tool (TS plugin, modes: create/validate; standardized bilingual README with slaid098.dev delimiters; local fs + remote gh api) — PR#112
│ │ ├── create-readme.ts # create-readme tool (TS plugin, modes: create/validate; standardized bilingual README with features table, 4 delimiter pairs for slaid098.dev; local fs + remote gh api) — PR#112, PR#116
│ │ ├── merge-pr.ts # merge-pr tool wrapper (orchestrator-safe gh pr merge; optional repo?: string) — PR#30, PR#65
│ │ ├── memory-access.ts # memory-access tool (bump frontmatter last_accessed/access_count, regex replace, atomic write tmp+rename) — PR#101
│ │ ├── memory-doctor.ts # memory-doctor tool (read-only diagnostics: rg, Python src.memory importability, env vars, memory dir, RAG index; markdown ✅/❌ report) — PR#101