feat(create-readme): add bilingual tagline and repo_name validation (#151)
* feat(create-readme): add bilingual tagline and repo_name validation * docs(repo-readme): bilingual tagline + repo_name validation rules * docs(handoff): add handoff and ADR-064 for bilingual tagline * docs(handoff): set PR number * docs(project-map): update after PR#151 bilingual tagline changes --------- Co-authored-by: opencode-agent <agent@opencode.local>
This commit is contained in:
parent
4bb504a604
commit
6c80eb8458
5 changed files with 226 additions and 18 deletions
|
|
@ -54,6 +54,10 @@ repos/{owner}/{repo}/contents/README.md` с base64-контентом и SHA.
|
||||||
Витрина **slaid098.dev** скачивает raw `README.md` из каждого репо и
|
Витрина **slaid098.dev** скачивает raw `README.md` из каждого репо и
|
||||||
извлекает фрагменты между разделителями:
|
извлекает фрагменты между разделителями:
|
||||||
|
|
||||||
|
- `<!-- tagline-en:start -->` ... `<!-- tagline-en:end -->` — английский
|
||||||
|
tagline (короткая фраза для карточки).
|
||||||
|
- `<!-- tagline-ru:start -->` ... `<!-- tagline-ru:end -->` — русский
|
||||||
|
tagline.
|
||||||
- `<!-- summary-en:start -->` ... `<!-- summary-en:end -->` — английский
|
- `<!-- summary-en:start -->` ... `<!-- summary-en:end -->` — английский
|
||||||
блок Why/What для карточки.
|
блок Why/What для карточки.
|
||||||
- `<!-- features-en:start -->` ... `<!-- features-en:end -->` — английская
|
- `<!-- features-en:start -->` ... `<!-- features-en:end -->` — английская
|
||||||
|
|
@ -73,7 +77,12 @@ repos/{owner}/{repo}/contents/README.md` с base64-контентом и SHA.
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# 🚀 {repo_name}
|
# 🚀 {repo_name}
|
||||||
> {tagline}
|
<!-- tagline-en:start -->
|
||||||
|
> {tagline_en}
|
||||||
|
<!-- tagline-en:end -->
|
||||||
|
<!-- tagline-ru:start -->
|
||||||
|
> {tagline_ru}
|
||||||
|
<!-- tagline-ru:end -->
|
||||||
|
|
||||||
[English](#-english) | [Русский](#-русский)
|
[English](#-english) | [Русский](#-русский)
|
||||||
|
|
||||||
|
|
@ -146,12 +155,13 @@ repos/{owner}/{repo}/contents/README.md` с base64-контентом и SHA.
|
||||||
👉 **[slaid098.dev/support](https://slaid098.dev/support)**
|
👉 **[slaid098.dev/support](https://slaid098.dev/support)**
|
||||||
```
|
```
|
||||||
|
|
||||||
`validate` проверяет: наличие всех 4 пар EN/RU разделителей (summary +
|
`validate` проверяет: наличие всех 6 пар EN/RU разделителей (tagline + summary +
|
||||||
features), непустой контент между ними, ссылку `slaid098.dev/support`, секции
|
features), непустой контент между ними, H1 title prefix `# 🚀 `, ссылку
|
||||||
Quick Start (EN) и Быстрый старт (RU), language switcher `[English]` /
|
`slaid098.dev/support`, секции Quick Start (EN) и Быстрый старт (RU), language
|
||||||
`[Русский]`, заголовок `## 🇷🇺 Русский` (не "Русская версия"), anchor
|
switcher `[English]` / `[Русский]`, заголовок `## 🇷🇺 Русский` (не "Русская
|
||||||
`[Русский](#-русский)` (не `#-русская-версия`). Шаги `quick_start_steps_*`
|
версия"), anchor `[Русский](#-русский)` (не `#-русская-версия`). Шаги
|
||||||
не влияют на валидацию — они рендерятся вне delimiter-пар (summary/features).
|
`quick_start_steps_*` не влияют на валидацию — они рендерятся вне delimiter-пар
|
||||||
|
(summary/features).
|
||||||
|
|
||||||
## 6. Независимость от repo-init
|
## 6. Независимость от repo-init
|
||||||
|
|
||||||
|
|
@ -167,8 +177,17 @@ Quick Start (EN) и Быстрый старт (RU), language switcher `[English]
|
||||||
`create-readme`:
|
`create-readme`:
|
||||||
|
|
||||||
- `mode` — `"create"` | `"validate"` (обязательный).
|
- `mode` — `"create"` | `"validate"` (обязательный).
|
||||||
- `repo_name`, `tagline`, `why_en`, `what_en`, `why_ru`, `what_ru`,
|
- `repo_name`, `tagline_en`, `tagline_ru`, `why_en`, `what_en`, `why_ru`,
|
||||||
`quick_start`, `features_en`, `features_ru` — обязательны для `create`.
|
`what_ru`, `quick_start`, `features_en`, `features_ru` — обязательны для
|
||||||
|
`create`.
|
||||||
|
- `repo_name` — должен быть **lowercase kebab-case** (regex
|
||||||
|
`^[a-z0-9]+(-[a-z0-9]+)*$`): только `a-z`, `0-9`, одиночные дефисы. Uppercase,
|
||||||
|
underscores, пробелы, leading/trailing/consecutive dashes — отвергаются.
|
||||||
|
- `tagline_en` — короткий английский tagline (1 предложение). **Не должен
|
||||||
|
содержать кириллицы** (валидируется regex `/[ЁА-яё]/`).
|
||||||
|
- `tagline_ru` — короткий русский tagline (1 предложение). **Должен содержать
|
||||||
|
кириллицу** (валидируется regex `/[ЁА-яё]/`). Гарантирует, что русский
|
||||||
|
tagline реально на русском, а не копия английского.
|
||||||
- `features_en` / `features_ru` — массив `{ emoji, name, description }[]`.
|
- `features_en` / `features_ru` — массив `{ emoji, name, description }[]`.
|
||||||
- `custom_sections_en` / `custom_sections_ru` — массивы
|
- `custom_sections_en` / `custom_sections_ru` — массивы
|
||||||
`{ title, content }` (optional).
|
`{ title, content }` (optional).
|
||||||
|
|
@ -213,7 +232,8 @@ Quick Start (EN) и Быстрый старт (RU), language switcher `[English]
|
||||||
{
|
{
|
||||||
"mode": "create",
|
"mode": "create",
|
||||||
"repo_name": "my-userscript",
|
"repo_name": "my-userscript",
|
||||||
"tagline": "One-line tagline.",
|
"tagline_en": "One-line tagline.",
|
||||||
|
"tagline_ru": "Короткий теглайн.",
|
||||||
"why_en": "Why this exists.",
|
"why_en": "Why this exists.",
|
||||||
"what_en": "What it does.",
|
"what_en": "What it does.",
|
||||||
"why_ru": "Зачем этот проект.",
|
"why_ru": "Зачем этот проект.",
|
||||||
|
|
@ -249,3 +269,19 @@ Access at [http://localhost:4096](http://localhost:4096)
|
||||||
|
|
||||||
`include_clone: false` + `quick_start: ""` → bash-блок не рендерится (только
|
`include_clone: false` + `quick_start: ""` → bash-блок не рендерится (только
|
||||||
шаги). Если `quick_start` непустой — bash-блок рендерится перед шагами.
|
шаги). Если `quick_start` непустой — bash-блок рендерится перед шагами.
|
||||||
|
|
||||||
|
## 9. Breaking change: tagline → tagline_en + tagline_ru
|
||||||
|
|
||||||
|
Параметр `tagline` **удалён** (PR #150). Раньше был один английский tagline;
|
||||||
|
теперь два — `tagline_en` (EN) и `tagline_ru` (RU) — оба обязательны для
|
||||||
|
`create`. Витрина slaid098.dev парсит оба через delimiter-теги
|
||||||
|
`<!-- tagline-en:start/end -->` и `<!-- tagline-ru:start/end -->`.
|
||||||
|
|
||||||
|
**Миграция:** замени `"tagline": "..."` на `"tagline_en": "..."` +
|
||||||
|
`"tagline_ru": "..."`. Старые вызовы с `tagline` падают с
|
||||||
|
`❌ tagline_en is required for create mode`.
|
||||||
|
|
||||||
|
**Существующие README** (без tagline delimiter-тегов) станут invalid при
|
||||||
|
`validate` — `Missing <!-- tagline-en:start --> delimiter` и
|
||||||
|
`Missing <!-- tagline-ru:start --> delimiter`. Регенерация README через
|
||||||
|
`create` (с новыми параметрами) делается отдельным шагом после merge.
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ type CustomSection = { title: string; content: string }
|
||||||
|
|
||||||
type CreateArgs = {
|
type CreateArgs = {
|
||||||
repo_name: string
|
repo_name: string
|
||||||
tagline: string
|
tagline_en: string
|
||||||
|
tagline_ru: string
|
||||||
why_en: string
|
why_en: string
|
||||||
what_en: string
|
what_en: string
|
||||||
why_ru: string
|
why_ru: string
|
||||||
|
|
@ -79,7 +80,12 @@ function generateReadme(args: CreateArgs): string {
|
||||||
const stepsRu = renderSteps(args.quick_start_steps_ru)
|
const stepsRu = renderSteps(args.quick_start_steps_ru)
|
||||||
|
|
||||||
return `# 🚀 ${args.repo_name}
|
return `# 🚀 ${args.repo_name}
|
||||||
> ${args.tagline}
|
<!-- tagline-en:start -->
|
||||||
|
> ${args.tagline_en}
|
||||||
|
<!-- tagline-en:end -->
|
||||||
|
<!-- tagline-ru:start -->
|
||||||
|
> ${args.tagline_ru}
|
||||||
|
<!-- tagline-ru:end -->
|
||||||
|
|
||||||
[English](#-english) | [Русский](#-русский)
|
[English](#-english) | [Русский](#-русский)
|
||||||
|
|
||||||
|
|
@ -131,6 +137,10 @@ function validateReadme(content: string): { ok: boolean; issues: string[] } {
|
||||||
const issues: string[] = []
|
const issues: string[] = []
|
||||||
|
|
||||||
const delimiters = [
|
const delimiters = [
|
||||||
|
"tagline-en:start",
|
||||||
|
"tagline-en:end",
|
||||||
|
"tagline-ru:start",
|
||||||
|
"tagline-ru:end",
|
||||||
"summary-en:start",
|
"summary-en:start",
|
||||||
"summary-en:end",
|
"summary-en:end",
|
||||||
"features-en:start",
|
"features-en:start",
|
||||||
|
|
@ -146,6 +156,8 @@ function validateReadme(content: string): { ok: boolean; issues: string[] } {
|
||||||
}
|
}
|
||||||
|
|
||||||
const pairs = [
|
const pairs = [
|
||||||
|
{ start: "<!-- tagline-en:start -->", end: "<!-- tagline-en:end -->", label: "EN tagline" },
|
||||||
|
{ start: "<!-- tagline-ru:start -->", end: "<!-- tagline-ru:end -->", label: "RU tagline" },
|
||||||
{ start: "<!-- summary-en:start -->", end: "<!-- summary-en:end -->", label: "EN summary" },
|
{ start: "<!-- summary-en:start -->", end: "<!-- summary-en:end -->", label: "EN summary" },
|
||||||
{ start: "<!-- features-en:start -->", end: "<!-- features-en:end -->", label: "EN features" },
|
{ start: "<!-- features-en:start -->", end: "<!-- features-en:end -->", label: "EN features" },
|
||||||
{ start: "<!-- summary-ru:start -->", end: "<!-- summary-ru:end -->", label: "RU summary" },
|
{ start: "<!-- summary-ru:start -->", end: "<!-- summary-ru:end -->", label: "RU summary" },
|
||||||
|
|
@ -157,6 +169,8 @@ function validateReadme(content: string): { ok: boolean; issues: string[] } {
|
||||||
issues.push(`${p.label} content between delimiters is empty`)
|
issues.push(`${p.label} content between delimiters is empty`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!content.includes("# 🚀 "))
|
||||||
|
issues.push("Missing H1 title prefix '# 🚀 '")
|
||||||
if (!content.includes("slaid098.dev/support"))
|
if (!content.includes("slaid098.dev/support"))
|
||||||
issues.push("Missing Support link (slaid098.dev/support)")
|
issues.push("Missing Support link (slaid098.dev/support)")
|
||||||
if (!content.includes("Quick Start"))
|
if (!content.includes("Quick Start"))
|
||||||
|
|
@ -188,10 +202,14 @@ export default tool({
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.describe("Repository name (e.g. 'anti-detect-mcp'). Required for create mode."),
|
.describe("Repository name (e.g. 'anti-detect-mcp'). Required for create mode."),
|
||||||
tagline: tool.schema
|
tagline_en: tool.schema
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.describe("Short English tagline (1 sentence). Required for create mode."),
|
.describe("Short English tagline (1 sentence). Required for create mode. Must not contain Cyrillic."),
|
||||||
|
tagline_ru: tool.schema
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe("Short Russian tagline (1 sentence). Required for create mode. Must contain Cyrillic."),
|
||||||
why_en: tool.schema
|
why_en: tool.schema
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
|
|
@ -290,7 +308,8 @@ export default tool({
|
||||||
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_en: args.tagline_en,
|
||||||
|
tagline_ru: args.tagline_ru,
|
||||||
why_en: args.why_en,
|
why_en: args.why_en,
|
||||||
what_en: args.what_en,
|
what_en: args.what_en,
|
||||||
why_ru: args.why_ru,
|
why_ru: args.why_ru,
|
||||||
|
|
@ -304,6 +323,13 @@ export default tool({
|
||||||
if (!args.features_ru || args.features_ru.length === 0)
|
if (!args.features_ru || args.features_ru.length === 0)
|
||||||
return `❌ features_ru is required for create mode`
|
return `❌ features_ru is required for create mode`
|
||||||
|
|
||||||
|
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(args.repo_name!))
|
||||||
|
return `❌ repo_name must be lowercase kebab-case (a-z0-9 with single dashes), got: "${args.repo_name}"`
|
||||||
|
if (/[ЁА-яё]/.test(args.tagline_en!))
|
||||||
|
return `❌ tagline_en must be English (no Cyrillic), got Cyrillic chars`
|
||||||
|
if (!/[ЁА-яё]/.test(args.tagline_ru!))
|
||||||
|
return `❌ tagline_ru must contain Cyrillic (Russian text), got: "${args.tagline_ru}"`
|
||||||
|
|
||||||
const hasBash = args.include_clone !== false || (args.quick_start ?? "") !== ""
|
const hasBash = args.include_clone !== false || (args.quick_start ?? "") !== ""
|
||||||
const hasStepsEn = !!args.quick_start_steps_en && args.quick_start_steps_en.length > 0
|
const hasStepsEn = !!args.quick_start_steps_en && args.quick_start_steps_en.length > 0
|
||||||
const hasStepsRu = !!args.quick_start_steps_ru && args.quick_start_steps_ru.length > 0
|
const hasStepsRu = !!args.quick_start_steps_ru && args.quick_start_steps_ru.length > 0
|
||||||
|
|
@ -314,7 +340,8 @@ export default tool({
|
||||||
|
|
||||||
const content = generateReadme({
|
const content = generateReadme({
|
||||||
repo_name: args.repo_name!,
|
repo_name: args.repo_name!,
|
||||||
tagline: args.tagline!,
|
tagline_en: args.tagline_en!,
|
||||||
|
tagline_ru: args.tagline_ru!,
|
||||||
why_en: args.why_en!,
|
why_en: args.why_en!,
|
||||||
what_en: args.what_en!,
|
what_en: args.what_en!,
|
||||||
why_ru: args.why_ru!,
|
why_ru: args.why_ru!,
|
||||||
|
|
|
||||||
87
docs/decisions/064-pr-151-bilingual-tagline.md
Normal file
87
docs/decisions/064-pr-151-bilingual-tagline.md
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
# ADR-064: Bilingual tagline and repo_name validation (PR-151)
|
||||||
|
|
||||||
|
## Статус
|
||||||
|
Accepted (2026-07-30)
|
||||||
|
|
||||||
|
## Контекст
|
||||||
|
ADR-062 (PR-146) и ADR-063 (PR-149) эволюционировали тулзу `create-readme`
|
||||||
|
вокруг Quick Start. Параметр `tagline` оставался единым английским теглайном
|
||||||
|
с момента ADR-058 (PR-116, README standard v2). Витрина **slaid098.dev**
|
||||||
|
парсит README через delimiter-теги (summary/features), но tagline
|
||||||
|
рендерился вне delimiterов — парсер не мог его надёжно извлечь, и RU tagline
|
||||||
|
отсутствовал вообще. Дополнительно: `repo_name` принимал любой строки
|
||||||
|
(uppercase, underscores, пробелы) — генерировал `git clone` ссылки вида
|
||||||
|
`https://github.com/slaid098/MyRepo.git` (валидные, но не matching actual
|
||||||
|
repo slug); `tagline_en` никто не проверял на отсутствие кириллицы, а
|
||||||
|
`tagline_ru` не существовало. Issue #150 требует: (A) билингвальный tagline
|
||||||
|
EN+RU с delimiter-тегами, (B) валидация английского `repo_name` (lowercase
|
||||||
|
kebab-case) и `tagline_en` (no Cyrillic), `tagline_ru` (require Cyrillic).
|
||||||
|
|
||||||
|
## Решение
|
||||||
|
1. **`CreateArgs` type** — `tagline: string` переименован в `tagline_en:
|
||||||
|
string` + `tagline_ru: string` (оба required, без `?`). Старый параметр
|
||||||
|
удалён (breaking).
|
||||||
|
2. **Рендер top-блока** — tagline обёрнут в delimiter-пары:
|
||||||
|
```
|
||||||
|
# 🚀 {repo_name}
|
||||||
|
<!-- tagline-en:start -->
|
||||||
|
> {tagline_en}
|
||||||
|
<!-- tagline-en:end -->
|
||||||
|
<!-- tagline-ru:start -->
|
||||||
|
> {tagline_ru}
|
||||||
|
<!-- tagline-ru:end -->
|
||||||
|
|
||||||
|
[English](#-english) | [Русский](#-русский)
|
||||||
|
```
|
||||||
|
EN-пара идёт первой, затем RU-пара, подряд без пустых строк между ними;
|
||||||
|
пустая строка перед language switcher. Spacing критичен для парсера.
|
||||||
|
3. **Content-валидация в `execute()`** — после presence-чеков (required
|
||||||
|
Record, falsy `!v`), ДО Quick Start guard (PR #149), добавлены:
|
||||||
|
- `repo_name`: `^[a-z0-9]+(-[a-z0-9]+)*$` — lowercase kebab-case, только
|
||||||
|
`a-z`/`0-9`, одиночные дефисы (не leading/trailing/consecutive).
|
||||||
|
- `tagline_en`: `/[ЁА-яё]/` reject — если есть кириллица → ошибка.
|
||||||
|
- `tagline_ru`: `/[ЁА-яё]/` require — если нет кириллицы → ошибка
|
||||||
|
(ловит копию английского tagline).
|
||||||
|
Порядок: presence → content (regex) → Quick Start guard.
|
||||||
|
4. **`validateReadme`** — добавлено 4 delimiter'а в массив (`tagline-en/ru
|
||||||
|
start/end`, итого 12), +2 пары в `pairs` (EN/RU tagline, с empty-content
|
||||||
|
check), +check H1 prefix `# 🚀 ` (substring). repo_name regex в validate
|
||||||
|
НЕ добавлен — legacy README с uppercase repo_name не должны ломаться
|
||||||
|
(валидируется только в create mode).
|
||||||
|
5. **Schema** — `tagline` переименован в `tagline_en`, обновлён `.describe()`
|
||||||
|
("Must not contain Cyrillic"); добавлен `tagline_ru` (".describe": "Must
|
||||||
|
contain Cyrillic"). Оба `.optional()` (required-логика в `execute()`).
|
||||||
|
6. **SKILL.md** — раздел 4 (delimiters): +tagline-en/ru; раздел 5 (шаблон):
|
||||||
|
top-блок с delimiter-тегами; раздел 5 (validate): "6 пар EN/RU
|
||||||
|
разделителей (tagline + summary + features)" + H1 prefix; раздел 7
|
||||||
|
(параметры): `tagline_en`/`tagline_ru` с правилами, `repo_name` с regex;
|
||||||
|
раздел 8 (пример): `tagline_en`/`tagline_ru`; новый раздел 9 "Breaking
|
||||||
|
change: tagline → tagline_en + tagline_ru" с инструкцией миграции.
|
||||||
|
7. **Backward compatibility** — BREAKING: `tagline` удалён, старые вызовы
|
||||||
|
падают с `❌ tagline_en is required for create mode`. Существующие README
|
||||||
|
(без tagline delimiter-тегов) станут invalid при `validate` —
|
||||||
|
`Missing <!-- tagline-en:start --> delimiter` и тег. Регенерация README
|
||||||
|
отдельный шаг после merge.
|
||||||
|
|
||||||
|
## Альтернативы
|
||||||
|
- **Оставить `tagline` как alias к `tagline_en`** — отвергнуто: молчаливый
|
||||||
|
дуализм (то `tagline`, то `tagline_en`) запутывает пользователей и парсер.
|
||||||
|
Явный breaking с понятным сообщением `❌ tagline_en is required` лучше для
|
||||||
|
миграции (одна правка вызова, а не поиск «почему RU tagline пустой»).
|
||||||
|
- **Один `tagline` + авто-перевод** — отвергнуто: перевод tagline — не
|
||||||
|
responsibility тулзы, качество перевода критично для карточки slaid098.dev.
|
||||||
|
Явный `tagline_ru` с require-Cyrillic-чеком гарантирует осмысленный русский.
|
||||||
|
- **Валидировать `repo_name` в `validateReadme`** — отвергнуто: legacy README
|
||||||
|
с uppercase repo_name (например, старые репо до стандарта) не должны
|
||||||
|
ломаться при `validate`. Regex только в `create` (новые README генерируются
|
||||||
|
по стандарту). `validate` проверяет структуру (delimiterы, заголовки), не
|
||||||
|
контент repo_name.
|
||||||
|
- **Строгий H1 check (regex на полный `# 🚀 {repo_name}`)** — отвергнуто:
|
||||||
|
`validate` не знает `repo_name` (работает с произвольным README).
|
||||||
|
Substring-чек `# 🚀 ` ловит отсутствие префикса, не валидирует что после
|
||||||
|
него. Достаточно для regression-детекции.
|
||||||
|
- **Три отдельных tagline-чек (presence + regex EN + regex RU) как один
|
||||||
|
guard** — отвергнуто: три независимых `if return` с разными сообщениями
|
||||||
|
читаемее, чем комбинированный guard. Пользователь видит конкретную ошибку
|
||||||
|
(repo_name / tagline_en / tagline_ru), а не «одна из трёх валидаций не
|
||||||
|
прошла».
|
||||||
58
docs/handoff/pr-151-bilingual-tagline.md
Normal file
58
docs/handoff/pr-151-bilingual-tagline.md
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
---
|
||||||
|
pr: 151
|
||||||
|
title: feat(create-readme): add bilingual tagline and repo_name validation
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что сделано
|
||||||
|
|
||||||
|
В `.opencode/tools/create-readme.ts` (mode `create`): параметр `tagline`
|
||||||
|
переименован в `tagline_en` + добавлен `tagline_ru` (оба required). Рендер
|
||||||
|
top-блока теперь обёрнут в delimiter-теги `<!-- tagline-en:start/end -->` и
|
||||||
|
`<!-- tagline-ru:start/end -->` (для парсера slaid098.dev). Добавлена
|
||||||
|
content-валидация в `execute()` ПОСЛЕ presence-чеков, ДО Quick Start guard:
|
||||||
|
`repo_name` — lowercase kebab-case regex `^[a-z0-9]+(-[a-z0-9]+)*$`;
|
||||||
|
`tagline_en` — no Cyrillic (`/[ЁА-яё]/`); `tagline_ru` — require Cyrillic.
|
||||||
|
`validateReadme`: +4 delimiter'а (tagline-en/ru start/end, итого 12),
|
||||||
|
+2 пары (EN/RU tagline), +check H1 prefix `# 🚀 `. Schema обновлена
|
||||||
|
(`tagline_en`/`tagline_ru` с обновлёнными `.describe()`). SKILL.md обновлён:
|
||||||
|
разделители tagline в список, шаблон top-блока, таблица параметров, пример
|
||||||
|
вызова, новый раздел 9 «Breaking change: tagline → tagline_en + tagline_ru».
|
||||||
|
Smoke-тест: 7 сценариев, 16 assertion'ов, все PASS (happy path + validate ok,
|
||||||
|
repo_name uppercase, repo_name underscore, tagline_en Cyrillic, tagline_ru no
|
||||||
|
Cyrillic, old tagline breaking, validateReadme на старом README без tagline
|
||||||
|
delimiterов).
|
||||||
|
|
||||||
|
## Почему
|
||||||
|
|
||||||
|
Issue #150: slaid098.dev парсер нуждается в отдельном RU tagline (раньше был
|
||||||
|
только один английский `tagline`). Валидация гарантирует: `repo_name` —
|
||||||
|
строго английский kebab-case (lowercase, без uppercase/underscore/пробелов);
|
||||||
|
`tagline_en` — реально на английском (без кириллицы); `tagline_ru` — реально
|
||||||
|
на русском (содержит кириллицу, не копия английского). Breaking change:
|
||||||
|
параметр `tagline` удалён — миграция на `tagline_en` + `tagline_ru`.
|
||||||
|
|
||||||
|
## Pending
|
||||||
|
|
||||||
|
Регенерация существующих README в репо slaid098 через `create-readme` с
|
||||||
|
новыми параметрами (`tagline_en`/`tagline_ru`) — отдельный шаг после merge
|
||||||
|
(PR/issue вне scope #150). Существующие README без tagline delimiter-тегов
|
||||||
|
станут invalid при `validate` (ожидаемо).
|
||||||
|
|
||||||
|
## Watch out
|
||||||
|
|
||||||
|
**BREAKING CHANGE**: параметр `tagline` удалён из `create-readme` (mode
|
||||||
|
`create`). Старые вызовы с `tagline` падают с `❌ tagline_en is required for
|
||||||
|
create mode`. Миграция: заменить `tagline` на `tagline_en` + добавить
|
||||||
|
`tagline_ru`. Существующие README без tagline delimiter-тегов станут
|
||||||
|
invalid при `validate` (`Missing <!-- tagline-en:start --> delimiter`) —
|
||||||
|
регенерация отдельный шаг (см. Pending).
|
||||||
|
|
||||||
|
Порядок валидации в `execute()` (create mode): presence-чеки (required
|
||||||
|
Record) → content-чеки (regex: repo_name, tagline_en, tagline_ru) → Quick
|
||||||
|
Start guard (PR #149). Content-чеки используют non-null assertion `args.x!`
|
||||||
|
— безопасно, т.к. presence-чеки уже прошли. `validateReadme` НЕ проверяет
|
||||||
|
repo_name regex — только create mode (legacy README с uppercase repo_name не
|
||||||
|
должны ломаться). H1 prefix check `# 🚀 ` — substring-чек, не строгий (ищет
|
||||||
|
presence префикса, не валидирует что после него). ADR-064 отмечает
|
||||||
|
backward-compat альтернативу «оставить tagline alias» — отвергнута (явный
|
||||||
|
breaking лучше молчаливого дуализма).
|
||||||
|
|
@ -36,7 +36,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, bilingual Why/What + Features table, GitHub metadata, quick_start_steps clickable steps + conditional bash block) — PR#112, PR#116, PR#118, PR#130, PR#146
|
│ │ ├── repo-readme/SKILL.md # Standardized README generation (create-readme tool: create vs validate, bilingual Why/What + Features table, GitHub metadata, quick_start_steps clickable steps + conditional bash block; tagline_en/tagline_ru required + delimiter-теги, breaking change note) — PR#112, PR#116, PR#118, PR#130, PR#146, PR#151
|
||||||
│ │ ├── 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
|
||||||
|
|
@ -45,7 +45,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 features table, include_clone/development_en/ru/quick_start_steps_en/ru optional params, clickable access_url [url](url), conditional bash block via hasBashBlock, RU heading 'Русский' + anchor checks, 4 delimiter pairs for slaid098.dev; local fs + remote gh api) — PR#112, PR#116, PR#118, PR#130, PR#146, PR#149 (quick_start optional + guard: Quick Start non-empty per-lang via hasBash/hasStepsEn/hasStepsRu; hasBashBlock handles undefined via ?? "")
|
│ │ ├── create-readme.ts # create-readme tool (TS plugin, modes: create/validate; standardized bilingual README with features table, include_clone/development_en/ru/quick_start_steps_en/ru optional params, clickable access_url [url](url), conditional bash block via hasBashBlock, RU heading 'Русский' + anchor checks, 6 delimiter pairs for slaid098.dev (summary-en/ru, features-en/ru, tagline-en/ru); tagline_en+tagline_ru required (BREAKING: tagline removed PR#151), content-валидация repo_name (lowercase kebab-case) + tagline_en (no Cyrillic) + tagline_ru (require Cyrillic), validateReadme H1 prefix check `# 🚀 `; local fs + remote gh api) — PR#112, PR#116, PR#118, PR#130, PR#146, PR#149 (quick_start optional + guard), PR#151 (bilingual tagline + repo_name validation, breaking)
|
||||||
│ │ ├── draw-image.ts # draw-image tool wrapper (opencode plugin, 5 args: template/title/subtitle?/slots?/out?; spawnSync node cli.ts render → sharp PNG) — PR#133
|
│ │ ├── draw-image.ts # draw-image tool wrapper (opencode plugin, 5 args: template/title/subtitle?/slots?/out?; spawnSync node cli.ts render → sharp PNG) — PR#133
|
||||||
│ │ ├── 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
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue