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:
parent
fc03889c6f
commit
464fa6ec83
6 changed files with 340 additions and 175 deletions
|
|
@ -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`).
|
||||
|
|
|
|||
|
|
@ -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\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
114
README.md
|
|
@ -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)**
|
||||
|
|
|
|||
47
docs/decisions/051-pr-116-readme-standard-v2.md
Normal file
47
docs/decisions/051-pr-116-readme-standard-v2.md
Normal 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) разделяет
|
||||
пользовательский контент и парсимый витриной.
|
||||
62
docs/handoff/pr-116-readme-standard-v2.md
Normal file
62
docs/handoff/pr-116-readme-standard-v2.md
Normal 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).
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue