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
|
# Repo README
|
||||||
|
|
||||||
Стандартизация `README.md` во всех репозиториях slaid098 через тулзу
|
Стандартизация `README.md` во всех репозиториях slaid098 через тулзу
|
||||||
`create-readme`. Тулза гарантирует структуру (разделители, Support block,
|
`create-readme`. Тулза гарантирует структуру (разделители, Features table,
|
||||||
Quick Start, language switcher). Скилл даёт контекст: когда вызывать тулзу и
|
Support block, Quick Start, language switcher). Скилл даёт контекст: когда
|
||||||
какие параметры передавать.
|
вызывать тулзу и какие параметры передавать.
|
||||||
|
|
||||||
## 1. Когда использовать тулзу
|
## 1. Когда использовать тулзу
|
||||||
|
|
||||||
- **Новый репо** → `create-readme` (mode: `create`) — генерирует
|
- **Новый репо** → `create-readme` (mode: `create`) — генерирует
|
||||||
стандартизированный README с нуля.
|
стандартизированный двуязычный README с нуля.
|
||||||
- **Проверка существующего README** → `create-readme` (mode: `validate`) —
|
- **Проверка существующего README** → `create-readme` (mode: `validate`) —
|
||||||
проверяет, что структура соответствует стандарту витрины.
|
проверяет, что структура соответствует стандарту витрины.
|
||||||
- **После ручных правок README** → всегда `validate`. Любая правка руками
|
- **После ручных правок README** → всегда `validate`. Любая правка руками
|
||||||
|
|
@ -23,20 +23,7 @@ Quick Start, language switcher). Скилл даёт контекст: когд
|
||||||
Не генерируй README вручную через Write — структура критична для парсинга
|
Не генерируй README вручную через Write — структура критична для парсинга
|
||||||
витриной. Только через тулзу `create-readme`.
|
витриной. Только через тулзу `create-readme`.
|
||||||
|
|
||||||
## 2. Tier system
|
## 2. GitHub metadata (отдельный шаг, НЕ в тулзе)
|
||||||
|
|
||||||
Тулза принимает параметр `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 (отдельный шаг, НЕ в тулзе)
|
|
||||||
|
|
||||||
`create-readme` отвечает только за файл `README.md`. Метаданные репозитория
|
`create-readme` отвечает только за файл `README.md`. Метаданные репозитория
|
||||||
настраиваются отдельно через `gh repo edit` (это bash, не тулза):
|
настраиваются отдельно через `gh repo edit` (это bash, не тулза):
|
||||||
|
|
@ -47,7 +34,7 @@ Quick Start, language switcher). Скилл даёт контекст: когд
|
||||||
|
|
||||||
Метаданные не дублируют README — они для карточки репо на GitHub и поиска.
|
Метаданные не дублируют README — они для карточки репо на GitHub и поиска.
|
||||||
|
|
||||||
## 4. Workflow
|
## 3. Workflow
|
||||||
|
|
||||||
1. `create-readme` (mode: `create`) → генерирует README с гарантированной
|
1. `create-readme` (mode: `create`) → генерирует README с гарантированной
|
||||||
структурой.
|
структурой.
|
||||||
|
|
@ -62,20 +49,25 @@ Quick Start, language switcher). Скилл даёт контекст: когд
|
||||||
(`owner/name`) — тулза сделает PUT через `gh api
|
(`owner/name`) — тулза сделает PUT через `gh api
|
||||||
repos/{owner}/{repo}/contents/README.md` с base64-контентом и SHA.
|
repos/{owner}/{repo}/contents/README.md` с base64-контентом и SHA.
|
||||||
|
|
||||||
## 5. Зачем разделители (контекст для агента)
|
## 4. Зачем разделители (контекст для агента)
|
||||||
|
|
||||||
Витрина **slaid098.dev** скачивает raw `README.md` из каждого репо и
|
Витрина **slaid098.dev** скачивает raw `README.md` из каждого репо и
|
||||||
извлекает фрагмент между разделителями:
|
извлекает фрагменты между разделителями:
|
||||||
|
|
||||||
- `<!-- summary-en:start -->` ... `<!-- summary-en:end -->` — английский
|
- `<!-- summary-en:start -->` ... `<!-- summary-en:end -->` — английский
|
||||||
блок для карточки.
|
блок Why/What для карточки.
|
||||||
- `<!-- summary-ru:start -->` ... `<!-- summary-ru:end -->` — русский блок.
|
- `<!-- features-en:start -->` ... `<!-- features-en:end -->` — английская
|
||||||
|
таблица фич.
|
||||||
|
- `<!-- summary-ru:start -->` ... `<!-- summary-ru:end -->` — русский блок
|
||||||
|
Зачем/Что.
|
||||||
|
- `<!-- features-ru:start -->` ... `<!-- features-ru:end -->` — русская
|
||||||
|
таблица фич.
|
||||||
|
|
||||||
Поэтому структура README **должна быть гарантирована тулзой**, а не агентом.
|
Поэтому структура README **должна быть гарантирована тулзой**, а не агентом.
|
||||||
Агент может менять текст *внутри* разделителей, но не должен удалять/двигать
|
Агент может менять текст *внутри* разделителей, но не должен удалять/двигать
|
||||||
сами разделители. `validate` ловит такие нарушения.
|
сами разделители. `validate` ловит такие нарушения.
|
||||||
|
|
||||||
## 6. Шаблон README (reference)
|
## 5. Шаблон README (reference)
|
||||||
|
|
||||||
Тулза `create-readme` генерирует ровно эту структуру (параметры подставляются):
|
Тулза `create-readme` генерирует ровно эту структуру (параметры подставляются):
|
||||||
|
|
||||||
|
|
@ -90,14 +82,22 @@ repos/{owner}/{repo}/contents/README.md` с base64-контентом и SHA.
|
||||||
## 🇬🇧 English
|
## 🇬🇧 English
|
||||||
|
|
||||||
<!-- summary-en:start -->
|
<!-- summary-en:start -->
|
||||||
### 🔴 Problem
|
### ❓ Why
|
||||||
{problem_en}
|
{why_en}
|
||||||
|
|
||||||
### 🟢 Solution
|
### ✅ What
|
||||||
{solution_en}
|
{what_en}
|
||||||
{custom_sections_en — доп. секции, если переданы}
|
|
||||||
<!-- summary-en:end -->
|
<!-- summary-en:end -->
|
||||||
{demo_gif строка, только если tier=flagship}
|
|
||||||
|
<!-- features-en:start -->
|
||||||
|
### Features
|
||||||
|
|
||||||
|
| Feature | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| {emoji} {name} | {description} |
|
||||||
|
...
|
||||||
|
<!-- features-en:end -->
|
||||||
|
{custom_sections_en — доп. секции, если переданы}
|
||||||
|
|
||||||
### ⚡ Quick Start
|
### ⚡ Quick Start
|
||||||
\`\`\`bash
|
\`\`\`bash
|
||||||
|
|
@ -110,14 +110,23 @@ git clone https://github.com/slaid098/{repo_name}.git
|
||||||
## 🇷🇺 Русская версия
|
## 🇷🇺 Русская версия
|
||||||
|
|
||||||
<!-- summary-ru:start -->
|
<!-- summary-ru:start -->
|
||||||
### 🔴 Проблема
|
### ❓ Зачем
|
||||||
{problem_ru}
|
{why_ru}
|
||||||
|
|
||||||
### 🟢 Решение
|
### ✅ Что
|
||||||
{solution_ru}
|
{what_ru}
|
||||||
{custom_sections_ru — доп. секции, если переданы}
|
|
||||||
<!-- summary-ru:end -->
|
<!-- summary-ru:end -->
|
||||||
|
|
||||||
|
<!-- features-ru:start -->
|
||||||
|
### Фичи
|
||||||
|
|
||||||
|
| Фича | Описание |
|
||||||
|
|------|----------|
|
||||||
|
| {emoji} {name} | {description} |
|
||||||
|
...
|
||||||
|
<!-- features-ru:end -->
|
||||||
|
{custom_sections_ru — доп. секции, если переданы}
|
||||||
|
|
||||||
### ⚡ Быстрый старт
|
### ⚡ Быстрый старт
|
||||||
\`\`\`bash
|
\`\`\`bash
|
||||||
git clone https://github.com/slaid098/{repo_name}.git
|
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?
|
Have questions or want to support?
|
||||||
👉 **[Visit Support & Contact Page](https://slaid098.dev/support)**
|
👉 **[slaid098.dev/support](https://slaid098.dev/support)**
|
||||||
{telegram строка, если передан}
|
{telegram строка, если передан}
|
||||||
```
|
```
|
||||||
|
|
||||||
`validate` проверяет: наличие обоих EN/RU разделителей, непустой контент
|
`validate` проверяет: наличие всех 4 пар EN/RU разделителей (summary +
|
||||||
между ними, ссылку `slaid098.dev/support`, секции Quick Start (EN) и Быстрый
|
features), непустой контент между ними, ссылку `slaid098.dev/support`, секции
|
||||||
старт (RU), language switcher `[English]` / `[Русский]`.
|
Quick Start (EN) и Быстрый старт (RU), language switcher `[English]` /
|
||||||
|
`[Русский]`.
|
||||||
|
|
||||||
## 7. Независимость от repo-init
|
## 6. Независимость от repo-init
|
||||||
|
|
||||||
- Скилл `repo-init` создаёт **пустой** `README.md` как часть инициализации
|
- Скилл `repo-init` создаёт **пустой** `README.md` как часть инициализации
|
||||||
репо.
|
репо.
|
||||||
|
|
@ -146,17 +156,16 @@ Have questions, need custom features, or want to support this project?
|
||||||
- Может применяться к существующим репо без `repo-init` — тулза перезапишет
|
- Может применяться к существующим репо без `repo-init` — тулза перезапишет
|
||||||
`README.md` (локально) или обновит через GitHub API (с SHA).
|
`README.md` (локально) или обновит через GitHub API (с SHA).
|
||||||
|
|
||||||
## 8. Параметры тулзы (кратко)
|
## 7. Параметры тулзы (кратко)
|
||||||
|
|
||||||
`create-readme`:
|
`create-readme`:
|
||||||
|
|
||||||
- `mode` — `"create"` | `"validate"` (обязательный).
|
- `mode` — `"create"` | `"validate"` (обязательный).
|
||||||
- `repo_name`, `tagline`, `problem_en`, `solution_en`, `problem_ru`,
|
- `repo_name`, `tagline`, `why_en`, `what_en`, `why_ru`, `what_ru`,
|
||||||
`solution_ru`, `quick_start` — обязательны для `create`.
|
`quick_start`, `features_en`, `features_ru` — обязательны для `create`.
|
||||||
- `tier` — `"flagship"` | `"utility"` (default `utility`).
|
- `features_en` / `features_ru` — массив `{ emoji, name, description }[]`.
|
||||||
- `telegram` — username без `@` (optional).
|
|
||||||
- `demo_gif` — путь/URL (только для `flagship`).
|
|
||||||
- `custom_sections_en` / `custom_sections_ru` — массивы
|
- `custom_sections_en` / `custom_sections_ru` — массивы
|
||||||
`{ title, content }` (optional).
|
`{ title, content }` (optional).
|
||||||
|
- `telegram` — username без `@` (optional).
|
||||||
- `repo` — `owner/name` для удалённой операции (optional).
|
- `repo` — `owner/name` для удалённой операции (optional).
|
||||||
- `file_path` — локальный путь (default `README.md`).
|
- `file_path` — локальный путь (default `README.md`).
|
||||||
|
|
|
||||||
|
|
@ -2,19 +2,21 @@ import { spawnSync } from "child_process"
|
||||||
import { readFileSync, writeFileSync } from "fs"
|
import { readFileSync, writeFileSync } from "fs"
|
||||||
import { tool } from "@opencode-ai/plugin"
|
import { tool } from "@opencode-ai/plugin"
|
||||||
|
|
||||||
|
type Feature = { emoji: string; name: string; description: string }
|
||||||
|
|
||||||
type CustomSection = { title: string; content: string }
|
type CustomSection = { title: string; content: string }
|
||||||
|
|
||||||
type CreateArgs = {
|
type CreateArgs = {
|
||||||
repo_name: string
|
repo_name: string
|
||||||
tagline: string
|
tagline: string
|
||||||
problem_en: string
|
why_en: string
|
||||||
solution_en: string
|
what_en: string
|
||||||
problem_ru: string
|
why_ru: string
|
||||||
solution_ru: string
|
what_ru: string
|
||||||
quick_start: string
|
quick_start: string
|
||||||
tier?: "flagship" | "utility"
|
features_en: Feature[]
|
||||||
|
features_ru: Feature[]
|
||||||
telegram?: string
|
telegram?: string
|
||||||
demo_gif?: string
|
|
||||||
custom_sections_en?: CustomSection[]
|
custom_sections_en?: CustomSection[]
|
||||||
custom_sections_ru?: 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)
|
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 {
|
function generateReadme(args: CreateArgs): string {
|
||||||
const customEn = (args.custom_sections_en || [])
|
const customEn = (args.custom_sections_en || [])
|
||||||
.map((s) => `\n\n### ${s.title}\n${s.content}`)
|
.map((s) => `\n\n### ${s.title}\n${s.content}`)
|
||||||
|
|
@ -33,10 +45,8 @@ function generateReadme(args: CreateArgs): string {
|
||||||
const customRu = (args.custom_sections_ru || [])
|
const customRu = (args.custom_sections_ru || [])
|
||||||
.map((s) => `\n\n### ${s.title}\n${s.content}`)
|
.map((s) => `\n\n### ${s.title}\n${s.content}`)
|
||||||
.join("")
|
.join("")
|
||||||
const demoLine =
|
|
||||||
args.tier === "flagship" && args.demo_gif ? `\n\n` : ""
|
|
||||||
const telegramLine = args.telegram
|
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}
|
return `# 🚀 ${args.repo_name}
|
||||||
|
|
@ -49,13 +59,17 @@ function generateReadme(args: CreateArgs): string {
|
||||||
## 🇬🇧 English
|
## 🇬🇧 English
|
||||||
|
|
||||||
<!-- summary-en:start -->
|
<!-- summary-en:start -->
|
||||||
### 🔴 Problem
|
### ❓ Why
|
||||||
${args.problem_en}
|
${args.why_en}
|
||||||
|
|
||||||
### 🟢 Solution
|
### ✅ What
|
||||||
${args.solution_en}${customEn}
|
${args.what_en}
|
||||||
<!-- summary-en:end -->
|
<!-- summary-en:end -->
|
||||||
${demoLine}
|
|
||||||
|
<!-- features-en:start -->
|
||||||
|
${renderFeaturesTable(args.features_en, false)}
|
||||||
|
<!-- features-en:end -->${customEn}
|
||||||
|
|
||||||
### ⚡ Quick Start
|
### ⚡ Quick Start
|
||||||
\`\`\`bash
|
\`\`\`bash
|
||||||
git clone https://github.com/slaid098/${args.repo_name}.git
|
git clone https://github.com/slaid098/${args.repo_name}.git
|
||||||
|
|
@ -67,13 +81,17 @@ ${args.quick_start}
|
||||||
## 🇷🇺 Русская версия
|
## 🇷🇺 Русская версия
|
||||||
|
|
||||||
<!-- summary-ru:start -->
|
<!-- summary-ru:start -->
|
||||||
### 🔴 Проблема
|
### ❓ Зачем
|
||||||
${args.problem_ru}
|
${args.why_ru}
|
||||||
|
|
||||||
### 🟢 Решение
|
### ✅ Что
|
||||||
${args.solution_ru}${customRu}
|
${args.what_ru}
|
||||||
<!-- summary-ru:end -->
|
<!-- summary-ru:end -->
|
||||||
|
|
||||||
|
<!-- features-ru:start -->
|
||||||
|
${renderFeaturesTable(args.features_ru, true)}
|
||||||
|
<!-- features-ru:end -->${customRu}
|
||||||
|
|
||||||
### ⚡ Быстрый старт
|
### ⚡ Быстрый старт
|
||||||
\`\`\`bash
|
\`\`\`bash
|
||||||
git clone https://github.com/slaid098/${args.repo_name}.git
|
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?
|
Have questions or want to support?
|
||||||
👉 **[Visit Support & Contact Page](https://slaid098.dev/support)**
|
👉 **[slaid098.dev/support](https://slaid098.dev/support)**${telegramLine}
|
||||||
${telegramLine}
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateReadme(content: string): { ok: boolean; issues: string[] } {
|
function validateReadme(content: string): { ok: boolean; issues: string[] } {
|
||||||
const issues: string[] = []
|
const issues: string[] = []
|
||||||
|
|
||||||
if (!content.includes("<!-- summary-en:start -->"))
|
const delimiters = [
|
||||||
issues.push("Missing <!-- summary-en:start --> delimiter")
|
"summary-en:start",
|
||||||
if (!content.includes("<!-- summary-en:end -->"))
|
"summary-en:end",
|
||||||
issues.push("Missing <!-- summary-en:end --> delimiter")
|
"features-en:start",
|
||||||
if (!content.includes("<!-- summary-ru:start -->"))
|
"features-en:end",
|
||||||
issues.push("Missing <!-- summary-ru:start --> delimiter")
|
"summary-ru:start",
|
||||||
if (!content.includes("<!-- summary-ru:end -->"))
|
"summary-ru:end",
|
||||||
issues.push("Missing <!-- summary-ru:end --> delimiter")
|
"features-ru:start",
|
||||||
|
"features-ru:end",
|
||||||
|
]
|
||||||
|
for (const d of delimiters) {
|
||||||
|
if (!content.includes(`<!-- ${d} -->`))
|
||||||
|
issues.push(`Missing <!-- ${d} --> delimiter`)
|
||||||
|
}
|
||||||
|
|
||||||
const enContent = extractBetween(
|
const pairs = [
|
||||||
content,
|
{ start: "<!-- summary-en:start -->", end: "<!-- summary-en:end -->", label: "EN summary" },
|
||||||
"<!-- summary-en:start -->",
|
{ start: "<!-- features-en:start -->", end: "<!-- features-en:end -->", label: "EN features" },
|
||||||
"<!-- summary-en:end -->",
|
{ start: "<!-- summary-ru:start -->", end: "<!-- summary-ru:end -->", label: "RU summary" },
|
||||||
)
|
{ start: "<!-- features-ru:start -->", end: "<!-- features-ru:end -->", label: "RU features" },
|
||||||
if (enContent !== null && !enContent.trim())
|
]
|
||||||
issues.push("EN summary content between delimiters is empty")
|
for (const p of pairs) {
|
||||||
const ruContent = extractBetween(
|
const between = extractBetween(content, p.start, p.end)
|
||||||
content,
|
if (between !== null && !between.trim())
|
||||||
"<!-- summary-ru:start -->",
|
issues.push(`${p.label} content between delimiters is empty`)
|
||||||
"<!-- summary-ru:end -->",
|
}
|
||||||
)
|
|
||||||
if (ruContent !== null && !ruContent.trim())
|
|
||||||
issues.push("RU summary content between delimiters is empty")
|
|
||||||
|
|
||||||
if (!content.includes("slaid098.dev/support"))
|
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"))
|
if (!content.includes("Quick Start"))
|
||||||
issues.push("Missing 'Quick Start' section (English)")
|
issues.push("Missing 'Quick Start' section (English)")
|
||||||
if (!content.includes("Быстрый старт"))
|
if (!content.includes("Быстрый старт"))
|
||||||
|
|
@ -133,7 +153,7 @@ function validateReadme(content: string): { ok: boolean; issues: string[] } {
|
||||||
|
|
||||||
export default tool({
|
export default tool({
|
||||||
description:
|
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: {
|
args: {
|
||||||
mode: tool.schema
|
mode: tool.schema
|
||||||
.enum(["create", "validate"])
|
.enum(["create", "validate"])
|
||||||
|
|
@ -146,38 +166,50 @@ export default tool({
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.describe("Short English tagline (1 sentence). Required for create mode."),
|
.describe("Short English tagline (1 sentence). Required for create mode."),
|
||||||
problem_en: tool.schema
|
why_en: tool.schema
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.describe("Problem statement in English (1-2 sentences). Required for create mode."),
|
.describe("Why this exists — in English (1-2 sentences). Required for create mode."),
|
||||||
solution_en: tool.schema
|
what_en: tool.schema
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.describe("Solution in English (1-2 sentences). Required for create mode."),
|
.describe("What it does — in English (1-2 sentences). Required for create mode."),
|
||||||
problem_ru: tool.schema
|
why_ru: tool.schema
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.describe("Problem statement in Russian (1-2 sentences). Required for create mode."),
|
.describe("Зачем этот проект — на русском (1-2 предложения). Required for create mode."),
|
||||||
solution_ru: tool.schema
|
what_ru: tool.schema
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.describe("Solution in Russian (1-2 sentences). Required for create mode."),
|
.describe("Что делает — на русском (1-2 предложения). Required for create mode."),
|
||||||
quick_start: tool.schema
|
quick_start: tool.schema
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.describe("Install/setup command (e.g. 'pip install -r requirements.txt'). Required for create mode."),
|
.describe("Install/setup command (e.g. 'pip install -r requirements.txt'). Required for create mode."),
|
||||||
tier: tool.schema
|
features_en: tool.schema
|
||||||
.enum(["flagship", "utility"])
|
.array(
|
||||||
|
tool.schema.object({
|
||||||
|
emoji: tool.schema.string(),
|
||||||
|
name: tool.schema.string(),
|
||||||
|
description: tool.schema.string(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
.optional()
|
.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
|
telegram: tool.schema
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.describe("Telegram username without @. Optional; adds Direct Telegram line to Support section."),
|
.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
|
custom_sections_en: tool.schema
|
||||||
.array(
|
.array(
|
||||||
tool.schema.object({
|
tool.schema.object({
|
||||||
|
|
@ -186,7 +218,7 @@ export default tool({
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.optional()
|
.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
|
custom_sections_ru: tool.schema
|
||||||
.array(
|
.array(
|
||||||
tool.schema.object({
|
tool.schema.object({
|
||||||
|
|
@ -195,7 +227,7 @@ export default tool({
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.optional()
|
.optional()
|
||||||
.describe("Additional sections rendered inside RU delimiters (after Решение)."),
|
.describe("Additional sections rendered after RU features block (outside delimiters)."),
|
||||||
repo: tool.schema
|
repo: tool.schema
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
|
|
@ -208,33 +240,36 @@ export default tool({
|
||||||
async execute(args, context) {
|
async execute(args, context) {
|
||||||
try {
|
try {
|
||||||
const file_path = args.file_path ?? "README.md"
|
const file_path = args.file_path ?? "README.md"
|
||||||
const tier = args.tier ?? "utility"
|
|
||||||
|
|
||||||
if (args.mode === "create") {
|
if (args.mode === "create") {
|
||||||
const required: Record<string, string | undefined> = {
|
const required: Record<string, string | undefined> = {
|
||||||
repo_name: args.repo_name,
|
repo_name: args.repo_name,
|
||||||
tagline: args.tagline,
|
tagline: args.tagline,
|
||||||
problem_en: args.problem_en,
|
why_en: args.why_en,
|
||||||
solution_en: args.solution_en,
|
what_en: args.what_en,
|
||||||
problem_ru: args.problem_ru,
|
why_ru: args.why_ru,
|
||||||
solution_ru: args.solution_ru,
|
what_ru: args.what_ru,
|
||||||
quick_start: args.quick_start,
|
quick_start: args.quick_start,
|
||||||
}
|
}
|
||||||
for (const [k, v] of Object.entries(required)) {
|
for (const [k, v] of Object.entries(required)) {
|
||||||
if (!v) return `❌ ${k} is required for create mode`
|
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({
|
const content = generateReadme({
|
||||||
repo_name: args.repo_name!,
|
repo_name: args.repo_name!,
|
||||||
tagline: args.tagline!,
|
tagline: args.tagline!,
|
||||||
problem_en: args.problem_en!,
|
why_en: args.why_en!,
|
||||||
solution_en: args.solution_en!,
|
what_en: args.what_en!,
|
||||||
problem_ru: args.problem_ru!,
|
why_ru: args.why_ru!,
|
||||||
solution_ru: args.solution_ru!,
|
what_ru: args.what_ru!,
|
||||||
quick_start: args.quick_start!,
|
quick_start: args.quick_start!,
|
||||||
tier,
|
features_en: args.features_en!,
|
||||||
|
features_ru: args.features_ru!,
|
||||||
telegram: args.telegram,
|
telegram: args.telegram,
|
||||||
demo_gif: args.demo_gif,
|
|
||||||
custom_sections_en: args.custom_sections_en,
|
custom_sections_en: args.custom_sections_en,
|
||||||
custom_sections_ru: args.custom_sections_ru,
|
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
|
```bash
|
||||||
git clone https://github.com/slaid098/opencode-config.git
|
git clone https://github.com/slaid098/opencode-config.git
|
||||||
cd opencode-config
|
cd opencode-config
|
||||||
|
|
@ -13,33 +41,37 @@ cp .env.example .env
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
Доступ: http://localhost:4096
|
---
|
||||||
|
|
||||||
### Структура
|
## 🇷🇺 Русская версия
|
||||||
|
|
||||||
| Путь | Описание |
|
<!-- summary-ru:start -->
|
||||||
|
### ❓ Зачем
|
||||||
|
Перенос конфига на новый сервер требовал настройки с нуля каждый раз. Плюс отсутствие версионирования — накосячил со скиллом и не можешь посмотреть, как было раньше.
|
||||||
|
|
||||||
|
### ✅ Что
|
||||||
|
Готовый конфиг в Docker. Клонировал, заполнил `.env`, запустил — работает. Версионирование через Git, память синкается через приватный репозиторий.
|
||||||
|
<!-- summary-ru:end -->
|
||||||
|
|
||||||
|
<!-- features-ru:start -->
|
||||||
|
### Фичи
|
||||||
|
|
||||||
|
| Фича | Описание |
|
||||||
|------|----------|
|
|------|----------|
|
||||||
| `AGENTS.md` | Глобальные правила оркестратора |
|
| 🐳 Переносимость | Docker-конфиг — поднимается на любом сервере одной командой |
|
||||||
| `.opencode/` | Конфигурация проекта |
|
| 📦 Версионирование | Git под капотом — откат к любой версии конфига |
|
||||||
| `.opencode/agents/` | Определения subagent'ов |
|
| 🌐 Глобальные скиллы | Все скиллы прокидываются через volume — работают во всех проектах |
|
||||||
| `.opencode/skills/` | Определения skills (14 skills) |
|
| 🧠 Переносимая память | Markdown + git, синк через приватный репозиторий — ассистент помнит всё |
|
||||||
| `.opencode/scripts/` | Python скрипты (pipeline-status, spec-status) |
|
| 🔍 Двойной поиск | Keyword (ripgrep) всегда работает, semantic — опционально через OpenAI API |
|
||||||
| `src/` | Python RAG CLI |
|
| 🔧 Пайплайн | ISSUE → IMPLEMENT → DOCS → CI → REVIEW → MERGE → MEMORY |
|
||||||
| `docs/` | Handoffs, ADRs, project map |
|
| 🤖 Subagent-оркестрация | Оркестратор только планирует, исполняют subagent'ы |
|
||||||
| `docker-compose.yml` | Docker compose конфиг |
|
| 📋 Линейное выполнение | Один пайплайн за раз — без конфликтов веток |
|
||||||
|
| 🐛 Auto-issue | Баг найден в процессе — агент сам создаёт GitHub issue и продолжает работу |
|
||||||
### Конфигурация
|
| 🛠️ Скиллы и команды | Встроенные скиллы и slash-команды, основной — `/run-pipeline` |
|
||||||
|
| 👥 Subagent'ы | Встроенные агенты по ролям — review, docs, memory sync |
|
||||||
Скопируйте `.env.example` → `.env`, заполните ключи.
|
<!-- features-ru:end -->
|
||||||
|
|
||||||
### Память
|
|
||||||
|
|
||||||
Файловая память (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
|
|
||||||
|
|
||||||
|
### ⚡ Быстрый старт
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/slaid098/opencode-config.git
|
git clone https://github.com/slaid098/opencode-config.git
|
||||||
cd opencode-config
|
cd opencode-config
|
||||||
|
|
@ -47,29 +79,9 @@ cp .env.example .env
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
Access at http://localhost:4096
|
---
|
||||||
|
|
||||||
### Structure
|
## 💬 Support and contacts / Поддержка и контакты
|
||||||
|
|
||||||
| Path | Description |
|
Have questions or want to support?
|
||||||
|------|-------------|
|
👉 **[slaid098.dev/support](https://slaid098.dev/support)**
|
||||||
| `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).
|
|
||||||
|
|
|
||||||
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
|
│ │ ├── python-development/SKILL.md # Python dev patterns
|
||||||
│ │ ├── release/SKILL.md # Tag + GitHub Release
|
│ │ ├── release/SKILL.md # Tag + GitHub Release
|
||||||
│ │ ├── repo-init/SKILL.md # New repository bootstrap
|
│ │ ├── 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)
|
│ │ ├── tunnel/SKILL.md # Cloudflare tunnel toggle (tool `tunnel()`: 1-й вызов start, 2-й stop) — PR#63 (восстановлен, удалён в PR#42)
|
||||||
│ │ ├── run-tests/SKILL.md # Test runner guide
|
│ │ ├── run-tests/SKILL.md # Test runner guide
|
||||||
│ │ └── spec/SKILL.md # 9-phase spec generation
|
│ │ └── 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
|
│ │ ├── 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-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-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
|
│ │ ├── 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-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
|
│ │ ├── 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