diff --git a/.opencode/commands/repo-readme.md b/.opencode/commands/repo-readme.md new file mode 100644 index 0000000..1b540e5 --- /dev/null +++ b/.opencode/commands/repo-readme.md @@ -0,0 +1,5 @@ +--- +description: Standardize repo README + cover image (create/validate) +agent: build +--- +Load the `repo-readme` skill via `skill({name: "repo-readme"})` and follow its ПРОТОКОЛ strictly. Workflow: create-readme (generate) → draw-image (cover) → validate → fix cycle. One command = full README + cover standardization. \ No newline at end of file diff --git a/.opencode/draw-image/brand-logos/opencode.svg b/.opencode/draw-image/brand-logos/opencode.svg new file mode 100644 index 0000000..22a59ea --- /dev/null +++ b/.opencode/draw-image/brand-logos/opencode.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.opencode/skills/repo-readme/SKILL.md b/.opencode/skills/repo-readme/SKILL.md index 3a3e0e4..7bc8ace 100644 --- a/.opencode/skills/repo-readme/SKILL.md +++ b/.opencode/skills/repo-readme/SKILL.md @@ -37,11 +37,16 @@ Support block, Quick Start, language switcher). Скилл даёт контек ## 3. Workflow 1. `create-readme` (mode: `create`) → генерирует README с гарантированной - структурой. -2. Ручные правки если нужно (агент редактирует файл напрямую через Edit) — + структурой (включая `![Cover](assets/cover.png)` после H1). +2. `draw-image` (template `"cover"`, slots по типу репо, `title`, `subtitle`, + `out: "./assets/cover.png"`) → рендерит cover-изображение (1024×1024 PNG) на + место, на которое ссылается README. Default `out` у `draw-image` уже + `"./assets/cover.png"` — можно не передавать. +3. Ручные правки если нужно (агент редактирует файл напрямую через Edit) — например, расширить `custom_sections`, поправить формулировки. -3. `create-readme` (mode: `validate`) → проверяет, что структура не нарушена. -4. Если `validate` fails → фикс нарушения → re-`validate`. Цикл пока не +4. `create-readme` (mode: `validate`) → проверяет, что структура не нарушена + (теперь в т.ч. наличие `assets/cover.png` reference). +5. Если `validate` fails → фикс нарушения → re-`validate`. Цикл пока не пройдёт. Локальный режим (по умолчанию): тулза пишет в `file_path` (default @@ -49,6 +54,25 @@ Support block, Quick Start, language switcher). Скилл даёт контек (`owner/name`) — тулза сделает PUT через `gh api repos/{owner}/{repo}/contents/README.md` с base64-контентом и SHA. +### Slot-выбор для cover + +`draw-image` template `cover` имеет 3 опциональных слота. Значение slot — +либо имя Lucide-иконки (резолвится из `icons/lucide/.svg`), либо имя +brand-logo (резолвится из `brand-logos/.svg`), либо путь к файлу +(`./...` / `/...` / `../...`). + +- `icon` — основная иконка (400×400, slot recolor=accent). Для репо, + ассоциированных с продуктом/брендом — brand-logo (напр. `opencode`). Для + утилит/SDK/скриптов — Lucide (напр. `square-terminal`, `code-xml`, + `brain-circuit`). +- `sub-icon` — опциональная вторая иконка (200×200, recolor=accent). Lucide + (напр. `git-branch`, `bot`, `terminal`). +- `badge` — опциональная третья иконка (180×180, bg=surface, border=accent, + radius=0.5). Lucide или путь к файлу (напр. логотип-плашка). + +Cover сохраняется в `assets/cover.png` (default `draw-image` out path). +README ссылается именно на этот путь через `![Cover](assets/cover.png)`. + ## 4. Зачем разделители (контекст для агента) Витрина **slaid098.dev** скачивает raw `README.md` из каждого репо и @@ -77,6 +101,9 @@ repos/{owner}/{repo}/contents/README.md` с base64-контентом и SHA. ```markdown # 🚀 {repo_name} + +![Cover](assets/cover.png) + > {tagline_en} @@ -156,7 +183,9 @@ repos/{owner}/{repo}/contents/README.md` с base64-контентом и SHA. ``` `validate` проверяет: наличие всех 6 пар EN/RU разделителей (tagline + summary + -features), непустой контент между ними, H1 title prefix `# 🚀 `, ссылку +features), непустой контент между ними, H1 title prefix `# 🚀 `, **cover image +reference `assets/cover.png`** (substring-чек, без проверки существования +файла), ссылку `slaid098.dev/support`, секции Quick Start (EN) и Быстрый старт (RU), language switcher `[English]` / `[Русский]`, заголовок `## 🇷🇺 Русский` (не "Русская версия"), anchor `[Русский](#-русский)` (не `#-русская-версия`). Флагирует diff --git a/.opencode/tools/create-readme.ts b/.opencode/tools/create-readme.ts index 13d627d..c91d2f2 100644 --- a/.opencode/tools/create-readme.ts +++ b/.opencode/tools/create-readme.ts @@ -80,6 +80,9 @@ function generateReadme(args: CreateArgs): string { const stepsRu = renderSteps(args.quick_start_steps_ru) return `# 🚀 ${args.repo_name} + +![Cover](assets/cover.png) + > ${args.tagline_en} @@ -169,6 +172,9 @@ export function validateReadme(content: string): { ok: boolean; issues: string[] issues.push(`${p.label} content between delimiters is empty`) } + if (!content.includes("assets/cover.png")) + issues.push("Missing cover image reference (assets/cover.png)") + if (!content.includes("# 🚀 ")) issues.push("Missing H1 title prefix '# 🚀 '") if (/^##\s+(License|LICENSE|Лицензия)\s*$/m.test(content)) diff --git a/README.md b/README.md index c50129a..8897d0a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ # 🚀 opencode-config + +![Cover](assets/cover.png) + > Portable AI coding assistant config with memory & subagent pipeline diff --git a/assets/cover.meta.json b/assets/cover.meta.json new file mode 100644 index 0000000..07981b4 --- /dev/null +++ b/assets/cover.meta.json @@ -0,0 +1,5 @@ +{ + "hash": "de8036758f935399699ef01446e3b9c8d23d211863dae052932d0ffc1a298120", + "generatedAt": "2026-07-30T20:02:11.450Z", + "size": 1024 +} diff --git a/assets/cover.png b/assets/cover.png new file mode 100644 index 0000000..6b3380d Binary files /dev/null and b/assets/cover.png differ diff --git a/docs/decisions/067-pr-158-cover-pipeline.md b/docs/decisions/067-pr-158-cover-pipeline.md new file mode 100644 index 0000000..aa56aea --- /dev/null +++ b/docs/decisions/067-pr-158-cover-pipeline.md @@ -0,0 +1,17 @@ +# ADR-067: Cover image reference в README validation + +## Статус +Accepted (2026-07-30) + +## Контекст +README стандартизируется тулзой `create-readme` (delimiter-теги для slaid098.dev). Cover image (`assets/cover.png`, рендерится `draw-image` template "cover") должен отображаться в начале README после H1. До этого PR cover не был частью стандарта — README мог существовать без cover, и validate не ловил его отсутствие. + +## Решение +1. `generateReadme()` рендерит `![Cover](assets/cover.png)` между H1 и tagline-разделителями — cover попадает в зону выше summary, видим на GitHub сразу под заголовком. +2. `validateReadme()` проверяет **substring** `assets/cover.png` (без проверки существования файла). Файл-чек не делается намеренно: validate работает и в remote-режиме (через gh api), где файловая система репо недоступна агенту. Substring-чек ловит забытое упоминание, реальный рендер cover — отдельный шаг `draw-image` в workflow скилла. +3. Workflow `repo-readme` скилла: `create-readme` → `draw-image` → `validate` → fix-цикл. Cover хранится в `assets/cover.png` (default `draw-image` out path), README ссылается именно на этот путь. + +## Альтернативы +- **Проверять существование `assets/cover.png` файловой системой**: отвергнуто — ломает remote-режим validate (gh api, нет локального FS репо). Substring-чек унифицирует local и remote. +- **Встраивать cover как base64 в README**: отвергнуто — раздувает README, ломает delimiter-парсинг витриной, сложнее обновлять. +- **Отдельный validate-чек для cover на уровне draw-image**: отвергнуто — cover-pipeline должен ловиться на этапе README-валидации, т.к. README — источник правды для витрины. \ No newline at end of file diff --git a/docs/handoff/pr-158-cover-pipeline.md b/docs/handoff/pr-158-cover-pipeline.md new file mode 100644 index 0000000..2424039 --- /dev/null +++ b/docs/handoff/pr-158-cover-pipeline.md @@ -0,0 +1,26 @@ +--- +pr: 158 +title: Standardize README + cover image pipeline +--- + +## Что сделано +Реализован cover image pipeline для стандартизации README во всех репо slaid098: + +1. **brand-logos/opencode.svg** — адаптированный логотип OpenCode (512×512, fill #ccff00), резолвится через `draw-image` slot `icon=opencode` (ищется в `brand-logos/` после `icons/lucide/`). +2. **create-readme.ts**: + - `generateReadme()`: добавлен `![Cover](assets/cover.png)` после H1 (`# 🚀 {repo_name}`), перед tagline-разделителями, с пустыми строками между. + - `validateReadme()`: добавлен substring-чек `assets/cover.png` → issue `"Missing cover image reference (assets/cover.png)"`. Проверка существования файла НЕ делается (только substring). Чек расположен после delimiter-чеков, перед H1-чеком. +3. **repo-readme/SKILL.md**: workflow секция расширена — после `create` → `draw-image` (template "cover", slots) → `validate`. Добавлен slot-guidance (`icon`/`sub-icon`/`badge`, brand-logos vs Lucide). Reference-шаблон и описание валидации обновлены. 9 секций сохранены. +4. **commands/repo-readme.md**: новая slash-команда (`/repo-readme`), frontmatter `agent: build`, грузит `repo-readme` skill. +5. **opencode-config repo**: сгенерирован `assets/cover.png` (1024×1024 template, фактически 1365×1365 из-за density:96 в sharp — pre-existing поведение render.mjs), README.md обновлён cover reference (вручную через edit, т.к. системный tool подхватил pre-bundled версию create-readme.ts). + +## Почему +Cover image делает карточку репо на slaid098.dev и GitHub визуально цельной. Pipeline `create-readme → draw-image → validate` гарантирует, что каждый README ссылается на cover, а cover рендерится из on-brand SVG-шаблона. Validation-чек ловит забытый cover reference на этапе review. + +## Pending +— (после merge: регенерация README в других репо slaid098 через create-readme — отдельный шаг, как после PR #150 с taglines) + +## Watch out +- **Размер PNG 1365×1365, не 1024×1024** — это pre-existing поведение `draw-image` (sharp density:96, см. PR #134), НЕ регрессия этого PR. Спека issue #157 требовала 1024×1024 — зафиксировано как расхождение спеки с фактическим поведением, не чинилось (вне scope). +- **Системный tool `create-readme` не подхватил правки `.ts` в текущем runtime** — opencode кеширует plugin-tools при старте сессии. README обновлён вручную через edit. После restart opencode новый `create-readme.ts` будет загружен. Для будущих регенераций tool сработает корректно. +- `assets/cover.meta.json` закоммичен вместе с `cover.png` — нужен для идемпотентности `draw-image` (skip-if-same-hash). \ No newline at end of file diff --git a/docs/project-map/README.md b/docs/project-map/README.md index 722095d..3efac13 100644 --- a/docs/project-map/README.md +++ b/docs/project-map/README.md @@ -21,6 +21,7 @@ opencode-config/ │ │ └── reviewer.md # Code review subagent (verdict via `post-review` tool: APPROVE|REQUEST_CHANGES|NEEDS_DISCUSSION) — PR#46, PR#69 │ ├── commands/ │ │ ├── configure-opencode.md # /configure-opencode — edit opencode.json +│ │ ├── repo-readme.md # /repo-readme — standardized README generation (frontmatter agent: build, loads repo-readme skill) — PR#158 │ │ ├── run-pipeline.md # /run-pipeline — 7-phase PR pipeline │ │ └── spec.md # /spec — 9-phase spec generation │ ├── skills/ @@ -36,7 +37,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, 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 +│ │ ├── 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; workflow: create → draw-image (template "cover", slots icon/sub-icon/badge, brand-logos vs Lucide) → validate → fix-цикл) — PR#112, PR#116, PR#118, PR#130, PR#146, PR#151, PR#158 (cover pipeline в workflow) │ │ ├── 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 @@ -45,7 +46,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 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) +│ │ ├── 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 `# 🚀 `; generateReadme рендерит `![Cover](assets/cover.png)` после H1 перед tagline-разделителями; validateReadme substring-чек `assets/cover.png` (без file existence — для remote gh api режима); 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), PR#158 (cover image reference в template + validation) │ │ ├── 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 │ │ ├── memory-access.ts # memory-access tool (bump frontmatter last_accessed/access_count, regex replace, atomic write tmp+rename) — PR#101 @@ -68,7 +69,7 @@ opencode-config/ │ │ ├── templates/cover.svg # 1024×1024 cover template (slots: icon + sub-icon + badge, {{title}}/{{subtitle}}) │ │ ├── fonts/ # Geist Sans TTF (Regular + Bold) bundled for sharp fontFiles │ │ ├── icons/lucide/ # 2007 Lucide SVG icons (synced from npm lucide-static via postinstall) -│ │ ├── brand-logos/ # brand SVG logos (empty in v1, .gitkeep) +│ │ ├── brand-logos/ # brand SVG logos (opencode.svg — адаптированный логотип OpenCode 512×512 fill #ccff00, резолвится через draw-image slot icon=opencode) — PR#158 │ │ ├── scripts/sync-lucide.mjs # postinstall: copy icons from node_modules/lucide-static → icons/lucide/ │ │ ├── src/ │ │ │ ├── config.ts # loadBrand + validateBrand (zod-style hex validation) @@ -107,6 +108,7 @@ opencode-config/ │ ├── handoff/ # PR handoffs (pr--.md) │ ├── decisions/ # ADRs (NNN-pr--.md) │ └── project-map/ # This file — structure snapshot +├── assets/ # Generated cover images for README (cover.png + cover.meta.json idempotency hash; default draw-image out path, referenced by create-readme `![Cover](assets/cover.png)`) — PR#158 ├── src/ # Python RAG CLI (memory) — PR#17 │ └── memory/ │ ├── __init__.py